@agent-native/core 0.171.3 → 0.172.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/actions/set-tool-approval-policy.d.ts +8 -0
- package/dist/agent/actions/set-tool-approval-policy.js +26 -0
- package/dist/agent/harness/migrations.js +8 -0
- package/dist/agent/harness/runner.js +114 -33
- package/dist/agent/harness/store.d.ts +9 -0
- package/dist/agent/harness/store.js +87 -23
- package/dist/agent/production-agent.d.ts +3 -0
- package/dist/agent/production-agent.js +23 -1
- package/dist/agent/run-manager.js +40 -9
- package/dist/agent/tool-approval-migrations.d.ts +21 -0
- package/dist/agent/tool-approval-migrations.js +32 -0
- package/dist/agent/tool-approval-store.d.ts +11 -0
- package/dist/agent/tool-approval-store.js +76 -1
- package/dist/agent-web/generator.d.ts +44 -10
- package/dist/agent-web/generator.js +38 -0
- package/dist/agent-web/index.d.ts +1 -1
- package/dist/cli/code-agent-executor.js +20 -3
- package/dist/client/AgentPanel.d.ts +4 -1
- package/dist/client/AgentPanel.js +11 -3
- package/dist/client/AssistantChat.d.ts +5 -5
- package/dist/client/AssistantChat.js +24 -11
- package/dist/client/chat/tool-call-display.d.ts +5 -5
- package/dist/client/chat/tool-call-display.js +27 -11
- package/dist/client/resources/McpIntegrationDialog.js +5 -5
- package/dist/client/resources/mcp-integration-catalog.js +1 -1
- package/dist/coding-tools/index.d.ts +31 -0
- package/dist/coding-tools/index.js +139 -3
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/localization/core-messages/ar-SA.js +3 -0
- package/dist/localization/core-messages/de-DE.js +3 -0
- package/dist/localization/core-messages/en-US.d.ts +3 -0
- package/dist/localization/core-messages/en-US.js +3 -0
- package/dist/localization/core-messages/es-ES.js +3 -0
- package/dist/localization/core-messages/fr-FR.js +3 -0
- package/dist/localization/core-messages/hi-IN.js +3 -0
- package/dist/localization/core-messages/ja-JP.js +3 -0
- package/dist/localization/core-messages/ko-KR.js +3 -0
- package/dist/localization/core-messages/pt-BR.js +3 -0
- package/dist/localization/core-messages/zh-CN.js +3 -0
- package/dist/localization/core-messages/zh-TW.js +3 -0
- package/dist/localization/core-messages.d.ts +3 -0
- package/dist/localization/default-messages.d.ts +1 -0
- package/dist/localization/default-messages.js +7 -6
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/action-discovery.js +5 -0
- package/dist/server/agent-chat/prompt-resources.d.ts +9 -0
- package/dist/server/agent-chat/prompt-resources.js +12 -2
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/release-schema.js +4 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/triggers/dispatcher.d.ts +24 -0
- package/dist/triggers/dispatcher.js +76 -18
- package/dist/vite/action-types-plugin.js +4 -0
- package/dist/vite/agent-web-plugin.d.ts +4 -6
- package/dist/vite/agent-web-plugin.js +2 -0
- package/package.json +2 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { IconLoader2, IconAlertTriangle, IconCircleX, IconCheck, IconChevronRight, IconCopy, IconCode, IconBrandSlack, IconTerminal2, IconDatabase, IconSearch, IconFileCode, IconShieldCheck, IconX, } from "@tabler/icons-react";
|
|
2
|
+
import { IconLoader2, IconAlertTriangle, IconCircleX, IconCheck, IconChevronDown, IconChevronRight, IconCopy, IconCode, IconBrandSlack, IconTerminal2, IconDatabase, IconSearch, IconFileCode, IconShieldCheck, IconX, } from "@tabler/icons-react";
|
|
3
3
|
import React, { useState, useEffect, useCallback, useLayoutEffect, useRef, } from "react";
|
|
4
4
|
import { formatAgentChatContextItemsForPrompt } from "../agent-chat.js";
|
|
5
5
|
import { AgentTaskCard } from "../AgentTaskCard.js";
|
|
6
6
|
import { writeClipboardText } from "../clipboard.js";
|
|
7
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "../components/ui/dropdown-menu.js";
|
|
7
8
|
import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
|
|
8
9
|
import { ConnectBuilderCard } from "../ConnectBuilderCard.js";
|
|
9
10
|
import { FileStorageSetupCard } from "../FileStorageSetupCard.js";
|
|
@@ -251,6 +252,8 @@ function ApprovalAffordance({ toolName, toolCallId, approval, }) {
|
|
|
251
252
|
const t = useT();
|
|
252
253
|
const ctx = React.useContext(ApprovalContext);
|
|
253
254
|
const [localResolution, setLocalResolution] = useState(null);
|
|
255
|
+
const [isAlwaysAllowing, setIsAlwaysAllowing] = useState(false);
|
|
256
|
+
const [alwaysAllowFailed, setAlwaysAllowFailed] = useState(false);
|
|
254
257
|
const retainedResolution = ctx?.getApprovalResolution?.(approval.approvalKey, toolCallId, approval.askId) ?? null;
|
|
255
258
|
const resolution = retainedResolution ??
|
|
256
259
|
localResolution ??
|
|
@@ -266,19 +269,32 @@ function ApprovalAffordance({ toolName, toolCallId, approval, }) {
|
|
|
266
269
|
if (resolution === "denied") {
|
|
267
270
|
return (_jsx("div", { className: "mt-1.5 text-xs text-muted-foreground", children: t("agentChat.approval.denied", { tool: toolName }) }));
|
|
268
271
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
272
|
+
const handleAlwaysAllow = async () => {
|
|
273
|
+
if (!ctx?.onAlwaysAllow || isAlwaysAllowing)
|
|
274
|
+
return;
|
|
275
|
+
setIsAlwaysAllowing(true);
|
|
276
|
+
setAlwaysAllowFailed(false);
|
|
277
|
+
try {
|
|
278
|
+
await ctx.onAlwaysAllow(approval.approvalKey, toolName);
|
|
279
|
+
setLocalResolution("approved");
|
|
280
|
+
ctx.onApprovalResolved?.(approval.approvalKey, "approved", toolCallId, approval.askId);
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
setAlwaysAllowFailed(true);
|
|
284
|
+
}
|
|
285
|
+
finally {
|
|
286
|
+
setIsAlwaysAllowing(false);
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
return (_jsxs("div", { className: "mt-1.5 flex min-w-0 flex-wrap items-center gap-2 rounded-md border border-border bg-muted/40 px-2.5 py-1.5", children: [_jsx(IconShieldCheck, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsx("span", { className: "min-w-0 flex-1 text-xs text-muted-foreground", children: t("agentChat.approval.question", { tool: toolName }) }), ctx && (_jsxs("div", { className: "inline-flex shrink-0 items-stretch", children: [_jsxs("button", { type: "button", disabled: isAlwaysAllowing, onClick: () => {
|
|
290
|
+
setLocalResolution("approved");
|
|
291
|
+
ctx.onApprovalResolved?.(approval.approvalKey, "approved", toolCallId, approval.askId);
|
|
292
|
+
ctx.onApprove(approval.approvalKey);
|
|
293
|
+
}, className: cn("inline-flex shrink-0 items-center gap-1 px-2.5 py-1 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90", ctx.onAlwaysAllow ? "rounded-s-md rounded-e-none" : "rounded-md", "disabled:pointer-events-none disabled:opacity-50"), children: [_jsx(IconCheck, { className: "h-3.5 w-3.5" }), t("agentChat.approval.approve")] }), ctx.onAlwaysAllow && (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { type: "button", disabled: isAlwaysAllowing, "aria-label": t("agentChat.approval.moreOptions"), title: t("agentChat.approval.moreOptions"), className: cn("inline-flex w-7 shrink-0 items-center justify-center rounded-s-none rounded-e-md border-s border-background/25 bg-foreground text-background transition-colors hover:bg-foreground/90", "disabled:pointer-events-none disabled:opacity-50"), children: _jsx(IconChevronDown, { className: "h-3.5 w-3.5" }) }) }), _jsx(DropdownMenuContent, { align: "end", children: _jsxs(DropdownMenuItem, { onSelect: () => void handleAlwaysAllow(), title: t("agentChat.approval.alwaysAllowActionHint"), children: [_jsx(IconShieldCheck, { className: "h-4 w-4" }), t("agentChat.approval.alwaysAllowAction")] }) })] }))] })), _jsxs("button", { type: "button", disabled: isAlwaysAllowing, onClick: () => {
|
|
278
294
|
setLocalResolution("denied");
|
|
279
295
|
ctx?.onApprovalResolved?.(approval.approvalKey, "denied", toolCallId, approval.askId);
|
|
280
296
|
ctx?.onDeny?.(approval.approvalKey);
|
|
281
|
-
}, className: cn("inline-flex shrink-0 items-center gap-1 rounded-md border border-border px-2.5 py-1 text-xs font-medium transition-colors", "text-foreground hover:bg-muted"), children: [_jsx(IconX, { className: "h-3.5 w-3.5" }), t("agentChat.approval.deny")] })] }));
|
|
297
|
+
}, className: cn("inline-flex shrink-0 items-center gap-1 rounded-md border border-border px-2.5 py-1 text-xs font-medium transition-colors", "text-foreground hover:bg-muted", "disabled:pointer-events-none disabled:opacity-50"), children: [_jsx(IconX, { className: "h-3.5 w-3.5" }), t("agentChat.approval.deny")] }), alwaysAllowFailed && (_jsx("span", { role: "alert", className: "basis-full text-xs text-destructive", children: t("agentChat.common.saveFailed") }))] }));
|
|
282
298
|
}
|
|
283
299
|
// ─── ToolCallDisplay ──────────────────────────────────────────────────────────
|
|
284
300
|
export function ToolCallDisplay({ toolName, toolCallId, argsText, args, result, mcpApp, chatUI, isRunning, outcome, structuredMeta, activity, approval, repeatCount, isLatestRunning = isRunning, isActiveTail, }) {
|
|
@@ -242,7 +242,7 @@ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId
|
|
|
242
242
|
});
|
|
243
243
|
};
|
|
244
244
|
const quickConnect = (integration) => {
|
|
245
|
-
if (hasOrg) {
|
|
245
|
+
if (hasOrg && supportsMcpIntegrationOrganizationScope(integration)) {
|
|
246
246
|
setSelected(integration);
|
|
247
247
|
setMode("choice");
|
|
248
248
|
return;
|
|
@@ -271,7 +271,7 @@ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId
|
|
|
271
271
|
const selectCatalogConnection = (integration) => {
|
|
272
272
|
if (!mcpServersQuery.isSuccess)
|
|
273
273
|
return;
|
|
274
|
-
if (hasOrg) {
|
|
274
|
+
if (hasOrg && supportsMcpIntegrationOrganizationScope(integration)) {
|
|
275
275
|
setSelected(integration);
|
|
276
276
|
setMode("choice");
|
|
277
277
|
return;
|
|
@@ -329,7 +329,7 @@ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId
|
|
|
329
329
|
if (quickConnectAttemptedRef.current === attemptKey)
|
|
330
330
|
return;
|
|
331
331
|
quickConnectAttemptedRef.current = attemptKey;
|
|
332
|
-
if (hasOrg) {
|
|
332
|
+
if (hasOrg && supportsMcpIntegrationOrganizationScope(integration)) {
|
|
333
333
|
setSelected(integration);
|
|
334
334
|
setMode("choice");
|
|
335
335
|
return;
|
|
@@ -507,7 +507,7 @@ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId
|
|
|
507
507
|
: t("mcpIntegrations.presetAuthDescription")
|
|
508
508
|
: t("mcpIntegrations.customDescription") })] }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-7 py-7 sm:px-10", children: _jsxs("div", { className: "mx-auto max-w-2xl space-y-3", children: [renderScopeSelector(), selected?.setupNoteKey && !selectedRequiresSetup ? (_jsx("div", { className: "rounded-md bg-muted/40 px-3 py-2 text-[11px] leading-relaxed text-muted-foreground", children: t(selected.setupNoteKey) })) : null, selectedRequiresSetup && selected && (_jsxs("div", { className: "mx-auto grid w-full max-w-xl gap-4 py-8", children: [_jsxs("div", { children: [_jsx("p", { className: "text-base font-semibold tracking-[-0.02em] text-foreground", children: t("mcpIntegrations.providerSetupRequired") }), _jsx("p", { className: "mt-1 text-sm leading-6 text-muted-foreground", children: t("mcpIntegrations.providerSetupDescription", {
|
|
509
509
|
name: selected.name,
|
|
510
|
-
}) })] }), selected.setupNoteKey ? (_jsx("p", { className: "text-sm leading-6 text-muted-foreground", children: t(selected.setupNoteKey) })) : null, selected.docsUrl ? (_jsxs("a", { href: selected.docsUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex w-fit items-center gap-1 text-sm font-medium text-foreground underline underline-offset-4 hover:text-muted-foreground", children: [t("mcpIntegrations.viewSetup"), _jsx(IconExternalLink, { className: "size-3.5" })] })) : null] })), !selected && (_jsxs("div", { className: "flex items-center justify-between gap-3 rounded-md bg-muted/30 px-3 py-2 text-[11px]", children: [_jsx("span", { className: "text-muted-foreground", children: customAuthMode === "oauth"
|
|
510
|
+
}) })] }), selected.setupNoteKey ? (_jsx("p", { className: "text-sm leading-6 text-muted-foreground", children: t(selected.setupNoteKey) })) : null, _jsxs("div", { className: "flex items-center justify-between gap-3 rounded-md border border-border px-3 py-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-sm font-medium text-foreground", children: t("mcpIntegrations.personalConnection") }), _jsx("p", { className: "text-[11px] leading-relaxed text-muted-foreground", children: t("mcpIntegrations.personalDescription") })] }), _jsx("span", { className: "shrink-0 text-[11px] text-muted-foreground", children: t("mcpIntegrations.personal") })] }), selected.docsUrl ? (_jsxs("a", { href: selected.docsUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex w-fit items-center gap-1 text-sm font-medium text-foreground underline underline-offset-4 hover:text-muted-foreground", children: [t("mcpIntegrations.viewSetup"), _jsx(IconExternalLink, { className: "size-3.5" })] })) : null] })), !selected && (_jsxs("div", { className: "flex items-center justify-between gap-3 rounded-md bg-muted/30 px-3 py-2 text-[11px]", children: [_jsx("span", { className: "text-muted-foreground", children: customAuthMode === "oauth"
|
|
511
511
|
? t(
|
|
512
512
|
/* i18n-key-ignore */
|
|
513
513
|
"mcpIntegrations.customOAuthDefault", { defaultValue: "Sign in with OAuth" })
|
|
@@ -541,5 +541,5 @@ export function McpIntegrationDialog({ open, onOpenChange, initialIntegrationId
|
|
|
541
541
|
}, placeholder: selected?.headerPlaceholder ??
|
|
542
542
|
t("mcpIntegrations.headersPlaceholder") })] })), selected?.docsUrl && (_jsxs("a", { href: selected.docsUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 text-[11px] text-muted-foreground underline hover:text-foreground", children: [t("mcpIntegrations.openSetupDocs"), _jsx(IconExternalLink, { className: "h-3 w-3" })] }))] })), testResult && (_jsxs("div", { className: cn("flex items-start gap-1 rounded-md px-3 py-2 text-[11px] leading-snug", testResult.ok
|
|
543
543
|
? "bg-green-500/5 text-green-600 dark:text-green-400"
|
|
544
|
-
: "bg-red-500/5 text-red-600 dark:text-red-400"), children: [testResult.ok && (_jsx(IconCheck, { className: "mt-0.5 h-3 w-3 shrink-0" })), _jsx("span", { className: "min-w-0 break-words", children: testResult.message })] })), error && (_jsx("div", { className: "break-words rounded-md bg-red-500/5 px-3 py-2 text-[11px] leading-snug text-red-600 dark:text-red-400", children: error }))] }) }), _jsxs("div", { className: "flex shrink-0 items-center justify-between gap-2 border-t border-border px-7 py-4", children: [!selectedRequiresSetup && (_jsx("button", { type: "button", onClick: runTest, disabled: !url.trim() || busy, className: "rounded-md border border-border bg-background px-3 py-1.5 text-[12px] font-medium text-foreground hover:bg-accent disabled:pointer-events-none disabled:opacity-40", children: t("mcpIntegrations.test") })), !selected && customAuthMode === "oauth" ? (_jsxs("button", { type: "button", onClick: connectCustomWithOAuth, disabled: !name.trim() || !url.trim() || busy, "aria-busy": busy, className: "rounded-md border border-border bg-background px-3 py-1.5 text-[12px] font-medium text-foreground hover:bg-accent disabled:pointer-events-none disabled:opacity-40", children: [busy && (_jsx(IconLoader2, { className: "me-1.5 inline h-3.5 w-3.5 animate-spin" })), t("mcpIntegrations.connectWithOAuth")] })) : null, selectedRequiresSetup ? (
|
|
544
|
+
: "bg-red-500/5 text-red-600 dark:text-red-400"), children: [testResult.ok && (_jsx(IconCheck, { className: "mt-0.5 h-3 w-3 shrink-0" })), _jsx("span", { className: "min-w-0 break-words", children: testResult.message })] })), error && (_jsx("div", { className: "break-words rounded-md bg-red-500/5 px-3 py-2 text-[11px] leading-snug text-red-600 dark:text-red-400", children: error }))] }) }), _jsxs("div", { className: "flex shrink-0 items-center justify-between gap-2 border-t border-border px-7 py-4", children: [!selectedRequiresSetup && (_jsx("button", { type: "button", onClick: runTest, disabled: !url.trim() || busy, className: "rounded-md border border-border bg-background px-3 py-1.5 text-[12px] font-medium text-foreground hover:bg-accent disabled:pointer-events-none disabled:opacity-40", children: t("mcpIntegrations.test") })), !selected && customAuthMode === "oauth" ? (_jsxs("button", { type: "button", onClick: connectCustomWithOAuth, disabled: !name.trim() || !url.trim() || busy, "aria-busy": busy, className: "rounded-md border border-border bg-background px-3 py-1.5 text-[12px] font-medium text-foreground hover:bg-accent disabled:pointer-events-none disabled:opacity-40", children: [busy && (_jsx(IconLoader2, { className: "me-1.5 inline h-3.5 w-3.5 animate-spin" })), t("mcpIntegrations.connectWithOAuth")] })) : null, selectedRequiresSetup ? (_jsx("div", { className: "ms-auto flex items-center gap-2", children: selected?.authMode === "oauth" && (_jsxs("button", { type: "button", onClick: () => connectWithOAuth(selected), disabled: busy, "aria-busy": busy, className: "inline-flex min-w-[132px] items-center justify-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-[12px] font-medium text-primary-foreground hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-40", children: [busy && (_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" })), t("mcpIntegrations.continueToConnect")] })) })) : selected?.authMode === "oauth" ? (_jsxs("button", { type: "button", onClick: () => connectWithOAuth(selected), disabled: !name.trim() || !url.trim() || busy, "aria-busy": busy, className: "inline-flex min-w-[92px] items-center justify-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-[12px] font-medium text-primary-foreground hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-40", children: [busy && _jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), t("mcpIntegrations.connectWithOAuth")] })) : !selected && customAuthMode === "oauth" ? null : (_jsxs("button", { type: "button", onClick: submitForm, disabled: !name.trim() || !url.trim() || busy, className: "inline-flex min-w-[92px] items-center justify-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-[12px] font-medium text-primary-foreground hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-40", children: [busy && _jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), t("mcpIntegrations.connect")] }))] })] }))] }) }));
|
|
545
545
|
}
|
|
@@ -272,7 +272,7 @@ export const DEFAULT_MCP_INTEGRATIONS = [
|
|
|
272
272
|
},
|
|
273
273
|
{
|
|
274
274
|
id: "atlassian",
|
|
275
|
-
name: "
|
|
275
|
+
name: "Jira",
|
|
276
276
|
provider: "atlassian",
|
|
277
277
|
description: "Read and write Jira issues and Confluence content.",
|
|
278
278
|
descriptionKey: "mcpIntegrations.catalog.atlassian.description",
|
|
@@ -5,6 +5,12 @@ export interface CodingCommandResult {
|
|
|
5
5
|
stderr: string;
|
|
6
6
|
timedOut: boolean;
|
|
7
7
|
durationMs?: number;
|
|
8
|
+
/**
|
|
9
|
+
* The command exited but something it started outlived it and still holds the
|
|
10
|
+
* output pipe, so the captured output stops here rather than at end-of-stream.
|
|
11
|
+
* Distinct from a clean finish — the caller is not looking at the whole story.
|
|
12
|
+
*/
|
|
13
|
+
outputStillOpen?: boolean;
|
|
8
14
|
}
|
|
9
15
|
/**
|
|
10
16
|
* Structured metadata emitted on tool_start / tool_done events so the UI can
|
|
@@ -103,4 +109,29 @@ export declare function truncateCodingOutput(value: string, max: number): string
|
|
|
103
109
|
* signal (error messages, test results, etc.).
|
|
104
110
|
*/
|
|
105
111
|
export declare function truncateBashOutput(value: string, headChars?: number, tailChars?: number): string;
|
|
112
|
+
/**
|
|
113
|
+
* Strip shell quoting so a policy regex sees the word the shell will actually
|
|
114
|
+
* run. Bash removes quotes before the command word exists, so `git 'checkout'`
|
|
115
|
+
* and `gi''t checkout` both execute `git checkout` while matching no rule
|
|
116
|
+
* written against the literal text — every denylist entry is otherwise one pair
|
|
117
|
+
* of quotes away from being bypassed.
|
|
118
|
+
*
|
|
119
|
+
* `unanalyzable` reports constructs whose executed text this pass cannot
|
|
120
|
+
* recover, so no rule matched against the canonical form can be trusted:
|
|
121
|
+
* `$'…'` ANSI-C escapes (`$'\x67it'` → `git`) and command substitution
|
|
122
|
+
* (`$(printf git) $(printf checkout) main` runs the forbidden operation while
|
|
123
|
+
* the string contains neither token). Callers must escalate rather than clear a
|
|
124
|
+
* command when this is set. Substitution inside single quotes is literal, so it
|
|
125
|
+
* does not set the flag.
|
|
126
|
+
*
|
|
127
|
+
* ponytail: plain parameter expansion (`$VAR`, `${VAR}`) can also build a token
|
|
128
|
+
* at runtime and is NOT flagged — doing so would make ordinary `cd $TMPDIR`
|
|
129
|
+
* commands require approval. Closing that needs a real shell parser, which is
|
|
130
|
+
* the upgrade path if this boundary ever has to hold against a determined
|
|
131
|
+
* attacker rather than a misbehaving model.
|
|
132
|
+
*/
|
|
133
|
+
export declare function canonicalizeShellCommand(command: string): {
|
|
134
|
+
canonical: string;
|
|
135
|
+
unanalyzable: boolean;
|
|
136
|
+
};
|
|
106
137
|
export declare function isReadOnlyShellCommand(command: string): boolean;
|
|
@@ -3,6 +3,10 @@ import fs from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
const DEFAULT_COMMAND_TIMEOUT_MS = 120_000;
|
|
5
5
|
const DEFAULT_MAX_OUTPUT_CHARS = 50_000;
|
|
6
|
+
/** How long `close` may lag `exit` before we settle on the exit code anyway. */
|
|
7
|
+
const CLOSE_GRACE_MS = 500;
|
|
8
|
+
/** How long a signalled process group has to exit before it is SIGKILLed. */
|
|
9
|
+
const SIGKILL_GRACE_MS = 1_000;
|
|
6
10
|
const DEFAULT_MAX_FILE_READ_CHARS = 120_000;
|
|
7
11
|
/**
|
|
8
12
|
* Output retention window for bash: keep first HEAD_CHARS + last TAIL_CHARS,
|
|
@@ -305,17 +309,45 @@ export function createCodingToolRegistry(options = {}) {
|
|
|
305
309
|
};
|
|
306
310
|
}
|
|
307
311
|
export async function runCodingCommand(command, cwd, timeoutMs, options = {}) {
|
|
312
|
+
// `detached` puts the child in its own process group so `abort` can signal
|
|
313
|
+
// everything it spawned. Without it a timeout signals only the `sh -c`
|
|
314
|
+
// wrapper, which a backgrounding command has usually already outlived.
|
|
308
315
|
const child = spawn(command, {
|
|
309
316
|
cwd,
|
|
310
317
|
shell: true,
|
|
311
318
|
stdio: ["pipe", "pipe", "pipe"],
|
|
319
|
+
detached: true,
|
|
312
320
|
env: { ...process.env, FORCE_COLOR: "0" },
|
|
313
321
|
});
|
|
314
322
|
let stdout = "";
|
|
315
323
|
let stderr = "";
|
|
316
324
|
let timedOut = false;
|
|
325
|
+
let outputStillOpen = false;
|
|
317
326
|
const startMs = Date.now();
|
|
318
|
-
const
|
|
327
|
+
const killGroup = (signal) => {
|
|
328
|
+
try {
|
|
329
|
+
if (child.pid !== undefined)
|
|
330
|
+
process.kill(-child.pid, signal);
|
|
331
|
+
else
|
|
332
|
+
child.kill(signal);
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
// The group is already gone, or we never got a pid. Fall back to the
|
|
336
|
+
// direct child; if that also throws the process is already reaped.
|
|
337
|
+
try {
|
|
338
|
+
child.kill(signal);
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
/* already exited */
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
let sigkillTimer;
|
|
346
|
+
const abort = () => {
|
|
347
|
+
killGroup("SIGTERM");
|
|
348
|
+
sigkillTimer ??= setTimeout(() => killGroup("SIGKILL"), SIGKILL_GRACE_MS);
|
|
349
|
+
sigkillTimer.unref?.();
|
|
350
|
+
};
|
|
319
351
|
const timer = setTimeout(() => {
|
|
320
352
|
timedOut = true;
|
|
321
353
|
abort();
|
|
@@ -341,9 +373,39 @@ export async function runCodingCommand(command, cwd, timeoutMs, options = {}) {
|
|
|
341
373
|
options.signal.addEventListener("abort", abort, { once: true });
|
|
342
374
|
}
|
|
343
375
|
try {
|
|
376
|
+
// `close` fires only once every inherited pipe is closed, so a command that
|
|
377
|
+
// leaves a grandchild running (`npm run dev &`) never reaches it. Treat
|
|
378
|
+
// `exit` as authoritative and allow `close` a short grace to deliver
|
|
379
|
+
// buffered output, then settle on what we have.
|
|
344
380
|
const code = await new Promise((resolve, reject) => {
|
|
345
|
-
|
|
346
|
-
|
|
381
|
+
let settled = false;
|
|
382
|
+
let graceTimer;
|
|
383
|
+
const settle = (value) => {
|
|
384
|
+
if (settled)
|
|
385
|
+
return;
|
|
386
|
+
settled = true;
|
|
387
|
+
if (graceTimer)
|
|
388
|
+
clearTimeout(graceTimer);
|
|
389
|
+
resolve(value);
|
|
390
|
+
};
|
|
391
|
+
child.once("error", (err) => {
|
|
392
|
+
if (settled)
|
|
393
|
+
return;
|
|
394
|
+
settled = true;
|
|
395
|
+
if (graceTimer)
|
|
396
|
+
clearTimeout(graceTimer);
|
|
397
|
+
reject(err);
|
|
398
|
+
});
|
|
399
|
+
child.once("close", (closeCode) => settle(closeCode));
|
|
400
|
+
child.once("exit", (exitCode) => {
|
|
401
|
+
if (settled)
|
|
402
|
+
return;
|
|
403
|
+
graceTimer = setTimeout(() => {
|
|
404
|
+
outputStillOpen = true;
|
|
405
|
+
settle(exitCode);
|
|
406
|
+
}, CLOSE_GRACE_MS);
|
|
407
|
+
graceTimer.unref?.();
|
|
408
|
+
});
|
|
347
409
|
});
|
|
348
410
|
return {
|
|
349
411
|
code,
|
|
@@ -351,10 +413,16 @@ export async function runCodingCommand(command, cwd, timeoutMs, options = {}) {
|
|
|
351
413
|
stderr,
|
|
352
414
|
timedOut,
|
|
353
415
|
durationMs: Date.now() - startMs,
|
|
416
|
+
...(outputStillOpen ? { outputStillOpen: true } : {}),
|
|
354
417
|
};
|
|
355
418
|
}
|
|
356
419
|
finally {
|
|
357
420
|
clearTimeout(timer);
|
|
421
|
+
// Deliberately NOT clearing `sigkillTimer`. It exists only when abort ran,
|
|
422
|
+
// and the exit-grace path can settle before the 1s escalation fires — a
|
|
423
|
+
// descendant that ignored SIGTERM would then outlive the call untouched.
|
|
424
|
+
// Letting it run costs nothing: it is unref'd, and `killGroup` on an
|
|
425
|
+
// already-dead group is a caught no-op.
|
|
358
426
|
options.signal?.removeEventListener("abort", abort);
|
|
359
427
|
}
|
|
360
428
|
}
|
|
@@ -399,6 +467,9 @@ export function formatCodingCommandResult(result, maxChars = DEFAULT_MAX_OUTPUT_
|
|
|
399
467
|
? ""
|
|
400
468
|
: `exitCode: ${result.code}`,
|
|
401
469
|
result.timedOut ? "timedOut: true" : "",
|
|
470
|
+
result.outputStillOpen
|
|
471
|
+
? "note: the command exited but a process it started is still running and holding the output stream, so the output below may be incomplete"
|
|
472
|
+
: "",
|
|
402
473
|
result.stdout ? `stdout:\n${result.stdout}` : "",
|
|
403
474
|
result.stderr ? `stderr:\n${result.stderr}` : "",
|
|
404
475
|
].filter(Boolean);
|
|
@@ -422,6 +493,71 @@ export function truncateBashOutput(value, headChars = BASH_OUTPUT_HEAD_CHARS, ta
|
|
|
422
493
|
const omitted = value.length - max;
|
|
423
494
|
return `${value.slice(0, headChars)}\n\n...[${omitted} chars omitted]\n\n${value.slice(value.length - tailChars)}`;
|
|
424
495
|
}
|
|
496
|
+
/**
|
|
497
|
+
* Strip shell quoting so a policy regex sees the word the shell will actually
|
|
498
|
+
* run. Bash removes quotes before the command word exists, so `git 'checkout'`
|
|
499
|
+
* and `gi''t checkout` both execute `git checkout` while matching no rule
|
|
500
|
+
* written against the literal text — every denylist entry is otherwise one pair
|
|
501
|
+
* of quotes away from being bypassed.
|
|
502
|
+
*
|
|
503
|
+
* `unanalyzable` reports constructs whose executed text this pass cannot
|
|
504
|
+
* recover, so no rule matched against the canonical form can be trusted:
|
|
505
|
+
* `$'…'` ANSI-C escapes (`$'\x67it'` → `git`) and command substitution
|
|
506
|
+
* (`$(printf git) $(printf checkout) main` runs the forbidden operation while
|
|
507
|
+
* the string contains neither token). Callers must escalate rather than clear a
|
|
508
|
+
* command when this is set. Substitution inside single quotes is literal, so it
|
|
509
|
+
* does not set the flag.
|
|
510
|
+
*
|
|
511
|
+
* ponytail: plain parameter expansion (`$VAR`, `${VAR}`) can also build a token
|
|
512
|
+
* at runtime and is NOT flagged — doing so would make ordinary `cd $TMPDIR`
|
|
513
|
+
* commands require approval. Closing that needs a real shell parser, which is
|
|
514
|
+
* the upgrade path if this boundary ever has to hold against a determined
|
|
515
|
+
* attacker rather than a misbehaving model.
|
|
516
|
+
*/
|
|
517
|
+
export function canonicalizeShellCommand(command) {
|
|
518
|
+
let canonical = "";
|
|
519
|
+
let unanalyzable = false;
|
|
520
|
+
let quote = null;
|
|
521
|
+
for (let i = 0; i < command.length; i += 1) {
|
|
522
|
+
const ch = command[i];
|
|
523
|
+
if (quote === "'") {
|
|
524
|
+
if (ch === "'")
|
|
525
|
+
quote = null;
|
|
526
|
+
else
|
|
527
|
+
canonical += ch;
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
if (ch === "\\") {
|
|
531
|
+
const next = command[i + 1];
|
|
532
|
+
if (next === undefined)
|
|
533
|
+
continue;
|
|
534
|
+
// Backslash-newline is a line continuation: both characters vanish.
|
|
535
|
+
if (next !== "\n")
|
|
536
|
+
canonical += next;
|
|
537
|
+
i += 1;
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
// Substitution expands inside double quotes as well as unquoted.
|
|
541
|
+
if (ch === "`" || (ch === "$" && command[i + 1] === "(")) {
|
|
542
|
+
unanalyzable = true;
|
|
543
|
+
}
|
|
544
|
+
if (quote === '"') {
|
|
545
|
+
if (ch === '"')
|
|
546
|
+
quote = null;
|
|
547
|
+
else
|
|
548
|
+
canonical += ch;
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
if (ch === "$" && command[i + 1] === "'")
|
|
552
|
+
unanalyzable = true;
|
|
553
|
+
if (ch === "'" || ch === '"') {
|
|
554
|
+
quote = ch;
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
canonical += ch;
|
|
558
|
+
}
|
|
559
|
+
return { canonical, unanalyzable };
|
|
560
|
+
}
|
|
425
561
|
export function isReadOnlyShellCommand(command) {
|
|
426
562
|
const normalized = command.trim().toLowerCase();
|
|
427
563
|
if (!normalized)
|
|
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
|
|
|
62
62
|
error: string;
|
|
63
63
|
states?: undefined;
|
|
64
64
|
} | {
|
|
65
|
-
error?: undefined;
|
|
66
65
|
states: {
|
|
67
66
|
clientId: number;
|
|
68
67
|
state: string;
|
|
69
68
|
}[];
|
|
69
|
+
error?: undefined;
|
|
70
70
|
}>>;
|
|
71
71
|
/**
|
|
72
72
|
* GET /_agent-native/collab/:docId/users
|
|
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
|
|
|
77
77
|
error: string;
|
|
78
78
|
users?: undefined;
|
|
79
79
|
} | {
|
|
80
|
-
error?: undefined;
|
|
81
80
|
users: {
|
|
82
81
|
clientId: number;
|
|
83
82
|
lastSeen: number;
|
|
84
83
|
}[];
|
|
84
|
+
error?: undefined;
|
|
85
85
|
}>>;
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
|
|
14
14
|
*/
|
|
15
15
|
export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
16
|
-
ok?: undefined;
|
|
17
16
|
error: string;
|
|
17
|
+
ok?: undefined;
|
|
18
18
|
} | {
|
|
19
19
|
error?: undefined;
|
|
20
20
|
ok: boolean;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const messages = {
|
|
2
2
|
"approval.alwaysAllow": "السماح دائمًا",
|
|
3
3
|
"approval.alwaysAllowHint": "الموافقة على هذا الأمر المحدد والسماح به دائمًا",
|
|
4
|
+
"approval.alwaysAllowAction": "السماح بهذا الإجراء دائمًا",
|
|
5
|
+
"approval.alwaysAllowActionHint": "الموافقة والسماح بهذا الإجراء دائمًا",
|
|
4
6
|
"approval.approve": "موافقة",
|
|
5
7
|
"approval.approved": "تمت الموافقة. جارٍ إعادة تشغيل {{tool}}...",
|
|
6
8
|
"approval.denied": "تم الرفض. لم يتم تشغيل {{tool}}.",
|
|
7
9
|
"approval.deny": "رفض",
|
|
10
|
+
"approval.moreOptions": "المزيد من خيارات الموافقة",
|
|
8
11
|
"approval.question": "هل توافق على تشغيل {{tool}}؟",
|
|
9
12
|
"auth.expiredDescription": "ربما انتهت صلاحية جلستك. سجّل الخروج ثم ادخل مجددًا لإعادة الاتصال.",
|
|
10
13
|
"auth.expiredTitle": "انتهت صلاحية الجلسة",
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const messages = {
|
|
2
2
|
"approval.alwaysAllow": "Immer erlauben",
|
|
3
3
|
"approval.alwaysAllowHint": "Diesen exakten Befehl genehmigen und immer erlauben",
|
|
4
|
+
"approval.alwaysAllowAction": "Diese Aktion immer erlauben",
|
|
5
|
+
"approval.alwaysAllowActionHint": "Genehmigen und diese Aktion immer erlauben",
|
|
4
6
|
"approval.approve": "Genehmigen",
|
|
5
7
|
"approval.approved": "Genehmigt. {{tool}} wird erneut ausgeführt...",
|
|
6
8
|
"approval.denied": "Abgelehnt. {{tool}} wurde nicht ausgeführt.",
|
|
7
9
|
"approval.deny": "Ablehnen",
|
|
10
|
+
"approval.moreOptions": "Weitere Genehmigungsoptionen",
|
|
8
11
|
"approval.question": "Ausführung von {{tool}} genehmigen?",
|
|
9
12
|
"auth.expiredDescription": "Deine Sitzung ist möglicherweise abgelaufen. Melde dich ab und wieder an, um die Verbindung wiederherzustellen.",
|
|
10
13
|
"auth.expiredTitle": "Sitzung abgelaufen",
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
declare const messages: {
|
|
2
2
|
readonly "approval.alwaysAllow": "Always allow";
|
|
3
3
|
readonly "approval.alwaysAllowHint": "Approve and always allow this exact command";
|
|
4
|
+
readonly "approval.alwaysAllowAction": "Always allow this action";
|
|
5
|
+
readonly "approval.alwaysAllowActionHint": "Approve and always allow this action";
|
|
4
6
|
readonly "approval.approve": "Approve";
|
|
5
7
|
readonly "approval.approved": "Approved. Re-running {{tool}}...";
|
|
6
8
|
readonly "approval.denied": "Denied. {{tool}} did not run.";
|
|
7
9
|
readonly "approval.deny": "Deny";
|
|
10
|
+
readonly "approval.moreOptions": "More approval options";
|
|
8
11
|
readonly "approval.question": "Approve to run {{tool}}?";
|
|
9
12
|
readonly "auth.expiredDescription": "Your session may have expired. Log out and log back in to reconnect.";
|
|
10
13
|
readonly "auth.expiredTitle": "Session expired";
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const messages = {
|
|
2
2
|
"approval.alwaysAllow": "Always allow",
|
|
3
3
|
"approval.alwaysAllowHint": "Approve and always allow this exact command",
|
|
4
|
+
"approval.alwaysAllowAction": "Always allow this action",
|
|
5
|
+
"approval.alwaysAllowActionHint": "Approve and always allow this action",
|
|
4
6
|
"approval.approve": "Approve",
|
|
5
7
|
"approval.approved": "Approved. Re-running {{tool}}...",
|
|
6
8
|
"approval.denied": "Denied. {{tool}} did not run.",
|
|
7
9
|
"approval.deny": "Deny",
|
|
10
|
+
"approval.moreOptions": "More approval options",
|
|
8
11
|
"approval.question": "Approve to run {{tool}}?",
|
|
9
12
|
"auth.expiredDescription": "Your session may have expired. Log out and log back in to reconnect.",
|
|
10
13
|
"auth.expiredTitle": "Session expired",
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const messages = {
|
|
2
2
|
"approval.alwaysAllow": "Permitir siempre",
|
|
3
3
|
"approval.alwaysAllowHint": "Aprobar y permitir siempre este comando exacto",
|
|
4
|
+
"approval.alwaysAllowAction": "Permitir siempre esta acción",
|
|
5
|
+
"approval.alwaysAllowActionHint": "Aprobar y permitir siempre esta acción",
|
|
4
6
|
"approval.approve": "Aprobar",
|
|
5
7
|
"approval.approved": "Aprobado. Ejecutando {{tool}} de nuevo...",
|
|
6
8
|
"approval.denied": "Denegado. {{tool}} no se ejecutó.",
|
|
7
9
|
"approval.deny": "Denegar",
|
|
10
|
+
"approval.moreOptions": "Más opciones de aprobación",
|
|
8
11
|
"approval.question": "¿Aprobar la ejecución de {{tool}}?",
|
|
9
12
|
"auth.expiredDescription": "Es posible que tu sesión haya caducado. Cierra sesión y vuelve a iniciarla para reconectarte.",
|
|
10
13
|
"auth.expiredTitle": "Sesión caducada",
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const messages = {
|
|
2
2
|
"approval.alwaysAllow": "Toujours autoriser",
|
|
3
3
|
"approval.alwaysAllowHint": "Approuver et toujours autoriser cette commande exacte",
|
|
4
|
+
"approval.alwaysAllowAction": "Toujours autoriser cette action",
|
|
5
|
+
"approval.alwaysAllowActionHint": "Approuver et toujours autoriser cette action",
|
|
4
6
|
"approval.approve": "Approuver",
|
|
5
7
|
"approval.approved": "Approuvé. Nouvelle exécution de {{tool}}...",
|
|
6
8
|
"approval.denied": "Refusé. {{tool}} n’a pas été exécuté.",
|
|
7
9
|
"approval.deny": "Refuser",
|
|
10
|
+
"approval.moreOptions": "Plus d’options d’approbation",
|
|
8
11
|
"approval.question": "Approuver l’exécution de {{tool}} ?",
|
|
9
12
|
"auth.expiredDescription": "Votre session a peut-être expiré. Déconnectez-vous, puis reconnectez-vous pour rétablir la connexion.",
|
|
10
13
|
"auth.expiredTitle": "Session expirée",
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const messages = {
|
|
2
2
|
"approval.alwaysAllow": "हमेशा अनुमति दें",
|
|
3
3
|
"approval.alwaysAllowHint": "इस सटीक कमांड को स्वीकृत करें और हमेशा अनुमति दें",
|
|
4
|
+
"approval.alwaysAllowAction": "इस कार्रवाई को हमेशा अनुमति दें",
|
|
5
|
+
"approval.alwaysAllowActionHint": "स्वीकृत करें और इस कार्रवाई को हमेशा अनुमति दें",
|
|
4
6
|
"approval.approve": "स्वीकृत करें",
|
|
5
7
|
"approval.approved": "स्वीकृत। {{tool}} को फिर से चलाया जा रहा है...",
|
|
6
8
|
"approval.denied": "अस्वीकृत। {{tool}} नहीं चला।",
|
|
7
9
|
"approval.deny": "अस्वीकार करें",
|
|
10
|
+
"approval.moreOptions": "अनुमति के और विकल्प",
|
|
8
11
|
"approval.question": "क्या {{tool}} को चलाने की अनुमति दें?",
|
|
9
12
|
"auth.expiredDescription": "आपका सत्र समाप्त हो गया हो सकता है। दोबारा कनेक्ट करने के लिए लॉग आउट करके फिर लॉग इन करें।",
|
|
10
13
|
"auth.expiredTitle": "सत्र समाप्त हो गया",
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const messages = {
|
|
2
2
|
"approval.alwaysAllow": "常に許可",
|
|
3
3
|
"approval.alwaysAllowHint": "この完全に同じコマンドを承認し、今後も常に許可します",
|
|
4
|
+
"approval.alwaysAllowAction": "このアクションを常に許可",
|
|
5
|
+
"approval.alwaysAllowActionHint": "承認してこのアクションを常に許可します",
|
|
4
6
|
"approval.approve": "承認",
|
|
5
7
|
"approval.approved": "承認しました。{{tool}} を再実行しています...",
|
|
6
8
|
"approval.denied": "拒否しました。{{tool}} は実行されませんでした。",
|
|
7
9
|
"approval.deny": "拒否",
|
|
10
|
+
"approval.moreOptions": "その他の承認オプション",
|
|
8
11
|
"approval.question": "{{tool}} の実行を承認しますか?",
|
|
9
12
|
"auth.expiredDescription": "セッションの有効期限が切れた可能性があります。再接続するには、ログアウトしてからもう一度ログインしてください。",
|
|
10
13
|
"auth.expiredTitle": "セッションの有効期限が切れました",
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const messages = {
|
|
2
2
|
"approval.alwaysAllow": "항상 허용",
|
|
3
3
|
"approval.alwaysAllowHint": "이 명령과 정확히 일치하는 명령을 승인하고 항상 허용합니다",
|
|
4
|
+
"approval.alwaysAllowAction": "이 작업 항상 허용",
|
|
5
|
+
"approval.alwaysAllowActionHint": "승인하고 이 작업을 항상 허용합니다",
|
|
4
6
|
"approval.approve": "승인",
|
|
5
7
|
"approval.approved": "승인했습니다. {{tool}}을(를) 다시 실행하는 중...",
|
|
6
8
|
"approval.denied": "거부했습니다. {{tool}}이(가) 실행되지 않았습니다.",
|
|
7
9
|
"approval.deny": "거부",
|
|
10
|
+
"approval.moreOptions": "추가 승인 옵션",
|
|
8
11
|
"approval.question": "{{tool}} 실행을 승인하시겠습니까?",
|
|
9
12
|
"auth.expiredDescription": "세션이 만료되었을 수 있습니다. 다시 연결하려면 로그아웃한 후 다시 로그인하세요.",
|
|
10
13
|
"auth.expiredTitle": "세션이 만료되었습니다",
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const messages = {
|
|
2
2
|
"approval.alwaysAllow": "Sempre permitir",
|
|
3
3
|
"approval.alwaysAllowHint": "Aprovar e sempre permitir este comando exato",
|
|
4
|
+
"approval.alwaysAllowAction": "Sempre permitir esta ação",
|
|
5
|
+
"approval.alwaysAllowActionHint": "Aprovar e sempre permitir esta ação",
|
|
4
6
|
"approval.approve": "Aprovar",
|
|
5
7
|
"approval.approved": "Aprovado. Executando {{tool}} novamente...",
|
|
6
8
|
"approval.denied": "Negado. {{tool}} não foi executado.",
|
|
7
9
|
"approval.deny": "Negar",
|
|
10
|
+
"approval.moreOptions": "Mais opções de aprovação",
|
|
8
11
|
"approval.question": "Aprovar a execução de {{tool}}?",
|
|
9
12
|
"auth.expiredDescription": "Sua sessão pode ter expirado. Saia e entre novamente para restabelecer a conexão.",
|
|
10
13
|
"auth.expiredTitle": "Sessão expirada",
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const messages = {
|
|
2
2
|
"approval.alwaysAllow": "始终允许",
|
|
3
3
|
"approval.alwaysAllowHint": "批准并始终允许此完全相同的命令",
|
|
4
|
+
"approval.alwaysAllowAction": "始终允许此操作",
|
|
5
|
+
"approval.alwaysAllowActionHint": "批准并始终允许此操作",
|
|
4
6
|
"approval.approve": "批准",
|
|
5
7
|
"approval.approved": "已批准。正在重新运行 {{tool}}...",
|
|
6
8
|
"approval.denied": "已拒绝。{{tool}} 未运行。",
|
|
7
9
|
"approval.deny": "拒绝",
|
|
10
|
+
"approval.moreOptions": "更多批准选项",
|
|
8
11
|
"approval.question": "批准运行 {{tool}}?",
|
|
9
12
|
"auth.expiredDescription": "您的会话可能已过期。请退出登录后重新登录以恢复连接。",
|
|
10
13
|
"auth.expiredTitle": "会话已过期",
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const messages = {
|
|
2
2
|
"approval.alwaysAllow": "一律允許",
|
|
3
3
|
"approval.alwaysAllowHint": "核准並一律允許這個完全相同的命令",
|
|
4
|
+
"approval.alwaysAllowAction": "一律允許此動作",
|
|
5
|
+
"approval.alwaysAllowActionHint": "核准並一律允許此動作",
|
|
4
6
|
"approval.approve": "核准",
|
|
5
7
|
"approval.approved": "已核准。正在重新執行 {{tool}}...",
|
|
6
8
|
"approval.denied": "已拒絕。{{tool}} 未執行。",
|
|
7
9
|
"approval.deny": "拒絕",
|
|
10
|
+
"approval.moreOptions": "更多核准選項",
|
|
8
11
|
"approval.question": "要核准執行 {{tool}} 嗎?",
|
|
9
12
|
"auth.expiredDescription": "您的工作階段可能已過期。請登出後重新登入以恢復連線。",
|
|
10
13
|
"auth.expiredTitle": "工作階段已過期",
|
|
@@ -3,10 +3,13 @@ export type CoreLocaleMessages = Record<string, unknown>;
|
|
|
3
3
|
export declare const englishAgentChatMessages: {
|
|
4
4
|
readonly "approval.alwaysAllow": "Always allow";
|
|
5
5
|
readonly "approval.alwaysAllowHint": "Approve and always allow this exact command";
|
|
6
|
+
readonly "approval.alwaysAllowAction": "Always allow this action";
|
|
7
|
+
readonly "approval.alwaysAllowActionHint": "Approve and always allow this action";
|
|
6
8
|
readonly "approval.approve": "Approve";
|
|
7
9
|
readonly "approval.approved": "Approved. Re-running {{tool}}...";
|
|
8
10
|
readonly "approval.denied": "Denied. {{tool}} did not run.";
|
|
9
11
|
readonly "approval.deny": "Deny";
|
|
12
|
+
readonly "approval.moreOptions": "More approval options";
|
|
10
13
|
readonly "approval.question": "Approve to run {{tool}}?";
|
|
11
14
|
readonly "auth.expiredDescription": "Your session may have expired. Log out and log back in to reconnect.";
|
|
12
15
|
readonly "auth.expiredTitle": "Session expired";
|