praxis-agent 0.9.0 → 0.11.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 (57) hide show
  1. package/README.md +18 -4
  2. package/dist/application/session-service.d.ts +5 -2
  3. package/dist/application/session-service.js +23 -0
  4. package/dist/application/subagent-service.d.ts +4 -2
  5. package/dist/application/subagent-service.js +4 -0
  6. package/dist/cli/interactive.d.ts +4 -3
  7. package/dist/cli/interactive.js +760 -195
  8. package/dist/cli/tui/claude-command-inventory.d.ts +368 -0
  9. package/dist/cli/tui/claude-command-inventory.js +220 -0
  10. package/dist/cli/tui/claude-style.d.ts +12 -14
  11. package/dist/cli/tui/claude-style.js +45 -67
  12. package/dist/cli/tui/config-dashboard.d.ts +4 -8
  13. package/dist/cli/tui/config-dashboard.js +4 -18
  14. package/dist/cli/tui/config-settings.js +1 -1
  15. package/dist/cli/tui/conversation-export.d.ts +1 -1
  16. package/dist/cli/tui/conversation-export.js +2 -11
  17. package/dist/cli/tui/mcp-elicitation.d.ts +75 -0
  18. package/dist/cli/tui/mcp-elicitation.js +497 -0
  19. package/dist/cli/tui/open-url.d.ts +2 -0
  20. package/dist/cli/tui/open-url.js +13 -0
  21. package/dist/cli/tui/permission-settings.d.ts +6 -0
  22. package/dist/cli/tui/permission-settings.js +84 -0
  23. package/dist/cli/tui/runtime-interactions.d.ts +2 -0
  24. package/dist/cli/tui/runtime-interactions.js +44 -17
  25. package/dist/cli/tui/slash-commands.js +3 -28
  26. package/dist/cli/tui/tool-permission.d.ts +36 -0
  27. package/dist/cli/tui/tool-permission.js +386 -0
  28. package/dist/cli.d.ts +2 -2
  29. package/dist/cli.js +28 -13
  30. package/dist/core/runtime.d.ts +50 -2
  31. package/dist/core/runtime.js +30 -3
  32. package/dist/extensions/claude-extension-tools.d.ts +3 -2
  33. package/dist/extensions/claude-extension-tools.js +15 -5
  34. package/dist/extensions/claude-extensions.d.ts +1 -0
  35. package/dist/extensions/claude-extensions.js +10 -0
  36. package/dist/hooks/claude-hook-tools.js +1 -1
  37. package/dist/mcp/claude-mcp-tools.js +12 -6
  38. package/dist/permissions/bash-ast.d.ts +39 -0
  39. package/dist/permissions/bash-ast.js +1046 -0
  40. package/dist/permissions/bash-normalization.d.ts +5 -0
  41. package/dist/permissions/bash-normalization.js +241 -0
  42. package/dist/permissions/bash-path-safety.d.ts +28 -0
  43. package/dist/permissions/bash-path-safety.js +575 -0
  44. package/dist/permissions/claude-permission-resolver.d.ts +7 -0
  45. package/dist/permissions/claude-permission-resolver.js +351 -40
  46. package/dist/permissions/claude-shell-permission.d.ts +4 -0
  47. package/dist/permissions/claude-shell-permission.js +108 -0
  48. package/dist/permissions/permission-updates.d.ts +16 -0
  49. package/dist/permissions/permission-updates.js +320 -0
  50. package/dist/permissions/sed-safety.d.ts +9 -0
  51. package/dist/permissions/sed-safety.js +172 -0
  52. package/dist/permissions/shell-rule-matching.d.ts +14 -0
  53. package/dist/permissions/shell-rule-matching.js +76 -0
  54. package/dist/tools/claude-interactive-tools.d.ts +10 -1
  55. package/dist/tools/claude-interactive-tools.js +10 -4
  56. package/dist/tools/local-tools.js +22 -2
  57. package/package.json +3 -1
package/README.md CHANGED
@@ -82,11 +82,12 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
82
82
  slash palette, tabbed help and shortcut surfaces, searchable resume picker,
83
83
  restored active-branch conversation history, streaming and expandable
