praxis-agent 0.32.0 → 0.34.0
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/dist/application/background-agent-manager.d.ts +48 -3
- package/dist/application/background-agent-manager.js +249 -73
- package/dist/application/background-task-runtime.d.ts +20 -1
- package/dist/application/background-task-runtime.js +45 -6
- package/dist/application/managed-worktree.d.ts +8 -0
- package/dist/application/managed-worktree.js +54 -2
- package/dist/application/session-service.d.ts +38 -0
- package/dist/application/session-service.js +590 -70
- package/dist/application/subagent-service.d.ts +34 -7
- package/dist/application/subagent-service.js +694 -135
- package/dist/cli/interactive.d.ts +1 -0
- package/dist/cli/interactive.js +26 -1
- package/dist/cli/tui/task-panel.d.ts +1 -1
- package/dist/cli/tui/task-panel.js +2 -1
- package/dist/cli-runtime.d.ts +1 -0
- package/dist/cli-runtime.js +111 -31
- package/dist/compatibility/claude/history.d.ts +8 -0
- package/dist/compatibility/claude/history.js +54 -0
- package/dist/compatibility/claude/interruption.d.ts +16 -0
- package/dist/compatibility/claude/interruption.js +147 -0
- package/dist/compatibility/claude/projection.d.ts +4 -0
- package/dist/compatibility/claude/projection.js +82 -0
- package/dist/compatibility/claude/schema.js +22 -0
- package/dist/compatibility/claude/session-metadata.d.ts +44 -0
- package/dist/compatibility/claude/session-metadata.js +180 -0
- package/dist/compatibility/claude/sidechain.d.ts +5 -0
- package/dist/compatibility/claude/sidechain.js +13 -4
- package/dist/persistence/claude-session-index.d.ts +39 -0
- package/dist/persistence/claude-session-index.js +175 -0
- package/dist/persistence/claude-sidechain-store.js +19 -3
- package/dist/persistence/claude-transcript-store.d.ts +6 -2
- package/dist/persistence/claude-transcript-store.js +78 -3
- package/dist/persistence/in-memory-transcript-store.js +1 -0
- package/dist/persistence/subagent-lifecycle-store.d.ts +66 -0
- package/dist/persistence/subagent-lifecycle-store.js +299 -0
- package/package.json +2 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { randomBytes, randomUUID } from 'node:crypto';
|
|
2
2
|
import { lstat, mkdir, readFile, readdir } from 'node:fs/promises';
|
|
3
|
-
import { join, relative } from 'node:path';
|
|
3
|
+
import { isAbsolute, join, relative } from 'node:path';
|
|
4
4
|
import { Ajv2020 } from 'ajv/dist/2020.js';
|
|
5
5
|
import { resolveClaudePaths } from '../compatibility/claude/paths.js';
|
|
6
6
|
import { workflowAgentFiles } from '../compatibility/claude/workflow.js';
|
|
7
|
-
import { createClaudeAsyncAgentToolUseResult, createClaudeAgentToolUseResult, createClaudeSidechainRoot, resolveClaudeSidechainPaths, toClaudeSidechainEntry, } from '../compatibility/claude/sidechain.js';
|
|
8
|
-
import { projectClaudeModelMessages } from '../compatibility/claude/projection.js';
|
|
7
|
+
import { createClaudeAsyncAgentToolUseResult, createClaudeAgentToolUseResult, createClaudeSidechainRoot, isClaudeAgentId, resolveClaudeSidechainPaths, toClaudeSidechainEntry, } from '../compatibility/claude/sidechain.js';
|
|
8
|
+
import { projectClaudeModelMessages, projectClaudeSidechainContinuationMessages, } from '../compatibility/claude/projection.js';
|
|
9
9
|
import { selectClaudeSchemaAdapter, } from '../compatibility/claude/schema.js';
|
|
10
10
|
import { createClaudeHookAttachmentEntries, translateProviderEvents, } from '../compatibility/claude/translation.js';
|
|
11
11
|
import { injectFirstUserMessageContext, } from '../core/context.js';
|
|
@@ -16,21 +16,105 @@ import { BUILTIN_STATUSLINE_AGENT_PATH, } from '../extensions/claude-extensions.
|
|
|
16
16
|
import { ClaudeHookToolCoordinator } from '../hooks/claude-hook-tools.js';
|
|
17
17
|
import { ClaudeSidechainStore } from '../persistence/claude-sidechain-store.js';
|
|
18
18
|
import { InMemorySidechainStore } from '../persistence/in-memory-sidechain-store.js';
|
|
19
|
+
import { SubagentLifecycleStore } from '../persistence/subagent-lifecycle-store.js';
|
|
19
20
|
import { resolveDataPlanePaths, } from '../persistence/data-plane.js';
|
|
20
|
-
import { BackgroundAgentManager, BackgroundAgentRunError, } from './background-agent-manager.js';
|
|
21
|
+
import { BackgroundAgentManager, BackgroundAgentRunError, BackgroundAgentShutdownError, backgroundAgentNotificationMarkers, } from './background-agent-manager.js';
|
|
21
22
|
import { isBackgroundBashTaskId } from './background-task-id.js';
|
|
22
|
-
import { createManagedWorktree, } from './managed-worktree.js';
|
|
23
|
+
import { createManagedWorktree, restoreManagedWorktree, } from './managed-worktree.js';
|
|
23
24
|
import { createWorkflowWorktree } from './workflow-worktree.js';
|
|
24
25
|
const DEFAULT_MAX_DEPTH = 4;
|
|
25
26
|
const DEFAULT_MAX_CALLS = 16;
|
|
26
27
|
const DEFAULT_MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
27
28
|
const SIDECHAIN_DISCOVERY_MAX_DEPTH = 4;
|
|
29
|
+
function mergeSubagentUsage(left, right) {
|
|
30
|
+
for (const usage of [left, right]) {
|
|
31
|
+
for (const field of [
|
|
32
|
+
'inputTokens',
|
|
33
|
+
'outputTokens',
|
|
34
|
+
'cacheReadInputTokens',
|
|
35
|
+
'cacheCreationInputTokens',
|
|
36
|
+
'webSearchRequests',
|
|
37
|
+
]) {
|
|
38
|
+
const value = usage[field];
|
|
39
|
+
if (value !== undefined && (!Number.isSafeInteger(value) || value < 0)) {
|
|
40
|
+
throw new Error(`Subagent usage has an invalid ${field}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for (const field of ['contextWindow', 'maxOutputTokens']) {
|
|
44
|
+
const value = usage[field];
|
|
45
|
+
if (value !== undefined && (!Number.isSafeInteger(value) || value < 1)) {
|
|
46
|
+
throw new Error(`Subagent usage has an invalid ${field}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const counters = {
|
|
51
|
+
inputTokens: left.inputTokens + right.inputTokens,
|
|
52
|
+
outputTokens: left.outputTokens + right.outputTokens,
|
|
53
|
+
cacheReadInputTokens: (left.cacheReadInputTokens ?? 0) + (right.cacheReadInputTokens ?? 0),
|
|
54
|
+
cacheCreationInputTokens: (left.cacheCreationInputTokens ?? 0) +
|
|
55
|
+
(right.cacheCreationInputTokens ?? 0),
|
|
56
|
+
webSearchRequests: (left.webSearchRequests ?? 0) + (right.webSearchRequests ?? 0),
|
|
57
|
+
};
|
|
58
|
+
if (Object.values(counters).some((value) => !Number.isSafeInteger(value))) {
|
|
59
|
+
throw new Error('Subagent usage total overflow');
|
|
60
|
+
}
|
|
61
|
+
const metadata = (field) => {
|
|
62
|
+
const leftValue = left[field];
|
|
63
|
+
const rightValue = right[field];
|
|
64
|
+
if (leftValue !== undefined &&
|
|
65
|
+
rightValue !== undefined &&
|
|
66
|
+
leftValue !== rightValue) {
|
|
67
|
+
throw new Error(`Subagent usage has conflicting ${field}`);
|
|
68
|
+
}
|
|
69
|
+
return leftValue ?? rightValue;
|
|
70
|
+
};
|
|
71
|
+
const contextWindow = metadata('contextWindow');
|
|
72
|
+
const maxOutputTokens = metadata('maxOutputTokens');
|
|
73
|
+
return {
|
|
74
|
+
inputTokens: counters.inputTokens,
|
|
75
|
+
outputTokens: counters.outputTokens,
|
|
76
|
+
...(counters.cacheReadInputTokens === 0
|
|
77
|
+
? {}
|
|
78
|
+
: { cacheReadInputTokens: counters.cacheReadInputTokens }),
|
|
79
|
+
...(counters.cacheCreationInputTokens === 0
|
|
80
|
+
? {}
|
|
81
|
+
: { cacheCreationInputTokens: counters.cacheCreationInputTokens }),
|
|
82
|
+
...(counters.webSearchRequests === 0
|
|
83
|
+
? {}
|
|
84
|
+
: { webSearchRequests: counters.webSearchRequests }),
|
|
85
|
+
...(contextWindow === undefined ? {} : { contextWindow }),
|
|
86
|
+
...(maxOutputTokens === undefined ? {} : { maxOutputTokens }),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function mergeSubagentModelUsage(target, source) {
|
|
90
|
+
for (const [model, usage] of Object.entries(source)) {
|
|
91
|
+
if (model.trim().length === 0) {
|
|
92
|
+
throw new Error('Subagent model usage has a blank model name');
|
|
93
|
+
}
|
|
94
|
+
target.set(model, mergeSubagentUsage(target.get(model) ?? { inputTokens: 0, outputTokens: 0 }, usage));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
class SubagentExecutionFailure extends Error {
|
|
98
|
+
result;
|
|
99
|
+
name = 'SubagentExecutionFailure';
|
|
100
|
+
constructor(cause, result) {
|
|
101
|
+
super(cause instanceof Error ? cause.message : String(cause), { cause });
|
|
102
|
+
this.result = result;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
28
105
|
const structuredOnlyTools = {
|
|
29
106
|
definitions: () => [],
|
|
30
107
|
schedulingPolicy: () => ({ concurrency: 'exclusive' }),
|
|
31
108
|
prepare: async (call) => call,
|
|
32
109
|
execute: async () => ({ content: '', isError: false }),
|
|
33
110
|
};
|
|
111
|
+
function sidechainAgentIdFromFile(fileName, suffix) {
|
|
112
|
+
if (!fileName.startsWith('agent-') || !fileName.endsWith(suffix)) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
const agentId = fileName.slice('agent-'.length, -suffix.length);
|
|
116
|
+
return isClaudeAgentId(agentId) ? agentId : undefined;
|
|
117
|
+
}
|
|
34
118
|
async function readSidechainMetadataName(metadataFile) {
|
|
35
119
|
try {
|
|
36
120
|
const value = JSON.parse(await readFile(metadataFile, 'utf8'));
|
|
@@ -368,6 +452,7 @@ export class ClaudeSubagentExecutor {
|
|
|
368
452
|
calls = 0;
|
|
369
453
|
background = new BackgroundAgentManager();
|
|
370
454
|
ephemeralSidechains = new Map();
|
|
455
|
+
foreground = new Map();
|
|
371
456
|
isEnabled(name) {
|
|
372
457
|
return this.options.toolNames?.includes(name) ?? true;
|
|
373
458
|
}
|
|
@@ -376,14 +461,55 @@ export class ClaudeSubagentExecutor {
|
|
|
376
461
|
this.schema = selectClaudeSchemaAdapter(options.claudeVersion);
|
|
377
462
|
}
|
|
378
463
|
async close() {
|
|
464
|
+
for (const agentId of [...this.foreground.keys()]) {
|
|
465
|
+
this.backgroundForegroundTask(agentId);
|
|
466
|
+
}
|
|
379
467
|
await this.background.close();
|
|
468
|
+
this.foreground.clear();
|
|
380
469
|
this.ephemeralSidechains.clear();
|
|
381
470
|
}
|
|
382
471
|
backgroundSnapshots() {
|
|
383
472
|
return this.background.snapshots();
|
|
384
473
|
}
|
|
474
|
+
hasForegroundTask() {
|
|
475
|
+
return this.foreground.size > 0;
|
|
476
|
+
}
|
|
477
|
+
backgroundForegroundTask(identifier) {
|
|
478
|
+
const candidates = [...this.foreground.entries()];
|
|
479
|
+
const selected = identifier === undefined
|
|
480
|
+
? candidates.at(-1)
|
|
481
|
+
: candidates.find(([agentId, task]) => agentId === identifier || task.spec.name === identifier);
|
|
482
|
+
if (!selected) {
|
|
483
|
+
throw new Error(identifier === undefined
|
|
484
|
+
? 'No foreground agent is running'
|
|
485
|
+
: `No foreground agent found with ID or name: ${identifier}`);
|
|
486
|
+
}
|
|
487
|
+
const [, task] = selected;
|
|
488
|
+
if (task.backgrounded) {
|
|
489
|
+
throw new Error(`Agent ${task.spec.agentId} is already backgrounded`);
|
|
490
|
+
}
|
|
491
|
+
const snapshot = this.background.adopt({
|
|
492
|
+
spec: task.spec,
|
|
493
|
+
controller: task.controller,
|
|
494
|
+
operation: task.operation,
|
|
495
|
+
startedAt: task.startedAt,
|
|
496
|
+
});
|
|
497
|
+
task.backgrounded = true;
|
|
498
|
+
task.detachParentSignal();
|
|
499
|
+
task.resolveHandoff();
|
|
500
|
+
return snapshot;
|
|
501
|
+
}
|
|
385
502
|
stopBackgroundTask(taskId) {
|
|
386
|
-
return this.background.
|
|
503
|
+
return this.background.stopAndWait(taskId);
|
|
504
|
+
}
|
|
505
|
+
outputBackgroundTask(taskId, options) {
|
|
506
|
+
return this.background.output(taskId, options);
|
|
507
|
+
}
|
|
508
|
+
sendBackgroundMessage(agentId, message, summary, toolUseId) {
|
|
509
|
+
return this.background.send(agentId, message, summary, toolUseId);
|
|
510
|
+
}
|
|
511
|
+
stopAllBackgroundTasks() {
|
|
512
|
+
return this.background.stopAll();
|
|
387
513
|
}
|
|
388
514
|
cwd() {
|
|
389
515
|
return this.options.cwdProvider?.() ?? this.options.cwd;
|
|
@@ -398,7 +524,7 @@ export class ClaudeSubagentExecutor {
|
|
|
398
524
|
? definition.model
|
|
399
525
|
: undefined;
|
|
400
526
|
const model = environmentModel || input.model || configuredModel;
|
|
401
|
-
const parentPermissionMode = this.options.parentPermissionMode?.();
|
|
527
|
+
const parentPermissionMode = this.options.parentPermissionMode?.() ?? 'default';
|
|
402
528
|
const definitionCanOverridePermissionMode = ![
|
|
403
529
|
'acceptEdits',
|
|
404
530
|
'auto',
|
|
@@ -406,7 +532,7 @@ export class ClaudeSubagentExecutor {
|
|
|
406
532
|
].includes(parentPermissionMode ?? 'default');
|
|
407
533
|
const permissionMode = input.permissionMode ??
|
|
408
534
|
(definitionCanOverridePermissionMode
|
|
409
|
-
? definition?.permissionMode
|
|
535
|
+
? (definition?.permissionMode ?? parentPermissionMode)
|
|
410
536
|
: parentPermissionMode);
|
|
411
537
|
const isolation = input.isolation ?? definition?.isolation;
|
|
412
538
|
return {
|
|
@@ -417,8 +543,8 @@ export class ClaudeSubagentExecutor {
|
|
|
417
543
|
runInBackground: input.runInBackground || definition?.background === true,
|
|
418
544
|
};
|
|
419
545
|
}
|
|
420
|
-
registry(sessionId, depth, promptIdForCall) {
|
|
421
|
-
return new ClaudeSubagentToolRegistry(this.options.baseTools, this, sessionId, depth, promptIdForCall);
|
|
546
|
+
registry(sessionId, depth, promptIdForCall, parentAgentId) {
|
|
547
|
+
return new ClaudeSubagentToolRegistry(this.options.baseTools, this, sessionId, depth, promptIdForCall, parentAgentId);
|
|
422
548
|
}
|
|
423
549
|
definitions() {
|
|
424
550
|
return {
|
|
@@ -558,7 +684,9 @@ export class ClaudeSubagentExecutor {
|
|
|
558
684
|
if (input.model && !this.options.providerForModel) {
|
|
559
685
|
throw new Error('Agent model overrides are unavailable for this provider');
|
|
560
686
|
}
|
|
561
|
-
if (input.permissionMode &&
|
|
687
|
+
if (input.permissionMode &&
|
|
688
|
+
input.permissionMode !== 'default' &&
|
|
689
|
+
!this.options.permissionResolverForMode) {
|
|
562
690
|
throw new Error('Agent permission mode overrides are unavailable');
|
|
563
691
|
}
|
|
564
692
|
return {
|
|
@@ -576,7 +704,7 @@ export class ClaudeSubagentExecutor {
|
|
|
576
704
|
},
|
|
577
705
|
};
|
|
578
706
|
}
|
|
579
|
-
async execute(call, sessionId, depth, promptId, context) {
|
|
707
|
+
async execute(call, sessionId, depth, promptId, context, parentAgentId) {
|
|
580
708
|
this.calls += 1;
|
|
581
709
|
const maxCalls = this.options.maxCalls ?? DEFAULT_MAX_CALLS;
|
|
582
710
|
if (this.calls > maxCalls) {
|
|
@@ -585,11 +713,12 @@ export class ClaudeSubagentExecutor {
|
|
|
585
713
|
const input = this.resolveAgentInput(parseAgentInput(call));
|
|
586
714
|
const spawnDepth = depth + 1;
|
|
587
715
|
const agentId = `a${randomBytes(8).toString('hex')}`;
|
|
588
|
-
const
|
|
716
|
+
const parentCwd = context.cwd;
|
|
717
|
+
const paths = this.sessionPaths(sessionId, parentCwd);
|
|
589
718
|
const initialIsolation = input.isolation
|
|
590
|
-
? await this.createAgentWorktree(paths.praxisRoot, sessionId, agentId)
|
|
719
|
+
? await this.createAgentWorktree(paths.praxisRoot, sessionId, agentId, parentCwd)
|
|
591
720
|
: undefined;
|
|
592
|
-
const agentCwd = initialIsolation?.cwd ??
|
|
721
|
+
const agentCwd = initialIsolation?.cwd ?? parentCwd;
|
|
593
722
|
const sidechainPaths = resolveClaudeSidechainPaths(paths.projectRoot, sessionId, agentId);
|
|
594
723
|
const sidechain = this.sidechainStore(sessionId, agentId, sidechainPaths, paths.praxisRoot);
|
|
595
724
|
const root = createClaudeSidechainRoot({
|
|
@@ -614,6 +743,8 @@ export class ClaudeSubagentExecutor {
|
|
|
614
743
|
? { permissionMode: input.permissionMode }
|
|
615
744
|
: {}),
|
|
616
745
|
...(input.isolation ? { isolation: input.isolation } : {}),
|
|
746
|
+
...(parentAgentId ? { parentAgentId } : {}),
|
|
747
|
+
...(initialIsolation ? { worktreePath: initialIsolation.cwd } : {}),
|
|
617
748
|
});
|
|
618
749
|
}
|
|
619
750
|
catch (error) {
|
|
@@ -623,10 +754,15 @@ export class ClaudeSubagentExecutor {
|
|
|
623
754
|
const provider = input.model
|
|
624
755
|
? (this.options.providerForModel?.(input.model) ?? this.options.provider)
|
|
625
756
|
: this.options.provider;
|
|
626
|
-
const
|
|
757
|
+
const lifecycleStore = this.options.persistence === 'memory'
|
|
758
|
+
? undefined
|
|
759
|
+
: new SubagentLifecycleStore(paths.praxisRoot, sessionId, agentId, sidechainPaths.transcriptFile);
|
|
760
|
+
const backgroundRun = this.createBackgroundAgentRun({
|
|
627
761
|
input,
|
|
762
|
+
parentCwd,
|
|
763
|
+
...(lifecycleStore ? { lifecycle: lifecycleStore } : {}),
|
|
628
764
|
...(initialIsolation ? { initialIsolation } : {}),
|
|
629
|
-
createIsolation: () => this.createAgentWorktree(paths.praxisRoot, sessionId, agentId),
|
|
765
|
+
createIsolation: () => this.createAgentWorktree(paths.praxisRoot, sessionId, agentId, parentCwd),
|
|
630
766
|
execute: (cwd, message, signal, continuation) => sidechain.withLease(async (lease) => this.runSidechain({
|
|
631
767
|
lease,
|
|
632
768
|
root,
|
|
@@ -643,48 +779,82 @@ export class ClaudeSubagentExecutor {
|
|
|
643
779
|
cwd,
|
|
644
780
|
})),
|
|
645
781
|
});
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
782
|
+
const resolvedModel = provider.model ?? 'praxis/provider';
|
|
783
|
+
const spec = {
|
|
784
|
+
agentId,
|
|
785
|
+
...(input.name ? { name: input.name } : {}),
|
|
786
|
+
agentType: input.subagentType,
|
|
787
|
+
description: input.description,
|
|
788
|
+
prompt: input.prompt,
|
|
789
|
+
toolUseId: call.id,
|
|
790
|
+
outputFile: sidechainPaths.transcriptFile,
|
|
791
|
+
resolvedModel,
|
|
792
|
+
run: backgroundRun.run,
|
|
793
|
+
markBackground: backgroundRun.markBackground,
|
|
794
|
+
...(lifecycleStore
|
|
795
|
+
? {
|
|
796
|
+
acknowledgeNotification: (notificationId) => lifecycleStore.acknowledgeNotification(notificationId),
|
|
797
|
+
prepareNotificationDetached: (notificationId, model) => lifecycleStore.prepareNotificationDetached(notificationId, model),
|
|
798
|
+
confirmNotificationDetached: (notificationId) => lifecycleStore.confirmNotificationDetached(notificationId),
|
|
799
|
+
}
|
|
800
|
+
: {}),
|
|
801
|
+
};
|
|
802
|
+
const asyncResult = () => ({
|
|
803
|
+
content: this.asyncLaunchResult({
|
|
649
804
|
agentId,
|
|
650
|
-
...(input.name ? { name: input.name } : {}),
|
|
651
|
-
agentType: input.subagentType,
|
|
652
805
|
description: input.description,
|
|
653
|
-
prompt: input.prompt,
|
|
654
|
-
toolUseId: call.id,
|
|
655
806
|
outputFile: sidechainPaths.transcriptFile,
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
807
|
+
...(initialIsolation ? { worktreePath: initialIsolation.cwd } : {}),
|
|
808
|
+
}),
|
|
809
|
+
isError: false,
|
|
810
|
+
nativeToolUseResult: {
|
|
811
|
+
...createClaudeAsyncAgentToolUseResult({
|
|
812
|
+
prompt: input.prompt,
|
|
661
813
|
agentId,
|
|
662
814
|
description: input.description,
|
|
815
|
+
resolvedModel,
|
|
663
816
|
outputFile: sidechainPaths.transcriptFile,
|
|
664
|
-
...(initialIsolation ? { worktreePath: initialIsolation.cwd } : {}),
|
|
665
817
|
}),
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
resolvedModel,
|
|
673
|
-
outputFile: sidechainPaths.transcriptFile,
|
|
674
|
-
}),
|
|
675
|
-
...(initialIsolation ? { worktreePath: initialIsolation.cwd } : {}),
|
|
676
|
-
},
|
|
677
|
-
};
|
|
818
|
+
...(initialIsolation ? { worktreePath: initialIsolation.cwd } : {}),
|
|
819
|
+
},
|
|
820
|
+
});
|
|
821
|
+
if (input.runInBackground) {
|
|
822
|
+
this.background.launch(spec);
|
|
823
|
+
return asyncResult();
|
|
678
824
|
}
|
|
679
825
|
const controller = new AbortController();
|
|
680
|
-
const abort = () => controller.abort();
|
|
826
|
+
const abort = () => controller.abort(context.signal?.reason);
|
|
681
827
|
context.signal?.addEventListener('abort', abort, { once: true });
|
|
828
|
+
if (context.signal?.aborted)
|
|
829
|
+
abort();
|
|
830
|
+
const operation = backgroundRun.run(input.prompt, controller.signal, false, call.id);
|
|
831
|
+
let resolveHandoff;
|
|
832
|
+
const handoff = new Promise((resolve) => {
|
|
833
|
+
resolveHandoff = resolve;
|
|
834
|
+
});
|
|
835
|
+
const foreground = {
|
|
836
|
+
spec,
|
|
837
|
+
controller,
|
|
838
|
+
operation,
|
|
839
|
+
startedAt: Date.now(),
|
|
840
|
+
detachParentSignal: () => context.signal?.removeEventListener('abort', abort),
|
|
841
|
+
resolveHandoff,
|
|
842
|
+
backgrounded: false,
|
|
843
|
+
};
|
|
844
|
+
this.foreground.set(agentId, foreground);
|
|
682
845
|
let result;
|
|
683
846
|
try {
|
|
684
|
-
|
|
847
|
+
const outcome = await Promise.race([
|
|
848
|
+
operation.then((value) => ({ kind: 'completed', value })),
|
|
849
|
+
handoff.then(() => ({ kind: 'backgrounded' })),
|
|
850
|
+
]);
|
|
851
|
+
if (outcome.kind === 'backgrounded')
|
|
852
|
+
return asyncResult();
|
|
853
|
+
result = outcome.value;
|
|
685
854
|
}
|
|
686
855
|
finally {
|
|
687
|
-
|
|
856
|
+
foreground.detachParentSignal();
|
|
857
|
+
this.foreground.delete(agentId);
|
|
688
858
|
}
|
|
689
859
|
const maxOutputBytes = this.options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
|
|
690
860
|
if (Buffer.byteLength(result.text) > maxOutputBytes) {
|
|
@@ -721,7 +891,7 @@ export class ClaudeSubagentExecutor {
|
|
|
721
891
|
agentId,
|
|
722
892
|
agentType: input.subagentType,
|
|
723
893
|
text: result.text,
|
|
724
|
-
resolvedModel
|
|
894
|
+
resolvedModel,
|
|
725
895
|
durationMs: result.durationMs,
|
|
726
896
|
usage: result.usage,
|
|
727
897
|
toolUseCount: result.toolUseCount,
|
|
@@ -750,23 +920,23 @@ export class ClaudeSubagentExecutor {
|
|
|
750
920
|
}
|
|
751
921
|
return store;
|
|
752
922
|
}
|
|
753
|
-
sessionPaths(sessionId) {
|
|
923
|
+
sessionPaths(sessionId, cwd = this.cwd()) {
|
|
754
924
|
return this.options.dataPlane === 'native'
|
|
755
925
|
? resolveDataPlanePaths({
|
|
756
926
|
dataPlane: 'native',
|
|
757
927
|
root: this.options.configRoot,
|
|
758
|
-
cwd
|
|
928
|
+
cwd,
|
|
759
929
|
sessionId,
|
|
760
930
|
})
|
|
761
931
|
: resolveClaudePaths({
|
|
762
932
|
configDir: this.options.configRoot,
|
|
763
|
-
cwd
|
|
933
|
+
cwd,
|
|
764
934
|
sessionId,
|
|
765
935
|
});
|
|
766
936
|
}
|
|
767
|
-
createAgentWorktree(praxisRoot, sessionId, agentId) {
|
|
937
|
+
createAgentWorktree(praxisRoot, sessionId, agentId, cwd = this.cwd()) {
|
|
768
938
|
return createManagedWorktree({
|
|
769
|
-
cwd
|
|
939
|
+
cwd,
|
|
770
940
|
parentDirectory: join(praxisRoot, 'agent-worktrees'),
|
|
771
941
|
directoryName: `${sessionId}-${agentId}`,
|
|
772
942
|
label: 'Agent',
|
|
@@ -775,55 +945,184 @@ export class ClaudeSubagentExecutor {
|
|
|
775
945
|
createBackgroundAgentRun(options) {
|
|
776
946
|
let availableIsolation = options.initialIsolation;
|
|
777
947
|
let retainedIsolation;
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
availableIsolation ??
|
|
782
|
-
(await options.createIsolation()))
|
|
783
|
-
: undefined;
|
|
784
|
-
availableIsolation = undefined;
|
|
948
|
+
let notificationExpected = options.input.runInBackground;
|
|
949
|
+
const run = async (message, signal, continuation, currentToolUseId) => {
|
|
950
|
+
await options.lifecycle?.write('running');
|
|
785
951
|
const startedAt = Date.now();
|
|
952
|
+
let isolation;
|
|
953
|
+
try {
|
|
954
|
+
isolation = options.input.isolation
|
|
955
|
+
? (retainedIsolation ??
|
|
956
|
+
availableIsolation ??
|
|
957
|
+
(await options.createIsolation()))
|
|
958
|
+
: undefined;
|
|
959
|
+
}
|
|
960
|
+
catch (error) {
|
|
961
|
+
const failureMessage = error instanceof Error ? error.message : String(error);
|
|
962
|
+
const failureResult = {
|
|
963
|
+
text: failureMessage,
|
|
964
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
965
|
+
toolUseCount: 0,
|
|
966
|
+
durationMs: Date.now() - startedAt,
|
|
967
|
+
};
|
|
968
|
+
const status = signal.aborted ? 'killed' : 'failed';
|
|
969
|
+
const notificationId = notificationExpected &&
|
|
970
|
+
!(signal.reason instanceof BackgroundAgentShutdownError)
|
|
971
|
+
? randomUUID()
|
|
972
|
+
: undefined;
|
|
973
|
+
await options.lifecycle?.write(status, failureMessage, {
|
|
974
|
+
result: failureResult,
|
|
975
|
+
...(notificationId
|
|
976
|
+
? {
|
|
977
|
+
notification: {
|
|
978
|
+
id: notificationId,
|
|
979
|
+
status,
|
|
980
|
+
toolUseId: currentToolUseId,
|
|
981
|
+
error: failureMessage,
|
|
982
|
+
},
|
|
983
|
+
}
|
|
984
|
+
: {}),
|
|
985
|
+
});
|
|
986
|
+
if (!notificationExpected)
|
|
987
|
+
throw error;
|
|
988
|
+
throw new BackgroundAgentRunError(failureMessage, { ...failureResult, ...(notificationId ? { notificationId } : {}) }, error);
|
|
989
|
+
}
|
|
990
|
+
availableIsolation = undefined;
|
|
786
991
|
let result;
|
|
787
992
|
let failure;
|
|
788
993
|
let cleanup;
|
|
789
994
|
try {
|
|
790
|
-
result = await options.execute(isolation?.cwd ??
|
|
995
|
+
result = await options.execute(isolation?.cwd ?? options.parentCwd, message, signal, continuation);
|
|
791
996
|
}
|
|
792
997
|
catch (error) {
|
|
793
998
|
failure = error;
|
|
794
999
|
}
|
|
795
1000
|
finally {
|
|
796
|
-
|
|
1001
|
+
try {
|
|
1002
|
+
cleanup = await isolation?.cleanup();
|
|
1003
|
+
}
|
|
1004
|
+
catch (error) {
|
|
1005
|
+
failure ??= error;
|
|
1006
|
+
}
|
|
797
1007
|
retainedIsolation = cleanup?.retained ? isolation : undefined;
|
|
798
1008
|
}
|
|
799
1009
|
if (failure !== undefined) {
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
1010
|
+
const underlyingFailure = failure instanceof SubagentExecutionFailure ? failure.cause : failure;
|
|
1011
|
+
const failureMessage = underlyingFailure instanceof Error
|
|
1012
|
+
? underlyingFailure.message
|
|
1013
|
+
: String(underlyingFailure);
|
|
1014
|
+
const detail = cleanup?.reason
|
|
1015
|
+
? `${failureMessage}\n${cleanup.reason}`
|
|
1016
|
+
: failureMessage;
|
|
1017
|
+
const partialResult = failure instanceof SubagentExecutionFailure
|
|
1018
|
+
? failure.result
|
|
1019
|
+
: undefined;
|
|
1020
|
+
const failureResult = {
|
|
1021
|
+
...partialResult,
|
|
1022
|
+
text: failureMessage,
|
|
1023
|
+
usage: partialResult?.usage ?? {
|
|
1024
|
+
inputTokens: 0,
|
|
1025
|
+
outputTokens: 0,
|
|
1026
|
+
},
|
|
1027
|
+
toolUseCount: partialResult?.toolUseCount ?? 0,
|
|
1028
|
+
durationMs: Date.now() - startedAt,
|
|
1029
|
+
...(isolation ? { isolationPath: isolation.cwd } : {}),
|
|
1030
|
+
...(cleanup ? { isolationRetained: cleanup.retained } : {}),
|
|
1031
|
+
...(cleanup?.reason ? { isolationWarning: cleanup.reason } : {}),
|
|
1032
|
+
};
|
|
1033
|
+
const status = signal.aborted ? 'killed' : 'failed';
|
|
1034
|
+
const notificationId = notificationExpected &&
|
|
1035
|
+
!(signal.reason instanceof BackgroundAgentShutdownError)
|
|
1036
|
+
? randomUUID()
|
|
1037
|
+
: undefined;
|
|
1038
|
+
await options.lifecycle?.write(status, detail, {
|
|
1039
|
+
result: failureResult,
|
|
1040
|
+
...(notificationId
|
|
1041
|
+
? {
|
|
1042
|
+
notification: {
|
|
1043
|
+
id: notificationId,
|
|
1044
|
+
status,
|
|
1045
|
+
toolUseId: currentToolUseId,
|
|
1046
|
+
error: detail,
|
|
1047
|
+
},
|
|
1048
|
+
}
|
|
1049
|
+
: {}),
|
|
1050
|
+
});
|
|
1051
|
+
if (!notificationExpected && !isolation && !cleanup?.reason) {
|
|
1052
|
+
throw underlyingFailure;
|
|
815
1053
|
}
|
|
816
|
-
throw
|
|
1054
|
+
throw new BackgroundAgentRunError(detail, { ...failureResult, ...(notificationId ? { notificationId } : {}) }, underlyingFailure);
|
|
817
1055
|
}
|
|
818
|
-
if (!result)
|
|
819
|
-
|
|
820
|
-
|
|
1056
|
+
if (!result) {
|
|
1057
|
+
const error = new Error('Agent completed without a result');
|
|
1058
|
+
const failureResult = {
|
|
1059
|
+
text: error.message,
|
|
1060
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
1061
|
+
toolUseCount: 0,
|
|
1062
|
+
durationMs: Date.now() - startedAt,
|
|
1063
|
+
...(isolation ? { isolationPath: isolation.cwd } : {}),
|
|
1064
|
+
...(cleanup ? { isolationRetained: cleanup.retained } : {}),
|
|
1065
|
+
...(cleanup?.reason ? { isolationWarning: cleanup.reason } : {}),
|
|
1066
|
+
};
|
|
1067
|
+
const notificationId = notificationExpected &&
|
|
1068
|
+
!(signal.reason instanceof BackgroundAgentShutdownError)
|
|
1069
|
+
? randomUUID()
|
|
1070
|
+
: undefined;
|
|
1071
|
+
await options.lifecycle?.write('failed', error.message, {
|
|
1072
|
+
result: failureResult,
|
|
1073
|
+
...(notificationId
|
|
1074
|
+
? {
|
|
1075
|
+
notification: {
|
|
1076
|
+
id: notificationId,
|
|
1077
|
+
status: 'failed',
|
|
1078
|
+
toolUseId: currentToolUseId,
|
|
1079
|
+
error: error.message,
|
|
1080
|
+
},
|
|
1081
|
+
}
|
|
1082
|
+
: {}),
|
|
1083
|
+
});
|
|
1084
|
+
if (!notificationExpected)
|
|
1085
|
+
throw error;
|
|
1086
|
+
throw new BackgroundAgentRunError(error.message, { ...failureResult, ...(notificationId ? { notificationId } : {}) }, error);
|
|
1087
|
+
}
|
|
1088
|
+
const finalResult = {
|
|
821
1089
|
...result,
|
|
822
1090
|
durationMs: Date.now() - startedAt,
|
|
823
1091
|
...(isolation ? { isolationPath: isolation.cwd } : {}),
|
|
824
1092
|
...(cleanup ? { isolationRetained: cleanup.retained } : {}),
|
|
825
1093
|
...(cleanup?.reason ? { isolationWarning: cleanup.reason } : {}),
|
|
826
1094
|
};
|
|
1095
|
+
const status = signal.aborted ? 'killed' : 'completed';
|
|
1096
|
+
const detail = signal.aborted
|
|
1097
|
+
? 'Agent was aborted before terminal cleanup'
|
|
1098
|
+
: undefined;
|
|
1099
|
+
const notificationId = notificationExpected &&
|
|
1100
|
+
!(signal.reason instanceof BackgroundAgentShutdownError)
|
|
1101
|
+
? randomUUID()
|
|
1102
|
+
: undefined;
|
|
1103
|
+
await options.lifecycle?.write(status, detail, {
|
|
1104
|
+
result: finalResult,
|
|
1105
|
+
...(notificationId
|
|
1106
|
+
? {
|
|
1107
|
+
notification: {
|
|
1108
|
+
id: notificationId,
|
|
1109
|
+
status,
|
|
1110
|
+
toolUseId: currentToolUseId,
|
|
1111
|
+
error: status === 'killed' ? (detail ?? null) : null,
|
|
1112
|
+
},
|
|
1113
|
+
}
|
|
1114
|
+
: {}),
|
|
1115
|
+
});
|
|
1116
|
+
return {
|
|
1117
|
+
...finalResult,
|
|
1118
|
+
...(notificationId ? { notificationId } : {}),
|
|
1119
|
+
};
|
|
1120
|
+
};
|
|
1121
|
+
return {
|
|
1122
|
+
run,
|
|
1123
|
+
markBackground: () => {
|
|
1124
|
+
notificationExpected = true;
|
|
1125
|
+
},
|
|
827
1126
|
};
|
|
828
1127
|
}
|
|
829
1128
|
async runWorkflowAgent(options) {
|
|
@@ -976,10 +1275,17 @@ export class ClaudeSubagentExecutor {
|
|
|
976
1275
|
input: { to, message, ...(summary === undefined ? {} : { summary }) },
|
|
977
1276
|
};
|
|
978
1277
|
}
|
|
979
|
-
async executeManagement(call, sessionId) {
|
|
1278
|
+
async executeManagement(call, sessionId, context) {
|
|
980
1279
|
if (call.name === 'TaskOutput') {
|
|
981
1280
|
const taskId = String(call.input.task_id);
|
|
982
|
-
await this.
|
|
1281
|
+
const owned = await this.options.outputOwnedBackgroundAgent?.(sessionId, taskId, {
|
|
1282
|
+
block: Boolean(call.input.block),
|
|
1283
|
+
timeout: Number(call.input.timeout),
|
|
1284
|
+
});
|
|
1285
|
+
if (owned !== undefined && owned !== null) {
|
|
1286
|
+
return { content: owned, isError: false };
|
|
1287
|
+
}
|
|
1288
|
+
await this.hydratePersistedTask(sessionId, taskId, context.cwd);
|
|
983
1289
|
return {
|
|
984
1290
|
content: await this.background.output(taskId, {
|
|
985
1291
|
block: Boolean(call.input.block),
|
|
@@ -990,23 +1296,77 @@ export class ClaudeSubagentExecutor {
|
|
|
990
1296
|
}
|
|
991
1297
|
if (call.name === 'TaskStop') {
|
|
992
1298
|
const taskId = String(call.input.task_id);
|
|
993
|
-
await this.
|
|
994
|
-
|
|
1299
|
+
const owned = await this.options.stopOwnedBackgroundAgent?.(sessionId, taskId);
|
|
1300
|
+
if (owned !== undefined && owned !== null) {
|
|
1301
|
+
return { content: owned, isError: false };
|
|
1302
|
+
}
|
|
1303
|
+
await this.hydratePersistedTask(sessionId, taskId, context.cwd);
|
|
1304
|
+
return {
|
|
1305
|
+
content: await this.background.stopAndWait(taskId),
|
|
1306
|
+
isError: false,
|
|
1307
|
+
};
|
|
995
1308
|
}
|
|
996
1309
|
const agentId = String(call.input.to);
|
|
997
|
-
|
|
1310
|
+
const summary = typeof call.input.summary === 'string' ? call.input.summary : undefined;
|
|
1311
|
+
const owned = this.options.sendOwnedBackgroundAgent?.(sessionId, agentId, String(call.input.message), summary, call.id);
|
|
1312
|
+
if (owned !== undefined && owned !== null) {
|
|
1313
|
+
return { content: owned, isError: false };
|
|
1314
|
+
}
|
|
1315
|
+
await this.hydratePersistedTask(sessionId, agentId, context.cwd);
|
|
998
1316
|
return {
|
|
999
|
-
content: this.background.send(agentId, String(call.input.message),
|
|
1317
|
+
content: this.background.send(agentId, String(call.input.message), summary, call.id),
|
|
1000
1318
|
isError: false,
|
|
1001
1319
|
};
|
|
1002
1320
|
}
|
|
1003
|
-
notifications(waitForRunning) {
|
|
1004
|
-
return this.background.notifications({ waitForRunning });
|
|
1321
|
+
notifications(waitForRunning, consume = true) {
|
|
1322
|
+
return this.background.notifications({ waitForRunning, consume });
|
|
1323
|
+
}
|
|
1324
|
+
acknowledgeNotifications(messages) {
|
|
1325
|
+
return this.background.acknowledge(messages);
|
|
1326
|
+
}
|
|
1327
|
+
prepareNotificationsDetached(messages) {
|
|
1328
|
+
return this.background.prepareNotificationsDetached(messages);
|
|
1329
|
+
}
|
|
1330
|
+
confirmNotificationsDetached(messages) {
|
|
1331
|
+
return this.background.confirmNotificationsDetached(messages);
|
|
1332
|
+
}
|
|
1333
|
+
reconcileDeliveredNotifications(delivered = this.options.notificationDelivered ?? (() => false)) {
|
|
1334
|
+
return this.background.acknowledgeDelivered(delivered);
|
|
1335
|
+
}
|
|
1336
|
+
reconcileDetachedNotifications(delivered) {
|
|
1337
|
+
return this.background.acknowledgeDeliveredAsDetached(delivered);
|
|
1338
|
+
}
|
|
1339
|
+
notificationClaimAgentIds() {
|
|
1340
|
+
return this.background.notificationClaimAgentIds();
|
|
1341
|
+
}
|
|
1342
|
+
async hydratePersistedTasks(sessionId, parentCwd, excludedAgentIds = new Set()) {
|
|
1343
|
+
if (this.options.persistence === 'memory')
|
|
1344
|
+
return;
|
|
1345
|
+
const paths = this.sessionPaths(sessionId, parentCwd);
|
|
1346
|
+
const candidates = await this.discoverSidechainCandidates(paths.projectRoot, sessionId);
|
|
1347
|
+
for (const agentId of new Set(candidates.map((candidate) => candidate.agentId))) {
|
|
1348
|
+
if (excludedAgentIds.has(agentId) || this.background.has(agentId)) {
|
|
1349
|
+
continue;
|
|
1350
|
+
}
|
|
1351
|
+
try {
|
|
1352
|
+
const lifecycle = await new SubagentLifecycleStore(paths.praxisRoot, sessionId, agentId).read();
|
|
1353
|
+
if (!lifecycle?.notifications?.some((notification) => !notification.consumed)) {
|
|
1354
|
+
continue;
|
|
1355
|
+
}
|
|
1356
|
+
await this.hydratePersistedTask(sessionId, agentId, parentCwd);
|
|
1357
|
+
}
|
|
1358
|
+
catch (error) {
|
|
1359
|
+
this.options.eventSink?.({
|
|
1360
|
+
type: 'warning',
|
|
1361
|
+
message: `Background agent ${agentId} could not be recovered automatically: ${error instanceof Error ? error.message : String(error)}`,
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1005
1365
|
}
|
|
1006
|
-
async
|
|
1366
|
+
async hydratePersistedTask(sessionId, identifier, parentCwd) {
|
|
1007
1367
|
if (this.background.has(identifier))
|
|
1008
1368
|
return;
|
|
1009
|
-
const paths = this.sessionPaths(sessionId);
|
|
1369
|
+
const paths = this.sessionPaths(sessionId, parentCwd);
|
|
1010
1370
|
const sidechainPaths = await this.resolvePersistedSidechain(paths.projectRoot, sessionId, identifier);
|
|
1011
1371
|
if (!sidechainPaths)
|
|
1012
1372
|
return;
|
|
@@ -1040,10 +1400,14 @@ export class ClaudeSubagentExecutor {
|
|
|
1040
1400
|
: '';
|
|
1041
1401
|
const projected = projectClaudeModelMessages(snapshot.entries);
|
|
1042
1402
|
const lastAssistant = projected.at(-1);
|
|
1043
|
-
|
|
1044
|
-
lastAssistant.
|
|
1045
|
-
(lastAssistant.toolCalls?.length ?? 0)
|
|
1046
|
-
|
|
1403
|
+
const completed = lastAssistant?.role === 'assistant' &&
|
|
1404
|
+
lastAssistant.content.trim().length > 0 &&
|
|
1405
|
+
(lastAssistant.toolCalls?.length ?? 0) === 0;
|
|
1406
|
+
const lifecycleStore = new SubagentLifecycleStore(paths.praxisRoot, sessionId, agentId, sidechainPaths.transcriptFile);
|
|
1407
|
+
const lifecycle = await lifecycleStore.read();
|
|
1408
|
+
const lifecycleIsCurrent = lifecycle !== null && (await lifecycleStore.matchesTranscript(lifecycle));
|
|
1409
|
+
if (lifecycleIsCurrent && lifecycle.status === 'completed' && !completed) {
|
|
1410
|
+
throw new Error(`Background agent ${agentId} has completed lifecycle state but an incomplete sidechain`);
|
|
1047
1411
|
}
|
|
1048
1412
|
const agentType = metadata?.agentType ?? 'general-purpose';
|
|
1049
1413
|
const description = metadata?.description ?? 'Recovered Claude sidechain';
|
|
@@ -1051,7 +1415,60 @@ export class ClaudeSubagentExecutor {
|
|
|
1051
1415
|
const spawnDepth = metadata?.spawnDepth ?? 1;
|
|
1052
1416
|
const name = metadata?.name;
|
|
1053
1417
|
const permissionMode = metadata?.permissionMode;
|
|
1054
|
-
|
|
1418
|
+
let isolation = metadata?.isolation;
|
|
1419
|
+
let restoredIsolation;
|
|
1420
|
+
let restoredCwd = parentCwd;
|
|
1421
|
+
if (isolation === 'worktree') {
|
|
1422
|
+
if (metadata?.worktreePath === undefined) {
|
|
1423
|
+
this.options.eventSink?.({
|
|
1424
|
+
type: 'warning',
|
|
1425
|
+
message: `Background agent ${agentId} has no retained worktree path; falling back to parent cwd ${parentCwd}`,
|
|
1426
|
+
});
|
|
1427
|
+
isolation = undefined;
|
|
1428
|
+
}
|
|
1429
|
+
else {
|
|
1430
|
+
try {
|
|
1431
|
+
restoredIsolation = await restoreManagedWorktree({
|
|
1432
|
+
cwd: parentCwd,
|
|
1433
|
+
path: metadata.worktreePath,
|
|
1434
|
+
label: 'Agent',
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
catch (error) {
|
|
1438
|
+
this.options.eventSink?.({
|
|
1439
|
+
type: 'warning',
|
|
1440
|
+
message: `Background agent ${agentId} could not restore its retained worktree; falling back to parent cwd ${parentCwd}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1441
|
+
});
|
|
1442
|
+
isolation = undefined;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
else {
|
|
1447
|
+
const persistedCwd = root.cwd;
|
|
1448
|
+
if (typeof persistedCwd === 'string' &&
|
|
1449
|
+
persistedCwd.length > 0 &&
|
|
1450
|
+
!persistedCwd.includes('\0') &&
|
|
1451
|
+
isAbsolute(persistedCwd)) {
|
|
1452
|
+
try {
|
|
1453
|
+
if (!(await lstat(persistedCwd)).isDirectory()) {
|
|
1454
|
+
throw new Error('persisted cwd is not a directory');
|
|
1455
|
+
}
|
|
1456
|
+
restoredCwd = persistedCwd;
|
|
1457
|
+
}
|
|
1458
|
+
catch (error) {
|
|
1459
|
+
this.options.eventSink?.({
|
|
1460
|
+
type: 'warning',
|
|
1461
|
+
message: `Background agent ${agentId} could not restore its persisted cwd; falling back to parent cwd ${parentCwd}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
else {
|
|
1466
|
+
this.options.eventSink?.({
|
|
1467
|
+
type: 'warning',
|
|
1468
|
+
message: `Background agent ${agentId} has an invalid persisted cwd; falling back to parent cwd ${parentCwd}`,
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1055
1472
|
const input = {
|
|
1056
1473
|
description,
|
|
1057
1474
|
prompt,
|
|
@@ -1062,9 +1479,12 @@ export class ClaudeSubagentExecutor {
|
|
|
1062
1479
|
runInBackground: true,
|
|
1063
1480
|
};
|
|
1064
1481
|
const provider = this.options.provider;
|
|
1065
|
-
const
|
|
1482
|
+
const backgroundRun = this.createBackgroundAgentRun({
|
|
1066
1483
|
input,
|
|
1067
|
-
|
|
1484
|
+
parentCwd: restoredCwd,
|
|
1485
|
+
lifecycle: lifecycleStore,
|
|
1486
|
+
...(restoredIsolation ? { initialIsolation: restoredIsolation } : {}),
|
|
1487
|
+
createIsolation: () => this.createAgentWorktree(paths.praxisRoot, sessionId, agentId, parentCwd),
|
|
1068
1488
|
execute: (cwd, message, signal, continuation) => sidechain.withLease(async (lease) => this.runSidechain({
|
|
1069
1489
|
lease,
|
|
1070
1490
|
root,
|
|
@@ -1081,7 +1501,7 @@ export class ClaudeSubagentExecutor {
|
|
|
1081
1501
|
cwd,
|
|
1082
1502
|
})),
|
|
1083
1503
|
});
|
|
1084
|
-
|
|
1504
|
+
const spec = {
|
|
1085
1505
|
agentId,
|
|
1086
1506
|
...(name ? { name } : {}),
|
|
1087
1507
|
agentType,
|
|
@@ -1090,16 +1510,57 @@ export class ClaudeSubagentExecutor {
|
|
|
1090
1510
|
toolUseId,
|
|
1091
1511
|
outputFile: sidechainPaths.transcriptFile,
|
|
1092
1512
|
resolvedModel: provider.model ?? 'praxis/provider',
|
|
1093
|
-
run,
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1513
|
+
run: backgroundRun.run,
|
|
1514
|
+
markBackground: backgroundRun.markBackground,
|
|
1515
|
+
acknowledgeNotification: (notificationId) => lifecycleStore.acknowledgeNotification(notificationId),
|
|
1516
|
+
prepareNotificationDetached: (notificationId, model) => lifecycleStore.prepareNotificationDetached(notificationId, model),
|
|
1517
|
+
confirmNotificationDetached: (notificationId) => lifecycleStore.confirmNotificationDetached(notificationId),
|
|
1518
|
+
};
|
|
1519
|
+
if (lifecycleIsCurrent && lifecycle.status === 'failed') {
|
|
1520
|
+
this.background.registerTerminal(spec, 'failed', lifecycle.detail ?? 'Persisted agent failed');
|
|
1521
|
+
}
|
|
1522
|
+
else if (lifecycleIsCurrent && lifecycle.status === 'killed') {
|
|
1523
|
+
this.background.registerTerminal(spec, 'stopped', lifecycle.detail ?? 'Persisted agent was killed');
|
|
1524
|
+
}
|
|
1525
|
+
else if (completed) {
|
|
1526
|
+
this.background.registerCompleted(spec, lifecycleIsCurrent && lifecycle.result
|
|
1527
|
+
? lifecycle.result
|
|
1528
|
+
: {
|
|
1529
|
+
text: lastAssistant.content,
|
|
1530
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
1531
|
+
toolUseCount: 0,
|
|
1532
|
+
durationMs: 0,
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
else {
|
|
1536
|
+
this.background.registerInterrupted(spec);
|
|
1537
|
+
}
|
|
1538
|
+
for (const notification of lifecycle?.notifications ?? []) {
|
|
1539
|
+
if (notification.consumed)
|
|
1540
|
+
continue;
|
|
1541
|
+
if (this.options.notificationDelivered?.({
|
|
1542
|
+
agentId,
|
|
1543
|
+
toolUseId: notification.toolUseId,
|
|
1544
|
+
status: notification.status,
|
|
1545
|
+
})) {
|
|
1546
|
+
if (notification.accounting?.kind === 'detached' &&
|
|
1547
|
+
!notification.accounting.delivered) {
|
|
1548
|
+
await lifecycleStore.confirmNotificationDetached(notification.id);
|
|
1549
|
+
}
|
|
1550
|
+
await lifecycleStore.acknowledgeNotification(notification.id);
|
|
1551
|
+
continue;
|
|
1552
|
+
}
|
|
1553
|
+
this.background.registerPersistedNotification(agentId, {
|
|
1554
|
+
id: notification.id,
|
|
1555
|
+
status: notification.status === 'killed' ? 'stopped' : notification.status,
|
|
1556
|
+
result: notification.result,
|
|
1557
|
+
error: notification.error,
|
|
1558
|
+
toolUseId: notification.toolUseId,
|
|
1559
|
+
});
|
|
1560
|
+
}
|
|
1100
1561
|
}
|
|
1101
1562
|
async resolvePersistedSidechain(projectRoot, sessionId, identifier) {
|
|
1102
|
-
const isAgentId =
|
|
1563
|
+
const isAgentId = isClaudeAgentId(identifier);
|
|
1103
1564
|
const isName = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/u.test(identifier);
|
|
1104
1565
|
if (!isAgentId && !isName)
|
|
1105
1566
|
return null;
|
|
@@ -1107,6 +1568,9 @@ export class ClaudeSubagentExecutor {
|
|
|
1107
1568
|
const matches = candidates.filter((candidate) => isAgentId
|
|
1108
1569
|
? candidate.agentId === identifier
|
|
1109
1570
|
: candidate.name === identifier);
|
|
1571
|
+
if (matches.length > 1) {
|
|
1572
|
+
throw new Error(`Ambiguous persisted background agent ${identifier}: ${matches.length} sidechains match`);
|
|
1573
|
+
}
|
|
1110
1574
|
matches.sort((left, right) => (right.metadataModifiedAt ?? -Infinity) -
|
|
1111
1575
|
(left.metadataModifiedAt ?? -Infinity) ||
|
|
1112
1576
|
left.agentId.localeCompare(right.agentId) ||
|
|
@@ -1151,11 +1615,9 @@ export class ClaudeSubagentExecutor {
|
|
|
1151
1615
|
}
|
|
1152
1616
|
if (!entry.isFile())
|
|
1153
1617
|
return;
|
|
1154
|
-
const
|
|
1155
|
-
if (
|
|
1156
|
-
const agentId =
|
|
1157
|
-
if (agentId === undefined)
|
|
1158
|
-
return;
|
|
1618
|
+
const metadataAgentId = sidechainAgentIdFromFile(entry.name, '.meta.json');
|
|
1619
|
+
if (metadataAgentId !== undefined) {
|
|
1620
|
+
const agentId = metadataAgentId;
|
|
1159
1621
|
const transcriptEntry = byName.get(`agent-${agentId}.jsonl`);
|
|
1160
1622
|
if (!transcriptEntry ||
|
|
1161
1623
|
transcriptEntry.isSymbolicLink() ||
|
|
@@ -1183,12 +1645,10 @@ export class ClaudeSubagentExecutor {
|
|
|
1183
1645
|
});
|
|
1184
1646
|
return;
|
|
1185
1647
|
}
|
|
1186
|
-
const
|
|
1187
|
-
if (
|
|
1188
|
-
return;
|
|
1189
|
-
const agentId = transcriptMatch[1];
|
|
1190
|
-
if (agentId === undefined)
|
|
1648
|
+
const transcriptAgentId = sidechainAgentIdFromFile(entry.name, '.jsonl');
|
|
1649
|
+
if (transcriptAgentId === undefined)
|
|
1191
1650
|
return;
|
|
1651
|
+
const agentId = transcriptAgentId;
|
|
1192
1652
|
// A metadata companion (even an invalid one) is owned by the
|
|
1193
1653
|
// metadata branch; only a bare transcript without metadata is a
|
|
1194
1654
|
// legacy candidate.
|
|
@@ -1218,7 +1678,7 @@ export class ClaudeSubagentExecutor {
|
|
|
1218
1678
|
}
|
|
1219
1679
|
async runSidechain(options) {
|
|
1220
1680
|
const cwd = options.cwd ?? this.cwd();
|
|
1221
|
-
const permissions = options.input.permissionMode
|
|
1681
|
+
const permissions = options.input.permissionMode && options.input.permissionMode !== 'default'
|
|
1222
1682
|
? this.options.permissionResolverForMode?.(options.input.permissionMode)
|
|
1223
1683
|
: this.options.permissions;
|
|
1224
1684
|
if (!permissions) {
|
|
@@ -1226,6 +1686,10 @@ export class ClaudeSubagentExecutor {
|
|
|
1226
1686
|
}
|
|
1227
1687
|
let snapshot = await options.lease.load();
|
|
1228
1688
|
if (options.continuationMessage !== undefined) {
|
|
1689
|
+
// Validate ambiguous tool linkage before the append-only sidechain is
|
|
1690
|
+
// mutated. The projection is rebuilt after appending the one explicit
|
|
1691
|
+
// continuation prompt so that prompt is visible to the provider.
|
|
1692
|
+
projectClaudeSidechainContinuationMessages(snapshot.entries);
|
|
1229
1693
|
const [entry] = translateProviderEvents([
|
|
1230
1694
|
{
|
|
1231
1695
|
type: 'user-text-block',
|
|
@@ -1253,6 +1717,12 @@ export class ClaudeSubagentExecutor {
|
|
|
1253
1717
|
}
|
|
1254
1718
|
let toolUseCount = 0;
|
|
1255
1719
|
let taskTokenTotal = 0;
|
|
1720
|
+
let recordedUsage = { inputTokens: 0, outputTokens: 0 };
|
|
1721
|
+
let pendingProviderUsage;
|
|
1722
|
+
const recordedModelUsage = new Map();
|
|
1723
|
+
let recordedDurationApiMs = 0;
|
|
1724
|
+
let recordedDurationApiWithoutRetriesMs = 0;
|
|
1725
|
+
let recordedApiDurationSeen = false;
|
|
1256
1726
|
let lastToolName;
|
|
1257
1727
|
const taskStartedAt = Date.now();
|
|
1258
1728
|
const append = async (entry) => {
|
|
@@ -1286,7 +1756,48 @@ export class ClaudeSubagentExecutor {
|
|
|
1286
1756
|
const scopedHooks = scopedHookSettings.length > 0
|
|
1287
1757
|
? this.options.hooks?.withAdditionalSettings(scopedHookSettings)
|
|
1288
1758
|
: this.options.hooks;
|
|
1289
|
-
const
|
|
1759
|
+
const commitProviderUsage = () => {
|
|
1760
|
+
if (!pendingProviderUsage)
|
|
1761
|
+
return;
|
|
1762
|
+
recordedUsage = mergeSubagentUsage(recordedUsage, pendingProviderUsage);
|
|
1763
|
+
const model = options.provider.model?.trim();
|
|
1764
|
+
if (model) {
|
|
1765
|
+
mergeSubagentModelUsage(recordedModelUsage, {
|
|
1766
|
+
[model]: {
|
|
1767
|
+
...pendingProviderUsage,
|
|
1768
|
+
...(options.provider.capabilities.contextWindowTokens === undefined
|
|
1769
|
+
? {}
|
|
1770
|
+
: {
|
|
1771
|
+
contextWindow: options.provider.capabilities.contextWindowTokens,
|
|
1772
|
+
}),
|
|
1773
|
+
...(options.provider.capabilities.maxOutputTokens === undefined
|
|
1774
|
+
? {}
|
|
1775
|
+
: {
|
|
1776
|
+
maxOutputTokens: options.provider.capabilities.maxOutputTokens,
|
|
1777
|
+
}),
|
|
1778
|
+
},
|
|
1779
|
+
});
|
|
1780
|
+
}
|
|
1781
|
+
pendingProviderUsage = undefined;
|
|
1782
|
+
};
|
|
1783
|
+
const recordApiDuration = (options) => {
|
|
1784
|
+
if (options.durationApiMs === undefined &&
|
|
1785
|
+
options.durationApiWithoutRetriesMs === undefined) {
|
|
1786
|
+
return;
|
|
1787
|
+
}
|
|
1788
|
+
const total = options.durationApiMs ?? 0;
|
|
1789
|
+
const withoutRetries = options.durationApiWithoutRetriesMs ?? total;
|
|
1790
|
+
if (!Number.isFinite(total) ||
|
|
1791
|
+
total < 0 ||
|
|
1792
|
+
!Number.isFinite(withoutRetries) ||
|
|
1793
|
+
withoutRetries < 0) {
|
|
1794
|
+
throw new Error('Subagent API duration must be finite and nonnegative');
|
|
1795
|
+
}
|
|
1796
|
+
recordedDurationApiMs += total;
|
|
1797
|
+
recordedDurationApiWithoutRetriesMs += withoutRetries;
|
|
1798
|
+
recordedApiDurationSeen = true;
|
|
1799
|
+
};
|
|
1800
|
+
const nestedTools = this.registry(String(options.root.sessionId), options.spawnDepth, () => options.promptId, options.agentId);
|
|
1290
1801
|
const agentMcp = customAgent?.mcpServers?.length
|
|
1291
1802
|
? await this.options.mcp?.connectAgent?.({
|
|
1292
1803
|
specs: customAgent.mcpServers,
|
|
@@ -1330,6 +1841,7 @@ export class ClaudeSubagentExecutor {
|
|
|
1330
1841
|
: permissions;
|
|
1331
1842
|
const emit = (event) => {
|
|
1332
1843
|
if (event.type === 'usage') {
|
|
1844
|
+
pendingProviderUsage = event.usage;
|
|
1333
1845
|
taskTokenTotal += event.usage.inputTokens + event.usage.outputTokens;
|
|
1334
1846
|
this.options.eventSink?.({
|
|
1335
1847
|
type: 'task-progress',
|
|
@@ -1381,6 +1893,7 @@ export class ClaudeSubagentExecutor {
|
|
|
1381
1893
|
});
|
|
1382
1894
|
const observer = {
|
|
1383
1895
|
assistantCompleted: async (message) => {
|
|
1896
|
+
commitProviderUsage();
|
|
1384
1897
|
const [entry] = translateProviderEvents([
|
|
1385
1898
|
{
|
|
1386
1899
|
type: 'assistant-message',
|
|
@@ -1398,6 +1911,13 @@ export class ClaudeSubagentExecutor {
|
|
|
1398
1911
|
await append(toClaudeSidechainEntry(entry, options.agentId, options.input.subagentType));
|
|
1399
1912
|
},
|
|
1400
1913
|
toolCompleted: async (call, result) => {
|
|
1914
|
+
if (result.usage) {
|
|
1915
|
+
recordedUsage = mergeSubagentUsage(recordedUsage, result.usage);
|
|
1916
|
+
}
|
|
1917
|
+
if (result.isError === false && result.modelUsage) {
|
|
1918
|
+
mergeSubagentModelUsage(recordedModelUsage, result.modelUsage);
|
|
1919
|
+
}
|
|
1920
|
+
recordApiDuration(result);
|
|
1401
1921
|
if (result.isError === false) {
|
|
1402
1922
|
const linesAdded = result.linesAdded ?? 0;
|
|
1403
1923
|
const linesRemoved = result.linesRemoved ?? 0;
|
|
@@ -1441,6 +1961,7 @@ export class ClaudeSubagentExecutor {
|
|
|
1441
1961
|
if (!entry)
|
|
1442
1962
|
throw new Error('Could not translate subagent follow-up');
|
|
1443
1963
|
await append(toClaudeSidechainEntry(entry, options.agentId, options.input.subagentType));
|
|
1964
|
+
await this.background.acknowledge([text]);
|
|
1444
1965
|
}
|
|
1445
1966
|
},
|
|
1446
1967
|
};
|
|
@@ -1508,7 +2029,9 @@ export class ClaudeSubagentExecutor {
|
|
|
1508
2029
|
...(composedSubagentPolicy
|
|
1509
2030
|
? []
|
|
1510
2031
|
: [{ role: 'system', content: system }]),
|
|
1511
|
-
...injectFirstUserMessageContext(
|
|
2032
|
+
...injectFirstUserMessageContext(options.continuationMessage === undefined
|
|
2033
|
+
? projectClaudeModelMessages(snapshot.entries)
|
|
2034
|
+
: projectClaudeSidechainContinuationMessages(snapshot.entries), assembledContext?.firstUserMessageContext),
|
|
1512
2035
|
...preloadedSkills,
|
|
1513
2036
|
];
|
|
1514
2037
|
observedMessages = messages;
|
|
@@ -1555,10 +2078,26 @@ export class ClaudeSubagentExecutor {
|
|
|
1555
2078
|
messages.push(`SubagentStop hook error: ${outcome.blockedReason}`);
|
|
1556
2079
|
}
|
|
1557
2080
|
}
|
|
2081
|
+
await this.hydratePersistedTasks(String(options.root.sessionId), options.cwd ?? this.cwd());
|
|
2082
|
+
await this.reconcileDeliveredNotifications((notification) => {
|
|
2083
|
+
const markers = backgroundAgentNotificationMarkers(notification);
|
|
2084
|
+
return snapshot.entries.some((entry) => {
|
|
2085
|
+
if (entry.type !== 'user')
|
|
2086
|
+
return false;
|
|
2087
|
+
const source = JSON.stringify(entry.message);
|
|
2088
|
+
return markers.every((marker) => source.includes(marker));
|
|
2089
|
+
});
|
|
2090
|
+
});
|
|
1558
2091
|
const background = await this.background.notifications({
|
|
1559
2092
|
waitForRunning: false,
|
|
1560
2093
|
excludeAgentId: options.agentId,
|
|
2094
|
+
consume: false,
|
|
1561
2095
|
});
|
|
2096
|
+
recordedUsage = mergeSubagentUsage(recordedUsage, background.usage);
|
|
2097
|
+
if (background.modelUsage) {
|
|
2098
|
+
mergeSubagentModelUsage(recordedModelUsage, background.modelUsage);
|
|
2099
|
+
}
|
|
2100
|
+
recordApiDuration(background);
|
|
1562
2101
|
messages.push(...background.messages);
|
|
1563
2102
|
messages.push(...((await this.options.backgroundTaskNotifications?.(true)) ?? []));
|
|
1564
2103
|
return {
|
|
@@ -1614,20 +2153,38 @@ export class ClaudeSubagentExecutor {
|
|
|
1614
2153
|
};
|
|
1615
2154
|
}
|
|
1616
2155
|
catch (error) {
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
2156
|
+
commitProviderUsage();
|
|
2157
|
+
const partialModelUsage = recordedModelUsage.size === 0
|
|
2158
|
+
? undefined
|
|
2159
|
+
: Object.fromEntries(recordedModelUsage);
|
|
2160
|
+
if (!(options.signal?.reason instanceof BackgroundAgentShutdownError)) {
|
|
2161
|
+
this.options.eventSink?.({
|
|
2162
|
+
type: 'task-notification',
|
|
2163
|
+
taskId: options.agentId,
|
|
2164
|
+
...(options.toolUseId ? { toolUseId: options.toolUseId } : {}),
|
|
2165
|
+
status: options.signal?.aborted ? 'stopped' : 'failed',
|
|
2166
|
+
outputFile: options.transcriptPath,
|
|
2167
|
+
summary: error instanceof Error ? error.message : String(error),
|
|
2168
|
+
usage: {
|
|
2169
|
+
totalTokens: recordedUsage.inputTokens + recordedUsage.outputTokens,
|
|
2170
|
+
toolUses: toolUseCount,
|
|
2171
|
+
durationMs: Date.now() - taskStartedAt,
|
|
2172
|
+
},
|
|
2173
|
+
});
|
|
2174
|
+
}
|
|
2175
|
+
throw new SubagentExecutionFailure(error, {
|
|
2176
|
+
text: error instanceof Error ? error.message : String(error),
|
|
2177
|
+
usage: recordedUsage,
|
|
2178
|
+
...(partialModelUsage ? { modelUsage: partialModelUsage } : {}),
|
|
2179
|
+
...(recordedApiDurationSeen
|
|
2180
|
+
? {
|
|
2181
|
+
durationApiMs: recordedDurationApiMs,
|
|
2182
|
+
durationApiWithoutRetriesMs: recordedDurationApiWithoutRetriesMs,
|
|
2183
|
+
}
|
|
2184
|
+
: {}),
|
|
2185
|
+
toolUseCount,
|
|
2186
|
+
durationMs: Date.now() - taskStartedAt,
|
|
1629
2187
|
});
|
|
1630
|
-
throw error;
|
|
1631
2188
|
}
|
|
1632
2189
|
finally {
|
|
1633
2190
|
await agentMcp?.close().catch((error) => {
|
|
@@ -1645,12 +2202,14 @@ class ClaudeSubagentToolRegistry {
|
|
|
1645
2202
|
sessionId;
|
|
1646
2203
|
depth;
|
|
1647
2204
|
promptIdForCall;
|
|
1648
|
-
|
|
2205
|
+
parentAgentId;
|
|
2206
|
+
constructor(base, executor, sessionId, depth, promptIdForCall, parentAgentId) {
|
|
1649
2207
|
this.base = base;
|
|
1650
2208
|
this.executor = executor;
|
|
1651
2209
|
this.sessionId = sessionId;
|
|
1652
2210
|
this.depth = depth;
|
|
1653
2211
|
this.promptIdForCall = promptIdForCall;
|
|
2212
|
+
this.parentAgentId = parentAgentId;
|
|
1654
2213
|
}
|
|
1655
2214
|
definitions() {
|
|
1656
2215
|
const base = this.base.definitions();
|
|
@@ -1698,21 +2257,21 @@ class ClaudeSubagentToolRegistry {
|
|
|
1698
2257
|
}
|
|
1699
2258
|
async execute(call, context) {
|
|
1700
2259
|
if (call.name === 'SendMessage') {
|
|
1701
|
-
return this.executor.executeManagement(call, this.sessionId);
|
|
2260
|
+
return this.executor.executeManagement(call, this.sessionId, context);
|
|
1702
2261
|
}
|
|
1703
2262
|
if (call.name === 'TaskOutput' || call.name === 'TaskStop') {
|
|
1704
2263
|
const taskId = call.input.task_id ?? call.input.shell_id;
|
|
1705
2264
|
if (typeof taskId === 'string' && isBackgroundBashTaskId(taskId)) {
|
|
1706
2265
|
return this.base.execute(call, context);
|
|
1707
2266
|
}
|
|
1708
|
-
return this.executor.executeManagement(call, this.sessionId);
|
|
2267
|
+
return this.executor.executeManagement(call, this.sessionId, context);
|
|
1709
2268
|
}
|
|
1710
2269
|
if (call.name !== 'Agent')
|
|
1711
2270
|
return this.base.execute(call, context);
|
|
1712
2271
|
const promptId = this.promptIdForCall(call.id);
|
|
1713
2272
|
if (!promptId)
|
|
1714
2273
|
throw new Error(`Could not locate prompt for Agent ${call.id}`);
|
|
1715
|
-
return this.executor.execute(call, this.sessionId, this.depth, promptId, context);
|
|
2274
|
+
return this.executor.execute(call, this.sessionId, this.depth, promptId, context, this.parentAgentId);
|
|
1716
2275
|
}
|
|
1717
2276
|
}
|
|
1718
2277
|
//# sourceMappingURL=subagent-service.js.map
|