@pellux/goodvibes-agent 1.1.7 → 1.3.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/CHANGELOG.md +329 -0
- package/README.md +46 -30
- package/dist/package/{ast-grep-napi.linux-x64-gnu-mkk8xwww.node → ast-grep-napi.linux-x64-gnu-swtppvy9.node} +0 -0
- package/dist/package/{ast-grep-napi.linux-x64-musl-ryqtgdv6.node → ast-grep-napi.linux-x64-musl-ttfcdtap.node} +0 -0
- package/dist/package/main.js +138903 -91477
- package/docs/README.md +14 -7
- package/docs/channels-remote-and-api.md +8 -5
- package/docs/connected-host.md +14 -12
- package/docs/getting-started.md +58 -30
- package/docs/knowledge-artifacts-and-multimodal.md +7 -5
- package/docs/project-planning.md +2 -2
- package/docs/providers-and-routing.md +11 -3
- package/docs/tools-and-commands.md +116 -45
- package/docs/voice-and-live-tts.md +3 -1
- package/package.json +3 -2
- package/release/release-notes.md +142 -5
- package/release/release-readiness.json +124 -94
- package/src/agent/assistant-cockpit.ts +247 -0
- package/src/agent/autonomy-schedule-format.ts +96 -0
- package/src/agent/autonomy-schedule.ts +514 -0
- package/src/agent/channel-delivery-receipts.ts +292 -0
- package/src/agent/competitive-feature-inventory.ts +294 -0
- package/src/agent/document-registry-types.ts +106 -0
- package/src/agent/document-registry.ts +734 -0
- package/src/agent/harness-control.ts +2 -2
- package/src/agent/memory-prompt.ts +2 -2
- package/src/agent/operator-actions.ts +50 -6
- package/src/agent/persona-registry.ts +8 -0
- package/src/agent/project-context-files.ts +273 -0
- package/src/agent/prompt-context-receipts.ts +502 -0
- package/src/agent/reminder-schedule-format.ts +16 -2
- package/src/agent/research-run-registry.ts +582 -0
- package/src/agent/research-source-registry.ts +441 -0
- package/src/agent/routine-registry.ts +19 -3
- package/src/agent/routine-schedule-format.ts +27 -2
- package/src/agent/runtime-profile-starters.ts +7 -0
- package/src/agent/runtime-profile.ts +106 -4
- package/src/agent/schedule-edit-format.ts +129 -0
- package/src/agent/schedule-edit.ts +496 -0
- package/src/agent/schedule-next-routes.ts +42 -0
- package/src/agent/setup-wizard-artifact-receipts.ts +366 -0
- package/src/agent/setup-wizard-checkpoint.ts +140 -0
- package/src/agent/setup-wizard.ts +683 -0
- package/src/agent/skill-registry-types.ts +102 -0
- package/src/agent/skill-registry.ts +81 -107
- package/src/agent/vibe-confirmation-routes.ts +62 -0
- package/src/agent/vibe-file.ts +285 -0
- package/src/cli/agent-knowledge-command.ts +45 -2
- package/src/cli/help.ts +3 -0
- package/src/cli/profiles-command.ts +25 -11
- package/src/config/agent-settings-policy.ts +1 -18
- package/src/input/agent-workspace-activation.ts +53 -0
- package/src/input/agent-workspace-artifact-browser-editor.ts +494 -0
- package/src/input/agent-workspace-artifact-metadata.ts +17 -0
- package/src/input/agent-workspace-basic-command-editors.ts +5 -1
- package/src/input/agent-workspace-categories.ts +153 -155
- package/src/input/agent-workspace-channel-triage.ts +481 -0
- package/src/input/agent-workspace-channels.ts +263 -0
- package/src/input/agent-workspace-command-editor.ts +152 -0
- package/src/input/agent-workspace-context-snapshot.ts +349 -0
- package/src/input/agent-workspace-delegation-editor-submission.ts +8 -0
- package/src/input/agent-workspace-document-editor.ts +502 -0
- package/src/input/agent-workspace-document-ops-editor.ts +523 -0
- package/src/input/agent-workspace-host-category.ts +1 -1
- package/src/input/agent-workspace-local-library-snapshot.ts +167 -0
- package/src/input/agent-workspace-model-compare-editor.ts +376 -0
- package/src/input/agent-workspace-model-compare-prompt-submission.ts +552 -0
- package/src/input/agent-workspace-model-compare-types.ts +82 -0
- package/src/input/agent-workspace-model-compare-utils.ts +35 -0
- package/src/input/agent-workspace-onboarding-categories.ts +141 -0
- package/src/input/agent-workspace-operations-command-editor-submission.ts +53 -0
- package/src/input/agent-workspace-operations-command-editors.ts +22 -0
- package/src/input/agent-workspace-research-report-editor.ts +212 -0
- package/src/input/agent-workspace-research-run-editor.ts +144 -0
- package/src/input/agent-workspace-research-source-editor.ts +167 -0
- package/src/input/agent-workspace-review-packet-snapshot.ts +593 -0
- package/src/input/agent-workspace-review-packet-utils.ts +265 -0
- package/src/input/agent-workspace-settings.ts +313 -30
- package/src/input/agent-workspace-setup-checkpoint-action.ts +127 -0
- package/src/input/agent-workspace-setup-snapshot.ts +217 -0
- package/src/input/agent-workspace-setup.ts +61 -1
- package/src/input/agent-workspace-snapshot.ts +184 -149
- package/src/input/agent-workspace-types.ts +287 -2
- package/src/input/agent-workspace.ts +23 -1
- package/src/input/command-registry.ts +9 -2
- package/src/input/commands/agent-runtime-profile-runtime.ts +28 -13
- package/src/input/commands/channels-runtime.ts +102 -114
- package/src/input/commands/delegation-runtime.ts +87 -24
- package/src/input/commands/knowledge-browser-flags.ts +12 -0
- package/src/input/commands/knowledge.ts +28 -14
- package/src/input/commands/operator-actions-runtime.ts +6 -3
- package/src/input/commands/schedule-runtime.ts +71 -14
- package/src/input/commands/session-content.ts +2 -2
- package/src/input/commands/shell-core.ts +2 -0
- package/src/input/commands/vibe-runtime.ts +140 -0
- package/src/input/commands.ts +2 -0
- package/src/input/connected-host-routes.ts +123 -0
- package/src/input/settings-modal-types.ts +14 -0
- package/src/input/setup-wizard-live-receipts.ts +76 -0
- package/src/main.ts +15 -15
- package/src/panels/qr-panel.ts +2 -2
- package/src/renderer/agent-workspace-context-lines.ts +701 -0
- package/src/renderer/agent-workspace.ts +68 -421
- package/src/renderer/settings-modal-helpers.ts +12 -0
- package/src/renderer/settings-modal.ts +12 -0
- package/src/runtime/bootstrap-command-context.ts +11 -1
- package/src/runtime/bootstrap-command-parts.ts +14 -3
- package/src/runtime/bootstrap-core.ts +39 -1
- package/src/runtime/bootstrap-shell.ts +6 -0
- package/src/runtime/bootstrap.ts +113 -30
- package/src/runtime/connected-host-auth.ts +3 -2
- package/src/runtime/execution-ledger.ts +231 -0
- package/src/runtime/services.ts +88 -10
- package/src/runtime/tool-permission-safety.ts +199 -5
- package/src/shell/session-continuity-hints.ts +39 -0
- package/src/tools/agent-artifacts-tool.ts +606 -0
- package/src/tools/agent-audit-tool.ts +155 -0
- package/src/tools/agent-autonomy-schedule-tool.ts +238 -0
- package/src/tools/agent-autonomy-tool.ts +140 -0
- package/src/tools/agent-channel-send-tool.ts +24 -2
- package/src/tools/agent-channels-tool.ts +140 -0
- package/src/tools/agent-computer-tool.ts +168 -0
- package/src/tools/agent-context-policy.ts +137 -7
- package/src/tools/agent-context-tool.ts +143 -0
- package/src/tools/agent-delegation-tool.ts +128 -0
- package/src/tools/agent-device-tool.ts +240 -0
- package/src/tools/agent-documents-tool.ts +684 -0
- package/src/tools/agent-execution-tool.ts +230 -0
- package/src/tools/agent-harness-agent-orchestration-policy.ts +75 -0
- package/src/tools/agent-harness-agent-orchestration.ts +791 -0
- package/src/tools/agent-harness-autonomy-intake.ts +785 -0
- package/src/tools/agent-harness-autonomy-live-records.ts +742 -0
- package/src/tools/agent-harness-autonomy-queue-types.ts +88 -0
- package/src/tools/agent-harness-autonomy-queue.ts +571 -0
- package/src/tools/agent-harness-autonomy-watcher-read-models.ts +509 -0
- package/src/tools/agent-harness-background-processes-types.ts +35 -0
- package/src/tools/agent-harness-background-processes.ts +798 -0
- package/src/tools/agent-harness-browser-cockpit-route.ts +373 -0
- package/src/tools/agent-harness-browser-control.ts +346 -0
- package/src/tools/agent-harness-browser-pwa-read-models.ts +622 -0
- package/src/tools/agent-harness-channel-metadata.ts +168 -10
- package/src/tools/agent-harness-cli-command-policy.ts +110 -0
- package/src/tools/agent-harness-command-catalog.ts +4 -2
- package/src/tools/agent-harness-command-runner.ts +42 -0
- package/src/tools/agent-harness-connected-host-status.ts +7 -4
- package/src/tools/agent-harness-delegation-posture.ts +234 -19
- package/src/tools/agent-harness-device-live-read-models.ts +366 -0
- package/src/tools/agent-harness-document-ops-reviewer-readiness.ts +234 -0
- package/src/tools/agent-harness-document-ops-types.ts +72 -0
- package/src/tools/agent-harness-document-ops.ts +671 -0
- package/src/tools/agent-harness-execution-history.ts +489 -0
- package/src/tools/agent-harness-execution-posture.ts +385 -0
- package/src/tools/agent-harness-file-recovery.ts +135 -0
- package/src/tools/agent-harness-interactive-runtime-records.ts +421 -0
- package/src/tools/agent-harness-keybinding-metadata.ts +16 -12
- package/src/tools/agent-harness-learning-curator-common.ts +102 -0
- package/src/tools/agent-harness-learning-curator-consolidation.ts +295 -0
- package/src/tools/agent-harness-learning-curator-proposals.ts +606 -0
- package/src/tools/agent-harness-learning-curator-types.ts +151 -0
- package/src/tools/agent-harness-learning-curator.ts +417 -0
- package/src/tools/agent-harness-local-model-benchmarks.ts +269 -0
- package/src/tools/agent-harness-local-model-cookbook.ts +238 -0
- package/src/tools/agent-harness-local-model-endpoints.ts +788 -0
- package/src/tools/agent-harness-local-model-smoke.ts +277 -0
- package/src/tools/agent-harness-local-model-url.ts +78 -0
- package/src/tools/agent-harness-media-posture.ts +223 -2
- package/src/tools/agent-harness-memory-external-providers.ts +796 -0
- package/src/tools/agent-harness-memory-posture.ts +672 -0
- package/src/tools/agent-harness-memory-provider-certification.ts +219 -0
- package/src/tools/agent-harness-memory-refinement.ts +340 -0
- package/src/tools/agent-harness-metadata.ts +100 -168
- package/src/tools/agent-harness-mode-catalog.ts +86 -33
- package/src/tools/agent-harness-model-catalog.ts +162 -0
- package/src/tools/agent-harness-model-provider-health.ts +283 -0
- package/src/tools/agent-harness-model-readiness.ts +432 -0
- package/src/tools/agent-harness-model-routing-types.ts +327 -0
- package/src/tools/agent-harness-model-routing-utils.ts +30 -0
- package/src/tools/agent-harness-model-routing.ts +165 -193
- package/src/tools/agent-harness-operator-methods.ts +115 -133
- package/src/tools/agent-harness-pairing-posture.ts +452 -18
- package/src/tools/agent-harness-personal-ops-certification.ts +116 -0
- package/src/tools/agent-harness-personal-ops-discovery.ts +533 -0
- package/src/tools/agent-harness-personal-ops-intake.ts +385 -0
- package/src/tools/agent-harness-personal-ops-lanes.ts +270 -0
- package/src/tools/agent-harness-personal-ops-operations.ts +225 -0
- package/src/tools/agent-harness-personal-ops-provider-records.ts +358 -0
- package/src/tools/agent-harness-personal-ops-provider-task-records.ts +321 -0
- package/src/tools/agent-harness-personal-ops-records.ts +684 -0
- package/src/tools/agent-harness-personal-ops-runner.ts +637 -0
- package/src/tools/agent-harness-personal-ops-types.ts +240 -0
- package/src/tools/agent-harness-personal-ops.ts +613 -0
- package/src/tools/agent-harness-project-context.ts +144 -0
- package/src/tools/agent-harness-prompt-context.ts +589 -0
- package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
- package/src/tools/agent-harness-release-evidence.ts +9 -7
- package/src/tools/agent-harness-release-readiness.ts +9 -7
- package/src/tools/agent-harness-remote-read-models.ts +541 -0
- package/src/tools/agent-harness-research-briefing.ts +446 -0
- package/src/tools/agent-harness-research-live-read-models.ts +500 -0
- package/src/tools/agent-harness-research-queue.ts +250 -0
- package/src/tools/agent-harness-research-runs.ts +307 -0
- package/src/tools/agent-harness-research-workflow.ts +395 -0
- package/src/tools/agent-harness-security-posture.ts +2 -2
- package/src/tools/agent-harness-service-posture.ts +9 -9
- package/src/tools/agent-harness-session-metadata.ts +3 -3
- package/src/tools/agent-harness-setup-connected-host.ts +501 -0
- package/src/tools/agent-harness-setup-describe.ts +557 -0
- package/src/tools/agent-harness-setup-handoffs.ts +502 -0
- package/src/tools/agent-harness-setup-model-helpers.ts +102 -0
- package/src/tools/agent-harness-setup-plan.ts +253 -0
- package/src/tools/agent-harness-setup-posture-types.ts +218 -0
- package/src/tools/agent-harness-setup-posture-utils.ts +173 -0
- package/src/tools/agent-harness-setup-posture.ts +449 -221
- package/src/tools/agent-harness-setup-smoke.ts +533 -0
- package/src/tools/agent-harness-sudo-posture.ts +114 -0
- package/src/tools/agent-harness-tool-schema.ts +142 -7
- package/src/tools/agent-harness-tool-types.ts +83 -0
- package/src/tools/agent-harness-tool-utils.ts +43 -0
- package/src/tools/agent-harness-tool.ts +319 -370
- package/src/tools/agent-harness-ui-surface-metadata.ts +76 -69
- package/src/tools/agent-harness-ui-surface-types.ts +46 -0
- package/src/tools/agent-harness-vibe-health.ts +105 -0
- package/src/tools/agent-harness-workspace-action-runner.ts +149 -0
- package/src/tools/agent-harness-workspace-actions.ts +114 -8
- package/src/tools/agent-harness-workspace-editor-execution.ts +214 -0
- package/src/tools/agent-harness-workspace-editor-runner.ts +755 -0
- package/src/tools/agent-host-tool.ts +159 -0
- package/src/tools/agent-knowledge-ingest-tool.ts +34 -1
- package/src/tools/agent-learning-consolidation-core.ts +327 -0
- package/src/tools/agent-learning-consolidation-tool.ts +758 -0
- package/src/tools/agent-memory-tool.ts +253 -0
- package/src/tools/agent-model-compare-export.ts +315 -0
- package/src/tools/agent-model-compare-handoff.ts +592 -0
- package/src/tools/agent-model-compare-judgment.ts +633 -0
- package/src/tools/agent-model-compare-run.ts +735 -0
- package/src/tools/agent-model-compare-tool.ts +698 -0
- package/src/tools/agent-model-compare-types.ts +191 -0
- package/src/tools/agent-model-compare-utils.ts +93 -0
- package/src/tools/agent-models-tool.ts +208 -0
- package/src/tools/agent-operator-action-tool.ts +1 -1
- package/src/tools/agent-operator-method-tool.ts +643 -0
- package/src/tools/agent-personal-ops-tool.ts +197 -0
- package/src/tools/agent-policy-explanation.ts +415 -0
- package/src/tools/agent-research-report-tool.ts +608 -0
- package/src/tools/agent-research-runner.ts +367 -0
- package/src/tools/agent-research-runs-tool.ts +434 -0
- package/src/tools/agent-research-sources-tool.ts +443 -0
- package/src/tools/agent-research-tool.ts +515 -0
- package/src/tools/agent-review-packet-presets-core.ts +757 -0
- package/src/tools/agent-review-packet-presets-tool.ts +466 -0
- package/src/tools/agent-review-packet-share-tool.ts +305 -0
- package/src/tools/agent-route-planner-candidates-setup.ts +411 -0
- package/src/tools/agent-route-planner-candidates-surfaces.ts +432 -0
- package/src/tools/agent-route-planner-candidates-work.ts +251 -0
- package/src/tools/agent-route-planner-helpers.ts +667 -0
- package/src/tools/agent-route-planner.ts +185 -0
- package/src/tools/agent-route-tool.ts +105 -0
- package/src/tools/agent-schedule-edit-tool.ts +210 -0
- package/src/tools/agent-schedule-tool.ts +282 -0
- package/src/tools/agent-security-tool.ts +145 -0
- package/src/tools/agent-sessions-tool.ts +122 -0
- package/src/tools/agent-settings-import-tool.ts +104 -0
- package/src/tools/agent-settings-tool.ts +176 -0
- package/src/tools/agent-setup-tool.ts +226 -0
- package/src/tools/agent-support-tool.ts +122 -0
- package/src/tools/agent-terminal-process-tools.ts +167 -0
- package/src/tools/agent-tool-policy-guard-types.ts +77 -0
- package/src/tools/agent-tool-policy-guard.ts +106 -85
- package/src/tools/agent-vibe-tool.ts +297 -0
- package/src/tools/agent-work-plan-tool.ts +265 -6
- package/src/tools/agent-workspace-tool.ts +305 -0
- package/src/tools/artifact-archive.ts +169 -0
- package/src/tools/tool-definition-compaction.ts +36 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
|
|
2
|
+
import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
|
|
3
|
+
import { AgentPersonaRegistry } from '../agent/persona-registry.ts';
|
|
4
|
+
import {
|
|
5
|
+
discoverVibeFiles,
|
|
6
|
+
initVibeFile,
|
|
7
|
+
loadVibeImportSource,
|
|
8
|
+
resolveVibePathReference,
|
|
9
|
+
type AgentVibeFile,
|
|
10
|
+
} from '../agent/vibe-file.ts';
|
|
11
|
+
import {
|
|
12
|
+
vibeImportPersonaConfirmationRoutes,
|
|
13
|
+
vibeInitConfirmationRoutes,
|
|
14
|
+
} from '../agent/vibe-confirmation-routes.ts';
|
|
15
|
+
import type { CommandContext } from '../input/command-registry.ts';
|
|
16
|
+
import { requireShellPaths } from '../input/commands/runtime-services.ts';
|
|
17
|
+
import { previewHarnessText } from './agent-harness-text.ts';
|
|
18
|
+
|
|
19
|
+
type AgentVibeAction = 'status' | 'show' | 'init' | 'import_persona';
|
|
20
|
+
|
|
21
|
+
interface AgentVibeToolArgs {
|
|
22
|
+
readonly action?: unknown;
|
|
23
|
+
readonly mode?: unknown;
|
|
24
|
+
readonly scope?: unknown;
|
|
25
|
+
readonly reference?: unknown;
|
|
26
|
+
readonly path?: unknown;
|
|
27
|
+
readonly target?: unknown;
|
|
28
|
+
readonly query?: unknown;
|
|
29
|
+
readonly name?: unknown;
|
|
30
|
+
readonly description?: unknown;
|
|
31
|
+
readonly force?: unknown;
|
|
32
|
+
readonly review?: unknown;
|
|
33
|
+
readonly use?: unknown;
|
|
34
|
+
readonly includeParameters?: unknown;
|
|
35
|
+
readonly confirm?: unknown;
|
|
36
|
+
readonly explicitUserRequest?: unknown;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function output(value: unknown): { readonly success: true; readonly output: string } {
|
|
40
|
+
return {
|
|
41
|
+
success: true,
|
|
42
|
+
output: typeof value === 'string' ? value : JSON.stringify(value, null, 2),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function error(message: string): { readonly success: false; readonly error: string } {
|
|
47
|
+
return { success: false, error: message };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function readString(value: unknown): string {
|
|
51
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function readBoolean(value: unknown): boolean {
|
|
55
|
+
return value === true || (typeof value === 'string' && ['true', 'yes', 'use', 'review', 'apply', 'run'].includes(value.trim().toLowerCase()));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function normalizeVibeAction(value: unknown): AgentVibeAction | null {
|
|
59
|
+
const action = readString(value).toLowerCase().replace(/-/g, '_');
|
|
60
|
+
if (!action) return null;
|
|
61
|
+
if (action === 'status' || action === 'summary' || action === 'list' || action === 'inspect') return 'status';
|
|
62
|
+
if (action === 'show' || action === 'file' || action === 'source' || action === 'read') return 'show';
|
|
63
|
+
if (action === 'init' || action === 'create' || action === 'new') return 'init';
|
|
64
|
+
if (action === 'import' || action === 'import_persona' || action === 'persona') return 'import_persona';
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function readAction(args: AgentVibeToolArgs): AgentVibeAction {
|
|
69
|
+
return normalizeVibeAction(args.action) ?? normalizeVibeAction(args.mode) ?? 'status';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function readReference(args: AgentVibeToolArgs): string {
|
|
73
|
+
return readString(args.path)
|
|
74
|
+
|| readString(args.reference)
|
|
75
|
+
|| readString(args.target)
|
|
76
|
+
|| readString(args.query)
|
|
77
|
+
|| readString(args.scope)
|
|
78
|
+
|| 'project';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function readScope(args: AgentVibeToolArgs): 'project' | 'global' {
|
|
82
|
+
return readString(args.scope).toLowerCase() === 'global' ? 'global' : 'project';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function requiresConfirmedUserIntent(args: AgentVibeToolArgs, action: AgentVibeAction): string | null {
|
|
86
|
+
if (!readBoolean(args.confirm)) return `vibe action:"${action}" requires confirm:true after the user asks for this personality change.`;
|
|
87
|
+
const explicitUserRequest = readString(args.explicitUserRequest);
|
|
88
|
+
if (!explicitUserRequest) return `vibe action:"${action}" requires explicitUserRequest when confirm is true.`;
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function describeVibeFile(file: AgentVibeFile): Record<string, unknown> {
|
|
93
|
+
return {
|
|
94
|
+
scope: file.scope,
|
|
95
|
+
path: file.path,
|
|
96
|
+
truncated: file.truncated,
|
|
97
|
+
name: file.frontmatter.name?.trim() || undefined,
|
|
98
|
+
description: file.frontmatter.description?.trim() || file.frontmatter.summary?.trim() || undefined,
|
|
99
|
+
bodyCharacters: file.body.length,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function createAgentVibeTool(commandContext: CommandContext): Tool {
|
|
104
|
+
return {
|
|
105
|
+
definition: {
|
|
106
|
+
name: 'vibe',
|
|
107
|
+
description: 'Inspect and manage VIBE.md personality files.',
|
|
108
|
+
parameters: {
|
|
109
|
+
type: 'object',
|
|
110
|
+
properties: {
|
|
111
|
+
action: {
|
|
112
|
+
type: 'string',
|
|
113
|
+
enum: ['status', 'show', 'init', 'import_persona'],
|
|
114
|
+
description: 'Read status/show; confirm init or import_persona.',
|
|
115
|
+
},
|
|
116
|
+
mode: { type: 'string', description: 'Alias for action.' },
|
|
117
|
+
scope: { type: 'string', enum: ['project', 'global'], description: 'VIBE.md scope for init/show/import.' },
|
|
118
|
+
reference: { type: 'string', description: 'project, global, or path reference.' },
|
|
119
|
+
path: { type: 'string', description: 'Path reference for show/import.' },
|
|
120
|
+
target: { type: 'string', description: 'Alias for reference.' },
|
|
121
|
+
query: { type: 'string', description: 'Alias for reference.' },
|
|
122
|
+
name: { type: 'string', description: 'Persona name when importing.' },
|
|
123
|
+
description: { type: 'string', description: 'Persona description when importing.' },
|
|
124
|
+
force: { type: 'boolean', description: 'Replace existing VIBE.md on confirmed init.' },
|
|
125
|
+
review: { type: 'boolean', description: 'Mark imported persona reviewed.' },
|
|
126
|
+
use: { type: 'boolean', description: 'Set imported persona active.' },
|
|
127
|
+
includeParameters: { type: 'boolean', description: 'Include searched paths and route details.' },
|
|
128
|
+
confirm: { type: 'boolean', description: 'Required true for init/import_persona.' },
|
|
129
|
+
explicitUserRequest: { type: 'string', description: 'User request authorizing confirmed personality effects.' },
|
|
130
|
+
},
|
|
131
|
+
additionalProperties: false,
|
|
132
|
+
},
|
|
133
|
+
sideEffects: ['state'],
|
|
134
|
+
concurrency: 'serial',
|
|
135
|
+
},
|
|
136
|
+
execute: async (rawArgs: unknown) => {
|
|
137
|
+
const args = (rawArgs && typeof rawArgs === 'object' && !Array.isArray(rawArgs) ? rawArgs : {}) as AgentVibeToolArgs;
|
|
138
|
+
const shellPaths = requireShellPaths(commandContext);
|
|
139
|
+
const action = readAction(args);
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
if (action === 'status') {
|
|
143
|
+
const snapshot = discoverVibeFiles(shellPaths);
|
|
144
|
+
return output({
|
|
145
|
+
status: snapshot.blocked.length > 0 || snapshot.files.some((file) => file.truncated)
|
|
146
|
+
? 'check'
|
|
147
|
+
: snapshot.files.length > 0
|
|
148
|
+
? 'ready'
|
|
149
|
+
: 'recommended',
|
|
150
|
+
action: 'vibe_status',
|
|
151
|
+
applied: snapshot.files.length,
|
|
152
|
+
blocked: snapshot.blocked.length,
|
|
153
|
+
truncated: snapshot.files.filter((file) => file.truncated).length,
|
|
154
|
+
projectInitPath: snapshot.projectInitPath,
|
|
155
|
+
globalInitPath: snapshot.globalInitPath,
|
|
156
|
+
files: snapshot.files.map(describeVibeFile),
|
|
157
|
+
blockedFiles: snapshot.blocked.map((file) => ({
|
|
158
|
+
scope: file.scope,
|
|
159
|
+
path: file.path,
|
|
160
|
+
reason: previewHarnessText(file.reason, 220),
|
|
161
|
+
})),
|
|
162
|
+
routes: {
|
|
163
|
+
showProject: 'vibe action:"show" scope:"project"',
|
|
164
|
+
showGlobal: 'vibe action:"show" scope:"global"',
|
|
165
|
+
initProject: 'vibe action:"init" scope:"project" confirm:true explicitUserRequest:"..."',
|
|
166
|
+
importProjectPersona: 'vibe action:"import_persona" scope:"project" review:true use:true confirm:true explicitUserRequest:"..."',
|
|
167
|
+
promptContext: 'context action:"prompt" includeParameters:true',
|
|
168
|
+
},
|
|
169
|
+
...(readBoolean(args.includeParameters) ? { searchedPaths: snapshot.searchedPaths } : {}),
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (action === 'show') {
|
|
174
|
+
const reference = readReference(args);
|
|
175
|
+
const source = loadVibeImportSource(shellPaths, reference);
|
|
176
|
+
return output({
|
|
177
|
+
status: 'ready',
|
|
178
|
+
action: 'vibe_show',
|
|
179
|
+
source: source.scope,
|
|
180
|
+
path: source.path,
|
|
181
|
+
name: source.name,
|
|
182
|
+
description: source.description,
|
|
183
|
+
bodyCharacters: source.body.length,
|
|
184
|
+
body: source.body,
|
|
185
|
+
policy: {
|
|
186
|
+
boundary: 'Loaded only after VIBE.md secret-looking content scan; blocked files are not returned.',
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (action === 'init') {
|
|
192
|
+
const scope = readScope(args);
|
|
193
|
+
const path = resolveVibePathReference(shellPaths, scope);
|
|
194
|
+
if (!readBoolean(args.confirm)) {
|
|
195
|
+
return output({
|
|
196
|
+
status: 'confirmation_required',
|
|
197
|
+
action: 'vibe_init',
|
|
198
|
+
scope,
|
|
199
|
+
path,
|
|
200
|
+
force: readBoolean(args.force),
|
|
201
|
+
next: 'Run with confirm:true and explicitUserRequest after the user asks to create or replace this VIBE.md file.',
|
|
202
|
+
confirmationRoutes: vibeInitConfirmationRoutes(scope, readBoolean(args.force)),
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
const intentError = requiresConfirmedUserIntent(args, 'init');
|
|
206
|
+
if (intentError) return error(intentError);
|
|
207
|
+
const result = initVibeFile(shellPaths, { scope, force: readBoolean(args.force) });
|
|
208
|
+
return output({
|
|
209
|
+
status: result.created ? 'created' : 'already_exists',
|
|
210
|
+
action: 'vibe_init',
|
|
211
|
+
scope,
|
|
212
|
+
path: result.path,
|
|
213
|
+
created: result.created,
|
|
214
|
+
next: result.created ? 'Edit this VIBE.md with the personality instructions the user wants.' : 'Use force:true only when the user asks to replace the existing file.',
|
|
215
|
+
policy: {
|
|
216
|
+
confirmation: 'confirmed',
|
|
217
|
+
explicitUserRequest: readString(args.explicitUserRequest),
|
|
218
|
+
boundary: 'Wrote only the scoped VIBE.md starter file.',
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (action === 'import_persona') {
|
|
224
|
+
const reference = readReference(args);
|
|
225
|
+
const source = loadVibeImportSource(shellPaths, reference);
|
|
226
|
+
const name = readString(args.name) || source.name;
|
|
227
|
+
const description = readString(args.description) || source.description;
|
|
228
|
+
if (!readBoolean(args.confirm)) {
|
|
229
|
+
return output({
|
|
230
|
+
status: 'confirmation_required',
|
|
231
|
+
action: 'vibe_import_persona',
|
|
232
|
+
source: source.scope,
|
|
233
|
+
path: source.path,
|
|
234
|
+
name,
|
|
235
|
+
description,
|
|
236
|
+
review: readBoolean(args.review),
|
|
237
|
+
use: readBoolean(args.use),
|
|
238
|
+
bodyCharacters: source.body.length,
|
|
239
|
+
next: 'Run with confirm:true and explicitUserRequest after the user asks to import VIBE.md into Agent-local personas.',
|
|
240
|
+
confirmationRoutes: vibeImportPersonaConfirmationRoutes({
|
|
241
|
+
reference,
|
|
242
|
+
name,
|
|
243
|
+
description,
|
|
244
|
+
review: readBoolean(args.review),
|
|
245
|
+
use: readBoolean(args.use),
|
|
246
|
+
}),
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
const intentError = requiresConfirmedUserIntent(args, 'import_persona');
|
|
250
|
+
if (intentError) return error(intentError);
|
|
251
|
+
const personaRegistry = AgentPersonaRegistry.fromShellPaths(shellPaths);
|
|
252
|
+
const created = personaRegistry.create({
|
|
253
|
+
name,
|
|
254
|
+
description,
|
|
255
|
+
body: source.body,
|
|
256
|
+
tags: ['vibe', source.scope],
|
|
257
|
+
triggers: ['vibe'],
|
|
258
|
+
source: 'imported',
|
|
259
|
+
provenance: `Imported VIBE.md (${source.scope}): ${source.path}`,
|
|
260
|
+
});
|
|
261
|
+
const reviewed = readBoolean(args.review) ? personaRegistry.markReviewed(created.id) : created;
|
|
262
|
+
const active = readBoolean(args.use) ? personaRegistry.setActive(created.id) : null;
|
|
263
|
+
return output({
|
|
264
|
+
status: 'imported',
|
|
265
|
+
action: 'vibe_import_persona',
|
|
266
|
+
source: source.scope,
|
|
267
|
+
path: source.path,
|
|
268
|
+
persona: {
|
|
269
|
+
id: reviewed.id,
|
|
270
|
+
name: reviewed.name,
|
|
271
|
+
description: reviewed.description,
|
|
272
|
+
reviewState: reviewed.reviewState,
|
|
273
|
+
active: active?.id === reviewed.id,
|
|
274
|
+
},
|
|
275
|
+
routes: {
|
|
276
|
+
inspectPersona: `agent_local_registry domain:"persona" action:"get" id:"${reviewed.id}"`,
|
|
277
|
+
promptContext: 'context action:"prompt" includeParameters:true',
|
|
278
|
+
},
|
|
279
|
+
policy: {
|
|
280
|
+
confirmation: 'confirmed',
|
|
281
|
+
explicitUserRequest: readString(args.explicitUserRequest),
|
|
282
|
+
boundary: 'Created only one Agent-local persona from secret-scanned VIBE.md content; no default Knowledge write.',
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return error('Unknown VIBE.md action. Use action:"status" to inspect personality files.');
|
|
288
|
+
} catch (caught) {
|
|
289
|
+
return error(caught instanceof Error ? caught.message : String(caught));
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export function registerAgentVibeTool(registry: ToolRegistry, commandContext: CommandContext): void {
|
|
296
|
+
if (!registry.has('vibe')) registry.register(createAgentVibeTool(commandContext));
|
|
297
|
+
}
|
|
@@ -13,27 +13,47 @@ export type AgentWorkPlanAction =
|
|
|
13
13
|
| 'create'
|
|
14
14
|
| 'update'
|
|
15
15
|
| 'set_status'
|
|
16
|
+
| 'dispatch_agents'
|
|
16
17
|
| 'remove'
|
|
17
18
|
| 'clear_completed';
|
|
18
19
|
|
|
19
20
|
export interface AgentWorkPlanToolArgs {
|
|
20
21
|
readonly action?: unknown;
|
|
21
22
|
readonly id?: unknown;
|
|
23
|
+
readonly ids?: unknown;
|
|
22
24
|
readonly title?: unknown;
|
|
23
25
|
readonly status?: unknown;
|
|
24
26
|
readonly owner?: unknown;
|
|
25
27
|
readonly source?: unknown;
|
|
26
28
|
readonly notes?: unknown;
|
|
29
|
+
readonly template?: unknown;
|
|
30
|
+
readonly model?: unknown;
|
|
31
|
+
readonly provider?: unknown;
|
|
32
|
+
readonly reasoningEffort?: unknown;
|
|
33
|
+
readonly tools?: unknown;
|
|
34
|
+
readonly successCriteria?: unknown;
|
|
35
|
+
readonly requiredEvidence?: unknown;
|
|
36
|
+
readonly writeScope?: unknown;
|
|
37
|
+
readonly executionProtocol?: unknown;
|
|
38
|
+
readonly reviewMode?: unknown;
|
|
39
|
+
readonly communicationLane?: unknown;
|
|
40
|
+
readonly cohort?: unknown;
|
|
41
|
+
readonly agentContext?: unknown;
|
|
27
42
|
readonly confirm?: unknown;
|
|
28
43
|
readonly explicitUserRequest?: unknown;
|
|
29
44
|
}
|
|
30
45
|
|
|
46
|
+
interface AgentWorkPlanToolOptions {
|
|
47
|
+
readonly toolRegistry?: Pick<ToolRegistry, 'has' | 'execute'>;
|
|
48
|
+
}
|
|
49
|
+
|
|
31
50
|
const ACTIONS: readonly AgentWorkPlanAction[] = [
|
|
32
51
|
'list',
|
|
33
52
|
'get',
|
|
34
53
|
'create',
|
|
35
54
|
'update',
|
|
36
55
|
'set_status',
|
|
56
|
+
'dispatch_agents',
|
|
37
57
|
'remove',
|
|
38
58
|
'clear_completed',
|
|
39
59
|
];
|
|
@@ -54,6 +74,12 @@ function readBoolean(value: unknown): boolean {
|
|
|
54
74
|
return value === true || value === 'true' || value === 'yes';
|
|
55
75
|
}
|
|
56
76
|
|
|
77
|
+
function readStringList(value: unknown): readonly string[] {
|
|
78
|
+
if (Array.isArray(value)) return value.map(readString).filter((entry) => entry.length > 0);
|
|
79
|
+
const text = readString(value);
|
|
80
|
+
return text ? text.split(/[\n,]/).map((entry) => entry.trim()).filter((entry) => entry.length > 0) : [];
|
|
81
|
+
}
|
|
82
|
+
|
|
57
83
|
function failure(error: string): { readonly success: false; readonly error: string } {
|
|
58
84
|
return { success: false, error };
|
|
59
85
|
}
|
|
@@ -74,6 +100,11 @@ function requireTitle(args: AgentWorkPlanToolArgs): string {
|
|
|
74
100
|
return title;
|
|
75
101
|
}
|
|
76
102
|
|
|
103
|
+
function dispatchIds(args: AgentWorkPlanToolArgs): readonly string[] {
|
|
104
|
+
const selected = [...readStringList(args.ids), readString(args.id)].filter((entry) => entry.length > 0);
|
|
105
|
+
return [...new Set(selected)];
|
|
106
|
+
}
|
|
107
|
+
|
|
77
108
|
function readOptionalStatus(value: unknown): WorkPlanItemStatus | undefined {
|
|
78
109
|
if (value === undefined || value === null || readString(value) === '') return undefined;
|
|
79
110
|
if (!isStatus(value)) throw new Error(`Invalid status. Valid values ${WORK_PLAN_STATUSES.join(', ')}.`);
|
|
@@ -91,6 +122,51 @@ function formatItem(item: WorkPlanItem): string {
|
|
|
91
122
|
return `${item.id} ${formatStatus(item.status)}${owner}${source}${completed} ${item.title}`;
|
|
92
123
|
}
|
|
93
124
|
|
|
125
|
+
function routeArg(value: string): string {
|
|
126
|
+
return JSON.stringify(value);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function agentRoute(mode: string, agentId: string): string {
|
|
130
|
+
return `agent { mode: ${routeArg(mode)}, agentId: ${routeArg(agentId)} }`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function workPlanRoute(action: string, id?: string, status?: WorkPlanItemStatus): string {
|
|
134
|
+
return [
|
|
135
|
+
`agent_work_plan action:${routeArg(action)}`,
|
|
136
|
+
id ? `id:${routeArg(id)}` : '',
|
|
137
|
+
status ? `status:${routeArg(status)}` : '',
|
|
138
|
+
].filter(Boolean).join(' ');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function linkedAgentId(item: WorkPlanItem): string {
|
|
142
|
+
const value = item.linked?.agentId;
|
|
143
|
+
return typeof value === 'string' && value.trim() ? value.trim() : '';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function nextRouteLine(id: string, route: string): string {
|
|
147
|
+
return ` ${id} ${route}`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function workPlanNextRouteLines(item: WorkPlanItem): readonly string[] {
|
|
151
|
+
const agentId = linkedAgentId(item);
|
|
152
|
+
const lines = [
|
|
153
|
+
' nextRoutes',
|
|
154
|
+
nextRouteLine('inspectWorkItem', workPlanRoute('get', item.id)),
|
|
155
|
+
nextRouteLine('markDone', workPlanRoute('set_status', item.id, 'done')),
|
|
156
|
+
nextRouteLine('markBlocked', workPlanRoute('set_status', item.id, 'blocked')),
|
|
157
|
+
];
|
|
158
|
+
if (agentId) {
|
|
159
|
+
lines.push(
|
|
160
|
+
nextRouteLine('inspectAgent', agentRoute('get', agentId)),
|
|
161
|
+
nextRouteLine('waitAgent', agentRoute('wait', agentId)),
|
|
162
|
+
nextRouteLine('messageAgent', agentRoute('message', agentId)),
|
|
163
|
+
nextRouteLine('cancelAgent', agentRoute('cancel', agentId)),
|
|
164
|
+
nextRouteLine('orchestrationDetail', `agent_harness mode:"agent_orchestration_agent" agentId:${routeArg(agentId)} includeParameters:true`),
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
return lines;
|
|
168
|
+
}
|
|
169
|
+
|
|
94
170
|
function formatItemDetail(item: WorkPlanItem): string {
|
|
95
171
|
return [
|
|
96
172
|
formatItem(item),
|
|
@@ -101,9 +177,16 @@ function formatItemDetail(item: WorkPlanItem): string {
|
|
|
101
177
|
: 'linked (none)',
|
|
102
178
|
'',
|
|
103
179
|
item.notes || '(no notes)',
|
|
180
|
+
'',
|
|
181
|
+
...workPlanNextRouteLines(item),
|
|
104
182
|
].join('\n');
|
|
105
183
|
}
|
|
106
184
|
|
|
185
|
+
function previewText(value: string, maxLength: number): string {
|
|
186
|
+
const normalized = value.replace(/\s+/g, ' ').trim();
|
|
187
|
+
return normalized.length > maxLength ? `${normalized.slice(0, Math.max(0, maxLength - 1))}...` : normalized;
|
|
188
|
+
}
|
|
189
|
+
|
|
107
190
|
function resolveItem(store: WorkPlanStore, idOrPrefix: string): WorkPlanItem {
|
|
108
191
|
const needle = idOrPrefix.trim();
|
|
109
192
|
if (!needle) throw new Error('id is required.');
|
|
@@ -118,6 +201,12 @@ function resolveItem(store: WorkPlanStore, idOrPrefix: string): WorkPlanItem {
|
|
|
118
201
|
throw new Error(`Work plan item not found ${needle}`);
|
|
119
202
|
}
|
|
120
203
|
|
|
204
|
+
function resolveDispatchItems(store: WorkPlanStore, args: AgentWorkPlanToolArgs): readonly WorkPlanItem[] {
|
|
205
|
+
const ids = dispatchIds(args);
|
|
206
|
+
if (ids.length === 0) throw new Error('id or ids is required for dispatch_agents.');
|
|
207
|
+
return ids.map((id) => resolveItem(store, id));
|
|
208
|
+
}
|
|
209
|
+
|
|
121
210
|
function listOutput(store: WorkPlanStore): string {
|
|
122
211
|
const plan = store.getActivePlan();
|
|
123
212
|
const counts = new Map<WorkPlanItemStatus, number>(WORK_PLAN_STATUSES.map((status) => [status, 0]));
|
|
@@ -134,6 +223,7 @@ function listOutput(store: WorkPlanStore): string {
|
|
|
134
223
|
}
|
|
135
224
|
lines.push('', ...plan.items.slice(0, 20).map(formatItem));
|
|
136
225
|
if (plan.items.length > 20) lines.push(`${plan.items.length - 20} more item(s) omitted.`);
|
|
226
|
+
lines.push('', 'Visible dispatch route: agent_work_plan action:"dispatch_agents" ids:["..."] confirm:true explicitUserRequest:"..."');
|
|
137
227
|
return lines.join('\n');
|
|
138
228
|
}
|
|
139
229
|
|
|
@@ -150,6 +240,21 @@ function requireDestructiveConfirmation(args: AgentWorkPlanToolArgs, action: str
|
|
|
150
240
|
return null;
|
|
151
241
|
}
|
|
152
242
|
|
|
243
|
+
function requireConfirmedDispatch(args: AgentWorkPlanToolArgs, items: readonly WorkPlanItem[], route: string): string | null {
|
|
244
|
+
const explicitUserRequest = readString(args.explicitUserRequest);
|
|
245
|
+
if (!explicitUserRequest) return 'explicitUserRequest is required before dispatching work plan items to visible agents.';
|
|
246
|
+
if (readBoolean(args.confirm)) return null;
|
|
247
|
+
return [
|
|
248
|
+
'Agent work plan dispatch preview',
|
|
249
|
+
' policy visible agent dispatch requires confirm:true and an explicit user request',
|
|
250
|
+
` request ${previewText(explicitUserRequest, 160)}`,
|
|
251
|
+
` route ${route}`,
|
|
252
|
+
` selected ${items.length}`,
|
|
253
|
+
...items.map((item) => ` - ${item.id} ${formatStatus(item.status)} ${previewText(item.title, 120)}`),
|
|
254
|
+
' no agents were spawned and no work-plan receipts were written',
|
|
255
|
+
].join('\n');
|
|
256
|
+
}
|
|
257
|
+
|
|
153
258
|
function updatePatch(args: AgentWorkPlanToolArgs): Parameters<WorkPlanStore['updateItem']>[1] {
|
|
154
259
|
const status = readOptionalStatus(args.status);
|
|
155
260
|
return {
|
|
@@ -161,28 +266,181 @@ function updatePatch(args: AgentWorkPlanToolArgs): Parameters<WorkPlanStore['upd
|
|
|
161
266
|
};
|
|
162
267
|
}
|
|
163
268
|
|
|
164
|
-
|
|
269
|
+
function optionalStringProperty(value: unknown): string | undefined {
|
|
270
|
+
return readString(value) || undefined;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function optionalStringListProperty(value: unknown): readonly string[] | undefined {
|
|
274
|
+
const entries = readStringList(value);
|
|
275
|
+
return entries.length > 0 ? entries : undefined;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function workPlanAgentContext(item: WorkPlanItem, args: AgentWorkPlanToolArgs): string {
|
|
279
|
+
return [
|
|
280
|
+
readString(args.agentContext),
|
|
281
|
+
`Work plan item id: ${item.id}`,
|
|
282
|
+
`Work plan item title: ${item.title}`,
|
|
283
|
+
item.owner ? `Work plan owner: ${item.owner}` : '',
|
|
284
|
+
item.source ? `Work plan source: ${item.source}` : '',
|
|
285
|
+
item.notes ? `Work plan notes:\n${item.notes}` : '',
|
|
286
|
+
'Keep progress visible, return evidence, and make the final status easy for the user to review.',
|
|
287
|
+
].filter((part) => part.trim().length > 0).join('\n\n');
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function agentTaskForItem(item: WorkPlanItem, args: AgentWorkPlanToolArgs): Record<string, unknown> {
|
|
291
|
+
return {
|
|
292
|
+
task: item.title,
|
|
293
|
+
...(optionalStringProperty(args.template) ? { template: optionalStringProperty(args.template) } : {}),
|
|
294
|
+
...(optionalStringProperty(args.model) ? { model: optionalStringProperty(args.model) } : {}),
|
|
295
|
+
...(optionalStringProperty(args.provider) ? { provider: optionalStringProperty(args.provider) } : {}),
|
|
296
|
+
...(optionalStringProperty(args.reasoningEffort) ? { reasoningEffort: optionalStringProperty(args.reasoningEffort) } : {}),
|
|
297
|
+
...(optionalStringListProperty(args.tools) ? { tools: optionalStringListProperty(args.tools), restrictTools: true } : {}),
|
|
298
|
+
...(optionalStringListProperty(args.successCriteria) ? { successCriteria: optionalStringListProperty(args.successCriteria) } : {}),
|
|
299
|
+
requiredEvidence: optionalStringListProperty(args.requiredEvidence) ?? ['result summary', 'verification or blocker', 'changed files or artifacts when applicable'],
|
|
300
|
+
...(optionalStringListProperty(args.writeScope) ? { writeScope: optionalStringListProperty(args.writeScope) } : {}),
|
|
301
|
+
...(optionalStringProperty(args.executionProtocol) ? { executionProtocol: optionalStringProperty(args.executionProtocol) } : {}),
|
|
302
|
+
...(optionalStringProperty(args.reviewMode) ? { reviewMode: optionalStringProperty(args.reviewMode) } : {}),
|
|
303
|
+
...(optionalStringProperty(args.communicationLane) ? { communicationLane: optionalStringProperty(args.communicationLane) } : {}),
|
|
304
|
+
context: workPlanAgentContext(item, args),
|
|
305
|
+
orchestrationNodeId: item.id,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function parseAgentToolOutput(outputText: string): Record<string, unknown> {
|
|
310
|
+
const parsed = JSON.parse(outputText) as unknown;
|
|
311
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('agent tool returned non-object output.');
|
|
312
|
+
return parsed as Record<string, unknown>;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function agentIdsFromDispatchPayload(payload: Record<string, unknown>): readonly string[] {
|
|
316
|
+
const direct = readString(payload.agentId);
|
|
317
|
+
if (direct) return [direct];
|
|
318
|
+
if (!Array.isArray(payload.agents)) return [];
|
|
319
|
+
return payload.agents
|
|
320
|
+
.map((entry) => entry && typeof entry === 'object' ? readString((entry as Record<string, unknown>).id) || readString((entry as Record<string, unknown>).agentId) : '')
|
|
321
|
+
.filter((entry) => entry.length > 0);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function appendDispatchReceipt(notes: string | undefined, agentId: string, route: string, cohort: string, explicitUserRequest: string): string {
|
|
325
|
+
const receipt = [
|
|
326
|
+
`Agent dispatch receipt ${new Date().toISOString()}`,
|
|
327
|
+
`agent ${agentId}`,
|
|
328
|
+
`route ${route}`,
|
|
329
|
+
`cohort ${cohort}`,
|
|
330
|
+
`request ${previewText(explicitUserRequest, 160)}`,
|
|
331
|
+
].join('; ');
|
|
332
|
+
return [notes?.trim() ?? '', receipt].filter((part) => part.length > 0).join('\n\n');
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function dispatchNextRouteLines(receipts: readonly { readonly item: WorkPlanItem; readonly agentId: string }[]): readonly string[] {
|
|
336
|
+
return [
|
|
337
|
+
' nextRoutes',
|
|
338
|
+
nextRouteLine('orchestration', 'agent_harness mode:"agent_orchestration" includeParameters:true'),
|
|
339
|
+
nextRouteLine('workPlan', 'agent_work_plan action:"list"'),
|
|
340
|
+
...receipts.flatMap((receipt, index) => {
|
|
341
|
+
const prefix = receipts.length > 1 ? `agent${index + 1}` : 'agent';
|
|
342
|
+
return [
|
|
343
|
+
nextRouteLine(`${prefix}.inspect`, agentRoute('get', receipt.agentId)),
|
|
344
|
+
nextRouteLine(`${prefix}.wait`, agentRoute('wait', receipt.agentId)),
|
|
345
|
+
nextRouteLine(`${prefix}.message`, agentRoute('message', receipt.agentId)),
|
|
346
|
+
nextRouteLine(`${prefix}.cancel`, agentRoute('cancel', receipt.agentId)),
|
|
347
|
+
nextRouteLine(`${prefix}.workItem`, workPlanRoute('get', receipt.item.id)),
|
|
348
|
+
];
|
|
349
|
+
}),
|
|
350
|
+
];
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async function dispatchAgentsFromWorkPlan(
|
|
354
|
+
store: WorkPlanStore,
|
|
355
|
+
args: AgentWorkPlanToolArgs,
|
|
356
|
+
options: AgentWorkPlanToolOptions,
|
|
357
|
+
): Promise<{ readonly success: true; readonly output: string } | { readonly success: false; readonly error: string }> {
|
|
358
|
+
if (!options.toolRegistry || !options.toolRegistry.has('agent')) {
|
|
359
|
+
return failure('agent_work_plan dispatch_agents requires the first-class agent tool to be registered.');
|
|
360
|
+
}
|
|
361
|
+
const items = resolveDispatchItems(store, args);
|
|
362
|
+
const plan = store.getActivePlan();
|
|
363
|
+
const cohort = readString(args.cohort) || `workplan-${plan.id}`;
|
|
364
|
+
const route = items.length === 1 ? 'agent { mode: "spawn" }' : 'agent { mode: "batch-spawn" }';
|
|
365
|
+
const denied = requireConfirmedDispatch(args, items, route);
|
|
366
|
+
if (denied) return failure(denied);
|
|
367
|
+
const explicitUserRequest = readString(args.explicitUserRequest);
|
|
368
|
+
const commonArgs = {
|
|
369
|
+
authoritativeTask: explicitUserRequest,
|
|
370
|
+
cohort,
|
|
371
|
+
orchestrationGraphId: `workplan:${plan.id}`,
|
|
372
|
+
};
|
|
373
|
+
const agentArgs = items.length === 1
|
|
374
|
+
? {
|
|
375
|
+
mode: 'spawn',
|
|
376
|
+
...commonArgs,
|
|
377
|
+
...agentTaskForItem(items[0]!, args),
|
|
378
|
+
}
|
|
379
|
+
: {
|
|
380
|
+
mode: 'batch-spawn',
|
|
381
|
+
...commonArgs,
|
|
382
|
+
tasks: items.map((item) => agentTaskForItem(item, args)),
|
|
383
|
+
};
|
|
384
|
+
const result = await options.toolRegistry.execute(`agent-work-plan-dispatch-${Date.now()}`, 'agent', agentArgs);
|
|
385
|
+
if (!result.success) return failure(result.error ?? 'Agent dispatch failed.');
|
|
386
|
+
const payload = parseAgentToolOutput(result.output ?? '{}');
|
|
387
|
+
const agentIds = agentIdsFromDispatchPayload(payload);
|
|
388
|
+
if (agentIds.length === 0) return failure('Agent dispatch succeeded but returned no agent ids to attach to the work plan.');
|
|
389
|
+
const receipts = items.map((item, index) => {
|
|
390
|
+
const agentId = agentIds[Math.min(index, agentIds.length - 1)]!;
|
|
391
|
+
const updated = store.updateItem(item.id, {
|
|
392
|
+
status: item.status === 'done' || item.status === 'cancelled' ? item.status : 'in_progress',
|
|
393
|
+
linked: { ...(item.linked ?? {}), agentId },
|
|
394
|
+
notes: appendDispatchReceipt(item.notes, agentId, route, cohort, explicitUserRequest),
|
|
395
|
+
});
|
|
396
|
+
return { item: updated, agentId };
|
|
397
|
+
});
|
|
398
|
+
return output([
|
|
399
|
+
'Dispatched Agent work plan items',
|
|
400
|
+
` route ${route}`,
|
|
401
|
+
` cohort ${cohort}`,
|
|
402
|
+
` saved receipts ${receipts.length}`,
|
|
403
|
+
...receipts.map((receipt) => ` - ${receipt.item.id} -> ${receipt.agentId} ${previewText(receipt.item.title, 100)}`),
|
|
404
|
+
...dispatchNextRouteLines(receipts),
|
|
405
|
+
].join('\n'));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export function createAgentWorkPlanTool(store: WorkPlanStore, options: AgentWorkPlanToolOptions = {}): Tool {
|
|
165
409
|
return {
|
|
166
410
|
definition: {
|
|
167
411
|
name: 'agent_work_plan',
|
|
168
|
-
description: '
|
|
412
|
+
description: 'Manage and dispatch approved visible Agent work plan items.',
|
|
169
413
|
parameters: {
|
|
170
414
|
type: 'object',
|
|
171
415
|
properties: {
|
|
172
416
|
action: { type: 'string', enum: [...ACTIONS] },
|
|
173
417
|
id: { type: 'string', description: 'Work plan item id or unique id prefix for get/update/set_status/remove.' },
|
|
418
|
+
ids: { type: 'array', items: { type: 'string' }, description: 'Work plan item ids or unique prefixes for dispatch_agents.' },
|
|
174
419
|
title: { type: 'string', description: 'Work plan item title for create/update.' },
|
|
175
420
|
status: { type: 'string', enum: [...WORK_PLAN_STATUSES], description: 'Work plan item status.' },
|
|
176
421
|
owner: { type: 'string', description: 'Optional owner label.' },
|
|
177
422
|
source: { type: 'string', description: 'Optional source/provenance label.' },
|
|
178
423
|
notes: { type: 'string', description: 'Optional work-plan notes.' },
|
|
179
|
-
|
|
180
|
-
|
|
424
|
+
template: { type: 'string', description: 'Agent template for dispatch_agents.' },
|
|
425
|
+
model: { type: 'string', description: 'Provider-qualified model for dispatch_agents.' },
|
|
426
|
+
provider: { type: 'string', description: 'Provider id for dispatch_agents when model is provider-qualified.' },
|
|
427
|
+
reasoningEffort: { type: 'string', enum: ['instant', 'low', 'medium', 'high'], description: 'Reasoning effort for dispatch_agents.' },
|
|
428
|
+
tools: { type: 'array', items: { type: 'string' }, description: 'Exact tool subset for dispatched agents.' },
|
|
429
|
+
successCriteria: { type: 'array', items: { type: 'string' }, description: 'Success criteria for dispatched agents.' },
|
|
430
|
+
requiredEvidence: { type: 'array', items: { type: 'string' }, description: 'Required evidence for dispatched agents.' },
|
|
431
|
+
writeScope: { type: 'array', items: { type: 'string' }, description: 'Expected write scope for dispatched agents.' },
|
|
432
|
+
executionProtocol: { type: 'string', enum: ['direct', 'gather-plan-apply'], description: 'Execution protocol for dispatch_agents.' },
|
|
433
|
+
reviewMode: { type: 'string', enum: ['none', 'wrfc'], description: 'Review mode for dispatch_agents.' },
|
|
434
|
+
communicationLane: { type: 'string', enum: ['parent-only', 'parent-and-children', 'cohort', 'direct'], description: 'Communication lane for dispatch_agents.' },
|
|
435
|
+
cohort: { type: 'string', description: 'Agent cohort for dispatch_agents. Defaults to the work plan id.' },
|
|
436
|
+
agentContext: { type: 'string', description: 'Additional context prepended to each dispatched agent task.' },
|
|
437
|
+
confirm: { type: 'boolean', description: 'Required true for remove, clear_completed, and dispatch_agents.' },
|
|
438
|
+
explicitUserRequest: { type: 'string', description: 'Required for remove, clear_completed, and agent dispatch.' },
|
|
181
439
|
},
|
|
182
440
|
required: ['action'],
|
|
183
441
|
additionalProperties: false,
|
|
184
442
|
},
|
|
185
|
-
sideEffects: ['state'],
|
|
443
|
+
sideEffects: ['state', 'agent', 'workflow'],
|
|
186
444
|
},
|
|
187
445
|
execute: async (rawArgs: unknown) => {
|
|
188
446
|
try {
|
|
@@ -190,6 +448,7 @@ export function createAgentWorkPlanTool(store: WorkPlanStore): Tool {
|
|
|
190
448
|
if (!isAction(args.action)) return failure(`Unknown Agent work plan action. Valid values ${ACTIONS.join(', ')}.`);
|
|
191
449
|
if (args.action === 'list') return output(listOutput(store));
|
|
192
450
|
if (args.action === 'get') return output(formatItemDetail(resolveItem(store, requireId(args))));
|
|
451
|
+
if (args.action === 'dispatch_agents') return dispatchAgentsFromWorkPlan(store, args, options);
|
|
193
452
|
if (args.action === 'create') {
|
|
194
453
|
const item = store.addItem(requireTitle(args), {
|
|
195
454
|
status: readOptionalStatus(args.status) ?? 'pending',
|
|
@@ -247,5 +506,5 @@ export function createAgentWorkPlanTool(store: WorkPlanStore): Tool {
|
|
|
247
506
|
}
|
|
248
507
|
|
|
249
508
|
export function registerAgentWorkPlanTool(registry: ToolRegistry, store: WorkPlanStore): void {
|
|
250
|
-
registry.register(createAgentWorkPlanTool(store));
|
|
509
|
+
registry.register(createAgentWorkPlanTool(store, { toolRegistry: registry }));
|
|
251
510
|
}
|