praxis-agent 0.3.0 → 0.5.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 (42) hide show
  1. package/README.md +4 -2
  2. package/dist/application/session-service.d.ts +29 -0
  3. package/dist/application/session-service.d.ts.map +1 -1
  4. package/dist/application/session-service.js +431 -31
  5. package/dist/application/session-service.js.map +1 -1
  6. package/dist/application/top-level-agent-manager.d.ts +15 -1
  7. package/dist/application/top-level-agent-manager.d.ts.map +1 -1
  8. package/dist/application/top-level-agent-manager.js +101 -34
  9. package/dist/application/top-level-agent-manager.js.map +1 -1
  10. package/dist/cli/agents-dashboard.d.ts.map +1 -1
  11. package/dist/cli/agents-dashboard.js +22 -6
  12. package/dist/cli/agents-dashboard.js.map +1 -1
  13. package/dist/cli/interactive.d.ts +22 -2
  14. package/dist/cli/interactive.d.ts.map +1 -1
  15. package/dist/cli/interactive.js +311 -5
  16. package/dist/cli/interactive.js.map +1 -1
  17. package/dist/cli/tui/claude-style.d.ts +15 -0
  18. package/dist/cli/tui/claude-style.d.ts.map +1 -1
  19. package/dist/cli/tui/claude-style.js +26 -0
  20. package/dist/cli/tui/claude-style.js.map +1 -1
  21. package/dist/cli/tui/clipboard.d.ts +3 -0
  22. package/dist/cli/tui/clipboard.d.ts.map +1 -1
  23. package/dist/cli/tui/clipboard.js +12 -0
  24. package/dist/cli/tui/clipboard.js.map +1 -1
  25. package/dist/cli/tui/slash-commands.d.ts.map +1 -1
  26. package/dist/cli/tui/slash-commands.js +10 -0
  27. package/dist/cli/tui/slash-commands.js.map +1 -1
  28. package/dist/cli.d.ts +11 -1
  29. package/dist/cli.d.ts.map +1 -1
  30. package/dist/cli.js +25 -1
  31. package/dist/cli.js.map +1 -1
  32. package/dist/compatibility/claude/schema.d.ts.map +1 -1
  33. package/dist/compatibility/claude/schema.js +11 -1
  34. package/dist/compatibility/claude/schema.js.map +1 -1
  35. package/dist/persistence/claude-job-store.d.ts +11 -1
  36. package/dist/persistence/claude-job-store.d.ts.map +1 -1
  37. package/dist/persistence/claude-job-store.js +23 -2
  38. package/dist/persistence/claude-job-store.js.map +1 -1
  39. package/dist/persistence/claude-transcript-store.d.ts.map +1 -1
  40. package/dist/persistence/claude-transcript-store.js +8 -2
  41. package/dist/persistence/claude-transcript-store.js.map +1 -1
  42. package/package.json +2 -1
package/README.md CHANGED
@@ -83,8 +83,10 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
83
83
  restored active-branch conversation history, streaming and expandable
84
84
  thinking, grouped multi-file reads, globally expandable tool results,
85
85
  command-specific `/add-dir`, `/copy`, `/branch`, `/rename`, `/export`,
86
- provider-backed `/compact`, native `/rewind`, runtime `/cd`, `/config`,
87
- `/usage`, `/mcp`, and live extension-reload controls,
86
+ provider-backed `/compact`, native `/rewind`, runtime `/cd`, transcript-free
87
+ `/btw` side questions with background-Agent handoff, interactive
88
+ `/background` terminal handoff, `/config`, `/usage`, `/mcp`, and live
89
+ extension-reload controls,
88
90
  cursor/history composer, per-session model/effort/permission controls,
89
91
  context/status/skill/task dashboards, prompt stash and continuation shortcuts,
90
92
  filterable `@` file and agent references, composer undo, `Ctrl+G` external
@@ -70,6 +70,16 @@ export interface SessionRunResult {
70
70
  costUsd?: number;
71
71
  modelUsage?: Readonly<Record<string, ModelUsage>>;
72
72
  }
