@gkorepanov/ccodex 0.4.2 → 0.4.4
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/config/compatibility.json +1 -1
- package/dist/claude/environment.d.ts +1 -0
- package/dist/claude/environment.js +6 -0
- package/dist/claude/environment.js.map +1 -0
- package/dist/claude/modelCatalog.js +2 -1
- package/dist/claude/modelCatalog.js.map +1 -1
- package/dist/claude/service.d.ts +2 -1
- package/dist/claude/service.js +27 -5
- package/dist/claude/service.js.map +1 -1
- package/dist/claude/session/providerRuntime.d.ts +1 -1
- package/dist/claude/session/providerRuntime.js +1 -1
- package/dist/claude/session/providerRuntime.js.map +1 -1
- package/dist/claude/session/providerRuntimeFactory.d.ts +1 -0
- package/dist/claude/session/providerRuntimeFactory.js +6 -3
- package/dist/claude/session/providerRuntimeFactory.js.map +1 -1
- package/dist/claude/session/runtime.d.ts +8 -0
- package/dist/claude/session/runtime.js +33 -5
- package/dist/claude/session/runtime.js.map +1 -1
- package/dist/claude/session/scopeState.d.ts +9 -4
- package/dist/claude/session/scopeState.js +6 -2
- package/dist/claude/session/scopeState.js.map +1 -1
- package/dist/claude/session/session.d.ts +8 -0
- package/dist/claude/session/session.js +262 -59
- package/dist/claude/session/session.js.map +1 -1
- package/dist/claude/session/shellRunner.js +2 -1
- package/dist/claude/session/shellRunner.js.map +1 -1
- package/dist/claude/toolMapper.js +8 -3
- package/dist/claude/toolMapper.js.map +1 -1
- package/dist/config/config.d.ts +1 -0
- package/dist/config/config.js +4 -1
- package/dist/config/config.js.map +1 -1
- package/dist/gateway/clientConnection.js +26 -26
- package/dist/gateway/clientConnection.js.map +1 -1
- package/dist/gateway/subscriptions.d.ts +1 -0
- package/dist/gateway/subscriptions.js +4 -0
- package/dist/gateway/subscriptions.js.map +1 -1
- package/dist/handoff/service.js +27 -11
- package/dist/handoff/service.js.map +1 -1
- package/dist/handoff/store.d.ts +1 -0
- package/dist/handoff/store.js +6 -0
- package/dist/handoff/store.js.map +1 -1
- package/examples/config.toml +1 -0
- package/legal/THIRD_PARTY_NOTICES.md +1 -28
- package/package.json +4 -4
|
@@ -9,7 +9,7 @@ import { ClaudeMailbox, createDeferred, } from "./mailbox.js";
|
|
|
9
9
|
import { remapBoundaries } from "./repository.js";
|
|
10
10
|
import { isImageRead, planSteps, projectToolCompletion, startTool, updateToolInput, } from "../toolMapper.js";
|
|
11
11
|
import { isFileMutationTool, toolPolicy } from "../permissionPolicy.js";
|
|
12
|
-
import { newChildScope, newMainStreamState, scopeProjection, streamItem, toolAt, } from "./scopeState.js";
|
|
12
|
+
import { newChildScope, newMainStreamState, scopeProjection, streamItem, taskUsesProvider, toolAt, } from "./scopeState.js";
|
|
13
13
|
import { systemNoticeText } from "../../gateway/transientNotice.js";
|
|
14
14
|
import { isClaudeStatusCommand } from "../statusCommand.js";
|
|
15
15
|
import { isCCodexStateCommand } from "../../state/stateCommand.js";
|
|
@@ -23,7 +23,9 @@ import { mapUserInput } from "../inputMapper.js";
|
|
|
23
23
|
import { bashCommandActions } from "../commandActions.js";
|
|
24
24
|
import { safeSessionPermissionUpdates } from "./permissionUpdates.js";
|
|
25
25
|
import { StaleClaudeRuntimeSettingsError, createProviderRuntime, providerPermissionMode, providerRuntimeSettings, runtimeTransportSettings, } from "./providerRuntimeFactory.js";
|
|
26
|
+
import { isRetryableClaudeRuntimeStartupError, } from "./providerRuntime.js";
|
|
26
27
|
import { assistantHasTools, backgroundOutputFile, backgroundTaskId, isNoQueryAcknowledgement, serverToolResult, toolResults, } from "./providerFacts.js";
|
|
28
|
+
import { normalizeClaudeModelIdentifier } from "../modelSelection.js";
|
|
27
29
|
const nullSource = { providerEventId: null, providerEventType: null };
|
|
28
30
|
const providerJournalMaxEvents = 20_000;
|
|
29
31
|
const providerJournalMaxBytes = 256 * 1_024 * 1_024;
|
|
@@ -193,10 +195,26 @@ export class ClaudeSession {
|
|
|
193
195
|
}
|
|
194
196
|
async ensureRuntime(reinitialize = false) {
|
|
195
197
|
const owner = await this.requireRuntimeOwner();
|
|
196
|
-
|
|
198
|
+
try {
|
|
199
|
+
await owner.ready;
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
await owner.activation.promise;
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
197
205
|
if (reinitialize)
|
|
198
206
|
await owner.runtime.reinitialize();
|
|
199
207
|
}
|
|
208
|
+
async ensureEphemeralRuntime() {
|
|
209
|
+
try {
|
|
210
|
+
await this.ensureRuntime();
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
if (!isRetryableClaudeRuntimeStartupError(error))
|
|
214
|
+
throw error;
|
|
215
|
+
await this.ensureRuntime();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
200
218
|
async materializeRuntime() {
|
|
201
219
|
for (;;) {
|
|
202
220
|
const claim = await this.submitLineage({
|
|
@@ -558,6 +576,7 @@ export class ClaudeSession {
|
|
|
558
576
|
reasoningSummary: record.reasoningSummary,
|
|
559
577
|
collaborationMode: record.collaborationMode,
|
|
560
578
|
outputSchema: record.outputSchema,
|
|
579
|
+
interactiveQuestions: this.runtimeDependencies.interactiveQuestions ?? true,
|
|
561
580
|
};
|
|
562
581
|
}
|
|
563
582
|
async createRuntimeGeneration(startup) {
|
|
@@ -592,7 +611,8 @@ export class ClaudeSession {
|
|
|
592
611
|
const ready = this.waitForProviderRuntime(runtime);
|
|
593
612
|
await Promise.resolve();
|
|
594
613
|
if (runtime.hasExited) {
|
|
595
|
-
|
|
614
|
+
await ready;
|
|
615
|
+
throw runtime.initializationFailure(new Error(`Claude runtime '${this.threadId}' became unavailable during initialization.`));
|
|
596
616
|
}
|
|
597
617
|
const transportSettings = runtimeTransportSettings(startup);
|
|
598
618
|
const rateLimitGeneration = dependencies.rateLimits.register({
|
|
@@ -650,6 +670,9 @@ export class ClaudeSession {
|
|
|
650
670
|
}),
|
|
651
671
|
]);
|
|
652
672
|
}
|
|
673
|
+
catch (error) {
|
|
674
|
+
throw runtime.initializationFailure(error);
|
|
675
|
+
}
|
|
653
676
|
finally {
|
|
654
677
|
if (timer)
|
|
655
678
|
clearTimeout(timer);
|
|
@@ -1152,7 +1175,6 @@ export class ClaudeSession {
|
|
|
1152
1175
|
}
|
|
1153
1176
|
catch (error) {
|
|
1154
1177
|
failure = error instanceof Error ? error.message : String(error);
|
|
1155
|
-
throw error;
|
|
1156
1178
|
}
|
|
1157
1179
|
finally {
|
|
1158
1180
|
await this.submitProviderProjection(fact.runtimeGeneration, {
|
|
@@ -1164,6 +1186,15 @@ export class ClaudeSession {
|
|
|
1164
1186
|
...(failure ? { error: failure } : {}),
|
|
1165
1187
|
});
|
|
1166
1188
|
}
|
|
1189
|
+
if (failure) {
|
|
1190
|
+
await this.submitProviderProjection(fact.runtimeGeneration, {
|
|
1191
|
+
type: "runtimeExited",
|
|
1192
|
+
runtimeGeneration: fact.runtimeGeneration,
|
|
1193
|
+
message: `Claude provider projection failed: ${failure}`,
|
|
1194
|
+
codexErrorInfo: null,
|
|
1195
|
+
});
|
|
1196
|
+
runtime.beginClose();
|
|
1197
|
+
}
|
|
1167
1198
|
});
|
|
1168
1199
|
}
|
|
1169
1200
|
async projectProviderMessage(projection, runtime, runtimeGeneration, message) {
|
|
@@ -1265,7 +1296,9 @@ export class ClaudeSession {
|
|
|
1265
1296
|
description: message.summary,
|
|
1266
1297
|
outputFile: message.output_file,
|
|
1267
1298
|
}, owner);
|
|
1268
|
-
|
|
1299
|
+
if (owner === this.threadId) {
|
|
1300
|
+
await this.providerSubmitLifecycle(projection, runtimeGeneration, { type: "taskNotification" });
|
|
1301
|
+
}
|
|
1269
1302
|
await this.applyProviderMainStream(projection, runtimeGeneration, {
|
|
1270
1303
|
kind: "taskComplete",
|
|
1271
1304
|
taskId: message.task_id,
|
|
@@ -1321,9 +1354,12 @@ export class ClaudeSession {
|
|
|
1321
1354
|
return "stateOnly";
|
|
1322
1355
|
}
|
|
1323
1356
|
if (message.type === "stream_event") {
|
|
1324
|
-
const
|
|
1325
|
-
if (
|
|
1326
|
-
await this.providerChildStreamEvent(projection, runtimeGeneration,
|
|
1357
|
+
const child = await this.providerChildThreadFor(runtimeGeneration, message.parent_tool_use_id);
|
|
1358
|
+
if (child?.active) {
|
|
1359
|
+
await this.providerChildStreamEvent(projection, runtimeGeneration, child.threadId, message);
|
|
1360
|
+
}
|
|
1361
|
+
else if (child) {
|
|
1362
|
+
return "retainedOnly";
|
|
1327
1363
|
}
|
|
1328
1364
|
else {
|
|
1329
1365
|
await this.providerMainStreamEvent(projection, runtimeGeneration, message);
|
|
@@ -1346,12 +1382,14 @@ export class ClaudeSession {
|
|
|
1346
1382
|
if (message.supersedes?.length) {
|
|
1347
1383
|
await this.providerEvictMessages(projection, runtimeGeneration, message.supersedes);
|
|
1348
1384
|
}
|
|
1349
|
-
const
|
|
1350
|
-
if (
|
|
1351
|
-
const itemIds = await this.providerProjectAssistant(projection, runtimeGeneration, message,
|
|
1352
|
-
await this.providerLinkItems(runtimeGeneration, message.uuid,
|
|
1385
|
+
const child = await this.providerChildThreadFor(runtimeGeneration, message.parent_tool_use_id);
|
|
1386
|
+
if (child?.active) {
|
|
1387
|
+
const itemIds = await this.providerProjectAssistant(projection, runtimeGeneration, message, child.threadId);
|
|
1388
|
+
await this.providerLinkItems(runtimeGeneration, message.uuid, child.threadId, itemIds);
|
|
1353
1389
|
return "projected";
|
|
1354
1390
|
}
|
|
1391
|
+
if (child)
|
|
1392
|
+
return "retainedOnly";
|
|
1355
1393
|
if (activeTurnId) {
|
|
1356
1394
|
if (message.error) {
|
|
1357
1395
|
await this.submitProviderProjection(runtimeGeneration, {
|
|
@@ -1387,14 +1425,16 @@ export class ClaudeSession {
|
|
|
1387
1425
|
if ("uuid" in message && typeof message.uuid === "string") {
|
|
1388
1426
|
runtime.releaseMessage(message.uuid);
|
|
1389
1427
|
}
|
|
1390
|
-
const
|
|
1391
|
-
if (
|
|
1392
|
-
const itemIds = await this.providerProjectToolResults(projection, runtimeGeneration, message,
|
|
1428
|
+
const child = await this.providerChildThreadFor(runtimeGeneration, message.parent_tool_use_id);
|
|
1429
|
+
if (child?.active) {
|
|
1430
|
+
const itemIds = await this.providerProjectToolResults(projection, runtimeGeneration, message, child.threadId);
|
|
1393
1431
|
if ("uuid" in message && typeof message.uuid === "string") {
|
|
1394
|
-
await this.providerLinkItems(runtimeGeneration, message.uuid,
|
|
1432
|
+
await this.providerLinkItems(runtimeGeneration, message.uuid, child.threadId, itemIds);
|
|
1395
1433
|
}
|
|
1396
1434
|
return "projected";
|
|
1397
1435
|
}
|
|
1436
|
+
if (child)
|
|
1437
|
+
return "retainedOnly";
|
|
1398
1438
|
if (activeTurnId && "uuid" in message && typeof message.uuid === "string") {
|
|
1399
1439
|
await this.submitProviderProjection(runtimeGeneration, {
|
|
1400
1440
|
type: "providerBoundary",
|
|
@@ -1717,6 +1757,12 @@ export class ClaudeSession {
|
|
|
1717
1757
|
if (name === "AskUserQuestion") {
|
|
1718
1758
|
return this.askProviderUser(projection, runtimeGeneration, input, options);
|
|
1719
1759
|
}
|
|
1760
|
+
if (providerPermissionMode(settings) === "auto") {
|
|
1761
|
+
return {
|
|
1762
|
+
behavior: "deny",
|
|
1763
|
+
message: options.decisionReason ?? options.description ?? `Claude Auto did not allow tool '${name}'.`,
|
|
1764
|
+
};
|
|
1765
|
+
}
|
|
1720
1766
|
if (name === "ExitPlanMode") {
|
|
1721
1767
|
return {
|
|
1722
1768
|
behavior: "deny",
|
|
@@ -2024,11 +2070,14 @@ export class ClaudeSession {
|
|
|
2024
2070
|
async providerChildThreadFor(runtimeGeneration, providerId) {
|
|
2025
2071
|
if (!providerId)
|
|
2026
2072
|
return undefined;
|
|
2027
|
-
|
|
2073
|
+
const inspection = await this.submitProviderProjection(runtimeGeneration, {
|
|
2028
2074
|
type: "inspectRuntime",
|
|
2029
2075
|
runtimeGeneration,
|
|
2030
2076
|
providerId,
|
|
2031
|
-
})
|
|
2077
|
+
});
|
|
2078
|
+
return inspection.childThreadId
|
|
2079
|
+
? { threadId: inspection.childThreadId, active: inspection.interruptible }
|
|
2080
|
+
: undefined;
|
|
2032
2081
|
}
|
|
2033
2082
|
async providerLinkItems(runtimeGeneration, providerMessageId, ownerThreadId, itemIds) {
|
|
2034
2083
|
await this.submitProviderProjection(runtimeGeneration, {
|
|
@@ -4106,8 +4155,22 @@ export class ClaudeSession {
|
|
|
4106
4155
|
if (command.runtimeGeneration !== this.runtimeGeneration
|
|
4107
4156
|
|| this.interruptFence || this.dropLateFacts)
|
|
4108
4157
|
return undefined;
|
|
4109
|
-
|
|
4110
|
-
|
|
4158
|
+
{
|
|
4159
|
+
const ownerThreadId = command.ownerThreadId ?? this.threadId;
|
|
4160
|
+
if (!this.scopes.has(ownerThreadId)) {
|
|
4161
|
+
if (command.fact.kind === "inspect")
|
|
4162
|
+
return undefined;
|
|
4163
|
+
switch (command.fact.kind) {
|
|
4164
|
+
case "taskStart":
|
|
4165
|
+
case "taskProgress":
|
|
4166
|
+
case "taskMembership":
|
|
4167
|
+
case "taskOutput":
|
|
4168
|
+
case "taskComplete":
|
|
4169
|
+
case "taskStop":
|
|
4170
|
+
return this.projectDetachedTaskFact(command.fact, command.source);
|
|
4171
|
+
}
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4111
4174
|
{
|
|
4112
4175
|
if (command.fact.kind === "taskComplete" && !command.fact.outputDrained) {
|
|
4113
4176
|
const task = this.tasks.get(command.fact.taskId);
|
|
@@ -4331,27 +4394,14 @@ export class ClaudeSession {
|
|
|
4331
4394
|
if (this.dropLateFacts || this.adminOperation && this.adminOperation.kind !== "rename"
|
|
4332
4395
|
|| this.repository.archived(command.threadId))
|
|
4333
4396
|
return false;
|
|
4334
|
-
const
|
|
4335
|
-
if (!
|
|
4397
|
+
const state = this.scopes.get(command.threadId);
|
|
4398
|
+
if (!state || command.requestedTurnId && command.requestedTurnId !== state.turnId
|
|
4399
|
+
|| this.repository.readTurn(command.threadId, state.turnId)?.status !== "inProgress")
|
|
4336
4400
|
return false;
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
?? uuidv7();
|
|
4342
|
-
const params = {
|
|
4343
|
-
error: {
|
|
4344
|
-
message: command.message,
|
|
4345
|
-
codexErrorInfo: command.codexErrorInfo,
|
|
4346
|
-
additionalDetails: null,
|
|
4347
|
-
},
|
|
4348
|
-
willRetry: false,
|
|
4349
|
-
threadId: command.threadId,
|
|
4350
|
-
turnId,
|
|
4351
|
-
};
|
|
4352
|
-
if (this.repository.appendEvent(command.threadId, turnId, "error", params)) {
|
|
4353
|
-
this.output.emit(command.threadId, "error", params);
|
|
4354
|
-
}
|
|
4401
|
+
this.projectMainStream(command.threadId, {
|
|
4402
|
+
kind: "instantAgent",
|
|
4403
|
+
text: systemNoticeText(command.message, "error"),
|
|
4404
|
+
}, nullSource);
|
|
4355
4405
|
return true;
|
|
4356
4406
|
}
|
|
4357
4407
|
case "startCompact": {
|
|
@@ -5188,8 +5238,13 @@ export class ClaudeSession {
|
|
|
5188
5238
|
this.pendingNoQuery = this.pendingInputs = 0;
|
|
5189
5239
|
this.preparedRuntimeInputs.clear();
|
|
5190
5240
|
this.disposeRuntimeOperations();
|
|
5191
|
-
if (active)
|
|
5241
|
+
if (active) {
|
|
5192
5242
|
active.commandCompleted = true;
|
|
5243
|
+
active.acknowledged = active.notifications;
|
|
5244
|
+
active.goals = 0;
|
|
5245
|
+
active.goalInFlight = false;
|
|
5246
|
+
delete active.request;
|
|
5247
|
+
}
|
|
5193
5248
|
this.settleForcedScopes(source);
|
|
5194
5249
|
if (fact.type === "runtimeExit" && generation !== undefined) {
|
|
5195
5250
|
this.runtimeGeneration = undefined;
|
|
@@ -5199,8 +5254,10 @@ export class ClaudeSession {
|
|
|
5199
5254
|
else if (fact.type === "timer" && active?.result && this.hasNotifications()
|
|
5200
5255
|
&& fact.generation === active.notifications && fact.generation > active.diagnosed) {
|
|
5201
5256
|
active.diagnosed = fact.generation;
|
|
5202
|
-
this.
|
|
5203
|
-
|
|
5257
|
+
this.runtimeDependencies?.logger.warn("claude.lifecycle.notification-unacknowledged", {
|
|
5258
|
+
threadId: this.threadId,
|
|
5259
|
+
generation: fact.generation,
|
|
5260
|
+
});
|
|
5204
5261
|
}
|
|
5205
5262
|
this.maybeFinish(source, fact.type === "sync");
|
|
5206
5263
|
}
|
|
@@ -5759,7 +5816,7 @@ export class ClaudeSession {
|
|
|
5759
5816
|
let owner = this.toolOwner(command.providerId);
|
|
5760
5817
|
if (!owner && command.ownerProviderId) {
|
|
5761
5818
|
owner = this.toolOwner(command.ownerProviderId);
|
|
5762
|
-
const task = [...this.tasks.values()].find((candidate) => candidate
|
|
5819
|
+
const task = [...this.tasks.values()].find((candidate) => taskUsesProvider(candidate, command.ownerProviderId) || candidate.taskId === command.ownerProviderId);
|
|
5763
5820
|
if (task?.childThreadId)
|
|
5764
5821
|
owner = this.scopes.get(task.childThreadId);
|
|
5765
5822
|
}
|
|
@@ -5892,11 +5949,12 @@ export class ClaudeSession {
|
|
|
5892
5949
|
}
|
|
5893
5950
|
let ownerThreadId = command.ownerThreadId ?? this.threadId;
|
|
5894
5951
|
let targetThreadId = ownerThreadId;
|
|
5952
|
+
let providerTask;
|
|
5895
5953
|
if (command.providerId) {
|
|
5896
5954
|
const scope = [...this.scopes.values()].find((candidate) => candidate.tools.has(command.providerId));
|
|
5897
|
-
|
|
5898
|
-
ownerThreadId =
|
|
5899
|
-
targetThreadId = ownerThreadId;
|
|
5955
|
+
providerTask = [...this.tasks.values()].find((candidate) => taskUsesProvider(candidate, command.providerId) || candidate.taskId === command.providerId);
|
|
5956
|
+
ownerThreadId = providerTask?.ownerThreadId ?? scope?.ownerThreadId ?? ownerThreadId;
|
|
5957
|
+
targetThreadId = providerTask?.childThreadId ?? ownerThreadId;
|
|
5900
5958
|
}
|
|
5901
5959
|
const childTask = command.childThreadId
|
|
5902
5960
|
? [...this.tasks.values()].find((candidate) => candidate.childThreadId === command.childThreadId)
|
|
@@ -5923,12 +5981,14 @@ export class ClaudeSession {
|
|
|
5923
5981
|
modelContextWindow: this.requireRecord(false).modelContextWindow,
|
|
5924
5982
|
interruptible: command.expectedTurnId
|
|
5925
5983
|
? targetState?.turnId === command.expectedTurnId
|
|
5926
|
-
: childTask ? !childTask.terminal && Boolean(targetState)
|
|
5984
|
+
: childTask ? !childTask.terminal && Boolean(targetState)
|
|
5985
|
+
: providerTask?.childThreadId ? !providerTask.terminal && Boolean(targetState)
|
|
5986
|
+
: Boolean(targetState),
|
|
5927
5987
|
ownerThreadId,
|
|
5928
5988
|
ownerTurnId: ownerState?.turnId ?? null,
|
|
5929
5989
|
taskIds,
|
|
5930
5990
|
childThreadId: command.providerId
|
|
5931
|
-
? [...this.tasks.values()].find((task) => task
|
|
5991
|
+
? [...this.tasks.values()].find((task) => taskUsesProvider(task, command.providerId) || task.taskId === command.providerId)?.childThreadId ?? null
|
|
5932
5992
|
: null,
|
|
5933
5993
|
taskId: childTask?.taskId ?? null,
|
|
5934
5994
|
quiescent: this.isQuiescent(),
|
|
@@ -6346,8 +6406,9 @@ export class ClaudeSession {
|
|
|
6346
6406
|
const existing = tool && turn.items.find((item) => item.id === tool.itemId);
|
|
6347
6407
|
const subagent = Boolean(fact.subagentType) || fact.taskType === "agent" || fact.taskType === "subagent";
|
|
6348
6408
|
if (!task && !existing && !subagent) {
|
|
6409
|
+
const providerId = fact.providerId ?? fact.taskId;
|
|
6349
6410
|
task = { taskId: fact.taskId, ownerThreadId: state.ownerThreadId,
|
|
6350
|
-
itemId: `claude-task:${fact.taskId}`, providerId:
|
|
6411
|
+
itemId: `claude-task:${fact.taskId}`, providerId, providerIds: new Set([providerId]),
|
|
6351
6412
|
turnId: turn.id, childThreadId: undefined, outputFile: fact.outputFile, terminal: false };
|
|
6352
6413
|
this.tasks.set(task.taskId, task);
|
|
6353
6414
|
}
|
|
@@ -6357,8 +6418,9 @@ export class ClaudeSession {
|
|
|
6357
6418
|
status: "inProgress", senderThreadId: state.ownerThreadId, receiverThreadIds: [],
|
|
6358
6419
|
prompt: fact.prompt ?? fact.description, model: null, reasoningEffort: null, agentsStates: {},
|
|
6359
6420
|
};
|
|
6421
|
+
const providerId = fact.providerId ?? item.id;
|
|
6360
6422
|
const createdTask = { taskId: fact.taskId, ownerThreadId: state.ownerThreadId, itemId: item.id,
|
|
6361
|
-
providerId:
|
|
6423
|
+
providerId, providerIds: new Set([providerId]), turnId: turn.id, childThreadId: undefined,
|
|
6362
6424
|
outputFile: fact.outputFile, terminal: false };
|
|
6363
6425
|
task = createdTask;
|
|
6364
6426
|
this.tasks.set(createdTask.taskId, createdTask);
|
|
@@ -6369,12 +6431,32 @@ export class ClaudeSession {
|
|
|
6369
6431
|
}, source);
|
|
6370
6432
|
}
|
|
6371
6433
|
if (subagent && item.type === "collabAgentToolCall") {
|
|
6372
|
-
|
|
6434
|
+
const requestedModel = typeof tool?.input.model === "string" ? tool.input.model : undefined;
|
|
6435
|
+
childThread = this.createChildScope(state.ownerThreadId, fact, source, requestedModel);
|
|
6373
6436
|
task.childThreadId = childThread.id;
|
|
6374
6437
|
item.receiverThreadIds = [childThread.id];
|
|
6375
6438
|
item.agentsStates = { [childThread.id]: { status: "running", message: fact.description } };
|
|
6376
6439
|
}
|
|
6377
6440
|
}
|
|
6441
|
+
const resumed = task?.terminal && subagent && task.childThreadId && fact.providerId
|
|
6442
|
+
&& !taskUsesProvider(task, fact.providerId);
|
|
6443
|
+
if (resumed && task?.childThreadId) {
|
|
6444
|
+
childThread = this.resumeChildScope(task.childThreadId, fact, source);
|
|
6445
|
+
task.terminal = false;
|
|
6446
|
+
task.providerId = fact.providerId;
|
|
6447
|
+
task.providerIds.add(fact.providerId);
|
|
6448
|
+
task.itemId = existing?.id ?? task.itemId;
|
|
6449
|
+
task.turnId = turn.id;
|
|
6450
|
+
task.outputFile = fact.outputFile;
|
|
6451
|
+
if (existing?.type === "collabAgentToolCall") {
|
|
6452
|
+
existing.receiverThreadIds = [task.childThreadId];
|
|
6453
|
+
existing.agentsStates = {
|
|
6454
|
+
[task.childThreadId]: { status: "running", message: fact.description },
|
|
6455
|
+
};
|
|
6456
|
+
if (tool)
|
|
6457
|
+
this.beginTool(turn, state, tool, true, source);
|
|
6458
|
+
}
|
|
6459
|
+
}
|
|
6378
6460
|
if (task && tool) {
|
|
6379
6461
|
tool.backgroundTaskId = task.taskId;
|
|
6380
6462
|
if (task.outputFile)
|
|
@@ -6402,8 +6484,24 @@ export class ClaudeSession {
|
|
|
6402
6484
|
handled = taskIds.length > 0;
|
|
6403
6485
|
}
|
|
6404
6486
|
else if (task) {
|
|
6405
|
-
const ownerState = this.
|
|
6487
|
+
const ownerState = this.scopes.get(task.ownerThreadId);
|
|
6406
6488
|
const ownerTurn = this.repository.readTurn(task.ownerThreadId, task.turnId);
|
|
6489
|
+
if (!ownerState || !ownerTurn || ownerTurn.status !== "inProgress") {
|
|
6490
|
+
if (fact.kind === "taskComplete" && !task.terminal) {
|
|
6491
|
+
this.stopBackgroundTailer(task);
|
|
6492
|
+
task.outputFile = fact.outputFile ?? task.outputFile;
|
|
6493
|
+
task.terminal = true;
|
|
6494
|
+
if (task.childThreadId)
|
|
6495
|
+
this.finishChildScope(task.childThreadId, fact.status, fact.summary, source);
|
|
6496
|
+
}
|
|
6497
|
+
return scopeProjection(turn, {
|
|
6498
|
+
handled: fact.kind === "taskComplete",
|
|
6499
|
+
taskIds: [task.taskId],
|
|
6500
|
+
outputFile: task.outputFile,
|
|
6501
|
+
childThreadId: task.childThreadId,
|
|
6502
|
+
terminal: task.terminal,
|
|
6503
|
+
});
|
|
6504
|
+
}
|
|
6407
6505
|
const item = ownerTurn.items.find((candidate) => candidate.id === task.itemId);
|
|
6408
6506
|
if (fact.kind === "taskProgress") {
|
|
6409
6507
|
if (item?.type === "collabAgentToolCall")
|
|
@@ -6444,10 +6542,12 @@ export class ClaudeSession {
|
|
|
6444
6542
|
status: fact.status === "completed" ? "completed" : fact.status === "stopped" ? "interrupted" : "errored",
|
|
6445
6543
|
message: fact.summary,
|
|
6446
6544
|
};
|
|
6447
|
-
ownerState.completedItems.
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6545
|
+
if (item.tool !== "sendInput" || !ownerState.completedItems.has(item.id)) {
|
|
6546
|
+
ownerState.completedItems.add(item.id);
|
|
6547
|
+
this.publishTurn(ownerTurn, "item/completed", {
|
|
6548
|
+
item, threadId: task.ownerThreadId, turnId: ownerTurn.id, completedAtMs: Date.now(),
|
|
6549
|
+
}, source);
|
|
6550
|
+
}
|
|
6451
6551
|
}
|
|
6452
6552
|
if (task.childThreadId)
|
|
6453
6553
|
this.finishChildScope(task.childThreadId, fact.status, fact.summary, source);
|
|
@@ -6488,11 +6588,49 @@ export class ClaudeSession {
|
|
|
6488
6588
|
tool: task ? this.scopes.get(task.ownerThreadId)?.tools.get(task.providerId) : undefined,
|
|
6489
6589
|
});
|
|
6490
6590
|
}
|
|
6591
|
+
projectDetachedTaskFact(fact, source) {
|
|
6592
|
+
if (!("taskId" in fact))
|
|
6593
|
+
return undefined;
|
|
6594
|
+
const task = this.tasks.get(fact.taskId);
|
|
6595
|
+
if (!task)
|
|
6596
|
+
return undefined;
|
|
6597
|
+
const turn = this.repository.readTurn(task.ownerThreadId, task.turnId);
|
|
6598
|
+
if (!turn)
|
|
6599
|
+
return undefined;
|
|
6600
|
+
if (fact.kind === "taskStart") {
|
|
6601
|
+
task.outputFile = fact.outputFile ?? task.outputFile;
|
|
6602
|
+
}
|
|
6603
|
+
else if (fact.kind === "taskComplete" && !task.terminal) {
|
|
6604
|
+
this.stopBackgroundTailer(task);
|
|
6605
|
+
task.outputFile = fact.outputFile ?? task.outputFile;
|
|
6606
|
+
task.terminal = true;
|
|
6607
|
+
if (task.childThreadId)
|
|
6608
|
+
this.finishChildScope(task.childThreadId, fact.status, fact.summary, source);
|
|
6609
|
+
}
|
|
6610
|
+
return scopeProjection(turn, {
|
|
6611
|
+
handled: true,
|
|
6612
|
+
taskIds: [task.taskId],
|
|
6613
|
+
outputFile: task.outputFile,
|
|
6614
|
+
childThreadId: task.childThreadId,
|
|
6615
|
+
terminal: task.terminal,
|
|
6616
|
+
});
|
|
6617
|
+
}
|
|
6491
6618
|
updateTool(turn, state, tool, input, source) {
|
|
6492
6619
|
const item = turn.items.find((candidate) => candidate.id === tool.itemId);
|
|
6493
6620
|
if (!item)
|
|
6494
6621
|
return;
|
|
6495
6622
|
updateToolInput(item, tool, input, state.record.thread.cwd);
|
|
6623
|
+
if (item.type === "collabAgentToolCall" && item.tool === "sendInput") {
|
|
6624
|
+
const target = typeof input.to === "string" ? input.to
|
|
6625
|
+
: typeof input.recipient === "string" ? input.recipient : undefined;
|
|
6626
|
+
const task = target ? this.tasks.get(target) : undefined;
|
|
6627
|
+
if (task?.childThreadId) {
|
|
6628
|
+
item.receiverThreadIds = [task.childThreadId];
|
|
6629
|
+
item.agentsStates = {
|
|
6630
|
+
[task.childThreadId]: { status: task.terminal ? "completed" : "running", message: null },
|
|
6631
|
+
};
|
|
6632
|
+
}
|
|
6633
|
+
}
|
|
6496
6634
|
if (tool.name === "TaskOutput") {
|
|
6497
6635
|
const id = typeof input.task_id === "string" ? input.task_id
|
|
6498
6636
|
: typeof input.taskId === "string" ? input.taskId : undefined;
|
|
@@ -6511,6 +6649,9 @@ export class ClaudeSession {
|
|
|
6511
6649
|
if (!item || tool.started || tool.foldedTaskId || isImageRead(tool, record.thread.cwd)
|
|
6512
6650
|
|| item.type === "commandExecution" && !item.command)
|
|
6513
6651
|
return;
|
|
6652
|
+
if (item.type === "collabAgentToolCall" && item.tool === "sendInput"
|
|
6653
|
+
&& (!item.prompt || item.receiverThreadIds.length === 0))
|
|
6654
|
+
return;
|
|
6514
6655
|
if (tool.name === "Bash" && tool.input.run_in_background === true && !tool.backgroundTaskId)
|
|
6515
6656
|
return;
|
|
6516
6657
|
if (!force
|
|
@@ -6536,6 +6677,16 @@ export class ClaudeSession {
|
|
|
6536
6677
|
const projection = projectToolCompletion(item, tool, output, isError, result, state.record.thread.cwd);
|
|
6537
6678
|
turn.items[index] = projection.completed;
|
|
6538
6679
|
state.completedItems.add(item.id);
|
|
6680
|
+
if (projection.completed.type === "collabAgentToolCall"
|
|
6681
|
+
&& projection.completed.tool === "spawnAgent" && projection.completed.model) {
|
|
6682
|
+
const childThreadId = projection.completed.receiverThreadIds[0];
|
|
6683
|
+
const child = childThreadId ? this.repository.read(childThreadId, false) : undefined;
|
|
6684
|
+
if (child)
|
|
6685
|
+
this.repository.update({
|
|
6686
|
+
...child,
|
|
6687
|
+
resolvedModel: normalizeClaudeModelIdentifier(projection.completed.model),
|
|
6688
|
+
});
|
|
6689
|
+
}
|
|
6539
6690
|
if (!tool.started) {
|
|
6540
6691
|
tool.started = true;
|
|
6541
6692
|
this.publishTurn(turn, "item/started", {
|
|
@@ -6548,9 +6699,17 @@ export class ClaudeSession {
|
|
|
6548
6699
|
item: projection.completed, threadId: state.ownerThreadId, turnId: turn.id, completedAtMs: Date.now(),
|
|
6549
6700
|
}, source);
|
|
6550
6701
|
}
|
|
6551
|
-
createChildScope(parentThreadId, fact, source) {
|
|
6702
|
+
createChildScope(parentThreadId, fact, source, requestedModel) {
|
|
6552
6703
|
const parent = this.repository.read(parentThreadId, false);
|
|
6553
|
-
const
|
|
6704
|
+
const normalized = requestedModel && requestedModel !== "inherit"
|
|
6705
|
+
? normalizeClaudeModelIdentifier(requestedModel) : undefined;
|
|
6706
|
+
const model = normalized
|
|
6707
|
+
? this.runtimeDependencies?.resolveChildModel?.(normalized) ?? {
|
|
6708
|
+
modelPickerId: `claude:${normalized}`,
|
|
6709
|
+
claudeModelValue: normalized,
|
|
6710
|
+
}
|
|
6711
|
+
: undefined;
|
|
6712
|
+
const { record, turn, item } = newChildScope(parent, parentThreadId, fact, model);
|
|
6554
6713
|
const { thread } = record;
|
|
6555
6714
|
const childThreadId = thread.id;
|
|
6556
6715
|
this.repository.create(record);
|
|
@@ -6565,6 +6724,50 @@ export class ClaudeSession {
|
|
|
6565
6724
|
this.publishAt(childThreadId, turn.id, "item/completed", { item, threadId: childThreadId, turnId: turn.id, completedAtMs: Date.now() }, source);
|
|
6566
6725
|
return thread;
|
|
6567
6726
|
}
|
|
6727
|
+
resumeChildScope(childThreadId, fact, source) {
|
|
6728
|
+
const record = this.repository.read(childThreadId, false);
|
|
6729
|
+
if (!record)
|
|
6730
|
+
throw new Error(`Unknown Claude child thread '${childThreadId}'.`);
|
|
6731
|
+
const startedAt = Math.floor(Date.now() / 1_000);
|
|
6732
|
+
const text = fact.prompt ?? fact.description;
|
|
6733
|
+
const item = {
|
|
6734
|
+
type: "userMessage", id: uuidv7(), clientId: null,
|
|
6735
|
+
content: [{ type: "text", text, text_elements: [] }],
|
|
6736
|
+
};
|
|
6737
|
+
const turn = {
|
|
6738
|
+
id: uuidv7(), items: [item], itemsView: "full", status: "inProgress", error: null,
|
|
6739
|
+
startedAt, completedAt: null, durationMs: null,
|
|
6740
|
+
};
|
|
6741
|
+
const updated = {
|
|
6742
|
+
...record,
|
|
6743
|
+
thread: {
|
|
6744
|
+
...record.thread,
|
|
6745
|
+
preview: text,
|
|
6746
|
+
status: { type: "active", activeFlags: [] },
|
|
6747
|
+
updatedAt: startedAt,
|
|
6748
|
+
recencyAt: startedAt,
|
|
6749
|
+
},
|
|
6750
|
+
};
|
|
6751
|
+
const status = { threadId: childThreadId, status: updated.thread.status };
|
|
6752
|
+
this.commitState(updated, [{
|
|
6753
|
+
turnId: turn.id,
|
|
6754
|
+
method: "thread/status/changed",
|
|
6755
|
+
params: status,
|
|
6756
|
+
providerEventId: source.providerEventId,
|
|
6757
|
+
providerEventType: source.providerEventType,
|
|
6758
|
+
}], turn, true);
|
|
6759
|
+
const state = newMainStreamState(childThreadId, turn.id, updated);
|
|
6760
|
+
state.completedItems.add(item.id);
|
|
6761
|
+
this.scopes.set(childThreadId, state);
|
|
6762
|
+
this.publishAt(childThreadId, turn.id, "turn/started", { threadId: childThreadId, turn }, source);
|
|
6763
|
+
this.publishAt(childThreadId, turn.id, "item/started", {
|
|
6764
|
+
item, threadId: childThreadId, turnId: turn.id, startedAtMs: Date.now(),
|
|
6765
|
+
}, source);
|
|
6766
|
+
this.publishAt(childThreadId, turn.id, "item/completed", {
|
|
6767
|
+
item, threadId: childThreadId, turnId: turn.id, completedAtMs: Date.now(),
|
|
6768
|
+
}, source);
|
|
6769
|
+
return updated.thread;
|
|
6770
|
+
}
|
|
6568
6771
|
finishChildScope(childThreadId, status, summary, source) {
|
|
6569
6772
|
const state = this.scopes.get(childThreadId);
|
|
6570
6773
|
const record = this.repository.read(childThreadId, false);
|