Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion application/single_app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
EXECUTOR_TYPE = 'thread'
EXECUTOR_MAX_WORKERS = 30
SESSION_TYPE = 'filesystem'
VERSION = "0.261.276"
VERSION = "0.261.277"
IS_DEVELOPMENT = is_development_env_enabled()

# Opt-out for deployments where App Service Easy Auth is active but the platform
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,15 @@
* because an editor saves one record as a whole.
*/
export function WorkspaceEditorFrame({
title, description, icon: TitleIcon, backTo, sections, dirty, saving, readOnly = false, error,
title, description, icon: TitleIcon, iconNode, backTo, sections, dirty, saving, readOnly = false, error,
onSave, onDiscard, saveLabel = 'Save changes', actions, saveDisabled = false, initialSection,
}: {
title: string;
description?: string;
/** The kind of record being edited, shown beside the title. */
icon?: LucideIcon;
/** The record's own icon, such as an agent's uploaded image; shown in place of `icon` when given. */

Check warning on line 79 in application/v2_ui/src/components/workspace/WorkspaceEditorFrame.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
iconNode?: ReactNode;
backTo: string;
sections: WorkspaceEditorSection[];
dirty: boolean;
Expand Down Expand Up @@ -250,10 +252,10 @@
</GlassButton>
<div className="mt-2 flex flex-wrap items-start justify-between gap-3">
<div className="flex min-w-0 flex-[1_1_20rem] items-start gap-3">
{TitleIcon ? (
{iconNode || TitleIcon ? (
<span aria-hidden="true"
className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl border border-edge-strong bg-surface-solid text-text-2">
<TitleIcon size={20} />
className="flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded-xl border border-edge-strong bg-surface-solid text-text-2">

Check warning on line 257 in application/v2_ui/src/components/workspace/WorkspaceEditorFrame.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Moderate - Changed line contains obfuscation, dynamic loading, or hidden payload marker. Recommendation%3A Confirm the changed code is not hiding behavior, decoding payloads, or bypassing normal review.
{iconNode ?? (TitleIcon ? <TitleIcon size={20} /> : null)}
</span>
) : null}
<div className="min-w-0 flex-1">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
<ul className="list-inside list-disc text-sm text-text-3">
{draft.actions_to_load.map((reference) => {
const action = resolveAgentAction(reference, actions);
return <li key={reference}>{action ? agentActionLabel(action) : reference}</li>;
return <li key={reference}>{action ? agentActionLabel(action) : 'Unavailable action'}</li>;

Check warning on line 59 in application/v2_ui/src/components/workspaceAgents/AgentActionPicker.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
})}
</ul>
{!readOnly ? confirmDetach ? (
Expand Down Expand Up @@ -127,9 +127,8 @@
: action.is_global ? <Pill tone="accent">Provided</Pill> : <Pill>Personal</Pill>}
</span>
<span className="mt-1 block break-words text-xs text-text-3">{action.description}</span>
<span className="mt-1 block break-all text-[11px] text-text-3">ID: {action.id}</span>
{action.type === 'agent' && target ? (
<span className="mt-1 block break-words text-xs text-text-2">Calls {targetDetails?.display_name || targetDetails?.name || target.id} · {target.scope_type}</span>
<span className="mt-1 block break-words text-xs text-text-2">Calls {targetDetails?.display_name || targetDetails?.name || 'another agent'} · {target.scope_type}</span>

Check warning on line 131 in application/v2_ui/src/components/workspaceAgents/AgentActionPicker.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
) : null}
{reason ? <span className="mt-1 block text-xs text-warn">{reason}{checked ? ' This saved reference remains until you remove it.' : ''}</span> : null}
</span>
Expand All @@ -156,10 +155,10 @@
{unresolved.length ? (
<EditorPanel tone="warn" title="Saved references to review"
description="Unlisted, ambiguous, and legacy references are kept during unrelated edits. Remove one only when you no longer want it assigned.">
{unresolved.map((reference) => (
{unresolved.map((reference, index) => (
<div key={reference} className="flex flex-wrap items-center justify-between gap-2 text-sm text-text-2">
<code className="min-w-0 break-all">{reference}</code>
{!readOnly ? <GlassButton type="button" size="sm" aria-label={`Remove action reference ${reference}`}
<span className="min-w-0 break-words">Unavailable action {unresolved.length > 1 ? index + 1 : ''}</span>
{!readOnly ? <GlassButton type="button" size="sm" aria-label={`Remove unavailable action${unresolved.length > 1 ? ` ${index + 1}` : ''}`}
onClick={() => setDraft((current) => ({ ...current, actions_to_load: current.actions_to_load.filter((item) => item !== reference) }))}>
<Trash2 size={13} />Remove reference
</GlassButton> : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,8 @@
</EditorFieldset>
<EditorGroup summary="Internal identity">
<AgentTextField label="Internal name" value={draft.name} pattern="[A-Za-z0-9_\-]+" required
help="Derived from the display name only for a new agent. Changing a saved display name does not rename this value or change its stable ID."
help="Derived from the display name only for a new agent. Changing a saved display name does not rename this value."

Check warning on line 168 in application/v2_ui/src/components/workspaceAgents/AgentIdentityFields.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
onChange={(value) => setDraft((current) => ({ ...current, name: value }))} />
<p className="break-all pb-1 text-xs text-text-3">Stable ID: {draft.id || 'Allocated when this agent is first saved.'}</p>
</EditorGroup>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ import { EditorDependents, EditorFieldset, EditorGroup, EditorPanel, EditorSwitc
import { SectionSearch } from '../workspace/primitives';
import { AgentField, AgentNotice, AgentTextField } from './AgentFields';

const SOURCE_TYPE_LABELS: Record<string, string> = {
personal: 'Personal workspace',
group: 'Group workspace',
public: 'Public workspace',
};

function sourceTypeLabel(scope: string): string {
return SOURCE_TYPE_LABELS[scope] ?? 'Workspace';
}

export function AgentKnowledgeFields({
draft, setDraft, catalog, loading, error, onRefresh, readOnly, knowledgeScopes = ['personal', 'public'],
}: {
Expand Down Expand Up @@ -44,7 +54,7 @@ export function AgentKnowledgeFields({
const tags = catalog?.tags ?? [];
const unavailableTags = config.tags.filter((tag) => !tags.some((item) => item.name === tag));
const visibleDocuments = documents.filter((document) =>
`${document.title} ${document.file_name} ${document.source_name} ${document.id}`.toLowerCase().includes(documentQuery.toLowerCase()) &&
`${document.title} ${document.file_name} ${document.source_name}`.toLowerCase().includes(documentQuery.toLowerCase()) &&
(!selectedKeys.size || selectedKeys.has(`${document.scope}:${document.source_id}`) || config.document_ids.includes(document.id)));
const active = catalog ? resolvedAgentDocuments(config, catalog) : [];

Expand Down Expand Up @@ -98,19 +108,19 @@ export function AgentKnowledgeFields({
: 'Only public workspaces can be assigned, because a global agent answers everyone.'} Removing a source does not silently remove stored document references.`}>
<SectionSearch value={sourceQuery} onChange={setSourceQuery} placeholder="Search knowledge workspaces" />
<div className="grid gap-2 sm:grid-cols-2">
{sources.filter((source) => `${source.label} ${source.id}`.toLowerCase().includes(sourceQuery.toLowerCase())).map((source) => (
{sources.filter((source) => `${source.label} ${sourceTypeLabel(source.scope)}`.toLowerCase().includes(sourceQuery.toLowerCase())).map((source) => (
<label key={knowledgeSourceKey(source)} className="flex items-start gap-2 rounded-xl border border-edge p-3 text-sm text-text-2">
<input type="checkbox" className="mt-1 accent-accent" checked={selectedKeys.has(knowledgeSourceKey(source))}
disabled={readOnly} onChange={(event) => toggleSource(source, event.target.checked)} />
<span className="min-w-0 break-words">{source.label}<span className="block break-all text-xs text-text-3">{source.scope} · {source.id}</span></span>
<span className="min-w-0 break-words">{source.label}<span className="block text-xs text-text-3">{sourceTypeLabel(source.scope)}</span></span>
</label>
))}
</div>
{!sources.length && !loading && !error ? <p className="text-xs text-text-3">No authorized workspace sources are available. You may still assign URLs.</p> : null}
{unavailableSourceKeys.map((key) => (
<div key={key} className="flex flex-wrap items-center justify-between gap-2 rounded-lg border border-warn/30 p-2">
<span className="break-all text-xs text-warn">Saved source unavailable here: {key}</span>
{!readOnly ? <GlassButton type="button" size="sm" aria-label={`Remove knowledge source ${key}`} onClick={() => {
<span className="text-xs text-warn">Saved {sourceTypeLabel(key.split(':')[0]).toLowerCase()} is no longer available here</span>
{!readOnly ? <GlassButton type="button" size="sm" aria-label={`Remove unavailable ${sourceTypeLabel(key.split(':')[0]).toLowerCase()}`} onClick={() => {
setDraft((current) => {
const currentConfig = readAgentKnowledge(current);
return updateAgentKnowledge(current, { scopes: {
Expand Down Expand Up @@ -147,7 +157,7 @@ export function AgentKnowledgeFields({
});
})} />
<span className="min-w-0 break-words">{document.title || document.file_name}
<span className="block break-all text-xs text-text-3">{document.source_name} · {document.id}</span>
<span className="block break-words text-xs text-text-3">{[document.source_name, document.title && document.file_name !== document.title ? document.file_name : ''].filter(Boolean).join(' · ')}</span>
{document.tags.length ? <span className="block text-xs text-text-3">{document.tags.join(', ')}</span> : null}
{checked && !inSources ? <span className="block text-xs text-warn">Saved reference retained; its workspace is not selected.</span> : null}
</span>
Expand All @@ -156,10 +166,10 @@ export function AgentKnowledgeFields({
})}
</div>
{visibleDocuments.length > documentLimit ? <GlassButton type="button" size="sm" onClick={() => setDocumentLimit((value) => value + 25)}>Show more documents ({visibleDocuments.length - documentLimit} remaining)</GlassButton> : null}
{unavailableDocuments.map((id) => (
{unavailableDocuments.map((id, index) => (
<div key={id} className="flex flex-wrap items-center justify-between gap-2">
<code className="break-all text-xs text-warn">Unavailable document: {id}</code>
{!readOnly ? <GlassButton type="button" size="sm" aria-label={`Remove document reference ${id}`}
<span className="text-xs text-warn">Saved document {unavailableDocuments.length > 1 ? `${index + 1} ` : ''}is no longer available</span>
{!readOnly ? <GlassButton type="button" size="sm" aria-label={`Remove unavailable document${unavailableDocuments.length > 1 ? ` ${index + 1}` : ''}`}
onClick={() => setDraft((current) => updateAgentKnowledge(current, { document_ids: readAgentKnowledge(current).document_ids.filter((item) => item !== id) }))}>Remove reference</GlassButton> : null}
</div>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
import {
AGENT_INPUT_CLASS, FOUNDRY_SETTINGS_KEYS, agentModelChoices, agentText, applyFoundryDiscovery, clearAgentDraftFields,
foundryEndpointMatches, foundrySettings, selectAgentModel, selectedAgentModel, selectFoundryEndpoint,
updateAgentSetting, type FoundryDiscoveryRecord,
updateAgentSetting, type AgentModelChoice, type FoundryDiscoveryRecord,

Check warning on line 11 in application/v2_ui/src/components/workspaceAgents/AgentModelFields.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
} from '../../lib/workspaceAgentAuthoring';
import { PROVIDER_OPTIONS } from '../../lib/modelConnections';
import { normalizeAgentKnowledgeUrl } from '../../lib/workspaceAgentKnowledge';
import { GlassButton } from '../ui/primitives';
import { EditorGroup, EditorRow, EditorSwitch } from '../workspace/EditorLayout';
Expand Down Expand Up @@ -46,6 +47,35 @@
neutralReadOnlyCopy?: boolean;
}

const GUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

/** A stored value only when it is a readable name; internal identifiers are never shown. */
function readableName(value: unknown): string {
const text = agentText(value).trim();

Check warning on line 54 in application/v2_ui/src/components/workspaceAgents/AgentModelFields.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
return text && !GUID_PATTERN.test(text) ? text : '';
}

function providerLabel(provider: string | undefined): string {
if (!provider) return '';
return PROVIDER_OPTIONS.find((option) => option.value === provider)?.label ?? readableName(provider);
}

/** What the agent calls, in the names people configured: deployment, model, connection and provider. */

Check warning on line 63 in application/v2_ui/src/components/workspaceAgents/AgentModelFields.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
function currentSelection(draft: AgentConfiguration, selected: AgentModelChoice | undefined): Array<[string, string]> {

Check warning on line 64 in application/v2_ui/src/components/workspaceAgents/AgentModelFields.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
const storedDeployment = draft.enable_agent_gpt_apim ? draft.azure_agent_apim_gpt_deployment : draft.azure_openai_gpt_deployment;

Check warning on line 65 in application/v2_ui/src/components/workspaceAgents/AgentModelFields.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.
const deployment = readableName(selected?.deployment) || readableName(storedDeployment) || readableName(draft.model_id);
const model = readableName(selected?.modelName) || deployment;
const connection = selected?.apim || draft.enable_agent_gpt_apim ? 'Azure API Management'
: readableName(selected?.endpointName) || (draft.model_endpoint_id ? 'Saved connection' : 'Configured default');
const provider = providerLabel(selected?.provider || draft.model_provider) || 'Configured default';
return [
['Deployment', deployment || 'Configured default'],
['Model', model || 'Configured default'],
['Connection', connection],
['Provider', provider],
];
}

function LocalModelFields({ draft, setDraft, options, original, allowCustomEndpoints, neutralReadOnlyCopy }: ModelFieldsProps) {
const choices = agentModelChoices(options);
const selected = selectedAgentModel(draft, choices);
Expand All @@ -62,18 +92,19 @@
const choice = choices.find((item) => item.key === event.target.value);
if (choice) setDraft((current) => selectAgentModel(current, choice));
}}>
<option value="">{draft.model_id || draft.azure_openai_gpt_deployment || draft.azure_agent_apim_gpt_deployment || 'Use configured default / choose a model'}</option>
<option value="">{readableName(draft.enable_agent_gpt_apim ? draft.azure_agent_apim_gpt_deployment : draft.azure_openai_gpt_deployment)
|| readableName(draft.model_id) || 'Use configured default / choose a model'}</option>
{choices.map((choice) => <option key={choice.key} value={choice.key}>{choice.label}</option>)}
</select>
</AgentField>
{!choices.length ? <AgentNotice>{neutralReadOnlyCopy
? 'Uses a configured model.'
: 'No enabled models are listed. You can retain the saved connection or configure a custom connection below.'}</AgentNotice> : null}
<EditorRow heading="Current selection" help="What the agent calls when it runs.">
<dl className="grid gap-3 rounded-lg border border-edge bg-surface-1 p-3 text-xs sm:grid-cols-3">
<div className="min-w-0"><dt className="text-text-3">Endpoint ID</dt><dd className="break-all text-text-1">{draft.model_endpoint_id || 'Legacy / configured default'}</dd></div>
<div className="min-w-0"><dt className="text-text-3">Model ID</dt><dd className="break-all text-text-1">{draft.model_id || draft.azure_openai_gpt_deployment || draft.azure_agent_apim_gpt_deployment || 'Configured default'}</dd></div>
<div className="min-w-0"><dt className="text-text-3">Provider</dt><dd className="break-all text-text-1">{draft.model_provider || 'Configured default'}</dd></div>
<dl className="grid gap-3 rounded-lg border border-edge bg-surface-1 p-3 text-xs sm:grid-cols-2 lg:grid-cols-4">
{currentSelection(draft, selected).map(([term, value]) => (
<div key={term} className="min-w-0"><dt className="text-text-3">{term}</dt><dd className="break-words text-text-1">{value}</dd></div>
))}
</dl>
</EditorRow>
<EditorGroup summary="Custom / legacy connection and APIM" open={customOpen} onToggle={setCustomOpen}>
Expand Down Expand Up @@ -203,8 +234,8 @@
}}>
<option value="">Manual Foundry project connection</option>
{endpointId && !selectedEndpoint ? <option value={endpointId}>{neutralReadOnlyCopy
? 'Uses a configured model' : `Saved connection unavailable · ${endpointId}`}</option> : null}
{endpoints.map((endpoint) => <option key={endpoint.id} value={endpoint.id}>{endpoint.name || endpoint.id} · {agentText(endpoint.scope) || 'global'}</option>)}
? 'Uses a configured model' : 'Saved connection unavailable'}</option> : null}
{endpoints.map((endpoint) => <option key={endpoint.id} value={endpoint.id}>{endpoint.name || 'Unnamed connection'} · {agentText(endpoint.scope) || 'global'}</option>)}
</select>
</AgentField>
<div className="flex flex-wrap items-center gap-3 pb-1">
Expand Down
Loading
Loading