@pellux/goodvibes-agent 0.1.20 → 0.1.22
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 +8 -0
- package/package.json +1 -1
- package/src/tools/wrfc-agent-guard.ts +68 -0
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GoodVibes Agent will be recorded here.
|
|
4
4
|
|
|
5
|
+
## 0.1.22 - 2026-05-31
|
|
6
|
+
|
|
7
|
+
- 07e4445 Mark control tool read-only in agent runtime
|
|
8
|
+
|
|
9
|
+
## 0.1.21 - 2026-05-31
|
|
10
|
+
|
|
11
|
+
- d83f325 Keep inspect scaffold dry-run-only in agent runtime
|
|
12
|
+
|
|
5
13
|
## 0.1.20 - 2026-05-31
|
|
6
14
|
|
|
7
15
|
- c0eca13 Block settings mutation tool in agent runtime
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.22",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Near-fork GoodVibes operator assistant with the GoodVibes TUI shell, renderer, input, fullscreen workspace, and daemon-connected Agent product brain.",
|
|
6
6
|
"type": "module",
|
|
@@ -54,6 +54,12 @@ type StateToolArgs = {
|
|
|
54
54
|
readonly [key: string]: unknown;
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
+
type InspectToolArgs = {
|
|
58
|
+
readonly mode?: unknown;
|
|
59
|
+
readonly dryRun?: unknown;
|
|
60
|
+
readonly [key: string]: unknown;
|
|
61
|
+
};
|
|
62
|
+
|
|
57
63
|
type AgentToolPolicyGuardOptions = {
|
|
58
64
|
readonly getLastUserMessage?: () => string | null;
|
|
59
65
|
};
|
|
@@ -90,6 +96,7 @@ const READ_ONLY_TEAM_TOOL_MODES = ['list', 'show'] as const;
|
|
|
90
96
|
const READ_ONLY_WORKLIST_TOOL_MODES = ['list', 'show'] as const;
|
|
91
97
|
const READ_ONLY_PACKET_TOOL_MODES = ['list', 'show'] as const;
|
|
92
98
|
const READ_ONLY_QUERY_TOOL_MODES = ['list', 'show'] as const;
|
|
99
|
+
const READ_ONLY_CONTROL_TOOL_MODES = ['commands', 'panels', 'subscriptions', 'sandbox-presets'] as const;
|
|
93
100
|
const READ_ONLY_REMOTE_TOOL_MODE_SET = new Set<string>(READ_ONLY_REMOTE_TOOL_MODES);
|
|
94
101
|
const READ_ONLY_CHANNEL_TOOL_MODE_SET = new Set<string>(READ_ONLY_CHANNEL_TOOL_MODES);
|
|
95
102
|
const READ_ONLY_MCP_TOOL_MODE_SET = new Set<string>(READ_ONLY_MCP_TOOL_MODES);
|
|
@@ -104,6 +111,7 @@ const READ_ONLY_TEAM_TOOL_MODE_SET = new Set<string>(READ_ONLY_TEAM_TOOL_MODES);
|
|
|
104
111
|
const READ_ONLY_WORKLIST_TOOL_MODE_SET = new Set<string>(READ_ONLY_WORKLIST_TOOL_MODES);
|
|
105
112
|
const READ_ONLY_PACKET_TOOL_MODE_SET = new Set<string>(READ_ONLY_PACKET_TOOL_MODES);
|
|
106
113
|
const READ_ONLY_QUERY_TOOL_MODE_SET = new Set<string>(READ_ONLY_QUERY_TOOL_MODES);
|
|
114
|
+
const READ_ONLY_CONTROL_TOOL_MODE_SET = new Set<string>(READ_ONLY_CONTROL_TOOL_MODES);
|
|
107
115
|
|
|
108
116
|
const LOCAL_AGENT_DENIAL = [
|
|
109
117
|
'GoodVibes Agent does not spawn local Engineer/Reviewer/Tester/Verifier roots or run local WRFC chains.',
|
|
@@ -159,12 +167,24 @@ const SETTINGS_MUTATION_DENIAL = [
|
|
|
159
167
|
'Secrets, tokens, passwords, daemon lifecycle settings, and service exposure settings require explicit user action outside the model tool surface.',
|
|
160
168
|
].join(' ');
|
|
161
169
|
|
|
170
|
+
const INSPECT_WRITE_DENIAL = [
|
|
171
|
+
'GoodVibes Agent only uses inspect scaffold mode for dry-run planning from the main conversation.',
|
|
172
|
+
'File scaffolding and code creation are disabled in the Agent model tool surface.',
|
|
173
|
+
'Delegate explicit build/implement/fix/review work to GoodVibes TUI instead.',
|
|
174
|
+
].join(' ');
|
|
175
|
+
|
|
162
176
|
const DURABLE_WORKFLOW_MUTATION_DENIAL = [
|
|
163
177
|
'GoodVibes Agent only inspects copied durable workflow tools from the main conversation.',
|
|
164
178
|
'Task, team, worklist, packet, and query creation or lifecycle mutation is disabled here.',
|
|
165
179
|
'Use explicit Agent CLI/slash commands or GoodVibes TUI delegation for intentional workflow changes.',
|
|
166
180
|
].join(' ');
|
|
167
181
|
|
|
182
|
+
const CONTROL_MUTATION_DENIAL = [
|
|
183
|
+
'GoodVibes Agent only inspects copied product-control surfaces from the main conversation.',
|
|
184
|
+
'Product-control mutation, daemon lifecycle, and service posture changes are disabled here.',
|
|
185
|
+
'Use explicit Agent CLI/slash commands for Agent-owned changes, and keep daemon lifecycle external.',
|
|
186
|
+
].join(' ');
|
|
187
|
+
|
|
168
188
|
export function installAgentToolPolicyGuard(registry: ToolRegistry, options: AgentToolPolicyGuardOptions = {}): void {
|
|
169
189
|
const agentTool = registry.list().find((tool) => tool.definition.name === 'agent');
|
|
170
190
|
if (!agentTool) throw new Error('Agent tool policy guard could not find the agent tool.');
|
|
@@ -200,6 +220,18 @@ export function installAgentToolPolicyGuard(registry: ToolRegistry, options: Age
|
|
|
200
220
|
wrapStateToolForAgentPolicy(tool);
|
|
201
221
|
} else if (tool.definition.name === 'goodvibes_settings') {
|
|
202
222
|
wrapBlockedSettingsToolForAgentPolicy(tool);
|
|
223
|
+
} else if (tool.definition.name === 'inspect') {
|
|
224
|
+
wrapInspectToolForAgentPolicy(tool);
|
|
225
|
+
} else if (tool.definition.name === 'control') {
|
|
226
|
+
wrapModeRestrictedToolForAgentPolicy(tool, {
|
|
227
|
+
allowedModes: READ_ONLY_CONTROL_TOOL_MODES,
|
|
228
|
+
modeSet: READ_ONLY_CONTROL_TOOL_MODE_SET,
|
|
229
|
+
description: [
|
|
230
|
+
'Read-only product-control inspection for GoodVibes Agent.',
|
|
231
|
+
'Command, panel, subscription, and sandbox preset catalogs can be inspected, but product-control mutation and daemon lifecycle are external.',
|
|
232
|
+
].join(' '),
|
|
233
|
+
denial: CONTROL_MUTATION_DENIAL,
|
|
234
|
+
});
|
|
203
235
|
} else if (tool.definition.name === 'task') {
|
|
204
236
|
wrapModeRestrictedToolForAgentPolicy(tool, {
|
|
205
237
|
allowedModes: READ_ONLY_TASK_TOOL_MODES,
|
|
@@ -320,6 +352,17 @@ export function wrapBlockedSettingsToolForAgentPolicy(tool: Tool): void {
|
|
|
320
352
|
tool.execute = async () => ({ success: false, error: SETTINGS_MUTATION_DENIAL });
|
|
321
353
|
}
|
|
322
354
|
|
|
355
|
+
export function wrapInspectToolForAgentPolicy(tool: Tool): void {
|
|
356
|
+
narrowInspectToolDefinitionForAgentPolicy(tool);
|
|
357
|
+
const originalExecute = tool.execute.bind(tool);
|
|
358
|
+
tool.execute = async (args) => {
|
|
359
|
+
const inspectArgs = args as InspectToolArgs;
|
|
360
|
+
const denial = validateInspectToolInvocationForAgentPolicy(inspectArgs);
|
|
361
|
+
if (denial) return { success: false, error: denial };
|
|
362
|
+
return originalExecute(normalizeInspectToolInvocationForAgentPolicy(inspectArgs) as Parameters<Tool['execute']>[0]);
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
323
366
|
export function validateExecToolInvocationForAgentPolicy(args: ExecToolArgs): string | null {
|
|
324
367
|
if (args.parallel === true) return BACKGROUND_EXEC_DENIAL;
|
|
325
368
|
if (Array.isArray(args.file_ops) && args.file_ops.length > 0) return BACKGROUND_EXEC_DENIAL;
|
|
@@ -404,6 +447,16 @@ export function validateStateToolInvocationForAgentPolicy(args: StateToolArgs):
|
|
|
404
447
|
return null;
|
|
405
448
|
}
|
|
406
449
|
|
|
450
|
+
export function validateInspectToolInvocationForAgentPolicy(args: InspectToolArgs): string | null {
|
|
451
|
+
if (args.mode === 'scaffold' && args.dryRun === false) return INSPECT_WRITE_DENIAL;
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
export function normalizeInspectToolInvocationForAgentPolicy(args: InspectToolArgs): InspectToolArgs {
|
|
456
|
+
if (args.mode !== 'scaffold') return args;
|
|
457
|
+
return { ...args, dryRun: true };
|
|
458
|
+
}
|
|
459
|
+
|
|
407
460
|
type ModeRestrictedToolPolicy = {
|
|
408
461
|
readonly allowedModes: readonly string[];
|
|
409
462
|
readonly modeSet: ReadonlySet<string>;
|
|
@@ -470,13 +523,16 @@ export const AGENT_READ_ONLY_TEAM_TOOL_MODES = READ_ONLY_TEAM_TOOL_MODES;
|
|
|
470
523
|
export const AGENT_READ_ONLY_WORKLIST_TOOL_MODES = READ_ONLY_WORKLIST_TOOL_MODES;
|
|
471
524
|
export const AGENT_READ_ONLY_PACKET_TOOL_MODES = READ_ONLY_PACKET_TOOL_MODES;
|
|
472
525
|
export const AGENT_READ_ONLY_QUERY_TOOL_MODES = READ_ONLY_QUERY_TOOL_MODES;
|
|
526
|
+
export const AGENT_READ_ONLY_CONTROL_TOOL_MODES = READ_ONLY_CONTROL_TOOL_MODES;
|
|
473
527
|
export const AGENT_REMOTE_MUTATION_DENIAL_MESSAGE = REMOTE_MUTATION_DENIAL;
|
|
474
528
|
export const AGENT_CHANNEL_ACTION_DENIAL_MESSAGE = CHANNEL_ACTION_DENIAL;
|
|
475
529
|
export const AGENT_MCP_SECURITY_MUTATION_DENIAL_MESSAGE = MCP_SECURITY_MUTATION_DENIAL;
|
|
476
530
|
export const AGENT_FETCH_NETWORK_MUTATION_DENIAL_MESSAGE = FETCH_NETWORK_MUTATION_DENIAL;
|
|
477
531
|
export const AGENT_STATE_MUTATION_DENIAL_MESSAGE = STATE_MUTATION_DENIAL;
|
|
478
532
|
export const AGENT_SETTINGS_MUTATION_DENIAL_MESSAGE = SETTINGS_MUTATION_DENIAL;
|
|
533
|
+
export const AGENT_INSPECT_WRITE_DENIAL_MESSAGE = INSPECT_WRITE_DENIAL;
|
|
479
534
|
export const AGENT_DURABLE_WORKFLOW_MUTATION_DENIAL_MESSAGE = DURABLE_WORKFLOW_MUTATION_DENIAL;
|
|
535
|
+
export const AGENT_CONTROL_MUTATION_DENIAL_MESSAGE = CONTROL_MUTATION_DENIAL;
|
|
480
536
|
|
|
481
537
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
482
538
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
@@ -610,8 +666,20 @@ function narrowStateToolDefinitionForAgentPolicy(tool: Tool): void {
|
|
|
610
666
|
narrowStringEnumProperty(properties, 'analyticsAction', READ_ONLY_STATE_ANALYTICS_ACTIONS, 'Read-only analytics actions allowed by GoodVibes Agent.');
|
|
611
667
|
}
|
|
612
668
|
|
|
669
|
+
function narrowInspectToolDefinitionForAgentPolicy(tool: Tool): void {
|
|
670
|
+
tool.definition.description = [
|
|
671
|
+
'Inspect and analyze project structure for GoodVibes Agent.',
|
|
672
|
+
'Scaffold mode is dry-run-only in the main conversation; code creation must be delegated to GoodVibes TUI.',
|
|
673
|
+
].join(' ');
|
|
674
|
+
|
|
675
|
+
const properties = tool.definition.parameters.properties;
|
|
676
|
+
if (!isRecord(properties)) return;
|
|
677
|
+
delete properties.dryRun;
|
|
678
|
+
}
|
|
679
|
+
|
|
613
680
|
function narrowModeToolDefinitionForAgentPolicy(tool: Tool, allowedModes: readonly string[], description: string): void {
|
|
614
681
|
tool.definition.description = description;
|
|
682
|
+
tool.definition.sideEffects = [];
|
|
615
683
|
|
|
616
684
|
const properties = tool.definition.parameters.properties;
|
|
617
685
|
if (!isRecord(properties)) return;
|
package/src/version.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { join } from 'node:path';
|
|
|
6
6
|
// The prebuild script updates the fallback value before compilation.
|
|
7
7
|
// Uses import.meta.dir (Bun) to locate package.json relative to this file,
|
|
8
8
|
// which is correct regardless of the process working directory.
|
|
9
|
-
let _version = '0.1.
|
|
9
|
+
let _version = '0.1.22';
|
|
10
10
|
let _sdkVersion = '0.33.35';
|
|
11
11
|
try {
|
|
12
12
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {
|