@songsid/agend 2.1.6-beta.3 → 2.1.6-beta.5
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/dist/agent-endpoint.d.ts +18 -0
- package/dist/agent-endpoint.js +53 -2
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/backend/claude-code.js +24 -0
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/factory.js +4 -1
- package/dist/backend/factory.js.map +1 -1
- package/dist/backend/muse.d.ts +90 -0
- package/dist/backend/muse.js +428 -0
- package/dist/backend/muse.js.map +1 -0
- package/dist/backend/types.d.ts +2 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/access-manager.d.ts +20 -0
- package/dist/channel/access-manager.js +25 -0
- package/dist/channel/access-manager.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +3 -0
- package/dist/channel/adapters/discord.js +13 -10
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/markdown-chunk.d.ts +8 -3
- package/dist/channel/markdown-chunk.js +12 -3
- package/dist/channel/markdown-chunk.js.map +1 -1
- package/dist/channel/mcp-server.js +9 -12
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.d.ts +6 -2
- package/dist/channel/mcp-tools.js +6 -36
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/tool-router.js +12 -2
- package/dist/channel/tool-router.js.map +1 -1
- package/dist/config-validator.js +21 -5
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.js +19 -6
- package/dist/daemon.js.map +1 -1
- package/dist/event-log.d.ts +9 -0
- package/dist/event-log.js +21 -0
- package/dist/event-log.js.map +1 -1
- package/dist/fleet-manager.d.ts +64 -0
- package/dist/fleet-manager.js +233 -26
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +11 -1
- package/dist/instance-lifecycle.js +8 -4
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/logger.js +42 -25
- package/dist/logger.js.map +1 -1
- package/dist/outbound-schemas.d.ts +1 -0
- package/dist/outbound-schemas.js +1 -1
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/quickstart-api.d.ts +3 -0
- package/dist/quickstart-api.js +1 -0
- package/dist/quickstart-api.js.map +1 -1
- package/dist/scheduler/db.js +17 -5
- package/dist/scheduler/db.js.map +1 -1
- package/dist/scheduler/db.test.js +34 -1
- package/dist/scheduler/db.test.js.map +1 -1
- package/dist/scheduler/types.d.ts +4 -0
- package/dist/scheduler/types.js.map +1 -1
- package/dist/setup-wizard.js +4 -0
- package/dist/setup-wizard.js.map +1 -1
- package/dist/steer-capability.js +4 -1
- package/dist/steer-capability.js.map +1 -1
- package/dist/tmux-manager.js +29 -4
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tool-permissions-notice.d.ts +38 -0
- package/dist/tool-permissions-notice.js +92 -0
- package/dist/tool-permissions-notice.js.map +1 -0
- package/dist/tool-permissions.d.ts +93 -0
- package/dist/tool-permissions.js +216 -0
- package/dist/tool-permissions.js.map +1 -0
- package/dist/topic-commands.js +1 -0
- package/dist/topic-commands.js.map +1 -1
- package/dist/web-api.js +2 -1
- package/dist/web-api.js.map +1 -1
- package/dist/web-terminal.d.ts +30 -1
- package/dist/web-terminal.js +86 -3
- package/dist/web-terminal.js.map +1 -1
- package/package.json +2 -1
package/dist/event-log.d.ts
CHANGED
|
@@ -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 = [];
|
package/dist/event-log.js.map
CHANGED
|
@@ -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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"}
|
package/dist/fleet-manager.d.ts
CHANGED
|
@@ -757,12 +757,43 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
757
757
|
* Pure: callers rely on being able to ask before claiming the dedup key.
|
|
758
758
|
*/
|
|
759
759
|
private botMessageDropReason;
|
|
760
|
+
/**
|
|
761
|
+
* Say so when an adapter's access mode is coming from its state file rather
|
|
762
|
+
* than from fleet.yaml. The state file wins by design — a pairing done at
|
|
763
|
+
* runtime has to survive a restart — but that also means an edit to
|
|
764
|
+
* `access.mode` silently does nothing, which is indistinguishable from the
|
|
765
|
+
* fleet not having reloaded. Naming the file is what makes it fixable.
|
|
766
|
+
*/
|
|
767
|
+
private warnIfAccessModeOverridden;
|
|
760
768
|
private handleInboundMessage;
|
|
761
769
|
/** Handle outbound tool calls from a daemon instance */
|
|
762
770
|
/** Warn (but don't block) when rate limits are high. 30-min debounce per instance. */
|
|
763
771
|
private rateLimitWarnedAt;
|
|
764
772
|
private warnIfRateLimited;
|
|
765
773
|
/** Handle outbound tool calls from a daemon instance */
|
|
774
|
+
/**
|
|
775
|
+
* Would this instance be allowed to use this tool?
|
|
776
|
+
*
|
|
777
|
+
* Stage 1 asks and records; nothing is refused yet. The recording is not only
|
|
778
|
+
* an observation window: `logActivity("tool_call")` sits on the outbound path
|
|
779
|
+
* only, so today the fleet has no idea what the agent endpoint or the typed
|
|
780
|
+
* IPC handlers are being asked to do — the one face with no authorization is
|
|
781
|
+
* also the one face with no telemetry.
|
|
782
|
+
*
|
|
783
|
+
* The profile comes from the instance the socket belongs to. Deliberately not
|
|
784
|
+
* `senderSessionName`, which arrives inside the message: a caller that fills
|
|
785
|
+
* in its own identity has not been identified.
|
|
786
|
+
*/
|
|
787
|
+
/**
|
|
788
|
+
* Say once, at startup, what the new default means for this fleet.
|
|
789
|
+
*
|
|
790
|
+
* Nothing is rewritten: an explicit `tool_set: full` is a choice somebody
|
|
791
|
+
* made, and marking an instance as a coordinator is a judgement about how
|
|
792
|
+
* their fleet is organised. Both stay theirs — this only makes sure they are
|
|
793
|
+
* not discovered by an agent failing at three in the morning.
|
|
794
|
+
*/
|
|
795
|
+
private announceToolPermissionsChange;
|
|
796
|
+
private checkToolPermission;
|
|
766
797
|
private handleOutboundFromInstance;
|
|
767
798
|
/** Side effects of a routed reply: cancel-button lifecycle, logs, SSE, chat log. */
|
|
768
799
|
private afterReplyRouted;
|
|
@@ -798,6 +829,24 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
798
829
|
private scheduleSourceAdapter;
|
|
799
830
|
private notifySourceTopic;
|
|
800
831
|
private notifyScheduleFailure;
|
|
832
|
+
/**
|
|
833
|
+
* The typed IPC messages, all through one door.
|
|
834
|
+
*
|
|
835
|
+
* They used to be five sibling branches on the dispatch, which is why the
|
|
836
|
+
* permission question had five places to be forgotten. Routing them together
|
|
837
|
+
* means the check above happens once and cannot be skipped by adding a
|
|
838
|
+
* sixth — a new type has to appear in `IPC_TYPE_TOOLS` to be dispatched at
|
|
839
|
+
* all.
|
|
840
|
+
*/
|
|
841
|
+
/**
|
|
842
|
+
* Answer a refused typed message the way its handler would have.
|
|
843
|
+
*
|
|
844
|
+
* These are request/response over IPC: dropping the message silently leaves
|
|
845
|
+
* the caller waiting for a reply that never comes, and a hung agent is a
|
|
846
|
+
* worse failure than a refused one.
|
|
847
|
+
*/
|
|
848
|
+
private refuseTypedIpc;
|
|
849
|
+
private dispatchTypedIpc;
|
|
801
850
|
private handleScheduleCrud;
|
|
802
851
|
private handleDecisionCrud;
|
|
803
852
|
/** Resolve display name for an instance, fallback to instance name. */
|
|
@@ -1043,6 +1092,21 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
1043
1092
|
* (or an assist for one they stopped) must not stay clickable for the rest
|
|
1044
1093
|
* of its 15 minutes.
|
|
1045
1094
|
*/
|
|
1095
|
+
/**
|
|
1096
|
+
* Collapse every still-armed button prompt, for a fleet that is going away.
|
|
1097
|
+
*
|
|
1098
|
+
* The map is in memory; the buttons are in the chat for up to 24 hours. A
|
|
1099
|
+
* restart therefore leaves them looking live — pressing one takes the stale
|
|
1100
|
+
* branch, which acknowledges the click and drops the dismissal without
|
|
1101
|
+
* saying so. Retiring them here means the user meets a spent prompt instead
|
|
1102
|
+
* of a live-looking dead one.
|
|
1103
|
+
*
|
|
1104
|
+
* Best effort and bounded. Each collapse is a platform call, a day of tips
|
|
1105
|
+
* can be many of them, and shutdown still has instances to stop. Whatever
|
|
1106
|
+
* has not finished by the deadline is abandoned — that is exactly today's
|
|
1107
|
+
* behaviour, so the budget can only leave things no worse than before.
|
|
1108
|
+
*/
|
|
1109
|
+
private retirePendingNoncePrompts;
|
|
1046
1110
|
clearNoncePromptsForInstance(instanceName: string): void;
|
|
1047
1111
|
/** A clean exit is intentional from the CLI's perspective, but often not from
|
|
1048
1112
|
* the operator's. Keep the instance notice passive and put the action in the
|
package/dist/fleet-manager.js
CHANGED
|
@@ -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";
|
|
@@ -215,6 +217,8 @@ const CLEAR_CONFIRM_TIMEOUT_MS = 15_000;
|
|
|
215
217
|
/** Default lifetime for long-lived nonce prompts (clear overrides this to 15s). */
|
|
216
218
|
const NONCE_BUTTON_TIMEOUT_MS = 15 * 60_000;
|
|
217
219
|
const TIP_BUTTON_TIMEOUT_MS = 24 * 60 * 60_000;
|
|
220
|
+
/** How long shutdown will spend retiring still-armed button prompts. */
|
|
221
|
+
const NONCE_RETIRE_BUDGET_MS = 5_000;
|
|
218
222
|
const CLI_ENV_TTL_MS = 24 * 60 * 60 * 1000; // hard validity bound for the cached CLI env
|
|
219
223
|
/**
|
|
220
224
|
* How old the cached CLI env may be before `/model` re-probes it live.
|
|
@@ -615,6 +619,7 @@ export class FleetManager {
|
|
|
615
619
|
// watching, and the fleet starting is the moment there is finally a process
|
|
616
620
|
// around to notice. Never throws: a lease that cannot be resolved blocks
|
|
617
621
|
// the next tunnel and says so, it does not block the fleet.
|
|
622
|
+
this.announceToolPermissionsChange();
|
|
618
623
|
void reapStaleTunnel(this.dataDir)
|
|
619
624
|
.then(outcome => {
|
|
620
625
|
if (outcome.kind === "manual")
|
|
@@ -3164,7 +3169,9 @@ export class FleetManager {
|
|
|
3164
3169
|
}
|
|
3165
3170
|
const accessDir = join(this.dataDir, "access");
|
|
3166
3171
|
mkdirSync(accessDir, { recursive: true });
|
|
3167
|
-
const
|
|
3172
|
+
const accessStatePath = join(accessDir, "access.json");
|
|
3173
|
+
const accessManager = new AccessManager(channelConfig.access ?? DEFAULT_OPEN_ACCESS, accessStatePath);
|
|
3174
|
+
this.warnIfAccessModeOverridden(accessManager, accessStatePath);
|
|
3168
3175
|
this.accessManager = accessManager;
|
|
3169
3176
|
const inboxDir = join(this.dataDir, "inbox");
|
|
3170
3177
|
mkdirSync(inboxDir, { recursive: true });
|
|
@@ -3556,7 +3563,9 @@ export class FleetManager {
|
|
|
3556
3563
|
}
|
|
3557
3564
|
const accessDir = join(this.dataDir, "access");
|
|
3558
3565
|
mkdirSync(accessDir, { recursive: true });
|
|
3559
|
-
const
|
|
3566
|
+
const accessStatePath = join(accessDir, `access-${adapterId}.json`);
|
|
3567
|
+
const accessManager = new AccessManager(channelConfig.access ?? DEFAULT_OPEN_ACCESS, accessStatePath);
|
|
3568
|
+
this.warnIfAccessModeOverridden(accessManager, accessStatePath);
|
|
3560
3569
|
const inboxDir = join(this.dataDir, "inbox");
|
|
3561
3570
|
mkdirSync(inboxDir, { recursive: true });
|
|
3562
3571
|
const adapter = await createAdapter(channelConfig, {
|
|
@@ -3934,22 +3943,15 @@ export class FleetManager {
|
|
|
3934
3943
|
}
|
|
3935
3944
|
await this.handleOutboundFromInstance(name, msg);
|
|
3936
3945
|
}
|
|
3937
|
-
else if (msg.type
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
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);
|
|
3946
|
+
else if (toolForIpcType(msg.type) !== null) {
|
|
3947
|
+
// Sink 2 of 3. Each of these types IS a tool and reaches its own
|
|
3948
|
+
// handler without passing through the outbound path — which is how
|
|
3949
|
+
// `update_decision`, a coordinator-only tool, kept a way through.
|
|
3950
|
+
const typed = this.checkToolPermission("ipc-typed", name, toolForIpcType(msg.type));
|
|
3951
|
+
if (typed.allowed)
|
|
3952
|
+
this.dispatchTypedIpc(name, msg);
|
|
3953
|
+
else
|
|
3954
|
+
this.refuseTypedIpc(name, msg, typed.message);
|
|
3953
3955
|
}
|
|
3954
3956
|
else if (msg.type === "instance_process_state") {
|
|
3955
3957
|
this.cacheInstanceProcessStatus(name, msg.status);
|
|
@@ -4318,13 +4320,36 @@ export class FleetManager {
|
|
|
4318
4320
|
const target = this.routing.resolve(threadId);
|
|
4319
4321
|
if (!target)
|
|
4320
4322
|
return "fleet topic: no instance routed for this thread";
|
|
4321
|
-
// Fleet topic:
|
|
4323
|
+
// Fleet topic: this gate lets the copy through when the adapter is open OR
|
|
4324
|
+
// collab is on for the instance. "Through" is not "delivered" — access
|
|
4325
|
+
// control runs next, and the two arms are not symmetric there:
|
|
4326
|
+
//
|
|
4327
|
+
// open adapter → isAllowed() returns true outright: really admitted.
|
|
4328
|
+
// locked + collab → the bot's user id still has to be on the allowlist,
|
|
4329
|
+
// so the message is normally refused a few lines later
|
|
4330
|
+
// under "Access DENIED for non-allowed user".
|
|
4331
|
+
//
|
|
4332
|
+
// Collab alone therefore does not admit a bot on a locked adapter; it only
|
|
4333
|
+
// declines to drop the copy here and leaves the decision to access control.
|
|
4322
4334
|
const isOpen = this.getChannelConfig(msg.adapterId)?.access?.mode === "open";
|
|
4323
4335
|
if (!isOpen && !this.collabInstances.has(target.name)) {
|
|
4324
4336
|
return `fleet topic: adapter not open and collab off for ${target.name}`;
|
|
4325
4337
|
}
|
|
4326
4338
|
return null;
|
|
4327
4339
|
}
|
|
4340
|
+
/**
|
|
4341
|
+
* Say so when an adapter's access mode is coming from its state file rather
|
|
4342
|
+
* than from fleet.yaml. The state file wins by design — a pairing done at
|
|
4343
|
+
* runtime has to survive a restart — but that also means an edit to
|
|
4344
|
+
* `access.mode` silently does nothing, which is indistinguishable from the
|
|
4345
|
+
* fleet not having reloaded. Naming the file is what makes it fixable.
|
|
4346
|
+
*/
|
|
4347
|
+
warnIfAccessModeOverridden(accessManager, statePath) {
|
|
4348
|
+
const configured = accessManager.overriddenConfigMode();
|
|
4349
|
+
if (!configured)
|
|
4350
|
+
return;
|
|
4351
|
+
this.logger.warn({ statePath, configured, inEffect: accessManager.getMode() }, `access mode "${accessManager.getMode()}" comes from the state file and overrides "${configured}" in fleet.yaml; delete ${statePath} to go back to the configured value`);
|
|
4352
|
+
}
|
|
4328
4353
|
async handleInboundMessage(msg) {
|
|
4329
4354
|
const threadId = msg.threadId || undefined;
|
|
4330
4355
|
this.logger.debug({ source: msg.source, chatId: msg.chatId, threadId, userId: msg.userId, isBotMessage: msg.isBotMessage, textLen: (msg.text ?? "").length, text: (msg.text ?? "").slice(0, 80) }, "handleInboundMessage entry");
|
|
@@ -4891,6 +4916,55 @@ export class FleetManager {
|
|
|
4891
4916
|
}
|
|
4892
4917
|
}
|
|
4893
4918
|
/** Handle outbound tool calls from a daemon instance */
|
|
4919
|
+
/**
|
|
4920
|
+
* Would this instance be allowed to use this tool?
|
|
4921
|
+
*
|
|
4922
|
+
* Stage 1 asks and records; nothing is refused yet. The recording is not only
|
|
4923
|
+
* an observation window: `logActivity("tool_call")` sits on the outbound path
|
|
4924
|
+
* only, so today the fleet has no idea what the agent endpoint or the typed
|
|
4925
|
+
* IPC handlers are being asked to do — the one face with no authorization is
|
|
4926
|
+
* also the one face with no telemetry.
|
|
4927
|
+
*
|
|
4928
|
+
* The profile comes from the instance the socket belongs to. Deliberately not
|
|
4929
|
+
* `senderSessionName`, which arrives inside the message: a caller that fills
|
|
4930
|
+
* in its own identity has not been identified.
|
|
4931
|
+
*/
|
|
4932
|
+
/**
|
|
4933
|
+
* Say once, at startup, what the new default means for this fleet.
|
|
4934
|
+
*
|
|
4935
|
+
* Nothing is rewritten: an explicit `tool_set: full` is a choice somebody
|
|
4936
|
+
* made, and marking an instance as a coordinator is a judgement about how
|
|
4937
|
+
* their fleet is organised. Both stay theirs — this only makes sure they are
|
|
4938
|
+
* not discovered by an agent failing at three in the morning.
|
|
4939
|
+
*/
|
|
4940
|
+
announceToolPermissionsChange() {
|
|
4941
|
+
try {
|
|
4942
|
+
const thirtyDaysAgo = new Date(Date.now() - 30 * 864e5).toISOString().slice(0, 19).replace("T", " ");
|
|
4943
|
+
const recent = [...(this.eventLog?.toolUseByInstance(thirtyDaysAgo) ?? new Map())]
|
|
4944
|
+
.map(([instance, tools]) => ({ instance, tools }));
|
|
4945
|
+
const notice = buildToolPermissionsNotice({
|
|
4946
|
+
defaultsToolSet: this.fleetConfig?.defaults?.tool_set,
|
|
4947
|
+
instances: (this.fleetConfig?.instances ?? {}),
|
|
4948
|
+
recent,
|
|
4949
|
+
});
|
|
4950
|
+
if (notice)
|
|
4951
|
+
this.logger.warn({ notice }, notice);
|
|
4952
|
+
}
|
|
4953
|
+
catch (err) {
|
|
4954
|
+
// Advice is not worth failing a startup over.
|
|
4955
|
+
this.logger.debug({ err }, "tool-permissions: could not build the migration notice");
|
|
4956
|
+
}
|
|
4957
|
+
}
|
|
4958
|
+
checkToolPermission(sink, instanceName, tool) {
|
|
4959
|
+
const profile = resolveToolSet(this.fleetConfig?.instances[instanceName], instanceName);
|
|
4960
|
+
const allowed = mayUseTool(profile, tool);
|
|
4961
|
+
if (!allowed) {
|
|
4962
|
+
this.logger.warn({ sink, instance: instanceName, profile, tool, enforced: true }, "tool-permissions: refused");
|
|
4963
|
+
return { allowed: false, message: toolRefusedMessage(profile, tool) };
|
|
4964
|
+
}
|
|
4965
|
+
this.logger.debug({ sink, instance: instanceName, profile, tool, enforced: true }, "tool-permissions: allowed");
|
|
4966
|
+
return { allowed: true, message: "" };
|
|
4967
|
+
}
|
|
4894
4968
|
async handleOutboundFromInstance(instanceName, msg) {
|
|
4895
4969
|
this.touchActivity(instanceName);
|
|
4896
4970
|
this.setTopicIcon(instanceName, "green");
|
|
@@ -4912,6 +4986,19 @@ export class FleetManager {
|
|
|
4912
4986
|
this.logger.warn({ instanceName, tool, requestId, fleetRequestId, error }, "Fleet outbound result could not be returned — instance IPC is disconnected");
|
|
4913
4987
|
}
|
|
4914
4988
|
};
|
|
4989
|
+
// Sink 1 of 3, and the first thing decided. Every MCP call and every direct
|
|
4990
|
+
// write to channel.sock lands here, whether or not mcp-server was ever
|
|
4991
|
+
// involved — which is why this is the boundary and the tool list the model
|
|
4992
|
+
// was shown is not.
|
|
4993
|
+
//
|
|
4994
|
+
// Above the adapter check on purpose: "retry shortly" is the wrong answer
|
|
4995
|
+
// to a call that will never be allowed, and an agent that believes it is a
|
|
4996
|
+
// timing problem will keep trying.
|
|
4997
|
+
const permitted = this.checkToolPermission("ipc-outbound", instanceName, tool);
|
|
4998
|
+
if (!permitted.allowed) {
|
|
4999
|
+
respond(null, permitted.message);
|
|
5000
|
+
return;
|
|
5001
|
+
}
|
|
4915
5002
|
if (this.worlds.size === 0) {
|
|
4916
5003
|
respond(null, "Channel adapters are not ready — retry shortly");
|
|
4917
5004
|
return;
|
|
@@ -5169,6 +5256,15 @@ export class FleetManager {
|
|
|
5169
5256
|
*/
|
|
5170
5257
|
scheduleSourceAdapter(schedule) {
|
|
5171
5258
|
const chatId = String(schedule.reply_chat_id);
|
|
5259
|
+
// New schedules carry the adapter that owned the source chat at creation.
|
|
5260
|
+
// Keep that persona across source-instance rebinding, but never trust a
|
|
5261
|
+
// stale adapter after the chat has moved to another world.
|
|
5262
|
+
const persistedWorld = schedule.reply_adapter_id
|
|
5263
|
+
? this.worlds.get(schedule.reply_adapter_id)
|
|
5264
|
+
: undefined;
|
|
5265
|
+
if (persistedWorld && String(persistedWorld.groupId) === chatId) {
|
|
5266
|
+
return persistedWorld.adapter;
|
|
5267
|
+
}
|
|
5172
5268
|
const sourceAdapterId = schedule.source ? this.getInstanceAdapterId(schedule.source) : undefined;
|
|
5173
5269
|
const sourceWorld = sourceAdapterId ? this.worlds.get(sourceAdapterId) : undefined;
|
|
5174
5270
|
if (sourceWorld && String(sourceWorld.groupId) === chatId)
|
|
@@ -5200,6 +5296,58 @@ export class FleetManager {
|
|
|
5200
5296
|
threadId: schedule.reply_thread_id ?? undefined,
|
|
5201
5297
|
}).catch((err) => this.logger.error({ err }, "Failed to send schedule failure notification"));
|
|
5202
5298
|
}
|
|
5299
|
+
/**
|
|
5300
|
+
* The typed IPC messages, all through one door.
|
|
5301
|
+
*
|
|
5302
|
+
* They used to be five sibling branches on the dispatch, which is why the
|
|
5303
|
+
* permission question had five places to be forgotten. Routing them together
|
|
5304
|
+
* means the check above happens once and cannot be skipped by adding a
|
|
5305
|
+
* sixth — a new type has to appear in `IPC_TYPE_TOOLS` to be dispatched at
|
|
5306
|
+
* all.
|
|
5307
|
+
*/
|
|
5308
|
+
/**
|
|
5309
|
+
* Answer a refused typed message the way its handler would have.
|
|
5310
|
+
*
|
|
5311
|
+
* These are request/response over IPC: dropping the message silently leaves
|
|
5312
|
+
* the caller waiting for a reply that never comes, and a hung agent is a
|
|
5313
|
+
* worse failure than a refused one.
|
|
5314
|
+
*/
|
|
5315
|
+
refuseTypedIpc(name, msg, message) {
|
|
5316
|
+
const ipc = this.instanceIpcClients.get(name);
|
|
5317
|
+
const fleetRequestId = msg.fleetRequestId;
|
|
5318
|
+
if (!ipc || !fleetRequestId)
|
|
5319
|
+
return;
|
|
5320
|
+
const type = String(msg.type);
|
|
5321
|
+
const responseType = type.startsWith("fleet_schedule_") ? "fleet_schedule_response"
|
|
5322
|
+
: type.startsWith("fleet_decision_") ? "fleet_decision_response"
|
|
5323
|
+
: type === "fleet_task" ? "fleet_task_response"
|
|
5324
|
+
: type === "fleet_set_display_name" ? "fleet_display_name_response"
|
|
5325
|
+
: "fleet_description_response";
|
|
5326
|
+
ipc.send({ type: responseType, fleetRequestId, error: message });
|
|
5327
|
+
}
|
|
5328
|
+
dispatchTypedIpc(name, msg) {
|
|
5329
|
+
const type = String(msg.type);
|
|
5330
|
+
if (type.startsWith("fleet_schedule_")) {
|
|
5331
|
+
this.handleScheduleCrud(name, msg);
|
|
5332
|
+
return;
|
|
5333
|
+
}
|
|
5334
|
+
if (type.startsWith("fleet_decision_")) {
|
|
5335
|
+
this.handleDecisionCrud(name, msg);
|
|
5336
|
+
return;
|
|
5337
|
+
}
|
|
5338
|
+
if (type === "fleet_task") {
|
|
5339
|
+
this.handleTaskCrud(name, msg);
|
|
5340
|
+
return;
|
|
5341
|
+
}
|
|
5342
|
+
if (type === "fleet_set_display_name") {
|
|
5343
|
+
this.handleSetDisplayName(name, msg);
|
|
5344
|
+
return;
|
|
5345
|
+
}
|
|
5346
|
+
if (type === "fleet_set_description") {
|
|
5347
|
+
this.handleSetDescription(name, msg);
|
|
5348
|
+
return;
|
|
5349
|
+
}
|
|
5350
|
+
}
|
|
5203
5351
|
handleScheduleCrud(instanceName, msg) {
|
|
5204
5352
|
const fleetRequestId = msg.fleetRequestId;
|
|
5205
5353
|
const payload = (msg.payload ?? {});
|
|
@@ -5207,6 +5355,14 @@ export class FleetManager {
|
|
|
5207
5355
|
const ipc = this.instanceIpcClients.get(instanceName);
|
|
5208
5356
|
if (!ipc)
|
|
5209
5357
|
return;
|
|
5358
|
+
if (!this.scheduler) {
|
|
5359
|
+
// It did answer before, with whatever TypeError fell out of the try
|
|
5360
|
+
// block — "Cannot read properties of null (reading 'list')" is a stack
|
|
5361
|
+
// trace wearing an error message, and the agent reading it cannot tell
|
|
5362
|
+
// that the fleet simply has no scheduler.
|
|
5363
|
+
ipc.send({ type: "fleet_schedule_response", fleetRequestId, error: "Schedules are unavailable — the fleet scheduler is not running" });
|
|
5364
|
+
return;
|
|
5365
|
+
}
|
|
5210
5366
|
try {
|
|
5211
5367
|
let result;
|
|
5212
5368
|
switch (msg.type) {
|
|
@@ -5219,6 +5375,7 @@ export class FleetManager {
|
|
|
5219
5375
|
target: payload.target || instanceName,
|
|
5220
5376
|
reply_chat_id: meta.chat_id,
|
|
5221
5377
|
reply_thread_id: meta.thread_id || null,
|
|
5378
|
+
reply_adapter_id: meta.adapter_id || null,
|
|
5222
5379
|
label: payload.label,
|
|
5223
5380
|
timezone: payload.timezone,
|
|
5224
5381
|
silent: !!(payload.silent),
|
|
@@ -5248,8 +5405,15 @@ export class FleetManager {
|
|
|
5248
5405
|
const payload = (msg.payload ?? {});
|
|
5249
5406
|
const meta = (msg.meta ?? {});
|
|
5250
5407
|
const ipc = this.instanceIpcClients.get(instanceName);
|
|
5251
|
-
if (!ipc
|
|
5408
|
+
if (!ipc)
|
|
5252
5409
|
return;
|
|
5410
|
+
if (!this.scheduler) {
|
|
5411
|
+
// Returning silently left the caller waiting for a response that was
|
|
5412
|
+
// never coming. Over IPC that is a hang, and a hang is a worse answer
|
|
5413
|
+
// than an error.
|
|
5414
|
+
ipc.send({ type: "fleet_decision_response", fleetRequestId, error: "Decisions are unavailable — the fleet scheduler is not running" });
|
|
5415
|
+
return;
|
|
5416
|
+
}
|
|
5253
5417
|
const db = this.scheduler.db;
|
|
5254
5418
|
const projectRoot = meta.working_directory || this.fleetConfig?.instances[instanceName]?.working_directory || "";
|
|
5255
5419
|
try {
|
|
@@ -5523,8 +5687,15 @@ export class FleetManager {
|
|
|
5523
5687
|
const payload = (msg.payload ?? {});
|
|
5524
5688
|
const meta = (msg.meta ?? {});
|
|
5525
5689
|
const ipc = this.instanceIpcClients.get(instanceName);
|
|
5526
|
-
if (!ipc
|
|
5690
|
+
if (!ipc)
|
|
5527
5691
|
return;
|
|
5692
|
+
if (!this.scheduler) {
|
|
5693
|
+
// Returning silently left the caller waiting for a response that was
|
|
5694
|
+
// never coming. Over IPC that is a hang, and a hang is a worse answer
|
|
5695
|
+
// than an error.
|
|
5696
|
+
ipc.send({ type: "fleet_task_response", fleetRequestId, error: "The task board is unavailable — the fleet scheduler is not running" });
|
|
5697
|
+
return;
|
|
5698
|
+
}
|
|
5528
5699
|
const db = this.scheduler.db;
|
|
5529
5700
|
const action = payload.action;
|
|
5530
5701
|
try {
|
|
@@ -6872,6 +7043,39 @@ export class FleetManager {
|
|
|
6872
7043
|
* (or an assist for one they stopped) must not stay clickable for the rest
|
|
6873
7044
|
* of its 15 minutes.
|
|
6874
7045
|
*/
|
|
7046
|
+
/**
|
|
7047
|
+
* Collapse every still-armed button prompt, for a fleet that is going away.
|
|
7048
|
+
*
|
|
7049
|
+
* The map is in memory; the buttons are in the chat for up to 24 hours. A
|
|
7050
|
+
* restart therefore leaves them looking live — pressing one takes the stale
|
|
7051
|
+
* branch, which acknowledges the click and drops the dismissal without
|
|
7052
|
+
* saying so. Retiring them here means the user meets a spent prompt instead
|
|
7053
|
+
* of a live-looking dead one.
|
|
7054
|
+
*
|
|
7055
|
+
* Best effort and bounded. Each collapse is a platform call, a day of tips
|
|
7056
|
+
* can be many of them, and shutdown still has instances to stop. Whatever
|
|
7057
|
+
* has not finished by the deadline is abandoned — that is exactly today's
|
|
7058
|
+
* behaviour, so the budget can only leave things no worse than before.
|
|
7059
|
+
*/
|
|
7060
|
+
async retirePendingNoncePrompts(budgetMs = NONCE_RETIRE_BUDGET_MS) {
|
|
7061
|
+
const entries = [...this.pendingNonceButtons.values()];
|
|
7062
|
+
this.pendingNonceButtons.clear();
|
|
7063
|
+
for (const entry of entries)
|
|
7064
|
+
if (entry.timer)
|
|
7065
|
+
clearTimeout(entry.timer);
|
|
7066
|
+
const collapses = entries
|
|
7067
|
+
.filter(entry => entry.messageId && entry.adapter.editMessageRemoveButtons)
|
|
7068
|
+
.map(entry => entry.adapter.editMessageRemoveButtons(entry.chatId, entry.messageId, entry.expiredText, entry.threadId).catch(err => this.logger.debug({ err, instanceName: entry.instanceName, prefix: entry.prefix }, "Failed to retire button prompt during shutdown")));
|
|
7069
|
+
if (!collapses.length)
|
|
7070
|
+
return;
|
|
7071
|
+
await Promise.race([
|
|
7072
|
+
Promise.allSettled(collapses),
|
|
7073
|
+
new Promise(resolve => {
|
|
7074
|
+
const timer = setTimeout(resolve, budgetMs);
|
|
7075
|
+
timer.unref?.();
|
|
7076
|
+
}),
|
|
7077
|
+
]);
|
|
7078
|
+
}
|
|
6875
7079
|
clearNoncePromptsForInstance(instanceName) {
|
|
6876
7080
|
for (const [nonce, entry] of this.pendingNonceButtons) {
|
|
6877
7081
|
if (entry.instanceName !== instanceName)
|
|
@@ -8656,6 +8860,8 @@ export class FleetManager {
|
|
|
8656
8860
|
// Grok reads AGENTS.md project docs; agy reads .agents/agents.md — the
|
|
8657
8861
|
// same files their writeConfig() appends fleet instructions to.
|
|
8658
8862
|
"grok": "AGENTS.md",
|
|
8863
|
+
// muse init scaffolds AGENTS.md and the binary reads it as project rules.
|
|
8864
|
+
"muse": "AGENTS.md",
|
|
8659
8865
|
"antigravity": ".agents/agents.md",
|
|
8660
8866
|
"mock": "CLAUDE.md",
|
|
8661
8867
|
};
|
|
@@ -8731,6 +8937,9 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
8731
8937
|
// vendor-canonical location is .grok/skills.
|
|
8732
8938
|
"opencode": [".agents", "skills"],
|
|
8733
8939
|
"grok": [".grok", "skills"],
|
|
8940
|
+
// Live-verified on muse 1.3.0: `muse skills list --source project` sees
|
|
8941
|
+
// .agents/skills and ignores .muse/skills.
|
|
8942
|
+
"muse": [".agents", "skills"],
|
|
8734
8943
|
"antigravity": [".agents", "skills"],
|
|
8735
8944
|
};
|
|
8736
8945
|
/** Copy general-knowledge steering + all role-eligible skills to General. */
|
|
@@ -10470,11 +10679,9 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
10470
10679
|
for (const pending of this.pendingClassicStarts.values())
|
|
10471
10680
|
clearTimeout(pending.timer);
|
|
10472
10681
|
this.pendingClassicStarts.clear();
|
|
10473
|
-
|
|
10474
|
-
|
|
10475
|
-
|
|
10476
|
-
}
|
|
10477
|
-
this.pendingNonceButtons.clear();
|
|
10682
|
+
// Adapters are still connected here — they are stopped further down — so
|
|
10683
|
+
// this is the last moment the prompts can be collapsed.
|
|
10684
|
+
await this.retirePendingNoncePrompts();
|
|
10478
10685
|
this.topicArchiver.stop();
|
|
10479
10686
|
this.scheduler?.shutdown();
|
|
10480
10687
|
// Stop instances in parallel batches to avoid long sequential waits.
|