praxis-agent 0.7.0 → 0.9.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/README.md +8 -0
- package/dist/application/background-agent-manager.d.ts +5 -0
- package/dist/application/background-agent-manager.js +21 -0
- package/dist/application/background-bash-manager.d.ts +14 -0
- package/dist/application/background-bash-manager.js +42 -4
- package/dist/application/background-task-runtime.d.ts +34 -0
- package/dist/application/background-task-runtime.js +59 -0
- package/dist/application/session-service.d.ts +21 -1
- package/dist/application/session-service.js +188 -17
- package/dist/application/session-worktree.d.ts +1 -0
- package/dist/application/session-worktree.js +13 -2
- package/dist/application/subagent-service.d.ts +3 -0
- package/dist/application/subagent-service.js +6 -0
- package/dist/application/workflow-manager.d.ts +70 -2
- package/dist/application/workflow-manager.js +21 -4
- package/dist/cli/agents-dashboard.d.ts +9 -0
- package/dist/cli/agents-dashboard.js +16 -5
- package/dist/cli/interactive.d.ts +41 -3
- package/dist/cli/interactive.js +1281 -183
- package/dist/cli/tui/claude-style.d.ts +31 -3
- package/dist/cli/tui/claude-style.js +171 -54
- package/dist/cli/tui/config-dashboard.d.ts +63 -0
- package/dist/cli/tui/config-dashboard.js +150 -0
- package/dist/cli/tui/config-settings.d.ts +32 -0
- package/dist/cli/tui/config-settings.js +276 -0
- package/dist/cli/tui/custom-themes.d.ts +21 -0
- package/dist/cli/tui/custom-themes.js +284 -0
- package/dist/cli/tui/file-picker.d.ts +3 -1
- package/dist/cli/tui/file-picker.js +8 -2
- package/dist/cli/tui/keybindings.js +0 -1
- package/dist/cli/tui/mcp-panel-controller.d.ts +32 -0
- package/dist/cli/tui/mcp-panel-controller.js +80 -0
- package/dist/cli/tui/mcp-panel-projector.d.ts +79 -0
- package/dist/cli/tui/mcp-panel-projector.js +181 -0
- package/dist/cli/tui/mcp-panel.d.ts +8 -0
- package/dist/cli/tui/mcp-panel.js +99 -0
- package/dist/cli/tui/recently-denied.d.ts +15 -0
- package/dist/cli/tui/recently-denied.js +19 -0
- package/dist/cli/tui/runtime-interactions.d.ts +17 -0
- package/dist/cli/tui/runtime-interactions.js +100 -0
- package/dist/cli/tui/runtime-settings.d.ts +40 -0
- package/dist/cli/tui/runtime-settings.js +84 -0
- package/dist/cli/tui/slash-commands.js +10 -0
- package/dist/cli/tui/task-panel.d.ts +53 -0
- package/dist/cli/tui/task-panel.js +204 -0
- package/dist/cli/tui/terminal-notifications.d.ts +10 -0
- package/dist/cli/tui/terminal-notifications.js +48 -0
- package/dist/cli/tui/terminal-setup.d.ts +24 -0
- package/dist/cli/tui/terminal-setup.js +618 -0
- package/dist/cli/tui/theme-settings.d.ts +8 -0
- package/dist/cli/tui/theme-settings.js +132 -0
- package/dist/cli/tui/theme.d.ts +53 -0
- package/dist/cli/tui/theme.js +265 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.js +97 -17
- package/dist/compatibility/claude/schema.js +2 -0
- package/dist/core/runtime.d.ts +12 -0
- package/dist/core/runtime.js +24 -0
- package/dist/maintenance/self-update.js +1 -1
- package/dist/mcp/claude-mcp-management.d.ts +6 -1
- package/dist/mcp/claude-mcp-management.js +191 -44
- package/dist/mcp/claude-mcp-oauth.js +2 -0
- package/dist/mcp/claude-mcp-tools.d.ts +31 -3
- package/dist/mcp/claude-mcp-tools.js +116 -34
- package/dist/permissions/claude-permission-resolver.d.ts +3 -0
- package/dist/permissions/claude-permission-resolver.js +42 -20
- package/dist/platform/claude-version.js +2 -1
- package/dist/plugins/claude-plugin-runtime.js +11 -3
- package/dist/tools/claude-interactive-tools.d.ts +7 -0
- package/dist/tools/claude-interactive-tools.js +21 -8
- package/dist/tools/claude-task-tools.d.ts +4 -0
- package/dist/tools/claude-task-tools.js +7 -0
- package/package.json +2 -1
- package/dist/application/background-agent-manager.d.ts.map +0 -1
- package/dist/application/background-agent-manager.js.map +0 -1
- package/dist/application/background-bash-manager.d.ts.map +0 -1
- package/dist/application/background-bash-manager.js.map +0 -1
- package/dist/application/background-task-id.d.ts.map +0 -1
- package/dist/application/background-task-id.js.map +0 -1
- package/dist/application/claude-project-purge.d.ts.map +0 -1
- package/dist/application/claude-project-purge.js.map +0 -1
- package/dist/application/managed-worktree.d.ts.map +0 -1
- package/dist/application/managed-worktree.js.map +0 -1
- package/dist/application/model-compactor.d.ts.map +0 -1
- package/dist/application/model-compactor.js.map +0 -1
- package/dist/application/scheduled-prompt-manager.d.ts.map +0 -1
- package/dist/application/scheduled-prompt-manager.js.map +0 -1
- package/dist/application/session-service.d.ts.map +0 -1
- package/dist/application/session-service.js.map +0 -1
- package/dist/application/session-worktree.d.ts.map +0 -1
- package/dist/application/session-worktree.js.map +0 -1
- package/dist/application/subagent-service.d.ts.map +0 -1
- package/dist/application/subagent-service.js.map +0 -1
- package/dist/application/tool-use-summary.d.ts.map +0 -1
- package/dist/application/tool-use-summary.js.map +0 -1
- package/dist/application/top-level-agent-manager.d.ts.map +0 -1
- package/dist/application/top-level-agent-manager.js.map +0 -1
- package/dist/application/workflow-manager.d.ts.map +0 -1
- package/dist/application/workflow-manager.js.map +0 -1
- package/dist/application/workflow-meta.d.ts.map +0 -1
- package/dist/application/workflow-meta.js.map +0 -1
- package/dist/application/workflow-runtime.d.ts.map +0 -1
- package/dist/application/workflow-runtime.js.map +0 -1
- package/dist/application/workflow-worktree.d.ts.map +0 -1
- package/dist/application/workflow-worktree.js.map +0 -1
- package/dist/cli/agents-dashboard.d.ts.map +0 -1
- package/dist/cli/agents-dashboard.js.map +0 -1
- package/dist/cli/controls.d.ts.map +0 -1
- package/dist/cli/controls.js.map +0 -1
- package/dist/cli/debug.d.ts.map +0 -1
- package/dist/cli/debug.js.map +0 -1
- package/dist/cli/interactive.d.ts.map +0 -1
- package/dist/cli/interactive.js.map +0 -1
- package/dist/cli/protocol.d.ts.map +0 -1
- package/dist/cli/protocol.js.map +0 -1
- package/dist/cli/tui/claude-style.d.ts.map +0 -1
- package/dist/cli/tui/claude-style.js.map +0 -1
- package/dist/cli/tui/clipboard.d.ts.map +0 -1
- package/dist/cli/tui/clipboard.js.map +0 -1
- package/dist/cli/tui/composer-editor.d.ts.map +0 -1
- package/dist/cli/tui/composer-editor.js.map +0 -1
- package/dist/cli/tui/composer-images.d.ts.map +0 -1
- package/dist/cli/tui/composer-images.js.map +0 -1
- package/dist/cli/tui/conversation-export.d.ts.map +0 -1
- package/dist/cli/tui/conversation-export.js.map +0 -1
- package/dist/cli/tui/external-editor.d.ts.map +0 -1
- package/dist/cli/tui/external-editor.js.map +0 -1
- package/dist/cli/tui/file-picker.d.ts.map +0 -1
- package/dist/cli/tui/file-picker.js.map +0 -1
- package/dist/cli/tui/git-diff.d.ts.map +0 -1
- package/dist/cli/tui/git-diff.js.map +0 -1
- package/dist/cli/tui/hook-settings.d.ts.map +0 -1
- package/dist/cli/tui/hook-settings.js.map +0 -1
- package/dist/cli/tui/keybindings.d.ts.map +0 -1
- package/dist/cli/tui/keybindings.js.map +0 -1
- package/dist/cli/tui/memory-files.d.ts.map +0 -1
- package/dist/cli/tui/memory-files.js.map +0 -1
- package/dist/cli/tui/permission-settings.d.ts.map +0 -1
- package/dist/cli/tui/permission-settings.js.map +0 -1
- package/dist/cli/tui/slash-commands.d.ts.map +0 -1
- package/dist/cli/tui/slash-commands.js.map +0 -1
- package/dist/cli/tui/terminal-suspend.d.ts.map +0 -1
- package/dist/cli/tui/terminal-suspend.js.map +0 -1
- package/dist/cli/tui/workspace-directories.d.ts.map +0 -1
- package/dist/cli/tui/workspace-directories.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/compatibility/claude/compaction.d.ts.map +0 -1
- package/dist/compatibility/claude/compaction.js.map +0 -1
- package/dist/compatibility/claude/context.d.ts.map +0 -1
- package/dist/compatibility/claude/context.js.map +0 -1
- package/dist/compatibility/claude/dynamic-context.d.ts.map +0 -1
- package/dist/compatibility/claude/dynamic-context.js.map +0 -1
- package/dist/compatibility/claude/file-history.d.ts.map +0 -1
- package/dist/compatibility/claude/file-history.js.map +0 -1
- package/dist/compatibility/claude/file-resources.d.ts.map +0 -1
- package/dist/compatibility/claude/file-resources.js.map +0 -1
- package/dist/compatibility/claude/fork.d.ts.map +0 -1
- package/dist/compatibility/claude/fork.js.map +0 -1
- package/dist/compatibility/claude/history.d.ts.map +0 -1
- package/dist/compatibility/claude/history.js.map +0 -1
- package/dist/compatibility/claude/ownership.d.ts.map +0 -1
- package/dist/compatibility/claude/ownership.js.map +0 -1
- package/dist/compatibility/claude/paths.d.ts.map +0 -1
- package/dist/compatibility/claude/paths.js.map +0 -1
- package/dist/compatibility/claude/pr-links.d.ts.map +0 -1
- package/dist/compatibility/claude/pr-links.js.map +0 -1
- package/dist/compatibility/claude/projection.d.ts.map +0 -1
- package/dist/compatibility/claude/projection.js.map +0 -1
- package/dist/compatibility/claude/schema.d.ts.map +0 -1
- package/dist/compatibility/claude/schema.js.map +0 -1
- package/dist/compatibility/claude/shared-resources.d.ts.map +0 -1
- package/dist/compatibility/claude/shared-resources.js.map +0 -1
- package/dist/compatibility/claude/sidechain.d.ts.map +0 -1
- package/dist/compatibility/claude/sidechain.js.map +0 -1
- package/dist/compatibility/claude/tool-links.d.ts.map +0 -1
- package/dist/compatibility/claude/tool-links.js.map +0 -1
- package/dist/compatibility/claude/translation.d.ts.map +0 -1
- package/dist/compatibility/claude/translation.js.map +0 -1
- package/dist/compatibility/claude/workflow-replay.d.ts.map +0 -1
- package/dist/compatibility/claude/workflow-replay.js.map +0 -1
- package/dist/compatibility/claude/workflow.d.ts.map +0 -1
- package/dist/compatibility/claude/workflow.js.map +0 -1
- package/dist/core/compaction.d.ts.map +0 -1
- package/dist/core/compaction.js.map +0 -1
- package/dist/core/context-budget.d.ts.map +0 -1
- package/dist/core/context-budget.js.map +0 -1
- package/dist/core/context.d.ts.map +0 -1
- package/dist/core/context.js.map +0 -1
- package/dist/core/runtime.d.ts.map +0 -1
- package/dist/core/runtime.js.map +0 -1
- package/dist/core/usage.d.ts.map +0 -1
- package/dist/core/usage.js.map +0 -1
- package/dist/extensions/claude-extension-tools.d.ts.map +0 -1
- package/dist/extensions/claude-extension-tools.js.map +0 -1
- package/dist/extensions/claude-extensions.d.ts.map +0 -1
- package/dist/extensions/claude-extensions.js.map +0 -1
- package/dist/hooks/claude-hook-tools.d.ts.map +0 -1
- package/dist/hooks/claude-hook-tools.js.map +0 -1
- package/dist/hooks/claude-hooks.d.ts.map +0 -1
- package/dist/hooks/claude-hooks.js.map +0 -1
- package/dist/maintenance/doctor.d.ts.map +0 -1
- package/dist/maintenance/doctor.js.map +0 -1
- package/dist/maintenance/self-update.d.ts.map +0 -1
- package/dist/maintenance/self-update.js.map +0 -1
- package/dist/mcp/claude-mcp-management.d.ts.map +0 -1
- package/dist/mcp/claude-mcp-management.js.map +0 -1
- package/dist/mcp/claude-mcp-oauth.d.ts.map +0 -1
- package/dist/mcp/claude-mcp-oauth.js.map +0 -1
- package/dist/mcp/claude-mcp-tools.d.ts.map +0 -1
- package/dist/mcp/claude-mcp-tools.js.map +0 -1
- package/dist/mcp/praxis-mcp-server.d.ts.map +0 -1
- package/dist/mcp/praxis-mcp-server.js.map +0 -1
- package/dist/permissions/claude-auto-classifier.d.ts.map +0 -1
- package/dist/permissions/claude-auto-classifier.js.map +0 -1
- package/dist/permissions/claude-permission-resolver.d.ts.map +0 -1
- package/dist/permissions/claude-permission-resolver.js.map +0 -1
- package/dist/persistence/claude-job-store.d.ts.map +0 -1
- package/dist/persistence/claude-job-store.js.map +0 -1
- package/dist/persistence/claude-scheduled-task-store.d.ts.map +0 -1
- package/dist/persistence/claude-scheduled-task-store.js.map +0 -1
- package/dist/persistence/claude-sidechain-store.d.ts.map +0 -1
- package/dist/persistence/claude-sidechain-store.js.map +0 -1
- package/dist/persistence/claude-task-store.d.ts.map +0 -1
- package/dist/persistence/claude-task-store.js.map +0 -1
- package/dist/persistence/claude-transcript-store.d.ts.map +0 -1
- package/dist/persistence/claude-transcript-store.js.map +0 -1
- package/dist/persistence/claude-workflow-store.d.ts.map +0 -1
- package/dist/persistence/claude-workflow-store.js.map +0 -1
- package/dist/persistence/in-memory-sidechain-store.d.ts.map +0 -1
- package/dist/persistence/in-memory-sidechain-store.js.map +0 -1
- package/dist/persistence/in-memory-transcript-store.d.ts.map +0 -1
- package/dist/persistence/in-memory-transcript-store.js.map +0 -1
- package/dist/platform/atomic-write.d.ts.map +0 -1
- package/dist/platform/atomic-write.js.map +0 -1
- package/dist/platform/bounded-process-runner.d.ts.map +0 -1
- package/dist/platform/bounded-process-runner.js.map +0 -1
- package/dist/platform/claude-version.d.ts.map +0 -1
- package/dist/platform/claude-version.js.map +0 -1
- package/dist/platform/command-shell.d.ts.map +0 -1
- package/dist/platform/command-shell.js.map +0 -1
- package/dist/platform/exclusive-file-lease.d.ts.map +0 -1
- package/dist/platform/exclusive-file-lease.js.map +0 -1
- package/dist/platform/sensitive-data.d.ts.map +0 -1
- package/dist/platform/sensitive-data.js.map +0 -1
- package/dist/platform/tmux-worktree.d.ts.map +0 -1
- package/dist/platform/tmux-worktree.js.map +0 -1
- package/dist/plugins/claude-plugin-eval-discovery.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-eval-discovery.js.map +0 -1
- package/dist/plugins/claude-plugin-eval-graders.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-eval-graders.js.map +0 -1
- package/dist/plugins/claude-plugin-eval-runner.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-eval-runner.js.map +0 -1
- package/dist/plugins/claude-plugin-eval-schema.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-eval-schema.js.map +0 -1
- package/dist/plugins/claude-plugin-eval.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-eval.js.map +0 -1
- package/dist/plugins/claude-plugin-maintenance.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-maintenance.js.map +0 -1
- package/dist/plugins/claude-plugin-marketplace.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-marketplace.js.map +0 -1
- package/dist/plugins/claude-plugin-mcpb.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-mcpb.js.map +0 -1
- package/dist/plugins/claude-plugin-runtime.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-runtime.js.map +0 -1
- package/dist/providers/anthropic-compatible.d.ts.map +0 -1
- package/dist/providers/anthropic-compatible.js.map +0 -1
- package/dist/providers/environment.d.ts.map +0 -1
- package/dist/providers/environment.js.map +0 -1
- package/dist/providers/fallback-provider.d.ts.map +0 -1
- package/dist/providers/fallback-provider.js.map +0 -1
- package/dist/providers/openai-compatible.d.ts.map +0 -1
- package/dist/providers/openai-compatible.js.map +0 -1
- package/dist/tools/claude-interactive-tools.d.ts.map +0 -1
- package/dist/tools/claude-interactive-tools.js.map +0 -1
- package/dist/tools/claude-lsp-formatters.d.ts.map +0 -1
- package/dist/tools/claude-lsp-formatters.js.map +0 -1
- package/dist/tools/claude-lsp-tool.d.ts.map +0 -1
- package/dist/tools/claude-lsp-tool.js.map +0 -1
- package/dist/tools/claude-scheduled-tools.d.ts.map +0 -1
- package/dist/tools/claude-scheduled-tools.js.map +0 -1
- package/dist/tools/claude-task-tools.d.ts.map +0 -1
- package/dist/tools/claude-task-tools.js.map +0 -1
- package/dist/tools/claude-user-message.d.ts.map +0 -1
- package/dist/tools/claude-user-message.js.map +0 -1
- package/dist/tools/claude-workflow-tools.d.ts.map +0 -1
- package/dist/tools/claude-workflow-tools.js.map +0 -1
- package/dist/tools/claude-worktree-tools.d.ts.map +0 -1
- package/dist/tools/claude-worktree-tools.js.map +0 -1
- package/dist/tools/filtered-tool-registry.d.ts.map +0 -1
- package/dist/tools/filtered-tool-registry.js.map +0 -1
- package/dist/tools/glob.d.ts.map +0 -1
- package/dist/tools/glob.js.map +0 -1
- package/dist/tools/local-tools.d.ts.map +0 -1
- package/dist/tools/local-tools.js.map +0 -1
- package/dist/tools/notebook.d.ts.map +0 -1
- package/dist/tools/notebook.js.map +0 -1
- package/dist/tools/pdf.d.ts.map +0 -1
- package/dist/tools/pdf.js.map +0 -1
- package/dist/tools/web.d.ts.map +0 -1
- package/dist/tools/web.js.map +0 -1
|
@@ -14,6 +14,7 @@ import { selectClaudeSchemaAdapter, } from '../compatibility/claude/schema.js';
|
|
|
14
14
|
import { findUnresolvedClaudeToolCalls } from '../compatibility/claude/tool-links.js';
|
|
15
15
|
import { createClaudeAgentSettingEntry, createClaudeHookAttachmentEntries, createClaudeLastPromptEntry, createClaudeRuleAttachmentEntry, translateProviderEvents, } from '../compatibility/claude/translation.js';
|
|
16
16
|
import { AgentRunCancelledError, AgentRuntime, } from '../core/runtime.js';
|
|
17
|
+
import { BackgroundTaskRuntime, } from './background-task-runtime.js';
|
|
17
18
|
import { usageCostUsd } from '../core/usage.js';
|
|
18
19
|
import { ContextBudget, estimateModelRequestTokens, } from '../core/context-budget.js';
|
|
19
20
|
import { injectFirstUserMessageContext, } from '../core/context.js';
|
|
@@ -26,7 +27,7 @@ import { ScheduledPromptManager } from './scheduled-prompt-manager.js';
|
|
|
26
27
|
import { ClaudeScheduledToolRegistry } from '../tools/claude-scheduled-tools.js';
|
|
27
28
|
import { ClaudeTaskToolRegistry } from '../tools/claude-task-tools.js';
|
|
28
29
|
import { ClaudeWorkflowToolRegistry } from '../tools/claude-workflow-tools.js';
|
|
29
|
-
import { WorkflowManager } from './workflow-manager.js';
|
|
30
|
+
import { WorkflowManager, } from './workflow-manager.js';
|
|
30
31
|
import { SessionWorktreeManager } from './session-worktree.js';
|
|
31
32
|
import { ClaudeWorktreeToolRegistry } from '../tools/claude-worktree-tools.js';
|
|
32
33
|
import { generateToolUseSummary } from './tool-use-summary.js';
|
|
@@ -88,12 +89,14 @@ export class ClaudeSessionService {
|
|
|
88
89
|
inMemoryStores = new Map();
|
|
89
90
|
scheduledPrompts;
|
|
90
91
|
workflowManager;
|
|
92
|
+
backgroundTasks;
|
|
91
93
|
worktreeManager;
|
|
92
94
|
sessionCwds = new Map();
|
|
93
95
|
hostedSubagents = new Set();
|
|
94
96
|
hostedSubagentsByRegistry = new WeakMap();
|
|
95
97
|
backgroundNotificationWrites = new Map();
|
|
96
98
|
downloadedFileResourceSessions = new Set();
|
|
99
|
+
mcpClosePromise;
|
|
97
100
|
runtimeCwd;
|
|
98
101
|
constructor(options) {
|
|
99
102
|
this.options = options;
|
|
@@ -112,11 +115,15 @@ export class ClaudeSessionService {
|
|
|
112
115
|
this.workflowManager = options.enableWorkflows
|
|
113
116
|
? new WorkflowManager(options.configRoot, options.cwd, () => this.activeCwd())
|
|
114
117
|
: null;
|
|
118
|
+
this.backgroundTasks = new BackgroundTaskRuntime(this.workflowManager);
|
|
115
119
|
this.worktreeManager =
|
|
116
120
|
options.enableWorktrees && options.workspace
|
|
117
121
|
? new SessionWorktreeManager({
|
|
118
122
|
workspace: options.workspace,
|
|
119
123
|
sessionId: '',
|
|
124
|
+
...(options.worktreeBaseRef
|
|
125
|
+
? { baseRef: options.worktreeBaseRef }
|
|
126
|
+
: {}),
|
|
120
127
|
})
|
|
121
128
|
: null;
|
|
122
129
|
}
|
|
@@ -126,13 +133,42 @@ export class ClaudeSessionService {
|
|
|
126
133
|
workflows() {
|
|
127
134
|
return this.workflowManager?.list() ?? [];
|
|
128
135
|
}
|
|
136
|
+
mcpInspect() {
|
|
137
|
+
return this.requireMcp().inspect();
|
|
138
|
+
}
|
|
139
|
+
mcpReconnect(name) {
|
|
140
|
+
return this.requireMcp().reconnect(name);
|
|
141
|
+
}
|
|
142
|
+
mcpAuthenticate(name) {
|
|
143
|
+
return this.requireMcp().authenticate(name);
|
|
144
|
+
}
|
|
145
|
+
mcpReload() {
|
|
146
|
+
return this.requireMcp().reload();
|
|
147
|
+
}
|
|
148
|
+
mcpTools(name) {
|
|
149
|
+
return this.requireMcp().tools(name);
|
|
150
|
+
}
|
|
151
|
+
requireMcp() {
|
|
152
|
+
if (!this.options.mcp)
|
|
153
|
+
throw new Error('MCP runtime is not configured');
|
|
154
|
+
return this.options.mcp;
|
|
155
|
+
}
|
|
156
|
+
taskSnapshots(sessionId) {
|
|
157
|
+
return this.backgroundTasks.snapshot(sessionId);
|
|
158
|
+
}
|
|
159
|
+
stopTask(sessionId, taskId) {
|
|
160
|
+
return this.backgroundTasks.stop(sessionId, taskId);
|
|
161
|
+
}
|
|
129
162
|
async close() {
|
|
130
163
|
this.scheduledPrompts?.close();
|
|
131
164
|
await Promise.all([...this.hostedSubagents].map((executor) => executor.close()));
|
|
132
165
|
await Promise.resolve();
|
|
133
166
|
await Promise.all([...this.backgroundNotificationWrites.values()]);
|
|
134
167
|
this.hostedSubagents.clear();
|
|
168
|
+
this.backgroundTasks.clear();
|
|
135
169
|
await this.workflowManager?.close();
|
|
170
|
+
this.mcpClosePromise ??= this.options.mcp?.close?.() ?? Promise.resolve();
|
|
171
|
+
await this.mcpClosePromise;
|
|
136
172
|
}
|
|
137
173
|
createHostedToolRegistry(sessionId) {
|
|
138
174
|
const baseTools = this.options.tools;
|
|
@@ -155,6 +191,8 @@ export class ClaudeSessionService {
|
|
|
155
191
|
: {}),
|
|
156
192
|
})
|
|
157
193
|
: null;
|
|
194
|
+
if (taskTools)
|
|
195
|
+
this.backgroundTasks.registerBash(sessionId, taskTools);
|
|
158
196
|
const scheduledTools = this.scheduledPrompts &&
|
|
159
197
|
(this.options.scheduledToolNames?.length ?? 0) > 0
|
|
160
198
|
? new ClaudeScheduledToolRegistry({
|
|
@@ -221,6 +259,9 @@ export class ClaudeSessionService {
|
|
|
221
259
|
: null;
|
|
222
260
|
if (subagentExecutor)
|
|
223
261
|
this.hostedSubagents.add(subagentExecutor);
|
|
262
|
+
if (subagentExecutor) {
|
|
263
|
+
this.backgroundTasks.registerAgents(sessionId, subagentExecutor);
|
|
264
|
+
}
|
|
224
265
|
const agentTools = subagentExecutor
|
|
225
266
|
? subagentExecutor.registry(sessionId, 0, (callId) => callId)
|
|
226
267
|
: wrappedBase;
|
|
@@ -810,6 +851,13 @@ export class ClaudeSessionService {
|
|
|
810
851
|
throw new Error(`Claude cd usage conflict: ${result.reason}`);
|
|
811
852
|
}
|
|
812
853
|
}
|
|
854
|
+
async approveRecentlyDenied(sessionId, display) {
|
|
855
|
+
await this.appendPermissionGrant(sessionId, display, false);
|
|
856
|
+
}
|
|
857
|
+
async retryRecentlyDenied(sessionId, display, signal) {
|
|
858
|
+
await this.appendPermissionGrant(sessionId, display, true);
|
|
859
|
+
return this.executeTurn(sessionId, '/permissions', true, signal, undefined, [], [], undefined, undefined, true);
|
|
860
|
+
}
|
|
813
861
|
async recordBtwUsage(sessionId, permissionMode = 'default') {
|
|
814
862
|
const activeSessionId = await this.ensureLocalSession(sessionId, permissionMode);
|
|
815
863
|
await this.appendInputHistory('/btw', activeSessionId);
|
|
@@ -1188,7 +1236,7 @@ export class ClaudeSessionService {
|
|
|
1188
1236
|
throw new Error(`Claude permission mode update conflict: ${result.reason}`);
|
|
1189
1237
|
}
|
|
1190
1238
|
}
|
|
1191
|
-
async executeTurn(sessionId, prompt, requireExisting, signal, name, images = [], documents = [], resumeSessionAt, shellCommand) {
|
|
1239
|
+
async executeTurn(sessionId, prompt, requireExisting, signal, name, images = [], documents = [], resumeSessionAt, shellCommand, skipUserPrompt = false) {
|
|
1192
1240
|
this.assertWritable();
|
|
1193
1241
|
if (prompt.length === 0 && images.length === 0 && documents.length === 0)
|
|
1194
1242
|
throw new Error('Prompt must not be empty');
|
|
@@ -1205,6 +1253,10 @@ export class ClaudeSessionService {
|
|
|
1205
1253
|
}
|
|
1206
1254
|
const pinnedCwd = this.sessionCwds.get(sessionId) ?? this.activeCwd();
|
|
1207
1255
|
this.sessionCwds.set(sessionId, pinnedCwd);
|
|
1256
|
+
if (this.options.workspace?.cwd() !== pinnedCwd) {
|
|
1257
|
+
this.options.workspace?.setCwd(pinnedCwd);
|
|
1258
|
+
}
|
|
1259
|
+
this.runtimeCwd = pinnedCwd;
|
|
1208
1260
|
const sessionPaths = this.paths(sessionId);
|
|
1209
1261
|
const toolResultDirectory = join(sessionPaths.projectRoot, sessionId, 'tool-results');
|
|
1210
1262
|
const store = this.turnStore(sessionId);
|
|
@@ -1470,6 +1522,10 @@ export class ClaudeSessionService {
|
|
|
1470
1522
|
if (typeof path !== 'string') {
|
|
1471
1523
|
return interactiveMessageTools.execute(call, context);
|
|
1472
1524
|
}
|
|
1525
|
+
if ((call.name === 'Write' || call.name === 'Edit') &&
|
|
1526
|
+
(await this.options.interactiveTools?.isPlanFile(sessionId, path))) {
|
|
1527
|
+
return interactiveMessageTools.execute(call, context);
|
|
1528
|
+
}
|
|
1473
1529
|
const snapshotMessageId = currentPromptId ??
|
|
1474
1530
|
this.promptIdForToolCall(snapshot.entries, call.id);
|
|
1475
1531
|
const assistantMessageId = lastAssistantUuid ??
|
|
@@ -1743,13 +1799,15 @@ export class ClaudeSessionService {
|
|
|
1743
1799
|
]
|
|
1744
1800
|
: []),
|
|
1745
1801
|
];
|
|
1746
|
-
const expansion =
|
|
1747
|
-
?
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1802
|
+
const expansion = skipUserPrompt
|
|
1803
|
+
? { userMessages: [] }
|
|
1804
|
+
: shellCommand === undefined
|
|
1805
|
+
? this.options.extensions
|
|
1806
|
+
? await this.options.extensions.expandPromptAsync(prompt, signal, toolResultDirectory)
|
|
1807
|
+
: { userMessages: [prompt] }
|
|
1808
|
+
: {
|
|
1809
|
+
userMessages: [`<bash-input>${shellCommand}</bash-input>`],
|
|
1810
|
+
};
|
|
1753
1811
|
const expandedMessages = expansion.messages ?? expansion.userMessages.map((text) => ({ text }));
|
|
1754
1812
|
const attachmentIndex = expansion.messages
|
|
1755
1813
|
? expandedMessages.length - 1
|
|
@@ -1786,7 +1844,7 @@ export class ClaudeSessionService {
|
|
|
1786
1844
|
? { documents }
|
|
1787
1845
|
: {}),
|
|
1788
1846
|
}));
|
|
1789
|
-
const agentMentionMessages = shellCommand === undefined
|
|
1847
|
+
const agentMentionMessages = shellCommand === undefined && !skipUserPrompt
|
|
1790
1848
|
? (this.options.extensions?.agentMentionMessages(prompt) ?? [])
|
|
1791
1849
|
: [];
|
|
1792
1850
|
const injectAgentMentionContext = (messages) => {
|
|
@@ -1819,7 +1877,7 @@ export class ClaudeSessionService {
|
|
|
1819
1877
|
const injectTurnContext = (messages) => injectAgentMentionContext(injectDynamicContext(messages));
|
|
1820
1878
|
let compactionAnchorUuid = this.lastMessageUuid(snapshot.entries);
|
|
1821
1879
|
const compactIfNeeded = async (pendingMessages = [], preservedUserMessages = []) => {
|
|
1822
|
-
if (!budget)
|
|
1880
|
+
if (!budget || this.options.autoCompact === false)
|
|
1823
1881
|
return;
|
|
1824
1882
|
const historyMessages = projectClaudeModelMessages(snapshot.entries);
|
|
1825
1883
|
const predicted = budget.evaluate([
|
|
@@ -1991,7 +2049,7 @@ export class ClaudeSessionService {
|
|
|
1991
2049
|
tail: historyTail,
|
|
1992
2050
|
};
|
|
1993
2051
|
}
|
|
1994
|
-
if (this.options.hooks) {
|
|
2052
|
+
if (this.options.hooks && !skipUserPrompt) {
|
|
1995
2053
|
const outcome = await this.options.hooks.run({
|
|
1996
2054
|
...hookSession,
|
|
1997
2055
|
hook_event_name: 'UserPromptSubmit',
|
|
@@ -2168,11 +2226,13 @@ export class ClaudeSessionService {
|
|
|
2168
2226
|
if (!finalLeafUuid) {
|
|
2169
2227
|
throw new Error('Could not locate final assistant response');
|
|
2170
2228
|
}
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2229
|
+
if (!skipUserPrompt) {
|
|
2230
|
+
await this.append(lease, snapshot.tail, createClaudeLastPromptEntry({
|
|
2231
|
+
sessionId,
|
|
2232
|
+
lastPrompt: prompt,
|
|
2233
|
+
leafUuid: finalLeafUuid,
|
|
2234
|
+
}));
|
|
2235
|
+
}
|
|
2176
2236
|
const totalUsage = mergeUsage(mergeUsage(mergeUsage(recoveryUsage, compactionUsage), shellUsage), result.usage);
|
|
2177
2237
|
return {
|
|
2178
2238
|
sessionId,
|
|
@@ -2494,6 +2554,117 @@ export class ClaudeSessionService {
|
|
|
2494
2554
|
sessionId,
|
|
2495
2555
|
})}\n`, 'utf8');
|
|
2496
2556
|
}
|
|
2557
|
+
async appendPermissionGrant(sessionId, display, retry) {
|
|
2558
|
+
this.assertWritable();
|
|
2559
|
+
const normalized = display.trim();
|
|
2560
|
+
if (!normalized)
|
|
2561
|
+
throw new Error('Permission action must not be empty');
|
|
2562
|
+
while (true) {
|
|
2563
|
+
const result = await this.turnStore(sessionId).withLease(async (lease) => {
|
|
2564
|
+
const snapshot = await lease.load();
|
|
2565
|
+
if (snapshot.entries.length === 0) {
|
|
2566
|
+
throw new Error(`Claude session not found: ${sessionId}`);
|
|
2567
|
+
}
|
|
2568
|
+
const timestamp = new Date().toISOString();
|
|
2569
|
+
const promptId = randomUUID();
|
|
2570
|
+
const commandUuid = randomUUID();
|
|
2571
|
+
const common = {
|
|
2572
|
+
isSidechain: false,
|
|
2573
|
+
promptId,
|
|
2574
|
+
timestamp,
|
|
2575
|
+
userType: 'external',
|
|
2576
|
+
entrypoint: 'cli',
|
|
2577
|
+
cwd: this.activeCwd(),
|
|
2578
|
+
sessionId,
|
|
2579
|
+
version: this.options.claudeVersion,
|
|
2580
|
+
gitBranch: null,
|
|
2581
|
+
};
|
|
2582
|
+
let parentUuid = this.logicalTailUuid(snapshot.tail);
|
|
2583
|
+
const entries = [];
|
|
2584
|
+
if (this.options.fileCheckpointing &&
|
|
2585
|
+
this.options.sessionPersistence !== false) {
|
|
2586
|
+
const fileHistory = new ClaudeFileHistory(this.options.configRoot, sessionId, [this.activeCwd(), ...(this.options.fileRewindRoots ?? [])]);
|
|
2587
|
+
entries.push(await fileHistory.snapshot(snapshot.entries, commandUuid));
|
|
2588
|
+
}
|
|
2589
|
+
if (retry) {
|
|
2590
|
+
const retryUuid = randomUUID();
|
|
2591
|
+
entries.push({
|
|
2592
|
+
parentUuid,
|
|
2593
|
+
isSidechain: false,
|
|
2594
|
+
type: 'system',
|
|
2595
|
+
subtype: 'permission_retry',
|
|
2596
|
+
content: `Allowed ${normalized}`,
|
|
2597
|
+
commands: [normalized],
|
|
2598
|
+
level: 'info',
|
|
2599
|
+
isMeta: false,
|
|
2600
|
+
timestamp,
|
|
2601
|
+
uuid: retryUuid,
|
|
2602
|
+
userType: 'external',
|
|
2603
|
+
entrypoint: 'cli',
|
|
2604
|
+
cwd: this.activeCwd(),
|
|
2605
|
+
sessionId,
|
|
2606
|
+
version: this.options.claudeVersion,
|
|
2607
|
+
gitBranch: null,
|
|
2608
|
+
});
|
|
2609
|
+
parentUuid = retryUuid;
|
|
2610
|
+
}
|
|
2611
|
+
else {
|
|
2612
|
+
const caveatUuid = randomUUID();
|
|
2613
|
+
entries.push({
|
|
2614
|
+
...common,
|
|
2615
|
+
parentUuid,
|
|
2616
|
+
type: 'user',
|
|
2617
|
+
message: {
|
|
2618
|
+
role: 'user',
|
|
2619
|
+
content: '<local-command-caveat>Caveat: The messages below were generated by the user while running local commands. DO NOT respond to these messages or otherwise consider them in your response unless the user explicitly asks you to.</local-command-caveat>',
|
|
2620
|
+
},
|
|
2621
|
+
isMeta: true,
|
|
2622
|
+
uuid: caveatUuid,
|
|
2623
|
+
});
|
|
2624
|
+
parentUuid = caveatUuid;
|
|
2625
|
+
}
|
|
2626
|
+
entries.push({
|
|
2627
|
+
...common,
|
|
2628
|
+
parentUuid,
|
|
2629
|
+
type: 'user',
|
|
2630
|
+
message: {
|
|
2631
|
+
role: 'user',
|
|
2632
|
+
content: '<command-name>/permissions</command-name>\n <command-message>permissions</command-message>\n <command-args></command-args>',
|
|
2633
|
+
},
|
|
2634
|
+
uuid: commandUuid,
|
|
2635
|
+
});
|
|
2636
|
+
const outputUuid = randomUUID();
|
|
2637
|
+
entries.push({
|
|
2638
|
+
...common,
|
|
2639
|
+
parentUuid: commandUuid,
|
|
2640
|
+
type: 'user',
|
|
2641
|
+
message: {
|
|
2642
|
+
role: 'user',
|
|
2643
|
+
content: `<local-command-stdout>${retry ? '(no content)' : `Approved ${normalized}`}</local-command-stdout>`,
|
|
2644
|
+
},
|
|
2645
|
+
uuid: outputUuid,
|
|
2646
|
+
});
|
|
2647
|
+
entries.push({
|
|
2648
|
+
...common,
|
|
2649
|
+
parentUuid: outputUuid,
|
|
2650
|
+
type: 'user',
|
|
2651
|
+
message: {
|
|
2652
|
+
role: 'user',
|
|
2653
|
+
content: `Permission granted for: ${normalized}. You may now retry this command if you would like.`,
|
|
2654
|
+
},
|
|
2655
|
+
isMeta: true,
|
|
2656
|
+
uuid: randomUUID(),
|
|
2657
|
+
});
|
|
2658
|
+
const appended = await lease.appendMany(snapshot.tail, entries);
|
|
2659
|
+
if (appended.status === 'conflict') {
|
|
2660
|
+
throw new Error(`Claude permission grant append conflict: ${appended.reason}`);
|
|
2661
|
+
}
|
|
2662
|
+
});
|
|
2663
|
+
if (result.status === 'completed')
|
|
2664
|
+
return;
|
|
2665
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2497
2668
|
async appendBackgroundNotification(sessionId, content) {
|
|
2498
2669
|
while (true) {
|
|
2499
2670
|
const result = await this.turnStore(sessionId).withLease(async (lease) => {
|
|
@@ -143,14 +143,25 @@ export class SessionWorktreeManager {
|
|
|
143
143
|
}
|
|
144
144
|
const worktreePath = join(root, '.claude', 'worktrees', name);
|
|
145
145
|
const branch = `worktree-${nameFromPath(name)}`;
|
|
146
|
+
const baseCommit = this.options.baseRef === 'head'
|
|
147
|
+
? originalHeadCommit
|
|
148
|
+
: ((await gitOptional(root, ['merge-base', 'HEAD', '@{upstream}'])) ??
|
|
149
|
+
originalHeadCommit);
|
|
146
150
|
await mkdir(join(root, '.claude', 'worktrees'), { recursive: true });
|
|
147
151
|
try {
|
|
148
|
-
await git(root, [
|
|
152
|
+
await git(root, [
|
|
153
|
+
'worktree',
|
|
154
|
+
'add',
|
|
155
|
+
'-b',
|
|
156
|
+
branch,
|
|
157
|
+
worktreePath,
|
|
158
|
+
baseCommit,
|
|
159
|
+
]);
|
|
149
160
|
const gitDirectory = await git(worktreePath, ['rev-parse', '--git-dir']);
|
|
150
161
|
const absoluteGitDirectory = isAbsolute(gitDirectory)
|
|
151
162
|
? gitDirectory
|
|
152
163
|
: resolve(worktreePath, gitDirectory);
|
|
153
|
-
await writeFile(join(absoluteGitDirectory, 'CLAUDE_BASE'), `${
|
|
164
|
+
await writeFile(join(absoluteGitDirectory, 'CLAUDE_BASE'), `${baseCommit}\n`);
|
|
154
165
|
}
|
|
155
166
|
catch (error) {
|
|
156
167
|
await execFileAsync('git', ['-C', root, 'worktree', 'remove', '--force', worktreePath], {
|
|
@@ -3,6 +3,7 @@ import { type ContextAssembler } from '../core/context.js';
|
|
|
3
3
|
import { type ModelProvider, type ModelToolCall, type ModelToolDefinition, type ModelUsage, type PermissionResolver, type PermissionApproval, type RuntimeEventSink, type ToolExecutionContext, type ToolExecutionResult, type ToolRegistry } from '../core/runtime.js';
|
|
4
4
|
import type { ClaudeExtensionCatalog } from '../extensions/claude-extensions.js';
|
|
5
5
|
import type { ClaudeHookRunner } from '../hooks/claude-hooks.js';
|
|
6
|
+
import { type BackgroundAgentSnapshot } from './background-agent-manager.js';
|
|
6
7
|
export interface WorkflowAgentRunOptions {
|
|
7
8
|
sessionId: string;
|
|
8
9
|
promptId: string;
|
|
@@ -74,6 +75,8 @@ export declare class ClaudeSubagentExecutor {
|
|
|
74
75
|
isEnabled(name: string): boolean;
|
|
75
76
|
constructor(options: ClaudeSubagentExecutorOptions);
|
|
76
77
|
close(): Promise<void>;
|
|
78
|
+
backgroundSnapshots(): readonly BackgroundAgentSnapshot[];
|
|
79
|
+
stopBackgroundTask(taskId: string): string;
|
|
77
80
|
private cwd;
|
|
78
81
|
registry(sessionId: string, depth: number, promptIdForCall: (callId: string) => string | null): ToolRegistry;
|
|
79
82
|
definitions(): ModelToolDefinition;
|
|
@@ -176,6 +176,12 @@ export class ClaudeSubagentExecutor {
|
|
|
176
176
|
await this.background.close();
|
|
177
177
|
this.ephemeralSidechains.clear();
|
|
178
178
|
}
|
|
179
|
+
backgroundSnapshots() {
|
|
180
|
+
return this.background.snapshots();
|
|
181
|
+
}
|
|
182
|
+
stopBackgroundTask(taskId) {
|
|
183
|
+
return this.background.stop(taskId);
|
|
184
|
+
}
|
|
179
185
|
cwd() {
|
|
180
186
|
return this.options.cwdProvider?.() ?? this.options.cwd;
|
|
181
187
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { ClaudeWorkflowStore } from '../persistence/claude-workflow-store.js';
|
|
1
2
|
import type { WorkflowAgentRunOptions, WorkflowAgentRunResult } from './subagent-service.js';
|
|
2
|
-
import { parseWorkflowScript, type ParsedWorkflowScript } from './workflow-meta.js';
|
|
3
|
+
import { parseWorkflowScript, type ParsedWorkflowScript, type WorkflowMeta } from './workflow-meta.js';
|
|
3
4
|
export interface WorkflowSource {
|
|
4
5
|
script: string;
|
|
5
6
|
parsed: ParsedWorkflowScript;
|
|
@@ -24,6 +25,71 @@ export interface WorkflowLaunchResult {
|
|
|
24
25
|
scriptFile: string;
|
|
25
26
|
transcriptDirectory: string;
|
|
26
27
|
}
|
|
28
|
+
export interface WorkflowProgress {
|
|
29
|
+
index: number;
|
|
30
|
+
agentId: string;
|
|
31
|
+
label: string;
|
|
32
|
+
phase?: string;
|
|
33
|
+
status: 'running' | 'completed' | 'error';
|
|
34
|
+
cached?: boolean;
|
|
35
|
+
result?: unknown;
|
|
36
|
+
error?: string;
|
|
37
|
+
totalTokens: number;
|
|
38
|
+
toolCalls: number;
|
|
39
|
+
durationMs: number;
|
|
40
|
+
resolvedModel?: string;
|
|
41
|
+
requestedModel?: string;
|
|
42
|
+
requestedAgentType?: string;
|
|
43
|
+
queuedAt: number;
|
|
44
|
+
startedAt?: number;
|
|
45
|
+
promptPreview: string;
|
|
46
|
+
isolationPath?: string;
|
|
47
|
+
isolationRetained?: boolean;
|
|
48
|
+
isolationWarning?: string;
|
|
49
|
+
}
|
|
50
|
+
interface WorkflowTask {
|
|
51
|
+
taskId: string;
|
|
52
|
+
runId: string;
|
|
53
|
+
sessionId: string;
|
|
54
|
+
meta: WorkflowMeta;
|
|
55
|
+
script: string;
|
|
56
|
+
scriptFile: string;
|
|
57
|
+
transcriptDirectory: string;
|
|
58
|
+
store: ClaudeWorkflowStore;
|
|
59
|
+
args: unknown;
|
|
60
|
+
defaultModel: string;
|
|
61
|
+
startTime: number;
|
|
62
|
+
durationMs: number | null;
|
|
63
|
+
status: 'running' | 'completed' | 'failed' | 'killed';
|
|
64
|
+
result: unknown;
|
|
65
|
+
error?: string;
|
|
66
|
+
logs: string[];
|
|
67
|
+
phases: {
|
|
68
|
+
title: string;
|
|
69
|
+
detail?: string;
|
|
70
|
+
model?: string;
|
|
71
|
+
}[];
|
|
72
|
+
progress: WorkflowProgress[];
|
|
73
|
+
agentCount: number;
|
|
74
|
+
totalTokens: number;
|
|
75
|
+
totalInputTokens: number;
|
|
76
|
+
totalToolCalls: number;
|
|
77
|
+
controller: AbortController;
|
|
78
|
+
promise: Promise<void>;
|
|
79
|
+
notificationPending: boolean;
|
|
80
|
+
}
|
|
81
|
+
export interface WorkflowTaskSnapshot {
|
|
82
|
+
task_id: string;
|
|
83
|
+
task_type: 'local_workflow';
|
|
84
|
+
status: WorkflowTask['status'];
|
|
85
|
+
summary: string;
|
|
86
|
+
run_id: string;
|
|
87
|
+
progress: readonly WorkflowProgress[];
|
|
88
|
+
result: unknown;
|
|
89
|
+
startTime: number;
|
|
90
|
+
durationMs: number;
|
|
91
|
+
error?: string;
|
|
92
|
+
}
|
|
27
93
|
export declare class WorkflowManager {
|
|
28
94
|
private readonly configRoot;
|
|
29
95
|
private readonly cwd;
|
|
@@ -32,11 +98,13 @@ export declare class WorkflowManager {
|
|
|
32
98
|
private readonly activeRuns;
|
|
33
99
|
launch(options: WorkflowLaunchOptions): Promise<WorkflowLaunchResult>;
|
|
34
100
|
has(taskId: string): boolean;
|
|
101
|
+
hasForSession(sessionId: string, taskId: string): boolean;
|
|
35
102
|
output(taskId: string, options: {
|
|
36
103
|
block: boolean;
|
|
37
104
|
timeout: number;
|
|
38
105
|
}): Promise<string>;
|
|
39
106
|
stop(taskId: string): string;
|
|
107
|
+
stopAndWait(taskId: string): Promise<void>;
|
|
40
108
|
notifications(waitForRunning: boolean): Promise<{
|
|
41
109
|
messages: string[];
|
|
42
110
|
usage: {
|
|
@@ -44,7 +112,7 @@ export declare class WorkflowManager {
|
|
|
44
112
|
outputTokens: number;
|
|
45
113
|
};
|
|
46
114
|
}>;
|
|
47
|
-
list(): readonly
|
|
115
|
+
list(sessionId?: string): readonly WorkflowTaskSnapshot[];
|
|
48
116
|
abortAll(): void;
|
|
49
117
|
close(): Promise<void>;
|
|
50
118
|
constructor(configRoot: string, cwd: string, cwdProvider?: (() => string) | undefined);
|
|
@@ -130,6 +130,7 @@ export class WorkflowManager {
|
|
|
130
130
|
args: structuredClone(options.args),
|
|
131
131
|
defaultModel: options.defaultModel,
|
|
132
132
|
startTime: Date.now(),
|
|
133
|
+
durationMs: null,
|
|
133
134
|
status: 'running',
|
|
134
135
|
result: null,
|
|
135
136
|
logs: [],
|
|
@@ -167,6 +168,9 @@ export class WorkflowManager {
|
|
|
167
168
|
has(taskId) {
|
|
168
169
|
return this.tasks.has(taskId);
|
|
169
170
|
}
|
|
171
|
+
hasForSession(sessionId, taskId) {
|
|
172
|
+
return this.tasks.get(taskId)?.sessionId === sessionId;
|
|
173
|
+
}
|
|
170
174
|
async output(taskId, options) {
|
|
171
175
|
const task = this.task(taskId);
|
|
172
176
|
if (options.block && task.status === 'running') {
|
|
@@ -200,6 +204,14 @@ export class WorkflowManager {
|
|
|
200
204
|
command: task.meta.description,
|
|
201
205
|
});
|
|
202
206
|
}
|
|
207
|
+
async stopAndWait(taskId) {
|
|
208
|
+
const task = this.task(taskId);
|
|
209
|
+
if (task.status !== 'running') {
|
|
210
|
+
throw new Error(`Task ${taskId} is not running (status: ${task.status})`);
|
|
211
|
+
}
|
|
212
|
+
task.controller.abort('TaskStop');
|
|
213
|
+
await task.promise;
|
|
214
|
+
}
|
|
203
215
|
async notifications(waitForRunning) {
|
|
204
216
|
if (waitForRunning) {
|
|
205
217
|
await Promise.all([...this.tasks.values()]
|
|
@@ -222,8 +234,10 @@ export class WorkflowManager {
|
|
|
222
234
|
usage: { inputTokens, outputTokens },
|
|
223
235
|
};
|
|
224
236
|
}
|
|
225
|
-
list() {
|
|
226
|
-
return [...this.tasks.values()]
|
|
237
|
+
list(sessionId) {
|
|
238
|
+
return [...this.tasks.values()]
|
|
239
|
+
.filter((task) => sessionId === undefined || task.sessionId === sessionId)
|
|
240
|
+
.map((task) => this.summary(task));
|
|
227
241
|
}
|
|
228
242
|
abortAll() {
|
|
229
243
|
for (const task of this.tasks.values()) {
|
|
@@ -444,12 +458,13 @@ export class WorkflowManager {
|
|
|
444
458
|
task.error = `${error instanceof Error ? (error.stack ?? error.message) : String(error)}`;
|
|
445
459
|
}
|
|
446
460
|
finally {
|
|
461
|
+
task.durationMs = Date.now() - task.startTime;
|
|
447
462
|
task.notificationPending = true;
|
|
448
463
|
await task.store.writeRun(this.runRecord(task));
|
|
449
464
|
}
|
|
450
465
|
}
|
|
451
466
|
runRecord(task) {
|
|
452
|
-
const durationMs = Date.now() - task.startTime;
|
|
467
|
+
const durationMs = task.durationMs ?? Date.now() - task.startTime;
|
|
453
468
|
return {
|
|
454
469
|
runId: task.runId,
|
|
455
470
|
timestamp: new Date().toISOString(),
|
|
@@ -482,6 +497,8 @@ export class WorkflowManager {
|
|
|
482
497
|
run_id: task.runId,
|
|
483
498
|
progress: task.progress,
|
|
484
499
|
result: task.result,
|
|
500
|
+
startTime: task.startTime,
|
|
501
|
+
durationMs: task.durationMs ?? Date.now() - task.startTime,
|
|
485
502
|
...(task.error ? { error: task.error } : {}),
|
|
486
503
|
};
|
|
487
504
|
}
|
|
@@ -558,7 +575,7 @@ export class WorkflowManager {
|
|
|
558
575
|
`agents_empty_result: ${empty}`,
|
|
559
576
|
`subagent_tokens: ${task.totalTokens}`,
|
|
560
577
|
`tool_uses: ${task.totalToolCalls}`,
|
|
561
|
-
`duration_ms: ${Date.now() - task.startTime}`,
|
|
578
|
+
`duration_ms: ${task.durationMs ?? Date.now() - task.startTime}`,
|
|
562
579
|
'</usage>',
|
|
563
580
|
'</task-notification>',
|
|
564
581
|
].join('\n');
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { type ReactElement } from 'react';
|
|
1
2
|
import type { TopLevelAgentSummary } from '../application/top-level-agent-manager.js';
|
|
3
|
+
import { type TuiThemeSettings } from './tui/theme.js';
|
|
2
4
|
export interface AgentsDashboardManager {
|
|
3
5
|
launch(options: {
|
|
4
6
|
prompt: string;
|
|
@@ -33,5 +35,12 @@ export declare function runAgentsDashboard(options: {
|
|
|
33
35
|
manager: AgentsDashboardManager;
|
|
34
36
|
defaults: AgentsDashboardDefaults;
|
|
35
37
|
signal?: AbortSignal;
|
|
38
|
+
}, dependencies?: {
|
|
39
|
+
loadThemeSettings?: () => Promise<TuiThemeSettings>;
|
|
40
|
+
renderDashboard?: (element: ReactElement, options: {
|
|
41
|
+
exitOnCtrlC: boolean;
|
|
42
|
+
}) => {
|
|
43
|
+
waitUntilExit(): Promise<void>;
|
|
44
|
+
};
|
|
36
45
|
}): Promise<number>;
|
|
37
46
|
//# sourceMappingURL=agents-dashboard.d.ts.map
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useCallback, useEffect, useRef, useState, } from 'react';
|
|
3
3
|
import { Box, Text, render, useApp, useInput } from 'ink';
|
|
4
|
+
import { loadTuiThemeSettings } from './tui/theme-settings.js';
|
|
5
|
+
import { DEFAULT_TUI_THEME_SETTINGS, TuiThemeProvider, useTuiPalette, } from './tui/theme.js';
|
|
4
6
|
const DASHBOARD_SECTIONS = [
|
|
5
7
|
'Ready for review',
|
|
6
8
|
'Needs input',
|
|
@@ -72,10 +74,12 @@ function groupAgents(agents) {
|
|
|
72
74
|
return DASHBOARD_SECTIONS.flatMap((section) => agents.filter((agent) => sectionFor(agent) === section));
|
|
73
75
|
}
|
|
74
76
|
function AgentSection({ title, agents, selected, }) {
|
|
75
|
-
|
|
77
|
+
const palette = useTuiPalette();
|
|
78
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { bold: true, children: [title, " (", agents.length, ")"] }), agents.length === 0 ? (_jsx(Text, { dimColor: true, children: " None" })) : (agents.map((agent) => (_jsxs(Text, { ...(agent === selected ? { color: palette.accent } : {}), children: [agent === selected ? '› ' : ' ', agent.name, " \u00B7 ", agentStatus(agent), " \u00B7 ", agentKey(agent)] }, agentKey(agent)))))] }));
|
|
76
79
|
}
|
|
77
80
|
export function AgentsDashboardApp({ manager, defaults, signal, refreshIntervalMs = 1_000, onCancel, }) {
|
|
78
81
|
const { exit } = useApp();
|
|
82
|
+
const palette = useTuiPalette();
|
|
79
83
|
const [agents, setAgents] = useState([]);
|
|
80
84
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
81
85
|
const selectedIndexRef = useRef(0);
|
|
@@ -410,14 +414,21 @@ export function AgentsDashboardApp({ manager, defaults, signal, refreshIntervalM
|
|
|
410
414
|
editInput();
|
|
411
415
|
});
|
|
412
416
|
const selected = agents[selectedIndex];
|
|
413
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color:
|
|
417
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color: palette.accent, children: "Praxis agents" }), _jsxs(Text, { dimColor: true, children: [agents.length, " sessions \u00B7 live refresh"] }), DASHBOARD_SECTIONS.map((title) => (_jsx(AgentSection, { title: title, agents: agents.filter((agent) => sectionFor(agent) === title), ...(selected === undefined ? {} : { selected }) }, title))), mode === 'attach' ? (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: ["Attached to ", attachedId] }), attachedOutput ? (_jsx(Text, { children: attachedOutput })) : (_jsx(Text, { dimColor: true, children: "Waiting for output\u2026" })), _jsxs(Text, { children: ["\u203A ", input] }), _jsx(Text, { dimColor: true, children: "Enter sends \u00B7 Esc detaches \u00B7 Ctrl+C exits" })] })) : mode === 'review' ? (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: ["Reviewing ", reviewAgent ? agentKey(reviewAgent) : ''] }), _jsx(Text, { children: attachedOutput || 'No review output.' }), _jsxs(Text, { children: ["\u203A ", input] }), _jsx(Text, { dimColor: true, children: "Enter resumes with prompt \u00B7 Esc returns to list" })] })) : (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: ["\u203A ", input || 'describe a task for a new background agent'] }), _jsx(Text, { dimColor: true, children: "Enter dispatches \u00B7 \u2191/\u2193 select \u00B7 Enter empty attaches \u00B7 Ctrl+X stops \u00B7 Ctrl+R refreshes \u00B7 ? help \u00B7 Esc exits" }), selected ? (_jsxs(Text, { dimColor: true, children: ["Selected ", agentKey(selected)] })) : null] })), busy ? _jsx(Text, { dimColor: true, children: "Working\u2026" }) : null, notice ? _jsx(Text, { color: palette.warning, children: notice }) : null, showHelp ? (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { bold: true, children: "Shortcuts" }), _jsx(Text, { children: "Enter dispatches or attaches \u00B7 Esc detaches/exits \u00B7 Ctrl+X stops local background agents \u00B7 Ctrl+R refreshes \u00B7 ? closes help" })] })) : null] }));
|
|
414
418
|
}
|
|
415
|
-
export async function runAgentsDashboard(options) {
|
|
419
|
+
export async function runAgentsDashboard(options, dependencies = {}) {
|
|
416
420
|
const controller = new AbortController();
|
|
417
421
|
const signal = options.signal
|
|
418
422
|
? AbortSignal.any([options.signal, controller.signal])
|
|
419
423
|
: controller.signal;
|
|
420
|
-
|
|
424
|
+
let settings = DEFAULT_TUI_THEME_SETTINGS;
|
|
425
|
+
try {
|
|
426
|
+
settings = await (dependencies.loadThemeSettings ?? loadTuiThemeSettings)();
|
|
427
|
+
}
|
|
428
|
+
catch {
|
|
429
|
+
// A corrupt shared settings file must not make the standalone dashboard unusable.
|
|
430
|
+
}
|
|
431
|
+
const instance = (dependencies.renderDashboard ?? render)(_jsx(TuiThemeProvider, { settings: settings, children: _jsx(AgentsDashboardApp, { manager: options.manager, defaults: options.defaults, signal: signal, onCancel: () => controller.abort() }) }), { exitOnCtrlC: false });
|
|
421
432
|
await instance.waitUntilExit();
|
|
422
433
|
return signal.aborted ? 130 : 0;
|
|
423
434
|
}
|