@leanandmean/coding-agent 0.74.1-scramjet.72 → 0.74.1-scramjet.74
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/README.md +8 -1
- package/dist/core/agent-session.d.ts +19 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +293 -68
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +5 -3
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +18 -5
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +9 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +7 -3
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +155 -34
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +35 -16
- package/dist/modes/print-mode.js.map +1 -1
- package/docs/compaction.md +6 -0
- package/docs/custom-provider.md +3 -1
- package/docs/extensions.md +13 -1
- package/docs/keybindings.md +1 -1
- package/docs/sdk.md +27 -0
- package/docs/settings.md +2 -0
- package/docs/terminal-setup.md +8 -4
- package/docs/terminal-verification.md +48 -0
- package/docs/tui.md +8 -0
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"print-mode.d.ts","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"print-mode.d.ts","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAI5E;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,yEAAyE;IACzE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,wDAAwD;IACxD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,mBAAmB,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyJ/G","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `pi -p \"prompt\"` - text output\n * - `pi --mode json \"prompt\"` - JSON event stream\n */\n\nimport type { ImageContent } from \"@leanandmean/ai\";\nimport type { RunSettlement } from \"../core/agent-session.js\";\nimport type { AgentSessionRuntime } from \"../core/agent-session-runtime.js\";\nimport { flushRawStdout, writeRawStdout } from \"../core/output-guard.js\";\nimport { killTrackedDetachedChildren } from \"../utils/shell.js\";\n\n/**\n * Options for print mode.\n */\nexport interface PrintModeOptions {\n\t/** Output mode: \"text\" for final response only, \"json\" for all events */\n\tmode: \"text\" | \"json\";\n\t/** Array of additional prompts to send after initialMessage */\n\tmessages?: string[];\n\t/** First message to send (may contain @file content) */\n\tinitialMessage?: string;\n\t/** Images to attach to the initial message */\n\tinitialImages?: ImageContent[];\n}\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n */\nexport async function runPrintMode(runtimeHost: AgentSessionRuntime, options: PrintModeOptions): Promise<number> {\n\tconst { mode, messages = [], initialMessage, initialImages } = options;\n\tlet exitCode = 0;\n\tlet outcome: RunSettlement | undefined;\n\tlet session = runtimeHost.session;\n\tlet unsubscribe: (() => void) | undefined;\n\tlet disposed = false;\n\tconst signalCleanupHandlers: Array<() => void> = [];\n\n\tconst disposeRuntime = async (): Promise<void> => {\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\tunsubscribe?.();\n\t\tawait runtimeHost.dispose();\n\t};\n\n\tconst registerSignalHandlers = (): void => {\n\t\tconst signals: NodeJS.Signals[] = [\"SIGTERM\"];\n\t\tif (process.platform !== \"win32\") {\n\t\t\tsignals.push(\"SIGHUP\");\n\t\t}\n\n\t\tfor (const signal of signals) {\n\t\t\tconst handler = () => {\n\t\t\t\tkillTrackedDetachedChildren();\n\t\t\t\tvoid disposeRuntime().finally(() => {\n\t\t\t\t\tprocess.exit(signal === \"SIGHUP\" ? 129 : 143);\n\t\t\t\t});\n\t\t\t};\n\t\t\tprocess.on(signal, handler);\n\t\t\tsignalCleanupHandlers.push(() => process.off(signal, handler));\n\t\t}\n\t};\n\n\tregisterSignalHandlers();\n\n\truntimeHost.setRebindSession(async () => {\n\t\tawait rebindSession();\n\t});\n\n\tconst rebindSession = async (): Promise<void> => {\n\t\tsession = runtimeHost.session;\n\t\tawait session.bindExtensions({\n\t\t\tcommandContextActions: {\n\t\t\t\twaitForIdle: () => session.agent.waitForIdle(),\n\t\t\t\tnewSession: async (newSessionOptions) => runtimeHost.newSession(newSessionOptions),\n\t\t\t\tfork: async (entryId, forkOptions) => {\n\t\t\t\t\tconst result = await runtimeHost.fork(entryId, forkOptions);\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tnavigateTree: async (targetId, navigateOptions) => {\n\t\t\t\t\tconst result = await session.navigateTree(targetId, {\n\t\t\t\t\t\tsummarize: navigateOptions?.summarize,\n\t\t\t\t\t\tcustomInstructions: navigateOptions?.customInstructions,\n\t\t\t\t\t\treplaceInstructions: navigateOptions?.replaceInstructions,\n\t\t\t\t\t\tlabel: navigateOptions?.label,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tswitchSession: async (sessionPath, switchOptions) => {\n\t\t\t\t\treturn runtimeHost.switchSession(sessionPath, switchOptions);\n\t\t\t\t},\n\t\t\t\treload: async () => {\n\t\t\t\t\tawait session.reload();\n\t\t\t\t},\n\t\t\t},\n\t\t\tonError: (err) => {\n\t\t\t\tconsole.error(`Extension error (${err.extensionPath}): ${err.error}`);\n\t\t\t},\n\t\t});\n\n\t\tunsubscribe?.();\n\t\tunsubscribe = session.subscribe((event) => {\n\t\t\tif (mode === \"json\") {\n\t\t\t\twriteRawStdout(`${JSON.stringify(event)}\\n`);\n\t\t\t}\n\t\t});\n\t};\n\n\ttry {\n\t\tif (mode === \"json\") {\n\t\t\tconst header = session.sessionManager.getHeader();\n\t\t\tif (header) {\n\t\t\t\twriteRawStdout(`${JSON.stringify(header)}\\n`);\n\t\t\t}\n\t\t}\n\n\t\tawait rebindSession();\n\n\t\t// SCRAMJET-DIVERGENCE: select the invocation's result, never mutable live history.\n\t\tconst prompt = async (text: string, images?: ImageContent[]) => {\n\t\t\toutcome = undefined;\n\t\t\tif (mode === \"json\") {\n\t\t\t\tawait session.prompt(text, { images });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet captured: Promise<RunSettlement> | undefined;\n\t\t\tawait session.prompt(text, {\n\t\t\t\timages,\n\t\t\t\tonRunSettlement(settlement) {\n\t\t\t\t\tcaptured = settlement;\n\t\t\t\t\tvoid settlement.catch(() => {});\n\t\t\t\t},\n\t\t\t});\n\t\t\tif (captured) outcome = await captured;\n\t\t};\n\t\tif (initialMessage) {\n\t\t\tawait prompt(initialMessage, initialImages);\n\t\t}\n\n\t\tfor (const message of messages) {\n\t\t\tawait prompt(message);\n\t\t}\n\n\t\tif (mode === \"text\") {\n\t\t\tconst assistantMsg = outcome?.assistantMessage;\n\t\t\tif (outcome && outcome.status !== \"completed\") {\n\t\t\t\tconsole.error(\n\t\t\t\t\toutcome.status === \"cancelled\"\n\t\t\t\t\t\t? \"Execution cancelled. Review interrupted work before submitting a new prompt.\"\n\t\t\t\t\t\t: `Execution failed: ${outcome.errorMessage}. Review the failure before trying again.`,\n\t\t\t\t);\n\t\t\t\texitCode = 1;\n\t\t\t}\n\t\t\tif (assistantMsg) {\n\t\t\t\tif (outcome?.status === \"failed\") {\n\t\t\t\t\tconst snapshot = assistantMsg.diagnostics?.find(\n\t\t\t\t\t\t(diagnostic) => diagnostic.type === \"provider_failure_snapshot\",\n\t\t\t\t\t)?.details?.text;\n\t\t\t\t\tif (typeof snapshot === \"string\") console.error(`Error details (local):\\n${snapshot}`);\n\t\t\t\t} else if (outcome?.status === \"completed\") {\n\t\t\t\t\tfor (const content of assistantMsg.content) {\n\t\t\t\t\t\tif (content.type === \"text\") {\n\t\t\t\t\t\t\twriteRawStdout(`${content.text}\\n`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn exitCode;\n\t} catch (error: unknown) {\n\t\tconsole.error(\n\t\t\t`${error instanceof Error ? error.message : String(error)}. Review the failure before trying again.`,\n\t\t);\n\t\treturn 1;\n\t} finally {\n\t\tfor (const cleanup of signalCleanupHandlers) {\n\t\t\tcleanup();\n\t\t}\n\t\tawait disposeRuntime();\n\t\tawait flushRawStdout();\n\t}\n}\n"]}
|
package/dist/modes/print-mode.js
CHANGED
|
@@ -14,6 +14,7 @@ import { killTrackedDetachedChildren } from "../utils/shell.js";
|
|
|
14
14
|
export async function runPrintMode(runtimeHost, options) {
|
|
15
15
|
const { mode, messages = [], initialMessage, initialImages } = options;
|
|
16
16
|
let exitCode = 0;
|
|
17
|
+
let outcome;
|
|
17
18
|
let session = runtimeHost.session;
|
|
18
19
|
let unsubscribe;
|
|
19
20
|
let disposed = false;
|
|
@@ -90,27 +91,45 @@ export async function runPrintMode(runtimeHost, options) {
|
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
93
|
await rebindSession();
|
|
94
|
+
// SCRAMJET-DIVERGENCE: select the invocation's result, never mutable live history.
|
|
95
|
+
const prompt = async (text, images) => {
|
|
96
|
+
outcome = undefined;
|
|
97
|
+
if (mode === "json") {
|
|
98
|
+
await session.prompt(text, { images });
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
let captured;
|
|
102
|
+
await session.prompt(text, {
|
|
103
|
+
images,
|
|
104
|
+
onRunSettlement(settlement) {
|
|
105
|
+
captured = settlement;
|
|
106
|
+
void settlement.catch(() => { });
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
if (captured)
|
|
110
|
+
outcome = await captured;
|
|
111
|
+
};
|
|
93
112
|
if (initialMessage) {
|
|
94
|
-
await
|
|
113
|
+
await prompt(initialMessage, initialImages);
|
|
95
114
|
}
|
|
96
115
|
for (const message of messages) {
|
|
97
|
-
await
|
|
116
|
+
await prompt(message);
|
|
98
117
|
}
|
|
99
118
|
if (mode === "text") {
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
119
|
+
const assistantMsg = outcome?.assistantMessage;
|
|
120
|
+
if (outcome && outcome.status !== "completed") {
|
|
121
|
+
console.error(outcome.status === "cancelled"
|
|
122
|
+
? "Execution cancelled. Review interrupted work before submitting a new prompt."
|
|
123
|
+
: `Execution failed: ${outcome.errorMessage}. Review the failure before trying again.`);
|
|
124
|
+
exitCode = 1;
|
|
125
|
+
}
|
|
126
|
+
if (assistantMsg) {
|
|
127
|
+
if (outcome?.status === "failed") {
|
|
128
|
+
const snapshot = assistantMsg.diagnostics?.find((diagnostic) => diagnostic.type === "provider_failure_snapshot")?.details?.text;
|
|
129
|
+
if (typeof snapshot === "string")
|
|
130
|
+
console.error(`Error details (local):\n${snapshot}`);
|
|
112
131
|
}
|
|
113
|
-
else {
|
|
132
|
+
else if (outcome?.status === "completed") {
|
|
114
133
|
for (const content of assistantMsg.content) {
|
|
115
134
|
if (content.type === "text") {
|
|
116
135
|
writeRawStdout(`${content.text}\n`);
|
|
@@ -122,7 +141,7 @@ export async function runPrintMode(runtimeHost, options) {
|
|
|
122
141
|
return exitCode;
|
|
123
142
|
}
|
|
124
143
|
catch (error) {
|
|
125
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
144
|
+
console.error(`${error instanceof Error ? error.message : String(error)}. Review the failure before trying again.`);
|
|
126
145
|
return 1;
|
|
127
146
|
}
|
|
128
147
|
finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"print-mode.js","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `pi -p \"prompt\"` - text output\n * - `pi --mode json \"prompt\"` - JSON event stream\n */\n\nimport type { AssistantMessage, ImageContent } from \"@leanandmean/ai\";\nimport type { AgentSessionRuntime } from \"../core/agent-session-runtime.js\";\nimport { flushRawStdout, writeRawStdout } from \"../core/output-guard.js\";\nimport { killTrackedDetachedChildren } from \"../utils/shell.js\";\n\n/**\n * Options for print mode.\n */\nexport interface PrintModeOptions {\n\t/** Output mode: \"text\" for final response only, \"json\" for all events */\n\tmode: \"text\" | \"json\";\n\t/** Array of additional prompts to send after initialMessage */\n\tmessages?: string[];\n\t/** First message to send (may contain @file content) */\n\tinitialMessage?: string;\n\t/** Images to attach to the initial message */\n\tinitialImages?: ImageContent[];\n}\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n */\nexport async function runPrintMode(runtimeHost: AgentSessionRuntime, options: PrintModeOptions): Promise<number> {\n\tconst { mode, messages = [], initialMessage, initialImages } = options;\n\tlet exitCode = 0;\n\tlet session = runtimeHost.session;\n\tlet unsubscribe: (() => void) | undefined;\n\tlet disposed = false;\n\tconst signalCleanupHandlers: Array<() => void> = [];\n\n\tconst disposeRuntime = async (): Promise<void> => {\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\tunsubscribe?.();\n\t\tawait runtimeHost.dispose();\n\t};\n\n\tconst registerSignalHandlers = (): void => {\n\t\tconst signals: NodeJS.Signals[] = [\"SIGTERM\"];\n\t\tif (process.platform !== \"win32\") {\n\t\t\tsignals.push(\"SIGHUP\");\n\t\t}\n\n\t\tfor (const signal of signals) {\n\t\t\tconst handler = () => {\n\t\t\t\tkillTrackedDetachedChildren();\n\t\t\t\tvoid disposeRuntime().finally(() => {\n\t\t\t\t\tprocess.exit(signal === \"SIGHUP\" ? 129 : 143);\n\t\t\t\t});\n\t\t\t};\n\t\t\tprocess.on(signal, handler);\n\t\t\tsignalCleanupHandlers.push(() => process.off(signal, handler));\n\t\t}\n\t};\n\n\tregisterSignalHandlers();\n\n\truntimeHost.setRebindSession(async () => {\n\t\tawait rebindSession();\n\t});\n\n\tconst rebindSession = async (): Promise<void> => {\n\t\tsession = runtimeHost.session;\n\t\tawait session.bindExtensions({\n\t\t\tcommandContextActions: {\n\t\t\t\twaitForIdle: () => session.agent.waitForIdle(),\n\t\t\t\tnewSession: async (newSessionOptions) => runtimeHost.newSession(newSessionOptions),\n\t\t\t\tfork: async (entryId, forkOptions) => {\n\t\t\t\t\tconst result = await runtimeHost.fork(entryId, forkOptions);\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tnavigateTree: async (targetId, navigateOptions) => {\n\t\t\t\t\tconst result = await session.navigateTree(targetId, {\n\t\t\t\t\t\tsummarize: navigateOptions?.summarize,\n\t\t\t\t\t\tcustomInstructions: navigateOptions?.customInstructions,\n\t\t\t\t\t\treplaceInstructions: navigateOptions?.replaceInstructions,\n\t\t\t\t\t\tlabel: navigateOptions?.label,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tswitchSession: async (sessionPath, switchOptions) => {\n\t\t\t\t\treturn runtimeHost.switchSession(sessionPath, switchOptions);\n\t\t\t\t},\n\t\t\t\treload: async () => {\n\t\t\t\t\tawait session.reload();\n\t\t\t\t},\n\t\t\t},\n\t\t\tonError: (err) => {\n\t\t\t\tconsole.error(`Extension error (${err.extensionPath}): ${err.error}`);\n\t\t\t},\n\t\t});\n\n\t\tunsubscribe?.();\n\t\tunsubscribe = session.subscribe((event) => {\n\t\t\tif (mode === \"json\") {\n\t\t\t\twriteRawStdout(`${JSON.stringify(event)}\\n`);\n\t\t\t}\n\t\t});\n\t};\n\n\ttry {\n\t\tif (mode === \"json\") {\n\t\t\tconst header = session.sessionManager.getHeader();\n\t\t\tif (header) {\n\t\t\t\twriteRawStdout(`${JSON.stringify(header)}\\n`);\n\t\t\t}\n\t\t}\n\n\t\tawait rebindSession();\n\n\t\tif (initialMessage) {\n\t\t\tawait session.prompt(initialMessage, { images: initialImages });\n\t\t}\n\n\t\tfor (const message of messages) {\n\t\t\tawait session.prompt(message);\n\t\t}\n\n\t\tif (mode === \"text\") {\n\t\t\tconst state = session.state;\n\t\t\tconst lastMessage = state.messages[state.messages.length - 1];\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = lastMessage as AssistantMessage;\n\t\t\t\tif (assistantMsg.stopReason === \"error\" || assistantMsg.stopReason === \"aborted\") {\n\t\t\t\t\tconsole.error(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);\n\t\t\t\t\tif (assistantMsg.stopReason === \"error\") {\n\t\t\t\t\t\tconst snapshot = assistantMsg.diagnostics?.find(\n\t\t\t\t\t\t\t(diagnostic) => diagnostic.type === \"provider_failure_snapshot\",\n\t\t\t\t\t\t)?.details?.text;\n\t\t\t\t\t\tif (typeof snapshot === \"string\") console.error(`Error details (local):\\n${snapshot}`);\n\t\t\t\t\t}\n\t\t\t\t\texitCode = 1;\n\t\t\t\t} else {\n\t\t\t\t\tfor (const content of assistantMsg.content) {\n\t\t\t\t\t\tif (content.type === \"text\") {\n\t\t\t\t\t\t\twriteRawStdout(`${content.text}\\n`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn exitCode;\n\t} catch (error: unknown) {\n\t\tconsole.error(error instanceof Error ? error.message : String(error));\n\t\treturn 1;\n\t} finally {\n\t\tfor (const cleanup of signalCleanupHandlers) {\n\t\t\tcleanup();\n\t\t}\n\t\tawait disposeRuntime();\n\t\tawait flushRawStdout();\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"print-mode.js","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `pi -p \"prompt\"` - text output\n * - `pi --mode json \"prompt\"` - JSON event stream\n */\n\nimport type { ImageContent } from \"@leanandmean/ai\";\nimport type { RunSettlement } from \"../core/agent-session.js\";\nimport type { AgentSessionRuntime } from \"../core/agent-session-runtime.js\";\nimport { flushRawStdout, writeRawStdout } from \"../core/output-guard.js\";\nimport { killTrackedDetachedChildren } from \"../utils/shell.js\";\n\n/**\n * Options for print mode.\n */\nexport interface PrintModeOptions {\n\t/** Output mode: \"text\" for final response only, \"json\" for all events */\n\tmode: \"text\" | \"json\";\n\t/** Array of additional prompts to send after initialMessage */\n\tmessages?: string[];\n\t/** First message to send (may contain @file content) */\n\tinitialMessage?: string;\n\t/** Images to attach to the initial message */\n\tinitialImages?: ImageContent[];\n}\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n */\nexport async function runPrintMode(runtimeHost: AgentSessionRuntime, options: PrintModeOptions): Promise<number> {\n\tconst { mode, messages = [], initialMessage, initialImages } = options;\n\tlet exitCode = 0;\n\tlet outcome: RunSettlement | undefined;\n\tlet session = runtimeHost.session;\n\tlet unsubscribe: (() => void) | undefined;\n\tlet disposed = false;\n\tconst signalCleanupHandlers: Array<() => void> = [];\n\n\tconst disposeRuntime = async (): Promise<void> => {\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\tunsubscribe?.();\n\t\tawait runtimeHost.dispose();\n\t};\n\n\tconst registerSignalHandlers = (): void => {\n\t\tconst signals: NodeJS.Signals[] = [\"SIGTERM\"];\n\t\tif (process.platform !== \"win32\") {\n\t\t\tsignals.push(\"SIGHUP\");\n\t\t}\n\n\t\tfor (const signal of signals) {\n\t\t\tconst handler = () => {\n\t\t\t\tkillTrackedDetachedChildren();\n\t\t\t\tvoid disposeRuntime().finally(() => {\n\t\t\t\t\tprocess.exit(signal === \"SIGHUP\" ? 129 : 143);\n\t\t\t\t});\n\t\t\t};\n\t\t\tprocess.on(signal, handler);\n\t\t\tsignalCleanupHandlers.push(() => process.off(signal, handler));\n\t\t}\n\t};\n\n\tregisterSignalHandlers();\n\n\truntimeHost.setRebindSession(async () => {\n\t\tawait rebindSession();\n\t});\n\n\tconst rebindSession = async (): Promise<void> => {\n\t\tsession = runtimeHost.session;\n\t\tawait session.bindExtensions({\n\t\t\tcommandContextActions: {\n\t\t\t\twaitForIdle: () => session.agent.waitForIdle(),\n\t\t\t\tnewSession: async (newSessionOptions) => runtimeHost.newSession(newSessionOptions),\n\t\t\t\tfork: async (entryId, forkOptions) => {\n\t\t\t\t\tconst result = await runtimeHost.fork(entryId, forkOptions);\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tnavigateTree: async (targetId, navigateOptions) => {\n\t\t\t\t\tconst result = await session.navigateTree(targetId, {\n\t\t\t\t\t\tsummarize: navigateOptions?.summarize,\n\t\t\t\t\t\tcustomInstructions: navigateOptions?.customInstructions,\n\t\t\t\t\t\treplaceInstructions: navigateOptions?.replaceInstructions,\n\t\t\t\t\t\tlabel: navigateOptions?.label,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tswitchSession: async (sessionPath, switchOptions) => {\n\t\t\t\t\treturn runtimeHost.switchSession(sessionPath, switchOptions);\n\t\t\t\t},\n\t\t\t\treload: async () => {\n\t\t\t\t\tawait session.reload();\n\t\t\t\t},\n\t\t\t},\n\t\t\tonError: (err) => {\n\t\t\t\tconsole.error(`Extension error (${err.extensionPath}): ${err.error}`);\n\t\t\t},\n\t\t});\n\n\t\tunsubscribe?.();\n\t\tunsubscribe = session.subscribe((event) => {\n\t\t\tif (mode === \"json\") {\n\t\t\t\twriteRawStdout(`${JSON.stringify(event)}\\n`);\n\t\t\t}\n\t\t});\n\t};\n\n\ttry {\n\t\tif (mode === \"json\") {\n\t\t\tconst header = session.sessionManager.getHeader();\n\t\t\tif (header) {\n\t\t\t\twriteRawStdout(`${JSON.stringify(header)}\\n`);\n\t\t\t}\n\t\t}\n\n\t\tawait rebindSession();\n\n\t\t// SCRAMJET-DIVERGENCE: select the invocation's result, never mutable live history.\n\t\tconst prompt = async (text: string, images?: ImageContent[]) => {\n\t\t\toutcome = undefined;\n\t\t\tif (mode === \"json\") {\n\t\t\t\tawait session.prompt(text, { images });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet captured: Promise<RunSettlement> | undefined;\n\t\t\tawait session.prompt(text, {\n\t\t\t\timages,\n\t\t\t\tonRunSettlement(settlement) {\n\t\t\t\t\tcaptured = settlement;\n\t\t\t\t\tvoid settlement.catch(() => {});\n\t\t\t\t},\n\t\t\t});\n\t\t\tif (captured) outcome = await captured;\n\t\t};\n\t\tif (initialMessage) {\n\t\t\tawait prompt(initialMessage, initialImages);\n\t\t}\n\n\t\tfor (const message of messages) {\n\t\t\tawait prompt(message);\n\t\t}\n\n\t\tif (mode === \"text\") {\n\t\t\tconst assistantMsg = outcome?.assistantMessage;\n\t\t\tif (outcome && outcome.status !== \"completed\") {\n\t\t\t\tconsole.error(\n\t\t\t\t\toutcome.status === \"cancelled\"\n\t\t\t\t\t\t? \"Execution cancelled. Review interrupted work before submitting a new prompt.\"\n\t\t\t\t\t\t: `Execution failed: ${outcome.errorMessage}. Review the failure before trying again.`,\n\t\t\t\t);\n\t\t\t\texitCode = 1;\n\t\t\t}\n\t\t\tif (assistantMsg) {\n\t\t\t\tif (outcome?.status === \"failed\") {\n\t\t\t\t\tconst snapshot = assistantMsg.diagnostics?.find(\n\t\t\t\t\t\t(diagnostic) => diagnostic.type === \"provider_failure_snapshot\",\n\t\t\t\t\t)?.details?.text;\n\t\t\t\t\tif (typeof snapshot === \"string\") console.error(`Error details (local):\\n${snapshot}`);\n\t\t\t\t} else if (outcome?.status === \"completed\") {\n\t\t\t\t\tfor (const content of assistantMsg.content) {\n\t\t\t\t\t\tif (content.type === \"text\") {\n\t\t\t\t\t\t\twriteRawStdout(`${content.text}\\n`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn exitCode;\n\t} catch (error: unknown) {\n\t\tconsole.error(\n\t\t\t`${error instanceof Error ? error.message : String(error)}. Review the failure before trying again.`,\n\t\t);\n\t\treturn 1;\n\t} finally {\n\t\tfor (const cleanup of signalCleanupHandlers) {\n\t\t\tcleanup();\n\t\t}\n\t\tawait disposeRuntime();\n\t\tawait flushRawStdout();\n\t}\n}\n"]}
|
package/docs/compaction.md
CHANGED
|
@@ -44,6 +44,12 @@ A new user prompt independently checks threshold maintenance using trustworthy s
|
|
|
44
44
|
|
|
45
45
|
You can also trigger manually with `/compact [instructions]`, where optional instructions focus the summary.
|
|
46
46
|
|
|
47
|
+
### Originating recovery and automatic handoffs
|
|
48
|
+
|
|
49
|
+
A captured [run settlement](sdk.md#capturing-originating-run-settlement) remains pending through its settlement-triggered automatic compaction and any selected 100 ms overflow or queued-message continuation. The continuation waits for originating Agent execution to unwind and adopts the same obligation; it cannot restart after cancellation, disposal, or manual-compaction drain. New-prompt threshold maintenance is independent preflight and does not inherit a previous run's obligation.
|
|
50
|
+
|
|
51
|
+
Skipped optional maintenance may retain successful output. Skipped or failed required overflow recovery produces a failed disposition with actionable detail rather than claiming completion; cancellation produces a cancelled disposition after owned active work unwinds. Compaction persistence/reconstruction and continuation infrastructure failures reject the captured obligation. Existing threshold policy, one-attempt overflow cap, queue semantics, and manual admission remain unchanged. Neither retry records nor continuation ownership restore execution on resume.
|
|
52
|
+
|
|
47
53
|
### Manual admission and terminal drain
|
|
48
54
|
|
|
49
55
|
Manual compaction has one owner. It rejects while manual/automatic compaction or branch summarization is in progress, or while an idle-origin harness tool is still executing. Refusal leaves execution, abort signals, accounting, subscriptions, and context untouched. Await the conflicting operation's persisted settlement before deliberately retrying; requests are not queued.
|
package/docs/custom-provider.md
CHANGED
|
@@ -533,7 +533,9 @@ Built-in failure catches preserve an existing primary diagnosis, classify witnes
|
|
|
533
533
|
|
|
534
534
|
Callback failures must never be inferred from provider prose or persist original callback exception details. Message origin describes who produced the message, not which operation failed. Retain partial provider output and mark the callback failure separately.
|
|
535
535
|
|
|
536
|
-
|
|
536
|
+
Codex `type: "error"` events normalize witnessed top-level and nested error fields into one closed diagnosis before display formatting. An in-stream `status: 503` with `server_error` supports provider/server evidence, not an HTTP request rejection. Unknown and contradictory codes remain governed by the neutral evidence utility; arbitrary payloads and displayed prose do not authorize recovery.
|
|
537
|
+
|
|
538
|
+
Completeness coverage is route-qualified: shared Responses, started Codex WebSockets, Chat's existing finish-reason check, and started Anthropic streams detect missing terminals. A started Codex WebSocket close 1000 without accepted terminal completion emits stream `missing_terminal_event`: the cause remains unknown and its evidence is not transient, but session policy can retry that category within configured limits. Already-received WebSocket frames are decoded in arrival order before close adjudication; accepted terminals and semantic errors retain precedence, and cancellation remains immediate. Pre-start SSE fallback, abnormal-close transport evidence, and non-retryable policy-close exclusions remain separate. Additional mandatory markers for Google/Vertex, Mistral, Bedrock and startless Anthropic SSE were not established from installed SDK contracts and are not enforced. In particular, Google prompt-feedback blocking can occur without a candidate finish reason; parser acceptance alone does not establish valid backend completion. The installed Google SDK exposes bodyless generation failures as plain errors without a supported raw-response hook; Google/Vertex therefore retain an explicit unknown diagnosis rather than inferring body absence from the error message. Chat and Mistral classify body absence from their available response objects. Responses and default Chat/Anthropic clients distinguish local SDK preparation from dispatch at the invocation-local fetch boundary, not from observational SDK-attempt diagnostics or a generic `TypeError`.
|
|
537
539
|
|
|
538
540
|
### Context Overflow Errors
|
|
539
541
|
|
package/docs/extensions.md
CHANGED
|
@@ -539,7 +539,7 @@ Returning `systemPromptSection` contributes a section for the current turn witho
|
|
|
539
539
|
|
|
540
540
|
#### agent_start / agent_end
|
|
541
541
|
|
|
542
|
-
Fired
|
|
542
|
+
Fired for each Agent attempt, including automatic retry/compaction continuations. Attempt-level `agent_end` is not final recovery settlement; use the captured context capability described below for deferred completion observation.
|
|
543
543
|
|
|
544
544
|
```typescript
|
|
545
545
|
pi.on("agent_start", async (_event, ctx) => {});
|
|
@@ -985,6 +985,18 @@ pi.on("tool_result", async (event, ctx) => {
|
|
|
985
985
|
});
|
|
986
986
|
```
|
|
987
987
|
|
|
988
|
+
### ctx.getRunSettlement()
|
|
989
|
+
|
|
990
|
+
Optional `getRunSettlement(): Promise<RunSettlement> | undefined` captures the exact originating execution in queued Agent lifecycle/message/tool-execution events and its owned automatic `session_before_compact` / `session_compact` hooks. All attempts and owned maintenance share the same promise as SDK `PromptOptions.onRunSettlement`; a retained run A context never observes run B. Manual/unowned compaction, new-prompt preflight, harness-origin message events, and contexts without explicit run attribution return `undefined` (including command/input, provider hooks and separately constructed tool/shortcut contexts). `fromExtension` describes summary authorship, not ownership. See [SDK settlement outcomes](sdk.md#capturing-originating-run-settlement) and [automatic handoffs](compaction.md#originating-recovery-and-automatic-handoffs).
|
|
991
|
+
|
|
992
|
+
Capture during a hook, attach rejection handling immediately, return, and observe from an independent continuation. Awaiting its own completion inside an awaited gating hook deadlocks because classification/persistence needs that hook to return. Settlement is observation, not global quiescence, a lease, or permission to bypass admission. Revalidate context and application identity before subsequent guarded actions; stale runner contexts still throw. Missing capabilities in hand-built/unsupported contexts require an actionable pause, not a streaming-idle fallback.
|
|
993
|
+
|
|
994
|
+
### ctx.sendMessage(message, options?)
|
|
995
|
+
|
|
996
|
+
Optional async sender with the same message/options shapes as replacement-context `sendMessage()` and [pi.sendMessage()](#pisendmessagemessage-options). Session-bound runner contexts provide it; unsupported hand-built contexts may omit it. It delegates to the existing `AgentSession.sendCustomMessage` operation: streaming delivery and `nextTurn` resolve at queue acceptance; an immediate triggered turn waits for its run/retries; idle non-triggering delivery persists without starting a turn. `nextTurn` ignores `triggerTurn`. Asynchronous admission, launch and persistence failures reject; rejection does not prove artifact absence, so never blindly resend.
|
|
997
|
+
|
|
998
|
+
Do not await an immediate-turn send from an awaited gating hook whose return the turn needs. Return from that hook and await independently. The legacy `pi.sendMessage(): void` and its extension error channel remain unchanged; missing async sending must not silently fall back to void delivery.
|
|
999
|
+
|
|
988
1000
|
### ctx.isIdle() / ctx.abort() / ctx.hasPendingMessages()
|
|
989
1001
|
|
|
990
1002
|
Control flow helpers.
|
package/docs/keybindings.md
CHANGED
|
@@ -22,7 +22,7 @@ Modifier combinations: `ctrl+shift+x`, `alt+ctrl+x`, `ctrl+shift+alt+x`, `ctrl+1
|
|
|
22
22
|
|
|
23
23
|
## Transcript browsing precedence
|
|
24
24
|
|
|
25
|
-
Interactive mode's retained viewport owns mouse wheel/trackpad scrolling, the rightmost draggable scrollbar and ordinary drag selection. Right-click a nonempty selection or use `tui.input.copy` (Ctrl+C by default) to copy displayed text. Without
|
|
25
|
+
Interactive mode's retained viewport owns mouse wheel/trackpad scrolling, the rightmost draggable scrollbar and ordinary drag selection. Right-click a nonempty selection or use `tui.input.copy` (Ctrl+C by default) to copy displayed text. Without selection, right-click pastes local clipboard text into the eligible, visible focused editor without submitting; selectors, overlays and approval controls do not paste. iTerm2 requires explicitly configured `ReportRightClick=true` for both right-click actions; this affects other mouse-reporting applications, and Scramjet does not change it automatically. Command+V remains terminal-native text paste at the editor caret without submission; it is not the `app.clipboard.pasteImage` action. Command+C and terminal Copy menus remain terminal-owned and cannot copy Scramjet’s application selection. Configuration and proof limits are described in [terminal setup](terminal-setup.md#transcript-browsing-and-copying). Without an application selection, Ctrl+C retains its normal editor/exit behavior (or the configured key's focused-component/application behavior). Escape clears selection and returns to the live tail.
|
|
26
26
|
|
|
27
27
|
While detached from the tail with the editor owning input, the viewport consumes this fixed set before editor bindings; focused selectors retain their own navigation and cancellation bindings:
|
|
28
28
|
|
package/docs/sdk.md
CHANGED
|
@@ -200,6 +200,7 @@ interface PromptOptions {
|
|
|
200
200
|
streamingBehavior?: "steer" | "followUp";
|
|
201
201
|
source?: InputSource;
|
|
202
202
|
preflightResult?: (success: boolean) => void;
|
|
203
|
+
onRunSettlement?: (settlement: Promise<RunSettlement>) => void;
|
|
203
204
|
}
|
|
204
205
|
```
|
|
205
206
|
|
|
@@ -283,6 +284,8 @@ session.agent.state.tools = tools; // copies the top-level array
|
|
|
283
284
|
await session.agent.waitForIdle();
|
|
284
285
|
```
|
|
285
286
|
|
|
287
|
+
`session.agent.getRunExecution()` synchronously captures the active execution's `Promise<void>`, or returns `undefined` outside a run. It settles after runtime cleanup and rejects with any escaping `prompt()` / `continue()` failure, including final harness-drain failures. It preserves Agent's caught-error behavior and does not include Session recovery or persistence. `waitForIdle()` still resolves even when execution rejects. Capture in a listener and observe independently after the listener returns; awaiting the originating execution inside its own awaited listener deadlocks.
|
|
288
|
+
|
|
286
289
|
### Events
|
|
287
290
|
|
|
288
291
|
Subscribe to events to receive streaming output and lifecycle notifications.
|
|
@@ -465,6 +468,30 @@ AgentSession classifies the finalized persisted assistant snapshot. Built-in ada
|
|
|
465
468
|
|
|
466
469
|
Recognized incomplete responses can use the existing bounded agent retry loop; tools from a failed response do not execute. Provider/SDK retries remain a separate layer. See [settings.md](settings.md#retry) for numeric validation, zero semantics and Codex-only server-delay suppression, and [custom-provider.md](custom-provider.md#failure-evidence) for evidence precedence. Neither these attempt limits nor forwarding provider options guarantees a universal request-count or elapsed-time bound.
|
|
467
470
|
|
|
471
|
+
#### Capturing originating run settlement
|
|
472
|
+
|
|
473
|
+
`onRunSettlement` synchronously receives one stable promise after an immediate Agent run is reserved, before launch (including a delayed `agent_start`). It is not called for handled commands/input, explicit streaming queue acceptance, or rejected preflight. A command handler's separately launched custom turn is not adopted. New-prompt threshold maintenance is preflight, not part of an earlier run's capture.
|
|
474
|
+
|
|
475
|
+
The exported `RunSettlement` union has `status: "completed" | "failed" | "cancelled"`. Results retain the eligible finalized provider `assistantMessage` snapshot when available; failed results also carry `errorMessage`. Operational provider failure, exhausted/disabled retry, failed required overflow recovery, and cancellation resolve with their disposition. Persistence/infrastructure failure, disposal, capture-callback refusal, and launch without a start reject. Completion waits for terminal persistence/classification, originating execution unwind, bounded retries, and applicable automatic maintenance plus its selected continuation. Awaited public Agent-listener failures during execution unwind reject the uncancelled capture even after an earlier successful or retryable `agent_end`; cancellation retains its cancelled disposition. Retry backoff and unwind waiting run outside the Session event queue so caught-failure events can drain before continuation admission. Agent's existing caught-error behavior for `prompt()` is unchanged. It means execution recovered, not that the task or a Scramjet command completed.
|
|
476
|
+
|
|
477
|
+
```typescript
|
|
478
|
+
let captured: Promise<RunSettlement> | undefined;
|
|
479
|
+
await session.prompt("Continue the work", {
|
|
480
|
+
onRunSettlement(settlement) {
|
|
481
|
+
captured = settlement;
|
|
482
|
+
void settlement.catch((error) => console.error("Run settlement failed:", error));
|
|
483
|
+
},
|
|
484
|
+
});
|
|
485
|
+
if (captured) {
|
|
486
|
+
const outcome = await captured;
|
|
487
|
+
console.log(outcome.status);
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
Run-bound extension event contexts expose the exact same promise through optional `ctx.getRunSettlement()`, including matching owned automatic compaction hooks across retries/continuations. Manual/unowned maintenance and new-prompt preflight expose no originating capture; command/input handling and explicit queue acceptance do not borrow a previous run. A retained event context is bound to its original run, not a mutable latest-result lookup. Contexts from direct public `session.agent.prompt()` / `continue()` executions also wait for that execution's captured outcome and its queued classification before settling, rejecting escaping execution failures even when the idle wait resolves. Independently admitted harness work after that execution becomes idle does not delay its capture; those direct calls still bypass Session prompt preflight. Optional `ctx.sendMessage()` exposes existing asynchronous custom sending without changing void `pi.sendMessage`; see [context capabilities and deadlock cautions](extensions.md#ctxgetrunsettlement).
|
|
492
|
+
|
|
493
|
+
Import `RunSettlement` from `@leanandmean/coding-agent`. Attach rejection handling immediately and observe independently; the callback is notification, not an awaited consumer hook. Throwing from it rejects the reservation before launch and propagates through `prompt()`. Never await a run's own settlement from an awaited gating hook: classification needs that hook to return. Capture observes one originating run, not global idleness or an admission lease; retained captures never substitute later prompts or harness output. Revalidate session/context before subsequent guarded actions. `prompt()` remains `Promise<void>` with operational outcomes delivered separately rather than newly thrown provider failures.
|
|
494
|
+
|
|
468
495
|
### API Keys and OAuth
|
|
469
496
|
|
|
470
497
|
API key resolution priority (handled by AuthStorage):
|
package/docs/settings.md
CHANGED
|
@@ -119,6 +119,8 @@ Agent retries include validated missing-terminal Responses streams and accepted
|
|
|
119
119
|
|
|
120
120
|
Codex SSE honors `retry.provider.maxRetries` (default three) and rejects server-requested waits above `retry.provider.maxRetryDelayMs`, including after the final inner attempt. That rejection suppresses outer agent retries too. Set the delay cap to `0` to disable the policy cap; long representable waits remain cancellable, and unrepresentable waits fail without retry. Other SDKs do not necessarily implement this delay cap or these request controls; forwarding an option is not proof of enforcement.
|
|
121
121
|
|
|
122
|
+
The interactive countdown describes a recovery attempt, not final task completion. Escape cancels the originating recovery even between attempt completion and retry classification. Enter/Alt+Enter in non-streaming recovery retain your draft with a wait/cancel explanation; existing streaming and compaction queues stay explicit. Final cancellation/failure leaves the editor usable and tells you to review interrupted work before submitting again. Recovered execution is not a claim that the task completed. No additional retry setting or universal timeout is introduced.
|
|
123
|
+
|
|
122
124
|
Counts must be nonnegative safe integers; millisecond settings must be integers from 0 through 2,147,483,647. Invalid values generate scoped settings diagnostics and inherit a valid lower-priority value or the default without rewriting the file. Nested `retry.provider` leaves merge individually. Zero retries disables that layer's retries; zero base delay removes intentional backoff; timeout zero retains provider/SDK semantics and does not universally disable timeouts. Codex direct API callers receive a local validation failure for invalid retry options.
|
|
123
125
|
|
|
124
126
|
```json
|
package/docs/terminal-setup.md
CHANGED
|
@@ -14,7 +14,11 @@ The leading dock gap shows `Session: N lines below · Ctrl+End: latest` when ren
|
|
|
14
14
|
|
|
15
15
|
Double-click to select a word or triple-click to select one displayed line, not adjacent wrapped lines. Clicks must land on the same cell within 500 ms of the preceding press; whitespace and punctuation select their own contiguous runs. Select longer ranges by ordinary dragging, including across a screen edge. After starting a drag, keep the left button held and use the wheel to extend the transcript selection, either with a stationary pointer or while adjusting its horizontal and vertical position; reversing the wheel adjusts the endpoint. Release before browsing if you want to keep the selection unchanged. A press without an initial drag does not start wheel selection. Drags cross between transcript and dock in either direction: a transcript-origin downward drag autoscrolls to the bottom before entering the dock; a dock-origin upward drag selects the displayed transcript portion without including the hidden gap below it. Wheel scrolling does not extend a selection confined to the dock. Right-click a nonempty selection or use `tui.input.copy` (Ctrl+C by default) to copy displayed text without ANSI controls or scrollbar cells. Built-in text, Markdown and editor copying omit known component padding and rejoin soft wraps, retaining genuine indentation, hard line breaks and structural boundaries. Tables, repeated quote prefixes and unannotated custom renderers retain their physical layout rather than guessing at their source. Output and waiting controls continue updating while selected. Copy snapshots the last painted selection immediately, before clipboard delivery; unpainted pointer changes cannot change that copy. A new selection must have painted before it can be copied. Resizing ends an active drag and remaps its selection; successful copy or Escape clears selection. Copy failures retain the selection and temporarily display a diagnostic on the last screen row without changing geometry; clicking that diagnostic dismisses it without selecting the covered text. Without selection, Ctrl+C retains its normal application behavior. Right-clicking any content area—including transcript, blank space, widgets, footer or editor—reads local clipboard text into the visible, focused editor at its caret without submitting. The scrollbar, overlays, selectors and approval controls do not paste. A pending repaint is flushed before reading; changing the input owner while waiting cancels the request. Repeated eligible right-clicks during one clipboard read share that read; only the latest still-current request inserts text. Other intervening input, focus loss, editing or session changes cancel a pending paste; focus-in, terminal cell-size notifications and unmodified right-button motion do not count as editing input. WSL prewarms one PowerShell process while the retained TUI runs and uses direct Windows Unicode clipboard access on explicit paste requests. It does not poll or cache clipboard contents. Startup and each read have five-second timeouts and text is limited to 10 MiB of UTF-8; brief clipboard-lock contention has a bounded acquisition retry, but failed reads are not automatically repeated. The reader closes on exit, suspension or external-editor handoff and prewarms again on resume. This avoids per-click process startup; clipboard contention and initial startup can still add latency. macOS, Wayland/X11 and Termux use their local backends. Unsupported or remote clipboard reads show an actionable error; use your terminal's Paste command instead.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Command+V remains ordinary terminal paste into the focused editor without submission. Command+C and terminal-owned Copy/context menus operate on terminal-native selection, not Scramjet’s application selection. Use right-click or Ctrl+C for application-selection Copy; selecting text or receiving live output does not itself copy. Clipboard failures leave ordinary keyboard input and terminal paste independent of clipboard delivery.
|
|
18
|
+
|
|
19
|
+
**iTerm2 requires explicitly configured `ReportRightClick=true` for both right-click Copy and no-selection Paste.** Scramjet never changes this preference; enabling it affects other mouse-reporting applications too. See [iTerm2 setup](#iterm2). Apple Terminal must be verified through its own right-click journey, not inferred from iTerm2. The earlier Command+C runs certify only the superseded implementation. See [replacement acceptance requirements, revision-specific results and historical evidence](terminal-verification.md#replacement-right-click-acceptance).
|
|
20
|
+
|
|
21
|
+
A terminal or multiplexer that intercepts pointer input must be configured to forward it. In tmux, enable `set -g mouse on` for the application's wheel/drag path; tmux's own copy mode is a separate interaction. See [tmux.md](tmux.md) for modified-key setup.
|
|
18
22
|
|
|
19
23
|
Home/End retain editor line-start/end behavior at every scroll position. Ctrl+Home navigates to the transcript beginning; Ctrl+End navigates to the bottom and resumes following. Submitting nonempty editor input with Enter or Alt+Enter resumes following immediately, before routing bash/slash commands or queueing messages. New user-message delivery also resumes following, while passive output preserves browsing. Alt+PageUp/Alt+PageDown provide keyboard-only entry into transcript browsing; when detached, PageUp/PageDown browse and Escape returns to the tail. Docked editing and presentation toggles keep the reading anchor, while ordinary undocked editing reveals its cursor. At the tail, normal editor/list bindings apply, and focused overlays retain keyboard precedence. See [keybindings.md](keybindings.md#transcript-browsing-precedence).
|
|
20
24
|
|
|
@@ -48,7 +52,7 @@ These checks do not establish every emulator/profile, physical trackpad's gestur
|
|
|
48
52
|
|
|
49
53
|
### Native selection differences — deferred
|
|
50
54
|
|
|
51
|
-
Terminal-owned selection is separate from Scramjet selection. Windows Terminal reserves Shift-mouse for its own screen-cell selection; [iTerm2 uses Option to bypass mouse reporting](https://iterm2.com/faq.html). These modifiers are terminal/profile-specific, not a universal Scramjet shortcut. They do not expose unpainted retained history or extend an application selection, and native copying can include visible padding or scrollbar cells. Scramjet's copy cleanup applies
|
|
55
|
+
Terminal-owned selection is separate from Scramjet selection. Windows Terminal reserves Shift-mouse for its own screen-cell selection; [iTerm2 uses Option to bypass mouse reporting](https://iterm2.com/faq.html). These modifiers are terminal/profile-specific, not a universal Scramjet shortcut. They do not expose unpainted retained history or extend an application selection, and native copying can include visible padding or scrollbar cells. Scramjet's copy cleanup applies to its application selection, through Ctrl+C and right-click—not terminal-owned selection.
|
|
52
56
|
|
|
53
57
|
Native-selection parity remains deferred. Retained edge scrolling uses a fixed speed, not the native distance-dependent acceleration demonstrated on Windows Terminal. Changing modifiers during an active edge drag can withhold its release from the application; clear the application selection to stop a stranded gesture. See the [historical comparison](terminal-verification.md#historical-windows-selection-comparison) for the observations and their limits.
|
|
54
58
|
|
|
@@ -71,7 +75,7 @@ Keyboard reporting works out of the box.
|
|
|
71
75
|
|
|
72
76
|
## iTerm2
|
|
73
77
|
|
|
74
|
-
Keyboard reporting works out of the box. **Right-click
|
|
78
|
+
Keyboard reporting works out of the box. **Right-click Copy and no-selection Paste require iTerm2's `ReportRightClick=true` preference** so mouse-reporting applications receive right-clicks instead of iTerm2's native context menu. With the preference off, iTerm2 retains the right-click instead of delivering Scramjet's Copy/Paste gesture; historical default-off checks are not replacement-candidate acceptance. This is an explicit setup requirement, not a runtime allowlist or automatic fallback.
|
|
75
79
|
|
|
76
80
|
Enable the right-click-reporting option in iTerm2's Pointer settings, or quit iTerm2 and set its existing preference from another terminal:
|
|
77
81
|
|
|
@@ -79,7 +83,7 @@ Enable the right-click-reporting option in iTerm2's Pointer settings, or quit iT
|
|
|
79
83
|
defaults write com.googlecode.iterm2 ReportRightClick -bool true
|
|
80
84
|
```
|
|
81
85
|
|
|
82
|
-
Restart iTerm2 afterward. This changes right-click behavior for mouse-reporting applications, not just Scramjet; Scramjet never changes the preference for you. Ctrl+C
|
|
86
|
+
Restart iTerm2 afterward. This changes right-click behavior for mouse-reporting applications, not just Scramjet; Scramjet never changes the preference for you. With reporting disabled, use Ctrl+C for application-selection Copy and Command+V for ordinary terminal Paste. Command+C and native iTerm2 menus remain terminal-owned and cannot access application-owned selection.
|
|
83
87
|
|
|
84
88
|
## Ghostty
|
|
85
89
|
|
|
@@ -16,6 +16,54 @@ The shared headless terminal feeds actual TUI writes into `@xterm/headless`; its
|
|
|
16
16
|
|
|
17
17
|
The native journey fixture is `packages/scramjet/tests/fixtures/interactive-viewport.mjs`. Hosted interaction and graphics drivers live in `.github/scripts/terminal-probe.py` and `.github/scripts/terminal-safety.py`; `.github/scripts/test-terminal-fixtures.py` exercises their evidence predicates. `.github/workflows/terminal-feasibility.yml` owns the hosted matrix. Build the runtime before running fixtures, because they load compiled packages. Windows desktop/clipboard execution through `.github/scripts/windows-terminal-probe.sh` requires separate explicit permission.
|
|
18
18
|
|
|
19
|
+
## Recovery input smoke — 2026-10-08
|
|
20
|
+
|
|
21
|
+
A separately authorized Windows Terminal 1.24.12741.0 / Windows build 22631 / WSL offline production InteractiveMode smoke ran against freshly built Stage 5 changes on the dirty working tree based on `08defe2b1e33b0505e9105d36858d241ac17aa60`. A disposable fixture used isolated in-memory settings, a synthetic provider, and the real terminal-indicator implementation; no paid calls, personal extensions, clipboard writes or terminal preference changes occurred.
|
|
22
|
+
|
|
23
|
+
Six targeted predicates passed: working title during retry backoff, native Enter draft retention, native Alt+Enter retention, Escape cancellation with an actionable outcome and waiting title, a subsequent successful prompt, and orderly exit. Screenshots confirmed the retained editor, retry countdown, attempt label, cancellation message and subsequent answer. Termios before/after matched; owned-window closure and pointer/focus restoration were verified. BEL timing is verified separately by deterministic indicator tests, not by this smoke (bell was disabled). The test window suppressed application title changes for safe identity-based ownership; emitted title values were recorded rather than asserting the visible window caption.
|
|
24
|
+
|
|
25
|
+
Reproduction: construct an isolated compiled InteractiveMode with one synthetic 503 attempt and ten-second backoff; use real desktop typing, Enter, Alt+Enter and Escape; submit deliberate new work and record input, editor, captured statuses, emitted titles and terminal restoration. Disposable probes reused the existing Windows driver's focus checks, screenshots and cleanup principles. Preliminary probes failed because the clean shell lacked Node on PATH and because fixture teardown ran inside stdin delivery; neither is acceptance evidence. The latter's test window was subsequently closed and the final probe deferred teardown beyond input delivery. The subsequent prompt retained the earlier draft alongside newly typed text; this was deliberately not a claim that Ctrl+A clears the editor. Linux/macOS/tmux, native audio/BEL, physical input devices and full-matrix acceptance were not rerun.
|
|
26
|
+
|
|
27
|
+
## Replacement right-click acceptance
|
|
28
|
+
|
|
29
|
+
The approved #608 / PR #610 scope replaces native Command+C interception with the existing application right-click interactions and removes the macOS shortcut/input Worker and its public capability. This intentionally supersedes the original native Command+C objective and acceptance criterion; it does not claim that objective was met by right-click. Right-click copies the last displayed application selection, or pastes into the eligible focused editor without submitting when there is no selection. Ctrl+C retains selected Copy versus ordinary editor/exit handling, and Command+V remains terminal paste. Command+C and terminal Copy menus remain terminal-owned, not application-selection Copy.
|
|
30
|
+
|
|
31
|
+
**Replacement evidence is revision-specific.** [PR #610](https://github.com/LeanAndMean/scramjet/pull/610) records candidate revisions, native results and their qualifications under the amended issue scope. Match the tested revision before relying on any result. The historical runs below certify only their original revisions and configurations. Apple Terminal support must be established by its own replacement journey, not inferred from iTerm2 or headless checks.
|
|
32
|
+
|
|
33
|
+
The required replacement matrix is Apple Terminal and iTerm2 on each of ARM64 and x64. Every combination must exercise rebuilt checkout packages, an isolated installed five-package candidate, all three installed-candidate negative controls, both external-editor paths with real inherited stdin, Ctrl+Z/resume, subsequent typing and paste, orderly exit and restoration. Record exact revision, architecture, terminal/macOS versions and effective profile. iTerm2 must explicitly verify `ReportRightClick=true`; Scramjet must not change it automatically, and the setting affects other mouse-reporting applications. A separate, small default-off iTerm2 check must verify that right-click is not delivered, alternatives still work, native menus are dismissed and cleanup succeeds; it is not supported right-click acceptance. Apple's F8/F9 browsing profile remains a separate keyboard qualification.
|
|
34
|
+
|
|
35
|
+
Required clipboard outcomes:
|
|
36
|
+
|
|
37
|
+
- Independently seed and verify a fresh sentinel, select known Unicode text with desktop events, and verify that selection and live output do not change the clipboard. Right-click must produce exact clipboard equality and clear selection without changing editor contents or submission count. Preserve wrapped, scrolled and transcript/editor-seam cases.
|
|
38
|
+
- No-selection right-click over both transcript and editor must insert exact multiline/Unicode text once at an interior caret, leave the correct caret and never submit. Selectors, overlays and approval controls remain excluded.
|
|
39
|
+
- Actual Command+V must insert the armed synthetic payload between `PREFIX` and `SUFFIX` in the real editor with the correct caret and no submission; a receipt counter is insufficient. Unexpected paste must remain consumed and absent from diagnostics.
|
|
40
|
+
- Deliberate no-op Copy, copy-on-selection and consumed-paste controls must fail the exact clipboard, sentinel-preservation and editor/caret predicates respectively. A broken report stays `passed: false`; only the expected failed predicate with complete startup, provenance and cleanup evidence establishes a rejected negative control.
|
|
41
|
+
|
|
42
|
+
Keep Linux, tmux and graphics/safety coverage and exact restoration checks, including Darwin's narrowly qualified transient `PENDIN` handling and exact post-read termios equality. Native publication gating remains caller-SHA-bound and read-only, rejecting failed, cancelled, skipped, incomplete or malformed required results; no prior green run substitutes. Installed acceptance archives do not prove byte equality with independently rebuilt publication archives. A clean/disposable build must exclude stale compiled native-input artifacts after source removal. See [release policy](../../../.github/RELEASING.md).
|
|
43
|
+
|
|
44
|
+
## macOS native Copy and Paste — 2026-10-08
|
|
45
|
+
|
|
46
|
+
**Superseded historical evidence of the abandoned native Command+C implementation.** The following results, native registration/permission challenges and stock-profile qualifications do not certify the right-click replacement or its required matrix.
|
|
47
|
+
|
|
48
|
+
[Issue #608](https://github.com/LeanAndMean/scramjet/issues/608#issuecomment-6050418989) preserves acceptance of clean commit `3810822bedce066d1c9acfcadc6d02f0c8112412` in [run 37712738710](https://github.com/LeanAndMean/scramjet/actions/runs/37712738710): all 13 jobs passed, with 16 positive reports / 960 required checks and 12 correctly rejected native controls. Apple Terminal 2.14 and iTerm2 3.6.11 ran on macOS 15.7.9 / 24G830, Node 22.23.2, on both ARM64 and x64 without tmux. Each stock terminal/architecture exercised both rebuilt checkout and isolated five-package installations. Stock Copy/Paste shortcuts were unchanged and no `ReportRightClick` override was applied. Apple's existing F8/F9 application-browsing fixture profile did not supply Copy, Paste or wheel gestures. Separately configured right-click journeys remain qualified by their settings.
|
|
49
|
+
|
|
50
|
+
Actual Command+C had to replace a fresh clipboard sentinel with exact selected Unicode text; selection and live output alone had to leave the sentinel untouched. Actual Command+V had to insert allowlisted multiline/Unicode text between `PREFIX` and `SUFFIX` at an interior caret, with the correct final caret and no submission. Unexpected paste remains consumed and absent from evidence. No-op Copy, copying on selection and consumed-paste controls intentionally produce `passed: false`; only the expected failed outcome plus startup, provenance and successful cleanup establishes `negativeControlRejected: true`.
|
|
51
|
+
|
|
52
|
+
Those native challenges covered unfinished browsing, Copy in another tab during a blocked UI loop, no delayed stale write after recovery, application focus loss, two instances with distinct selections and immediate return-Copy, real registration conflicts and reselection, both external-editor paths with genuine stdin, Ctrl+Z/fg, orderly exit and restoration. Darwin restoration permits only the SDK-identified transient `PENDIN` state bit before a read, then requires exact post-read equality. No configuration flag is ignored or rewritten. Earlier failed attempts, including conflict/focus-transfer defects and a VTE shell-startup oracle failure, remain preserved on the issue; later success does not relabel them.
|
|
53
|
+
|
|
54
|
+
Desktop automation is privileged, and terminal-child processes inherit hosted-terminal grants. Independently launched receivers used the **installed production native adapter** with Accessibility, input-listening, event-posting and screen-capture preflights all false, made no permission requests, and delivered actual Command+C with successful cleanup. That separate evidence establishes the permission boundary of the abandoned native adapter, not a requirement or proof for the replacement. Stale-ID and failed-release challenges used real Carbon resources with explicitly injected identity/unregister failure; they do not claim a spontaneous OS cleanup failure. This record is not acceptance of every later revision, profile, physical trackpad, remote path or arbitrary extension, nor reproduction of the reporters' installations.
|
|
55
|
+
|
|
56
|
+
### How the earlier validation allowed the escape
|
|
57
|
+
|
|
58
|
+
- **Requirement mismatch:** the [#551 plan](https://github.com/LeanAndMean/scramjet/issues/551#issuecomment-5732527548) explicitly accepted Ctrl+C. The native driver tested Ctrl+C, not the macOS user's expected Command+C. The superseded native action addressed that previously accepted behavior gap; it was not evidence of a regression from an earlier Command+C implementation. The approved replacement now deliberately leaves Command+C terminal-owned.
|
|
59
|
+
- **Paste oracle stopped too early:** the old fixture consumed matching bracketed paste before editor delivery; `desktopPasteRoundTrip` accepted a receipt counter. The [2026-10-06 run](https://github.com/LeanAndMean/scramjet/actions/runs/37527119543) at `0ac415e71f670e90136d6c760c96ccc9b5e053f1` passed while both macOS editor snapshots stayed `Synthetic editorRIGHT-PASTE` and `pasteMatches` changed from 0 to 1. That proves a false-positive acceptance boundary, **not production Command+V failure**.
|
|
60
|
+
- **Review boundary:** [PR #560's F9 assessment](https://github.com/LeanAndMean/scramjet/pull/560#issuecomment-5765359890) and [repair](https://github.com/LeanAndMean/scramjet/pull/560#issuecomment-5765969234) removed pasted payloads from diagnostics by preserving interception. That privacy safeguard was meaningful, but it did not establish editor insertion. The current explicitly armed synthetic payload forwards original bytes to the real editor without exposing unexpected paste.
|
|
61
|
+
- **Change/release boundary:** PR #560 merged on 2026-09-28 as `ff3a116cbfe12cc2b8d157824d4628d4ac265dfe`, tagged `v0.103.0`. Its checked-in release workflow, and the pre-#608 workflow, allowed tag publication without a native dependency. Historical native success and an observable aggregate were not enforced tag-publication prerequisites. These repository facts do not identify either reporter's installed version or prove which release caused their symptoms.
|
|
62
|
+
|
|
63
|
+
The reusable native workflow now gates `release.yml` publication at the caller's exact `GITHUB_SHA`, with read-only native permissions, pinned action commits, clean checkout/fixture provenance, installed-candidate records, all required matrix results and native negative controls. Failed, cancelled, skipped, incomplete or malformed required results block publication; no latest-main or ancestor result is consulted. PR checks use their event merge SHA, while a tag reruns acceptance at its own SHA. Package integrity records describe the native acceptance installation, not equality to independently rebuilt publication tarballs. See [release policy](../../../.github/RELEASING.md) for immutable recovery and non-publishing verification boundaries.
|
|
64
|
+
|
|
65
|
+
The original reporter clarified that Command+V **works**. A separate reported Command+V failure and the unreconfirmed right-click complaint remain unresolved; passing these journeys does not disprove either. Reproduction still needs installed Scramjet version, terminal/macOS version, selection method, profile, SSH/tmux status, custom keybindings/extensions and exact expected/observed behavior. An old process surviving an update is not an established cause.
|
|
66
|
+
|
|
19
67
|
## Exit retention and right-click latency — 2026-09-28
|
|
20
68
|
|
|
21
69
|
A user-authorized targeted Windows Terminal 1.24.11911.0 / Windows build 22631 / WSL Ubuntu journey exercised rebuilt packages on the dirty working tree based on `6c06a61812dcc9fc324b570eef35d6bbbd487a43`. An isolated production InteractiveMode used a nonempty model scope, 180 numbered styled history rows, above/below widgets, an editor draft and an underlined footer. It loaded no personal extensions and made no model calls. This was a targeted check, not full-matrix acceptance.
|
package/docs/tui.md
CHANGED
|
@@ -50,6 +50,14 @@ In retained mode, tool-attached context uses `renderNow({ requireFlush: true })`
|
|
|
50
50
|
|
|
51
51
|
Built-in Kitty/iTerm2 placements are re-anchored to absolute rows only when their full extent fits; clipped spans show a placeholder and remain reachable by scrolling. Repeated reports of unchanged cell dimensions are consumed without invalidating components or disturbing image reading anchors; genuine dimension changes still invalidate and re-render images. `Component.setViewportHeight?(height)` is an optional image-sizing bound, propagated by standard Container/Box rendering, including newly added children. Custom wrappers must forward it to nested images. It must not truncate ordinary text or bound the total stacked document. Built-in Image fits using existing aspect-preserving sizing without changing source data; finalized viewport caches refresh on height changes. iTerm2 placements specify both cell dimensions to avoid rounded width exceeding reserved height. Transcript images are withheld with visible placeholders during overlays and selection; clearing either restores eligible placements. Bounded overlays forward their resolved image-height limit before rendering and use the same atomic geometry validation before clipping, including nested/padded images. A higher overlapping overlay suppresses the entire lower image placement; clearing it restores eligible placements. Custom graphics envelopes without recognizable placement geometry cannot be safely clipped and receive a placeholder. Built-in graphics checks do not establish arbitrary custom-envelope compatibility. See [terminal-setup.md](terminal-setup.md#native-compatibility-evidence) for the bounded native evidence.
|
|
52
52
|
|
|
53
|
+
### Terminal input and clipboard boundaries
|
|
54
|
+
|
|
55
|
+
`ProcessTerminal` uses the ordinary direct stdin reader and `StdinBuffer` on macOS as on the other ordinary terminal paths. The parser and listener are installed before stdin resumes. Terminal input owns framing, protocol negotiation, bounded draining and mode restoration; clipboard backends do not acquire, revoke or fault keyboard-input ownership. The abandoned native shortcut/input subsystem and its public capability are removed: `Terminal.configureNativeCopy`, native Copy control/notice types and `onNativeCopyAvailability` are not supported APIs. There is no replacement helper, global shortcut registration, clipboard polling or copy-on-selection behavior.
|
|
56
|
+
|
|
57
|
+
`RetainedViewport` owns selection, painted-text capture and single-flight Copy settlement through `copy`; InteractiveMode owns clipboard callbacks and paste eligibility through `requestPaste`. Right-click with selection copies the last displayed highlight; without selection it requests Paste into the eligible focused editor without submitting. Ctrl+C preserves selection Copy versus ordinary no-selection editor/exit handling. Command+V remains terminal paste; Command+C and terminal Copy menus cannot access application selection. iTerm2 must explicitly enable `ReportRightClick=true`, affecting other mouse-reporting applications too; the application must not configure it. See [terminal setup](terminal-setup.md#iterm2) and [replacement acceptance](terminal-verification.md#replacement-right-click-acceptance).
|
|
58
|
+
|
|
59
|
+
Both external-editor paths restart only after successful drain and stop; failed suspension removes its pending resume handler without sending SIGTSTP. Orderly shutdown retains emergency handlers through terminal flushing and runtime disposal. Failed exit snapshots still restore and flush terminal modes. These are ordinary terminal safeguards, not clipboard-dependent input ownership. Existing Windows terminal initialization through Koffi, platform clipboard backends and the separate WSL clipboard reader remain independent of the removed macOS input subsystem.
|
|
60
|
+
|
|
53
61
|
### Minimum usable geometry
|
|
54
62
|
|
|
55
63
|
InteractiveMode opts into `minimumSize: { columns: 12, rows: 3 }`, measured in physical terminal cells. Below that threshold the retained renderer shows a bounded resize notice, suppresses overlays/images/cursors and ordinary input, and does not render components into a destructive temporary budget. Draft and autocomplete state survive recovery. Live dimensions guard shrink-before-paint; a painted resize notice keeps input blocked until a usable replacement paint has flushed. This opt-in protection requires terminal flushing; standalone callers without `minimumSize` retain their existing small-screen behavior.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leanandmean/coding-agent",
|
|
3
|
-
"version": "0.74.1-scramjet.
|
|
3
|
+
"version": "0.74.1-scramjet.74",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
"test": "vitest --run --passWithNoTests"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@leanandmean/agent": "0.74.1-scramjet.
|
|
41
|
-
"@leanandmean/ai": "0.74.1-scramjet.
|
|
42
|
-
"@leanandmean/tui": "0.74.1-scramjet.
|
|
40
|
+
"@leanandmean/agent": "0.74.1-scramjet.53",
|
|
41
|
+
"@leanandmean/ai": "0.74.1-scramjet.49",
|
|
42
|
+
"@leanandmean/tui": "0.74.1-scramjet.46",
|
|
43
43
|
"@silvia-odwyer/photon-node": "^0.3.4",
|
|
44
44
|
"chalk": "^5.5.0",
|
|
45
45
|
"dictionary-en": "^4.0.0",
|