praxis-agent 0.14.0 → 0.16.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 (33) hide show
  1. package/README.md +5 -2
  2. package/dist/application/session-service.d.ts +18 -0
  3. package/dist/application/session-service.js +180 -41
  4. package/dist/application/subagent-service.d.ts +7 -1
  5. package/dist/application/subagent-service.js +238 -53
  6. package/dist/cli/interactive.d.ts +7 -1
  7. package/dist/cli/interactive.js +77 -3
  8. package/dist/cli/protocol.d.ts +13 -0
  9. package/dist/cli/protocol.js +34 -0
  10. package/dist/cli/tui/claude-command-inventory.d.ts +1 -2
  11. package/dist/cli/tui/claude-command-inventory.js +1 -6
  12. package/dist/cli/tui/claude-style.d.ts +4 -1
  13. package/dist/cli/tui/claude-style.js +8 -3
  14. package/dist/cli/tui/slash-commands.d.ts +1 -0
  15. package/dist/cli/tui/slash-commands.js +11 -0
  16. package/dist/cli/tui/theme.d.ts +2 -0
  17. package/dist/cli/tui/theme.js +69 -0
  18. package/dist/cli.d.ts +6 -0
  19. package/dist/cli.js +95 -27
  20. package/dist/compatibility/claude/agent-color.d.ts +25 -0
  21. package/dist/compatibility/claude/agent-color.js +79 -0
  22. package/dist/compatibility/claude/fork.js +5 -1
  23. package/dist/compatibility/claude/schema.js +13 -1
  24. package/dist/extensions/claude-extensions.d.ts +19 -1
  25. package/dist/extensions/claude-extensions.js +126 -1
  26. package/dist/hooks/claude-hooks.d.ts +3 -1
  27. package/dist/hooks/claude-hooks.js +14 -0
  28. package/dist/mcp/claude-mcp-tools.d.ts +18 -0
  29. package/dist/mcp/claude-mcp-tools.js +46 -0
  30. package/dist/persistence/claude-transcript-store.js +1 -0
  31. package/dist/tools/claude-interactive-tools.d.ts +1 -0
  32. package/dist/tools/claude-interactive-tools.js +3 -0
  33. package/package.json +2 -1
package/README.md CHANGED
@@ -95,7 +95,8 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
95
95
  tabs, `/sandbox` mode/dependency/override/config controls, local cached
96
96
  `/release-notes`, Claude-compatible `/statusline` command execution and setup
97
97
  agent, source-aligned `/init` project-instruction onboarding with its enhanced
98
- skills/hooks flow, `/mcp`, `/memory` shared instruction and auto-memory
98
+ skills/hooks flow, provider-free per-session `/color` prompt-bar styling,
99
+ `/mcp`, `/memory` shared instruction and auto-memory
99
100
  access, and live extension-reload controls,
100
101
  cursor/history composer, per-session model/effort/permission controls,
101
102
  context/status/skill/task dashboards, prompt stash and continuation shortcuts,
@@ -137,7 +138,9 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
137
138
  workspace-directory add/remove controls, path confinement, credential
138
139
  redaction, and sanitized child processes.
139
140
  - **Durable local work** — resumable sessions, full-history forks, file
140
- checkpoints, tasks, foreground/background subagents, and top-level agents.
141
+ checkpoints, tasks, foreground/background subagents, top-level agents, and
142
+ Claude-compatible main-thread agent definitions with native prompt, model,
143
+ tool, memory, first-turn, and resume behavior.
141
144
  - **Claude-compatible ecosystem** — shared instructions with recursive `@`
142
145
  imports, memory, skills, commands, agents, hooks, settings, MCP servers,
143
146
  plugins, and transcript data.
@@ -1,3 +1,4 @@
1
+ import { type AgentColorName, type AgentColorSelection } from '../compatibility/claude/agent-color.js';
1
2
  import type { ClaudeConditionalRuleResolver } from '../compatibility/claude/context.js';
2
3
  import { type ClaudeFileResource, type ClaudeFileResourceConfig } from '../compatibility/claude/file-resources.js';
3
4
  import { type ClaudeDisplayTranscriptItem } from '../compatibility/claude/projection.js';
