@gajae-code/coding-agent 0.11.3 → 0.11.5
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/CHANGELOG.md +47 -0
- package/dist/types/cli/notify-cli.d.ts +5 -1
- package/dist/types/cli/worktree-cli.d.ts +8 -25
- package/dist/types/commands/gc.d.ts +4 -0
- package/dist/types/commands/worktree.d.ts +7 -32
- package/dist/types/config/settings-schema.d.ts +20 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +28 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +9 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -0
- package/dist/types/gjc-runtime/managed-owner-admission.d.ts +42 -0
- package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +41 -0
- package/dist/types/gjc-runtime/state-argv.d.ts +38 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +9 -0
- package/dist/types/gjc-runtime/ultragoal-owner-loss-recovery.d.ts +94 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
- package/dist/types/harness-control-plane/owner.d.ts +3 -1
- package/dist/types/internal-urls/local-protocol.d.ts +18 -0
- package/dist/types/main.d.ts +2 -2
- package/dist/types/modes/components/ansi-display-validator.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +8 -3
- package/dist/types/modes/components/notifications-settings-editor.d.ts +4 -0
- package/dist/types/modes/components/session-observer-overlay.d.ts +3 -1
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-transcript-format.d.ts +77 -0
- package/dist/types/modes/components/transcript-viewer-overlay.d.ts +3 -0
- package/dist/types/modes/controllers/goal-mode-controller.d.ts +8 -1
- package/dist/types/modes/controllers/input-controller.d.ts +8 -2
- package/dist/types/modes/controllers/selector-controller.d.ts +15 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +3 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +16 -0
- package/dist/types/plan-mode/approved-plan.d.ts +1 -0
- package/dist/types/runtime-mcp/manager.d.ts +2 -0
- package/dist/types/runtime-mcp/oauth-discovery.d.ts +12 -1
- package/dist/types/sdk/broker/broker.d.ts +1 -0
- package/dist/types/sdk/broker/discovery.d.ts +10 -0
- package/dist/types/sdk/broker/ensure.d.ts +24 -1
- package/dist/types/sdk/broker/internal.d.ts +3 -0
- package/dist/types/sdk/broker/session-index.d.ts +12 -0
- package/dist/types/sdk/broker/state-version.d.ts +2 -1
- package/dist/types/sdk/bus/chat-daemon-cli.d.ts +3 -1
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +39 -4
- package/dist/types/sdk/bus/config-commands.d.ts +8 -0
- package/dist/types/sdk/bus/config.d.ts +22 -0
- package/dist/types/sdk/bus/index.d.ts +162 -2
- package/dist/types/sdk/bus/notification-service.d.ts +71 -8
- package/dist/types/sdk/bus/session-control.d.ts +11 -8
- package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +5 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -3
- package/dist/types/sdk/bus/telegram-daemon-control.d.ts +23 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +214 -20
- package/dist/types/sdk/session.d.ts +16 -1
- package/dist/types/secrets/index.d.ts +4 -2
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +31 -2
- package/dist/types/session/artifacts.d.ts +30 -8
- package/dist/types/session/internal/managed-session-scope.d.ts +7 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +63 -6
- package/dist/types/session/session-manager.d.ts +88 -18
- package/dist/types/session/session-storage.d.ts +27 -0
- package/dist/types/session/streaming-output.d.ts +4 -0
- package/dist/types/task/executor.d.ts +9 -0
- package/dist/types/tool-discovery/tool-index.d.ts +2 -0
- package/dist/types/tools/ask.d.ts +87 -12
- package/dist/types/tools/bash-skill-urls.d.ts +2 -2
- package/dist/types/tools/browser/screenshot-format.d.ts +8 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +11 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +1 -1
- package/dist/types/tools/browser/tab-worker-path-resolver.d.ts +2 -0
- package/dist/types/tools/browser/tab-worker-smoke.d.ts +16 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/path-utils.d.ts +9 -14
- package/dist/types/tools/render-utils.d.ts +1 -8
- package/dist/types/tools/resource-gc.d.ts +14 -0
- package/dist/types/utils/image-loading.d.ts +23 -0
- package/dist/types/utils/image-resize.d.ts +2 -0
- package/dist/types/utils/pasted-image-loading.d.ts +39 -0
- package/dist/types/utils/pasted-image-path.d.ts +21 -19
- package/dist/types/web/scrapers/html-to-markdown.d.ts +5 -0
- package/dist/types/web/scrapers/types.d.ts +1 -5
- package/package.json +25 -9
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/scripts/run-threat-model-check.ts +60 -0
- package/src/cli/notify-cli.ts +40 -18
- package/src/cli/worktree-cli.ts +9 -287
- package/src/cli.ts +10 -9
- package/src/commands/gc.ts +11 -1
- package/src/commands/sdk.ts +10 -4
- package/src/commands/worktree.ts +10 -53
- package/src/config/model-profiles.ts +15 -15
- package/src/config/settings-schema.ts +17 -0
- package/src/daemon/runtime.ts +2 -2
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +70 -49
- package/src/defaults/gjc/skills/ralplan/SKILL.md +0 -6
- package/src/defaults/gjc/skills/team/SKILL.md +7 -9
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +0 -2
- package/src/eval/js/shared/runtime.ts +1 -1
- package/src/eval/py/index.ts +2 -2
- package/src/export/html/template.generated.ts +1 -1
- package/src/extensibility/plugins/marketplace/manager.ts +94 -10
- package/src/gjc-runtime/deep-interview-recorder.ts +7 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +10 -0
- package/src/gjc-runtime/deep-interview-state.ts +127 -3
- package/src/gjc-runtime/gc-render.ts +19 -1
- package/src/gjc-runtime/gc-runtime.ts +68 -3
- package/src/gjc-runtime/launch-tmux.ts +61 -6
- package/src/gjc-runtime/managed-owner-admission.ts +352 -0
- package/src/gjc-runtime/managed-owner-supervisor.ts +162 -0
- package/src/gjc-runtime/state-argv.ts +292 -0
- package/src/gjc-runtime/state-runtime.ts +80 -222
- package/src/gjc-runtime/team-runtime.ts +1 -1
- package/src/gjc-runtime/tmux-owner-isolation.ts +117 -0
- package/src/gjc-runtime/ultragoal-owner-loss-recovery.ts +328 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +14 -0
- package/src/harness-control-plane/owner.ts +59 -3
- package/src/hindsight/client.ts +65 -2
- package/src/internal-urls/agent-protocol.ts +76 -5
- package/src/internal-urls/docs-index.generated.ts +7 -6
- package/src/internal-urls/local-protocol.ts +416 -18
- package/src/lsp/index.ts +4 -88
- package/src/main.ts +37 -13
- package/src/modes/acp/acp-agent.ts +8 -0
- package/src/modes/components/ansi-display-validator.ts +54 -0
- package/src/modes/components/custom-editor.ts +103 -32
- package/src/modes/components/custom-provider-wizard.ts +26 -1
- package/src/modes/components/notifications-settings-editor.ts +26 -2
- package/src/modes/components/session-observer-overlay.ts +40 -37
- package/src/modes/components/settings-selector.ts +3 -1
- package/src/modes/components/tool-transcript-format.ts +393 -0
- package/src/modes/components/transcript-viewer-overlay.ts +63 -1
- package/src/modes/controllers/goal-mode-controller.ts +20 -1
- package/src/modes/controllers/input-controller.ts +98 -37
- package/src/modes/controllers/plan-mode-controller.ts +2 -0
- package/src/modes/controllers/runtime-mcp-command-controller.ts +3 -0
- package/src/modes/controllers/selector-controller.ts +71 -9
- package/src/modes/interactive-mode.ts +14 -2
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -1
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +17 -1
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +4 -2
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +120 -3
- package/src/plan-mode/approved-plan.ts +3 -1
- package/src/prompts/agents/architect.md +1 -0
- package/src/prompts/agents/critic.md +1 -0
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/system-prompt.md +2 -1
- package/src/prompts/tools/lsp.md +3 -3
- package/src/runtime-mcp/manager.ts +16 -1
- package/src/runtime-mcp/oauth-discovery.ts +116 -26
- package/src/sdk/broker/broker.ts +139 -32
- package/src/sdk/broker/discovery.ts +125 -0
- package/src/sdk/broker/ensure.ts +103 -19
- package/src/sdk/broker/internal.ts +10 -0
- package/src/sdk/broker/session-index.ts +309 -38
- package/src/sdk/broker/state-version.ts +5 -2
- package/src/sdk/bus/chat-daemon-cli.ts +72 -10
- package/src/sdk/bus/chat-daemon-control.ts +526 -130
- package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
- package/src/sdk/bus/config-commands.ts +22 -0
- package/src/sdk/bus/config.ts +37 -0
- package/src/sdk/bus/index.ts +384 -92
- package/src/sdk/bus/lifecycle-control-runtime.ts +53 -3
- package/src/sdk/bus/notification-service.ts +284 -62
- package/src/sdk/bus/recent-activity.ts +14 -5
- package/src/sdk/bus/session-control.ts +150 -44
- package/src/sdk/bus/telegram-daemon-cli.ts +20 -14
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -3
- package/src/sdk/bus/telegram-daemon-control.ts +294 -76
- package/src/sdk/bus/telegram-daemon.ts +1842 -281
- package/src/sdk/protocol/operation-inventory.generated.json +11 -0
- package/src/sdk/session.ts +155 -27
- package/src/secrets/index.ts +53 -10
- package/src/secrets/obfuscator.ts +21 -18
- package/src/secrets/regex.ts +2 -0
- package/src/session/agent-session.ts +495 -253
- package/src/session/artifacts.ts +188 -23
- package/src/session/context-estimation.ts +7 -2
- package/src/session/internal/managed-session-scope.ts +215 -39
- package/src/session/internal/managed-session-storage.ts +1152 -46
- package/src/session/session-manager.ts +1055 -148
- package/src/session/session-storage.ts +109 -11
- package/src/session/streaming-output.ts +6 -0
- package/src/task/executor.ts +71 -26
- package/src/task/index.ts +63 -20
- package/src/tool-discovery/tool-index.ts +18 -0
- package/src/tools/ask.ts +329 -57
- package/src/tools/bash-allowed-prefixes.ts +79 -29
- package/src/tools/bash-skill-urls.ts +14 -3
- package/src/tools/bash.ts +2 -2
- package/src/tools/browser/launch.ts +3 -1
- package/src/tools/browser/readable.ts +1 -1
- package/src/tools/browser/screenshot-format.ts +34 -0
- package/src/tools/browser/tab-protocol.ts +3 -0
- package/src/tools/browser/tab-supervisor.ts +125 -122
- package/src/tools/browser/tab-worker-entry.ts +33 -14
- package/src/tools/browser/tab-worker-path-resolver.ts +67 -0
- package/src/tools/browser/tab-worker-smoke.ts +87 -0
- package/src/tools/browser/tab-worker.ts +7 -5
- package/src/tools/index.ts +4 -0
- package/src/tools/path-utils.ts +42 -35
- package/src/tools/plan-mode-guard.ts +1 -0
- package/src/tools/read.ts +7 -1
- package/src/tools/render-utils.ts +1 -27
- package/src/tools/resource-gc.ts +123 -34
- package/src/tools/sqlite-reader.ts +3 -2
- package/src/tools/subagent.ts +2 -0
- package/src/utils/image-loading.ts +85 -34
- package/src/utils/image-resize.ts +16 -9
- package/src/utils/pasted-image-loading.ts +514 -0
- package/src/utils/pasted-image-path.ts +223 -98
- package/src/web/scrapers/html-to-markdown.ts +68 -0
- package/src/web/scrapers/readthedocs.ts +1 -1
- package/src/web/scrapers/types.ts +1 -68
- package/src/web/search/providers/openai-compatible.ts +76 -23
|
@@ -2,7 +2,7 @@ import * as fs from "node:fs/promises";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { type AgentMessage, ThinkingLevel } from "@gajae-code/agent-core";
|
|
4
4
|
import { type AutocompleteProvider, matchesKey, type SlashCommand } from "@gajae-code/tui";
|
|
5
|
-
import { $env, sanitizeText } from "@gajae-code/utils";
|
|
5
|
+
import { $env, logger, sanitizeText } from "@gajae-code/utils";
|
|
6
6
|
import { type AppKeybinding, KEYBINDINGS } from "../../config/keybindings";
|
|
7
7
|
import { isSettingsInitialized, settings } from "../../config/settings";
|
|
8
8
|
import { resolveSubskillActivationForSkillInvocation } from "../../extensibility/gjc-plugins";
|
|
@@ -22,12 +22,14 @@ import { getUserMessageViewportAnchorIds } from "../../session/session-manager";
|
|
|
22
22
|
import { executeBuiltinSlashCommand } from "../../slash-commands/builtin-registry";
|
|
23
23
|
import { copyToClipboard, readImageFromClipboard } from "../../utils/clipboard";
|
|
24
24
|
import { getEditorCommand, openInEditor } from "../../utils/external-editor";
|
|
25
|
-
import { ensureSupportedImageInput, ImageInputTooLargeError
|
|
25
|
+
import { ensureSupportedImageInput, ImageInputTooLargeError } from "../../utils/image-loading";
|
|
26
26
|
import { resizeImage } from "../../utils/image-resize";
|
|
27
|
-
import {
|
|
27
|
+
import { loadPastedImageBatch, PastedImageBatchError } from "../../utils/pasted-image-loading";
|
|
28
|
+
import { formatPastedImageReference, parsePastedImagePaths } from "../../utils/pasted-image-path";
|
|
28
29
|
import { generateSessionTitle, setSessionTerminalTitle } from "../../utils/title-generator";
|
|
29
30
|
import { ActionRegistry, APP_ACTION_METADATA } from "../action-registry";
|
|
30
31
|
import { CommandPalette, type CommandPaletteAction, type CommandPaletteEntry } from "../components/command-palette";
|
|
32
|
+
import type { PasteTextContext } from "../components/custom-editor";
|
|
31
33
|
import { QueuePaneComponent } from "../components/queue-pane";
|
|
32
34
|
import { type QueuedMessageMoveDirection, QueuedMessageSelectorComponent } from "../components/queued-message-selector";
|
|
33
35
|
|
|
@@ -44,14 +46,23 @@ const EMPTY_EDITOR_DOUBLE_ESCAPE_WINDOW_MS = 500;
|
|
|
44
46
|
const IMAGE_PLACEHOLDER_PATTERN = /\[image ([1-9]\d*)\]/g;
|
|
45
47
|
const IMAGE_PLACEHOLDER_PRESENT_PATTERN = /\[image [1-9]\d*\]/;
|
|
46
48
|
|
|
49
|
+
interface InputControllerDependencies {
|
|
50
|
+
loadPastedImageBatch?: typeof loadPastedImageBatch;
|
|
51
|
+
}
|
|
52
|
+
|
|
47
53
|
function isExpandable(obj: unknown): obj is Expandable {
|
|
48
54
|
return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
export class InputController {
|
|
52
58
|
readonly actionRegistry: ActionRegistry<void>;
|
|
59
|
+
readonly #loadPastedImageBatch: typeof loadPastedImageBatch;
|
|
53
60
|
|
|
54
|
-
constructor(
|
|
61
|
+
constructor(
|
|
62
|
+
private ctx: InteractiveModeContext,
|
|
63
|
+
dependencies: InputControllerDependencies = {},
|
|
64
|
+
) {
|
|
65
|
+
this.#loadPastedImageBatch = dependencies.loadPastedImageBatch ?? loadPastedImageBatch;
|
|
55
66
|
this.actionRegistry = new ActionRegistry({
|
|
56
67
|
context: undefined,
|
|
57
68
|
showError: actionId => this.ctx.showError(actionId),
|
|
@@ -585,11 +596,11 @@ export class InputController {
|
|
|
585
596
|
);
|
|
586
597
|
this.ctx.editor.onCopyPrompt = () => this.#executeAction("app.clipboard.copyPrompt");
|
|
587
598
|
this.#registerCommandPaletteAction("app.clipboard.copyPrompt", copyPrompt);
|
|
588
|
-
this.ctx.editor.onPasteText = text => this.handleTextPaste(text);
|
|
589
|
-
this.ctx.editor.onPastePendingInputCleared = (reason,
|
|
599
|
+
this.ctx.editor.onPasteText = (text, context) => this.handleTextPaste(text, context);
|
|
600
|
+
this.ctx.editor.onPastePendingInputCleared = (reason, restoredInputCount) => {
|
|
590
601
|
const reasonText = reason === "timeout" ? "timed out" : "exceeded the input queue limit";
|
|
591
602
|
this.ctx.showWarning(
|
|
592
|
-
`Paste handling ${reasonText};
|
|
603
|
+
`Paste handling ${reasonText}; restored the original paste and ${restoredInputCount} buffered input event${restoredInputCount === 1 ? "" : "s"}.`,
|
|
593
604
|
);
|
|
594
605
|
};
|
|
595
606
|
const expandTools = () => this.toggleToolOutputExpansion();
|
|
@@ -1522,46 +1533,96 @@ export class InputController {
|
|
|
1522
1533
|
return true;
|
|
1523
1534
|
}
|
|
1524
1535
|
|
|
1525
|
-
handleTextPaste(text: string): boolean | Promise<boolean> {
|
|
1526
|
-
|
|
1527
|
-
|
|
1536
|
+
handleTextPaste(text: string, context: PasteTextContext): boolean | Promise<boolean> {
|
|
1537
|
+
if (this.ctx.isBashMode || this.ctx.isPythonMode) return false;
|
|
1538
|
+
const parsed = parsePastedImagePaths(text, { cwd: this.ctx.sessionManager.getCwd() });
|
|
1539
|
+
if (!parsed) return false;
|
|
1540
|
+
if (parsed.kind === "too-many") {
|
|
1541
|
+
this.ctx.showStatus(`Cannot attach more than ${parsed.maxCandidates} pasted images.`);
|
|
1542
|
+
return false;
|
|
1543
|
+
}
|
|
1544
|
+
return this.#attachPastedImagePaths(parsed.paths, parsed.requiresConfirmation, context);
|
|
1528
1545
|
}
|
|
1529
1546
|
|
|
1530
|
-
/**
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1547
|
+
/** Return false on every failure so CustomEditor restores the exact paste. */
|
|
1548
|
+
async #attachPastedImagePaths(
|
|
1549
|
+
imagePaths: readonly string[],
|
|
1550
|
+
requiresConfirmation: boolean,
|
|
1551
|
+
context: PasteTextContext,
|
|
1552
|
+
): Promise<boolean> {
|
|
1553
|
+
const signal = context.signal;
|
|
1554
|
+
const editor = this.ctx.editor;
|
|
1555
|
+
const editorText = editor.getText();
|
|
1556
|
+
const pendingImages = this.ctx.pendingImages;
|
|
1557
|
+
const pendingImageCount = pendingImages.length;
|
|
1536
1558
|
try {
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1559
|
+
if (requiresConfirmation) {
|
|
1560
|
+
const selection = await this.ctx.showHookSelector(
|
|
1561
|
+
`Attach ${imagePaths.length} pasted images?\nOnly attach files you intend to send to the model.`,
|
|
1562
|
+
["Attach images", "Paste paths literally"],
|
|
1563
|
+
{ signal, helpText: "Enter: choose · Esc: paste paths literally" },
|
|
1564
|
+
);
|
|
1565
|
+
signal.throwIfAborted();
|
|
1566
|
+
if (selection !== "Attach images") return false;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
const loaded = await this.#loadPastedImageBatch({
|
|
1570
|
+
paths: imagePaths,
|
|
1540
1571
|
autoResize: this.ctx.settings.get("images.autoResize"),
|
|
1572
|
+
sourcePolicy: requiresConfirmation ? "confirmed" : "automatic-temp",
|
|
1573
|
+
signal,
|
|
1541
1574
|
});
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1575
|
+
signal.throwIfAborted();
|
|
1576
|
+
return context.commit(() => {
|
|
1577
|
+
if (
|
|
1578
|
+
this.ctx.editor !== editor ||
|
|
1579
|
+
editor.getText() !== editorText ||
|
|
1580
|
+
this.ctx.pendingImages !== pendingImages ||
|
|
1581
|
+
pendingImages.length !== pendingImageCount
|
|
1582
|
+
) {
|
|
1583
|
+
return false;
|
|
1584
|
+
}
|
|
1546
1585
|
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1586
|
+
const firstImageNumber = pendingImages.length + 1;
|
|
1587
|
+
const references = loaded.sourcePaths.map((sourcePath, index) =>
|
|
1588
|
+
formatPastedImageReference(`[image ${firstImageNumber + index}]`, sourcePath),
|
|
1589
|
+
);
|
|
1590
|
+
try {
|
|
1591
|
+
editor.insertText(`${references.join(" ")} `);
|
|
1592
|
+
this.ctx.pendingImages = [...pendingImages, ...loaded.images];
|
|
1593
|
+
} catch (error) {
|
|
1594
|
+
try {
|
|
1595
|
+
editor.setText(editorText);
|
|
1596
|
+
} catch {
|
|
1597
|
+
// Preserve the original mutation error when rollback itself fails.
|
|
1598
|
+
} finally {
|
|
1599
|
+
this.ctx.pendingImages = pendingImages;
|
|
1600
|
+
}
|
|
1601
|
+
throw error;
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
try {
|
|
1605
|
+
this.ctx.showStatus(
|
|
1606
|
+
loaded.images.length === 1
|
|
1607
|
+
? `Attached image: ${path.basename(loaded.sourcePaths[0] ?? "")}`
|
|
1608
|
+
: `Attached ${loaded.images.length} images`,
|
|
1609
|
+
{ dim: true },
|
|
1610
|
+
);
|
|
1611
|
+
this.ctx.ui.requestRender();
|
|
1612
|
+
} catch (error) {
|
|
1613
|
+
logger.warn("Pasted images attached but status rendering failed", { error: String(error) });
|
|
1614
|
+
}
|
|
1615
|
+
return true;
|
|
1616
|
+
});
|
|
1559
1617
|
} catch (error) {
|
|
1560
|
-
if (
|
|
1618
|
+
if (signal.aborted) return false;
|
|
1619
|
+
if (error instanceof ImageInputTooLargeError || error instanceof PastedImageBatchError) {
|
|
1561
1620
|
this.ctx.showStatus(error.message);
|
|
1562
1621
|
return false;
|
|
1563
1622
|
}
|
|
1564
|
-
this.ctx.showStatus(
|
|
1623
|
+
this.ctx.showStatus(
|
|
1624
|
+
imagePaths.length === 1 ? "Failed to attach pasted image" : "Failed to attach pasted images",
|
|
1625
|
+
);
|
|
1565
1626
|
return false;
|
|
1566
1627
|
}
|
|
1567
1628
|
}
|
|
@@ -334,6 +334,7 @@ export class PlanModeController {
|
|
|
334
334
|
return planFilePath.startsWith("local:")
|
|
335
335
|
? resolveLocalUrlToPath(normalizeLocalScheme(planFilePath), {
|
|
336
336
|
getArtifactsDir: () => this.ctx.sessionManager.getArtifactsDir(),
|
|
337
|
+
isManagedDestination: () => this.ctx.sessionManager.isManagedDestination(),
|
|
337
338
|
getSessionId: () => this.ctx.sessionManager.getSessionId(),
|
|
338
339
|
})
|
|
339
340
|
: path.resolve(this.ctx.sessionManager.getCwd(), planFilePath);
|
|
@@ -448,6 +449,7 @@ export class PlanModeController {
|
|
|
448
449
|
await Bun.write(
|
|
449
450
|
resolveLocalUrlToPath(options.finalPlanFilePath, {
|
|
450
451
|
getArtifactsDir: () => this.ctx.sessionManager.getArtifactsDir(),
|
|
452
|
+
isManagedDestination: () => this.ctx.sessionManager.isManagedDestination(),
|
|
451
453
|
getSessionId: () => this.ctx.sessionManager.getSessionId(),
|
|
452
454
|
}),
|
|
453
455
|
planContent,
|
|
@@ -1450,6 +1450,9 @@ export class MCPCommandController {
|
|
|
1450
1450
|
if (!this.ctx.mcpManager) {
|
|
1451
1451
|
return;
|
|
1452
1452
|
}
|
|
1453
|
+
if (this.ctx.mcpManager.isConnectionSetSealed()) {
|
|
1454
|
+
throw new Error("This session's plugin-bundle MCP connections are fixed for its lifetime.");
|
|
1455
|
+
}
|
|
1453
1456
|
|
|
1454
1457
|
// Disconnect all existing servers
|
|
1455
1458
|
await this.ctx.mcpManager.disconnectAll();
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
theme,
|
|
44
44
|
} from "../../modes/theme/theme";
|
|
45
45
|
import type { InteractiveModeContext, OAuthSelectorOptions } from "../../modes/types";
|
|
46
|
-
import { getNotificationConfig, maskToken } from "../../sdk/bus/config";
|
|
46
|
+
import { getNotificationConfig, isTelegramConfigured, maskToken } from "../../sdk/bus/config";
|
|
47
47
|
import {
|
|
48
48
|
clearTelegramActivationMarker,
|
|
49
49
|
createTelegramActivationMarker,
|
|
@@ -228,6 +228,13 @@ export interface NotificationsEditorOperationDependencies {
|
|
|
228
228
|
saveTelegramInactive: typeof saveTelegramInactive;
|
|
229
229
|
removeTelegramConfiguration: typeof removeTelegramConfiguration;
|
|
230
230
|
unregisterNotificationRoot: typeof unregisterNotificationRoot;
|
|
231
|
+
reloadTelegramDaemon(settings: Settings): Promise<{ ok: boolean; message: string }>;
|
|
232
|
+
restartTelegramDaemon(settings: Settings): Promise<{ ok: boolean; message: string }>;
|
|
233
|
+
stopTelegramDaemon(settings: Settings): Promise<{
|
|
234
|
+
ok: boolean;
|
|
235
|
+
message: string;
|
|
236
|
+
before?: { health?: string };
|
|
237
|
+
}>;
|
|
231
238
|
}
|
|
232
239
|
|
|
233
240
|
const notificationEditorOperationDependencies: NotificationsEditorOperationDependencies = {
|
|
@@ -245,6 +252,11 @@ const notificationEditorOperationDependencies: NotificationsEditorOperationDepen
|
|
|
245
252
|
saveTelegramInactive,
|
|
246
253
|
removeTelegramConfiguration,
|
|
247
254
|
unregisterNotificationRoot,
|
|
255
|
+
reloadTelegramDaemon: async settings =>
|
|
256
|
+
await new TelegramDaemonController(settings).reload({ spawnIfStopped: false }),
|
|
257
|
+
restartTelegramDaemon: async settings =>
|
|
258
|
+
await new TelegramDaemonController(settings).reload({ spawnIfStopped: true }),
|
|
259
|
+
stopTelegramDaemon: async settings => await new TelegramDaemonController(settings).stop(),
|
|
248
260
|
};
|
|
249
261
|
|
|
250
262
|
function unavailableNotificationSessionStatus(): NotificationSessionStatus {
|
|
@@ -333,6 +345,8 @@ export function createNotificationsEditorOperations(
|
|
|
333
345
|
sessionScope: config.sessionScope,
|
|
334
346
|
richEnabled: config.rich.enabled,
|
|
335
347
|
richDraftEnabled: config.richDraft.enabled,
|
|
348
|
+
toolActivityEnabled: config.toolActivity.enabled,
|
|
349
|
+
streamingEnabled: config.streaming.enabled,
|
|
336
350
|
},
|
|
337
351
|
};
|
|
338
352
|
},
|
|
@@ -470,6 +484,7 @@ export function createNotificationsEditorOperations(
|
|
|
470
484
|
tokenFingerprint: setup.tokenFingerprint,
|
|
471
485
|
richEnabled: input.richEnabled,
|
|
472
486
|
richDraftEnabled: input.richDraftEnabled,
|
|
487
|
+
streamingEnabled: input.streamingEnabled,
|
|
473
488
|
};
|
|
474
489
|
drafts.set(draft, token);
|
|
475
490
|
const pairingMessage =
|
|
@@ -513,6 +528,7 @@ export function createNotificationsEditorOperations(
|
|
|
513
528
|
{ path: "notifications.telegram.chatId", op: "set", value: draft.chatId },
|
|
514
529
|
{ path: "notifications.telegram.rich.enabled", op: "set", value: draft.richEnabled },
|
|
515
530
|
{ path: "notifications.telegram.richDraft.enabled", op: "set", value: draft.richDraftEnabled },
|
|
531
|
+
{ path: "notifications.telegram.streaming.enabled", op: "set", value: draft.streamingEnabled },
|
|
516
532
|
]);
|
|
517
533
|
drafts.delete(draft);
|
|
518
534
|
const activationMarker = createTelegramActivationMarker({
|
|
@@ -676,14 +692,60 @@ export function createNotificationsEditorOperations(
|
|
|
676
692
|
},
|
|
677
693
|
|
|
678
694
|
commitPreferences: async preferences => {
|
|
695
|
+
let daemonWasRunningForDisable = false;
|
|
679
696
|
try {
|
|
680
|
-
const
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
697
|
+
const before = services.getNotificationConfig(ctx.settings);
|
|
698
|
+
const disablingToolActivity =
|
|
699
|
+
isTelegramConfigured(before) && before.toolActivity.enabled && !preferences.toolActivityEnabled;
|
|
700
|
+
if (disablingToolActivity) {
|
|
701
|
+
const stopped = await services.stopTelegramDaemon(ctx.settings);
|
|
702
|
+
if (!stopped.ok)
|
|
703
|
+
throw new Error(
|
|
704
|
+
`Notification preferences were not saved because daemon stop failed: ${stopped.message}`,
|
|
705
|
+
);
|
|
706
|
+
daemonWasRunningForDisable = stopped.before?.health === "running";
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
let receipt: Awaited<ReturnType<typeof ctx.settings.commitAtomicBatch>>;
|
|
710
|
+
try {
|
|
711
|
+
receipt = await ctx.settings.commitAtomicBatch([
|
|
712
|
+
{ path: "notifications.redact", op: "set", value: preferences.redact },
|
|
713
|
+
{ path: "notifications.verbosity", op: "set", value: preferences.verbosity },
|
|
714
|
+
{ path: "notifications.sessionScope", op: "set", value: preferences.sessionScope },
|
|
715
|
+
{ path: "notifications.telegram.rich.enabled", op: "set", value: preferences.richEnabled },
|
|
716
|
+
{ path: "notifications.telegram.richDraft.enabled", op: "set", value: preferences.richDraftEnabled },
|
|
717
|
+
{ path: "notifications.telegram.streaming.enabled", op: "set", value: preferences.streamingEnabled },
|
|
718
|
+
{
|
|
719
|
+
path: "notifications.telegram.toolActivity.enabled",
|
|
720
|
+
op: "set",
|
|
721
|
+
value: preferences.toolActivityEnabled,
|
|
722
|
+
},
|
|
723
|
+
]);
|
|
724
|
+
} catch (error) {
|
|
725
|
+
if (daemonWasRunningForDisable) {
|
|
726
|
+
try {
|
|
727
|
+
const restarted = await services.restartTelegramDaemon(ctx.settings);
|
|
728
|
+
if (!restarted.ok) throw new Error(restarted.message);
|
|
729
|
+
} catch (restartError) {
|
|
730
|
+
const commitMessage = error instanceof Error ? error.message : String(error);
|
|
731
|
+
const restartMessage = restartError instanceof Error ? restartError.message : String(restartError);
|
|
732
|
+
throw new Error(
|
|
733
|
+
`Notification preference commit failed (${commitMessage}) and daemon restart failed (${restartMessage}).`,
|
|
734
|
+
{ cause: new AggregateError([error, restartError]) },
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
throw error;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
const config = services.getNotificationConfig(ctx.settings);
|
|
742
|
+
if (isTelegramConfigured(config)) {
|
|
743
|
+
const reload = daemonWasRunningForDisable
|
|
744
|
+
? await services.restartTelegramDaemon(ctx.settings)
|
|
745
|
+
: await services.reloadTelegramDaemon(ctx.settings);
|
|
746
|
+
if (!reload.ok)
|
|
747
|
+
throw new Error(`Notification preferences were saved, but daemon reload failed: ${reload.message}`);
|
|
748
|
+
}
|
|
687
749
|
await notifyAfterDurableCommit();
|
|
688
750
|
return { receipt, message: "Notification preferences saved atomically." };
|
|
689
751
|
} catch (error) {
|
|
@@ -1060,7 +1122,7 @@ export class SelectorController {
|
|
|
1060
1122
|
};
|
|
1061
1123
|
wizard = new CustomProviderWizardComponent(
|
|
1062
1124
|
input => {
|
|
1063
|
-
|
|
1125
|
+
return submit(input);
|
|
1064
1126
|
},
|
|
1065
1127
|
() => {
|
|
1066
1128
|
done();
|
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
} from "./components/pet-capability";
|
|
63
63
|
import type { ToolExecutionHandle } from "./components/tool-execution";
|
|
64
64
|
import { StatusLineComponent } from "./components/tool-status-header";
|
|
65
|
+
import { composeToolText } from "./components/tool-transcript-format";
|
|
65
66
|
import {
|
|
66
67
|
WelcomeComponent,
|
|
67
68
|
type WelcomeLogoMode,
|
|
@@ -1817,17 +1818,28 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1817
1818
|
result?.content
|
|
1818
1819
|
.filter(part => part.type === "text")
|
|
1819
1820
|
.map(part => part.text)
|
|
1820
|
-
.join("\n")
|
|
1821
|
+
.join("\n")
|
|
1822
|
+
.trim() ?? "";
|
|
1821
1823
|
items.push({
|
|
1822
1824
|
kind: "tool",
|
|
1823
1825
|
source: { toolCallId: content.id, content, result },
|
|
1824
1826
|
getPayload: () => ({
|
|
1825
|
-
text:
|
|
1827
|
+
text: composeToolText({
|
|
1828
|
+
name: content.name,
|
|
1829
|
+
args: content.arguments,
|
|
1830
|
+
intent: content.intent,
|
|
1831
|
+
resultText,
|
|
1832
|
+
isError: result?.isError ?? false,
|
|
1833
|
+
hasResult: toolResults.has(content.id),
|
|
1834
|
+
}),
|
|
1826
1835
|
metadata: {
|
|
1827
1836
|
name: content.name,
|
|
1828
1837
|
arguments: content.arguments,
|
|
1829
1838
|
intent: content.intent,
|
|
1830
1839
|
isError: result?.isError ?? false,
|
|
1840
|
+
resultText,
|
|
1841
|
+
hasResult: toolResults.has(content.id),
|
|
1842
|
+
detailsData: result?.details,
|
|
1831
1843
|
},
|
|
1832
1844
|
source: { content, result },
|
|
1833
1845
|
}),
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
* This is the pure mapping primitive used by SDK-native workflow gate emitters.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { isDeepInterviewAskQuestion } from "../../../deep-interview/render-middleware";
|
|
14
|
+
import {
|
|
15
|
+
assertDeepInterviewInputWithinLimit,
|
|
16
|
+
MAX_USER_RESPONSE_LENGTH,
|
|
17
|
+
} from "../../../gjc-runtime/deep-interview-state";
|
|
13
18
|
import type { OpenGateInput } from "./workflow-gate-broker";
|
|
14
19
|
import {
|
|
15
20
|
type AskGateDeepInterviewState,
|
|
@@ -134,7 +139,17 @@ export function buildAskGateStageState(question: AskGateQuestion, labels: string
|
|
|
134
139
|
/** Build the `workflow_gate` open-input for one ask question. */
|
|
135
140
|
export function questionToGate(question: AskGateQuestion): OpenGateInput {
|
|
136
141
|
const labels = question.options.map(o => o.label);
|
|
137
|
-
const schema = buildAskGateAnswerSchema(
|
|
142
|
+
const schema = buildAskGateAnswerSchema(
|
|
143
|
+
{
|
|
144
|
+
multi: question.multi,
|
|
145
|
+
allowEmpty: question.allowEmpty,
|
|
146
|
+
customMaxLength:
|
|
147
|
+
question.deepInterview || isDeepInterviewAskQuestion(question.question)
|
|
148
|
+
? MAX_USER_RESPONSE_LENGTH
|
|
149
|
+
: undefined,
|
|
150
|
+
},
|
|
151
|
+
labels,
|
|
152
|
+
);
|
|
138
153
|
return {
|
|
139
154
|
stage: question.workflowGate?.stage ?? "deep-interview",
|
|
140
155
|
kind: question.workflowGate?.kind ?? "question",
|
|
@@ -229,6 +244,12 @@ export function gateAnswerToResult(question: AskGateQuestion, answer: unknown):
|
|
|
229
244
|
if (other && (answer.custom === undefined || answer.custom.trim() === "")) {
|
|
230
245
|
throw new DeepInterviewGateError("missing_custom", "custom text is required when `other` is true");
|
|
231
246
|
}
|
|
247
|
+
if (
|
|
248
|
+
other &&
|
|
249
|
+
(question.deepInterview || isDeepInterviewAskQuestion(question.question)) &&
|
|
250
|
+
answer.custom !== undefined
|
|
251
|
+
)
|
|
252
|
+
assertDeepInterviewInputWithinLimit(answer.custom, MAX_USER_RESPONSE_LENGTH, "user_response");
|
|
232
253
|
return {
|
|
233
254
|
id: question.id,
|
|
234
255
|
question: question.question,
|
|
@@ -261,6 +282,15 @@ export function classifyAskGateDisposition(
|
|
|
261
282
|
multi: (state as Record<string, unknown>).multi === true,
|
|
262
283
|
allowEmpty: (state as Record<string, unknown>).allow_empty === true,
|
|
263
284
|
};
|
|
285
|
+
if (
|
|
286
|
+
(state as Record<string, unknown>).deep_interview_metadata === true &&
|
|
287
|
+
typeof (answer as Record<string, unknown>)?.custom === "string"
|
|
288
|
+
)
|
|
289
|
+
assertDeepInterviewInputWithinLimit(
|
|
290
|
+
(answer as Record<string, unknown>).custom as string,
|
|
291
|
+
MAX_USER_RESPONSE_LENGTH,
|
|
292
|
+
"user_response",
|
|
293
|
+
);
|
|
264
294
|
if (typeof answer === "object" && answer !== null && (answer as Record<string, unknown>).action === "clarify") {
|
|
265
295
|
gateAnswerToResult(question, answer);
|
|
266
296
|
return "resolve_without_commit";
|
|
@@ -76,6 +76,13 @@ export interface NotificationGateResolutionOptions {
|
|
|
76
76
|
closeClaimInvalid(reason: string): void;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
export class NotificationGatePolicyChangedError extends Error {
|
|
80
|
+
constructor() {
|
|
81
|
+
super("Notification policy changed while resolving the workflow gate");
|
|
82
|
+
this.name = "NotificationGatePolicyChangedError";
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
79
86
|
export interface AskSelectedAckRecoveryParticipant {
|
|
80
87
|
requestRecoveredAskSelectedAck(input: {
|
|
81
88
|
sessionId: string;
|
|
@@ -361,7 +368,8 @@ export class BrokerWorkflowGateEmitter implements WorkflowGateEmitter {
|
|
|
361
368
|
daemonDeadlineAt: Date.now() + 8_000,
|
|
362
369
|
hostTimeoutMs: 10_000,
|
|
363
370
|
});
|
|
364
|
-
} catch {
|
|
371
|
+
} catch (error) {
|
|
372
|
+
if (error instanceof NotificationGatePolicyChangedError) throw error;
|
|
365
373
|
outcome = { status: "unknown", reason: "host_timeout" };
|
|
366
374
|
}
|
|
367
375
|
this.#broker.updateAckPolicy(response.gate_id, {
|
|
@@ -378,6 +386,14 @@ export class BrokerWorkflowGateEmitter implements WorkflowGateEmitter {
|
|
|
378
386
|
else closeClaimInvalid(resolution.error?.code ?? "invalid_answer");
|
|
379
387
|
return resolution;
|
|
380
388
|
} catch (error) {
|
|
389
|
+
if (error instanceof NotificationGatePolicyChangedError) {
|
|
390
|
+
try {
|
|
391
|
+
closeClaimInvalid(error.message);
|
|
392
|
+
} finally {
|
|
393
|
+
this.quarantineGate(response.gate_id);
|
|
394
|
+
}
|
|
395
|
+
throw error;
|
|
396
|
+
}
|
|
381
397
|
closeClaimInvalid(error instanceof Error ? error.message : "invalid_answer");
|
|
382
398
|
this.#scheduleRecovery();
|
|
383
399
|
throw error;
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* swapped in later without changing callers.
|
|
13
13
|
*/
|
|
14
14
|
import { createHash } from "node:crypto";
|
|
15
|
+
import { deepInterviewCharacterCount } from "../../../gjc-runtime/deep-interview-state";
|
|
15
16
|
import type { JsonSchema, WorkflowGateValidationError } from "./workflow-gate-types";
|
|
16
17
|
|
|
17
18
|
/** Keywords this wrapper understands. Any other keyword is rejected. */
|
|
@@ -238,7 +239,8 @@ function validateValue(schema: JsonSchema, value: unknown, path: string, errors:
|
|
|
238
239
|
if (!ok) errors.push({ path, keyword: "enum", message: "value not in enum", expected: schema.enum });
|
|
239
240
|
}
|
|
240
241
|
if (typeof value === "string") {
|
|
241
|
-
|
|
242
|
+
const characterCount = deepInterviewCharacterCount(value);
|
|
243
|
+
if (schema.minLength !== undefined && characterCount < schema.minLength) {
|
|
242
244
|
errors.push({
|
|
243
245
|
path,
|
|
244
246
|
keyword: "minLength",
|
|
@@ -246,7 +248,7 @@ function validateValue(schema: JsonSchema, value: unknown, path: string, errors:
|
|
|
246
248
|
expected: schema.minLength,
|
|
247
249
|
});
|
|
248
250
|
}
|
|
249
|
-
if (schema.maxLength !== undefined &&
|
|
251
|
+
if (schema.maxLength !== undefined && characterCount > schema.maxLength) {
|
|
250
252
|
errors.push({
|
|
251
253
|
path,
|
|
252
254
|
keyword: "maxLength",
|