@cjhyy/code-shell-capability-coding 0.8.13 → 0.8.20
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.
|
@@ -10,6 +10,9 @@ export interface AgentRunResult {
|
|
|
10
10
|
export declare function runWithLines(adapter: AgentAdapter, lines: string[], exitCode: number | null): AgentRunResult;
|
|
11
11
|
export interface DriverRunOpts extends Omit<BuildArgsOpts, "permissionMode"> {
|
|
12
12
|
permissionMode?: PermissionMode;
|
|
13
|
+
/** Called once as soon as the external CLI reports its durable session/thread
|
|
14
|
+
* id. This normally happens near process startup, well before the turn exits. */
|
|
15
|
+
onSessionId?: (sessionId: string) => void;
|
|
13
16
|
}
|
|
14
17
|
export declare function detectCodexImageInput(command: string, cwd: string, signal?: AbortSignal): Promise<boolean>;
|
|
15
18
|
/** Spawn ONE headless agent run, collect stream-json to exit, return result.
|
|
@@ -233,6 +233,20 @@ export function runAgentOnce(adapter, opts, signal) {
|
|
|
233
233
|
detached: false,
|
|
234
234
|
stdio: [viaStdin ? "pipe" : "ignore", "pipe", "pipe"],
|
|
235
235
|
});
|
|
236
|
+
let reportedSessionId = "";
|
|
237
|
+
const reportSessionId = (candidate) => {
|
|
238
|
+
if (reportedSessionId || typeof candidate !== "string" || !candidate.trim())
|
|
239
|
+
return;
|
|
240
|
+
reportedSessionId = candidate.trim();
|
|
241
|
+
try {
|
|
242
|
+
opts.onSessionId?.(reportedSessionId);
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
// Session observation is best-effort metadata. A UI/persistence
|
|
246
|
+
// listener must never be able to fail the external agent run.
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
reportSessionId(opts.resumeSessionId);
|
|
236
250
|
let settled = false;
|
|
237
251
|
let abortRequested = false;
|
|
238
252
|
let termination;
|
|
@@ -254,7 +268,18 @@ export function runAgentOnce(adapter, opts, signal) {
|
|
|
254
268
|
const lines = [];
|
|
255
269
|
if (child.stdout) {
|
|
256
270
|
const rl = createInterface({ input: child.stdout });
|
|
257
|
-
rl.on("line", (line) =>
|
|
271
|
+
rl.on("line", (line) => {
|
|
272
|
+
lines.push(line);
|
|
273
|
+
if (!reportedSessionId) {
|
|
274
|
+
try {
|
|
275
|
+
reportSessionId(adapter.parseResult([line]).sessionId);
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
// Keep collecting output. The complete parse at exit remains the
|
|
279
|
+
// source of truth and will surface malformed output normally.
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
});
|
|
258
283
|
}
|
|
259
284
|
child.on("error", (err) => {
|
|
260
285
|
if (settled)
|
|
@@ -276,7 +301,9 @@ export function runAgentOnce(adapter, opts, signal) {
|
|
|
276
301
|
settled = true;
|
|
277
302
|
cleanup();
|
|
278
303
|
void (termination ?? Promise.resolve()).then(() => {
|
|
279
|
-
|
|
304
|
+
const result = runWithLines(adapter, lines, code);
|
|
305
|
+
reportSessionId(result.sessionId);
|
|
306
|
+
resolve(result);
|
|
280
307
|
});
|
|
281
308
|
});
|
|
282
309
|
})().catch(reject);
|
|
@@ -9,5 +9,7 @@ export interface ExternalRuntimeTurnInput {
|
|
|
9
9
|
text: string;
|
|
10
10
|
clientMessageId?: string;
|
|
11
11
|
attachments?: readonly ExternalRuntimeAttachment[];
|
|
12
|
+
/** Host-injected continuation (for example, a background-job completion). */
|
|
13
|
+
injected?: boolean;
|
|
12
14
|
}
|
|
13
15
|
export declare function textWithAttachmentReferences(input: ExternalRuntimeTurnInput): string;
|
|
@@ -18,6 +18,7 @@ type Runner = (opts: {
|
|
|
18
18
|
permissionMode?: PermMode;
|
|
19
19
|
signal?: AbortSignal;
|
|
20
20
|
imagePaths?: string[];
|
|
21
|
+
onSessionId?: (sessionId: string) => void;
|
|
21
22
|
}) => Promise<AgentRunResult>;
|
|
22
23
|
type SessionStore = {
|
|
23
24
|
get(cli: DriveCli, sessionId: string): ExternalAgentSessionBinding | undefined;
|
|
@@ -46,6 +47,7 @@ type LegacyRunner = (opts: {
|
|
|
46
47
|
cwd: string;
|
|
47
48
|
permissionMode?: PermMode;
|
|
48
49
|
signal?: AbortSignal;
|
|
50
|
+
onSessionId?: (sessionId: string) => void;
|
|
49
51
|
}) => Promise<AgentRunResult>;
|
|
50
52
|
export declare function makeDriveClaudeCodeTool(runner?: LegacyRunner, options?: DriveAgentToolOptions): (args: Record<string, unknown>, ctx?: ToolContext) => Promise<string>;
|
|
51
53
|
export declare const driveClaudeCodeTool: (args: Record<string, unknown>, ctx?: ToolContext) => Promise<string>;
|
|
@@ -124,6 +124,7 @@ const defaultRunner = (opts) => {
|
|
|
124
124
|
cwd: opts.cwd,
|
|
125
125
|
permissionMode: opts.permissionMode ?? "default",
|
|
126
126
|
imagePaths: opts.imagePaths,
|
|
127
|
+
onSessionId: opts.onSessionId,
|
|
127
128
|
}, opts.signal);
|
|
128
129
|
};
|
|
129
130
|
function newDriveJobId() {
|
|
@@ -167,8 +168,7 @@ function hasRunningDriveWriter(workspaceCwd, workspaceRoot) {
|
|
|
167
168
|
return true;
|
|
168
169
|
}
|
|
169
170
|
for (const lease of foregroundDriveLeases.values()) {
|
|
170
|
-
if (lease.effectiveWorkspaceRoot === workspaceRoot ||
|
|
171
|
-
lease.workspaceRoot === workspaceRoot) {
|
|
171
|
+
if (lease.effectiveWorkspaceRoot === workspaceRoot || lease.workspaceRoot === workspaceRoot) {
|
|
172
172
|
return true;
|
|
173
173
|
}
|
|
174
174
|
}
|
|
@@ -325,7 +325,8 @@ function formatDriveJobListLine(job) {
|
|
|
325
325
|
`${job.jobId}`,
|
|
326
326
|
`status=${job.status}`,
|
|
327
327
|
`cli=${cli}`,
|
|
328
|
-
`
|
|
328
|
+
`ownerSession=${job.sessionId}`,
|
|
329
|
+
`externalSessionId=${job.ccSessionId ?? "pending"}`,
|
|
329
330
|
`launchCwd=${launchCwd}`,
|
|
330
331
|
...(job.worktreePath ? [`worktree=${job.worktreePath}`] : []),
|
|
331
332
|
...(job.worktreeBranch ? [`branch=${job.worktreeBranch}`] : []),
|
|
@@ -648,7 +649,11 @@ function trackBackgroundRun(params) {
|
|
|
648
649
|
return { error: "Error: DriveAgent owner session closed before the job could start." };
|
|
649
650
|
}
|
|
650
651
|
try {
|
|
651
|
-
run = params.start()
|
|
652
|
+
run = params.start((externalSessionId) => {
|
|
653
|
+
if (!isValidSessionId(externalSessionId))
|
|
654
|
+
return;
|
|
655
|
+
backgroundJobRegistry.recordArtifacts(jobId, { ccSessionId: externalSessionId });
|
|
656
|
+
});
|
|
652
657
|
}
|
|
653
658
|
catch (error) {
|
|
654
659
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -713,10 +718,19 @@ export function makeDriveAgentTool(runner = defaultRunner, fixedCli, options = {
|
|
|
713
718
|
? "default"
|
|
714
719
|
: "bypassPermissions";
|
|
715
720
|
const isWritableRun = permissionMode !== "default";
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
//
|
|
719
|
-
|
|
721
|
+
const externalRuntime = isExternalRuntimeContext(ctx);
|
|
722
|
+
const background = args.background !== false;
|
|
723
|
+
// A detached external-runtime job is safe only when its host explicitly
|
|
724
|
+
// promises to drain notificationQueue and inject the result into a later
|
|
725
|
+
// runtime turn. Silently changing background:true into a foreground wait is
|
|
726
|
+
// incorrect: Codex's yieldable exec wrapper can return control to the model
|
|
727
|
+
// while the nested MCP call remains pending, producing concurrent work that
|
|
728
|
+
// looks as though it came from the delegated agent.
|
|
729
|
+
if (externalRuntime && background && ctx?.externalRuntimeBackgroundDelivery !== true) {
|
|
730
|
+
return (`Error: background ${cli === "codex" ? "Codex" : "Claude Code"} delegation is not ` +
|
|
731
|
+
"supported by this external-runtime host because it cannot deliver the completion. " +
|
|
732
|
+
"Retry with background:false.");
|
|
733
|
+
}
|
|
720
734
|
const cliName = cli === "codex" ? "Codex" : "Claude Code";
|
|
721
735
|
if (background && !isValidSessionId(ctx?.sessionId)) {
|
|
722
736
|
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.`;
|
|
@@ -867,7 +881,7 @@ export function makeDriveAgentTool(runner = defaultRunner, fixedCli, options = {
|
|
|
867
881
|
permissionMode,
|
|
868
882
|
imagePaths,
|
|
869
883
|
};
|
|
870
|
-
const foregroundHandoffMs =
|
|
884
|
+
const foregroundHandoffMs = externalRuntime
|
|
871
885
|
? Number.POSITIVE_INFINITY
|
|
872
886
|
: (options.foregroundHandoffMs ?? DRIVE_AGENT_FOREGROUND_HANDOFF_MS);
|
|
873
887
|
if (background) {
|
|
@@ -888,7 +902,11 @@ export function makeDriveAgentTool(runner = defaultRunner, fixedCli, options = {
|
|
|
888
902
|
isolation,
|
|
889
903
|
worktree: managedWorktree,
|
|
890
904
|
promptSummary,
|
|
891
|
-
start: () => startRun(runner, {
|
|
905
|
+
start: (onSessionId) => startRun(runner, {
|
|
906
|
+
...runOptsBase,
|
|
907
|
+
signal: abortController.signal,
|
|
908
|
+
onSessionId,
|
|
909
|
+
}),
|
|
892
910
|
abort: () => abortController.abort(),
|
|
893
911
|
sessionStore,
|
|
894
912
|
writable: isWritableRun,
|
|
@@ -904,6 +922,11 @@ export function makeDriveAgentTool(runner = defaultRunner, fixedCli, options = {
|
|
|
904
922
|
return [
|
|
905
923
|
resumeNote,
|
|
906
924
|
`已在后台启动 ${cliName}(jobId ${tracked.jobId})。完成后会通知你结果,无需轮询。`,
|
|
925
|
+
...(externalRuntime
|
|
926
|
+
? [
|
|
927
|
+
"除非用户明确要求并行工作,否则不要在本地重复执行已委派的任务;请结束当前回复,等待完成通知触发续接。",
|
|
928
|
+
]
|
|
929
|
+
: []),
|
|
907
930
|
worktreeStartNote(managedWorktree),
|
|
908
931
|
]
|
|
909
932
|
.filter(Boolean)
|
|
@@ -921,7 +944,16 @@ export function makeDriveAgentTool(runner = defaultRunner, fixedCli, options = {
|
|
|
921
944
|
return appendLifecycleNote(lease.error, lifecycle);
|
|
922
945
|
}
|
|
923
946
|
const foregroundAbort = makeAbortController(callerSignal, true);
|
|
924
|
-
|
|
947
|
+
let liveExternalSessionId;
|
|
948
|
+
let backgroundSessionIdSink;
|
|
949
|
+
const run = startRun(runner, {
|
|
950
|
+
...runOptsBase,
|
|
951
|
+
signal: foregroundAbort.signal,
|
|
952
|
+
onSessionId: (sessionId) => {
|
|
953
|
+
liveExternalSessionId = sessionId;
|
|
954
|
+
backgroundSessionIdSink?.(sessionId);
|
|
955
|
+
},
|
|
956
|
+
});
|
|
925
957
|
let result;
|
|
926
958
|
try {
|
|
927
959
|
result = await waitForForegroundOrHandoff(run, foregroundHandoffMs);
|
|
@@ -947,7 +979,12 @@ export function makeDriveAgentTool(runner = defaultRunner, fixedCli, options = {
|
|
|
947
979
|
isolation,
|
|
948
980
|
worktree: managedWorktree,
|
|
949
981
|
promptSummary,
|
|
950
|
-
start: () =>
|
|
982
|
+
start: (onSessionId) => {
|
|
983
|
+
backgroundSessionIdSink = onSessionId;
|
|
984
|
+
if (liveExternalSessionId)
|
|
985
|
+
onSessionId(liveExternalSessionId);
|
|
986
|
+
return run;
|
|
987
|
+
},
|
|
951
988
|
abort: () => foregroundAbort.abort(),
|
|
952
989
|
sessionStore,
|
|
953
990
|
writable: isWritableRun,
|
|
@@ -1100,7 +1137,8 @@ function inspectDriveAgentJob(jobId) {
|
|
|
1100
1137
|
`jobId: ${job.jobId}`,
|
|
1101
1138
|
`status: ${job.status}`,
|
|
1102
1139
|
`cli: ${job.cli ?? "unknown"}`,
|
|
1103
|
-
`
|
|
1140
|
+
`ownerSession: ${job.sessionId}`,
|
|
1141
|
+
`externalSessionId: ${job.ccSessionId ?? "pending"}`,
|
|
1104
1142
|
`launchCwd: ${job.launchCwd ?? job.cwd ?? "(unknown cwd)"}`,
|
|
1105
1143
|
`startedAt: ${new Date(job.startedAt).toISOString()}`,
|
|
1106
1144
|
`duration: ${jobDurationSeconds(job)}`,
|
|
@@ -1121,8 +1159,6 @@ function inspectDriveAgentJob(jobId) {
|
|
|
1121
1159
|
lines.push(`worktreeLifecycle: ${job.worktreeLifecycle}`);
|
|
1122
1160
|
if (job.finishedAt !== undefined)
|
|
1123
1161
|
lines.push(`finishedAt: ${new Date(job.finishedAt).toISOString()}`);
|
|
1124
|
-
if (job.ccSessionId)
|
|
1125
|
-
lines.push(`ccSessionId: ${job.ccSessionId}`);
|
|
1126
1162
|
if (job.changedFiles && job.changedFiles.length > 0) {
|
|
1127
1163
|
lines.push("changedFiles:", ...job.changedFiles.map((file) => `- ${file}`));
|
|
1128
1164
|
}
|
|
@@ -1275,7 +1311,7 @@ export const driveClaudeCodeToolDef = {
|
|
|
1275
1311
|
};
|
|
1276
1312
|
export function makeDriveClaudeCodeTool(runner, options) {
|
|
1277
1313
|
const generic = runner
|
|
1278
|
-
? ({ prompt, resumeSessionId, model, cwd, permissionMode, signal }) => runner({ prompt, resumeSessionId, model, cwd, permissionMode, signal })
|
|
1314
|
+
? ({ prompt, resumeSessionId, model, cwd, permissionMode, signal, onSessionId }) => runner({ prompt, resumeSessionId, model, cwd, permissionMode, signal, onSessionId })
|
|
1279
1315
|
: undefined;
|
|
1280
1316
|
return makeDriveAgentTool(generic ?? defaultRunner, "claude", options);
|
|
1281
1317
|
}
|
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.20",
|
|
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.20"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
|
45
45
|
"node": ">=20.10"
|