@pellux/goodvibes-agent 1.0.31 → 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 +59 -2
- package/README.md +72 -58
- package/dist/package/main.js +7572 -2430
- package/docs/README.md +25 -21
- package/docs/channels-remote-and-api.md +6 -2
- package/docs/connected-host.md +27 -6
- package/docs/getting-started.md +87 -66
- package/docs/knowledge-artifacts-and-multimodal.md +16 -4
- package/docs/project-planning.md +2 -2
- package/docs/providers-and-routing.md +3 -2
- package/docs/release-and-publishing.md +15 -11
- package/docs/tools-and-commands.md +150 -128
- package/docs/voice-and-live-tts.md +1 -1
- package/package.json +8 -3
- package/release/live-verification/live-verification.json +148 -0
- package/release/live-verification/live-verification.md +187 -0
- package/release/performance-snapshot.json +57 -0
- package/release/release-notes.md +19 -0
- package/release/release-readiness.json +581 -0
- package/src/agent/harness-control.ts +42 -3
- package/src/cli/agent-knowledge-command.ts +5 -5
- package/src/cli/agent-knowledge-format.ts +11 -0
- package/src/cli/agent-knowledge-runtime.ts +92 -13
- package/src/cli/bundle-command.ts +5 -4
- package/src/cli/entrypoint.ts +5 -2
- package/src/cli/external-runtime.ts +2 -15
- package/src/cli/management.ts +4 -3
- package/src/input/commands/guidance-runtime.ts +1 -1
- package/src/input/commands/knowledge.ts +2 -2
- package/src/runtime/bootstrap.ts +10 -18
- package/src/runtime/connected-host-auth.ts +16 -0
- package/src/tools/agent-analysis-registry-policy.ts +2 -9
- package/src/tools/agent-channel-send-tool.ts +3 -9
- 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 +177 -0
- 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 +9 -3
- package/src/tools/agent-harness-delegation-posture.ts +216 -0
- package/src/tools/agent-harness-keybinding-metadata.ts +57 -22
- package/src/tools/agent-harness-mcp-metadata.ts +248 -0
- package/src/tools/agent-harness-media-posture.ts +282 -0
- package/src/tools/agent-harness-metadata.ts +44 -9
- package/src/tools/agent-harness-model-routing.ts +501 -0
- package/src/tools/agent-harness-model-tool-catalog.ts +7 -2
- package/src/tools/agent-harness-notification-metadata.ts +217 -0
- package/src/tools/agent-harness-operator-methods.ts +285 -0
- package/src/tools/agent-harness-pairing-posture.ts +265 -0
- package/src/tools/agent-harness-panel-metadata.ts +26 -12
- package/src/tools/agent-harness-provider-account-metadata.ts +205 -0
- package/src/tools/agent-harness-release-evidence.ts +364 -0
- package/src/tools/agent-harness-release-readiness.ts +298 -0
- package/src/tools/agent-harness-security-posture.ts +648 -0
- package/src/tools/agent-harness-service-posture.ts +207 -0
- package/src/tools/agent-harness-session-metadata.ts +284 -0
- package/src/tools/agent-harness-setup-posture.ts +295 -0
- package/src/tools/agent-harness-tool-schema.ts +104 -27
- package/src/tools/agent-harness-tool.ts +251 -235
- package/src/tools/agent-harness-ui-surface-metadata.ts +20 -12
- package/src/tools/agent-harness-workspace-actions.ts +260 -0
- package/src/tools/agent-knowledge-ingest-tool.ts +4 -10
- package/src/tools/agent-knowledge-tool.ts +120 -25
- package/src/tools/agent-local-registry-tool.ts +3 -7
- package/src/tools/agent-media-generate-tool.ts +2 -8
- package/src/tools/agent-notify-tool.ts +3 -8
- package/src/tools/agent-operator-action-tool.ts +4 -10
- package/src/tools/agent-operator-briefing-tool.ts +1 -6
- package/src/tools/agent-read-policy.ts +1 -4
- package/src/tools/agent-reminder-schedule-tool.ts +4 -9
- package/src/tools/agent-tool-policy-guard.ts +15 -51
- package/src/tools/agent-web-search-policy.ts +1 -4
- package/src/tools/agent-work-plan-tool.ts +1 -6
- package/src/version.ts +2 -2
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { CommandContext } from '../input/command-registry.ts';
|
|
2
|
+
import type { AgentWorkspaceChannelStatus } from '../input/agent-workspace-channels.ts';
|
|
3
|
+
import { buildAgentWorkspaceChannels } from '../input/agent-workspace-channels.ts';
|
|
4
|
+
|
|
5
|
+
export interface AgentHarnessChannelArgs {
|
|
6
|
+
readonly channelId?: unknown;
|
|
7
|
+
readonly target?: unknown;
|
|
8
|
+
readonly query?: unknown;
|
|
9
|
+
readonly includeParameters?: unknown;
|
|
10
|
+
readonly limit?: unknown;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type ChannelLookupSource = 'channelId' | 'target' | 'query';
|
|
14
|
+
|
|
15
|
+
type ChannelResolution =
|
|
16
|
+
| { readonly status: 'found'; readonly channel: Record<string, unknown> }
|
|
17
|
+
| { readonly status: 'ambiguous'; readonly input: string; readonly candidates: readonly Record<string, unknown>[] }
|
|
18
|
+
| { readonly status: 'missing_lookup'; readonly usage: string };
|
|
19
|
+
|
|
20
|
+
function readString(value: unknown): string {
|
|
21
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function readLimit(value: unknown, fallback: number): number {
|
|
25
|
+
const parsed = typeof value === 'string' && value.trim() ? Number(value) : value;
|
|
26
|
+
if (typeof parsed !== 'number' || !Number.isFinite(parsed)) return fallback;
|
|
27
|
+
return Math.max(1, Math.min(500, Math.trunc(parsed)));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function channelLookupFromArgs(args: AgentHarnessChannelArgs): { readonly source: ChannelLookupSource; readonly input: string } | null {
|
|
31
|
+
const channelId = readString(args.channelId);
|
|
32
|
+
if (channelId) return { source: 'channelId', input: channelId };
|
|
33
|
+
const target = readString(args.target);
|
|
34
|
+
if (target) return { source: 'target', input: target };
|
|
35
|
+
const query = readString(args.query);
|
|
36
|
+
return query ? { source: 'query', input: query } : null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function channelSearchText(channel: AgentWorkspaceChannelStatus): string {
|
|
40
|
+
return [
|
|
41
|
+
channel.id,
|
|
42
|
+
channel.label,
|
|
43
|
+
channel.delivery,
|
|
44
|
+
channel.risk,
|
|
45
|
+
channel.riskLabel,
|
|
46
|
+
channel.setupState,
|
|
47
|
+
channel.nextStep,
|
|
48
|
+
...channel.requiredKeys,
|
|
49
|
+
...channel.missingRequiredKeys,
|
|
50
|
+
...channel.defaultTargetKeys,
|
|
51
|
+
...channel.configuredDefaultTargetKeys,
|
|
52
|
+
].join('\n').toLowerCase();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function describeChannelCandidate(channel: AgentWorkspaceChannelStatus): Record<string, unknown> {
|
|
56
|
+
return {
|
|
57
|
+
channelId: channel.id,
|
|
58
|
+
label: channel.label,
|
|
59
|
+
setupState: channel.setupState,
|
|
60
|
+
ready: channel.ready,
|
|
61
|
+
delivery: channel.delivery,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function describeChannel(
|
|
66
|
+
channel: AgentWorkspaceChannelStatus,
|
|
67
|
+
options: { readonly includeParameters?: boolean; readonly lookup?: Record<string, unknown> } = {},
|
|
68
|
+
): Record<string, unknown> {
|
|
69
|
+
return {
|
|
70
|
+
id: channel.id,
|
|
71
|
+
label: channel.label,
|
|
72
|
+
enabled: channel.enabled,
|
|
73
|
+
ready: channel.ready,
|
|
74
|
+
setupState: channel.setupState,
|
|
75
|
+
delivery: channel.delivery,
|
|
76
|
+
risk: channel.risk,
|
|
77
|
+
riskLabel: channel.riskLabel,
|
|
78
|
+
missingConfigCount: channel.missingConfigCount,
|
|
79
|
+
defaultTarget: channel.defaultTarget,
|
|
80
|
+
nextStep: channel.nextStep,
|
|
81
|
+
requiredConfigKeys: channel.requiredKeys,
|
|
82
|
+
missingConfigKeys: channel.missingRequiredKeys,
|
|
83
|
+
defaultTargetKeys: channel.defaultTargetKeys,
|
|
84
|
+
configuredDefaultTargetKeys: channel.configuredDefaultTargetKeys,
|
|
85
|
+
...(options.lookup ? { lookup: options.lookup } : {}),
|
|
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
|
+
deliveryTargetShape: 'surface[:route[:label]]',
|
|
108
|
+
exampleTarget: `${channel.id}:route:Label`,
|
|
109
|
+
},
|
|
110
|
+
} : {}),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function channelReadinessCatalogStatus(context: CommandContext): Record<string, unknown> {
|
|
115
|
+
const channels = buildAgentWorkspaceChannels(context);
|
|
116
|
+
return {
|
|
117
|
+
modes: ['channels', 'channel'],
|
|
118
|
+
channels: channels.length,
|
|
119
|
+
enabled: channels.filter((channel) => channel.enabled).length,
|
|
120
|
+
ready: channels.filter((channel) => channel.ready).length,
|
|
121
|
+
attention: channels.filter((channel) => channel.enabled && channel.setupState !== 'ready').length,
|
|
122
|
+
readOnly: true,
|
|
123
|
+
deliveryTool: 'agent_channel_send',
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function listHarnessChannels(context: CommandContext, args: AgentHarnessChannelArgs): Record<string, unknown> {
|
|
128
|
+
const query = readString(args.query).toLowerCase();
|
|
129
|
+
const includeParameters = args.includeParameters === true;
|
|
130
|
+
const limit = readLimit(args.limit, 200);
|
|
131
|
+
const channels = buildAgentWorkspaceChannels(context);
|
|
132
|
+
const filtered = channels
|
|
133
|
+
.filter((channel) => !query || channelSearchText(channel).includes(query))
|
|
134
|
+
.slice(0, limit);
|
|
135
|
+
return {
|
|
136
|
+
channels: filtered.map((channel) => describeChannel(channel, { includeParameters })),
|
|
137
|
+
returned: filtered.length,
|
|
138
|
+
total: channels.length,
|
|
139
|
+
enabled: channels.filter((channel) => channel.enabled).length,
|
|
140
|
+
ready: channels.filter((channel) => channel.ready).length,
|
|
141
|
+
attention: channels.filter((channel) => channel.enabled && channel.setupState !== 'ready').length,
|
|
142
|
+
policy: 'Read-only channel readiness catalog. It returns key names, setup state, delivery posture, and model routes without printing secrets or sending messages.',
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function describeHarnessChannel(context: CommandContext, args: AgentHarnessChannelArgs): ChannelResolution {
|
|
147
|
+
const lookup = channelLookupFromArgs(args);
|
|
148
|
+
if (!lookup) {
|
|
149
|
+
return {
|
|
150
|
+
status: 'missing_lookup',
|
|
151
|
+
usage: 'channel requires channelId, target, or query. Use mode:"channels" to inspect available channel ids.',
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
const channels = buildAgentWorkspaceChannels(context);
|
|
155
|
+
const normalized = lookup.input.toLowerCase();
|
|
156
|
+
const exact = channels.find((channel) => channel.id === lookup.input);
|
|
157
|
+
if (exact) return { status: 'found', channel: describeChannel(exact, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'id' } }) };
|
|
158
|
+
const insensitiveId = channels.find((channel) => channel.id.toLowerCase() === normalized);
|
|
159
|
+
if (insensitiveId) return { status: 'found', channel: describeChannel(insensitiveId, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'case-insensitive-id' } }) };
|
|
160
|
+
const insensitiveLabel = channels.find((channel) => channel.label.toLowerCase() === normalized);
|
|
161
|
+
if (insensitiveLabel) return { status: 'found', channel: describeChannel(insensitiveLabel, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'case-insensitive-label' } }) };
|
|
162
|
+
const searched = channels.filter((channel) => channelSearchText(channel).includes(normalized));
|
|
163
|
+
if (searched.length === 1) {
|
|
164
|
+
return { status: 'found', channel: describeChannel(searched[0]!, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'search' } }) };
|
|
165
|
+
}
|
|
166
|
+
if (searched.length > 1) {
|
|
167
|
+
return {
|
|
168
|
+
status: 'ambiguous',
|
|
169
|
+
input: lookup.input,
|
|
170
|
+
candidates: searched.slice(0, 8).map(describeChannelCandidate),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
status: 'missing_lookup',
|
|
175
|
+
usage: `Unknown channel ${lookup.input}. Use mode:"channels" to inspect available channel ids.`,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -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
|
-
|
|
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: {
|
|
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
|
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import type { CommandContext } from '../input/command-registry.ts';
|
|
2
|
+
|
|
3
|
+
export interface AgentHarnessDelegationArgs {
|
|
4
|
+
readonly delegationRouteId?: unknown;
|
|
5
|
+
readonly target?: unknown;
|
|
6
|
+
readonly query?: unknown;
|
|
7
|
+
readonly includeParameters?: unknown;
|
|
8
|
+
readonly limit?: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type DelegationResolution =
|
|
12
|
+
| { readonly status: 'found'; readonly route: Record<string, unknown> }
|
|
13
|
+
| { readonly status: 'ambiguous'; readonly input: string; readonly candidates: readonly Record<string, unknown>[] }
|
|
14
|
+
| { readonly status: 'missing_lookup'; readonly usage: string };
|
|
15
|
+
|
|
16
|
+
type DelegationLookupSource = 'delegationRouteId' | 'target' | 'query';
|
|
17
|
+
|
|
18
|
+
interface DelegationRoute {
|
|
19
|
+
readonly id: string;
|
|
20
|
+
readonly label: string;
|
|
21
|
+
readonly detail: string;
|
|
22
|
+
readonly effect: 'read-only' | 'main-conversation' | 'delegated-work' | 'blocked';
|
|
23
|
+
readonly command?: string;
|
|
24
|
+
readonly commandTemplate?: string;
|
|
25
|
+
readonly workspaceActionId?: string;
|
|
26
|
+
readonly confirmationRequired?: boolean;
|
|
27
|
+
readonly reviewPolicy?: 'explicit-only' | 'not-applicable';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function readString(value: unknown): string {
|
|
31
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function readLimit(value: unknown, fallback: number): number {
|
|
35
|
+
const parsed = typeof value === 'string' && value.trim() ? Number(value) : value;
|
|
36
|
+
if (typeof parsed !== 'number' || !Number.isFinite(parsed)) return fallback;
|
|
37
|
+
return Math.max(1, Math.min(500, Math.trunc(parsed)));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function lookupFromArgs(args: AgentHarnessDelegationArgs): { readonly source: DelegationLookupSource; readonly input: string } | null {
|
|
41
|
+
const delegationRouteId = readString(args.delegationRouteId);
|
|
42
|
+
if (delegationRouteId) return { source: 'delegationRouteId', input: delegationRouteId };
|
|
43
|
+
const target = readString(args.target);
|
|
44
|
+
if (target) return { source: 'target', input: target };
|
|
45
|
+
const query = readString(args.query);
|
|
46
|
+
return query ? { source: 'query', input: query } : null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function routes(): readonly DelegationRoute[] {
|
|
50
|
+
return [
|
|
51
|
+
{
|
|
52
|
+
id: 'delegate-build-task',
|
|
53
|
+
label: 'Delegate build/fix/review task',
|
|
54
|
+
detail: 'Send one explicit build, fix, implementation, patch, or review task to GoodVibes TUI/shared-session routes with the full original ask.',
|
|
55
|
+
effect: 'delegated-work',
|
|
56
|
+
commandTemplate: '/delegate <full original user ask>',
|
|
57
|
+
workspaceActionId: 'delegate-task',
|
|
58
|
+
confirmationRequired: true,
|
|
59
|
+
reviewPolicy: 'explicit-only',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 'delegate-build-task-with-review',
|
|
63
|
+
label: 'Delegate with requested review',
|
|
64
|
+
detail: 'Request delegated review only when the user explicitly asks for review or explicitly requests review as part of a build/fix/review handoff.',
|
|
65
|
+
effect: 'delegated-work',
|
|
66
|
+
commandTemplate: '/delegate --review <full original user ask>',
|
|
67
|
+
workspaceActionId: 'delegate-task',
|
|
68
|
+
confirmationRequired: true,
|
|
69
|
+
reviewPolicy: 'explicit-only',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: 'delegation-status',
|
|
73
|
+
label: 'Delegation status',
|
|
74
|
+
detail: 'Inspect build-delegation receipts and shared-session status without starting coding work.',
|
|
75
|
+
effect: 'read-only',
|
|
76
|
+
command: '/delegate status',
|
|
77
|
+
workspaceActionId: 'delegation-status',
|
|
78
|
+
reviewPolicy: 'not-applicable',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: 'ordinary-agent-work',
|
|
82
|
+
label: 'Ordinary Agent work',
|
|
83
|
+
detail: 'Planning, research, operations, memory, configuration, approvals, automation observability, media generation, and ordinary assistant work stay in the main Agent conversation or first-class Agent tools.',
|
|
84
|
+
effect: 'main-conversation',
|
|
85
|
+
reviewPolicy: 'not-applicable',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: 'local-coding-blocked',
|
|
89
|
+
label: 'Local coding execution blocked in Agent',
|
|
90
|
+
detail: 'Agent does not own file edits, git/worktree workflows, execution isolation UX, coding panels, or local delegated review chains.',
|
|
91
|
+
effect: 'blocked',
|
|
92
|
+
reviewPolicy: 'not-applicable',
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function routeSearchText(route: DelegationRoute): string {
|
|
98
|
+
return [
|
|
99
|
+
route.id,
|
|
100
|
+
route.label,
|
|
101
|
+
route.detail,
|
|
102
|
+
route.effect,
|
|
103
|
+
route.command ?? '',
|
|
104
|
+
route.commandTemplate ?? '',
|
|
105
|
+
route.workspaceActionId ?? '',
|
|
106
|
+
route.reviewPolicy ?? '',
|
|
107
|
+
].join('\n').toLowerCase();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function describeCandidate(route: DelegationRoute): Record<string, unknown> {
|
|
111
|
+
return {
|
|
112
|
+
delegationRouteId: route.id,
|
|
113
|
+
label: route.label,
|
|
114
|
+
effect: route.effect,
|
|
115
|
+
confirmationRequired: route.confirmationRequired === true,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function describeRoute(
|
|
120
|
+
context: CommandContext,
|
|
121
|
+
route: DelegationRoute,
|
|
122
|
+
options: { readonly includeParameters?: boolean; readonly lookup?: Record<string, unknown> } = {},
|
|
123
|
+
): Record<string, unknown> {
|
|
124
|
+
return {
|
|
125
|
+
delegationRouteId: route.id,
|
|
126
|
+
label: route.label,
|
|
127
|
+
detail: route.detail,
|
|
128
|
+
effect: route.effect,
|
|
129
|
+
...(route.command ? { command: route.command } : {}),
|
|
130
|
+
...(route.commandTemplate ? { commandTemplate: route.commandTemplate } : {}),
|
|
131
|
+
...(route.workspaceActionId ? { workspaceActionId: route.workspaceActionId } : {}),
|
|
132
|
+
confirmationRequired: route.confirmationRequired === true,
|
|
133
|
+
reviewPolicy: route.reviewPolicy ?? 'not-applicable',
|
|
134
|
+
runtime: {
|
|
135
|
+
sessionId: context.session.runtime.sessionId,
|
|
136
|
+
operatorClientAttached: Boolean(context.clients?.operator),
|
|
137
|
+
},
|
|
138
|
+
...(options.lookup ? { lookup: options.lookup } : {}),
|
|
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
|
+
modelAccess: {
|
|
146
|
+
inspectDelegation: 'agent_harness mode:"delegation_posture"',
|
|
147
|
+
inspectRoute: 'agent_harness mode:"delegation_route"',
|
|
148
|
+
workspaceAction: route.workspaceActionId ? `agent_harness mode:"workspace_action" actionId:"${route.workspaceActionId}"` : null,
|
|
149
|
+
runWorkspaceAction: route.workspaceActionId ? `agent_harness mode:"run_workspace_action" actionId:"${route.workspaceActionId}" confirm:true explicitUserRequest:"..."` : null,
|
|
150
|
+
runCommand: route.command ? `agent_harness mode:"run_command" command:"${route.command}" confirm:true explicitUserRequest:"..."` : null,
|
|
151
|
+
runCommandTemplate: route.commandTemplate ? `agent_harness mode:"run_command" command:"${route.commandTemplate}" confirm:true explicitUserRequest:"..."` : null,
|
|
152
|
+
},
|
|
153
|
+
} : {}),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function delegationPostureCatalogStatus(context: CommandContext): Record<string, unknown> {
|
|
158
|
+
return {
|
|
159
|
+
modes: ['delegation_posture', 'delegation_route'],
|
|
160
|
+
routes: routes().length,
|
|
161
|
+
operatorClientAttached: Boolean(context.clients?.operator),
|
|
162
|
+
readOnly: true,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function delegationPostureSummary(context: CommandContext, args: AgentHarnessDelegationArgs): Record<string, unknown> {
|
|
167
|
+
const query = readString(args.query).toLowerCase();
|
|
168
|
+
const includeParameters = args.includeParameters === true;
|
|
169
|
+
const filtered = routes()
|
|
170
|
+
.filter((route) => !query || routeSearchText(route).includes(query))
|
|
171
|
+
.slice(0, readLimit(args.limit, 100));
|
|
172
|
+
return {
|
|
173
|
+
status: 'available',
|
|
174
|
+
summary: {
|
|
175
|
+
routes: routes().length,
|
|
176
|
+
operatorClientAttached: Boolean(context.clients?.operator),
|
|
177
|
+
sessionId: context.session.runtime.sessionId,
|
|
178
|
+
delegatedReviewPolicy: 'explicit-build-delegation-only',
|
|
179
|
+
normalChatPolicy: 'ordinary assistant work stays in the main Agent conversation',
|
|
180
|
+
codingOwnership: 'GoodVibes TUI owns file edits, git/worktree workflows, execution isolation UX, coding panels, and delegated review coordination',
|
|
181
|
+
},
|
|
182
|
+
routes: filtered.map((route) => describeRoute(context, route, { includeParameters })),
|
|
183
|
+
returned: filtered.length,
|
|
184
|
+
total: routes().length,
|
|
185
|
+
policy: 'Read-only delegation posture. Delegation submission requires an explicit build/fix/review/implementation user request, visible confirmation, and preservation of the full original ask.',
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function describeHarnessDelegationRoute(context: CommandContext, args: AgentHarnessDelegationArgs): DelegationResolution {
|
|
190
|
+
const lookup = lookupFromArgs(args);
|
|
191
|
+
if (!lookup) {
|
|
192
|
+
return {
|
|
193
|
+
status: 'missing_lookup',
|
|
194
|
+
usage: 'delegation_route requires delegationRouteId, target, or query. Use mode:"delegation_posture" to inspect delegation route ids.',
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
const all = routes();
|
|
198
|
+
const normalized = lookup.input.toLowerCase();
|
|
199
|
+
const exact = all.find((route) => route.id === lookup.input);
|
|
200
|
+
if (exact) return { status: 'found', route: describeRoute(context, exact, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'id' } }) };
|
|
201
|
+
const insensitive = all.find((route) => route.id.toLowerCase() === normalized);
|
|
202
|
+
if (insensitive) return { status: 'found', route: describeRoute(context, insensitive, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'case-insensitive-id' } }) };
|
|
203
|
+
const searched = all.filter((route) => routeSearchText(route).includes(normalized));
|
|
204
|
+
if (searched.length === 1) return { status: 'found', route: describeRoute(context, searched[0]!, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'search' } }) };
|
|
205
|
+
if (searched.length > 1) {
|
|
206
|
+
return {
|
|
207
|
+
status: 'ambiguous',
|
|
208
|
+
input: lookup.input,
|
|
209
|
+
candidates: searched.slice(0, 8).map(describeCandidate),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
status: 'missing_lookup',
|
|
214
|
+
usage: `Unknown delegation route ${lookup.input}. Use mode:"delegation_posture" to inspect delegation route ids.`,
|
|
215
|
+
};
|
|
216
|
+
}
|