73
+ export interface SideQuestionResult {
74
+ sessionId: string;
75
+ text: string;
76
+ usage: ModelUsage;
77
+ costUsd?: number;
78
+ }
79
+ export interface SideQuestionForkResult {
80
+ agentId: string;
81
+ name: string;
82
+ }
73
83
  export interface SessionSummary {
74
84
  sessionId: string;
75
85
  name?: string;
@@ -93,6 +103,10 @@ export interface ForkResult {
93
103
  sessionId: string;
94
104
  parentSessionId: string;
95
105
  }
106
+ export interface SessionForkCheckpoint {
107
+ resumeSessionAt: string;
108
+ entryCount: number;
109
+ }
96
110
  export interface ManualCompactResult {
97
111
  summary: string;
98
112
  usage: ModelUsage;
@@ -122,6 +136,8 @@ export declare class ClaudeSessionService {
122
136
  private readonly worktreeManager;
123
137
  private readonly sessionCwds;
124
138
  private readonly hostedSubagents;
139
+ private readonly hostedSubagentsByRegistry;
140
+ private readonly backgroundNotificationWrites;
125
141
  private readonly downloadedFileResourceSessions;
126
142
  private runtimeCwd;
127
143
  constructor(options: ClaudeSessionServiceOptions);
@@ -133,6 +149,8 @@ export declare class ClaudeSessionService {
133
149
  runShell(command: string, signal?: AbortSignal, sessionId?: string, name?: string): Promise<SessionRunResult>;
134
150
  resume(sessionId: string, prompt: string, signal?: AbortSignal, name?: string, images?: readonly ModelImage[], documents?: readonly ModelDocument[], resumeSessionAt?: string): Promise<SessionRunResult>;
135
151
  resumeShell(sessionId: string, command: string, signal?: AbortSignal, name?: string, resumeSessionAt?: string): Promise<SessionRunResult>;
152
+ answerSideQuestion(sessionId: string | undefined, question: string, signal?: AbortSignal, onDelta?: (delta: string) => void, permissionMode?: ClaudePermissionMode): Promise<SideQuestionResult>;
153
+ forkSideQuestion(sessionId: string, question: string, signal?: AbortSignal): Promise<SideQuestionForkResult>;
136
154
  promptSuggestion(sessionId: string, signal?: AbortSignal): Promise<string | null>;
137
155
  sessionNameSuggestion(sessionId: string, signal?: AbortSignal): Promise<string | null>;
138
156
  sessions(): Promise<SessionSummary[]>;
@@ -142,8 +160,13 @@ export declare class ClaudeSessionService {
142
160
  rename(sessionId: string, name: string): Promise<void>;
143
161
  changeCwd(sessionId: string | undefined, requestedCwd: string): Promise<string>;
144
162
  recordCdUsage(sessionId: string): Promise<void>;
163
+ recordBtwUsage(sessionId: string | undefined, permissionMode?: ClaudePermissionMode): Promise<string>;
164
+ recordBackgroundUsage(sessionId: string | undefined, permissionMode?: ClaudePermissionMode): Promise<string>;
165
+ recordBackgroundLaunch(sessionId: string): Promise<SessionForkCheckpoint>;
145
166
  compact(sessionId: string, signal?: AbortSignal, selection?: ManualCompactSelection): Promise<ManualCompactResult>;
146
167
  fork(parentSessionId: string, sessionId?: string, resumeSessionAt?: string): Promise<ForkResult>;
168
+ ensureFork(parentSessionId: string, sessionId: string, checkpoint?: SessionForkCheckpoint): Promise<ForkResult>;
169
+ private nativeForkEntries;
147
170
  rewindFiles(sessionId: string, userMessageId: string): Promise<void>;
148
171
  rewindPoints(sessionId: string): Promise<RewindPoint[]>;
149
172
  setPermissionMode(sessionId: string, permissionMode: ClaudePermissionMode): Promise<void>;
@@ -162,7 +185,13 @@ export declare class ClaudeSessionService {
162
185
  private restoreWorktree;
163
186
  private paths;
164
187
  private appendCdCommand;
188
+ private appendSystemLocalCommand;
189
+ private ensureLocalSession;
190
+ private appendInputHistory;
191
+ private appendBackgroundNotification;
192
+ private enqueueBackgroundNotifications;
165
193
  private cdCommandEntries;
194
+ private localCommandEntries;
166
195
  private store;
167
196
  private turnStore;
168
197
  private assertSessionPersistence;
@@ -1 +1 @@
1
- {"version":3,"file":"session-service.d.ts","sourceRoot":"","sources":["../../src/application/session-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-service.d.ts","sourceRoot":"","sources":["../../src/application/session-service.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { copyFile, link, lstat, mkdir, readdir, realpath, stat, unlink, } from 'node:fs/promises';
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
5
  import { createClaudeCompactEntries, formatClaudeCompactSummary, getCumulativeDroppedTokens, } from '../compatibility/claude/compaction.js';
@@ -91,6 +91,8 @@ export class ClaudeSessionService {
91
91
  worktreeManager;
92
92
  sessionCwds = new Map();
93
93
  hostedSubagents = new Set();
94
+ hostedSubagentsByRegistry = new WeakMap();
95
+ backgroundNotificationWrites = new Map();
94
96
  downloadedFileResourceSessions = new Set();
95
97
  runtimeCwd;
96
98
  constructor(options) {
@@ -127,6 +129,8 @@ export class ClaudeSessionService {
127
129
  async close() {
128
130
  this.scheduledPrompts?.close();
129
131
  await Promise.all([...this.hostedSubagents].map((executor) => executor.close()));
132
+ await Promise.resolve();
133
+ await Promise.all([...this.backgroundNotificationWrites.values()]);
130
134
  this.hostedSubagents.clear();
131
135
  await this.workflowManager?.close();
132
136
  }
@@ -293,7 +297,7 @@ export class ClaudeSessionService {
293
297
  'Monitor',
294
298
  'PushNotification',
295
299
  ];
296
- return {
300
+ const hostedRegistry = {
297
301
  definitions: () => {
298
302
  const definitions = interactiveRegistry.definitions();
299
303
  return [...definitions].sort((left, right) => {
@@ -306,6 +310,10 @@ export class ClaudeSessionService {
306
310
  prepare: (call, context) => interactiveRegistry.prepare(call, context),
307
311
  execute: (call, context) => interactiveRegistry.execute(call, context),
308
312
  };
313
+ if (subagentExecutor) {
314
+ this.hostedSubagentsByRegistry.set(hostedRegistry, subagentExecutor);
315
+ }
316
+ return hostedRegistry;
309
317
  }
310
318
  async run(prompt, signal, sessionId = randomUUID(), name, images, documents) {
311
319
  this.worktreeManager?.bindSession(sessionId);
@@ -323,6 +331,119 @@ export class ClaudeSessionService {
323
331
  this.worktreeManager?.bindSession(sessionId);
324
332
  return this.executeTurn(sessionId, `! ${command}`, true, signal, name, [], [], resumeSessionAt, command);
325
333
  }
334
+ async answerSideQuestion(sessionId, question, signal, onDelta, permissionMode = 'default') {
335
+ const prompt = question.trim();
336
+ if (!prompt)
337
+ throw new Error('Side question must not be empty');
338
+ const activeSessionId = await this.ensureLocalSession(sessionId, permissionMode);
339
+ await this.appendInputHistory(`/btw ${prompt}`, activeSessionId);
340
+ const provider = this.provider();
341
+ let entries = [];
342
+ if (sessionId) {
343
+ const loaded = this.options.sessionPersistence === false
344
+ ? await this.turnStore(activeSessionId).withLease((lease) => lease.load())
345
+ : {
346
+ status: 'completed',
347
+ value: await this.store(activeSessionId).loadReadOnly(),
348
+ };
349
+ if (loaded.status === 'conflict') {
350
+ throw new Error(`Claude side question conflict: ${loaded.reason}`);
351
+ }
352
+ if (loaded.value.entries.length === 0) {
353
+ throw new Error(`Claude session not found: ${activeSessionId}`);
354
+ }
355
+ entries = loaded.value.entries;
356
+ this.restoreWorktree(entries);
357
+ }
358
+ const assembledContext = await this.options.contextAssembler?.assemble({
359
+ cwd: this.activeCwd(),
360
+ });
361
+ const messages = [
362
+ ...(assembledContext?.systemMessages ?? []),
363
+ ...injectFirstUserMessageContext([
364
+ ...projectClaudeModelMessages(entries),
365
+ { role: 'user', content: prompt },
366
+ ], assembledContext?.firstUserMessageContext),
367
+ ];
368
+ const budget = this.contextBudget(provider);
369
+ if (budget)
370
+ budget.assertFits(budget.evaluate(messages));
371
+ let text = '';
372
+ let usage = { inputTokens: 0, outputTokens: 0 };
373
+ for await (const event of provider.complete({
374
+ messages,
375
+ ...(this.options.effort ? { effort: this.options.effort } : {}),
376
+ ...(signal ? { signal } : {}),
377
+ })) {
378
+ if (event.type === 'text-delta') {
379
+ text += event.delta;
380
+ onDelta?.(event.delta);
381
+ }
382
+ else if (event.type === 'usage') {
383
+ usage = event.usage;
384
+ }
385
+ else if (event.type === 'tool-call') {
386
+ throw new Error('Side questions cannot call tools; press f to fork');
387
+ }
388
+ }
389
+ const pricing = this.options.pricing?.resolve(provider.model ?? 'praxis/provider');
390
+ return {
391
+ sessionId: activeSessionId,
392
+ text,
393
+ usage,
394
+ ...(pricing ? { costUsd: usageCostUsd(usage, pricing) } : {}),
395
+ };
396
+ }
397
+ async forkSideQuestion(sessionId, question, signal) {
398
+ const prompt = question.trim();
399
+ if (!prompt)
400
+ throw new Error('Side question must not be empty');
401
+ const name = prompt
402
+ .toLowerCase()
403
+ .replace(/[^a-z0-9]+/gu, '-')
404
+ .replace(/^-+|-+$/gu, '')
405
+ .split('-')
406
+ .filter(Boolean)
407
+ .slice(0, 3)
408
+ .join('-')
409
+ .slice(0, 64) || 'side-question';
410
+ const registry = this.createHostedToolRegistry(sessionId);
411
+ const call = {
412
+ id: randomUUID(),
413
+ name: 'Agent',
414
+ input: {
415
+ description: prompt,
416
+ prompt,
417
+ subagent_type: 'general-purpose',
418
+ run_in_background: true,
419
+ name,
420
+ },
421
+ };
422
+ const context = {
423
+ cwd: this.activeCwd(),
424
+ ...(signal ? { signal } : {}),
425
+ };
426
+ const prepared = await registry.prepare(call, context);
427
+ const result = await registry.execute(prepared, context);
428
+ const agentId = result.nativeToolUseResult?.agentId;
429
+ if (result.isError || typeof agentId !== 'string') {
430
+ throw new Error(result.content || 'Could not fork side question');
431
+ }
432
+ await this.appendSystemLocalCommand(sessionId, 'btw', prompt, `⑂ forked ${name} (${agentId.slice(-4)})`);
433
+ const executor = this.hostedSubagentsByRegistry.get(registry);
434
+ if (executor) {
435
+ void executor
436
+ .notifications(true)
437
+ .then(({ messages }) => this.enqueueBackgroundNotifications(sessionId, messages))
438
+ .catch((error) => this.options.eventSink?.({
439
+ type: 'warning',
440
+ message: error instanceof Error
441
+ ? error.message
442
+ : `Could not persist background notification: ${String(error)}`,
443
+ }));
444
+ }
445
+ return { agentId, name };
446
+ }
326
447
  async promptSuggestion(sessionId, signal) {
327
448
  const provider = this.provider();
328
449
  const loaded = this.options.sessionPersistence === false
@@ -689,6 +810,113 @@ export class ClaudeSessionService {
689
810
  throw new Error(`Claude cd usage conflict: ${result.reason}`);
690
811
  }
691
812
  }
813
+ async recordBtwUsage(sessionId, permissionMode = 'default') {
814
+ const activeSessionId = await this.ensureLocalSession(sessionId, permissionMode);
815
+ await this.appendInputHistory('/btw', activeSessionId);
816
+ while (true) {
817
+ const result = await this.turnStore(activeSessionId).withLease(async (lease) => {
818
+ const snapshot = await lease.load();
819
+ if (snapshot.entries.length === 0) {
820
+ throw new Error(`Claude session not found: ${activeSessionId}`);
821
+ }
822
+ const entries = this.localCommandEntries(activeSessionId, this.activeCwd(), this.logicalTailUuid(snapshot.tail), 'btw', '', 'Usage: /btw <your question>');
823
+ const leafUuid = entries.at(-1)?.uuid;
824
+ if (typeof leafUuid !== 'string') {
825
+ throw new Error('Claude btw local command pair is incomplete');
826
+ }
827
+ const appended = await lease.appendMany(snapshot.tail, [
828
+ ...entries,
829
+ { type: 'last-prompt', leafUuid, sessionId: activeSessionId },
830
+ ]);
831
+ if (appended.status === 'conflict') {
832
+ throw new Error(`Claude local command append conflict: ${appended.reason}`);
833
+ }
834
+ });
835
+ if (result.status === 'completed')
836
+ return activeSessionId;
837
+ await new Promise((resolve) => setTimeout(resolve, 25));
838
+ }
839
+ }
840
+ async recordBackgroundUsage(sessionId, permissionMode = 'default') {
841
+ this.assertWritable();
842
+ const activeSessionId = await this.ensureLocalSession(sessionId, permissionMode);
843
+ await this.appendInputHistory('/background', activeSessionId);
844
+ while (true) {
845
+ const result = await this.turnStore(activeSessionId).withLease(async (lease) => {
846
+ const snapshot = await lease.load();
847
+ if (snapshot.entries.length === 0) {
848
+ throw new Error(`Claude session not found: ${activeSessionId}`);
849
+ }
850
+ const timestamp = new Date().toISOString();
851
+ const promptId = randomUUID();
852
+ const firstUuid = randomUUID();
853
+ const commandUuid = randomUUID();
854
+ const common = {
855
+ isSidechain: false,
856
+ promptId,
857
+ timestamp,
858
+ userType: 'external',
859
+ entrypoint: 'cli',
860
+ cwd: this.activeCwd(),
861
+ sessionId: activeSessionId,
862
+ version: this.options.claudeVersion,
863
+ gitBranch: null,
864
+ };
865
+ const appended = await lease.appendMany(snapshot.tail, [
866
+ {
867
+ ...common,
868
+ parentUuid: this.logicalTailUuid(snapshot.tail),
869
+ type: 'user',
870
+ message: {
871
+ role: 'user',
872
+ content: '<local-command-caveat>Caveat: The messages below were generated by the user while running local commands. DO NOT respond to these messages or otherwise consider them in your response unless the user explicitly asks you to.</local-command-caveat>',
873
+ },
874
+ isMeta: true,
875
+ uuid: firstUuid,
876
+ },
877
+ {
878
+ ...common,
879
+ parentUuid: firstUuid,
880
+ type: 'user',
881
+ message: {
882
+ role: 'user',
883
+ content: '<command-name>/background</command-name>\n <command-message>background</command-message>\n <command-args></command-args>',
884
+ },
885
+ uuid: commandUuid,
886
+ },
887
+ {
888
+ ...common,
889
+ parentUuid: commandUuid,
890
+ type: 'user',
891
+ message: {
892
+ role: 'user',
893
+ content: '<local-command-stdout>Nothing to background yet — send a message first.</local-command-stdout>',
894
+ },
895
+ uuid: randomUUID(),
896
+ },
897
+ ]);
898
+ if (appended.status === 'conflict') {
899
+ throw new Error(`Claude background command append conflict: ${appended.reason}`);
900
+ }
901
+ });
902
+ if (result.status === 'completed')
903
+ return activeSessionId;
904
+ await new Promise((resolve) => setTimeout(resolve, 25));
905
+ }
906
+ }
907
+ async recordBackgroundLaunch(sessionId) {
908
+ this.assertWritable();
909
+ const result = await this.turnStore(sessionId).withLease((lease) => lease.load());
910
+ if (result.status === 'conflict') {
911
+ throw new Error(`Claude background checkpoint conflict: ${result.reason}`);
912
+ }
913
+ const resumeSessionAt = this.logicalTailUuid(result.value.tail);
914
+ if (!resumeSessionAt) {
915
+ throw new Error(`Claude session not found: ${sessionId}`);
916
+ }
917
+ await this.appendInputHistory('/background', sessionId);
918
+ return { resumeSessionAt, entryCount: result.value.entries.length };
919
+ }
692
920
  async compact(sessionId, signal, selection) {
693
921
  this.assertWritable();
694
922
  const provider = this.provider();
@@ -811,6 +1039,33 @@ export class ClaudeSessionService {
811
1039
  async fork(parentSessionId, sessionId = randomUUID(), resumeSessionAt) {
812
1040
  this.assertSessionPersistence();
813
1041
  this.assertWritable();
1042
+ const entries = await this.nativeForkEntries(parentSessionId, sessionId, resumeSessionAt);
1043
+ const result = await this.store(sessionId).create(entries);
1044
+ if (result.status === 'conflict') {
1045
+ throw new Error('Generated Claude fork session already exists');
1046
+ }
1047
+ return { sessionId, parentSessionId };
1048
+ }
1049
+ async ensureFork(parentSessionId, sessionId, checkpoint) {
1050
+ this.assertSessionPersistence();
1051
+ this.assertWritable();
1052
+ const expected = await this.nativeForkEntries(parentSessionId, sessionId, checkpoint?.resumeSessionAt, checkpoint?.entryCount);
1053
+ const target = this.store(sessionId);
1054
+ const created = await target.create(expected);
1055
+ if (created.status === 'created')
1056
+ return { sessionId, parentSessionId };
1057
+ const existing = await target.withLease((lease) => lease.load());
1058
+ if (existing.status === 'conflict') {
1059
+ throw new Error(`Claude transcript fork conflict: ${existing.reason}`);
1060
+ }
1061
+ if (existing.value.entries.length < expected.length ||
1062
+ expected.some((entry, index) => JSON.stringify(existing.value.entries[index]) !==
1063
+ JSON.stringify(entry))) {
1064
+ throw new Error('Claude handoff target is not the expected native fork');
1065
+ }
1066
+ return { sessionId, parentSessionId };
1067
+ }
1068
+ async nativeForkEntries(parentSessionId, sessionId, resumeSessionAt, sourceEntryCount) {
814
1069
  const sourceResult = await this.store(parentSessionId).withLease((lease) => lease.load());
815
1070
  if (sourceResult.status === 'conflict') {
816
1071
  throw new Error(`Claude transcript fork conflict: ${sourceResult.reason}`);
@@ -819,17 +1074,18 @@ export class ClaudeSessionService {
819
1074
  if (source.entries.length === 0) {
820
1075
  throw new Error(`Claude session not found: ${parentSessionId}`);
821
1076
  }
822
- const target = this.store(sessionId);
823
- const result = await target.create(createClaudeNativeFork({
824
- source: source.entries,
1077
+ if (sourceEntryCount !== undefined &&
1078
+ (sourceEntryCount <= 0 || sourceEntryCount > source.entries.length)) {
1079
+ throw new Error('Claude handoff source checkpoint is no longer available');
1080
+ }
1081
+ return createClaudeNativeFork({
1082
+ source: sourceEntryCount === undefined
1083
+ ? source.entries
1084
+ : source.entries.slice(0, sourceEntryCount),
825
1085
  sourceSessionId: parentSessionId,
826
1086
  sessionId,
827
1087
  ...(resumeSessionAt === undefined ? {} : { resumeSessionAt }),
828
- }));
829
- if (result.status === 'conflict') {
830
- throw new Error('Generated Claude fork session already exists');
831
- }
832
- return { sessionId, parentSessionId };
1088
+ });
833
1089
  }
834
1090
  async rewindFiles(sessionId, userMessageId) {
835
1091
  const result = await this.store(sessionId).withLease(async (lease) => {
@@ -2183,9 +2439,128 @@ export class ClaudeSessionService {
2183
2439
  throw new Error(`Claude local command conflict: ${result.reason}`);
2184
2440
  }
2185
2441
  }
2442
+ async appendSystemLocalCommand(sessionId, command, args, output) {
2443
+ while (true) {
2444
+ const result = await this.turnStore(sessionId).withLease(async (lease) => {
2445
+ const snapshot = await lease.load();
2446
+ if (snapshot.entries.length === 0) {
2447
+ throw new Error(`Claude session not found: ${sessionId}`);
2448
+ }
2449
+ const appendResult = await lease.appendMany(snapshot.tail, this.localCommandEntries(sessionId, this.activeCwd(), this.logicalTailUuid(snapshot.tail), command, args, output));
2450
+ if (appendResult.status === 'conflict') {
2451
+ throw new Error(`Claude local command append conflict: ${appendResult.reason}`);
2452
+ }
2453
+ });
2454
+ if (result.status === 'completed')
2455
+ return;
2456
+ await new Promise((resolve) => setTimeout(resolve, 25));
2457
+ }
2458
+ }
2459
+ async ensureLocalSession(sessionId, permissionMode) {
2460
+ if (sessionId)
2461
+ return sessionId;
2462
+ this.assertWritable();
2463
+ const createdSessionId = randomUUID();
2464
+ const store = this.turnStore(createdSessionId);
2465
+ const created = await store.create([
2466
+ { type: 'mode', mode: 'normal', sessionId: createdSessionId },
2467
+ {
2468
+ type: 'permission-mode',
2469
+ permissionMode,
2470
+ sessionId: createdSessionId,
2471
+ },
2472
+ ]);
2473
+ if (created.status === 'conflict') {
2474
+ throw new Error(`Session ID ${createdSessionId} is already in use`);
2475
+ }
2476
+ this.sessionCwds.set(createdSessionId, this.activeCwd());
2477
+ this.options.interactiveTools?.restore(createdSessionId, [
2478
+ { type: 'mode', mode: 'normal', sessionId: createdSessionId },
2479
+ {
2480
+ type: 'permission-mode',
2481
+ permissionMode,
2482
+ sessionId: createdSessionId,
2483
+ },
2484
+ ]);
2485
+ return createdSessionId;
2486
+ }
2487
+ async appendInputHistory(display, sessionId) {
2488
+ await mkdir(this.options.configRoot, { recursive: true });
2489
+ await appendFile(join(this.options.configRoot, 'history.jsonl'), `${JSON.stringify({
2490
+ display,
2491
+ pastedContents: {},
2492
+ timestamp: Date.now(),
2493
+ project: this.activeCwd(),
2494
+ sessionId,
2495
+ })}\n`, 'utf8');
2496
+ }
2497
+ async appendBackgroundNotification(sessionId, content) {
2498
+ while (true) {
2499
+ const result = await this.turnStore(sessionId).withLease(async (lease) => {
2500
+ const snapshot = await lease.load();
2501
+ if (snapshot.entries.length === 0) {
2502
+ throw new Error(`Claude session not found: ${sessionId}`);
2503
+ }
2504
+ const timestamp = new Date().toISOString();
2505
+ const entries = [
2506
+ {
2507
+ type: 'queue-operation',
2508
+ operation: 'enqueue',
2509
+ timestamp,
2510
+ sessionId,
2511
+ content,
2512
+ },
2513
+ {
2514
+ type: 'queue-operation',
2515
+ operation: 'dequeue',
2516
+ timestamp,
2517
+ sessionId,
2518
+ },
2519
+ {
2520
+ parentUuid: this.logicalTailUuid(snapshot.tail),
2521
+ isSidechain: false,
2522
+ promptId: randomUUID(),
2523
+ type: 'user',
2524
+ message: { role: 'user', content },
2525
+ uuid: randomUUID(),
2526
+ timestamp,
2527
+ permissionMode: 'default',
2528
+ origin: { kind: 'task-notification' },
2529
+ promptSource: 'system',
2530
+ userType: 'external',
2531
+ entrypoint: 'cli',
2532
+ cwd: this.activeCwd(),
2533
+ sessionId,
2534
+ version: this.options.claudeVersion,
2535
+ gitBranch: null,
2536
+ },
2537
+ ];
2538
+ const appended = await lease.appendMany(snapshot.tail, entries);
2539
+ if (appended.status === 'conflict') {
2540
+ throw new Error(`Claude background notification append conflict: ${appended.reason}`);
2541
+ }
2542
+ });
2543
+ if (result.status === 'completed')
2544
+ return;
2545
+ await new Promise((resolve) => setTimeout(resolve, 25));
2546
+ }
2547
+ }
2548
+ enqueueBackgroundNotifications(sessionId, messages) {
2549
+ const previous = this.backgroundNotificationWrites.get(sessionId);
2550
+ const queued = (previous ?? Promise.resolve()).then(async () => {
2551
+ for (const message of messages) {
2552
+ await this.appendBackgroundNotification(sessionId, message);
2553
+ }
2554
+ });
2555
+ this.backgroundNotificationWrites.set(sessionId, queued);
2556
+ const cleanup = () => {
2557
+ if (this.backgroundNotificationWrites.get(sessionId) === queued)
2558
+ this.backgroundNotificationWrites.delete(sessionId);
2559
+ };
2560
+ void queued.then(cleanup, cleanup);
2561
+ return queued;
2562
+ }
2186
2563
  cdCommandEntries(sessionId, cwd, parentUuid) {
2187
- const firstUuid = randomUUID();
2188
- const secondUuid = randomUUID();
2189
2564
  const timestamp = new Date().toISOString();
2190
2565
  const common = {
2191
2566
  isSidechain: false,
@@ -2197,38 +2572,63 @@ export class ClaudeSessionService {
2197
2572
  version: this.options.claudeVersion,
2198
2573
  gitBranch: null,
2199
2574
  };
2575
+ const localCommands = this.localCommandEntries(sessionId, cwd, parentUuid, 'cd', cwd, `Moved to ${cwd}`, timestamp);
2576
+ const secondUuid = localCommands[1]?.uuid;
2577
+ if (typeof secondUuid !== 'string') {
2578
+ throw new Error('Claude cd local command pair is incomplete');
2579
+ }
2200
2580
  return [
2581
+ ...localCommands,
2201
2582
  {
2202
2583
  ...common,
2584
+ parentUuid: secondUuid,
2585
+ promptId: randomUUID(),
2586
+ type: 'user',
2587
+ message: {
2588
+ role: 'user',
2589
+ content: `<system-reminder>\nThe session's working directory has changed to ${cwd} (via /cd). The environment block at the start of this conversation still names the previous directory — that information is stale. All tool calls and relative paths now resolve from ${cwd}.\n</system-reminder>`,
2590
+ },
2591
+ isMeta: true,
2592
+ uuid: randomUUID(),
2593
+ },
2594
+ ];
2595
+ }
2596
+ localCommandEntries(sessionId, cwd, parentUuid, command, args, output, timestamp = new Date().toISOString()) {
2597
+ const firstUuid = randomUUID();
2598
+ return [
2599
+ {
2600
+ parentUuid,
2601
+ isSidechain: false,
2203
2602
  type: 'system',
2204
2603
  subtype: 'local_command',
2205
- isMeta: false,
2604
+ content: `<command-name>/${command}</command-name>\n <command-message>${command}</command-message>\n <command-args>${args}</command-args>`,
2206
2605
  level: 'info',
2207
- parentUuid,
2606
+ timestamp,
2208
2607
  uuid: firstUuid,
2209
- content: `<command-name>/cd</command-name>\n <command-message>cd</command-message>\n <command-args>${cwd}</command-args>`,
2608
+ isMeta: false,
2609
+ userType: 'external',
2610
+ entrypoint: 'cli',
2611
+ cwd,
2612
+ sessionId,
2613
+ version: this.options.claudeVersion,
2614
+ gitBranch: null,
2210
2615
  },
2211
2616
  {
2212
- ...common,
2617
+ parentUuid: firstUuid,
2618
+ isSidechain: false,
2213
2619
  type: 'system',
2214
2620
  subtype: 'local_command',
2215
- isMeta: false,
2621
+ content: `<local-command-stdout>${output}</local-command-stdout>`,
2216
2622
  level: 'info',
2217
- parentUuid: firstUuid,
2218
- uuid: secondUuid,
2219
- content: `<local-command-stdout>Moved to ${cwd}</local-command-stdout>`,
2220
- },
2221
- {
2222
- ...common,
2223
- parentUuid: secondUuid,
2224
- promptId: randomUUID(),
2225
- type: 'user',
2226
- message: {
2227
- role: 'user',
2228
- content: `<system-reminder>\nThe session's working directory has changed to ${cwd} (via /cd). The environment block at the start of this conversation still names the previous directory — that information is stale. All tool calls and relative paths now resolve from ${cwd}.\n</system-reminder>`,
2229
- },
2230
- isMeta: true,
2623
+ timestamp,
2231
2624
  uuid: randomUUID(),
2625
+ isMeta: false,
2626
+ userType: 'external',
2627
+ entrypoint: 'cli',
2628
+ cwd,
2629
+ sessionId,
2630
+ version: this.options.claudeVersion,
2631
+ gitBranch: null,
2232
2632
  },
2233
2633
  ];
2234
2634
  }