@pellux/goodvibes-agent 1.0.33 → 1.0.34
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 +22 -0
- package/README.md +71 -59
- package/dist/package/main.js +565 -420
- package/docs/README.md +25 -21
- package/docs/channels-remote-and-api.md +2 -2
- package/docs/connected-host.md +3 -3
- package/docs/getting-started.md +87 -86
- package/docs/providers-and-routing.md +3 -3
- package/docs/release-and-publishing.md +3 -3
- package/docs/tools-and-commands.md +149 -139
- package/docs/voice-and-live-tts.md +1 -1
- package/package.json +1 -1
- package/release/live-verification/live-verification.json +2 -2
- package/release/live-verification/live-verification.md +2 -2
- package/release/release-notes.md +6 -15
- package/release/release-readiness.json +4 -4
- package/src/agent/harness-control.ts +42 -3
- package/src/runtime/bootstrap.ts +10 -18
- package/src/tools/agent-analysis-registry-policy.ts +2 -9
- package/src/tools/agent-channel-send-tool.ts +1 -5
- package/src/tools/agent-context-policy.ts +1 -5
- package/src/tools/agent-find-policy.ts +1 -4
- package/src/tools/agent-harness-channel-metadata.ts +23 -23
- package/src/tools/agent-harness-cli-metadata.ts +4 -1
- package/src/tools/agent-harness-command-catalog.ts +10 -3
- package/src/tools/agent-harness-connected-host-status.ts +8 -2
- package/src/tools/agent-harness-delegation-posture.ts +5 -5
- package/src/tools/agent-harness-mcp-metadata.ts +30 -28
- package/src/tools/agent-harness-media-posture.ts +9 -9
- package/src/tools/agent-harness-metadata.ts +25 -7
- package/src/tools/agent-harness-model-routing.ts +14 -14
- package/src/tools/agent-harness-model-tool-catalog.ts +7 -2
- package/src/tools/agent-harness-notification-metadata.ts +17 -17
- package/src/tools/agent-harness-pairing-posture.ts +7 -7
- package/src/tools/agent-harness-panel-metadata.ts +26 -12
- package/src/tools/agent-harness-provider-account-metadata.ts +33 -31
- package/src/tools/agent-harness-security-posture.ts +9 -7
- package/src/tools/agent-harness-service-posture.ts +22 -16
- package/src/tools/agent-harness-session-metadata.ts +9 -7
- package/src/tools/agent-harness-setup-posture.ts +6 -6
- package/src/tools/agent-harness-tool-schema.ts +1 -0
- package/src/tools/agent-harness-tool.ts +79 -36
- package/src/tools/agent-harness-ui-surface-metadata.ts +19 -11
- package/src/tools/agent-harness-workspace-actions.ts +29 -1
- package/src/tools/agent-knowledge-ingest-tool.ts +1 -5
- package/src/tools/agent-knowledge-tool.ts +1 -5
- package/src/tools/agent-local-registry-tool.ts +1 -4
- package/src/tools/agent-media-generate-tool.ts +1 -5
- package/src/tools/agent-notify-tool.ts +1 -5
- package/src/tools/agent-operator-action-tool.ts +1 -5
- package/src/tools/agent-operator-briefing-tool.ts +1 -5
- package/src/tools/agent-read-policy.ts +1 -4
- package/src/tools/agent-reminder-schedule-tool.ts +1 -5
- package/src/tools/agent-tool-policy-guard.ts +7 -34
- package/src/tools/agent-web-search-policy.ts +1 -4
- package/src/tools/agent-work-plan-tool.ts +1 -5
- package/src/version.ts +1 -1
|
@@ -77,6 +77,18 @@ export interface HarnessSettingDescriptor {
|
|
|
77
77
|
readonly lookup?: HarnessSettingLookup;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
export interface HarnessSettingSummary {
|
|
81
|
+
readonly key: string;
|
|
82
|
+
readonly category: string;
|
|
83
|
+
readonly type: ConfigSetting['type'];
|
|
84
|
+
readonly value: unknown;
|
|
85
|
+
readonly configured: boolean;
|
|
86
|
+
readonly writable: boolean;
|
|
87
|
+
readonly visibleInWorkspace: boolean;
|
|
88
|
+
readonly summary: string;
|
|
89
|
+
readonly enumValues?: readonly string[];
|
|
90
|
+
}
|
|
91
|
+
|
|
80
92
|
export interface HarnessSettingMutationResult {
|
|
81
93
|
readonly key: string;
|
|
82
94
|
readonly action: 'set' | 'reset';
|
|
@@ -91,6 +103,11 @@ function valuesEqual(left: unknown, right: unknown): boolean {
|
|
|
91
103
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
92
104
|
}
|
|
93
105
|
|
|
106
|
+
function previewText(value: string, maxLength = 120): string {
|
|
107
|
+
const normalized = value.replace(/\s+/g, ' ').trim();
|
|
108
|
+
return normalized.length <= maxLength ? normalized : `${normalized.slice(0, maxLength - 1).trimEnd()}...`;
|
|
109
|
+
}
|
|
110
|
+
|
|
94
111
|
function clampLimit(value: unknown, fallback = DEFAULT_SETTING_LIMIT): number {
|
|
95
112
|
if (typeof value !== 'number' || !Number.isFinite(value)) return fallback;
|
|
96
113
|
return Math.max(1, Math.min(500, Math.trunc(value)));
|
|
@@ -164,10 +181,30 @@ export function describeHarnessSetting(
|
|
|
164
181
|
};
|
|
165
182
|
}
|
|
166
183
|
|
|
184
|
+
export function describeHarnessSettingSummary(
|
|
185
|
+
configManager: Pick<ConfigManager, 'get'>,
|
|
186
|
+
setting: ConfigSetting,
|
|
187
|
+
): HarnessSettingSummary {
|
|
188
|
+
const value = configManager.get(setting.key as ConfigKey);
|
|
189
|
+
const hostOwned = isExternalHostOwnedSettingKey(setting.key);
|
|
190
|
+
return {
|
|
191
|
+
key: setting.key,
|
|
192
|
+
category: setting.key.split('.')[0] ?? '',
|
|
193
|
+
type: setting.type,
|
|
194
|
+
value: redactHarnessSettingValue(setting.key, value),
|
|
195
|
+
configured: !valuesEqual(value, setting.default),
|
|
196
|
+
writable: !hostOwned,
|
|
197
|
+
visibleInWorkspace: !isAgentHiddenSettingKey(setting.key),
|
|
198
|
+
summary: previewText(setting.description),
|
|
199
|
+
...(setting.enumValues ? { enumValues: setting.enumValues } : {}),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
167
203
|
export function listHarnessSettings(
|
|
168
204
|
configManager: Pick<ConfigManager, 'get' | 'getSchema'>,
|
|
169
205
|
filters: HarnessSettingFilters = {},
|
|
170
|
-
|
|
206
|
+
options: { readonly includeParameters?: boolean } = {},
|
|
207
|
+
): readonly (HarnessSettingDescriptor | HarnessSettingSummary)[] {
|
|
171
208
|
const key = filters.key?.trim();
|
|
172
209
|
const category = filters.category?.trim();
|
|
173
210
|
const prefix = filters.prefix?.trim();
|
|
@@ -185,7 +222,9 @@ export function listHarnessSettings(
|
|
|
185
222
|
}
|
|
186
223
|
return true;
|
|
187
224
|
})
|
|
188
|
-
.map((setting) =>
|
|
225
|
+
.map((setting) => options.includeParameters
|
|
226
|
+
? describeHarnessSetting(configManager, setting)
|
|
227
|
+
: describeHarnessSettingSummary(configManager, setting))
|
|
189
228
|
.slice(0, limit);
|
|
190
229
|
}
|
|
191
230
|
|
|
@@ -358,7 +397,7 @@ export async function resetHarnessSetting(
|
|
|
358
397
|
};
|
|
359
398
|
}
|
|
360
399
|
|
|
361
|
-
export function formatHarnessSettingList(settings: readonly HarnessSettingDescriptor[]): string {
|
|
400
|
+
export function formatHarnessSettingList(settings: readonly (HarnessSettingDescriptor | HarnessSettingSummary)[]): string {
|
|
362
401
|
if (settings.length === 0) return 'No settings matched.';
|
|
363
402
|
return [
|
|
364
403
|
`Settings (${settings.length})`,
|
package/src/runtime/bootstrap.ts
CHANGED
|
@@ -46,24 +46,16 @@ import { registerAgentHarnessTool } from '../tools/agent-harness-tool.ts';
|
|
|
46
46
|
|
|
47
47
|
const GOODVIBES_AGENT_OPERATOR_POLICY = [
|
|
48
48
|
'## GoodVibes Agent Operator Policy',
|
|
49
|
-
'-
|
|
50
|
-
'-
|
|
51
|
-
'-
|
|
52
|
-
'-
|
|
53
|
-
'-
|
|
54
|
-
'-
|
|
55
|
-
'-
|
|
56
|
-
'-
|
|
57
|
-
'-
|
|
58
|
-
'-
|
|
59
|
-
'- When the user explicitly asks Agent to send an alert, message, or notification to configured notification targets, use the `agent_notify` tool with confirm:true and the original user request. Do not infer external notifications from vague suggestions, and never create channel routes or account authorizations from the main conversation.',
|
|
60
|
-
'- When the user explicitly asks Agent to send one message to a specific configured channel, route, webhook, or link target, use the `agent_channel_send` tool with confirm:true and the original user request. It can deliver one message through configured delivery strategies, but must not create routes, authorize accounts, manage connected-host hosting, use default knowledge, use non-Agent knowledge spaces, create separate Agent jobs, or request GoodVibes TUI delegation.',
|
|
61
|
-
'- When the user explicitly asks for a reminder or asks Agent to schedule a reminder, use the `agent_reminder_schedule` tool with confirm:true and the original user request. That creates one connected schedules.create reminder on the external GoodVibes host. Do not infer reminders from vague suggestions or routine startup, and never create local scheduler jobs.',
|
|
62
|
-
'- GoodVibes TUI delegation is never the default Agent reasoning path. Do not delegate planning, research, operations, knowledge, memory, configuration, approvals, automation observability, or ordinary assistant work.',
|
|
63
|
-
'- GoodVibes Agent is not the coding TUI. Do not use the `agent` tool to create coding-role Agent jobs or batch job roots from Agent.',
|
|
64
|
-
'- When the user explicitly asks to build, implement, fix, patch, or review code, preserve the full original user ask and delegate one build request to GoodVibes TUI through the public shared-session/build-delegation contract. Include clear executionIntent and request delegated review only for explicit build/fix/review work or when the user explicitly asks for delegated Agent review.',
|
|
65
|
-
'- Do not narrow explicit build/fix/review requests into design-only, read-only, or no-write work unless the user explicitly requested that limitation. TUI owns file edits, git/worktree work, execution isolation UX, and delegated review coordination.',
|
|
66
|
-
'- If a stable public delegation route is unavailable, say that the task needs GoodVibes TUI delegation and report the missing route instead of pretending to implement it locally or creating sibling Agent jobs.',
|
|
49
|
+
'- 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.',
|
|
50
|
+
'- Connected-host lifecycle is external. Do not start, stop, restart, install, expose, or mutate host listeners/network posture from Agent.',
|
|
51
|
+
'- Read tools: `agent_operator_briefing` for connected work/approvals/automation/schedules, `agent_knowledge` for isolated Agent Knowledge, `agent_harness` for harness catalogs/settings/status.',
|
|
52
|
+
'- 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.',
|
|
53
|
+
'- 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.',
|
|
54
|
+
'- Agent Knowledge must use only `/api/goodvibes-agent/knowledge/*` and fail closed. Do not use default knowledge or non-Agent knowledge spaces.',
|
|
55
|
+
'- External delivery, media generation, reminders, settings writes, slash-command mirrors, workspace action mirrors, and destructive local changes require explicit user intent and the owning tool/command confirmation.',
|
|
56
|
+
'- Routines run in this serial conversation unless explicitly promoted to a connected schedule. Do not create hidden Agent jobs or local scheduler jobs.',
|
|
57
|
+
'- Do not delegate planning, research, operations, knowledge, memory, configuration, approvals, observability, or ordinary assistant work.',
|
|
58
|
+
'- For explicit build, implement, fix, patch, or review requests, preserve the full original ask and use the public shared-session/build-delegation route. If that route is unavailable, report the missing route instead of pretending to perform the work locally.',
|
|
67
59
|
].join('\n');
|
|
68
60
|
|
|
69
61
|
function joinPromptParts(...parts: Array<string | null | undefined>): string {
|
|
@@ -82,11 +82,7 @@ export function validateRegistryToolInvocationForAgentPolicy(args: RegistryToolA
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
function narrowAnalyzeToolDefinitionForAgentPolicy(tool: Tool): void {
|
|
85
|
-
tool.definition.description =
|
|
86
|
-
'Run local, static project analysis for GoodVibes Agent.',
|
|
87
|
-
'npm registry upgrade checks and hidden secondary LLM diff analysis are disabled in the main conversation.',
|
|
88
|
-
'Delegate package-upgrade and review workflows to GoodVibes TUI when they become build/fix/review work.',
|
|
89
|
-
].join(' ');
|
|
85
|
+
tool.definition.description = 'Run local, static project analysis for GoodVibes Agent.';
|
|
90
86
|
tool.definition.sideEffects = ['read_fs'];
|
|
91
87
|
|
|
92
88
|
const properties = tool.definition.parameters.properties;
|
|
@@ -101,10 +97,7 @@ function narrowAnalyzeToolDefinitionForAgentPolicy(tool: Tool): void {
|
|
|
101
97
|
}
|
|
102
98
|
|
|
103
99
|
function narrowRegistryToolDefinitionForAgentPolicy(tool: Tool): void {
|
|
104
|
-
tool.definition.description =
|
|
105
|
-
'Discover and preview GoodVibes Agent skills, agents, and tools.',
|
|
106
|
-
'Full content materialization and arbitrary .goodvibes file reads are disabled in the main conversation.',
|
|
107
|
-
].join(' ');
|
|
100
|
+
tool.definition.description = 'Discover and preview GoodVibes Agent registry entries.';
|
|
108
101
|
tool.definition.sideEffects = ['read_fs'];
|
|
109
102
|
|
|
110
103
|
const properties = tool.definition.parameters.properties;
|
|
@@ -41,11 +41,7 @@ export function createAgentChannelSendTool(
|
|
|
41
41
|
return {
|
|
42
42
|
definition: {
|
|
43
43
|
name: 'agent_channel_send',
|
|
44
|
-
description:
|
|
45
|
-
'Send one confirmed message through a configured Agent target.',
|
|
46
|
-
'Use only for explicit send/message/alert requests.',
|
|
47
|
-
'Provide exactly one target.',
|
|
48
|
-
].join(' '),
|
|
44
|
+
description: 'Send one confirmed message through one configured Agent target.',
|
|
49
45
|
parameters: {
|
|
50
46
|
type: 'object',
|
|
51
47
|
properties: {
|
|
@@ -7,11 +7,7 @@ const CONTEXT_TOOL_DENIAL = [
|
|
|
7
7
|
].join(' ');
|
|
8
8
|
|
|
9
9
|
export function wrapBlockedContextToolForAgentPolicy(tool: Tool): void {
|
|
10
|
-
tool.definition.description =
|
|
11
|
-
'Blocked in GoodVibes Agent main conversation: non-Agent runtime context.',
|
|
12
|
-
'Use explicit Agent CLI/slash status, compat, setup, and Agent Knowledge commands for product-scoped context.',
|
|
13
|
-
'Default knowledge, non-Agent knowledge segments, and non-Agent runtime assumptions are not Agent fallbacks.',
|
|
14
|
-
].join(' ');
|
|
10
|
+
tool.definition.description = 'Blocked in GoodVibes Agent: non-Agent runtime context.';
|
|
15
11
|
tool.definition.sideEffects = [];
|
|
16
12
|
tool.definition.parameters = {
|
|
17
13
|
type: 'object',
|
|
@@ -82,10 +82,7 @@ export function normalizeFindToolInvocationForAgentPolicy(args: FindToolArgs): F
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
function narrowFindToolDefinitionForAgentPolicy(tool: Tool): void {
|
|
85
|
-
tool.definition.description =
|
|
86
|
-
'Search the project for GoodVibes Agent using serial, gitignore-respecting read-only queries.',
|
|
87
|
-
'Hidden-file scans, symlink traversal, gitignore bypass, broad previews, full symbol dumps, and parallel search are disabled in the main conversation.',
|
|
88
|
-
].join(' ');
|
|
85
|
+
tool.definition.description = 'Search project files with serial, gitignore-respecting read-only queries.';
|
|
89
86
|
tool.definition.sideEffects = ['read_fs'];
|
|
90
87
|
tool.definition.concurrency = 'serial';
|
|
91
88
|
|
|
@@ -83,31 +83,31 @@ function describeChannel(
|
|
|
83
83
|
defaultTargetKeys: channel.defaultTargetKeys,
|
|
84
84
|
configuredDefaultTargetKeys: channel.configuredDefaultTargetKeys,
|
|
85
85
|
...(options.lookup ? { lookup: options.lookup } : {}),
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
86
|
+
...(options.includeParameters ? {
|
|
87
|
+
policy: {
|
|
88
|
+
effect: 'read-only',
|
|
89
|
+
values: 'Config key names and target key names are shown; secret values and stored target values are never returned.',
|
|
90
|
+
delivery: 'Use agent_channel_send only for one explicit, confirmed delivery target requested by the user.',
|
|
91
|
+
setup: 'Use connected-host setup/account/policy routes only as read-only diagnostics unless another confirmed first-class tool owns the mutation.',
|
|
92
|
+
},
|
|
93
|
+
modelAccess: {
|
|
94
|
+
sendTool: 'agent_channel_send',
|
|
95
|
+
notificationTool: 'agent_notify',
|
|
96
|
+
reminderTool: 'agent_reminder_schedule',
|
|
97
|
+
slashCommandDetail: `/channels show ${channel.id}`,
|
|
98
|
+
readOnlyConnectedRoutes: [
|
|
99
|
+
'/channels accounts',
|
|
100
|
+
'/channels policies',
|
|
101
|
+
'/channels status',
|
|
102
|
+
`/channels doctor ${channel.id}`,
|
|
103
|
+
`/channels setup ${channel.id}`,
|
|
104
|
+
],
|
|
105
|
+
settingsFilter: `agent_harness mode:"settings" prefix:"surfaces.${channel.id}" includeHidden:true`,
|
|
106
|
+
connectedHostBoundary: 'agent_harness mode:"connected_host_capability" query:"delivery"',
|
|
107
107
|
deliveryTargetShape: 'surface[:route[:label]]',
|
|
108
108
|
exampleTarget: `${channel.id}:route:Label`,
|
|
109
|
-
}
|
|
110
|
-
},
|
|
109
|
+
},
|
|
110
|
+
} : {}),
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -14,6 +14,7 @@ export interface AgentHarnessCliArgs {
|
|
|
14
14
|
readonly cliCommand?: unknown;
|
|
15
15
|
readonly commandName?: unknown;
|
|
16
16
|
readonly target?: unknown;
|
|
17
|
+
readonly includeParameters?: unknown;
|
|
17
18
|
readonly limit?: unknown;
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -106,12 +107,14 @@ export function blockedHarnessCliCommandTokens(): readonly string[] {
|
|
|
106
107
|
export function listHarnessCliCommands(args: AgentHarnessCliArgs): readonly Record<string, unknown>[] {
|
|
107
108
|
const query = readString(args.query);
|
|
108
109
|
const limit = readLimit(args.limit, 200);
|
|
110
|
+
const includeParameters = args.includeParameters === true;
|
|
109
111
|
return listGoodVibesCliCommands()
|
|
110
112
|
.filter((command) => command !== 'unknown')
|
|
111
113
|
.map((command) => describeCliCommand(command))
|
|
112
114
|
.filter((command) => cliCommandMatches(command, query))
|
|
113
115
|
.sort((a, b) => String(a.name).localeCompare(String(b.name)))
|
|
114
|
-
.slice(0, limit)
|
|
116
|
+
.slice(0, limit)
|
|
117
|
+
.map((command) => includeParameters ? command : cliCommandCandidate(command));
|
|
115
118
|
}
|
|
116
119
|
|
|
117
120
|
function cliInputFromArgs(args: AgentHarnessCliArgs): { readonly source: CliCommandLookup['source']; readonly input: string } | null {
|
|
@@ -8,6 +8,7 @@ export interface AgentHarnessCommandCatalogArgs {
|
|
|
8
8
|
readonly commandName?: unknown;
|
|
9
9
|
readonly args?: unknown;
|
|
10
10
|
readonly target?: unknown;
|
|
11
|
+
readonly includeParameters?: unknown;
|
|
11
12
|
readonly limit?: unknown;
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -38,6 +39,11 @@ function readStringArray(value: unknown): readonly string[] {
|
|
|
38
39
|
return value.map((entry) => typeof entry === 'string' ? entry : String(entry));
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
function previewText(value: string, maxLength = 120): string {
|
|
43
|
+
const normalized = value.replace(/\s+/g, ' ').trim();
|
|
44
|
+
return normalized.length <= maxLength ? normalized : `${normalized.slice(0, maxLength - 1).trimEnd()}...`;
|
|
45
|
+
}
|
|
46
|
+
|
|
41
47
|
function commandMatches(command: SlashCommand, query: string): boolean {
|
|
42
48
|
if (!query) return true;
|
|
43
49
|
const haystack = [
|
|
@@ -76,8 +82,8 @@ function describeCommandCandidate(command: SlashCommand): Record<string, unknown
|
|
|
76
82
|
name: command.name,
|
|
77
83
|
slash: `/${command.name}`,
|
|
78
84
|
aliases: command.aliases ?? [],
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
summary: previewText(command.description),
|
|
86
|
+
...(command.argsHint ? { argsHint: command.argsHint } : {}),
|
|
81
87
|
};
|
|
82
88
|
}
|
|
83
89
|
|
|
@@ -165,11 +171,12 @@ export function resolveHarnessCommandDetail(commandRegistry: CommandRegistry, ar
|
|
|
165
171
|
export function listHarnessCommands(commandRegistry: CommandRegistry, args: AgentHarnessCommandCatalogArgs): readonly Record<string, unknown>[] {
|
|
166
172
|
const query = readString(args.query);
|
|
167
173
|
const limit = readLimit(args.limit, 200);
|
|
174
|
+
const includeParameters = args.includeParameters === true;
|
|
168
175
|
return commandRegistry.list()
|
|
169
176
|
.filter((command) => commandMatches(command, query))
|
|
170
177
|
.sort((a, b) => a.name.localeCompare(b.name))
|
|
171
178
|
.slice(0, limit)
|
|
172
|
-
.map((command) => describeCommand(command));
|
|
179
|
+
.map((command) => includeParameters ? describeCommand(command) : describeCommandCandidate(command));
|
|
173
180
|
}
|
|
174
181
|
|
|
175
182
|
export function describeHarnessCommand(commandRegistry: CommandRegistry, args: AgentHarnessCommandCatalogArgs): Record<string, unknown> | null {
|
|
@@ -73,6 +73,7 @@ function connectedHostFindings(
|
|
|
73
73
|
export async function connectedHostStatusSummary(
|
|
74
74
|
context: CommandContext,
|
|
75
75
|
toolRegistry: ToolRegistry,
|
|
76
|
+
options: { readonly includeParameters?: boolean } = {},
|
|
76
77
|
): Promise<Record<string, unknown>> {
|
|
77
78
|
const homeDirectory = resolveHomeDirectory(context);
|
|
78
79
|
const workingDirectory = resolveWorkingDirectory(context);
|
|
@@ -134,13 +135,18 @@ export async function connectedHostStatusSummary(
|
|
|
134
135
|
},
|
|
135
136
|
],
|
|
136
137
|
findings: connectedHostFindings(runtime, tokenUsable),
|
|
137
|
-
|
|
138
|
+
capabilitySummary: {
|
|
139
|
+
routeFamilies: connectedHostRouteFamilies().length,
|
|
140
|
+
availableCapabilities: connectedHostCapabilityMap(toolRegistry).filter((capability) => capability.available === true).length,
|
|
141
|
+
blockedCapabilities: blockedConnectedHostCapabilities().length,
|
|
142
|
+
},
|
|
143
|
+
...(options.includeParameters ? { modelAccess: {
|
|
138
144
|
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.',
|
|
139
145
|
cliMirrors: ['goodvibes-agent status --json', 'goodvibes-agent doctor', 'goodvibes-agent compat'],
|
|
140
146
|
tuiMirrors: ['Agent Workspace -> Home -> Host compatibility', 'Agent Workspace -> Home -> Doctor diagnostics', 'Agent Workspace -> Home -> Review health'],
|
|
141
147
|
},
|
|
142
148
|
routeFamilies: connectedHostRouteFamilies(),
|
|
143
149
|
capabilities: connectedHostCapabilityMap(toolRegistry),
|
|
144
|
-
blockedCapabilities: blockedConnectedHostCapabilities(),
|
|
150
|
+
blockedCapabilities: blockedConnectedHostCapabilities() } : {}),
|
|
145
151
|
};
|
|
146
152
|
}
|
|
@@ -136,12 +136,12 @@ function describeRoute(
|
|
|
136
136
|
operatorClientAttached: Boolean(context.clients?.operator),
|
|
137
137
|
},
|
|
138
138
|
...(options.lookup ? { lookup: options.lookup } : {}),
|
|
139
|
-
policy: {
|
|
140
|
-
effect: route.effect,
|
|
141
|
-
values: 'Delegation posture returns policy, route, confirmation, and runtime availability metadata only; it does not submit delegated work.',
|
|
142
|
-
mutation: 'Delegated work submission stays a visible confirmed workspace or slash-command flow and must preserve the full original user ask.',
|
|
143
|
-
},
|
|
144
139
|
...(options.includeParameters ? {
|
|
140
|
+
policy: {
|
|
141
|
+
effect: route.effect,
|
|
142
|
+
values: 'Delegation posture returns policy, route, confirmation, and runtime availability metadata only; it does not submit delegated work.',
|
|
143
|
+
mutation: 'Delegated work submission stays a visible confirmed workspace or slash-command flow and must preserve the full original user ask.',
|
|
144
|
+
},
|
|
145
145
|
modelAccess: {
|
|
146
146
|
inspectDelegation: 'agent_harness mode:"delegation_posture"',
|
|
147
147
|
inspectRoute: 'agent_harness mode:"delegation_route"',
|
|
@@ -116,34 +116,36 @@ function describeServer(
|
|
|
116
116
|
})),
|
|
117
117
|
toolCount: tools.length,
|
|
118
118
|
} : { toolCount: tools.length }),
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
119
|
+
...(options.includeParameters ? {
|
|
120
|
+
policy: {
|
|
121
|
+
effect: 'read-only',
|
|
122
|
+
values: 'Server posture returns trust, role, connection, quarantine, and tool metadata; env values and secret config values are never returned.',
|
|
123
|
+
mutation: 'MCP add/remove/reload/trust/role/quarantine operations stay explicit confirmation-gated workspace or slash-command flows.',
|
|
124
|
+
allowAll: 'allow-all trust decisions remain routed through Settings -> MCP, not direct command escalation.',
|
|
125
|
+
},
|
|
126
|
+
modelAccess: {
|
|
127
|
+
reviewCommand: '/mcp review',
|
|
128
|
+
serversCommand: '/mcp servers',
|
|
129
|
+
toolsCommand: `/mcp tools ${server.name}`,
|
|
130
|
+
repairCommand: `/mcp repair ${server.name}`,
|
|
131
|
+
authReviewCommand: '/mcp auth-review',
|
|
132
|
+
configCommand: '/mcp config',
|
|
133
|
+
workspaceActionIds: [
|
|
134
|
+
'mcp-review',
|
|
135
|
+
'mcp-tools-server',
|
|
136
|
+
'mcp-repair',
|
|
137
|
+
'mcp-config',
|
|
138
|
+
'mcp-add-server',
|
|
139
|
+
'mcp-settings',
|
|
140
|
+
],
|
|
141
|
+
confirmationGatedCommands: [
|
|
142
|
+
`/mcp trust ${server.name} <constrained|ask-on-risk|blocked> --yes`,
|
|
143
|
+
`/mcp role ${server.name} <role> --yes`,
|
|
144
|
+
`/mcp quarantine ${server.name} approve <operatorId> --yes`,
|
|
145
|
+
`/mcp remove ${server.name} --yes`,
|
|
146
|
+
],
|
|
147
|
+
},
|
|
148
|
+
} : {}),
|
|
147
149
|
};
|
|
148
150
|
}
|
|
149
151
|
|
|
@@ -118,12 +118,12 @@ function describeProvider(
|
|
|
118
118
|
generateMedia: 'agent_media_generate with confirm:true and explicitUserRequest',
|
|
119
119
|
ttsSettings: 'agent_harness settings/get_setting/set_setting for tts.provider, tts.voice, tts.llmProvider, and tts.llmModel',
|
|
120
120
|
},
|
|
121
|
+
policy: {
|
|
122
|
+
effect: 'read-only',
|
|
123
|
+
values: 'Provider posture returns capability, setup, selected, health, and safe environment key names only; secret values and media payloads are never returned.',
|
|
124
|
+
mutation: 'Media generation, voice enable/disable, TTS setting changes, and bundle export stay explicit confirmation-gated tool, setting, workspace, or slash-command flows.',
|
|
125
|
+
},
|
|
121
126
|
} : {}),
|
|
122
|
-
policy: {
|
|
123
|
-
effect: 'read-only',
|
|
124
|
-
values: 'Provider posture returns capability, setup, selected, health, and safe environment key names only; secret values and media payloads are never returned.',
|
|
125
|
-
mutation: 'Media generation, voice enable/disable, TTS setting changes, and bundle export stay explicit confirmation-gated tool, setting, workspace, or slash-command flows.',
|
|
126
|
-
},
|
|
127
127
|
};
|
|
128
128
|
}
|
|
129
129
|
|
|
@@ -215,14 +215,14 @@ export async function mediaPostureSummary(context: CommandContext, args: AgentHa
|
|
|
215
215
|
returned: filtered.length,
|
|
216
216
|
total: providers.length,
|
|
217
217
|
policy: 'Read-only voice/media posture. Media generation, voice enable/disable, TTS setting changes, and bundle export stay confirmation-gated through first-class tools, settings modes, workspace actions, or slash-command mirrors.',
|
|
218
|
-
modelAccess: {
|
|
218
|
+
...(args.includeParameters === true ? { modelAccess: {
|
|
219
219
|
mediaGenerateTool: 'agent_media_generate',
|
|
220
220
|
providerCatalogMode: 'media_posture',
|
|
221
221
|
singleProviderMode: 'media_provider',
|
|
222
|
-
ttsProviderPicker: 'open_ui_surface surfaceId:"tts-provider-picker"',
|
|
223
|
-
ttsVoicePicker: 'open_ui_surface surfaceId:"tts-voice-picker"',
|
|
222
|
+
ttsProviderPicker: 'agent_harness mode:"open_ui_surface" surfaceId:"tts-provider-picker" confirm:true explicitUserRequest:"..."',
|
|
223
|
+
ttsVoicePicker: 'agent_harness mode:"open_ui_surface" surfaceId:"tts-voice-picker" confirm:true explicitUserRequest:"..."',
|
|
224
224
|
commands: ['/media providers', '/voice review', '/tts <prompt>', '/image <path>'],
|
|
225
|
-
},
|
|
225
|
+
} } : {}),
|
|
226
226
|
};
|
|
227
227
|
}
|
|
228
228
|
|
|
@@ -618,22 +618,40 @@ export function describeConnectedHostCapability(
|
|
|
618
618
|
return null;
|
|
619
619
|
}
|
|
620
620
|
|
|
621
|
-
export function connectedHostSummary(
|
|
621
|
+
export function connectedHostSummary(
|
|
622
|
+
context: CommandContext,
|
|
623
|
+
toolRegistry: ToolRegistry,
|
|
624
|
+
options: { readonly includeParameters?: boolean } = {},
|
|
625
|
+
): Record<string, unknown> {
|
|
622
626
|
const shellPaths = context.workspace.shellPaths;
|
|
623
627
|
const homeDirectory = shellPaths?.homeDirectory ?? context.platform.configManager.getHomeDirectory() ?? '';
|
|
624
628
|
const connection = resolveAgentConnectedHostConnection(context.platform.configManager, homeDirectory);
|
|
629
|
+
const routeFamilies = connectedHostRouteFamilies();
|
|
630
|
+
const capabilities = connectedHostCapabilityMap(toolRegistry);
|
|
631
|
+
const blockedCapabilities = blockedConnectedHostCapabilities();
|
|
625
632
|
return {
|
|
626
633
|
baseUrl: connection.baseUrl,
|
|
627
634
|
operatorToken: connection.token ? 'configured' : 'missing',
|
|
628
635
|
tokenPath: connection.tokenPath,
|
|
629
636
|
ownership: 'external-connected-host',
|
|
630
637
|
lifecycle: 'GoodVibes Agent can use public connected-host operator routes, but does not start, stop, restart, install, expose, or mutate the host listener.',
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
638
|
+
modes: {
|
|
639
|
+
servicePosture: 'service_posture/service_endpoint',
|
|
640
|
+
operatorMethods: 'operator_methods/operator_method',
|
|
641
|
+
liveStatus: 'connected_host_status',
|
|
642
|
+
capabilityDetail: 'connected_host_capability',
|
|
643
|
+
},
|
|
644
|
+
counts: {
|
|
645
|
+
routeFamilies: routeFamilies.length,
|
|
646
|
+
allowedCapabilities: capabilities.length,
|
|
647
|
+
availableCapabilities: capabilities.filter((capability) => capability.available === true).length,
|
|
648
|
+
blockedCapabilities: blockedCapabilities.length,
|
|
649
|
+
},
|
|
650
|
+
...(options.includeParameters === true ? {
|
|
651
|
+
routeFamilies,
|
|
652
|
+
capabilities,
|
|
653
|
+
blockedCapabilities,
|
|
654
|
+
} : {}),
|
|
637
655
|
};
|
|
638
656
|
}
|
|
639
657
|
|
|
@@ -320,19 +320,19 @@ function describeRoute(route: RouteCandidate, options: { readonly includeParamet
|
|
|
320
320
|
commands: route.commands,
|
|
321
321
|
uiSurfaces: route.uiSurfaces,
|
|
322
322
|
...(options.lookup ? { lookup: options.lookup } : {}),
|
|
323
|
-
policy: {
|
|
324
|
-
effect: 'read-only',
|
|
325
|
-
values: 'Model routing posture returns model ids, provider ids, route state, capabilities, and safe setting keys only; provider credentials are never returned.',
|
|
326
|
-
mutation: 'Model/provider selection, catalog refresh, favorites, custom provider edits, and route setting changes stay explicit user-facing picker, settings, workspace, or slash-command flows.',
|
|
327
|
-
},
|
|
328
323
|
...(options.includeParameters ? {
|
|
324
|
+
policy: {
|
|
325
|
+
effect: 'read-only',
|
|
326
|
+
values: 'Model routing posture returns model ids, provider ids, route state, capabilities, and safe setting keys only; provider credentials are never returned.',
|
|
327
|
+
mutation: 'Model/provider selection, catalog refresh, favorites, custom provider edits, and route setting changes stay explicit user-facing picker, settings, workspace, or slash-command flows.',
|
|
328
|
+
},
|
|
329
329
|
modelAccess: {
|
|
330
330
|
inspectRouting: 'agent_harness mode:"model_routing"',
|
|
331
331
|
inspectRoute: 'agent_harness mode:"model_route"',
|
|
332
332
|
settingRead: 'agent_harness mode:"get_setting"',
|
|
333
333
|
settingMutation: 'agent_harness mode:"set_setting" confirm:true explicitUserRequest:"..."',
|
|
334
|
-
openModelPicker: 'agent_harness mode:"open_ui_surface" surfaceId:"model-picker" confirm:true',
|
|
335
|
-
openProviderPicker: 'agent_harness mode:"open_ui_surface" surfaceId:"provider-picker" confirm:true',
|
|
334
|
+
openModelPicker: 'agent_harness mode:"open_ui_surface" surfaceId:"model-picker" confirm:true explicitUserRequest:"..."',
|
|
335
|
+
openProviderPicker: 'agent_harness mode:"open_ui_surface" surfaceId:"provider-picker" confirm:true explicitUserRequest:"..."',
|
|
336
336
|
},
|
|
337
337
|
} : {}),
|
|
338
338
|
};
|
|
@@ -350,19 +350,19 @@ function describeModel(model: ModelCandidate, options: { readonly includeParamet
|
|
|
350
350
|
pinned: model.pinned,
|
|
351
351
|
contextWindow: model.contextWindow,
|
|
352
352
|
reasoningEffort: model.reasoningEffort,
|
|
353
|
-
capabilities: model.capabilities,
|
|
354
353
|
...(options.lookup ? { lookup: options.lookup } : {}),
|
|
355
|
-
policy: {
|
|
356
|
-
effect: 'read-only',
|
|
357
|
-
mutation: 'Selecting this model stays a visible picker, settings, workspace, or slash-command flow with explicit user request.',
|
|
358
|
-
},
|
|
359
354
|
...(options.includeParameters ? {
|
|
355
|
+
capabilities: model.capabilities,
|
|
356
|
+
policy: {
|
|
357
|
+
effect: 'read-only',
|
|
358
|
+
mutation: 'Selecting this model stays a visible picker, settings, workspace, or slash-command flow with explicit user request.',
|
|
359
|
+
},
|
|
360
360
|
modelAccess: {
|
|
361
361
|
selectModelCommand: `/model ${model.registryKey}`,
|
|
362
362
|
selectProviderCommand: `/provider ${model.providerId}`,
|
|
363
363
|
pinCommand: `/pin ${model.registryKey}`,
|
|
364
364
|
unpinCommand: `/unpin ${model.registryKey}`,
|
|
365
|
-
setMainModel: `agent_harness mode:"set_setting" key:"provider.model" value:"${model.registryKey}" confirm:true`,
|
|
365
|
+
setMainModel: `agent_harness mode:"set_setting" key:"provider.model" value:"${model.registryKey}" confirm:true explicitUserRequest:"..."`,
|
|
366
366
|
},
|
|
367
367
|
} : {}),
|
|
368
368
|
};
|
|
@@ -433,7 +433,7 @@ export async function modelRoutingSummary(context: CommandContext, args: AgentHa
|
|
|
433
433
|
reasoningEffort: modelReasoning(currentModel),
|
|
434
434
|
capabilities: modelCapabilities(currentModel),
|
|
435
435
|
pinned: false,
|
|
436
|
-
}) : null,
|
|
436
|
+
}, { includeParameters }) : null,
|
|
437
437
|
},
|
|
438
438
|
providers: providerIds,
|
|
439
439
|
routes: filteredRoutes.slice(0, limit).map((route) => describeRoute(route, { includeParameters })),
|
|
@@ -25,6 +25,11 @@ function readLimit(value: unknown, fallback: number): number {
|
|
|
25
25
|
return Math.max(1, Math.min(500, Math.trunc(parsed)));
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
function previewText(value: string, maxLength = 120): string {
|
|
29
|
+
const normalized = value.replace(/\s+/g, ' ').trim();
|
|
30
|
+
return normalized.length <= maxLength ? normalized : `${normalized.slice(0, maxLength - 1).trimEnd()}...`;
|
|
31
|
+
}
|
|
32
|
+
|
|
28
33
|
function modelToolSearchText(tool: HarnessModelToolDefinition): string {
|
|
29
34
|
return [
|
|
30
35
|
tool.name,
|
|
@@ -45,7 +50,7 @@ function modelToolLookupFromArgs(args: AgentHarnessModelToolCatalogArgs): { read
|
|
|
45
50
|
function describeModelTool(tool: HarnessModelToolDefinition, options: { readonly includeParameters?: boolean; readonly lookup?: Record<string, unknown> } = {}): Record<string, unknown> {
|
|
46
51
|
return {
|
|
47
52
|
name: tool.name,
|
|
48
|
-
description: tool.description,
|
|
53
|
+
...(options.includeParameters ? { description: tool.description } : { summary: previewText(tool.description) }),
|
|
49
54
|
sideEffects: tool.sideEffects ?? [],
|
|
50
55
|
concurrency: tool.concurrency ?? 'parallel',
|
|
51
56
|
supportsProgress: tool.supportsProgress ?? false,
|
|
@@ -58,7 +63,7 @@ function describeModelTool(tool: HarnessModelToolDefinition, options: { readonly
|
|
|
58
63
|
function describeModelToolCandidates(tools: readonly HarnessModelToolDefinition[]): readonly Record<string, unknown>[] {
|
|
59
64
|
return tools.slice(0, 8).map((tool) => ({
|
|
60
65
|
toolName: tool.name,
|
|
61
|
-
|
|
66
|
+
summary: previewText(tool.description),
|
|
62
67
|
sideEffects: tool.sideEffects ?? [],
|
|
63
68
|
}));
|
|
64
69
|
}
|