praxis-agent 0.46.1 → 0.46.4

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.
@@ -19,6 +19,12 @@ export function projectTuiFocusStack(input) {
19
19
  layer: { kind: 'cancelable', target: 'plan-approval' },
20
20
  };
21
21
  }
22
+ else if (input.cdTrust) {
23
+ higher = {
24
+ id: 'cd-trust',
25
+ layer: { kind: 'cancelable', target: 'cd-trust' },
26
+ };
27
+ }
22
28
  else if (input.question) {
23
29
  higher = {
24
30
  id: 'question',
@@ -1,7 +1,7 @@
1
1
  import { type ComposerKeyProjection } from './composer-key-router.js';
2
2
  import type { ComposerEditorState } from './composer-editor.js';
3
3
  export type TuiScrollIntent = 'page-older' | 'page-newer' | 'half-page-older' | 'half-page-newer' | 'line-older' | 'line-newer' | 'none';
4
- export type TuiCancellationTarget = 'permission' | 'plan-approval' | 'question' | 'elicitation' | 'elicitation-url-waiting' | 'elicitation-options' | 'file-picker' | 'command-palette';
4
+ export type TuiCancellationTarget = 'permission' | 'plan-approval' | 'cd-trust' | 'question' | 'elicitation' | 'elicitation-url-waiting' | 'elicitation-options' | 'file-picker' | 'command-palette';
5
5
  export type TuiInteractionLayer = {
6
6
  readonly kind: 'none';
7
7
  } | {
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { type ForkResult, type ManualCompactResult, type ManualCompactSelection, type RewindPoint, type SessionForkCheckpoint, type SessionInspection, type SessionRunResult, type SessionSummary, type SideQuestionForkResult, type SideQuestionResult } from './application/session-service.js';
2
+ import { type CwdInspection, type ForkResult, type ManualCompactResult, type ManualCompactSelection, type RewindPoint, type SessionForkCheckpoint, type SessionInspection, type SessionRunResult, type SessionSummary, type SideQuestionForkResult, type SideQuestionResult } from './application/session-service.js';
3
3
  import type { TeamLeadOperations } from './application/team-lead-operations.js';
4
4
  import type { ClaudeSessionCostSnapshot } from './application/session-cost-tracker.js';
5
5
  import { type AgentColorName, type AgentColorSelection } from './core/agent-color.js';
@@ -43,7 +43,8 @@ interface SessionCommands {
43
43
  setPermissionMode?(sessionId: string, mode: ClaudePermissionMode): Promise<void>;
44
44
  rewindFiles?(sessionId: string, userMessageId: string): Promise<void>;
45
45
  rewindPoints?(sessionId: string): Promise<RewindPoint[]>;
46
- changeCwd?(sessionId: string | undefined, cwd: string): Promise<string>;
46
+ changeCwd?(sessionId: string | undefined, cwd: string, expectedCanonicalTarget?: string): Promise<string>;
47
+ inspectCwd?(cwd: string): Promise<CwdInspection>;
47
48
  notify?(sessionId: string | undefined, message: string, notificationType: string, title?: string): void;
48
49
  recordCdUsage?(sessionId: string): Promise<void>;
49
50
  approveRecentlyDenied?(sessionId: string, display: string): Promise<void>;
@@ -2049,7 +2049,8 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
2049
2049
  setPermissionMode: (sessionId, permissionMode) => service.setPermissionMode(sessionId, permissionMode),
2050
2050
  rewindFiles: (sessionId, userMessageId) => service.rewindFiles(sessionId, userMessageId),
2051
2051
  rewindPoints: (sessionId) => service.rewindPoints(sessionId),
2052
- changeCwd: (sessionId, cwd) => service.changeCwd(sessionId, cwd),
2052
+ changeCwd: (sessionId, cwd, expectedCanonicalTarget) => service.changeCwd(sessionId, cwd, expectedCanonicalTarget),
2053
+ inspectCwd: (cwd) => service.inspectCwd(cwd),
2053
2054
  notify: (sessionId, message, notificationType, title) => service.notifyDetached(sessionId, message, notificationType, title),
2054
2055
  recordCdUsage: (sessionId) => service.recordCdUsage(sessionId),
2055
2056
  approveRecentlyDenied: (sessionId, display) => service.approveRecentlyDenied(sessionId, display),
@@ -7,8 +7,4 @@
7
7
  export type NativeTranscriptEntry = Record<string, unknown> & {
8
8
  type: string;
9
9
  };
10
- export declare function isNativeForkableEntryType(type: string): boolean;
11
- /** Copy only projection metadata; persisted native events are copied by
12
- * NativeSessionTranscript.forkTo and retain their event identity semantics. */
13
- export declare function copyNativeEntryWithSessionId(entry: NativeTranscriptEntry, sessionId: string): NativeTranscriptEntry;
14
10
  //# sourceMappingURL=schema.d.ts.map
@@ -1,24 +1,2 @@
1
- const FORKABLE_ENTRY_TYPES = new Set([
2
- 'agent-color',
3
- 'agent-name',
4
- 'agent-setting',
5
- 'ai-title',
6
- 'assistant',
7
- 'attachment',
8
- 'custom-title',
9
- 'last-prompt',
10
- 'mode',
11
- 'permission-mode',
12
- 'pr-link',
13
- 'system',
14
- 'user',
15
- ]);
16
- export function isNativeForkableEntryType(type) {
17
- return FORKABLE_ENTRY_TYPES.has(type);
18
- }
19
- /** Copy only projection metadata; persisted native events are copied by
20
- * NativeSessionTranscript.forkTo and retain their event identity semantics. */
21
- export function copyNativeEntryWithSessionId(entry, sessionId) {
22
- return { ...entry, sessionId };
23
- }
1
+ export {};
24
2
  //# sourceMappingURL=schema.js.map
@@ -5,6 +5,7 @@ export interface ProcessResult {
5
5
  code: number;
6
6
  timedOut: boolean;
7
7
  truncated: boolean;
8
+ controlOutput?: string;
8
9
  }
9
10
  export interface BoundedProcessRunnerOptions {
10
11
  cwd: string;
@@ -18,6 +19,9 @@ export interface RunProcessOptions {
18
19
  signal?: AbortSignal;
19
20
  onOutput?: (output: string) => void | Promise<void>;
20
21
  env?: Readonly<Record<string, string>>;
22
+ controlOutputBytes?: number;
23
+ controlOutputFd?: number;
24
+ scriptInput?: string;
21
25
  }
22
26
  export declare function joinedProcessOutput(result: ProcessResult): string;
23
27
  export declare class BoundedProcessRunner {
@@ -87,15 +87,35 @@ export class BoundedProcessRunner {
87
87
  const sensitiveValues = sensitiveEnvironmentValues(process.env);
88
88
  const longestSensitiveValueBytes = sensitiveValues.reduce((longest, value) => Math.max(longest, Buffer.byteLength(value)), 0);
89
89
  const rawOutputLimit = this.options.maxOutputBytes + Math.max(3, longestSensitiveValueBytes);
90
+ const controlOutputFd = options.controlOutputBytes === undefined
91
+ ? undefined
92
+ : (options.controlOutputFd ?? 3);
93
+ const scriptInputFd = options.scriptInput === undefined ? undefined : 4;
94
+ if (controlOutputFd !== undefined &&
95
+ scriptInputFd !== undefined &&
96
+ controlOutputFd === scriptInputFd) {
97
+ reject(new Error('Process control output and script input FDs conflict'));
98
+ return;
99
+ }
100
+ const highestFd = Math.max(2, controlOutputFd ?? 2, scriptInputFd ?? 2);
101
+ const stdio = Array.from({ length: highestFd + 1 }, () => 'ignore');
102
+ stdio[1] = 'pipe';
103
+ stdio[2] = 'pipe';
104
+ if (controlOutputFd !== undefined)
105
+ stdio[controlOutputFd] = 'pipe';
106
+ if (scriptInputFd !== undefined)
107
+ stdio[scriptInputFd] = 'pipe';
90
108
  const child = spawn(options.command, options.args, {
91
109
  cwd: options.cwd ?? this.options.cwd,
92
110
  detached: process.platform !== 'win32',
93
111
  env: { ...sanitizeChildEnvironment(), ...options.env },
94
- stdio: ['ignore', 'pipe', 'pipe'],
112
+ stdio,
95
113
  });
96
114
  const chunks = { stdout: [], stderr: [] };
97
115
  const combined = [];
98
116
  const retainedBytes = { stdout: 0, stderr: 0, combined: 0 };
117
+ const controlChunks = [];
118
+ let controlBytes = 0;
99
119
  let outputBytes = 0;
100
120
  let timedOut = false;
101
121
  let settled = false;
@@ -136,8 +156,41 @@ export class BoundedProcessRunner {
136
156
  }
137
157
  updateLiveOutput();
138
158
  };
159
+ if (!child.stdout || !child.stderr) {
160
+ reject(new Error('Process output streams were not created'));
161
+ return;
162
+ }
139
163
  child.stdout.on('data', (chunk) => retain('stdout', chunk));
140
164
  child.stderr.on('data', (chunk) => retain('stderr', chunk));
165
+ const controlStream = controlOutputFd === undefined ? undefined : child.stdio[controlOutputFd];
166
+ if (controlStream) {
167
+ controlStream.on('data', (chunk) => {
168
+ const remaining = Math.max(0, (options.controlOutputBytes ?? 0) - controlBytes);
169
+ if (remaining <= 0)
170
+ return;
171
+ const retained = chunk.subarray(0, remaining);
172
+ controlChunks.push(retained);
173
+ controlBytes += retained.length;
174
+ });
175
+ child.once('exit', () => {
176
+ setImmediate(() => controlStream.destroy());
177
+ });
178
+ }
179
+ const scriptStream = scriptInputFd === undefined ? undefined : child.stdio[scriptInputFd];
180
+ if (scriptStream) {
181
+ scriptStream.on('error', () => {
182
+ // The child process result remains authoritative if it exits before
183
+ // consuming the complete script.
184
+ });
185
+ if (!('end' in scriptStream)) {
186
+ reject(new Error('Process script input stream is not writable'));
187
+ return;
188
+ }
189
+ scriptStream.end(options.scriptInput);
190
+ child.once('exit', () => {
191
+ setImmediate(() => scriptStream.destroy());
192
+ });
193
+ }
141
194
  const kill = () => {
142
195
  if (child.pid === undefined)
143
196
  return;
@@ -200,6 +253,11 @@ export class BoundedProcessRunner {
200
253
  code: code ?? 1,
201
254
  timedOut,
202
255
  truncated,
256
+ ...(options.controlOutputBytes === undefined
257
+ ? {}
258
+ : {
259
+ controlOutput: Buffer.concat(controlChunks).toString('utf8'),
260
+ }),
203
261
  });
204
262
  }, reject);
205
263
  });
@@ -21,6 +21,7 @@ export interface SandboxBackend {
21
21
  export interface SandboxCommandInput {
22
22
  command: string;
23
23
  dangerouslyDisableSandbox?: boolean;
24
+ executionCommand?: string;
24
25
  }
25
26
  export declare class ClaudeSandboxRuntime {
26
27
  private readonly backend;
@@ -136,7 +136,7 @@ export class ClaudeSandboxRuntime {
136
136
  throw new Error('Sandbox has not been initialized');
137
137
  if (!this.shouldUseSandbox(input))
138
138
  return input.command;
139
- return this.backend.wrapWithSandbox(input.command, options.shell, undefined, options.signal, {
139
+ return this.backend.wrapWithSandbox(input.executionCommand ?? input.command, options.shell, undefined, options.signal, {
140
140
  ...(options.commandId ? { commandId: options.commandId } : {}),
141
141
  commandText: input.command,
142
142
  });
@@ -26,6 +26,7 @@ export interface BashSandboxRuntime {
26
26
  wrapCommand(input: {
27
27
  command: string;
28
28
  dangerouslyDisableSandbox?: boolean;
29
+ executionCommand?: string;
29
30
  }, options?: {
30
31
  shell?: string;
31
32
  signal?: AbortSignal;
@@ -43,6 +44,7 @@ export declare class LocalToolRegistry implements ToolRegistry {
43
44
  private readonly maxOutputBytes;
44
45
  private readonly maxFileBytes;
45
46
  private readonly maxShellTimeoutMs;
47
+ private readonly maxSearchTimeoutMs;
46
48
  private readonly processRunner;
47
49
  private readonly enableReportFindings;
48
50
  private readonly environment;
@@ -50,11 +52,14 @@ export declare class LocalToolRegistry implements ToolRegistry {
50
52
  private readonly sandbox;
51
53
  private readonly homeDirectory;
52
54
  private readonly configRoot;
55
+ private readonly sessionCwds;
53
56
  private readonly protectedWriteReason;
57
+ private readonly mutationTargetExisted;
54
58
  constructor(options: LocalToolRegistryOptions);
55
59
  private assertProtectedWritePath;
56
60
  private assertProtectedBashCommand;
57
61
  private currentCwd;
62
+ private currentBashCwd;
58
63
  definitions(): readonly ModelToolDefinition[];
59
64
  schedulingPolicy(call: ModelToolCall): {
60
65
  concurrency: "concurrent";
@@ -78,7 +83,9 @@ export declare class LocalToolRegistry implements ToolRegistry {
78
83
  private workspacePath;
79
84
  private filePath;
80
85
  private resolvePath;
86
+ private pathExists;
81
87
  private assertStablePath;
88
+ private assertStableCreationParent;
82
89
  private globRoot;
83
90
  private wasSuccessfullyRead;
84
91
  private read;