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,907 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { createConnection, createServer } from 'node:net';
|
|
4
|
+
import { mkdir, readFile, readdir, realpath, rm } from 'node:fs/promises';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import { dirname, join, resolve } from 'node:path';
|
|
7
|
+
import { resolveClaudePaths } from '../compatibility/claude/paths.js';
|
|
8
|
+
import { ClaudeJobStore, isProcessAlive, newClaudeJobIdentity, } from '../persistence/claude-job-store.js';
|
|
9
|
+
import { writeFileAtomically } from '../platform/atomic-write.js';
|
|
10
|
+
import { redactSensitiveText, sanitizeChildEnvironment, sensitiveEnvironmentValues, } from '../platform/sensitive-data.js';
|
|
11
|
+
const STARTUP_CONTROL_WAIT_MS = 1_000;
|
|
12
|
+
const ATTACH_READY_WAIT_MS = 5_000;
|
|
13
|
+
const WORKER_REGISTRATION_GRACE_MS = 5_000;
|
|
14
|
+
const JOB_CREATE_ATTEMPTS = 32;
|
|
15
|
+
const MAX_JOB_OUTPUT_BYTES = 1024 * 1024;
|
|
16
|
+
const MAX_ATTACH_PROMPT_BYTES = 1024 * 1024;
|
|
17
|
+
const MAX_WIRE_LINE_BYTES = 8 * 1024 * 1024;
|
|
18
|
+
const MAX_SOCKET_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
19
|
+
const SOCKET_RETRY_INTERVAL_MS = 25;
|
|
20
|
+
// Background workers are trusted Praxis runtimes, but still must not inherit
|
|
21
|
+
// unrelated credentials or shell startup injection from the launching shell.
|
|
22
|
+
// Provider/file credentials are restored explicitly because worker creates its
|
|
23
|
+
// own provider from the same CLI/environment contract.
|
|
24
|
+
const WORKER_RUNTIME_ENVIRONMENT = [
|
|
25
|
+
'PRAXIS_API_KEY',
|
|
26
|
+
'PRAXIS_MODEL',
|
|
27
|
+
'PRAXIS_PROVIDER',
|
|
28
|
+
'PRAXIS_BASE_URL',
|
|
29
|
+
'PRAXIS_MAX_OUTPUT_TOKENS',
|
|
30
|
+
'PRAXIS_ANTHROPIC_VERSION',
|
|
31
|
+
'PRAXIS_ANTHROPIC_WEB_SEARCH',
|
|
32
|
+
'PRAXIS_CONTEXT_WINDOW_TOKENS',
|
|
33
|
+
'PRAXIS_CONTEXT_RESERVE_TOKENS',
|
|
34
|
+
'PRAXIS_PRICING_JSON',
|
|
35
|
+
'PRAXIS_FILES_BASE_URL',
|
|
36
|
+
'PRAXIS_FILES_BEARER_TOKEN',
|
|
37
|
+
'PRAXIS_FILES_API_KEY',
|
|
38
|
+
'PRAXIS_MCP_OAUTH_STORE',
|
|
39
|
+
'CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING',
|
|
40
|
+
];
|
|
41
|
+
function workerEnvironment(source, configRoot) {
|
|
42
|
+
const environment = sanitizeChildEnvironment({}, source);
|
|
43
|
+
for (const name of WORKER_RUNTIME_ENVIRONMENT) {
|
|
44
|
+
const value = source[name];
|
|
45
|
+
if (value !== undefined)
|
|
46
|
+
environment[name] = value;
|
|
47
|
+
}
|
|
48
|
+
environment.CLAUDE_CONFIG_DIR = configRoot;
|
|
49
|
+
return environment;
|
|
50
|
+
}
|
|
51
|
+
function socketPath(configRoot, id) {
|
|
52
|
+
const owner = typeof process.getuid === 'function' ? process.getuid() : 0;
|
|
53
|
+
const rootHash = createHash('sha256')
|
|
54
|
+
.update(configRoot)
|
|
55
|
+
.digest('hex')
|
|
56
|
+
.slice(0, 12);
|
|
57
|
+
return join(tmpdir(), `praxis-agent-${owner}`, rootHash, `${id}.sock`);
|
|
58
|
+
}
|
|
59
|
+
function writeWire(socket, message) {
|
|
60
|
+
if (socket.destroyed)
|
|
61
|
+
return;
|
|
62
|
+
if (socket.writableLength > MAX_SOCKET_BUFFER_BYTES) {
|
|
63
|
+
socket.destroy();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
socket.write(`${JSON.stringify(message)}\n`);
|
|
67
|
+
}
|
|
68
|
+
function clearWorkerFields(state) {
|
|
69
|
+
const next = { ...state };
|
|
70
|
+
delete next.pid;
|
|
71
|
+
delete next.socketPath;
|
|
72
|
+
delete next.controlToken;
|
|
73
|
+
delete next.inFlight;
|
|
74
|
+
return next;
|
|
75
|
+
}
|
|
76
|
+
function nativeClaudeSession(value) {
|
|
77
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
78
|
+
return;
|
|
79
|
+
const record = value;
|
|
80
|
+
const interactive = Number.isSafeInteger(record.pid) &&
|
|
81
|
+
typeof record.cwd === 'string' &&
|
|
82
|
+
record.kind === 'interactive' &&
|
|
83
|
+
Number.isSafeInteger(record.startedAt) &&
|
|
84
|
+
typeof record.sessionId === 'string' &&
|
|
85
|
+
typeof record.name === 'string' &&
|
|
86
|
+
['active', 'idle'].includes(String(record.status));
|
|
87
|
+
if (interactive)
|
|
88
|
+
return record;
|
|
89
|
+
if (typeof record.id !== 'string' ||
|
|
90
|
+
typeof record.cwd !== 'string' ||
|
|
91
|
+
record.kind !== 'background' ||
|
|
92
|
+
!Number.isSafeInteger(record.startedAt) ||
|
|
93
|
+
typeof record.sessionId !== 'string' ||
|
|
94
|
+
typeof record.name !== 'string' ||
|
|
95
|
+
!['done', 'failed'].includes(String(record.state)))
|
|
96
|
+
return;
|
|
97
|
+
return record;
|
|
98
|
+
}
|
|
99
|
+
function parseWire(line) {
|
|
100
|
+
try {
|
|
101
|
+
const value = JSON.parse(line);
|
|
102
|
+
if (typeof value !== 'object' ||
|
|
103
|
+
value === null ||
|
|
104
|
+
Array.isArray(value) ||
|
|
105
|
+
typeof value.type !== 'string') {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function lines(socket, receive) {
|
|
115
|
+
let buffer = '';
|
|
116
|
+
socket.setEncoding('utf8');
|
|
117
|
+
socket.on('data', (chunk) => {
|
|
118
|
+
buffer += chunk;
|
|
119
|
+
if (Buffer.byteLength(buffer) > MAX_WIRE_LINE_BYTES &&
|
|
120
|
+
!buffer.includes('\n')) {
|
|
121
|
+
socket.destroy();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
for (;;) {
|
|
125
|
+
const newline = buffer.indexOf('\n');
|
|
126
|
+
if (newline < 0)
|
|
127
|
+
break;
|
|
128
|
+
const line = buffer.slice(0, newline);
|
|
129
|
+
buffer = buffer.slice(newline + 1);
|
|
130
|
+
if (Buffer.byteLength(line) > MAX_WIRE_LINE_BYTES) {
|
|
131
|
+
socket.destroy();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const message = parseWire(line);
|
|
135
|
+
if (message)
|
|
136
|
+
receive(message);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
function transientSocketError(error) {
|
|
141
|
+
const code = error.code;
|
|
142
|
+
return code === 'ENOENT' || code === 'ECONNREFUSED';
|
|
143
|
+
}
|
|
144
|
+
function waitForSocketRetry(signal) {
|
|
145
|
+
return new Promise((resolveWait, rejectWait) => {
|
|
146
|
+
if (signal?.aborted) {
|
|
147
|
+
rejectWait(new Error('Attach cancelled'));
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const abort = () => {
|
|
151
|
+
clearTimeout(timer);
|
|
152
|
+
rejectWait(new Error('Attach cancelled'));
|
|
153
|
+
};
|
|
154
|
+
const timer = setTimeout(() => {
|
|
155
|
+
signal?.removeEventListener('abort', abort);
|
|
156
|
+
resolveWait();
|
|
157
|
+
}, SOCKET_RETRY_INTERVAL_MS);
|
|
158
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
async function connectToWorker(path, timeoutMs, signal) {
|
|
162
|
+
const deadline = Date.now() + timeoutMs;
|
|
163
|
+
for (;;) {
|
|
164
|
+
if (signal?.aborted)
|
|
165
|
+
throw new Error('Attach cancelled');
|
|
166
|
+
const socket = createConnection(path);
|
|
167
|
+
try {
|
|
168
|
+
await new Promise((resolveConnect, rejectConnect) => {
|
|
169
|
+
const connected = () => {
|
|
170
|
+
socket.removeListener('error', failed);
|
|
171
|
+
resolveConnect();
|
|
172
|
+
};
|
|
173
|
+
const failed = (error) => {
|
|
174
|
+
socket.removeListener('connect', connected);
|
|
175
|
+
rejectConnect(error);
|
|
176
|
+
};
|
|
177
|
+
socket.once('connect', connected);
|
|
178
|
+
socket.once('error', failed);
|
|
179
|
+
});
|
|
180
|
+
return socket;
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
socket.destroy();
|
|
184
|
+
if (!transientSocketError(error) || Date.now() >= deadline)
|
|
185
|
+
throw error;
|
|
186
|
+
await waitForSocketRetry(signal);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async function canonicalDirectory(path) {
|
|
191
|
+
try {
|
|
192
|
+
return await realpath(path);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
return resolve(path);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
export class TopLevelAgentManager {
|
|
199
|
+
options;
|
|
200
|
+
store;
|
|
201
|
+
constructor(options) {
|
|
202
|
+
this.options = options;
|
|
203
|
+
this.store = new ClaudeJobStore(options.configRoot);
|
|
204
|
+
}
|
|
205
|
+
async launch(options) {
|
|
206
|
+
const cwd = await canonicalDirectory(options.cwd ?? this.options.cwd);
|
|
207
|
+
let identity;
|
|
208
|
+
let state;
|
|
209
|
+
for (let attempt = 0; attempt < JOB_CREATE_ATTEMPTS; attempt += 1) {
|
|
210
|
+
const generated = newClaudeJobIdentity();
|
|
211
|
+
const sessionId = options.resumeSessionId ?? generated.sessionId;
|
|
212
|
+
const now = new Date().toISOString();
|
|
213
|
+
const candidate = {
|
|
214
|
+
state: 'working',
|
|
215
|
+
detail: 'starting',
|
|
216
|
+
tempo: 'active',
|
|
217
|
+
inFlight: { tasks: 1, queued: 0, kinds: ['prompt'] },
|
|
218
|
+
tokens: 0,
|
|
219
|
+
output: null,
|
|
220
|
+
children: null,
|
|
221
|
+
template: 'bg',
|
|
222
|
+
respawnFlags: [...options.argv],
|
|
223
|
+
intent: options.prompt,
|
|
224
|
+
sessionId,
|
|
225
|
+
resumeSessionId: sessionId,
|
|
226
|
+
daemonShort: generated.id,
|
|
227
|
+
cliVersion: this.options.version,
|
|
228
|
+
cwd,
|
|
229
|
+
backend: 'daemon',
|
|
230
|
+
praxisOwner: 1,
|
|
231
|
+
createdAt: now,
|
|
232
|
+
updatedAt: now,
|
|
233
|
+
firstTerminalAt: null,
|
|
234
|
+
socketPath: socketPath(this.options.configRoot, generated.id),
|
|
235
|
+
controlToken: randomBytes(24).toString('hex'),
|
|
236
|
+
};
|
|
237
|
+
if (await this.store.create(candidate, {
|
|
238
|
+
version: 1,
|
|
239
|
+
argv: [...options.argv],
|
|
240
|
+
resume: options.resumeSessionId !== undefined,
|
|
241
|
+
})) {
|
|
242
|
+
identity = { id: generated.id, sessionId };
|
|
243
|
+
state = candidate;
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (!identity || !state)
|
|
248
|
+
throw new Error('Could not allocate agent ID');
|
|
249
|
+
let child;
|
|
250
|
+
try {
|
|
251
|
+
child = spawn(this.options.executablePath ?? process.execPath, [this.options.cliPath, '__background-worker', identity.id], {
|
|
252
|
+
cwd,
|
|
253
|
+
env: workerEnvironment(this.options.environment ?? process.env, this.options.configRoot),
|
|
254
|
+
detached: true,
|
|
255
|
+
stdio: 'ignore',
|
|
256
|
+
});
|
|
257
|
+
await new Promise((resolveSpawn, rejectSpawn) => {
|
|
258
|
+
const spawned = () => {
|
|
259
|
+
child.removeListener('error', failed);
|
|
260
|
+
resolveSpawn();
|
|
261
|
+
};
|
|
262
|
+
const failed = (error) => {
|
|
263
|
+
child.removeListener('spawn', spawned);
|
|
264
|
+
rejectSpawn(error);
|
|
265
|
+
};
|
|
266
|
+
child.once('spawn', spawned);
|
|
267
|
+
child.once('error', failed);
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
const failedAt = new Date().toISOString();
|
|
272
|
+
const message = redactSensitiveText(error instanceof Error ? error.message : String(error), sensitiveEnvironmentValues(this.options.environment ?? process.env));
|
|
273
|
+
await this.store.update(identity.id, (current) => ({
|
|
274
|
+
...clearWorkerFields(current),
|
|
275
|
+
state: 'failed',
|
|
276
|
+
detail: message,
|
|
277
|
+
tempo: 'idle',
|
|
278
|
+
updatedAt: failedAt,
|
|
279
|
+
firstTerminalAt: failedAt,
|
|
280
|
+
}));
|
|
281
|
+
throw new Error('Could not start background agent', { cause: error });
|
|
282
|
+
}
|
|
283
|
+
const childPid = child.pid;
|
|
284
|
+
if (!childPid) {
|
|
285
|
+
await this.store.update(identity.id, (current) => ({
|
|
286
|
+
...clearWorkerFields(current),
|
|
287
|
+
state: 'failed',
|
|
288
|
+
detail: 'worker failed to start',
|
|
289
|
+
tempo: 'idle',
|
|
290
|
+
updatedAt: new Date().toISOString(),
|
|
291
|
+
firstTerminalAt: new Date().toISOString(),
|
|
292
|
+
}));
|
|
293
|
+
throw new Error('Could not start background agent');
|
|
294
|
+
}
|
|
295
|
+
child.unref();
|
|
296
|
+
await this.store.update(identity.id, (current) => ({
|
|
297
|
+
...current,
|
|
298
|
+
pid: childPid,
|
|
299
|
+
updatedAt: new Date().toISOString(),
|
|
300
|
+
}));
|
|
301
|
+
return identity;
|
|
302
|
+
}
|
|
303
|
+
async list(options) {
|
|
304
|
+
const cwd = options.cwd === undefined
|
|
305
|
+
? undefined
|
|
306
|
+
: await canonicalDirectory(options.cwd);
|
|
307
|
+
const states = await this.store.list();
|
|
308
|
+
const reconciled = await Promise.all(states.map(async (state) => {
|
|
309
|
+
if (state.state !== 'working' || (await this.workerAlive(state))) {
|
|
310
|
+
return state;
|
|
311
|
+
}
|
|
312
|
+
return this.store.update(state.daemonShort, (current) => {
|
|
313
|
+
if (current.state !== 'working' || current.pid !== state.pid) {
|
|
314
|
+
return current;
|
|
315
|
+
}
|
|
316
|
+
const now = new Date().toISOString();
|
|
317
|
+
return {
|
|
318
|
+
...clearWorkerFields(current),
|
|
319
|
+
state: 'failed',
|
|
320
|
+
detail: 'worker exited unexpectedly',
|
|
321
|
+
tempo: 'idle',
|
|
322
|
+
updatedAt: now,
|
|
323
|
+
firstTerminalAt: current.firstTerminalAt ?? now,
|
|
324
|
+
};
|
|
325
|
+
});
|
|
326
|
+
}));
|
|
327
|
+
const praxis = reconciled
|
|
328
|
+
.filter((state) => cwd === undefined || state.cwd === cwd)
|
|
329
|
+
.filter((state) => options.all || state.state === 'working')
|
|
330
|
+
.sort((left, right) => right.createdAt.localeCompare(left.createdAt))
|
|
331
|
+
.map((state) => ({
|
|
332
|
+
...(state.state === 'working' && state.pid !== undefined
|
|
333
|
+
? { pid: state.pid }
|
|
334
|
+
: {}),
|
|
335
|
+
id: state.daemonShort,
|
|
336
|
+
cwd: state.cwd,
|
|
337
|
+
kind: 'background',
|
|
338
|
+
startedAt: Date.parse(state.createdAt),
|
|
339
|
+
sessionId: state.sessionId,
|
|
340
|
+
name: state.intent,
|
|
341
|
+
...(state.state === 'working'
|
|
342
|
+
? {
|
|
343
|
+
status: state.tempo === 'idle'
|
|
344
|
+
? 'idle'
|
|
345
|
+
: 'active',
|
|
346
|
+
}
|
|
347
|
+
: {}),
|
|
348
|
+
state: state.state,
|
|
349
|
+
}));
|
|
350
|
+
const native = await this.nativeSessions(cwd, new Set(reconciled.map((state) => state.sessionId)), options.all);
|
|
351
|
+
return [...praxis, ...native].sort((left, right) => right.startedAt - left.startedAt);
|
|
352
|
+
}
|
|
353
|
+
async nativeSessions(cwd, knownSessionIds, all) {
|
|
354
|
+
let files;
|
|
355
|
+
try {
|
|
356
|
+
files = await readdir(join(this.options.configRoot, 'sessions'));
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
if (error.code === 'ENOENT')
|
|
360
|
+
return [];
|
|
361
|
+
throw error;
|
|
362
|
+
}
|
|
363
|
+
const sessions = await Promise.all(files
|
|
364
|
+
.filter((file) => file.endsWith('.json'))
|
|
365
|
+
.map(async (file) => {
|
|
366
|
+
try {
|
|
367
|
+
return nativeClaudeSession(JSON.parse(await readFile(join(this.options.configRoot, 'sessions', file), 'utf8')));
|
|
368
|
+
}
|
|
369
|
+
catch {
|
|
370
|
+
return undefined;
|
|
371
|
+
}
|
|
372
|
+
}));
|
|
373
|
+
return sessions
|
|
374
|
+
.filter((session) => session !== undefined)
|
|
375
|
+
.filter((session) => !knownSessionIds.has(session.sessionId))
|
|
376
|
+
.filter((session) => cwd === undefined || session.cwd === cwd)
|
|
377
|
+
.filter((session) => all || session.status !== undefined);
|
|
378
|
+
}
|
|
379
|
+
async logs(id) {
|
|
380
|
+
await this.store.read(id);
|
|
381
|
+
return this.store.output(id);
|
|
382
|
+
}
|
|
383
|
+
async review(agent) {
|
|
384
|
+
if (agent.id !== undefined) {
|
|
385
|
+
try {
|
|
386
|
+
await this.store.read(agent.id);
|
|
387
|
+
return this.logs(agent.id);
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
if (!String(error).includes('No agent found'))
|
|
391
|
+
throw error;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
try {
|
|
395
|
+
return await readFile(resolveClaudePaths({
|
|
396
|
+
configDir: this.options.configRoot,
|
|
397
|
+
cwd: agent.cwd,
|
|
398
|
+
sessionId: agent.sessionId,
|
|
399
|
+
}).sessionFile, 'utf8');
|
|
400
|
+
}
|
|
401
|
+
catch (error) {
|
|
402
|
+
if (error.code === 'ENOENT') {
|
|
403
|
+
return 'No local transcript is available for this Claude session.\n';
|
|
404
|
+
}
|
|
405
|
+
throw error;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
async stop(id) {
|
|
409
|
+
const state = await this.store.read(id);
|
|
410
|
+
if (state.state !== 'working') {
|
|
411
|
+
throw new Error(`Agent ${id} is not running (state: ${state.state})`);
|
|
412
|
+
}
|
|
413
|
+
let stopped = false;
|
|
414
|
+
if (state.socketPath && state.controlToken) {
|
|
415
|
+
try {
|
|
416
|
+
const response = await this.request(state.socketPath, { type: 'stop', token: state.controlToken }, STARTUP_CONTROL_WAIT_MS);
|
|
417
|
+
stopped = response.type === 'stopped';
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
// Worker may still be between spawn and control-socket initialization.
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (!stopped &&
|
|
424
|
+
state.pid !== undefined &&
|
|
425
|
+
(await this.workerAlive(state))) {
|
|
426
|
+
process.kill(state.pid, 'SIGTERM');
|
|
427
|
+
}
|
|
428
|
+
const now = new Date().toISOString();
|
|
429
|
+
await this.store.update(id, (current) => ({
|
|
430
|
+
...clearWorkerFields(current),
|
|
431
|
+
state: 'stopped',
|
|
432
|
+
detail: 'stopped',
|
|
433
|
+
tempo: 'idle',
|
|
434
|
+
updatedAt: now,
|
|
435
|
+
firstTerminalAt: current.firstTerminalAt ?? now,
|
|
436
|
+
}));
|
|
437
|
+
}
|
|
438
|
+
async attach(id, input, output, signal) {
|
|
439
|
+
const state = await this.store.read(id);
|
|
440
|
+
if (state.state !== 'working' || !state.socketPath || !state.controlToken) {
|
|
441
|
+
throw new Error(`Agent ${id} is not attachable (state: ${state.state})`);
|
|
442
|
+
}
|
|
443
|
+
const socket = await connectToWorker(state.socketPath, ATTACH_READY_WAIT_MS, signal);
|
|
444
|
+
let readyResolve;
|
|
445
|
+
let readyReject;
|
|
446
|
+
let isReady = false;
|
|
447
|
+
const ready = new Promise((resolveReady, rejectReady) => {
|
|
448
|
+
readyResolve = resolveReady;
|
|
449
|
+
readyReject = rejectReady;
|
|
450
|
+
});
|
|
451
|
+
const readyTimer = setTimeout(() => {
|
|
452
|
+
readyReject?.(new Error(`Agent ${id} attach handshake timed out`));
|
|
453
|
+
socket.destroy();
|
|
454
|
+
}, ATTACH_READY_WAIT_MS);
|
|
455
|
+
const completions = new Map();
|
|
456
|
+
lines(socket, (message) => {
|
|
457
|
+
if (message.type === 'ready') {
|
|
458
|
+
isReady = true;
|
|
459
|
+
clearTimeout(readyTimer);
|
|
460
|
+
if (message.text)
|
|
461
|
+
output(message.text);
|
|
462
|
+
readyResolve?.();
|
|
463
|
+
}
|
|
464
|
+
else if (message.type === 'output' && message.text) {
|
|
465
|
+
output(message.text);
|
|
466
|
+
}
|
|
467
|
+
else if (message.type === 'turn-complete' && message.requestId) {
|
|
468
|
+
completions.get(message.requestId)?.resolve();
|
|
469
|
+
completions.delete(message.requestId);
|
|
470
|
+
}
|
|
471
|
+
else if (message.type === 'turn-error' && message.requestId) {
|
|
472
|
+
completions
|
|
473
|
+
.get(message.requestId)
|
|
474
|
+
?.reject(new Error(message.message ?? 'Agent turn failed'));
|
|
475
|
+
completions.delete(message.requestId);
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
const connectionClosed = () => {
|
|
479
|
+
const error = new Error(`Agent ${id} detached unexpectedly`);
|
|
480
|
+
if (!isReady)
|
|
481
|
+
readyReject?.(error);
|
|
482
|
+
for (const completion of completions.values())
|
|
483
|
+
completion.reject(error);
|
|
484
|
+
completions.clear();
|
|
485
|
+
};
|
|
486
|
+
socket.once('close', connectionClosed);
|
|
487
|
+
writeWire(socket, { type: 'attach', token: state.controlToken });
|
|
488
|
+
const abort = () => socket.destroy();
|
|
489
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
490
|
+
try {
|
|
491
|
+
await ready;
|
|
492
|
+
const decoder = new TextDecoder();
|
|
493
|
+
let inputBuffer = '';
|
|
494
|
+
const sendPrompt = async (prompt) => {
|
|
495
|
+
if (prompt.trim().length === 0)
|
|
496
|
+
return;
|
|
497
|
+
if (Buffer.byteLength(prompt) > MAX_ATTACH_PROMPT_BYTES) {
|
|
498
|
+
throw new Error(`Agent prompt exceeds ${MAX_ATTACH_PROMPT_BYTES} bytes`);
|
|
499
|
+
}
|
|
500
|
+
const requestId = randomBytes(12).toString('hex');
|
|
501
|
+
const completion = new Promise((resolveTurn, rejectTurn) => {
|
|
502
|
+
completions.set(requestId, {
|
|
503
|
+
resolve: resolveTurn,
|
|
504
|
+
reject: rejectTurn,
|
|
505
|
+
});
|
|
506
|
+
});
|
|
507
|
+
writeWire(socket, { type: 'prompt', text: prompt, requestId });
|
|
508
|
+
await completion;
|
|
509
|
+
};
|
|
510
|
+
for await (const chunk of input) {
|
|
511
|
+
if (signal?.aborted)
|
|
512
|
+
break;
|
|
513
|
+
inputBuffer +=
|
|
514
|
+
typeof chunk === 'string'
|
|
515
|
+
? chunk
|
|
516
|
+
: decoder.decode(chunk, { stream: true });
|
|
517
|
+
if (Buffer.byteLength(inputBuffer) > MAX_ATTACH_PROMPT_BYTES &&
|
|
518
|
+
!inputBuffer.includes('\n')) {
|
|
519
|
+
throw new Error(`Agent prompt exceeds ${MAX_ATTACH_PROMPT_BYTES} bytes`);
|
|
520
|
+
}
|
|
521
|
+
for (;;) {
|
|
522
|
+
const newline = inputBuffer.indexOf('\n');
|
|
523
|
+
if (newline < 0)
|
|
524
|
+
break;
|
|
525
|
+
const prompt = inputBuffer.slice(0, newline).replace(/\r$/u, '');
|
|
526
|
+
inputBuffer = inputBuffer.slice(newline + 1);
|
|
527
|
+
await sendPrompt(prompt);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
inputBuffer += decoder.decode();
|
|
531
|
+
await sendPrompt(inputBuffer.replace(/\r$/u, ''));
|
|
532
|
+
writeWire(socket, { type: 'detach' });
|
|
533
|
+
}
|
|
534
|
+
finally {
|
|
535
|
+
clearTimeout(readyTimer);
|
|
536
|
+
signal?.removeEventListener('abort', abort);
|
|
537
|
+
socket.removeListener('close', connectionClosed);
|
|
538
|
+
socket.end();
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
async request(path, message, timeoutMs) {
|
|
542
|
+
return new Promise((resolveRequest, reject) => {
|
|
543
|
+
const socket = createConnection(path);
|
|
544
|
+
const timer = setTimeout(() => {
|
|
545
|
+
socket.destroy();
|
|
546
|
+
reject(new Error('Agent control request timed out'));
|
|
547
|
+
}, timeoutMs);
|
|
548
|
+
timer.unref?.();
|
|
549
|
+
const finish = (operation) => {
|
|
550
|
+
clearTimeout(timer);
|
|
551
|
+
socket.end();
|
|
552
|
+
operation();
|
|
553
|
+
};
|
|
554
|
+
socket.once('error', (error) => finish(() => reject(error)));
|
|
555
|
+
socket.once('connect', () => writeWire(socket, message));
|
|
556
|
+
lines(socket, (response) => finish(() => resolveRequest(response)));
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
async workerAlive(state) {
|
|
560
|
+
if (state.pid === undefined || !isProcessAlive(state.pid))
|
|
561
|
+
return false;
|
|
562
|
+
try {
|
|
563
|
+
const value = JSON.parse(await readFile(join(this.options.configRoot, 'sessions', `${state.pid}.json`), 'utf8'));
|
|
564
|
+
return (typeof value === 'object' &&
|
|
565
|
+
value !== null &&
|
|
566
|
+
!Array.isArray(value) &&
|
|
567
|
+
value.pid === state.pid &&
|
|
568
|
+
value.jobId === state.daemonShort &&
|
|
569
|
+
value.sessionId === state.sessionId);
|
|
570
|
+
}
|
|
571
|
+
catch {
|
|
572
|
+
const createdAt = Date.parse(state.createdAt);
|
|
573
|
+
return (state.detail === 'starting' &&
|
|
574
|
+
Number.isFinite(createdAt) &&
|
|
575
|
+
Date.now() - createdAt < WORKER_REGISTRATION_GRACE_MS);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
export async function runTopLevelAgentWorker(options) {
|
|
580
|
+
const store = new ClaudeJobStore(options.configRoot);
|
|
581
|
+
const initial = await store.read(options.id);
|
|
582
|
+
const dispatch = await store.readDispatch(options.id);
|
|
583
|
+
const sensitiveValues = sensitiveEnvironmentValues(process.env);
|
|
584
|
+
const safeText = (text) => redactSensitiveText(text, sensitiveValues);
|
|
585
|
+
const safeErrorMessage = (error) => safeText(error instanceof Error ? error.message : String(error));
|
|
586
|
+
if (initial.state !== 'working')
|
|
587
|
+
return;
|
|
588
|
+
if (!initial.socketPath || !initial.controlToken) {
|
|
589
|
+
throw new Error(`Agent ${options.id} has no control endpoint`);
|
|
590
|
+
}
|
|
591
|
+
const socketFile = initial.socketPath;
|
|
592
|
+
const processFile = join(options.configRoot, 'sessions', `${process.pid}.json`);
|
|
593
|
+
const processStartedAt = Date.now();
|
|
594
|
+
const writeProcessStatus = (status) => {
|
|
595
|
+
const now = Date.now();
|
|
596
|
+
return writeFileAtomically(processFile, `${JSON.stringify({
|
|
597
|
+
pid: process.pid,
|
|
598
|
+
sessionId: initial.sessionId,
|
|
599
|
+
cwd: initial.cwd,
|
|
600
|
+
startedAt: processStartedAt,
|
|
601
|
+
version: initial.cliVersion,
|
|
602
|
+
kind: 'bg',
|
|
603
|
+
entrypoint: 'cli',
|
|
604
|
+
name: initial.intent,
|
|
605
|
+
jobId: options.id,
|
|
606
|
+
status,
|
|
607
|
+
updatedAt: now,
|
|
608
|
+
statusUpdatedAt: now,
|
|
609
|
+
})}\n`);
|
|
610
|
+
};
|
|
611
|
+
await mkdir(dirname(socketFile), { recursive: true });
|
|
612
|
+
await rm(socketFile, { force: true });
|
|
613
|
+
const clients = new Set();
|
|
614
|
+
const broadcast = (message) => {
|
|
615
|
+
for (const client of clients)
|
|
616
|
+
writeWire(client, message);
|
|
617
|
+
};
|
|
618
|
+
let liveTurnText = '';
|
|
619
|
+
let outputWriteError;
|
|
620
|
+
let outputWrites = Promise.resolve();
|
|
621
|
+
let runtime;
|
|
622
|
+
try {
|
|
623
|
+
runtime = await options.createRuntime((event) => {
|
|
624
|
+
if (event.type === 'text-delta') {
|
|
625
|
+
const delta = safeText(event.delta);
|
|
626
|
+
liveTurnText += delta;
|
|
627
|
+
outputWrites = outputWrites
|
|
628
|
+
.then(() => store.appendOutput(options.id, delta))
|
|
629
|
+
.catch((error) => {
|
|
630
|
+
outputWriteError = error;
|
|
631
|
+
});
|
|
632
|
+
broadcast({ type: 'output', text: delta });
|
|
633
|
+
}
|
|
634
|
+
}, dispatch);
|
|
635
|
+
}
|
|
636
|
+
catch (error) {
|
|
637
|
+
const message = safeErrorMessage(error);
|
|
638
|
+
const failedAt = new Date().toISOString();
|
|
639
|
+
await store.appendOutput(options.id, `${message}\n`);
|
|
640
|
+
await store.appendTimeline(options.id, {
|
|
641
|
+
at: failedAt,
|
|
642
|
+
state: 'failed',
|
|
643
|
+
detail: message,
|
|
644
|
+
text: message,
|
|
645
|
+
});
|
|
646
|
+
await store.update(options.id, (state) => ({
|
|
647
|
+
...clearWorkerFields(state),
|
|
648
|
+
state: 'failed',
|
|
649
|
+
detail: message,
|
|
650
|
+
tempo: 'idle',
|
|
651
|
+
updatedAt: failedAt,
|
|
652
|
+
firstTerminalAt: state.firstTerminalAt ?? failedAt,
|
|
653
|
+
}));
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
let activeController;
|
|
657
|
+
let closing = false;
|
|
658
|
+
let turnQueue = Promise.resolve();
|
|
659
|
+
let finishWorker;
|
|
660
|
+
const finished = new Promise((resolveFinished) => {
|
|
661
|
+
finishWorker = resolveFinished;
|
|
662
|
+
});
|
|
663
|
+
const server = createServer((client) => {
|
|
664
|
+
let authenticated = false;
|
|
665
|
+
lines(client, (message) => {
|
|
666
|
+
if (!authenticated) {
|
|
667
|
+
if (message.type === 'stop' && message.token === initial.controlToken) {
|
|
668
|
+
authenticated = true;
|
|
669
|
+
writeWire(client, { type: 'stopped' });
|
|
670
|
+
void setStopped('stopped').catch(() => undefined);
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
if (message.type !== 'attach' ||
|
|
674
|
+
message.token !== initial.controlToken) {
|
|
675
|
+
client.destroy();
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
authenticated = true;
|
|
679
|
+
clients.add(client);
|
|
680
|
+
void store.output(options.id).then((history) => {
|
|
681
|
+
writeWire(client, {
|
|
682
|
+
type: 'ready',
|
|
683
|
+
text: history,
|
|
684
|
+
});
|
|
685
|
+
});
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
if (message.type === 'detach') {
|
|
689
|
+
client.end();
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
if (message.type === 'prompt' && message.text && message.requestId) {
|
|
693
|
+
const { text, requestId } = message;
|
|
694
|
+
turnQueue = turnQueue.then(() => runTurn(text, true, requestId, client));
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
client.once('close', () => clients.delete(client));
|
|
698
|
+
});
|
|
699
|
+
const closeControl = () => {
|
|
700
|
+
for (const client of clients)
|
|
701
|
+
client.end();
|
|
702
|
+
if (server.listening)
|
|
703
|
+
server.close();
|
|
704
|
+
finishWorker?.();
|
|
705
|
+
};
|
|
706
|
+
const setStopped = async (detail) => {
|
|
707
|
+
if (closing)
|
|
708
|
+
return;
|
|
709
|
+
liveTurnText = '';
|
|
710
|
+
closing = true;
|
|
711
|
+
activeController?.abort();
|
|
712
|
+
const now = new Date().toISOString();
|
|
713
|
+
try {
|
|
714
|
+
await store.update(options.id, (state) => ({
|
|
715
|
+
...clearWorkerFields(state),
|
|
716
|
+
state: 'stopped',
|
|
717
|
+
detail,
|
|
718
|
+
tempo: 'idle',
|
|
719
|
+
updatedAt: now,
|
|
720
|
+
firstTerminalAt: state.firstTerminalAt ?? now,
|
|
721
|
+
}));
|
|
722
|
+
await store.appendTimeline(options.id, {
|
|
723
|
+
at: now,
|
|
724
|
+
state: 'stopped',
|
|
725
|
+
detail,
|
|
726
|
+
text: detail,
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
finally {
|
|
730
|
+
broadcast({ type: 'stopped' });
|
|
731
|
+
closeControl();
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
const failWorker = async (error) => {
|
|
735
|
+
if (closing)
|
|
736
|
+
return;
|
|
737
|
+
closing = true;
|
|
738
|
+
activeController?.abort();
|
|
739
|
+
const message = safeErrorMessage(error);
|
|
740
|
+
const now = new Date().toISOString();
|
|
741
|
+
try {
|
|
742
|
+
await store.appendOutput(options.id, `${message}\n`);
|
|
743
|
+
await store.trimOutput(options.id, MAX_JOB_OUTPUT_BYTES);
|
|
744
|
+
await store.appendTimeline(options.id, {
|
|
745
|
+
at: now,
|
|
746
|
+
state: 'failed',
|
|
747
|
+
detail: message,
|
|
748
|
+
text: message,
|
|
749
|
+
});
|
|
750
|
+
await store.update(options.id, (state) => ({
|
|
751
|
+
...clearWorkerFields(state),
|
|
752
|
+
state: 'failed',
|
|
753
|
+
detail: message,
|
|
754
|
+
tempo: 'idle',
|
|
755
|
+
updatedAt: now,
|
|
756
|
+
firstTerminalAt: state.firstTerminalAt ?? now,
|
|
757
|
+
}));
|
|
758
|
+
}
|
|
759
|
+
finally {
|
|
760
|
+
broadcast({ type: 'failed', message });
|
|
761
|
+
closeControl();
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
async function runTurn(prompt, resume, requestId, client) {
|
|
765
|
+
if (closing)
|
|
766
|
+
return;
|
|
767
|
+
liveTurnText = '';
|
|
768
|
+
outputWriteError = undefined;
|
|
769
|
+
activeController = new AbortController();
|
|
770
|
+
const now = new Date().toISOString();
|
|
771
|
+
const activated = await store.update(options.id, (state) => state.state !== 'working'
|
|
772
|
+
? state
|
|
773
|
+
: {
|
|
774
|
+
...state,
|
|
775
|
+
detail: safeText(prompt),
|
|
776
|
+
tempo: 'active',
|
|
777
|
+
inFlight: { tasks: 1, queued: 0, kinds: ['prompt'] },
|
|
778
|
+
updatedAt: now,
|
|
779
|
+
});
|
|
780
|
+
if (closing || activated.state !== 'working')
|
|
781
|
+
return;
|
|
782
|
+
await writeProcessStatus('working');
|
|
783
|
+
try {
|
|
784
|
+
const result = resume
|
|
785
|
+
? await runtime.resume(initial.sessionId, prompt, activeController.signal)
|
|
786
|
+
: await runtime.run(prompt, activeController.signal, initial.sessionId);
|
|
787
|
+
if (closing)
|
|
788
|
+
return;
|
|
789
|
+
const completedAt = new Date().toISOString();
|
|
790
|
+
await outputWrites;
|
|
791
|
+
if (outputWriteError)
|
|
792
|
+
throw outputWriteError;
|
|
793
|
+
const resultText = safeText(result.text);
|
|
794
|
+
if (liveTurnText.length === 0 && resultText.length > 0) {
|
|
795
|
+
broadcast({ type: 'output', text: resultText });
|
|
796
|
+
await store.appendOutput(options.id, resultText);
|
|
797
|
+
}
|
|
798
|
+
await store.appendOutput(options.id, '\n');
|
|
799
|
+
await store.trimOutput(options.id, MAX_JOB_OUTPUT_BYTES);
|
|
800
|
+
await store.appendTimeline(options.id, {
|
|
801
|
+
at: completedAt,
|
|
802
|
+
state: 'working',
|
|
803
|
+
detail: resultText,
|
|
804
|
+
text: resultText,
|
|
805
|
+
});
|
|
806
|
+
const idled = await store.update(options.id, (state) => state.state !== 'working'
|
|
807
|
+
? state
|
|
808
|
+
: {
|
|
809
|
+
...state,
|
|
810
|
+
detail: resultText,
|
|
811
|
+
tempo: 'idle',
|
|
812
|
+
inFlight: { tasks: 0, queued: 0, kinds: [] },
|
|
813
|
+
tokens: state.tokens +
|
|
814
|
+
result.usage.inputTokens +
|
|
815
|
+
result.usage.outputTokens,
|
|
816
|
+
updatedAt: completedAt,
|
|
817
|
+
});
|
|
818
|
+
if (!closing && idled.state === 'working') {
|
|
819
|
+
await writeProcessStatus('idle');
|
|
820
|
+
}
|
|
821
|
+
if (requestId && client) {
|
|
822
|
+
writeWire(client, { type: 'turn-complete', requestId });
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
catch (error) {
|
|
826
|
+
if (closing || activeController.signal.aborted)
|
|
827
|
+
return;
|
|
828
|
+
const message = safeErrorMessage(error);
|
|
829
|
+
const failedAt = new Date().toISOString();
|
|
830
|
+
if (resume) {
|
|
831
|
+
try {
|
|
832
|
+
await store.appendOutput(options.id, `${message}\n`);
|
|
833
|
+
await store.trimOutput(options.id, MAX_JOB_OUTPUT_BYTES);
|
|
834
|
+
await store.appendTimeline(options.id, {
|
|
835
|
+
at: failedAt,
|
|
836
|
+
state: 'working',
|
|
837
|
+
detail: message,
|
|
838
|
+
text: message,
|
|
839
|
+
});
|
|
840
|
+
const idled = await store.update(options.id, (state) => state.state !== 'working'
|
|
841
|
+
? state
|
|
842
|
+
: {
|
|
843
|
+
...state,
|
|
844
|
+
detail: message,
|
|
845
|
+
tempo: 'idle',
|
|
846
|
+
inFlight: { tasks: 0, queued: 0, kinds: [] },
|
|
847
|
+
updatedAt: failedAt,
|
|
848
|
+
});
|
|
849
|
+
if (!closing && idled.state === 'working') {
|
|
850
|
+
await writeProcessStatus('idle');
|
|
851
|
+
}
|
|
852
|
+
if (requestId && client) {
|
|
853
|
+
writeWire(client, { type: 'turn-error', requestId, message });
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
catch (persistenceError) {
|
|
857
|
+
await failWorker(persistenceError);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
else {
|
|
861
|
+
await failWorker(error);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
finally {
|
|
865
|
+
activeController = undefined;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
server.on('error', (error) => void failWorker(error).catch(() => closeControl()));
|
|
869
|
+
await new Promise((resolveListen, reject) => {
|
|
870
|
+
server.once('error', reject);
|
|
871
|
+
server.listen(socketFile, resolveListen);
|
|
872
|
+
});
|
|
873
|
+
const registered = await store.update(options.id, (state) => state.state !== 'working'
|
|
874
|
+
? state
|
|
875
|
+
: {
|
|
876
|
+
...state,
|
|
877
|
+
pid: process.pid,
|
|
878
|
+
detail: 'starting',
|
|
879
|
+
tempo: 'active',
|
|
880
|
+
socketPath: socketFile,
|
|
881
|
+
updatedAt: new Date().toISOString(),
|
|
882
|
+
});
|
|
883
|
+
if (registered.state !== 'working') {
|
|
884
|
+
server.close();
|
|
885
|
+
await runtime.close?.();
|
|
886
|
+
await rm(socketFile, { force: true });
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
await writeProcessStatus('working');
|
|
890
|
+
const stop = () => void setStopped('stopped').catch(() => undefined);
|
|
891
|
+
process.once('SIGTERM', stop);
|
|
892
|
+
process.once('SIGINT', stop);
|
|
893
|
+
turnQueue = turnQueue.then(() => runTurn(initial.intent, dispatch.resume));
|
|
894
|
+
try {
|
|
895
|
+
await finished;
|
|
896
|
+
}
|
|
897
|
+
finally {
|
|
898
|
+
process.removeListener('SIGTERM', stop);
|
|
899
|
+
process.removeListener('SIGINT', stop);
|
|
900
|
+
await turnQueue.catch(() => undefined);
|
|
901
|
+
await outputWrites;
|
|
902
|
+
await runtime.close?.();
|
|
903
|
+
await rm(socketFile, { force: true });
|
|
904
|
+
await rm(processFile, { force: true });
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
//# sourceMappingURL=top-level-agent-manager.js.map
|