@pellux/goodvibes-agent 1.1.6 → 1.1.7

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,12 @@
2
2
 
3
3
  Product-facing release notes for GoodVibes Agent.
4
4
 
5
+ ## 1.1.7 - 2026-06-05
6
+
7
+ - Hardened the Agent model tool surface so goodvibes_context routes to the Agent harness instead of dead-ending.
8
+ - Added tool execution and permission safety guards so registered model tools return structured failures instead of aborting turns.
9
+ - Added registered-tool smoke coverage for the Agent-guarded platform tool roster.
10
+
5
11
  ## 1.1.6 - 2026-06-05
6
12
 
7
13
  - Fixed Import GoodVibes settings so it also imports active and pending provider subscriptions from the GoodVibes TUI user store into Agent-owned subscription state.
@@ -450372,23 +450372,6 @@ function createSchema2(db) {
450372
450372
  `);
450373
450373
  db.run(`CREATE INDEX IF NOT EXISTS idx_knowledge_schedules_job_id ON knowledge_schedules(job_id)`);
450374
450374
  }
450375
- function getKnowledgeSchemaStatements() {
450376
- const statements = [];
450377
- createSchema2({
450378
- run(sql) {
450379
- const normalized = sql.trim();
450380
- if (normalized.length > 0)
450381
- statements.push(normalized);
450382
- }
450383
- });
450384
- return statements;
450385
- }
450386
- function renderKnowledgeSchemaSql() {
450387
- return `${getKnowledgeSchemaStatements().map((statement) => statement.endsWith(";") ? statement : `${statement};`).join(`
450388
-
450389
- `)}
450390
- `;
450391
- }
450392
450375
  function rowObject(columns, values2) {
450393
450376
  return Object.fromEntries(columns.map((column, index) => [column, values2[index]]));
450394
450377
  }
@@ -763671,9 +763654,6 @@ var init_ingest = __esm(() => {
763671
763654
  // node_modules/@pellux/goodvibes-sdk/dist/platform/knowledge/browser-history/index.js
763672
763655
  var init_browser_history = __esm(() => {
763673
763656
  init_ingest();
763674
- init_discover();
763675
- init_readers();
763676
- init_paths2();
763677
763657
  });
763678
763658
 
763679
763659
  // node_modules/graphql/jsutils/devAssert.mjs
@@ -781313,7 +781293,6 @@ var init_semantic = __esm(() => {
781313
781293
  init_llm();
781314
781294
  init_gap_repair();
781315
781295
  init_service4();
781316
- init_self_improvement();
781317
781296
  });
781318
781297
 
781319
781298
  // node_modules/@pellux/goodvibes-sdk/dist/platform/knowledge/home-graph/helpers.js
@@ -785282,40 +785261,8 @@ var init_map_view = __esm(() => {
785282
785261
  });
785283
785262
 
785284
785263
  // node_modules/@pellux/goodvibes-sdk/dist/platform/knowledge/home-graph/types.js
785285
- var HOME_GRAPH_NODE_KINDS, HOME_GRAPH_RELATIONS, HOME_GRAPH_CAPABILITIES;
785264
+ var HOME_GRAPH_CAPABILITIES;
785286
785265
  var init_types15 = __esm(() => {
785287
- HOME_GRAPH_NODE_KINDS = [
785288
- "ha_home",
785289
- "ha_entity",
785290
- "ha_device",
785291
- "ha_area",
785292
- "ha_automation",
785293
- "ha_script",
785294
- "ha_scene",
785295
- "ha_label",
785296
- "ha_integration",
785297
- "ha_room",
785298
- "ha_device_passport",
785299
- "ha_maintenance_item",
785300
- "ha_troubleshooting_case",
785301
- "ha_purchase",
785302
- "ha_network_node"
785303
- ];
785304
- HOME_GRAPH_RELATIONS = [
785305
- "controls",
785306
- "located_in",
785307
- "belongs_to_device",
785308
- "has_manual",
785309
- "has_receipt",
785310
- "has_warranty",
785311
- "has_issue",
785312
- "fixed_by",
785313
- "uses_battery",
785314
- "connected_via",
785315
- "part_of_network",
785316
- "mentioned_by",
785317
- "source_for"
785318
- ];
785319
785266
  HOME_GRAPH_CAPABILITIES = [
785320
785267
  "knowledge-space-isolation",
785321
785268
  "snapshot-sync",
@@ -786722,7 +786669,6 @@ var init_service5 = __esm(() => {
786722
786669
  var init_home_graph = __esm(() => {
786723
786670
  init_service5();
786724
786671
  init_extension();
786725
- init_types15();
786726
786672
  });
786727
786673
 
786728
786674
  // node_modules/@pellux/goodvibes-sdk/dist/platform/knowledge/store-refinement.js
@@ -789050,14 +788996,10 @@ var init_service6 = __esm(() => {
789050
788996
  var init_project_planning = __esm(() => {
789051
788997
  init_service6();
789052
788998
  init_helpers3();
789053
- init_readiness();
789054
788999
  });
789055
789000
 
789056
789001
  // node_modules/@pellux/goodvibes-sdk/dist/platform/knowledge/persistence.js
789057
- var init_persistence = __esm(() => {
789058
- init_store_schema();
789059
- init_store_load();
789060
- });
789002
+ var init_persistence = () => {};
789061
789003
 
789062
789004
  // node_modules/@pellux/goodvibes-sdk/dist/platform/knowledge/projections.js
789063
789005
  class KnowledgeProjectionService {
@@ -792565,21 +792507,21 @@ var init_service7 = __esm(() => {
792565
792507
  var exports_knowledge = {};
792566
792508
  __export(exports_knowledge, {
792567
792509
  withKnowledgeSpace: () => withKnowledgeSpace,
792568
- uniqKnowledgeValues: () => uniq2,
792569
- stabilizeKnowledgeText: () => stableText,
792570
- runKnowledgeSemanticSelfImprovement: () => runKnowledgeSemanticSelfImprovement,
792510
+ uniqKnowledgeValues: () => uniq3,
792511
+ stabilizeKnowledgeText: () => stableText2,
792512
+ runKnowledgeSemanticSelfImprovement: () => runKnowledgeSemanticSelfImprovement2,
792571
792513
  resolveProjectPlanningSpace: () => resolveProjectPlanningSpace,
792572
- resolveKnowledgeDbPathFromControlPlaneDir: () => resolveKnowledgeDbPathFromControlPlaneDir,
792514
+ resolveKnowledgeDbPathFromControlPlaneDir: () => resolveKnowledgeDbPathFromControlPlaneDir2,
792573
792515
  renderPacket: () => renderPacket,
792574
792516
  renderKnowledgeSchemaSql: () => renderKnowledgeSchemaSql,
792575
792517
  renderKnowledgeMap: () => renderKnowledgeMap,
792576
792518
  readKnowledgeSearchText: () => readKnowledgeSearchText,
792577
- readBrowserKnowledgeProfile: () => readBrowserKnowledgeProfile,
792519
+ readBrowserKnowledgeProfile: () => readBrowserKnowledgeProfile2,
792578
792520
  projectPlanningSourceId: () => projectPlanningSourceId,
792579
792521
  projectPlanningProjectIdFromPath: () => projectPlanningProjectIdFromPath,
792580
792522
  projectPlanningCanonicalUri: () => projectPlanningCanonicalUri,
792581
792523
  projectKnowledgeSpaceId: () => projectKnowledgeSpaceId,
792582
- parseKnowledgeJsonValue: () => parseJsonValue,
792524
+ parseKnowledgeJsonValue: () => parseJsonValue2,
792583
792525
  normalizeProjectId: () => normalizeProjectId,
792584
792526
  normalizeKnowledgeSpaceId: () => normalizeKnowledgeSpaceId,
792585
792527
  normalizeHomeAssistantInstallationId: () => normalizeHomeAssistantInstallationId,
@@ -792587,12 +792529,12 @@ __export(exports_knowledge, {
792587
792529
  materializeGeneratedKnowledgeProjection: () => materializeGeneratedKnowledgeProjection,
792588
792530
  looksLikeRawPdfPayload: () => looksLikeRawPdfPayload,
792589
792531
  looksBinaryLikeText: () => looksBinaryLikeText,
792590
- loadKnowledgeStoreSnapshot: () => loadKnowledgeStoreSnapshot,
792532
+ loadKnowledgeStoreSnapshot: () => loadKnowledgeStoreSnapshot2,
792591
792533
  listGeneratedKnowledgePages: () => listGeneratedKnowledgePages,
792592
- listBrowserKinds: () => listBrowserKinds,
792534
+ listBrowserKinds: () => listBrowserKinds2,
792593
792535
  knowledgeSpaceMetadata: () => knowledgeSpaceMetadata,
792594
792536
  knowledgePageSourceWeight: () => knowledgePageSourceWeight,
792595
- knowledgeNowMs: () => nowMs,
792537
+ knowledgeNowMs: () => nowMs2,
792596
792538
  knowledgeExtractionNeedsRefresh: () => knowledgeExtractionNeedsRefresh,
792597
792539
  isUsefulKnowledgePageSourceCandidate: () => isUsefulKnowledgePageSourceCandidate,
792598
792540
  isUsefulKnowledgePageSource: () => isUsefulKnowledgePageSource,
@@ -792612,12 +792554,12 @@ __export(exports_knowledge, {
792612
792554
  generatedKnowledgeSourceId: () => generatedKnowledgeSourceId,
792613
792555
  generatedKnowledgeCanonicalUri: () => generatedKnowledgeCanonicalUri,
792614
792556
  extractKnowledgeArtifact: () => extractKnowledgeArtifact,
792615
- evaluateProjectPlanningReadiness: () => evaluateProjectPlanningReadiness,
792616
- discoverBrowserKnowledgeProfiles: () => discoverBrowserKnowledgeProfiles,
792557
+ evaluateProjectPlanningReadiness: () => evaluateProjectPlanningReadiness2,
792558
+ discoverBrowserKnowledgeProfiles: () => discoverBrowserKnowledgeProfiles2,
792617
792559
  createWebKnowledgeGapRepairer: () => createWebKnowledgeGapRepairer,
792618
792560
  createProviderBackedKnowledgeSemanticLlm: () => createProviderBackedKnowledgeSemanticLlm,
792619
792561
  createMemoryApi: () => createMemoryApi,
792620
- createKnowledgeSchema: () => createSchema2,
792562
+ createKnowledgeSchema: () => createSchema3,
792621
792563
  createKnowledgeApi: () => createKnowledgeApi,
792622
792564
  createDefaultKnowledgeConnectorRegistry: () => createDefaultKnowledgeConnectorRegistry,
792623
792565
  compareKnowledgePageSources: () => compareKnowledgePageSources,
@@ -795056,7 +794998,7 @@ var init_delivery_manager = __esm(() => {
795056
794998
  });
795057
794999
 
795058
795000
  // node_modules/@pellux/goodvibes-sdk/dist/platform/automation/scheduler-capacity.js
795059
- function computeSchedulerCapacity(slotsTotal, runs, nowMs2 = Date.now()) {
795001
+ function computeSchedulerCapacity(slotsTotal, runs, nowMs3 = Date.now()) {
795060
795002
  let slotsInUse = 0;
795061
795003
  const queuedRuns = [];
795062
795004
  for (const run7 of runs) {
@@ -795066,7 +795008,7 @@ function computeSchedulerCapacity(slotsTotal, runs, nowMs2 = Date.now()) {
795066
795008
  queuedRuns.push(run7);
795067
795009
  }
795068
795010
  const queueDepth = queuedRuns.length;
795069
- const oldestQueuedAgeMs = queueDepth > 0 ? nowMs2 - Math.min(...queuedRuns.map((r6) => r6.queuedAt)) : null;
795011
+ const oldestQueuedAgeMs = queueDepth > 0 ? nowMs3 - Math.min(...queuedRuns.map((r6) => r6.queuedAt)) : null;
795070
795012
  return { slotsTotal, slotsInUse, queueDepth, oldestQueuedAgeMs };
795071
795013
  }
795072
795014
 
@@ -817092,7 +817034,7 @@ var createStyledCell = (char, overrides = {}) => ({
817092
817034
  // src/version.ts
817093
817035
  import { readFileSync } from "fs";
817094
817036
  import { join } from "path";
817095
- var _version = "1.1.6";
817037
+ var _version = "1.1.7";
817096
817038
  try {
817097
817039
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, "..", "package.json"), "utf-8"));
817098
817040
  _version = typeof pkg.version === "string" ? pkg.version : _version;
@@ -839605,7 +839547,7 @@ var WORK_PLAN_STATUSES = [
839605
839547
  "failed",
839606
839548
  "cancelled"
839607
839549
  ];
839608
- function nowMs2() {
839550
+ function nowMs3() {
839609
839551
  return Date.now();
839610
839552
  }
839611
839553
  function isObject4(value) {
@@ -839711,7 +839653,7 @@ class WorkPlanStore {
839711
839653
  if (!normalizedTitle)
839712
839654
  throw new Error("Work plan item title is required.");
839713
839655
  const plan = this.readPlan();
839714
- const time4 = nowMs2();
839656
+ const time4 = nowMs3();
839715
839657
  const item = {
839716
839658
  id: createItemId(),
839717
839659
  title: normalizedTitle,
@@ -839735,7 +839677,7 @@ class WorkPlanStore {
839735
839677
  updateItem(idOrPrefix, patch2) {
839736
839678
  const plan = this.readPlan();
839737
839679
  const item = this.resolveItem(plan, idOrPrefix);
839738
- const time4 = nowMs2();
839680
+ const time4 = nowMs3();
839739
839681
  const nextStatus = patch2.status ?? item.status;
839740
839682
  const next = this.pruneItem({
839741
839683
  ...item,
@@ -839768,7 +839710,7 @@ class WorkPlanStore {
839768
839710
  removeItem(idOrPrefix) {
839769
839711
  const plan = this.readPlan();
839770
839712
  const item = this.resolveItem(plan, idOrPrefix);
839771
- const time4 = nowMs2();
839713
+ const time4 = nowMs3();
839772
839714
  const remaining = plan.items.filter((candidate) => candidate.id !== item.id);
839773
839715
  this.writePlan({
839774
839716
  ...plan,
@@ -839788,7 +839730,7 @@ class WorkPlanStore {
839788
839730
  ...plan,
839789
839731
  items: remaining,
839790
839732
  activeItemId: remaining[0]?.id,
839791
- updatedAt: nowMs2()
839733
+ updatedAt: nowMs3()
839792
839734
  });
839793
839735
  return removed;
839794
839736
  }
@@ -839827,7 +839769,7 @@ class WorkPlanStore {
839827
839769
  const parsed = JSON.parse(raw);
839828
839770
  if (!isObject4(parsed))
839829
839771
  return this.createEmptyPlan();
839830
- const time4 = nowMs2();
839772
+ const time4 = nowMs3();
839831
839773
  const createdAt = typeof parsed.createdAt === "number" ? parsed.createdAt : time4;
839832
839774
  const updatedAt = typeof parsed.updatedAt === "number" ? parsed.updatedAt : createdAt;
839833
839775
  const items = Array.isArray(parsed.items) ? parsed.items.map((item) => normalizeItem(item, createdAt)).filter((item) => item !== null) : [];
@@ -839846,7 +839788,7 @@ class WorkPlanStore {
839846
839788
  };
839847
839789
  }
839848
839790
  createEmptyPlan() {
839849
- const time4 = nowMs2();
839791
+ const time4 = nowMs3();
839850
839792
  return {
839851
839793
  id: createPlanId(this.options.projectId, this.options.projectRoot),
839852
839794
  projectId: this.options.projectId,
@@ -840631,20 +840573,132 @@ function isRecord17(value) {
840631
840573
  }
840632
840574
 
840633
840575
  // src/tools/agent-context-policy.ts
840634
- var CONTEXT_TOOL_DENIAL = [
840635
- "GoodVibes Agent does not expose non-Agent runtime context through model tools in the main conversation.",
840636
- "The runtime context tool can describe assumptions that are outside the Agent product boundary.",
840637
- "Use explicit Agent CLI/slash commands such as status, compat, setup, and isolated Agent Knowledge instead."
840638
- ].join(" ");
840639
- function wrapBlockedContextToolForAgentPolicy(tool2) {
840640
- tool2.definition.description = "Blocked in GoodVibes Agent: non-Agent runtime context.";
840576
+ var AGENT_CONTEXT_TOOL_MODES = [
840577
+ "summary",
840578
+ "capabilities",
840579
+ "modes",
840580
+ "tools",
840581
+ "tool",
840582
+ "settings",
840583
+ "setting",
840584
+ "commands",
840585
+ "command",
840586
+ "workspace",
840587
+ "status"
840588
+ ];
840589
+ var CONTEXT_TOOL_FALLBACK = {
840590
+ runtime: "GoodVibes Agent",
840591
+ preferredTool: "agent_harness",
840592
+ routes: {
840593
+ summary: 'agent_harness mode:"summary"',
840594
+ capabilities: 'agent_harness mode:"summary" includeParameters:true',
840595
+ tools: 'agent_harness mode:"tools"',
840596
+ tool: 'agent_harness mode:"tool"',
840597
+ settings: 'agent_harness mode:"settings"',
840598
+ commands: 'agent_harness mode:"commands"',
840599
+ workspace: 'agent_harness mode:"workspace"',
840600
+ status: 'agent_harness mode:"connected_host_status"'
840601
+ }
840602
+ };
840603
+ function wrapAgentContextToolForAgentPolicy(tool2, registry5) {
840604
+ tool2.definition.description = "Inspect GoodVibes Agent harness capabilities.";
840641
840605
  tool2.definition.sideEffects = [];
840642
840606
  tool2.definition.parameters = {
840643
840607
  type: "object",
840644
- properties: {},
840608
+ properties: {
840609
+ mode: { type: "string", enum: [...AGENT_CONTEXT_TOOL_MODES] },
840610
+ query: { type: "string" },
840611
+ target: { type: "string" },
840612
+ toolName: { type: "string" },
840613
+ command: { type: "string" },
840614
+ commandName: { type: "string" },
840615
+ key: { type: "string" },
840616
+ category: { type: "string" },
840617
+ prefix: { type: "string" },
840618
+ includeHidden: { type: "boolean" },
840619
+ includeParameters: { type: "boolean" },
840620
+ limit: { type: "number" }
840621
+ },
840645
840622
  additionalProperties: false
840646
840623
  };
840647
- tool2.execute = async () => ({ success: false, error: CONTEXT_TOOL_DENIAL });
840624
+ tool2.execute = async (rawArgs) => {
840625
+ const args2 = rawArgs;
840626
+ const mode = readAgentContextMode(args2.mode);
840627
+ const harnessMode = agentContextModeToHarnessMode(mode);
840628
+ const harnessTool = registry5?.list().find((entry) => entry.definition.name === "agent_harness");
840629
+ if (!harnessTool) {
840630
+ return ok3({
840631
+ ...CONTEXT_TOOL_FALLBACK,
840632
+ status: "agent_harness_not_registered_yet",
840633
+ requestedMode: mode,
840634
+ route: `agent_harness mode:"${harnessMode}"`
840635
+ });
840636
+ }
840637
+ const harnessArgs = {
840638
+ mode: harnessMode,
840639
+ query: args2.query,
840640
+ target: args2.target,
840641
+ toolName: args2.toolName,
840642
+ command: args2.command,
840643
+ commandName: args2.commandName,
840644
+ key: args2.key,
840645
+ category: args2.category,
840646
+ prefix: args2.prefix,
840647
+ includeHidden: args2.includeHidden,
840648
+ includeParameters: mode === "capabilities" ? true : args2.includeParameters,
840649
+ limit: args2.limit
840650
+ };
840651
+ const result2 = await harnessTool.execute(dropUndefined(harnessArgs));
840652
+ if (!result2.success)
840653
+ return result2;
840654
+ return ok3({
840655
+ source: "agent_harness",
840656
+ requestedMode: mode,
840657
+ route: `agent_harness mode:"${harnessMode}"`,
840658
+ result: parseToolOutput2(result2.output)
840659
+ });
840660
+ };
840661
+ }
840662
+ function readAgentContextMode(value) {
840663
+ return typeof value === "string" && AGENT_CONTEXT_TOOL_MODES.includes(value) ? value : "summary";
840664
+ }
840665
+ function agentContextModeToHarnessMode(mode) {
840666
+ if (mode === "capabilities")
840667
+ return "summary";
840668
+ if (mode === "modes")
840669
+ return "modes";
840670
+ if (mode === "tools")
840671
+ return "tools";
840672
+ if (mode === "tool")
840673
+ return "tool";
840674
+ if (mode === "settings")
840675
+ return "settings";
840676
+ if (mode === "setting")
840677
+ return "get_setting";
840678
+ if (mode === "commands")
840679
+ return "commands";
840680
+ if (mode === "command")
840681
+ return "command";
840682
+ if (mode === "workspace")
840683
+ return "workspace";
840684
+ if (mode === "status")
840685
+ return "connected_host_status";
840686
+ return "summary";
840687
+ }
840688
+ function dropUndefined(value) {
840689
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
840690
+ }
840691
+ function parseToolOutput2(value) {
840692
+ if (typeof value !== "string")
840693
+ return value ?? null;
840694
+ try {
840695
+ return JSON.parse(value);
840696
+ } catch {
840697
+ return value;
840698
+ }
840699
+ }
840700
+ function ok3(value) {
840701
+ return { success: true, output: JSON.stringify(value, null, 2) };
840648
840702
  }
840649
840703
 
840650
840704
  // src/tools/agent-find-policy.ts
@@ -841115,7 +841169,7 @@ function installAgentToolPolicyGuard(registry5, options = {}) {
841115
841169
  } else if (tool2.definition.name === "state") {
841116
841170
  wrapStateToolForAgentPolicy(tool2);
841117
841171
  } else if (tool2.definition.name === "goodvibes_context") {
841118
- wrapBlockedContextToolForAgentPolicy(tool2);
841172
+ wrapAgentContextToolForAgentPolicy(tool2, registry5);
841119
841173
  } else if (tool2.definition.name === "goodvibes_settings") {
841120
841174
  wrapBlockedSettingsToolForAgentPolicy(tool2);
841121
841175
  } else if (tool2.definition.name === "inspect") {
@@ -848607,6 +848661,119 @@ function compactRegisteredToolDefinitions(toolRegistry) {
848607
848661
  }
848608
848662
  }
848609
848663
 
848664
+ // src/tools/tool-execution-safety.ts
848665
+ var TOOL_SAFETY_MARKER = Symbol.for("goodvibes-agent.tool-execution-safety-wrapped");
848666
+ var REGISTRY_SAFETY_MARKER = Symbol.for("goodvibes-agent.tool-registry-safety-installed");
848667
+ function installToolExecutionSafetyGuard(registry5) {
848668
+ const marked = registry5;
848669
+ if (marked[REGISTRY_SAFETY_MARKER])
848670
+ return;
848671
+ marked[REGISTRY_SAFETY_MARKER] = true;
848672
+ for (const tool2 of registry5.list())
848673
+ wrapToolExecutionSafety(tool2);
848674
+ const originalRegister = registry5.register.bind(registry5);
848675
+ registry5.register = (tool2) => {
848676
+ wrapToolExecutionSafety(tool2);
848677
+ originalRegister(tool2);
848678
+ };
848679
+ }
848680
+ function wrapToolExecutionSafety(tool2) {
848681
+ const marked = tool2;
848682
+ if (marked[TOOL_SAFETY_MARKER])
848683
+ return;
848684
+ marked[TOOL_SAFETY_MARKER] = true;
848685
+ const originalExecute = tool2.execute.bind(tool2);
848686
+ tool2.execute = async (args2) => {
848687
+ try {
848688
+ return await originalExecute(args2);
848689
+ } catch (error51) {
848690
+ return {
848691
+ success: false,
848692
+ error: summarizeError(error51)
848693
+ };
848694
+ }
848695
+ };
848696
+ }
848697
+
848698
+ // src/runtime/tool-permission-safety.ts
848699
+ var SAFETY_MARKER = Symbol.for("goodvibes-agent.permission-safety-installed");
848700
+ var READ_TOOL_NAMES = new Set([
848701
+ "read",
848702
+ "find",
848703
+ "fetch",
848704
+ "analyze",
848705
+ "inspect",
848706
+ "state",
848707
+ "registry",
848708
+ "goodvibes_context",
848709
+ "agent_harness",
848710
+ "agent_knowledge",
848711
+ "agent_operator_briefing"
848712
+ ]);
848713
+ var WRITE_TOOL_NAMES = new Set([
848714
+ "write",
848715
+ "edit",
848716
+ "goodvibes_settings",
848717
+ "agent_knowledge_ingest",
848718
+ "agent_local_registry",
848719
+ "agent_work_plan"
848720
+ ]);
848721
+ var EXECUTE_TOOL_NAMES = new Set(["exec", "repl"]);
848722
+ function installPermissionManagerSafetyGuard(manager5) {
848723
+ const marked = manager5;
848724
+ if (marked[SAFETY_MARKER])
848725
+ return;
848726
+ marked[SAFETY_MARKER] = true;
848727
+ const originalGetCategory = manager5.getCategory.bind(manager5);
848728
+ const originalCheck = manager5.check.bind(manager5);
848729
+ const originalCheckDetailed = manager5.checkDetailed?.bind(manager5);
848730
+ manager5.getCategory = (toolName, args2 = {}) => {
848731
+ try {
848732
+ return originalGetCategory(toolName, args2);
848733
+ } catch {
848734
+ return fallbackPermissionCategory(toolName);
848735
+ }
848736
+ };
848737
+ manager5.check = async (toolName, args2) => {
848738
+ try {
848739
+ return await originalCheck(toolName, args2);
848740
+ } catch {
848741
+ return fallbackPermissionCategory(toolName) === "read";
848742
+ }
848743
+ };
848744
+ if (originalCheckDetailed) {
848745
+ manager5.checkDetailed = async (toolName, args2) => {
848746
+ try {
848747
+ return await originalCheckDetailed(toolName, args2);
848748
+ } catch (error51) {
848749
+ const category = fallbackPermissionCategory(toolName);
848750
+ const approved = category === "read";
848751
+ return {
848752
+ approved,
848753
+ persisted: false,
848754
+ sourceLayer: "runtime_mode",
848755
+ reasonCode: approved ? "config_allow" : "config_deny",
848756
+ analysis: {
848757
+ classification: "generic",
848758
+ riskLevel: category === "read" ? "low" : "high",
848759
+ summary: `Permission fallback for ${toolName}: ${summarizeError(error51)}`,
848760
+ reasons: ["permission-manager-exception"]
848761
+ }
848762
+ };
848763
+ }
848764
+ };
848765
+ }
848766
+ }
848767
+ function fallbackPermissionCategory(toolName) {
848768
+ if (READ_TOOL_NAMES.has(toolName))
848769
+ return "read";
848770
+ if (WRITE_TOOL_NAMES.has(toolName))
848771
+ return "write";
848772
+ if (EXECUTE_TOOL_NAMES.has(toolName))
848773
+ return "execute";
848774
+ return "delegate";
848775
+ }
848776
+
848610
848777
  // src/runtime/agent-runtime-events.ts
848611
848778
  var AGENT_STATUS_INTERVAL_MS2 = 30000;
848612
848779
  function withRouter2(getSystemMessageRouter, action2) {
@@ -848863,6 +849030,7 @@ async function initializeBootstrapCore(stdout, options, getControlPlaneRecentEve
848863
849030
  installAgentToolPolicyGuard(toolRegistry, {
848864
849031
  getLastUserMessage: () => conversation.getLastUserMessage()
848865
849032
  });
849033
+ installToolExecutionSafetyGuard(toolRegistry);
848866
849034
  compactRegisteredToolDefinitions(toolRegistry);
848867
849035
  services.agentOrchestrator.setDependencies({
848868
849036
  surfaceRoot: GOODVIBES_AGENT_SURFACE_ROOT,
@@ -849001,6 +849169,7 @@ async function initializeBootstrapCore(stdout, options, getControlPlaneRecentEve
849001
849169
  sessionId: runtimeSessionIdRef.value,
849002
849170
  localPrompt: permissionPromptRef.requestPermission
849003
849171
  }), createPermissionConfigReader(configManager), policyRuntimeState, services.hookDispatcher, featureFlags);
849172
+ installPermissionManagerSafetyGuard(permissionManager);
849004
849173
  await hookWorkbench.loadAndApplyManagedHooks();
849005
849174
  const runtime2 = {
849006
849175
  model: configManager.get("provider.model"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-agent",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "private": false,
5
5
  "description": "GoodVibes personal operator assistant TUI with a proactive Agent product brain, isolated Agent Knowledge, local profiles, routines, skills, personas, and explicit build delegation.",
6
6
  "type": "module",
@@ -41,6 +41,8 @@ import { registerAgentReminderScheduleTool } from '../tools/agent-reminder-sched
41
41
  import { getTerminalSize } from '../shell/terminal-size.ts';
42
42
  import { registerAgentWorkPlanTool } from '../tools/agent-work-plan-tool.ts';
43
43
  import { compactRegisteredToolDefinitions } from '../tools/tool-definition-compaction.ts';
44
+ import { installToolExecutionSafetyGuard } from '../tools/tool-execution-safety.ts';
45
+ import { installPermissionManagerSafetyGuard } from './tool-permission-safety.ts';
44
46
  import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
45
47
  import { registerAgentRuntimeEvents } from './agent-runtime-events.ts';
46
48
 
@@ -251,6 +253,7 @@ export async function initializeBootstrapCore(
251
253
  installAgentToolPolicyGuard(toolRegistry, {
252
254
  getLastUserMessage: () => conversation.getLastUserMessage(),
253
255
  });
256
+ installToolExecutionSafetyGuard(toolRegistry);
254
257
  compactRegisteredToolDefinitions(toolRegistry);
255
258
  services.agentOrchestrator.setDependencies({
256
259
  surfaceRoot: GOODVIBES_AGENT_SURFACE_ROOT,
@@ -432,6 +435,7 @@ export async function initializeBootstrapCore(
432
435
  services.hookDispatcher,
433
436
  featureFlags,
434
437
  );
438
+ installPermissionManagerSafetyGuard(permissionManager);
435
439
  await hookWorkbench.loadAndApplyManagedHooks();
436
440
 
437
441
  const runtime: MutableRuntimeState = {
@@ -0,0 +1,93 @@
1
+ import type { PermissionCategory, PermissionCheckResult } from '@pellux/goodvibes-sdk/platform/permissions';
2
+ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
3
+
4
+ type PermissionManagerLike = {
5
+ check(toolName: string, args: Record<string, unknown>): Promise<boolean>;
6
+ checkDetailed?: (toolName: string, args: Record<string, unknown>) => Promise<PermissionCheckResult>;
7
+ getCategory(toolName: string, args?: Record<string, unknown>): PermissionCategory;
8
+ };
9
+
10
+ const SAFETY_MARKER = Symbol.for('goodvibes-agent.permission-safety-installed');
11
+
12
+ const READ_TOOL_NAMES = new Set([
13
+ 'read',
14
+ 'find',
15
+ 'fetch',
16
+ 'analyze',
17
+ 'inspect',
18
+ 'state',
19
+ 'registry',
20
+ 'goodvibes_context',
21
+ 'agent_harness',
22
+ 'agent_knowledge',
23
+ 'agent_operator_briefing',
24
+ ]);
25
+
26
+ const WRITE_TOOL_NAMES = new Set([
27
+ 'write',
28
+ 'edit',
29
+ 'goodvibes_settings',
30
+ 'agent_knowledge_ingest',
31
+ 'agent_local_registry',
32
+ 'agent_work_plan',
33
+ ]);
34
+
35
+ const EXECUTE_TOOL_NAMES = new Set(['exec', 'repl']);
36
+
37
+ type MarkedPermissionManager = PermissionManagerLike & { [SAFETY_MARKER]?: true };
38
+
39
+ export function installPermissionManagerSafetyGuard(manager: PermissionManagerLike): void {
40
+ const marked = manager as MarkedPermissionManager;
41
+ if (marked[SAFETY_MARKER]) return;
42
+ marked[SAFETY_MARKER] = true;
43
+
44
+ const originalGetCategory = manager.getCategory.bind(manager);
45
+ const originalCheck = manager.check.bind(manager);
46
+ const originalCheckDetailed = manager.checkDetailed?.bind(manager);
47
+
48
+ manager.getCategory = (toolName, args = {}) => {
49
+ try {
50
+ return originalGetCategory(toolName, args);
51
+ } catch {
52
+ return fallbackPermissionCategory(toolName);
53
+ }
54
+ };
55
+
56
+ manager.check = async (toolName, args) => {
57
+ try {
58
+ return await originalCheck(toolName, args);
59
+ } catch {
60
+ return fallbackPermissionCategory(toolName) === 'read';
61
+ }
62
+ };
63
+
64
+ if (originalCheckDetailed) {
65
+ manager.checkDetailed = async (toolName, args) => {
66
+ try {
67
+ return await originalCheckDetailed(toolName, args);
68
+ } catch (error) {
69
+ const category = fallbackPermissionCategory(toolName);
70
+ const approved = category === 'read';
71
+ return {
72
+ approved,
73
+ persisted: false,
74
+ sourceLayer: 'runtime_mode',
75
+ reasonCode: approved ? 'config_allow' : 'config_deny',
76
+ analysis: {
77
+ classification: 'generic',
78
+ riskLevel: category === 'read' ? 'low' : 'high',
79
+ summary: `Permission fallback for ${toolName}: ${summarizeError(error)}`,
80
+ reasons: ['permission-manager-exception'],
81
+ },
82
+ };
83
+ }
84
+ };
85
+ }
86
+ }
87
+
88
+ export function fallbackPermissionCategory(toolName: string): PermissionCategory {
89
+ if (READ_TOOL_NAMES.has(toolName)) return 'read';
90
+ if (WRITE_TOOL_NAMES.has(toolName)) return 'write';
91
+ if (EXECUTE_TOOL_NAMES.has(toolName)) return 'execute';
92
+ return 'delegate';
93
+ }
@@ -1,20 +1,147 @@
1
1
  import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
2
+ import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
2
3
 
3
- const CONTEXT_TOOL_DENIAL = [
4
- 'GoodVibes Agent does not expose non-Agent runtime context through model tools in the main conversation.',
5
- 'The runtime context tool can describe assumptions that are outside the Agent product boundary.',
6
- 'Use explicit Agent CLI/slash commands such as status, compat, setup, and isolated Agent Knowledge instead.',
7
- ].join(' ');
4
+ const AGENT_CONTEXT_TOOL_MODES = [
5
+ 'summary',
6
+ 'capabilities',
7
+ 'modes',
8
+ 'tools',
9
+ 'tool',
10
+ 'settings',
11
+ 'setting',
12
+ 'commands',
13
+ 'command',
14
+ 'workspace',
15
+ 'status',
16
+ ] as const;
8
17
 
9
- export function wrapBlockedContextToolForAgentPolicy(tool: Tool): void {
10
- tool.definition.description = 'Blocked in GoodVibes Agent: non-Agent runtime context.';
18
+ type AgentContextMode = typeof AGENT_CONTEXT_TOOL_MODES[number];
19
+
20
+ type AgentContextArgs = {
21
+ readonly mode?: unknown;
22
+ readonly query?: unknown;
23
+ readonly target?: unknown;
24
+ readonly toolName?: unknown;
25
+ readonly command?: unknown;
26
+ readonly commandName?: unknown;
27
+ readonly key?: unknown;
28
+ readonly category?: unknown;
29
+ readonly prefix?: unknown;
30
+ readonly includeHidden?: unknown;
31
+ readonly includeParameters?: unknown;
32
+ readonly limit?: unknown;
33
+ };
34
+
35
+ const CONTEXT_TOOL_FALLBACK = {
36
+ runtime: 'GoodVibes Agent',
37
+ preferredTool: 'agent_harness',
38
+ routes: {
39
+ summary: 'agent_harness mode:"summary"',
40
+ capabilities: 'agent_harness mode:"summary" includeParameters:true',
41
+ tools: 'agent_harness mode:"tools"',
42
+ tool: 'agent_harness mode:"tool"',
43
+ settings: 'agent_harness mode:"settings"',
44
+ commands: 'agent_harness mode:"commands"',
45
+ workspace: 'agent_harness mode:"workspace"',
46
+ status: 'agent_harness mode:"connected_host_status"',
47
+ },
48
+ };
49
+
50
+ export function wrapAgentContextToolForAgentPolicy(tool: Tool, registry?: ToolRegistry): void {
51
+ tool.definition.description = 'Inspect GoodVibes Agent harness capabilities.';
11
52
  tool.definition.sideEffects = [];
12
53
  tool.definition.parameters = {
13
54
  type: 'object',
14
- properties: {},
55
+ properties: {
56
+ mode: { type: 'string', enum: [...AGENT_CONTEXT_TOOL_MODES] },
57
+ query: { type: 'string' },
58
+ target: { type: 'string' },
59
+ toolName: { type: 'string' },
60
+ command: { type: 'string' },
61
+ commandName: { type: 'string' },
62
+ key: { type: 'string' },
63
+ category: { type: 'string' },
64
+ prefix: { type: 'string' },
65
+ includeHidden: { type: 'boolean' },
66
+ includeParameters: { type: 'boolean' },
67
+ limit: { type: 'number' },
68
+ },
15
69
  additionalProperties: false,
16
70
  };
17
- tool.execute = async () => ({ success: false, error: CONTEXT_TOOL_DENIAL });
71
+ tool.execute = async (rawArgs) => {
72
+ const args = rawArgs as AgentContextArgs;
73
+ const mode = readAgentContextMode(args.mode);
74
+ const harnessMode = agentContextModeToHarnessMode(mode);
75
+ const harnessTool = registry?.list().find((entry) => entry.definition.name === 'agent_harness');
76
+ if (!harnessTool) {
77
+ return ok({
78
+ ...CONTEXT_TOOL_FALLBACK,
79
+ status: 'agent_harness_not_registered_yet',
80
+ requestedMode: mode,
81
+ route: `agent_harness mode:"${harnessMode}"`,
82
+ });
83
+ }
84
+
85
+ const harnessArgs = {
86
+ mode: harnessMode,
87
+ query: args.query,
88
+ target: args.target,
89
+ toolName: args.toolName,
90
+ command: args.command,
91
+ commandName: args.commandName,
92
+ key: args.key,
93
+ category: args.category,
94
+ prefix: args.prefix,
95
+ includeHidden: args.includeHidden,
96
+ includeParameters: mode === 'capabilities' ? true : args.includeParameters,
97
+ limit: args.limit,
98
+ };
99
+ const result = await harnessTool.execute(dropUndefined(harnessArgs));
100
+ if (!result.success) return result;
101
+ return ok({
102
+ source: 'agent_harness',
103
+ requestedMode: mode,
104
+ route: `agent_harness mode:"${harnessMode}"`,
105
+ result: parseToolOutput(result.output),
106
+ });
107
+ };
108
+ }
109
+
110
+ export const AGENT_CONTEXT_TOOL_COMPATIBILITY_MODES = AGENT_CONTEXT_TOOL_MODES;
111
+
112
+ function readAgentContextMode(value: unknown): AgentContextMode {
113
+ return typeof value === 'string' && AGENT_CONTEXT_TOOL_MODES.includes(value as AgentContextMode)
114
+ ? value as AgentContextMode
115
+ : 'summary';
18
116
  }
19
117
 
20
- export const AGENT_CONTEXT_TOOL_DENIAL_MESSAGE = CONTEXT_TOOL_DENIAL;
118
+ function agentContextModeToHarnessMode(mode: AgentContextMode): string {
119
+ if (mode === 'capabilities') return 'summary';
120
+ if (mode === 'modes') return 'modes';
121
+ if (mode === 'tools') return 'tools';
122
+ if (mode === 'tool') return 'tool';
123
+ if (mode === 'settings') return 'settings';
124
+ if (mode === 'setting') return 'get_setting';
125
+ if (mode === 'commands') return 'commands';
126
+ if (mode === 'command') return 'command';
127
+ if (mode === 'workspace') return 'workspace';
128
+ if (mode === 'status') return 'connected_host_status';
129
+ return 'summary';
130
+ }
131
+
132
+ function dropUndefined<T extends Record<string, unknown>>(value: T): Record<string, unknown> {
133
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
134
+ }
135
+
136
+ function parseToolOutput(value: unknown): unknown {
137
+ if (typeof value !== 'string') return value ?? null;
138
+ try {
139
+ return JSON.parse(value);
140
+ } catch {
141
+ return value;
142
+ }
143
+ }
144
+
145
+ function ok(value: unknown): { readonly success: true; readonly output: string } {
146
+ return { success: true, output: JSON.stringify(value, null, 2) };
147
+ }
@@ -5,7 +5,7 @@ import {
5
5
  wrapRegistryToolForAgentPolicy,
6
6
  } from './agent-analysis-registry-policy.ts';
7
7
  import {
8
- wrapBlockedContextToolForAgentPolicy,
8
+ wrapAgentContextToolForAgentPolicy,
9
9
  } from './agent-context-policy.ts';
10
10
  import { wrapFindToolForAgentPolicy } from './agent-find-policy.ts';
11
11
  import { wrapReadToolForAgentPolicy } from './agent-read-policy.ts';
@@ -223,7 +223,7 @@ export function installAgentToolPolicyGuard(registry: ToolRegistry, options: Age
223
223
  } else if (tool.definition.name === 'state') {
224
224
  wrapStateToolForAgentPolicy(tool);
225
225
  } else if (tool.definition.name === 'goodvibes_context') {
226
- wrapBlockedContextToolForAgentPolicy(tool);
226
+ wrapAgentContextToolForAgentPolicy(tool, registry);
227
227
  } else if (tool.definition.name === 'goodvibes_settings') {
228
228
  wrapBlockedSettingsToolForAgentPolicy(tool);
229
229
  } else if (tool.definition.name === 'inspect') {
@@ -538,8 +538,8 @@ export const AGENT_DURABLE_WORKFLOW_MUTATION_DENIAL_MESSAGE = DURABLE_WORKFLOW_M
538
538
  export const AGENT_CONTROL_MUTATION_DENIAL_MESSAGE = CONTROL_MUTATION_DENIAL;
539
539
 
540
540
  export {
541
- AGENT_CONTEXT_TOOL_DENIAL_MESSAGE,
542
- wrapBlockedContextToolForAgentPolicy,
541
+ AGENT_CONTEXT_TOOL_COMPATIBILITY_MODES,
542
+ wrapAgentContextToolForAgentPolicy,
543
543
  } from './agent-context-policy.ts';
544
544
 
545
545
  export {
@@ -0,0 +1,41 @@
1
+ import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
2
+ import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
3
+ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
4
+
5
+ const TOOL_SAFETY_MARKER = Symbol.for('goodvibes-agent.tool-execution-safety-wrapped');
6
+ const REGISTRY_SAFETY_MARKER = Symbol.for('goodvibes-agent.tool-registry-safety-installed');
7
+
8
+ type MarkedTool = Tool & { [TOOL_SAFETY_MARKER]?: true };
9
+ type MarkedRegistry = ToolRegistry & { [REGISTRY_SAFETY_MARKER]?: true };
10
+
11
+ export function installToolExecutionSafetyGuard(registry: ToolRegistry): void {
12
+ const marked = registry as MarkedRegistry;
13
+ if (marked[REGISTRY_SAFETY_MARKER]) return;
14
+ marked[REGISTRY_SAFETY_MARKER] = true;
15
+
16
+ for (const tool of registry.list()) wrapToolExecutionSafety(tool);
17
+
18
+ const originalRegister = registry.register.bind(registry);
19
+ registry.register = (tool: Tool): void => {
20
+ wrapToolExecutionSafety(tool);
21
+ originalRegister(tool);
22
+ };
23
+ }
24
+
25
+ export function wrapToolExecutionSafety(tool: Tool): void {
26
+ const marked = tool as MarkedTool;
27
+ if (marked[TOOL_SAFETY_MARKER]) return;
28
+ marked[TOOL_SAFETY_MARKER] = true;
29
+
30
+ const originalExecute = tool.execute.bind(tool);
31
+ tool.execute = async (args) => {
32
+ try {
33
+ return await originalExecute(args);
34
+ } catch (error) {
35
+ return {
36
+ success: false,
37
+ error: summarizeError(error),
38
+ };
39
+ }
40
+ };
41
+ }
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.1.6';
9
+ let _version = '1.1.7';
10
10
  try {
11
11
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {
12
12
  readonly version?: unknown;