praxis-agent 0.27.0 → 0.29.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.
Files changed (47) hide show
  1. package/dist/application/session-service.d.ts +1 -0
  2. package/dist/application/session-service.js +137 -14
  3. package/dist/application/subagent-service.d.ts +1 -0
  4. package/dist/application/subagent-service.js +30 -2
  5. package/dist/cli-runtime.js +51 -20
  6. package/dist/compatibility/claude/context.d.ts +19 -4
  7. package/dist/compatibility/claude/context.js +198 -24
  8. package/dist/core/context.d.ts +13 -0
  9. package/dist/core/prompt-composer.d.ts +27 -0
  10. package/dist/core/prompt-composer.js +99 -0
  11. package/dist/core/runtime.d.ts +14 -0
  12. package/dist/core/runtime.js +116 -28
  13. package/dist/core/tool-execution-scheduler.d.ts +38 -0
  14. package/dist/core/tool-execution-scheduler.js +95 -0
  15. package/dist/core/tool-scheduling-policy.d.ts +4 -0
  16. package/dist/core/tool-scheduling-policy.js +24 -0
  17. package/dist/extensions/claude-extension-tools.d.ts +1 -0
  18. package/dist/extensions/claude-extension-tools.js +6 -0
  19. package/dist/hooks/claude-hook-tools.d.ts +4 -0
  20. package/dist/hooks/claude-hook-tools.js +6 -0
  21. package/dist/mcp/claude-mcp-tools.d.ts +10 -0
  22. package/dist/mcp/claude-mcp-tools.js +65 -0
  23. package/dist/permissions/claude-permission-resolver.js +3 -3
  24. package/dist/permissions/permission-updates.d.ts +1 -0
  25. package/dist/permissions/permission-updates.js +13 -1
  26. package/dist/providers/anthropic-compatible.js +15 -0
  27. package/dist/tools/claude-capabilities.d.ts +1 -0
  28. package/dist/tools/claude-capabilities.js +5 -0
  29. package/dist/tools/claude-interactive-tools.js +7 -0
  30. package/dist/tools/claude-lsp-tool.js +7 -0
  31. package/dist/tools/claude-scheduled-tools.d.ts +1 -0
  32. package/dist/tools/claude-scheduled-tools.js +7 -0
  33. package/dist/tools/claude-task-tools.d.ts +1 -0
  34. package/dist/tools/claude-task-tools.js +11 -0
  35. package/dist/tools/claude-user-message.d.ts +1 -0
  36. package/dist/tools/claude-user-message.js +7 -0
  37. package/dist/tools/claude-workflow-tools.d.ts +1 -0
  38. package/dist/tools/claude-workflow-tools.js +7 -0
  39. package/dist/tools/claude-worktree-tools.d.ts +1 -0
  40. package/dist/tools/claude-worktree-tools.js +7 -0
  41. package/dist/tools/filtered-tool-registry.d.ts +1 -0
  42. package/dist/tools/filtered-tool-registry.js +5 -0
  43. package/dist/tools/local-tools.d.ts +17 -0
  44. package/dist/tools/local-tools.js +86 -1
  45. package/dist/tools/web.d.ts +1 -0
  46. package/dist/tools/web.js +27 -0
  47. package/package.json +1 -1
