@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.
- package/dist/index.js +1 -1
- package/dist/src/agent/dispatch-bridge.d.ts +1 -1
- package/dist/src/agent/node-pairing-bridge.d.ts +11 -8
- package/dist/src/agent/node-pairing-bridge.js +6 -2
- package/dist/src/agent/operator-scope.d.ts +19 -0
- package/dist/src/agent/operator-scope.js +54 -0
- package/dist/src/agent/subagent-registry.js +0 -3
- package/dist/src/channel-actions.js +3 -1
- package/dist/src/channel.js +0 -2
- package/dist/src/collect-message-media-paths.js +10 -1
- package/dist/src/friday-session.js +34 -10
- package/dist/src/history/normalize-message.js +22 -8
- package/dist/src/http/handlers/agent-config.js +10 -4
- package/dist/src/http/handlers/cancel.js +4 -2
- package/dist/src/http/handlers/device-approve.js +3 -1
- package/dist/src/http/handlers/files-download.js +6 -8
- package/dist/src/http/handlers/files.js +1 -1
- package/dist/src/http/handlers/health.js +18 -4
- package/dist/src/http/handlers/history-messages.js +1 -1
- package/dist/src/http/handlers/history-sessions.js +5 -3
- package/dist/src/http/handlers/messages.js +34 -11
- package/dist/src/http/handlers/models-list.js +1 -1
- package/dist/src/http/handlers/nodes-approve.js +1 -6
- package/dist/src/http/handlers/plugin-info.js +1 -1
- package/dist/src/http/server.js +4 -2
- package/dist/src/link-preview/og-parse.js +3 -1
- package/dist/src/plugin-install-info.js +4 -1
- package/dist/src/session/session-manager.js +9 -3
- package/dist/src/session-usage-store.js +3 -1
- package/dist/src/skills-discovery.d.ts +5 -4
- package/dist/src/skills-discovery.js +27 -22
- package/dist/src/sse/offline-queue.js +4 -1
- package/dist/src/tool-catalog.js +2 -3
- package/dist/src/upgrade-runtime.d.ts +1 -1
- package/dist/src/version.js +3 -1
- package/index.ts +43 -35
- package/install.js +131 -43
- package/package.json +10 -1
- package/src/agent/abort-run.ts +2 -3
- package/src/agent/dispatch-bridge.ts +2 -1
- package/src/agent/media-bridge.ts +9 -2
- package/src/agent/node-pairing-bridge.ts +29 -15
- package/src/agent/operator-scope.test.ts +66 -0
- package/src/agent/operator-scope.ts +63 -0
- package/src/agent/run-usage-accumulator.ts +4 -2
- package/src/agent/subagent-registry.ts +0 -4
- package/src/agent-run-context-bridge.ts +3 -1
- package/src/channel-actions.test.ts +10 -4
- package/src/channel-actions.ts +3 -1
- package/src/channel.outbound.test.ts +18 -4
- package/src/channel.ts +121 -123
- package/src/collect-message-media-paths.ts +15 -6
- package/src/config.ts +1 -4
- package/src/e2e/agents-list.e2e.test.ts +9 -2
- package/src/e2e/attachments-inbound.e2e.test.ts +5 -1
- package/src/e2e/attachments-outbound.e2e.test.ts +7 -2
- package/src/e2e/auto-approve.integration.test.ts +13 -7
- package/src/e2e/cancel-reconnect-errors.e2e.test.ts +18 -3
- package/src/e2e/connect-and-connected.e2e.test.ts +5 -1
- package/src/e2e/offline-replay.e2e.test.ts +17 -3
- package/src/e2e/send-text.e2e.test.ts +11 -2
- package/src/e2e/slash-commands.e2e.test.ts +5 -1
- package/src/e2e/status-cors-auth.e2e.test.ts +11 -2
- package/src/e2e/subagent-smoke.e2e.test.ts +68 -28
- package/src/e2e/subagent.e2e.test.ts +136 -53
- package/src/e2e/tool-lifecycle.e2e.test.ts +5 -1
- package/src/friday-session.forward-agent.test.ts +44 -12
- package/src/friday-session.ts +44 -20
- package/src/history/normalize-message.test.ts +35 -8
- package/src/history/normalize-message.ts +24 -12
- package/src/history/read-transcript.ts +1 -4
- package/src/http/handlers/agent-config.test.ts +10 -3
- package/src/http/handlers/agent-config.ts +22 -8
- package/src/http/handlers/agents-list.test.ts +1 -5
- package/src/http/handlers/cancel.test.ts +12 -3
- package/src/http/handlers/cancel.ts +4 -2
- package/src/http/handlers/device-approve.test.ts +12 -3
- package/src/http/handlers/device-approve.ts +33 -21
- package/src/http/handlers/files-download.ts +17 -13
- package/src/http/handlers/files.test.ts +8 -2
- package/src/http/handlers/files.ts +21 -7
- package/src/http/handlers/health.test.ts +43 -11
- package/src/http/handlers/health.ts +22 -6
- package/src/http/handlers/history-messages.test.ts +51 -9
- package/src/http/handlers/history-messages.ts +4 -1
- package/src/http/handlers/history-sessions.test.ts +46 -9
- package/src/http/handlers/history-sessions.ts +5 -3
- package/src/http/handlers/history-set-title.test.ts +14 -5
- package/src/http/handlers/link-preview.test.ts +57 -16
- package/src/http/handlers/link-preview.ts +4 -1
- package/src/http/handlers/messages.test.ts +12 -8
- package/src/http/handlers/messages.ts +67 -19
- package/src/http/handlers/models-list.ts +14 -8
- package/src/http/handlers/nodes-approve.test.ts +15 -4
- package/src/http/handlers/nodes-approve.ts +38 -40
- package/src/http/handlers/plugin-info.ts +5 -6
- package/src/http/handlers/plugin-upgrade.ts +4 -1
- package/src/http/handlers/sse.ts +3 -1
- package/src/http/server.ts +9 -6
- package/src/link-preview/og-parse.test.ts +6 -2
- package/src/link-preview/og-parse.ts +10 -3
- package/src/link-preview/preview-service.ts +4 -1
- package/src/link-preview/ssrf-guard.test.ts +72 -15
- package/src/link-preview/ssrf-guard.ts +2 -1
- package/src/media-fetch.test.ts +7 -2
- package/src/media-fetch.ts +1 -2
- package/src/openclaw.d.ts +26 -9
- package/src/plugin-install-info.ts +20 -9
- package/src/run-metadata.ts +2 -1
- package/src/session/session-manager.ts +19 -11
- package/src/session-usage-snapshot.ts +3 -1
- package/src/session-usage-store.ts +3 -1
- package/src/skills-discovery.test.ts +14 -10
- package/src/skills-discovery.ts +43 -27
- package/src/sse/emitter.test.ts +1 -1
- package/src/sse/emitter.ts +9 -3
- package/src/sse/offline-queue.ts +17 -8
- package/src/test-support/app-simulator.ts +17 -3
- package/src/test-support/mock-dispatch.ts +17 -4
- package/src/thinking-levels.ts +3 -1
- package/src/tool-catalog.ts +16 -7
- package/src/upgrade-runtime.ts +4 -2
- package/src/version.ts +5 -1
- package/tsconfig.json +1 -1
package/src/skills-discovery.ts
CHANGED
|
@@ -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
|
|
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(
|
|
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
|
|
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)
|
|
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(
|
|
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
|
|
204
|
-
*
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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 = (
|
|
232
|
-
| Record<string, unknown>
|
|
233
|
-
|
|
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)
|
|
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)));
|
package/src/sse/emitter.test.ts
CHANGED
|
@@ -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(
|
|
81
|
+
expect(body).not.toContain('text":"a"');
|
|
82
82
|
|
|
83
83
|
sseEmitter.removeConnection("device-replay");
|
|
84
84
|
});
|
package/src/sse/emitter.ts
CHANGED
|
@@ -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 =
|
|
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`,
|
package/src/sse/offline-queue.ts
CHANGED
|
@@ -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(
|
|
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(
|
|
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 (
|
|
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:
|
|
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(
|
|
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: {
|
|
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 {
|
|
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 (
|
|
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?.(
|
|
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: {
|
|
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
|
});
|
package/src/thinking-levels.ts
CHANGED
|
@@ -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(
|
|
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);
|
package/src/tool-catalog.ts
CHANGED
|
@@ -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: {
|
|
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")
|
|
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 = (
|
|
176
|
-
?.
|
|
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(
|
|
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 =
|
|
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));
|
package/src/upgrade-runtime.ts
CHANGED
|
@@ -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
|
|
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?: {
|
|
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 (
|
|
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