@songsid/agend 2.1.6-beta.3 → 2.1.6-beta.4

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.
@@ -27,6 +27,15 @@ export declare class EventLog {
27
27
  insert(instance: string, type: string, payload?: Record<string, unknown>): void;
28
28
  query(opts?: QueryOpts): EventRow[];
29
29
  logActivity(event: string, sender: string, summary: string, receiver?: string, detail?: string): void;
30
+ /**
31
+ * Which tools each instance has called lately, from the activity log.
32
+ *
33
+ * `summary` is written as `tool(argument)`, so the name is everything up to
34
+ * the first bracket. Only the outbound path writes these rows — the agent
35
+ * endpoint and the typed IPC handlers do not — so this is a floor on what an
36
+ * instance has used, never a complete account of it.
37
+ */
38
+ toolUseByInstance(sinceIso: string): Map<string, Map<string, number>>;
30
39
  listActivity(opts?: {
31
40
  since?: string;
32
41
  limit?: number;
package/dist/event-log.js CHANGED
@@ -93,6 +93,27 @@ export class EventLog {
93
93
  logActivity(event, sender, summary, receiver, detail) {
94
94
  this.db.prepare("INSERT INTO activity (event, sender, receiver, summary, detail) VALUES (?, ?, ?, ?, ?)").run(event, sender, receiver ?? null, summary, detail ?? null);
95
95
  }
96
+ /**
97
+ * Which tools each instance has called lately, from the activity log.
98
+ *
99
+ * `summary` is written as `tool(argument)`, so the name is everything up to
100
+ * the first bracket. Only the outbound path writes these rows — the agent
101
+ * endpoint and the typed IPC handlers do not — so this is a floor on what an
102
+ * instance has used, never a complete account of it.
103
+ */
104
+ toolUseByInstance(sinceIso) {
105
+ const rows = this.db.prepare("SELECT sender, summary FROM activity WHERE event = 'tool_call' AND timestamp >= ?").all(sinceIso);
106
+ const out = new Map();
107
+ for (const row of rows) {
108
+ const tool = /^([a-z_]+)/.exec(row.summary ?? "")?.[1];
109
+ if (!tool || !row.sender)
110
+ continue;
111
+ const tools = out.get(row.sender) ?? new Map();
112
+ tools.set(tool, (tools.get(tool) ?? 0) + 1);
113
+ out.set(row.sender, tools);
114
+ }
115
+ return out;
116
+ }
96
117
  listActivity(opts) {
97
118
  let sql = "SELECT * FROM activity";
98
119
  const params = [];
@@ -1 +1 @@
1
- {"version":3,"file":"event-log.js","sourceRoot":"","sources":["../src/event-log.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"event-log.js","sourceRoot":"","sources":["../src/event-log.ts"],"names":[],"mappings":"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"}
@@ -763,6 +763,29 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
763
763
  private rateLimitWarnedAt;
764
764
  private warnIfRateLimited;
765
765
  /** Handle outbound tool calls from a daemon instance */
766
+ /**
767
+ * Would this instance be allowed to use this tool?
768
+ *
769
+ * Stage 1 asks and records; nothing is refused yet. The recording is not only
770
+ * an observation window: `logActivity("tool_call")` sits on the outbound path
771
+ * only, so today the fleet has no idea what the agent endpoint or the typed
772
+ * IPC handlers are being asked to do — the one face with no authorization is
773
+ * also the one face with no telemetry.
774
+ *
775
+ * The profile comes from the instance the socket belongs to. Deliberately not
776
+ * `senderSessionName`, which arrives inside the message: a caller that fills
777
+ * in its own identity has not been identified.
778
+ */
779
+ /**
780
+ * Say once, at startup, what the new default means for this fleet.
781
+ *
782
+ * Nothing is rewritten: an explicit `tool_set: full` is a choice somebody
783
+ * made, and marking an instance as a coordinator is a judgement about how
784
+ * their fleet is organised. Both stay theirs — this only makes sure they are
785
+ * not discovered by an agent failing at three in the morning.
786
+ */
787
+ private announceToolPermissionsChange;
788
+ private checkToolPermission;
766
789
  private handleOutboundFromInstance;
767
790
  /** Side effects of a routed reply: cancel-button lifecycle, logs, SSE, chat log. */
768
791
  private afterReplyRouted;
@@ -798,6 +821,24 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
798
821
  private scheduleSourceAdapter;
799
822
  private notifySourceTopic;
800
823
  private notifyScheduleFailure;
824
+ /**
825
+ * The typed IPC messages, all through one door.
826
+ *
827
+ * They used to be five sibling branches on the dispatch, which is why the
828
+ * permission question had five places to be forgotten. Routing them together
829
+ * means the check above happens once and cannot be skipped by adding a
830
+ * sixth — a new type has to appear in `IPC_TYPE_TOOLS` to be dispatched at
831
+ * all.
832
+ */
833
+ /**
834
+ * Answer a refused typed message the way its handler would have.
835
+ *
836
+ * These are request/response over IPC: dropping the message silently leaves
837
+ * the caller waiting for a reply that never comes, and a hung agent is a
838
+ * worse failure than a refused one.
839
+ */
840
+ private refuseTypedIpc;
841
+ private dispatchTypedIpc;
801
842
  private handleScheduleCrud;
802
843
  private handleDecisionCrud;
803
844
  /** Resolve display name for an instance, fallback to instance name. */
@@ -61,7 +61,9 @@ import { clearPausedMarker } from "./pause-marker.js";
61
61
  import { isFleetStartCommandLine, readProcessCommandLine, releaseProcessFleetLock } from "./fleet-lock.js";
62
62
  import { isSetupComplete, markSetupComplete } from "./setup-marker.js";
63
63
  import { manualCleanupMessage, reapStaleTunnel } from "./tunnel/lease.js";
64
+ import { buildToolPermissionsNotice } from "./tool-permissions-notice.js";
64
65
  import { GENERAL_PAUSE_ERROR, isGeneralInstance } from "./general-instance.js";
66
+ import { mayUseTool, resolveToolSet, toolForIpcType, toolRefusedMessage, } from "./tool-permissions.js";
65
67
  import { decideWebGate, loadOrCreateWebToken, readWebToken } from "./web-auth.js";
66
68
  import { fleetLevelDifferences, fleetLevelSignature } from "./fleet-level-config.js";
67
69
  import { checkSelfRestartAllowance, recordSelfRestartAttempt } from "./self-restart-limit.js";
@@ -615,6 +617,7 @@ export class FleetManager {
615
617
  // watching, and the fleet starting is the moment there is finally a process
616
618
  // around to notice. Never throws: a lease that cannot be resolved blocks
617
619
  // the next tunnel and says so, it does not block the fleet.
620
+ this.announceToolPermissionsChange();
618
621
  void reapStaleTunnel(this.dataDir)
619
622
  .then(outcome => {
620
623
  if (outcome.kind === "manual")
@@ -3934,22 +3937,15 @@ export class FleetManager {
3934
3937
  }
3935
3938
  await this.handleOutboundFromInstance(name, msg);
3936
3939
  }
3937
- else if (msg.type === "fleet_schedule_create" || msg.type === "fleet_schedule_list" ||
3938
- msg.type === "fleet_schedule_update" || msg.type === "fleet_schedule_delete") {
3939
- this.handleScheduleCrud(name, msg);
3940
- }
3941
- else if (msg.type === "fleet_decision_create" || msg.type === "fleet_decision_list" ||
3942
- msg.type === "fleet_decision_update") {
3943
- this.handleDecisionCrud(name, msg);
3944
- }
3945
- else if (msg.type === "fleet_task") {
3946
- this.handleTaskCrud(name, msg);
3947
- }
3948
- else if (msg.type === "fleet_set_display_name") {
3949
- this.handleSetDisplayName(name, msg);
3950
- }
3951
- else if (msg.type === "fleet_set_description") {
3952
- this.handleSetDescription(name, msg);
3940
+ else if (toolForIpcType(msg.type) !== null) {
3941
+ // Sink 2 of 3. Each of these types IS a tool and reaches its own
3942
+ // handler without passing through the outbound path — which is how
3943
+ // `update_decision`, a coordinator-only tool, kept a way through.
3944
+ const typed = this.checkToolPermission("ipc-typed", name, toolForIpcType(msg.type));
3945
+ if (typed.allowed)
3946
+ this.dispatchTypedIpc(name, msg);
3947
+ else
3948
+ this.refuseTypedIpc(name, msg, typed.message);
3953
3949
  }
3954
3950
  else if (msg.type === "instance_process_state") {
3955
3951
  this.cacheInstanceProcessStatus(name, msg.status);
@@ -4891,6 +4887,55 @@ export class FleetManager {
4891
4887
  }
4892
4888
  }
4893
4889
  /** Handle outbound tool calls from a daemon instance */
4890
+ /**
4891
+ * Would this instance be allowed to use this tool?
4892
+ *
4893
+ * Stage 1 asks and records; nothing is refused yet. The recording is not only
4894
+ * an observation window: `logActivity("tool_call")` sits on the outbound path
4895
+ * only, so today the fleet has no idea what the agent endpoint or the typed
4896
+ * IPC handlers are being asked to do — the one face with no authorization is
4897
+ * also the one face with no telemetry.
4898
+ *
4899
+ * The profile comes from the instance the socket belongs to. Deliberately not
4900
+ * `senderSessionName`, which arrives inside the message: a caller that fills
4901
+ * in its own identity has not been identified.
4902
+ */
4903
+ /**
4904
+ * Say once, at startup, what the new default means for this fleet.
4905
+ *
4906
+ * Nothing is rewritten: an explicit `tool_set: full` is a choice somebody
4907
+ * made, and marking an instance as a coordinator is a judgement about how
4908
+ * their fleet is organised. Both stay theirs — this only makes sure they are
4909
+ * not discovered by an agent failing at three in the morning.
4910
+ */
4911
+ announceToolPermissionsChange() {
4912
+ try {
4913
+ const thirtyDaysAgo = new Date(Date.now() - 30 * 864e5).toISOString().slice(0, 19).replace("T", " ");
4914
+ const recent = [...(this.eventLog?.toolUseByInstance(thirtyDaysAgo) ?? new Map())]
4915
+ .map(([instance, tools]) => ({ instance, tools }));
4916
+ const notice = buildToolPermissionsNotice({
4917
+ defaultsToolSet: this.fleetConfig?.defaults?.tool_set,
4918
+ instances: (this.fleetConfig?.instances ?? {}),
4919
+ recent,
4920
+ });
4921
+ if (notice)
4922
+ this.logger.warn({ notice }, notice);
4923
+ }
4924
+ catch (err) {
4925
+ // Advice is not worth failing a startup over.
4926
+ this.logger.debug({ err }, "tool-permissions: could not build the migration notice");
4927
+ }
4928
+ }
4929
+ checkToolPermission(sink, instanceName, tool) {
4930
+ const profile = resolveToolSet(this.fleetConfig?.instances[instanceName], instanceName);
4931
+ const allowed = mayUseTool(profile, tool);
4932
+ if (!allowed) {
4933
+ this.logger.warn({ sink, instance: instanceName, profile, tool, enforced: true }, "tool-permissions: refused");
4934
+ return { allowed: false, message: toolRefusedMessage(profile, tool) };
4935
+ }
4936
+ this.logger.debug({ sink, instance: instanceName, profile, tool, enforced: true }, "tool-permissions: allowed");
4937
+ return { allowed: true, message: "" };
4938
+ }
4894
4939
  async handleOutboundFromInstance(instanceName, msg) {
4895
4940
  this.touchActivity(instanceName);
4896
4941
  this.setTopicIcon(instanceName, "green");
@@ -4912,6 +4957,19 @@ export class FleetManager {
4912
4957
  this.logger.warn({ instanceName, tool, requestId, fleetRequestId, error }, "Fleet outbound result could not be returned — instance IPC is disconnected");
4913
4958
  }
4914
4959
  };
4960
+ // Sink 1 of 3, and the first thing decided. Every MCP call and every direct
4961
+ // write to channel.sock lands here, whether or not mcp-server was ever
4962
+ // involved — which is why this is the boundary and the tool list the model
4963
+ // was shown is not.
4964
+ //
4965
+ // Above the adapter check on purpose: "retry shortly" is the wrong answer
4966
+ // to a call that will never be allowed, and an agent that believes it is a
4967
+ // timing problem will keep trying.
4968
+ const permitted = this.checkToolPermission("ipc-outbound", instanceName, tool);
4969
+ if (!permitted.allowed) {
4970
+ respond(null, permitted.message);
4971
+ return;
4972
+ }
4915
4973
  if (this.worlds.size === 0) {
4916
4974
  respond(null, "Channel adapters are not ready — retry shortly");
4917
4975
  return;
@@ -5200,6 +5258,58 @@ export class FleetManager {
5200
5258
  threadId: schedule.reply_thread_id ?? undefined,
5201
5259
  }).catch((err) => this.logger.error({ err }, "Failed to send schedule failure notification"));
5202
5260
  }
5261
+ /**
5262
+ * The typed IPC messages, all through one door.
5263
+ *
5264
+ * They used to be five sibling branches on the dispatch, which is why the
5265
+ * permission question had five places to be forgotten. Routing them together
5266
+ * means the check above happens once and cannot be skipped by adding a
5267
+ * sixth — a new type has to appear in `IPC_TYPE_TOOLS` to be dispatched at
5268
+ * all.
5269
+ */
5270
+ /**
5271
+ * Answer a refused typed message the way its handler would have.
5272
+ *
5273
+ * These are request/response over IPC: dropping the message silently leaves
5274
+ * the caller waiting for a reply that never comes, and a hung agent is a
5275
+ * worse failure than a refused one.
5276
+ */
5277
+ refuseTypedIpc(name, msg, message) {
5278
+ const ipc = this.instanceIpcClients.get(name);
5279
+ const fleetRequestId = msg.fleetRequestId;
5280
+ if (!ipc || !fleetRequestId)
5281
+ return;
5282
+ const type = String(msg.type);
5283
+ const responseType = type.startsWith("fleet_schedule_") ? "fleet_schedule_response"
5284
+ : type.startsWith("fleet_decision_") ? "fleet_decision_response"
5285
+ : type === "fleet_task" ? "fleet_task_response"
5286
+ : type === "fleet_set_display_name" ? "fleet_display_name_response"
5287
+ : "fleet_description_response";
5288
+ ipc.send({ type: responseType, fleetRequestId, error: message });
5289
+ }
5290
+ dispatchTypedIpc(name, msg) {
5291
+ const type = String(msg.type);
5292
+ if (type.startsWith("fleet_schedule_")) {
5293
+ this.handleScheduleCrud(name, msg);
5294
+ return;
5295
+ }
5296
+ if (type.startsWith("fleet_decision_")) {
5297
+ this.handleDecisionCrud(name, msg);
5298
+ return;
5299
+ }
5300
+ if (type === "fleet_task") {
5301
+ this.handleTaskCrud(name, msg);
5302
+ return;
5303
+ }
5304
+ if (type === "fleet_set_display_name") {
5305
+ this.handleSetDisplayName(name, msg);
5306
+ return;
5307
+ }
5308
+ if (type === "fleet_set_description") {
5309
+ this.handleSetDescription(name, msg);
5310
+ return;
5311
+ }
5312
+ }
5203
5313
  handleScheduleCrud(instanceName, msg) {
5204
5314
  const fleetRequestId = msg.fleetRequestId;
5205
5315
  const payload = (msg.payload ?? {});
@@ -5207,6 +5317,14 @@ export class FleetManager {
5207
5317
  const ipc = this.instanceIpcClients.get(instanceName);
5208
5318
  if (!ipc)
5209
5319
  return;
5320
+ if (!this.scheduler) {
5321
+ // It did answer before, with whatever TypeError fell out of the try
5322
+ // block — "Cannot read properties of null (reading 'list')" is a stack
5323
+ // trace wearing an error message, and the agent reading it cannot tell
5324
+ // that the fleet simply has no scheduler.
5325
+ ipc.send({ type: "fleet_schedule_response", fleetRequestId, error: "Schedules are unavailable — the fleet scheduler is not running" });
5326
+ return;
5327
+ }
5210
5328
  try {
5211
5329
  let result;
5212
5330
  switch (msg.type) {
@@ -5248,8 +5366,15 @@ export class FleetManager {
5248
5366
  const payload = (msg.payload ?? {});
5249
5367
  const meta = (msg.meta ?? {});
5250
5368
  const ipc = this.instanceIpcClients.get(instanceName);
5251
- if (!ipc || !this.scheduler)
5369
+ if (!ipc)
5370
+ return;
5371
+ if (!this.scheduler) {
5372
+ // Returning silently left the caller waiting for a response that was
5373
+ // never coming. Over IPC that is a hang, and a hang is a worse answer
5374
+ // than an error.
5375
+ ipc.send({ type: "fleet_decision_response", fleetRequestId, error: "Decisions are unavailable — the fleet scheduler is not running" });
5252
5376
  return;
5377
+ }
5253
5378
  const db = this.scheduler.db;
5254
5379
  const projectRoot = meta.working_directory || this.fleetConfig?.instances[instanceName]?.working_directory || "";
5255
5380
  try {
@@ -5523,8 +5648,15 @@ export class FleetManager {
5523
5648
  const payload = (msg.payload ?? {});
5524
5649
  const meta = (msg.meta ?? {});
5525
5650
  const ipc = this.instanceIpcClients.get(instanceName);
5526
- if (!ipc || !this.scheduler)
5651
+ if (!ipc)
5652
+ return;
5653
+ if (!this.scheduler) {
5654
+ // Returning silently left the caller waiting for a response that was
5655
+ // never coming. Over IPC that is a hang, and a hang is a worse answer
5656
+ // than an error.
5657
+ ipc.send({ type: "fleet_task_response", fleetRequestId, error: "The task board is unavailable — the fleet scheduler is not running" });
5527
5658
  return;
5659
+ }
5528
5660
  const db = this.scheduler.db;
5529
5661
  const action = payload.action;
5530
5662
  try {