@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.
Files changed (61) hide show
  1. package/dist/agent/actions/set-tool-approval-policy.d.ts +8 -0
  2. package/dist/agent/actions/set-tool-approval-policy.js +26 -0
  3. package/dist/agent/harness/migrations.js +8 -0
  4. package/dist/agent/harness/runner.js +114 -33
  5. package/dist/agent/harness/store.d.ts +9 -0
  6. package/dist/agent/harness/store.js +87 -23
  7. package/dist/agent/production-agent.d.ts +3 -0
  8. package/dist/agent/production-agent.js +23 -1
  9. package/dist/agent/run-manager.js +40 -9
  10. package/dist/agent/tool-approval-migrations.d.ts +21 -0
  11. package/dist/agent/tool-approval-migrations.js +32 -0
  12. package/dist/agent/tool-approval-store.d.ts +11 -0
  13. package/dist/agent/tool-approval-store.js +76 -1
  14. package/dist/agent-web/generator.d.ts +44 -10
  15. package/dist/agent-web/generator.js +38 -0
  16. package/dist/agent-web/index.d.ts +1 -1
  17. package/dist/cli/code-agent-executor.js +20 -3
  18. package/dist/client/AgentPanel.d.ts +4 -1
  19. package/dist/client/AgentPanel.js +11 -3
  20. package/dist/client/AssistantChat.d.ts +5 -5
  21. package/dist/client/AssistantChat.js +24 -11
  22. package/dist/client/chat/tool-call-display.d.ts +5 -5
  23. package/dist/client/chat/tool-call-display.js +27 -11
  24. package/dist/client/resources/McpIntegrationDialog.js +5 -5
  25. package/dist/client/resources/mcp-integration-catalog.js +1 -1
  26. package/dist/coding-tools/index.d.ts +31 -0
  27. package/dist/coding-tools/index.js +139 -3
  28. package/dist/collab/awareness.d.ts +2 -2
  29. package/dist/collab/struct-routes.d.ts +1 -1
  30. package/dist/localization/core-messages/ar-SA.js +3 -0
  31. package/dist/localization/core-messages/de-DE.js +3 -0
  32. package/dist/localization/core-messages/en-US.d.ts +3 -0
  33. package/dist/localization/core-messages/en-US.js +3 -0
  34. package/dist/localization/core-messages/es-ES.js +3 -0
  35. package/dist/localization/core-messages/fr-FR.js +3 -0
  36. package/dist/localization/core-messages/hi-IN.js +3 -0
  37. package/dist/localization/core-messages/ja-JP.js +3 -0
  38. package/dist/localization/core-messages/ko-KR.js +3 -0
  39. package/dist/localization/core-messages/pt-BR.js +3 -0
  40. package/dist/localization/core-messages/zh-CN.js +3 -0
  41. package/dist/localization/core-messages/zh-TW.js +3 -0
  42. package/dist/localization/core-messages.d.ts +3 -0
  43. package/dist/localization/default-messages.d.ts +1 -0
  44. package/dist/localization/default-messages.js +7 -6
  45. package/dist/observability/routes.d.ts +3 -3
  46. package/dist/progress/routes.d.ts +1 -1
  47. package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
  48. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  49. package/dist/secrets/routes.d.ts +6 -6
  50. package/dist/server/action-discovery.js +5 -0
  51. package/dist/server/agent-chat/prompt-resources.d.ts +9 -0
  52. package/dist/server/agent-chat/prompt-resources.js +12 -2
  53. package/dist/server/realtime-token.d.ts +1 -1
  54. package/dist/server/release-schema.js +4 -1
  55. package/dist/server/transcribe-voice.d.ts +1 -1
  56. package/dist/triggers/dispatcher.d.ts +24 -0
  57. package/dist/triggers/dispatcher.js +76 -18
  58. package/dist/vite/action-types-plugin.js +4 -0
  59. package/dist/vite/agent-web-plugin.d.ts +4 -6
  60. package/dist/vite/agent-web-plugin.js +2 -0
  61. 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
- 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("button", { type: "button", onClick: () => {
270
- setLocalResolution("approved");
271
- ctx.onApprovalResolved?.(approval.approvalKey, "approved", toolCallId, approval.askId);
272
- ctx.onApprove(approval.approvalKey);
273
- }, className: cn("inline-flex shrink-0 items-center gap-1 rounded-md px-2.5 py-1 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90"), children: [_jsx(IconCheck, { className: "h-3.5 w-3.5" }), t("agentChat.approval.approve")] })), ctx?.onAlwaysAllow && (_jsxs("button", { type: "button", onClick: () => {
274
- setLocalResolution("approved");
275
- ctx.onApprovalResolved?.(approval.approvalKey, "approved", toolCallId, approval.askId);
276
- ctx.onAlwaysAllow?.(approval.approvalKey);
277
- }, title: t("agentChat.approval.alwaysAllowHint"), 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(IconShieldCheck, { className: "h-3.5 w-3.5" }), t("agentChat.approval.alwaysAllow")] })), _jsxs("button", { type: "button", onClick: () => {
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 ? (_jsxs("div", { className: "ms-auto flex items-center gap-2", children: [selected?.docsUrl ? (_jsxs("a", { href: selected.docsUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex min-w-[92px] items-center justify-center gap-1.5 rounded-md border border-border bg-background px-3 py-1.5 text-[12px] font-medium text-foreground hover:bg-accent", children: [t("mcpIntegrations.viewSetup"), _jsx(IconExternalLink, { className: "h-3 w-3" })] })) : null, 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")] }))] })] }))] }) }));
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: "Atlassian",
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 abort = () => child.kill("SIGTERM");
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
- child.once("error", reject);
346
- child.once("close", resolve);
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";
@@ -1014,6 +1014,7 @@ declare const messages: {
1014
1014
  continueToConnect: string;
1015
1015
  setupTitle: string;
1016
1016
  personal: string;
1017
+ personalConnection: string;
1017
1018
  organization: string;
1018
1019
  scopeQuestion: string;
1019
1020
  scopeChoiceTitle: string;