@cjhyy/code-shell-capability-coding 0.8.1 → 0.8.3
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 +5 -3
- package/dist/external-runtimes/claude-code/event-translator.d.ts +2 -0
- package/dist/external-runtimes/claude-code/event-translator.js +31 -6
- package/dist/external-runtimes/claude-code/runtime.d.ts +7 -1
- package/dist/external-runtimes/claude-code/runtime.js +38 -12
- package/dist/external-runtimes/codex/event-translator.d.ts +2 -0
- package/dist/external-runtimes/codex/event-translator.js +104 -6
- package/dist/external-runtimes/codex/runtime.d.ts +12 -1
- package/dist/external-runtimes/codex/runtime.js +55 -5
- package/dist/external-runtimes/index.d.ts +2 -0
- package/dist/external-runtimes/index.js +1 -0
- package/dist/external-runtimes/session-factory.d.ts +8 -1
- package/dist/external-runtimes/session-factory.js +12 -1
- package/dist/external-runtimes/turn-input.d.ts +13 -0
- package/dist/external-runtimes/turn-input.js +15 -0
- package/dist/index.capability.js +3 -11
- package/dist/tools/brief.d.ts +7 -1
- package/dist/tools/brief.js +7 -1
- package/dist/tools/drive-agent.js +14 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Optional coding capability pack for `@cjhyy/code-shell-core`. It owns
|
|
4
4
|
CodeShell's coding policy and implementations: the `terminal-coding` preset,
|
|
5
|
-
coding prompt, Git/worktree behavior, LSP, ApplyPatch, NotebookEdit,
|
|
5
|
+
coding prompt, Git/worktree behavior, LSP, ApplyPatch, NotebookEdit,
|
|
6
6
|
review/quota helpers, and external coding-agent adapters.
|
|
7
7
|
|
|
8
8
|
The package is deliberately separate from core. A service building a customer
|
|
@@ -52,8 +52,10 @@ import { probeClaudeCli } from "@cjhyy/code-shell-capability-coding/orchestratio
|
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
These are subpaths of the same npm package, not independently versioned
|
|
55
|
-
packages. LSP, ApplyPatch,
|
|
56
|
-
|
|
55
|
+
packages. LSP, ApplyPatch, NotebookEdit, and other compatibility exports remain
|
|
56
|
+
available from the root. The legacy `briefTool` formatter is also retained as a
|
|
57
|
+
root compatibility export, but it is no longer registered with the default
|
|
58
|
+
coding preset: user-facing Markdown should be returned as normal assistant text.
|
|
57
59
|
|
|
58
60
|
## Boundary
|
|
59
61
|
|
|
@@ -34,6 +34,8 @@ export declare class ClaudeEventTranslator {
|
|
|
34
34
|
/** Accumulates `input_json_delta` per tool block so args land as one object. */
|
|
35
35
|
private readonly toolInput;
|
|
36
36
|
constructor(options: ClaudeEventTranslatorOptions);
|
|
37
|
+
/** Reset turn-scoped state while preserving the durable Claude session id. */
|
|
38
|
+
beginTurn(): void;
|
|
37
39
|
/** Claude session id, learned from the `system/init` line. */
|
|
38
40
|
runtimeSessionId?: string;
|
|
39
41
|
/**
|
|
@@ -31,6 +31,11 @@ export class ClaudeEventTranslator {
|
|
|
31
31
|
this.options = options;
|
|
32
32
|
this.codeshellServer = options.codeshellServerName ?? "codeshell_tools";
|
|
33
33
|
}
|
|
34
|
+
/** Reset turn-scoped state while preserving the durable Claude session id. */
|
|
35
|
+
beginTurn() {
|
|
36
|
+
this.terminal = false;
|
|
37
|
+
this.toolInput.clear();
|
|
38
|
+
}
|
|
34
39
|
/** Claude session id, learned from the `system/init` line. */
|
|
35
40
|
runtimeSessionId;
|
|
36
41
|
/**
|
|
@@ -188,12 +193,32 @@ export class ClaudeEventTranslator {
|
|
|
188
193
|
if (this.terminal)
|
|
189
194
|
return [];
|
|
190
195
|
this.terminal = true;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
196
|
+
const events = [];
|
|
197
|
+
const usage = asRecord(message.usage);
|
|
198
|
+
const number = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
199
|
+
const promptTokens = number(usage?.input_tokens);
|
|
200
|
+
if (promptTokens !== undefined) {
|
|
201
|
+
const completionTokens = number(usage?.output_tokens);
|
|
202
|
+
const cacheReadTokens = number(usage?.cache_read_input_tokens);
|
|
203
|
+
const cacheCreationTokens = number(usage?.cache_creation_input_tokens);
|
|
204
|
+
events.push({
|
|
205
|
+
type: "usage_update",
|
|
206
|
+
promptTokens,
|
|
207
|
+
promptTokensSource: "provider_usage",
|
|
208
|
+
promptTokensConfidence: "high",
|
|
209
|
+
...(completionTokens !== undefined ? { completionTokens } : {}),
|
|
210
|
+
...(cacheReadTokens !== undefined ? { cacheReadTokens } : {}),
|
|
211
|
+
...(cacheCreationTokens !== undefined ? { cacheCreationTokens } : {}),
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
const reason = terminalReasonFor(str(message.subtype), message.is_error === true);
|
|
215
|
+
if (reason === "model_error") {
|
|
216
|
+
const detail = str(message.result) ?? str(message.error);
|
|
217
|
+
if (detail)
|
|
218
|
+
events.push({ type: "error", error: detail });
|
|
219
|
+
}
|
|
220
|
+
events.push({ type: "turn_complete", reason });
|
|
221
|
+
return events;
|
|
197
222
|
}
|
|
198
223
|
isCodeshellHostTool(toolName) {
|
|
199
224
|
return toolName.startsWith(`mcp__${this.codeshellServer}__`);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { StreamEvent } from "@cjhyy/code-shell-core/extension";
|
|
2
2
|
import type { McpBridgeHandle } from "../shared/mcp-bridge.js";
|
|
3
|
+
import { type ExternalRuntimeTurnInput } from "../turn-input.js";
|
|
3
4
|
export interface ClaudeRuntimeOptions {
|
|
4
5
|
cwd: string;
|
|
5
6
|
/** CodeShell business session id — the authorization subject. */
|
|
@@ -12,6 +13,8 @@ export interface ClaudeRuntimeOptions {
|
|
|
12
13
|
/** Extra args, inserted before the runtime's own. */
|
|
13
14
|
extraArgs?: readonly string[];
|
|
14
15
|
model?: string;
|
|
16
|
+
resumeRuntimeSessionId?: string;
|
|
17
|
+
initialContext?: string;
|
|
15
18
|
serverName?: string;
|
|
16
19
|
log?: (event: string, data: Record<string, unknown>) => void;
|
|
17
20
|
}
|
|
@@ -30,6 +33,8 @@ export declare class ClaudeCodeRuntime {
|
|
|
30
33
|
private child?;
|
|
31
34
|
private claudeSessionId?;
|
|
32
35
|
private closed;
|
|
36
|
+
private firstTurn;
|
|
37
|
+
private terminalSeen;
|
|
33
38
|
constructor(options: ClaudeRuntimeOptions, hooks?: ClaudeRuntimeHooks);
|
|
34
39
|
/** Claude session id, once the first turn has reported it. Resume key only. */
|
|
35
40
|
get runtimeSessionId(): string | undefined;
|
|
@@ -41,8 +46,9 @@ export declare class ClaudeCodeRuntime {
|
|
|
41
46
|
* positional prompt after it is swallowed as another config value (measured),
|
|
42
47
|
* and a prompt on the command line would also be visible in `ps`.
|
|
43
48
|
*/
|
|
44
|
-
send(
|
|
49
|
+
send(input: ExternalRuntimeTurnInput): Promise<ClaudeTurnHandle>;
|
|
45
50
|
private onLine;
|
|
51
|
+
private emit;
|
|
46
52
|
/**
|
|
47
53
|
* Interrupt the active turn.
|
|
48
54
|
*
|
|
@@ -19,6 +19,7 @@ import { createInterface } from "node:readline";
|
|
|
19
19
|
import { ClaudeEventTranslator } from "./event-translator.js";
|
|
20
20
|
import { claudeBridgeArgs, CLAUDE_MCP_SERVER_NAME } from "./mcp-config.js";
|
|
21
21
|
import { buildRuntimeSpawnEnv } from "../shared/spawn-env.js";
|
|
22
|
+
import { textWithAttachmentReferences } from "../turn-input.js";
|
|
22
23
|
export class ClaudeCodeRuntime {
|
|
23
24
|
options;
|
|
24
25
|
hooks;
|
|
@@ -28,6 +29,8 @@ export class ClaudeCodeRuntime {
|
|
|
28
29
|
child;
|
|
29
30
|
claudeSessionId;
|
|
30
31
|
closed = false;
|
|
32
|
+
firstTurn = true;
|
|
33
|
+
terminalSeen = false;
|
|
31
34
|
constructor(options, hooks = {}) {
|
|
32
35
|
this.options = options;
|
|
33
36
|
this.hooks = hooks;
|
|
@@ -36,6 +39,7 @@ export class ClaudeCodeRuntime {
|
|
|
36
39
|
sessionId: options.businessSessionId,
|
|
37
40
|
codeshellServerName: options.serverName ?? CLAUDE_MCP_SERVER_NAME,
|
|
38
41
|
});
|
|
42
|
+
this.claudeSessionId = options.resumeRuntimeSessionId;
|
|
39
43
|
}
|
|
40
44
|
/** Claude session id, once the first turn has reported it. Resume key only. */
|
|
41
45
|
get runtimeSessionId() {
|
|
@@ -49,11 +53,13 @@ export class ClaudeCodeRuntime {
|
|
|
49
53
|
* positional prompt after it is swallowed as another config value (measured),
|
|
50
54
|
* and a prompt on the command line would also be visible in `ps`.
|
|
51
55
|
*/
|
|
52
|
-
async send(
|
|
56
|
+
async send(input) {
|
|
53
57
|
if (this.closed)
|
|
54
58
|
throw new Error("ClaudeCodeRuntime is closed");
|
|
55
59
|
if (this.child)
|
|
56
60
|
throw new Error("a turn is already running");
|
|
61
|
+
this.terminalSeen = false;
|
|
62
|
+
this.translator.beginTurn();
|
|
57
63
|
const wiring = claudeBridgeArgs({
|
|
58
64
|
bridge: this.options.bridge,
|
|
59
65
|
exposedToolNames: this.options.exposedToolNames,
|
|
@@ -94,19 +100,34 @@ export class ClaudeCodeRuntime {
|
|
|
94
100
|
if (trimmed)
|
|
95
101
|
this.log("claude.stderr", { bytes: trimmed.length });
|
|
96
102
|
});
|
|
103
|
+
let text = textWithAttachmentReferences(input);
|
|
104
|
+
if (this.firstTurn && !this.options.resumeRuntimeSessionId && this.options.initialContext) {
|
|
105
|
+
text = `${this.options.initialContext}\n\n<current_user_request>\n${text}\n</current_user_request>`;
|
|
106
|
+
}
|
|
107
|
+
this.firstTurn = false;
|
|
97
108
|
child.stdin.end(text);
|
|
98
109
|
const done = new Promise((resolve) => {
|
|
99
|
-
|
|
110
|
+
let finished = false;
|
|
111
|
+
const finish = (code, error) => {
|
|
112
|
+
if (finished)
|
|
113
|
+
return;
|
|
114
|
+
finished = true;
|
|
100
115
|
lines.close();
|
|
101
116
|
// Clean up the config file that carried the bearer token.
|
|
102
117
|
wiring.cleanup();
|
|
103
118
|
this.child = undefined;
|
|
119
|
+
if (!this.terminalSeen) {
|
|
120
|
+
const detail = error?.message ??
|
|
121
|
+
`Claude Code exited without a terminal result (code ${code ?? "unknown"})`;
|
|
122
|
+
this.emit({ type: "error", error: detail });
|
|
123
|
+
this.emit({ type: "turn_complete", reason: "model_error" });
|
|
124
|
+
}
|
|
104
125
|
resolve();
|
|
105
126
|
};
|
|
106
|
-
child.once("exit", finish);
|
|
127
|
+
child.once("exit", (code) => finish(code));
|
|
107
128
|
child.once("error", (error) => {
|
|
108
129
|
this.log("claude.spawn_failed", { error: error.message.slice(0, 200) });
|
|
109
|
-
finish();
|
|
130
|
+
finish(undefined, error);
|
|
110
131
|
});
|
|
111
132
|
});
|
|
112
133
|
return { done };
|
|
@@ -124,14 +145,9 @@ export class ClaudeCodeRuntime {
|
|
|
124
145
|
return;
|
|
125
146
|
}
|
|
126
147
|
for (const event of this.translator.translate(parsed)) {
|
|
127
|
-
|
|
128
|
-
this.
|
|
129
|
-
|
|
130
|
-
catch (error) {
|
|
131
|
-
this.log("claude.event_handler_failed", {
|
|
132
|
-
error: error instanceof Error ? error.name : "unknown",
|
|
133
|
-
});
|
|
134
|
-
}
|
|
148
|
+
if (event.type === "turn_complete")
|
|
149
|
+
this.terminalSeen = true;
|
|
150
|
+
this.emit(event);
|
|
135
151
|
}
|
|
136
152
|
if (!this.claudeSessionId && this.translator.runtimeSessionId) {
|
|
137
153
|
this.claudeSessionId = this.translator.runtimeSessionId;
|
|
@@ -141,6 +157,16 @@ export class ClaudeCodeRuntime {
|
|
|
141
157
|
});
|
|
142
158
|
}
|
|
143
159
|
}
|
|
160
|
+
emit(event) {
|
|
161
|
+
try {
|
|
162
|
+
this.hooks.onEvent?.(event);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
this.log("claude.event_handler_failed", {
|
|
166
|
+
error: error instanceof Error ? error.name : "unknown",
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
144
170
|
/**
|
|
145
171
|
* Interrupt the active turn.
|
|
146
172
|
*
|
|
@@ -56,6 +56,8 @@ export declare class CodexEventTranslator {
|
|
|
56
56
|
private onTurnCompleted;
|
|
57
57
|
private onError;
|
|
58
58
|
private onAgentDelta;
|
|
59
|
+
private onReasoningDelta;
|
|
60
|
+
private onTokenUsage;
|
|
59
61
|
/**
|
|
60
62
|
* A CodeShell Host Tool call must not produce a card here — `ToolExecutor`
|
|
61
63
|
* already emits one, and two unsynchronised sources for one operation is
|
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
const CODESHELL_MCP_SERVER = "codeshell_tools";
|
|
3
3
|
/** Cap on remembered finished turns — a session is long-lived, the set is not. */
|
|
4
4
|
const MAX_TOMBSTONES = 256;
|
|
5
|
+
/** Thread items that represent observable runtime work rather than prose/state. */
|
|
6
|
+
const TOOL_ITEM_TYPES = new Set([
|
|
7
|
+
"commandExecution",
|
|
8
|
+
"fileChange",
|
|
9
|
+
"mcpToolCall",
|
|
10
|
+
"dynamicToolCall",
|
|
11
|
+
"collabAgentToolCall",
|
|
12
|
+
"webSearch",
|
|
13
|
+
"imageView",
|
|
14
|
+
"sleep",
|
|
15
|
+
"imageGeneration",
|
|
16
|
+
]);
|
|
5
17
|
function asRecord(value) {
|
|
6
18
|
return value && typeof value === "object" && !Array.isArray(value)
|
|
7
19
|
? value
|
|
@@ -69,13 +81,17 @@ export class CodexEventTranslator {
|
|
|
69
81
|
return this.onError(params);
|
|
70
82
|
case "item/agentMessage/delta":
|
|
71
83
|
return this.onAgentDelta(params);
|
|
84
|
+
case "item/reasoning/summaryTextDelta":
|
|
85
|
+
case "item/reasoning/textDelta":
|
|
86
|
+
return this.onReasoningDelta(params);
|
|
87
|
+
case "thread/tokenUsage/updated":
|
|
88
|
+
return this.onTokenUsage(params);
|
|
72
89
|
case "item/started":
|
|
73
90
|
return this.onItemStarted(params);
|
|
74
91
|
case "item/completed":
|
|
75
92
|
return this.onItemCompleted(params);
|
|
76
93
|
default:
|
|
77
|
-
//
|
|
78
|
-
// is either handled elsewhere or deliberately not surfaced.
|
|
94
|
+
// Rate limits, plan updates and MCP status are not chat events.
|
|
79
95
|
return [];
|
|
80
96
|
}
|
|
81
97
|
}
|
|
@@ -118,7 +134,18 @@ export class CodexEventTranslator {
|
|
|
118
134
|
if (turnId)
|
|
119
135
|
this.remember(turnId);
|
|
120
136
|
this.activeTurnId = undefined;
|
|
121
|
-
|
|
137
|
+
const reason = terminalReasonFor(str(turn?.status));
|
|
138
|
+
if (reason === "model_error") {
|
|
139
|
+
const error = asRecord(turn?.error);
|
|
140
|
+
const detail = str(error?.message) ?? str(error?.additionalDetails);
|
|
141
|
+
if (detail) {
|
|
142
|
+
return [
|
|
143
|
+
{ type: "error", error: detail },
|
|
144
|
+
{ type: "turn_complete", reason },
|
|
145
|
+
];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return [{ type: "turn_complete", reason }];
|
|
122
149
|
}
|
|
123
150
|
onError(params) {
|
|
124
151
|
// A retryable error is not terminal. Reporting completion here would close
|
|
@@ -132,7 +159,12 @@ export class CodexEventTranslator {
|
|
|
132
159
|
if (turnId)
|
|
133
160
|
this.remember(turnId);
|
|
134
161
|
this.activeTurnId = undefined;
|
|
135
|
-
|
|
162
|
+
const error = asRecord(params.error);
|
|
163
|
+
const message = str(error?.message) ?? str(error?.additionalDetails) ?? "Codex turn failed";
|
|
164
|
+
return [
|
|
165
|
+
{ type: "error", error: message },
|
|
166
|
+
{ type: "turn_complete", reason: "model_error" },
|
|
167
|
+
];
|
|
136
168
|
}
|
|
137
169
|
onAgentDelta(params) {
|
|
138
170
|
if (this.isStale(str(params.turnId)))
|
|
@@ -140,6 +172,50 @@ export class CodexEventTranslator {
|
|
|
140
172
|
const delta = str(params.delta);
|
|
141
173
|
return delta ? [{ type: "text_delta", text: delta }] : [];
|
|
142
174
|
}
|
|
175
|
+
onReasoningDelta(params) {
|
|
176
|
+
if (this.isStale(str(params.turnId)))
|
|
177
|
+
return [];
|
|
178
|
+
const delta = str(params.delta);
|
|
179
|
+
return delta ? [{ type: "thinking_delta", text: delta }] : [];
|
|
180
|
+
}
|
|
181
|
+
onTokenUsage(params) {
|
|
182
|
+
if (this.isStale(str(params.turnId)))
|
|
183
|
+
return [];
|
|
184
|
+
const usage = asRecord(params.tokenUsage);
|
|
185
|
+
const total = asRecord(usage?.total);
|
|
186
|
+
const last = asRecord(usage?.last);
|
|
187
|
+
const number = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
188
|
+
const promptTokens = number(last?.inputTokens);
|
|
189
|
+
if (promptTokens === undefined)
|
|
190
|
+
return [];
|
|
191
|
+
const cacheReadTokens = number(last?.cachedInputTokens);
|
|
192
|
+
const cumulativePromptTokens = number(total?.inputTokens);
|
|
193
|
+
const cumulativeCacheReadTokens = number(total?.cachedInputTokens);
|
|
194
|
+
const cacheCreationTokens = number(last?.cacheWriteInputTokens);
|
|
195
|
+
const cumulativeCacheCreationTokens = number(total?.cacheWriteInputTokens);
|
|
196
|
+
const completionTokens = number(last?.outputTokens);
|
|
197
|
+
const cumulativeCompletionTokens = number(total?.outputTokens);
|
|
198
|
+
return [
|
|
199
|
+
{
|
|
200
|
+
type: "usage_update",
|
|
201
|
+
promptTokens,
|
|
202
|
+
promptTokensSource: "provider_usage",
|
|
203
|
+
promptTokensConfidence: "high",
|
|
204
|
+
...(cacheReadTokens !== undefined ? { cacheReadTokens } : {}),
|
|
205
|
+
...(cacheCreationTokens !== undefined ? { cacheCreationTokens } : {}),
|
|
206
|
+
singleTurnPromptTokens: promptTokens,
|
|
207
|
+
...(cacheReadTokens !== undefined ? { singleTurnCacheReadTokens: cacheReadTokens } : {}),
|
|
208
|
+
...(cacheCreationTokens !== undefined
|
|
209
|
+
? { singleTurnCacheCreationTokens: cacheCreationTokens }
|
|
210
|
+
: {}),
|
|
211
|
+
...(cumulativePromptTokens !== undefined ? { cumulativePromptTokens } : {}),
|
|
212
|
+
...(cumulativeCacheReadTokens !== undefined ? { cumulativeCacheReadTokens } : {}),
|
|
213
|
+
...(cumulativeCacheCreationTokens !== undefined ? { cumulativeCacheCreationTokens } : {}),
|
|
214
|
+
...(completionTokens !== undefined ? { completionTokens } : {}),
|
|
215
|
+
...(cumulativeCompletionTokens !== undefined ? { cumulativeCompletionTokens } : {}),
|
|
216
|
+
},
|
|
217
|
+
];
|
|
218
|
+
}
|
|
143
219
|
/**
|
|
144
220
|
* A CodeShell Host Tool call must not produce a card here — `ToolExecutor`
|
|
145
221
|
* already emits one, and two unsynchronised sources for one operation is
|
|
@@ -161,6 +237,8 @@ export class CodexEventTranslator {
|
|
|
161
237
|
const type = str(item?.type);
|
|
162
238
|
if (!item || !id || !type)
|
|
163
239
|
return [];
|
|
240
|
+
if (!TOOL_ITEM_TYPES.has(type))
|
|
241
|
+
return [];
|
|
164
242
|
if (this.isCodeshellHostTool(item))
|
|
165
243
|
return [];
|
|
166
244
|
const { id: _id, type: _type, ...args } = item;
|
|
@@ -174,13 +252,33 @@ export class CodexEventTranslator {
|
|
|
174
252
|
const type = str(item?.type);
|
|
175
253
|
if (!item || !id || !type)
|
|
176
254
|
return [];
|
|
255
|
+
if (!TOOL_ITEM_TYPES.has(type))
|
|
256
|
+
return [];
|
|
177
257
|
if (this.isCodeshellHostTool(item))
|
|
178
258
|
return [];
|
|
179
|
-
const
|
|
259
|
+
const rawOutput = item.aggregatedOutput ?? item.output ?? item.text ?? item.result ?? item.changes;
|
|
260
|
+
let output;
|
|
261
|
+
if (typeof rawOutput === "string")
|
|
262
|
+
output = rawOutput;
|
|
263
|
+
else if (rawOutput !== undefined && rawOutput !== null) {
|
|
264
|
+
try {
|
|
265
|
+
output = JSON.stringify(rawOutput);
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
output = String(rawOutput);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const error = asRecord(item.error);
|
|
272
|
+
const errorMessage = str(error?.message) ?? str(item.error);
|
|
180
273
|
return [
|
|
181
274
|
{
|
|
182
275
|
type: "tool_result",
|
|
183
|
-
result: {
|
|
276
|
+
result: {
|
|
277
|
+
id,
|
|
278
|
+
toolName: type,
|
|
279
|
+
...(output !== undefined ? { result: output } : {}),
|
|
280
|
+
...(errorMessage ? { error: errorMessage, isError: true } : {}),
|
|
281
|
+
},
|
|
184
282
|
},
|
|
185
283
|
];
|
|
186
284
|
}
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import type { StreamEvent } from "@cjhyy/code-shell-core/extension";
|
|
19
19
|
import { type AppServerClientOptions } from "./app-server-client.js";
|
|
20
20
|
import { type McpBridgeHandle } from "../shared/mcp-bridge.js";
|
|
21
|
+
import { type ExternalRuntimeTurnInput } from "../turn-input.js";
|
|
21
22
|
export interface CodexRuntimeOptions {
|
|
22
23
|
cwd: string;
|
|
23
24
|
/** CodeShell business session id — the authorization subject, never the thread id. */
|
|
@@ -31,6 +32,9 @@ export interface CodexRuntimeOptions {
|
|
|
31
32
|
*/
|
|
32
33
|
bridgeServerName?: string;
|
|
33
34
|
model?: string;
|
|
35
|
+
resumeRuntimeSessionId?: string;
|
|
36
|
+
initialContext?: string;
|
|
37
|
+
developerInstructions?: string;
|
|
34
38
|
/** Codex sandbox mode. Kebab-case per protocol (`workspace-write`, …). */
|
|
35
39
|
sandbox?: string;
|
|
36
40
|
/** Codex approval policy. Also kebab-case. */
|
|
@@ -59,6 +63,11 @@ export interface CodexRuntimeHooks {
|
|
|
59
63
|
method: string;
|
|
60
64
|
params: unknown;
|
|
61
65
|
}) => Promise<NativeApprovalDecision> | NativeApprovalDecision;
|
|
66
|
+
/** Answer Codex's request_user_input tool through the owning CodeShell UI. */
|
|
67
|
+
onUserInput?: (request: {
|
|
68
|
+
method: string;
|
|
69
|
+
params: unknown;
|
|
70
|
+
}) => Promise<unknown> | unknown;
|
|
62
71
|
}
|
|
63
72
|
export declare class CodexRuntime {
|
|
64
73
|
private readonly options;
|
|
@@ -69,6 +78,8 @@ export declare class CodexRuntime {
|
|
|
69
78
|
private translator?;
|
|
70
79
|
private threadId?;
|
|
71
80
|
private started;
|
|
81
|
+
private resumed;
|
|
82
|
+
private firstTurn;
|
|
72
83
|
private activeTurn?;
|
|
73
84
|
constructor(options: CodexRuntimeOptions, hooks?: CodexRuntimeHooks);
|
|
74
85
|
/** Codex thread id, once the thread exists. Protocol routing only — never the
|
|
@@ -90,7 +101,7 @@ export declare class CodexRuntime {
|
|
|
90
101
|
* tombstones are what keep a late `turn/started` from reactivating a session
|
|
91
102
|
* that already reported terminal.
|
|
92
103
|
*/
|
|
93
|
-
send(
|
|
104
|
+
send(input: ExternalRuntimeTurnInput): Promise<CodexTurnHandle>;
|
|
94
105
|
/**
|
|
95
106
|
* Interrupt the active turn.
|
|
96
107
|
*
|
|
@@ -2,6 +2,7 @@ import { CodexAppServerClient } from "./app-server-client.js";
|
|
|
2
2
|
import { CodexEventTranslator } from "./event-translator.js";
|
|
3
3
|
import { buildRuntimeSpawnEnv } from "../shared/spawn-env.js";
|
|
4
4
|
import { codexBridgeConfigArgs } from "../shared/mcp-bridge.js";
|
|
5
|
+
import { textWithAttachmentReferences } from "../turn-input.js";
|
|
5
6
|
/** A thread/start or turn/start that hangs is worse than one that fails. */
|
|
6
7
|
const CRITICAL_RPC_TIMEOUT_MS = 60_000;
|
|
7
8
|
/** Interrupt is a fail-safe; an unbounded wait silently defeats it. */
|
|
@@ -15,6 +16,8 @@ export class CodexRuntime {
|
|
|
15
16
|
translator;
|
|
16
17
|
threadId;
|
|
17
18
|
started = false;
|
|
19
|
+
resumed = false;
|
|
20
|
+
firstTurn = true;
|
|
18
21
|
activeTurn;
|
|
19
22
|
constructor(options, hooks = {}) {
|
|
20
23
|
this.options = options;
|
|
@@ -57,15 +60,41 @@ export class CodexRuntime {
|
|
|
57
60
|
clientInfo: { name: "codeshell", title: "CodeShell", version: "1" },
|
|
58
61
|
capabilities: { experimentalApi: true },
|
|
59
62
|
}, CRITICAL_RPC_TIMEOUT_MS);
|
|
60
|
-
|
|
63
|
+
let thread;
|
|
64
|
+
if (this.options.resumeRuntimeSessionId) {
|
|
65
|
+
try {
|
|
66
|
+
thread = (await this.client.request("thread/resume", {
|
|
67
|
+
threadId: this.options.resumeRuntimeSessionId,
|
|
68
|
+
cwd: this.options.cwd,
|
|
69
|
+
...(this.options.model ? { model: this.options.model } : {}),
|
|
70
|
+
...(this.options.sandbox ? { sandbox: this.options.sandbox } : {}),
|
|
71
|
+
...(this.options.approvalPolicy ? { approvalPolicy: this.options.approvalPolicy } : {}),
|
|
72
|
+
...(this.options.developerInstructions
|
|
73
|
+
? { developerInstructions: this.options.developerInstructions }
|
|
74
|
+
: {}),
|
|
75
|
+
}, CRITICAL_RPC_TIMEOUT_MS));
|
|
76
|
+
this.resumed = true;
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
this.log("runtime.thread_resume_failed", {
|
|
80
|
+
businessSessionId: this.options.businessSessionId,
|
|
81
|
+
error: error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
thread ??= (await this.client.request("thread/start", {
|
|
61
86
|
cwd: this.options.cwd,
|
|
62
87
|
...(this.options.model ? { model: this.options.model } : {}),
|
|
63
88
|
...(this.options.sandbox ? { sandbox: this.options.sandbox } : {}),
|
|
64
89
|
...(this.options.approvalPolicy ? { approvalPolicy: this.options.approvalPolicy } : {}),
|
|
90
|
+
...(this.options.developerInstructions
|
|
91
|
+
? { developerInstructions: this.options.developerInstructions }
|
|
92
|
+
: {}),
|
|
65
93
|
}, CRITICAL_RPC_TIMEOUT_MS));
|
|
66
94
|
const threadId = thread?.thread?.id;
|
|
67
|
-
if (!threadId)
|
|
68
|
-
throw new Error("thread/start returned no thread id
|
|
95
|
+
if (!threadId) {
|
|
96
|
+
throw new Error(`${this.resumed ? "thread/resume" : "thread/start"} returned no thread id`);
|
|
97
|
+
}
|
|
69
98
|
this.threadId = threadId;
|
|
70
99
|
this.translator = new CodexEventTranslator({
|
|
71
100
|
threadId,
|
|
@@ -74,6 +103,7 @@ export class CodexRuntime {
|
|
|
74
103
|
this.log("runtime.thread_started", {
|
|
75
104
|
businessSessionId: this.options.businessSessionId,
|
|
76
105
|
threadIdPrefix: threadId.slice(0, 8),
|
|
106
|
+
resumed: this.resumed,
|
|
77
107
|
});
|
|
78
108
|
}
|
|
79
109
|
/**
|
|
@@ -84,16 +114,31 @@ export class CodexRuntime {
|
|
|
84
114
|
* tombstones are what keep a late `turn/started` from reactivating a session
|
|
85
115
|
* that already reported terminal.
|
|
86
116
|
*/
|
|
87
|
-
async send(
|
|
117
|
+
async send(input) {
|
|
88
118
|
if (!this.threadId)
|
|
89
119
|
throw new Error("CodexRuntime.send() before start()");
|
|
90
120
|
let resolveDone;
|
|
91
121
|
const done = new Promise((resolve) => (resolveDone = resolve));
|
|
92
122
|
this.activeTurn = { resolve: resolveDone };
|
|
123
|
+
let text = textWithAttachmentReferences(input);
|
|
124
|
+
if (this.firstTurn && !this.resumed && this.options.initialContext) {
|
|
125
|
+
text = `${this.options.initialContext}\n\n<current_user_request>\n${text}\n</current_user_request>`;
|
|
126
|
+
}
|
|
127
|
+
this.firstTurn = false;
|
|
128
|
+
const imageInputs = (input.attachments ?? [])
|
|
129
|
+
.filter((attachment) => attachment.kind === "image" || attachment.mime?.toLowerCase().startsWith("image/"))
|
|
130
|
+
.map((attachment) => ({
|
|
131
|
+
type: "localImage",
|
|
132
|
+
path: attachment.path,
|
|
133
|
+
...(attachment.detail === "low" || attachment.detail === "high"
|
|
134
|
+
? { detail: attachment.detail }
|
|
135
|
+
: {}),
|
|
136
|
+
}));
|
|
93
137
|
const response = (await this.client.request("turn/start", {
|
|
94
138
|
threadId: this.threadId,
|
|
139
|
+
...(input.clientMessageId ? { clientUserMessageId: input.clientMessageId } : {}),
|
|
95
140
|
// `text_elements` is required by the protocol even when empty.
|
|
96
|
-
input: [{ type: "text", text, text_elements: [] }],
|
|
141
|
+
input: [{ type: "text", text, text_elements: [] }, ...imageInputs],
|
|
97
142
|
}, CRITICAL_RPC_TIMEOUT_MS));
|
|
98
143
|
const turnId = response?.turn?.id;
|
|
99
144
|
if (this.activeTurn)
|
|
@@ -171,6 +216,11 @@ export class CodexRuntime {
|
|
|
171
216
|
if (method === "mcpServer/elicitation/request") {
|
|
172
217
|
return this.answerMcpElicitation(params);
|
|
173
218
|
}
|
|
219
|
+
if (method === "item/tool/requestUserInput") {
|
|
220
|
+
return this.hooks.onUserInput
|
|
221
|
+
? await this.hooks.onUserInput({ method, params })
|
|
222
|
+
: { answers: {} };
|
|
223
|
+
}
|
|
174
224
|
if (!method.includes("requestApproval")) {
|
|
175
225
|
// Some other server request (user input, …). Leave unhandled so the client
|
|
176
226
|
// answers method-not-found rather than inventing consent.
|
|
@@ -20,6 +20,8 @@ export { SessionContextStore } from "./shared/session-context-store.js";
|
|
|
20
20
|
export type { ResolveRequest, SessionContextResult, SessionContextMissReason, ToolHostRef, } from "./shared/session-context-store.js";
|
|
21
21
|
export { buildRuntimeSpawnEnv } from "./shared/spawn-env.js";
|
|
22
22
|
export type { RuntimeSpawnEnvOptions } from "./shared/spawn-env.js";
|
|
23
|
+
export { textWithAttachmentReferences } from "./turn-input.js";
|
|
24
|
+
export type { ExternalRuntimeAttachment, ExternalRuntimeTurnInput } from "./turn-input.js";
|
|
23
25
|
export { CodexEventTranslator } from "./codex/event-translator.js";
|
|
24
26
|
export { CodexAppServerClient } from "./codex/app-server-client.js";
|
|
25
27
|
export type { AppServerClientOptions } from "./codex/app-server-client.js";
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
export { CODEX_MCP_TOKEN_ENV_VAR, codexBridgeConfigArgs, startLoopbackMcpBridge, threadIdFromMeta, } from "./shared/mcp-bridge.js";
|
|
18
18
|
export { SessionContextStore } from "./shared/session-context-store.js";
|
|
19
19
|
export { buildRuntimeSpawnEnv } from "./shared/spawn-env.js";
|
|
20
|
+
export { textWithAttachmentReferences } from "./turn-input.js";
|
|
20
21
|
export { CodexEventTranslator } from "./codex/event-translator.js";
|
|
21
22
|
export { CodexAppServerClient } from "./codex/app-server-client.js";
|
|
22
23
|
export { CodexRuntime } from "./codex/runtime.js";
|
|
@@ -23,6 +23,7 @@ import type { PermissionRule, ToolDefinition } from "@cjhyy/code-shell-core/exte
|
|
|
23
23
|
import { createSessionToolHost, type ExternalToolExposurePolicy, type ToolVisibilityInputs } from "@cjhyy/code-shell-core/extension";
|
|
24
24
|
import { type CodexRuntimeHooks, type CodexRuntimeOptions } from "./codex/runtime.js";
|
|
25
25
|
import { type ClaudeRuntimeHooks } from "./claude-code/runtime.js";
|
|
26
|
+
import type { ExternalRuntimeTurnInput } from "./turn-input.js";
|
|
26
27
|
export type ExternalRuntimeKind = "codex" | "claude-code";
|
|
27
28
|
/**
|
|
28
29
|
* Everything the host must decide. Deliberately no defaults for the
|
|
@@ -52,6 +53,12 @@ export interface ExternalRuntimeSessionOptions {
|
|
|
52
53
|
contextOverrides?: Parameters<typeof createSessionToolHost>[0]["contextOverrides"];
|
|
53
54
|
settingsScope?: Parameters<typeof createSessionToolHost>[0]["settingsScope"];
|
|
54
55
|
model?: string;
|
|
56
|
+
/** Runtime thread/session id recovered from durable Desktop state. */
|
|
57
|
+
resumeRuntimeSessionId?: string;
|
|
58
|
+
/** Context replayed only when resume is unavailable and a fresh thread starts. */
|
|
59
|
+
initialContext?: string;
|
|
60
|
+
/** Stable host guidance applied to new and resumed runtime threads. */
|
|
61
|
+
developerInstructions?: string;
|
|
55
62
|
/** Codex only. Kebab-case per protocol. */
|
|
56
63
|
sandbox?: string;
|
|
57
64
|
/** Codex only. Kebab-case per protocol. */
|
|
@@ -76,7 +83,7 @@ export interface ExternalRuntimeSession {
|
|
|
76
83
|
readonly runtimeSessionId: string | undefined;
|
|
77
84
|
/** Tools actually exposed, after the allowlist and visibility guards. */
|
|
78
85
|
listTools(): readonly ToolDefinition[];
|
|
79
|
-
send(
|
|
86
|
+
send(input: ExternalRuntimeTurnInput): Promise<{
|
|
80
87
|
done: Promise<void>;
|
|
81
88
|
}>;
|
|
82
89
|
interrupt(): Promise<void>;
|
|
@@ -66,6 +66,13 @@ export async function startExternalRuntimeSession(options) {
|
|
|
66
66
|
businessSessionId: options.businessSessionId,
|
|
67
67
|
bridge,
|
|
68
68
|
...(options.model ? { model: options.model } : {}),
|
|
69
|
+
...(options.resumeRuntimeSessionId
|
|
70
|
+
? { resumeRuntimeSessionId: options.resumeRuntimeSessionId }
|
|
71
|
+
: {}),
|
|
72
|
+
...(options.initialContext ? { initialContext: options.initialContext } : {}),
|
|
73
|
+
...(options.developerInstructions
|
|
74
|
+
? { developerInstructions: options.developerInstructions }
|
|
75
|
+
: {}),
|
|
69
76
|
...(options.sandbox ? { sandbox: options.sandbox } : {}),
|
|
70
77
|
...(options.approvalPolicy ? { approvalPolicy: options.approvalPolicy } : {}),
|
|
71
78
|
...(options.codexClient ? { client: options.codexClient } : {}),
|
|
@@ -81,6 +88,10 @@ export async function startExternalRuntimeSession(options) {
|
|
|
81
88
|
bridge,
|
|
82
89
|
exposedToolNames,
|
|
83
90
|
...(options.model ? { model: options.model } : {}),
|
|
91
|
+
...(options.resumeRuntimeSessionId
|
|
92
|
+
? { resumeRuntimeSessionId: options.resumeRuntimeSessionId }
|
|
93
|
+
: {}),
|
|
94
|
+
...(options.initialContext ? { initialContext: options.initialContext } : {}),
|
|
84
95
|
...(options.claudeExtraArgs ? { extraArgs: options.claudeExtraArgs } : {}),
|
|
85
96
|
...(options.claudeCommand ? { command: options.claudeCommand } : {}),
|
|
86
97
|
log,
|
|
@@ -103,7 +114,7 @@ export async function startExternalRuntimeSession(options) {
|
|
|
103
114
|
return activeRuntime.runtimeSessionId;
|
|
104
115
|
},
|
|
105
116
|
listTools: () => activeHost.listTools(),
|
|
106
|
-
send: (
|
|
117
|
+
send: (input) => activeRuntime.send(input),
|
|
107
118
|
interrupt: () => activeRuntime.interrupt(),
|
|
108
119
|
close,
|
|
109
120
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Transport-neutral user input for Codex / Claude Code runtimes. */
|
|
2
|
+
export interface ExternalRuntimeAttachment {
|
|
3
|
+
path: string;
|
|
4
|
+
kind?: "image" | "file" | "directory";
|
|
5
|
+
mime?: string;
|
|
6
|
+
detail?: "low" | "standard" | "high";
|
|
7
|
+
}
|
|
8
|
+
export interface ExternalRuntimeTurnInput {
|
|
9
|
+
text: string;
|
|
10
|
+
clientMessageId?: string;
|
|
11
|
+
attachments?: readonly ExternalRuntimeAttachment[];
|
|
12
|
+
}
|
|
13
|
+
export declare function textWithAttachmentReferences(input: ExternalRuntimeTurnInput): string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function textWithAttachmentReferences(input) {
|
|
2
|
+
const references = (input.attachments ?? [])
|
|
3
|
+
.filter((attachment) => attachment.path.trim())
|
|
4
|
+
.map((attachment) => `- ${attachment.kind ?? "file"}: ${attachment.path}`);
|
|
5
|
+
if (references.length === 0)
|
|
6
|
+
return input.text;
|
|
7
|
+
return [
|
|
8
|
+
input.text,
|
|
9
|
+
"",
|
|
10
|
+
"<codeshell_attachments>",
|
|
11
|
+
"The user explicitly attached these local paths. Inspect them when relevant:",
|
|
12
|
+
...references,
|
|
13
|
+
"</codeshell_attachments>",
|
|
14
|
+
].join("\n");
|
|
15
|
+
}
|
package/dist/index.capability.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { BUILTIN_AGENT_PRESETS, BUILTIN_TOOLS, derivePresetExposure, } from "@cjhyy/code-shell-core/extension";
|
|
4
|
-
import { briefTool, briefToolDef } from "./tools/brief.js";
|
|
5
4
|
import { lspTool, lspToolDef } from "./tools/lsp.js";
|
|
6
5
|
import { notebookEditTool, notebookEditToolDef } from "./tools/notebook-edit.js";
|
|
7
6
|
import { applyPatchTool, applyPatchToolDef } from "./tools/apply-patch/index.js";
|
|
@@ -16,6 +15,9 @@ function defineTool(definition, execute, exposure) {
|
|
|
16
15
|
return { definition, execute, exposure };
|
|
17
16
|
}
|
|
18
17
|
export const CODING_TOOLS = [
|
|
18
|
+
// `briefTool` remains a root compatibility export, but is deliberately not
|
|
19
|
+
// model-exposed here: its Markdown return value is a tool result, not a
|
|
20
|
+
// user-facing assistant message (especially important for headless runs).
|
|
19
21
|
defineTool({
|
|
20
22
|
...driveAgentToolDef,
|
|
21
23
|
source: "builtin",
|
|
@@ -92,16 +94,6 @@ export const CODING_TOOLS = [
|
|
|
92
94
|
},
|
|
93
95
|
},
|
|
94
96
|
}, applyPatchTool, { presetTags: ["terminal-coding"] }),
|
|
95
|
-
defineTool({
|
|
96
|
-
...briefToolDef,
|
|
97
|
-
source: "builtin",
|
|
98
|
-
permissionDefault: "allow",
|
|
99
|
-
isReadOnly: true,
|
|
100
|
-
isConcurrencySafe: true,
|
|
101
|
-
}, briefTool, {
|
|
102
|
-
presetTags: ["terminal-coding"],
|
|
103
|
-
defaultPermissionRules: [{ tool: "Brief", decision: "allow" }],
|
|
104
|
-
}),
|
|
105
97
|
defineTool({
|
|
106
98
|
...notebookEditToolDef,
|
|
107
99
|
source: "builtin",
|
package/dist/tools/brief.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Legacy Brief formatter.
|
|
3
|
+
*
|
|
4
|
+
* Kept as a root compatibility export for existing programmatic consumers. It
|
|
5
|
+
* is intentionally not registered in the default coding capability because a
|
|
6
|
+
* tool result is not a user-facing assistant message.
|
|
3
7
|
*/
|
|
4
8
|
import type { ToolDefinition } from "@cjhyy/code-shell-core/extension";
|
|
9
|
+
/** @deprecated Return user-facing Markdown as normal assistant text instead. */
|
|
5
10
|
export declare const briefToolDef: ToolDefinition;
|
|
11
|
+
/** @deprecated Return user-facing Markdown as normal assistant text instead. */
|
|
6
12
|
export declare function briefTool(args: Record<string, unknown>): Promise<string>;
|
package/dist/tools/brief.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Legacy Brief formatter.
|
|
3
|
+
*
|
|
4
|
+
* Kept as a root compatibility export for existing programmatic consumers. It
|
|
5
|
+
* is intentionally not registered in the default coding capability because a
|
|
6
|
+
* tool result is not a user-facing assistant message.
|
|
3
7
|
*/
|
|
8
|
+
/** @deprecated Return user-facing Markdown as normal assistant text instead. */
|
|
4
9
|
export const briefToolDef = {
|
|
5
10
|
name: "Brief",
|
|
6
11
|
description: "Send a structured brief/summary message. Useful for providing concise status updates, " +
|
|
@@ -25,6 +30,7 @@ export const briefToolDef = {
|
|
|
25
30
|
required: ["content"],
|
|
26
31
|
},
|
|
27
32
|
};
|
|
33
|
+
/** @deprecated Return user-facing Markdown as normal assistant text instead. */
|
|
28
34
|
export async function briefTool(args) {
|
|
29
35
|
const title = args.title;
|
|
30
36
|
const content = args.content;
|
|
@@ -107,6 +107,9 @@ export const driveAgentToolDef = {
|
|
|
107
107
|
required: ["prompt"],
|
|
108
108
|
},
|
|
109
109
|
};
|
|
110
|
+
function isExternalRuntimeContext(ctx) {
|
|
111
|
+
return (ctx?.externalRuntime === true);
|
|
112
|
+
}
|
|
110
113
|
const defaultRunner = (opts) => {
|
|
111
114
|
const { adapter, command } = CLI_ADAPTERS[opts.cli];
|
|
112
115
|
return runAgentOnce(adapter, {
|
|
@@ -601,7 +604,7 @@ function trackBackgroundRun(params) {
|
|
|
601
604
|
return { jobId, ...(warning ? { warning } : {}) };
|
|
602
605
|
}
|
|
603
606
|
async function waitForForegroundOrHandoff(run, handoffMs) {
|
|
604
|
-
if (handoffMs < 0) {
|
|
607
|
+
if (!Number.isFinite(handoffMs) || handoffMs < 0) {
|
|
605
608
|
return { kind: "completed", result: await run };
|
|
606
609
|
}
|
|
607
610
|
let timer;
|
|
@@ -651,9 +654,14 @@ export function makeDriveAgentTool(runner = defaultRunner, fixedCli, options = {
|
|
|
651
654
|
args.permissionMode === "acceptEdits" ||
|
|
652
655
|
args.permissionMode === "bypassPermissions"
|
|
653
656
|
? args.permissionMode
|
|
654
|
-
:
|
|
657
|
+
: isExternalRuntimeContext(ctx)
|
|
658
|
+
? "default"
|
|
659
|
+
: "bypassPermissions";
|
|
655
660
|
const isWritableRun = permissionMode !== "default";
|
|
656
|
-
|
|
661
|
+
// External runtimes do not participate in the native Engine's wake-up loop.
|
|
662
|
+
// A detached result would be queued for an Engine that is not driving this
|
|
663
|
+
// turn, so keep delegation attached to the parent runtime.
|
|
664
|
+
const background = isExternalRuntimeContext(ctx) ? false : args.background !== false;
|
|
657
665
|
const cliName = cli === "codex" ? "Codex" : "Claude Code";
|
|
658
666
|
if (background && !isValidSessionId(ctx?.sessionId)) {
|
|
659
667
|
return `Error: cannot start a background ${cliName} job without a session — its result notification would be dropped. Retry with background:false, or ensure the tool runs inside a session.`;
|
|
@@ -801,7 +809,9 @@ export function makeDriveAgentTool(runner = defaultRunner, fixedCli, options = {
|
|
|
801
809
|
permissionMode,
|
|
802
810
|
imagePaths,
|
|
803
811
|
};
|
|
804
|
-
const foregroundHandoffMs =
|
|
812
|
+
const foregroundHandoffMs = isExternalRuntimeContext(ctx)
|
|
813
|
+
? Number.POSITIVE_INFINITY
|
|
814
|
+
: (options.foregroundHandoffMs ?? DRIVE_AGENT_FOREGROUND_HANDOFF_MS);
|
|
805
815
|
if (background) {
|
|
806
816
|
// Fail loud on a missing sessionId: a background job whose completion
|
|
807
817
|
// notification can't be routed (enqueue drops invalid/empty sessionId)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cjhyy/code-shell-capability-coding",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "Coding capability pack for the generic code-shell agent core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\""
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@cjhyy/code-shell-core": "0.8.
|
|
42
|
+
"@cjhyy/code-shell-core": "0.8.3"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
|
45
45
|
"node": ">=20.10"
|