@syengup/friday-channel-next 0.1.36 → 0.1.38

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.
Files changed (124) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/src/agent/dispatch-bridge.d.ts +1 -1
  3. package/dist/src/agent/node-pairing-bridge.d.ts +11 -8
  4. package/dist/src/agent/node-pairing-bridge.js +6 -2
  5. package/dist/src/agent/operator-scope.d.ts +19 -0
  6. package/dist/src/agent/operator-scope.js +54 -0
  7. package/dist/src/agent/subagent-registry.js +0 -3
  8. package/dist/src/channel-actions.js +3 -1
  9. package/dist/src/channel.js +0 -2
  10. package/dist/src/collect-message-media-paths.js +10 -1
  11. package/dist/src/friday-session.js +34 -10
  12. package/dist/src/history/normalize-message.js +22 -8
  13. package/dist/src/http/handlers/agent-config.js +10 -4
  14. package/dist/src/http/handlers/cancel.js +4 -2
  15. package/dist/src/http/handlers/device-approve.js +3 -1
  16. package/dist/src/http/handlers/files-download.js +6 -8
  17. package/dist/src/http/handlers/files.js +1 -1
  18. package/dist/src/http/handlers/health.js +18 -4
  19. package/dist/src/http/handlers/history-messages.js +1 -1
  20. package/dist/src/http/handlers/history-sessions.js +5 -3
  21. package/dist/src/http/handlers/messages.js +34 -11
  22. package/dist/src/http/handlers/models-list.js +1 -1
  23. package/dist/src/http/handlers/nodes-approve.js +1 -6
  24. package/dist/src/http/handlers/plugin-info.js +1 -1
  25. package/dist/src/http/server.js +4 -2
  26. package/dist/src/link-preview/og-parse.js +3 -1
  27. package/dist/src/plugin-install-info.js +4 -1
  28. package/dist/src/session/session-manager.js +9 -3
  29. package/dist/src/session-usage-store.js +3 -1
  30. package/dist/src/skills-discovery.d.ts +5 -4
  31. package/dist/src/skills-discovery.js +27 -22
  32. package/dist/src/sse/offline-queue.js +4 -1
  33. package/dist/src/tool-catalog.js +2 -3
  34. package/dist/src/upgrade-runtime.d.ts +1 -1
  35. package/dist/src/version.js +3 -1
  36. package/index.ts +43 -35
  37. package/install.js +131 -43
  38. package/package.json +10 -1
  39. package/src/agent/abort-run.ts +2 -3
  40. package/src/agent/dispatch-bridge.ts +2 -1
  41. package/src/agent/media-bridge.ts +9 -2
  42. package/src/agent/node-pairing-bridge.ts +29 -15
  43. package/src/agent/operator-scope.test.ts +66 -0
  44. package/src/agent/operator-scope.ts +63 -0
  45. package/src/agent/run-usage-accumulator.ts +4 -2
  46. package/src/agent/subagent-registry.ts +0 -4
  47. package/src/agent-run-context-bridge.ts +3 -1
  48. package/src/channel-actions.test.ts +10 -4
  49. package/src/channel-actions.ts +3 -1
  50. package/src/channel.outbound.test.ts +18 -4
  51. package/src/channel.ts +121 -123
  52. package/src/collect-message-media-paths.ts +15 -6
  53. package/src/config.ts +1 -4
  54. package/src/e2e/agents-list.e2e.test.ts +9 -2
  55. package/src/e2e/attachments-inbound.e2e.test.ts +5 -1
  56. package/src/e2e/attachments-outbound.e2e.test.ts +7 -2
  57. package/src/e2e/auto-approve.integration.test.ts +13 -7
  58. package/src/e2e/cancel-reconnect-errors.e2e.test.ts +18 -3
  59. package/src/e2e/connect-and-connected.e2e.test.ts +5 -1
  60. package/src/e2e/offline-replay.e2e.test.ts +17 -3
  61. package/src/e2e/send-text.e2e.test.ts +11 -2
  62. package/src/e2e/slash-commands.e2e.test.ts +5 -1
  63. package/src/e2e/status-cors-auth.e2e.test.ts +11 -2
  64. package/src/e2e/subagent-smoke.e2e.test.ts +68 -28
  65. package/src/e2e/subagent.e2e.test.ts +136 -53
  66. package/src/e2e/tool-lifecycle.e2e.test.ts +5 -1
  67. package/src/friday-session.forward-agent.test.ts +44 -12
  68. package/src/friday-session.ts +44 -20
  69. package/src/history/normalize-message.test.ts +35 -8
  70. package/src/history/normalize-message.ts +24 -12
  71. package/src/history/read-transcript.ts +1 -4
  72. package/src/http/handlers/agent-config.test.ts +10 -3
  73. package/src/http/handlers/agent-config.ts +22 -8
  74. package/src/http/handlers/agents-list.test.ts +1 -5
  75. package/src/http/handlers/cancel.test.ts +12 -3
  76. package/src/http/handlers/cancel.ts +4 -2
  77. package/src/http/handlers/device-approve.test.ts +12 -3
  78. package/src/http/handlers/device-approve.ts +33 -21
  79. package/src/http/handlers/files-download.ts +17 -13
  80. package/src/http/handlers/files.test.ts +8 -2
  81. package/src/http/handlers/files.ts +21 -7
  82. package/src/http/handlers/health.test.ts +43 -11
  83. package/src/http/handlers/health.ts +22 -6
  84. package/src/http/handlers/history-messages.test.ts +51 -9
  85. package/src/http/handlers/history-messages.ts +4 -1
  86. package/src/http/handlers/history-sessions.test.ts +46 -9
  87. package/src/http/handlers/history-sessions.ts +5 -3
  88. package/src/http/handlers/history-set-title.test.ts +14 -5
  89. package/src/http/handlers/link-preview.test.ts +57 -16
  90. package/src/http/handlers/link-preview.ts +4 -1
  91. package/src/http/handlers/messages.test.ts +12 -8
  92. package/src/http/handlers/messages.ts +67 -19
  93. package/src/http/handlers/models-list.ts +14 -8
  94. package/src/http/handlers/nodes-approve.test.ts +15 -4
  95. package/src/http/handlers/nodes-approve.ts +38 -40
  96. package/src/http/handlers/plugin-info.ts +5 -6
  97. package/src/http/handlers/plugin-upgrade.ts +4 -1
  98. package/src/http/handlers/sse.ts +3 -1
  99. package/src/http/server.ts +9 -6
  100. package/src/link-preview/og-parse.test.ts +6 -2
  101. package/src/link-preview/og-parse.ts +10 -3
  102. package/src/link-preview/preview-service.ts +4 -1
  103. package/src/link-preview/ssrf-guard.test.ts +72 -15
  104. package/src/link-preview/ssrf-guard.ts +2 -1
  105. package/src/media-fetch.test.ts +7 -2
  106. package/src/media-fetch.ts +1 -2
  107. package/src/openclaw.d.ts +26 -9
  108. package/src/plugin-install-info.ts +20 -9
  109. package/src/run-metadata.ts +2 -1
  110. package/src/session/session-manager.ts +19 -11
  111. package/src/session-usage-snapshot.ts +3 -1
  112. package/src/session-usage-store.ts +3 -1
  113. package/src/skills-discovery.test.ts +14 -10
  114. package/src/skills-discovery.ts +43 -27
  115. package/src/sse/emitter.test.ts +1 -1
  116. package/src/sse/emitter.ts +9 -3
  117. package/src/sse/offline-queue.ts +17 -8
  118. package/src/test-support/app-simulator.ts +17 -3
  119. package/src/test-support/mock-dispatch.ts +17 -4
  120. package/src/thinking-levels.ts +3 -1
  121. package/src/tool-catalog.ts +16 -7
  122. package/src/upgrade-runtime.ts +4 -2
  123. package/src/version.ts +5 -1
  124. package/tsconfig.json +1 -1
