@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 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.20",
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.20';
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 {