@pellux/goodvibes-agent 1.0.1 → 1.0.2

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.
@@ -0,0 +1,18 @@
1
+ import type { AgentSkillRequirement } from '../agent/skill-registry.ts';
2
+
3
+ function readList(value: unknown): readonly string[] {
4
+ if (typeof value === 'string') return value.split(',').map((entry) => entry.trim()).filter(Boolean);
5
+ if (!Array.isArray(value)) return [];
6
+ return value.filter((entry): entry is string => typeof entry === 'string').map((entry) => entry.trim()).filter(Boolean);
7
+ }
8
+
9
+ export function buildAgentLocalRequirements(
10
+ requiresEnv: unknown,
11
+ requiresCommands: unknown,
12
+ ): readonly AgentSkillRequirement[] | undefined {
13
+ if (requiresEnv === undefined && requiresCommands === undefined) return undefined;
14
+ return [
15
+ ...readList(requiresEnv).map((name) => ({ kind: 'env' as const, name })),
16
+ ...readList(requiresCommands).map((name) => ({ kind: 'command' as const, name })),
17
+ ];
18
+ }
@@ -1,33 +1,17 @@
1
1
  import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
2
2
  import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
3
3
  import type { ShellPathService } from '@/runtime/index.ts';
4
- import {
5
- MemoryRegistry,
6
- type MemoryClass,
7
- type MemoryRecord,
8
- type MemoryScope,
9
- } from '@pellux/goodvibes-sdk/platform/state';
4
+ import { MemoryRegistry, type MemoryClass, type MemoryRecord, type MemoryScope } from '@pellux/goodvibes-sdk/platform/state';
10
5
  import { AgentPersonaRegistry, type AgentPersonaRecord } from '../agent/persona-registry.ts';
11
6
  import { AgentNoteRegistry, type AgentNoteRecord } from '../agent/note-registry.ts';
12
7
  import { AgentRoutineRegistry, type AgentRoutineRecord } from '../agent/routine-registry.ts';
13
8
  import { AgentSkillRegistry, type AgentSkillBundleRecord, type AgentSkillRecord } from '../agent/skill-registry.ts';
14
9
  import { assertNoSecretLikeMemoryText } from '../agent/memory-safety.ts';
15
10
  import { formatAgentRecordOrigin, formatAgentRecordReference, formatAgentRecordReviewState } from '../agent/record-labels.ts';
11
+ import { buildAgentLocalRequirements } from './agent-local-registry-requirements.ts';
16
12
 
17
13
  export type AgentLocalRegistryDomain = 'memory' | 'note' | 'persona' | 'skill' | 'skill_bundle' | 'routine';
18
- export type AgentLocalRegistryAction =
19
- | 'list'
20
- | 'search'
21
- | 'get'
22
- | 'create'
23
- | 'update'
24
- | 'enable'
25
- | 'disable'
26
- | 'review'
27
- | 'stale'
28
- | 'use'
29
- | 'clear_active'
30
- | 'start';
14
+ export type AgentLocalRegistryAction = 'list' | 'search' | 'get' | 'create' | 'update' | 'enable' | 'disable' | 'review' | 'stale' | 'use' | 'clear_active' | 'start' | 'delete';
31
15
 
