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.
Files changed (36) hide show
  1. package/dist/application/background-agent-manager.d.ts +48 -3
  2. package/dist/application/background-agent-manager.js +249 -73
  3. package/dist/application/background-task-runtime.d.ts +20 -1
  4. package/dist/application/background-task-runtime.js +45 -6
  5. package/dist/application/managed-worktree.d.ts +8 -0
  6. package/dist/application/managed-worktree.js +54 -2
  7. package/dist/application/session-service.d.ts +38 -0
  8. package/dist/application/session-service.js +590 -70
  9. package/dist/application/subagent-service.d.ts +34 -7
  10. package/dist/application/subagent-service.js +694 -135
  11. package/dist/cli/interactive.d.ts +1 -0
  12. package/dist/cli/interactive.js +26 -1
  13. package/dist/cli/tui/task-panel.d.ts +1 -1
  14. package/dist/cli/tui/task-panel.js +2 -1
  15. package/dist/cli-runtime.d.ts +1 -0
  16. package/dist/cli-runtime.js +111 -31
  17. package/dist/compatibility/claude/history.d.ts +8 -0
  18. package/dist/compatibility/claude/history.js +54 -0
  19. package/dist/compatibility/claude/interruption.d.ts +16 -0
  20. package/dist/compatibility/claude/interruption.js +147 -0
  21. package/dist/compatibility/claude/projection.d.ts +4 -0
  22. package/dist/compatibility/claude/projection.js +82 -0
  23. package/dist/compatibility/claude/schema.js +22 -0
  24. package/dist/compatibility/claude/session-metadata.d.ts +44 -0
  25. package/dist/compatibility/claude/session-metadata.js +180 -0
  26. package/dist/compatibility/claude/sidechain.d.ts +5 -0
  27. package/dist/compatibility/claude/sidechain.js +13 -4
  28. package/dist/persistence/claude-session-index.d.ts +39 -0
  29. package/dist/persistence/claude-session-index.js +175 -0
  30. package/dist/persistence/claude-sidechain-store.js +19 -3
  31. package/dist/persistence/claude-transcript-store.d.ts +6 -2
  32. package/dist/persistence/claude-transcript-store.js +78 -3
  33. package/dist/persistence/in-memory-transcript-store.js +1 -0
  34. package/dist/persistence/subagent-lifecycle-store.d.ts +66 -0
  35. package/dist/persistence/subagent-lifecycle-store.js +299 -0
  36. package/package.json +2 -1
@@ -1,6 +1,6 @@
1
1
  import { execFile } from 'node:child_process';
2
- import { lstat, mkdir } from 'node:fs/promises';
3
- import { join, resolve } from 'node:path';
2
+ import { lstat, mkdir, realpath } from 'node:fs/promises';
3
+ import { isAbsolute, join, resolve } from 'node:path';
4
4
  import { promisify } from 'node:util';
5
5
  const execFileAsync = promisify(execFile);
6
6
  async function git(cwd, args) {
@@ -124,4 +124,56 @@ export async function createManagedWorktree(options) {
124
124
  },
125
125
  };
126
126
  }
127
+ /** Reattaches only to a real, registered Git worktree. Persisted metadata is
128
+ * untrusted input, so an arbitrary directory can never become an execution
129
+ * cwd merely because it exists. Restored worktrees are retained for audit. */
130
+ export async function restoreManagedWorktree(options) {
131
+ if (!isAbsolute(options.path) || options.path.includes('\0')) {
132
+ throw new Error(`Invalid retained ${options.label.toLowerCase()} worktree path`);
133
+ }
134
+ const path = resolve(options.path);
135
+ let entry;
136
+ try {
137
+ entry = await lstat(path);
138
+ }
139
+ catch (error) {
140
+ if (error.code === 'ENOENT') {
141
+ throw new Error(`Retained ${options.label.toLowerCase()} worktree is missing: ${path}`);
142
+ }
143
+ throw error;
144
+ }
145
+ if (entry.isSymbolicLink() || !entry.isDirectory()) {
146
+ throw new Error(`Retained ${options.label.toLowerCase()} worktree must be a real directory: ${path}`);
147
+ }
148
+ let root;
149
+ let registered;
150
+ let canonicalPath;
151
+ try {
152
+ ;
153
+ [root, canonicalPath] = await Promise.all([
154
+ git(options.cwd, ['rev-parse', '--show-toplevel']),
155
+ realpath(path),
156
+ ]);
157
+ registered = await registeredWorktrees(root);
158
+ }
159
+ catch (error) {
160
+ throw new Error(`Could not inspect retained ${options.label.toLowerCase()} worktree ${path}: ${error.message}`);
161
+ }
162
+ if (!registered.has(path) && !registered.has(canonicalPath)) {
163
+ throw new Error(`Retained ${options.label.toLowerCase()} worktree is not registered: ${path}`);
164
+ }
165
+ const worktreeRoot = await realpath(resolve(await git(path, ['rev-parse', '--show-toplevel'])));
166
+ if (worktreeRoot !== canonicalPath) {
167
+ throw new Error(`Retained ${options.label.toLowerCase()} worktree path is not its root: ${path}`);
168
+ }
169
+ return {
170
+ cwd: path,
171
+ async cleanup() {
172
+ return {
173
+ retained: true,
174
+ reason: `${options.label} worktree was restored and retained at ${path}`,
175
+ };
176
+ },
177
+ };
178
+ }
127
179
  //# sourceMappingURL=managed-worktree.js.map