84
84
  thinking, grouped multi-file reads, globally expandable tool results,
85
- command-specific `/add-dir`, `/copy`, `/branch`, `/rename`, `/export`,
85
+ command-specific `/add-dir`, code-aware `/copy`, `/branch`, `/rename`, `/export`,
86
86
  provider-free read-only shared `/hooks`, provider-backed `/compact`, native
87
87
  `/rewind`, runtime `/cd`, transcript-free
88
88
  `/btw` side questions with background-Agent handoff, interactive
89
- `/background` terminal handoff, `/config`, `/usage`, `/mcp`, `/memory` shared
89
+ `/background` terminal handoff, unified `/status`/`/config`/`/usage` settings
90
+ tabs, `/mcp`, `/memory` shared
90
91
  instruction and auto-memory access, and live extension-reload controls,
91
92
  cursor/history composer, per-session model/effort/permission controls,
92
93
  context/status/skill/task dashboards, prompt stash and continuation shortcuts,
@@ -108,8 +109,21 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
108
109
  image, web, scheduled prompts, workflows, and worktrees.
109
110
  - **Permission boundary** — local allow/ask/deny rules, safe and bare modes,
110
111
  searchable scoped-rule creation/removal, local/project/user atomic settings
111
- writes, interactive workspace-directory add/remove controls, path confinement,
112
- credential redaction, and sanitized child processes.
112
+ writes, tool-specific Bash/PowerShell/file/notebook/WebFetch/Skill approval
113
+ dialogs, editable reusable shell and Skill rules, source-root-aware Claude
114
+ file-rule matching, atomic session permission updates, compound-shell rule
115
+ suggestions backed by a bounded Bash AST, source-shaped exact/prefix/wildcard
116
+ matching, wrapper and environment normalization, fail-closed Bash semantic
117
+ checks with control-flow-aware variable scopes, declaration and literal-only
118
+ arithmetic analysis, exact `cat` heredoc handling, argv-based
119
+ command/redirection path validation, full symlink-chain checks, dangerous-removal,
120
+ sensitive-file, and suspicious Windows-path gates, source-ordered strict sed
121
+ constraints, internal auto-memory/session/task path handling, compound `cd` +
122
+ Git protection, mode-ordered `acceptEdits` handling, live raw/resolved-path
123
+ grants for external directories,
124
+ safe-property Skill auto-allow, interactive
125
+ workspace-directory add/remove controls, path confinement, credential
126
+ redaction, and sanitized child processes.
113
127
  - **Durable local work** — resumable sessions, full-history forks, file
114
128
  checkpoints, tasks, foreground/background subagents, and top-level agents.
115
129
  - **Claude-compatible ecosystem** — shared instructions with recursive `@`
@@ -1,7 +1,7 @@
1
1
  import type { ClaudeConditionalRuleResolver } from '../compatibility/claude/context.js';
2
2
  import { type ClaudeFileResource, type ClaudeFileResourceConfig } from '../compatibility/claude/file-resources.js';
3
3
  import { type ClaudeDisplayTranscriptItem } from '../compatibility/claude/projection.js';
4
- import { type ModelDocument, type ModelImage, type ModelToolCall, type ModelProvider, type ModelUsage, type PermissionApproval, type PermissionResolver, type RuntimeEventSink, type ToolRegistry } from '../core/runtime.js';
4
+ 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';
5
5
  import { type BackgroundTaskSnapshot } from './background-task-runtime.js';
6
6
  import type { ModelPricingRegistry } from '../core/usage.js';
7
7
  import type { Compactor } from '../core/compaction.js';