32
16
  export interface AgentLocalRegistryToolArgs {
33
17
  readonly domain?: unknown;
@@ -48,28 +32,20 @@ export interface AgentLocalRegistryToolArgs {
48
32
  readonly steps?: unknown;
49
33
  readonly skills?: unknown;
50
34
  readonly skillIds?: unknown;
35
+ readonly requiresEnv?: unknown;
36
+ readonly requiresCommands?: unknown;
51
37
  readonly triggers?: unknown;
52
38
  readonly tags?: unknown;
53
39
  readonly reason?: unknown;
54
40
  readonly enabled?: unknown;
41
+ readonly activate?: unknown;
55
42
  readonly provenance?: unknown;
43
+ readonly confirm?: unknown;
44
+ readonly explicitUserRequest?: unknown;
56
45
  }
57
46
 
58
47
  const DOMAINS: readonly AgentLocalRegistryDomain[] = ['memory', 'note', 'persona', 'skill', 'skill_bundle', 'routine'];
59
- const ACTIONS: readonly AgentLocalRegistryAction[] = [
60
- 'list',
61
- 'search',
62
- 'get',
63
- 'create',
64
- 'update',
65
- 'enable',
66
- 'disable',
67
- 'review',
68
- 'stale',
69
- 'use',
70
- 'clear_active',
71
- 'start',
72
- ];
48
+ const ACTIONS: readonly AgentLocalRegistryAction[] = ['list', 'search', 'get', 'create', 'update', 'enable', 'disable', 'review', 'stale', 'use', 'clear_active', 'start', 'delete'];
73
49
  const MEMORY_CLASSES: readonly MemoryClass[] = ['decision', 'constraint', 'incident', 'pattern', 'fact', 'risk', 'runbook', 'architecture', 'ownership'];
74
50
  const MEMORY_SCOPES: readonly MemoryScope[] = ['session', 'project', 'team'];
75
51
  const AGENT_TOOL_PROVENANCE = 'agent-local-registry-tool';
@@ -86,6 +62,10 @@ function readString(value: unknown): string {
86
62
  return typeof value === 'string' ? value.trim() : '';
87
63
  }
88
64
 
65
+ function readAffirmative(value: unknown): boolean {
66
+ const normalized = readString(value).toLowerCase(); return value === true || (typeof value === 'string' && (normalized === '' || normalized === 'yes' || normalized === 'y' || normalized === 'true' || normalized === 'on'));
67
+ }
68
+
89
69
  function readStringList(value: unknown): readonly string[] {
90
70
  if (typeof value === 'string') {
91
71
  return value.split(',').map((entry) => entry.trim()).filter(Boolean);
@@ -130,6 +110,12 @@ function requireId(args: AgentLocalRegistryToolArgs): string {
130
110
  return id;
131
111
  }
132
112
 
113
+ function requireConfirmedDelete(args: AgentLocalRegistryToolArgs, label: string): void {
114
+ const explicitUserRequest = readString(args.explicitUserRequest);
115
+ if (!explicitUserRequest) throw new Error(`${label} deletion requires explicitUserRequest with the user's exact request or a short faithful summary.`);
116
+ if (args.confirm !== true) throw new Error(`${label} deletion requires confirm:true after an explicit user request.`);
117
+ }
118
+
133
119
  function requireName(args: AgentLocalRegistryToolArgs): string {
134
120
  const name = readString(args.name);
135
121
  if (!name) throw new Error('name is required.');
@@ -202,6 +188,7 @@ async function handleMemory(registry: MemoryRegistry, action: AgentLocalRegistry
202
188
  const summary = requireSummary(args);
203
189
  const detail = readString(args.detail || args.body);
204
190
  const tags = readStringList(args.tags);
191
+ const confidence = readOptionalConfidence(args.confidence);
205
192
  assertNoSecretLikeMemoryText([summary, detail, ...tags]);
206
193
  const record = await registry.add({
207
194
  scope: readMemoryScope(args),
@@ -209,6 +196,7 @@ async function handleMemory(registry: MemoryRegistry, action: AgentLocalRegistry
209
196
  summary,
210
197
  detail,
211
198
  tags: [...tags],
199
+ ...(confidence === undefined ? {} : { review: { state: 'fresh' as const, confidence } }),
212
200
  provenance: [{ kind: 'event', ref: readString(args.provenance) || AGENT_TOOL_PROVENANCE }],
213
201
  });
214
202
  return [
@@ -258,6 +246,15 @@ async function handleMemory(registry: MemoryRegistry, action: AgentLocalRegistry
258
246
  ` id ${record.id}`,
259
247
  ].join('\n');
260
248
  }
249
+ if (action === 'delete') {
250
+ const id = requireId(args);
251
+ requireConfirmedDelete(args, 'Agent-local memory');
252
+ if (!registry.delete(id)) return `Unknown Agent-local memory ${id}`;
253
+ return [
254
+ 'Deleted Agent-local memory',
255
+ ` id ${id}`,
256
+ ].join('\n');
257
+ }
261
258
  throw new Error(`Action ${action} is not valid for memory.`);
262
259
  }
263
260
 
@@ -332,6 +329,15 @@ function handleNote(shellPaths: ShellPathService, action: AgentLocalRegistryActi
332
329
  ` id ${note.id}`,
333
330
  ].join('\n');
334
331
  }
332
+ if (action === 'delete') {
333
+ requireConfirmedDelete(args, 'Agent-local note');
334
+ const note = registry.deleteNote(requireId(args));
335
+ return [
336
+ 'Deleted Agent-local note',
337
+ ` id ${note.id}`,
338
+ ` title ${note.title}`,
339
+ ].join('\n');
340
+ }
335
341
  throw new Error(`Action ${action} is not valid for notes.`);
336
342
  }
337
343
 
@@ -406,6 +412,7 @@ function handlePersona(shellPaths: ShellPathService, action: AgentLocalRegistryA
406
412
  source: 'agent',
407
413
  provenance: readString(args.provenance) || AGENT_TOOL_PROVENANCE,
408
414
  });
415
+ if (args.activate !== undefined && readAffirmative(args.activate)) registry.setActive(persona.id);
409
416
  return [
410
417
  'Created Agent-local persona',
411
418
  ` id ${persona.id}`,
@@ -413,6 +420,7 @@ function handlePersona(shellPaths: ShellPathService, action: AgentLocalRegistryA
413
420
  ].join('\n');
414
421
  }
415
422
  if (action === 'update') {
423
+ const wasActive = registry.snapshot().activePersonaId === requireId(args);
416
424
  const persona = registry.update(requireId(args), {
417
425
  name: readString(args.name) || undefined,
418
426
  description: readString(args.description) || undefined,
@@ -421,6 +429,7 @@ function handlePersona(shellPaths: ShellPathService, action: AgentLocalRegistryA
421
429
  triggers: args.triggers === undefined ? undefined : readStringList(args.triggers),
422
430
  provenance: readString(args.provenance) || AGENT_TOOL_PROVENANCE,
423
431
  });
432
+ if (args.activate !== undefined) { if (readAffirmative(args.activate)) registry.setActive(persona.id); else if (wasActive) registry.clearActive(); }
424
433
  return [
425
434
  'Updated Agent-local persona',
426
435
  ` id ${persona.id}`,
@@ -453,6 +462,15 @@ function handlePersona(shellPaths: ShellPathService, action: AgentLocalRegistryA
453
462
  ` id ${persona.id}`,
454
463
  ].join('\n');
455
464
  }
465
+ if (action === 'delete') {
466
+ requireConfirmedDelete(args, 'Agent-local persona');
467
+ const persona = registry.deletePersona(requireId(args));
468
+ return [
469
+ 'Deleted Agent-local persona',
470
+ ` id ${persona.id}`,
471
+ ` name ${persona.name}`,
472
+ ].join('\n');
473
+ }
456
474
  throw new Error(`Action ${action} is not valid for personas.`);
457
475
  }
458
476
 
@@ -480,6 +498,7 @@ function handleSkill(shellPaths: ShellPathService, action: AgentLocalRegistryAct
480
498
  procedure: requireTextField(args.procedure, 'procedure'),
481
499
  triggers: readStringList(args.triggers),
482
500
  tags: readStringList(args.tags),
501
+ requirements: buildAgentLocalRequirements(args.requiresEnv, args.requiresCommands),
483
502
  enabled: args.enabled === true,
484
503
  source: 'agent',
485
504
  provenance: readString(args.provenance) || AGENT_TOOL_PROVENANCE,
@@ -497,6 +516,7 @@ function handleSkill(shellPaths: ShellPathService, action: AgentLocalRegistryAct
497
516
  procedure: readString(args.procedure) || undefined,
498
517
  triggers: args.triggers === undefined ? undefined : readStringList(args.triggers),
499
518
  tags: args.tags === undefined ? undefined : readStringList(args.tags),
519
+ requirements: buildAgentLocalRequirements(args.requiresEnv, args.requiresCommands),
500
520
  provenance: readString(args.provenance) || AGENT_TOOL_PROVENANCE,
501
521
  });
502
522
  return [
@@ -527,6 +547,15 @@ function handleSkill(shellPaths: ShellPathService, action: AgentLocalRegistryAct
527
547
  ` id ${skill.id}`,
528
548
  ].join('\n');
529
549
  }
550
+ if (action === 'delete') {
551
+ requireConfirmedDelete(args, 'Agent-local skill');
552
+ const skill = registry.deleteSkill(requireId(args));
553
+ return [
554
+ 'Deleted Agent-local skill',
555
+ ` id ${skill.id}`,
556
+ ` name ${skill.name}`,
557
+ ].join('\n');
558
+ }
530
559
  throw new Error(`Action ${action} is not valid for skills.`);
531
560
  }
532
561
 
@@ -595,6 +624,15 @@ function handleSkillBundle(shellPaths: ShellPathService, action: AgentLocalRegis
595
624
  ` id ${bundle.id}`,
596
625
  ].join('\n');
597
626
  }
627
+ if (action === 'delete') {
628
+ requireConfirmedDelete(args, 'Agent-local skill bundle');
629
+ const bundle = registry.deleteBundle(requireId(args));
630
+ return [
631
+ 'Deleted Agent-local skill bundle',
632
+ ` id ${bundle.id}`,
633
+ ` name ${bundle.name}`,
634
+ ].join('\n');
635
+ }
598
636
  throw new Error(`Action ${action} is not valid for skill bundles.`);
599
637
  }
