@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
package/src/main.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
VERSION,
|
|
21
21
|
} from "@gajae-code/utils";
|
|
22
22
|
import chalk from "chalk";
|
|
23
|
+
import { runCli } from "./cli";
|
|
23
24
|
import type { Args } from "./cli/args";
|
|
24
25
|
import { processFileArguments } from "./cli/file-processor";
|
|
25
26
|
import { buildInitialMessage } from "./cli/initial-message";
|
|
@@ -36,6 +37,8 @@ import { BUNDLED_GROK_BUILD_EXTENSION_ID, getBundledGrokBuildExtensionFactory }
|
|
|
36
37
|
import { initializeWithSettings } from "./discovery";
|
|
37
38
|
import { exportFromFile } from "./export/html";
|
|
38
39
|
import type { ExtensionUIContext } from "./extensibility/extensions/types";
|
|
40
|
+
import { admitManagedOwnerBeforeCli, completeManagedOwnerRecovery } from "./gjc-runtime/managed-owner-admission";
|
|
41
|
+
import { isManagedOwnerSupervisorArgv, runManagedOwnerSupervisor } from "./gjc-runtime/managed-owner-supervisor";
|
|
39
42
|
import { persistCoordinatorRuntimeInputReady } from "./gjc-runtime/session-state-sidecar";
|
|
40
43
|
import { isTmuxOwnerIsolationCliArgv, runTmuxOwnerIsolationCliFromStdin } from "./gjc-runtime/tmux-owner-isolation-cli";
|
|
41
44
|
import type { AcpStartupOptions } from "./modes/acp/startup-options";
|
|
@@ -58,6 +61,7 @@ import type { AgentSession } from "./session/agent-session";
|
|
|
58
61
|
import {
|
|
59
62
|
type ResumeSessionIdentity,
|
|
60
63
|
resolveResumableSession,
|
|
64
|
+
type SessionDestination,
|
|
61
65
|
type SessionDirectoryMigrationPolicy,
|
|
62
66
|
type SessionInfo,
|
|
63
67
|
SessionManager,
|
|
@@ -535,7 +539,8 @@ type ListForResumePickerReadOnly = (cwd: string, sessionDir?: string) => Promise
|
|
|
535
539
|
type SelectResumeSession = (sessions: SessionInfo[]) => Promise<SessionSelectionResult>;
|
|
536
540
|
type OpenExistingSessionStrict = (
|
|
537
541
|
identity: ResumeSessionIdentity,
|
|
538
|
-
|
|
542
|
+
destination: SessionDestination,
|
|
543
|
+
storage?: undefined,
|
|
539
544
|
migrationPolicy?: SessionDirectoryMigrationPolicy,
|
|
540
545
|
) => Promise<StrictSessionOpenResult>;
|
|
541
546
|
|
|
@@ -722,6 +727,10 @@ export async function createSessionManager(
|
|
|
722
727
|
activeSettings: Settings = settings,
|
|
723
728
|
): Promise<SessionManager | undefined> {
|
|
724
729
|
const migrationPolicy = activeSettings.get("session.directoryMigration") === "disabled" ? "disabled" : "copy-retain";
|
|
730
|
+
const sessionDestination = () =>
|
|
731
|
+
parsed.sessionDir
|
|
732
|
+
? SessionManager.explicitDestination(parsed.sessionDir)
|
|
733
|
+
: SessionManager.managedDestination(cwd, activeSettings.getAgentDir());
|
|
725
734
|
if (parsed.resume === true) {
|
|
726
735
|
return undefined;
|
|
727
736
|
}
|
|
@@ -731,13 +740,13 @@ export async function createSessionManager(
|
|
|
731
740
|
}
|
|
732
741
|
const forkSource = parsed.fork;
|
|
733
742
|
if (forkSource.includes("/") || forkSource.includes("\\") || forkSource.endsWith(".jsonl")) {
|
|
734
|
-
return await SessionManager.forkFrom(forkSource, cwd,
|
|
743
|
+
return await SessionManager.forkFrom(forkSource, cwd, sessionDestination(), undefined, migrationPolicy);
|
|
735
744
|
}
|
|
736
745
|
const match = await resolveResumableSession(forkSource, cwd, parsed.sessionDir);
|
|
737
746
|
if (!match) {
|
|
738
747
|
throw new Error(`Session "${forkSource}" not found.`);
|
|
739
748
|
}
|
|
740
|
-
return await SessionManager.forkFrom(match.session.path, cwd,
|
|
749
|
+
return await SessionManager.forkFrom(match.session.path, cwd, sessionDestination(), undefined, migrationPolicy);
|
|
741
750
|
}
|
|
742
751
|
|
|
743
752
|
if (parsed.noSession) {
|
|
@@ -746,7 +755,12 @@ export async function createSessionManager(
|
|
|
746
755
|
if (typeof parsed.resume === "string") {
|
|
747
756
|
const sessionArg = parsed.resume;
|
|
748
757
|
if (sessionArg.includes("/") || sessionArg.includes("\\") || sessionArg.endsWith(".jsonl")) {
|
|
749
|
-
return await SessionManager.open(
|
|
758
|
+
return await SessionManager.open(
|
|
759
|
+
sessionArg,
|
|
760
|
+
SessionManager.explicitDestination(path.dirname(sessionArg)),
|
|
761
|
+
undefined,
|
|
762
|
+
migrationPolicy,
|
|
763
|
+
);
|
|
750
764
|
}
|
|
751
765
|
const match = await resolveResumableSession(sessionArg, cwd, parsed.sessionDir);
|
|
752
766
|
if (!match) {
|
|
@@ -763,21 +777,21 @@ export async function createSessionManager(
|
|
|
763
777
|
return await SessionManager.forkFrom(
|
|
764
778
|
match.session.path,
|
|
765
779
|
cwd,
|
|
766
|
-
|
|
780
|
+
sessionDestination(),
|
|
767
781
|
undefined,
|
|
768
782
|
migrationPolicy,
|
|
769
783
|
);
|
|
770
784
|
}
|
|
771
785
|
}
|
|
772
|
-
return await SessionManager.open(match.session.path,
|
|
786
|
+
return await SessionManager.open(match.session.path, sessionDestination(), undefined, migrationPolicy);
|
|
773
787
|
}
|
|
774
788
|
if (parsed.continue) {
|
|
775
|
-
return await SessionManager.continueRecent(cwd,
|
|
789
|
+
return await SessionManager.continueRecent(cwd, sessionDestination(), undefined, migrationPolicy);
|
|
776
790
|
}
|
|
777
791
|
// --resume without value is handled separately (needs picker UI)
|
|
778
792
|
// If --session-dir provided without --continue/--resume, create new session there
|
|
779
793
|
if (parsed.sessionDir) {
|
|
780
|
-
return SessionManager.create(cwd, parsed.sessionDir);
|
|
794
|
+
return SessionManager.create(cwd, SessionManager.explicitDestination(parsed.sessionDir));
|
|
781
795
|
}
|
|
782
796
|
// A lifecycle `/session_create` child must start a FRESH session that adopts
|
|
783
797
|
// the pre-allocated id (GJC_SESSION_ID), never auto-resume existing history in
|
|
@@ -795,14 +809,13 @@ export async function createSessionManager(
|
|
|
795
809
|
// buildSessionOptions restores the session's model/thinking instead of
|
|
796
810
|
// overriding them with CLI defaults.
|
|
797
811
|
if (activeSettings.get("autoResume")) {
|
|
798
|
-
const manager = await SessionManager.continueRecent(cwd,
|
|
812
|
+
const manager = await SessionManager.continueRecent(cwd, sessionDestination(), undefined, migrationPolicy);
|
|
799
813
|
if (manager.getEntries().length > 0) {
|
|
800
814
|
parsed.continue = true;
|
|
801
815
|
}
|
|
802
816
|
return manager;
|
|
803
817
|
}
|
|
804
|
-
|
|
805
|
-
return SessionManager.create(cwd, sessionDir);
|
|
818
|
+
return SessionManager.create(cwd, SessionManager.managedDestination(cwd, activeSettings.getAgentDir()));
|
|
806
819
|
}
|
|
807
820
|
|
|
808
821
|
async function maybeAutoChdir(parsed: Args): Promise<void> {
|
|
@@ -1106,7 +1119,9 @@ export async function runRootCommand(
|
|
|
1106
1119
|
try {
|
|
1107
1120
|
opened = await (deps.openExistingSessionStrict ?? SessionManager.openExistingStrict)(
|
|
1108
1121
|
selection.identity,
|
|
1109
|
-
parsedArgs.sessionDir
|
|
1122
|
+
parsedArgs.sessionDir
|
|
1123
|
+
? SessionManager.explicitDestination(parsedArgs.sessionDir)
|
|
1124
|
+
: SessionManager.managedDestination(resumeCwd, settings.getAgentDir()),
|
|
1110
1125
|
undefined,
|
|
1111
1126
|
resumeMigrationPolicy,
|
|
1112
1127
|
);
|
|
@@ -1604,6 +1619,15 @@ export async function main(args: string[]): Promise<void> {
|
|
|
1604
1619
|
await runTmuxOwnerIsolationCliFromStdin();
|
|
1605
1620
|
return;
|
|
1606
1621
|
}
|
|
1607
|
-
|
|
1622
|
+
if (isManagedOwnerSupervisorArgv(args)) {
|
|
1623
|
+
await runManagedOwnerSupervisor();
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
const admission = await admitManagedOwnerBeforeCli();
|
|
1627
|
+
if (admission.kind === "blocked") return;
|
|
1628
|
+
if (admission.kind === "recovery") {
|
|
1629
|
+
await completeManagedOwnerRecovery(admission.context);
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1608
1632
|
await runCli(args.length === 0 ? ["launch"] : args);
|
|
1609
1633
|
}
|
|
@@ -74,6 +74,7 @@ interface PromptWaiter {
|
|
|
74
74
|
/** Highest inbound frame sequence already observed when the prompt was acknowledged. */
|
|
75
75
|
boundary: number;
|
|
76
76
|
correlation: PromptCorrelation;
|
|
77
|
+
messageProgress?: { textEmitted: boolean; thoughtEmitted: boolean };
|
|
77
78
|
pendingTerminal?: PromptCorrelation;
|
|
78
79
|
resolve: (response: PromptResponse) => void;
|
|
79
80
|
reject: (error: Error) => void;
|
|
@@ -1298,9 +1299,16 @@ export class AcpAgent implements Agent {
|
|
|
1298
1299
|
if (wirePayload) {
|
|
1299
1300
|
for (const notification of mapAgentWireEventPayloadToAcpSessionUpdates(wirePayload as never, id, {
|
|
1300
1301
|
cwd: record.cwd,
|
|
1302
|
+
getMessageProgress: message => {
|
|
1303
|
+
if (!activePrompt || !object(message)) return undefined;
|
|
1304
|
+
activePrompt.messageProgress ??= { textEmitted: false, thoughtEmitted: false };
|
|
1305
|
+
return activePrompt.messageProgress;
|
|
1306
|
+
},
|
|
1301
1307
|
}))
|
|
1302
1308
|
await this.#publishSessionUpdate(id, notification, adapter);
|
|
1303
1309
|
}
|
|
1310
|
+
if (event.type === "message_end" && object(event.message)?.role === "assistant" && activePrompt)
|
|
1311
|
+
activePrompt.messageProgress = undefined;
|
|
1304
1312
|
if (event.type === "agent_end") await this.#emitEndOfTurnUpdates(id, adapter);
|
|
1305
1313
|
if (activePrompt) this.#settlePrompt(record, activePrompt);
|
|
1306
1314
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Removes every terminal control sequence except bounded SGR parameters (0-255). */
|
|
2
|
+
export function validateDisplayLine(line: string): string {
|
|
3
|
+
let result = "";
|
|
4
|
+
for (let index = 0; index < line.length; index++) {
|
|
5
|
+
const char = line[index]!;
|
|
6
|
+
const code = char.charCodeAt(0);
|
|
7
|
+
if (char === "\t") {
|
|
8
|
+
result += " ";
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
if (char === "\x1b") {
|
|
12
|
+
const next = line[index + 1];
|
|
13
|
+
if (next === "[") {
|
|
14
|
+
const match = /^\x1b\[([0-9]{1,3}(?:;[0-9]{1,3})*)m/.exec(line.slice(index));
|
|
15
|
+
if (match && match[0].length <= 64 && match[1]!.split(";").every(parameter => Number(parameter) <= 255)) {
|
|
16
|
+
result += match[0];
|
|
17
|
+
index += match[0].length - 1;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
index = skipEscapeSequence(line, index + 2, /[@-~]/);
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (next === "]" || next === "P" || next === "_" || next === "^" || next === "X") {
|
|
24
|
+
index = skipStringControl(line, index + 2);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (next === "_") {
|
|
28
|
+
index = skipStringControl(line, index + 2);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
index += next ? 1 : 0;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (code >= 0x80 && code <= 0x9f) continue;
|
|
35
|
+
if (code < 0x20 || code === 0x7f) continue;
|
|
36
|
+
result += char;
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function skipEscapeSequence(text: string, start: number, final: RegExp): number {
|
|
42
|
+
for (let index = start; index < text.length; index++) {
|
|
43
|
+
if (final.test(text[index]!)) return index;
|
|
44
|
+
}
|
|
45
|
+
return text.length - 1;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function skipStringControl(text: string, start: number): number {
|
|
49
|
+
for (let index = start; index < text.length; index++) {
|
|
50
|
+
if (text[index] === "\x07") return index;
|
|
51
|
+
if (text[index] === "\x1b" && text[index + 1] === "\\") return index + 1;
|
|
52
|
+
}
|
|
53
|
+
return text.length - 1;
|
|
54
|
+
}
|
|
@@ -59,9 +59,22 @@ const DEFAULT_ACTION_KEYS = Object.fromEntries(
|
|
|
59
59
|
|
|
60
60
|
const PASTE_DECISION_TIMEOUT_MS = 5_000;
|
|
61
61
|
const PENDING_PASTE_INPUT_MAX = 64;
|
|
62
|
+
const PENDING_PASTE_INPUT_MAX_BYTES = 256 * 1024;
|
|
62
63
|
|
|
63
64
|
type PastePendingClearReason = "timeout" | "queue-limit";
|
|
64
65
|
|
|
66
|
+
export interface PasteTextContext {
|
|
67
|
+
readonly signal: AbortSignal;
|
|
68
|
+
/** Run one synchronous composer mutation only while this paste transaction is current. */
|
|
69
|
+
commit(commit: () => boolean): boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface PendingPasteDecision {
|
|
73
|
+
readonly controller: AbortController;
|
|
74
|
+
readonly pasteContent: string;
|
|
75
|
+
readonly remaining: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
65
78
|
/**
|
|
66
79
|
* Custom editor that handles configurable app-level shortcuts for coding-agent.
|
|
67
80
|
*/
|
|
@@ -94,9 +107,9 @@ export class CustomEditor extends Editor {
|
|
|
94
107
|
/** Called when the configured image-paste shortcut is pressed. */
|
|
95
108
|
onPasteImage?: () => Promise<boolean>;
|
|
96
109
|
/** Called before bracketed paste content is inserted. Return true to consume it. */
|
|
97
|
-
onPasteText?: (text: string) => boolean | Promise<boolean>;
|
|
98
|
-
/** Called when
|
|
99
|
-
onPastePendingInputCleared?: (reason: PastePendingClearReason,
|
|
110
|
+
onPasteText?: (text: string, context: PasteTextContext) => boolean | Promise<boolean>;
|
|
111
|
+
/** Called when pending paste handling is cancelled after restoring queued input. */
|
|
112
|
+
onPastePendingInputCleared?: (reason: PastePendingClearReason, restoredInputCount: number) => void;
|
|
100
113
|
/** Called when the configured dequeue shortcut is pressed. */
|
|
101
114
|
onDequeue?: () => void;
|
|
102
115
|
/** Called when the configured queue shortcut is pressed. */
|
|
@@ -117,7 +130,9 @@ export class CustomEditor extends Editor {
|
|
|
117
130
|
#pasteDecisionPending = false;
|
|
118
131
|
#pasteDecisionToken = 0;
|
|
119
132
|
#pasteDecisionTimeout: NodeJS.Timeout | undefined;
|
|
133
|
+
#pendingPasteDecision: PendingPasteDecision | undefined;
|
|
120
134
|
#pendingPasteInput: string[] = [];
|
|
135
|
+
#pendingPasteInputBytes = 0;
|
|
121
136
|
|
|
122
137
|
setActionKeys(action: ConfigurableEditorAction, keys: KeyId[]): void {
|
|
123
138
|
this.#actionKeys.set(action, [...keys]);
|
|
@@ -165,75 +180,127 @@ export class CustomEditor extends Editor {
|
|
|
165
180
|
}
|
|
166
181
|
}
|
|
167
182
|
|
|
168
|
-
#
|
|
183
|
+
#resetPendingPasteState(abortReason?: Error): string[] {
|
|
169
184
|
this.#clearPasteDecisionTimeout();
|
|
185
|
+
if (abortReason) this.#pendingPasteDecision?.controller.abort(abortReason);
|
|
170
186
|
this.#pasteDecisionPending = false;
|
|
171
187
|
this.#pasteDecisionToken += 1;
|
|
172
|
-
|
|
188
|
+
this.#pendingPasteDecision = undefined;
|
|
189
|
+
const queuedInput = this.#pendingPasteInput;
|
|
173
190
|
this.#pendingPasteInput = [];
|
|
174
|
-
|
|
191
|
+
this.#pendingPasteInputBytes = 0;
|
|
192
|
+
return queuedInput;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
#pendingPasteEventCount(): number {
|
|
196
|
+
return this.#pendingPasteInput.length + (this.#pendingPasteDecision?.remaining ? 1 : 0);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
#restoreCancelledPaste(reason: PastePendingClearReason, overflowInput?: string): void {
|
|
200
|
+
const decision = this.#pendingPasteDecision;
|
|
201
|
+
const queuedInput = this.#resetPendingPasteState(new Error(`Paste handling cancelled: ${reason}`));
|
|
202
|
+
if (decision) {
|
|
203
|
+
super.handleInput(`\x1b[200~${decision.pasteContent}\x1b[201~`);
|
|
204
|
+
this.#drainPendingPasteInput(decision.remaining, queuedInput);
|
|
205
|
+
if (overflowInput !== undefined) this.handleInput(overflowInput);
|
|
206
|
+
}
|
|
207
|
+
this.onPastePendingInputCleared?.(
|
|
208
|
+
reason,
|
|
209
|
+
queuedInput.length + (decision?.remaining ? 1 : 0) + (overflowInput === undefined ? 0 : 1),
|
|
210
|
+
);
|
|
175
211
|
}
|
|
176
212
|
|
|
177
213
|
#startPasteDecisionTimeout(token: number): void {
|
|
178
214
|
this.#clearPasteDecisionTimeout();
|
|
179
215
|
this.#pasteDecisionTimeout = setTimeout(() => {
|
|
180
216
|
if (token !== this.#pasteDecisionToken) return;
|
|
181
|
-
|
|
182
|
-
this.onPastePendingInputCleared?.("timeout", droppedInputCount);
|
|
217
|
+
this.#restoreCancelledPaste("timeout");
|
|
183
218
|
}, PASTE_DECISION_TIMEOUT_MS);
|
|
184
219
|
this.#pasteDecisionTimeout.unref?.();
|
|
185
220
|
}
|
|
186
221
|
|
|
187
222
|
dispose(): void {
|
|
188
|
-
this.#
|
|
223
|
+
this.#resetPendingPasteState(new Error("Editor disposed during paste handling"));
|
|
189
224
|
this.#pasteHandler = new BracketedPasteHandler();
|
|
190
225
|
super.dispose();
|
|
191
226
|
}
|
|
192
227
|
|
|
193
|
-
#drainPendingPasteInput(initialInput
|
|
228
|
+
#drainPendingPasteInput(initialInput: string | undefined, queuedInput: readonly string[]): void {
|
|
194
229
|
if (initialInput && initialInput.length > 0) {
|
|
195
230
|
this.handleInput(initialInput);
|
|
196
231
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (nextInput === undefined) break;
|
|
200
|
-
this.handleInput(nextInput);
|
|
232
|
+
for (const input of queuedInput) {
|
|
233
|
+
this.handleInput(input);
|
|
201
234
|
}
|
|
202
235
|
}
|
|
203
236
|
|
|
204
237
|
#handleBracketedPaste(pasteContent: string, remaining: string): void {
|
|
205
|
-
const
|
|
238
|
+
const remainingBytes = Buffer.byteLength(remaining);
|
|
239
|
+
if (remainingBytes > PENDING_PASTE_INPUT_MAX_BYTES) {
|
|
240
|
+
super.handleInput(`\x1b[200~${pasteContent}\x1b[201~`);
|
|
241
|
+
if (remaining.length > 0) this.handleInput(remaining);
|
|
242
|
+
this.onPastePendingInputCleared?.("queue-limit", 1);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const token = this.#pasteDecisionToken + 1;
|
|
246
|
+
const controller = new AbortController();
|
|
247
|
+
this.#pasteDecisionToken = token;
|
|
248
|
+
this.#pasteDecisionPending = true;
|
|
249
|
+
this.#pendingPasteDecision = { controller, pasteContent, remaining };
|
|
250
|
+
this.#pendingPasteInputBytes = remainingBytes;
|
|
251
|
+
let commitUsed = false;
|
|
252
|
+
|
|
253
|
+
const applyPasteResult = (handled: boolean | undefined) => {
|
|
206
254
|
if (token !== this.#pasteDecisionToken) return;
|
|
207
|
-
this.#
|
|
255
|
+
const queuedInput = this.#resetPendingPasteState();
|
|
208
256
|
if (!handled) {
|
|
209
257
|
super.handleInput(`\x1b[200~${pasteContent}\x1b[201~`);
|
|
210
258
|
}
|
|
211
|
-
this.#
|
|
212
|
-
this.#drainPendingPasteInput(remaining);
|
|
259
|
+
this.#drainPendingPasteInput(remaining, queuedInput);
|
|
213
260
|
};
|
|
214
|
-
|
|
261
|
+
|
|
262
|
+
let pasteResult: boolean | Promise<boolean> | undefined;
|
|
263
|
+
try {
|
|
264
|
+
pasteResult = this.onPasteText?.(pasteContent, {
|
|
265
|
+
signal: controller.signal,
|
|
266
|
+
commit: commit => {
|
|
267
|
+
if (
|
|
268
|
+
commitUsed ||
|
|
269
|
+
token !== this.#pasteDecisionToken ||
|
|
270
|
+
!this.#pasteDecisionPending ||
|
|
271
|
+
controller.signal.aborted
|
|
272
|
+
) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
commitUsed = true;
|
|
276
|
+
return commit();
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
} catch {
|
|
280
|
+
applyPasteResult(false);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
215
283
|
|
|
216
284
|
if (pasteResult instanceof Promise) {
|
|
217
|
-
const token = this.#pasteDecisionToken + 1;
|
|
218
|
-
this.#pasteDecisionToken = token;
|
|
219
|
-
this.#pasteDecisionPending = true;
|
|
220
285
|
this.#startPasteDecisionTimeout(token);
|
|
221
|
-
void pasteResult.then(
|
|
222
|
-
handled => applyPasteResult(token, handled),
|
|
223
|
-
() => applyPasteResult(token, false),
|
|
224
|
-
);
|
|
286
|
+
void pasteResult.then(applyPasteResult, () => applyPasteResult(false));
|
|
225
287
|
} else {
|
|
226
|
-
applyPasteResult(
|
|
288
|
+
applyPasteResult(pasteResult);
|
|
227
289
|
}
|
|
228
290
|
}
|
|
229
291
|
|
|
230
292
|
handleInput(data: string): void {
|
|
231
293
|
if (this.#pasteDecisionPending) {
|
|
232
|
-
|
|
233
|
-
if (
|
|
234
|
-
|
|
235
|
-
this
|
|
294
|
+
const inputBytes = Buffer.byteLength(data);
|
|
295
|
+
if (
|
|
296
|
+
this.#pendingPasteEventCount() >= PENDING_PASTE_INPUT_MAX ||
|
|
297
|
+
this.#pendingPasteInputBytes + inputBytes > PENDING_PASTE_INPUT_MAX_BYTES
|
|
298
|
+
) {
|
|
299
|
+
this.#restoreCancelledPaste("queue-limit", data);
|
|
300
|
+
return;
|
|
236
301
|
}
|
|
302
|
+
this.#pendingPasteInput.push(data);
|
|
303
|
+
this.#pendingPasteInputBytes += inputBytes;
|
|
237
304
|
return;
|
|
238
305
|
}
|
|
239
306
|
|
|
@@ -245,8 +312,12 @@ export class CustomEditor extends Editor {
|
|
|
245
312
|
}
|
|
246
313
|
|
|
247
314
|
if (this.onPasteText) {
|
|
248
|
-
const paste =
|
|
315
|
+
const paste =
|
|
316
|
+
data === "\x1b" && !this.#pasteHandler.hasPendingFrame
|
|
317
|
+
? ({ handled: false } as const)
|
|
318
|
+
: this.#pasteHandler.process(data);
|
|
249
319
|
if (paste.handled) {
|
|
320
|
+
if (paste.leading.length > 0) this.handleInput(paste.leading);
|
|
250
321
|
if (paste.pasteContent !== undefined) {
|
|
251
322
|
this.onViewportFollowLive?.();
|
|
252
323
|
this.#handleBracketedPaste(paste.pasteContent, paste.remaining);
|
|
@@ -41,6 +41,7 @@ export class CustomProviderWizardComponent extends Container {
|
|
|
41
41
|
credential: "",
|
|
42
42
|
models: "",
|
|
43
43
|
};
|
|
44
|
+
#submitInFlight = false;
|
|
44
45
|
#onSubmit: (input: CustomProviderWizardSubmit) => void;
|
|
45
46
|
#onCancel: () => void;
|
|
46
47
|
#onRender: () => void;
|
|
@@ -294,7 +295,7 @@ export class CustomProviderWizardComponent extends Container {
|
|
|
294
295
|
this.#step = "credential";
|
|
295
296
|
} else if (this.#step === "confirm" || this.#step === "force-confirm") {
|
|
296
297
|
if (this.#selectedIndex === 0) {
|
|
297
|
-
this.#
|
|
298
|
+
this.#submit();
|
|
298
299
|
return;
|
|
299
300
|
}
|
|
300
301
|
this.#step = "models";
|
|
@@ -303,6 +304,30 @@ export class CustomProviderWizardComponent extends Container {
|
|
|
303
304
|
this.#onRender();
|
|
304
305
|
}
|
|
305
306
|
|
|
307
|
+
#submit(): void {
|
|
308
|
+
if (this.#submitInFlight) return;
|
|
309
|
+
this.#submitInFlight = true;
|
|
310
|
+
let submission: unknown;
|
|
311
|
+
try {
|
|
312
|
+
submission = this.#onSubmit(this.#buildInput(this.#step === "force-confirm"));
|
|
313
|
+
} catch (error) {
|
|
314
|
+
this.#submitInFlight = false;
|
|
315
|
+
throw error;
|
|
316
|
+
}
|
|
317
|
+
if (!(submission instanceof Promise)) {
|
|
318
|
+
this.#submitInFlight = false;
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
void submission.then(
|
|
322
|
+
() => {
|
|
323
|
+
this.#submitInFlight = false;
|
|
324
|
+
},
|
|
325
|
+
() => {
|
|
326
|
+
this.#submitInFlight = false;
|
|
327
|
+
},
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
306
331
|
#buildInput(force: boolean): CustomProviderWizardSubmit {
|
|
307
332
|
const input = {
|
|
308
333
|
compatibility: this.#state.compatibility,
|
|
@@ -30,6 +30,8 @@ export interface NotificationsEditorPreferences {
|
|
|
30
30
|
sessionScope: "all" | "primary";
|
|
31
31
|
richEnabled: boolean;
|
|
32
32
|
richDraftEnabled: boolean;
|
|
33
|
+
toolActivityEnabled: boolean;
|
|
34
|
+
streamingEnabled: boolean;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
/** Secret-safe snapshot used to render the Notifications tab. */
|
|
@@ -50,6 +52,7 @@ export interface NotificationsEditorSetupInput {
|
|
|
50
52
|
chatId?: string;
|
|
51
53
|
richEnabled: boolean;
|
|
52
54
|
richDraftEnabled: boolean;
|
|
55
|
+
streamingEnabled: boolean;
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
/**
|
|
@@ -62,6 +65,7 @@ export interface PreparedTelegramConfiguration {
|
|
|
62
65
|
tokenFingerprint?: string;
|
|
63
66
|
richEnabled: boolean;
|
|
64
67
|
richDraftEnabled: boolean;
|
|
68
|
+
streamingEnabled: boolean;
|
|
65
69
|
}
|
|
66
70
|
|
|
67
71
|
export interface NotificationsPreflightResult {
|
|
@@ -155,7 +159,8 @@ type HomeActionId =
|
|
|
155
159
|
| "recover"
|
|
156
160
|
| "reconnect"
|
|
157
161
|
| "remove"
|
|
158
|
-
| "preferences"
|
|
162
|
+
| "preferences"
|
|
163
|
+
| "tool-activity";
|
|
159
164
|
|
|
160
165
|
interface Action {
|
|
161
166
|
id: HomeActionId | "telegram" | "external" | "save" | "save-inactive" | "cancel" | "confirm";
|
|
@@ -190,6 +195,8 @@ function emptyState(): NotificationsEditorState {
|
|
|
190
195
|
sessionScope: "all",
|
|
191
196
|
richEnabled: true,
|
|
192
197
|
richDraftEnabled: false,
|
|
198
|
+
toolActivityEnabled: true,
|
|
199
|
+
streamingEnabled: true,
|
|
193
200
|
},
|
|
194
201
|
};
|
|
195
202
|
}
|
|
@@ -549,6 +556,7 @@ export class NotificationsSettingsEditorComponent implements Component, Focusabl
|
|
|
549
556
|
chatId,
|
|
550
557
|
richEnabled: preferences.richEnabled,
|
|
551
558
|
richDraftEnabled: preferences.richDraftEnabled,
|
|
559
|
+
streamingEnabled: preferences.streamingEnabled,
|
|
552
560
|
},
|
|
553
561
|
signal,
|
|
554
562
|
),
|
|
@@ -928,6 +936,12 @@ export class NotificationsSettingsEditorComponent implements Component, Focusabl
|
|
|
928
936
|
case "refresh":
|
|
929
937
|
draft.richDraftEnabled = !draft.richDraftEnabled;
|
|
930
938
|
return;
|
|
939
|
+
case "tool-activity":
|
|
940
|
+
draft.toolActivityEnabled = !draft.toolActivityEnabled;
|
|
941
|
+
return;
|
|
942
|
+
case "test":
|
|
943
|
+
draft.streamingEnabled = !draft.streamingEnabled;
|
|
944
|
+
return;
|
|
931
945
|
default:
|
|
932
946
|
return;
|
|
933
947
|
}
|
|
@@ -1068,6 +1082,16 @@ export class NotificationsSettingsEditorComponent implements Component, Focusabl
|
|
|
1068
1082
|
label: `Telegram rich drafts: ${draft.richDraftEnabled ? "on" : "off"}`,
|
|
1069
1083
|
description: "Toggle the unsaved rich-draft preference.",
|
|
1070
1084
|
},
|
|
1085
|
+
{
|
|
1086
|
+
id: "tool-activity",
|
|
1087
|
+
label: `Telegram tool activity: ${draft.toolActivityEnabled ? "on" : "off"}`,
|
|
1088
|
+
description: "Toggle tool start and completion notifications.",
|
|
1089
|
+
},
|
|
1090
|
+
{
|
|
1091
|
+
id: "test",
|
|
1092
|
+
label: `Telegram streaming: ${draft.streamingEnabled ? "on" : "off"}`,
|
|
1093
|
+
description: "Toggle the unsaved Telegram streaming preference.",
|
|
1094
|
+
},
|
|
1071
1095
|
{ id: "save", label: "Save preferences", description: "Atomically persist this preference draft." },
|
|
1072
1096
|
{ id: "cancel", label: "Cancel and discard draft", description: "Leave saved preferences unchanged." },
|
|
1073
1097
|
];
|
|
@@ -1162,7 +1186,7 @@ export class NotificationsSettingsEditorComponent implements Component, Focusabl
|
|
|
1162
1186
|
);
|
|
1163
1187
|
lines.push(
|
|
1164
1188
|
this.#truncate(
|
|
1165
|
-
` Private chat: ${draft?.chatId ?? "(expired)"} rich: ${draft?.richEnabled ? "on" : "off"} drafts: ${draft?.richDraftEnabled ? "on" : "off"}`,
|
|
1189
|
+
` Private chat: ${draft?.chatId ?? "(expired)"} rich: ${draft?.richEnabled ? "on" : "off"} drafts: ${draft?.richDraftEnabled ? "on" : "off"} streaming: ${draft?.streamingEnabled ? "on" : "off"}`,
|
|
1166
1190
|
width,
|
|
1167
1191
|
),
|
|
1168
1192
|
);
|