@pellux/goodvibes-agent 1.0.35 → 1.0.36
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 +15 -9
- package/README.md +8 -7
- package/dist/package/{ast-grep-napi.linux-x64-gnu-swtppvy9.node → ast-grep-napi.linux-x64-gnu-mkk8xwww.node} +0 -0
- package/dist/package/{ast-grep-napi.linux-x64-musl-ttfcdtap.node → ast-grep-napi.linux-x64-musl-ryqtgdv6.node} +0 -0
- package/dist/package/main.js +51504 -49337
- package/docs/README.md +6 -5
- package/docs/channels-remote-and-api.md +3 -3
- package/docs/connected-host.md +3 -3
- package/docs/getting-started.md +12 -7
- package/docs/knowledge-artifacts-and-multimodal.md +9 -4
- package/docs/project-planning.md +3 -3
- package/docs/providers-and-routing.md +2 -2
- package/docs/release-and-publishing.md +8 -8
- package/docs/tools-and-commands.md +31 -18
- package/docs/voice-and-live-tts.md +1 -1
- package/package.json +1 -1
- package/release/live-verification/live-verification.json +3 -3
- package/release/live-verification/live-verification.md +2 -4
- package/release/release-notes.md +4 -4
- package/release/release-readiness.json +46 -46
- package/src/agent/harness-control.ts +35 -9
- package/src/agent/reminder-schedule-format.ts +2 -5
- package/src/agent/reminder-schedule.ts +4 -11
- package/src/agent/routine-schedule-format.ts +3 -6
- package/src/agent/routine-schedule-promotion.ts +5 -14
- package/src/agent/routine-schedule-receipts.ts +4 -9
- package/src/cli/agent-knowledge-command.ts +6 -11
- package/src/cli/agent-knowledge-format.ts +4 -9
- package/src/cli/agent-knowledge-runtime.ts +7 -14
- package/src/cli/external-runtime.ts +3 -46
- package/src/cli/help.ts +2 -2
- package/src/cli/status.ts +6 -7
- package/src/input/agent-workspace-categories.ts +23 -23
- package/src/input/agent-workspace-channels.ts +3 -3
- package/src/input/agent-workspace-operations-command-editors.ts +1 -1
- package/src/input/command-registry.ts +1 -1
- package/src/input/commands/compat-runtime.ts +1 -1
- package/src/input/commands/planning-runtime.ts +1 -1
- package/src/input/commands/runtime-services.ts +1 -1
- package/src/input/mcp-workspace.ts +1 -1
- package/src/input/submission-router.ts +1 -1
- package/src/panels/builtin/shared.ts +1 -1
- package/src/panels/project-planning-panel.ts +4 -4
- package/src/panels/qr-panel.ts +2 -2
- package/src/renderer/conversation-overlays.ts +1 -0
- package/src/runtime/bootstrap-command-parts.ts +1 -1
- package/src/runtime/bootstrap-core.ts +1 -1
- package/src/runtime/bootstrap-hook-bridge.ts +1 -1
- package/src/runtime/bootstrap-shell.ts +1 -1
- package/src/runtime/bootstrap.ts +5 -4
- package/src/runtime/context.ts +2 -2
- package/src/runtime/index.ts +88 -5
- package/src/runtime/store/state.ts +1 -1
- package/src/tools/agent-channel-send-tool.ts +1 -1
- package/src/tools/agent-harness-channel-metadata.ts +36 -25
- package/src/tools/agent-harness-cli-metadata.ts +19 -1
- package/src/tools/agent-harness-command-catalog.ts +15 -4
- package/src/tools/agent-harness-connected-host-status.ts +7 -6
- package/src/tools/agent-harness-delegation-posture.ts +15 -4
- package/src/tools/agent-harness-keybinding-metadata.ts +67 -16
- package/src/tools/agent-harness-mcp-metadata.ts +11 -3
- package/src/tools/agent-harness-media-posture.ts +25 -14
- package/src/tools/agent-harness-metadata.ts +97 -6
- package/src/tools/agent-harness-mode-catalog.ts +81 -28
- package/src/tools/agent-harness-model-routing.ts +18 -3
- package/src/tools/agent-harness-model-tool-catalog.ts +85 -7
- package/src/tools/agent-harness-notification-metadata.ts +30 -19
- package/src/tools/agent-harness-operator-methods.ts +13 -8
- package/src/tools/agent-harness-pairing-posture.ts +17 -5
- package/src/tools/agent-harness-panel-metadata.ts +10 -4
- package/src/tools/agent-harness-provider-account-metadata.ts +46 -35
- package/src/tools/agent-harness-release-evidence.ts +31 -3
- package/src/tools/agent-harness-release-readiness.ts +30 -0
- package/src/tools/agent-harness-security-posture.ts +18 -4
- package/src/tools/agent-harness-service-posture.ts +8 -1
- package/src/tools/agent-harness-session-metadata.ts +24 -8
- package/src/tools/agent-harness-setup-posture.ts +32 -21
- package/src/tools/agent-harness-text.ts +6 -0
- package/src/tools/agent-harness-tool.ts +26 -34
- package/src/tools/agent-harness-ui-surface-metadata.ts +84 -84
- package/src/tools/agent-harness-workspace-actions.ts +64 -3
- package/src/tools/agent-knowledge-ingest-tool.ts +1 -1
- package/src/tools/agent-knowledge-tool.ts +1 -1
- package/src/tools/agent-media-generate-tool.ts +2 -2
- package/src/tools/agent-notify-tool.ts +1 -1
- package/src/tools/agent-reminder-schedule-tool.ts +1 -1
- package/src/tools/tool-definition-compaction.ts +35 -8
- package/src/version.ts +2 -8
|
@@ -95,7 +95,7 @@ export interface BuiltinPanelDeps {
|
|
|
95
95
|
planManager?: ExecutionPlanManager;
|
|
96
96
|
/** Adaptive planner for ops strategy panels. */
|
|
97
97
|
adaptivePlanner?: AdaptivePlanner;
|
|
98
|
-
/** Passive
|
|
98
|
+
/** Passive project planning artifact service. */
|
|
99
99
|
projectPlanningService?: ProjectPlanningService;
|
|
100
100
|
/** Stable workspace project id for project:<projectId> planning namespaces. */
|
|
101
101
|
projectPlanningProjectId?: string;
|
|
@@ -192,7 +192,7 @@ export class ProjectPlanningPanel extends BasePanel {
|
|
|
192
192
|
lines: buildEmptyState(
|
|
193
193
|
width,
|
|
194
194
|
'No project planning state has been saved for this workspace.',
|
|
195
|
-
'Describe the intended operator outcome in normal chat to let the Agent start the planning interview. The
|
|
195
|
+
'Describe the intended operator outcome in normal chat to let the Agent start the planning interview. The planning artifact service stores and evaluates artifacts; it does not start execution.',
|
|
196
196
|
[],
|
|
197
197
|
C,
|
|
198
198
|
),
|
|
@@ -234,7 +234,7 @@ export class ProjectPlanningPanel extends BasePanel {
|
|
|
234
234
|
|
|
235
235
|
return buildPanelWorkspace(width, height, {
|
|
236
236
|
title: this.loading ? 'Project Planning - loading' : 'Project Planning',
|
|
237
|
-
intro: '
|
|
237
|
+
intro: 'Planning artifacts for the current Agent workspace. Conversation control stays in the main Agent turn.',
|
|
238
238
|
sections: [{ ...scroll.section, lines: sectionLines }],
|
|
239
239
|
footerLines: this.footerLines(width, footerSummary),
|
|
240
240
|
palette: C,
|
|
@@ -557,8 +557,8 @@ export class ProjectPlanningPanel extends BasePanel {
|
|
|
557
557
|
actions.push({
|
|
558
558
|
id: 'scope-agent-first',
|
|
559
559
|
label: 'Agent-first scope',
|
|
560
|
-
detail: 'Fix Agent behavior here; report
|
|
561
|
-
answer: 'Scope is Agent-owned behavior first. If a blocker
|
|
560
|
+
detail: 'Fix Agent behavior here; report public-runtime blockers instead of patching around external runtime bugs.',
|
|
561
|
+
answer: 'Scope is Agent-owned behavior first. If a blocker belongs to the public runtime, report the exact contract/runtime issue instead of patching around it locally. Include runtime contracts only where Agent consumes public runtime routes.',
|
|
562
562
|
});
|
|
563
563
|
}
|
|
564
564
|
actions.push({
|
package/src/panels/qr-panel.ts
CHANGED
|
@@ -31,7 +31,7 @@ export interface QrPanelConnectionInfo {
|
|
|
31
31
|
readonly username: string;
|
|
32
32
|
/** Bootstrap password for companion authentication */
|
|
33
33
|
readonly password?: string;
|
|
34
|
-
/**
|
|
34
|
+
/** Pairing payload version (defaults to '0.0.0' if omitted) */
|
|
35
35
|
readonly version?: string;
|
|
36
36
|
/** Surface identifier (defaults to GoodVibes Agent if omitted) */
|
|
37
37
|
readonly surface?: string;
|
|
@@ -54,7 +54,7 @@ export type CopyToClipboardFn = (text: string) => void;
|
|
|
54
54
|
* Shows connection URL, truncated token, and username above the QR code.
|
|
55
55
|
* Supports `r` to regenerate the token and `c` to copy the token.
|
|
56
56
|
*
|
|
57
|
-
* QR matrix generation uses
|
|
57
|
+
* QR matrix generation uses `generateQrMatrix` via `encodeConnectionPayload`.
|
|
58
58
|
*/
|
|
59
59
|
export class QrPanel extends BasePanel {
|
|
60
60
|
private connectionInfo: QrPanelConnectionInfo;
|
|
@@ -43,6 +43,7 @@ export function applyConversationOverlays(
|
|
|
43
43
|
if (input.onboardingWizard.active) {
|
|
44
44
|
const lines = renderOnboardingWizard(input.onboardingWizard, conversationWidth, viewportHeight);
|
|
45
45
|
next = replaceViewportWithOverlay(lines, conversationWidth, viewportHeight);
|
|
46
|
+
if (!input.modelPicker.active) return next;
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
if (input.filePicker.active) {
|
|
@@ -133,7 +133,7 @@ export interface BootstrapCommandSectionOptions {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
function unwiredShellAction(name: string): never {
|
|
136
|
-
throw new Error(`
|
|
136
|
+
throw new Error(`Agent runtime action "${name}" was called before the operator route was attached.`);
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
export function createBootstrapCommandActions(
|
|
@@ -149,7 +149,7 @@ export async function initializeBootstrapCore(
|
|
|
149
149
|
metadata: {
|
|
150
150
|
product: 'goodvibes-agent',
|
|
151
151
|
surfaceRoot: GOODVIBES_AGENT_SURFACE_ROOT,
|
|
152
|
-
clientKindNote: '
|
|
152
|
+
clientKindNote: 'Connected-host client kind; GoodVibes Agent remains an interactive operator TUI.',
|
|
153
153
|
},
|
|
154
154
|
}, 'bootstrap.control-plane');
|
|
155
155
|
|
|
@@ -85,7 +85,7 @@ export function createResumeSessionHandler(options: ResumeSessionOptions): (sess
|
|
|
85
85
|
sessionId: options.runtime.sessionId,
|
|
86
86
|
timestamp: Date.now(),
|
|
87
87
|
payload: { sessionId },
|
|
88
|
-
}).catch((err: unknown) => logger.debug('Hook
|
|
88
|
+
}).catch((err: unknown) => logger.debug('Hook route fire error', {
|
|
89
89
|
path: 'Lifecycle:session:load',
|
|
90
90
|
error: summarizeError(err),
|
|
91
91
|
}));
|
|
@@ -92,7 +92,7 @@ export function submitPlanningAnswerWithShellFallback(
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
options.addSystemMessage([
|
|
95
|
-
'[Planning] Could not submit the selected answer because the prompt
|
|
95
|
+
'[Planning] Could not submit the selected answer because the prompt route is not ready.',
|
|
96
96
|
`Paste this answer into the prompt to continue planning: ${answer}`,
|
|
97
97
|
].join('\n'));
|
|
98
98
|
options.requestRender();
|
package/src/runtime/bootstrap.ts
CHANGED
|
@@ -50,6 +50,7 @@ const GOODVIBES_AGENT_OPERATOR_POLICY = [
|
|
|
50
50
|
'- Work serially in the main conversation by default: answer, inspect, summarize, remember useful non-secret facts, configure Agent-local state, and use safe read-only connected-host/operator routes.',
|
|
51
51
|
'- Connected-host lifecycle is external. Do not start, stop, restart, install, expose, or mutate host listeners/network posture from Agent.',
|
|
52
52
|
'- Read tools: `agent_operator_briefing` for connected work/approvals/automation/schedules, `agent_knowledge` for isolated Agent Knowledge, `agent_harness` for harness catalogs/settings/status.',
|
|
53
|
+
'- Harness access: use `agent_harness` modes `commands`/`run_command`, `workspace_actions`/`run_workspace_action`, and `settings`/`set_setting`/`reset_setting` to use the same surfaces the user can use.',
|
|
53
54
|
'- State tools: `agent_work_plan` for visible local work items; `agent_local_registry` for Agent-local notes, memory, personas, skills, bundles, and routines. Keep records non-secret, sourced, and reviewable.',
|
|
54
55
|
'- Confirmed tools: use `agent_operator_action`, `agent_knowledge_ingest`, `agent_media_generate`, `agent_notify`, `agent_channel_send`, and `agent_reminder_schedule` only for explicit user requests with confirm:true and explicitUserRequest.',
|
|
55
56
|
'- Agent Knowledge must use only `/api/goodvibes-agent/knowledge/*` and fail closed. Do not use default knowledge or non-Agent knowledge spaces.',
|
|
@@ -89,9 +90,9 @@ export type BootstrapContext = RuntimeContext & {
|
|
|
89
90
|
agentStatusIntervalRef: { value: ReturnType<typeof setInterval> | null };
|
|
90
91
|
/** Mutable refs for viewport/scroll/render functions; main.ts patches these after constructing UI state. */
|
|
91
92
|
orchestratorRefs: { getViewportHeight: () => number; scrollToEnd: (vHeight: number) => void; requestRender: () => void };
|
|
92
|
-
/** Patch the bootstrap-owned render
|
|
93
|
+
/** Patch the bootstrap-owned render route after main.ts constructs the real render loop. */
|
|
93
94
|
setRenderRequest: (fn: () => void) => void;
|
|
94
|
-
/** Shell-owned permission prompt
|
|
95
|
+
/** Shell-owned permission prompt route that main.ts patches after UI setup. */
|
|
95
96
|
permissionPromptRef: { requestPermission: PermissionRequestHandler };
|
|
96
97
|
/** Load the most recently saved conversation from disk. */
|
|
97
98
|
loadLastConversation: () => { messages: Array<Record<string, unknown>> } | null;
|
|
@@ -130,7 +131,7 @@ export type BootstrapContext = RuntimeContext & {
|
|
|
130
131
|
* 1. Config, caches, keybindings
|
|
131
132
|
* 2. Runtime event bus, conversation, compositor, selection
|
|
132
133
|
* 3. Tool registry + agent wiring
|
|
133
|
-
* 4. Runtime bus subscriptions (delegation, subagent, hook
|
|
134
|
+
* 4. Runtime bus subscriptions (delegation, subagent, hook route)
|
|
134
135
|
* 5. Providers, webhooks, PermissionManager, HookDispatcher
|
|
135
136
|
* 6. Orchestrator and Agent-local task read models
|
|
136
137
|
* 7. MCP auto-connect + workspace/panel manager
|
|
@@ -547,7 +548,7 @@ export async function bootstrapRuntime(
|
|
|
547
548
|
},
|
|
548
549
|
};
|
|
549
550
|
|
|
550
|
-
// Wire exit from options if provided; otherwise main.ts binds the
|
|
551
|
+
// Wire exit from options if provided; otherwise main.ts binds the operator route.
|
|
551
552
|
if (options?.exit) {
|
|
552
553
|
ctx.commandContext.exit = options.exit;
|
|
553
554
|
}
|
package/src/runtime/context.ts
CHANGED
|
@@ -35,8 +35,8 @@ export interface BootstrapOptions {
|
|
|
35
35
|
configManager: import('@pellux/goodvibes-sdk/platform/config').ConfigManager;
|
|
36
36
|
/**
|
|
37
37
|
* Callback invoked when the app should exit.
|
|
38
|
-
* If provided,
|
|
39
|
-
* Otherwise main.ts binds the
|
|
38
|
+
* If provided, the exit route is wired during bootstrap.
|
|
39
|
+
* Otherwise main.ts binds the app-owned exit route immediately after bootstrap returns.
|
|
40
40
|
*/
|
|
41
41
|
exit?: () => void;
|
|
42
42
|
}
|
package/src/runtime/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Runtime module barrel for GoodVibes Agent.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* god-barrel. This file keeps the Agent app on public
|
|
4
|
+
* The shared runtime removed private deep imports and the runtime root
|
|
5
|
+
* god-barrel. This file keeps the Agent app on public runtime exports
|
|
6
6
|
* while preserving the local import surface used by the shell.
|
|
7
7
|
*/
|
|
8
8
|
|
|
@@ -50,7 +50,7 @@ export function createFeatureFlagManager(): FeatureFlagManager {
|
|
|
50
50
|
return manager;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
// Public
|
|
53
|
+
// Public runtime exports.
|
|
54
54
|
export * from '@pellux/goodvibes-sdk/platform/runtime/state';
|
|
55
55
|
export * from '@pellux/goodvibes-sdk/platform/runtime/store';
|
|
56
56
|
export * from '@pellux/goodvibes-sdk/platform/runtime/ui';
|
|
@@ -491,8 +491,6 @@ export const canonicalize = security.canonicalize;
|
|
|
491
491
|
export const classifyCommand = security.classifyCommand;
|
|
492
492
|
export const classifySegment = security.classifySegment;
|
|
493
493
|
export const collectCommandNodes = security.collectCommandNodes;
|
|
494
|
-
export const evaluateCommandAST = security.evaluateCommandAST;
|
|
495
|
-
export const evaluateSegmentNode = security.evaluateSegmentNode;
|
|
496
494
|
export const higherPriority = security.higherPriority;
|
|
497
495
|
export const parseAST = security.parseAST;
|
|
498
496
|
export const parseCommandAST = security.parseCommandAST;
|
|
@@ -505,6 +503,91 @@ export const verifyBundle = security.verifyBundle;
|
|
|
505
503
|
export const MAX_INPUT_LENGTH = security.MAX_INPUT_LENGTH;
|
|
506
504
|
export const MAX_TOKEN_COUNT = security.MAX_TOKEN_COUNT;
|
|
507
505
|
|
|
506
|
+
type RuntimeSegmentVerdict = ReturnType<typeof security.evaluateSegmentNode>;
|
|
507
|
+
type RuntimeCompoundVerdict = ReturnType<typeof security.evaluateCommandAST>;
|
|
508
|
+
type RuntimeShellNode = Parameters<typeof security.evaluateCommandAST>[1];
|
|
509
|
+
|
|
510
|
+
const AGENT_OBFUSCATION_CHECKS: Array<{ description: string; test: (raw: string) => boolean }> = [
|
|
511
|
+
{
|
|
512
|
+
description: 'base64-encoded argument (possible command injection)',
|
|
513
|
+
test: (raw) =>
|
|
514
|
+
extractInspectableShellWords(raw).some((word) =>
|
|
515
|
+
/^[A-Za-z0-9+/]+={0,2}$/.test(word) && word.length >= 12 && word.length % 4 === 0,
|
|
516
|
+
),
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
description: 'URL-encoded content in argument',
|
|
520
|
+
test: (raw) => /%[0-9a-fA-F]{2}/.test(raw),
|
|
521
|
+
},
|
|
522
|
+
];
|
|
523
|
+
|
|
524
|
+
function extractInspectableShellWords(raw: string): string[] {
|
|
525
|
+
return (raw.match(/"[^"]*"|'[^']*'|`[^`]*`|\S+/g) ?? []).map((word) =>
|
|
526
|
+
word.replace(/^["'`]+|["'`;|&]+$/g, ''),
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function agentObfuscationPatterns(raw: string, existing: readonly string[]): string[] {
|
|
531
|
+
const patterns = new Set(existing);
|
|
532
|
+
for (const check of AGENT_OBFUSCATION_CHECKS) {
|
|
533
|
+
if (check.test(raw)) {
|
|
534
|
+
patterns.add(check.description);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return [...patterns];
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function enforceAgentObfuscationVerdict(verdict: RuntimeSegmentVerdict): RuntimeSegmentVerdict {
|
|
541
|
+
const obfuscationPatterns = agentObfuscationPatterns(verdict.raw, verdict.obfuscationPatterns);
|
|
542
|
+
if (obfuscationPatterns.length === verdict.obfuscationPatterns.length) {
|
|
543
|
+
return verdict;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
return {
|
|
547
|
+
...verdict,
|
|
548
|
+
allowed: false,
|
|
549
|
+
reason: `obfuscation detected: ${obfuscationPatterns.join('; ')}`,
|
|
550
|
+
hasObfuscation: true,
|
|
551
|
+
obfuscationPatterns,
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
export function evaluateSegmentNode(
|
|
556
|
+
node: Security.CommandNode,
|
|
557
|
+
allowedClasses?: ReadonlySet<Security.CommandClassification>,
|
|
558
|
+
): RuntimeSegmentVerdict {
|
|
559
|
+
const verdict = allowedClasses === undefined
|
|
560
|
+
? security.evaluateSegmentNode(node)
|
|
561
|
+
: security.evaluateSegmentNode(node, allowedClasses);
|
|
562
|
+
return enforceAgentObfuscationVerdict(verdict);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
export function evaluateCommandAST(
|
|
566
|
+
original: string,
|
|
567
|
+
ast: RuntimeShellNode,
|
|
568
|
+
allowedClasses?: ReadonlySet<Security.CommandClassification>,
|
|
569
|
+
): RuntimeCompoundVerdict {
|
|
570
|
+
const compound = allowedClasses === undefined
|
|
571
|
+
? security.evaluateCommandAST(original, ast)
|
|
572
|
+
: security.evaluateCommandAST(original, ast, allowedClasses);
|
|
573
|
+
const segments = compound.segments.map(enforceAgentObfuscationVerdict);
|
|
574
|
+
const allowed = segments.every((segment) => segment.allowed);
|
|
575
|
+
const hasObfuscation = segments.some((segment) => segment.hasObfuscation);
|
|
576
|
+
|
|
577
|
+
const next: RuntimeCompoundVerdict = {
|
|
578
|
+
...compound,
|
|
579
|
+
allowed,
|
|
580
|
+
segments,
|
|
581
|
+
hasObfuscation,
|
|
582
|
+
};
|
|
583
|
+
if (allowed) {
|
|
584
|
+
delete next.denialExplanation;
|
|
585
|
+
} else {
|
|
586
|
+
next.denialExplanation = security.buildDenialExplanation(original, segments);
|
|
587
|
+
}
|
|
588
|
+
return next;
|
|
589
|
+
}
|
|
590
|
+
|
|
508
591
|
export type AuthInspectionSnapshot = Security.AuthInspectionSnapshot;
|
|
509
592
|
export type ProviderAuthInspection = Security.ProviderAuthInspection;
|
|
510
593
|
export type DivergenceDashboardSnapshot = Security.DivergenceDashboardSnapshot;
|
|
@@ -34,7 +34,7 @@ import type { DiscoveryDomainState } from '@/runtime/index.ts';
|
|
|
34
34
|
import type { IntelligenceDomainState } from '@/runtime/index.ts';
|
|
35
35
|
import type { UiPerfDomainState } from './domains/ui-perf.ts';
|
|
36
36
|
// UiPerfDomainState is structurally identical to SDK's SurfacePerfDomainState.
|
|
37
|
-
// Export as SurfacePerfDomainState alias for
|
|
37
|
+
// Export as SurfacePerfDomainState alias for shared runtime callers.
|
|
38
38
|
export type { UiPerfDomainState };
|
|
39
39
|
|
|
40
40
|
import { createInitialSessionState } from '@/runtime/index.ts';
|
|
@@ -75,7 +75,7 @@ export function createAgentChannelSendTool(
|
|
|
75
75
|
},
|
|
76
76
|
explicitUserRequest: {
|
|
77
77
|
type: 'string',
|
|
78
|
-
description: '
|
|
78
|
+
description: 'User request authorizing this delivery.',
|
|
79
79
|
},
|
|
80
80
|
},
|
|
81
81
|
required: ['message', 'confirm', 'explicitUserRequest'],
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CommandContext } from '../input/command-registry.ts';
|
|
2
2
|
import type { AgentWorkspaceChannelStatus } from '../input/agent-workspace-channels.ts';
|
|
3
3
|
import { buildAgentWorkspaceChannels } from '../input/agent-workspace-channels.ts';
|
|
4
|
+
import { previewHarnessText } from './agent-harness-text.ts';
|
|
4
5
|
|
|
5
6
|
export interface AgentHarnessChannelArgs {
|
|
6
7
|
readonly channelId?: unknown;
|
|
@@ -59,9 +60,14 @@ function describeChannelCandidate(channel: AgentWorkspaceChannelStatus): Record<
|
|
|
59
60
|
setupState: channel.setupState,
|
|
60
61
|
ready: channel.ready,
|
|
61
62
|
delivery: channel.delivery,
|
|
63
|
+
modelRoute: channelModelRoute(),
|
|
62
64
|
};
|
|
63
65
|
}
|
|
64
66
|
|
|
67
|
+
function channelModelRoute(): string {
|
|
68
|
+
return 'agent_channel_send or agent_harness mode:"channel"';
|
|
69
|
+
}
|
|
70
|
+
|
|
65
71
|
function describeChannel(
|
|
66
72
|
channel: AgentWorkspaceChannelStatus,
|
|
67
73
|
options: { readonly includeParameters?: boolean; readonly lookup?: Record<string, unknown> } = {},
|
|
@@ -82,32 +88,37 @@ function describeChannel(
|
|
|
82
88
|
missingConfigKeys: channel.missingRequiredKeys,
|
|
83
89
|
defaultTargetKeys: channel.defaultTargetKeys,
|
|
84
90
|
configuredDefaultTargetKeys: channel.configuredDefaultTargetKeys,
|
|
91
|
+
modelRoute: channelModelRoute(),
|
|
85
92
|
...(options.lookup ? { lookup: options.lookup } : {}),
|
|
86
|
-
...(options.includeParameters
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
93
|
+
...(options.includeParameters
|
|
94
|
+
? {
|
|
95
|
+
policy: {
|
|
96
|
+
effect: 'read-only',
|
|
97
|
+
values: 'Config key names and target key names are shown; secret values and stored target values are never returned.',
|
|
98
|
+
delivery: 'Use agent_channel_send only for one explicit, confirmed delivery target requested by the user.',
|
|
99
|
+
setup: 'Use connected-host setup/account/policy routes only as read-only diagnostics unless another confirmed first-class tool owns the mutation.',
|
|
100
|
+
},
|
|
101
|
+
modelAccess: {
|
|
102
|
+
sendTool: 'agent_channel_send',
|
|
103
|
+
notificationTool: 'agent_notify',
|
|
104
|
+
reminderTool: 'agent_reminder_schedule',
|
|
105
|
+
slashCommandDetail: `/channels show ${channel.id}`,
|
|
106
|
+
readOnlyConnectedRoutes: [
|
|
107
|
+
'/channels accounts',
|
|
108
|
+
'/channels policies',
|
|
109
|
+
'/channels status',
|
|
110
|
+
`/channels doctor ${channel.id}`,
|
|
111
|
+
`/channels setup ${channel.id}`,
|
|
112
|
+
],
|
|
113
|
+
settingsFilter: `agent_harness mode:"settings" prefix:"surfaces.${channel.id}" includeHidden:true`,
|
|
114
|
+
connectedHostBoundary: 'agent_harness mode:"connected_host_capability" query:"delivery"',
|
|
115
|
+
deliveryTargetShape: 'surface[:route[:label]]',
|
|
116
|
+
exampleTarget: `${channel.id}:route:Label`,
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
: {
|
|
120
|
+
summary: previewHarnessText(channel.nextStep || `${channel.label} channel ${channel.setupState}`),
|
|
121
|
+
}),
|
|
111
122
|
};
|
|
112
123
|
}
|
|
113
124
|
|
|
@@ -34,6 +34,11 @@ function readLimit(value: unknown, fallback: number): number {
|
|
|
34
34
|
return Math.max(1, Math.min(500, Math.trunc(parsed)));
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function previewText(value: string, maxLength = 56): string {
|
|
38
|
+
const normalized = value.replace(/\s+/g, ' ').trim();
|
|
39
|
+
return normalized.length <= maxLength ? normalized : `${normalized.slice(0, maxLength - 3).trimEnd()}...`;
|
|
40
|
+
}
|
|
41
|
+
|
|
37
42
|
function redactCliLookupInput(input: string): string {
|
|
38
43
|
return input
|
|
39
44
|
.replace(/(--config=)([^=\s]+)=\S+/g, '$1$2=<redacted>')
|
|
@@ -58,6 +63,8 @@ function fallbackCliSummary(command: GoodVibesCliCommand): string {
|
|
|
58
63
|
function describeCliCommand(command: GoodVibesCliCommand, lookup?: CliCommandLookup): Record<string, unknown> {
|
|
59
64
|
const help = describeGoodVibesCommandHelp(command);
|
|
60
65
|
const tokens = cliCommandTokens(command);
|
|
66
|
+
const policy = describeCliCommandPolicy(command);
|
|
67
|
+
const modelRoute = previewText(policy.preferredModelTool ?? 'agent_harness mode:"cli_command"');
|
|
61
68
|
return {
|
|
62
69
|
name: command,
|
|
63
70
|
tokens,
|
|
@@ -69,7 +76,13 @@ function describeCliCommand(command: GoodVibesCliCommand, lookup?: CliCommandLoo
|
|
|
69
76
|
aliases: help?.aliases ?? tokens.filter((token) => token !== command),
|
|
70
77
|
subcommands: help?.subcommands ?? [],
|
|
71
78
|
examples: help?.examples ?? [],
|
|
72
|
-
|
|
79
|
+
modelRoute,
|
|
80
|
+
modelAccess: {
|
|
81
|
+
inspect: `agent_harness mode:"cli_command" cliCommand:"${command}"`,
|
|
82
|
+
executeEquivalent: policy.preferredModelTool ?? 'Use the current conversation or an explicit user-requested shell command.',
|
|
83
|
+
preferred: modelRoute,
|
|
84
|
+
},
|
|
85
|
+
policy,
|
|
73
86
|
};
|
|
74
87
|
}
|
|
75
88
|
|
|
@@ -88,11 +101,16 @@ function cliCommandMatches(command: Record<string, unknown>, query: string): boo
|
|
|
88
101
|
}
|
|
89
102
|
|
|
90
103
|
function cliCommandCandidate(command: Record<string, unknown>): Record<string, unknown> {
|
|
104
|
+
const policy = typeof command.policy === 'object' && command.policy !== null
|
|
105
|
+
? command.policy as { readonly effect?: unknown; readonly preferredModelTool?: unknown }
|
|
106
|
+
: {};
|
|
91
107
|
return {
|
|
92
108
|
name: command.name,
|
|
93
109
|
tokens: command.tokens,
|
|
94
110
|
invocation: command.invocation,
|
|
95
111
|
summary: command.summary,
|
|
112
|
+
effect: typeof policy.effect === 'string' ? policy.effect : 'unknown',
|
|
113
|
+
...(typeof policy.preferredModelTool === 'string' ? { modelRoute: previewText(policy.preferredModelTool) } : {}),
|
|
96
114
|
};
|
|
97
115
|
}
|
|
98
116
|
|
|
@@ -39,9 +39,9 @@ function readStringArray(value: unknown): readonly string[] {
|
|
|
39
39
|
return value.map((entry) => typeof entry === 'string' ? entry : String(entry));
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
function previewText(value: string, maxLength =
|
|
42
|
+
function previewText(value: string, maxLength = 56): string {
|
|
43
43
|
const normalized = value.replace(/\s+/g, ' ').trim();
|
|
44
|
-
return normalized.length <= maxLength ? normalized : `${normalized.slice(0, maxLength -
|
|
44
|
+
return normalized.length <= maxLength ? normalized : `${normalized.slice(0, maxLength - 3).trimEnd()}...`;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
function commandMatches(command: SlashCommand, query: string): boolean {
|
|
@@ -57,6 +57,8 @@ function commandMatches(command: SlashCommand, query: string): boolean {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
function describeCommand(command: SlashCommand, lookup?: CommandDetailLookup): Record<string, unknown> {
|
|
60
|
+
const policy = describeCommandPolicy(command.name);
|
|
61
|
+
const modelRoute = previewText(policy.preferredModelTool ?? 'agent_harness mode:"command", mode:"run_command"');
|
|
60
62
|
return {
|
|
61
63
|
name: command.name,
|
|
62
64
|
slash: `/${command.name}`,
|
|
@@ -64,6 +66,12 @@ function describeCommand(command: SlashCommand, lookup?: CommandDetailLookup): R
|
|
|
64
66
|
description: command.description,
|
|
65
67
|
usage: command.usage ?? '',
|
|
66
68
|
argsHint: command.argsHint ?? command.usage ?? '',
|
|
69
|
+
modelRoute,
|
|
70
|
+
modelAccess: {
|
|
71
|
+
inspect: `agent_harness mode:"command" commandName:"${command.name}"`,
|
|
72
|
+
run: `agent_harness mode:"run_command" commandName:"${command.name}" confirm:true explicitUserRequest:"..."`,
|
|
73
|
+
preferred: modelRoute,
|
|
74
|
+
},
|
|
67
75
|
...(lookup ? {
|
|
68
76
|
lookup: {
|
|
69
77
|
source: lookup.source,
|
|
@@ -73,16 +81,19 @@ function describeCommand(command: SlashCommand, lookup?: CommandDetailLookup): R
|
|
|
73
81
|
resolvedBy: lookup.resolvedBy,
|
|
74
82
|
},
|
|
75
83
|
} : {}),
|
|
76
|
-
policy
|
|
84
|
+
policy,
|
|
77
85
|
};
|
|
78
86
|
}
|
|
79
87
|
|
|
80
88
|
function describeCommandCandidate(command: SlashCommand): Record<string, unknown> {
|
|
89
|
+
const policy = describeCommandPolicy(command.name);
|
|
81
90
|
return {
|
|
82
91
|
name: command.name,
|
|
83
92
|
slash: `/${command.name}`,
|
|
84
93
|
aliases: command.aliases ?? [],
|
|
85
94
|
summary: previewText(command.description),
|
|
95
|
+
effect: policy.effect,
|
|
96
|
+
modelRoute: previewText(policy.preferredModelTool ?? 'agent_harness mode:"command", mode:"run_command"'),
|
|
86
97
|
...(command.argsHint ? { argsHint: command.argsHint } : {}),
|
|
87
98
|
};
|
|
88
99
|
}
|
|
@@ -170,7 +181,7 @@ export function resolveHarnessCommandDetail(commandRegistry: CommandRegistry, ar
|
|
|
170
181
|
|
|
171
182
|
export function listHarnessCommands(commandRegistry: CommandRegistry, args: AgentHarnessCommandCatalogArgs): readonly Record<string, unknown>[] {
|
|
172
183
|
const query = readString(args.query);
|
|
173
|
-
const limit = readLimit(args.limit,
|
|
184
|
+
const limit = readLimit(args.limit, 500);
|
|
174
185
|
const includeParameters = args.includeParameters === true;
|
|
175
186
|
return commandRegistry.list()
|
|
176
187
|
.filter((command) => commandMatches(command, query))
|
|
@@ -39,11 +39,11 @@ function connectedHostFindings(
|
|
|
39
39
|
});
|
|
40
40
|
} else if (!runtime.compatible) {
|
|
41
41
|
findings.push({
|
|
42
|
-
id: 'connected-host-
|
|
42
|
+
id: 'connected-host-incompatible',
|
|
43
43
|
severity: 'warning',
|
|
44
|
-
summary: 'Connected host
|
|
45
|
-
cause:
|
|
46
|
-
action: 'Update the owning GoodVibes host so its
|
|
44
|
+
summary: 'Connected host compatibility does not satisfy Agent readiness.',
|
|
45
|
+
cause: 'Connected host is reachable, but at least one public Agent route is unavailable or incompatible.',
|
|
46
|
+
action: 'Update the owning GoodVibes host so its public Agent routes are compatible.',
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -88,6 +88,7 @@ export async function connectedHostStatusSummary(
|
|
|
88
88
|
return {
|
|
89
89
|
ownership: 'external-connected-host',
|
|
90
90
|
readOnly: true,
|
|
91
|
+
modelRoute: 'agent_harness mode:"connected_host_status" or mode:"service_posture"',
|
|
91
92
|
timeoutMs: CONNECTED_HOST_STATUS_TIMEOUT_MS,
|
|
92
93
|
lifecycle: 'GoodVibes Agent can inspect connected-host readiness and use public operator routes, but does not start, stop, restart, install, expose, or mutate the host listener.',
|
|
93
94
|
paths: {
|
|
@@ -122,8 +123,6 @@ export async function connectedHostStatusSummary(
|
|
|
122
123
|
route: '/status',
|
|
123
124
|
reachable: runtime.reachable,
|
|
124
125
|
statusCode: runtime.statusCode,
|
|
125
|
-
sdkVersion: runtime.version,
|
|
126
|
-
expectedSdkVersion: runtime.expectedVersion,
|
|
127
126
|
compatible: runtime.compatible,
|
|
128
127
|
},
|
|
129
128
|
{
|
|
@@ -142,6 +141,8 @@ export async function connectedHostStatusSummary(
|
|
|
142
141
|
},
|
|
143
142
|
...(options.includeParameters ? { modelAccess: {
|
|
144
143
|
diagnostics: 'Use mode:"connected_host_status" for live read-only host readiness, mode:"service_posture" for endpoint posture, mode:"service_endpoint" for one endpoint, and mode:"connected_host" for capability and boundary inventory.',
|
|
144
|
+
daemonAliases: 'mode:"daemon_status" is an alias for mode:"connected_host_status"; mode:"daemon" is an alias for mode:"connected_host".',
|
|
145
|
+
lifecycleBlocked: 'Start, stop, restart, install, expose, and listener mutation are not exposed through Agent.',
|
|
145
146
|
cliMirrors: ['goodvibes-agent status --json', 'goodvibes-agent doctor', 'goodvibes-agent compat'],
|
|
146
147
|
tuiMirrors: ['Agent Workspace -> Home -> Host compatibility', 'Agent Workspace -> Home -> Doctor diagnostics', 'Agent Workspace -> Home -> Review health'],
|
|
147
148
|
},
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CommandContext } from '../input/command-registry.ts';
|
|
2
|
+
import { previewHarnessText } from './agent-harness-text.ts';
|
|
2
3
|
|
|
3
4
|
export interface AgentHarnessDelegationArgs {
|
|
4
5
|
readonly delegationRouteId?: unknown;
|
|
@@ -113,9 +114,16 @@ function describeCandidate(route: DelegationRoute): Record<string, unknown> {
|
|
|
113
114
|
label: route.label,
|
|
114
115
|
effect: route.effect,
|
|
115
116
|
confirmationRequired: route.confirmationRequired === true,
|
|
117
|
+
modelRoute: delegationRouteModelRoute(route),
|
|
116
118
|
};
|
|
117
119
|
}
|
|
118
120
|
|
|
121
|
+
function delegationRouteModelRoute(route: DelegationRoute): string {
|
|
122
|
+
if (route.workspaceActionId) return 'agent_harness mode:"run_workspace_action"';
|
|
123
|
+
if (route.command || route.commandTemplate) return 'agent_harness mode:"run_command"';
|
|
124
|
+
return route.effect === 'main-conversation' ? 'main conversation' : 'agent_harness mode:"delegation_route"';
|
|
125
|
+
}
|
|
126
|
+
|
|
119
127
|
function describeRoute(
|
|
120
128
|
context: CommandContext,
|
|
121
129
|
route: DelegationRoute,
|
|
@@ -124,17 +132,20 @@ function describeRoute(
|
|
|
124
132
|
return {
|
|
125
133
|
delegationRouteId: route.id,
|
|
126
134
|
label: route.label,
|
|
127
|
-
detail: route.detail,
|
|
135
|
+
...(options.includeParameters ? { detail: route.detail } : { summary: previewHarnessText(route.detail) }),
|
|
128
136
|
effect: route.effect,
|
|
129
|
-
...(route.command ? { command: route.command } : {}),
|
|
130
|
-
...(route.commandTemplate ? { commandTemplate: route.commandTemplate } : {}),
|
|
131
|
-
...(route.workspaceActionId ? { workspaceActionId: route.workspaceActionId } : {}),
|
|
132
137
|
confirmationRequired: route.confirmationRequired === true,
|
|
133
138
|
reviewPolicy: route.reviewPolicy ?? 'not-applicable',
|
|
139
|
+
modelRoute: delegationRouteModelRoute(route),
|
|
134
140
|
runtime: {
|
|
135
141
|
sessionId: context.session.runtime.sessionId,
|
|
136
142
|
operatorClientAttached: Boolean(context.clients?.operator),
|
|
137
143
|
},
|
|
144
|
+
...(options.includeParameters ? {
|
|
145
|
+
...(route.command ? { command: route.command } : {}),
|
|
146
|
+
...(route.commandTemplate ? { commandTemplate: route.commandTemplate } : {}),
|
|
147
|
+
...(route.workspaceActionId ? { workspaceActionId: route.workspaceActionId } : {}),
|
|
148
|
+
} : {}),
|
|
138
149
|
...(options.lookup ? { lookup: options.lookup } : {}),
|
|
139
150
|
...(options.includeParameters ? {
|
|
140
151
|
policy: {
|