600
638
 
@@ -621,6 +659,7 @@ function handleRoutine(shellPaths: ShellPathService, action: AgentLocalRegistryA
621
659
  steps: requireTextField(args.steps, 'steps'),
622
660
  triggers: readStringList(args.triggers),
623
661
  tags: readStringList(args.tags),
662
+ requirements: buildAgentLocalRequirements(args.requiresEnv, args.requiresCommands),
624
663
  enabled: args.enabled === true,
625
664
  source: 'agent',
626
665
  provenance: readString(args.provenance) || AGENT_TOOL_PROVENANCE,
@@ -638,6 +677,7 @@ function handleRoutine(shellPaths: ShellPathService, action: AgentLocalRegistryA
638
677
  steps: readString(args.steps) || undefined,
639
678
  triggers: args.triggers === undefined ? undefined : readStringList(args.triggers),
640
679
  tags: args.tags === undefined ? undefined : readStringList(args.tags),
680
+ requirements: buildAgentLocalRequirements(args.requiresEnv, args.requiresCommands),
641
681
  provenance: readString(args.provenance) || AGENT_TOOL_PROVENANCE,
642
682
  });
643
683
  return [
@@ -679,6 +719,15 @@ function handleRoutine(shellPaths: ShellPathService, action: AgentLocalRegistryA
679
719
  ` id ${routine.id}`,
680
720
  ].join('\n');
681
721
  }
