@sayknow-cli/coding-agent 0.3.12 → 0.3.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/types/autoresearch/tools/init-experiment.d.ts +1 -1
- package/dist/types/autoresearch/tools/log-experiment.d.ts +2 -2
- package/dist/types/cli/classify-install-target.d.ts +0 -10
- package/dist/types/cli/daemon-cli.d.ts +2 -0
- package/dist/types/cli/initial-message.d.ts +1 -1
- package/dist/types/cli/malloc-env-guard.d.ts +65 -0
- package/dist/types/cli/malloc-env-guard.test.d.ts +1 -0
- package/dist/types/cli/notify-cli.d.ts +3 -1
- package/dist/types/cli/session-picker.d.ts +4 -3
- package/dist/types/cli/update-cli.d.ts +22 -3
- package/dist/types/cli.d.ts +5 -0
- package/dist/types/commands/daemon.d.ts +6 -2
- package/dist/types/commands/launch.d.ts +1 -0
- package/dist/types/commands/notify.d.ts +6 -0
- package/dist/types/commands/web-search.d.ts +1 -1
- package/dist/types/commit/agentic/tools/propose-changelog.d.ts +4 -4
- package/dist/types/commit/agentic/tools/propose-commit.d.ts +9 -9
- package/dist/types/commit/agentic/tools/schemas.d.ts +9 -9
- package/dist/types/commit/agentic/tools/split-commit.d.ts +9 -9
- package/dist/types/commit/analysis/conventional.d.ts +1 -1
- package/dist/types/commit/analysis/summary.d.ts +1 -1
- package/dist/types/commit/changelog/generate.d.ts +3 -3
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/map-phase.d.ts +1 -1
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +1 -1
- package/dist/types/commit/shared-llm.d.ts +9 -9
- package/dist/types/config/keybindings.d.ts +134 -129
- package/dist/types/config/model-profile-activation.d.ts +1 -0
- package/dist/types/config/model-registry.d.ts +35 -35
- package/dist/types/config/models-config-schema.d.ts +111 -111
- package/dist/types/config/settings-schema.d.ts +56 -16
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/coordinator-mcp/model-preset.d.ts +45 -0
- package/dist/types/coordinator-mcp/policy.d.ts +3 -0
- package/dist/types/coordinator-mcp/server.d.ts +79 -6
- package/dist/types/coordinator-mcp/session-reaper.d.ts +53 -0
- package/dist/types/daemon/control-types.d.ts +10 -0
- package/dist/types/daemon/operator-contract.d.ts +47 -0
- package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/models/catalog.d.ts +4 -0
- package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/shared/errors.d.ts +12 -12
- package/dist/types/discovery/helpers.d.ts +1 -1
- package/dist/types/discovery/substitute-plugin-root.d.ts +0 -4
- package/dist/types/edit/modes/patch.d.ts +2 -2
- package/dist/types/extensibility/extensions/types.d.ts +3 -1
- package/dist/types/goals/tools/goal-tool.d.ts +6 -6
- package/dist/types/i18n/messages/en.d.ts +8 -8
- package/dist/types/internal-urls/local-protocol.d.ts +13 -9
- package/dist/types/lsp/index.d.ts +9 -9
- package/dist/types/lsp/types.d.ts +9 -9
- package/dist/types/main.d.ts +66 -2
- package/dist/types/modes/acp/acp-agent.d.ts +19 -2
- package/dist/types/modes/acp/acp-mode.d.ts +12 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -1
- package/dist/types/modes/components/custom-editor.d.ts +2 -0
- package/dist/types/modes/components/hook-selector.d.ts +5 -0
- package/dist/types/modes/components/irc-sidebar.d.ts +32 -0
- package/dist/types/modes/components/pet-selector.d.ts +11 -0
- package/dist/types/modes/components/sayknow-pet-widget.d.ts +59 -0
- package/dist/types/modes/components/session-selector.d.ts +22 -9
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/status-line/types.d.ts +1 -1
- package/dist/types/modes/components/status-line.d.ts +0 -10
- package/dist/types/modes/components/tool-execution.d.ts +1 -0
- package/dist/types/modes/components/user-message.d.ts +2 -1
- package/dist/types/modes/controllers/event-controller.d.ts +4 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +24 -2
- package/dist/types/modes/irc-observation-ledger.d.ts +22 -0
- package/dist/types/modes/print-mode.d.ts +3 -4
- package/dist/types/modes/rpc/rpc-client.d.ts +7 -1
- package/dist/types/modes/rpc/rpc-mode.d.ts +2 -1
- package/dist/types/modes/rpc/rpc-types.d.ts +17 -1
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +8 -0
- package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +5 -0
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +45 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +42 -2
- package/dist/types/modes/theme/defaults/index.d.ts +568 -568
- package/dist/types/modes/types.d.ts +28 -2
- package/dist/types/modes/utils/abort-message.d.ts +1 -1
- package/dist/types/modes/utils/context-usage.d.ts +4 -19
- package/dist/types/modes/utils/irc-message.d.ts +38 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +13 -4
- package/dist/types/notifications/config.d.ts +34 -0
- package/dist/types/notifications/daemon-paths.d.ts +7 -0
- package/dist/types/notifications/index.d.ts +27 -0
- package/dist/types/notifications/lifecycle-control-runtime.d.ts +12 -4
- package/dist/types/notifications/notification-service.d.ts +144 -0
- package/dist/types/notifications/rate-limit-pool.d.ts +27 -9
- package/dist/types/notifications/telegram-daemon-control.d.ts +6 -0
- package/dist/types/notifications/telegram-daemon.d.ts +130 -3
- package/dist/types/notifications/telegram-reference.d.ts +19 -2
- package/dist/types/notifications/topic-registry.d.ts +24 -4
- package/dist/types/registry/agent-registry.d.ts +2 -0
- package/dist/types/rlm/index.d.ts +1 -1
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +40 -3
- package/dist/types/session/cache-economics.d.ts +79 -0
- package/dist/types/session/context-estimation.d.ts +31 -0
- package/dist/types/session/provider-safety-stop.d.ts +5 -0
- package/dist/types/session/session-manager.d.ts +124 -4
- package/dist/types/session/session-storage.d.ts +155 -13
- package/dist/types/setup/credential-auto-import.d.ts +2 -2
- package/dist/types/skc-runtime/session-state-sidecar.d.ts +34 -3
- package/dist/types/skc-runtime/state-schema.d.ts +19 -19
- package/dist/types/skc-runtime/tmux-common.d.ts +6 -0
- package/dist/types/skc-runtime/tmux-owner-isolation-cli.d.ts +5 -0
- package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +309 -0
- package/dist/types/skc-runtime/tmux-sessions.d.ts +49 -17
- package/dist/types/task/agents.d.ts +2 -2
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/receipt.d.ts +3 -1
- package/dist/types/task/types.d.ts +35 -27
- package/dist/types/tools/ask-answer-registry.d.ts +5 -0
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/browser/tab-supervisor.d.ts +6 -0
- package/dist/types/tools/browser.d.ts +28 -28
- package/dist/types/tools/computer.d.ts +20 -20
- package/dist/types/tools/cron.d.ts +2 -2
- package/dist/types/tools/debug.d.ts +40 -40
- package/dist/types/tools/gh.d.ts +10 -10
- package/dist/types/tools/image-gen.d.ts +2 -2
- package/dist/types/tools/index.d.ts +58 -6
- package/dist/types/tools/monitor.d.ts +6 -6
- package/dist/types/tools/skill-discovery.d.ts +4 -4
- package/dist/types/tools/subagent.d.ts +12 -12
- package/dist/types/tools/telegram-send.d.ts +7 -0
- package/dist/types/tools/todo-write.d.ts +8 -8
- package/dist/types/utils/external-editor.d.ts +2 -0
- package/dist/types/web/insane/bridge.d.ts +9 -9
- package/dist/types/web/search/index.d.ts +4 -4
- package/package.json +8 -8
- package/scripts/g004-tmux-smoke.ts +196 -36
- package/src/cli/daemon-cli.ts +19 -29
- package/src/cli/malloc-env-guard.test.ts +104 -0
- package/src/cli/malloc-env-guard.ts +149 -0
- package/src/cli/notify-cli.ts +78 -14
- package/src/cli/session-picker.ts +23 -40
- package/src/cli/update-cli.ts +56 -37
- package/src/cli.ts +51 -10
- package/src/commands/daemon.ts +19 -5
- package/src/commands/harness.ts +515 -47
- package/src/commands/launch.ts +11 -3
- package/src/commands/notify.ts +5 -1
- package/src/commands/session.ts +6 -1
- package/src/config/keybindings.ts +5 -0
- package/src/config/model-profile-activation.ts +1 -1
- package/src/config/model-profiles.ts +14 -14
- package/src/config/settings-schema.ts +41 -1
- package/src/config/settings.ts +96 -45
- package/src/coordinator/contract.ts +1 -0
- package/src/coordinator-mcp/model-preset.ts +100 -0
- package/src/coordinator-mcp/policy.ts +30 -0
- package/src/coordinator-mcp/server.ts +2958 -430
- package/src/coordinator-mcp/session-reaper.ts +129 -0
- package/src/daemon/control-types.ts +11 -0
- package/src/daemon/operator-contract.ts +121 -0
- package/src/defaults/skc/extensions/grok-cli-vendor/src/models/catalog.ts +57 -17
- package/src/defaults/skc/extensions/grok-cli-vendor/src/payload/sanitize.ts +15 -2
- package/src/defaults/skc/extensions/grok-cli-vendor/src/provider/register.ts +5 -1
- package/src/extensibility/extensions/runner.ts +1 -1
- package/src/extensibility/extensions/types.ts +3 -1
- package/src/internal-urls/docs-index.generated.ts +10 -9
- package/src/internal-urls/local-protocol.ts +29 -10
- package/src/main.ts +301 -80
- package/src/modes/acp/acp-agent.ts +730 -100
- package/src/modes/acp/acp-mode.ts +29 -2
- package/src/modes/components/assistant-message.ts +32 -3
- package/src/modes/components/bash-execution.ts +43 -13
- package/src/modes/components/custom-editor.ts +5 -0
- package/src/modes/components/footer.ts +3 -3
- package/src/modes/components/hook-selector.ts +12 -0
- package/src/modes/components/irc-sidebar.ts +206 -0
- package/src/modes/components/pet-selector.ts +55 -0
- package/src/modes/components/sayknow-pet-widget.ts +360 -0
- package/src/modes/components/session-selector.ts +222 -221
- package/src/modes/components/settings-selector.ts +10 -0
- package/src/modes/components/status-line/segments.ts +14 -7
- package/src/modes/components/status-line/types.ts +1 -1
- package/src/modes/components/status-line.ts +4 -171
- package/src/modes/components/tool-execution.ts +47 -6
- package/src/modes/components/user-message.ts +26 -9
- package/src/modes/controllers/command-controller.ts +28 -2
- package/src/modes/controllers/event-controller.ts +159 -36
- package/src/modes/controllers/extension-ui-controller.ts +37 -18
- package/src/modes/controllers/input-controller.ts +8 -2
- package/src/modes/controllers/selector-controller.ts +62 -15
- package/src/modes/interactive-mode.ts +199 -7
- package/src/modes/irc-observation-ledger.ts +127 -0
- package/src/modes/print-mode.ts +228 -92
- package/src/modes/rpc/rpc-client.ts +58 -1
- package/src/modes/rpc/rpc-mode.ts +286 -85
- package/src/modes/rpc/rpc-types.ts +15 -1
- package/src/modes/shared/agent-wire/command-dispatch.ts +33 -1
- package/src/modes/shared/agent-wire/command-validation.ts +10 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +45 -2
- package/src/modes/shared/agent-wire/scopes.ts +1 -0
- package/src/modes/shared/agent-wire/unattended-session.ts +288 -1
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +130 -20
- package/src/modes/types.ts +34 -2
- package/src/modes/utils/context-usage.ts +56 -92
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/irc-message.ts +138 -0
- package/src/modes/utils/ui-helpers.ts +216 -50
- package/src/notifications/config.ts +44 -0
- package/src/notifications/daemon-paths.ts +8 -0
- package/src/notifications/index.ts +535 -75
- package/src/notifications/lifecycle-control-runtime.ts +930 -77
- package/src/notifications/notification-service.ts +710 -0
- package/src/notifications/rate-limit-pool.ts +46 -12
- package/src/notifications/telegram-daemon-control.ts +27 -2
- package/src/notifications/telegram-daemon.ts +635 -94
- package/src/notifications/telegram-reference.ts +73 -13
- package/src/notifications/topic-registry.ts +86 -9
- package/src/prompts/system/irc-peer-roster.md +1 -0
- package/src/prompts/system/subagent-system-prompt.md +1 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/tools/irc.md +1 -1
- package/src/registry/agent-registry.ts +3 -0
- package/src/sdk.ts +52 -1
- package/src/secrets/obfuscator.ts +4 -2
- package/src/session/agent-session.ts +670 -217
- package/src/session/cache-economics.ts +275 -0
- package/src/session/context-estimation.ts +91 -0
- package/src/session/provider-safety-stop.ts +9 -0
- package/src/session/session-dump-format.ts +6 -0
- package/src/session/session-manager.ts +809 -39
- package/src/session/session-storage.ts +582 -38
- package/src/skc-runtime/session-state-sidecar.ts +756 -135
- package/src/skc-runtime/tmux-common.ts +26 -7
- package/src/skc-runtime/tmux-gc.ts +52 -9
- package/src/skc-runtime/tmux-owner-isolation-cli.ts +185 -0
- package/src/skc-runtime/tmux-owner-isolation.ts +2060 -0
- package/src/skc-runtime/tmux-sessions.ts +871 -68
- package/src/slash-commands/builtin-registry.ts +153 -9
- package/src/slash-commands/helpers/context-report.ts +26 -4
- package/src/task/executor.ts +12 -3
- package/src/task/index.ts +8 -0
- package/src/task/receipt.ts +7 -2
- package/src/task/types.ts +26 -0
- package/src/tools/ask-answer-registry.ts +25 -0
- package/src/tools/browser/tab-supervisor.ts +63 -6
- package/src/tools/browser.ts +8 -3
- package/src/tools/index.ts +81 -6
- package/src/tools/sqlite-reader.ts +8 -4
- package/src/tui/output-block.ts +18 -6
- package/src/utils/external-editor.ts +6 -1
package/src/modes/print-mode.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - `skc --mode json "prompt"` - JSON event stream
|
|
7
7
|
*/
|
|
8
8
|
import { type AssistantMessage, type ImageContent, isContextOverflow } from "@sayknow-cli/ai";
|
|
9
|
-
import { logger, sanitizeText } from "@sayknow-cli/utils";
|
|
9
|
+
import { isKnownSinkPeerClosedError, logger, sanitizeText } from "@sayknow-cli/utils";
|
|
10
10
|
import type { AgentSession } from "../session/agent-session";
|
|
11
11
|
import { isSilentAbort } from "../session/messages";
|
|
12
12
|
import { initializeExtensions } from "./runtime-init";
|
|
@@ -24,9 +24,8 @@ export interface PrintModeOptions {
|
|
|
24
24
|
/** Images to attach to the initial message */
|
|
25
25
|
initialImages?: ImageContent[];
|
|
26
26
|
/**
|
|
27
|
-
* When true,
|
|
28
|
-
*
|
|
29
|
-
* finalization/cleanup before the process exits.
|
|
27
|
+
* When true, a terminal assistant error/abort leaves process exit status
|
|
28
|
+
* untouched so the caller can run its own finalization and status handling.
|
|
30
29
|
*/
|
|
31
30
|
suppressProcessExit?: boolean;
|
|
32
31
|
}
|
|
@@ -42,7 +41,7 @@ export interface PrintModeOptions {
|
|
|
42
41
|
* events from the subscription and does not run this terminal-error branch, so
|
|
43
42
|
* it is intentionally NOT covered by this exit code.
|
|
44
43
|
*/
|
|
45
|
-
export const CONTEXT_OVERFLOW_EXIT_CODE =
|
|
44
|
+
export const CONTEXT_OVERFLOW_EXIT_CODE = 78;
|
|
46
45
|
|
|
47
46
|
/**
|
|
48
47
|
* Build an actionable stderr diagnostic for a terminal context-overflow error in
|
|
@@ -58,113 +57,250 @@ function formatContextOverflowError(message: AssistantMessage, autoCompactionEna
|
|
|
58
57
|
return `${guidance}${providerDetail}`;
|
|
59
58
|
}
|
|
60
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Own process stdout only while print mode is actively writing. This lets this
|
|
62
|
+
* mode absorb a peer-closing its output pipe without changing error-listener
|
|
63
|
+
* ownership for the interactive/TUI and clipboard paths.
|
|
64
|
+
*/
|
|
65
|
+
function createPrintStdoutOwner(): {
|
|
66
|
+
write(chunk: string): void;
|
|
67
|
+
flush(): Promise<void>;
|
|
68
|
+
dispose(): void;
|
|
69
|
+
} {
|
|
70
|
+
let epipeLatched = false;
|
|
71
|
+
let writeError: unknown;
|
|
72
|
+
let hasWriteError = false;
|
|
73
|
+
const pendingWrites = new Set<{ done: Promise<void>; settle(): void }>();
|
|
74
|
+
|
|
75
|
+
const isEpipe = (error: unknown): boolean => {
|
|
76
|
+
try {
|
|
77
|
+
return (error as NodeJS.ErrnoException | null | undefined)?.code === "EPIPE";
|
|
78
|
+
} catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const settlePendingWrites = (): void => {
|
|
84
|
+
for (const pending of [...pendingWrites]) pending.settle();
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const handleWriteError = (error: unknown): void => {
|
|
88
|
+
// An EPIPE from this owned stdout sink proves its peer closed. A destroyed
|
|
89
|
+
// stream does not prove that independently, so it is tolerated only after
|
|
90
|
+
// this owner has already observed and latched an EPIPE.
|
|
91
|
+
if (isEpipe(error)) {
|
|
92
|
+
epipeLatched = true;
|
|
93
|
+
settlePendingWrites();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (epipeLatched && isKnownSinkPeerClosedError(error)) {
|
|
97
|
+
settlePendingWrites();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!hasWriteError) {
|
|
102
|
+
writeError = error;
|
|
103
|
+
hasWriteError = true;
|
|
104
|
+
}
|
|
105
|
+
settlePendingWrites();
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const onStdoutError = (error: Error): void => {
|
|
109
|
+
handleWriteError(error);
|
|
110
|
+
};
|
|
111
|
+
process.stdout.on("error", onStdoutError);
|
|
112
|
+
|
|
113
|
+
const waitForWrites = async (): Promise<void> => {
|
|
114
|
+
while (pendingWrites.size > 0) {
|
|
115
|
+
await Promise.all([...pendingWrites].map(pending => pending.done));
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
write(chunk: string): void {
|
|
121
|
+
if (epipeLatched || hasWriteError) return;
|
|
122
|
+
|
|
123
|
+
let settled = false;
|
|
124
|
+
const completion = Promise.withResolvers<void>();
|
|
125
|
+
let pending: { done: Promise<void>; settle(): void };
|
|
126
|
+
pending = {
|
|
127
|
+
done: completion.promise,
|
|
128
|
+
settle: (): void => {
|
|
129
|
+
if (settled) return;
|
|
130
|
+
settled = true;
|
|
131
|
+
pendingWrites.delete(pending);
|
|
132
|
+
completion.resolve();
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
const complete = (error?: Error | null): void => {
|
|
136
|
+
if (settled) return;
|
|
137
|
+
if (error) handleWriteError(error);
|
|
138
|
+
pending.settle();
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
pendingWrites.add(pending);
|
|
142
|
+
try {
|
|
143
|
+
process.stdout.write(chunk, complete);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
handleWriteError(error);
|
|
146
|
+
pending.settle();
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
async flush(): Promise<void> {
|
|
151
|
+
await waitForWrites();
|
|
152
|
+
if (!epipeLatched && !hasWriteError) {
|
|
153
|
+
this.write("");
|
|
154
|
+
await waitForWrites();
|
|
155
|
+
}
|
|
156
|
+
if (hasWriteError) throw writeError;
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
dispose(): void {
|
|
160
|
+
process.stdout.removeListener("error", onStdoutError);
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function writeStderrAndQuiesce(chunk: string): Promise<void> {
|
|
166
|
+
const completion = Promise.withResolvers<void>();
|
|
167
|
+
let settled = false;
|
|
168
|
+
const complete = (error?: unknown): void => {
|
|
169
|
+
if (settled) return;
|
|
170
|
+
settled = true;
|
|
171
|
+
if (error === undefined || error === null) completion.resolve();
|
|
172
|
+
else completion.reject(error);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
process.stderr.write(chunk, complete);
|
|
177
|
+
} catch (error) {
|
|
178
|
+
complete(error);
|
|
179
|
+
}
|
|
180
|
+
await completion.promise;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function throwCollectedErrors(errors: unknown[]): void {
|
|
184
|
+
const uniqueErrors = [...new Set(errors)];
|
|
185
|
+
if (uniqueErrors.length === 0) return;
|
|
186
|
+
if (uniqueErrors.length === 1) throw uniqueErrors[0];
|
|
187
|
+
throw new AggregateError(uniqueErrors, "Print mode failed while flushing output and disposing the session");
|
|
188
|
+
}
|
|
189
|
+
|
|
61
190
|
/**
|
|
62
191
|
* Run in print (single-shot) mode.
|
|
63
192
|
* Sends prompts to the agent and outputs the result.
|
|
64
193
|
*/
|
|
65
194
|
export async function runPrintMode(session: AgentSession, options: PrintModeOptions): Promise<void> {
|
|
66
195
|
const { mode, messages = [], initialMessage, initialImages } = options;
|
|
196
|
+
const stdout = createPrintStdoutOwner();
|
|
197
|
+
const failures: unknown[] = [];
|
|
198
|
+
let unsubscribe: (() => void) | undefined;
|
|
67
199
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
200
|
+
try {
|
|
201
|
+
// Emit session header for JSON mode.
|
|
202
|
+
if (mode === "json") {
|
|
203
|
+
const header = session.sessionManager.getHeader();
|
|
204
|
+
if (header) stdout.write(`${JSON.stringify(header)}\n`);
|
|
73
205
|
}
|
|
74
|
-
}
|
|
75
|
-
// Set up extensions for print mode (no UI, no command context)
|
|
76
|
-
await initializeExtensions(session, {
|
|
77
|
-
reportSendError: (action, err) => {
|
|
78
|
-
process.stderr.write(
|
|
79
|
-
`Extension ${action === "extension_send" ? "sendMessage" : "sendUserMessage"} failed: ${err.message}\n`,
|
|
80
|
-
);
|
|
81
|
-
},
|
|
82
|
-
reportRuntimeError: err => {
|
|
83
|
-
process.stderr.write(`Extension error (${err.extensionPath}): ${err.error}\n`);
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
206
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
207
|
+
// Set up extensions for print mode (no UI, no command context).
|
|
208
|
+
await initializeExtensions(session, {
|
|
209
|
+
reportSendError: (action, err) => {
|
|
210
|
+
process.stderr.write(
|
|
211
|
+
`Extension ${action === "extension_send" ? "sendMessage" : "sendUserMessage"} failed: ${err.message}\n`,
|
|
212
|
+
);
|
|
213
|
+
},
|
|
214
|
+
reportRuntimeError: err => {
|
|
215
|
+
process.stderr.write(`Extension error (${err.extensionPath}): ${err.error}\n`);
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// AgentSession persists events internally. Print mode only needs a listener
|
|
220
|
+
// when it must render the JSON event stream.
|
|
90
221
|
if (mode === "json") {
|
|
91
|
-
|
|
222
|
+
unsubscribe = session.subscribe(event => {
|
|
223
|
+
stdout.write(`${JSON.stringify(event)}\n`);
|
|
224
|
+
});
|
|
92
225
|
}
|
|
93
|
-
});
|
|
94
226
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
227
|
+
// Send initial message with attachments.
|
|
228
|
+
if (initialMessage !== undefined) {
|
|
229
|
+
await logger.time("print:prompt:initial", () => session.prompt(initialMessage, { images: initialImages }));
|
|
230
|
+
}
|
|
99
231
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
232
|
+
// Send remaining messages.
|
|
233
|
+
for (const message of messages) {
|
|
234
|
+
await logger.time("print:prompt:next", () => session.prompt(message));
|
|
235
|
+
}
|
|
104
236
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const errorLine = isOverflow
|
|
128
|
-
? formatContextOverflowError(assistantMsg, session.autoCompactionEnabled)
|
|
129
|
-
: sanitizeText(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);
|
|
130
|
-
const exitCode = isOverflow ? CONTEXT_OVERFLOW_EXIT_CODE : 1;
|
|
131
|
-
const flushed = process.stderr.write(`${errorLine}\n`);
|
|
132
|
-
// When the caller owns finalization (RLM autonomous), return instead of
|
|
133
|
-
// exiting so its cleanup runs; the caller surfaces a non-zero exit itself.
|
|
134
|
-
if (!options.suppressProcessExit) {
|
|
135
|
-
if (flushed) {
|
|
136
|
-
process.exit(exitCode);
|
|
137
|
-
} else {
|
|
138
|
-
process.stderr.once("drain", () => process.exit(exitCode));
|
|
139
|
-
}
|
|
237
|
+
// In text mode, output final response.
|
|
238
|
+
if (mode === "text") {
|
|
239
|
+
const lastMessage = session.state.messages.findLast(message => message.role === "assistant");
|
|
240
|
+
if (lastMessage?.role === "assistant") {
|
|
241
|
+
const assistantMsg = lastMessage as AssistantMessage;
|
|
242
|
+
let printContent = true;
|
|
243
|
+
|
|
244
|
+
// Check for error/aborted — skip silent-abort (plan-mode compaction transition).
|
|
245
|
+
if (
|
|
246
|
+
(assistantMsg.stopReason === "error" || assistantMsg.stopReason === "aborted") &&
|
|
247
|
+
!isSilentAbort(assistantMsg.errorMessage)
|
|
248
|
+
) {
|
|
249
|
+
const isOverflow =
|
|
250
|
+
assistantMsg.stopReason === "error" && isContextOverflow(assistantMsg, session.model?.contextWindow);
|
|
251
|
+
const errorLine = isOverflow
|
|
252
|
+
? formatContextOverflowError(assistantMsg, session.autoCompactionEnabled)
|
|
253
|
+
: sanitizeText(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);
|
|
254
|
+
const exitCode = isOverflow ? CONTEXT_OVERFLOW_EXIT_CODE : 1;
|
|
255
|
+
|
|
256
|
+
if (!options.suppressProcessExit) process.exitCode = exitCode;
|
|
257
|
+
await writeStderrAndQuiesce(`${errorLine}\n`);
|
|
258
|
+
printContent = false;
|
|
140
259
|
}
|
|
141
|
-
}
|
|
142
260
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
261
|
+
if (
|
|
262
|
+
assistantMsg.errorMessage &&
|
|
263
|
+
assistantMsg.stopReason !== "error" &&
|
|
264
|
+
assistantMsg.stopReason !== "aborted"
|
|
265
|
+
) {
|
|
266
|
+
await writeStderrAndQuiesce(`${sanitizeText(assistantMsg.errorMessage)}\n`);
|
|
267
|
+
}
|
|
150
268
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
269
|
+
if (printContent) {
|
|
270
|
+
for (const content of assistantMsg.content) {
|
|
271
|
+
if (content.type === "text") stdout.write(`${sanitizeText(content.text)}\n`);
|
|
272
|
+
}
|
|
155
273
|
}
|
|
156
274
|
}
|
|
157
275
|
}
|
|
158
|
-
}
|
|
159
276
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
277
|
+
// Observe callback and stream errors from every preceding print-mode write.
|
|
278
|
+
await stdout.flush();
|
|
279
|
+
} catch (error) {
|
|
280
|
+
failures.push(error);
|
|
281
|
+
} finally {
|
|
282
|
+
// The JSON subscriber remains live while disposal emits its final events.
|
|
283
|
+
try {
|
|
284
|
+
await session.dispose();
|
|
285
|
+
} catch (error) {
|
|
286
|
+
failures.push(error);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Disposal may have written JSON or delivered a late stdout error.
|
|
290
|
+
try {
|
|
291
|
+
await stdout.flush();
|
|
292
|
+
} catch (error) {
|
|
293
|
+
failures.push(error);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
try {
|
|
297
|
+
unsubscribe?.();
|
|
298
|
+
} catch (error) {
|
|
299
|
+
failures.push(error);
|
|
300
|
+
} finally {
|
|
301
|
+
stdout.dispose();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
168
304
|
|
|
169
|
-
|
|
305
|
+
throwCollectedErrors(failures);
|
|
170
306
|
}
|
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Spawns the agent in RPC mode and provides a typed API for all operations.
|
|
5
5
|
*/
|
|
6
|
-
import
|
|
6
|
+
import {
|
|
7
|
+
type AgentEvent,
|
|
8
|
+
type AgentMessage,
|
|
9
|
+
type AgentToolResult,
|
|
10
|
+
type ResolvedThinkingLevel,
|
|
11
|
+
ThinkingLevel,
|
|
12
|
+
} from "@sayknow-cli/agent-core";
|
|
7
13
|
import type { CompactionResult } from "@sayknow-cli/agent-core/compaction";
|
|
8
14
|
import type { ImageContent, Model } from "@sayknow-cli/ai";
|
|
9
15
|
import { isRecord, ptree, readJsonl } from "@sayknow-cli/utils";
|
|
@@ -64,6 +70,12 @@ export interface RpcClientOptions {
|
|
|
64
70
|
|
|
65
71
|
export type ModelInfo = Pick<Model, "provider" | "id" | "contextWindow" | "reasoning" | "thinking">;
|
|
66
72
|
|
|
73
|
+
export type RpcDefaultModelSelection = {
|
|
74
|
+
readonly provider: string;
|
|
75
|
+
readonly modelId: string;
|
|
76
|
+
readonly thinkingLevel: ResolvedThinkingLevel;
|
|
77
|
+
};
|
|
78
|
+
|
|
67
79
|
export type RpcEventListener = (event: AgentEvent) => void;
|
|
68
80
|
export type RpcSessionEventListener = (event: AgentSessionEvent) => void;
|
|
69
81
|
|
|
@@ -94,6 +106,7 @@ export function defineRpcClientTool<
|
|
|
94
106
|
}
|
|
95
107
|
|
|
96
108
|
const agentWireEventTypes = new Set<AgentWireEventType>(AGENT_WIRE_EVENT_TYPES);
|
|
109
|
+
const DEFAULT_MODEL_SELECTION_TIMEOUT_MS = 600_000;
|
|
97
110
|
const coreAgentEventTypes = new Set<AgentEvent["type"]>([
|
|
98
111
|
"agent_start",
|
|
99
112
|
"agent_end",
|
|
@@ -107,6 +120,29 @@ const coreAgentEventTypes = new Set<AgentEvent["type"]>([
|
|
|
107
120
|
"tool_execution_end",
|
|
108
121
|
]);
|
|
109
122
|
|
|
123
|
+
function isResolvedThinkingLevel(value: unknown): value is ResolvedThinkingLevel {
|
|
124
|
+
return (
|
|
125
|
+
value === ThinkingLevel.Off ||
|
|
126
|
+
value === ThinkingLevel.Minimal ||
|
|
127
|
+
value === ThinkingLevel.Low ||
|
|
128
|
+
value === ThinkingLevel.Medium ||
|
|
129
|
+
value === ThinkingLevel.High ||
|
|
130
|
+
value === ThinkingLevel.XHigh ||
|
|
131
|
+
value === ThinkingLevel.Max
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function isDefaultModelSelection(value: unknown): value is RpcDefaultModelSelection {
|
|
136
|
+
return (
|
|
137
|
+
isRecord(value) &&
|
|
138
|
+
typeof value.provider === "string" &&
|
|
139
|
+
value.provider.trim().length > 0 &&
|
|
140
|
+
typeof value.modelId === "string" &&
|
|
141
|
+
value.modelId.trim().length > 0 &&
|
|
142
|
+
isResolvedThinkingLevel(value.thinkingLevel)
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
110
146
|
function isRpcResponse(value: unknown): value is RpcResponse {
|
|
111
147
|
if (!isRecord(value)) return false;
|
|
112
148
|
if (value.type !== "response") return false;
|
|
@@ -626,6 +662,27 @@ export class RpcClient {
|
|
|
626
662
|
return this.#getData(response);
|
|
627
663
|
}
|
|
628
664
|
|
|
665
|
+
async setDefaultModelSelection(
|
|
666
|
+
provider: string,
|
|
667
|
+
modelId: string,
|
|
668
|
+
thinkingLevel: ResolvedThinkingLevel,
|
|
669
|
+
): Promise<RpcDefaultModelSelection> {
|
|
670
|
+
const response = await this.#send(
|
|
671
|
+
{
|
|
672
|
+
type: "set_default_model_selection",
|
|
673
|
+
provider,
|
|
674
|
+
modelId,
|
|
675
|
+
thinkingLevel,
|
|
676
|
+
},
|
|
677
|
+
DEFAULT_MODEL_SELECTION_TIMEOUT_MS,
|
|
678
|
+
);
|
|
679
|
+
if (!response.success) return this.#getData(response);
|
|
680
|
+
if (response.command !== "set_default_model_selection" || !isDefaultModelSelection(response.data)) {
|
|
681
|
+
throw new Error("Invalid set_default_model_selection response");
|
|
682
|
+
}
|
|
683
|
+
return this.#getData(response);
|
|
684
|
+
}
|
|
685
|
+
|
|
629
686
|
/**
|
|
630
687
|
* Cycle to next model.
|
|
631
688
|
*/
|