@@ -24,6 +25,7 @@ export interface ClaudeSessionServiceOptions {
24
25
  tools?: ToolRegistry;
25
26
  permissions?: PermissionResolver;
26
27
  permissionResolverForMode?: (mode: AgentPermissionMode) => PermissionResolver;
28
+ permissionMode?: ClaudePermissionMode;
27
29
  persistPermissionUpdates?: (updates: readonly PermissionUpdate[]) => void | Promise<void>;
28
30
  approveTool?: (call: ModelToolCall, originalCall?: ModelToolCall, decision?: PermissionDecision) => PermissionApproval | Promise<PermissionApproval>;
29
31
  approveRecovery?: (call: ModelToolCall) => boolean | Promise<boolean>;
@@ -43,6 +45,11 @@ export interface ClaudeSessionServiceOptions {
43
45
  enableDynamicWakeups?: boolean;
44
46
  enableWorkflows?: boolean;
45
47
  providerForModel?: (model: string) => ModelProvider;
48
+ providerForMainModel?: (model: string) => ModelProvider;
49
+ explicitModel?: boolean;
50
+ explicitSystemPrompt?: boolean;
51
+ agentInitialPromptHandledExternally?: boolean;
52
+ agentSystemPromptOverridesExplicit?: boolean;
46
53
  effort?: string;
47
54
  maxModelTurns?: number;
48
55
  betas?: readonly string[];
@@ -149,6 +156,7 @@ export declare class ClaudeSessionService {
149
156
  private readonly hostedSubagentsByRegistry;
150
157
  private readonly backgroundNotificationWrites;
151
158
  private readonly downloadedFileResourceSessions;
159
+ private activeProvider;
152
160
  private mcpClosePromise;
153
161
  private runtimeCwd;
154
162
  constructor(options: ClaudeSessionServiceOptions);
@@ -175,6 +183,7 @@ export declare class ClaudeSessionService {
175
183
  sessionNameSuggestion(sessionId: string, signal?: AbortSignal): Promise<string | null>;
176
184
  sessions(): Promise<SessionSummary[]>;
177
185
  inspect(sessionId: string): Promise<SessionInspection>;
186
+ readEffectiveAgentColor(sessionId: string): Promise<AgentColorName | undefined>;
178
187
  export(sessionId: string): Promise<Buffer>;
179
188
  transcript(sessionId: string, resumeSessionAt?: string): Promise<ClaudeDisplayTranscriptItem[]>;
180
189
  rename(sessionId: string, name: string): Promise<void>;
@@ -183,6 +192,9 @@ export declare class ClaudeSessionService {
183
192
  approveRecentlyDenied(sessionId: string, display: string): Promise<void>;
184
193
  retryRecentlyDenied(sessionId: string, display: string, signal?: AbortSignal): Promise<SessionRunResult>;
185
194
  recordBtwUsage(sessionId: string | undefined, permissionMode?: ClaudePermissionMode): Promise<string>;
195
+ recordColorUsage(sessionId: string | undefined, selection: AgentColorSelection, display: string, permissionMode?: ClaudePermissionMode, options?: {
196
+ createSession?: boolean;
197
+ }): Promise<string>;
186
198
  recordBackgroundUsage(sessionId: string | undefined, permissionMode?: ClaudePermissionMode): Promise<string>;
187
199
  recordBackgroundLaunch(sessionId: string): Promise<SessionForkCheckpoint>;
188
200
  compact(sessionId: string, signal?: AbortSignal, selection?: ManualCompactSelection): Promise<ManualCompactResult>;
@@ -207,6 +219,7 @@ export declare class ClaudeSessionService {
207
219
  private restoreWorktree;
208
220
  private paths;
209
221
  private appendCdCommand;
222
+ private appendAgentColorUsage;
210
223
  private appendSystemLocalCommand;
211
224
  private ensureLocalSession;
212
225
  private appendInputHistory;
@@ -221,6 +234,11 @@ export declare class ClaudeSessionService {
221
234
  private assertWritable;
222
235
  private sessionStatus;
223
236
  private provider;
237
+ model(): string | undefined;
238
+ private providerForAgent;
239
+ private resolveAgent;
240
+ private mainAgentSystemPrompt;
241
+ private assembledSystemMessages;
224
242
  private contextBudget;
225
243
  private append;
226
244
  private logicalTailUuid;
@@ -2,6 +2,7 @@ import { randomUUID } from 'node:crypto';
2
2
  import { appendFile, copyFile, link, lstat, mkdir, readdir, realpath, stat, unlink, } from 'node:fs/promises';
3
3
  import { homedir } from 'node:os';
4
4
  import { basename, extname, isAbsolute, join, relative } from 'node:path';
5
+ import { AGENT_COLOR_DEFAULT, agentColorMessage, getClaudeEffectiveAgentColor, } from '../compatibility/claude/agent-color.js';
5
6
  import { createClaudeCompactEntries, formatClaudeCompactSummary, getCumulativeDroppedTokens, } from '../compatibility/claude/compaction.js';
6
7
  import { isClaudeSessionId, resolveClaudePaths, resolveClaudeScheduledTaskFile, } from '../compatibility/claude/paths.js';
7
8
  import { downloadClaudeFileResources, } from '../compatibility/claude/file-resources.js';
@@ -22,7 +23,7 @@ import { ClaudeHookToolCoordinator } from '../hooks/claude-hook-tools.js';
22
23
  import { ClaudeTranscriptStore, } from '../persistence/claude-transcript-store.js';
23
24
  import { InMemoryTranscriptStore } from '../persistence/in-memory-transcript-store.js';
24
25
  import { ModelCompactor } from './model-compactor.js';
25
- import { ClaudeSubagentExecutor, StructuredOutputRegistry, } from './subagent-service.js';
26
+ import { agentMemoryPrompt, ClaudeSubagentExecutor, StructuredOutputRegistry, } from './subagent-service.js';
26
27
  import { ScheduledPromptManager } from './scheduled-prompt-manager.js';
27
28
  import { ClaudeScheduledToolRegistry } from '../tools/claude-scheduled-tools.js';
28
29
  import { ClaudeTaskToolRegistry } from '../tools/claude-task-tools.js';
@@ -30,8 +31,29 @@ import { ClaudeWorkflowToolRegistry } from '../tools/claude-workflow-tools.js';
30
31
  import { WorkflowManager, } from './workflow-manager.js';
31
32
  import { SessionWorktreeManager } from './session-worktree.js';
32
33
  import { ClaudeWorktreeToolRegistry } from '../tools/claude-worktree-tools.js';
34
+ import { FilteredToolRegistry } from '../tools/filtered-tool-registry.js';
33
35
  import { generateToolUseSummary } from './tool-use-summary.js';
34
36
  import { ClaudeUserMessageToolRegistry, CLAUDE_USER_MESSAGE_PROMPT, } from '../tools/claude-user-message.js';
37
+ function agentPermissionMode(mode) {
38
+ return mode === undefined || mode === 'manual' ? 'default' : mode;
39
+ }
40
+ function agentToolName(rule) {
41
+ const opening = rule.indexOf('(');
42
+ return (opening < 0 ? rule : rule.slice(0, opening)).trim();
43
+ }
44
+ function mainAgentToolNames(tools, agent) {
45
+ const requested = agent.tools ? new Set(agent.tools.map(agentToolName)) : null;
46
+ if (requested && agent.memory) {
47
+ requested.add('Read');
48
+ requested.add('Edit');
49
+ requested.add('Write');
50
+ }
51
+ const disallowed = new Set(agent.disallowedTools?.map(agentToolName) ?? []);
52
+ return tools
53
+ .definitions()
54
+ .map(({ name }) => name)
55
+ .filter((name) => (!requested || requested.has(name)) && !disallowed.has(name));
56
+ }
35
57
  const emptyToolRegistry = {
36
58
  definitions: () => [],
37
59
  prepare: async (call) => call,
@@ -97,6 +119,7 @@ export class ClaudeSessionService {
97
119
  hostedSubagentsByRegistry = new WeakMap();
98
120
  backgroundNotificationWrites = new Map();
99
121
  downloadedFileResourceSessions = new Set();
122
+ activeProvider;
100
123
  mcpClosePromise;
101
124
  runtimeCwd;
102
125
  constructor(options) {
@@ -127,6 +150,12 @@ export class ClaudeSessionService {
127
150
  : {}),
128
151
  })
129
152
  : null;
153
+ const configuredAgent = options.agent
154
+ ? (options.extensions?.agent(options.agent) ?? null)
155
+ : null;
156
+ if (configuredAgent && options.provider) {
157
+ this.activeProvider = this.providerForAgent(configuredAgent);
158
+ }
130
159
  }
131
160
  nextScheduledPrompt(signal) {
132
161
  return this.scheduledPrompts?.next(signal) ?? Promise.resolve(null);
@@ -240,12 +269,15 @@ export class ClaudeSessionService {
240
269
  permissionResolverForMode: this.options.permissionResolverForMode,
241
270
  }
242
271
  : {}),
272
+ parentPermissionMode: () => agentPermissionMode(this.options.interactiveTools?.mode(sessionId) ??
273
+ this.options.permissionMode),
243
274
  ...(this.options.subagentToolNames
244
275
  ? { toolNames: this.options.subagentToolNames }
245
276
  : {}),
246
277
  ...(this.options.extensions
247
278
  ? { extensions: this.options.extensions }
248
279
  : {}),
280
+ ...(this.options.mcp ? { mcp: this.options.mcp } : {}),
249
281
  ...(this.options.hooks ? { hooks: this.options.hooks } : {}),
250
282
  ...(this.options.contextAssembler
251
283
  ? { contextAssembler: this.options.contextAssembler }
@@ -501,7 +533,6 @@ export class ClaudeSessionService {
501
533
  return { agentId, name };
502
534
  }
503
535
  async promptSuggestion(sessionId, signal) {
504
- const provider = this.provider();
505
536
  const loaded = this.options.sessionPersistence === false
506
537
  ? await this.turnStore(sessionId).withLease((lease) => lease.load())
507
538
  : {
@@ -513,20 +544,19 @@ export class ClaudeSessionService {
513
544
  const entries = loaded.value;
514
545
  this.restoreWorktree(entries.entries);
515
546
  const agentName = this.options.agent ?? getClaudeAgentSetting(entries.entries);
516
- const agent = agentName ? this.options.extensions?.agent(agentName) : null;
547
+ const agent = this.resolveAgent(agentName);
548
+ const provider = this.providerForAgent(agent);
549
+ this.activeProvider = provider;
517
550
  const assembledContext = await this.options.contextAssembler?.assemble({
518
551
  cwd: this.activeCwd(),
519
552
  });
553
+ const agentSystem = await this.mainAgentSystemPrompt(agent);
554
+ const assembledSystemMessages = this.assembledSystemMessages(agent, assembledContext?.systemMessages ?? []);
520
555
  const contextMessages = [
521
- ...(assembledContext?.systemMessages ?? []),
522
- ...(agent
523
- ? [
524
- {
525
- role: 'system',
526
- content: `# Agent definition: ${agent.name}\n\n${agent.body}`,
527
- },
528
- ]
556
+ ...(agentSystem
557
+ ? [{ role: 'system', content: agentSystem }]
529
558
  : []),
559
+ ...assembledSystemMessages,
530
560
  ];
531
561
  const messages = [
532
562
  ...contextMessages,
@@ -535,11 +565,16 @@ export class ClaudeSessionService {
535
565
  { role: 'user', content: PROMPT_SUGGESTION_INSTRUCTION },
536
566
  ], assembledContext?.firstUserMessageContext),
537
567
  ];
568
+ const suggestionTools = agent && this.options.tools
569
+ ? new FilteredToolRegistry(this.options.tools, {
570
+ tools: mainAgentToolNames(this.options.tools, agent),
571
+ })
572
+ : this.options.tools;
538
573
  let suggestion = '';
539
574
  for await (const event of provider.complete({
540
575
  messages,
541
576
  ...(provider.capabilities.tools
542
- ? { tools: this.options.tools?.definitions() ?? [] }
577
+ ? { tools: suggestionTools?.definitions() ?? [] }
543
578
  : {}),
544
579
  ...(this.options.effort ? { effort: this.options.effort } : {}),
545
580
  ...(signal ? { signal } : {}),
@@ -662,6 +697,11 @@ export class ClaudeSessionService {
662
697
  : {}),
663
698
  };
664
699
  }
700
+ async readEffectiveAgentColor(sessionId) {
701
+ this.assertSessionPersistence();
702
+ const recovery = await this.store(sessionId).loadReadOnly();
703
+ return getClaudeEffectiveAgentColor(recovery.entries, sessionId);
704
+ }
665
705
  async export(sessionId) {
666
706
  this.assertSessionPersistence();
667
707
  try {
@@ -900,6 +940,21 @@ export class ClaudeSessionService {
900
940
  await new Promise((resolve) => setTimeout(resolve, 25));
901
941
  }
902
942
  }
943
+ async recordColorUsage(sessionId, selection, display, permissionMode = 'default', options = {}) {
944
+ const output = agentColorMessage(selection);
945
+ const agentColor = selection.kind === 'color'
946
+ ? selection.color
947
+ : selection.kind === 'reset'
948
+ ? AGENT_COLOR_DEFAULT
949
+ : undefined;
950
+ const createLocalSession = sessionId === undefined || options.createSession === true;
951
+ const activeSessionId = await this.ensureLocalSession(sessionId, permissionMode, createLocalSession ? agentColor : undefined, options.createSession === true);
952
+ if (this.options.sessionPersistence !== false) {
953
+ await this.appendInputHistory(display, activeSessionId);
954
+ }
955
+ await this.appendAgentColorUsage(activeSessionId, display, output, createLocalSession ? undefined : agentColor);
956
+ return activeSessionId;
957
+ }
903
958
  async recordBackgroundUsage(sessionId, permissionMode = 'default') {
904
959
  this.assertWritable();
905
960
  const activeSessionId = await this.ensureLocalSession(sessionId, permissionMode);
@@ -1332,7 +1387,17 @@ export class ClaudeSessionService {
1332
1387
  tail: appendResult.tail,
1333
1388
  };
1334
1389
  }
1335
- const provider = this.provider();
1390
+ const agentName = this.options.agent ?? getClaudeAgentSetting(snapshot.entries);
1391
+ const agent = this.resolveAgent(agentName);
1392
+ const provider = this.providerForAgent(agent);
1393
+ this.activeProvider = provider;
1394
+ const effectivePrompt = !requireExisting &&
1395
+ !skipUserPrompt &&
1396
+ !this.options.agentInitialPromptHandledExternally &&
1397
+ shellCommand === undefined &&
1398
+ agent?.initialPrompt
1399
+ ? `${agent.initialPrompt}\n\n${prompt}`
1400
+ : prompt;
1336
1401
  const initialPricing = this.options.pricing?.resolve(provider.model ?? 'praxis/provider');
1337
1402
  if (this.options.maxBudgetUsd !== undefined && !initialPricing) {
1338
1403
  throw new Error(`Cannot enforce --max-budget-usd: no pricing is configured for model ${provider.model ?? 'praxis/provider'}`);
@@ -1450,12 +1515,15 @@ export class ClaudeSessionService {
1450
1515
  permissionResolverForMode: this.options.permissionResolverForMode,
1451
1516
  }
1452
1517
  : {}),
1518
+ parentPermissionMode: () => agentPermissionMode(this.options.interactiveTools?.mode(sessionId) ??
1519
+ this.options.permissionMode),
1453
1520
  ...(this.options.subagentToolNames
1454
1521
  ? { toolNames: this.options.subagentToolNames }
1455
1522
  : {}),
1456
1523
  ...(this.options.extensions
1457
1524
  ? { extensions: this.options.extensions }
1458
1525
  : {}),
1526
+ ...(this.options.mcp ? { mcp: this.options.mcp } : {}),
1459
1527
  ...(this.options.hooks ? { hooks: this.options.hooks } : {}),
1460
1528
  ...(this.options.contextAssembler
1461
1529
  ? { contextAssembler: this.options.contextAssembler }
@@ -1498,7 +1566,7 @@ export class ClaudeSessionService {
1498
1566
  promptIdForCall: (callId) => currentPromptId ??
1499
1567
  this.promptIdForToolCall(snapshot.entries, callId),
1500
1568
  defaultModel: provider.model ?? 'praxis/provider',
1501
- tokenBudget: workflowTokenTarget(prompt),
1569
+ tokenBudget: workflowTokenTarget(effectivePrompt),
1502
1570
  enabled: true,
1503
1571
  })
1504
1572
  : agentTools;
@@ -1575,9 +1643,14 @@ export class ClaudeSessionService {
1575
1643
  const structuredCapture = this.options.structuredOutputSchema
1576
1644
  ? { calls: 0, value: undefined }
1577
1645
  : undefined;
1578
- const structuredTools = this.options.structuredOutputSchema && structuredCapture
1579
- ? new StructuredOutputRegistry(fileHistoryTools ?? this.options.tools ?? emptyToolRegistry, this.options.structuredOutputSchema, structuredCapture)
1646
+ const agentScopedTools = agent && fileHistoryTools
1647
+ ? new FilteredToolRegistry(fileHistoryTools, {
1648
+ tools: mainAgentToolNames(fileHistoryTools, agent),
1649
+ })
1580
1650
  : fileHistoryTools;
1651
+ const structuredTools = this.options.structuredOutputSchema && structuredCapture
1652
+ ? new StructuredOutputRegistry(agentScopedTools ?? this.options.tools ?? emptyToolRegistry, this.options.structuredOutputSchema, structuredCapture)
1653
+ : agentScopedTools;
1581
1654
  const hookTools = this.options.hooks && structuredTools && turnPermissions
1582
1655
  ? new ClaudeHookToolCoordinator({
1583
1656
  tools: structuredTools,
@@ -1768,14 +1841,8 @@ export class ClaudeSessionService {
1768
1841
  inputTokens: usage.inputTokens + (result.usage?.inputTokens ?? 0),
1769
1842
  outputTokens: usage.outputTokens + (result.usage?.outputTokens ?? 0),
1770
1843
  }), { inputTokens: 0, outputTokens: 0 });
1771
- const agentName = this.options.agent ?? getClaudeAgentSetting(snapshot.entries);
1772
- const agent = agentName
1773
- ? this.options.extensions?.agent(agentName)
1774
- : null;
1775
- if (agentName && !agent) {
1776
- throw new Error(`Unknown Claude agent ${agentName}`);
1777
- }
1778
1844
  if (this.options.agent &&
1845
+ agent &&
1779
1846
  getClaudeAgentSetting(snapshot.entries) !== this.options.agent) {
1780
1847
  const agentSetting = createClaudeAgentSettingEntry(sessionId, this.options.agent);
1781
1848
  const settingTail = await this.append(lease, snapshot.tail, agentSetting);
@@ -1787,9 +1854,14 @@ export class ClaudeSessionService {
1787
1854
  const assembledContext = await this.options.contextAssembler?.assemble({
1788
1855
  cwd: this.activeCwd(),
1789
1856
  });
1857
+ const agentSystem = await this.mainAgentSystemPrompt(agent);
1858
+ const assembledSystemMessages = this.assembledSystemMessages(agent, assembledContext?.systemMessages ?? []);
1790
1859
  const planModeMessage = this.options.interactiveTools?.contextMessage(sessionId);
1791
1860
  const contextMessages = [
1792
- ...(assembledContext?.systemMessages ?? []),
1861
+ ...(agentSystem
1862
+ ? [{ role: 'system', content: agentSystem }]
1863
+ : []),
1864
+ ...assembledSystemMessages,
1793
1865
  ...(planModeMessage
1794
1866
  ? [{ role: 'system', content: planModeMessage }]
1795
1867
  : []),
@@ -1801,14 +1873,6 @@ export class ClaudeSessionService {
1801
1873
  },
1802
1874
  ]
1803
1875
  : []),
1804
- ...(agent
1805
- ? [
1806
- {
1807
- role: 'system',
1808
- content: `# Agent definition: ${agent.name}\n\n${agent.body}`,
1809
- },
1810
- ]
1811
- : []),
1812
1876
  ...(this.options.structuredOutputSchema
1813
1877
  ? [
1814
1878
  {
@@ -1822,8 +1886,8 @@ export class ClaudeSessionService {
1822
1886
  ? { userMessages: [] }
1823
1887
  : shellCommand === undefined
1824
1888
  ? this.options.extensions
1825
- ? await this.options.extensions.expandPromptAsync(prompt, signal, toolResultDirectory)
1826
- : { userMessages: [prompt] }
1889
+ ? await this.options.extensions.expandPromptAsync(effectivePrompt, signal, toolResultDirectory)
1890
+ : { userMessages: [effectivePrompt] }
1827
1891
  : {
1828
1892
  userMessages: [`<bash-input>${shellCommand}</bash-input>`],
1829
1893
  };
@@ -1864,7 +1928,8 @@ export class ClaudeSessionService {
1864
1928
  : {}),
1865
1929
  }));
1866
1930
  const agentMentionMessages = shellCommand === undefined && !skipUserPrompt
1867
- ? (this.options.extensions?.agentMentionMessages(prompt) ?? [])
1931
+ ? (this.options.extensions?.agentMentionMessages(effectivePrompt) ??
1932
+ [])
1868
1933
  : [];
1869
1934
  const injectAgentMentionContext = (messages) => {
1870
1935
  if (agentMentionMessages.length === 0)
@@ -1875,7 +1940,7 @@ export class ClaudeSessionService {
1875
1940
  const message = messages[index];
1876
1941
  if (message?.role === 'user' &&
1877
1942
  typeof message.content === 'string' &&
1878
- message.content.endsWith(prompt)) {
1943
+ message.content.endsWith(effectivePrompt)) {
1879
1944
  insertionIndex = index;
1880
1945
  foundPrompt = true;
1881
1946
  break;
@@ -2073,7 +2138,7 @@ export class ClaudeSessionService {
2073
2138
  ...hookSession,
2074
2139
  hook_event_name: 'UserPromptSubmit',
2075
2140
  prompt_id: currentPromptId ?? randomUUID(),
2076
- prompt,
2141
+ prompt: effectivePrompt,
2077
2142
  }, undefined, signal);
2078
2143
  await recordHookOutcome(outcome);
2079
2144
  if (outcome.blockedReason) {
@@ -2252,7 +2317,7 @@ export class ClaudeSessionService {
2252
2317
  if (!skipUserPrompt) {
2253
2318
  await this.append(lease, snapshot.tail, createClaudeLastPromptEntry({
2254
2319
  sessionId,
2255
- lastPrompt: prompt,
2320
+ lastPrompt: effectivePrompt,
2256
2321
  leafUuid: finalLeafUuid,
2257
2322
  }));
2258
2323
  }
@@ -2522,6 +2587,35 @@ export class ClaudeSessionService {
2522
2587
  throw new Error(`Claude local command conflict: ${result.reason}`);
2523
2588
  }
2524
2589
  }
2590
+ async appendAgentColorUsage(sessionId, display, output, agentColor) {
2591
+ const args = display.replace(/^\/color\s*/u, '').trim();
2592
+ while (true) {
2593
+ const result = await this.turnStore(sessionId).withLease(async (lease) => {
2594
+ const snapshot = await lease.load();
2595
+ if (snapshot.entries.length === 0) {
2596
+ throw new Error(`Claude session not found: ${sessionId}`);
2597
+ }
2598
+ const appended = await lease.appendMany(snapshot.tail, [
2599
+ ...(agentColor === undefined
2600
+ ? []
2601
+ : [
2602
+ {
2603
+ type: 'agent-color',
2604
+ agentColor,
2605
+ sessionId,
2606
+ },
2607
+ ]),
2608
+ ...this.localCommandEntries(sessionId, this.activeCwd(), this.logicalTailUuid(snapshot.tail), 'color', args, output),
2609
+ ]);
2610
+ if (appended.status === 'conflict') {
2611
+ throw new Error(`Claude color local command append conflict: ${appended.reason}`);
2612
+ }
2613
+ });
2614
+ if (result.status === 'completed')
2615
+ return;
2616
+ await new Promise((resolve) => setTimeout(resolve, 25));
2617
+ }
2618
+ }
2525
2619
  async appendSystemLocalCommand(sessionId, command, args, output) {
2526
2620
  while (true) {
2527
2621
  const result = await this.turnStore(sessionId).withLease(async (lease) => {
@@ -2539,13 +2633,22 @@ export class ClaudeSessionService {
2539
2633
  await new Promise((resolve) => setTimeout(resolve, 25));
2540
2634
  }
2541
2635
  }
2542
- async ensureLocalSession(sessionId, permissionMode) {
2543
- if (sessionId)
2636
+ async ensureLocalSession(sessionId, permissionMode, agentColor, createExplicit = false) {
2637
+ if (sessionId !== undefined && !createExplicit)
2544
2638
  return sessionId;
2545
2639
  this.assertWritable();
2546
- const createdSessionId = randomUUID();
2640
+ const createdSessionId = sessionId ?? randomUUID();
2547
2641
  const store = this.turnStore(createdSessionId);
2548
2642
  const created = await store.create([
2643
+ ...(agentColor === undefined
2644
+ ? []
2645
+ : [
2646
+ {
2647
+ type: 'agent-color',
2648
+ agentColor,
2649
+ sessionId: createdSessionId,
2650
+ },
2651
+ ]),
2549
2652
  { type: 'mode', mode: 'normal', sessionId: createdSessionId },
2550
2653
  {
2551
2654
  type: 'permission-mode',
@@ -2865,6 +2968,42 @@ export class ClaudeSessionService {
2865
2968
  }
2866
2969
  return this.options.provider;
2867
2970
  }
2971
+ model() {
2972
+ return this.activeProvider?.model ?? this.options.provider?.model;
2973
+ }
2974
+ providerForAgent(agent) {
2975
+ const inherited = this.provider();
2976
+ const selectProvider = this.options.providerForMainModel ?? this.options.providerForModel;
2977
+ if (this.options.explicitModel ||
2978
+ !agent?.model ||
2979
+ agent.model === 'inherit' ||
2980
+ !selectProvider) {
2981
+ return inherited;
2982
+ }
2983
+ return selectProvider(agent.model);
2984
+ }
2985
+ resolveAgent(name) {
2986
+ if (!name)
2987
+ return null;
2988
+ return this.options.extensions?.agent(name) ?? null;
2989
+ }
2990
+ async mainAgentSystemPrompt(agent) {
2991
+ if (!agent ||
2992
+ (this.options.explicitSystemPrompt &&
2993
+ !this.options.agentSystemPromptOverridesExplicit)) {
2994
+ return null;
2995
+ }
2996
+ const memory = await agentMemoryPrompt(this.options.configRoot, this.activeCwd(), agent);
2997
+ const system = memory ? `${agent.body}\n\n${memory}` : agent.body;
2998
+ return system.trim() ? system : null;
2999
+ }
3000
+ assembledSystemMessages(agent, messages) {
3001
+ return agent &&
3002
+ this.options.explicitSystemPrompt &&
3003
+ this.options.agentSystemPromptOverridesExplicit
3004
+ ? messages.slice(1)
3005
+ : messages;
3006
+ }
2868
3007
  contextBudget(provider) {
2869
3008
  if (this.options.contextBudget)
2870
3009
  return this.options.contextBudget;
@@ -1,8 +1,9 @@
1
1
  import { type ClaudeSidechainPermissionMode } from '../compatibility/claude/sidechain.js';
2
2
  import { type ContextAssembler } from '../core/context.js';
3
3
  import { type ModelProvider, type ModelToolCall, type ModelToolDefinition, type ModelUsage, type PermissionResolver, type PermissionApproval, type PermissionDecision, type PermissionUpdate, type RuntimeEventSink, type ToolExecutionContext, type ToolExecutionResult, type ToolRegistry } from '../core/runtime.js';
4
- import { type ClaudeExtensionCatalog } from '../extensions/claude-extensions.js';
4
+ import { type ClaudeAgentRuntimeDefinition, type ClaudeExtensionCatalog } from '../extensions/claude-extensions.js';
5
5
  import type { ClaudeHookRunner } from '../hooks/claude-hooks.js';
6
+ import type { ClaudeMcpRuntime } from '../mcp/claude-mcp-tools.js';
6
7
  import { type BackgroundAgentSnapshot } from './background-agent-manager.js';
7
8
  export interface WorkflowAgentRunOptions {
8
9
  sessionId: string;
@@ -43,6 +44,7 @@ export declare class StructuredOutputRegistry implements ToolRegistry {
43
44
  prepare(call: ModelToolCall, context: ToolExecutionContext): Promise<ModelToolCall>;
44
45
  execute(call: ModelToolCall, context: ToolExecutionContext): Promise<ToolExecutionResult>;
45
46
  }
47
+ export declare function agentMemoryPrompt(configRoot: string, cwd: string, definition: ClaudeAgentRuntimeDefinition | null): Promise<string | null>;
46
48
  export interface ClaudeSubagentExecutorOptions {
47
49
  configRoot: string;
48
50
  cwd: string;
@@ -52,7 +54,9 @@ export interface ClaudeSubagentExecutorOptions {
52
54
  baseTools: ToolRegistry;
53
55
  permissions: PermissionResolver;
54
56
  permissionResolverForMode?: (mode: AgentPermissionMode) => PermissionResolver;
57
+ parentPermissionMode?: () => AgentPermissionMode;
55
58
  extensions?: ClaudeExtensionCatalog;
59
+ mcp?: ClaudeMcpRuntime;
56
60
  hooks?: ClaudeHookRunner;
57
61
  contextAssembler?: ContextAssembler;
58
62
  contextReserveTokens?: number;
@@ -80,6 +84,8 @@ export declare class ClaudeSubagentExecutor {
80
84
  backgroundSnapshots(): readonly BackgroundAgentSnapshot[];
81
85
  stopBackgroundTask(taskId: string): string;
82
86
  private cwd;
87
+ private agentDefinition;
88
+ private resolveAgentInput;
83
89
  registry(sessionId: string, depth: number, promptIdForCall: (callId: string) => string | null): ToolRegistry;
84
90
  definitions(): ModelToolDefinition;
85
91
  managementDefinitions(): readonly ModelToolDefinition[];