722
+ if (action === 'delete') {
723
+ requireConfirmedDelete(args, 'Agent-local routine');
724
+ const routine = registry.deleteRoutine(requireId(args));
725
+ return [
726
+ 'Deleted Agent-local routine',
727
+ ` id ${routine.id}`,
728
+ ` name ${routine.name}`,
729
+ ].join('\n');
730
+ }
682
731
  throw new Error(`Action ${action} is not valid for routines.`);
683
732
  }
684
733
 
@@ -689,7 +738,7 @@ export function createAgentLocalRegistryTool(shellPaths: ShellPathService, memor
689
738
  description: [
690
739
  'Inspect and maintain GoodVibes Agent-local notes, memory, personas, skills, and routines from the main conversation.',
691
740
  'Use this for safe self-improvement: capture scratchpad notes, remember durable non-secret facts, create or refine reusable behavior, bundle related skills, enable skills/routines, choose personas, review/stale records, and start routines in the same serial conversation.',
692
- 'This tool cannot delete records, create schedules, mutate connected hosts, send messages, run background jobs, or delegate build work.',
741
+ 'Destructive record deletion requires confirm:true plus explicitUserRequest. This tool cannot create schedules, mutate connected hosts, send messages, run background jobs, or delegate build work.',
693
742
  ].join(' '),
694
743
  parameters: {
695
744
  type: 'object',
@@ -712,11 +761,16 @@ export function createAgentLocalRegistryTool(shellPaths: ShellPathService, memor
712
761
  steps: { type: 'string', description: 'Routine steps.' },
713
762
  skills: { type: 'array', items: { type: 'string' }, description: 'Skill ids for skill_bundle create/update.' },
714
763
  skillIds: { type: 'array', items: { type: 'string' }, description: 'Skill ids for skill_bundle create/update.' },
764
+ requiresEnv: { type: 'array', items: { type: 'string' }, description: 'Environment variable names required by a skill or routine. Values are never stored.' },
765
+ requiresCommands: { type: 'array', items: { type: 'string' }, description: 'Command names required by a skill or routine.' },
715
766
  triggers: { type: 'array', items: { type: 'string' } },
716
767
  tags: { type: 'array', items: { type: 'string' } },
717
768
  reason: { type: 'string' },
718
769
  enabled: { type: 'boolean' },
770
+ activate: { type: 'boolean', description: 'Activate a created/updated persona, or clear it when updating the active persona with false.' },
719
771
  provenance: { type: 'string' },
772
+ confirm: { type: 'boolean', description: 'Required true for delete after an explicit user request.' },
773
+ explicitUserRequest: { type: 'string', description: 'Exact user request or faithful short summary authorizing delete.' },
720
774
  },
721
775
  required: ['domain', 'action'],
722
776
  additionalProperties: false,
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 = '1.0.1';
9
+ let _version = '1.0.2';
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 {