@pellux/goodvibes-agent 1.1.7 → 1.2.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 +170 -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 +132577 -91483
- 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 +56 -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 +114 -45
- package/docs/voice-and-live-tts.md +3 -1
- package/package.json +3 -2
- package/release/release-notes.md +130 -5
- package/release/release-readiness.json +120 -90
- 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 +296 -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-checkpoint.ts +140 -0
- package/src/agent/setup-wizard.ts +447 -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 +262 -30
- package/src/input/agent-workspace-setup-checkpoint-action.ts +127 -0
- package/src/input/agent-workspace-setup-snapshot.ts +198 -0
- package/src/input/agent-workspace-setup.ts +34 -1
- package/src/input/agent-workspace-snapshot.ts +162 -147
- package/src/input/agent-workspace-types.ts +287 -2
- package/src/input/agent-workspace.ts +18 -0
- 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/main.ts +15 -15
- package/src/panels/qr-panel.ts +2 -2
- package/src/renderer/agent-workspace-context-lines.ts +686 -0
- package/src/renderer/agent-workspace.ts +30 -419
- 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.ts +703 -0
- package/src/tools/agent-harness-autonomy-intake.ts +785 -0
- package/src/tools/agent-harness-autonomy-live-records.ts +588 -0
- package/src/tools/agent-harness-autonomy-queue-types.ts +88 -0
- package/src/tools/agent-harness-autonomy-queue.ts +560 -0
- package/src/tools/agent-harness-background-processes-types.ts +35 -0
- package/src/tools/agent-harness-background-processes.ts +794 -0
- package/src/tools/agent-harness-browser-cockpit-route.ts +219 -0
- package/src/tools/agent-harness-browser-control.ts +338 -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-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 +382 -0
- package/src/tools/agent-harness-file-recovery.ts +135 -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 +199 -0
- package/src/tools/agent-harness-local-model-cookbook.ts +238 -0
- package/src/tools/agent-harness-local-model-endpoints.ts +673 -0
- package/src/tools/agent-harness-media-posture.ts +208 -2
- package/src/tools/agent-harness-memory-posture.ts +556 -0
- package/src/tools/agent-harness-metadata.ts +100 -168
- package/src/tools/agent-harness-mode-catalog.ts +84 -33
- package/src/tools/agent-harness-model-catalog.ts +162 -0
- package/src/tools/agent-harness-model-provider-health.ts +186 -0
- package/src/tools/agent-harness-model-readiness.ts +406 -0
- package/src/tools/agent-harness-model-routing-types.ts +266 -0
- package/src/tools/agent-harness-model-routing-utils.ts +30 -0
- package/src/tools/agent-harness-model-routing.ts +137 -190
- package/src/tools/agent-harness-operator-methods.ts +115 -133
- package/src/tools/agent-harness-pairing-posture.ts +431 -18
- 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 +204 -0
- package/src/tools/agent-harness-personal-ops-records.ts +732 -0
- package/src/tools/agent-harness-personal-ops-runner.ts +637 -0
- package/src/tools/agent-harness-personal-ops-types.ts +222 -0
- package/src/tools/agent-harness-personal-ops.ts +606 -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-research-briefing.ts +427 -0
- package/src/tools/agent-harness-research-queue.ts +250 -0
- package/src/tools/agent-harness-research-runs.ts +295 -0
- package/src/tools/agent-harness-research-workflow.ts +322 -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 +101 -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 +508 -0
- package/src/tools/agent-harness-sudo-posture.ts +114 -0
- package/src/tools/agent-harness-tool-schema.ts +120 -7
- package/src/tools/agent-harness-tool-types.ts +78 -0
- package/src/tools/agent-harness-tool-utils.ts +43 -0
- package/src/tools/agent-harness-tool.ts +311 -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 +214 -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 +722 -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-runs-tool.ts +434 -0
- package/src/tools/agent-research-sources-tool.ts +443 -0
- package/src/tools/agent-research-tool.ts +687 -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
|
@@ -163,8 +163,8 @@ export function redactHarnessSettingValue(key: string, value: unknown): unknown
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
function settingModelRoute(setting: ConfigSetting): string {
|
|
166
|
-
if (isExternalHostOwnedSettingKey(setting.key)) return
|
|
167
|
-
return
|
|
166
|
+
if (isExternalHostOwnedSettingKey(setting.key)) return `settings get key:${setting.key}`;
|
|
167
|
+
return `settings set|reset key:${setting.key}`;
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
export function describeHarnessSetting(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MemoryRecord, MemoryRegistry } from '@pellux/goodvibes-sdk/platform/state';
|
|
2
2
|
const DEFAULT_LIMIT = 10;
|
|
3
|
-
export const MIN_PROMPT_MEMORY_CONFIDENCE =
|
|
3
|
+
export const MIN_PROMPT_MEMORY_CONFIDENCE = 70;
|
|
4
4
|
|
|
5
5
|
export function isPromptActiveMemory(record: MemoryRecord): boolean {
|
|
6
6
|
return record.reviewState === 'reviewed' && record.confidence >= MIN_PROMPT_MEMORY_CONFIDENCE;
|
|
@@ -28,7 +28,7 @@ export function buildReviewedMemoryPrompt(memoryRegistry: MemoryRegistry, limit
|
|
|
28
28
|
if (records.length === 0) return null;
|
|
29
29
|
return [
|
|
30
30
|
'## Reviewed GoodVibes Agent Memory',
|
|
31
|
-
|
|
31
|
+
`Use these local, reviewed, non-secret memory records with confidence >= ${MIN_PROMPT_MEMORY_CONFIDENCE}% to avoid asking repeat questions and to preserve durable user preferences, constraints, and operating facts.`,
|
|
32
32
|
...records.map(formatMemoryLine),
|
|
33
33
|
].join('\n');
|
|
34
34
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { OperatorMethodInput } from '@pellux/goodvibes-sdk/contracts';
|
|
2
|
+
import { scheduleNextRouteLines } from './schedule-next-routes.ts';
|
|
2
3
|
|
|
3
4
|
export type JsonRecord = Record<string, unknown>;
|
|
4
5
|
|
|
@@ -9,8 +10,12 @@ export type AutomationActionId =
|
|
|
9
10
|
| 'automation.jobs.resume'
|
|
10
11
|
| 'automation.runs.cancel'
|
|
11
12
|
| 'automation.runs.retry'
|
|
13
|
+
| 'schedules.delete'
|
|
14
|
+
| 'schedules.disable'
|
|
15
|
+
| 'schedules.enable'
|
|
12
16
|
| 'schedules.run';
|
|
13
17
|
export type OperatorActionId = ApprovalActionId | AutomationActionId;
|
|
18
|
+
export type OperatorActionHttpMethod = 'POST' | 'DELETE';
|
|
14
19
|
|
|
15
20
|
type ApprovalActionInput =
|
|
16
21
|
| OperatorMethodInput<'approvals.approve'>
|
|
@@ -33,6 +38,7 @@ export interface OperatorActionDescriptor {
|
|
|
33
38
|
readonly label: string;
|
|
34
39
|
readonly pathTemplate: string;
|
|
35
40
|
readonly targetField: 'approvalId' | 'jobId' | 'runId' | 'scheduleId';
|
|
41
|
+
readonly httpMethod?: OperatorActionHttpMethod;
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
export interface OperatorActionRequest {
|
|
@@ -45,6 +51,7 @@ export interface OperatorActionRequest {
|
|
|
45
51
|
export interface OperatorActionSuccess {
|
|
46
52
|
readonly ok: true;
|
|
47
53
|
readonly methodId: OperatorActionId;
|
|
54
|
+
readonly httpMethod: OperatorActionHttpMethod;
|
|
48
55
|
readonly path: string;
|
|
49
56
|
readonly body: unknown;
|
|
50
57
|
}
|
|
@@ -58,6 +65,7 @@ export interface OperatorActionFailure {
|
|
|
58
65
|
| 'connected_host_error';
|
|
59
66
|
readonly error: string;
|
|
60
67
|
readonly methodId: OperatorActionId;
|
|
68
|
+
readonly httpMethod: OperatorActionHttpMethod;
|
|
61
69
|
readonly path: string;
|
|
62
70
|
}
|
|
63
71
|
|
|
@@ -125,6 +133,25 @@ export const OPERATOR_ACTIONS: Record<OperatorActionId, OperatorActionDescriptor
|
|
|
125
133
|
pathTemplate: '/api/automation/schedules/{scheduleId}/run',
|
|
126
134
|
targetField: 'scheduleId',
|
|
127
135
|
},
|
|
136
|
+
'schedules.enable': {
|
|
137
|
+
action: 'schedules.enable',
|
|
138
|
+
label: 'enable schedule',
|
|
139
|
+
pathTemplate: '/api/automation/schedules/{scheduleId}/enable',
|
|
140
|
+
targetField: 'scheduleId',
|
|
141
|
+
},
|
|
142
|
+
'schedules.disable': {
|
|
143
|
+
action: 'schedules.disable',
|
|
144
|
+
label: 'disable schedule',
|
|
145
|
+
pathTemplate: '/api/automation/schedules/{scheduleId}/disable',
|
|
146
|
+
targetField: 'scheduleId',
|
|
147
|
+
},
|
|
148
|
+
'schedules.delete': {
|
|
149
|
+
action: 'schedules.delete',
|
|
150
|
+
label: 'delete schedule',
|
|
151
|
+
pathTemplate: '/api/automation/schedules/{scheduleId}',
|
|
152
|
+
targetField: 'scheduleId',
|
|
153
|
+
httpMethod: 'DELETE',
|
|
154
|
+
},
|
|
128
155
|
};
|
|
129
156
|
|
|
130
157
|
export const OPERATOR_ACTION_IDS = Object.keys(OPERATOR_ACTIONS) as readonly OperatorActionId[];
|
|
@@ -154,6 +181,10 @@ export function renderOperatorActionPath(descriptor: OperatorActionDescriptor, t
|
|
|
154
181
|
return descriptor.pathTemplate.replace(`{${descriptor.targetField}}`, encodeURIComponent(targetId));
|
|
155
182
|
}
|
|
156
183
|
|
|
184
|
+
export function operatorActionHttpMethod(descriptor: OperatorActionDescriptor): OperatorActionHttpMethod {
|
|
185
|
+
return descriptor.httpMethod ?? 'POST';
|
|
186
|
+
}
|
|
187
|
+
|
|
157
188
|
function approvalBody(args: OperatorActionArgs): ApprovalActionInput {
|
|
158
189
|
const note = readOperatorActionString(args.note);
|
|
159
190
|
const body: ApprovalActionInput = {
|
|
@@ -214,6 +245,7 @@ function summarizeError(error: unknown): string {
|
|
|
214
245
|
|
|
215
246
|
function classifyHttpFailure(
|
|
216
247
|
methodId: OperatorActionId,
|
|
248
|
+
httpMethod: OperatorActionHttpMethod,
|
|
217
249
|
path: string,
|
|
218
250
|
status: number,
|
|
219
251
|
body: unknown,
|
|
@@ -222,6 +254,7 @@ function classifyHttpFailure(
|
|
|
222
254
|
return {
|
|
223
255
|
ok: false,
|
|
224
256
|
methodId,
|
|
257
|
+
httpMethod,
|
|
225
258
|
path,
|
|
226
259
|
kind: status === 401 || status === 403
|
|
227
260
|
? 'auth_required'
|
|
@@ -237,11 +270,13 @@ export async function postOperatorAction(
|
|
|
237
270
|
request: OperatorActionRequest,
|
|
238
271
|
): Promise<OperatorActionResult> {
|
|
239
272
|
const path = renderOperatorActionPath(request.descriptor, request.targetId);
|
|
273
|
+
const httpMethod = operatorActionHttpMethod(request.descriptor);
|
|
240
274
|
const token = connection.token;
|
|
241
275
|
if (!token) {
|
|
242
276
|
return {
|
|
243
277
|
ok: false,
|
|
244
278
|
methodId: request.descriptor.action,
|
|
279
|
+
httpMethod,
|
|
245
280
|
path,
|
|
246
281
|
kind: 'auth_required',
|
|
247
282
|
error: connection.tokenPath ? `no connected-host operator token found at ${connection.tokenPath}` : 'no connected-host operator token found',
|
|
@@ -251,7 +286,7 @@ export async function postOperatorAction(
|
|
|
251
286
|
authorization: `Bearer ${token}`,
|
|
252
287
|
};
|
|
253
288
|
const init: RequestInit = {
|
|
254
|
-
method:
|
|
289
|
+
method: httpMethod,
|
|
255
290
|
headers,
|
|
256
291
|
};
|
|
257
292
|
if (request.body) {
|
|
@@ -261,10 +296,11 @@ export async function postOperatorAction(
|
|
|
261
296
|
try {
|
|
262
297
|
const response = await fetch(`${connection.baseUrl}${path}`, init);
|
|
263
298
|
const body = await readResponseBody(response);
|
|
264
|
-
if (!response.ok) return classifyHttpFailure(request.descriptor.action, path, response.status, body);
|
|
299
|
+
if (!response.ok) return classifyHttpFailure(request.descriptor.action, httpMethod, path, response.status, body);
|
|
265
300
|
return {
|
|
266
301
|
ok: true,
|
|
267
302
|
methodId: request.descriptor.action,
|
|
303
|
+
httpMethod,
|
|
268
304
|
path,
|
|
269
305
|
body,
|
|
270
306
|
};
|
|
@@ -272,6 +308,7 @@ export async function postOperatorAction(
|
|
|
272
308
|
return {
|
|
273
309
|
ok: false,
|
|
274
310
|
methodId: request.descriptor.action,
|
|
311
|
+
httpMethod,
|
|
275
312
|
path,
|
|
276
313
|
kind: 'connected_host_unavailable',
|
|
277
314
|
error: summarizeError(error),
|
|
@@ -297,13 +334,20 @@ function formatOperatorActionFailureKind(kind: OperatorActionFailure['kind']): s
|
|
|
297
334
|
return 'connected host error';
|
|
298
335
|
}
|
|
299
336
|
|
|
337
|
+
function scheduleIdFromActionPath(path: string): string {
|
|
338
|
+
const match = /\/api\/automation\/schedules\/([^/]+)/.exec(path);
|
|
339
|
+
return match?.[1] ? decodeURIComponent(match[1]) : '';
|
|
340
|
+
}
|
|
341
|
+
|
|
300
342
|
export function formatOperatorActionSuccess(baseUrl: string, result: OperatorActionSuccess): string {
|
|
343
|
+
const scheduleId = result.methodId.startsWith('schedules.') ? scheduleIdFromActionPath(result.path) : '';
|
|
301
344
|
return [
|
|
302
345
|
'Agent operator action completed',
|
|
303
346
|
` method: ${result.methodId}`,
|
|
304
|
-
` route:
|
|
347
|
+
` route: ${result.httpMethod} ${result.path}`,
|
|
305
348
|
` connected host: ${baseUrl}`,
|
|
306
349
|
` status: ${statusFromOperatorActionBody(result.body)}`,
|
|
350
|
+
...(scheduleId ? scheduleNextRouteLines(scheduleId, { deleted: result.methodId === 'schedules.delete' }) : []),
|
|
307
351
|
].join('\n');
|
|
308
352
|
}
|
|
309
353
|
|
|
@@ -311,7 +355,7 @@ export function formatOperatorActionFailure(result: OperatorActionFailure): stri
|
|
|
311
355
|
return [
|
|
312
356
|
`Agent operator action failed: ${result.kind} (${formatOperatorActionFailureKind(result.kind)})`,
|
|
313
357
|
` method: ${result.methodId}`,
|
|
314
|
-
` route:
|
|
358
|
+
` route: ${result.httpMethod} ${result.path}`,
|
|
315
359
|
` error: ${result.error}`,
|
|
316
360
|
].join('\n');
|
|
317
361
|
}
|
|
@@ -322,7 +366,7 @@ export function formatOperatorActionPreview(request: OperatorActionRequest, expl
|
|
|
322
366
|
` method ${request.descriptor.action}`,
|
|
323
367
|
` action ${request.descriptor.label}`,
|
|
324
368
|
` target ${request.targetId}`,
|
|
325
|
-
` route
|
|
369
|
+
` route ${operatorActionHttpMethod(request.descriptor)} ${renderOperatorActionPath(request.descriptor, request.targetId)}`,
|
|
326
370
|
explicitUserRequest ? ` requested by ${explicitUserRequest}` : ' requested by (missing)',
|
|
327
371
|
'',
|
|
328
372
|
'Confirmation required: type yes in the workspace form or pass --yes only when the user explicitly asked GoodVibes Agent to perform this exact operator action.',
|
|
@@ -335,7 +379,7 @@ export function formatOperatorActionToolPreview(request: OperatorActionRequest,
|
|
|
335
379
|
` method ${request.descriptor.action}`,
|
|
336
380
|
` action ${request.descriptor.label}`,
|
|
337
381
|
` target ${request.targetId}`,
|
|
338
|
-
` route
|
|
382
|
+
` route ${operatorActionHttpMethod(request.descriptor)} ${renderOperatorActionPath(request.descriptor, request.targetId)}`,
|
|
339
383
|
explicitUserRequest ? ` requested by ${explicitUserRequest}` : ' requested by (missing)',
|
|
340
384
|
'',
|
|
341
385
|
'Model tool confirmation required. Call this tool with confirm:true only when the user explicitly asked GoodVibes Agent to perform this exact operator action.',
|
|
@@ -369,6 +369,14 @@ export class AgentPersonaRegistry {
|
|
|
369
369
|
export function buildActivePersonaPrompt(shellPaths: ShellPathService): string | null {
|
|
370
370
|
const active = AgentPersonaRegistry.fromShellPaths(shellPaths).snapshot().activePersona;
|
|
371
371
|
if (!active) return null;
|
|
372
|
+
if (active.reviewState !== 'reviewed') {
|
|
373
|
+
return [
|
|
374
|
+
'## Active GoodVibes Agent Persona',
|
|
375
|
+
`Name: ${active.name}`,
|
|
376
|
+
`Review: ${formatAgentRecordReviewState(active.reviewState)}`,
|
|
377
|
+
'This persona is active but not applied because it is not reviewed. Use the learning curator or persona review route before allowing it to steer assistant behavior.',
|
|
378
|
+
].join('\n');
|
|
379
|
+
}
|
|
372
380
|
return [
|
|
373
381
|
'## Active GoodVibes Agent Persona',
|
|
374
382
|
`Name: ${active.name}`,
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
3
|
+
import type { ShellPathService } from '@/runtime/index.ts';
|
|
4
|
+
import { assertNoSecretLikeText } from './persona-registry.ts';
|
|
5
|
+
|
|
6
|
+
export type AgentProjectContextKind =
|
|
7
|
+
| 'hermes'
|
|
8
|
+
| 'agents'
|
|
9
|
+
| 'claude'
|
|
10
|
+
| 'soul'
|
|
11
|
+
| 'cursor';
|
|
12
|
+
|
|
13
|
+
export type AgentProjectContextScope = 'project' | 'subdirectory' | 'cwd' | 'global';
|
|
14
|
+
|
|
15
|
+
export interface AgentProjectContextFile {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly path: string;
|
|
18
|
+
readonly kind: AgentProjectContextKind;
|
|
19
|
+
readonly scope: AgentProjectContextScope;
|
|
20
|
+
readonly source: string;
|
|
21
|
+
readonly priority: number;
|
|
22
|
+
readonly body: string;
|
|
23
|
+
readonly truncated: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface BlockedAgentProjectContextFile {
|
|
27
|
+
readonly id: string;
|
|
28
|
+
readonly path: string;
|
|
29
|
+
readonly kind: AgentProjectContextKind;
|
|
30
|
+
readonly scope: AgentProjectContextScope;
|
|
31
|
+
readonly source: string;
|
|
32
|
+
readonly priority: number;
|
|
33
|
+
readonly reason: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface AgentProjectContextSnapshot {
|
|
37
|
+
readonly files: readonly AgentProjectContextFile[];
|
|
38
|
+
readonly blocked: readonly BlockedAgentProjectContextFile[];
|
|
39
|
+
readonly searchedPaths: readonly string[];
|
|
40
|
+
readonly workingDirectory: string;
|
|
41
|
+
readonly targetDirectory: string;
|
|
42
|
+
readonly gitRoot: string | null;
|
|
43
|
+
readonly progressiveTargetAware: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
type AgentProjectContextPaths = Pick<ShellPathService, 'workingDirectory' | 'homeDirectory' | 'expandHomePath' | 'resolveWorkspacePath'>;
|
|
47
|
+
|
|
48
|
+
interface CandidateContextFile {
|
|
49
|
+
readonly path: string;
|
|
50
|
+
readonly kind: AgentProjectContextKind;
|
|
51
|
+
readonly scope: AgentProjectContextScope;
|
|
52
|
+
readonly source: string;
|
|
53
|
+
readonly priority: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface AgentProjectContextDiscoveryOptions {
|
|
57
|
+
readonly targetPath?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const MAX_CONTEXT_FILE_CHARS = 10_000;
|
|
61
|
+
const MAX_TOTAL_CONTEXT_CHARS = 32_000;
|
|
62
|
+
const MAX_PROJECT_DEPTH = 32;
|
|
63
|
+
|
|
64
|
+
function safeStat(path: string): ReturnType<typeof statSync> | null {
|
|
65
|
+
try {
|
|
66
|
+
return statSync(path);
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function findGitRoot(workingDirectory: string): string | null {
|
|
73
|
+
let current = resolve(workingDirectory);
|
|
74
|
+
for (let depth = 0; depth < MAX_PROJECT_DEPTH; depth += 1) {
|
|
75
|
+
if (existsSync(join(current, '.git'))) return current;
|
|
76
|
+
const parent = dirname(current);
|
|
77
|
+
if (parent === current) break;
|
|
78
|
+
current = parent;
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function pathInside(parent: string, child: string): boolean {
|
|
84
|
+
const rel = relative(resolve(parent), resolve(child));
|
|
85
|
+
return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function resolveTargetDirectory(shellPaths: AgentProjectContextPaths, targetPath?: string): string {
|
|
89
|
+
const trimmed = typeof targetPath === 'string' ? targetPath.trim() : '';
|
|
90
|
+
if (!trimmed) return resolve(shellPaths.workingDirectory);
|
|
91
|
+
const expanded = shellPaths.expandHomePath(trimmed);
|
|
92
|
+
const resolved = isAbsolute(expanded) ? resolve(expanded) : shellPaths.resolveWorkspacePath(expanded);
|
|
93
|
+
if (!pathInside(shellPaths.workingDirectory, resolved)) return resolve(shellPaths.workingDirectory);
|
|
94
|
+
const stat = safeStat(resolved);
|
|
95
|
+
return stat?.isDirectory() ? resolved : dirname(resolved);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function contextWalkRoots(workingDirectory: string, targetDirectory: string): { readonly roots: readonly string[]; readonly gitRoot: string | null } {
|
|
99
|
+
const gitRoot = findGitRoot(workingDirectory);
|
|
100
|
+
const start = gitRoot && pathInside(gitRoot, targetDirectory) ? gitRoot : resolve(workingDirectory);
|
|
101
|
+
const roots: string[] = [];
|
|
102
|
+
let current = resolve(targetDirectory);
|
|
103
|
+
for (let depth = 0; depth < MAX_PROJECT_DEPTH; depth += 1) {
|
|
104
|
+
roots.push(current);
|
|
105
|
+
if (current === start) break;
|
|
106
|
+
const parent = dirname(current);
|
|
107
|
+
if (parent === current) break;
|
|
108
|
+
current = parent;
|
|
109
|
+
}
|
|
110
|
+
return { roots: roots.reverse(), gitRoot };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function cursorRuleCandidates(workingDirectory: string): readonly CandidateContextFile[] {
|
|
114
|
+
const dir = join(workingDirectory, '.cursor', 'rules');
|
|
115
|
+
const stat = safeStat(dir);
|
|
116
|
+
if (!stat?.isDirectory()) return [];
|
|
117
|
+
try {
|
|
118
|
+
return readdirSync(dir)
|
|
119
|
+
.filter((entry) => entry.endsWith('.mdc'))
|
|
120
|
+
.sort((left, right) => left.localeCompare(right))
|
|
121
|
+
.map((entry, index) => ({
|
|
122
|
+
path: join(dir, entry),
|
|
123
|
+
kind: 'cursor' as const,
|
|
124
|
+
scope: 'cwd' as const,
|
|
125
|
+
source: '.cursor/rules/*.mdc',
|
|
126
|
+
priority: 70 + index,
|
|
127
|
+
}));
|
|
128
|
+
} catch {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function candidateContextFiles(shellPaths: AgentProjectContextPaths, targetDirectory: string): readonly CandidateContextFile[] {
|
|
134
|
+
const { roots } = contextWalkRoots(shellPaths.workingDirectory, targetDirectory);
|
|
135
|
+
const candidates: CandidateContextFile[] = [];
|
|
136
|
+
const hermesHome = process.env.HERMES_HOME?.trim();
|
|
137
|
+
if (hermesHome) {
|
|
138
|
+
candidates.push({
|
|
139
|
+
path: join(shellPaths.expandHomePath(hermesHome), 'SOUL.md'),
|
|
140
|
+
kind: 'soul',
|
|
141
|
+
scope: 'global',
|
|
142
|
+
source: 'HERMES_HOME/SOUL.md',
|
|
143
|
+
priority: 10,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const workingDirectory = resolve(shellPaths.workingDirectory);
|
|
148
|
+
for (const [index, root] of roots.entries()) {
|
|
149
|
+
const scope: AgentProjectContextScope = root === workingDirectory ? 'cwd' : pathInside(workingDirectory, root) ? 'subdirectory' : 'project';
|
|
150
|
+
candidates.push(
|
|
151
|
+
{ path: join(root, '.hermes.md'), kind: 'hermes', scope, source: '.hermes.md', priority: 20 + index },
|
|
152
|
+
{ path: join(root, 'HERMES.md'), kind: 'hermes', scope, source: 'HERMES.md', priority: 25 + index },
|
|
153
|
+
{ path: join(root, 'AGENTS.md'), kind: 'agents', scope, source: 'AGENTS.md', priority: 40 + index },
|
|
154
|
+
{ path: join(root, 'CLAUDE.md'), kind: 'claude', scope, source: 'CLAUDE.md', priority: 55 + index },
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
candidates.push({
|
|
159
|
+
path: join(shellPaths.workingDirectory, '.cursorrules'),
|
|
160
|
+
kind: 'cursor',
|
|
161
|
+
scope: 'cwd',
|
|
162
|
+
source: '.cursorrules',
|
|
163
|
+
priority: 70,
|
|
164
|
+
});
|
|
165
|
+
candidates.push(...cursorRuleCandidates(shellPaths.workingDirectory));
|
|
166
|
+
return candidates;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function contextFileId(candidate: Pick<CandidateContextFile, 'path' | 'source'>): string {
|
|
170
|
+
const base = resolve(candidate.path).replace(/[^a-zA-Z0-9]+/g, '-').replace(/^-+|-+$/g, '').toLowerCase();
|
|
171
|
+
return `${candidate.source.replace(/[^a-zA-Z0-9]+/g, '-').replace(/^-+|-+$/g, '').toLowerCase()}-${base || 'context'}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function dedupeCandidates(candidates: readonly CandidateContextFile[]): readonly CandidateContextFile[] {
|
|
175
|
+
const seen = new Set<string>();
|
|
176
|
+
const result: CandidateContextFile[] = [];
|
|
177
|
+
for (const candidate of candidates) {
|
|
178
|
+
const path = resolve(candidate.path);
|
|
179
|
+
if (seen.has(path)) continue;
|
|
180
|
+
seen.add(path);
|
|
181
|
+
result.push({ ...candidate, path });
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function readContextCandidate(candidate: CandidateContextFile): AgentProjectContextFile | BlockedAgentProjectContextFile | null {
|
|
187
|
+
const stat = safeStat(candidate.path);
|
|
188
|
+
if (!stat?.isFile()) return null;
|
|
189
|
+
let content = '';
|
|
190
|
+
try {
|
|
191
|
+
content = readFileSync(candidate.path, 'utf-8');
|
|
192
|
+
} catch (error) {
|
|
193
|
+
return {
|
|
194
|
+
...candidate,
|
|
195
|
+
id: contextFileId(candidate),
|
|
196
|
+
reason: `Could not read file: ${error instanceof Error ? error.message : String(error)}`,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
const body = content.trim();
|
|
200
|
+
if (!body) {
|
|
201
|
+
return {
|
|
202
|
+
...candidate,
|
|
203
|
+
id: contextFileId(candidate),
|
|
204
|
+
reason: 'File is empty.',
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
assertNoSecretLikeText([content], 'Project context files');
|
|
209
|
+
} catch (error) {
|
|
210
|
+
return {
|
|
211
|
+
...candidate,
|
|
212
|
+
id: contextFileId(candidate),
|
|
213
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
...candidate,
|
|
218
|
+
id: contextFileId(candidate),
|
|
219
|
+
body: body.length > MAX_CONTEXT_FILE_CHARS ? body.slice(0, MAX_CONTEXT_FILE_CHARS).trimEnd() : body,
|
|
220
|
+
truncated: body.length > MAX_CONTEXT_FILE_CHARS,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function discoverProjectContextFiles(
|
|
225
|
+
shellPaths: AgentProjectContextPaths,
|
|
226
|
+
options: AgentProjectContextDiscoveryOptions = {},
|
|
227
|
+
): AgentProjectContextSnapshot {
|
|
228
|
+
const targetDirectory = resolveTargetDirectory(shellPaths, options.targetPath);
|
|
229
|
+
const { gitRoot } = contextWalkRoots(shellPaths.workingDirectory, targetDirectory);
|
|
230
|
+
const files: AgentProjectContextFile[] = [];
|
|
231
|
+
const blocked: BlockedAgentProjectContextFile[] = [];
|
|
232
|
+
const candidates = dedupeCandidates(candidateContextFiles(shellPaths, targetDirectory));
|
|
233
|
+
for (const candidate of candidates) {
|
|
234
|
+
const record = readContextCandidate(candidate);
|
|
235
|
+
if (!record) continue;
|
|
236
|
+
if ('body' in record) files.push(record);
|
|
237
|
+
else blocked.push(record);
|
|
238
|
+
}
|
|
239
|
+
files.sort((left, right) => left.priority - right.priority || left.path.localeCompare(right.path));
|
|
240
|
+
blocked.sort((left, right) => left.priority - right.priority || left.path.localeCompare(right.path));
|
|
241
|
+
return {
|
|
242
|
+
files,
|
|
243
|
+
blocked,
|
|
244
|
+
searchedPaths: candidates.map((candidate) => candidate.path),
|
|
245
|
+
workingDirectory: resolve(shellPaths.workingDirectory),
|
|
246
|
+
targetDirectory,
|
|
247
|
+
gitRoot,
|
|
248
|
+
progressiveTargetAware: targetDirectory !== resolve(shellPaths.workingDirectory),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function buildProjectContextPrompt(shellPaths: AgentProjectContextPaths): string | null {
|
|
253
|
+
const snapshot = discoverProjectContextFiles(shellPaths);
|
|
254
|
+
if (snapshot.files.length === 0 && snapshot.blocked.length === 0) return null;
|
|
255
|
+
const lines: string[] = [
|
|
256
|
+
'## Project Context Files',
|
|
257
|
+
'These project-authored instructions shape the current workspace. Follow them only when they do not conflict with explicit user instructions, safety policy, tool contracts, confirmation requirements, or secret-handling rules.',
|
|
258
|
+
];
|
|
259
|
+
let remaining = MAX_TOTAL_CONTEXT_CHARS;
|
|
260
|
+
for (const file of snapshot.files) {
|
|
261
|
+
if (remaining <= 0) break;
|
|
262
|
+
const body = file.body.slice(0, remaining).trimEnd();
|
|
263
|
+
remaining -= body.length;
|
|
264
|
+
lines.push('', `### ${file.source}`, `Source: ${file.path}`, `Kind: ${file.kind}; scope: ${file.scope}`, '', body);
|
|
265
|
+
if (file.truncated || body.length < file.body.length) lines.push('', `[${file.source} truncated for prompt safety.]`);
|
|
266
|
+
}
|
|
267
|
+
if (snapshot.blocked.length > 0) {
|
|
268
|
+
lines.push('', '### Blocked Project Context Files');
|
|
269
|
+
for (const blocked of snapshot.blocked) lines.push(`- ${blocked.path}: ${blocked.reason}`);
|
|
270
|
+
lines.push('Blocked project context content was not loaded.');
|
|
271
|
+
}
|
|
272
|
+
return lines.join('\n');
|
|
273
|
+
}
|