@@ -12,7 +12,9 @@
12
12
  * marker core's `loadSkillsFromDir` uses).
13
13
  *
14
14
  * Each discovered skill is tagged with a `source` category for the UI:
15
- * - workspace : the agent's own + the shared default-agent workspace `skills/`
15
+ * - workspace : the TARGET agent's own workspace `skills/` only mirroring ControlUI,
16
+ * which scans the single workspace resolved for that agent and never folds
17
+ * in another agent's workspace (main is just another agent, not a shared pool)
16
18
  * - installed : managed skills dir (`<configDir>/skills`, sibling of the workspace)
17
19
  * - built-in : bundled core skills (`<openclaw>/skills`)
18
20
  * - extra : skills from ENABLED extensions (`<openclaw>/dist/extensions/<ext>/skills`,
@@ -80,7 +82,12 @@ function parseSkillFrontmatter(content: string): { name?: string; description?:
80
82
  * not descended into further; other directories are recursed up to a bounded depth.
81
83
  * First occurrence of an id wins (call higher-priority sources first). Best-effort.
82
84
  */
83
- function collectSkills(root: string, source: SkillSource, out: Map<string, DiscoveredSkill>, depth = 0): void {
85
+ function collectSkills(
86
+ root: string,
87
+ source: SkillSource,
88
+ out: Map<string, DiscoveredSkill>,
89
+ depth = 0,
90
+ ): void {
84
91
  if (depth > MAX_SKILL_WALK_DEPTH) return;
85
92
  let entries: fs.Dirent[];
86
93
  try {
@@ -149,14 +156,17 @@ function computeOpenClawRoot(): string | null {
149
156
  * enabled extensions, so we gate on the same set (extension dir name == plugin id).
150
157
  */
151
158
  export function enabledExtensionNames(cfg: unknown): Set<string> {
152
- const plugins = (cfg as Record<string, unknown> | undefined)?.plugins as Record<string, unknown> | undefined;
159
+ const plugins = (cfg as Record<string, unknown> | undefined)?.plugins as
160
+ | Record<string, unknown>
161
+ | undefined;
153
162
  const names = new Set<string>();
154
163
  const allow = plugins?.allow;
155
164
  if (Array.isArray(allow)) for (const n of allow) if (typeof n === "string") names.add(n);
156
165
  const entries = plugins?.entries as Record<string, unknown> | undefined;
157
166
  if (entries && typeof entries === "object") {
158
167
  for (const [name, val] of Object.entries(entries)) {
159
- if (val && typeof val === "object" && (val as Record<string, unknown>).enabled === true) names.add(name);
168
+ if (val && typeof val === "object" && (val as Record<string, unknown>).enabled === true)
169
+ names.add(name);
160
170
  }
161
171
  }
162
172
  return names;
@@ -168,7 +178,9 @@ export function enabledExtensionNames(cfg: unknown): Set<string> {
168
178
  * → "extra" (core tags these `source: "extension"`). Extension skills are included
169
179
  * only when the extension is enabled, matching ControlUI's EXTRA bucket.
170
180
  */
171
- function bundledSkillSources(enabledExtensions: Set<string>): Array<{ dir: string; source: SkillSource }> {
181
+ function bundledSkillSources(
182
+ enabledExtensions: Set<string>,
183
+ ): Array<{ dir: string; source: SkillSource }> {
172
184
  const root = resolveOpenClawRoot();
173
185
  if (!root) return [];
174
186
  const out: Array<{ dir: string; source: SkillSource }> = [
@@ -200,9 +212,8 @@ function resolveDefaultAgentId(cfg: Record<string, unknown> | undefined): string
200
212
 
201
213
  /**
202
214
  * Full set of skills `agentId` can load, sorted by id, each tagged with its source
203
- * category. Aggregates the agent's workspace, the shared root workspace, the managed
204
- * dir, config extra dirs, and bundled core/extension skills. Every source is optional
205
- * and failure-tolerant.
215
+ * category. Aggregates the TARGET agent's own workspace, the managed dir, config extra
216
+ * dirs, and bundled core/extension skills. Every source is optional and failure-tolerant.
206
217
  */
207
218
  export function discoverAvailableSkills(cfg: unknown, agentId: string): DiscoveredSkill[] {
208
219
  const c = cfg as Record<string, unknown> | undefined;
@@ -210,29 +221,34 @@ export function discoverAvailableSkills(cfg: unknown, agentId: string): Discover
210
221
  const sources: Array<{ dir: string; source: SkillSource }> = [];
211
222
 
212
223
  if (resolveWs) {
213
- const defaultId = resolveDefaultAgentId(c);
214
- const ids = agentId === defaultId ? [agentId] : [agentId, defaultId];
215
- let defaultWs: string | undefined;
216
- for (const id of ids) {
217
- try {
218
- const ws = resolveWs(cfg, id);
219
- if (ws) {
220
- sources.push({ dir: path.join(ws, "skills"), source: "workspace" });
221
- if (id === defaultId) defaultWs = ws;
222
- }
223
- } catch {
224
- // skip unresolvable workspace
225
- }
224
+ // Workspace skills come ONLY from the target agent's own workspace — matching ControlUI's
225
+ // `resolveSkillsAgentWorkspace`→`buildWorkspaceSkillStatus(workspaceDir)`, which scans the
226
+ // single resolved workspace. Folding in the default agent's workspace (the old behavior)
227
+ // leaked main's skills into every other agent's catalog.
228
+ try {
229
+ const ws = resolveWs(cfg, agentId);
230
+ if (ws) sources.push({ dir: path.join(ws, "skills"), source: "workspace" });
231
+ } catch {
232
+ // skip unresolvable workspace
233
+ }
234
+ // Managed skills dir: `<configDir>/skills`. It is agent-independent; anchor it off the
235
+ // DEFAULT agent's workspace parent (the default workspace lives directly under configDir,
236
+ // whereas non-default workspaces may be nested under it).
237
+ try {
238
+ const defaultWs = resolveWs(cfg, resolveDefaultAgentId(c));
239
+ if (defaultWs)
240
+ sources.push({ dir: path.join(path.dirname(defaultWs), "skills"), source: "installed" });
241
+ } catch {
242
+ // skip unresolvable managed dir
226
243
  }
227
- // Managed skills dir: `<configDir>/skills`, the workspace's parent sibling.
228
- if (defaultWs) sources.push({ dir: path.join(path.dirname(defaultWs), "skills"), source: "installed" });
229
244
  }
230
245
 
231
- const extraDirs = ((c?.skills as Record<string, unknown> | undefined)?.load as
232
- | Record<string, unknown>
233
- | undefined)?.extraDirs;
246
+ const extraDirs = (
247
+ (c?.skills as Record<string, unknown> | undefined)?.load as Record<string, unknown> | undefined
248
+ )?.extraDirs;
234
249
  if (Array.isArray(extraDirs)) {
235
- for (const d of extraDirs) if (typeof d === "string" && d.trim()) sources.push({ dir: d.trim(), source: "extra" });
250
+ for (const d of extraDirs)
251
+ if (typeof d === "string" && d.trim()) sources.push({ dir: d.trim(), source: "extra" });
236
252
  }
237
253
 
238
254
  sources.push(...bundledSkillSources(enabledExtensionNames(c)));
@@ -78,7 +78,7 @@ describe("sseEmitter", () => {
78
78
  const body = c.writes.join("");
79
79
  expect(body).toContain("id: 2");
80
80
  expect(body).toContain("id: 3");
81
- expect(body).not.toContain("text\":\"a\"");
81
+ expect(body).not.toContain('text":"a"');
82
82
 
83
83
  sseEmitter.removeConnection("device-replay");
84
84
  });
@@ -4,7 +4,14 @@ import { fridaySseOfflineQueue } from "./offline-queue.js";
4
4
 
5
5
  const logger = createFridayNextLogger("sse", "info");
6
6
 
7
- export type SseEventType = "connected" | "agent" | "deliver" | "tool-hook" | "outbound" | "ping" | "subagent";
7
+ export type SseEventType =
8
+ | "connected"
9
+ | "agent"
10
+ | "deliver"
11
+ | "tool-hook"
12
+ | "outbound"
13
+ | "ping"
14
+ | "subagent";
8
15
 
9
16
  export interface SseEvent {
10
17
  type: SseEventType;
@@ -36,8 +43,7 @@ export class SseConnection {
36
43
 
37
44
  send(entry: BacklogEntry | SseEvent, flushNow?: boolean): void {
38
45
  if (this.closed) return;
39
- const normalized =
40
- "id" in entry && "event" in entry ? entry : { id: Date.now(), event: entry as SseEvent };
46
+ const normalized = "id" in entry && "event" in entry ? entry : { id: Date.now(), event: entry };
41
47
  const payload = JSON.stringify(normalized.event.data);
42
48
  this.pending.push(
43
49
  `id: ${normalized.id}\nevent: ${normalized.event.type}\ndata: ${payload}\n\n`,
@@ -21,7 +21,9 @@ export function setOfflineQueueBaseDirForTest(dir: string | null): void {
21
21
  export function resolveFridayNextEventsQueueDir(): string {
22
22
  if (testQueueBaseDir) return testQueueBaseDir;
23
23
  try {
24
- const cfg = resolveFridayNextConfig(getHostOpenClawConfigSnapshot(getFridayNextRuntime().config));
24
+ const cfg = resolveFridayNextConfig(
25
+ getHostOpenClawConfigSnapshot(getFridayNextRuntime().config),
26
+ );
25
27
  return path.join(path.dirname(cfg.historyDir), "events-queue");
26
28
  } catch {
27
29
  return path.join(os.homedir(), ".openclaw", "friday-next", "events-queue");
@@ -71,7 +73,13 @@ export class FridaySseOfflineQueue {
71
73
  return this.scanMaxId(deviceId.trim().toUpperCase());
72
74
  }
73
75
 
74
- append(deviceId: string, id: number, event: string, data: Record<string, unknown>, backlogLimit: number): void {
76
+ append(
77
+ deviceId: string,
78
+ id: number,
79
+ event: string,
80
+ data: Record<string, unknown>,
81
+ backlogLimit: number,
82
+ ): void {
75
83
  if (event === "connected") return;
76
84
  this.ensureDir();
77
85
  const file = this.devicePath(deviceId);
@@ -119,7 +127,12 @@ export class FridaySseOfflineQueue {
119
127
  if (!line.trim()) continue;
120
128
  try {
121
129
  const o = JSON.parse(line) as PersistedSseEntry;
122
- if (typeof o.id === "number" && typeof o.event === "string" && o.data && typeof o.data === "object") {
130
+ if (
131
+ typeof o.id === "number" &&
132
+ typeof o.event === "string" &&
133
+ o.data &&
134
+ typeof o.data === "object"
135
+ ) {
123
136
  all.push(o);
124
137
  }
125
138
  } catch {
@@ -128,11 +141,7 @@ export class FridaySseOfflineQueue {
128
141
  }
129
142
  if (all.length <= keep) return;
130
143
  const slice = all.slice(-keep);
131
- fs.writeFileSync(
132
- file,
133
- slice.map((e) => JSON.stringify(e) + "\n").join(""),
134
- "utf8",
135
- );
144
+ fs.writeFileSync(file, slice.map((e) => JSON.stringify(e) + "\n").join(""), "utf8");
136
145
  }
137
146
  }
138
147
 
@@ -68,7 +68,9 @@ class MockRes extends Writable {
68
68
  }
69
69
 
70
70
  function createRouteHarness() {
71
- let routeHandler: ((req: Readable & { method?: string; url?: string }, res: Writable) => Promise<boolean>) | null = null;
71
+ let routeHandler:
72
+ | ((req: Readable & { method?: string; url?: string }, res: Writable) => Promise<boolean>)
73
+ | null = null;
72
74
  const fakeApi = {
73
75
  logger: { info: () => {}, warn: () => {}, error: () => {}, debug: () => {} },
74
76
  registerHttpRoute(route: { handler: (req: never, res: never) => Promise<boolean> }) {
@@ -193,7 +195,14 @@ export function createAppSimulator(opts?: { deviceId?: string; token?: string })
193
195
  });
194
196
  return { status: res.statusCode, body: jsonBody(res) };
195
197
  },
196
- async uploadFiles(parts: Array<{ name: string; filename: string; contentType: string; content: string | Buffer }>) {
198
+ async uploadFiles(
199
+ parts: Array<{
200
+ name: string;
201
+ filename: string;
202
+ contentType: string;
203
+ content: string | Buffer;
204
+ }>,
205
+ ) {
197
206
  const boundary = "----friday-next-e2e-boundary";
198
207
  const chunks: Buffer[] = [];
199
208
  for (const part of parts) {
@@ -235,7 +244,12 @@ export function createAppSimulator(opts?: { deviceId?: string; token?: string })
235
244
  const res = await request({ method: "OPTIONS", path, headers: { origin } });
236
245
  return { status: res.statusCode, headers: res.headers };
237
246
  },
238
- async rawRequest(arg: { method: string; path: string; headers?: Headers; body?: string | Buffer }) {
247
+ async rawRequest(arg: {
248
+ method: string;
249
+ path: string;
250
+ headers?: Headers;
251
+ body?: string | Buffer;
252
+ }) {
239
253
  const res = await request(arg);
240
254
  return { status: res.statusCode, body: res.body.toString("utf-8"), headers: res.headers };
241
255
  },
@@ -1,7 +1,12 @@
1
- import { __setMockFridayDispatchForTests, __resetMockFridayDispatchForTests } from "../agent/dispatch-bridge.js";
1
+ import {
2
+ __setMockFridayDispatchForTests,
3
+ __resetMockFridayDispatchForTests,
4
+ } from "../agent/dispatch-bridge.js";
2
5
  import { forwardAgentEventRaw } from "../friday-session.js";
3
6
 
4
- type DispatchArg = Parameters<typeof __setMockFridayDispatchForTests>[0] extends (arg: infer A) => unknown
7
+ type DispatchArg = Parameters<typeof __setMockFridayDispatchForTests>[0] extends (
8
+ arg: infer A,
9
+ ) => unknown
5
10
  ? A
6
11
  : never;
7
12
 
@@ -133,12 +138,20 @@ export class MockDispatchScript {
133
138
 
134
139
  block(text: string, mediaUrls: string[] = [], audioAsVoice = false): this {
135
140
  this.steps.push(async (_args, callbacks) => {
136
- await callbacks.deliver?.({ text, mediaUrls, audioAsVoice } as never, { kind: "block" } as never);
141
+ await callbacks.deliver?.(
142
+ { text, mediaUrls, audioAsVoice } as never,
143
+ { kind: "block" } as never,
144
+ );
137
145
  });
138
146
  return this;
139
147
  }
140
148
 
141
- deliverFinal(payload: { text: string; mediaUrls?: string[]; audioAsVoice?: boolean; isError?: boolean }): this {
149
+ deliverFinal(payload: {
150
+ text: string;
151
+ mediaUrls?: string[];
152
+ audioAsVoice?: boolean;
153
+ isError?: boolean;
154
+ }): this {
142
155
  this.steps.push(async (_args, callbacks) => {
143
156
  await callbacks.deliver?.(payload as never, { kind: "final" } as never);
144
157
  });
@@ -53,7 +53,9 @@ export function resolveModelThinking(
53
53
  }
54
54
 
55
55
  /** Resolves thinking levels for a full `provider/model` ref (or bare model id). */
56
- export function resolveModelThinkingForRef(modelRef: string | undefined | null): ResolvedModelThinking {
56
+ export function resolveModelThinkingForRef(
57
+ modelRef: string | undefined | null,
58
+ ): ResolvedModelThinking {
57
59
  if (!modelRef) return { levels: BASE_THINKING_LEVELS };
58
60
  const split = splitModelRef(modelRef);
59
61
  return resolveModelThinking(split.provider, split.modelId);
@@ -15,7 +15,6 @@
15
15
 
16
16
  import fs from "node:fs";
17
17
  import path from "node:path";
18
- import { getFridayAgentForwardRuntime } from "./agent-forward-runtime.js";
19
18
  import { resolveOpenClawRoot } from "./skills-discovery.js";
20
19
  import { normalizeAgentId } from "./agent-id.js";
21
20
 
@@ -38,7 +37,11 @@ interface CoreCatalogResult {
38
37
  profiles: Array<{ id: string; label: string }>;
39
38
  groups: CoreCatalogGroup[];
40
39
  }
41
- type BuildFn = (params: { cfg: unknown; agentId?: string; includePlugins?: boolean }) => CoreCatalogResult;
40
+ type BuildFn = (params: {
41
+ cfg: unknown;
42
+ agentId?: string;
43
+ includePlugins?: boolean;
44
+ }) => CoreCatalogResult;
42
45
 
43
46
  let cachedBuildFn: BuildFn | null | undefined;
44
47
 
@@ -127,7 +130,8 @@ async function locateBuildFn(): Promise<BuildFn | null> {
127
130
  if (!content.includes("function buildToolsCatalogResult")) continue;
128
131
  try {
129
132
  const mod = (await import(path.join(distDir, file))) as Record<string, unknown>;
130
- if (typeof mod.buildToolsCatalogResult === "function") return mod.buildToolsCatalogResult as BuildFn;
133
+ if (typeof mod.buildToolsCatalogResult === "function")
134
+ return mod.buildToolsCatalogResult as BuildFn;
131
135
  } catch {
132
136
  // unreadable/non-importable candidate → keep scanning
133
137
  }
@@ -172,8 +176,9 @@ function readStringArray(value: unknown): string[] {
172
176
 
173
177
  /** Read an agent's `tools` config block from the host config. */
174
178
  function findAgentTools(cfg: unknown, agentId: string): AgentToolsConfigShape | undefined {
175
- const list = ((cfg as Record<string, unknown> | undefined)?.agents as Record<string, unknown> | undefined)
176
- ?.list as Array<Record<string, unknown>> | undefined;
179
+ const list = (
180
+ (cfg as Record<string, unknown> | undefined)?.agents as Record<string, unknown> | undefined
181
+ )?.list as Array<Record<string, unknown>> | undefined;
177
182
  if (!Array.isArray(list)) return undefined;
178
183
  const entry = list.find((a) => a && typeof a === "object" && normalizeAgentId(a.id) === agentId);
179
184
  return entry?.tools as AgentToolsConfigShape | undefined;
@@ -183,7 +188,10 @@ function findAgentTools(cfg: unknown, agentId: string): AgentToolsConfigShape |
183
188
  * The agent's full tool catalog with per-tool effective state, or null if the core
184
189
  * catalog builder can't be located.
185
190
  */
186
- export async function buildAgentToolsCatalog(cfg: unknown, agentId: string): Promise<AgentToolsCatalog | null> {
191
+ export async function buildAgentToolsCatalog(
192
+ cfg: unknown,
193
+ agentId: string,
194
+ ): Promise<AgentToolsCatalog | null> {
187
195
  const build = await loadBuildFn();
188
196
  if (!build) return null;
189
197
  // Snapshot the channel registry so we can undo the build's `surface:"channel"` re-pin
@@ -214,7 +222,8 @@ export async function buildAgentToolsCatalog(cfg: unknown, agentId: string): Pro
214
222
  }
215
223
 
216
224
  const tools = findAgentTools(cfg, agentId);
217
- const profile = (typeof tools?.profile === "string" && tools.profile.trim()) ? tools.profile.trim() : null;
225
+ const profile =
226
+ typeof tools?.profile === "string" && tools.profile.trim() ? tools.profile.trim() : null;
218
227
  const allow = new Set(readStringArray(tools?.allow));
219
228
  const alsoAllow = new Set(readStringArray(tools?.alsoAllow));
220
229
  const deny = new Set(readStringArray(tools?.deny));
@@ -28,7 +28,7 @@ export type UpgradeRuntime = {
28
28
  /** Mutate the config file; `afterWrite: { mode: "restart" }` triggers a safe gateway restart. */
29
29
  mutateConfigFile: (params: {
30
30
  afterWrite: ConfigAfterWrite;
31
- mutate: (draft: unknown) => unknown | void;
31
+ mutate: (draft: unknown) => unknown;
32
32
  }) => Promise<unknown>;
33
33
  /**
34
34
  * Filesystem path of THIS loaded plugin (`api.source`). Used to infer the install
@@ -42,7 +42,9 @@ let upgradeRuntime: UpgradeRuntime | null = null;
42
42
 
43
43
  export function setUpgradeRuntime(api: OpenClawPluginApi): void {
44
44
  const runtime = api.runtime as unknown as {
45
- system?: { runCommandWithTimeout?: (argv: string[], opts: unknown) => Promise<SpawnResultLike> };
45
+ system?: {
46
+ runCommandWithTimeout?: (argv: string[], opts: unknown) => Promise<SpawnResultLike>;
47
+ };
46
48
  config: {
47
49
  current: () => unknown;
48
50
  mutateConfigFile?: (params: unknown) => Promise<unknown>;
package/src/version.ts CHANGED
@@ -22,7 +22,11 @@ function resolvePluginVersion(): string {
22
22
  const path = fileURLToPath(new URL(rel, import.meta.url));
23
23
  const raw = readFileSync(path, "utf8");
24
24
  const pkg = JSON.parse(raw) as { name?: string; version?: string };
25
- if (pkg.name === "@syengup/friday-channel-next" && typeof pkg.version === "string" && pkg.version) {
25
+ if (
26
+ pkg.name === "@syengup/friday-channel-next" &&
27
+ typeof pkg.version === "string" &&
28
+ pkg.version
29
+ ) {
26
30
  return pkg.version;
27
31
  }
28
32
  } catch {
package/tsconfig.json CHANGED
@@ -14,4 +14,4 @@
14
14
  },
15
15
  "include": ["index.ts", "src/**/*.ts", "src/**/*.d.ts"],
16
16
  "exclude": ["src/e2e/**", "src/**/*.test.ts", "src/test-support/**", "scripts/**"]
17
- }
17
+ }