@gkorepanov/ccodex 0.4.1 → 0.4.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/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 +1 -1
- package/dist/claude/service.js +21 -7
- 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 +2 -0
- package/dist/claude/session/providerRuntimeFactory.js +7 -4
- 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 +5 -3
- package/dist/claude/session/scopeState.js +3 -0
- package/dist/claude/session/scopeState.js.map +1 -1
- package/dist/claude/session/session.d.ts +5 -0
- package/dist/claude/session/session.js +242 -56
- 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 +2 -0
- package/dist/config/config.js +7 -1
- package/dist/config/config.js.map +1 -1
- package/dist/gateway/clientConnection.d.ts +2 -1
- package/dist/gateway/clientConnection.js +45 -35
- package/dist/gateway/clientConnection.js.map +1 -1
- package/dist/gateway/server.js +8 -1
- package/dist/gateway/server.js.map +1 -1
- package/dist/gateway/stockSideThreads.d.ts +37 -0
- package/dist/gateway/stockSideThreads.js +246 -0
- package/dist/gateway/stockSideThreads.js.map +1 -0
- package/dist/gateway/threadList.d.ts +3 -2
- package/dist/gateway/threadList.js +12 -6
- package/dist/gateway/threadList.js.map +1 -1
- package/examples/config.toml +2 -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,6 +23,7 @@ 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";
|
|
27
28
|
const nullSource = { providerEventId: null, providerEventType: null };
|
|
28
29
|
const providerJournalMaxEvents = 20_000;
|
|
@@ -193,10 +194,26 @@ export class ClaudeSession {
|
|
|
193
194
|
}
|
|
194
195
|
async ensureRuntime(reinitialize = false) {
|
|
195
196
|
const owner = await this.requireRuntimeOwner();
|
|
196
|
-
|
|
197
|
+
try {
|
|
198
|
+
await owner.ready;
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
await owner.activation.promise;
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
197
204
|
if (reinitialize)
|
|
198
205
|
await owner.runtime.reinitialize();
|
|
199
206
|
}
|
|
207
|
+
async ensureEphemeralRuntime() {
|
|
208
|
+
try {
|
|
209
|
+
await this.ensureRuntime();
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
if (!isRetryableClaudeRuntimeStartupError(error))
|
|
213
|
+
throw error;
|
|
214
|
+
await this.ensureRuntime();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
200
217
|
async materializeRuntime() {
|
|
201
218
|
for (;;) {
|
|
202
219
|
const claim = await this.submitLineage({
|
|
@@ -539,6 +556,9 @@ export class ClaudeSession {
|
|
|
539
556
|
resume: resumeOverride ?? record.lastClaudeMessageUuid !== null,
|
|
540
557
|
cwd: record.thread.cwd,
|
|
541
558
|
ephemeral: record.thread.ephemeral,
|
|
559
|
+
persistSession: !record.thread.ephemeral || Boolean(this.runtimeDependencies.persistUserSideSessions
|
|
560
|
+
&& !record.thread.parentThreadId
|
|
561
|
+
&& record.thread.threadSource === "user"),
|
|
542
562
|
claudeBinary: this.runtimeDependencies.claudeBinary,
|
|
543
563
|
model: record.claudeModelValue,
|
|
544
564
|
settingsGeneration: settingsGeneration(record),
|
|
@@ -555,6 +575,7 @@ export class ClaudeSession {
|
|
|
555
575
|
reasoningSummary: record.reasoningSummary,
|
|
556
576
|
collaborationMode: record.collaborationMode,
|
|
557
577
|
outputSchema: record.outputSchema,
|
|
578
|
+
interactiveQuestions: this.runtimeDependencies.interactiveQuestions ?? true,
|
|
558
579
|
};
|
|
559
580
|
}
|
|
560
581
|
async createRuntimeGeneration(startup) {
|
|
@@ -589,7 +610,8 @@ export class ClaudeSession {
|
|
|
589
610
|
const ready = this.waitForProviderRuntime(runtime);
|
|
590
611
|
await Promise.resolve();
|
|
591
612
|
if (runtime.hasExited) {
|
|
592
|
-
|
|
613
|
+
await ready;
|
|
614
|
+
throw runtime.initializationFailure(new Error(`Claude runtime '${this.threadId}' became unavailable during initialization.`));
|
|
593
615
|
}
|
|
594
616
|
const transportSettings = runtimeTransportSettings(startup);
|
|
595
617
|
const rateLimitGeneration = dependencies.rateLimits.register({
|
|
@@ -647,6 +669,9 @@ export class ClaudeSession {
|
|
|
647
669
|
}),
|
|
648
670
|
]);
|
|
649
671
|
}
|
|
672
|
+
catch (error) {
|
|
673
|
+
throw runtime.initializationFailure(error);
|
|
674
|
+
}
|
|
650
675
|
finally {
|
|
651
676
|
if (timer)
|
|
652
677
|
clearTimeout(timer);
|
|
@@ -1149,7 +1174,6 @@ export class ClaudeSession {
|
|
|
1149
1174
|
}
|
|
1150
1175
|
catch (error) {
|
|
1151
1176
|
failure = error instanceof Error ? error.message : String(error);
|
|
1152
|
-
throw error;
|
|
1153
1177
|
}
|
|
1154
1178
|
finally {
|
|
1155
1179
|
await this.submitProviderProjection(fact.runtimeGeneration, {
|
|
@@ -1161,6 +1185,15 @@ export class ClaudeSession {
|
|
|
1161
1185
|
...(failure ? { error: failure } : {}),
|
|
1162
1186
|
});
|
|
1163
1187
|
}
|
|
1188
|
+
if (failure) {
|
|
1189
|
+
await this.submitProviderProjection(fact.runtimeGeneration, {
|
|
1190
|
+
type: "runtimeExited",
|
|
1191
|
+
runtimeGeneration: fact.runtimeGeneration,
|
|
1192
|
+
message: `Claude provider projection failed: ${failure}`,
|
|
1193
|
+
codexErrorInfo: null,
|
|
1194
|
+
});
|
|
1195
|
+
runtime.beginClose();
|
|
1196
|
+
}
|
|
1164
1197
|
});
|
|
1165
1198
|
}
|
|
1166
1199
|
async projectProviderMessage(projection, runtime, runtimeGeneration, message) {
|
|
@@ -1262,7 +1295,9 @@ export class ClaudeSession {
|
|
|
1262
1295
|
description: message.summary,
|
|
1263
1296
|
outputFile: message.output_file,
|
|
1264
1297
|
}, owner);
|
|
1265
|
-
|
|
1298
|
+
if (owner === this.threadId) {
|
|
1299
|
+
await this.providerSubmitLifecycle(projection, runtimeGeneration, { type: "taskNotification" });
|
|
1300
|
+
}
|
|
1266
1301
|
await this.applyProviderMainStream(projection, runtimeGeneration, {
|
|
1267
1302
|
kind: "taskComplete",
|
|
1268
1303
|
taskId: message.task_id,
|
|
@@ -1318,9 +1353,12 @@ export class ClaudeSession {
|
|
|
1318
1353
|
return "stateOnly";
|
|
1319
1354
|
}
|
|
1320
1355
|
if (message.type === "stream_event") {
|
|
1321
|
-
const
|
|
1322
|
-
if (
|
|
1323
|
-
await this.providerChildStreamEvent(projection, runtimeGeneration,
|
|
1356
|
+
const child = await this.providerChildThreadFor(runtimeGeneration, message.parent_tool_use_id);
|
|
1357
|
+
if (child?.active) {
|
|
1358
|
+
await this.providerChildStreamEvent(projection, runtimeGeneration, child.threadId, message);
|
|
1359
|
+
}
|
|
1360
|
+
else if (child) {
|
|
1361
|
+
return "retainedOnly";
|
|
1324
1362
|
}
|
|
1325
1363
|
else {
|
|
1326
1364
|
await this.providerMainStreamEvent(projection, runtimeGeneration, message);
|
|
@@ -1343,12 +1381,14 @@ export class ClaudeSession {
|
|
|
1343
1381
|
if (message.supersedes?.length) {
|
|
1344
1382
|
await this.providerEvictMessages(projection, runtimeGeneration, message.supersedes);
|
|
1345
1383
|
}
|
|
1346
|
-
const
|
|
1347
|
-
if (
|
|
1348
|
-
const itemIds = await this.providerProjectAssistant(projection, runtimeGeneration, message,
|
|
1349
|
-
await this.providerLinkItems(runtimeGeneration, message.uuid,
|
|
1384
|
+
const child = await this.providerChildThreadFor(runtimeGeneration, message.parent_tool_use_id);
|
|
1385
|
+
if (child?.active) {
|
|
1386
|
+
const itemIds = await this.providerProjectAssistant(projection, runtimeGeneration, message, child.threadId);
|
|
1387
|
+
await this.providerLinkItems(runtimeGeneration, message.uuid, child.threadId, itemIds);
|
|
1350
1388
|
return "projected";
|
|
1351
1389
|
}
|
|
1390
|
+
if (child)
|
|
1391
|
+
return "retainedOnly";
|
|
1352
1392
|
if (activeTurnId) {
|
|
1353
1393
|
if (message.error) {
|
|
1354
1394
|
await this.submitProviderProjection(runtimeGeneration, {
|
|
@@ -1384,14 +1424,16 @@ export class ClaudeSession {
|
|
|
1384
1424
|
if ("uuid" in message && typeof message.uuid === "string") {
|
|
1385
1425
|
runtime.releaseMessage(message.uuid);
|
|
1386
1426
|
}
|
|
1387
|
-
const
|
|
1388
|
-
if (
|
|
1389
|
-
const itemIds = await this.providerProjectToolResults(projection, runtimeGeneration, message,
|
|
1427
|
+
const child = await this.providerChildThreadFor(runtimeGeneration, message.parent_tool_use_id);
|
|
1428
|
+
if (child?.active) {
|
|
1429
|
+
const itemIds = await this.providerProjectToolResults(projection, runtimeGeneration, message, child.threadId);
|
|
1390
1430
|
if ("uuid" in message && typeof message.uuid === "string") {
|
|
1391
|
-
await this.providerLinkItems(runtimeGeneration, message.uuid,
|
|
1431
|
+
await this.providerLinkItems(runtimeGeneration, message.uuid, child.threadId, itemIds);
|
|
1392
1432
|
}
|
|
1393
1433
|
return "projected";
|
|
1394
1434
|
}
|
|
1435
|
+
if (child)
|
|
1436
|
+
return "retainedOnly";
|
|
1395
1437
|
if (activeTurnId && "uuid" in message && typeof message.uuid === "string") {
|
|
1396
1438
|
await this.submitProviderProjection(runtimeGeneration, {
|
|
1397
1439
|
type: "providerBoundary",
|
|
@@ -1714,6 +1756,12 @@ export class ClaudeSession {
|
|
|
1714
1756
|
if (name === "AskUserQuestion") {
|
|
1715
1757
|
return this.askProviderUser(projection, runtimeGeneration, input, options);
|
|
1716
1758
|
}
|
|
1759
|
+
if (providerPermissionMode(settings) === "auto") {
|
|
1760
|
+
return {
|
|
1761
|
+
behavior: "deny",
|
|
1762
|
+
message: options.decisionReason ?? options.description ?? `Claude Auto did not allow tool '${name}'.`,
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1717
1765
|
if (name === "ExitPlanMode") {
|
|
1718
1766
|
return {
|
|
1719
1767
|
behavior: "deny",
|
|
@@ -2021,11 +2069,14 @@ export class ClaudeSession {
|
|
|
2021
2069
|
async providerChildThreadFor(runtimeGeneration, providerId) {
|
|
2022
2070
|
if (!providerId)
|
|
2023
2071
|
return undefined;
|
|
2024
|
-
|
|
2072
|
+
const inspection = await this.submitProviderProjection(runtimeGeneration, {
|
|
2025
2073
|
type: "inspectRuntime",
|
|
2026
2074
|
runtimeGeneration,
|
|
2027
2075
|
providerId,
|
|
2028
|
-
})
|
|
2076
|
+
});
|
|
2077
|
+
return inspection.childThreadId
|
|
2078
|
+
? { threadId: inspection.childThreadId, active: inspection.interruptible }
|
|
2079
|
+
: undefined;
|
|
2029
2080
|
}
|
|
2030
2081
|
async providerLinkItems(runtimeGeneration, providerMessageId, ownerThreadId, itemIds) {
|
|
2031
2082
|
await this.submitProviderProjection(runtimeGeneration, {
|
|
@@ -4103,8 +4154,22 @@ export class ClaudeSession {
|
|
|
4103
4154
|
if (command.runtimeGeneration !== this.runtimeGeneration
|
|
4104
4155
|
|| this.interruptFence || this.dropLateFacts)
|
|
4105
4156
|
return undefined;
|
|
4106
|
-
|
|
4107
|
-
|
|
4157
|
+
{
|
|
4158
|
+
const ownerThreadId = command.ownerThreadId ?? this.threadId;
|
|
4159
|
+
if (!this.scopes.has(ownerThreadId)) {
|
|
4160
|
+
if (command.fact.kind === "inspect")
|
|
4161
|
+
return undefined;
|
|
4162
|
+
switch (command.fact.kind) {
|
|
4163
|
+
case "taskStart":
|
|
4164
|
+
case "taskProgress":
|
|
4165
|
+
case "taskMembership":
|
|
4166
|
+
case "taskOutput":
|
|
4167
|
+
case "taskComplete":
|
|
4168
|
+
case "taskStop":
|
|
4169
|
+
return this.projectDetachedTaskFact(command.fact, command.source);
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
4172
|
+
}
|
|
4108
4173
|
{
|
|
4109
4174
|
if (command.fact.kind === "taskComplete" && !command.fact.outputDrained) {
|
|
4110
4175
|
const task = this.tasks.get(command.fact.taskId);
|
|
@@ -4328,27 +4393,14 @@ export class ClaudeSession {
|
|
|
4328
4393
|
if (this.dropLateFacts || this.adminOperation && this.adminOperation.kind !== "rename"
|
|
4329
4394
|
|| this.repository.archived(command.threadId))
|
|
4330
4395
|
return false;
|
|
4331
|
-
const
|
|
4332
|
-
if (!
|
|
4396
|
+
const state = this.scopes.get(command.threadId);
|
|
4397
|
+
if (!state || command.requestedTurnId && command.requestedTurnId !== state.turnId
|
|
4398
|
+
|| this.repository.readTurn(command.threadId, state.turnId)?.status !== "inProgress")
|
|
4333
4399
|
return false;
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
?? uuidv7();
|
|
4339
|
-
const params = {
|
|
4340
|
-
error: {
|
|
4341
|
-
message: command.message,
|
|
4342
|
-
codexErrorInfo: command.codexErrorInfo,
|
|
4343
|
-
additionalDetails: null,
|
|
4344
|
-
},
|
|
4345
|
-
willRetry: false,
|
|
4346
|
-
threadId: command.threadId,
|
|
4347
|
-
turnId,
|
|
4348
|
-
};
|
|
4349
|
-
if (this.repository.appendEvent(command.threadId, turnId, "error", params)) {
|
|
4350
|
-
this.output.emit(command.threadId, "error", params);
|
|
4351
|
-
}
|
|
4400
|
+
this.projectMainStream(command.threadId, {
|
|
4401
|
+
kind: "instantAgent",
|
|
4402
|
+
text: systemNoticeText(command.message, "error"),
|
|
4403
|
+
}, nullSource);
|
|
4352
4404
|
return true;
|
|
4353
4405
|
}
|
|
4354
4406
|
case "startCompact": {
|
|
@@ -5185,8 +5237,13 @@ export class ClaudeSession {
|
|
|
5185
5237
|
this.pendingNoQuery = this.pendingInputs = 0;
|
|
5186
5238
|
this.preparedRuntimeInputs.clear();
|
|
5187
5239
|
this.disposeRuntimeOperations();
|
|
5188
|
-
if (active)
|
|
5240
|
+
if (active) {
|
|
5189
5241
|
active.commandCompleted = true;
|
|
5242
|
+
active.acknowledged = active.notifications;
|
|
5243
|
+
active.goals = 0;
|
|
5244
|
+
active.goalInFlight = false;
|
|
5245
|
+
delete active.request;
|
|
5246
|
+
}
|
|
5190
5247
|
this.settleForcedScopes(source);
|
|
5191
5248
|
if (fact.type === "runtimeExit" && generation !== undefined) {
|
|
5192
5249
|
this.runtimeGeneration = undefined;
|
|
@@ -5196,8 +5253,10 @@ export class ClaudeSession {
|
|
|
5196
5253
|
else if (fact.type === "timer" && active?.result && this.hasNotifications()
|
|
5197
5254
|
&& fact.generation === active.notifications && fact.generation > active.diagnosed) {
|
|
5198
5255
|
active.diagnosed = fact.generation;
|
|
5199
|
-
this.
|
|
5200
|
-
|
|
5256
|
+
this.runtimeDependencies?.logger.warn("claude.lifecycle.notification-unacknowledged", {
|
|
5257
|
+
threadId: this.threadId,
|
|
5258
|
+
generation: fact.generation,
|
|
5259
|
+
});
|
|
5201
5260
|
}
|
|
5202
5261
|
this.maybeFinish(source, fact.type === "sync");
|
|
5203
5262
|
}
|
|
@@ -5756,7 +5815,7 @@ export class ClaudeSession {
|
|
|
5756
5815
|
let owner = this.toolOwner(command.providerId);
|
|
5757
5816
|
if (!owner && command.ownerProviderId) {
|
|
5758
5817
|
owner = this.toolOwner(command.ownerProviderId);
|
|
5759
|
-
const task = [...this.tasks.values()].find((candidate) => candidate
|
|
5818
|
+
const task = [...this.tasks.values()].find((candidate) => taskUsesProvider(candidate, command.ownerProviderId) || candidate.taskId === command.ownerProviderId);
|
|
5760
5819
|
if (task?.childThreadId)
|
|
5761
5820
|
owner = this.scopes.get(task.childThreadId);
|
|
5762
5821
|
}
|
|
@@ -5889,11 +5948,12 @@ export class ClaudeSession {
|
|
|
5889
5948
|
}
|
|
5890
5949
|
let ownerThreadId = command.ownerThreadId ?? this.threadId;
|
|
5891
5950
|
let targetThreadId = ownerThreadId;
|
|
5951
|
+
let providerTask;
|
|
5892
5952
|
if (command.providerId) {
|
|
5893
5953
|
const scope = [...this.scopes.values()].find((candidate) => candidate.tools.has(command.providerId));
|
|
5894
|
-
|
|
5895
|
-
ownerThreadId =
|
|
5896
|
-
targetThreadId = ownerThreadId;
|
|
5954
|
+
providerTask = [...this.tasks.values()].find((candidate) => taskUsesProvider(candidate, command.providerId) || candidate.taskId === command.providerId);
|
|
5955
|
+
ownerThreadId = providerTask?.ownerThreadId ?? scope?.ownerThreadId ?? ownerThreadId;
|
|
5956
|
+
targetThreadId = providerTask?.childThreadId ?? ownerThreadId;
|
|
5897
5957
|
}
|
|
5898
5958
|
const childTask = command.childThreadId
|
|
5899
5959
|
? [...this.tasks.values()].find((candidate) => candidate.childThreadId === command.childThreadId)
|
|
@@ -5920,12 +5980,14 @@ export class ClaudeSession {
|
|
|
5920
5980
|
modelContextWindow: this.requireRecord(false).modelContextWindow,
|
|
5921
5981
|
interruptible: command.expectedTurnId
|
|
5922
5982
|
? targetState?.turnId === command.expectedTurnId
|
|
5923
|
-
: childTask ? !childTask.terminal && Boolean(targetState)
|
|
5983
|
+
: childTask ? !childTask.terminal && Boolean(targetState)
|
|
5984
|
+
: providerTask?.childThreadId ? !providerTask.terminal && Boolean(targetState)
|
|
5985
|
+
: Boolean(targetState),
|
|
5924
5986
|
ownerThreadId,
|
|
5925
5987
|
ownerTurnId: ownerState?.turnId ?? null,
|
|
5926
5988
|
taskIds,
|
|
5927
5989
|
childThreadId: command.providerId
|
|
5928
|
-
? [...this.tasks.values()].find((task) => task
|
|
5990
|
+
? [...this.tasks.values()].find((task) => taskUsesProvider(task, command.providerId) || task.taskId === command.providerId)?.childThreadId ?? null
|
|
5929
5991
|
: null,
|
|
5930
5992
|
taskId: childTask?.taskId ?? null,
|
|
5931
5993
|
quiescent: this.isQuiescent(),
|
|
@@ -6343,8 +6405,9 @@ export class ClaudeSession {
|
|
|
6343
6405
|
const existing = tool && turn.items.find((item) => item.id === tool.itemId);
|
|
6344
6406
|
const subagent = Boolean(fact.subagentType) || fact.taskType === "agent" || fact.taskType === "subagent";
|
|
6345
6407
|
if (!task && !existing && !subagent) {
|
|
6408
|
+
const providerId = fact.providerId ?? fact.taskId;
|
|
6346
6409
|
task = { taskId: fact.taskId, ownerThreadId: state.ownerThreadId,
|
|
6347
|
-
itemId: `claude-task:${fact.taskId}`, providerId:
|
|
6410
|
+
itemId: `claude-task:${fact.taskId}`, providerId, providerIds: new Set([providerId]),
|
|
6348
6411
|
turnId: turn.id, childThreadId: undefined, outputFile: fact.outputFile, terminal: false };
|
|
6349
6412
|
this.tasks.set(task.taskId, task);
|
|
6350
6413
|
}
|
|
@@ -6354,8 +6417,9 @@ export class ClaudeSession {
|
|
|
6354
6417
|
status: "inProgress", senderThreadId: state.ownerThreadId, receiverThreadIds: [],
|
|
6355
6418
|
prompt: fact.prompt ?? fact.description, model: null, reasoningEffort: null, agentsStates: {},
|
|
6356
6419
|
};
|
|
6420
|
+
const providerId = fact.providerId ?? item.id;
|
|
6357
6421
|
const createdTask = { taskId: fact.taskId, ownerThreadId: state.ownerThreadId, itemId: item.id,
|
|
6358
|
-
providerId:
|
|
6422
|
+
providerId, providerIds: new Set([providerId]), turnId: turn.id, childThreadId: undefined,
|
|
6359
6423
|
outputFile: fact.outputFile, terminal: false };
|
|
6360
6424
|
task = createdTask;
|
|
6361
6425
|
this.tasks.set(createdTask.taskId, createdTask);
|
|
@@ -6372,6 +6436,25 @@ export class ClaudeSession {
|
|
|
6372
6436
|
item.agentsStates = { [childThread.id]: { status: "running", message: fact.description } };
|
|
6373
6437
|
}
|
|
6374
6438
|
}
|
|
6439
|
+
const resumed = task?.terminal && subagent && task.childThreadId && fact.providerId
|
|
6440
|
+
&& !taskUsesProvider(task, fact.providerId);
|
|
6441
|
+
if (resumed && task?.childThreadId) {
|
|
6442
|
+
childThread = this.resumeChildScope(task.childThreadId, fact, source);
|
|
6443
|
+
task.terminal = false;
|
|
6444
|
+
task.providerId = fact.providerId;
|
|
6445
|
+
task.providerIds.add(fact.providerId);
|
|
6446
|
+
task.itemId = existing?.id ?? task.itemId;
|
|
6447
|
+
task.turnId = turn.id;
|
|
6448
|
+
task.outputFile = fact.outputFile;
|
|
6449
|
+
if (existing?.type === "collabAgentToolCall") {
|
|
6450
|
+
existing.receiverThreadIds = [task.childThreadId];
|
|
6451
|
+
existing.agentsStates = {
|
|
6452
|
+
[task.childThreadId]: { status: "running", message: fact.description },
|
|
6453
|
+
};
|
|
6454
|
+
if (tool)
|
|
6455
|
+
this.beginTool(turn, state, tool, true, source);
|
|
6456
|
+
}
|
|
6457
|
+
}
|
|
6375
6458
|
if (task && tool) {
|
|
6376
6459
|
tool.backgroundTaskId = task.taskId;
|
|
6377
6460
|
if (task.outputFile)
|
|
@@ -6399,8 +6482,24 @@ export class ClaudeSession {
|
|
|
6399
6482
|
handled = taskIds.length > 0;
|
|
6400
6483
|
}
|
|
6401
6484
|
else if (task) {
|
|
6402
|
-
const ownerState = this.
|
|
6485
|
+
const ownerState = this.scopes.get(task.ownerThreadId);
|
|
6403
6486
|
const ownerTurn = this.repository.readTurn(task.ownerThreadId, task.turnId);
|
|
6487
|
+
if (!ownerState || !ownerTurn || ownerTurn.status !== "inProgress") {
|
|
6488
|
+
if (fact.kind === "taskComplete" && !task.terminal) {
|
|
6489
|
+
this.stopBackgroundTailer(task);
|
|
6490
|
+
task.outputFile = fact.outputFile ?? task.outputFile;
|
|
6491
|
+
task.terminal = true;
|
|
6492
|
+
if (task.childThreadId)
|
|
6493
|
+
this.finishChildScope(task.childThreadId, fact.status, fact.summary, source);
|
|
6494
|
+
}
|
|
6495
|
+
return scopeProjection(turn, {
|
|
6496
|
+
handled: fact.kind === "taskComplete",
|
|
6497
|
+
taskIds: [task.taskId],
|
|
6498
|
+
outputFile: task.outputFile,
|
|
6499
|
+
childThreadId: task.childThreadId,
|
|
6500
|
+
terminal: task.terminal,
|
|
6501
|
+
});
|
|
6502
|
+
}
|
|
6404
6503
|
const item = ownerTurn.items.find((candidate) => candidate.id === task.itemId);
|
|
6405
6504
|
if (fact.kind === "taskProgress") {
|
|
6406
6505
|
if (item?.type === "collabAgentToolCall")
|
|
@@ -6441,10 +6540,12 @@ export class ClaudeSession {
|
|
|
6441
6540
|
status: fact.status === "completed" ? "completed" : fact.status === "stopped" ? "interrupted" : "errored",
|
|
6442
6541
|
message: fact.summary,
|
|
6443
6542
|
};
|
|
6444
|
-
ownerState.completedItems.
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6543
|
+
if (item.tool !== "sendInput" || !ownerState.completedItems.has(item.id)) {
|
|
6544
|
+
ownerState.completedItems.add(item.id);
|
|
6545
|
+
this.publishTurn(ownerTurn, "item/completed", {
|
|
6546
|
+
item, threadId: task.ownerThreadId, turnId: ownerTurn.id, completedAtMs: Date.now(),
|
|
6547
|
+
}, source);
|
|
6548
|
+
}
|
|
6448
6549
|
}
|
|
6449
6550
|
if (task.childThreadId)
|
|
6450
6551
|
this.finishChildScope(task.childThreadId, fact.status, fact.summary, source);
|
|
@@ -6485,11 +6586,49 @@ export class ClaudeSession {
|
|
|
6485
6586
|
tool: task ? this.scopes.get(task.ownerThreadId)?.tools.get(task.providerId) : undefined,
|
|
6486
6587
|
});
|
|
6487
6588
|
}
|
|
6589
|
+
projectDetachedTaskFact(fact, source) {
|
|
6590
|
+
if (!("taskId" in fact))
|
|
6591
|
+
return undefined;
|
|
6592
|
+
const task = this.tasks.get(fact.taskId);
|
|
6593
|
+
if (!task)
|
|
6594
|
+
return undefined;
|
|
6595
|
+
const turn = this.repository.readTurn(task.ownerThreadId, task.turnId);
|
|
6596
|
+
if (!turn)
|
|
6597
|
+
return undefined;
|
|
6598
|
+
if (fact.kind === "taskStart") {
|
|
6599
|
+
task.outputFile = fact.outputFile ?? task.outputFile;
|
|
6600
|
+
}
|
|
6601
|
+
else if (fact.kind === "taskComplete" && !task.terminal) {
|
|
6602
|
+
this.stopBackgroundTailer(task);
|
|
6603
|
+
task.outputFile = fact.outputFile ?? task.outputFile;
|
|
6604
|
+
task.terminal = true;
|
|
6605
|
+
if (task.childThreadId)
|
|
6606
|
+
this.finishChildScope(task.childThreadId, fact.status, fact.summary, source);
|
|
6607
|
+
}
|
|
6608
|
+
return scopeProjection(turn, {
|
|
6609
|
+
handled: true,
|
|
6610
|
+
taskIds: [task.taskId],
|
|
6611
|
+
outputFile: task.outputFile,
|
|
6612
|
+
childThreadId: task.childThreadId,
|
|
6613
|
+
terminal: task.terminal,
|
|
6614
|
+
});
|
|
6615
|
+
}
|
|
6488
6616
|
updateTool(turn, state, tool, input, source) {
|
|
6489
6617
|
const item = turn.items.find((candidate) => candidate.id === tool.itemId);
|
|
6490
6618
|
if (!item)
|
|
6491
6619
|
return;
|
|
6492
6620
|
updateToolInput(item, tool, input, state.record.thread.cwd);
|
|
6621
|
+
if (item.type === "collabAgentToolCall" && item.tool === "sendInput") {
|
|
6622
|
+
const target = typeof input.to === "string" ? input.to
|
|
6623
|
+
: typeof input.recipient === "string" ? input.recipient : undefined;
|
|
6624
|
+
const task = target ? this.tasks.get(target) : undefined;
|
|
6625
|
+
if (task?.childThreadId) {
|
|
6626
|
+
item.receiverThreadIds = [task.childThreadId];
|
|
6627
|
+
item.agentsStates = {
|
|
6628
|
+
[task.childThreadId]: { status: task.terminal ? "completed" : "running", message: null },
|
|
6629
|
+
};
|
|
6630
|
+
}
|
|
6631
|
+
}
|
|
6493
6632
|
if (tool.name === "TaskOutput") {
|
|
6494
6633
|
const id = typeof input.task_id === "string" ? input.task_id
|
|
6495
6634
|
: typeof input.taskId === "string" ? input.taskId : undefined;
|
|
@@ -6508,6 +6647,9 @@ export class ClaudeSession {
|
|
|
6508
6647
|
if (!item || tool.started || tool.foldedTaskId || isImageRead(tool, record.thread.cwd)
|
|
6509
6648
|
|| item.type === "commandExecution" && !item.command)
|
|
6510
6649
|
return;
|
|
6650
|
+
if (item.type === "collabAgentToolCall" && item.tool === "sendInput"
|
|
6651
|
+
&& (!item.prompt || item.receiverThreadIds.length === 0))
|
|
6652
|
+
return;
|
|
6511
6653
|
if (tool.name === "Bash" && tool.input.run_in_background === true && !tool.backgroundTaskId)
|
|
6512
6654
|
return;
|
|
6513
6655
|
if (!force
|
|
@@ -6562,6 +6704,50 @@ export class ClaudeSession {
|
|
|
6562
6704
|
this.publishAt(childThreadId, turn.id, "item/completed", { item, threadId: childThreadId, turnId: turn.id, completedAtMs: Date.now() }, source);
|
|
6563
6705
|
return thread;
|
|
6564
6706
|
}
|
|
6707
|
+
resumeChildScope(childThreadId, fact, source) {
|
|
6708
|
+
const record = this.repository.read(childThreadId, false);
|
|
6709
|
+
if (!record)
|
|
6710
|
+
throw new Error(`Unknown Claude child thread '${childThreadId}'.`);
|
|
6711
|
+
const startedAt = Math.floor(Date.now() / 1_000);
|
|
6712
|
+
const text = fact.prompt ?? fact.description;
|
|
6713
|
+
const item = {
|
|
6714
|
+
type: "userMessage", id: uuidv7(), clientId: null,
|
|
6715
|
+
content: [{ type: "text", text, text_elements: [] }],
|
|
6716
|
+
};
|
|
6717
|
+
const turn = {
|
|
6718
|
+
id: uuidv7(), items: [item], itemsView: "full", status: "inProgress", error: null,
|
|
6719
|
+
startedAt, completedAt: null, durationMs: null,
|
|
6720
|
+
};
|
|
6721
|
+
const updated = {
|
|
6722
|
+
...record,
|
|
6723
|
+
thread: {
|
|
6724
|
+
...record.thread,
|
|
6725
|
+
preview: text,
|
|
6726
|
+
status: { type: "active", activeFlags: [] },
|
|
6727
|
+
updatedAt: startedAt,
|
|
6728
|
+
recencyAt: startedAt,
|
|
6729
|
+
},
|
|
6730
|
+
};
|
|
6731
|
+
const status = { threadId: childThreadId, status: updated.thread.status };
|
|
6732
|
+
this.commitState(updated, [{
|
|
6733
|
+
turnId: turn.id,
|
|
6734
|
+
method: "thread/status/changed",
|
|
6735
|
+
params: status,
|
|
6736
|
+
providerEventId: source.providerEventId,
|
|
6737
|
+
providerEventType: source.providerEventType,
|
|
6738
|
+
}], turn, true);
|
|
6739
|
+
const state = newMainStreamState(childThreadId, turn.id, updated);
|
|
6740
|
+
state.completedItems.add(item.id);
|
|
6741
|
+
this.scopes.set(childThreadId, state);
|
|
6742
|
+
this.publishAt(childThreadId, turn.id, "turn/started", { threadId: childThreadId, turn }, source);
|
|
6743
|
+
this.publishAt(childThreadId, turn.id, "item/started", {
|
|
6744
|
+
item, threadId: childThreadId, turnId: turn.id, startedAtMs: Date.now(),
|
|
6745
|
+
}, source);
|
|
6746
|
+
this.publishAt(childThreadId, turn.id, "item/completed", {
|
|
6747
|
+
item, threadId: childThreadId, turnId: turn.id, completedAtMs: Date.now(),
|
|
6748
|
+
}, source);
|
|
6749
|
+
return updated.thread;
|
|
6750
|
+
}
|
|
6565
6751
|
finishChildScope(childThreadId, status, summary, source) {
|
|
6566
6752
|
const state = this.scopes.get(childThreadId);
|
|
6567
6753
|
const record = this.repository.read(childThreadId, false);
|