@@ -24,7 +24,8 @@ export interface ClaudeSessionServiceOptions {
24
24
  tools?: ToolRegistry;
25
25
  permissions?: PermissionResolver;
26
26
  permissionResolverForMode?: (mode: AgentPermissionMode) => PermissionResolver;
27
- approveTool?: (call: ModelToolCall, originalCall?: ModelToolCall) => PermissionApproval | Promise<PermissionApproval>;
27
+ persistPermissionUpdates?: (updates: readonly PermissionUpdate[]) => void | Promise<void>;
28
+ approveTool?: (call: ModelToolCall, originalCall?: ModelToolCall, decision?: PermissionDecision) => PermissionApproval | Promise<PermissionApproval>;
28
29
  approveRecovery?: (call: ModelToolCall) => boolean | Promise<boolean>;
29
30
  contextAssembler?: ContextAssembler;
30
31
  conditionalRuleResolver?: Pick<ClaudeConditionalRuleResolver, 'resolve'>;
@@ -143,6 +144,7 @@ export declare class ClaudeSessionService {
143
144
  private readonly backgroundTasks;
144
145
  private readonly worktreeManager;
145
146
  private readonly sessionCwds;
147
+ private readonly sessionPermissionUpdates;
146
148
  private readonly hostedSubagents;
147
149
  private readonly hostedSubagentsByRegistry;
148
150
  private readonly backgroundNotificationWrites;
@@ -160,6 +162,7 @@ export declare class ClaudeSessionService {
160
162
  private requireMcp;
161
163
  taskSnapshots(sessionId: string): Promise<BackgroundTaskSnapshot>;
162
164
  stopTask(sessionId: string, taskId: string): Promise<void>;
165
+ private applyPermissionUpdates;
163
166
  close(): Promise<void>;
164
167
  createHostedToolRegistry(sessionId: string): ToolRegistry;
165
168
  run(prompt: string, signal?: AbortSignal, sessionId?: string, name?: string, images?: readonly ModelImage[], documents?: readonly ModelDocument[]): Promise<SessionRunResult>;
@@ -92,6 +92,7 @@ export class ClaudeSessionService {
92
92
  backgroundTasks;
93
93
  worktreeManager;
94
94
  sessionCwds = new Map();
95
+ sessionPermissionUpdates = new Map();
95
96
  hostedSubagents = new Set();
96
97
  hostedSubagentsByRegistry = new WeakMap();
97
98
  backgroundNotificationWrites = new Map();
@@ -159,6 +160,18 @@ export class ClaudeSessionService {
159
160
  stopTask(sessionId, taskId) {
160
161
  return this.backgroundTasks.stop(sessionId, taskId);
161
162
  }
163
+ async applyPermissionUpdates(sessionId, updates) {
164
+ if (updates.length === 0)
165
+ return;
166
+ await this.options.persistPermissionUpdates?.(updates);
167
+ const current = this.sessionPermissionUpdates.get(sessionId) ?? [];
168
+ current.push(...updates);
169
+ this.sessionPermissionUpdates.set(sessionId, current);
170
+ const mode = updates.findLast((update) => update.type === 'setMode')?.mode;
171
+ if (mode && this.options.interactiveTools) {
172
+ await this.options.interactiveTools.setMode(sessionId, mode);
173
+ }
174
+ }
162
175
  async close() {
163
176
  this.scheduledPrompts?.close();
164
177
  await Promise.all([...this.hostedSubagents].map((executor) => executor.close()));
@@ -247,6 +260,8 @@ export class ClaudeSessionService {
247
260
  ...(this.options.approveTool
248
261
  ? { approveTool: this.options.approveTool }
249
262
  : {}),
263
+ permissionUpdates: () => this.sessionPermissionUpdates.get(sessionId) ?? [],
264
+ onPermissionUpdates: (updates) => this.applyPermissionUpdates(sessionId, updates),
250
265
  ...(this.options.eventSink
251
266
  ? { eventSink: this.options.eventSink }
252
267
  : {}),
@@ -1455,6 +1470,8 @@ export class ClaudeSessionService {
1455
1470
  ...(this.options.approveTool
1456
1471
  ? { approveTool: this.options.approveTool }
1457
1472
  : {}),
1473
+ permissionUpdates: () => this.sessionPermissionUpdates.get(sessionId) ?? [],
1474
+ onPermissionUpdates: (updates) => this.applyPermissionUpdates(sessionId, updates),
1458
1475
  ...(this.options.eventSink
1459
1476
  ? { eventSink: this.options.eventSink }
1460
1477
  : {}),
@@ -1723,6 +1740,8 @@ export class ClaudeSessionService {
1723
1740
  toolResultDirectory,
1724
1741
  messages: projectClaudeModelMessages(snapshot.entries),
1725
1742
  observer,
1743
+ permissionUpdates: this.sessionPermissionUpdates.get(sessionId) ?? [],
1744
+ onPermissionUpdates: (updates) => this.applyPermissionUpdates(sessionId, updates),
1726
1745
  ...(signal ? { signal } : {}),
1727
1746
  ...(approveRecovery
1728
1747
  ? {
@@ -2087,6 +2106,8 @@ export class ClaudeSessionService {
2087
2106
  ...(this.options.approveTool
2088
2107
  ? { approveTool: this.options.approveTool }
2089
2108
  : {}),
2109
+ permissionUpdates: this.sessionPermissionUpdates.get(sessionId) ?? [],
2110
+ onPermissionUpdates: (updates) => this.applyPermissionUpdates(sessionId, updates),
2090
2111
  ...(signal ? { signal } : {}),
2091
2112
  });
2092
2113
  }
@@ -2215,6 +2236,8 @@ export class ClaudeSessionService {
2215
2236
  ...(this.options.approveTool
2216
2237
  ? { approveTool: this.options.approveTool }
2217
2238
  : {}),
2239
+ permissionUpdates: this.sessionPermissionUpdates.get(sessionId) ?? [],
2240
+ onPermissionUpdates: (updates) => this.applyPermissionUpdates(sessionId, updates),
2218
2241
  };
2219
2242
  const result = signal
2220
2243
  ? await runtime.run({ ...runtimeRequest, signal })
@@ -1,6 +1,6 @@
1
1
  import { type ClaudeSidechainPermissionMode } from '../compatibility/claude/sidechain.js';
2
2
  import { type ContextAssembler } from '../core/context.js';
3
- import { type ModelProvider, type ModelToolCall, type ModelToolDefinition, type ModelUsage, type PermissionResolver, type PermissionApproval, type RuntimeEventSink, type ToolExecutionContext, type ToolExecutionResult, type ToolRegistry } from '../core/runtime.js';
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
4
  import type { ClaudeExtensionCatalog } from '../extensions/claude-extensions.js';
5
5
  import type { ClaudeHookRunner } from '../hooks/claude-hooks.js';
6
6
  import { type BackgroundAgentSnapshot } from './background-agent-manager.js';
@@ -56,7 +56,9 @@ export interface ClaudeSubagentExecutorOptions {
56
56
  hooks?: ClaudeHookRunner;
57
57
  contextAssembler?: ContextAssembler;
58
58
  contextReserveTokens?: number;
59
- approveTool?: (call: ModelToolCall, originalCall?: ModelToolCall) => PermissionApproval | Promise<PermissionApproval>;
59
+ approveTool?: (call: ModelToolCall, originalCall?: ModelToolCall, decision?: PermissionDecision) => PermissionApproval | Promise<PermissionApproval>;
60
+ permissionUpdates?: () => readonly PermissionUpdate[];
61
+ onPermissionUpdates?: (updates: readonly PermissionUpdate[]) => void | Promise<void>;
60
62
  eventSink?: RuntimeEventSink;
61
63
  maxDepth?: number;
62
64
  maxCalls?: number;
@@ -1139,6 +1139,10 @@ export class ClaudeSubagentExecutor {
1139
1139
  ...(this.options.approveTool
1140
1140
  ? { approveTool: this.options.approveTool }
1141
1141
  : {}),
1142
+ permissionUpdates: this.options.permissionUpdates?.() ?? [],
1143
+ ...(this.options.onPermissionUpdates
1144
+ ? { onPermissionUpdates: this.options.onPermissionUpdates }
1145
+ : {}),
1142
1146
  ...(this.options.hooks || this.options.backgroundTaskNotifications
1143
1147
  ? {
1144
1148
  onStop: async (text) => {
@@ -1,5 +1,5 @@
1
1
  import type { ForkResult, ManualCompactResult, ManualCompactSelection, RewindPoint, SessionForkCheckpoint, SessionRunResult, SessionSummary, SideQuestionForkResult, SideQuestionResult } from '../application/session-service.js';
2
- import type { ModelImage, ModelToolCall, RuntimeEventSink } from '../core/runtime.js';
2
+ import type { ModelImage, ModelToolCall, PermissionApproval, PermissionDecision, RuntimeEventSink } from '../core/runtime.js';
3
3
  import type { CliElicitationRequest, CliElicitationResult, CliRuntimeInfo } from './protocol.js';
4
4
  import type { ClaudeInteractiveToolCallbacks } from '../tools/claude-interactive-tools.js';
5
5
  import { type ClaudePermissionMode } from '../permissions/claude-permission-resolver.js';
@@ -71,7 +71,7 @@ export interface InteractiveServiceFactory {
71
71
  requireProvider: boolean;
72
72
  hooksOnly?: boolean;
73
73
  approveRecovery?: (call: ModelToolCall) => boolean | Promise<boolean>;
74
- approveTool?: (call: ModelToolCall) => boolean | Promise<boolean>;
74
+ approveTool?: (call: ModelToolCall, originalCall?: ModelToolCall, decision?: PermissionDecision) => PermissionApproval | Promise<PermissionApproval>;
75
75
  onElicitation?: (request: CliElicitationRequest) => Promise<CliElicitationResult>;
76
76
  askUser?: ClaudeInteractiveToolCallbacks['askUser'];
77
77
  approvePlan?: ClaudeInteractiveToolCallbacks['approvePlan'];
@@ -172,8 +172,9 @@ interface InteractiveAppProps {
172
172
  runtimeSettings?: PraxisRuntimeSettings;
173
173
  runtimeSettingsTarget?: ConfigSettingsTarget;
174
174
  notificationWriter?: TuiNotificationWriter;
175
+ elicitationUrlOpener?: (url: string) => void | Promise<void>;
175
176
  }
176
- export declare function InteractiveApp({ factory, initialSessions, initialPrompt, initialHistory, signal, onCancel, onTurnChange, onCleanup, onRendererChange, onBackground, axScreenReader, allowNewSession, resume, display, terminalWidth, slashCommands, agents, allowDangerouslySkipPermissions, additionalDirectories, diffLoader, fileLoader, externalEditor, keybindingsConfigRoot, keybindingsFile, keybindingsLoader, keybindingsEditor, memoryFilesLoader, memoryEditor, memoryFolderOpener, suspendProcess, clipboardReader, clipboardWriter, sideQuestionClipboardWriter, exportWriter, permissionRuleStore, recentlyDeniedStore: suppliedRecentlyDeniedStore, terminalSetup: terminalSetupOverride, themeStore, initialThemeSettings, initialThemeLoadError, workspaceDirectoryResolver, workspaceDirectoryCompleter, runtimeSettings: suppliedRuntimeSettings, runtimeSettingsTarget, notificationWriter, }: InteractiveAppProps): import("react").JSX.Element;
177
+ export declare function InteractiveApp({ factory, initialSessions, initialPrompt, initialHistory, signal, onCancel, onTurnChange, onCleanup, onRendererChange, onBackground, axScreenReader, allowNewSession, resume, display, terminalWidth, slashCommands, agents, allowDangerouslySkipPermissions, additionalDirectories, diffLoader, fileLoader, externalEditor, keybindingsConfigRoot, keybindingsFile, keybindingsLoader, keybindingsEditor, memoryFilesLoader, memoryEditor, memoryFolderOpener, suspendProcess, clipboardReader, clipboardWriter, sideQuestionClipboardWriter, exportWriter, permissionRuleStore, recentlyDeniedStore: suppliedRecentlyDeniedStore, terminalSetup: terminalSetupOverride, themeStore, initialThemeSettings, initialThemeLoadError, workspaceDirectoryResolver, workspaceDirectoryCompleter, runtimeSettings: suppliedRuntimeSettings, runtimeSettingsTarget, notificationWriter, elicitationUrlOpener, }: InteractiveAppProps): import("react").JSX.Element;
177
178
  export declare function runInteractive(options: {
178
179
  factory: InteractiveServiceFactory;
179
180
  initialPrompt?: string;