@pellux/goodvibes-agent 0.1.19 → 0.1.21
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 +71 -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.21 - 2026-05-31
|
|
6
|
+
|
|
7
|
+
- d83f325 Keep inspect scaffold dry-run-only in agent runtime
|
|
8
|
+
|
|
9
|
+
## 0.1.20 - 2026-05-31
|
|
10
|
+
|
|
11
|
+
- c0eca13 Block settings mutation tool in agent runtime
|
|
12
|
+
|
|
5
13
|
## 0.1.19 - 2026-05-31
|
|
6
14
|
|
|
7
15
|
- a4255d5 Restrict durable workflow tool mutations 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.21",
|
|
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
|
};
|
|
@@ -153,6 +159,18 @@ const STATE_MUTATION_DENIAL = [
|
|
|
153
159
|
'Use Agent-owned memory, skills, personas, routines, and explicit CLI/slash commands for intentional local state changes.',
|
|
154
160
|
].join(' ');
|
|
155
161
|
|
|
162
|
+
const SETTINGS_MUTATION_DENIAL = [
|
|
163
|
+
'GoodVibes Agent does not mutate configuration through model tools in the main conversation.',
|
|
164
|
+
'Use explicit Agent CLI/slash settings commands for intentional config changes.',
|
|
165
|
+
'Secrets, tokens, passwords, daemon lifecycle settings, and service exposure settings require explicit user action outside the model tool surface.',
|
|
166
|
+
].join(' ');
|
|
167
|
+
|
|
168
|
+
const INSPECT_WRITE_DENIAL = [
|
|
169
|
+
'GoodVibes Agent only uses inspect scaffold mode for dry-run planning from the main conversation.',
|
|
170
|
+
'File scaffolding and code creation are disabled in the Agent model tool surface.',
|
|
171
|
+
'Delegate explicit build/implement/fix/review work to GoodVibes TUI instead.',
|
|
172
|
+
].join(' ');
|
|
173
|
+
|
|
156
174
|
const DURABLE_WORKFLOW_MUTATION_DENIAL = [
|
|
157
175
|
'GoodVibes Agent only inspects copied durable workflow tools from the main conversation.',
|
|
158
176
|
'Task, team, worklist, packet, and query creation or lifecycle mutation is disabled here.',
|
|
@@ -192,6 +210,10 @@ export function installAgentToolPolicyGuard(registry: ToolRegistry, options: Age
|
|
|
192
210
|
wrapFetchToolForAgentPolicy(tool);
|
|
193
211
|
} else if (tool.definition.name === 'state') {
|
|
194
212
|
wrapStateToolForAgentPolicy(tool);
|
|
213
|
+
} else if (tool.definition.name === 'goodvibes_settings') {
|
|
214
|
+
wrapBlockedSettingsToolForAgentPolicy(tool);
|
|
215
|
+
} else if (tool.definition.name === 'inspect') {
|
|
216
|
+
wrapInspectToolForAgentPolicy(tool);
|
|
195
217
|
} else if (tool.definition.name === 'task') {
|
|
196
218
|
wrapModeRestrictedToolForAgentPolicy(tool, {
|
|
197
219
|
allowedModes: READ_ONLY_TASK_TOOL_MODES,
|
|
@@ -297,6 +319,32 @@ export function wrapStateToolForAgentPolicy(tool: Tool): void {
|
|
|
297
319
|
};
|
|
298
320
|
}
|
|
299
321
|
|
|
322
|
+
export function wrapBlockedSettingsToolForAgentPolicy(tool: Tool): void {
|
|
323
|
+
tool.definition.description = [
|
|
324
|
+
'Blocked in GoodVibes Agent main conversation: configuration mutation.',
|
|
325
|
+
'Use explicit Agent CLI/slash settings commands for intentional config changes.',
|
|
326
|
+
'Daemon lifecycle and service exposure remain externally managed by GoodVibes TUI/daemon.',
|
|
327
|
+
].join(' ');
|
|
328
|
+
tool.definition.sideEffects = [];
|
|
329
|
+
tool.definition.parameters = {
|
|
330
|
+
type: 'object',
|
|
331
|
+
properties: {},
|
|
332
|
+
additionalProperties: false,
|
|
333
|
+
};
|
|
334
|
+
tool.execute = async () => ({ success: false, error: SETTINGS_MUTATION_DENIAL });
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export function wrapInspectToolForAgentPolicy(tool: Tool): void {
|
|
338
|
+
narrowInspectToolDefinitionForAgentPolicy(tool);
|
|
339
|
+
const originalExecute = tool.execute.bind(tool);
|
|
340
|
+
tool.execute = async (args) => {
|
|
341
|
+
const inspectArgs = args as InspectToolArgs;
|
|
342
|
+
const denial = validateInspectToolInvocationForAgentPolicy(inspectArgs);
|
|
343
|
+
if (denial) return { success: false, error: denial };
|
|
344
|
+
return originalExecute(normalizeInspectToolInvocationForAgentPolicy(inspectArgs) as Parameters<Tool['execute']>[0]);
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
300
348
|
export function validateExecToolInvocationForAgentPolicy(args: ExecToolArgs): string | null {
|
|
301
349
|
if (args.parallel === true) return BACKGROUND_EXEC_DENIAL;
|
|
302
350
|
if (Array.isArray(args.file_ops) && args.file_ops.length > 0) return BACKGROUND_EXEC_DENIAL;
|
|
@@ -381,6 +429,16 @@ export function validateStateToolInvocationForAgentPolicy(args: StateToolArgs):
|
|
|
381
429
|
return null;
|
|
382
430
|
}
|
|
383
431
|
|
|
432
|
+
export function validateInspectToolInvocationForAgentPolicy(args: InspectToolArgs): string | null {
|
|
433
|
+
if (args.mode === 'scaffold' && args.dryRun === false) return INSPECT_WRITE_DENIAL;
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export function normalizeInspectToolInvocationForAgentPolicy(args: InspectToolArgs): InspectToolArgs {
|
|
438
|
+
if (args.mode !== 'scaffold') return args;
|
|
439
|
+
return { ...args, dryRun: true };
|
|
440
|
+
}
|
|
441
|
+
|
|
384
442
|
type ModeRestrictedToolPolicy = {
|
|
385
443
|
readonly allowedModes: readonly string[];
|
|
386
444
|
readonly modeSet: ReadonlySet<string>;
|
|
@@ -452,6 +510,8 @@ export const AGENT_CHANNEL_ACTION_DENIAL_MESSAGE = CHANNEL_ACTION_DENIAL;
|
|
|
452
510
|
export const AGENT_MCP_SECURITY_MUTATION_DENIAL_MESSAGE = MCP_SECURITY_MUTATION_DENIAL;
|
|
453
511
|
export const AGENT_FETCH_NETWORK_MUTATION_DENIAL_MESSAGE = FETCH_NETWORK_MUTATION_DENIAL;
|
|
454
512
|
export const AGENT_STATE_MUTATION_DENIAL_MESSAGE = STATE_MUTATION_DENIAL;
|
|
513
|
+
export const AGENT_SETTINGS_MUTATION_DENIAL_MESSAGE = SETTINGS_MUTATION_DENIAL;
|
|
514
|
+
export const AGENT_INSPECT_WRITE_DENIAL_MESSAGE = INSPECT_WRITE_DENIAL;
|
|
455
515
|
export const AGENT_DURABLE_WORKFLOW_MUTATION_DENIAL_MESSAGE = DURABLE_WORKFLOW_MUTATION_DENIAL;
|
|
456
516
|
|
|
457
517
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
@@ -586,6 +646,17 @@ function narrowStateToolDefinitionForAgentPolicy(tool: Tool): void {
|
|
|
586
646
|
narrowStringEnumProperty(properties, 'analyticsAction', READ_ONLY_STATE_ANALYTICS_ACTIONS, 'Read-only analytics actions allowed by GoodVibes Agent.');
|
|
587
647
|
}
|
|
588
648
|
|
|
649
|
+
function narrowInspectToolDefinitionForAgentPolicy(tool: Tool): void {
|
|
650
|
+
tool.definition.description = [
|
|
651
|
+
'Inspect and analyze project structure for GoodVibes Agent.',
|
|
652
|
+
'Scaffold mode is dry-run-only in the main conversation; code creation must be delegated to GoodVibes TUI.',
|
|
653
|
+
].join(' ');
|
|
654
|
+
|
|
655
|
+
const properties = tool.definition.parameters.properties;
|
|
656
|
+
if (!isRecord(properties)) return;
|
|
657
|
+
delete properties.dryRun;
|
|
658
|
+
}
|
|
659
|
+
|
|
589
660
|
function narrowModeToolDefinitionForAgentPolicy(tool: Tool, allowedModes: readonly string[], description: string): void {
|
|
590
661
|
tool.definition.description = description;
|
|
591
662
|
|
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.21';
|
|
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 {
|