praxis-agent 0.1.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.
- package/LICENSE +21 -0
- package/README.md +534 -0
- package/dist/application/background-agent-manager.d.ts +66 -0
- package/dist/application/background-agent-manager.d.ts.map +1 -0
- package/dist/application/background-agent-manager.js +362 -0
- package/dist/application/background-agent-manager.js.map +1 -0
- package/dist/application/background-bash-manager.d.ts +54 -0
- package/dist/application/background-bash-manager.d.ts.map +1 -0
- package/dist/application/background-bash-manager.js +354 -0
- package/dist/application/background-bash-manager.js.map +1 -0
- package/dist/application/background-task-id.d.ts +5 -0
- package/dist/application/background-task-id.d.ts.map +1 -0
- package/dist/application/background-task-id.js +25 -0
- package/dist/application/background-task-id.js.map +1 -0
- package/dist/application/claude-project-purge.d.ts +50 -0
- package/dist/application/claude-project-purge.d.ts.map +1 -0
- package/dist/application/claude-project-purge.js +459 -0
- package/dist/application/claude-project-purge.js.map +1 -0
- package/dist/application/managed-worktree.d.ts +15 -0
- package/dist/application/managed-worktree.d.ts.map +1 -0
- package/dist/application/managed-worktree.js +127 -0
- package/dist/application/managed-worktree.js.map +1 -0
- package/dist/application/model-compactor.d.ts +8 -0
- package/dist/application/model-compactor.d.ts.map +1 -0
- package/dist/application/model-compactor.js +70 -0
- package/dist/application/model-compactor.js.map +1 -0
- package/dist/application/scheduled-prompt-manager.d.ts +69 -0
- package/dist/application/scheduled-prompt-manager.d.ts.map +1 -0
- package/dist/application/scheduled-prompt-manager.js +387 -0
- package/dist/application/scheduled-prompt-manager.js.map +1 -0
- package/dist/application/session-service.d.ts +142 -0
- package/dist/application/session-service.d.ts.map +1 -0
- package/dist/application/session-service.js +1690 -0
- package/dist/application/session-service.js.map +1 -0
- package/dist/application/session-worktree.d.ts +55 -0
- package/dist/application/session-worktree.d.ts.map +1 -0
- package/dist/application/session-worktree.js +311 -0
- package/dist/application/session-worktree.js.map +1 -0
- package/dist/application/subagent-service.d.ts +98 -0
- package/dist/application/subagent-service.d.ts.map +1 -0
- package/dist/application/subagent-service.js +1305 -0
- package/dist/application/subagent-service.js.map +1 -0
- package/dist/application/tool-use-summary.d.ts +8 -0
- package/dist/application/tool-use-summary.d.ts.map +1 -0
- package/dist/application/tool-use-summary.js +53 -0
- package/dist/application/tool-use-summary.js.map +1 -0
- package/dist/application/top-level-agent-manager.d.ts +58 -0
- package/dist/application/top-level-agent-manager.d.ts.map +1 -0
- package/dist/application/top-level-agent-manager.js +907 -0
- package/dist/application/top-level-agent-manager.js.map +1 -0
- package/dist/application/workflow-manager.d.ts +61 -0
- package/dist/application/workflow-manager.d.ts.map +1 -0
- package/dist/application/workflow-manager.js +582 -0
- package/dist/application/workflow-manager.js.map +1 -0
- package/dist/application/workflow-meta.d.ts +18 -0
- package/dist/application/workflow-meta.d.ts.map +1 -0
- package/dist/application/workflow-meta.js +146 -0
- package/dist/application/workflow-meta.js.map +1 -0
- package/dist/application/workflow-runtime.d.ts +29 -0
- package/dist/application/workflow-runtime.d.ts.map +1 -0
- package/dist/application/workflow-runtime.js +176 -0
- package/dist/application/workflow-runtime.js.map +1 -0
- package/dist/application/workflow-worktree.d.ts +9 -0
- package/dist/application/workflow-worktree.d.ts.map +1 -0
- package/dist/application/workflow-worktree.js +11 -0
- package/dist/application/workflow-worktree.js.map +1 -0
- package/dist/cli/agents-dashboard.d.ts +37 -0
- package/dist/cli/agents-dashboard.d.ts.map +1 -0
- package/dist/cli/agents-dashboard.js +408 -0
- package/dist/cli/agents-dashboard.js.map +1 -0
- package/dist/cli/controls.d.ts +15 -0
- package/dist/cli/controls.d.ts.map +1 -0
- package/dist/cli/controls.js +219 -0
- package/dist/cli/controls.js.map +1 -0
- package/dist/cli/debug.d.ts +13 -0
- package/dist/cli/debug.d.ts.map +1 -0
- package/dist/cli/debug.js +37 -0
- package/dist/cli/debug.js.map +1 -0
- package/dist/cli/interactive.d.ts +63 -0
- package/dist/cli/interactive.d.ts.map +1 -0
- package/dist/cli/interactive.js +600 -0
- package/dist/cli/interactive.js.map +1 -0
- package/dist/cli/protocol.d.ts +251 -0
- package/dist/cli/protocol.d.ts.map +1 -0
- package/dist/cli/protocol.js +2146 -0
- package/dist/cli/protocol.js.map +1 -0
- package/dist/cli.d.ts +116 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +3901 -0
- package/dist/cli.js.map +1 -0
- package/dist/compatibility/claude/compaction.d.ts +19 -0
- package/dist/compatibility/claude/compaction.d.ts.map +1 -0
- package/dist/compatibility/claude/compaction.js +82 -0
- package/dist/compatibility/claude/compaction.js.map +1 -0
- package/dist/compatibility/claude/context.d.ts +24 -0
- package/dist/compatibility/claude/context.d.ts.map +1 -0
- package/dist/compatibility/claude/context.js +92 -0
- package/dist/compatibility/claude/context.js.map +1 -0
- package/dist/compatibility/claude/dynamic-context.d.ts +20 -0
- package/dist/compatibility/claude/dynamic-context.d.ts.map +1 -0
- package/dist/compatibility/claude/dynamic-context.js +168 -0
- package/dist/compatibility/claude/dynamic-context.js.map +1 -0
- package/dist/compatibility/claude/file-history.d.ts +14 -0
- package/dist/compatibility/claude/file-history.d.ts.map +1 -0
- package/dist/compatibility/claude/file-history.js +340 -0
- package/dist/compatibility/claude/file-history.js.map +1 -0
- package/dist/compatibility/claude/file-resources.d.ts +28 -0
- package/dist/compatibility/claude/file-resources.d.ts.map +1 -0
- package/dist/compatibility/claude/file-resources.js +168 -0
- package/dist/compatibility/claude/file-resources.js.map +1 -0
- package/dist/compatibility/claude/fork.d.ts +9 -0
- package/dist/compatibility/claude/fork.d.ts.map +1 -0
- package/dist/compatibility/claude/fork.js +225 -0
- package/dist/compatibility/claude/fork.js.map +1 -0
- package/dist/compatibility/claude/history.d.ts +4 -0
- package/dist/compatibility/claude/history.d.ts.map +1 -0
- package/dist/compatibility/claude/history.js +86 -0
- package/dist/compatibility/claude/history.js.map +1 -0
- package/dist/compatibility/claude/ownership.d.ts +87 -0
- package/dist/compatibility/claude/ownership.d.ts.map +1 -0
- package/dist/compatibility/claude/ownership.js +100 -0
- package/dist/compatibility/claude/ownership.js.map +1 -0
- package/dist/compatibility/claude/paths.d.ts +17 -0
- package/dist/compatibility/claude/paths.d.ts.map +1 -0
- package/dist/compatibility/claude/paths.js +54 -0
- package/dist/compatibility/claude/paths.js.map +1 -0
- package/dist/compatibility/claude/pr-links.d.ts +25 -0
- package/dist/compatibility/claude/pr-links.d.ts.map +1 -0
- package/dist/compatibility/claude/pr-links.js +80 -0
- package/dist/compatibility/claude/pr-links.js.map +1 -0
- package/dist/compatibility/claude/projection.d.ts +11 -0
- package/dist/compatibility/claude/projection.d.ts.map +1 -0
- package/dist/compatibility/claude/projection.js +325 -0
- package/dist/compatibility/claude/projection.js.map +1 -0
- package/dist/compatibility/claude/schema.d.ts +18 -0
- package/dist/compatibility/claude/schema.d.ts.map +1 -0
- package/dist/compatibility/claude/schema.js +983 -0
- package/dist/compatibility/claude/schema.js.map +1 -0
- package/dist/compatibility/claude/shared-resources.d.ts +51 -0
- package/dist/compatibility/claude/shared-resources.d.ts.map +1 -0
- package/dist/compatibility/claude/shared-resources.js +382 -0
- package/dist/compatibility/claude/shared-resources.js.map +1 -0
- package/dist/compatibility/claude/sidechain.d.ts +50 -0
- package/dist/compatibility/claude/sidechain.d.ts.map +1 -0
- package/dist/compatibility/claude/sidechain.js +90 -0
- package/dist/compatibility/claude/sidechain.js.map +1 -0
- package/dist/compatibility/claude/tool-links.d.ts +13 -0
- package/dist/compatibility/claude/tool-links.d.ts.map +1 -0
- package/dist/compatibility/claude/tool-links.js +61 -0
- package/dist/compatibility/claude/tool-links.js.map +1 -0
- package/dist/compatibility/claude/translation.d.ts +71 -0
- package/dist/compatibility/claude/translation.d.ts.map +1 -0
- package/dist/compatibility/claude/translation.js +443 -0
- package/dist/compatibility/claude/translation.js.map +1 -0
- package/dist/compatibility/claude/workflow-replay.d.ts +10 -0
- package/dist/compatibility/claude/workflow-replay.d.ts.map +1 -0
- package/dist/compatibility/claude/workflow-replay.js +66 -0
- package/dist/compatibility/claude/workflow-replay.js.map +1 -0
- package/dist/compatibility/claude/workflow.d.ts +33 -0
- package/dist/compatibility/claude/workflow.d.ts.map +1 -0
- package/dist/compatibility/claude/workflow.js +76 -0
- package/dist/compatibility/claude/workflow.js.map +1 -0
- package/dist/core/compaction.d.ts +16 -0
- package/dist/core/compaction.d.ts.map +1 -0
- package/dist/core/compaction.js +2 -0
- package/dist/core/compaction.js.map +1 -0
- package/dist/core/context-budget.d.ts +28 -0
- package/dist/core/context-budget.d.ts.map +1 -0
- package/dist/core/context-budget.js +108 -0
- package/dist/core/context-budget.js.map +1 -0
- package/dist/core/context.d.ts +16 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +13 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/runtime.d.ts +394 -0
- package/dist/core/runtime.d.ts.map +1 -0
- package/dist/core/runtime.js +507 -0
- package/dist/core/runtime.js.map +1 -0
- package/dist/core/usage.d.ts +26 -0
- package/dist/core/usage.d.ts.map +1 -0
- package/dist/core/usage.js +133 -0
- package/dist/core/usage.js.map +1 -0
- package/dist/extensions/claude-extension-tools.d.ts +16 -0
- package/dist/extensions/claude-extension-tools.d.ts.map +1 -0
- package/dist/extensions/claude-extension-tools.js +78 -0
- package/dist/extensions/claude-extension-tools.js.map +1 -0
- package/dist/extensions/claude-extensions.d.ts +41 -0
- package/dist/extensions/claude-extensions.d.ts.map +1 -0
- package/dist/extensions/claude-extensions.js +270 -0
- package/dist/extensions/claude-extensions.js.map +1 -0
- package/dist/hooks/claude-hook-tools.d.ts +26 -0
- package/dist/hooks/claude-hook-tools.d.ts.map +1 -0
- package/dist/hooks/claude-hook-tools.js +129 -0
- package/dist/hooks/claude-hook-tools.js.map +1 -0
- package/dist/hooks/claude-hooks.d.ts +87 -0
- package/dist/hooks/claude-hooks.d.ts.map +1 -0
- package/dist/hooks/claude-hooks.js +411 -0
- package/dist/hooks/claude-hooks.js.map +1 -0
- package/dist/maintenance/doctor.d.ts +33 -0
- package/dist/maintenance/doctor.d.ts.map +1 -0
- package/dist/maintenance/doctor.js +391 -0
- package/dist/maintenance/doctor.js.map +1 -0
- package/dist/maintenance/self-update.d.ts +31 -0
- package/dist/maintenance/self-update.d.ts.map +1 -0
- package/dist/maintenance/self-update.js +81 -0
- package/dist/maintenance/self-update.js.map +1 -0
- package/dist/mcp/claude-mcp-management.d.ts +28 -0
- package/dist/mcp/claude-mcp-management.d.ts.map +1 -0
- package/dist/mcp/claude-mcp-management.js +169 -0
- package/dist/mcp/claude-mcp-management.js.map +1 -0
- package/dist/mcp/claude-mcp-oauth.d.ts +113 -0
- package/dist/mcp/claude-mcp-oauth.d.ts.map +1 -0
- package/dist/mcp/claude-mcp-oauth.js +1053 -0
- package/dist/mcp/claude-mcp-oauth.js.map +1 -0
- package/dist/mcp/claude-mcp-tools.d.ts +98 -0
- package/dist/mcp/claude-mcp-tools.d.ts.map +1 -0
- package/dist/mcp/claude-mcp-tools.js +1246 -0
- package/dist/mcp/claude-mcp-tools.js.map +1 -0
- package/dist/mcp/praxis-mcp-server.d.ts +35 -0
- package/dist/mcp/praxis-mcp-server.d.ts.map +1 -0
- package/dist/mcp/praxis-mcp-server.js +520 -0
- package/dist/mcp/praxis-mcp-server.js.map +1 -0
- package/dist/permissions/claude-auto-classifier.d.ts +20 -0
- package/dist/permissions/claude-auto-classifier.d.ts.map +1 -0
- package/dist/permissions/claude-auto-classifier.js +140 -0
- package/dist/permissions/claude-auto-classifier.js.map +1 -0
- package/dist/permissions/claude-permission-resolver.d.ts +30 -0
- package/dist/permissions/claude-permission-resolver.d.ts.map +1 -0
- package/dist/permissions/claude-permission-resolver.js +334 -0
- package/dist/permissions/claude-permission-resolver.js.map +1 -0
- package/dist/persistence/claude-job-store.d.ts +68 -0
- package/dist/persistence/claude-job-store.d.ts.map +1 -0
- package/dist/persistence/claude-job-store.js +311 -0
- package/dist/persistence/claude-job-store.js.map +1 -0
- package/dist/persistence/claude-scheduled-task-store.d.ts +29 -0
- package/dist/persistence/claude-scheduled-task-store.d.ts.map +1 -0
- package/dist/persistence/claude-scheduled-task-store.js +163 -0
- package/dist/persistence/claude-scheduled-task-store.js.map +1 -0
- package/dist/persistence/claude-sidechain-store.d.ts +18 -0
- package/dist/persistence/claude-sidechain-store.d.ts.map +1 -0
- package/dist/persistence/claude-sidechain-store.js +107 -0
- package/dist/persistence/claude-sidechain-store.js.map +1 -0
- package/dist/persistence/claude-task-store.d.ts +72 -0
- package/dist/persistence/claude-task-store.d.ts.map +1 -0
- package/dist/persistence/claude-task-store.js +440 -0
- package/dist/persistence/claude-task-store.js.map +1 -0
- package/dist/persistence/claude-transcript-store.d.ts +85 -0
- package/dist/persistence/claude-transcript-store.d.ts.map +1 -0
- package/dist/persistence/claude-transcript-store.js +385 -0
- package/dist/persistence/claude-transcript-store.js.map +1 -0
- package/dist/persistence/claude-workflow-store.d.ts +41 -0
- package/dist/persistence/claude-workflow-store.d.ts.map +1 -0
- package/dist/persistence/claude-workflow-store.js +263 -0
- package/dist/persistence/claude-workflow-store.js.map +1 -0
- package/dist/persistence/in-memory-sidechain-store.d.ts +12 -0
- package/dist/persistence/in-memory-sidechain-store.d.ts.map +1 -0
- package/dist/persistence/in-memory-sidechain-store.js +30 -0
- package/dist/persistence/in-memory-sidechain-store.js.map +1 -0
- package/dist/persistence/in-memory-transcript-store.d.ts +15 -0
- package/dist/persistence/in-memory-transcript-store.d.ts.map +1 -0
- package/dist/persistence/in-memory-transcript-store.js +95 -0
- package/dist/persistence/in-memory-transcript-store.js.map +1 -0
- package/dist/platform/atomic-write.d.ts +5 -0
- package/dist/platform/atomic-write.d.ts.map +1 -0
- package/dist/platform/atomic-write.js +25 -0
- package/dist/platform/atomic-write.js.map +1 -0
- package/dist/platform/bounded-process-runner.d.ts +28 -0
- package/dist/platform/bounded-process-runner.d.ts.map +1 -0
- package/dist/platform/bounded-process-runner.js +209 -0
- package/dist/platform/bounded-process-runner.js.map +1 -0
- package/dist/platform/claude-version.d.ts +5 -0
- package/dist/platform/claude-version.d.ts.map +1 -0
- package/dist/platform/claude-version.js +17 -0
- package/dist/platform/claude-version.js.map +1 -0
- package/dist/platform/command-shell.d.ts +3 -0
- package/dist/platform/command-shell.d.ts.map +1 -0
- package/dist/platform/command-shell.js +15 -0
- package/dist/platform/command-shell.js.map +1 -0
- package/dist/platform/exclusive-file-lease.d.ts +15 -0
- package/dist/platform/exclusive-file-lease.d.ts.map +1 -0
- package/dist/platform/exclusive-file-lease.js +265 -0
- package/dist/platform/exclusive-file-lease.js.map +1 -0
- package/dist/platform/sensitive-data.d.ts +8 -0
- package/dist/platform/sensitive-data.d.ts.map +1 -0
- package/dist/platform/sensitive-data.js +138 -0
- package/dist/platform/sensitive-data.js.map +1 -0
- package/dist/platform/tmux-worktree.d.ts +30 -0
- package/dist/platform/tmux-worktree.d.ts.map +1 -0
- package/dist/platform/tmux-worktree.js +138 -0
- package/dist/platform/tmux-worktree.js.map +1 -0
- package/dist/plugins/claude-plugin-eval-discovery.d.ts +20 -0
- package/dist/plugins/claude-plugin-eval-discovery.d.ts.map +1 -0
- package/dist/plugins/claude-plugin-eval-discovery.js +128 -0
- package/dist/plugins/claude-plugin-eval-discovery.js.map +1 -0
- package/dist/plugins/claude-plugin-eval-graders.d.ts +49 -0
- package/dist/plugins/claude-plugin-eval-graders.d.ts.map +1 -0
- package/dist/plugins/claude-plugin-eval-graders.js +137 -0
- package/dist/plugins/claude-plugin-eval-graders.js.map +1 -0
- package/dist/plugins/claude-plugin-eval-runner.d.ts +50 -0
- package/dist/plugins/claude-plugin-eval-runner.d.ts.map +1 -0
- package/dist/plugins/claude-plugin-eval-runner.js +189 -0
- package/dist/plugins/claude-plugin-eval-runner.js.map +1 -0
- package/dist/plugins/claude-plugin-eval-schema.d.ts +80 -0
- package/dist/plugins/claude-plugin-eval-schema.d.ts.map +1 -0
- package/dist/plugins/claude-plugin-eval-schema.js +436 -0
- package/dist/plugins/claude-plugin-eval-schema.js.map +1 -0
- package/dist/plugins/claude-plugin-eval.d.ts +66 -0
- package/dist/plugins/claude-plugin-eval.d.ts.map +1 -0
- package/dist/plugins/claude-plugin-eval.js +438 -0
- package/dist/plugins/claude-plugin-eval.js.map +1 -0
- package/dist/plugins/claude-plugin-maintenance.d.ts +39 -0
- package/dist/plugins/claude-plugin-maintenance.d.ts.map +1 -0
- package/dist/plugins/claude-plugin-maintenance.js +336 -0
- package/dist/plugins/claude-plugin-maintenance.js.map +1 -0
- package/dist/plugins/claude-plugin-marketplace.d.ts +97 -0
- package/dist/plugins/claude-plugin-marketplace.d.ts.map +1 -0
- package/dist/plugins/claude-plugin-marketplace.js +1433 -0
- package/dist/plugins/claude-plugin-marketplace.js.map +1 -0
- package/dist/plugins/claude-plugin-mcpb.d.ts +71 -0
- package/dist/plugins/claude-plugin-mcpb.d.ts.map +1 -0
- package/dist/plugins/claude-plugin-mcpb.js +885 -0
- package/dist/plugins/claude-plugin-mcpb.js.map +1 -0
- package/dist/plugins/claude-plugin-runtime.d.ts +120 -0
- package/dist/plugins/claude-plugin-runtime.d.ts.map +1 -0
- package/dist/plugins/claude-plugin-runtime.js +1263 -0
- package/dist/plugins/claude-plugin-runtime.js.map +1 -0
- package/dist/providers/anthropic-compatible.d.ts +35 -0
- package/dist/providers/anthropic-compatible.d.ts.map +1 -0
- package/dist/providers/anthropic-compatible.js +733 -0
- package/dist/providers/anthropic-compatible.js.map +1 -0
- package/dist/providers/environment.d.ts +14 -0
- package/dist/providers/environment.d.ts.map +1 -0
- package/dist/providers/environment.js +67 -0
- package/dist/providers/environment.js.map +1 -0
- package/dist/providers/fallback-provider.d.ts +17 -0
- package/dist/providers/fallback-provider.d.ts.map +1 -0
- package/dist/providers/fallback-provider.js +70 -0
- package/dist/providers/fallback-provider.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +30 -0
- package/dist/providers/openai-compatible.d.ts.map +1 -0
- package/dist/providers/openai-compatible.js +409 -0
- package/dist/providers/openai-compatible.js.map +1 -0
- package/dist/tools/claude-interactive-tools.d.ts +58 -0
- package/dist/tools/claude-interactive-tools.d.ts.map +1 -0
- package/dist/tools/claude-interactive-tools.js +390 -0
- package/dist/tools/claude-interactive-tools.js.map +1 -0
- package/dist/tools/claude-lsp-formatters.d.ts +2 -0
- package/dist/tools/claude-lsp-formatters.d.ts.map +1 -0
- package/dist/tools/claude-lsp-formatters.js +291 -0
- package/dist/tools/claude-lsp-formatters.js.map +1 -0
- package/dist/tools/claude-lsp-tool.d.ts +22 -0
- package/dist/tools/claude-lsp-tool.d.ts.map +1 -0
- package/dist/tools/claude-lsp-tool.js +744 -0
- package/dist/tools/claude-lsp-tool.js.map +1 -0
- package/dist/tools/claude-scheduled-tools.d.ts +20 -0
- package/dist/tools/claude-scheduled-tools.d.ts.map +1 -0
- package/dist/tools/claude-scheduled-tools.js +344 -0
- package/dist/tools/claude-scheduled-tools.js.map +1 -0
- package/dist/tools/claude-task-tools.d.ts +30 -0
- package/dist/tools/claude-task-tools.d.ts.map +1 -0
- package/dist/tools/claude-task-tools.js +503 -0
- package/dist/tools/claude-task-tools.js.map +1 -0
- package/dist/tools/claude-user-message.d.ts +16 -0
- package/dist/tools/claude-user-message.d.ts.map +1 -0
- package/dist/tools/claude-user-message.js +98 -0
- package/dist/tools/claude-user-message.js.map +1 -0
- package/dist/tools/claude-workflow-tools.d.ts +27 -0
- package/dist/tools/claude-workflow-tools.d.ts.map +1 -0
- package/dist/tools/claude-workflow-tools.js +257 -0
- package/dist/tools/claude-workflow-tools.js.map +1 -0
- package/dist/tools/claude-worktree-tools.d.ts +17 -0
- package/dist/tools/claude-worktree-tools.d.ts.map +1 -0
- package/dist/tools/claude-worktree-tools.js +135 -0
- package/dist/tools/claude-worktree-tools.js.map +1 -0
- package/dist/tools/filtered-tool-registry.d.ts +16 -0
- package/dist/tools/filtered-tool-registry.d.ts.map +1 -0
- package/dist/tools/filtered-tool-registry.js +51 -0
- package/dist/tools/filtered-tool-registry.js.map +1 -0
- package/dist/tools/glob.d.ts +9 -0
- package/dist/tools/glob.d.ts.map +1 -0
- package/dist/tools/glob.js +88 -0
- package/dist/tools/glob.js.map +1 -0
- package/dist/tools/local-tools.d.ts +47 -0
- package/dist/tools/local-tools.d.ts.map +1 -0
- package/dist/tools/local-tools.js +1082 -0
- package/dist/tools/local-tools.js.map +1 -0
- package/dist/tools/notebook.d.ts +22 -0
- package/dist/tools/notebook.d.ts.map +1 -0
- package/dist/tools/notebook.js +154 -0
- package/dist/tools/notebook.js.map +1 -0
- package/dist/tools/web.d.ts +48 -0
- package/dist/tools/web.d.ts.map +1 -0
- package/dist/tools/web.js +726 -0
- package/dist/tools/web.js.map +1 -0
- package/package.json +145 -0
|
@@ -0,0 +1,1690 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { lstat, readdir, stat } from 'node:fs/promises';
|
|
3
|
+
import { basename, extname, isAbsolute, join, relative } from 'node:path';
|
|
4
|
+
import { createClaudeCompactEntries, formatClaudeCompactSummary, getCumulativeDroppedTokens, } from '../compatibility/claude/compaction.js';
|
|
5
|
+
import { isClaudeSessionId, resolveClaudePaths, resolveClaudeScheduledTaskFile, } from '../compatibility/claude/paths.js';
|
|
6
|
+
import { downloadClaudeFileResources, } from '../compatibility/claude/file-resources.js';
|
|
7
|
+
import { createClaudeNativeFork } from '../compatibility/claude/fork.js';
|
|
8
|
+
import { selectClaudeTranscriptAtMessage } from '../compatibility/claude/history.js';
|
|
9
|
+
import { ClaudeFileHistory } from '../compatibility/claude/file-history.js';
|
|
10
|
+
import { getClaudePrLink } from '../compatibility/claude/pr-links.js';
|
|
11
|
+
import { getClaudeAgentSetting, getClaudeLastPrompt, projectClaudeModelMessages, } from '../compatibility/claude/projection.js';
|
|
12
|
+
import { selectClaudeSchemaAdapter, } from '../compatibility/claude/schema.js';
|
|
13
|
+
import { findUnresolvedClaudeToolCalls } from '../compatibility/claude/tool-links.js';
|
|
14
|
+
import { createClaudeAgentSettingEntry, createClaudeHookAttachmentEntries, createClaudeLastPromptEntry, createClaudeRuleAttachmentEntry, translateProviderEvents, } from '../compatibility/claude/translation.js';
|
|
15
|
+
import { AgentRunCancelledError, AgentRuntime, } from '../core/runtime.js';
|
|
16
|
+
import { usageCostUsd } from '../core/usage.js';
|
|
17
|
+
import { ContextBudget } from '../core/context-budget.js';
|
|
18
|
+
import { injectFirstUserMessageContext, } from '../core/context.js';
|
|
19
|
+
import { ClaudeHookToolCoordinator } from '../hooks/claude-hook-tools.js';
|
|
20
|
+
import { ClaudeTranscriptStore, } from '../persistence/claude-transcript-store.js';
|
|
21
|
+
import { InMemoryTranscriptStore } from '../persistence/in-memory-transcript-store.js';
|
|
22
|
+
import { ModelCompactor } from './model-compactor.js';
|
|
23
|
+
import { ClaudeSubagentExecutor, StructuredOutputRegistry, } from './subagent-service.js';
|
|
24
|
+
import { ScheduledPromptManager } from './scheduled-prompt-manager.js';
|
|
25
|
+
import { ClaudeScheduledToolRegistry } from '../tools/claude-scheduled-tools.js';
|
|
26
|
+
import { ClaudeTaskToolRegistry } from '../tools/claude-task-tools.js';
|
|
27
|
+
import { ClaudeWorkflowToolRegistry } from '../tools/claude-workflow-tools.js';
|
|
28
|
+
import { WorkflowManager } from './workflow-manager.js';
|
|
29
|
+
import { SessionWorktreeManager } from './session-worktree.js';
|
|
30
|
+
import { ClaudeWorktreeToolRegistry } from '../tools/claude-worktree-tools.js';
|
|
31
|
+
import { generateToolUseSummary } from './tool-use-summary.js';
|
|
32
|
+
import { ClaudeUserMessageToolRegistry, CLAUDE_USER_MESSAGE_PROMPT, } from '../tools/claude-user-message.js';
|
|
33
|
+
const emptyToolRegistry = {
|
|
34
|
+
definitions: () => [],
|
|
35
|
+
prepare: async (call) => call,
|
|
36
|
+
execute: async () => ({ content: '', isError: false }),
|
|
37
|
+
};
|
|
38
|
+
function mergeUsage(left, right) {
|
|
39
|
+
const cacheReadInputTokens = (left.cacheReadInputTokens ?? 0) + (right.cacheReadInputTokens ?? 0);
|
|
40
|
+
const cacheCreationInputTokens = (left.cacheCreationInputTokens ?? 0) + (right.cacheCreationInputTokens ?? 0);
|
|
41
|
+
return {
|
|
42
|
+
inputTokens: left.inputTokens + right.inputTokens,
|
|
43
|
+
outputTokens: left.outputTokens + right.outputTokens,
|
|
44
|
+
...(cacheReadInputTokens === 0 ? {} : { cacheReadInputTokens }),
|
|
45
|
+
...(cacheCreationInputTokens === 0 ? {} : { cacheCreationInputTokens }),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export function workflowTokenTarget(prompt) {
|
|
49
|
+
const match = /(?:^|\s)\+(\d+(?:\.\d+)?)([km])\b/iu.exec(prompt);
|
|
50
|
+
if (!match)
|
|
51
|
+
return null;
|
|
52
|
+
const value = Number(match[1]);
|
|
53
|
+
if (!Number.isFinite(value) || value <= 0)
|
|
54
|
+
return null;
|
|
55
|
+
return Math.floor(value * (match[2]?.toLowerCase() === 'm' ? 1_000_000 : 1_000));
|
|
56
|
+
}
|
|
57
|
+
const PROMPT_SUGGESTION_INSTRUCTION = `[SUGGESTION MODE: Suggest what the user might naturally type next into Claude Code.]
|
|
58
|
+
|
|
59
|
+
FIRST: Look at the user's recent messages and original request.
|
|
60
|
+
|
|
61
|
+
Your job is to predict what THEY would type - not what you think they should do.
|
|
62
|
+
|
|
63
|
+
Reply with ONLY the suggestion, no quotes or explanation.
|
|
64
|
+
Use 2-12 words. Do not ask a question, evaluate the prior response, introduce a new idea, or use a Claude voice. If the topic is unsafe or sensitive, reply with an empty string.`;
|
|
65
|
+
function validPromptSuggestion(value) {
|
|
66
|
+
const suggestion = value.trim();
|
|
67
|
+
if (!suggestion)
|
|
68
|
+
return null;
|
|
69
|
+
const words = suggestion.split(/\s+/u);
|
|
70
|
+
if (words.length < 2 || words.length > 12)
|
|
71
|
+
return null;
|
|
72
|
+
if (/[??\n\r]/u.test(suggestion) || /[.!。!]/u.test(suggestion))
|
|
73
|
+
return null;
|
|
74
|
+
return suggestion;
|
|
75
|
+
}
|
|
76
|
+
export class ClaudeSessionService {
|
|
77
|
+
options;
|
|
78
|
+
schema;
|
|
79
|
+
inMemoryStores = new Map();
|
|
80
|
+
scheduledPrompts;
|
|
81
|
+
workflowManager;
|
|
82
|
+
worktreeManager;
|
|
83
|
+
sessionCwds = new Map();
|
|
84
|
+
hostedSubagents = new Set();
|
|
85
|
+
downloadedFileResourceSessions = new Set();
|
|
86
|
+
constructor(options) {
|
|
87
|
+
this.options = options;
|
|
88
|
+
this.schema = selectClaudeSchemaAdapter(options.claudeVersion);
|
|
89
|
+
this.scheduledPrompts =
|
|
90
|
+
options.tools && (options.scheduledToolNames?.length ?? 0) > 0
|
|
91
|
+
? new ScheduledPromptManager({
|
|
92
|
+
filePath: resolveClaudeScheduledTaskFile(options.cwd),
|
|
93
|
+
lockFile: join(options.configRoot, 'praxis', 'locks', 'scheduled-tasks.lock'),
|
|
94
|
+
...(options.enableDynamicWakeups === undefined
|
|
95
|
+
? {}
|
|
96
|
+
: { dynamicWakeupsEnabled: options.enableDynamicWakeups }),
|
|
97
|
+
})
|
|
98
|
+
: null;
|
|
99
|
+
this.workflowManager = options.enableWorkflows
|
|
100
|
+
? new WorkflowManager(options.configRoot, options.cwd, () => this.activeCwd())
|
|
101
|
+
: null;
|
|
102
|
+
this.worktreeManager =
|
|
103
|
+
options.enableWorktrees && options.workspace
|
|
104
|
+
? new SessionWorktreeManager({
|
|
105
|
+
workspace: options.workspace,
|
|
106
|
+
sessionId: '',
|
|
107
|
+
})
|
|
108
|
+
: null;
|
|
109
|
+
}
|
|
110
|
+
nextScheduledPrompt(signal) {
|
|
111
|
+
return this.scheduledPrompts?.next(signal) ?? Promise.resolve(null);
|
|
112
|
+
}
|
|
113
|
+
workflows() {
|
|
114
|
+
return this.workflowManager?.list() ?? [];
|
|
115
|
+
}
|
|
116
|
+
async close() {
|
|
117
|
+
this.scheduledPrompts?.close();
|
|
118
|
+
await Promise.all([...this.hostedSubagents].map((executor) => executor.close()));
|
|
119
|
+
this.hostedSubagents.clear();
|
|
120
|
+
await this.workflowManager?.close();
|
|
121
|
+
}
|
|
122
|
+
createHostedToolRegistry(sessionId) {
|
|
123
|
+
const baseTools = this.options.tools;
|
|
124
|
+
if (!baseTools)
|
|
125
|
+
throw new Error('Hosted tool registry requires base tools');
|
|
126
|
+
const paths = this.paths(sessionId);
|
|
127
|
+
const taskTools = (this.options.taskToolNames?.length ?? 0) > 0
|
|
128
|
+
? new ClaudeTaskToolRegistry({
|
|
129
|
+
base: baseTools,
|
|
130
|
+
cwd: this.activeCwd(),
|
|
131
|
+
cwdProvider: () => this.activeCwd(),
|
|
132
|
+
praxisRoot: paths.praxisRoot,
|
|
133
|
+
sessionId,
|
|
134
|
+
taskRoot: paths.taskRoot,
|
|
135
|
+
...(this.options.eventSink
|
|
136
|
+
? { eventSink: this.options.eventSink }
|
|
137
|
+
: {}),
|
|
138
|
+
...(this.options.taskToolNames
|
|
139
|
+
? { enabledTools: this.options.taskToolNames }
|
|
140
|
+
: {}),
|
|
141
|
+
})
|
|
142
|
+
: null;
|
|
143
|
+
const scheduledTools = this.scheduledPrompts &&
|
|
144
|
+
(this.options.scheduledToolNames?.length ?? 0) > 0
|
|
145
|
+
? new ClaudeScheduledToolRegistry({
|
|
146
|
+
base: taskTools ?? baseTools,
|
|
147
|
+
manager: this.scheduledPrompts,
|
|
148
|
+
sessionId,
|
|
149
|
+
...(this.options.scheduledToolNames
|
|
150
|
+
? { enabledTools: this.options.scheduledToolNames }
|
|
151
|
+
: {}),
|
|
152
|
+
})
|
|
153
|
+
: null;
|
|
154
|
+
const wrappedBase = scheduledTools ?? taskTools ?? baseTools;
|
|
155
|
+
const subagentExecutor = (this.options.enableSubagents || this.options.enableWorkflows) &&
|
|
156
|
+
this.options.permissions
|
|
157
|
+
? new ClaudeSubagentExecutor({
|
|
158
|
+
configRoot: this.options.configRoot,
|
|
159
|
+
cwd: this.activeCwd(),
|
|
160
|
+
cwdProvider: () => this.activeCwd(),
|
|
161
|
+
claudeVersion: this.options.claudeVersion,
|
|
162
|
+
provider: this.options.provider ??
|
|
163
|
+
(() => {
|
|
164
|
+
throw new Error('A model provider is required to execute hosted Agent tools');
|
|
165
|
+
})(),
|
|
166
|
+
persistence: this.options.sessionPersistence === false ? 'memory' : 'disk',
|
|
167
|
+
...(this.options.providerForModel
|
|
168
|
+
? { providerForModel: this.options.providerForModel }
|
|
169
|
+
: {}),
|
|
170
|
+
baseTools: wrappedBase,
|
|
171
|
+
permissions: this.options.permissions,
|
|
172
|
+
...(this.options.permissionResolverForMode
|
|
173
|
+
? {
|
|
174
|
+
permissionResolverForMode: this.options.permissionResolverForMode,
|
|
175
|
+
}
|
|
176
|
+
: {}),
|
|
177
|
+
...(this.options.subagentToolNames
|
|
178
|
+
? { toolNames: this.options.subagentToolNames }
|
|
179
|
+
: {}),
|
|
180
|
+
...(this.options.extensions
|
|
181
|
+
? { extensions: this.options.extensions }
|
|
182
|
+
: {}),
|
|
183
|
+
...(this.options.hooks ? { hooks: this.options.hooks } : {}),
|
|
184
|
+
...(this.options.contextAssembler
|
|
185
|
+
? { contextAssembler: this.options.contextAssembler }
|
|
186
|
+
: {}),
|
|
187
|
+
...((this.options.contextBudget?.reserveTokens ??
|
|
188
|
+
this.options.contextReserveTokens) === undefined
|
|
189
|
+
? {}
|
|
190
|
+
: {
|
|
191
|
+
contextReserveTokens: this.options.contextBudget?.reserveTokens ??
|
|
192
|
+
this.options.contextReserveTokens,
|
|
193
|
+
}),
|
|
194
|
+
...(this.options.approveTool
|
|
195
|
+
? { approveTool: this.options.approveTool }
|
|
196
|
+
: {}),
|
|
197
|
+
...(this.options.eventSink
|
|
198
|
+
? { eventSink: this.options.eventSink }
|
|
199
|
+
: {}),
|
|
200
|
+
...(taskTools
|
|
201
|
+
? {
|
|
202
|
+
backgroundTaskNotifications: (waitForRunning) => taskTools.notifications(waitForRunning),
|
|
203
|
+
}
|
|
204
|
+
: {}),
|
|
205
|
+
})
|
|
206
|
+
: null;
|
|
207
|
+
if (subagentExecutor)
|
|
208
|
+
this.hostedSubagents.add(subagentExecutor);
|
|
209
|
+
const agentTools = subagentExecutor
|
|
210
|
+
? subagentExecutor.registry(sessionId, 0, (callId) => callId)
|
|
211
|
+
: wrappedBase;
|
|
212
|
+
const workflowTools = this.workflowManager && subagentExecutor
|
|
213
|
+
? new ClaudeWorkflowToolRegistry({
|
|
214
|
+
base: agentTools,
|
|
215
|
+
manager: this.workflowManager,
|
|
216
|
+
executor: subagentExecutor,
|
|
217
|
+
cwd: this.activeCwd(),
|
|
218
|
+
cwdProvider: () => this.activeCwd(),
|
|
219
|
+
configRoot: this.options.configRoot,
|
|
220
|
+
sessionId,
|
|
221
|
+
promptIdForCall: (callId) => callId,
|
|
222
|
+
defaultModel: this.options.provider?.model ?? 'praxis/provider',
|
|
223
|
+
tokenBudget: null,
|
|
224
|
+
enabled: true,
|
|
225
|
+
})
|
|
226
|
+
: agentTools;
|
|
227
|
+
if (this.worktreeManager)
|
|
228
|
+
this.worktreeManager.bindSession(sessionId);
|
|
229
|
+
const registry = this.worktreeManager && this.options.workspace
|
|
230
|
+
? new ClaudeWorktreeToolRegistry({
|
|
231
|
+
base: workflowTools,
|
|
232
|
+
manager: this.worktreeManager,
|
|
233
|
+
workspace: this.options.workspace,
|
|
234
|
+
...(this.options.worktreeToolNames
|
|
235
|
+
? { enabledTools: this.options.worktreeToolNames }
|
|
236
|
+
: {}),
|
|
237
|
+
})
|
|
238
|
+
: workflowTools;
|
|
239
|
+
const messageRegistry = this.options.brief
|
|
240
|
+
? new ClaudeUserMessageToolRegistry(registry, (message) => this.options.eventSink?.({
|
|
241
|
+
type: 'user-message',
|
|
242
|
+
message: message.message,
|
|
243
|
+
status: message.status,
|
|
244
|
+
...(message.attachments.length
|
|
245
|
+
? { attachments: message.attachments }
|
|
246
|
+
: {}),
|
|
247
|
+
}))
|
|
248
|
+
: registry;
|
|
249
|
+
const interactiveRegistry = this.options.interactiveTools
|
|
250
|
+
? this.options.interactiveTools.registry(messageRegistry, sessionId)
|
|
251
|
+
: messageRegistry;
|
|
252
|
+
const preferredOrder = [
|
|
253
|
+
'Agent',
|
|
254
|
+
'AskUserQuestion',
|
|
255
|
+
'TaskOutput',
|
|
256
|
+
'Bash',
|
|
257
|
+
'EnterPlanMode',
|
|
258
|
+
'Read',
|
|
259
|
+
'LSP',
|
|
260
|
+
'Edit',
|
|
261
|
+
'Write',
|
|
262
|
+
'NotebookEdit',
|
|
263
|
+
'WebFetch',
|
|
264
|
+
'ReportFindings',
|
|
265
|
+
'WebSearch',
|
|
266
|
+
'TaskStop',
|
|
267
|
+
'Skill',
|
|
268
|
+
'DesignSync',
|
|
269
|
+
'TaskCreate',
|
|
270
|
+
'TaskGet',
|
|
271
|
+
'TaskUpdate',
|
|
272
|
+
'ExitPlanMode',
|
|
273
|
+
'EnterWorktree',
|
|
274
|
+
'ExitWorktree',
|
|
275
|
+
'SendMessage',
|
|
276
|
+
'SendUserMessage',
|
|
277
|
+
'Workflow',
|
|
278
|
+
'CronCreate',
|
|
279
|
+
'CronDelete',
|
|
280
|
+
'CronList',
|
|
281
|
+
'ScheduleWakeup',
|
|
282
|
+
'Monitor',
|
|
283
|
+
'PushNotification',
|
|
284
|
+
];
|
|
285
|
+
return {
|
|
286
|
+
definitions: () => {
|
|
287
|
+
const definitions = interactiveRegistry.definitions();
|
|
288
|
+
return [...definitions].sort((left, right) => {
|
|
289
|
+
const leftIndex = preferredOrder.indexOf(left.name);
|
|
290
|
+
const rightIndex = preferredOrder.indexOf(right.name);
|
|
291
|
+
return ((leftIndex < 0 ? preferredOrder.length : leftIndex) -
|
|
292
|
+
(rightIndex < 0 ? preferredOrder.length : rightIndex));
|
|
293
|
+
});
|
|
294
|
+
},
|
|
295
|
+
prepare: (call, context) => interactiveRegistry.prepare(call, context),
|
|
296
|
+
execute: (call, context) => interactiveRegistry.execute(call, context),
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
async run(prompt, signal, sessionId = randomUUID(), name, images, documents) {
|
|
300
|
+
this.worktreeManager?.bindSession(sessionId);
|
|
301
|
+
return this.executeTurn(sessionId, prompt, false, signal, name, images, documents);
|
|
302
|
+
}
|
|
303
|
+
async resume(sessionId, prompt, signal, name, images, documents, resumeSessionAt) {
|
|
304
|
+
this.worktreeManager?.bindSession(sessionId);
|
|
305
|
+
return this.executeTurn(sessionId, prompt, true, signal, name, images, documents, resumeSessionAt);
|
|
306
|
+
}
|
|
307
|
+
async promptSuggestion(sessionId, signal) {
|
|
308
|
+
const provider = this.provider();
|
|
309
|
+
const loaded = this.options.sessionPersistence === false
|
|
310
|
+
? await this.turnStore(sessionId).withLease((lease) => lease.load())
|
|
311
|
+
: {
|
|
312
|
+
status: 'completed',
|
|
313
|
+
value: await this.store(sessionId).loadReadOnly(),
|
|
314
|
+
};
|
|
315
|
+
if (loaded.status === 'conflict')
|
|
316
|
+
return null;
|
|
317
|
+
const entries = loaded.value;
|
|
318
|
+
this.restoreWorktree(entries.entries);
|
|
319
|
+
const agentName = this.options.agent ?? getClaudeAgentSetting(entries.entries);
|
|
320
|
+
const agent = agentName ? this.options.extensions?.agent(agentName) : null;
|
|
321
|
+
const assembledContext = await this.options.contextAssembler?.assemble({
|
|
322
|
+
cwd: this.activeCwd(),
|
|
323
|
+
});
|
|
324
|
+
const contextMessages = [
|
|
325
|
+
...(assembledContext?.systemMessages ?? []),
|
|
326
|
+
...(agent
|
|
327
|
+
? [
|
|
328
|
+
{
|
|
329
|
+
role: 'system',
|
|
330
|
+
content: `# Agent definition: ${agent.name}\n\n${agent.body}`,
|
|
331
|
+
},
|
|
332
|
+
]
|
|
333
|
+
: []),
|
|
334
|
+
];
|
|
335
|
+
const messages = [
|
|
336
|
+
...contextMessages,
|
|
337
|
+
...injectFirstUserMessageContext([
|
|
338
|
+
...projectClaudeModelMessages(entries.entries),
|
|
339
|
+
{ role: 'user', content: PROMPT_SUGGESTION_INSTRUCTION },
|
|
340
|
+
], assembledContext?.firstUserMessageContext),
|
|
341
|
+
];
|
|
342
|
+
let suggestion = '';
|
|
343
|
+
for await (const event of provider.complete({
|
|
344
|
+
messages,
|
|
345
|
+
...(provider.capabilities.tools
|
|
346
|
+
? { tools: this.options.tools?.definitions() ?? [] }
|
|
347
|
+
: {}),
|
|
348
|
+
...(this.options.effort ? { effort: this.options.effort } : {}),
|
|
349
|
+
...(signal ? { signal } : {}),
|
|
350
|
+
})) {
|
|
351
|
+
if (event.type === 'text-delta')
|
|
352
|
+
suggestion += event.delta;
|
|
353
|
+
if (event.type === 'tool-call')
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
return validPromptSuggestion(suggestion);
|
|
357
|
+
}
|
|
358
|
+
async sessions() {
|
|
359
|
+
const paths = this.paths(randomUUID());
|
|
360
|
+
let names;
|
|
361
|
+
try {
|
|
362
|
+
names = await readdir(paths.projectRoot);
|
|
363
|
+
}
|
|
364
|
+
catch (error) {
|
|
365
|
+
if (error.code === 'ENOENT')
|
|
366
|
+
return [];
|
|
367
|
+
throw error;
|
|
368
|
+
}
|
|
369
|
+
const summaries = await Promise.all(names
|
|
370
|
+
.filter((name) => extname(name) === '.jsonl')
|
|
371
|
+
.map(async (name) => {
|
|
372
|
+
const sessionId = basename(name, '.jsonl');
|
|
373
|
+
if (!isClaudeSessionId(sessionId))
|
|
374
|
+
return null;
|
|
375
|
+
const sessionFile = join(paths.projectRoot, name);
|
|
376
|
+
try {
|
|
377
|
+
const metadata = await lstat(sessionFile);
|
|
378
|
+
if (!metadata.isFile())
|
|
379
|
+
return null;
|
|
380
|
+
const recovery = await this.store(sessionId).loadReadOnly();
|
|
381
|
+
if (!(await lstat(sessionFile)).isFile())
|
|
382
|
+
return null;
|
|
383
|
+
const name = this.sessionName(recovery.entries);
|
|
384
|
+
const prLink = getClaudePrLink(recovery.entries, sessionId);
|
|
385
|
+
return {
|
|
386
|
+
sessionId,
|
|
387
|
+
...(name === null ? {} : { name }),
|
|
388
|
+
lastPrompt: getClaudeLastPrompt(recovery.entries),
|
|
389
|
+
updatedAt: metadata.mtime.toISOString(),
|
|
390
|
+
status: this.sessionStatus(recovery.issue, recovery.entries.length),
|
|
391
|
+
issue: recovery.issue,
|
|
392
|
+
...(prLink
|
|
393
|
+
? {
|
|
394
|
+
prNumber: prLink.prNumber,
|
|
395
|
+
prUrl: prLink.prUrl,
|
|
396
|
+
prRepository: prLink.prRepository,
|
|
397
|
+
}
|
|
398
|
+
: {}),
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
catch (error) {
|
|
402
|
+
if (typeof error.code === 'string') {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
throw error;
|
|
406
|
+
}
|
|
407
|
+
}));
|
|
408
|
+
return summaries
|
|
409
|
+
.filter((summary) => summary !== null)
|
|
410
|
+
.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
411
|
+
}
|
|
412
|
+
async inspect(sessionId) {
|
|
413
|
+
this.assertSessionPersistence();
|
|
414
|
+
const paths = this.paths(sessionId);
|
|
415
|
+
let metadata;
|
|
416
|
+
try {
|
|
417
|
+
metadata = await stat(paths.sessionFile);
|
|
418
|
+
}
|
|
419
|
+
catch (error) {
|
|
420
|
+
if (error.code === 'ENOENT') {
|
|
421
|
+
throw new Error(`Claude session not found: ${sessionId}`);
|
|
422
|
+
}
|
|
423
|
+
throw error;
|
|
424
|
+
}
|
|
425
|
+
const recovery = await this.store(sessionId).loadReadOnly();
|
|
426
|
+
const prLink = getClaudePrLink(recovery.entries, sessionId);
|
|
427
|
+
return {
|
|
428
|
+
sessionId,
|
|
429
|
+
lastPrompt: getClaudeLastPrompt(recovery.entries),
|
|
430
|
+
updatedAt: metadata.mtime.toISOString(),
|
|
431
|
+
status: this.sessionStatus(recovery.issue, recovery.entries.length),
|
|
432
|
+
issue: recovery.issue,
|
|
433
|
+
claudeVersion: this.options.claudeVersion,
|
|
434
|
+
writeMode: this.schema.writeMode,
|
|
435
|
+
entryCount: recovery.entries.length,
|
|
436
|
+
byteLength: recovery.tail.byteLength,
|
|
437
|
+
newlineTerminated: recovery.tail.newlineTerminated,
|
|
438
|
+
...(prLink
|
|
439
|
+
? {
|
|
440
|
+
prNumber: prLink.prNumber,
|
|
441
|
+
prUrl: prLink.prUrl,
|
|
442
|
+
prRepository: prLink.prRepository,
|
|
443
|
+
}
|
|
444
|
+
: {}),
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
async export(sessionId) {
|
|
448
|
+
this.assertSessionPersistence();
|
|
449
|
+
try {
|
|
450
|
+
return await this.store(sessionId).exportReadOnly();
|
|
451
|
+
}
|
|
452
|
+
catch (error) {
|
|
453
|
+
if (error.code === 'ENOENT') {
|
|
454
|
+
throw new Error(`Claude session not found: ${sessionId}`);
|
|
455
|
+
}
|
|
456
|
+
throw error;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
async fork(parentSessionId, sessionId = randomUUID(), resumeSessionAt) {
|
|
460
|
+
this.assertSessionPersistence();
|
|
461
|
+
this.assertWritable();
|
|
462
|
+
const sourceResult = await this.store(parentSessionId).withLease((lease) => lease.load());
|
|
463
|
+
if (sourceResult.status === 'conflict') {
|
|
464
|
+
throw new Error(`Claude transcript fork conflict: ${sourceResult.reason}`);
|
|
465
|
+
}
|
|
466
|
+
const source = sourceResult.value;
|
|
467
|
+
if (source.entries.length === 0) {
|
|
468
|
+
throw new Error(`Claude session not found: ${parentSessionId}`);
|
|
469
|
+
}
|
|
470
|
+
const target = this.store(sessionId);
|
|
471
|
+
const result = await target.create(createClaudeNativeFork({
|
|
472
|
+
source: source.entries,
|
|
473
|
+
sourceSessionId: parentSessionId,
|
|
474
|
+
sessionId,
|
|
475
|
+
...(resumeSessionAt === undefined ? {} : { resumeSessionAt }),
|
|
476
|
+
}));
|
|
477
|
+
if (result.status === 'conflict') {
|
|
478
|
+
throw new Error('Generated Claude fork session already exists');
|
|
479
|
+
}
|
|
480
|
+
return { sessionId, parentSessionId };
|
|
481
|
+
}
|
|
482
|
+
async rewindFiles(sessionId, userMessageId) {
|
|
483
|
+
const result = await this.store(sessionId).withLease(async (lease) => {
|
|
484
|
+
const snapshot = await lease.load();
|
|
485
|
+
if (snapshot.entries.length === 0) {
|
|
486
|
+
throw new Error(`Claude session not found: ${sessionId}`);
|
|
487
|
+
}
|
|
488
|
+
this.restoreWorktree(snapshot.entries);
|
|
489
|
+
this.options.interactiveTools?.restore(sessionId, snapshot.entries);
|
|
490
|
+
await new ClaudeFileHistory(this.options.configRoot, sessionId, [
|
|
491
|
+
this.activeCwd(),
|
|
492
|
+
...(this.options.fileRewindRoots ?? []),
|
|
493
|
+
]).rewind(snapshot.entries, userMessageId);
|
|
494
|
+
});
|
|
495
|
+
if (result.status === 'conflict') {
|
|
496
|
+
throw new Error(`Claude file rewind conflict: ${result.reason}`);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
async executeTurn(sessionId, prompt, requireExisting, signal, name, images = [], documents = [], resumeSessionAt) {
|
|
500
|
+
this.assertWritable();
|
|
501
|
+
if (prompt.length === 0 && images.length === 0 && documents.length === 0)
|
|
502
|
+
throw new Error('Prompt must not be empty');
|
|
503
|
+
if (name !== undefined && name.length === 0) {
|
|
504
|
+
throw new Error('Session name must not be empty');
|
|
505
|
+
}
|
|
506
|
+
await this.ensureFileResources(sessionId, signal);
|
|
507
|
+
this.worktreeManager?.bindSession(sessionId);
|
|
508
|
+
if (this.options.initialWorktree) {
|
|
509
|
+
await this.worktreeManager?.ensureInitial(this.options.initialWorktreeName);
|
|
510
|
+
}
|
|
511
|
+
const pinnedCwd = this.sessionCwds.get(sessionId) ?? this.activeCwd();
|
|
512
|
+
this.sessionCwds.set(sessionId, pinnedCwd);
|
|
513
|
+
const sessionPaths = this.paths(sessionId);
|
|
514
|
+
const toolResultDirectory = join(sessionPaths.projectRoot, sessionId, 'tool-results');
|
|
515
|
+
const store = this.turnStore(sessionId);
|
|
516
|
+
const leaseResult = await store.withLease(async (lease) => {
|
|
517
|
+
if (!requireExisting) {
|
|
518
|
+
const initialization = name === undefined
|
|
519
|
+
? await store.reserve()
|
|
520
|
+
: await store.create(this.sessionNameEntries(sessionId, name));
|
|
521
|
+
if (initialization.status === 'conflict') {
|
|
522
|
+
throw new Error(`Session ID ${sessionId} is already in use`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
let snapshot = await lease.load();
|
|
526
|
+
if (requireExisting &&
|
|
527
|
+
this.options.sessionPersistence === false &&
|
|
528
|
+
snapshot.entries.length === 0) {
|
|
529
|
+
const persisted = await this.store(sessionId).load();
|
|
530
|
+
if (persisted.entries.length > 0) {
|
|
531
|
+
const imported = await store.create(persisted.entries);
|
|
532
|
+
if (imported.status === 'created')
|
|
533
|
+
snapshot = await lease.load();
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
if (requireExisting && snapshot.entries.length === 0) {
|
|
537
|
+
throw new Error(`Claude session not found: ${sessionId}`);
|
|
538
|
+
}
|
|
539
|
+
if (resumeSessionAt !== undefined) {
|
|
540
|
+
snapshot = {
|
|
541
|
+
entries: selectClaudeTranscriptAtMessage(snapshot.entries, resumeSessionAt),
|
|
542
|
+
tail: { ...snapshot.tail, branchParentUuid: resumeSessionAt },
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
const initialTransition = this.worktreeManager?.consumeTransition('__initial__');
|
|
546
|
+
if (initialTransition && snapshot.entries.length === 0) {
|
|
547
|
+
const stateEntry = {
|
|
548
|
+
type: 'worktree-state',
|
|
549
|
+
worktreeSession: initialTransition.state,
|
|
550
|
+
sessionId,
|
|
551
|
+
};
|
|
552
|
+
const stateTail = await this.append(lease, snapshot.tail, stateEntry);
|
|
553
|
+
snapshot = {
|
|
554
|
+
entries: [...snapshot.entries, stateEntry],
|
|
555
|
+
tail: stateTail,
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
this.restoreWorktree(snapshot.entries);
|
|
559
|
+
this.options.interactiveTools?.restore(sessionId, snapshot.entries);
|
|
560
|
+
if (requireExisting &&
|
|
561
|
+
name !== undefined &&
|
|
562
|
+
!this.hasSessionName(snapshot.entries, name)) {
|
|
563
|
+
const entries = this.sessionNameEntries(sessionId, name);
|
|
564
|
+
const appendResult = await lease.appendMany(snapshot.tail, entries);
|
|
565
|
+
if (appendResult.status === 'conflict') {
|
|
566
|
+
throw new Error(`Claude transcript append conflict: ${appendResult.reason}`);
|
|
567
|
+
}
|
|
568
|
+
snapshot = {
|
|
569
|
+
entries: [...snapshot.entries, ...entries],
|
|
570
|
+
tail: appendResult.tail,
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
const provider = this.provider();
|
|
574
|
+
const initialPricing = this.options.pricing?.resolve(provider.model ?? 'praxis/provider');
|
|
575
|
+
if (this.options.maxBudgetUsd !== undefined && !initialPricing) {
|
|
576
|
+
throw new Error(`Cannot enforce --max-budget-usd: no pricing is configured for model ${provider.model ?? 'praxis/provider'}`);
|
|
577
|
+
}
|
|
578
|
+
let currentPromptId = null;
|
|
579
|
+
let lastAssistantUuid = null;
|
|
580
|
+
const fileHistory = this.options.fileCheckpointing &&
|
|
581
|
+
this.options.sessionPersistence !== false
|
|
582
|
+
? new ClaudeFileHistory(this.options.configRoot, sessionId, [
|
|
583
|
+
this.activeCwd(),
|
|
584
|
+
...(this.options.fileRewindRoots ?? []),
|
|
585
|
+
])
|
|
586
|
+
: null;
|
|
587
|
+
const unresolvedToolCalls = findUnresolvedClaudeToolCalls(snapshot.entries);
|
|
588
|
+
const pendingRecoveryToolCallIds = new Set(unresolvedToolCalls.map((call) => call.id));
|
|
589
|
+
const pendingRecoveryHookOutcomes = [];
|
|
590
|
+
const currentHookCwd = () => this.activeCwd();
|
|
591
|
+
const hookSession = {
|
|
592
|
+
session_id: sessionId,
|
|
593
|
+
transcript_path: sessionPaths.sessionFile,
|
|
594
|
+
get cwd() {
|
|
595
|
+
return currentHookCwd();
|
|
596
|
+
},
|
|
597
|
+
permission_mode: 'default',
|
|
598
|
+
};
|
|
599
|
+
const appendHookOutcome = async (outcome) => {
|
|
600
|
+
for (const entry of createClaudeHookAttachmentEntries(outcome, this.translationContext(sessionId, snapshot))) {
|
|
601
|
+
const tail = await this.append(lease, snapshot.tail, entry);
|
|
602
|
+
snapshot = { entries: [...snapshot.entries, entry], tail };
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
const recordHookOutcome = async (outcome, deferUntilApproval = false) => {
|
|
606
|
+
if (deferUntilApproval) {
|
|
607
|
+
pendingRecoveryHookOutcomes.push(outcome);
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
await appendHookOutcome(outcome);
|
|
611
|
+
};
|
|
612
|
+
const flushRecoveryHookOutcomes = async () => {
|
|
613
|
+
const entries = [];
|
|
614
|
+
let history = snapshot.entries;
|
|
615
|
+
let parentUuid = this.logicalTailUuid(snapshot.tail);
|
|
616
|
+
for (const outcome of pendingRecoveryHookOutcomes) {
|
|
617
|
+
const outcomeEntries = createClaudeHookAttachmentEntries(outcome, {
|
|
618
|
+
...this.translationContext(sessionId, snapshot),
|
|
619
|
+
parentUuid,
|
|
620
|
+
history,
|
|
621
|
+
});
|
|
622
|
+
entries.push(...outcomeEntries);
|
|
623
|
+
history = [...history, ...outcomeEntries];
|
|
624
|
+
const lastEntry = outcomeEntries.at(-1);
|
|
625
|
+
if (typeof lastEntry?.uuid === 'string')
|
|
626
|
+
parentUuid = lastEntry.uuid;
|
|
627
|
+
}
|
|
628
|
+
if (entries.length === 0) {
|
|
629
|
+
pendingRecoveryHookOutcomes.length = 0;
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
const appendResult = await lease.appendMany(snapshot.tail, entries);
|
|
633
|
+
if (appendResult.status === 'conflict') {
|
|
634
|
+
throw new Error(`Claude transcript append conflict: ${appendResult.reason}`);
|
|
635
|
+
}
|
|
636
|
+
snapshot = { entries: history, tail: appendResult.tail };
|
|
637
|
+
pendingRecoveryHookOutcomes.length = 0;
|
|
638
|
+
};
|
|
639
|
+
const taskTools = this.options.tools && (this.options.taskToolNames?.length ?? 0) > 0
|
|
640
|
+
? new ClaudeTaskToolRegistry({
|
|
641
|
+
base: this.options.tools,
|
|
642
|
+
cwd: this.activeCwd(),
|
|
643
|
+
cwdProvider: () => this.activeCwd(),
|
|
644
|
+
praxisRoot: sessionPaths.praxisRoot,
|
|
645
|
+
sessionId,
|
|
646
|
+
taskRoot: sessionPaths.taskRoot,
|
|
647
|
+
...(this.options.eventSink
|
|
648
|
+
? { eventSink: this.options.eventSink }
|
|
649
|
+
: {}),
|
|
650
|
+
...(this.options.taskToolNames
|
|
651
|
+
? { enabledTools: this.options.taskToolNames }
|
|
652
|
+
: {}),
|
|
653
|
+
})
|
|
654
|
+
: null;
|
|
655
|
+
const scheduledTools = this.scheduledPrompts &&
|
|
656
|
+
this.options.tools &&
|
|
657
|
+
(this.options.scheduledToolNames?.length ?? 0) > 0
|
|
658
|
+
? new ClaudeScheduledToolRegistry({
|
|
659
|
+
base: taskTools ?? this.options.tools,
|
|
660
|
+
manager: this.scheduledPrompts,
|
|
661
|
+
sessionId,
|
|
662
|
+
...(this.options.scheduledToolNames
|
|
663
|
+
? { enabledTools: this.options.scheduledToolNames }
|
|
664
|
+
: {}),
|
|
665
|
+
})
|
|
666
|
+
: null;
|
|
667
|
+
const baseTools = scheduledTools ?? taskTools ?? this.options.tools;
|
|
668
|
+
const turnPermissions = this.options.interactiveTools?.permissions(sessionId) ??
|
|
669
|
+
this.options.permissions;
|
|
670
|
+
const subagentExecutor = (this.options.enableSubagents || this.options.enableWorkflows) &&
|
|
671
|
+
baseTools &&
|
|
672
|
+
turnPermissions
|
|
673
|
+
? new ClaudeSubagentExecutor({
|
|
674
|
+
configRoot: this.options.configRoot,
|
|
675
|
+
cwd: this.activeCwd(),
|
|
676
|
+
cwdProvider: () => this.activeCwd(),
|
|
677
|
+
claudeVersion: this.options.claudeVersion,
|
|
678
|
+
provider,
|
|
679
|
+
persistence: this.options.sessionPersistence === false ? 'memory' : 'disk',
|
|
680
|
+
...(this.options.providerForModel
|
|
681
|
+
? { providerForModel: this.options.providerForModel }
|
|
682
|
+
: {}),
|
|
683
|
+
baseTools,
|
|
684
|
+
permissions: turnPermissions,
|
|
685
|
+
...(this.options.permissionResolverForMode
|
|
686
|
+
? {
|
|
687
|
+
permissionResolverForMode: this.options.permissionResolverForMode,
|
|
688
|
+
}
|
|
689
|
+
: {}),
|
|
690
|
+
...(this.options.subagentToolNames
|
|
691
|
+
? { toolNames: this.options.subagentToolNames }
|
|
692
|
+
: {}),
|
|
693
|
+
...(this.options.extensions
|
|
694
|
+
? { extensions: this.options.extensions }
|
|
695
|
+
: {}),
|
|
696
|
+
...(this.options.hooks ? { hooks: this.options.hooks } : {}),
|
|
697
|
+
...(this.options.contextAssembler
|
|
698
|
+
? { contextAssembler: this.options.contextAssembler }
|
|
699
|
+
: {}),
|
|
700
|
+
...((this.options.contextBudget?.reserveTokens ??
|
|
701
|
+
this.options.contextReserveTokens) === undefined
|
|
702
|
+
? {}
|
|
703
|
+
: {
|
|
704
|
+
contextReserveTokens: this.options.contextBudget?.reserveTokens ??
|
|
705
|
+
this.options.contextReserveTokens,
|
|
706
|
+
}),
|
|
707
|
+
...(this.options.approveTool
|
|
708
|
+
? { approveTool: this.options.approveTool }
|
|
709
|
+
: {}),
|
|
710
|
+
...(this.options.eventSink
|
|
711
|
+
? { eventSink: this.options.eventSink }
|
|
712
|
+
: {}),
|
|
713
|
+
...(taskTools
|
|
714
|
+
? {
|
|
715
|
+
backgroundTaskNotifications: (waitForRunning) => taskTools.notifications(waitForRunning),
|
|
716
|
+
}
|
|
717
|
+
: {}),
|
|
718
|
+
})
|
|
719
|
+
: null;
|
|
720
|
+
const agentTools = subagentExecutor
|
|
721
|
+
? subagentExecutor.registry(sessionId, 0, (callId) => currentPromptId ??
|
|
722
|
+
this.promptIdForToolCall(snapshot.entries, callId))
|
|
723
|
+
: baseTools;
|
|
724
|
+
const turnTools = this.workflowManager && subagentExecutor && agentTools
|
|
725
|
+
? new ClaudeWorkflowToolRegistry({
|
|
726
|
+
base: agentTools,
|
|
727
|
+
manager: this.workflowManager,
|
|
728
|
+
executor: subagentExecutor,
|
|
729
|
+
cwd: this.activeCwd(),
|
|
730
|
+
cwdProvider: () => this.activeCwd(),
|
|
731
|
+
configRoot: this.options.configRoot,
|
|
732
|
+
sessionId,
|
|
733
|
+
promptIdForCall: (callId) => currentPromptId ??
|
|
734
|
+
this.promptIdForToolCall(snapshot.entries, callId),
|
|
735
|
+
defaultModel: provider.model ?? 'praxis/provider',
|
|
736
|
+
tokenBudget: workflowTokenTarget(prompt),
|
|
737
|
+
enabled: true,
|
|
738
|
+
})
|
|
739
|
+
: agentTools;
|
|
740
|
+
const workspaceTools = this.worktreeManager &&
|
|
741
|
+
turnTools &&
|
|
742
|
+
this.options.workspace &&
|
|
743
|
+
(this.options.worktreeToolNames?.length ?? 0) > 0
|
|
744
|
+
? new ClaudeWorktreeToolRegistry({
|
|
745
|
+
base: turnTools,
|
|
746
|
+
manager: this.worktreeManager,
|
|
747
|
+
workspace: this.options.workspace,
|
|
748
|
+
enabledTools: this.options.worktreeToolNames ?? [],
|
|
749
|
+
})
|
|
750
|
+
: turnTools;
|
|
751
|
+
const messageTools = this.options.brief && workspaceTools
|
|
752
|
+
? new ClaudeUserMessageToolRegistry(workspaceTools, (message) => this.options.eventSink?.({
|
|
753
|
+
type: 'user-message',
|
|
754
|
+
message: message.message,
|
|
755
|
+
status: message.status,
|
|
756
|
+
...(message.attachments.length
|
|
757
|
+
? { attachments: message.attachments }
|
|
758
|
+
: {}),
|
|
759
|
+
}))
|
|
760
|
+
: workspaceTools;
|
|
761
|
+
const interactiveMessageTools = this.options.interactiveTools && messageTools
|
|
762
|
+
? this.options.interactiveTools.registry(messageTools, sessionId)
|
|
763
|
+
: messageTools;
|
|
764
|
+
const fileHistoryTools = fileHistory && interactiveMessageTools
|
|
765
|
+
? {
|
|
766
|
+
definitions: () => interactiveMessageTools.definitions(),
|
|
767
|
+
prepare: (call, context) => interactiveMessageTools.prepare(call, context),
|
|
768
|
+
execute: async (call, context) => {
|
|
769
|
+
const path = call.name === 'Write' || call.name === 'Edit'
|
|
770
|
+
? call.input.file_path
|
|
771
|
+
: call.name === 'NotebookEdit'
|
|
772
|
+
? call.input.notebook_path
|
|
773
|
+
: undefined;
|
|
774
|
+
if (typeof path !== 'string') {
|
|
775
|
+
return interactiveMessageTools.execute(call, context);
|
|
776
|
+
}
|
|
777
|
+
const snapshotMessageId = currentPromptId ??
|
|
778
|
+
this.promptIdForToolCall(snapshot.entries, call.id);
|
|
779
|
+
const assistantMessageId = lastAssistantUuid ??
|
|
780
|
+
this.assistantIdForToolCall(snapshot.entries, call.id);
|
|
781
|
+
if (!snapshotMessageId || !assistantMessageId) {
|
|
782
|
+
throw new Error('Claude file history could not link tool call');
|
|
783
|
+
}
|
|
784
|
+
const prepared = await fileHistory.prepareMutation(snapshot.entries, snapshotMessageId, path);
|
|
785
|
+
let result;
|
|
786
|
+
try {
|
|
787
|
+
result = await interactiveMessageTools.execute(call, context);
|
|
788
|
+
}
|
|
789
|
+
catch (error) {
|
|
790
|
+
await prepared.rollback();
|
|
791
|
+
throw error;
|
|
792
|
+
}
|
|
793
|
+
if (result.isError) {
|
|
794
|
+
await prepared.rollback();
|
|
795
|
+
return result;
|
|
796
|
+
}
|
|
797
|
+
const entry = prepared.commit(assistantMessageId);
|
|
798
|
+
if (entry) {
|
|
799
|
+
const tail = await this.append(lease, snapshot.tail, entry);
|
|
800
|
+
snapshot = { entries: [...snapshot.entries, entry], tail };
|
|
801
|
+
}
|
|
802
|
+
return result;
|
|
803
|
+
},
|
|
804
|
+
}
|
|
805
|
+
: interactiveMessageTools;
|
|
806
|
+
const structuredCapture = this.options.structuredOutputSchema
|
|
807
|
+
? { calls: 0, value: undefined }
|
|
808
|
+
: undefined;
|
|
809
|
+
const structuredTools = this.options.structuredOutputSchema && structuredCapture
|
|
810
|
+
? new StructuredOutputRegistry(fileHistoryTools ?? this.options.tools ?? emptyToolRegistry, this.options.structuredOutputSchema, structuredCapture)
|
|
811
|
+
: fileHistoryTools;
|
|
812
|
+
const hookTools = this.options.hooks && structuredTools && turnPermissions
|
|
813
|
+
? new ClaudeHookToolCoordinator({
|
|
814
|
+
tools: structuredTools,
|
|
815
|
+
permissions: turnPermissions,
|
|
816
|
+
hooks: this.options.hooks,
|
|
817
|
+
session: hookSession,
|
|
818
|
+
recordOutcome: recordHookOutcome,
|
|
819
|
+
deferPreToolUseOutcome: (call) => pendingRecoveryToolCallIds.has(call.id),
|
|
820
|
+
})
|
|
821
|
+
: null;
|
|
822
|
+
const runtime = new AgentRuntime(provider, this.options.eventSink, {
|
|
823
|
+
emitInitialContextState: false,
|
|
824
|
+
...(this.options.emitToolUseSummaries
|
|
825
|
+
? {
|
|
826
|
+
generateToolUseSummary: ({ tools, lastAssistantText, signal }) => generateToolUseSummary(provider, tools, signal, lastAssistantText),
|
|
827
|
+
}
|
|
828
|
+
: {}),
|
|
829
|
+
...(this.options.pricing
|
|
830
|
+
? {
|
|
831
|
+
costUsd: (usage) => {
|
|
832
|
+
const pricing = this.options.pricing?.resolve(provider.model ?? 'praxis/provider');
|
|
833
|
+
return pricing ? usageCostUsd(usage, pricing) : undefined;
|
|
834
|
+
},
|
|
835
|
+
}
|
|
836
|
+
: {}),
|
|
837
|
+
...(this.options.maxBudgetUsd === undefined
|
|
838
|
+
? {}
|
|
839
|
+
: { maxBudgetUsd: this.options.maxBudgetUsd }),
|
|
840
|
+
...(hookTools
|
|
841
|
+
? { tools: hookTools, permissions: hookTools }
|
|
842
|
+
: {
|
|
843
|
+
...(structuredTools ? { tools: structuredTools } : {}),
|
|
844
|
+
...(turnPermissions ? { permissions: turnPermissions } : {}),
|
|
845
|
+
}),
|
|
846
|
+
});
|
|
847
|
+
let currentTurnUserMessages = null;
|
|
848
|
+
const observer = {
|
|
849
|
+
assistantCompleted: async (message) => {
|
|
850
|
+
const [entry] = translateProviderEvents([
|
|
851
|
+
{
|
|
852
|
+
type: 'assistant-message',
|
|
853
|
+
text: message.content,
|
|
854
|
+
...(message.thinkingBlocks
|
|
855
|
+
? { thinkingBlocks: message.thinkingBlocks }
|
|
856
|
+
: {}),
|
|
857
|
+
toolCalls: message.toolCalls ?? [],
|
|
858
|
+
providerMessageId: `msg_${randomUUID().replaceAll('-', '')}`,
|
|
859
|
+
model: provider.model ?? 'praxis/provider',
|
|
860
|
+
},
|
|
861
|
+
], this.translationContext(sessionId, snapshot));
|
|
862
|
+
if (!entry)
|
|
863
|
+
throw new Error('Could not translate assistant response');
|
|
864
|
+
const tail = await this.append(lease, snapshot.tail, entry);
|
|
865
|
+
snapshot = { entries: [...snapshot.entries, entry], tail };
|
|
866
|
+
lastAssistantUuid =
|
|
867
|
+
typeof entry.uuid === 'string' ? entry.uuid : lastAssistantUuid;
|
|
868
|
+
},
|
|
869
|
+
toolCompleted: async (call, toolResult) => {
|
|
870
|
+
const transition = this.worktreeManager?.consumeTransition(call.id);
|
|
871
|
+
if (transition) {
|
|
872
|
+
const stateEntry = {
|
|
873
|
+
type: 'worktree-state',
|
|
874
|
+
worktreeSession: transition.state,
|
|
875
|
+
sessionId,
|
|
876
|
+
};
|
|
877
|
+
const stateTail = await this.append(lease, snapshot.tail, stateEntry);
|
|
878
|
+
snapshot = {
|
|
879
|
+
entries: [...snapshot.entries, stateEntry],
|
|
880
|
+
tail: stateTail,
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
const permissionMode = this.options.interactiveTools?.consumeTransition(call.id);
|
|
884
|
+
if (permissionMode) {
|
|
885
|
+
const modeEntry = {
|
|
886
|
+
type: 'permission-mode',
|
|
887
|
+
permissionMode,
|
|
888
|
+
sessionId,
|
|
889
|
+
};
|
|
890
|
+
const modeTail = await this.append(lease, snapshot.tail, modeEntry);
|
|
891
|
+
snapshot = {
|
|
892
|
+
entries: [...snapshot.entries, modeEntry],
|
|
893
|
+
tail: modeTail,
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
const [entry] = translateProviderEvents([
|
|
897
|
+
{
|
|
898
|
+
type: 'tool-result',
|
|
899
|
+
toolCallId: call.id,
|
|
900
|
+
content: toolResult.content,
|
|
901
|
+
...(toolResult.contentBlocks
|
|
902
|
+
? { contentBlocks: toolResult.contentBlocks }
|
|
903
|
+
: {}),
|
|
904
|
+
...(toolResult.images ? { images: toolResult.images } : {}),
|
|
905
|
+
...(toolResult.documents
|
|
906
|
+
? { documents: toolResult.documents }
|
|
907
|
+
: {}),
|
|
908
|
+
isError: toolResult.isError,
|
|
909
|
+
...(toolResult.nativeToolUseResult
|
|
910
|
+
? { nativeToolUseResult: toolResult.nativeToolUseResult }
|
|
911
|
+
: {}),
|
|
912
|
+
...(toolResult.nativeMcpMeta
|
|
913
|
+
? { nativeMcpMeta: toolResult.nativeMcpMeta }
|
|
914
|
+
: {}),
|
|
915
|
+
},
|
|
916
|
+
], this.translationContext(sessionId, snapshot));
|
|
917
|
+
if (!entry)
|
|
918
|
+
throw new Error('Could not translate tool result');
|
|
919
|
+
const tail = await this.append(lease, snapshot.tail, entry);
|
|
920
|
+
snapshot = { entries: [...snapshot.entries, entry], tail };
|
|
921
|
+
if (toolResult.isError ||
|
|
922
|
+
call.name !== 'Read' ||
|
|
923
|
+
!this.options.conditionalRuleResolver ||
|
|
924
|
+
!toolResult.accessedPaths) {
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
const attachedRulePaths = this.attachedRulePaths(snapshot.entries);
|
|
928
|
+
for (const filePath of toolResult.accessedPaths) {
|
|
929
|
+
const rules = await this.options.conditionalRuleResolver.resolve(filePath, [...attachedRulePaths]);
|
|
930
|
+
for (const rule of rules) {
|
|
931
|
+
const attachment = createClaudeRuleAttachmentEntry(rule, this.displayRulePath(rule.path), this.translationContext(sessionId, snapshot));
|
|
932
|
+
const attachmentTail = await this.append(lease, snapshot.tail, attachment);
|
|
933
|
+
snapshot = {
|
|
934
|
+
entries: [...snapshot.entries, attachment],
|
|
935
|
+
tail: attachmentTail,
|
|
936
|
+
};
|
|
937
|
+
attachedRulePaths.add(rule.path);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
},
|
|
941
|
+
followUpUserMessagesCompleted: async (messages) => {
|
|
942
|
+
for (const content of messages) {
|
|
943
|
+
const [followUpEntry] = translateProviderEvents([{ type: 'user-text-block', text: content }], this.translationContext(sessionId, snapshot));
|
|
944
|
+
if (!followUpEntry) {
|
|
945
|
+
throw new Error('Could not translate tool follow-up message');
|
|
946
|
+
}
|
|
947
|
+
const followUpTail = await this.append(lease, snapshot.tail, followUpEntry);
|
|
948
|
+
snapshot = {
|
|
949
|
+
entries: [...snapshot.entries, followUpEntry],
|
|
950
|
+
tail: followUpTail,
|
|
951
|
+
};
|
|
952
|
+
currentTurnUserMessages?.push(content);
|
|
953
|
+
}
|
|
954
|
+
},
|
|
955
|
+
};
|
|
956
|
+
try {
|
|
957
|
+
if (this.options.hooks) {
|
|
958
|
+
const outcome = await this.options.hooks.run({
|
|
959
|
+
...hookSession,
|
|
960
|
+
hook_event_name: 'SessionStart',
|
|
961
|
+
source: requireExisting ? 'resume' : 'startup',
|
|
962
|
+
}, requireExisting ? 'resume' : 'startup', signal);
|
|
963
|
+
await recordHookOutcome(outcome, pendingRecoveryToolCallIds.size > 0);
|
|
964
|
+
if (outcome.blockedReason) {
|
|
965
|
+
throw new Error(`SessionStart hook error: ${outcome.blockedReason}`);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
const approveRecovery = this.options.approveRecovery;
|
|
969
|
+
const recoveryRequest = {
|
|
970
|
+
cwd: this.activeCwd(),
|
|
971
|
+
toolResultDirectory,
|
|
972
|
+
messages: projectClaudeModelMessages(snapshot.entries),
|
|
973
|
+
observer,
|
|
974
|
+
...(signal ? { signal } : {}),
|
|
975
|
+
...(approveRecovery
|
|
976
|
+
? {
|
|
977
|
+
approveRecovery: async (call) => {
|
|
978
|
+
if (!(await approveRecovery(call))) {
|
|
979
|
+
throw new Error(`Claude session tool call ${call.id} recovery was declined`);
|
|
980
|
+
}
|
|
981
|
+
if (signal?.aborted)
|
|
982
|
+
throw new AgentRunCancelledError();
|
|
983
|
+
await flushRecoveryHookOutcomes();
|
|
984
|
+
pendingRecoveryToolCallIds.delete(call.id);
|
|
985
|
+
return true;
|
|
986
|
+
},
|
|
987
|
+
approveTool: () => true,
|
|
988
|
+
}
|
|
989
|
+
: {}),
|
|
990
|
+
};
|
|
991
|
+
const unresolvedToolCall = unresolvedToolCalls[0];
|
|
992
|
+
if (unresolvedToolCall && !approveRecovery) {
|
|
993
|
+
throw new Error(`Claude session tool call ${unresolvedToolCall.id} requires explicit recovery approval`);
|
|
994
|
+
}
|
|
995
|
+
const recoveryResults = await runtime.recoverToolCalls(unresolvedToolCalls, recoveryRequest);
|
|
996
|
+
const recoveryUsage = recoveryResults.reduce((usage, result) => ({
|
|
997
|
+
inputTokens: usage.inputTokens + (result.usage?.inputTokens ?? 0),
|
|
998
|
+
outputTokens: usage.outputTokens + (result.usage?.outputTokens ?? 0),
|
|
999
|
+
}), { inputTokens: 0, outputTokens: 0 });
|
|
1000
|
+
const agentName = this.options.agent ?? getClaudeAgentSetting(snapshot.entries);
|
|
1001
|
+
const agent = agentName
|
|
1002
|
+
? this.options.extensions?.agent(agentName)
|
|
1003
|
+
: null;
|
|
1004
|
+
if (agentName && !agent) {
|
|
1005
|
+
throw new Error(`Unknown Claude agent ${agentName}`);
|
|
1006
|
+
}
|
|
1007
|
+
if (this.options.agent &&
|
|
1008
|
+
getClaudeAgentSetting(snapshot.entries) !== this.options.agent) {
|
|
1009
|
+
const agentSetting = createClaudeAgentSettingEntry(sessionId, this.options.agent);
|
|
1010
|
+
const settingTail = await this.append(lease, snapshot.tail, agentSetting);
|
|
1011
|
+
snapshot = {
|
|
1012
|
+
entries: [...snapshot.entries, agentSetting],
|
|
1013
|
+
tail: settingTail,
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
const assembledContext = await this.options.contextAssembler?.assemble({
|
|
1017
|
+
cwd: this.activeCwd(),
|
|
1018
|
+
});
|
|
1019
|
+
const planModeMessage = this.options.interactiveTools?.contextMessage(sessionId);
|
|
1020
|
+
const contextMessages = [
|
|
1021
|
+
...(assembledContext?.systemMessages ?? []),
|
|
1022
|
+
...(planModeMessage
|
|
1023
|
+
? [{ role: 'system', content: planModeMessage }]
|
|
1024
|
+
: []),
|
|
1025
|
+
...(this.options.brief
|
|
1026
|
+
? [
|
|
1027
|
+
{
|
|
1028
|
+
role: 'system',
|
|
1029
|
+
content: CLAUDE_USER_MESSAGE_PROMPT,
|
|
1030
|
+
},
|
|
1031
|
+
]
|
|
1032
|
+
: []),
|
|
1033
|
+
...(agent
|
|
1034
|
+
? [
|
|
1035
|
+
{
|
|
1036
|
+
role: 'system',
|
|
1037
|
+
content: `# Agent definition: ${agent.name}\n\n${agent.body}`,
|
|
1038
|
+
},
|
|
1039
|
+
]
|
|
1040
|
+
: []),
|
|
1041
|
+
...(this.options.structuredOutputSchema
|
|
1042
|
+
? [
|
|
1043
|
+
{
|
|
1044
|
+
role: 'system',
|
|
1045
|
+
content: 'You MUST call StructuredOutput exactly once at the end with a value matching the requested JSON Schema.',
|
|
1046
|
+
},
|
|
1047
|
+
]
|
|
1048
|
+
: []),
|
|
1049
|
+
];
|
|
1050
|
+
const expansion = this.options.extensions
|
|
1051
|
+
? await this.options.extensions.expandPromptAsync(prompt, signal, toolResultDirectory)
|
|
1052
|
+
: { userMessages: [prompt] };
|
|
1053
|
+
const expandedMessages = expansion.messages ?? expansion.userMessages.map((text) => ({ text }));
|
|
1054
|
+
const attachmentIndex = expansion.messages
|
|
1055
|
+
? expandedMessages.length - 1
|
|
1056
|
+
: 0;
|
|
1057
|
+
currentTurnUserMessages = [...expansion.userMessages];
|
|
1058
|
+
this.options.eventSink?.({
|
|
1059
|
+
type: 'state',
|
|
1060
|
+
state: 'assembling-context',
|
|
1061
|
+
});
|
|
1062
|
+
let compactionUsage = {
|
|
1063
|
+
inputTokens: 0,
|
|
1064
|
+
outputTokens: 0,
|
|
1065
|
+
};
|
|
1066
|
+
const definitions = provider.capabilities.tools
|
|
1067
|
+
? (structuredTools?.definitions() ?? [])
|
|
1068
|
+
: [];
|
|
1069
|
+
const budget = this.contextBudget(provider);
|
|
1070
|
+
const pendingUserMessages = expandedMessages.map((message, index) => ({
|
|
1071
|
+
role: 'user',
|
|
1072
|
+
content: message.text,
|
|
1073
|
+
...(message.contentBlocks?.length
|
|
1074
|
+
? { contentBlocks: message.contentBlocks }
|
|
1075
|
+
: {}),
|
|
1076
|
+
...((index === attachmentIndex && images.length > 0) ||
|
|
1077
|
+
message.images?.length
|
|
1078
|
+
? {
|
|
1079
|
+
images: [
|
|
1080
|
+
...(index === attachmentIndex ? images : []),
|
|
1081
|
+
...(message.images ?? []),
|
|
1082
|
+
],
|
|
1083
|
+
}
|
|
1084
|
+
: {}),
|
|
1085
|
+
...(index === attachmentIndex && documents.length > 0
|
|
1086
|
+
? { documents }
|
|
1087
|
+
: {}),
|
|
1088
|
+
}));
|
|
1089
|
+
const injectDynamicContext = (messages) => injectFirstUserMessageContext(messages, assembledContext?.firstUserMessageContext);
|
|
1090
|
+
let compactionAnchorUuid = this.lastMessageUuid(snapshot.entries);
|
|
1091
|
+
const compactIfNeeded = async (pendingMessages = [], preservedUserMessages = []) => {
|
|
1092
|
+
if (!budget)
|
|
1093
|
+
return;
|
|
1094
|
+
const historyMessages = projectClaudeModelMessages(snapshot.entries);
|
|
1095
|
+
const predicted = budget.evaluate([
|
|
1096
|
+
...contextMessages,
|
|
1097
|
+
...injectDynamicContext([...historyMessages, ...pendingMessages]),
|
|
1098
|
+
], definitions);
|
|
1099
|
+
if (!predicted.shouldCompact)
|
|
1100
|
+
return;
|
|
1101
|
+
const irreducibleMessages = [
|
|
1102
|
+
...contextMessages,
|
|
1103
|
+
...injectDynamicContext([
|
|
1104
|
+
...pendingMessages,
|
|
1105
|
+
...preservedUserMessages.map((content) => ({
|
|
1106
|
+
role: 'user',
|
|
1107
|
+
content,
|
|
1108
|
+
})),
|
|
1109
|
+
]),
|
|
1110
|
+
];
|
|
1111
|
+
const irreducible = budget.evaluate(irreducibleMessages, definitions);
|
|
1112
|
+
budget.assertFits(irreducible);
|
|
1113
|
+
const logicalParentUuid = compactionAnchorUuid;
|
|
1114
|
+
if (!logicalParentUuid || historyMessages.length === 0) {
|
|
1115
|
+
budget.assertFits(predicted);
|
|
1116
|
+
throw new Error('Cannot compact an empty Claude transcript');
|
|
1117
|
+
}
|
|
1118
|
+
if (findUnresolvedClaudeToolCalls(snapshot.entries).length > 0) {
|
|
1119
|
+
throw new Error('Cannot compact a Claude session with unresolved tool calls');
|
|
1120
|
+
}
|
|
1121
|
+
this.options.eventSink?.({ type: 'state', state: 'compacting' });
|
|
1122
|
+
const compactEnvelope = budget.evaluate([
|
|
1123
|
+
...irreducibleMessages,
|
|
1124
|
+
{
|
|
1125
|
+
role: 'user',
|
|
1126
|
+
content: formatClaudeCompactSummary(''),
|
|
1127
|
+
},
|
|
1128
|
+
], definitions);
|
|
1129
|
+
let targetTokens = Math.min(8192, compactEnvelope.availableTokens - compactEnvelope.estimatedTokens);
|
|
1130
|
+
if (targetTokens < 1) {
|
|
1131
|
+
budget.assertFits(budget.evaluate([
|
|
1132
|
+
...irreducibleMessages,
|
|
1133
|
+
{
|
|
1134
|
+
role: 'user',
|
|
1135
|
+
content: formatClaudeCompactSummary('a'),
|
|
1136
|
+
},
|
|
1137
|
+
], definitions));
|
|
1138
|
+
targetTokens = 1;
|
|
1139
|
+
}
|
|
1140
|
+
const compacted = await (this.options.compactor ?? new ModelCompactor(provider)).compact({
|
|
1141
|
+
messages: historyMessages,
|
|
1142
|
+
targetTokens,
|
|
1143
|
+
contextWindowTokens: budget.contextWindowTokens,
|
|
1144
|
+
...(signal ? { signal } : {}),
|
|
1145
|
+
});
|
|
1146
|
+
const boundaryUuid = randomUUID();
|
|
1147
|
+
const summaryUuid = randomUUID();
|
|
1148
|
+
const timestamp = new Date().toISOString();
|
|
1149
|
+
const preTokens = budget.evaluate([...contextMessages, ...injectDynamicContext(historyMessages)], definitions).estimatedTokens;
|
|
1150
|
+
const compactEntries = (postTokens) => {
|
|
1151
|
+
const uuids = [boundaryUuid, summaryUuid];
|
|
1152
|
+
return createClaudeCompactEntries({
|
|
1153
|
+
sessionId,
|
|
1154
|
+
logicalParentUuid,
|
|
1155
|
+
summary: compacted.summary,
|
|
1156
|
+
preTokens,
|
|
1157
|
+
postTokens,
|
|
1158
|
+
previousCumulativeDroppedTokens: getCumulativeDroppedTokens(snapshot.entries),
|
|
1159
|
+
durationMs: compacted.durationMs,
|
|
1160
|
+
cwd: this.activeCwd(),
|
|
1161
|
+
claudeVersion: this.options.claudeVersion,
|
|
1162
|
+
gitBranch: null,
|
|
1163
|
+
createUuid: () => uuids.shift() ?? randomUUID(),
|
|
1164
|
+
now: () => timestamp,
|
|
1165
|
+
});
|
|
1166
|
+
};
|
|
1167
|
+
const provisionalEntries = compactEntries(0);
|
|
1168
|
+
const compactSummaryUuid = provisionalEntries.at(-1)?.uuid;
|
|
1169
|
+
if (typeof compactSummaryUuid !== 'string') {
|
|
1170
|
+
throw new Error('Could not create Claude compact summary');
|
|
1171
|
+
}
|
|
1172
|
+
const replayUuids = preservedUserMessages.map(() => randomUUID());
|
|
1173
|
+
const replayEntries = translateProviderEvents(preservedUserMessages.map((text, index) => index === 0
|
|
1174
|
+
? { type: 'user-text', text }
|
|
1175
|
+
: { type: 'user-text-block', text }), {
|
|
1176
|
+
sessionId,
|
|
1177
|
+
parentUuid: compactSummaryUuid,
|
|
1178
|
+
cwd: this.activeCwd(),
|
|
1179
|
+
claudeVersion: this.options.claudeVersion,
|
|
1180
|
+
gitBranch: null,
|
|
1181
|
+
history: [...snapshot.entries, ...provisionalEntries],
|
|
1182
|
+
createUuid: () => replayUuids.shift() ?? randomUUID(),
|
|
1183
|
+
now: () => timestamp,
|
|
1184
|
+
});
|
|
1185
|
+
const compactedHistory = projectClaudeModelMessages([
|
|
1186
|
+
...snapshot.entries,
|
|
1187
|
+
...provisionalEntries,
|
|
1188
|
+
...replayEntries,
|
|
1189
|
+
]);
|
|
1190
|
+
const afterHistory = budget.evaluate([...contextMessages, ...injectDynamicContext(compactedHistory)], definitions);
|
|
1191
|
+
const afterPending = budget.evaluate([
|
|
1192
|
+
...contextMessages,
|
|
1193
|
+
...injectDynamicContext([
|
|
1194
|
+
...compactedHistory,
|
|
1195
|
+
...pendingMessages,
|
|
1196
|
+
]),
|
|
1197
|
+
], definitions);
|
|
1198
|
+
budget.assertFits(afterPending);
|
|
1199
|
+
const entries = [
|
|
1200
|
+
...compactEntries(afterHistory.estimatedTokens),
|
|
1201
|
+
...replayEntries,
|
|
1202
|
+
];
|
|
1203
|
+
if (signal?.aborted)
|
|
1204
|
+
throw new AgentRunCancelledError();
|
|
1205
|
+
const appendResult = await lease.appendMany(snapshot.tail, entries);
|
|
1206
|
+
if (appendResult.status === 'conflict') {
|
|
1207
|
+
throw new Error(`Claude transcript append conflict: ${appendResult.reason}`);
|
|
1208
|
+
}
|
|
1209
|
+
snapshot = {
|
|
1210
|
+
entries: [...snapshot.entries, ...entries],
|
|
1211
|
+
tail: appendResult.tail,
|
|
1212
|
+
};
|
|
1213
|
+
this.options.eventSink?.({
|
|
1214
|
+
type: 'compact-boundary',
|
|
1215
|
+
trigger: 'auto',
|
|
1216
|
+
preTokens,
|
|
1217
|
+
uuid: boundaryUuid,
|
|
1218
|
+
});
|
|
1219
|
+
compactionAnchorUuid = compactSummaryUuid;
|
|
1220
|
+
compactionUsage = {
|
|
1221
|
+
inputTokens: compactionUsage.inputTokens + compacted.usage.inputTokens,
|
|
1222
|
+
outputTokens: compactionUsage.outputTokens + compacted.usage.outputTokens,
|
|
1223
|
+
};
|
|
1224
|
+
};
|
|
1225
|
+
await compactIfNeeded(pendingUserMessages);
|
|
1226
|
+
for (const [index, message] of expandedMessages.entries()) {
|
|
1227
|
+
const messageImages = [
|
|
1228
|
+
...(index === attachmentIndex ? images : []),
|
|
1229
|
+
...(message.images ?? []),
|
|
1230
|
+
];
|
|
1231
|
+
const [userEntry] = translateProviderEvents([
|
|
1232
|
+
messageImages.length > 0 ||
|
|
1233
|
+
(index === attachmentIndex && documents.length > 0)
|
|
1234
|
+
? {
|
|
1235
|
+
type: 'user-message',
|
|
1236
|
+
text: message.text,
|
|
1237
|
+
images: messageImages,
|
|
1238
|
+
...(index === attachmentIndex && documents.length > 0
|
|
1239
|
+
? { documents }
|
|
1240
|
+
: {}),
|
|
1241
|
+
}
|
|
1242
|
+
: index === 0
|
|
1243
|
+
? { type: 'user-text', text: message.text }
|
|
1244
|
+
: { type: 'user-text-block', text: message.text },
|
|
1245
|
+
], this.translationContext(sessionId, snapshot));
|
|
1246
|
+
if (!userEntry)
|
|
1247
|
+
throw new Error('Could not translate user prompt');
|
|
1248
|
+
if (currentPromptId === null && typeof userEntry.uuid === 'string') {
|
|
1249
|
+
currentPromptId = userEntry.uuid;
|
|
1250
|
+
compactionAnchorUuid ??= userEntry.uuid;
|
|
1251
|
+
}
|
|
1252
|
+
const userTail = await this.append(lease, snapshot.tail, userEntry);
|
|
1253
|
+
snapshot = {
|
|
1254
|
+
entries: [...snapshot.entries, userEntry],
|
|
1255
|
+
tail: userTail,
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
if (fileHistory && currentPromptId) {
|
|
1259
|
+
const historySnapshot = await fileHistory.snapshot(snapshot.entries, currentPromptId);
|
|
1260
|
+
const historyTail = await this.append(lease, snapshot.tail, historySnapshot);
|
|
1261
|
+
snapshot = {
|
|
1262
|
+
entries: [...snapshot.entries, historySnapshot],
|
|
1263
|
+
tail: historyTail,
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
if (this.options.hooks) {
|
|
1267
|
+
const outcome = await this.options.hooks.run({
|
|
1268
|
+
...hookSession,
|
|
1269
|
+
hook_event_name: 'UserPromptSubmit',
|
|
1270
|
+
prompt_id: currentPromptId ?? randomUUID(),
|
|
1271
|
+
prompt,
|
|
1272
|
+
}, undefined, signal);
|
|
1273
|
+
await recordHookOutcome(outcome);
|
|
1274
|
+
if (outcome.blockedReason) {
|
|
1275
|
+
throw new Error(`UserPromptSubmit hook error: ${outcome.blockedReason}`);
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
if (budget) {
|
|
1279
|
+
await compactIfNeeded([], currentTurnUserMessages ?? []);
|
|
1280
|
+
budget.assertFits(budget.evaluate([
|
|
1281
|
+
...contextMessages,
|
|
1282
|
+
...injectDynamicContext(projectClaudeModelMessages(snapshot.entries)),
|
|
1283
|
+
], definitions));
|
|
1284
|
+
}
|
|
1285
|
+
let stopHookActive = false;
|
|
1286
|
+
const runtimeRequest = {
|
|
1287
|
+
messages: [
|
|
1288
|
+
...contextMessages,
|
|
1289
|
+
...injectDynamicContext(projectClaudeModelMessages(snapshot.entries)),
|
|
1290
|
+
],
|
|
1291
|
+
cwd: this.activeCwd(),
|
|
1292
|
+
toolResultDirectory,
|
|
1293
|
+
observer,
|
|
1294
|
+
...(this.options.effort ? { effort: this.options.effort } : {}),
|
|
1295
|
+
...(this.options.maxModelTurns
|
|
1296
|
+
? { maxModelTurns: this.options.maxModelTurns }
|
|
1297
|
+
: {}),
|
|
1298
|
+
...(this.options.betas?.length ? { betas: this.options.betas } : {}),
|
|
1299
|
+
...(this.options.collectMetrics ? { collectMetrics: true } : {}),
|
|
1300
|
+
reloadMessages: async () => {
|
|
1301
|
+
await compactIfNeeded([], currentTurnUserMessages ?? []);
|
|
1302
|
+
return [
|
|
1303
|
+
...contextMessages,
|
|
1304
|
+
...injectDynamicContext(projectClaudeModelMessages(snapshot.entries)),
|
|
1305
|
+
];
|
|
1306
|
+
},
|
|
1307
|
+
...(this.options.hooks ||
|
|
1308
|
+
subagentExecutor ||
|
|
1309
|
+
taskTools ||
|
|
1310
|
+
this.scheduledPrompts ||
|
|
1311
|
+
this.workflowManager
|
|
1312
|
+
? {
|
|
1313
|
+
onStop: async (text) => {
|
|
1314
|
+
const messages = [];
|
|
1315
|
+
const outcome = await this.options.hooks?.run({
|
|
1316
|
+
...hookSession,
|
|
1317
|
+
hook_event_name: 'Stop',
|
|
1318
|
+
stop_hook_active: stopHookActive,
|
|
1319
|
+
last_assistant_message: text,
|
|
1320
|
+
}, undefined, signal);
|
|
1321
|
+
if (outcome) {
|
|
1322
|
+
await recordHookOutcome(outcome);
|
|
1323
|
+
if (outcome.blockedReason) {
|
|
1324
|
+
stopHookActive = true;
|
|
1325
|
+
messages.push(`Stop hook error: ${outcome.blockedReason}`);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
const background = await subagentExecutor?.notifications(true);
|
|
1329
|
+
if (background)
|
|
1330
|
+
messages.push(...background.messages);
|
|
1331
|
+
const workflow = await this.workflowManager?.notifications(true);
|
|
1332
|
+
if (workflow)
|
|
1333
|
+
messages.push(...workflow.messages);
|
|
1334
|
+
const bashMessages = await taskTools?.notifications(true);
|
|
1335
|
+
if (bashMessages)
|
|
1336
|
+
messages.push(...bashMessages);
|
|
1337
|
+
const scheduled = await this.scheduledPrompts?.drainDue();
|
|
1338
|
+
if (scheduled) {
|
|
1339
|
+
messages.push(...scheduled.map(({ prompt }) => prompt));
|
|
1340
|
+
}
|
|
1341
|
+
return {
|
|
1342
|
+
messages,
|
|
1343
|
+
...(background || workflow
|
|
1344
|
+
? {
|
|
1345
|
+
usage: {
|
|
1346
|
+
inputTokens: (background?.usage.inputTokens ?? 0) +
|
|
1347
|
+
(workflow?.usage.inputTokens ?? 0),
|
|
1348
|
+
outputTokens: (background?.usage.outputTokens ?? 0) +
|
|
1349
|
+
(workflow?.usage.outputTokens ?? 0),
|
|
1350
|
+
},
|
|
1351
|
+
}
|
|
1352
|
+
: {}),
|
|
1353
|
+
};
|
|
1354
|
+
},
|
|
1355
|
+
}
|
|
1356
|
+
: {}),
|
|
1357
|
+
...(this.options.approveTool
|
|
1358
|
+
? { approveTool: this.options.approveTool }
|
|
1359
|
+
: {}),
|
|
1360
|
+
};
|
|
1361
|
+
const result = signal
|
|
1362
|
+
? await runtime.run({ ...runtimeRequest, signal })
|
|
1363
|
+
: await runtime.run(runtimeRequest);
|
|
1364
|
+
if (structuredCapture && structuredCapture.calls !== 1) {
|
|
1365
|
+
throw new Error(`StructuredOutput must be called exactly once (received ${structuredCapture.calls})`);
|
|
1366
|
+
}
|
|
1367
|
+
const finalLeafUuid = lastAssistantUuid;
|
|
1368
|
+
if (!finalLeafUuid) {
|
|
1369
|
+
throw new Error('Could not locate final assistant response');
|
|
1370
|
+
}
|
|
1371
|
+
await this.append(lease, snapshot.tail, createClaudeLastPromptEntry({
|
|
1372
|
+
sessionId,
|
|
1373
|
+
lastPrompt: prompt,
|
|
1374
|
+
leafUuid: finalLeafUuid,
|
|
1375
|
+
}));
|
|
1376
|
+
const totalUsage = mergeUsage(mergeUsage(recoveryUsage, compactionUsage), result.usage);
|
|
1377
|
+
return {
|
|
1378
|
+
sessionId,
|
|
1379
|
+
text: structuredCapture && structuredCapture.calls === 1
|
|
1380
|
+
? JSON.stringify(structuredCapture.value)
|
|
1381
|
+
: result.text,
|
|
1382
|
+
usage: totalUsage,
|
|
1383
|
+
...(result.durationApiMs === undefined
|
|
1384
|
+
? {}
|
|
1385
|
+
: { durationApiMs: result.durationApiMs }),
|
|
1386
|
+
...(this.options.pricing
|
|
1387
|
+
? (() => {
|
|
1388
|
+
const pricing = this.options.pricing?.resolve(provider.model ?? 'praxis/provider');
|
|
1389
|
+
return pricing
|
|
1390
|
+
? { costUsd: usageCostUsd(result.usage, pricing) }
|
|
1391
|
+
: {};
|
|
1392
|
+
})()
|
|
1393
|
+
: {}),
|
|
1394
|
+
...(provider.model
|
|
1395
|
+
? { modelUsage: { [provider.model]: result.usage } }
|
|
1396
|
+
: {}),
|
|
1397
|
+
...(structuredCapture && structuredCapture.calls === 1
|
|
1398
|
+
? { structuredOutput: structuredCapture.value }
|
|
1399
|
+
: {}),
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
finally {
|
|
1403
|
+
try {
|
|
1404
|
+
const outcome = await this.options.hooks?.run({
|
|
1405
|
+
...hookSession,
|
|
1406
|
+
hook_event_name: 'SessionEnd',
|
|
1407
|
+
reason: 'other',
|
|
1408
|
+
}, 'other');
|
|
1409
|
+
const failedExecutions = outcome?.executions.filter((execution) => execution.exitCode !== 0) ?? [];
|
|
1410
|
+
for (const execution of failedExecutions) {
|
|
1411
|
+
const detail = execution.stderr.trim() ||
|
|
1412
|
+
execution.stdout.trim() ||
|
|
1413
|
+
`exit code ${execution.exitCode}`;
|
|
1414
|
+
this.options.eventSink?.({
|
|
1415
|
+
type: 'warning',
|
|
1416
|
+
message: `SessionEnd hook failed: ${detail}`,
|
|
1417
|
+
});
|
|
1418
|
+
}
|
|
1419
|
+
if (outcome?.blockedReason &&
|
|
1420
|
+
outcome.executions.at(-1)?.exitCode === 0) {
|
|
1421
|
+
this.options.eventSink?.({
|
|
1422
|
+
type: 'warning',
|
|
1423
|
+
message: `SessionEnd hook failed: ${outcome.blockedReason}`,
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
catch (error) {
|
|
1428
|
+
this.options.eventSink?.({
|
|
1429
|
+
type: 'warning',
|
|
1430
|
+
message: `SessionEnd hook failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
});
|
|
1435
|
+
if (leaseResult.status === 'conflict') {
|
|
1436
|
+
throw new Error(`Claude transcript append conflict: ${leaseResult.reason}`);
|
|
1437
|
+
}
|
|
1438
|
+
return leaseResult.value;
|
|
1439
|
+
}
|
|
1440
|
+
async ensureFileResources(sessionId, signal) {
|
|
1441
|
+
const resources = this.options.fileResources ?? [];
|
|
1442
|
+
if (resources.length === 0 ||
|
|
1443
|
+
this.downloadedFileResourceSessions.has(sessionId)) {
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
const config = this.options.fileResourceConfig;
|
|
1447
|
+
if (!config) {
|
|
1448
|
+
throw new Error('--file requires PRAXIS_FILES_BEARER_TOKEN, PRAXIS_FILES_API_KEY, or PRAXIS_API_KEY');
|
|
1449
|
+
}
|
|
1450
|
+
const downloads = await downloadClaudeFileResources(resources, {
|
|
1451
|
+
...config,
|
|
1452
|
+
sessionId,
|
|
1453
|
+
...(signal ? { signal } : {}),
|
|
1454
|
+
});
|
|
1455
|
+
this.downloadedFileResourceSessions.add(sessionId);
|
|
1456
|
+
for (const download of downloads) {
|
|
1457
|
+
if (download.success)
|
|
1458
|
+
continue;
|
|
1459
|
+
this.options.eventSink?.({
|
|
1460
|
+
type: 'warning',
|
|
1461
|
+
message: `File ${download.fileId} failed to download: ${download.error ?? 'unknown error'}`,
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
translationContext(sessionId, snapshot) {
|
|
1466
|
+
return {
|
|
1467
|
+
sessionId,
|
|
1468
|
+
parentUuid: this.logicalTailUuid(snapshot.tail),
|
|
1469
|
+
cwd: this.activeCwd(),
|
|
1470
|
+
claudeVersion: this.options.claudeVersion,
|
|
1471
|
+
gitBranch: null,
|
|
1472
|
+
...(this.options.sessionKind === undefined
|
|
1473
|
+
? {}
|
|
1474
|
+
: { sessionKind: this.options.sessionKind }),
|
|
1475
|
+
history: snapshot.entries,
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
attachedRulePaths(entries) {
|
|
1479
|
+
const paths = new Set();
|
|
1480
|
+
for (const entry of entries) {
|
|
1481
|
+
if (entry.type !== 'attachment')
|
|
1482
|
+
continue;
|
|
1483
|
+
const attachment = entry.attachment;
|
|
1484
|
+
if (typeof attachment !== 'object' ||
|
|
1485
|
+
attachment === null ||
|
|
1486
|
+
Array.isArray(attachment)) {
|
|
1487
|
+
continue;
|
|
1488
|
+
}
|
|
1489
|
+
const path = attachment.path;
|
|
1490
|
+
if (typeof path === 'string')
|
|
1491
|
+
paths.add(path);
|
|
1492
|
+
}
|
|
1493
|
+
return paths;
|
|
1494
|
+
}
|
|
1495
|
+
sessionNameEntries(sessionId, name) {
|
|
1496
|
+
return [
|
|
1497
|
+
{ type: 'custom-title', customTitle: name, sessionId },
|
|
1498
|
+
{ type: 'agent-name', agentName: name, sessionId },
|
|
1499
|
+
];
|
|
1500
|
+
}
|
|
1501
|
+
hasSessionName(entries, name) {
|
|
1502
|
+
return this.sessionName(entries) === name;
|
|
1503
|
+
}
|
|
1504
|
+
sessionName(entries) {
|
|
1505
|
+
let customTitle;
|
|
1506
|
+
let agentName;
|
|
1507
|
+
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
1508
|
+
const entry = entries[index];
|
|
1509
|
+
if (customTitle === undefined && entry?.type === 'custom-title') {
|
|
1510
|
+
customTitle = entry.customTitle;
|
|
1511
|
+
}
|
|
1512
|
+
if (agentName === undefined && entry?.type === 'agent-name') {
|
|
1513
|
+
agentName = entry.agentName;
|
|
1514
|
+
}
|
|
1515
|
+
if (customTitle !== undefined && agentName !== undefined)
|
|
1516
|
+
break;
|
|
1517
|
+
}
|
|
1518
|
+
if (typeof customTitle === 'string' && customTitle === agentName) {
|
|
1519
|
+
return customTitle;
|
|
1520
|
+
}
|
|
1521
|
+
if (typeof customTitle === 'string')
|
|
1522
|
+
return customTitle;
|
|
1523
|
+
if (typeof agentName === 'string')
|
|
1524
|
+
return agentName;
|
|
1525
|
+
return null;
|
|
1526
|
+
}
|
|
1527
|
+
lastMessageUuid(entries) {
|
|
1528
|
+
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
1529
|
+
const entry = entries[index];
|
|
1530
|
+
if (entry &&
|
|
1531
|
+
typeof entry.uuid === 'string' &&
|
|
1532
|
+
typeof entry.message === 'object' &&
|
|
1533
|
+
entry.message !== null &&
|
|
1534
|
+
!Array.isArray(entry.message)) {
|
|
1535
|
+
return entry.uuid;
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
return null;
|
|
1539
|
+
}
|
|
1540
|
+
promptIdForToolCall(entries, callId) {
|
|
1541
|
+
const byUuid = new Map();
|
|
1542
|
+
let source;
|
|
1543
|
+
for (const entry of entries) {
|
|
1544
|
+
if (typeof entry.uuid === 'string')
|
|
1545
|
+
byUuid.set(entry.uuid, entry);
|
|
1546
|
+
if (entry.type !== 'assistant' ||
|
|
1547
|
+
typeof entry.message !== 'object' ||
|
|
1548
|
+
entry.message === null ||
|
|
1549
|
+
Array.isArray(entry.message)) {
|
|
1550
|
+
continue;
|
|
1551
|
+
}
|
|
1552
|
+
const message = entry.message;
|
|
1553
|
+
if (typeof message !== 'object' ||
|
|
1554
|
+
message === null ||
|
|
1555
|
+
!Array.isArray(message.content)) {
|
|
1556
|
+
continue;
|
|
1557
|
+
}
|
|
1558
|
+
const content = message.content;
|
|
1559
|
+
if (content.some((block) => typeof block === 'object' &&
|
|
1560
|
+
block !== null &&
|
|
1561
|
+
block.type === 'tool_use' &&
|
|
1562
|
+
block.id === callId)) {
|
|
1563
|
+
source = entry;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
let candidate = source;
|
|
1567
|
+
while (candidate) {
|
|
1568
|
+
if (candidate.type === 'user' &&
|
|
1569
|
+
typeof candidate.promptId === 'string' &&
|
|
1570
|
+
(candidate.promptSource === 'interactive' ||
|
|
1571
|
+
candidate.promptSource === 'sdk')) {
|
|
1572
|
+
return candidate.promptId;
|
|
1573
|
+
}
|
|
1574
|
+
candidate =
|
|
1575
|
+
typeof candidate.parentUuid === 'string'
|
|
1576
|
+
? byUuid.get(candidate.parentUuid)
|
|
1577
|
+
: undefined;
|
|
1578
|
+
}
|
|
1579
|
+
return null;
|
|
1580
|
+
}
|
|
1581
|
+
assistantIdForToolCall(entries, callId) {
|
|
1582
|
+
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
1583
|
+
const entry = entries[index];
|
|
1584
|
+
if (entry?.type !== 'assistant' ||
|
|
1585
|
+
typeof entry.uuid !== 'string' ||
|
|
1586
|
+
typeof entry.message !== 'object' ||
|
|
1587
|
+
entry.message === null ||
|
|
1588
|
+
!Array.isArray(entry.message.content)) {
|
|
1589
|
+
continue;
|
|
1590
|
+
}
|
|
1591
|
+
if (entry.message.content.some((block) => typeof block === 'object' &&
|
|
1592
|
+
block !== null &&
|
|
1593
|
+
block.type === 'tool_use' &&
|
|
1594
|
+
block.id === callId)) {
|
|
1595
|
+
return entry.uuid;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
return null;
|
|
1599
|
+
}
|
|
1600
|
+
displayRulePath(rulePath) {
|
|
1601
|
+
const pathFromCwd = relative(this.activeCwd(), rulePath);
|
|
1602
|
+
return pathFromCwd.startsWith('..') || isAbsolute(pathFromCwd)
|
|
1603
|
+
? rulePath
|
|
1604
|
+
: pathFromCwd;
|
|
1605
|
+
}
|
|
1606
|
+
activeCwd() {
|
|
1607
|
+
return this.options.workspace?.cwd() ?? this.options.cwd;
|
|
1608
|
+
}
|
|
1609
|
+
restoreWorktree(entries) {
|
|
1610
|
+
const latest = [...entries]
|
|
1611
|
+
.reverse()
|
|
1612
|
+
.find((entry) => entry.type === 'worktree-state');
|
|
1613
|
+
if (!latest || !this.worktreeManager)
|
|
1614
|
+
return;
|
|
1615
|
+
const state = latest.worktreeSession;
|
|
1616
|
+
if (!state || typeof state !== 'object' || Array.isArray(state))
|
|
1617
|
+
return;
|
|
1618
|
+
this.worktreeManager.restore(state);
|
|
1619
|
+
}
|
|
1620
|
+
paths(sessionId) {
|
|
1621
|
+
return resolveClaudePaths({
|
|
1622
|
+
configDir: this.options.configRoot,
|
|
1623
|
+
cwd: this.sessionCwds.get(sessionId) ?? this.options.cwd,
|
|
1624
|
+
sessionId,
|
|
1625
|
+
});
|
|
1626
|
+
}
|
|
1627
|
+
store(sessionId) {
|
|
1628
|
+
const paths = this.paths(sessionId);
|
|
1629
|
+
return new ClaudeTranscriptStore({
|
|
1630
|
+
sessionFile: paths.sessionFile,
|
|
1631
|
+
lockFile: join(paths.praxisRoot, 'locks', `${sessionId}.lock`),
|
|
1632
|
+
schema: this.schema,
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
turnStore(sessionId) {
|
|
1636
|
+
if (this.options.sessionPersistence !== false)
|
|
1637
|
+
return this.store(sessionId);
|
|
1638
|
+
let store = this.inMemoryStores.get(sessionId);
|
|
1639
|
+
if (!store) {
|
|
1640
|
+
store = new InMemoryTranscriptStore();
|
|
1641
|
+
this.inMemoryStores.set(sessionId, store);
|
|
1642
|
+
}
|
|
1643
|
+
return store;
|
|
1644
|
+
}
|
|
1645
|
+
assertSessionPersistence() {
|
|
1646
|
+
if (this.options.sessionPersistence === false) {
|
|
1647
|
+
throw new Error('Session persistence is disabled');
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
assertWritable() {
|
|
1651
|
+
if (this.schema.writeMode !== 'read-write') {
|
|
1652
|
+
throw new Error(`Claude ${this.options.claudeVersion} session is read-only`);
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
sessionStatus(issue, entryCount) {
|
|
1656
|
+
if (issue || entryCount === 0)
|
|
1657
|
+
return 'corrupt';
|
|
1658
|
+
return this.schema.writeMode === 'read-write' ? 'ready' : 'read-only';
|
|
1659
|
+
}
|
|
1660
|
+
provider() {
|
|
1661
|
+
if (!this.options.provider) {
|
|
1662
|
+
throw new Error('A model provider is required for run and resume');
|
|
1663
|
+
}
|
|
1664
|
+
return this.options.provider;
|
|
1665
|
+
}
|
|
1666
|
+
contextBudget(provider) {
|
|
1667
|
+
if (this.options.contextBudget)
|
|
1668
|
+
return this.options.contextBudget;
|
|
1669
|
+
const contextWindowTokens = provider.capabilities.contextWindowTokens;
|
|
1670
|
+
if (contextWindowTokens === undefined)
|
|
1671
|
+
return null;
|
|
1672
|
+
return new ContextBudget({
|
|
1673
|
+
contextWindowTokens,
|
|
1674
|
+
...(this.options.contextReserveTokens === undefined
|
|
1675
|
+
? {}
|
|
1676
|
+
: { reserveTokens: this.options.contextReserveTokens }),
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1679
|
+
async append(lease, tail, entry) {
|
|
1680
|
+
const result = await lease.append(tail, entry);
|
|
1681
|
+
if (result.status === 'conflict') {
|
|
1682
|
+
throw new Error(`Claude transcript append conflict: ${result.reason}`);
|
|
1683
|
+
}
|
|
1684
|
+
return result.tail;
|
|
1685
|
+
}
|
|
1686
|
+
logicalTailUuid(tail) {
|
|
1687
|
+
return tail.branchParentUuid ?? tail.lastUuid;
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
//# sourceMappingURL=session-service.js.map
|