@@ -208,6 +208,7 @@ export declare class ClaudeSessionService {
208
208
  private applyPermissionUpdates;
209
209
  close(): Promise<void>;
210
210
  transitionHookSession(sessionId: string, reason: Exclude<HookSessionEndReason, 'other'>): Promise<void>;
211
+ reloadContextResources(sessionId: string): void;
211
212
  createHostedToolRegistry(sessionId: string): ToolRegistry;
212
213
  run(prompt: string, signal?: AbortSignal, sessionId?: string, name?: string, images?: readonly ModelImage[], documents?: readonly ModelDocument[]): Promise<SessionRunResult>;
213
214
  runShell(command: string, signal?: AbortSignal, sessionId?: string, name?: string): Promise<SessionRunResult>;
@@ -16,6 +16,7 @@ import { selectClaudeSchemaAdapter, } from '../compatibility/claude/schema.js';
16
16
  import { findUnresolvedClaudeToolCalls, getClaudeContentBlocks, } from '../compatibility/claude/tool-links.js';
17
17
  import { createClaudeAgentSettingEntry, createClaudeHookAttachmentEntries, createClaudeLastPromptEntry, createClaudeRuleAttachmentEntry, translateProviderEvents, } from '../compatibility/claude/translation.js';
18
18
  import { AgentRunCancelledError, AgentRuntime, } from '../core/runtime.js';
19
+ import { resolveToolSchedulingPolicy } from '../core/tool-scheduling-policy.js';
19
20
  import { BackgroundTaskRuntime, } from './background-task-runtime.js';
20
21
  import { usageCostUsd } from '../core/usage.js';
21
22
  import { ContextBudget, ContextRecoveryPlanner, estimateModelRequestTokens, isPromptTooLongError, } from '../core/context-budget.js';
@@ -61,6 +62,7 @@ function mainAgentToolNames(tools, agent) {
61
62
  }
62
63
  const emptyToolRegistry = {
63
64
  definitions: () => [],
65
+ schedulingPolicy: () => ({ concurrency: 'exclusive' }),
64
66
  prepare: async (call) => call,
65
67
  execute: async () => ({ content: '', isError: false }),
66
68
  };
@@ -754,6 +756,16 @@ export class ClaudeSessionService {
754
756
  }
755
757
  async transitionHookSession(sessionId, reason) {
756
758
  await this.hookLifecycle.transition(sessionId, reason);
759
+ this.options.contextAssembler?.invalidate?.({
760
+ lifecycleId: sessionId,
761
+ reason: reason === 'resume' ? 'restore' : 'clear',
762
+ });
763
+ }
764
+ reloadContextResources(sessionId) {
765
+ this.options.contextAssembler?.invalidate?.({
766
+ lifecycleId: sessionId,
767
+ reason: 'resource-reload',
768
+ });
757
769
  }
758
770
  createHostedToolRegistry(sessionId) {
759
771
  const baseTools = this.options.tools;
@@ -943,6 +955,7 @@ export class ClaudeSessionService {
943
955
  (rightIndex < 0 ? preferredOrder.length : rightIndex));
944
956
  });
945
957
  },
958
+ schedulingPolicy: (call) => resolveToolSchedulingPolicy(capabilityRegistry, call),
946
959
  prepare: (call, context) => capabilityRegistry.prepare(call, context),
947
960
  execute: (call, context) => capabilityRegistry.execute(call, context),
948
961
  };
@@ -993,6 +1006,7 @@ export class ClaudeSessionService {
993
1006
  }
994
1007
  const assembledContext = await this.options.contextAssembler?.assemble({
995
1008
  cwd: this.activeCwd(),
1009
+ lifecycleId: activeSessionId,
996
1010
  });