@@ -4,6 +4,8 @@ import { type DataPlane } from '../persistence/data-plane.js';
4
4
  import { type ClaudeFileResource, type ClaudeFileResourceConfig } from '../compatibility/claude/file-resources.js';
5
5
  import { type ClaudeDisplayTranscriptItem } from '../compatibility/claude/projection.js';
6
6
  import { type ClaudeTranscriptEntry } from '../compatibility/claude/schema.js';
7
+ import { type ClaudeSessionMetadata } from '../compatibility/claude/session-metadata.js';
8
+ import { type ClaudeInterruptionClassification } from '../compatibility/claude/interruption.js';
7
9
  import { type ModelDocument, type ModelImage, type ModelToolCall, type ModelProvider, type ModelUsage, type PermissionApproval, type PermissionDecision, type PermissionResolver, type PermissionUpdate, type RuntimeEventSink, type ToolRegistry } from '../core/runtime.js';
8
10
  import { type BackgroundTaskSnapshot } from './background-task-runtime.js';
9
11
  import type { ModelPricingRegistry } from '../core/usage.js';
@@ -38,6 +40,8 @@ export interface ClaudeSessionServiceOptions {
38
40
  persistPermissionUpdates?: (updates: readonly PermissionUpdate[]) => void | Promise<void>;
39
41
  approveTool?: (call: ModelToolCall, originalCall?: ModelToolCall, decision?: PermissionDecision) => PermissionApproval | Promise<PermissionApproval>;
40
42
  approveRecovery?: (call: ModelToolCall) => boolean | Promise<boolean>;
43
+ /** Explicit Claude-compatible opt-in for replaying an interrupted turn. */
44
+ resumeInterruptedTurn?: boolean;
41
45
  contextAssembler?: ContextAssembler;
42
46
  conditionalRuleResolver?: Pick<ClaudeConditionalRuleResolver, 'resolve'>;
43
47
  extensions?: ClaudeExtensionCatalog;
@@ -125,6 +129,12 @@ export interface SideQuestionForkResult {
125
129
  export interface SessionSummary {
126
130
  sessionId: string;
127
131
  name?: string;
132
+ tag?: string;
133
+ agentName?: string;
134
+ agentColor?: string;
135
+ agentSetting?: string;
136
+ permissionMode?: string;
137
+ mode?: string;
128
138
  lastPrompt: string | null;
129
139
  updatedAt: string;
130
140
  status: SessionStatus;
@@ -183,10 +193,18 @@ export declare class ClaudeSessionService {
183
193
  private readonly worktreeManager;
184
194
  private readonly sessionCwds;
185
195
  private readonly discoveredProjectRoots;
196
+ private readonly explicitSessionFiles;
197
+ private readonly explicitResumeLeafUuids;
186
198
  private readonly sessionPermissionUpdates;
187
199
  private readonly hostedSubagents;
200
+ private readonly subagentExecutors;
188
201
  private readonly hostedSubagentsByRegistry;
202
+ private readonly subagentExecutorSessions;
203
+ private readonly hostedSubagentNotificationPumps;
204
+ private readonly hostedSubagentPumpPromises;
205
+ private readonly hostedSubagentNotificationReservations;
189
206
  private readonly backgroundNotificationWrites;
207
+ private closing;
190
208
  private readonly downloadedFileResourceSessions;
191
209
  private readonly detachedHookRuns;
192
210
  private activeProvider;
@@ -194,6 +212,9 @@ export declare class ClaudeSessionService {
194
212
  private readonly sessionCostTrackers;
195
213
  private activeCostSessionId;
196
214
  private closeCostSavePromise;
215
+ private closeMetadataSavePromise;
216
+ private readonly durableMetadataSessions;
217
+ private readonly durableMetadataSnapshots;
197
218
  private readonly sessionMemoryControllers;
198
219
  private resolvedSessionMemoryProvider;
199
220
  private readonly hookLifecycle;
@@ -212,6 +233,10 @@ export declare class ClaudeSessionService {
212
233
  private requireMcp;
213
234
  taskSnapshots(sessionId: string): Promise<BackgroundTaskSnapshot>;
214
235
  stopTask(sessionId: string, taskId: string): Promise<void>;
236
+ backgroundForegroundTask(sessionId: string): import("./background-agent-manager.js").BackgroundAgentSnapshot;
237
+ private sendOwnedBackgroundAgent;
238
+ private sessionSubagentExecutors;
239
+ private collectSubagentNotifications;
215
240
  private applyPermissionUpdates;
216
241
  close(): Promise<void>;
217
242
  transitionHookSession(sessionId: string, reason: Exclude<HookSessionEndReason, 'other'>): Promise<void>;
@@ -240,14 +265,19 @@ export declare class ClaudeSessionService {
240
265
  resumeShell(sessionId: string, command: string, signal?: AbortSignal, name?: string, resumeSessionAt?: string): Promise<SessionRunResult>;
241
266
  answerSideQuestion(sessionId: string | undefined, question: string, signal?: AbortSignal, onDelta?: (delta: string) => void, permissionMode?: ClaudePermissionMode): Promise<SideQuestionResult>;
242
267
  forkSideQuestion(sessionId: string, question: string, signal?: AbortSignal): Promise<SideQuestionForkResult>;
268
+ private startHostedSubagentNotificationPump;
243
269
  promptSuggestion(sessionId: string, signal?: AbortSignal): Promise<string | null>;
244
270
  sessionNameSuggestion(sessionId: string, signal?: AbortSignal): Promise<string | null>;
245
271
  sessions(): Promise<SessionSummary[]>;
246
272
  inspect(sessionId: string): Promise<SessionInspection>;
247
273
  readEffectiveAgentColor(sessionId: string): Promise<AgentColorName | undefined>;
248
274
  export(sessionId: string): Promise<Buffer>;
275
+ registerResumePath(requestedPath: string): Promise<SessionSummary>;
249
276
  transcript(sessionId: string, resumeSessionAt?: string): Promise<ClaudeDisplayTranscriptItem[]>;
277
+ interruption(sessionId: string): Promise<ClaudeInterruptionClassification>;
278
+ metadata(sessionId: string): Promise<ClaudeSessionMetadata>;
250
279
  rename(sessionId: string, name: string): Promise<void>;
280
+ tag(sessionId: string, tag: string): Promise<void>;
251
281
  changeCwd(sessionId: string | undefined, requestedCwd: string): Promise<string>;
252
282
  recordCdUsage(sessionId: string): Promise<void>;
253
283
  approveRecentlyDenied(sessionId: string, display: string): Promise<void>;
@@ -269,6 +299,7 @@ export declare class ClaudeSessionService {
269
299
  private persistActiveSessionCost;
270
300
  private recordAuxiliaryMetrics;
271
301
  costSnapshot(sessionId: string): Promise<ClaudeSessionCostSnapshot>;
302
+ private withDetachedSubagentUsage;
272
303
  private executeTurn;
273
304
  private ensureFileResources;
274
305
  private translationContext;
@@ -303,7 +334,14 @@ export declare class ClaudeSessionService {
303
334
  private cdCommandEntries;
304
335
  private localCommandEntries;
305
336
  private store;
337
+ /**
338
+ * Refreshes metadata under the transcript lease before writing a compact
339
+ * tail snapshot. This prevents a long-lived process from overwriting a
340
+ * newer title or tag appended by another writer.
341
+ */
342
+ private reappendDurableMetadata;
306
343
  private turnStore;
344
+ private rememberDurableMetadata;
307
345
  private assertSessionPersistence;
308
346
  private assertWritable;
309
347
  private sessionStatus;