997
1011
  const messages = [
998
1012
  ...(assembledContext?.systemMessages ?? []),
@@ -1103,13 +1117,18 @@ export class ClaudeSessionService {
1103
1117
  const agent = this.resolveAgent(agentName);
1104
1118
  const provider = this.providerForAgent(agent);
1105
1119
  this.activeProvider = provider;
1120
+ const agentSystem = await this.mainAgentSystemPrompt(agent);
1106
1121
  const assembledContext = await this.options.contextAssembler?.assemble({
1107
1122
  cwd: this.activeCwd(),
1123
+ lifecycleId: sessionId,
1124
+ ...(agentSystem ? { mode: 'agent', baseSystemPrompt: agentSystem } : {}),
1108
1125
  });
1109
- const agentSystem = await this.mainAgentSystemPrompt(agent);
1110
- const assembledSystemMessages = this.assembledSystemMessages(agent, assembledContext?.systemMessages ?? []);
1126
+ const hasPromptManifest = assembledContext?.promptSections !== undefined;
1127
+ const assembledSystemMessages = hasPromptManifest
1128
+ ? (assembledContext?.systemMessages ?? [])
1129
+ : this.assembledSystemMessages(agent, assembledContext?.systemMessages ?? []);
1111
1130
  const contextMessages = [
1112
- ...(agentSystem
1131
+ ...(!hasPromptManifest && agentSystem
1113
1132
  ? [{ role: 'system', content: agentSystem }]
1114
1133
  : []),
1115
1134
  ...assembledSystemMessages,
@@ -1407,12 +1426,22 @@ export class ClaudeSessionService {
1407
1426
  this.sessionCwds.set(sessionId, cwd);
1408
1427
  this.runtimeCwd = cwd;
1409
1428
  this.options.workspace?.setCwd(cwd);
1429
+ this.options.contextAssembler?.invalidate?.({
1430
+ lifecycleId: sessionId,
1431
+ reason: 'cwd',
1432
+ });
1410
1433
  return cwd;
1411
1434
  }
1412
1435
  this.runtimeCwd = cwd;
1413
1436
  this.options.workspace?.setCwd(cwd);
1414
1437
  if (sessionId)
1415
1438
  this.sessionCwds.set(sessionId, cwd);
1439
+ if (sessionId) {
1440
+ this.options.contextAssembler?.invalidate?.({
1441
+ lifecycleId: sessionId,
1442
+ reason: 'cwd',
1443
+ });
1444
+ }
1416
1445
  return cwd;
1417
1446
  }
1418
1447
  async recordCdUsage(sessionId) {
@@ -1788,6 +1817,10 @@ export class ClaudeSessionService {
1788
1817
  if (result.status === 'conflict') {
1789
1818
  throw new Error('Generated Claude fork session already exists');
1790
1819
  }
1820
+ this.options.contextAssembler?.invalidate?.({
1821
+ lifecycleId: sessionId,
1822
+ reason: 'fork',
1823
+ });
1791
1824
  return { sessionId, parentSessionId };
1792
1825
  }
1793
1826
  async ensureFork(parentSessionId, sessionId, checkpoint) {
@@ -1796,8 +1829,13 @@ export class ClaudeSessionService {
1796
1829
  const expected = await this.nativeForkEntries(parentSessionId, sessionId, checkpoint?.resumeSessionAt, checkpoint?.entryCount);
1797
1830
  const target = this.store(sessionId);
1798
1831
  const created = await target.create(expected);
1799
- if (created.status === 'created')
1832
+ if (created.status === 'created') {
1833
+ this.options.contextAssembler?.invalidate?.({
1834
+ lifecycleId: sessionId,
1835
+ reason: 'fork',
1836
+ });
1800
1837
  return { sessionId, parentSessionId };
1838
+ }
1801
1839
  const existing = await target.withLease((lease) => lease.load());
1802
1840
  if (existing.status === 'conflict') {
1803
1841
  throw new Error(`Claude transcript fork conflict: ${existing.reason}`);
@@ -1807,6 +1845,10 @@ export class ClaudeSessionService {
1807
1845
  JSON.stringify(entry))) {
1808
1846
  throw new Error('Claude handoff target is not the expected native fork');
1809
1847
  }
1848
+ this.options.contextAssembler?.invalidate?.({
1849
+ lifecycleId: sessionId,
1850
+ reason: 'fork',
1851
+ });
1810
1852
  return { sessionId, parentSessionId };
1811
1853
  }
1812
1854
  async nativeForkEntries(parentSessionId, sessionId, resumeSessionAt, sourceEntryCount) {
@@ -2316,6 +2358,10 @@ export class ClaudeSessionService {
2316
2358
  const fileHistoryTools = fileHistory && interactiveMessageTools
2317
2359
  ? {
2318
2360
  definitions: () => interactiveMessageTools.definitions(),
2361
+ schedulingPolicy: (call) => ({
2362
+ ...resolveToolSchedulingPolicy(interactiveMessageTools, call),
2363
+ startAfterAssistant: true,
2364
+ }),
2319
2365
  prepare: (call, context) => interactiveMessageTools.prepare(call, context),
2320
2366
  execute: async (call, context) => {
2321
2367
  const path = call.name === 'Write' || call.name === 'Edit'
@@ -2435,6 +2481,10 @@ export class ClaudeSessionService {
2435
2481
  currentTurnToolCalls += 1;
2436
2482
  const transition = this.worktreeManager?.consumeTransition(call.id);
2437
2483
  if (transition) {
2484
+ this.options.contextAssembler?.invalidate?.({
2485
+ lifecycleId: sessionId,
2486
+ reason: 'worktree',
2487
+ });
2438
2488
  const stateEntry = {
2439
2489
  type: 'worktree-state',
2440
2490
  worktreeSession: transition.state,
@@ -2577,29 +2627,83 @@ export class ClaudeSessionService {
2577
2627
  let planModeMessage;
2578
2628
  let sessionMemoryMessage = null;
2579
2629
  let contextMessages = [];
2630
+ let stableSystemMessageCount;
2580
2631
  const refreshRuntimeContext = async () => {
2581
- assembledContext = await this.options.contextAssembler?.assemble({
2582
- cwd: this.activeCwd(),
2583
- });
2584
2632
  agentSystem = await this.mainAgentSystemPrompt(agent);
2585
- const assembledSystemMessages = this.assembledSystemMessages(agent, assembledContext?.systemMessages ?? []);
2586
2633
  planModeMessage =
2587
2634
  this.options.interactiveTools?.contextMessage(sessionId);
2588
2635
  sessionMemoryMessage = sessionMemory
2589
2636
  ? this.sessionMemoryMessage(await sessionMemory.summary())
2590
2637
  : null;
2591
- contextMessages = [
2638
+ const additionalSections = [
2639
+ ...(planModeMessage
2640
+ ? [
2641
+ {
2642
+ id: 'plan-mode',
2643
+ placement: 'system',
2644
+ stability: 'volatile',
2645
+ content: planModeMessage,
2646
+ },
2647
+ ]
2648
+ : []),
2649
+ ...(sessionMemoryMessage
2650
+ ? [
2651
+ {
2652
+ id: 'session-memory',
2653
+ placement: 'system',
2654
+ stability: 'volatile',
2655
+ content: sessionMemoryMessage,
2656
+ },
2657
+ ]
2658
+ : []),
2659
+ ...(this.options.brief
2660
+ ? [
2661
+ {
2662
+ id: 'brief-output',
2663
+ placement: 'system',
2664
+ stability: 'volatile',
2665
+ content: CLAUDE_USER_MESSAGE_PROMPT,
2666
+ },
2667
+ ]
2668
+ : []),
2669
+ ...(this.options.structuredOutputSchema
2670
+ ? [
2671
+ {
2672
+ id: 'structured-output',
2673
+ placement: 'system',
2674
+ stability: 'volatile',
2675
+ content: 'You MUST call StructuredOutput exactly once at the end with a value matching the requested JSON Schema.',
2676
+ },
2677
+ ]
2678
+ : []),
2679
+ ];
2680
+ assembledContext = await this.options.contextAssembler?.assemble({
2681
+ cwd: this.activeCwd(),
2682
+ lifecycleId: sessionId,
2592
2683
  ...(agentSystem
2684
+ ? { mode: 'agent', baseSystemPrompt: agentSystem }
2685
+ : {}),
2686
+ additionalSections,
2687
+ });
2688
+ const hasPromptManifest = assembledContext?.promptSections !== undefined;
2689
+ stableSystemMessageCount = hasPromptManifest
2690
+ ? assembledContext?.stableSystemSectionCount
2691
+ : undefined;
2692
+ const assembledSystemMessages = hasPromptManifest
2693
+ ? (assembledContext?.systemMessages ?? [])
2694
+ : this.assembledSystemMessages(agent, assembledContext?.systemMessages ?? []);
2695
+ contextMessages = [
2696
+ ...(!hasPromptManifest && agentSystem
2593
2697
  ? [{ role: 'system', content: agentSystem }]
2594
2698
  : []),
2595
2699
  ...assembledSystemMessages,
2596
- ...(planModeMessage
2700
+ ...(!hasPromptManifest && planModeMessage
2597
2701
  ? [{ role: 'system', content: planModeMessage }]
2598
2702
  : []),
2599
- ...(sessionMemoryMessage
2703
+ ...(!hasPromptManifest && sessionMemoryMessage
2600
2704
  ? [{ role: 'system', content: sessionMemoryMessage }]
2601
2705
  : []),
2602
- ...(this.options.brief
2706
+ ...(!hasPromptManifest && this.options.brief
2603
2707
  ? [
2604
2708
  {
2605
2709
  role: 'system',
@@ -2607,7 +2711,7 @@ export class ClaudeSessionService {
2607
2711
  },
2608
2712
  ]
2609
2713
  : []),
2610
- ...(this.options.structuredOutputSchema
2714
+ ...(!hasPromptManifest && this.options.structuredOutputSchema
2611
2715
  ? [
2612
2716
  {
2613
2717
  role: 'system',
@@ -2908,6 +3012,10 @@ export class ClaudeSessionService {
2908
3012
  if (outcome)
2909
3013
  await recordHookOutcome(outcome);
2910
3014
  }
3015
+ this.options.contextAssembler?.invalidate?.({
3016
+ lifecycleId: sessionId,
3017
+ reason: 'compact',
3018
+ });
2911
3019
  await refreshRuntimeContext();
2912
3020
  this.options.eventSink?.({
2913
3021
  type: 'compact-boundary',
@@ -3079,11 +3187,14 @@ export class ClaudeSessionService {
3079
3187
  ...contextMessages,
3080
3188
  ...injectTurnContext(projectClaudeModelMessages(snapshot.entries)),
3081
3189
  ],
3190
+ ...(stableSystemMessageCount === undefined
3191
+ ? {}
3192
+ : { stableSystemMessageCount }),
3082
3193
  cwd: this.activeCwd(),
3083
3194
  toolResultDirectory,
3084
3195
  observer,
3085
3196
  ...(this.options.effort ? { effort: this.options.effort } : {}),
3086
- ...(this.options.maxModelTurns
3197
+ ...(this.options.maxModelTurns !== undefined
3087
3198
  ? { maxModelTurns: this.options.maxModelTurns }
3088
3199
  : {}),
3089
3200
  ...(this.options.betas?.length ? { betas: this.options.betas } : {}),
@@ -3092,6 +3203,12 @@ export class ClaudeSessionService {
3092
3203
  : {}),
3093
3204
  reloadMessages: async () => {
3094
3205
  await compactIfNeeded([], currentTurnUserMessages ?? []);
3206
+ if (stableSystemMessageCount === undefined) {
3207
+ delete runtimeRequest.stableSystemMessageCount;
3208
+ }
3209
+ else {
3210
+ runtimeRequest.stableSystemMessageCount = stableSystemMessageCount;
3211
+ }
3095
3212
  return [
3096
3213
  ...contextMessages,
3097
3214
  ...injectTurnContext(projectClaudeModelMessages(snapshot.entries)),
@@ -3206,6 +3323,12 @@ export class ClaudeSessionService {
3206
3323
  ...contextMessages,
3207
3324
  ...injectTurnContext(projectClaudeModelMessages(snapshot.entries)),
3208
3325
  ];
3326
+ if (stableSystemMessageCount === undefined) {
3327
+ delete runtimeRequest.stableSystemMessageCount;
3328
+ }
3329
+ else {
3330
+ runtimeRequest.stableSystemMessageCount = stableSystemMessageCount;
3331
+ }
3209
3332
  try {
3210
3333
  result = await attemptMainTurn();
3211
3334
  }
@@ -45,6 +45,7 @@ export declare class StructuredOutputRegistry implements ToolRegistry {
45
45
  value: unknown;
46
46
  } | undefined);
47
47
  definitions(): readonly ModelToolDefinition[];
48
+ schedulingPolicy(call: ModelToolCall): import("../core/runtime.js").ToolSchedulingPolicy;
48
49
  prepare(call: ModelToolCall, context: ToolExecutionContext): Promise<ModelToolCall>;
49
50
  execute(call: ModelToolCall, context: ToolExecutionContext): Promise<ToolExecutionResult>;
50
51
  }
@@ -11,6 +11,7 @@ import { createClaudeHookAttachmentEntries, translateProviderEvents, } from '../
11
11
  import { injectFirstUserMessageContext, } from '../core/context.js';
12
12
  import { ContextBudget } from '../core/context-budget.js';
13
13
  import { AgentRuntime, } from '../core/runtime.js';
14
+ import { resolveToolSchedulingPolicy } from '../core/tool-scheduling-policy.js';
14
15
  import { BUILTIN_STATUSLINE_AGENT_PATH, } from '../extensions/claude-extensions.js';
15
16
  import { ClaudeHookToolCoordinator } from '../hooks/claude-hook-tools.js';
16
17
  import { ClaudeSidechainStore } from '../persistence/claude-sidechain-store.js';
@@ -26,6 +27,7 @@ const DEFAULT_MAX_OUTPUT_BYTES = 1024 * 1024;
26
27
  const SIDECHAIN_DISCOVERY_MAX_DEPTH = 4;
27
28
  const structuredOnlyTools = {
28
29
  definitions: () => [],
30
+ schedulingPolicy: () => ({ concurrency: 'exclusive' }),
29
31
  prepare: async (call) => call,
30
32
  execute: async () => ({ content: '', isError: false }),
31
33
  };
@@ -68,6 +70,12 @@ export class StructuredOutputRegistry {
68
70
  ]),
69
71
  ];
70
72
  }
73
+ schedulingPolicy(call) {
74
+ if (call.name === 'StructuredOutput') {
75
+ return { concurrency: 'exclusive' };
76
+ }
77
+ return resolveToolSchedulingPolicy(this.base, call);
78
+ }
71
79
  prepare(call, context) {
72
80
  if (call.name !== 'StructuredOutput')
73
81
  return this.base.prepare(call, context);
@@ -106,6 +114,12 @@ class RestrictedToolRegistry {
106
114
  .definitions()
107
115
  .filter((definition) => this.allowed.has(definition.name));
108
116
  }
117
+ schedulingPolicy(call) {
118
+ if (!this.allowed.has(call.name)) {
119
+ return { concurrency: 'exclusive' };
120
+ }
121
+ return resolveToolSchedulingPolicy(this.base, call);
122
+ }
109
123
  prepare(call, context) {
110
124
  if (!this.allowed.has(call.name))
111
125
  throw new Error(`Tool ${call.name} is unavailable to this agent`);
@@ -1353,7 +1367,9 @@ export class ClaudeSubagentExecutor {
1353
1367
  const runtime = new AgentRuntime(options.provider, emit, {
1354
1368
  tools: runtimeTools,
1355
1369
  permissions: runtimePermissions,
1356
- maxModelTurns: customAgent?.maxTurns ?? 16,
1370
+ ...(customAgent?.maxTurns === undefined
1371
+ ? {}
1372
+ : { maxModelTurns: customAgent.maxTurns }),
1357
1373
  maxModelOutputBytes: this.options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES,
1358
1374
  maxToolCallsPerTurn: 32,
1359
1375
  maxToolInputBytes: 1024 * 1024,
@@ -1477,10 +1493,16 @@ export class ClaudeSubagentExecutor {
1477
1493
  const assembleMessages = async () => {
1478
1494
  const assembledContext = await this.options.contextAssembler?.assemble({
1479
1495
  cwd,
1496
+ lifecycleId: options.agentId,
1497
+ mode: 'subagent',
1498
+ baseSystemPrompt: system,
1480
1499
  });
1500
+ const composedSubagentPolicy = assembledContext?.promptSections?.some((section) => section.id === 'subagent-policy');
1481
1501
  const messages = [
1482
1502
  ...(assembledContext?.systemMessages ?? []),
1483
- { role: 'system', content: system },
1503
+ ...(composedSubagentPolicy
1504
+ ? []
1505
+ : [{ role: 'system', content: system }]),
1484
1506
  ...injectFirstUserMessageContext(projectClaudeModelMessages(snapshot.entries), assembledContext?.firstUserMessageContext),
1485
1507
  ...preloadedSkills,
1486
1508
  ];
@@ -1647,6 +1669,12 @@ class ClaudeSubagentToolRegistry {
1647
1669
  ...ordinary.slice(insertionIndex),
1648
1670
  ];
1649
1671
  }
1672
+ schedulingPolicy(call) {
1673
+ if (['Agent', 'SendMessage', 'TaskOutput', 'TaskStop'].includes(call.name)) {
1674
+ return { concurrency: 'exclusive', cancelOnInterrupt: true };
1675
+ }
1676
+ return resolveToolSchedulingPolicy(this.base, call);
1677
+ }
1650
1678
  async prepare(call, context) {
1651
1679
  if (call.name === 'Agent')
1652
1680
  return this.executor.prepare(call, this.depth);
@@ -802,12 +802,6 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
802
802
  ? {}
803
803
  : { permissionMode: interactivePermissionMode }),
804
804
  }, cwd);
805
- const effectiveAppendSystemPrompt = [
806
- runtimeSettingsPrompt,
807
- cli.appendSystemPrompt,
808
- ]
809
- .filter((value) => value !== undefined)
810
- .join('\n');
811
805
  const debug = cli.debug !== undefined || cli.debugFile !== undefined
812
806
  ? createCliDebugSink(eventSink, {
813
807
  cwd,
@@ -1175,6 +1169,7 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
1175
1169
  });
1176
1170
  return filterDisabledMcpResources(candidates, await management.disabled());
1177
1171
  };
1172
+ let contextAssembler;
1178
1173
  const mcpTools = await ClaudeMcpToolRegistry.connect({
1179
1174
  base: simpleMode
1180
1175
  ? localTools
@@ -1219,6 +1214,7 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
1219
1214
  configRoot,
1220
1215
  onWarning: (message) => runtimeEventSink({ type: 'warning', message }),
1221
1216
  onPromptsChanged: (prompts) => extensions.setMcpPrompts(prompts),
1217
+ onInstructionsChanged: () => contextAssembler?.invalidate({ reason: 'tool-pool' }),
1222
1218
  authenticateServer: async (name) => {
1223
1219
  const record = await new ClaudeMcpManagement({
1224
1220
  dataPlane,
@@ -1374,6 +1370,54 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
1374
1370
  },
1375
1371
  })
1376
1372
  : undefined;
1373
+ contextAssembler = new ClaudeContextAssembler({
1374
+ loadResources: loadContextResources,
1375
+ loadDynamicContext: (runtimeCwd = workspace.cwd()) => loadClaudeDynamicContext({
1376
+ cwd: runtimeCwd,
1377
+ ...(memoryDirectory ? { memoryDirectory } : {}),
1378
+ }),
1379
+ loadMcpInstructions: async () => mcpTools.instructions(),
1380
+ loadSessionGuidance: async () => {
1381
+ const toolNames = [
1382
+ ...new Set([
1383
+ ...filteredTools.definitions().map((definition) => definition.name),
1384
+ ...selectedTaskRuntimeTools,
1385
+ ...selectedScheduledTools,
1386
+ ...selectedWorkflowTools,
1387
+ ...selectedWorktreeTools,
1388
+ ...selectedInteractiveTools,
1389
+ ...(enableSubagents ? selectedAgentTools : []),
1390
+ ]),
1391
+ ].sort();
1392
+ const skillNames = extensions
1393
+ .modelInvocableSkills()
1394
+ .map((skill) => skill.name)
1395
+ .sort();
1396
+ if (toolNames.length === 0 &&
1397
+ skillNames.length === 0 &&
1398
+ runtimeSettingsPrompt === undefined)
1399
+ return undefined;
1400
+ return [
1401
+ '# Session capabilities',
1402
+ ...(toolNames.length > 0
1403
+ ? [`Enabled tools: ${toolNames.join(', ')}`]
1404
+ : []),
1405
+ ...(skillNames.length > 0
1406
+ ? [`Model-invocable skills: ${skillNames.join(', ')}`]
1407
+ : []),
1408
+ 'Use a capability only when it directly helps complete the request, and follow its declared input contract.',
1409
+ ...(runtimeSettingsPrompt ? ['', runtimeSettingsPrompt] : []),
1410
+ ].join('\n');
1411
+ },
1412
+ excludeDynamicSystemPromptSections: cli.excludeDynamicSystemPromptSections,
1413
+ ...(cli.systemPrompt === undefined
1414
+ ? {}
1415
+ : { systemPrompt: cli.systemPrompt }),
1416
+ ...(cli.appendSystemPrompt
1417
+ ? { appendSystemPrompt: cli.appendSystemPrompt }
1418
+ : {}),
1419
+ ...(simpleMode ? { bare: true } : {}),
1420
+ });
1377
1421
  const service = new ClaudeSessionService({
1378
1422
  ...options,
1379
1423
  provider: hostedToolProvider,
@@ -1409,20 +1453,7 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
1409
1453
  ...(interactiveTools ? { interactiveTools } : {}),
1410
1454
  ...(hooks ? { hooks } : {}),
1411
1455
  ...(selectedMainAgent ? { agent: selectedMainAgent } : {}),
1412
- contextAssembler: new ClaudeContextAssembler({
1413
- loadResources: loadContextResources,
1414
- loadDynamicContext: (runtimeCwd = workspace.cwd()) => loadClaudeDynamicContext({
1415
- cwd: runtimeCwd,
1416
- ...(memoryDirectory ? { memoryDirectory } : {}),
1417
- }),
1418
- excludeDynamicSystemPromptSections: cli.excludeDynamicSystemPromptSections,
1419
- ...(cli.systemPrompt === undefined
1420
- ? {}
1421
- : { systemPrompt: cli.systemPrompt }),
1422
- ...(effectiveAppendSystemPrompt
1423
- ? { appendSystemPrompt: effectiveAppendSystemPrompt }
1424
- : {}),
1425
- }),
1456
+ contextAssembler,
1426
1457
  conditionalRuleResolver: new ClaudeConditionalRuleResolver({
1427
1458
  loadResources: loadContextResources,
1428
1459
  }),
@@ -1,12 +1,20 @@
1
- import type { AssembledContext, ContextAssembler } from '../../core/context.js';
1
+ import type { AssembledContext, ContextAssembler, ContextAssemblyOptions, ContextInvalidationOptions } from '../../core/context.js';
2
2
  import { type ClaudeDynamicContextSections } from './dynamic-context.js';
3
3
  import type { ClaudeContextResources, ClaudeConditionalRule } from './shared-resources.js';
4
4
  export interface ClaudeContextAssemblerOptions {
5
5
  loadResources(cwd?: string): Promise<ClaudeContextResources>;
6
6
  loadDynamicContext?(cwd?: string): Promise<ClaudeDynamicContextSections>;
7
+ loadMcpInstructions?(): Promise<readonly ClaudeMcpInstruction[]>;
8
+ loadSessionGuidance?(): Promise<string | undefined>;
7
9
  excludeDynamicSystemPromptSections?: boolean;
8
10
  systemPrompt?: string;
9
11
  appendSystemPrompt?: string;
12
+ bare?: boolean;
13
+ now?(): Date;
14
+ }
15
+ export interface ClaudeMcpInstruction {
16
+ server: string;
17
+ instructions: string;
10
18
  }
11
19
  export type ClaudeConditionalRuleResolverOptions = ClaudeContextAssemblerOptions;
12
20
  export declare class ClaudeConditionalRuleResolver {
@@ -16,9 +24,16 @@ export declare class ClaudeConditionalRuleResolver {
16
24
  }
17
25
  export declare class ClaudeContextAssembler implements ContextAssembler {
18
26
  private readonly options;
27
+ private readonly composer;
28
+ private readonly snapshots;
19
29
  constructor(options: ClaudeContextAssemblerOptions);
20
- assemble(options?: {
21
- cwd?: string;
22
- }): Promise<AssembledContext>;
30
+ assemble(options?: ContextAssemblyOptions): Promise<AssembledContext>;
31
+ invalidate(options: ContextInvalidationOptions): void;
32
+ private snapshot;
33
+ private loadResources;
34
+ private loadDynamicContext;
35
+ private loadMcpInstructions;
36
+ private loadSessionGuidance;
37
+ private currentDate;
23
38
  }
24
39
  //# sourceMappingURL=context.d.ts.map