@percena/weft 0.4.0-next.2 → 0.4.0-next.3
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/action-bridge.cjs +11 -0
- package/dist/action-bridge.d.cts +1 -11
- package/dist/action-bridge.d.ts +1 -11
- package/dist/action-bridge.js +5 -0
- package/dist/styles/index.css +2 -212
- package/package.json +27 -26
- package/dist/auth.cjs +0 -241
- package/dist/auth.d.cts +0 -1
- package/dist/auth.d.ts +0 -1
- package/dist/auth.js +0 -208
- package/dist/automations.cjs +0 -3044
- package/dist/automations.d.cts +0 -4774
- package/dist/automations.d.ts +0 -4774
- package/dist/automations.js +0 -2965
- package/dist/factory.cjs +0 -5057
- package/dist/factory.d.cts +0 -7909
- package/dist/factory.d.ts +0 -7909
- package/dist/factory.js +0 -5008
- package/dist/providers.cjs +0 -6154
- package/dist/providers.d.cts +0 -6024
- package/dist/providers.d.ts +0 -6024
- package/dist/providers.js +0 -6110
- package/dist/server.cjs +0 -9137
- package/dist/server.d.cts +0 -9868
- package/dist/server.d.ts +0 -9868
- package/dist/server.js +0 -9118
- package/dist/skills.cjs +0 -505
- package/dist/skills.d.cts +0 -218
- package/dist/skills.d.ts +0 -218
- package/dist/skills.js +0 -458
- package/dist/sources.cjs +0 -1710
- package/dist/sources.d.cts +0 -3978
- package/dist/sources.d.ts +0 -3978
- package/dist/sources.js +0 -1675
package/dist/factory.js
DELETED
|
@@ -1,5008 +0,0 @@
|
|
|
1
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
2
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
-
}) : x)(function(x) {
|
|
5
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
-
});
|
|
8
|
-
var __esm = (fn, res) => function __init() {
|
|
9
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
// ../packages/sources/dist/chunk-QOB7RMSO.js
|
|
13
|
-
var init_chunk_QOB7RMSO = __esm({
|
|
14
|
-
"../packages/sources/dist/chunk-QOB7RMSO.js"() {
|
|
15
|
-
"use strict";
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
// ../packages/sources/dist/chunk-DGUM43GV.js
|
|
20
|
-
var init_chunk_DGUM43GV = __esm({
|
|
21
|
-
"../packages/sources/dist/chunk-DGUM43GV.js"() {
|
|
22
|
-
"use strict";
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// ../packages/adaptor/dist/chunk-DGUM43GV.js
|
|
27
|
-
var __require3;
|
|
28
|
-
var init_chunk_DGUM43GV2 = __esm({
|
|
29
|
-
"../packages/adaptor/dist/chunk-DGUM43GV.js"() {
|
|
30
|
-
"use strict";
|
|
31
|
-
__require3 = /* @__PURE__ */ ((x) => typeof __require !== "undefined" ? __require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
32
|
-
get: (a, b) => (typeof __require !== "undefined" ? __require : a)[b]
|
|
33
|
-
}) : x)(function(x) {
|
|
34
|
-
if (typeof __require !== "undefined") return __require.apply(this, arguments);
|
|
35
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
// ../packages/adaptor/dist/chunk-XW533RAZ.js
|
|
41
|
-
var init_chunk_XW533RAZ = __esm({
|
|
42
|
-
"../packages/adaptor/dist/chunk-XW533RAZ.js"() {
|
|
43
|
-
"use strict";
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
// ../packages/runtime-core/dist/index.js
|
|
48
|
-
var RUNTIME_KINDS = ["native-sdk", "app-server", "compatible-sdk", "cli-fallback"];
|
|
49
|
-
function mapPermissionModeToCodexParams(mode) {
|
|
50
|
-
switch (mode) {
|
|
51
|
-
case "safe":
|
|
52
|
-
return { approvalPolicy: "untrusted", approvalsReviewer: "user", sandbox: "read-only" };
|
|
53
|
-
case "allow-all":
|
|
54
|
-
return { approvalPolicy: "never", approvalsReviewer: "user", sandbox: "danger-full-access" };
|
|
55
|
-
case "ask":
|
|
56
|
-
default:
|
|
57
|
-
return { approvalPolicy: "on-request", approvalsReviewer: "user", sandbox: "workspace-write" };
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
function mapCodexSandboxModeToSandboxPolicy(sandbox) {
|
|
61
|
-
if (sandbox === "read-only") return { type: "readOnly" };
|
|
62
|
-
if (sandbox === "danger-full-access") return { type: "dangerFullAccess" };
|
|
63
|
-
return { type: "workspaceWrite" };
|
|
64
|
-
}
|
|
65
|
-
var initialRuntimeState = {
|
|
66
|
-
status: "idle",
|
|
67
|
-
acceptedMessages: [],
|
|
68
|
-
queuedMessages: []
|
|
69
|
-
};
|
|
70
|
-
function reduceRuntimeState(state = initialRuntimeState, action) {
|
|
71
|
-
switch (action.type) {
|
|
72
|
-
case "preflight_start":
|
|
73
|
-
return { ...state, status: "preflighting", lastError: void 0 };
|
|
74
|
-
case "preflight_ok":
|
|
75
|
-
return { ...state, status: "ready", lastError: void 0 };
|
|
76
|
-
case "preflight_error":
|
|
77
|
-
return { ...state, status: "failed", lastError: action.error };
|
|
78
|
-
case "starting":
|
|
79
|
-
if (state.status === "ready" || state.status === "idle") {
|
|
80
|
-
return { ...state, status: "starting" };
|
|
81
|
-
}
|
|
82
|
-
return state;
|
|
83
|
-
case "send_message":
|
|
84
|
-
if (state.status === "running" || state.status === "waiting_for_permission") {
|
|
85
|
-
return {
|
|
86
|
-
...state,
|
|
87
|
-
queuedMessages: [...state.queuedMessages, action.message]
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
return {
|
|
91
|
-
...state,
|
|
92
|
-
status: "running",
|
|
93
|
-
acceptedMessages: [...state.acceptedMessages, action.message]
|
|
94
|
-
};
|
|
95
|
-
case "permission_request":
|
|
96
|
-
return {
|
|
97
|
-
...state,
|
|
98
|
-
status: "waiting_for_permission",
|
|
99
|
-
waitingPermissionRequestId: action.requestId
|
|
100
|
-
};
|
|
101
|
-
case "permission_response":
|
|
102
|
-
return {
|
|
103
|
-
...state,
|
|
104
|
-
status: "running",
|
|
105
|
-
waitingPermissionRequestId: void 0
|
|
106
|
-
};
|
|
107
|
-
case "turn_completed":
|
|
108
|
-
if (state.status === "running") {
|
|
109
|
-
return { ...state, status: "turn_completed" };
|
|
110
|
-
}
|
|
111
|
-
return state;
|
|
112
|
-
case "complete": {
|
|
113
|
-
if (state.status === "turn_completed") {
|
|
114
|
-
const [nextMessage2, ...remaining2] = state.queuedMessages;
|
|
115
|
-
if (nextMessage2) {
|
|
116
|
-
return {
|
|
117
|
-
...state,
|
|
118
|
-
status: "running",
|
|
119
|
-
acceptedMessages: [...state.acceptedMessages, nextMessage2],
|
|
120
|
-
queuedMessages: remaining2
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
return { ...state, status: "ready" };
|
|
124
|
-
}
|
|
125
|
-
const [nextMessage, ...remaining] = state.queuedMessages;
|
|
126
|
-
if (nextMessage) {
|
|
127
|
-
return {
|
|
128
|
-
...state,
|
|
129
|
-
status: "running",
|
|
130
|
-
acceptedMessages: [...state.acceptedMessages, nextMessage],
|
|
131
|
-
queuedMessages: remaining
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
return { ...state, status: "ready" };
|
|
135
|
-
}
|
|
136
|
-
case "abort":
|
|
137
|
-
return {
|
|
138
|
-
...state,
|
|
139
|
-
status: "ready",
|
|
140
|
-
lastError: action.reason,
|
|
141
|
-
queuedMessages: [],
|
|
142
|
-
waitingPermissionRequestId: void 0
|
|
143
|
-
};
|
|
144
|
-
case "error":
|
|
145
|
-
return { ...state, status: "failed", lastError: action.error };
|
|
146
|
-
case "dispose":
|
|
147
|
-
return {
|
|
148
|
-
...state,
|
|
149
|
-
status: "disposed",
|
|
150
|
-
queuedMessages: [],
|
|
151
|
-
waitingPermissionRequestId: void 0
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
function createRuntimeExtensionContext(context = {}) {
|
|
156
|
-
return {
|
|
157
|
-
policy: context.policy,
|
|
158
|
-
sources: context.sources ? { enabledSourceSlugs: unique(context.sources.enabledSourceSlugs) } : void 0,
|
|
159
|
-
skills: context.skills ? { activeSkillSlugs: unique(context.skills.activeSkillSlugs) } : void 0,
|
|
160
|
-
commandOrigin: context.commandOrigin,
|
|
161
|
-
hostServices: context.hostServices
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
function sanitizeProviderSourceTools(sourceTools) {
|
|
165
|
-
if (!sourceTools || sourceTools.length === 0) return [];
|
|
166
|
-
const sanitized = [];
|
|
167
|
-
for (const sourceTool of sourceTools) {
|
|
168
|
-
if (sourceTool.kind === "api-source") {
|
|
169
|
-
const defaultHeaders = sourceTool.defaultHeaders ? copyNonCredentialStringRecord(sourceTool.defaultHeaders) : void 0;
|
|
170
|
-
sanitized.push({
|
|
171
|
-
kind: "api-source",
|
|
172
|
-
sourceSlug: sourceTool.sourceSlug,
|
|
173
|
-
baseUrl: sourceTool.baseUrl,
|
|
174
|
-
authType: sourceTool.authType,
|
|
175
|
-
...defaultHeaders && Object.keys(defaultHeaders).length > 0 ? { defaultHeaders } : {},
|
|
176
|
-
...sourceTool.credentialRef ? { credentialRef: sanitizeSourceCredentialRef(sourceTool.credentialRef) } : {}
|
|
177
|
-
});
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
if (sourceTool.kind === "local-source") {
|
|
181
|
-
sanitized.push({
|
|
182
|
-
kind: "local-source",
|
|
183
|
-
sourceSlug: sourceTool.sourceSlug,
|
|
184
|
-
path: sourceTool.path,
|
|
185
|
-
...sourceTool.format ? { format: sourceTool.format } : {}
|
|
186
|
-
});
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
if (sourceTool.kind === "mcp-server") {
|
|
190
|
-
const env = sourceTool.env ? copyNonCredentialStringRecord(sourceTool.env) : void 0;
|
|
191
|
-
const headers = sourceTool.headers ? copyNonCredentialStringRecord(sourceTool.headers) : void 0;
|
|
192
|
-
sanitized.push({
|
|
193
|
-
kind: "mcp-server",
|
|
194
|
-
sourceSlug: sourceTool.sourceSlug,
|
|
195
|
-
transport: sourceTool.transport,
|
|
196
|
-
...sourceTool.url ? { url: sourceTool.url } : {},
|
|
197
|
-
...sourceTool.command ? { command: sourceTool.command } : {},
|
|
198
|
-
...sourceTool.args ? { args: sourceTool.args.filter((value) => typeof value === "string") } : {},
|
|
199
|
-
...env && Object.keys(env).length > 0 ? { env } : {},
|
|
200
|
-
...headers && Object.keys(headers).length > 0 ? { headers } : {},
|
|
201
|
-
...sourceTool.credentialRef ? { credentialRef: sanitizeSourceCredentialRef(sourceTool.credentialRef) } : {}
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return sanitized;
|
|
206
|
-
}
|
|
207
|
-
var sessionToolRequestCounter = 0;
|
|
208
|
-
async function invokeSessionTool(options) {
|
|
209
|
-
const requestId = `session-tool-${++sessionToolRequestCounter}`;
|
|
210
|
-
const origin = originFromSessionToolRequest(options.request) ?? options.commandOrigin;
|
|
211
|
-
const policyDecision = await evaluateSessionToolPolicy(options, requestId);
|
|
212
|
-
const timelineRefs = [];
|
|
213
|
-
const append3 = (item) => {
|
|
214
|
-
const envelope = options.appendTimeline?.(item);
|
|
215
|
-
if (envelope) {
|
|
216
|
-
timelineRefs.push({ epoch: envelope.epoch, seq: envelope.seq });
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
if (policyDecision.decision !== "allow") {
|
|
220
|
-
const reason = policyDecision.decision === "deny" ? policyDecision.reason : policyDecision.reason;
|
|
221
|
-
append3({
|
|
222
|
-
type: "host_state_changed",
|
|
223
|
-
state: {
|
|
224
|
-
kind: "host_tool_denied",
|
|
225
|
-
requestId,
|
|
226
|
-
toolName: options.toolName,
|
|
227
|
-
reason
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
return {
|
|
231
|
-
ok: false,
|
|
232
|
-
requestId,
|
|
233
|
-
toolName: options.toolName,
|
|
234
|
-
origin,
|
|
235
|
-
policyDecision,
|
|
236
|
-
reason,
|
|
237
|
-
timelineRefs
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
append3({
|
|
241
|
-
type: "host_state_changed",
|
|
242
|
-
state: {
|
|
243
|
-
kind: "host_tool_invoked",
|
|
244
|
-
requestId,
|
|
245
|
-
toolName: options.toolName,
|
|
246
|
-
...origin ? { origin } : {}
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
const callback = options.bridge[options.toolName];
|
|
250
|
-
if (!callback) {
|
|
251
|
-
const reason = `Session tool bridge callback is not registered: ${String(options.toolName)}`;
|
|
252
|
-
append3({
|
|
253
|
-
type: "host_state_changed",
|
|
254
|
-
state: {
|
|
255
|
-
kind: "host_tool_result",
|
|
256
|
-
requestId,
|
|
257
|
-
toolName: options.toolName,
|
|
258
|
-
ok: false,
|
|
259
|
-
reason
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
return {
|
|
263
|
-
ok: false,
|
|
264
|
-
requestId,
|
|
265
|
-
toolName: options.toolName,
|
|
266
|
-
origin,
|
|
267
|
-
policyDecision,
|
|
268
|
-
reason,
|
|
269
|
-
timelineRefs
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
try {
|
|
273
|
-
const result = await callback(options.request);
|
|
274
|
-
append3({
|
|
275
|
-
type: "host_state_changed",
|
|
276
|
-
state: {
|
|
277
|
-
kind: "host_tool_result",
|
|
278
|
-
requestId,
|
|
279
|
-
toolName: options.toolName,
|
|
280
|
-
ok: true
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
return {
|
|
284
|
-
ok: true,
|
|
285
|
-
requestId,
|
|
286
|
-
toolName: options.toolName,
|
|
287
|
-
origin,
|
|
288
|
-
policyDecision,
|
|
289
|
-
result,
|
|
290
|
-
timelineRefs
|
|
291
|
-
};
|
|
292
|
-
} catch (error) {
|
|
293
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
294
|
-
append3({
|
|
295
|
-
type: "host_state_changed",
|
|
296
|
-
state: {
|
|
297
|
-
kind: "host_tool_result",
|
|
298
|
-
requestId,
|
|
299
|
-
toolName: options.toolName,
|
|
300
|
-
ok: false,
|
|
301
|
-
reason
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
return {
|
|
305
|
-
ok: false,
|
|
306
|
-
requestId,
|
|
307
|
-
toolName: options.toolName,
|
|
308
|
-
origin,
|
|
309
|
-
policyDecision,
|
|
310
|
-
reason,
|
|
311
|
-
timelineRefs
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
async function evaluateSessionToolPolicy(options, requestId) {
|
|
316
|
-
if (!options.policy) return { decision: "allow" };
|
|
317
|
-
return options.policy({
|
|
318
|
-
toolName: `host.${String(options.toolName)}`,
|
|
319
|
-
input: recordFromSessionToolRequest(options.request),
|
|
320
|
-
toolIntent: { kind: "unknown", toolName: `host.${String(options.toolName)}` },
|
|
321
|
-
scope: { type: "session", sessionId: options.sessionId }
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
function originFromSessionToolRequest(request) {
|
|
325
|
-
const record = recordFromSessionToolRequest(request);
|
|
326
|
-
const origin = record.origin ?? record.commandOrigin;
|
|
327
|
-
return isCommandOrigin(origin) ? origin : void 0;
|
|
328
|
-
}
|
|
329
|
-
function recordFromSessionToolRequest(request) {
|
|
330
|
-
return request && typeof request === "object" ? { ...request } : {};
|
|
331
|
-
}
|
|
332
|
-
function isCommandOrigin(value) {
|
|
333
|
-
if (!value || typeof value !== "object") return false;
|
|
334
|
-
const type = value.type;
|
|
335
|
-
return type === "user" || type === "automation" || type === "scheduler" || type === "host" || type === "replay" || type === "system";
|
|
336
|
-
}
|
|
337
|
-
function unique(values) {
|
|
338
|
-
return [...new Set(values)];
|
|
339
|
-
}
|
|
340
|
-
function sanitizeSourceCredentialRef(credentialRef) {
|
|
341
|
-
return {
|
|
342
|
-
type: credentialRef.type,
|
|
343
|
-
sourceSlug: credentialRef.sourceSlug,
|
|
344
|
-
...credentialRef.workspaceId ? { workspaceId: credentialRef.workspaceId } : {}
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
function copyStringRecord(record) {
|
|
348
|
-
return Object.fromEntries(
|
|
349
|
-
Object.entries(record).filter((entry) => typeof entry[0] === "string" && typeof entry[1] === "string")
|
|
350
|
-
);
|
|
351
|
-
}
|
|
352
|
-
function copyNonCredentialStringRecord(record) {
|
|
353
|
-
return Object.fromEntries(
|
|
354
|
-
Object.entries(copyStringRecord(record)).filter(([key]) => !isCredentialKey(key))
|
|
355
|
-
);
|
|
356
|
-
}
|
|
357
|
-
function isCredentialKey(key) {
|
|
358
|
-
const normalized = key.toLowerCase();
|
|
359
|
-
return normalized === "authorization" || normalized === "proxy-authorization" || normalized === "cookie" || normalized === "set-cookie" || normalized.includes("api-key") || normalized.includes("apikey") || normalized.includes("token") || normalized.includes("secret") || normalized.includes("password");
|
|
360
|
-
}
|
|
361
|
-
function findCandidate(candidates, kind) {
|
|
362
|
-
return candidates.find((candidate) => candidate.kind === kind);
|
|
363
|
-
}
|
|
364
|
-
function defaultRuntimeKindOrder(provider) {
|
|
365
|
-
if (provider === "codex") {
|
|
366
|
-
return ["app-server", "native-sdk", "compatible-sdk", "cli-fallback"];
|
|
367
|
-
}
|
|
368
|
-
return RUNTIME_KINDS;
|
|
369
|
-
}
|
|
370
|
-
function firstAvailableCandidate(candidates, provider) {
|
|
371
|
-
for (const kind of defaultRuntimeKindOrder(provider)) {
|
|
372
|
-
const candidate = findCandidate(candidates, kind);
|
|
373
|
-
if (candidate?.available) return candidate;
|
|
374
|
-
}
|
|
375
|
-
return void 0;
|
|
376
|
-
}
|
|
377
|
-
function selectRuntimeCandidate(options) {
|
|
378
|
-
const preferred = options.preferredRuntime ? findCandidate(options.candidates, options.preferredRuntime) : void 0;
|
|
379
|
-
if (preferred?.available) {
|
|
380
|
-
return { selected: preferred.kind, fallback: false };
|
|
381
|
-
}
|
|
382
|
-
if (preferred && options.allowFallback !== true) {
|
|
383
|
-
return {
|
|
384
|
-
fallback: false,
|
|
385
|
-
error: preferred.reason ?? `${preferred.kind} runtime is unavailable`
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
const selected = firstAvailableCandidate(options.candidates, options.provider);
|
|
389
|
-
if (!selected) {
|
|
390
|
-
return {
|
|
391
|
-
fallback: false,
|
|
392
|
-
error: options.candidates.map((candidate) => candidate.reason).filter(Boolean).join("; ") || "No runtime candidates are available"
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
const fallback = Boolean(preferred && selected.kind !== preferred.kind);
|
|
396
|
-
return {
|
|
397
|
-
selected: selected.kind,
|
|
398
|
-
fallback,
|
|
399
|
-
fallbackReason: fallback ? preferred?.reason ?? `${preferred?.kind ?? "preferred"} runtime is unavailable` : void 0
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
function createRuntimeCapabilityReport(options) {
|
|
403
|
-
const selection = selectRuntimeCandidate(options);
|
|
404
|
-
const extensionCapabilities = normalizeExtensionCapabilities(options.extensionCapabilities);
|
|
405
|
-
return {
|
|
406
|
-
provider: options.provider,
|
|
407
|
-
candidates: options.candidates,
|
|
408
|
-
preferredRuntime: options.preferredRuntime,
|
|
409
|
-
allowFallback: options.allowFallback === true,
|
|
410
|
-
auth: options.auth,
|
|
411
|
-
...extensionCapabilities,
|
|
412
|
-
...selection
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
function normalizeExtensionCapabilities(capabilities = {}) {
|
|
416
|
-
return {
|
|
417
|
-
policyCapabilities: capabilities.policy ?? {
|
|
418
|
-
supported: false,
|
|
419
|
-
modes: [],
|
|
420
|
-
approvals: false,
|
|
421
|
-
toolPolicy: false
|
|
422
|
-
},
|
|
423
|
-
sourceCapabilities: capabilities.sources ?? {
|
|
424
|
-
supported: false
|
|
425
|
-
},
|
|
426
|
-
skillCapabilities: capabilities.skills ?? {
|
|
427
|
-
supported: false
|
|
428
|
-
},
|
|
429
|
-
automationCapabilities: capabilities.automations ?? {
|
|
430
|
-
supported: false,
|
|
431
|
-
eventBus: false,
|
|
432
|
-
schedulerHost: false,
|
|
433
|
-
promptAction: false,
|
|
434
|
-
webhookAction: false
|
|
435
|
-
},
|
|
436
|
-
hostToolCapabilities: capabilities.hostTools ?? {
|
|
437
|
-
supported: false,
|
|
438
|
-
sessionTools: false,
|
|
439
|
-
workflowTransitions: false,
|
|
440
|
-
browserActions: false,
|
|
441
|
-
metadataWrites: false
|
|
442
|
-
}
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// ../packages/timeline/dist/index.js
|
|
447
|
-
function createTimelineSequencer(options) {
|
|
448
|
-
let seq = options.startSeq ?? 0;
|
|
449
|
-
const now = options.now ?? Date.now;
|
|
450
|
-
return {
|
|
451
|
-
append(item, rawRef2) {
|
|
452
|
-
seq += 1;
|
|
453
|
-
return appendTimelineItem({
|
|
454
|
-
sessionId: options.sessionId,
|
|
455
|
-
provider: options.provider,
|
|
456
|
-
epoch: options.epoch,
|
|
457
|
-
seq,
|
|
458
|
-
timestamp: now(),
|
|
459
|
-
item,
|
|
460
|
-
rawRef: rawRef2
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
|
-
function appendTimelineItem(envelope) {
|
|
466
|
-
return {
|
|
467
|
-
...envelope,
|
|
468
|
-
rawRef: envelope.rawRef ? { ...envelope.rawRef } : void 0
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
function createTimelineCursor(cursor) {
|
|
472
|
-
return {
|
|
473
|
-
epoch: cursor.epoch,
|
|
474
|
-
afterSeq: cursor.afterSeq
|
|
475
|
-
};
|
|
476
|
-
}
|
|
477
|
-
function fetchTimeline(timeline, request = {}) {
|
|
478
|
-
const ordered = sortTimeline(timeline);
|
|
479
|
-
const cursor = request.cursor ?? cursorFromTimelineStart(ordered);
|
|
480
|
-
const items = ordered.filter((item) => item.epoch === cursor.epoch && item.seq > cursor.afterSeq).slice(0, request.limit);
|
|
481
|
-
const lastSeq = items.at(-1)?.seq ?? cursor.afterSeq;
|
|
482
|
-
const firstSeq = items[0]?.seq;
|
|
483
|
-
return {
|
|
484
|
-
items,
|
|
485
|
-
nextCursor: {
|
|
486
|
-
epoch: cursor.epoch,
|
|
487
|
-
afterSeq: lastSeq
|
|
488
|
-
},
|
|
489
|
-
hasGap: firstSeq !== void 0 && firstSeq > cursor.afterSeq + 1
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
function cursorFromTimelineStart(timeline) {
|
|
493
|
-
return {
|
|
494
|
-
epoch: timeline[0]?.epoch ?? "default",
|
|
495
|
-
afterSeq: 0
|
|
496
|
-
};
|
|
497
|
-
}
|
|
498
|
-
function sortTimeline(timeline) {
|
|
499
|
-
return [...timeline].sort((left, right) => {
|
|
500
|
-
if (left.epoch !== right.epoch) return left.epoch.localeCompare(right.epoch);
|
|
501
|
-
return left.seq - right.seq;
|
|
502
|
-
});
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
// ../packages/sources/dist/index.js
|
|
506
|
-
init_chunk_QOB7RMSO();
|
|
507
|
-
init_chunk_DGUM43GV();
|
|
508
|
-
import { join, dirname } from "path";
|
|
509
|
-
import { homedir as homedir2 } from "os";
|
|
510
|
-
function createSourceActivationPlan(options) {
|
|
511
|
-
const requestedSourceSlugs = unique2(options.requestedSourceSlugs);
|
|
512
|
-
const states = new Map(options.sourceStates.map((source) => [source.sourceSlug, source]));
|
|
513
|
-
const activeSourceSlugs = [];
|
|
514
|
-
const authRequiredSourceSlugs = [];
|
|
515
|
-
const missingSourceSlugs = [];
|
|
516
|
-
const blockedSourceSlugs = [];
|
|
517
|
-
const timelineItems = [];
|
|
518
|
-
for (const sourceSlug of requestedSourceSlugs) {
|
|
519
|
-
const state = states.get(sourceSlug) ?? {
|
|
520
|
-
sourceSlug,
|
|
521
|
-
enabled: false,
|
|
522
|
-
authenticated: false,
|
|
523
|
-
status: "missing"
|
|
524
|
-
};
|
|
525
|
-
if (state.enabled && state.authenticated && state.status === "active") {
|
|
526
|
-
activeSourceSlugs.push(sourceSlug);
|
|
527
|
-
} else if (state.enabled && !state.authenticated) {
|
|
528
|
-
authRequiredSourceSlugs.push(sourceSlug);
|
|
529
|
-
} else if (state.status === "missing") {
|
|
530
|
-
missingSourceSlugs.push(sourceSlug);
|
|
531
|
-
} else {
|
|
532
|
-
blockedSourceSlugs.push(sourceSlug);
|
|
533
|
-
}
|
|
534
|
-
timelineItems.push({
|
|
535
|
-
type: "source_state_changed",
|
|
536
|
-
source: {
|
|
537
|
-
sourceSlug: state.sourceSlug,
|
|
538
|
-
status: state.status,
|
|
539
|
-
enabled: state.enabled,
|
|
540
|
-
authenticated: state.authenticated,
|
|
541
|
-
...state.reason ? { reason: state.reason } : {}
|
|
542
|
-
}
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
return {
|
|
546
|
-
requestedSourceSlugs,
|
|
547
|
-
activeSourceSlugs,
|
|
548
|
-
authRequiredSourceSlugs,
|
|
549
|
-
missingSourceSlugs,
|
|
550
|
-
blockedSourceSlugs,
|
|
551
|
-
timelineItems
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
function unique2(values) {
|
|
555
|
-
return [...new Set(values)];
|
|
556
|
-
}
|
|
557
|
-
function createSourceToolAssemblyPlan(options) {
|
|
558
|
-
const bySlug = new Map(options.sources.map((source) => [source.config.slug, source]));
|
|
559
|
-
const tools = [];
|
|
560
|
-
const blockedSourceSlugs = [];
|
|
561
|
-
for (const sourceSlug of unique22(options.activeSourceSlugs)) {
|
|
562
|
-
const source = bySlug.get(sourceSlug);
|
|
563
|
-
if (!source) {
|
|
564
|
-
blockedSourceSlugs.push(sourceSlug);
|
|
565
|
-
continue;
|
|
566
|
-
}
|
|
567
|
-
const descriptor = descriptorFromSource(
|
|
568
|
-
source,
|
|
569
|
-
options.credentialRefs?.[sourceSlug],
|
|
570
|
-
options.allowedStdioEnvKeys ?? []
|
|
571
|
-
);
|
|
572
|
-
if (descriptor) {
|
|
573
|
-
tools.push(descriptor);
|
|
574
|
-
} else {
|
|
575
|
-
blockedSourceSlugs.push(sourceSlug);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
return { tools, blockedSourceSlugs };
|
|
579
|
-
}
|
|
580
|
-
function descriptorFromSource(source, credentialRef, allowedStdioEnvKeys) {
|
|
581
|
-
const { config } = source;
|
|
582
|
-
if (config.type === "api" && config.api) {
|
|
583
|
-
return {
|
|
584
|
-
kind: "api-source",
|
|
585
|
-
sourceSlug: config.slug,
|
|
586
|
-
baseUrl: config.api.baseUrl,
|
|
587
|
-
authType: config.api.authType,
|
|
588
|
-
...headersOption("defaultHeaders", config.api.defaultHeaders),
|
|
589
|
-
...credentialRef ? { credentialRef } : {}
|
|
590
|
-
};
|
|
591
|
-
}
|
|
592
|
-
if (config.type === "local" && config.local) {
|
|
593
|
-
return {
|
|
594
|
-
kind: "local-source",
|
|
595
|
-
sourceSlug: config.slug,
|
|
596
|
-
path: config.local.path,
|
|
597
|
-
...config.local.format ? { format: config.local.format } : {}
|
|
598
|
-
};
|
|
599
|
-
}
|
|
600
|
-
if (config.type === "mcp" && config.mcp) {
|
|
601
|
-
const transport = config.mcp.transport ?? "http";
|
|
602
|
-
return {
|
|
603
|
-
kind: "mcp-server",
|
|
604
|
-
sourceSlug: config.slug,
|
|
605
|
-
transport,
|
|
606
|
-
...config.mcp.url ? { url: config.mcp.url } : {},
|
|
607
|
-
...config.mcp.command ? { command: config.mcp.command } : {},
|
|
608
|
-
...config.mcp.args ? { args: [...config.mcp.args] } : {},
|
|
609
|
-
...transport === "stdio" ? { env: scrubEnv(config.mcp.env, allowedStdioEnvKeys) } : {},
|
|
610
|
-
...transport !== "stdio" ? headersOption("headers", config.mcp.headers) : {},
|
|
611
|
-
...credentialRef ? { credentialRef } : {}
|
|
612
|
-
};
|
|
613
|
-
}
|
|
614
|
-
return void 0;
|
|
615
|
-
}
|
|
616
|
-
function scrubEnv(env, allowedKeys) {
|
|
617
|
-
if (!env) return void 0;
|
|
618
|
-
const allowed = new Set(allowedKeys);
|
|
619
|
-
const scrubbed = Object.fromEntries(
|
|
620
|
-
Object.entries(env).filter(([key]) => allowed.has(key))
|
|
621
|
-
);
|
|
622
|
-
return Object.keys(scrubbed).length > 0 ? scrubbed : void 0;
|
|
623
|
-
}
|
|
624
|
-
function headersOption(key, headers) {
|
|
625
|
-
const scrubbed = scrubCredentialHeaders(headers);
|
|
626
|
-
return scrubbed ? { [key]: scrubbed } : {};
|
|
627
|
-
}
|
|
628
|
-
function scrubCredentialHeaders(headers) {
|
|
629
|
-
if (!headers) return void 0;
|
|
630
|
-
const scrubbed = Object.fromEntries(
|
|
631
|
-
Object.entries(headers).filter(([key]) => !isCredentialHeader(key))
|
|
632
|
-
);
|
|
633
|
-
return Object.keys(scrubbed).length > 0 ? scrubbed : void 0;
|
|
634
|
-
}
|
|
635
|
-
function isCredentialHeader(key) {
|
|
636
|
-
const normalized = key.toLowerCase();
|
|
637
|
-
return normalized === "authorization" || normalized === "proxy-authorization" || normalized === "cookie" || normalized === "set-cookie" || normalized.includes("api-key") || normalized.includes("apikey") || normalized.includes("token") || normalized.includes("secret");
|
|
638
|
-
}
|
|
639
|
-
function unique22(values) {
|
|
640
|
-
return [...new Set(values)];
|
|
641
|
-
}
|
|
642
|
-
function createSourceRuntimeAssemblyPlan(options) {
|
|
643
|
-
const activation = createSourceActivationPlan({
|
|
644
|
-
requestedSourceSlugs: options.requestedSourceSlugs,
|
|
645
|
-
sourceStates: options.sourceStates
|
|
646
|
-
});
|
|
647
|
-
const toolAssembly = createSourceToolAssemblyPlan({
|
|
648
|
-
activeSourceSlugs: activation.activeSourceSlugs,
|
|
649
|
-
sources: options.sources,
|
|
650
|
-
credentialRefs: options.credentialRefs,
|
|
651
|
-
allowedStdioEnvKeys: options.allowedStdioEnvKeys
|
|
652
|
-
});
|
|
653
|
-
return {
|
|
654
|
-
requestedSourceSlugs: activation.requestedSourceSlugs,
|
|
655
|
-
activeSourceSlugs: activation.activeSourceSlugs,
|
|
656
|
-
authRequiredSourceSlugs: activation.authRequiredSourceSlugs,
|
|
657
|
-
missingSourceSlugs: activation.missingSourceSlugs,
|
|
658
|
-
blockedSourceSlugs: unique3([
|
|
659
|
-
...activation.blockedSourceSlugs,
|
|
660
|
-
...toolAssembly.blockedSourceSlugs
|
|
661
|
-
]),
|
|
662
|
-
toolBlockedSourceSlugs: toolAssembly.blockedSourceSlugs,
|
|
663
|
-
sourceTools: toolAssembly.tools,
|
|
664
|
-
timelineItems: activation.timelineItems
|
|
665
|
-
};
|
|
666
|
-
}
|
|
667
|
-
function unique3(values) {
|
|
668
|
-
return [...new Set(values)];
|
|
669
|
-
}
|
|
670
|
-
function createSourceRuntimeProviderOptions(options) {
|
|
671
|
-
const plan = createSourceRuntimeAssemblyPlan(options);
|
|
672
|
-
const sourceTools = sanitizeProviderSourceTools(
|
|
673
|
-
scrubProviderCredentialHeaders(plan.sourceTools)
|
|
674
|
-
);
|
|
675
|
-
return {
|
|
676
|
-
extensions: {
|
|
677
|
-
sources: {
|
|
678
|
-
enabledSourceSlugs: plan.activeSourceSlugs
|
|
679
|
-
}
|
|
680
|
-
},
|
|
681
|
-
sourceTools,
|
|
682
|
-
timelineItems: plan.timelineItems,
|
|
683
|
-
capabilityDegradation: {
|
|
684
|
-
authRequiredSourceSlugs: plan.authRequiredSourceSlugs,
|
|
685
|
-
missingSourceSlugs: plan.missingSourceSlugs,
|
|
686
|
-
blockedSourceSlugs: plan.blockedSourceSlugs,
|
|
687
|
-
toolBlockedSourceSlugs: plan.toolBlockedSourceSlugs
|
|
688
|
-
}
|
|
689
|
-
};
|
|
690
|
-
}
|
|
691
|
-
function scrubProviderCredentialHeaders(sourceTools) {
|
|
692
|
-
return sourceTools.map((sourceTool) => {
|
|
693
|
-
if (sourceTool.kind === "api-source" && sourceTool.defaultHeaders) {
|
|
694
|
-
const defaultHeaders = scrubCredentialHeaders2(sourceTool.defaultHeaders);
|
|
695
|
-
return {
|
|
696
|
-
...sourceTool,
|
|
697
|
-
...Object.keys(defaultHeaders).length > 0 ? { defaultHeaders } : { defaultHeaders: void 0 }
|
|
698
|
-
};
|
|
699
|
-
}
|
|
700
|
-
if (sourceTool.kind === "mcp-server" && sourceTool.headers) {
|
|
701
|
-
const headers = scrubCredentialHeaders2(sourceTool.headers);
|
|
702
|
-
return {
|
|
703
|
-
...sourceTool,
|
|
704
|
-
...Object.keys(headers).length > 0 ? { headers } : { headers: void 0 }
|
|
705
|
-
};
|
|
706
|
-
}
|
|
707
|
-
return sourceTool;
|
|
708
|
-
});
|
|
709
|
-
}
|
|
710
|
-
function scrubCredentialHeaders2(headers) {
|
|
711
|
-
const scrubbed = {};
|
|
712
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
713
|
-
if (isCredentialHeader2(key)) continue;
|
|
714
|
-
scrubbed[key] = value;
|
|
715
|
-
}
|
|
716
|
-
return scrubbed;
|
|
717
|
-
}
|
|
718
|
-
function isCredentialHeader2(key) {
|
|
719
|
-
const normalized = key.toLowerCase();
|
|
720
|
-
return normalized === "authorization" || normalized === "proxy-authorization" || normalized === "cookie" || normalized === "set-cookie" || normalized.includes("api-key") || normalized.includes("apikey") || normalized.includes("token") || normalized.includes("secret");
|
|
721
|
-
}
|
|
722
|
-
var MAX_DOWNLOAD_SIZE = 50 * 1024 * 1024;
|
|
723
|
-
var MAGIC = Buffer.from("WEFT01\0\0");
|
|
724
|
-
var DEFAULT_PATH = join(homedir2(), ".weft", "credentials.enc");
|
|
725
|
-
|
|
726
|
-
// ../packages/local-runtime/dist/chunk-BGP42L4K.js
|
|
727
|
-
var PushAgentEventStream = class {
|
|
728
|
-
connected = false;
|
|
729
|
-
listeners = null;
|
|
730
|
-
connect(onEvent, onError, onClose) {
|
|
731
|
-
this.listeners = { onEvent, onError, onClose };
|
|
732
|
-
this.connected = true;
|
|
733
|
-
}
|
|
734
|
-
disconnect() {
|
|
735
|
-
this.listeners = null;
|
|
736
|
-
this.connected = false;
|
|
737
|
-
}
|
|
738
|
-
isConnected() {
|
|
739
|
-
return this.connected;
|
|
740
|
-
}
|
|
741
|
-
emit(event) {
|
|
742
|
-
this.listeners?.onEvent(event);
|
|
743
|
-
}
|
|
744
|
-
fail(error) {
|
|
745
|
-
this.listeners?.onError?.(error);
|
|
746
|
-
}
|
|
747
|
-
close() {
|
|
748
|
-
this.connected = false;
|
|
749
|
-
this.listeners?.onClose?.();
|
|
750
|
-
}
|
|
751
|
-
};
|
|
752
|
-
function parseJsonLine(line) {
|
|
753
|
-
const trimmed = line.trim();
|
|
754
|
-
if (!trimmed) return null;
|
|
755
|
-
try {
|
|
756
|
-
return JSON.parse(trimmed);
|
|
757
|
-
} catch {
|
|
758
|
-
return null;
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
function asRecord(value) {
|
|
762
|
-
return value && typeof value === "object" ? value : {};
|
|
763
|
-
}
|
|
764
|
-
function getText(value) {
|
|
765
|
-
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
766
|
-
}
|
|
767
|
-
function normalizeUsage(raw) {
|
|
768
|
-
const inputTokens = typeof raw.input_tokens === "number" ? raw.input_tokens : typeof raw.inputTokens === "number" ? raw.inputTokens : 0;
|
|
769
|
-
const outputTokens = typeof raw.output_tokens === "number" ? raw.output_tokens : typeof raw.outputTokens === "number" ? raw.outputTokens : 0;
|
|
770
|
-
return { inputTokens, outputTokens };
|
|
771
|
-
}
|
|
772
|
-
function mapClaudeStreamJsonLine(line) {
|
|
773
|
-
const payload = asRecord(parseJsonLine(line));
|
|
774
|
-
const type = getText(payload.type);
|
|
775
|
-
if (!type) return [];
|
|
776
|
-
if (type === "system" && payload.subtype === "init") {
|
|
777
|
-
return [{ type: "status", message: "Claude session initialized" }];
|
|
778
|
-
}
|
|
779
|
-
if (type === "stream_event") {
|
|
780
|
-
const event = asRecord(payload.event);
|
|
781
|
-
if (event.type === "content_block_delta") {
|
|
782
|
-
const delta = asRecord(event.delta);
|
|
783
|
-
const text = getText(delta.text);
|
|
784
|
-
if (text) {
|
|
785
|
-
return [{
|
|
786
|
-
type: "text_delta",
|
|
787
|
-
text,
|
|
788
|
-
turnId: getText(asRecord(payload.message).id) ?? getText(payload.message_id) ?? "msg_1"
|
|
789
|
-
}];
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
return [];
|
|
793
|
-
}
|
|
794
|
-
if (type === "assistant") {
|
|
795
|
-
const message = asRecord(payload.message);
|
|
796
|
-
const turnId = getText(message.id);
|
|
797
|
-
const content = Array.isArray(message.content) ? message.content.map(asRecord) : [];
|
|
798
|
-
const events = [];
|
|
799
|
-
for (const item of content) {
|
|
800
|
-
if (item.type === "tool_use") {
|
|
801
|
-
const toolUseId = getText(item.id);
|
|
802
|
-
const toolName = getText(item.name);
|
|
803
|
-
if (toolUseId && toolName) {
|
|
804
|
-
events.push({
|
|
805
|
-
type: "tool_start",
|
|
806
|
-
toolName,
|
|
807
|
-
toolUseId,
|
|
808
|
-
input: asRecord(item.input),
|
|
809
|
-
turnId
|
|
810
|
-
});
|
|
811
|
-
}
|
|
812
|
-
} else if (item.type === "text") {
|
|
813
|
-
const text = getText(item.text);
|
|
814
|
-
if (text) {
|
|
815
|
-
events.push({
|
|
816
|
-
type: "text_complete",
|
|
817
|
-
text,
|
|
818
|
-
isIntermediate: message.stop_reason === "tool_use",
|
|
819
|
-
turnId
|
|
820
|
-
});
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
return events;
|
|
825
|
-
}
|
|
826
|
-
if (type === "result") {
|
|
827
|
-
if (payload.subtype && payload.subtype !== "success") {
|
|
828
|
-
return [{ type: "error", message: `Claude finished with ${String(payload.subtype)}` }, { type: "complete" }];
|
|
829
|
-
}
|
|
830
|
-
return [{ type: "complete", usage: normalizeUsage(asRecord(payload.usage)) }];
|
|
831
|
-
}
|
|
832
|
-
if (type === "error") {
|
|
833
|
-
return [{ type: "error", message: getText(payload.message) ?? "Claude stream error" }];
|
|
834
|
-
}
|
|
835
|
-
return [];
|
|
836
|
-
}
|
|
837
|
-
function mapCodexExecJsonLine(line) {
|
|
838
|
-
const payload = asRecord(parseJsonLine(line));
|
|
839
|
-
const type = getText(payload.type);
|
|
840
|
-
if (!type) return [];
|
|
841
|
-
if (type === "session.started" || type === "session_started" || type === "thread.started" || type === "thread_started") {
|
|
842
|
-
return [{ type: "status", message: "Codex session initialized" }];
|
|
843
|
-
}
|
|
844
|
-
if (type === "turn.started" || type === "turn_started") {
|
|
845
|
-
return [{ type: "status", message: "Codex turn started" }];
|
|
846
|
-
}
|
|
847
|
-
if (type === "item.started" || type === "item_started") {
|
|
848
|
-
const item = asRecord(payload.item);
|
|
849
|
-
const itemType = getText(item.type);
|
|
850
|
-
if (itemType === "command_execution" || itemType === "commandExecution") {
|
|
851
|
-
const toolUseId = getText(item.id) ?? getText(payload.item_id) ?? `tool-${Date.now()}`;
|
|
852
|
-
const command = getText(item.command);
|
|
853
|
-
return [{
|
|
854
|
-
type: "tool_start",
|
|
855
|
-
toolName: "Bash",
|
|
856
|
-
toolUseId,
|
|
857
|
-
input: command ? { command } : asRecord(item.arguments),
|
|
858
|
-
turnId: getText(payload.turn_id) ?? getText(payload.turnId)
|
|
859
|
-
}];
|
|
860
|
-
}
|
|
861
|
-
return [];
|
|
862
|
-
}
|
|
863
|
-
if (type === "item.completed" || type === "item_completed") {
|
|
864
|
-
const item = asRecord(payload.item);
|
|
865
|
-
const itemType = getText(item.type);
|
|
866
|
-
if (itemType === "agent_message" || itemType === "agentMessage") {
|
|
867
|
-
const text = getText(item.text) ?? getText(item.message);
|
|
868
|
-
return text ? [{
|
|
869
|
-
type: "text_complete",
|
|
870
|
-
text,
|
|
871
|
-
isIntermediate: false,
|
|
872
|
-
turnId: getText(payload.turn_id) ?? getText(payload.turnId) ?? getText(item.id)
|
|
873
|
-
}] : [];
|
|
874
|
-
}
|
|
875
|
-
if (itemType === "command_execution" || itemType === "commandExecution") {
|
|
876
|
-
const toolUseId = getText(item.id) ?? getText(payload.item_id) ?? `tool-${Date.now()}`;
|
|
877
|
-
const exitCode = typeof item.exit_code === "number" ? item.exit_code : typeof item.exitCode === "number" ? item.exitCode : 0;
|
|
878
|
-
return [{
|
|
879
|
-
type: "tool_result",
|
|
880
|
-
toolName: "Bash",
|
|
881
|
-
toolUseId,
|
|
882
|
-
result: getText(item.aggregated_output) ?? getText(item.aggregatedOutput) ?? getText(item.output) ?? "",
|
|
883
|
-
isError: exitCode !== 0 || item.is_error === true,
|
|
884
|
-
turnId: getText(payload.turn_id) ?? getText(payload.turnId)
|
|
885
|
-
}];
|
|
886
|
-
}
|
|
887
|
-
return [];
|
|
888
|
-
}
|
|
889
|
-
if (type === "agent_message_delta" || type === "message_delta" || type === "response.output_text.delta") {
|
|
890
|
-
const text = getText(payload.delta) ?? getText(payload.text);
|
|
891
|
-
return text ? [{ type: "text_delta", text }] : [];
|
|
892
|
-
}
|
|
893
|
-
if (type === "agent_message" || type === "message" || type === "response.output_text.done") {
|
|
894
|
-
const text = getText(payload.message) ?? getText(payload.text);
|
|
895
|
-
return text ? [{ type: "text_complete", text, isIntermediate: false }] : [];
|
|
896
|
-
}
|
|
897
|
-
if (type === "exec_command_begin" || type === "tool_call_begin") {
|
|
898
|
-
const toolUseId = getText(payload.call_id) ?? getText(payload.id) ?? `tool-${Date.now()}`;
|
|
899
|
-
const command = getText(payload.command);
|
|
900
|
-
return [{
|
|
901
|
-
type: "tool_start",
|
|
902
|
-
toolName: getText(payload.tool_name) ?? "Bash",
|
|
903
|
-
toolUseId,
|
|
904
|
-
input: command ? { command } : asRecord(payload.arguments)
|
|
905
|
-
}];
|
|
906
|
-
}
|
|
907
|
-
if (type === "exec_command_end" || type === "tool_call_end") {
|
|
908
|
-
const toolUseId = getText(payload.call_id) ?? getText(payload.id) ?? `tool-${Date.now()}`;
|
|
909
|
-
const exitCode = typeof payload.exit_code === "number" ? payload.exit_code : 0;
|
|
910
|
-
return [{
|
|
911
|
-
type: "tool_result",
|
|
912
|
-
toolName: getText(payload.tool_name) ?? "Bash",
|
|
913
|
-
toolUseId,
|
|
914
|
-
result: getText(payload.output) ?? getText(payload.result) ?? "",
|
|
915
|
-
isError: exitCode !== 0 || payload.is_error === true
|
|
916
|
-
}];
|
|
917
|
-
}
|
|
918
|
-
if (type === "turn_completed" || type === "turn.completed" || type === "task_complete" || type === "response.completed") {
|
|
919
|
-
return [{ type: "complete", usage: normalizeUsage(asRecord(payload.usage)) }];
|
|
920
|
-
}
|
|
921
|
-
if (type === "error") {
|
|
922
|
-
return [{ type: "error", message: getText(payload.message) ?? "Codex stream error" }];
|
|
923
|
-
}
|
|
924
|
-
return [];
|
|
925
|
-
}
|
|
926
|
-
function createLocalTimelineProjector(options) {
|
|
927
|
-
const sequencer = createTimelineSequencer(options);
|
|
928
|
-
let lastTurnId;
|
|
929
|
-
return {
|
|
930
|
-
project(event) {
|
|
931
|
-
const items = mapAgentEventToTimelineItems(event, lastTurnId);
|
|
932
|
-
const eventTurnId = turnIdFromAgentEvent(event);
|
|
933
|
-
const itemTurnId = items.map((item) => "turnId" in item ? item.turnId : void 0).find((turnId) => typeof turnId === "string");
|
|
934
|
-
if (eventTurnId ?? itemTurnId) lastTurnId = eventTurnId ?? itemTurnId;
|
|
935
|
-
return items.map((item) => append(sequencer, item, event.type));
|
|
936
|
-
}
|
|
937
|
-
};
|
|
938
|
-
}
|
|
939
|
-
function append(sequencer, item, providerEventType) {
|
|
940
|
-
return sequencer.append(item, { providerEventType });
|
|
941
|
-
}
|
|
942
|
-
function mapAgentEventToTimelineItems(event, lastTurnId) {
|
|
943
|
-
switch (event.type) {
|
|
944
|
-
case "status":
|
|
945
|
-
case "info":
|
|
946
|
-
return [{ type: "session_status", status: event.message }];
|
|
947
|
-
case "text_delta": {
|
|
948
|
-
const turnId = event.turnId ?? lastTurnId ?? "turn-local";
|
|
949
|
-
return [{
|
|
950
|
-
type: "assistant_message_delta",
|
|
951
|
-
text: event.text,
|
|
952
|
-
messageId: messageIdForTurn(turnId),
|
|
953
|
-
turnId
|
|
954
|
-
}];
|
|
955
|
-
}
|
|
956
|
-
case "text_complete": {
|
|
957
|
-
const turnId = event.turnId ?? lastTurnId ?? "turn-local";
|
|
958
|
-
return [{
|
|
959
|
-
type: "assistant_message",
|
|
960
|
-
text: event.text,
|
|
961
|
-
messageId: messageIdForTurn(turnId),
|
|
962
|
-
turnId
|
|
963
|
-
}];
|
|
964
|
-
}
|
|
965
|
-
case "reasoning_delta": {
|
|
966
|
-
const turnId = event.turnId ?? lastTurnId ?? "turn-local";
|
|
967
|
-
return [{
|
|
968
|
-
type: "assistant_message_delta",
|
|
969
|
-
text: event.text,
|
|
970
|
-
messageId: messageIdForTurn(turnId),
|
|
971
|
-
turnId
|
|
972
|
-
}];
|
|
973
|
-
}
|
|
974
|
-
case "reasoning": {
|
|
975
|
-
const turnId = event.turnId ?? lastTurnId ?? "turn-local";
|
|
976
|
-
return [{
|
|
977
|
-
type: "assistant_message",
|
|
978
|
-
text: event.text,
|
|
979
|
-
messageId: messageIdForTurn(turnId),
|
|
980
|
-
turnId
|
|
981
|
-
}];
|
|
982
|
-
}
|
|
983
|
-
case "tool_start":
|
|
984
|
-
return [{
|
|
985
|
-
type: "tool_call",
|
|
986
|
-
callId: event.toolUseId,
|
|
987
|
-
name: event.toolName,
|
|
988
|
-
status: "running",
|
|
989
|
-
detail: { input: event.input },
|
|
990
|
-
turnId: event.turnId
|
|
991
|
-
}];
|
|
992
|
-
case "tool_result":
|
|
993
|
-
return [{
|
|
994
|
-
type: "tool_result",
|
|
995
|
-
callId: event.toolUseId,
|
|
996
|
-
result: event.result,
|
|
997
|
-
isError: event.isError,
|
|
998
|
-
turnId: event.turnId
|
|
999
|
-
}];
|
|
1000
|
-
case "permission_request":
|
|
1001
|
-
return [{
|
|
1002
|
-
type: "permission_requested",
|
|
1003
|
-
request: {
|
|
1004
|
-
requestId: event.requestId,
|
|
1005
|
-
toolName: event.toolName,
|
|
1006
|
-
input: { command: event.command },
|
|
1007
|
-
reason: event.reason
|
|
1008
|
-
}
|
|
1009
|
-
}];
|
|
1010
|
-
case "permission_response":
|
|
1011
|
-
return [{
|
|
1012
|
-
type: "permission_resolved",
|
|
1013
|
-
requestId: event.requestId,
|
|
1014
|
-
resolution: {
|
|
1015
|
-
allowed: event.allowed,
|
|
1016
|
-
remember: event.remember ?? false
|
|
1017
|
-
}
|
|
1018
|
-
}];
|
|
1019
|
-
case "complete": {
|
|
1020
|
-
const items = [];
|
|
1021
|
-
if (lastTurnId) {
|
|
1022
|
-
items.push({
|
|
1023
|
-
type: "turn_completed",
|
|
1024
|
-
turnId: lastTurnId,
|
|
1025
|
-
usage: event.usage
|
|
1026
|
-
});
|
|
1027
|
-
}
|
|
1028
|
-
items.push({ type: "session_status", status: "ready" });
|
|
1029
|
-
return items;
|
|
1030
|
-
}
|
|
1031
|
-
case "error":
|
|
1032
|
-
if (lastTurnId) {
|
|
1033
|
-
return [{
|
|
1034
|
-
type: "turn_failed",
|
|
1035
|
-
turnId: lastTurnId,
|
|
1036
|
-
error: { message: event.message }
|
|
1037
|
-
}];
|
|
1038
|
-
}
|
|
1039
|
-
return [{ type: "session_status", status: "failed" }];
|
|
1040
|
-
case "typed_error":
|
|
1041
|
-
if (lastTurnId) {
|
|
1042
|
-
return [{
|
|
1043
|
-
type: "turn_failed",
|
|
1044
|
-
turnId: lastTurnId,
|
|
1045
|
-
error: event.error
|
|
1046
|
-
}];
|
|
1047
|
-
}
|
|
1048
|
-
return [{ type: "session_status", status: "failed" }];
|
|
1049
|
-
case "source_activated":
|
|
1050
|
-
return [{
|
|
1051
|
-
type: "source_state_changed",
|
|
1052
|
-
source: {
|
|
1053
|
-
sourceSlug: event.sourceSlug,
|
|
1054
|
-
state: "active",
|
|
1055
|
-
originalMessage: event.originalMessage
|
|
1056
|
-
}
|
|
1057
|
-
}];
|
|
1058
|
-
case "usage_update":
|
|
1059
|
-
return [{ type: "session_status", status: "usage_update" }];
|
|
1060
|
-
case "working_directory_changed":
|
|
1061
|
-
return [{
|
|
1062
|
-
type: "session_status",
|
|
1063
|
-
status: `working_directory:${event.workingDirectory}`
|
|
1064
|
-
}];
|
|
1065
|
-
case "compaction_started":
|
|
1066
|
-
return [{ type: "compaction_started" }];
|
|
1067
|
-
case "compaction_boundary":
|
|
1068
|
-
return [{ type: "compaction_boundary", summary: event.summary }];
|
|
1069
|
-
case "task_backgrounded":
|
|
1070
|
-
case "shell_backgrounded":
|
|
1071
|
-
case "task_progress":
|
|
1072
|
-
case "task_completed":
|
|
1073
|
-
case "shell_killed":
|
|
1074
|
-
case "steer_undelivered":
|
|
1075
|
-
return [{ type: "session_status", status: event.type }];
|
|
1076
|
-
default:
|
|
1077
|
-
return [];
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
function turnIdFromAgentEvent(event) {
|
|
1081
|
-
if ("turnId" in event) return event.turnId;
|
|
1082
|
-
return void 0;
|
|
1083
|
-
}
|
|
1084
|
-
function messageIdForTurn(turnId) {
|
|
1085
|
-
return `${turnId}:assistant`;
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
// ../packages/adaptor/dist/chunk-DMO7RTY3.js
|
|
1089
|
-
import { execFile } from "child_process";
|
|
1090
|
-
import { spawn } from "child_process";
|
|
1091
|
-
import { createInterface } from "readline";
|
|
1092
|
-
var NESTED_AGENT_ENV_PREFIXES = [
|
|
1093
|
-
"CLAUDECODE",
|
|
1094
|
-
"CODEX_HOME",
|
|
1095
|
-
// All CODEX_* prefix keys
|
|
1096
|
-
"CODEX_",
|
|
1097
|
-
"CLAUDE_CODE_ENTRYPOINT"
|
|
1098
|
-
];
|
|
1099
|
-
function shouldStripNestedAgentEnv(key) {
|
|
1100
|
-
return NESTED_AGENT_ENV_PREFIXES.some(
|
|
1101
|
-
(prefix) => key === prefix || key.startsWith(prefix + "_") || key.startsWith(prefix)
|
|
1102
|
-
);
|
|
1103
|
-
}
|
|
1104
|
-
var CLAUDE_AGENT_SDK_CLIENT_APP = "weft/agent-bridge";
|
|
1105
|
-
function createSanitizedClaudeEnvironment(source = process.env) {
|
|
1106
|
-
const env = {};
|
|
1107
|
-
for (const [key, value] of Object.entries(source)) {
|
|
1108
|
-
if (value !== void 0 && !shouldStripNestedAgentEnv(key)) {
|
|
1109
|
-
env[key] = value;
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
env.CLAUDE_AGENT_SDK_CLIENT_APP = CLAUDE_AGENT_SDK_CLIENT_APP;
|
|
1113
|
-
return env;
|
|
1114
|
-
}
|
|
1115
|
-
function resolveClaudeExecutable(env) {
|
|
1116
|
-
return env.WEFT_CLAUDE_EXECUTABLE || void 0;
|
|
1117
|
-
}
|
|
1118
|
-
async function readClaudeAuth(executable, env) {
|
|
1119
|
-
const resolvedEnv = env ?? createSanitizedClaudeEnvironment();
|
|
1120
|
-
const claudeBin = executable ?? resolveClaudeExecutable(resolvedEnv) ?? "claude";
|
|
1121
|
-
try {
|
|
1122
|
-
const { stdout, exitCode } = await new Promise((resolve) => {
|
|
1123
|
-
execFile(claudeBin, ["auth", "status", "--json"], { env: resolvedEnv }, (error, stdout2, stderr) => {
|
|
1124
|
-
resolve({
|
|
1125
|
-
stdout: stdout2?.toString() ?? "",
|
|
1126
|
-
stderr: stderr?.toString() ?? "",
|
|
1127
|
-
exitCode: error ? error.code ?? 1 : 0
|
|
1128
|
-
});
|
|
1129
|
-
});
|
|
1130
|
-
});
|
|
1131
|
-
if (exitCode !== 0) {
|
|
1132
|
-
return {
|
|
1133
|
-
mode: "provider-owned",
|
|
1134
|
-
configured: false,
|
|
1135
|
-
source: `${claudeBin} auth status --json`,
|
|
1136
|
-
error: `Claude Code CLI returned exit code ${exitCode}. Ensure \`claude\` is on PATH or set WEFT_CLAUDE_EXECUTABLE.`
|
|
1137
|
-
};
|
|
1138
|
-
}
|
|
1139
|
-
const status = JSON.parse(stdout);
|
|
1140
|
-
return {
|
|
1141
|
-
mode: "provider-owned",
|
|
1142
|
-
configured: status.loggedIn === true,
|
|
1143
|
-
source: `${claudeBin} auth status --json`,
|
|
1144
|
-
method: status.authMethod,
|
|
1145
|
-
provider: status.apiProvider,
|
|
1146
|
-
accountPresent: Boolean(status.account)
|
|
1147
|
-
};
|
|
1148
|
-
} catch (err) {
|
|
1149
|
-
return {
|
|
1150
|
-
mode: "provider-owned",
|
|
1151
|
-
configured: false,
|
|
1152
|
-
source: `${claudeBin} auth status --json`,
|
|
1153
|
-
error: `Claude Code auth detection failed: ${err.message}. Run \`claude\` and complete login, or set CLAUDE_CONFIG_DIR.`
|
|
1154
|
-
};
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
var DEFAULT_REQUEST_TIMEOUT_MS = 1e4;
|
|
1158
|
-
var nextRpcId = 1;
|
|
1159
|
-
function makeRequest(method, params) {
|
|
1160
|
-
return { jsonrpc: "2.0", id: nextRpcId++, method, params };
|
|
1161
|
-
}
|
|
1162
|
-
function resolveCodexExecutable(env) {
|
|
1163
|
-
return env.WEFT_CODEX_EXECUTABLE || void 0;
|
|
1164
|
-
}
|
|
1165
|
-
function codexAuthFromResult(result, source) {
|
|
1166
|
-
const requiresOpenaiAuth = result.requiresOpenaiAuth === true;
|
|
1167
|
-
const accountPresent = Boolean(result.account);
|
|
1168
|
-
const configured = !requiresOpenaiAuth || accountPresent;
|
|
1169
|
-
return {
|
|
1170
|
-
mode: "provider-owned",
|
|
1171
|
-
configured,
|
|
1172
|
-
source,
|
|
1173
|
-
accountPresent,
|
|
1174
|
-
requiresOpenaiAuth,
|
|
1175
|
-
error: configured ? void 0 : codexAuthMissingMessage()
|
|
1176
|
-
};
|
|
1177
|
-
}
|
|
1178
|
-
async function readCodexAuth(executable, env, requestTimeoutMs) {
|
|
1179
|
-
const resolvedEnv = env ?? Object.fromEntries(
|
|
1180
|
-
Object.entries(process.env).filter(([, v]) => v !== void 0)
|
|
1181
|
-
);
|
|
1182
|
-
const codexBin = executable ?? resolveCodexExecutable(resolvedEnv) ?? "codex";
|
|
1183
|
-
const timeout = requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
1184
|
-
const source = `${codexBin} app-server account/read`;
|
|
1185
|
-
let proc = null;
|
|
1186
|
-
try {
|
|
1187
|
-
proc = spawn(codexBin, ["app-server"], {
|
|
1188
|
-
env: resolvedEnv,
|
|
1189
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
1190
|
-
});
|
|
1191
|
-
} catch (err) {
|
|
1192
|
-
return {
|
|
1193
|
-
mode: "provider-owned",
|
|
1194
|
-
configured: false,
|
|
1195
|
-
source,
|
|
1196
|
-
error: `Codex auth detection failed: ${err.message}. Ensure \`codex\` is on PATH or set WEFT_CODEX_EXECUTABLE.`
|
|
1197
|
-
};
|
|
1198
|
-
}
|
|
1199
|
-
const spawnError = new Promise((_, reject) => {
|
|
1200
|
-
proc.on("error", (err) => {
|
|
1201
|
-
reject(new Error(`Codex auth detection failed: ${err.message}. Ensure \`codex\` is on PATH or set WEFT_CODEX_EXECUTABLE.`));
|
|
1202
|
-
});
|
|
1203
|
-
});
|
|
1204
|
-
try {
|
|
1205
|
-
const rl = createInterface({ input: proc.stdout, crlfDelay: Infinity });
|
|
1206
|
-
const writeMsg = (msg) => {
|
|
1207
|
-
proc.stdin.write(JSON.stringify(msg) + "\n");
|
|
1208
|
-
};
|
|
1209
|
-
const readResponse = () => {
|
|
1210
|
-
const linePromise = new Promise((resolve, reject) => {
|
|
1211
|
-
const deadline = Date.now() + timeout;
|
|
1212
|
-
const timer = setTimeout(() => {
|
|
1213
|
-
reject(new Error(`Timeout waiting for Codex app-server response (${timeout}ms)`));
|
|
1214
|
-
}, Math.max(0, deadline - Date.now()));
|
|
1215
|
-
rl.once("line", (line) => {
|
|
1216
|
-
clearTimeout(timer);
|
|
1217
|
-
const trimmed = line.trim();
|
|
1218
|
-
if (trimmed) {
|
|
1219
|
-
try {
|
|
1220
|
-
resolve(JSON.parse(trimmed));
|
|
1221
|
-
} catch {
|
|
1222
|
-
reject(new Error(`Invalid JSON-RPC response: ${trimmed}`));
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
});
|
|
1226
|
-
});
|
|
1227
|
-
return Promise.race([linePromise, spawnError]);
|
|
1228
|
-
};
|
|
1229
|
-
writeMsg(makeRequest("initialize", {
|
|
1230
|
-
protocolVersion: "2025-03-26",
|
|
1231
|
-
capabilities: {},
|
|
1232
|
-
clientInfo: { name: "weft", title: "Weft", version: "0.1.0" }
|
|
1233
|
-
}));
|
|
1234
|
-
const initResp = await readResponse();
|
|
1235
|
-
if (initResp.error) {
|
|
1236
|
-
rl.close();
|
|
1237
|
-
proc.kill();
|
|
1238
|
-
return {
|
|
1239
|
-
mode: "provider-owned",
|
|
1240
|
-
configured: false,
|
|
1241
|
-
source,
|
|
1242
|
-
error: `Codex initialize failed: ${initResp.error.message}`
|
|
1243
|
-
};
|
|
1244
|
-
}
|
|
1245
|
-
writeMsg({ jsonrpc: "2.0", method: "initialized", params: {} });
|
|
1246
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
1247
|
-
writeMsg(makeRequest("account/read", { refreshToken: false }));
|
|
1248
|
-
const accountResp = await readResponse();
|
|
1249
|
-
rl.close();
|
|
1250
|
-
proc.kill();
|
|
1251
|
-
if (accountResp.error) {
|
|
1252
|
-
return {
|
|
1253
|
-
mode: "provider-owned",
|
|
1254
|
-
configured: false,
|
|
1255
|
-
source,
|
|
1256
|
-
error: `Codex account/read failed: ${accountResp.error.message}`
|
|
1257
|
-
};
|
|
1258
|
-
}
|
|
1259
|
-
return codexAuthFromResult(
|
|
1260
|
-
accountResp.result,
|
|
1261
|
-
source
|
|
1262
|
-
);
|
|
1263
|
-
} catch (err) {
|
|
1264
|
-
try {
|
|
1265
|
-
proc.kill();
|
|
1266
|
-
} catch {
|
|
1267
|
-
}
|
|
1268
|
-
return {
|
|
1269
|
-
mode: "provider-owned",
|
|
1270
|
-
configured: false,
|
|
1271
|
-
source,
|
|
1272
|
-
error: `Codex auth detection failed: ${err.message}. Ensure \`codex\` is on PATH or set WEFT_CODEX_EXECUTABLE.`
|
|
1273
|
-
};
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
function codexAuthMissingMessage() {
|
|
1277
|
-
return "Codex authentication is not configured. Run `codex login` or set CODEX_HOME to a directory with valid auth.";
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
|
-
// ../packages/adaptor/dist/auth/index.js
|
|
1281
|
-
init_chunk_DGUM43GV2();
|
|
1282
|
-
|
|
1283
|
-
// ../packages/local-runtime/dist/index.js
|
|
1284
|
-
import { spawn as spawn2 } from "child_process";
|
|
1285
|
-
import { createInterface as createInterface2 } from "readline";
|
|
1286
|
-
function createSessionId(provider) {
|
|
1287
|
-
return `local-${provider}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1288
|
-
}
|
|
1289
|
-
function cleanEnv(source) {
|
|
1290
|
-
return Object.fromEntries(
|
|
1291
|
-
Object.entries(source).filter((entry) => entry[1] !== void 0)
|
|
1292
|
-
);
|
|
1293
|
-
}
|
|
1294
|
-
function mapClaudePermissionMode(mode) {
|
|
1295
|
-
if (!mode) return void 0;
|
|
1296
|
-
if (mode === "allow-all") return "bypassPermissions";
|
|
1297
|
-
if (mode === "safe") return "plan";
|
|
1298
|
-
return "default";
|
|
1299
|
-
}
|
|
1300
|
-
function buildLocalCliArgs(options, message) {
|
|
1301
|
-
if (options.provider === "claude") {
|
|
1302
|
-
const args2 = [
|
|
1303
|
-
"-p",
|
|
1304
|
-
"--output-format",
|
|
1305
|
-
"stream-json",
|
|
1306
|
-
"--include-partial-messages"
|
|
1307
|
-
];
|
|
1308
|
-
if (options.model) args2.push("--model", options.model);
|
|
1309
|
-
const effort = options.reasoningEffort ?? options.thinkingLevel;
|
|
1310
|
-
if (effort) args2.push("--effort", effort);
|
|
1311
|
-
const permissionMode = mapClaudePermissionMode(options.permissionMode);
|
|
1312
|
-
if (permissionMode) args2.push("--permission-mode", permissionMode);
|
|
1313
|
-
args2.push(message);
|
|
1314
|
-
return args2;
|
|
1315
|
-
}
|
|
1316
|
-
const args = ["exec", "--json", "--cd", options.cwd];
|
|
1317
|
-
if (options.model) args.push("--model", options.model);
|
|
1318
|
-
if (options.reasoningEffort) args.push("--config", `model_reasoning_effort="${options.reasoningEffort}"`);
|
|
1319
|
-
if (options.permissionMode) {
|
|
1320
|
-
const codexParams = mapPermissionModeToCodexParams(options.permissionMode);
|
|
1321
|
-
args.push("--config", `approval_policy="${codexParams.approvalPolicy}"`);
|
|
1322
|
-
args.push("--sandbox", codexParams.sandbox);
|
|
1323
|
-
}
|
|
1324
|
-
args.push(message);
|
|
1325
|
-
return args;
|
|
1326
|
-
}
|
|
1327
|
-
function optionsForSend(options, sendOptions) {
|
|
1328
|
-
if (!sendOptions?.permissionMode) return options;
|
|
1329
|
-
return {
|
|
1330
|
-
...options,
|
|
1331
|
-
permissionMode: sendOptions.permissionMode
|
|
1332
|
-
};
|
|
1333
|
-
}
|
|
1334
|
-
function mapProviderLine(provider, line) {
|
|
1335
|
-
return provider === "claude" ? mapClaudeStreamJsonLine(line) : mapCodexExecJsonLine(line);
|
|
1336
|
-
}
|
|
1337
|
-
function createLocalAgentSession(options) {
|
|
1338
|
-
const events = new PushAgentEventStream();
|
|
1339
|
-
const sessionId = options.sessionId ?? createSessionId(options.provider);
|
|
1340
|
-
let state = initialRuntimeState;
|
|
1341
|
-
let proc = null;
|
|
1342
|
-
const queuedSendOptions = [];
|
|
1343
|
-
function dispatch(action) {
|
|
1344
|
-
state = reduceRuntimeState(state, action);
|
|
1345
|
-
}
|
|
1346
|
-
async function preflight() {
|
|
1347
|
-
dispatch({ type: "preflight_start" });
|
|
1348
|
-
const auth = options.provider === "claude" ? await readClaudeAuth(options.executable, options.env ?? createSanitizedClaudeEnvironment()) : await readCodexAuth(options.executable, options.env, options.requestTimeoutMs);
|
|
1349
|
-
if (auth.configured) {
|
|
1350
|
-
dispatch({ type: "preflight_ok" });
|
|
1351
|
-
} else {
|
|
1352
|
-
dispatch({ type: "preflight_error", error: auth.error ?? `${options.provider} auth is not configured` });
|
|
1353
|
-
}
|
|
1354
|
-
return auth;
|
|
1355
|
-
}
|
|
1356
|
-
async function consumeStdout(stdout) {
|
|
1357
|
-
const rl = createInterface2({ input: stdout, crlfDelay: Infinity });
|
|
1358
|
-
let sawComplete = false;
|
|
1359
|
-
for await (const line of rl) {
|
|
1360
|
-
for (const event of mapProviderLine(options.provider, line)) {
|
|
1361
|
-
if (event.type === "permission_request") {
|
|
1362
|
-
dispatch({ type: "permission_request", requestId: event.requestId });
|
|
1363
|
-
}
|
|
1364
|
-
if (event.type === "complete") {
|
|
1365
|
-
if (!sawComplete) dispatch({ type: "complete" });
|
|
1366
|
-
sawComplete = true;
|
|
1367
|
-
}
|
|
1368
|
-
events.emit(event);
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
1371
|
-
return sawComplete;
|
|
1372
|
-
}
|
|
1373
|
-
async function consumeStderr(stderr) {
|
|
1374
|
-
const chunks = [];
|
|
1375
|
-
for await (const chunk of stderr) {
|
|
1376
|
-
chunks.push(chunk);
|
|
1377
|
-
}
|
|
1378
|
-
return Buffer.concat(chunks).toString();
|
|
1379
|
-
}
|
|
1380
|
-
async function runMessage(message, sendOptions, alreadyAccepted = false) {
|
|
1381
|
-
if (!alreadyAccepted) {
|
|
1382
|
-
dispatch({ type: "send_message", message });
|
|
1383
|
-
}
|
|
1384
|
-
const acceptedCountAfterCurrentMessage = state.acceptedMessages.length;
|
|
1385
|
-
events.emit({ type: "status", message: `${options.provider} runtime running` });
|
|
1386
|
-
const executable = options.executable ?? options.provider;
|
|
1387
|
-
const args = buildLocalCliArgs(optionsForSend(options, sendOptions), message);
|
|
1388
|
-
const env = options.provider === "claude" ? options.env ?? createSanitizedClaudeEnvironment() : options.env ?? cleanEnv(process.env);
|
|
1389
|
-
try {
|
|
1390
|
-
proc = spawn2(executable, args, {
|
|
1391
|
-
cwd: options.cwd,
|
|
1392
|
-
env,
|
|
1393
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
1394
|
-
});
|
|
1395
|
-
} catch (err) {
|
|
1396
|
-
const messageText = `${options.provider} runtime failed to start: ${err.message}`;
|
|
1397
|
-
dispatch({ type: "error", error: messageText });
|
|
1398
|
-
events.emit({ type: "error", message: messageText });
|
|
1399
|
-
throw new Error(messageText);
|
|
1400
|
-
}
|
|
1401
|
-
const stderrPromise = consumeStderr(proc.stderr);
|
|
1402
|
-
const sawComplete = await consumeStdout(proc.stdout);
|
|
1403
|
-
const exitCode = await new Promise((resolve) => {
|
|
1404
|
-
proc.on("close", (code) => resolve(code ?? 0));
|
|
1405
|
-
});
|
|
1406
|
-
const stderr = await stderrPromise;
|
|
1407
|
-
if (exitCode !== 0) {
|
|
1408
|
-
const messageText = stderr.trim() || `${options.provider} exited with code ${exitCode}`;
|
|
1409
|
-
dispatch({ type: "error", error: messageText });
|
|
1410
|
-
events.emit({ type: "error", message: messageText });
|
|
1411
|
-
throw new Error(messageText);
|
|
1412
|
-
}
|
|
1413
|
-
if (!sawComplete && state.status === "running") {
|
|
1414
|
-
dispatch({ type: "complete" });
|
|
1415
|
-
events.emit({ type: "complete" });
|
|
1416
|
-
}
|
|
1417
|
-
const nextMessage = state.status === "running" ? state.acceptedMessages[acceptedCountAfterCurrentMessage] : void 0;
|
|
1418
|
-
if (nextMessage !== void 0) {
|
|
1419
|
-
await runMessage(nextMessage, queuedSendOptions.shift(), true);
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
async function sendMessage(message, sendOptions) {
|
|
1423
|
-
if (state.status === "failed" || state.status === "disposed") {
|
|
1424
|
-
throw new Error(state.lastError ?? `Runtime is ${state.status}`);
|
|
1425
|
-
}
|
|
1426
|
-
if (state.status === "running" || state.status === "waiting_for_permission") {
|
|
1427
|
-
queuedSendOptions.push(sendOptions);
|
|
1428
|
-
dispatch({ type: "send_message", message });
|
|
1429
|
-
events.emit({ type: "status", message: `${options.provider} runtime queued message` });
|
|
1430
|
-
return;
|
|
1431
|
-
}
|
|
1432
|
-
if (state.status === "idle") {
|
|
1433
|
-
const auth = await preflight();
|
|
1434
|
-
if (!auth.configured) {
|
|
1435
|
-
throw new Error(auth.error ?? `${options.provider} auth is not configured`);
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
await runMessage(message, sendOptions);
|
|
1439
|
-
}
|
|
1440
|
-
return {
|
|
1441
|
-
sessionId,
|
|
1442
|
-
provider: options.provider,
|
|
1443
|
-
events,
|
|
1444
|
-
preflight,
|
|
1445
|
-
getState() {
|
|
1446
|
-
return state;
|
|
1447
|
-
},
|
|
1448
|
-
commands: {
|
|
1449
|
-
sendMessage,
|
|
1450
|
-
async abort(reason) {
|
|
1451
|
-
proc?.kill();
|
|
1452
|
-
proc = null;
|
|
1453
|
-
queuedSendOptions.splice(0, queuedSendOptions.length);
|
|
1454
|
-
dispatch({ type: "abort", reason });
|
|
1455
|
-
events.emit({ type: "error", message: reason ?? "Aborted" });
|
|
1456
|
-
events.emit({ type: "complete" });
|
|
1457
|
-
},
|
|
1458
|
-
async respondToPermission(_requestId, _allowed, _remember) {
|
|
1459
|
-
dispatch({ type: "permission_response" });
|
|
1460
|
-
},
|
|
1461
|
-
async dispose() {
|
|
1462
|
-
proc?.kill();
|
|
1463
|
-
proc = null;
|
|
1464
|
-
queuedSendOptions.splice(0, queuedSendOptions.length);
|
|
1465
|
-
dispatch({ type: "dispose" });
|
|
1466
|
-
events.close();
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
};
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1472
|
-
// ../packages/adaptor/dist/index.js
|
|
1473
|
-
init_chunk_XW533RAZ();
|
|
1474
|
-
init_chunk_DGUM43GV2();
|
|
1475
|
-
import bashParser from "bash-parser";
|
|
1476
|
-
import { existsSync as existsSync2, readFileSync, writeFileSync, renameSync, unlinkSync, appendFileSync, mkdirSync, statSync } from "fs";
|
|
1477
|
-
import { homedir as homedir22 } from "os";
|
|
1478
|
-
import { join as join4 } from "path";
|
|
1479
|
-
import { isContextOverflow } from "@mariozechner/pi-ai";
|
|
1480
|
-
var IS_DEV_RUNTIME = !!process.env.WEFT_DEV_RUNTIME;
|
|
1481
|
-
var debugEnabled = typeof process !== "undefined" && process.env?.WEFT_DEBUG === "1";
|
|
1482
|
-
function isCliJsonOnlyMode() {
|
|
1483
|
-
return typeof process !== "undefined" && process.env?.WEFT_CLI_JSON_ONLY === "1";
|
|
1484
|
-
}
|
|
1485
|
-
function detectEnvironment() {
|
|
1486
|
-
if (typeof process === "undefined") {
|
|
1487
|
-
return "electron-renderer";
|
|
1488
|
-
}
|
|
1489
|
-
if (process.type === "browser") {
|
|
1490
|
-
return "electron-main";
|
|
1491
|
-
}
|
|
1492
|
-
if (process.type === "renderer") {
|
|
1493
|
-
return "electron-renderer";
|
|
1494
|
-
}
|
|
1495
|
-
return "cli";
|
|
1496
|
-
}
|
|
1497
|
-
var electronLog = null;
|
|
1498
|
-
var electronLogChecked = false;
|
|
1499
|
-
function getElectronLog() {
|
|
1500
|
-
if (electronLogChecked) {
|
|
1501
|
-
return electronLog ?? null;
|
|
1502
|
-
}
|
|
1503
|
-
electronLogChecked = true;
|
|
1504
|
-
try {
|
|
1505
|
-
const loaded = __require3("electron-log/main");
|
|
1506
|
-
electronLog = loaded?.default ?? loaded ?? null;
|
|
1507
|
-
} catch {
|
|
1508
|
-
electronLog = null;
|
|
1509
|
-
}
|
|
1510
|
-
return electronLog ?? null;
|
|
1511
|
-
}
|
|
1512
|
-
function isDebugEnabled() {
|
|
1513
|
-
if (isCliJsonOnlyMode()) return false;
|
|
1514
|
-
return debugEnabled;
|
|
1515
|
-
}
|
|
1516
|
-
function safeStringify(obj) {
|
|
1517
|
-
try {
|
|
1518
|
-
return JSON.stringify(obj);
|
|
1519
|
-
} catch {
|
|
1520
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
1521
|
-
return JSON.stringify(obj, (_key, value) => {
|
|
1522
|
-
if (typeof value === "object" && value !== null) {
|
|
1523
|
-
if (seen.has(value)) {
|
|
1524
|
-
return "[Circular]";
|
|
1525
|
-
}
|
|
1526
|
-
seen.add(value);
|
|
1527
|
-
}
|
|
1528
|
-
return value;
|
|
1529
|
-
});
|
|
1530
|
-
}
|
|
1531
|
-
}
|
|
1532
|
-
function formatMessage(scope, message, args) {
|
|
1533
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1534
|
-
const scopeStr = scope ? `[${scope}] ` : "";
|
|
1535
|
-
const argsStr = args.length > 0 ? " " + args.map((a) => typeof a === "object" ? safeStringify(a) : String(a)).join(" ") : "";
|
|
1536
|
-
return `${timestamp} ${scopeStr}${message}${argsStr}
|
|
1537
|
-
`;
|
|
1538
|
-
}
|
|
1539
|
-
function output(formatted) {
|
|
1540
|
-
const env = detectEnvironment();
|
|
1541
|
-
if (env === "electron-main") {
|
|
1542
|
-
const log2 = getElectronLog();
|
|
1543
|
-
log2?.info?.(formatted.trim());
|
|
1544
|
-
}
|
|
1545
|
-
if (env === "electron-renderer") {
|
|
1546
|
-
console.log(formatted.trim());
|
|
1547
|
-
} else if (typeof process !== "undefined" && process.stderr) {
|
|
1548
|
-
process.stderr.write(formatted);
|
|
1549
|
-
} else {
|
|
1550
|
-
console.log(formatted.trim());
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
function createLogger(scope) {
|
|
1554
|
-
const logWithLevel = (level, message, args) => {
|
|
1555
|
-
if (!isDebugEnabled()) return;
|
|
1556
|
-
const levelStr = level.toUpperCase().padEnd(5);
|
|
1557
|
-
output(formatMessage(scope, `${levelStr} ${message}`, args));
|
|
1558
|
-
};
|
|
1559
|
-
return {
|
|
1560
|
-
debug: (message, ...args) => logWithLevel("debug", message, args),
|
|
1561
|
-
info: (message, ...args) => logWithLevel("info", message, args),
|
|
1562
|
-
warn: (message, ...args) => logWithLevel("warn", message, args),
|
|
1563
|
-
error: (message, ...args) => logWithLevel("error", message, args)
|
|
1564
|
-
};
|
|
1565
|
-
}
|
|
1566
|
-
var IS_PACKAGED = process.argv.some((arg) => arg.includes("app.asar"));
|
|
1567
|
-
var INTERCEPTOR_LOGGING_ENABLED = !IS_PACKAGED;
|
|
1568
|
-
var DEBUG = INTERCEPTOR_LOGGING_ENABLED && (process.argv.includes("--debug") || process.env.WEFT_DEBUG === "1");
|
|
1569
|
-
var CONFIG_FILE = join4(homedir22(), ".weft", "config.json");
|
|
1570
|
-
var _sessionDir = process.env.WEFT_SESSION_DIR || null;
|
|
1571
|
-
var LOG_DIR = join4(homedir22(), ".weft", "logs");
|
|
1572
|
-
var LOG_FILE = join4(LOG_DIR, "interceptor.log");
|
|
1573
|
-
try {
|
|
1574
|
-
if (!existsSync2(LOG_DIR)) {
|
|
1575
|
-
mkdirSync(LOG_DIR, { recursive: true });
|
|
1576
|
-
}
|
|
1577
|
-
} catch {
|
|
1578
|
-
}
|
|
1579
|
-
var MAX_LOG_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
1580
|
-
try {
|
|
1581
|
-
if (existsSync2(LOG_FILE)) {
|
|
1582
|
-
const stat = statSync(LOG_FILE);
|
|
1583
|
-
if (Date.now() - stat.mtimeMs > MAX_LOG_AGE_MS) {
|
|
1584
|
-
const prevLog = LOG_FILE + ".prev";
|
|
1585
|
-
renameSync(LOG_FILE, prevLog);
|
|
1586
|
-
}
|
|
1587
|
-
}
|
|
1588
|
-
} catch {
|
|
1589
|
-
}
|
|
1590
|
-
var MAX_ERROR_AGE_MS = 5 * 60 * 1e3;
|
|
1591
|
-
var log = createLogger("tool-matching");
|
|
1592
|
-
|
|
1593
|
-
// ../packages/provider-claude/dist/index.js
|
|
1594
|
-
function createClaudeNativeSdkDriver(options) {
|
|
1595
|
-
return new ClaudeNativeSdkDriver(options);
|
|
1596
|
-
}
|
|
1597
|
-
var ClaudeNativeSdkDriver = class {
|
|
1598
|
-
constructor(options) {
|
|
1599
|
-
this.options = options;
|
|
1600
|
-
}
|
|
1601
|
-
options;
|
|
1602
|
-
activeQuery = null;
|
|
1603
|
-
activeSequencer = null;
|
|
1604
|
-
pendingPermissions = /* @__PURE__ */ new Map();
|
|
1605
|
-
turnCounter = 0;
|
|
1606
|
-
async sendMessage(input, sequencer) {
|
|
1607
|
-
const turnId = input.options?.turnId ?? `claude-turn-${++this.turnCounter}`;
|
|
1608
|
-
const queryRunner = await this.getQueryRunner();
|
|
1609
|
-
this.activeSequencer = sequencer;
|
|
1610
|
-
sequencer.append({ type: "turn_started", turnId }, { providerEventType: "claude-sdk:send_message" });
|
|
1611
|
-
try {
|
|
1612
|
-
const query = queryRunner({
|
|
1613
|
-
prompt: input.message,
|
|
1614
|
-
options: {
|
|
1615
|
-
cwd: this.options.cwd,
|
|
1616
|
-
model: input.options?.model ?? this.options.model,
|
|
1617
|
-
effort: input.options?.reasoningEffort ?? this.options.reasoningEffort,
|
|
1618
|
-
env: this.options.env,
|
|
1619
|
-
includePartialMessages: true,
|
|
1620
|
-
includeHookEvents: true,
|
|
1621
|
-
...sourceToolOptions(this.options.sourceTools),
|
|
1622
|
-
...mapPermissionMode(this.options.permissionMode),
|
|
1623
|
-
canUseTool: (toolName, toolInput, context) => this.handleCanUseTool(toolName, toolInput, context, sequencer)
|
|
1624
|
-
}
|
|
1625
|
-
});
|
|
1626
|
-
this.activeQuery = query;
|
|
1627
|
-
for await (const message of query) {
|
|
1628
|
-
this.projectSdkMessage(message, turnId, sequencer);
|
|
1629
|
-
}
|
|
1630
|
-
} catch (error) {
|
|
1631
|
-
sequencer.append({
|
|
1632
|
-
type: "turn_failed",
|
|
1633
|
-
turnId,
|
|
1634
|
-
error: { message: error instanceof Error ? error.message : String(error) }
|
|
1635
|
-
}, { providerEventType: "claude-sdk:error" });
|
|
1636
|
-
throw error;
|
|
1637
|
-
} finally {
|
|
1638
|
-
this.activeQuery = null;
|
|
1639
|
-
this.activeSequencer = null;
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
async abort(reason) {
|
|
1643
|
-
if (this.activeQuery?.interrupt) {
|
|
1644
|
-
await this.activeQuery.interrupt();
|
|
1645
|
-
return;
|
|
1646
|
-
}
|
|
1647
|
-
this.activeQuery?.close?.();
|
|
1648
|
-
if (this.activeSequencer) {
|
|
1649
|
-
this.activeSequencer.append({
|
|
1650
|
-
type: "session_status",
|
|
1651
|
-
status: reason ? `aborted: ${reason}` : "aborted"
|
|
1652
|
-
}, { providerEventType: "claude-sdk:abort" });
|
|
1653
|
-
}
|
|
1654
|
-
}
|
|
1655
|
-
async respondToPermission(requestId, allowed, remember) {
|
|
1656
|
-
const pending = this.pendingPermissions.get(requestId);
|
|
1657
|
-
if (!pending) return;
|
|
1658
|
-
this.pendingPermissions.delete(requestId);
|
|
1659
|
-
const resolution = { allowed, remember };
|
|
1660
|
-
this.activeSequencer?.append({
|
|
1661
|
-
type: "permission_resolved",
|
|
1662
|
-
requestId,
|
|
1663
|
-
resolution
|
|
1664
|
-
}, { providerEventType: "claude-sdk:permission_response" });
|
|
1665
|
-
pending.resolve(allowed ? { behavior: "allow", toolUseID: requestId, decisionClassification: remember ? "user_permanent" : "user_temporary" } : { behavior: "deny", message: "Permission denied by user", toolUseID: requestId, decisionClassification: "user_reject" });
|
|
1666
|
-
}
|
|
1667
|
-
async dispose() {
|
|
1668
|
-
this.activeQuery?.close?.();
|
|
1669
|
-
for (const [requestId, pending] of this.pendingPermissions) {
|
|
1670
|
-
pending.resolve({ behavior: "deny", message: "Runtime disposed", toolUseID: requestId });
|
|
1671
|
-
}
|
|
1672
|
-
this.pendingPermissions.clear();
|
|
1673
|
-
}
|
|
1674
|
-
async getQueryRunner() {
|
|
1675
|
-
if (this.options.query) return this.options.query;
|
|
1676
|
-
const sdk = this.options.loadSdk ? await this.options.loadSdk() : await import("@anthropic-ai/claude-agent-sdk");
|
|
1677
|
-
return sdk.query;
|
|
1678
|
-
}
|
|
1679
|
-
async handleCanUseTool(toolName, input, context, sequencer) {
|
|
1680
|
-
const requestId = context.toolUseID || `${toolName}-${this.pendingPermissions.size + 1}`;
|
|
1681
|
-
const policyDecision = await this.evaluatePolicy(toolName, input, requestId);
|
|
1682
|
-
if (policyDecision.decision === "allow") {
|
|
1683
|
-
return { behavior: "allow", toolUseID: requestId };
|
|
1684
|
-
}
|
|
1685
|
-
if (policyDecision.decision === "deny") {
|
|
1686
|
-
sequencer.append({
|
|
1687
|
-
type: "permission_resolved",
|
|
1688
|
-
requestId,
|
|
1689
|
-
resolution: { allowed: false, reason: policyDecision.reason }
|
|
1690
|
-
}, { providerEventType: "claude-sdk:permission_denied" });
|
|
1691
|
-
return { behavior: "deny", message: policyDecision.reason, toolUseID: requestId };
|
|
1692
|
-
}
|
|
1693
|
-
sequencer.append({
|
|
1694
|
-
type: "permission_requested",
|
|
1695
|
-
request: {
|
|
1696
|
-
requestId,
|
|
1697
|
-
toolName,
|
|
1698
|
-
input,
|
|
1699
|
-
reason: policyDecision.reason
|
|
1700
|
-
}
|
|
1701
|
-
}, { providerEventType: "claude-sdk:can_use_tool" });
|
|
1702
|
-
return new Promise((resolve) => {
|
|
1703
|
-
this.pendingPermissions.set(requestId, { resolve });
|
|
1704
|
-
context.signal.addEventListener("abort", () => {
|
|
1705
|
-
if (!this.pendingPermissions.delete(requestId)) return;
|
|
1706
|
-
resolve({ behavior: "deny", message: "Permission request aborted", toolUseID: requestId });
|
|
1707
|
-
}, { once: true });
|
|
1708
|
-
});
|
|
1709
|
-
}
|
|
1710
|
-
async evaluatePolicy(toolName, input, requestId) {
|
|
1711
|
-
if (!this.options.policy) return { decision: "allow" };
|
|
1712
|
-
return this.options.policy({
|
|
1713
|
-
toolName,
|
|
1714
|
-
input,
|
|
1715
|
-
toolIntent: normalizeRuntimeToolIntent(toolName, input),
|
|
1716
|
-
scope: { type: "tool-call", callId: requestId }
|
|
1717
|
-
});
|
|
1718
|
-
}
|
|
1719
|
-
projectSdkMessage(message, turnId, sequencer) {
|
|
1720
|
-
const record = asRecord2(message);
|
|
1721
|
-
const type = stringValue(record.type);
|
|
1722
|
-
if (!type) return;
|
|
1723
|
-
if (type === "stream_event") {
|
|
1724
|
-
for (const item of projectStreamEvent(record, turnId)) {
|
|
1725
|
-
sequencer.append(item, { providerEventType: "claude-sdk:stream_event" });
|
|
1726
|
-
}
|
|
1727
|
-
return;
|
|
1728
|
-
}
|
|
1729
|
-
if (type === "assistant") {
|
|
1730
|
-
for (const item of projectAssistantMessage(record, turnId)) {
|
|
1731
|
-
sequencer.append(item, { providerEventType: "claude-sdk:assistant" });
|
|
1732
|
-
}
|
|
1733
|
-
return;
|
|
1734
|
-
}
|
|
1735
|
-
if (type === "user") {
|
|
1736
|
-
for (const item of projectUserMessage(record, turnId)) {
|
|
1737
|
-
sequencer.append(item, { providerEventType: "claude-sdk:user" });
|
|
1738
|
-
}
|
|
1739
|
-
return;
|
|
1740
|
-
}
|
|
1741
|
-
if (type === "result") {
|
|
1742
|
-
sequencer.append(projectResultMessage(record, turnId), { providerEventType: "claude-sdk:result" });
|
|
1743
|
-
return;
|
|
1744
|
-
}
|
|
1745
|
-
if (type === "system") {
|
|
1746
|
-
const status = projectSystemStatus(record);
|
|
1747
|
-
if (status) sequencer.append(status, { providerEventType: `claude-sdk:system:${stringValue(record.subtype) ?? "unknown"}` });
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1750
|
-
};
|
|
1751
|
-
function mapPermissionMode(mode) {
|
|
1752
|
-
if (mode === "safe") return { permissionMode: "plan" };
|
|
1753
|
-
if (mode === "allow-all") return { permissionMode: "bypassPermissions", allowDangerouslySkipPermissions: true };
|
|
1754
|
-
return { permissionMode: "default" };
|
|
1755
|
-
}
|
|
1756
|
-
function sourceToolOptions(sourceTools) {
|
|
1757
|
-
const mcpServers = mapClaudeMcpServers(sourceTools);
|
|
1758
|
-
return mcpServers ? { mcpServers } : {};
|
|
1759
|
-
}
|
|
1760
|
-
function mapClaudeMcpServers(sourceTools) {
|
|
1761
|
-
const servers = {};
|
|
1762
|
-
for (const sourceTool of sanitizeProviderSourceTools(sourceTools)) {
|
|
1763
|
-
if (sourceTool.kind !== "mcp-server") continue;
|
|
1764
|
-
if (sourceTool.transport === "stdio" && sourceTool.command) {
|
|
1765
|
-
servers[sourceTool.sourceSlug] = {
|
|
1766
|
-
type: "stdio",
|
|
1767
|
-
command: sourceTool.command,
|
|
1768
|
-
...sourceTool.args ? { args: sourceTool.args } : {},
|
|
1769
|
-
...sourceTool.env ? { env: sourceTool.env } : {}
|
|
1770
|
-
};
|
|
1771
|
-
continue;
|
|
1772
|
-
}
|
|
1773
|
-
if ((sourceTool.transport === "http" || sourceTool.transport === "sse") && sourceTool.url) {
|
|
1774
|
-
servers[sourceTool.sourceSlug] = {
|
|
1775
|
-
type: sourceTool.transport,
|
|
1776
|
-
url: sourceTool.url,
|
|
1777
|
-
...sourceTool.headers ? { headers: sourceTool.headers } : {}
|
|
1778
|
-
};
|
|
1779
|
-
}
|
|
1780
|
-
}
|
|
1781
|
-
return Object.keys(servers).length > 0 ? servers : void 0;
|
|
1782
|
-
}
|
|
1783
|
-
function normalizeRuntimeToolIntent(toolName, input) {
|
|
1784
|
-
if (toolName === "Bash") {
|
|
1785
|
-
const command = String(input.command ?? "");
|
|
1786
|
-
return {
|
|
1787
|
-
kind: "bash",
|
|
1788
|
-
command,
|
|
1789
|
-
baseCommand: baseCommand(command)
|
|
1790
|
-
};
|
|
1791
|
-
}
|
|
1792
|
-
if (["Write", "Edit", "MultiEdit", "NotebookEdit"].includes(toolName)) {
|
|
1793
|
-
return {
|
|
1794
|
-
kind: "file_write",
|
|
1795
|
-
toolName,
|
|
1796
|
-
path: String(input.file_path ?? input.path ?? "")
|
|
1797
|
-
};
|
|
1798
|
-
}
|
|
1799
|
-
if (toolName === "MCP" || toolName.startsWith("mcp__")) {
|
|
1800
|
-
return {
|
|
1801
|
-
kind: "mcp",
|
|
1802
|
-
name: String(input.name ?? input.toolName ?? input.tool ?? toolName)
|
|
1803
|
-
};
|
|
1804
|
-
}
|
|
1805
|
-
if (toolName === "API") {
|
|
1806
|
-
const url = input.url ? String(input.url) : void 0;
|
|
1807
|
-
return {
|
|
1808
|
-
kind: "api",
|
|
1809
|
-
method: String(input.method ?? "GET").toUpperCase(),
|
|
1810
|
-
path: pathFromUrl(url ?? String(input.path ?? "/")),
|
|
1811
|
-
...url ? { url } : {}
|
|
1812
|
-
};
|
|
1813
|
-
}
|
|
1814
|
-
return { kind: "unknown", toolName };
|
|
1815
|
-
}
|
|
1816
|
-
function baseCommand(command) {
|
|
1817
|
-
return command.trim().split(/\s+/)[0] ?? "";
|
|
1818
|
-
}
|
|
1819
|
-
function pathFromUrl(value) {
|
|
1820
|
-
try {
|
|
1821
|
-
return new URL(value).pathname;
|
|
1822
|
-
} catch {
|
|
1823
|
-
return value.startsWith("/") ? value : `/${value}`;
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
function projectStreamEvent(record, turnId) {
|
|
1827
|
-
const event = asRecord2(record.event);
|
|
1828
|
-
if (event.type !== "content_block_delta") return [];
|
|
1829
|
-
const delta = asRecord2(event.delta);
|
|
1830
|
-
const messageId = stringValue(record.uuid) ?? `${turnId}-partial`;
|
|
1831
|
-
if (delta.type === "text_delta") {
|
|
1832
|
-
return [{
|
|
1833
|
-
type: "assistant_message_delta",
|
|
1834
|
-
text: stringValue(delta.text) ?? "",
|
|
1835
|
-
messageId,
|
|
1836
|
-
turnId
|
|
1837
|
-
}];
|
|
1838
|
-
}
|
|
1839
|
-
if (delta.type === "thinking_delta") {
|
|
1840
|
-
return [{
|
|
1841
|
-
type: "reasoning_delta",
|
|
1842
|
-
text: stringValue(delta.thinking) ?? stringValue(delta.text) ?? "",
|
|
1843
|
-
messageId,
|
|
1844
|
-
turnId
|
|
1845
|
-
}];
|
|
1846
|
-
}
|
|
1847
|
-
return [];
|
|
1848
|
-
}
|
|
1849
|
-
function projectAssistantMessage(record, turnId) {
|
|
1850
|
-
const message = asRecord2(record.message);
|
|
1851
|
-
const messageId = stringValue(record.uuid) ?? stringValue(message.id) ?? `${turnId}-assistant`;
|
|
1852
|
-
const content = Array.isArray(message.content) ? message.content : [];
|
|
1853
|
-
const items = [];
|
|
1854
|
-
for (const block of content) {
|
|
1855
|
-
const contentBlock = asRecord2(block);
|
|
1856
|
-
if (contentBlock.type === "text") {
|
|
1857
|
-
items.push({
|
|
1858
|
-
type: "assistant_message",
|
|
1859
|
-
text: stringValue(contentBlock.text) ?? "",
|
|
1860
|
-
messageId,
|
|
1861
|
-
turnId
|
|
1862
|
-
});
|
|
1863
|
-
} else if (contentBlock.type === "thinking") {
|
|
1864
|
-
items.push({
|
|
1865
|
-
type: "reasoning",
|
|
1866
|
-
text: stringValue(contentBlock.thinking) ?? stringValue(contentBlock.text) ?? "",
|
|
1867
|
-
messageId,
|
|
1868
|
-
turnId
|
|
1869
|
-
});
|
|
1870
|
-
} else if (contentBlock.type === "tool_use") {
|
|
1871
|
-
items.push({
|
|
1872
|
-
type: "tool_call",
|
|
1873
|
-
callId: stringValue(contentBlock.id) ?? `${messageId}-tool-${items.length}`,
|
|
1874
|
-
name: stringValue(contentBlock.name) ?? "unknown",
|
|
1875
|
-
status: "running",
|
|
1876
|
-
detail: contentBlock.input,
|
|
1877
|
-
turnId
|
|
1878
|
-
});
|
|
1879
|
-
}
|
|
1880
|
-
}
|
|
1881
|
-
return items;
|
|
1882
|
-
}
|
|
1883
|
-
function projectUserMessage(record, turnId) {
|
|
1884
|
-
const message = asRecord2(record.message);
|
|
1885
|
-
const content = Array.isArray(message.content) ? message.content : [];
|
|
1886
|
-
const items = [];
|
|
1887
|
-
for (const block of content) {
|
|
1888
|
-
const contentBlock = asRecord2(block);
|
|
1889
|
-
if (contentBlock.type !== "tool_result") continue;
|
|
1890
|
-
items.push({
|
|
1891
|
-
type: "tool_result",
|
|
1892
|
-
callId: stringValue(contentBlock.tool_use_id) ?? "unknown",
|
|
1893
|
-
result: normalizeToolResult(contentBlock.content),
|
|
1894
|
-
isError: booleanValue(contentBlock.is_error),
|
|
1895
|
-
turnId
|
|
1896
|
-
});
|
|
1897
|
-
}
|
|
1898
|
-
return items;
|
|
1899
|
-
}
|
|
1900
|
-
function projectResultMessage(record, turnId) {
|
|
1901
|
-
const subtype = stringValue(record.subtype);
|
|
1902
|
-
if (subtype === "success") {
|
|
1903
|
-
return {
|
|
1904
|
-
type: "turn_completed",
|
|
1905
|
-
turnId,
|
|
1906
|
-
usage: record.usage
|
|
1907
|
-
};
|
|
1908
|
-
}
|
|
1909
|
-
return {
|
|
1910
|
-
type: "turn_failed",
|
|
1911
|
-
turnId,
|
|
1912
|
-
error: {
|
|
1913
|
-
subtype,
|
|
1914
|
-
errors: record.errors,
|
|
1915
|
-
stopReason: record.stop_reason
|
|
1916
|
-
}
|
|
1917
|
-
};
|
|
1918
|
-
}
|
|
1919
|
-
function projectSystemStatus(record) {
|
|
1920
|
-
const subtype = stringValue(record.subtype);
|
|
1921
|
-
if (subtype === "status") {
|
|
1922
|
-
return { type: "session_status", status: stringValue(record.status) ?? "unknown" };
|
|
1923
|
-
}
|
|
1924
|
-
if (subtype === "session_state_changed") {
|
|
1925
|
-
return { type: "session_status", status: stringValue(record.state) ?? "unknown" };
|
|
1926
|
-
}
|
|
1927
|
-
if (subtype === "permission_denied") {
|
|
1928
|
-
return {
|
|
1929
|
-
type: "permission_resolved",
|
|
1930
|
-
requestId: stringValue(record.tool_use_id) ?? "unknown",
|
|
1931
|
-
resolution: {
|
|
1932
|
-
allowed: false,
|
|
1933
|
-
reason: stringValue(record.decision_reason) ?? stringValue(record.message)
|
|
1934
|
-
}
|
|
1935
|
-
};
|
|
1936
|
-
}
|
|
1937
|
-
return void 0;
|
|
1938
|
-
}
|
|
1939
|
-
function normalizeToolResult(content) {
|
|
1940
|
-
if (!Array.isArray(content)) return content;
|
|
1941
|
-
return content.map((item) => {
|
|
1942
|
-
const record = asRecord2(item);
|
|
1943
|
-
return record.type === "text" ? stringValue(record.text) ?? "" : record;
|
|
1944
|
-
}).join("\n");
|
|
1945
|
-
}
|
|
1946
|
-
function asRecord2(value) {
|
|
1947
|
-
return value && typeof value === "object" ? value : {};
|
|
1948
|
-
}
|
|
1949
|
-
function stringValue(value) {
|
|
1950
|
-
return typeof value === "string" ? value : void 0;
|
|
1951
|
-
}
|
|
1952
|
-
function booleanValue(value) {
|
|
1953
|
-
return typeof value === "boolean" ? value : void 0;
|
|
1954
|
-
}
|
|
1955
|
-
function createClaudeRuntimeCapabilityReport(options) {
|
|
1956
|
-
const nativeAvailable = options.candidates.some((candidate) => candidate.kind === "native-sdk" && candidate.available);
|
|
1957
|
-
return createRuntimeCapabilityReport({
|
|
1958
|
-
provider: "claude",
|
|
1959
|
-
candidates: options.candidates,
|
|
1960
|
-
preferredRuntime: "native-sdk",
|
|
1961
|
-
allowFallback: options.allowFallback,
|
|
1962
|
-
auth: options.auth,
|
|
1963
|
-
extensionCapabilities: {
|
|
1964
|
-
policy: {
|
|
1965
|
-
supported: true,
|
|
1966
|
-
degraded: !nativeAvailable,
|
|
1967
|
-
modes: ["safe", "ask", "allow-all"],
|
|
1968
|
-
approvals: nativeAvailable,
|
|
1969
|
-
toolPolicy: true,
|
|
1970
|
-
reason: nativeAvailable ? void 0 : "CLI fallback cannot complete provider-native permission callbacks"
|
|
1971
|
-
},
|
|
1972
|
-
sources: {
|
|
1973
|
-
supported: nativeAvailable,
|
|
1974
|
-
registry: nativeAvailable,
|
|
1975
|
-
mcpTools: nativeAvailable,
|
|
1976
|
-
reason: nativeAvailable ? void 0 : "Source tools require native SDK or host MCP wiring"
|
|
1977
|
-
},
|
|
1978
|
-
skills: {
|
|
1979
|
-
supported: nativeAvailable,
|
|
1980
|
-
registry: nativeAvailable,
|
|
1981
|
-
activationPlan: nativeAvailable,
|
|
1982
|
-
scopedPolicy: nativeAvailable,
|
|
1983
|
-
reason: nativeAvailable ? void 0 : "Skill activation is degraded in CLI fallback"
|
|
1984
|
-
},
|
|
1985
|
-
automations: {
|
|
1986
|
-
supported: true,
|
|
1987
|
-
degraded: !nativeAvailable,
|
|
1988
|
-
eventBus: true,
|
|
1989
|
-
schedulerHost: false,
|
|
1990
|
-
promptAction: true,
|
|
1991
|
-
webhookAction: false,
|
|
1992
|
-
reason: nativeAvailable ? void 0 : "Automation hooks are degraded in CLI fallback"
|
|
1993
|
-
},
|
|
1994
|
-
hostTools: hostToolCapabilitiesFromExtensions(options.extensions)
|
|
1995
|
-
}
|
|
1996
|
-
});
|
|
1997
|
-
}
|
|
1998
|
-
function hostToolCapabilitiesFromExtensions(extensions) {
|
|
1999
|
-
const sessionTools = extensions?.hostServices?.sessionTools;
|
|
2000
|
-
if (!sessionTools) {
|
|
2001
|
-
return {
|
|
2002
|
-
supported: false,
|
|
2003
|
-
sessionTools: false,
|
|
2004
|
-
workflowTransitions: false,
|
|
2005
|
-
browserActions: false,
|
|
2006
|
-
metadataWrites: false
|
|
2007
|
-
};
|
|
2008
|
-
}
|
|
2009
|
-
const workflowTransitions = Boolean(sessionTools.submitPlan);
|
|
2010
|
-
const browserActions = Boolean(sessionTools.runBrowserAction);
|
|
2011
|
-
const metadataWrites = Boolean(sessionTools.updateSessionMetadata);
|
|
2012
|
-
const degraded = !workflowTransitions || !browserActions || !metadataWrites;
|
|
2013
|
-
return {
|
|
2014
|
-
supported: true,
|
|
2015
|
-
degraded: degraded || void 0,
|
|
2016
|
-
sessionTools: true,
|
|
2017
|
-
workflowTransitions,
|
|
2018
|
-
browserActions,
|
|
2019
|
-
metadataWrites,
|
|
2020
|
-
reason: degraded ? "Host session tool bridge is only partially attached" : void 0
|
|
2021
|
-
};
|
|
2022
|
-
}
|
|
2023
|
-
function createClaudeProviderRuntime(options) {
|
|
2024
|
-
const report = createClaudeRuntimeCapabilityReport(options);
|
|
2025
|
-
const extensions = createRuntimeExtensionContext(options.extensions);
|
|
2026
|
-
let runtimeDriver = options.driver;
|
|
2027
|
-
const timeline = [];
|
|
2028
|
-
const stream = new PushTimelineStream();
|
|
2029
|
-
const baseSequencer = createTimelineSequencer({
|
|
2030
|
-
sessionId: options.sessionId ?? "claude-session",
|
|
2031
|
-
provider: "claude",
|
|
2032
|
-
epoch: options.epoch ?? "default",
|
|
2033
|
-
now: options.now
|
|
2034
|
-
});
|
|
2035
|
-
const sequencer = {
|
|
2036
|
-
append(item, rawRef2) {
|
|
2037
|
-
syncStateFromTimelineItem(item);
|
|
2038
|
-
const envelope = baseSequencer.append(item, rawRef2);
|
|
2039
|
-
timeline.push(envelope);
|
|
2040
|
-
stream.emit(envelope);
|
|
2041
|
-
return envelope;
|
|
2042
|
-
}
|
|
2043
|
-
};
|
|
2044
|
-
let state = initialRuntimeState;
|
|
2045
|
-
function dispatch(action) {
|
|
2046
|
-
state = reduceRuntimeState(state, action);
|
|
2047
|
-
}
|
|
2048
|
-
function syncStateFromTimelineItem(item) {
|
|
2049
|
-
if (item.type === "permission_requested") {
|
|
2050
|
-
dispatch({ type: "permission_request", requestId: item.request.requestId });
|
|
2051
|
-
}
|
|
2052
|
-
if (item.type === "permission_resolved") {
|
|
2053
|
-
dispatch({ type: "permission_response" });
|
|
2054
|
-
}
|
|
2055
|
-
if (item.type === "turn_completed") {
|
|
2056
|
-
dispatch({ type: "turn_completed" });
|
|
2057
|
-
}
|
|
2058
|
-
}
|
|
2059
|
-
function appendFailure(message) {
|
|
2060
|
-
sequencer.append({
|
|
2061
|
-
type: "turn_failed",
|
|
2062
|
-
turnId: "provider-runtime",
|
|
2063
|
-
error: { message }
|
|
2064
|
-
});
|
|
2065
|
-
}
|
|
2066
|
-
function getDriver() {
|
|
2067
|
-
if (runtimeDriver) return runtimeDriver;
|
|
2068
|
-
if (report.selected === "cli-fallback") {
|
|
2069
|
-
runtimeDriver = createClaudeCliFallbackDriver({
|
|
2070
|
-
session: options.createCliFallbackSession?.() ?? createLocalAgentSession({
|
|
2071
|
-
provider: "claude",
|
|
2072
|
-
cwd: options.cwd,
|
|
2073
|
-
sessionId: options.sessionId,
|
|
2074
|
-
model: options.model,
|
|
2075
|
-
thinkingLevel: options.reasoningEffort,
|
|
2076
|
-
permissionMode: options.permissionMode,
|
|
2077
|
-
executable: options.executable,
|
|
2078
|
-
env: cleanEnv2(options.env)
|
|
2079
|
-
}),
|
|
2080
|
-
sessionId: options.sessionId ?? "claude-session",
|
|
2081
|
-
epoch: options.epoch ?? "default",
|
|
2082
|
-
now: options.now
|
|
2083
|
-
});
|
|
2084
|
-
return runtimeDriver;
|
|
2085
|
-
}
|
|
2086
|
-
if (report.selected !== "native-sdk") return void 0;
|
|
2087
|
-
runtimeDriver = createClaudeNativeSdkDriver({
|
|
2088
|
-
cwd: options.cwd,
|
|
2089
|
-
model: options.model,
|
|
2090
|
-
reasoningEffort: options.reasoningEffort,
|
|
2091
|
-
env: options.env,
|
|
2092
|
-
permissionMode: options.permissionMode,
|
|
2093
|
-
policy: options.policy ?? extensions.policy?.hook,
|
|
2094
|
-
sourceTools: options.sourceTools,
|
|
2095
|
-
query: options.query,
|
|
2096
|
-
loadSdk: options.loadSdk
|
|
2097
|
-
});
|
|
2098
|
-
return runtimeDriver;
|
|
2099
|
-
}
|
|
2100
|
-
return {
|
|
2101
|
-
sessionId: options.sessionId ?? "claude-session",
|
|
2102
|
-
provider: "claude",
|
|
2103
|
-
runtimeKind: report.selected ?? "native-sdk",
|
|
2104
|
-
events: stream,
|
|
2105
|
-
commands: {
|
|
2106
|
-
async sendMessage(message, sendOptions) {
|
|
2107
|
-
const driver = getDriver();
|
|
2108
|
-
if (!driver) {
|
|
2109
|
-
const error = "Claude provider driver is not attached";
|
|
2110
|
-
dispatch({ type: "error", error });
|
|
2111
|
-
appendFailure(error);
|
|
2112
|
-
throw new Error(error);
|
|
2113
|
-
}
|
|
2114
|
-
dispatch({ type: "send_message", message });
|
|
2115
|
-
try {
|
|
2116
|
-
await driver.sendMessage({
|
|
2117
|
-
message,
|
|
2118
|
-
options: sendOptions,
|
|
2119
|
-
origin: sendOptions?.commandOrigin ?? extensions.commandOrigin
|
|
2120
|
-
}, sequencer);
|
|
2121
|
-
dispatch({ type: "complete" });
|
|
2122
|
-
} catch (err) {
|
|
2123
|
-
const error = err instanceof Error ? err.message : String(err);
|
|
2124
|
-
dispatch({ type: "error", error });
|
|
2125
|
-
appendFailure(error);
|
|
2126
|
-
throw err;
|
|
2127
|
-
}
|
|
2128
|
-
},
|
|
2129
|
-
async abort(reason) {
|
|
2130
|
-
dispatch({ type: "abort", reason });
|
|
2131
|
-
await runtimeDriver?.abort?.(reason);
|
|
2132
|
-
},
|
|
2133
|
-
async respondToPermission(requestId, allowed, remember) {
|
|
2134
|
-
dispatch({ type: "permission_response" });
|
|
2135
|
-
await runtimeDriver?.respondToPermission?.(requestId, allowed, remember);
|
|
2136
|
-
},
|
|
2137
|
-
async dispose() {
|
|
2138
|
-
dispatch({ type: "dispose" });
|
|
2139
|
-
stream.disconnect();
|
|
2140
|
-
await runtimeDriver?.dispose?.();
|
|
2141
|
-
}
|
|
2142
|
-
},
|
|
2143
|
-
async preflight() {
|
|
2144
|
-
dispatch({ type: "preflight_ok" });
|
|
2145
|
-
if (!report.selected) {
|
|
2146
|
-
dispatch({ type: "preflight_error", error: report.error ?? "No runtime selected" });
|
|
2147
|
-
}
|
|
2148
|
-
sequencer.append({ type: "runtime_capability_report", report });
|
|
2149
|
-
return report;
|
|
2150
|
-
},
|
|
2151
|
-
async fetchTimeline(request) {
|
|
2152
|
-
if (!request.cursor && timeline.length === 0) {
|
|
2153
|
-
return {
|
|
2154
|
-
items: [],
|
|
2155
|
-
nextCursor: createTimelineCursor({ epoch: options.epoch ?? "default", afterSeq: 0 }),
|
|
2156
|
-
hasGap: false
|
|
2157
|
-
};
|
|
2158
|
-
}
|
|
2159
|
-
return fetchTimeline(timeline, request);
|
|
2160
|
-
},
|
|
2161
|
-
getState() {
|
|
2162
|
-
return state;
|
|
2163
|
-
}
|
|
2164
|
-
};
|
|
2165
|
-
}
|
|
2166
|
-
var PushTimelineStream = class {
|
|
2167
|
-
listeners = /* @__PURE__ */ new Set();
|
|
2168
|
-
connect(onEvent, onError, onClose) {
|
|
2169
|
-
this.listeners.add({ onEvent, onError, onClose });
|
|
2170
|
-
}
|
|
2171
|
-
disconnect() {
|
|
2172
|
-
this.listeners.clear();
|
|
2173
|
-
}
|
|
2174
|
-
isConnected() {
|
|
2175
|
-
return this.listeners.size > 0;
|
|
2176
|
-
}
|
|
2177
|
-
emit(event) {
|
|
2178
|
-
for (const listener of this.listeners) listener.onEvent(event);
|
|
2179
|
-
}
|
|
2180
|
-
};
|
|
2181
|
-
function createClaudeCliFallbackDriver(options) {
|
|
2182
|
-
const projector = createLocalTimelineProjector({
|
|
2183
|
-
sessionId: options.sessionId,
|
|
2184
|
-
provider: "claude",
|
|
2185
|
-
epoch: options.epoch,
|
|
2186
|
-
now: options.now
|
|
2187
|
-
});
|
|
2188
|
-
let connected = false;
|
|
2189
|
-
let activeSequencer;
|
|
2190
|
-
function connect(sequencer) {
|
|
2191
|
-
activeSequencer = sequencer;
|
|
2192
|
-
if (connected) return;
|
|
2193
|
-
connected = true;
|
|
2194
|
-
options.session.events.connect((event) => {
|
|
2195
|
-
if (!activeSequencer) return;
|
|
2196
|
-
for (const envelope of projector.project(event)) {
|
|
2197
|
-
activeSequencer.append(envelope.item, envelope.rawRef);
|
|
2198
|
-
}
|
|
2199
|
-
});
|
|
2200
|
-
}
|
|
2201
|
-
return {
|
|
2202
|
-
async sendMessage(input, sequencer) {
|
|
2203
|
-
connect(sequencer);
|
|
2204
|
-
await options.session.commands.sendMessage(input.message, input.options);
|
|
2205
|
-
},
|
|
2206
|
-
abort(reason) {
|
|
2207
|
-
return options.session.commands.abort(reason);
|
|
2208
|
-
},
|
|
2209
|
-
respondToPermission(requestId, allowed, remember) {
|
|
2210
|
-
return options.session.commands.respondToPermission(requestId, allowed, remember);
|
|
2211
|
-
},
|
|
2212
|
-
dispose() {
|
|
2213
|
-
return options.session.commands.dispose();
|
|
2214
|
-
}
|
|
2215
|
-
};
|
|
2216
|
-
}
|
|
2217
|
-
function cleanEnv2(env) {
|
|
2218
|
-
if (!env) return void 0;
|
|
2219
|
-
return Object.fromEntries(
|
|
2220
|
-
Object.entries(env).filter((entry) => entry[1] !== void 0)
|
|
2221
|
-
);
|
|
2222
|
-
}
|
|
2223
|
-
|
|
2224
|
-
// ../packages/provider-codex/dist/index.js
|
|
2225
|
-
import { Buffer as Buffer2 } from "buffer";
|
|
2226
|
-
import { spawn as spawn3 } from "child_process";
|
|
2227
|
-
import { createInterface as createInterface3 } from "readline";
|
|
2228
|
-
function createCodexAppServerDriver(options) {
|
|
2229
|
-
let threadId = options.threadId;
|
|
2230
|
-
let activeSequencer;
|
|
2231
|
-
const pendingTurns = /* @__PURE__ */ new Map();
|
|
2232
|
-
const pendingPermissions = /* @__PURE__ */ new Map();
|
|
2233
|
-
const turnAliases = /* @__PURE__ */ new Map();
|
|
2234
|
-
const commandTurnIds = /* @__PURE__ */ new Map();
|
|
2235
|
-
const unsubscribeNotification = options.client.onNotification((notification) => {
|
|
2236
|
-
if (!activeSequencer) return;
|
|
2237
|
-
handleNotification(notification, activeSequencer, pendingTurns, turnAliases, commandTurnIds);
|
|
2238
|
-
});
|
|
2239
|
-
const unsubscribeRequest = options.client.onRequest(async (request) => {
|
|
2240
|
-
if (!activeSequencer) return defaultRequestResponse(request);
|
|
2241
|
-
return handleServerRequest(request, activeSequencer);
|
|
2242
|
-
});
|
|
2243
|
-
async function handleServerRequest(request, sequencer) {
|
|
2244
|
-
if (request.method === "item/commandExecution/requestApproval") {
|
|
2245
|
-
const params = asRecord3(request.params);
|
|
2246
|
-
const requestId = stringValue2(params.approvalId) ?? stringValue2(params.itemId) ?? String(request.id);
|
|
2247
|
-
const input = {
|
|
2248
|
-
command: stringValue2(params.command),
|
|
2249
|
-
cwd: stringValue2(params.cwd)
|
|
2250
|
-
};
|
|
2251
|
-
const decision = await evaluatePolicy(options.policy, {
|
|
2252
|
-
toolName: "Bash",
|
|
2253
|
-
input,
|
|
2254
|
-
toolIntent: normalizeRuntimeToolIntent2("Bash", input),
|
|
2255
|
-
scope: params.itemId ? { type: "tool-call", callId: String(params.itemId) } : void 0
|
|
2256
|
-
});
|
|
2257
|
-
return resolveApprovalDecision({
|
|
2258
|
-
requestId,
|
|
2259
|
-
toolName: "Bash",
|
|
2260
|
-
input,
|
|
2261
|
-
reason: stringValue2(params.reason) ?? decisionReason(decision),
|
|
2262
|
-
decision,
|
|
2263
|
-
sequencer
|
|
2264
|
-
});
|
|
2265
|
-
}
|
|
2266
|
-
if (request.method === "item/fileChange/requestApproval") {
|
|
2267
|
-
const params = asRecord3(request.params);
|
|
2268
|
-
const requestId = stringValue2(params.approvalId) ?? stringValue2(params.itemId) ?? String(request.id);
|
|
2269
|
-
const input = { changes: params.changes };
|
|
2270
|
-
const decision = await evaluatePolicy(options.policy, {
|
|
2271
|
-
toolName: "fileChange",
|
|
2272
|
-
input,
|
|
2273
|
-
toolIntent: normalizeRuntimeToolIntent2("fileChange", input),
|
|
2274
|
-
scope: params.itemId ? { type: "tool-call", callId: String(params.itemId) } : void 0
|
|
2275
|
-
});
|
|
2276
|
-
return resolveApprovalDecision({
|
|
2277
|
-
requestId,
|
|
2278
|
-
toolName: "fileChange",
|
|
2279
|
-
input,
|
|
2280
|
-
reason: stringValue2(params.reason) ?? decisionReason(decision),
|
|
2281
|
-
decision,
|
|
2282
|
-
sequencer
|
|
2283
|
-
});
|
|
2284
|
-
}
|
|
2285
|
-
return defaultRequestResponse(request);
|
|
2286
|
-
}
|
|
2287
|
-
function resolveApprovalDecision(args) {
|
|
2288
|
-
if (args.decision.decision === "allow") {
|
|
2289
|
-
return { decision: "accept" };
|
|
2290
|
-
}
|
|
2291
|
-
if (args.decision.decision === "deny") {
|
|
2292
|
-
args.sequencer.append({
|
|
2293
|
-
type: "permission_resolved",
|
|
2294
|
-
requestId: args.requestId,
|
|
2295
|
-
resolution: { allowed: false, reason: args.decision.reason }
|
|
2296
|
-
});
|
|
2297
|
-
return { decision: "decline" };
|
|
2298
|
-
}
|
|
2299
|
-
args.sequencer.append({
|
|
2300
|
-
type: "permission_requested",
|
|
2301
|
-
request: {
|
|
2302
|
-
requestId: args.requestId,
|
|
2303
|
-
toolName: args.toolName,
|
|
2304
|
-
input: args.input,
|
|
2305
|
-
reason: args.reason
|
|
2306
|
-
}
|
|
2307
|
-
}, rawRef("server_request"));
|
|
2308
|
-
return new Promise((resolve) => {
|
|
2309
|
-
pendingPermissions.set(args.requestId, { resolve });
|
|
2310
|
-
});
|
|
2311
|
-
}
|
|
2312
|
-
return {
|
|
2313
|
-
async sendMessage(input, sequencer) {
|
|
2314
|
-
activeSequencer = sequencer;
|
|
2315
|
-
if (!threadId) {
|
|
2316
|
-
const response2 = await options.client.request("thread/start", {
|
|
2317
|
-
cwd: options.cwd,
|
|
2318
|
-
experimentalRawEvents: false,
|
|
2319
|
-
persistExtendedHistory: false,
|
|
2320
|
-
...threadStartConfigParams({
|
|
2321
|
-
model: input.options?.model ?? options.model,
|
|
2322
|
-
reasoningEffort: input.options?.reasoningEffort ?? options.reasoningEffort,
|
|
2323
|
-
sourceTools: options.sourceTools
|
|
2324
|
-
}),
|
|
2325
|
-
...threadStartPermissionParams(options)
|
|
2326
|
-
});
|
|
2327
|
-
threadId = readThreadId(response2);
|
|
2328
|
-
}
|
|
2329
|
-
const response = await options.client.request("turn/start", {
|
|
2330
|
-
threadId,
|
|
2331
|
-
input: [{ type: "text", text: input.message, text_elements: [] }],
|
|
2332
|
-
cwd: options.cwd,
|
|
2333
|
-
...turnStartPermissionParams(input.options?.permissionMode)
|
|
2334
|
-
});
|
|
2335
|
-
const providerTurnId = readTurnId(response);
|
|
2336
|
-
if (!providerTurnId) return;
|
|
2337
|
-
const timelineTurnId2 = input.options?.turnId ?? providerTurnId;
|
|
2338
|
-
turnAliases.set(providerTurnId, timelineTurnId2);
|
|
2339
|
-
await new Promise((resolve) => {
|
|
2340
|
-
pendingTurns.set(providerTurnId, { resolve });
|
|
2341
|
-
});
|
|
2342
|
-
},
|
|
2343
|
-
async respondToPermission(requestId, allowed, remember) {
|
|
2344
|
-
const pending = pendingPermissions.get(requestId);
|
|
2345
|
-
if (!pending) return;
|
|
2346
|
-
pendingPermissions.delete(requestId);
|
|
2347
|
-
activeSequencer?.append({
|
|
2348
|
-
type: "permission_resolved",
|
|
2349
|
-
requestId,
|
|
2350
|
-
resolution: { allowed, remember }
|
|
2351
|
-
});
|
|
2352
|
-
pending.resolve({ decision: allowed ? remember ? "acceptForSession" : "accept" : "decline" });
|
|
2353
|
-
},
|
|
2354
|
-
async abort() {
|
|
2355
|
-
for (const turn of pendingTurns.values()) {
|
|
2356
|
-
turn.resolve();
|
|
2357
|
-
}
|
|
2358
|
-
pendingTurns.clear();
|
|
2359
|
-
},
|
|
2360
|
-
async dispose() {
|
|
2361
|
-
unsubscribeNotification();
|
|
2362
|
-
unsubscribeRequest();
|
|
2363
|
-
pendingPermissions.clear();
|
|
2364
|
-
pendingTurns.clear();
|
|
2365
|
-
}
|
|
2366
|
-
};
|
|
2367
|
-
}
|
|
2368
|
-
function threadStartPermissionParams(options) {
|
|
2369
|
-
return {
|
|
2370
|
-
...options.approvalPolicy ? { approvalPolicy: options.approvalPolicy } : {},
|
|
2371
|
-
...options.approvalsReviewer ? { approvalsReviewer: options.approvalsReviewer } : {},
|
|
2372
|
-
...options.sandbox ? { sandbox: options.sandbox } : {}
|
|
2373
|
-
};
|
|
2374
|
-
}
|
|
2375
|
-
function turnStartPermissionParams(permissionMode) {
|
|
2376
|
-
const params = permissionMode ? mapPermissionModeToCodexParams(permissionMode) : void 0;
|
|
2377
|
-
return params ? {
|
|
2378
|
-
approvalPolicy: params.approvalPolicy,
|
|
2379
|
-
approvalsReviewer: params.approvalsReviewer,
|
|
2380
|
-
sandboxPolicy: mapCodexSandboxModeToSandboxPolicy(params.sandbox)
|
|
2381
|
-
} : {};
|
|
2382
|
-
}
|
|
2383
|
-
function threadStartConfigParams(options) {
|
|
2384
|
-
const config = {};
|
|
2385
|
-
if (options.model) config.model = options.model;
|
|
2386
|
-
if (options.reasoningEffort) config.model_reasoning_effort = options.reasoningEffort;
|
|
2387
|
-
const sanitized = sanitizeProviderSourceTools(options.sourceTools);
|
|
2388
|
-
if (sanitized.length > 0) config.weftSources = sanitized;
|
|
2389
|
-
if (Object.keys(config).length === 0) return {};
|
|
2390
|
-
return {
|
|
2391
|
-
config
|
|
2392
|
-
};
|
|
2393
|
-
}
|
|
2394
|
-
function normalizeRuntimeToolIntent2(toolName, input) {
|
|
2395
|
-
if (toolName === "Bash") {
|
|
2396
|
-
const command = String(input.command ?? "");
|
|
2397
|
-
return {
|
|
2398
|
-
kind: "bash",
|
|
2399
|
-
command,
|
|
2400
|
-
baseCommand: command.trim().split(/\s+/)[0] ?? ""
|
|
2401
|
-
};
|
|
2402
|
-
}
|
|
2403
|
-
if (toolName === "fileChange") {
|
|
2404
|
-
return {
|
|
2405
|
-
kind: "file_write",
|
|
2406
|
-
toolName,
|
|
2407
|
-
path: pathFromFileChanges(input.changes)
|
|
2408
|
-
};
|
|
2409
|
-
}
|
|
2410
|
-
if (toolName === "MCP") {
|
|
2411
|
-
return {
|
|
2412
|
-
kind: "mcp",
|
|
2413
|
-
name: String(input.name ?? input.toolName ?? input.tool ?? "")
|
|
2414
|
-
};
|
|
2415
|
-
}
|
|
2416
|
-
return { kind: "unknown", toolName };
|
|
2417
|
-
}
|
|
2418
|
-
function pathFromFileChanges(changes) {
|
|
2419
|
-
if (!Array.isArray(changes)) return "";
|
|
2420
|
-
const firstChange = asRecord3(changes[0]);
|
|
2421
|
-
return stringValue2(firstChange.path) ?? stringValue2(firstChange.filePath) ?? "";
|
|
2422
|
-
}
|
|
2423
|
-
async function evaluatePolicy(policy, request) {
|
|
2424
|
-
return policy?.(request) ?? { decision: "ask", reason: "Codex app-server requested approval" };
|
|
2425
|
-
}
|
|
2426
|
-
function handleNotification(notification, sequencer, pendingTurns, turnAliases, commandTurnIds) {
|
|
2427
|
-
const params = asRecord3(notification.params);
|
|
2428
|
-
if (notification.method === "turn/started") {
|
|
2429
|
-
const providerTurnId = readTurnId(params);
|
|
2430
|
-
if (providerTurnId) {
|
|
2431
|
-
append2(sequencer, { type: "turn_started", turnId: timelineTurnId(providerTurnId, turnAliases) }, notification.method);
|
|
2432
|
-
}
|
|
2433
|
-
return;
|
|
2434
|
-
}
|
|
2435
|
-
if (notification.method === "item/agentMessage/delta") {
|
|
2436
|
-
const providerTurnId = String(params.turnId ?? "codex-turn");
|
|
2437
|
-
append2(sequencer, {
|
|
2438
|
-
type: "assistant_message_delta",
|
|
2439
|
-
text: String(params.delta ?? ""),
|
|
2440
|
-
messageId: String(params.itemId ?? "codex-message"),
|
|
2441
|
-
turnId: timelineTurnId(providerTurnId, turnAliases)
|
|
2442
|
-
}, notification.method);
|
|
2443
|
-
return;
|
|
2444
|
-
}
|
|
2445
|
-
if (notification.method === "item/started") {
|
|
2446
|
-
const item = asRecord3(params.item);
|
|
2447
|
-
const providerTurnId = String(params.turnId ?? "codex-turn");
|
|
2448
|
-
const turnId = timelineTurnId(providerTurnId, turnAliases);
|
|
2449
|
-
const timelineItem = toolCallFromThreadItem(item, turnId);
|
|
2450
|
-
if (timelineItem?.type === "tool_call") commandTurnIds.set(timelineItem.callId, turnId);
|
|
2451
|
-
if (timelineItem) append2(sequencer, timelineItem, notification.method);
|
|
2452
|
-
return;
|
|
2453
|
-
}
|
|
2454
|
-
if (notification.method === "item/commandExecution/outputDelta") {
|
|
2455
|
-
const callId = stringValue2(params.itemId) ?? stringValue2(params.callId) ?? stringValue2(params.id);
|
|
2456
|
-
if (!callId) return;
|
|
2457
|
-
const providerTurnId = String(params.turnId ?? "codex-turn");
|
|
2458
|
-
const turnId = params.turnId ? timelineTurnId(providerTurnId, turnAliases) : commandTurnIds.get(callId) ?? timelineTurnId(providerTurnId, turnAliases);
|
|
2459
|
-
commandTurnIds.set(callId, turnId);
|
|
2460
|
-
const text = outputDeltaText(params);
|
|
2461
|
-
if (!text) return;
|
|
2462
|
-
append2(sequencer, {
|
|
2463
|
-
type: "tool_output_delta",
|
|
2464
|
-
callId,
|
|
2465
|
-
text,
|
|
2466
|
-
stream: stringValue2(params.stream),
|
|
2467
|
-
turnId
|
|
2468
|
-
}, notification.method);
|
|
2469
|
-
return;
|
|
2470
|
-
}
|
|
2471
|
-
if (notification.method === "item/completed") {
|
|
2472
|
-
const item = asRecord3(params.item);
|
|
2473
|
-
const providerTurnId = String(params.turnId ?? "codex-turn");
|
|
2474
|
-
const completedItems = completedTimelineItemsFromThreadItem(item, timelineTurnId(providerTurnId, turnAliases));
|
|
2475
|
-
for (const item2 of completedItems) {
|
|
2476
|
-
append2(sequencer, item2, notification.method);
|
|
2477
|
-
if (item2.type === "tool_result") commandTurnIds.delete(item2.callId);
|
|
2478
|
-
}
|
|
2479
|
-
return;
|
|
2480
|
-
}
|
|
2481
|
-
if (notification.method === "turn/completed") {
|
|
2482
|
-
const providerTurnId = readTurnId(params);
|
|
2483
|
-
if (!providerTurnId) return;
|
|
2484
|
-
append2(sequencer, { type: "turn_completed", turnId: timelineTurnId(providerTurnId, turnAliases) }, notification.method);
|
|
2485
|
-
pendingTurns.get(providerTurnId)?.resolve();
|
|
2486
|
-
pendingTurns.delete(providerTurnId);
|
|
2487
|
-
turnAliases.delete(providerTurnId);
|
|
2488
|
-
}
|
|
2489
|
-
}
|
|
2490
|
-
function timelineTurnId(providerTurnId, turnAliases) {
|
|
2491
|
-
return turnAliases.get(providerTurnId) ?? providerTurnId;
|
|
2492
|
-
}
|
|
2493
|
-
function toolCallFromThreadItem(item, turnId) {
|
|
2494
|
-
if (item.type === "commandExecution") {
|
|
2495
|
-
return {
|
|
2496
|
-
type: "tool_call",
|
|
2497
|
-
callId: String(item.id ?? "command"),
|
|
2498
|
-
name: "commandExecution",
|
|
2499
|
-
status: "running",
|
|
2500
|
-
detail: {
|
|
2501
|
-
command: item.command,
|
|
2502
|
-
cwd: item.cwd,
|
|
2503
|
-
commandActions: item.commandActions
|
|
2504
|
-
},
|
|
2505
|
-
turnId
|
|
2506
|
-
};
|
|
2507
|
-
}
|
|
2508
|
-
if (item.type === "mcpToolCall") {
|
|
2509
|
-
return {
|
|
2510
|
-
type: "tool_call",
|
|
2511
|
-
callId: String(item.id ?? "mcp"),
|
|
2512
|
-
name: `${String(item.server ?? "mcp")}.${String(item.tool ?? "tool")}`,
|
|
2513
|
-
status: "running",
|
|
2514
|
-
detail: item.arguments,
|
|
2515
|
-
turnId
|
|
2516
|
-
};
|
|
2517
|
-
}
|
|
2518
|
-
if (item.type === "dynamicToolCall") {
|
|
2519
|
-
return {
|
|
2520
|
-
type: "tool_call",
|
|
2521
|
-
callId: String(item.id ?? "dynamic"),
|
|
2522
|
-
name: `${String(item.namespace ?? "dynamic")}.${String(item.tool ?? "tool")}`,
|
|
2523
|
-
status: "running",
|
|
2524
|
-
detail: item.arguments,
|
|
2525
|
-
turnId
|
|
2526
|
-
};
|
|
2527
|
-
}
|
|
2528
|
-
if (item.type === "fileChange") {
|
|
2529
|
-
return {
|
|
2530
|
-
type: "tool_call",
|
|
2531
|
-
callId: String(item.id ?? "file-change"),
|
|
2532
|
-
name: "fileChange",
|
|
2533
|
-
status: "running",
|
|
2534
|
-
detail: item.changes,
|
|
2535
|
-
turnId
|
|
2536
|
-
};
|
|
2537
|
-
}
|
|
2538
|
-
return void 0;
|
|
2539
|
-
}
|
|
2540
|
-
function completedTimelineItemsFromThreadItem(item, turnId) {
|
|
2541
|
-
if (item.type === "agentMessage") {
|
|
2542
|
-
return [{
|
|
2543
|
-
type: "assistant_message",
|
|
2544
|
-
text: String(item.text ?? ""),
|
|
2545
|
-
messageId: String(item.id ?? "codex-message"),
|
|
2546
|
-
turnId
|
|
2547
|
-
}];
|
|
2548
|
-
}
|
|
2549
|
-
if (item.type === "reasoning") {
|
|
2550
|
-
const content = Array.isArray(item.content) ? item.content.join("\n") : "";
|
|
2551
|
-
return [{
|
|
2552
|
-
type: "reasoning",
|
|
2553
|
-
text: content,
|
|
2554
|
-
messageId: String(item.id ?? "codex-reasoning"),
|
|
2555
|
-
turnId
|
|
2556
|
-
}];
|
|
2557
|
-
}
|
|
2558
|
-
if (item.type === "commandExecution") {
|
|
2559
|
-
return [{
|
|
2560
|
-
type: "tool_result",
|
|
2561
|
-
callId: String(item.id ?? "command"),
|
|
2562
|
-
result: item.aggregatedOutput ?? "",
|
|
2563
|
-
isError: item.exitCode !== void 0 && item.exitCode !== null && item.exitCode !== 0,
|
|
2564
|
-
turnId
|
|
2565
|
-
}];
|
|
2566
|
-
}
|
|
2567
|
-
if (item.type === "mcpToolCall") {
|
|
2568
|
-
return [{
|
|
2569
|
-
type: "tool_result",
|
|
2570
|
-
callId: String(item.id ?? "mcp"),
|
|
2571
|
-
result: item.error ?? item.result,
|
|
2572
|
-
isError: Boolean(item.error),
|
|
2573
|
-
turnId
|
|
2574
|
-
}];
|
|
2575
|
-
}
|
|
2576
|
-
if (item.type === "dynamicToolCall") {
|
|
2577
|
-
return [{
|
|
2578
|
-
type: "tool_result",
|
|
2579
|
-
callId: String(item.id ?? "dynamic"),
|
|
2580
|
-
result: item.contentItems,
|
|
2581
|
-
isError: item.success === false,
|
|
2582
|
-
turnId
|
|
2583
|
-
}];
|
|
2584
|
-
}
|
|
2585
|
-
if (item.type === "fileChange") {
|
|
2586
|
-
return [{
|
|
2587
|
-
type: "tool_result",
|
|
2588
|
-
callId: String(item.id ?? "file-change"),
|
|
2589
|
-
result: item.changes,
|
|
2590
|
-
isError: item.status === "failed",
|
|
2591
|
-
turnId
|
|
2592
|
-
}];
|
|
2593
|
-
}
|
|
2594
|
-
return [];
|
|
2595
|
-
}
|
|
2596
|
-
function outputDeltaText(params) {
|
|
2597
|
-
for (const key of ["delta", "text", "output", "chunk"]) {
|
|
2598
|
-
const value = stringValue2(params[key]);
|
|
2599
|
-
if (value) return value;
|
|
2600
|
-
}
|
|
2601
|
-
const deltaBase64 = stringValue2(params.deltaBase64);
|
|
2602
|
-
if (!deltaBase64) return "";
|
|
2603
|
-
return decodeBase64Text(deltaBase64);
|
|
2604
|
-
}
|
|
2605
|
-
function decodeBase64Text(value) {
|
|
2606
|
-
try {
|
|
2607
|
-
return Buffer2.from(value, "base64").toString("utf8");
|
|
2608
|
-
} catch {
|
|
2609
|
-
return "";
|
|
2610
|
-
}
|
|
2611
|
-
}
|
|
2612
|
-
function append2(sequencer, item, providerEventType) {
|
|
2613
|
-
sequencer.append(item, rawRef(providerEventType));
|
|
2614
|
-
}
|
|
2615
|
-
function rawRef(providerEventType) {
|
|
2616
|
-
return { providerEventType };
|
|
2617
|
-
}
|
|
2618
|
-
function decisionReason(decision) {
|
|
2619
|
-
return decision.decision === "ask" || decision.decision === "deny" ? decision.reason : void 0;
|
|
2620
|
-
}
|
|
2621
|
-
function readThreadId(response) {
|
|
2622
|
-
const record = asRecord3(response);
|
|
2623
|
-
const thread = asRecord3(record.thread);
|
|
2624
|
-
const id = stringValue2(thread.id) ?? stringValue2(record.threadId);
|
|
2625
|
-
if (!id) throw new Error("Codex app-server did not return a thread id");
|
|
2626
|
-
return id;
|
|
2627
|
-
}
|
|
2628
|
-
function readTurnId(response) {
|
|
2629
|
-
const record = asRecord3(response);
|
|
2630
|
-
const turn = asRecord3(record.turn);
|
|
2631
|
-
return stringValue2(turn.id) ?? stringValue2(record.turnId);
|
|
2632
|
-
}
|
|
2633
|
-
function defaultRequestResponse(request) {
|
|
2634
|
-
if (request.method === "item/commandExecution/requestApproval") return { decision: "decline" };
|
|
2635
|
-
if (request.method === "item/fileChange/requestApproval") return { decision: "decline" };
|
|
2636
|
-
if (request.method === "item/permissions/requestApproval") return {
|
|
2637
|
-
permissions: {
|
|
2638
|
-
fileSystem: null,
|
|
2639
|
-
network: null
|
|
2640
|
-
},
|
|
2641
|
-
scope: "turn"
|
|
2642
|
-
};
|
|
2643
|
-
return {};
|
|
2644
|
-
}
|
|
2645
|
-
function stringValue2(value) {
|
|
2646
|
-
return typeof value === "string" ? value : void 0;
|
|
2647
|
-
}
|
|
2648
|
-
function asRecord3(value) {
|
|
2649
|
-
return value && typeof value === "object" ? value : {};
|
|
2650
|
-
}
|
|
2651
|
-
var DEFAULT_REQUEST_TIMEOUT_MS2 = 1e4;
|
|
2652
|
-
function createCodexAppServerJsonRpcClient(options) {
|
|
2653
|
-
const timeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS2;
|
|
2654
|
-
const pending = /* @__PURE__ */ new Map();
|
|
2655
|
-
const notificationHandlers = /* @__PURE__ */ new Set();
|
|
2656
|
-
const requestHandlers = /* @__PURE__ */ new Set();
|
|
2657
|
-
let nextId = 1;
|
|
2658
|
-
const unsubscribeTransport = options.transport.onMessage((message) => {
|
|
2659
|
-
void handleMessage(message);
|
|
2660
|
-
});
|
|
2661
|
-
function write(message) {
|
|
2662
|
-
void options.transport.write(JSON.stringify(message));
|
|
2663
|
-
}
|
|
2664
|
-
async function request(method, params) {
|
|
2665
|
-
const id = nextId++;
|
|
2666
|
-
write({
|
|
2667
|
-
jsonrpc: "2.0",
|
|
2668
|
-
id,
|
|
2669
|
-
method,
|
|
2670
|
-
...params === void 0 ? {} : { params }
|
|
2671
|
-
});
|
|
2672
|
-
return new Promise((resolve, reject) => {
|
|
2673
|
-
const timer = setTimeout(() => {
|
|
2674
|
-
pending.delete(id);
|
|
2675
|
-
reject(new Error(`Codex app-server request timed out: ${method}`));
|
|
2676
|
-
}, timeoutMs);
|
|
2677
|
-
pending.set(id, { resolve, reject, timer });
|
|
2678
|
-
});
|
|
2679
|
-
}
|
|
2680
|
-
async function handleMessage(message) {
|
|
2681
|
-
const record = asRecord22(message);
|
|
2682
|
-
if (typeof record.id === "number" && typeof record.method === "string") {
|
|
2683
|
-
await handleServerRequest(record.id, {
|
|
2684
|
-
id: record.id,
|
|
2685
|
-
method: record.method,
|
|
2686
|
-
params: record.params
|
|
2687
|
-
});
|
|
2688
|
-
return;
|
|
2689
|
-
}
|
|
2690
|
-
if (typeof record.id === "number") {
|
|
2691
|
-
handleResponse(record);
|
|
2692
|
-
return;
|
|
2693
|
-
}
|
|
2694
|
-
if (typeof record.method === "string") {
|
|
2695
|
-
const notification = {
|
|
2696
|
-
method: record.method,
|
|
2697
|
-
params: record.params
|
|
2698
|
-
};
|
|
2699
|
-
for (const handler of notificationHandlers) handler(notification);
|
|
2700
|
-
}
|
|
2701
|
-
}
|
|
2702
|
-
function handleResponse(response) {
|
|
2703
|
-
const entry = pending.get(response.id);
|
|
2704
|
-
if (!entry) return;
|
|
2705
|
-
pending.delete(response.id);
|
|
2706
|
-
clearTimeout(entry.timer);
|
|
2707
|
-
if (response.error) {
|
|
2708
|
-
entry.reject(new Error(response.error.message));
|
|
2709
|
-
return;
|
|
2710
|
-
}
|
|
2711
|
-
entry.resolve(response.result);
|
|
2712
|
-
}
|
|
2713
|
-
async function handleServerRequest(id, request2) {
|
|
2714
|
-
const handler = requestHandlers.values().next().value;
|
|
2715
|
-
if (!handler) {
|
|
2716
|
-
write({ jsonrpc: "2.0", id, result: {} });
|
|
2717
|
-
return;
|
|
2718
|
-
}
|
|
2719
|
-
try {
|
|
2720
|
-
const result = await handler(request2);
|
|
2721
|
-
write({ jsonrpc: "2.0", id, result });
|
|
2722
|
-
} catch (error) {
|
|
2723
|
-
write({
|
|
2724
|
-
jsonrpc: "2.0",
|
|
2725
|
-
id,
|
|
2726
|
-
error: {
|
|
2727
|
-
code: -32e3,
|
|
2728
|
-
message: error.message
|
|
2729
|
-
}
|
|
2730
|
-
});
|
|
2731
|
-
}
|
|
2732
|
-
}
|
|
2733
|
-
return {
|
|
2734
|
-
async initialize() {
|
|
2735
|
-
await request("initialize", {
|
|
2736
|
-
protocolVersion: "2025-03-26",
|
|
2737
|
-
capabilities: {},
|
|
2738
|
-
clientInfo: options.clientInfo ?? {
|
|
2739
|
-
name: "weft",
|
|
2740
|
-
title: "Weft",
|
|
2741
|
-
version: "0.1.0"
|
|
2742
|
-
}
|
|
2743
|
-
});
|
|
2744
|
-
write({ jsonrpc: "2.0", method: "initialized", params: {} });
|
|
2745
|
-
},
|
|
2746
|
-
request,
|
|
2747
|
-
onNotification(handler) {
|
|
2748
|
-
notificationHandlers.add(handler);
|
|
2749
|
-
return () => notificationHandlers.delete(handler);
|
|
2750
|
-
},
|
|
2751
|
-
onRequest(handler) {
|
|
2752
|
-
requestHandlers.add(handler);
|
|
2753
|
-
return () => requestHandlers.delete(handler);
|
|
2754
|
-
},
|
|
2755
|
-
close() {
|
|
2756
|
-
unsubscribeTransport();
|
|
2757
|
-
for (const entry of pending.values()) {
|
|
2758
|
-
clearTimeout(entry.timer);
|
|
2759
|
-
entry.reject(new Error("Codex app-server client closed"));
|
|
2760
|
-
}
|
|
2761
|
-
pending.clear();
|
|
2762
|
-
notificationHandlers.clear();
|
|
2763
|
-
requestHandlers.clear();
|
|
2764
|
-
options.transport.close?.();
|
|
2765
|
-
}
|
|
2766
|
-
};
|
|
2767
|
-
}
|
|
2768
|
-
async function createCodexAppServerSubprocessClient(options = {}) {
|
|
2769
|
-
const transport = createNodeSubprocessTransport({
|
|
2770
|
-
executable: options.executable ?? "codex",
|
|
2771
|
-
args: options.args ?? ["app-server"],
|
|
2772
|
-
env: options.env
|
|
2773
|
-
});
|
|
2774
|
-
const client = createCodexAppServerJsonRpcClient({
|
|
2775
|
-
transport,
|
|
2776
|
-
requestTimeoutMs: options.requestTimeoutMs
|
|
2777
|
-
});
|
|
2778
|
-
try {
|
|
2779
|
-
await client.initialize();
|
|
2780
|
-
} catch (error) {
|
|
2781
|
-
client.close();
|
|
2782
|
-
throw error;
|
|
2783
|
-
}
|
|
2784
|
-
return client;
|
|
2785
|
-
}
|
|
2786
|
-
function createNodeSubprocessTransport(options) {
|
|
2787
|
-
const proc = spawn3(options.executable, options.args, {
|
|
2788
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
2789
|
-
env: options.env ?? process.env
|
|
2790
|
-
});
|
|
2791
|
-
const handlers = /* @__PURE__ */ new Set();
|
|
2792
|
-
let closed = false;
|
|
2793
|
-
if (!proc.stdout || !proc.stdin) {
|
|
2794
|
-
throw new Error("Failed to create subprocess with piped stdio");
|
|
2795
|
-
}
|
|
2796
|
-
const rl = createInterface3({ input: proc.stdout });
|
|
2797
|
-
rl.on("line", (line) => {
|
|
2798
|
-
const trimmed = line.trim();
|
|
2799
|
-
if (!trimmed) return;
|
|
2800
|
-
try {
|
|
2801
|
-
const message = JSON.parse(trimmed);
|
|
2802
|
-
for (const handler of handlers) handler(message);
|
|
2803
|
-
} catch {
|
|
2804
|
-
}
|
|
2805
|
-
});
|
|
2806
|
-
return {
|
|
2807
|
-
write(message) {
|
|
2808
|
-
if (closed) return;
|
|
2809
|
-
proc.stdin.write(`${message}
|
|
2810
|
-
`);
|
|
2811
|
-
},
|
|
2812
|
-
onMessage(handler) {
|
|
2813
|
-
handlers.add(handler);
|
|
2814
|
-
return () => handlers.delete(handler);
|
|
2815
|
-
},
|
|
2816
|
-
close() {
|
|
2817
|
-
closed = true;
|
|
2818
|
-
handlers.clear();
|
|
2819
|
-
rl.close();
|
|
2820
|
-
proc.kill();
|
|
2821
|
-
}
|
|
2822
|
-
};
|
|
2823
|
-
}
|
|
2824
|
-
function asRecord22(value) {
|
|
2825
|
-
return value && typeof value === "object" ? value : {};
|
|
2826
|
-
}
|
|
2827
|
-
function createCodexRuntimeCapabilityReport(options) {
|
|
2828
|
-
const appServerAvailable = options.candidates.some((candidate) => candidate.kind === "app-server" && candidate.available);
|
|
2829
|
-
const nativeAvailable = options.candidates.some((candidate) => candidate.kind === "native-sdk" && candidate.available);
|
|
2830
|
-
const strongRuntimeAvailable = appServerAvailable || nativeAvailable;
|
|
2831
|
-
return createRuntimeCapabilityReport({
|
|
2832
|
-
provider: "codex",
|
|
2833
|
-
candidates: options.candidates,
|
|
2834
|
-
preferredRuntime: "app-server",
|
|
2835
|
-
allowFallback: options.allowFallback,
|
|
2836
|
-
auth: options.auth,
|
|
2837
|
-
extensionCapabilities: {
|
|
2838
|
-
policy: {
|
|
2839
|
-
supported: true,
|
|
2840
|
-
degraded: !strongRuntimeAvailable,
|
|
2841
|
-
modes: ["safe", "ask", "allow-all"],
|
|
2842
|
-
approvals: strongRuntimeAvailable,
|
|
2843
|
-
toolPolicy: true,
|
|
2844
|
-
reason: strongRuntimeAvailable ? void 0 : "codex exec fallback cannot complete app-server approval callbacks"
|
|
2845
|
-
},
|
|
2846
|
-
sources: {
|
|
2847
|
-
supported: strongRuntimeAvailable,
|
|
2848
|
-
registry: strongRuntimeAvailable,
|
|
2849
|
-
mcpTools: strongRuntimeAvailable,
|
|
2850
|
-
reason: strongRuntimeAvailable ? void 0 : "Source tools require app-server, SDK, or host MCP wiring"
|
|
2851
|
-
},
|
|
2852
|
-
skills: {
|
|
2853
|
-
supported: strongRuntimeAvailable,
|
|
2854
|
-
registry: strongRuntimeAvailable,
|
|
2855
|
-
activationPlan: strongRuntimeAvailable,
|
|
2856
|
-
scopedPolicy: strongRuntimeAvailable,
|
|
2857
|
-
reason: strongRuntimeAvailable ? void 0 : "Skill activation is degraded in CLI fallback"
|
|
2858
|
-
},
|
|
2859
|
-
automations: {
|
|
2860
|
-
supported: true,
|
|
2861
|
-
degraded: !strongRuntimeAvailable,
|
|
2862
|
-
eventBus: true,
|
|
2863
|
-
schedulerHost: false,
|
|
2864
|
-
promptAction: true,
|
|
2865
|
-
webhookAction: false,
|
|
2866
|
-
reason: strongRuntimeAvailable ? void 0 : "Automation hooks are degraded in CLI fallback"
|
|
2867
|
-
},
|
|
2868
|
-
hostTools: hostToolCapabilitiesFromExtensions2(options.extensions)
|
|
2869
|
-
}
|
|
2870
|
-
});
|
|
2871
|
-
}
|
|
2872
|
-
function hostToolCapabilitiesFromExtensions2(extensions) {
|
|
2873
|
-
const sessionTools = extensions?.hostServices?.sessionTools;
|
|
2874
|
-
if (!sessionTools) {
|
|
2875
|
-
return {
|
|
2876
|
-
supported: false,
|
|
2877
|
-
sessionTools: false,
|
|
2878
|
-
workflowTransitions: false,
|
|
2879
|
-
browserActions: false,
|
|
2880
|
-
metadataWrites: false
|
|
2881
|
-
};
|
|
2882
|
-
}
|
|
2883
|
-
const workflowTransitions = Boolean(sessionTools.submitPlan);
|
|
2884
|
-
const browserActions = Boolean(sessionTools.runBrowserAction);
|
|
2885
|
-
const metadataWrites = Boolean(sessionTools.updateSessionMetadata);
|
|
2886
|
-
const degraded = !workflowTransitions || !browserActions || !metadataWrites;
|
|
2887
|
-
return {
|
|
2888
|
-
supported: true,
|
|
2889
|
-
degraded: degraded || void 0,
|
|
2890
|
-
sessionTools: true,
|
|
2891
|
-
workflowTransitions,
|
|
2892
|
-
browserActions,
|
|
2893
|
-
metadataWrites,
|
|
2894
|
-
reason: degraded ? "Host session tool bridge is only partially attached" : void 0
|
|
2895
|
-
};
|
|
2896
|
-
}
|
|
2897
|
-
function createCodexProviderRuntime(options) {
|
|
2898
|
-
const report = createCodexRuntimeCapabilityReport(options);
|
|
2899
|
-
const extensions = createRuntimeExtensionContext(options.extensions);
|
|
2900
|
-
let runtimeDriver = options.driver;
|
|
2901
|
-
let ownedAppServerClient;
|
|
2902
|
-
const timeline = [];
|
|
2903
|
-
const stream = new PushTimelineStream2();
|
|
2904
|
-
const baseSequencer = createTimelineSequencer({
|
|
2905
|
-
sessionId: options.sessionId ?? "codex-session",
|
|
2906
|
-
provider: "codex",
|
|
2907
|
-
epoch: options.epoch ?? "default",
|
|
2908
|
-
now: options.now
|
|
2909
|
-
});
|
|
2910
|
-
const sequencer = {
|
|
2911
|
-
append(item, rawRef2) {
|
|
2912
|
-
syncStateFromTimelineItem(item);
|
|
2913
|
-
const envelope = baseSequencer.append(item, rawRef2);
|
|
2914
|
-
timeline.push(envelope);
|
|
2915
|
-
stream.emit(envelope);
|
|
2916
|
-
return envelope;
|
|
2917
|
-
}
|
|
2918
|
-
};
|
|
2919
|
-
let state = initialRuntimeState;
|
|
2920
|
-
function dispatch(action) {
|
|
2921
|
-
state = reduceRuntimeState(state, action);
|
|
2922
|
-
}
|
|
2923
|
-
function syncStateFromTimelineItem(item) {
|
|
2924
|
-
if (item.type === "permission_requested") {
|
|
2925
|
-
dispatch({ type: "permission_request", requestId: item.request.requestId });
|
|
2926
|
-
}
|
|
2927
|
-
if (item.type === "permission_resolved") {
|
|
2928
|
-
dispatch({ type: "permission_response" });
|
|
2929
|
-
}
|
|
2930
|
-
if (item.type === "turn_completed") {
|
|
2931
|
-
dispatch({ type: "turn_completed" });
|
|
2932
|
-
}
|
|
2933
|
-
}
|
|
2934
|
-
function appendFailure(message) {
|
|
2935
|
-
sequencer.append({
|
|
2936
|
-
type: "turn_failed",
|
|
2937
|
-
turnId: "provider-runtime",
|
|
2938
|
-
error: { message }
|
|
2939
|
-
});
|
|
2940
|
-
}
|
|
2941
|
-
async function getDriver() {
|
|
2942
|
-
if (runtimeDriver) return runtimeDriver;
|
|
2943
|
-
if (report.selected === "cli-fallback") {
|
|
2944
|
-
runtimeDriver = createCodexCliFallbackDriver({
|
|
2945
|
-
session: options.createCliFallbackSession?.() ?? createLocalAgentSession({
|
|
2946
|
-
provider: "codex",
|
|
2947
|
-
cwd: options.cwd,
|
|
2948
|
-
sessionId: options.sessionId,
|
|
2949
|
-
model: options.model,
|
|
2950
|
-
reasoningEffort: options.reasoningEffort,
|
|
2951
|
-
permissionMode: options.permissionMode,
|
|
2952
|
-
executable: options.executable,
|
|
2953
|
-
env: options.env
|
|
2954
|
-
}),
|
|
2955
|
-
sessionId: options.sessionId ?? "codex-session",
|
|
2956
|
-
epoch: options.epoch ?? "default",
|
|
2957
|
-
now: options.now
|
|
2958
|
-
});
|
|
2959
|
-
return runtimeDriver;
|
|
2960
|
-
}
|
|
2961
|
-
if (report.selected === "native-sdk") {
|
|
2962
|
-
runtimeDriver = {
|
|
2963
|
-
async sendMessage() {
|
|
2964
|
-
throw new Error("Codex native SDK runtime is not yet implemented. Use app-server or cli-fallback instead.");
|
|
2965
|
-
}
|
|
2966
|
-
};
|
|
2967
|
-
return runtimeDriver;
|
|
2968
|
-
}
|
|
2969
|
-
if (report.selected !== "app-server") return void 0;
|
|
2970
|
-
const client = options.appServerClient ?? await (options.createAppServerClient?.() ?? createCodexAppServerSubprocessClient(options.appServerSubprocess));
|
|
2971
|
-
if (!options.appServerClient) {
|
|
2972
|
-
ownedAppServerClient = client;
|
|
2973
|
-
}
|
|
2974
|
-
runtimeDriver = createCodexAppServerDriver({
|
|
2975
|
-
cwd: options.cwd,
|
|
2976
|
-
client,
|
|
2977
|
-
model: options.model,
|
|
2978
|
-
reasoningEffort: options.reasoningEffort,
|
|
2979
|
-
approvalPolicy: options.approvalPolicy,
|
|
2980
|
-
approvalsReviewer: options.approvalsReviewer,
|
|
2981
|
-
sandbox: options.sandbox,
|
|
2982
|
-
policy: options.policy ?? extensions.policy?.hook,
|
|
2983
|
-
sourceTools: options.sourceTools
|
|
2984
|
-
});
|
|
2985
|
-
return runtimeDriver;
|
|
2986
|
-
}
|
|
2987
|
-
return {
|
|
2988
|
-
sessionId: options.sessionId ?? "codex-session",
|
|
2989
|
-
provider: "codex",
|
|
2990
|
-
runtimeKind: report.selected ?? "app-server",
|
|
2991
|
-
events: stream,
|
|
2992
|
-
commands: {
|
|
2993
|
-
async sendMessage(message, sendOptions) {
|
|
2994
|
-
const driver = await getDriver();
|
|
2995
|
-
if (!driver) {
|
|
2996
|
-
const error = "Codex provider driver is not attached";
|
|
2997
|
-
dispatch({ type: "error", error });
|
|
2998
|
-
appendFailure(error);
|
|
2999
|
-
throw new Error(error);
|
|
3000
|
-
}
|
|
3001
|
-
dispatch({ type: "send_message", message });
|
|
3002
|
-
try {
|
|
3003
|
-
await driver.sendMessage({
|
|
3004
|
-
message,
|
|
3005
|
-
options: sendOptions,
|
|
3006
|
-
origin: sendOptions?.commandOrigin ?? extensions.commandOrigin
|
|
3007
|
-
}, sequencer);
|
|
3008
|
-
dispatch({ type: "complete" });
|
|
3009
|
-
} catch (err) {
|
|
3010
|
-
const error = err instanceof Error ? err.message : String(err);
|
|
3011
|
-
dispatch({ type: "error", error });
|
|
3012
|
-
appendFailure(error);
|
|
3013
|
-
throw err;
|
|
3014
|
-
}
|
|
3015
|
-
},
|
|
3016
|
-
async abort(reason) {
|
|
3017
|
-
dispatch({ type: "abort", reason });
|
|
3018
|
-
await runtimeDriver?.abort?.(reason);
|
|
3019
|
-
},
|
|
3020
|
-
async respondToPermission(requestId, allowed, remember) {
|
|
3021
|
-
dispatch({ type: "permission_response" });
|
|
3022
|
-
await runtimeDriver?.respondToPermission?.(requestId, allowed, remember);
|
|
3023
|
-
},
|
|
3024
|
-
async dispose() {
|
|
3025
|
-
dispatch({ type: "dispose" });
|
|
3026
|
-
stream.disconnect();
|
|
3027
|
-
await runtimeDriver?.dispose?.();
|
|
3028
|
-
ownedAppServerClient?.close?.();
|
|
3029
|
-
}
|
|
3030
|
-
},
|
|
3031
|
-
async preflight() {
|
|
3032
|
-
dispatch({ type: "preflight_ok" });
|
|
3033
|
-
if (!report.selected) {
|
|
3034
|
-
dispatch({ type: "preflight_error", error: report.error ?? "No runtime selected" });
|
|
3035
|
-
}
|
|
3036
|
-
sequencer.append({ type: "runtime_capability_report", report });
|
|
3037
|
-
return report;
|
|
3038
|
-
},
|
|
3039
|
-
async fetchTimeline(request) {
|
|
3040
|
-
if (!request.cursor && timeline.length === 0) {
|
|
3041
|
-
return {
|
|
3042
|
-
items: [],
|
|
3043
|
-
nextCursor: createTimelineCursor({ epoch: options.epoch ?? "default", afterSeq: 0 }),
|
|
3044
|
-
hasGap: false
|
|
3045
|
-
};
|
|
3046
|
-
}
|
|
3047
|
-
return fetchTimeline(timeline, request);
|
|
3048
|
-
},
|
|
3049
|
-
getState() {
|
|
3050
|
-
return state;
|
|
3051
|
-
}
|
|
3052
|
-
};
|
|
3053
|
-
}
|
|
3054
|
-
var PushTimelineStream2 = class {
|
|
3055
|
-
listeners = /* @__PURE__ */ new Set();
|
|
3056
|
-
connect(onEvent, onError, onClose) {
|
|
3057
|
-
this.listeners.add({ onEvent, onError, onClose });
|
|
3058
|
-
}
|
|
3059
|
-
disconnect() {
|
|
3060
|
-
this.listeners.clear();
|
|
3061
|
-
}
|
|
3062
|
-
isConnected() {
|
|
3063
|
-
return this.listeners.size > 0;
|
|
3064
|
-
}
|
|
3065
|
-
emit(event) {
|
|
3066
|
-
for (const listener of this.listeners) listener.onEvent(event);
|
|
3067
|
-
}
|
|
3068
|
-
};
|
|
3069
|
-
function createCodexCliFallbackDriver(options) {
|
|
3070
|
-
const projector = createLocalTimelineProjector({
|
|
3071
|
-
sessionId: options.sessionId,
|
|
3072
|
-
provider: "codex",
|
|
3073
|
-
epoch: options.epoch,
|
|
3074
|
-
now: options.now
|
|
3075
|
-
});
|
|
3076
|
-
let connected = false;
|
|
3077
|
-
let activeSequencer;
|
|
3078
|
-
function connect(sequencer) {
|
|
3079
|
-
activeSequencer = sequencer;
|
|
3080
|
-
if (connected) return;
|
|
3081
|
-
connected = true;
|
|
3082
|
-
options.session.events.connect((event) => {
|
|
3083
|
-
if (!activeSequencer) return;
|
|
3084
|
-
for (const envelope of projector.project(event)) {
|
|
3085
|
-
activeSequencer.append(envelope.item, envelope.rawRef);
|
|
3086
|
-
}
|
|
3087
|
-
});
|
|
3088
|
-
}
|
|
3089
|
-
return {
|
|
3090
|
-
async sendMessage(input, sequencer) {
|
|
3091
|
-
connect(sequencer);
|
|
3092
|
-
await options.session.commands.sendMessage(input.message, input.options);
|
|
3093
|
-
},
|
|
3094
|
-
abort(reason) {
|
|
3095
|
-
return options.session.commands.abort(reason);
|
|
3096
|
-
},
|
|
3097
|
-
respondToPermission(requestId, allowed, remember) {
|
|
3098
|
-
return options.session.commands.respondToPermission(requestId, allowed, remember);
|
|
3099
|
-
},
|
|
3100
|
-
dispose() {
|
|
3101
|
-
return options.session.commands.dispose();
|
|
3102
|
-
}
|
|
3103
|
-
};
|
|
3104
|
-
}
|
|
3105
|
-
|
|
3106
|
-
// ../packages/client/dist/index.js
|
|
3107
|
-
var FlitroHttpClient = class {
|
|
3108
|
-
baseUrl;
|
|
3109
|
-
timeout;
|
|
3110
|
-
apiKey;
|
|
3111
|
-
tenantId;
|
|
3112
|
-
onTokenExpired;
|
|
3113
|
-
token;
|
|
3114
|
-
constructor(options) {
|
|
3115
|
-
this.baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
3116
|
-
this.timeout = options.timeout ?? 3e4;
|
|
3117
|
-
this.apiKey = options.apiKey ?? "";
|
|
3118
|
-
this.tenantId = options.tenantId ?? "";
|
|
3119
|
-
this.token = options.token ?? "";
|
|
3120
|
-
this.onTokenExpired = options.onTokenExpired;
|
|
3121
|
-
}
|
|
3122
|
-
/** Current Authorization bearer (scoped token wins over apiKey). */
|
|
3123
|
-
getBearerToken() {
|
|
3124
|
-
return this.token || this.apiKey;
|
|
3125
|
-
}
|
|
3126
|
-
/** Replace the scoped token (e.g. after a host-backend refresh). */
|
|
3127
|
-
setToken(token) {
|
|
3128
|
-
this.token = token;
|
|
3129
|
-
}
|
|
3130
|
-
buildHeaders() {
|
|
3131
|
-
const headers = { "Content-Type": "application/json" };
|
|
3132
|
-
const bearer = this.getBearerToken();
|
|
3133
|
-
if (bearer) headers["Authorization"] = `Bearer ${bearer}`;
|
|
3134
|
-
if (!this.token && this.tenantId) headers["X-Tenant-ID"] = this.tenantId;
|
|
3135
|
-
return headers;
|
|
3136
|
-
}
|
|
3137
|
-
async preflight() {
|
|
3138
|
-
return this.get("/v1/capabilities");
|
|
3139
|
-
}
|
|
3140
|
-
async health() {
|
|
3141
|
-
return this.get("/v1/health");
|
|
3142
|
-
}
|
|
3143
|
-
async createSession(options) {
|
|
3144
|
-
return this.post("/v1/sessions", {
|
|
3145
|
-
title: options?.title ?? "Weft session",
|
|
3146
|
-
model: options?.model,
|
|
3147
|
-
skill_names: options?.skillNames,
|
|
3148
|
-
mcp_server_names: options?.mcpServerNames
|
|
3149
|
-
});
|
|
3150
|
-
}
|
|
3151
|
-
async getSession(sessionId) {
|
|
3152
|
-
return this.get(`/v1/sessions/${encodeURIComponent(sessionId)}`);
|
|
3153
|
-
}
|
|
3154
|
-
async sendMessage(sessionId, message, options) {
|
|
3155
|
-
const budget = options?.budget ? {
|
|
3156
|
-
max_steps: options.budget.maxSteps,
|
|
3157
|
-
max_tokens: options.budget.maxTokens,
|
|
3158
|
-
max_wall_time_sec: options.budget.maxWallTimeSec
|
|
3159
|
-
} : void 0;
|
|
3160
|
-
return this.post(`/v1/sessions/${encodeURIComponent(sessionId)}/runs`, {
|
|
3161
|
-
message,
|
|
3162
|
-
model: options?.model,
|
|
3163
|
-
skill_names: options?.skillNames,
|
|
3164
|
-
mcp_server_names: options?.mcpServerNames,
|
|
3165
|
-
tool_names: options?.toolNames,
|
|
3166
|
-
approval_policy: options?.approvalPolicy,
|
|
3167
|
-
execution_mode: options?.executionMode,
|
|
3168
|
-
workspace_id: options?.workspaceId,
|
|
3169
|
-
budget
|
|
3170
|
-
});
|
|
3171
|
-
}
|
|
3172
|
-
async cancelRun(runId) {
|
|
3173
|
-
return this.post(`/v1/runs/${encodeURIComponent(runId)}/cancel`, {});
|
|
3174
|
-
}
|
|
3175
|
-
async resumeTool(runId, resumeData) {
|
|
3176
|
-
return this.post(
|
|
3177
|
-
`/v1/runs/${encodeURIComponent(runId)}/resume-tool`,
|
|
3178
|
-
{ resume_data: resumeData }
|
|
3179
|
-
);
|
|
3180
|
-
}
|
|
3181
|
-
async respondToPermission(sessionId, requestId, allowed, options) {
|
|
3182
|
-
return this.post(
|
|
3183
|
-
`/v1/sessions/${encodeURIComponent(sessionId)}/permission-response`,
|
|
3184
|
-
{ requestId, allowed, remember: options?.remember, text: options?.text, answer: options?.answer }
|
|
3185
|
-
);
|
|
3186
|
-
}
|
|
3187
|
-
async patchSession(sessionId, patch) {
|
|
3188
|
-
return this.patch(
|
|
3189
|
-
`/v1/sessions/${encodeURIComponent(sessionId)}`,
|
|
3190
|
-
patch
|
|
3191
|
-
);
|
|
3192
|
-
}
|
|
3193
|
-
async listModels() {
|
|
3194
|
-
return this.get("/v1/models");
|
|
3195
|
-
}
|
|
3196
|
-
async fetchTimeline(sessionId, afterSeq, limit) {
|
|
3197
|
-
const params = new URLSearchParams();
|
|
3198
|
-
if (afterSeq !== void 0) params.set("after_seq", String(afterSeq));
|
|
3199
|
-
if (limit !== void 0) params.set("limit", String(limit));
|
|
3200
|
-
const qs = params.toString();
|
|
3201
|
-
const url = `/v1/sessions/${encodeURIComponent(sessionId)}/timeline/fetch${qs ? `?${qs}` : ""}`;
|
|
3202
|
-
return this.get(url);
|
|
3203
|
-
}
|
|
3204
|
-
/** Returns the SSE stream URL for a session's canonical timeline. */
|
|
3205
|
-
sessionTimelineUrl(sessionId) {
|
|
3206
|
-
return `${this.baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/timeline`;
|
|
3207
|
-
}
|
|
3208
|
-
async get(path) {
|
|
3209
|
-
return this.request("GET", path, void 0);
|
|
3210
|
-
}
|
|
3211
|
-
async post(path, body) {
|
|
3212
|
-
return this.request("POST", path, body);
|
|
3213
|
-
}
|
|
3214
|
-
async patch(path, body) {
|
|
3215
|
-
return this.request("PATCH", path, body);
|
|
3216
|
-
}
|
|
3217
|
-
async request(method, path, body, isRetry = false) {
|
|
3218
|
-
const url = `${this.baseUrl}${path}`;
|
|
3219
|
-
const controller = new AbortController();
|
|
3220
|
-
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
3221
|
-
try {
|
|
3222
|
-
const response = await fetch(url, {
|
|
3223
|
-
method,
|
|
3224
|
-
headers: this.buildHeaders(),
|
|
3225
|
-
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
3226
|
-
signal: controller.signal
|
|
3227
|
-
});
|
|
3228
|
-
if (response.status === 401 && this.token && this.onTokenExpired && !isRetry) {
|
|
3229
|
-
const fresh = await this.onTokenExpired();
|
|
3230
|
-
if (fresh) {
|
|
3231
|
-
this.token = fresh;
|
|
3232
|
-
return this.request(method, path, body, true);
|
|
3233
|
-
}
|
|
3234
|
-
}
|
|
3235
|
-
if (!response.ok) {
|
|
3236
|
-
let errorMsg = `Flitro HTTP ${response.status}`;
|
|
3237
|
-
try {
|
|
3238
|
-
const errBody = await response.json();
|
|
3239
|
-
if (errBody.error) errorMsg = `${errorMsg}: ${errBody.error}`;
|
|
3240
|
-
} catch {
|
|
3241
|
-
}
|
|
3242
|
-
throw new Error(errorMsg);
|
|
3243
|
-
}
|
|
3244
|
-
return response.json();
|
|
3245
|
-
} finally {
|
|
3246
|
-
clearTimeout(timer);
|
|
3247
|
-
}
|
|
3248
|
-
}
|
|
3249
|
-
};
|
|
3250
|
-
var FlitroFetchSseTimelineStream = class {
|
|
3251
|
-
listeners = /* @__PURE__ */ new Set();
|
|
3252
|
-
abortController = null;
|
|
3253
|
-
connected = false;
|
|
3254
|
-
disposed = false;
|
|
3255
|
-
afterSeq;
|
|
3256
|
-
reconnectAttempts = 0;
|
|
3257
|
-
tokenOverride = "";
|
|
3258
|
-
getBearerToken;
|
|
3259
|
-
onTokenExpired;
|
|
3260
|
-
options;
|
|
3261
|
-
constructor(options) {
|
|
3262
|
-
this.afterSeq = options.initialAfterSeq ?? 0;
|
|
3263
|
-
this.getBearerToken = options.getBearerToken;
|
|
3264
|
-
this.onTokenExpired = options.onTokenExpired;
|
|
3265
|
-
this.options = {
|
|
3266
|
-
url: options.url,
|
|
3267
|
-
apiKey: options.apiKey ?? "",
|
|
3268
|
-
tenantId: options.tenantId ?? "",
|
|
3269
|
-
reconnectDelayMs: options.reconnectDelayMs ?? 1500,
|
|
3270
|
-
maxReconnectAttempts: options.maxReconnectAttempts ?? 20,
|
|
3271
|
-
initialAfterSeq: this.afterSeq,
|
|
3272
|
-
now: options.now ?? (() => Date.now())
|
|
3273
|
-
};
|
|
3274
|
-
}
|
|
3275
|
-
connect(onEvent, onError, onClose) {
|
|
3276
|
-
this.disposed = false;
|
|
3277
|
-
this.listeners.add({ onEvent, onError, onClose });
|
|
3278
|
-
if (!this.connected) {
|
|
3279
|
-
this.connected = true;
|
|
3280
|
-
this.reconnectAttempts = 0;
|
|
3281
|
-
this.startFetch();
|
|
3282
|
-
}
|
|
3283
|
-
}
|
|
3284
|
-
disconnect() {
|
|
3285
|
-
this.disposed = true;
|
|
3286
|
-
this.connected = false;
|
|
3287
|
-
this.abortController?.abort();
|
|
3288
|
-
for (const listener of this.listeners) {
|
|
3289
|
-
listener.onClose?.();
|
|
3290
|
-
}
|
|
3291
|
-
this.listeners.clear();
|
|
3292
|
-
}
|
|
3293
|
-
isConnected() {
|
|
3294
|
-
return this.connected && !this.disposed;
|
|
3295
|
-
}
|
|
3296
|
-
buildUrl() {
|
|
3297
|
-
let url = this.options.url;
|
|
3298
|
-
if (this.afterSeq > 0) {
|
|
3299
|
-
url += (url.includes("?") ? "&" : "?") + `after_seq=${this.afterSeq}`;
|
|
3300
|
-
}
|
|
3301
|
-
return url;
|
|
3302
|
-
}
|
|
3303
|
-
buildHeaders() {
|
|
3304
|
-
const h = { Accept: "text/event-stream" };
|
|
3305
|
-
const bearer = this.tokenOverride || this.getBearerToken?.() || this.options.apiKey;
|
|
3306
|
-
if (bearer) h["Authorization"] = `Bearer ${bearer}`;
|
|
3307
|
-
if (this.options.tenantId) h["X-Tenant-ID"] = this.options.tenantId;
|
|
3308
|
-
return h;
|
|
3309
|
-
}
|
|
3310
|
-
async startFetch() {
|
|
3311
|
-
if (this.disposed) return;
|
|
3312
|
-
this.abortController = new AbortController();
|
|
3313
|
-
try {
|
|
3314
|
-
const response = await fetch(this.buildUrl(), {
|
|
3315
|
-
headers: this.buildHeaders(),
|
|
3316
|
-
signal: this.abortController.signal
|
|
3317
|
-
});
|
|
3318
|
-
if (response.status === 401 && this.onTokenExpired) {
|
|
3319
|
-
const fresh = await this.onTokenExpired();
|
|
3320
|
-
if (fresh) this.tokenOverride = fresh;
|
|
3321
|
-
}
|
|
3322
|
-
if (!response.ok || !response.body) {
|
|
3323
|
-
throw new Error(`Flitro SSE: HTTP ${response.status}`);
|
|
3324
|
-
}
|
|
3325
|
-
this.reconnectAttempts = 0;
|
|
3326
|
-
const reader = response.body.getReader();
|
|
3327
|
-
const decoder = new TextDecoder();
|
|
3328
|
-
let buffer = "";
|
|
3329
|
-
while (true) {
|
|
3330
|
-
const { done, value } = await reader.read();
|
|
3331
|
-
if (done) break;
|
|
3332
|
-
buffer += decoder.decode(value, { stream: true });
|
|
3333
|
-
const envelopes = parseSseBuffer(buffer);
|
|
3334
|
-
buffer = envelopes.remainder;
|
|
3335
|
-
for (const envelope of envelopes.items) {
|
|
3336
|
-
if (envelope.seq > this.afterSeq) {
|
|
3337
|
-
this.afterSeq = envelope.seq;
|
|
3338
|
-
}
|
|
3339
|
-
for (const listener of this.listeners) {
|
|
3340
|
-
listener.onEvent(envelope);
|
|
3341
|
-
}
|
|
3342
|
-
}
|
|
3343
|
-
}
|
|
3344
|
-
if (!this.disposed) {
|
|
3345
|
-
this.connected = false;
|
|
3346
|
-
this.scheduleReconnect();
|
|
3347
|
-
}
|
|
3348
|
-
} catch (err) {
|
|
3349
|
-
if (this.disposed) return;
|
|
3350
|
-
if (err instanceof Error && err.name === "AbortError") return;
|
|
3351
|
-
this.connected = false;
|
|
3352
|
-
this.scheduleReconnect();
|
|
3353
|
-
}
|
|
3354
|
-
}
|
|
3355
|
-
scheduleReconnect() {
|
|
3356
|
-
if (this.disposed) return;
|
|
3357
|
-
if (this.reconnectAttempts >= this.options.maxReconnectAttempts) {
|
|
3358
|
-
this.emitError(new Error(`Flitro SSE: max reconnect attempts reached`));
|
|
3359
|
-
return;
|
|
3360
|
-
}
|
|
3361
|
-
this.reconnectAttempts++;
|
|
3362
|
-
const delay = Math.min(this.options.reconnectDelayMs * this.reconnectAttempts, 3e4);
|
|
3363
|
-
setTimeout(() => {
|
|
3364
|
-
this.connected = true;
|
|
3365
|
-
this.startFetch();
|
|
3366
|
-
}, delay);
|
|
3367
|
-
}
|
|
3368
|
-
emitError(err) {
|
|
3369
|
-
for (const listener of this.listeners) listener.onError?.(err);
|
|
3370
|
-
}
|
|
3371
|
-
};
|
|
3372
|
-
function parseSseBuffer(buffer) {
|
|
3373
|
-
const items = [];
|
|
3374
|
-
const blocks = buffer.split("\n\n");
|
|
3375
|
-
const remainder = blocks.pop() ?? "";
|
|
3376
|
-
for (const block of blocks) {
|
|
3377
|
-
let eventType = "";
|
|
3378
|
-
let data = "";
|
|
3379
|
-
for (const line of block.split("\n")) {
|
|
3380
|
-
if (line.startsWith("event: ")) {
|
|
3381
|
-
eventType = line.slice(7).trim();
|
|
3382
|
-
} else if (line.startsWith("data: ")) {
|
|
3383
|
-
data = line.slice(6).trim();
|
|
3384
|
-
}
|
|
3385
|
-
}
|
|
3386
|
-
if (eventType === "timeline" && data) {
|
|
3387
|
-
try {
|
|
3388
|
-
items.push(JSON.parse(data));
|
|
3389
|
-
} catch {
|
|
3390
|
-
}
|
|
3391
|
-
}
|
|
3392
|
-
}
|
|
3393
|
-
return { items, remainder };
|
|
3394
|
-
}
|
|
3395
|
-
function createFlitroTimelineStream(options) {
|
|
3396
|
-
return new FlitroFetchSseTimelineStream(options);
|
|
3397
|
-
}
|
|
3398
|
-
|
|
3399
|
-
// ../packages/provider-flitro/dist/index.js
|
|
3400
|
-
function mapPermissionModeToApprovalPolicy(mode) {
|
|
3401
|
-
if (mode === "allow-all") return "auto";
|
|
3402
|
-
if (mode === "safe") return "never";
|
|
3403
|
-
if (mode === "ask") return "on-request";
|
|
3404
|
-
return void 0;
|
|
3405
|
-
}
|
|
3406
|
-
function createFlitroDriver(options) {
|
|
3407
|
-
let activeRunId;
|
|
3408
|
-
return {
|
|
3409
|
-
async sendMessage(input, _sequencer) {
|
|
3410
|
-
const run = await options.client.sendMessage(
|
|
3411
|
-
options.sessionId,
|
|
3412
|
-
input.message,
|
|
3413
|
-
{
|
|
3414
|
-
model: input.options?.model ?? options.model,
|
|
3415
|
-
skillNames: options.skillNames,
|
|
3416
|
-
mcpServerNames: options.mcpServerNames,
|
|
3417
|
-
// Per-message permission choice (from the chat panel selector) wins
|
|
3418
|
-
// over the runtime's creation-time default.
|
|
3419
|
-
approvalPolicy: mapPermissionModeToApprovalPolicy(input.options?.permissionMode) ?? options.approvalPolicy
|
|
3420
|
-
}
|
|
3421
|
-
);
|
|
3422
|
-
activeRunId = run.run_id;
|
|
3423
|
-
},
|
|
3424
|
-
async abort(reason) {
|
|
3425
|
-
if (activeRunId) {
|
|
3426
|
-
await options.client.cancelRun(activeRunId).catch(() => {
|
|
3427
|
-
});
|
|
3428
|
-
activeRunId = void 0;
|
|
3429
|
-
}
|
|
3430
|
-
},
|
|
3431
|
-
async respondToPermission(requestId, allowed, remember) {
|
|
3432
|
-
await options.client.respondToPermission(options.sessionId, requestId, allowed, {
|
|
3433
|
-
remember
|
|
3434
|
-
});
|
|
3435
|
-
},
|
|
3436
|
-
async resumeTool(runId, resumeData) {
|
|
3437
|
-
await options.client.resumeTool(runId, resumeData);
|
|
3438
|
-
},
|
|
3439
|
-
async dispose() {
|
|
3440
|
-
if (activeRunId) {
|
|
3441
|
-
await options.client.cancelRun(activeRunId).catch(() => {
|
|
3442
|
-
});
|
|
3443
|
-
activeRunId = void 0;
|
|
3444
|
-
}
|
|
3445
|
-
}
|
|
3446
|
-
};
|
|
3447
|
-
}
|
|
3448
|
-
function createFlitroRuntimeCapabilityReport(options) {
|
|
3449
|
-
const appServerAvailable = options.candidates.some((c) => c.kind === "app-server" && c.available);
|
|
3450
|
-
return createRuntimeCapabilityReport({
|
|
3451
|
-
provider: "flitro",
|
|
3452
|
-
candidates: options.candidates,
|
|
3453
|
-
preferredRuntime: "app-server",
|
|
3454
|
-
allowFallback: options.allowFallback,
|
|
3455
|
-
auth: options.auth,
|
|
3456
|
-
extensionCapabilities: {
|
|
3457
|
-
policy: {
|
|
3458
|
-
supported: true,
|
|
3459
|
-
degraded: !appServerAvailable || void 0,
|
|
3460
|
-
modes: ["safe", "ask", "allow-all"],
|
|
3461
|
-
approvals: appServerAvailable,
|
|
3462
|
-
toolPolicy: appServerAvailable,
|
|
3463
|
-
reason: appServerAvailable ? void 0 : "Flitro server is unavailable"
|
|
3464
|
-
},
|
|
3465
|
-
sources: {
|
|
3466
|
-
supported: appServerAvailable,
|
|
3467
|
-
registry: appServerAvailable,
|
|
3468
|
-
mcpTools: appServerAvailable,
|
|
3469
|
-
credentialGateway: false,
|
|
3470
|
-
reason: appServerAvailable ? void 0 : "Source tools require a running Flitro server"
|
|
3471
|
-
},
|
|
3472
|
-
skills: {
|
|
3473
|
-
supported: appServerAvailable,
|
|
3474
|
-
registry: appServerAvailable,
|
|
3475
|
-
activationPlan: appServerAvailable,
|
|
3476
|
-
scopedPolicy: appServerAvailable,
|
|
3477
|
-
reason: appServerAvailable ? void 0 : "Skill activation requires a running Flitro server"
|
|
3478
|
-
},
|
|
3479
|
-
automations: {
|
|
3480
|
-
supported: appServerAvailable,
|
|
3481
|
-
degraded: !appServerAvailable || void 0,
|
|
3482
|
-
eventBus: appServerAvailable,
|
|
3483
|
-
schedulerHost: appServerAvailable,
|
|
3484
|
-
promptAction: appServerAvailable,
|
|
3485
|
-
webhookAction: false,
|
|
3486
|
-
reason: appServerAvailable ? void 0 : "Automations require a running Flitro server"
|
|
3487
|
-
},
|
|
3488
|
-
hostTools: {
|
|
3489
|
-
supported: appServerAvailable,
|
|
3490
|
-
sessionTools: false,
|
|
3491
|
-
workflowTransitions: false,
|
|
3492
|
-
browserActions: false,
|
|
3493
|
-
metadataWrites: appServerAvailable
|
|
3494
|
-
}
|
|
3495
|
-
}
|
|
3496
|
-
});
|
|
3497
|
-
}
|
|
3498
|
-
function createFlitroProviderRuntime(options) {
|
|
3499
|
-
const report = createFlitroRuntimeCapabilityReport(options);
|
|
3500
|
-
const extensions = createRuntimeExtensionContext(options.extensions);
|
|
3501
|
-
const client = new FlitroHttpClient(options.server);
|
|
3502
|
-
let resolvedSessionId = options.sessionId ?? "";
|
|
3503
|
-
let epoch = options.epoch ?? (resolvedSessionId ? `flitro-${resolvedSessionId}` : "flitro-pending");
|
|
3504
|
-
const now = options.now ?? (() => Date.now());
|
|
3505
|
-
const timeline = [];
|
|
3506
|
-
const seenTimelineKeys = /* @__PURE__ */ new Set();
|
|
3507
|
-
const stream = new PushTimelineStream3();
|
|
3508
|
-
let state = initialRuntimeState;
|
|
3509
|
-
let runtimeDriver = options.driver;
|
|
3510
|
-
let sseStream;
|
|
3511
|
-
const pendingMessages = [];
|
|
3512
|
-
function dispatch(action) {
|
|
3513
|
-
state = reduceRuntimeState(state, action);
|
|
3514
|
-
}
|
|
3515
|
-
function syncStateFromEnvelope(envelope) {
|
|
3516
|
-
const item = envelope.item;
|
|
3517
|
-
if (item.type === "permission_requested") {
|
|
3518
|
-
dispatch({ type: "permission_request", requestId: item.request.requestId });
|
|
3519
|
-
} else if (item.type === "permission_resolved") {
|
|
3520
|
-
dispatch({ type: "permission_response" });
|
|
3521
|
-
} else if (item.type === "turn_completed") {
|
|
3522
|
-
dispatch({ type: "turn_completed" });
|
|
3523
|
-
dispatch({ type: "complete" });
|
|
3524
|
-
void sendNextPendingMessage();
|
|
3525
|
-
} else if (item.type === "turn_failed") {
|
|
3526
|
-
const err = item.error;
|
|
3527
|
-
dispatch({ type: "error", error: err?.message ?? "turn failed" });
|
|
3528
|
-
} else if (item.type === "tool_suspended") {
|
|
3529
|
-
dispatch({ type: "permission_request", requestId: `tool-suspend:${item.callId}` });
|
|
3530
|
-
} else if (item.type === "tool_resumed") {
|
|
3531
|
-
dispatch({ type: "permission_response" });
|
|
3532
|
-
} else if (item.type === "session_status") {
|
|
3533
|
-
if (item.status === "running") {
|
|
3534
|
-
dispatch({ type: "starting" });
|
|
3535
|
-
} else if (item.status === "ended") {
|
|
3536
|
-
dispatch({ type: "abort" });
|
|
3537
|
-
}
|
|
3538
|
-
}
|
|
3539
|
-
}
|
|
3540
|
-
function appendEnvelope(envelope) {
|
|
3541
|
-
if (envelope.seq > 0) {
|
|
3542
|
-
const key = `${envelope.epoch || envelope.sessionId || "unknown"}:${envelope.seq}`;
|
|
3543
|
-
if (seenTimelineKeys.has(key)) return;
|
|
3544
|
-
seenTimelineKeys.add(key);
|
|
3545
|
-
}
|
|
3546
|
-
syncStateFromEnvelope(envelope);
|
|
3547
|
-
timeline.push(envelope);
|
|
3548
|
-
stream.emit(envelope);
|
|
3549
|
-
}
|
|
3550
|
-
function appendFailure(message) {
|
|
3551
|
-
const item = { type: "turn_failed", turnId: "provider-runtime", error: { message } };
|
|
3552
|
-
const envelope = {
|
|
3553
|
-
sessionId: resolvedSessionId,
|
|
3554
|
-
provider: "flitro",
|
|
3555
|
-
seq: timeline.length + 1,
|
|
3556
|
-
epoch,
|
|
3557
|
-
timestamp: now(),
|
|
3558
|
-
item
|
|
3559
|
-
};
|
|
3560
|
-
appendEnvelope(envelope);
|
|
3561
|
-
}
|
|
3562
|
-
function getDriver() {
|
|
3563
|
-
if (runtimeDriver) return runtimeDriver;
|
|
3564
|
-
runtimeDriver = createFlitroDriver({
|
|
3565
|
-
client,
|
|
3566
|
-
sessionId: resolvedSessionId,
|
|
3567
|
-
model: options.model,
|
|
3568
|
-
skillNames: options.skillNames,
|
|
3569
|
-
mcpServerNames: options.mcpServerNames,
|
|
3570
|
-
approvalPolicy: options.approvalPolicy
|
|
3571
|
-
});
|
|
3572
|
-
return runtimeDriver;
|
|
3573
|
-
}
|
|
3574
|
-
async function sendToFlitro(message, sendOptions) {
|
|
3575
|
-
const driver = getDriver();
|
|
3576
|
-
try {
|
|
3577
|
-
await driver.sendMessage({ message, options: sendOptions }, {
|
|
3578
|
-
append: (item, rawRef2) => {
|
|
3579
|
-
const envelope = {
|
|
3580
|
-
sessionId: resolvedSessionId,
|
|
3581
|
-
provider: "flitro",
|
|
3582
|
-
seq: timeline.length + 1,
|
|
3583
|
-
epoch,
|
|
3584
|
-
timestamp: now(),
|
|
3585
|
-
item,
|
|
3586
|
-
rawRef: rawRef2
|
|
3587
|
-
};
|
|
3588
|
-
appendEnvelope(envelope);
|
|
3589
|
-
return envelope;
|
|
3590
|
-
}
|
|
3591
|
-
});
|
|
3592
|
-
} catch (err) {
|
|
3593
|
-
const error = err instanceof Error ? err.message : String(err);
|
|
3594
|
-
dispatch({ type: "error", error });
|
|
3595
|
-
appendFailure(error);
|
|
3596
|
-
throw err;
|
|
3597
|
-
}
|
|
3598
|
-
}
|
|
3599
|
-
async function sendNextPendingMessage() {
|
|
3600
|
-
const next = pendingMessages.shift();
|
|
3601
|
-
if (!next) return;
|
|
3602
|
-
try {
|
|
3603
|
-
await sendToFlitro(next.message, next.options);
|
|
3604
|
-
} catch {
|
|
3605
|
-
}
|
|
3606
|
-
}
|
|
3607
|
-
async function ensureSession() {
|
|
3608
|
-
if (resolvedSessionId) return;
|
|
3609
|
-
const session = await client.createSession({
|
|
3610
|
-
model: options.model,
|
|
3611
|
-
skillNames: options.skillNames,
|
|
3612
|
-
mcpServerNames: options.mcpServerNames
|
|
3613
|
-
});
|
|
3614
|
-
resolvedSessionId = session.session_id;
|
|
3615
|
-
if (!options.epoch) {
|
|
3616
|
-
epoch = `flitro-${resolvedSessionId}`;
|
|
3617
|
-
}
|
|
3618
|
-
connectSse();
|
|
3619
|
-
}
|
|
3620
|
-
function connectSse() {
|
|
3621
|
-
if (!resolvedSessionId || sseStream) return;
|
|
3622
|
-
sseStream = createFlitroTimelineStream({
|
|
3623
|
-
url: client.sessionTimelineUrl(resolvedSessionId),
|
|
3624
|
-
apiKey: options.server.apiKey,
|
|
3625
|
-
tenantId: options.server.tenantId,
|
|
3626
|
-
// Scoped embed tokens rotate; always read the client's current bearer
|
|
3627
|
-
// and propagate refreshed tokens back to it.
|
|
3628
|
-
getBearerToken: () => client.getBearerToken(),
|
|
3629
|
-
onTokenExpired: options.server.onTokenExpired ? async () => {
|
|
3630
|
-
const fresh = await options.server.onTokenExpired?.();
|
|
3631
|
-
if (fresh) client.setToken(fresh);
|
|
3632
|
-
return fresh;
|
|
3633
|
-
} : void 0
|
|
3634
|
-
});
|
|
3635
|
-
sseStream.connect(
|
|
3636
|
-
// The client package types `item` as unknown; the server emits the
|
|
3637
|
-
// canonical Weft envelope, so this is the narrowing point.
|
|
3638
|
-
(envelope) => appendEnvelope(envelope),
|
|
3639
|
-
(_err) => {
|
|
3640
|
-
}
|
|
3641
|
-
);
|
|
3642
|
-
}
|
|
3643
|
-
if (resolvedSessionId) {
|
|
3644
|
-
connectSse();
|
|
3645
|
-
}
|
|
3646
|
-
return {
|
|
3647
|
-
get sessionId() {
|
|
3648
|
-
return resolvedSessionId;
|
|
3649
|
-
},
|
|
3650
|
-
provider: "flitro",
|
|
3651
|
-
runtimeKind: report.selected ?? "app-server",
|
|
3652
|
-
events: stream,
|
|
3653
|
-
commands: {
|
|
3654
|
-
async sendMessage(message, sendOptions) {
|
|
3655
|
-
await ensureSession();
|
|
3656
|
-
const shouldQueue = state.status === "running" || state.status === "waiting_for_permission";
|
|
3657
|
-
dispatch({ type: "send_message", message });
|
|
3658
|
-
if (shouldQueue) {
|
|
3659
|
-
pendingMessages.push({ message, options: sendOptions });
|
|
3660
|
-
return;
|
|
3661
|
-
}
|
|
3662
|
-
await sendToFlitro(message, sendOptions);
|
|
3663
|
-
},
|
|
3664
|
-
async abort(reason) {
|
|
3665
|
-
dispatch({ type: "abort", reason });
|
|
3666
|
-
pendingMessages.length = 0;
|
|
3667
|
-
await runtimeDriver?.abort?.(reason);
|
|
3668
|
-
},
|
|
3669
|
-
async respondToPermission(requestId, allowed, remember) {
|
|
3670
|
-
await getDriver().respondToPermission?.(requestId, allowed, remember);
|
|
3671
|
-
dispatch({ type: "permission_response" });
|
|
3672
|
-
},
|
|
3673
|
-
async resumeTool(runId, resumeData) {
|
|
3674
|
-
await getDriver().resumeTool?.(runId, resumeData);
|
|
3675
|
-
},
|
|
3676
|
-
async dispose() {
|
|
3677
|
-
dispatch({ type: "dispose" });
|
|
3678
|
-
pendingMessages.length = 0;
|
|
3679
|
-
sseStream?.disconnect();
|
|
3680
|
-
sseStream = void 0;
|
|
3681
|
-
await runtimeDriver?.dispose?.();
|
|
3682
|
-
}
|
|
3683
|
-
},
|
|
3684
|
-
async preflight() {
|
|
3685
|
-
dispatch({ type: "preflight_start" });
|
|
3686
|
-
if (!report.selected) {
|
|
3687
|
-
dispatch({ type: "preflight_error", error: report.error ?? "No runtime selected" });
|
|
3688
|
-
} else {
|
|
3689
|
-
dispatch({ type: "preflight_ok" });
|
|
3690
|
-
}
|
|
3691
|
-
const capItem = { type: "runtime_capability_report", report };
|
|
3692
|
-
const capEnvelope = {
|
|
3693
|
-
sessionId: resolvedSessionId,
|
|
3694
|
-
provider: "flitro",
|
|
3695
|
-
seq: timeline.length + 1,
|
|
3696
|
-
epoch,
|
|
3697
|
-
timestamp: now(),
|
|
3698
|
-
item: capItem
|
|
3699
|
-
};
|
|
3700
|
-
appendEnvelope(capEnvelope);
|
|
3701
|
-
return report;
|
|
3702
|
-
},
|
|
3703
|
-
async fetchTimeline(request = {}) {
|
|
3704
|
-
if (request.cursor?.epoch === epoch || !request.cursor) {
|
|
3705
|
-
const localResult = fetchTimeline(timeline, request);
|
|
3706
|
-
if (localResult.items.length > 0) return localResult;
|
|
3707
|
-
}
|
|
3708
|
-
if (resolvedSessionId) {
|
|
3709
|
-
try {
|
|
3710
|
-
const afterSeq = request.cursor?.afterSeq ?? 0;
|
|
3711
|
-
const result = await client.fetchTimeline(resolvedSessionId, afterSeq, request.limit);
|
|
3712
|
-
return {
|
|
3713
|
-
items: result.items,
|
|
3714
|
-
nextCursor: createTimelineCursor({
|
|
3715
|
-
epoch: result.nextCursor.epoch,
|
|
3716
|
-
afterSeq: result.nextCursor.afterSeq
|
|
3717
|
-
}),
|
|
3718
|
-
hasGap: result.hasGap
|
|
3719
|
-
};
|
|
3720
|
-
} catch {
|
|
3721
|
-
}
|
|
3722
|
-
}
|
|
3723
|
-
return {
|
|
3724
|
-
items: [],
|
|
3725
|
-
nextCursor: createTimelineCursor({ epoch, afterSeq: 0 }),
|
|
3726
|
-
hasGap: false
|
|
3727
|
-
};
|
|
3728
|
-
},
|
|
3729
|
-
getState() {
|
|
3730
|
-
return state;
|
|
3731
|
-
}
|
|
3732
|
-
};
|
|
3733
|
-
}
|
|
3734
|
-
var PushTimelineStream3 = class {
|
|
3735
|
-
listeners = /* @__PURE__ */ new Set();
|
|
3736
|
-
connect(onEvent, onError, onClose) {
|
|
3737
|
-
this.listeners.add({ onEvent, onError, onClose });
|
|
3738
|
-
}
|
|
3739
|
-
disconnect() {
|
|
3740
|
-
for (const l of this.listeners) l.onClose?.();
|
|
3741
|
-
this.listeners.clear();
|
|
3742
|
-
}
|
|
3743
|
-
isConnected() {
|
|
3744
|
-
return this.listeners.size > 0;
|
|
3745
|
-
}
|
|
3746
|
-
emit(event) {
|
|
3747
|
-
for (const l of this.listeners) l.onEvent(event);
|
|
3748
|
-
}
|
|
3749
|
-
};
|
|
3750
|
-
|
|
3751
|
-
// ../packages/runtime-factory/dist/index.js
|
|
3752
|
-
function createHostAgentRuntime(options) {
|
|
3753
|
-
const sourceRuntime = options.sourceRuntime ? createSourceRuntimeProviderOptions(options.sourceRuntime) : void 0;
|
|
3754
|
-
const extensions = mergeExtensions(options.extensions, options.policy, sourceRuntime);
|
|
3755
|
-
if (options.provider === "claude") {
|
|
3756
|
-
return {
|
|
3757
|
-
runtime: createClaudeProviderRuntime({
|
|
3758
|
-
cwd: options.cwd,
|
|
3759
|
-
sessionId: options.sessionId,
|
|
3760
|
-
epoch: options.epoch,
|
|
3761
|
-
now: options.now,
|
|
3762
|
-
candidates: options.candidates,
|
|
3763
|
-
auth: options.auth,
|
|
3764
|
-
allowFallback: options.allowFallback,
|
|
3765
|
-
extensions,
|
|
3766
|
-
sourceTools: sourceRuntime?.sourceTools,
|
|
3767
|
-
model: options.claude?.model ?? options.model,
|
|
3768
|
-
reasoningEffort: options.claude?.reasoningEffort ?? options.reasoningEffort,
|
|
3769
|
-
env: options.claude?.env,
|
|
3770
|
-
permissionMode: options.claude?.permissionMode,
|
|
3771
|
-
query: options.claude?.query,
|
|
3772
|
-
loadSdk: options.claude?.loadSdk
|
|
3773
|
-
}),
|
|
3774
|
-
sourceRuntime
|
|
3775
|
-
};
|
|
3776
|
-
}
|
|
3777
|
-
if (options.provider === "flitro") {
|
|
3778
|
-
if (!options.flitro?.server) {
|
|
3779
|
-
throw new Error('createHostAgentRuntime: flitro.server is required for provider="flitro"');
|
|
3780
|
-
}
|
|
3781
|
-
return {
|
|
3782
|
-
runtime: createFlitroProviderRuntime({
|
|
3783
|
-
server: options.flitro.server,
|
|
3784
|
-
sessionId: options.sessionId,
|
|
3785
|
-
epoch: options.epoch,
|
|
3786
|
-
now: options.now,
|
|
3787
|
-
candidates: options.candidates,
|
|
3788
|
-
auth: options.auth,
|
|
3789
|
-
allowFallback: options.allowFallback,
|
|
3790
|
-
extensions,
|
|
3791
|
-
model: options.flitro.model ?? options.model,
|
|
3792
|
-
skillNames: options.flitro.skillNames,
|
|
3793
|
-
mcpServerNames: options.flitro.mcpServerNames,
|
|
3794
|
-
approvalPolicy: options.flitro.approvalPolicy
|
|
3795
|
-
}),
|
|
3796
|
-
sourceRuntime
|
|
3797
|
-
};
|
|
3798
|
-
}
|
|
3799
|
-
return {
|
|
3800
|
-
runtime: createCodexProviderRuntime({
|
|
3801
|
-
cwd: options.cwd,
|
|
3802
|
-
sessionId: options.sessionId,
|
|
3803
|
-
epoch: options.epoch,
|
|
3804
|
-
now: options.now,
|
|
3805
|
-
candidates: options.candidates,
|
|
3806
|
-
auth: options.auth,
|
|
3807
|
-
allowFallback: options.allowFallback,
|
|
3808
|
-
extensions,
|
|
3809
|
-
sourceTools: sourceRuntime?.sourceTools,
|
|
3810
|
-
appServerClient: options.codex?.appServerClient,
|
|
3811
|
-
createAppServerClient: options.codex?.createAppServerClient,
|
|
3812
|
-
model: options.codex?.model ?? options.model,
|
|
3813
|
-
reasoningEffort: options.codex?.reasoningEffort ?? options.reasoningEffort,
|
|
3814
|
-
permissionMode: options.codex?.permissionMode,
|
|
3815
|
-
approvalPolicy: options.codex?.approvalPolicy,
|
|
3816
|
-
approvalsReviewer: options.codex?.approvalsReviewer,
|
|
3817
|
-
sandbox: options.codex?.sandbox,
|
|
3818
|
-
appServerSubprocess: options.codex?.appServerSubprocess
|
|
3819
|
-
}),
|
|
3820
|
-
sourceRuntime
|
|
3821
|
-
};
|
|
3822
|
-
}
|
|
3823
|
-
function createHostRuntimeController(options) {
|
|
3824
|
-
const { runtime, sourceRuntime } = options;
|
|
3825
|
-
const hostTimeline = [];
|
|
3826
|
-
const hostTimelineEpoch = options.hostTimelineEpoch ?? `host:${runtime.sessionId}`;
|
|
3827
|
-
const hostTimelineSequencer = createTimelineSequencer({
|
|
3828
|
-
sessionId: runtime.sessionId,
|
|
3829
|
-
provider: "host",
|
|
3830
|
-
epoch: hostTimelineEpoch,
|
|
3831
|
-
now: options.now
|
|
3832
|
-
});
|
|
3833
|
-
let sendCount = 0;
|
|
3834
|
-
return {
|
|
3835
|
-
sessionId: runtime.sessionId,
|
|
3836
|
-
provider: runtime.provider,
|
|
3837
|
-
runtimeKind: runtime.runtimeKind,
|
|
3838
|
-
async preflight() {
|
|
3839
|
-
const capabilityReport = await runtime.preflight();
|
|
3840
|
-
return {
|
|
3841
|
-
sessionId: runtime.sessionId,
|
|
3842
|
-
provider: runtime.provider,
|
|
3843
|
-
runtimeKind: runtime.runtimeKind,
|
|
3844
|
-
state: runtime.getState(),
|
|
3845
|
-
capabilityReport,
|
|
3846
|
-
...sourceRuntime ? { sourceRuntime } : {}
|
|
3847
|
-
};
|
|
3848
|
-
},
|
|
3849
|
-
async sendMessage(request) {
|
|
3850
|
-
await runtime.commands.sendMessage(request.message, {
|
|
3851
|
-
...request.turnId ? { turnId: request.turnId } : {},
|
|
3852
|
-
...request.model ? { model: request.model } : {},
|
|
3853
|
-
...request.reasoningEffort ? { reasoningEffort: request.reasoningEffort } : {},
|
|
3854
|
-
...request.permissionMode ? { permissionMode: request.permissionMode } : {},
|
|
3855
|
-
...request.commandOrigin ? { commandOrigin: request.commandOrigin } : {}
|
|
3856
|
-
});
|
|
3857
|
-
sendCount += 1;
|
|
3858
|
-
return { ok: true, commandId: `send:${sendCount}` };
|
|
3859
|
-
},
|
|
3860
|
-
async abort(reason) {
|
|
3861
|
-
await runtime.commands.abort(reason);
|
|
3862
|
-
return { ok: true, commandId: "abort" };
|
|
3863
|
-
},
|
|
3864
|
-
async respondToPermission(request) {
|
|
3865
|
-
await runtime.commands.respondToPermission(request.requestId, request.allowed, request.remember);
|
|
3866
|
-
return { ok: true, commandId: `permission:${request.requestId}` };
|
|
3867
|
-
},
|
|
3868
|
-
invokeSessionTool(request) {
|
|
3869
|
-
const bridge = options.sessionTools ?? {};
|
|
3870
|
-
return invokeSessionTool({
|
|
3871
|
-
sessionId: runtime.sessionId,
|
|
3872
|
-
toolName: request.toolName,
|
|
3873
|
-
request: request.request,
|
|
3874
|
-
bridge,
|
|
3875
|
-
policy: options.hostToolPolicy,
|
|
3876
|
-
commandOrigin: request.commandOrigin,
|
|
3877
|
-
appendTimeline(item) {
|
|
3878
|
-
const envelope = hostTimelineSequencer.append(item);
|
|
3879
|
-
hostTimeline.push(envelope);
|
|
3880
|
-
return envelope;
|
|
3881
|
-
}
|
|
3882
|
-
});
|
|
3883
|
-
},
|
|
3884
|
-
async fetchTimeline(request = {}) {
|
|
3885
|
-
if (request.cursor?.epoch === hostTimelineEpoch) {
|
|
3886
|
-
return fetchTimeline(hostTimeline, request);
|
|
3887
|
-
}
|
|
3888
|
-
return runtime.fetchTimeline(request);
|
|
3889
|
-
},
|
|
3890
|
-
subscribe(onEvent, onError, onClose) {
|
|
3891
|
-
runtime.events.connect(onEvent, onError, onClose);
|
|
3892
|
-
return () => runtime.events.disconnect();
|
|
3893
|
-
},
|
|
3894
|
-
async dispose() {
|
|
3895
|
-
await runtime.commands.dispose();
|
|
3896
|
-
return { ok: true, commandId: "dispose" };
|
|
3897
|
-
}
|
|
3898
|
-
};
|
|
3899
|
-
}
|
|
3900
|
-
function mergeExtensions(base, policy, sourceRuntime) {
|
|
3901
|
-
return {
|
|
3902
|
-
...base,
|
|
3903
|
-
...policy ? { policy } : {},
|
|
3904
|
-
sources: sourceRuntime?.extensions.sources ?? base?.sources
|
|
3905
|
-
};
|
|
3906
|
-
}
|
|
3907
|
-
|
|
3908
|
-
// ../packages/host-services/dist/index.js
|
|
3909
|
-
import { createHash } from "crypto";
|
|
3910
|
-
import { createHash as createHash2 } from "crypto";
|
|
3911
|
-
import { createHash as createHash3 } from "crypto";
|
|
3912
|
-
import { watch, existsSync, readFileSync as readFileSync2 } from "fs";
|
|
3913
|
-
import { createHash as createHash4 } from "crypto";
|
|
3914
|
-
var DEFAULT_APPROVAL_TTL_MS = 12e4;
|
|
3915
|
-
function createPrivilegedExecutionBroker(options = {}) {
|
|
3916
|
-
const pending = /* @__PURE__ */ new Map();
|
|
3917
|
-
const now = options.now ?? Date.now;
|
|
3918
|
-
const audit = options.audit ?? (() => void 0);
|
|
3919
|
-
function auditEvent(event, timestamp = now()) {
|
|
3920
|
-
audit({ ...event, timestamp });
|
|
3921
|
-
}
|
|
3922
|
-
return {
|
|
3923
|
-
createRequest(input) {
|
|
3924
|
-
const timestamp = now();
|
|
3925
|
-
const approvalTtlMs = input.approvalTtlMs ?? options.approvalTtlMs ?? DEFAULT_APPROVAL_TTL_MS;
|
|
3926
|
-
const policy = validatePrivilegedCommand(input.command);
|
|
3927
|
-
const request = {
|
|
3928
|
-
requestId: input.requestId,
|
|
3929
|
-
sessionId: input.sessionId,
|
|
3930
|
-
command: input.command,
|
|
3931
|
-
commandHash: hashCommand(input.command),
|
|
3932
|
-
reason: input.reason,
|
|
3933
|
-
impact: input.impact,
|
|
3934
|
-
approvalTtlMs,
|
|
3935
|
-
createdAt: timestamp,
|
|
3936
|
-
expiresAt: timestamp + approvalTtlMs,
|
|
3937
|
-
policyAllowed: policy.allowed,
|
|
3938
|
-
policyReason: policy.reason
|
|
3939
|
-
};
|
|
3940
|
-
pending.set(input.requestId, request);
|
|
3941
|
-
auditEvent({
|
|
3942
|
-
event: "privileged_request_created",
|
|
3943
|
-
requestId: request.requestId,
|
|
3944
|
-
sessionId: request.sessionId,
|
|
3945
|
-
commandHash: request.commandHash,
|
|
3946
|
-
policyAllowed: request.policyAllowed,
|
|
3947
|
-
policyReason: request.policyReason
|
|
3948
|
-
}, timestamp);
|
|
3949
|
-
return request;
|
|
3950
|
-
},
|
|
3951
|
-
resolveApproval(requestId, approved, resolveOptions = {}) {
|
|
3952
|
-
const timestamp = resolveOptions.now ?? now();
|
|
3953
|
-
const request = pending.get(requestId);
|
|
3954
|
-
if (!request) return { ok: false, reason: "not_found" };
|
|
3955
|
-
pending.delete(requestId);
|
|
3956
|
-
if (resolveOptions.expectedCommandHash && resolveOptions.expectedCommandHash !== request.commandHash) {
|
|
3957
|
-
auditEvent({
|
|
3958
|
-
event: "privileged_request_hash_mismatch",
|
|
3959
|
-
requestId: request.requestId,
|
|
3960
|
-
sessionId: request.sessionId,
|
|
3961
|
-
commandHash: request.commandHash
|
|
3962
|
-
}, timestamp);
|
|
3963
|
-
return { ok: false, reason: "command_hash_mismatch" };
|
|
3964
|
-
}
|
|
3965
|
-
if (!request.policyAllowed) {
|
|
3966
|
-
auditEvent({
|
|
3967
|
-
event: "privileged_request_blocked_by_policy",
|
|
3968
|
-
requestId: request.requestId,
|
|
3969
|
-
sessionId: request.sessionId,
|
|
3970
|
-
commandHash: request.commandHash,
|
|
3971
|
-
policyAllowed: false,
|
|
3972
|
-
policyReason: request.policyReason
|
|
3973
|
-
}, timestamp);
|
|
3974
|
-
return { ok: false, reason: "blocked_by_policy" };
|
|
3975
|
-
}
|
|
3976
|
-
if (timestamp > request.expiresAt) {
|
|
3977
|
-
auditEvent({
|
|
3978
|
-
event: "privileged_request_expired",
|
|
3979
|
-
requestId: request.requestId,
|
|
3980
|
-
sessionId: request.sessionId,
|
|
3981
|
-
commandHash: request.commandHash
|
|
3982
|
-
}, timestamp);
|
|
3983
|
-
return { ok: false, reason: "expired" };
|
|
3984
|
-
}
|
|
3985
|
-
auditEvent({
|
|
3986
|
-
event: approved ? "privileged_request_approved" : "privileged_request_denied",
|
|
3987
|
-
requestId: request.requestId,
|
|
3988
|
-
sessionId: request.sessionId,
|
|
3989
|
-
commandHash: request.commandHash
|
|
3990
|
-
}, timestamp);
|
|
3991
|
-
return approved ? { ok: true, request } : { ok: false, reason: "blocked_by_policy" };
|
|
3992
|
-
}
|
|
3993
|
-
};
|
|
3994
|
-
}
|
|
3995
|
-
function hashCommand(command) {
|
|
3996
|
-
return createHash("sha256").update(command, "utf8").digest("hex");
|
|
3997
|
-
}
|
|
3998
|
-
function validatePrivilegedCommand(command) {
|
|
3999
|
-
const normalized = command.trim().toLowerCase();
|
|
4000
|
-
const allowed = /^brew\s+install\s+--cask\s+\S+/.test(normalized) || /^brew\s+upgrade\s+--cask\s+\S+/.test(normalized) || /^installer\s+-pkg\s+\S+.*\s+-target\s+\//.test(normalized);
|
|
4001
|
-
return allowed ? { allowed: true } : {
|
|
4002
|
-
allowed: false,
|
|
4003
|
-
reason: "Privileged execution policy only allows brew cask install/upgrade and installer -pkg -target / commands"
|
|
4004
|
-
};
|
|
4005
|
-
}
|
|
4006
|
-
function stableHash(value) {
|
|
4007
|
-
return createHash2("sha256").update(stableStringify(value), "utf8").digest("hex").slice(0, 16);
|
|
4008
|
-
}
|
|
4009
|
-
function stableStringify(value) {
|
|
4010
|
-
if (Array.isArray(value)) {
|
|
4011
|
-
return `[${value.map(stableStringify).join(",")}]`;
|
|
4012
|
-
}
|
|
4013
|
-
if (value && typeof value === "object") {
|
|
4014
|
-
const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).sort(([left], [right]) => left.localeCompare(right));
|
|
4015
|
-
return `{${entries.map(([key, entryValue]) => `${JSON.stringify(key)}:${stableStringify(entryValue)}`).join(",")}}`;
|
|
4016
|
-
}
|
|
4017
|
-
return JSON.stringify(value);
|
|
4018
|
-
}
|
|
4019
|
-
function redactSecrets(text) {
|
|
4020
|
-
const patterns = [
|
|
4021
|
-
/(authorization\s*[:=]\s*)(bearer\s+)?[^\s,;]+/gi,
|
|
4022
|
-
/(token\s*[:=]\s*)[^\s,;]+/gi,
|
|
4023
|
-
/(api[_-]?key\s*[:=]\s*)[^\s,;]+/gi,
|
|
4024
|
-
/(secret\s*[:=]\s*)[^\s,;]+/gi
|
|
4025
|
-
];
|
|
4026
|
-
let redacted = false;
|
|
4027
|
-
let output2 = text;
|
|
4028
|
-
for (const pattern of patterns) {
|
|
4029
|
-
output2 = output2.replace(pattern, (_match, ...args) => {
|
|
4030
|
-
const prefix = typeof args[0] === "string" ? args[0] : "";
|
|
4031
|
-
const optionalPrefix = typeof args[1] === "string" ? args[1] : "";
|
|
4032
|
-
redacted = true;
|
|
4033
|
-
return `${prefix}${optionalPrefix}[REDACTED]`;
|
|
4034
|
-
});
|
|
4035
|
-
}
|
|
4036
|
-
return { text: output2, redacted };
|
|
4037
|
-
}
|
|
4038
|
-
function cloneCommandOrigin(origin) {
|
|
4039
|
-
return { ...origin };
|
|
4040
|
-
}
|
|
4041
|
-
var DEFAULT_MAX_INLINE_BYTES = 60 * 1024;
|
|
4042
|
-
function createToolOutputPolicy(options = {}) {
|
|
4043
|
-
const maxInlineBytes = options.maxInlineBytes ?? DEFAULT_MAX_INLINE_BYTES;
|
|
4044
|
-
return {
|
|
4045
|
-
process(input) {
|
|
4046
|
-
const redaction = redactSecrets(input.text);
|
|
4047
|
-
const byteLength = Buffer.byteLength(redaction.text, "utf8");
|
|
4048
|
-
const shouldInline = byteLength <= maxInlineBytes;
|
|
4049
|
-
if (shouldInline) {
|
|
4050
|
-
return {
|
|
4051
|
-
callId: input.callId,
|
|
4052
|
-
toolName: input.toolName,
|
|
4053
|
-
action: "inline",
|
|
4054
|
-
byteLength,
|
|
4055
|
-
redacted: redaction.redacted,
|
|
4056
|
-
truncated: false,
|
|
4057
|
-
inlineText: redaction.text,
|
|
4058
|
-
...input.intent ? { intent: input.intent } : {}
|
|
4059
|
-
};
|
|
4060
|
-
}
|
|
4061
|
-
const truncated = truncateByBytes(redaction.text, maxInlineBytes);
|
|
4062
|
-
const source = { ...input, text: redaction.text };
|
|
4063
|
-
const summary = options.summarizer ? options.summarizer({ text: truncated, source }) : truncated;
|
|
4064
|
-
return {
|
|
4065
|
-
callId: input.callId,
|
|
4066
|
-
toolName: input.toolName,
|
|
4067
|
-
action: "artifact",
|
|
4068
|
-
byteLength,
|
|
4069
|
-
redacted: redaction.redacted,
|
|
4070
|
-
truncated: true,
|
|
4071
|
-
summary,
|
|
4072
|
-
artifactRef: options.artifactRef?.(input) ?? `tool-output://${input.callId}`,
|
|
4073
|
-
...input.intent ? { intent: input.intent } : {}
|
|
4074
|
-
};
|
|
4075
|
-
}
|
|
4076
|
-
};
|
|
4077
|
-
}
|
|
4078
|
-
function truncateByBytes(text, maxBytes) {
|
|
4079
|
-
const buffer = Buffer.from(text, "utf8");
|
|
4080
|
-
if (buffer.byteLength <= maxBytes) return text;
|
|
4081
|
-
return buffer.subarray(0, maxBytes).toString("utf8");
|
|
4082
|
-
}
|
|
4083
|
-
function createSourceAuthoringPlan(input) {
|
|
4084
|
-
const slug = slugify(input.serviceName);
|
|
4085
|
-
const fallbackDecision = decideSourceBrowserFallback(input);
|
|
4086
|
-
return {
|
|
4087
|
-
recommendedPath: fallbackDecision ? "browser-fallback" : "source",
|
|
4088
|
-
workspaceId: input.workspaceId,
|
|
4089
|
-
...input.purpose ? { purpose: input.purpose } : {},
|
|
4090
|
-
source: {
|
|
4091
|
-
id: `${slug}_planned`,
|
|
4092
|
-
slug,
|
|
4093
|
-
name: input.serviceName.trim(),
|
|
4094
|
-
provider: slug
|
|
4095
|
-
},
|
|
4096
|
-
validationSteps: [
|
|
4097
|
-
"validate_config",
|
|
4098
|
-
"test_connection",
|
|
4099
|
-
"verify_auth",
|
|
4100
|
-
"write_guide",
|
|
4101
|
-
"seed_read_only_policy"
|
|
4102
|
-
],
|
|
4103
|
-
...fallbackDecision ? { browserFallbackDecision: fallbackDecision } : {}
|
|
4104
|
-
};
|
|
4105
|
-
}
|
|
4106
|
-
function decideSourceBrowserFallback(input) {
|
|
4107
|
-
if (input.uiOnly || input.repeatable === false) {
|
|
4108
|
-
return {
|
|
4109
|
-
reason: "one_off_or_ui_only",
|
|
4110
|
-
reusableSourceLater: true,
|
|
4111
|
-
detail: "Prefer browser fallback for one-off or UI-only work; create a source later when the workflow becomes repeatable."
|
|
4112
|
-
};
|
|
4113
|
-
}
|
|
4114
|
-
if (input.authReliability === "brittle") {
|
|
4115
|
-
return {
|
|
4116
|
-
reason: "brittle_auth",
|
|
4117
|
-
reusableSourceLater: true,
|
|
4118
|
-
detail: "Prefer browser fallback until source authentication is reliable enough to reuse."
|
|
4119
|
-
};
|
|
4120
|
-
}
|
|
4121
|
-
if (input.structuredAccess === false) {
|
|
4122
|
-
return {
|
|
4123
|
-
reason: "not_structured",
|
|
4124
|
-
reusableSourceLater: true,
|
|
4125
|
-
detail: "Prefer browser fallback because the task does not need reusable structured access."
|
|
4126
|
-
};
|
|
4127
|
-
}
|
|
4128
|
-
return void 0;
|
|
4129
|
-
}
|
|
4130
|
-
function slugify(value) {
|
|
4131
|
-
const slug = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
4132
|
-
return slug || "source";
|
|
4133
|
-
}
|
|
4134
|
-
function expandWebhookTemplate(input) {
|
|
4135
|
-
const variables = input.variables ?? {};
|
|
4136
|
-
const secrets = input.secrets ?? {};
|
|
4137
|
-
const receipts = /* @__PURE__ */ new Map();
|
|
4138
|
-
const missing = /* @__PURE__ */ new Set();
|
|
4139
|
-
const expanded = input.template.replace(/\$\{([A-Z0-9_]+)\}|\$([A-Z0-9_]+)/g, (match, braced, bare) => {
|
|
4140
|
-
const name = braced ?? bare;
|
|
4141
|
-
if (!name) return match;
|
|
4142
|
-
if (Object.prototype.hasOwnProperty.call(secrets, name) && secrets[name] !== void 0) {
|
|
4143
|
-
receipts.set(name, { name, found: true, source: "secret", redacted: true });
|
|
4144
|
-
return String(secrets[name]);
|
|
4145
|
-
}
|
|
4146
|
-
if (Object.prototype.hasOwnProperty.call(variables, name) && variables[name] !== void 0) {
|
|
4147
|
-
receipts.set(name, { name, found: true, source: "variable", redacted: false });
|
|
4148
|
-
return String(variables[name]);
|
|
4149
|
-
}
|
|
4150
|
-
missing.add(name);
|
|
4151
|
-
receipts.set(name, { name, found: false, redacted: false });
|
|
4152
|
-
return match;
|
|
4153
|
-
});
|
|
4154
|
-
let redactedPreview = expanded;
|
|
4155
|
-
for (const [name, value] of Object.entries(secrets)) {
|
|
4156
|
-
if (!value) continue;
|
|
4157
|
-
redactedPreview = redactedPreview.split(value).join("[REDACTED]");
|
|
4158
|
-
if (!receipts.has(name)) {
|
|
4159
|
-
receipts.set(name, { name, found: false, source: "secret", redacted: true });
|
|
4160
|
-
}
|
|
4161
|
-
}
|
|
4162
|
-
return {
|
|
4163
|
-
expanded,
|
|
4164
|
-
redactedPreview,
|
|
4165
|
-
missingVariables: [...missing].sort(),
|
|
4166
|
-
variables: [...receipts.values()].sort((left, right) => left.name.localeCompare(right.name))
|
|
4167
|
-
};
|
|
4168
|
-
}
|
|
4169
|
-
function createMessagingAccessPolicy(options = {}) {
|
|
4170
|
-
const owners = new Set(options.owners ?? []);
|
|
4171
|
-
const allowedSenders = new Set(options.allowedSenders ?? []);
|
|
4172
|
-
const pendingSenders = new Set(options.pendingSenders ?? []);
|
|
4173
|
-
return {
|
|
4174
|
-
authorize(request) {
|
|
4175
|
-
const commandOrigin = {
|
|
4176
|
-
type: "remote",
|
|
4177
|
-
channel: request.channel,
|
|
4178
|
-
actorId: request.actorId
|
|
4179
|
-
};
|
|
4180
|
-
if (owners.has(request.actorId)) {
|
|
4181
|
-
return { decision: "allow", reason: "owner", commandOrigin };
|
|
4182
|
-
}
|
|
4183
|
-
if (allowedSenders.has(request.actorId)) {
|
|
4184
|
-
return { decision: "allow", reason: "sender_allowed", commandOrigin };
|
|
4185
|
-
}
|
|
4186
|
-
if (pendingSenders.has(request.actorId)) {
|
|
4187
|
-
return { decision: "ask", reason: "sender_pending_approval", commandOrigin };
|
|
4188
|
-
}
|
|
4189
|
-
return { decision: "deny", reason: "sender_not_allowed", commandOrigin };
|
|
4190
|
-
}
|
|
4191
|
-
};
|
|
4192
|
-
}
|
|
4193
|
-
function createInMemoryAuditStore(options = {}) {
|
|
4194
|
-
const now = options.now ?? Date.now;
|
|
4195
|
-
const entries = [];
|
|
4196
|
-
return {
|
|
4197
|
-
async append(input) {
|
|
4198
|
-
const seq = entries.length + 1;
|
|
4199
|
-
const entry = {
|
|
4200
|
-
...cloneAuditEntryInput(input),
|
|
4201
|
-
auditId: `audit:${seq}`,
|
|
4202
|
-
seq,
|
|
4203
|
-
timestamp: now()
|
|
4204
|
-
};
|
|
4205
|
-
entries.push(entry);
|
|
4206
|
-
return {
|
|
4207
|
-
auditId: entry.auditId,
|
|
4208
|
-
seq: entry.seq,
|
|
4209
|
-
timestamp: entry.timestamp,
|
|
4210
|
-
entry: cloneAuditEntry(entry)
|
|
4211
|
-
};
|
|
4212
|
-
},
|
|
4213
|
-
async list(filter = {}) {
|
|
4214
|
-
return filterAuditEntries(entries, filter).map(cloneAuditEntry);
|
|
4215
|
-
},
|
|
4216
|
-
getSnapshot() {
|
|
4217
|
-
return { entries: entries.map(cloneAuditEntry) };
|
|
4218
|
-
}
|
|
4219
|
-
};
|
|
4220
|
-
}
|
|
4221
|
-
function cloneAuditEntryInput(input) {
|
|
4222
|
-
return {
|
|
4223
|
-
...input,
|
|
4224
|
-
...input.actor ? { actor: { ...input.actor } } : {},
|
|
4225
|
-
...input.metadata ? { metadata: { ...input.metadata } } : {}
|
|
4226
|
-
};
|
|
4227
|
-
}
|
|
4228
|
-
function cloneAuditEntry(entry) {
|
|
4229
|
-
return {
|
|
4230
|
-
...entry,
|
|
4231
|
-
...entry.actor ? { actor: { ...entry.actor } } : {},
|
|
4232
|
-
...entry.metadata ? { metadata: { ...entry.metadata } } : {}
|
|
4233
|
-
};
|
|
4234
|
-
}
|
|
4235
|
-
function filterAuditEntries(entries, filter) {
|
|
4236
|
-
return entries.filter((entry) => !filter.sessionId || entry.sessionId === filter.sessionId).filter((entry) => !filter.category || entry.category === filter.category).slice(0, filter.limit ?? Number.POSITIVE_INFINITY);
|
|
4237
|
-
}
|
|
4238
|
-
function createInMemoryArtifactStore(options = {}) {
|
|
4239
|
-
const now = options.now ?? Date.now;
|
|
4240
|
-
const artifacts = /* @__PURE__ */ new Map();
|
|
4241
|
-
return {
|
|
4242
|
-
async write(input) {
|
|
4243
|
-
const redaction = redactSecrets(input.text);
|
|
4244
|
-
const record = {
|
|
4245
|
-
artifactRef: input.artifactRef,
|
|
4246
|
-
...input.sessionId ? { sessionId: input.sessionId } : {},
|
|
4247
|
-
toolName: input.toolName,
|
|
4248
|
-
contentType: input.contentType,
|
|
4249
|
-
text: redaction.text,
|
|
4250
|
-
byteLength: Buffer.byteLength(redaction.text, "utf8"),
|
|
4251
|
-
redacted: redaction.redacted,
|
|
4252
|
-
createdAt: now(),
|
|
4253
|
-
...input.metadata ? { metadata: { ...input.metadata } } : {}
|
|
4254
|
-
};
|
|
4255
|
-
artifacts.set(record.artifactRef, record);
|
|
4256
|
-
return artifactReceipt(record);
|
|
4257
|
-
},
|
|
4258
|
-
async read(artifactRef) {
|
|
4259
|
-
const record = artifacts.get(artifactRef);
|
|
4260
|
-
return record ? cloneArtifactRecord(record) : void 0;
|
|
4261
|
-
},
|
|
4262
|
-
async list(filter = {}) {
|
|
4263
|
-
return filterArtifactRecords([...artifacts.values()], filter).map(artifactReceipt);
|
|
4264
|
-
},
|
|
4265
|
-
getSnapshot() {
|
|
4266
|
-
return {
|
|
4267
|
-
artifacts: [...artifacts.values()].map(cloneArtifactRecord)
|
|
4268
|
-
};
|
|
4269
|
-
}
|
|
4270
|
-
};
|
|
4271
|
-
}
|
|
4272
|
-
function filterArtifactRecords(records, filter) {
|
|
4273
|
-
return records.filter((record) => !filter.sessionId || record.sessionId === filter.sessionId).filter((record) => !filter.toolName || record.toolName === filter.toolName).slice(0, filter.limit ?? Number.POSITIVE_INFINITY);
|
|
4274
|
-
}
|
|
4275
|
-
function artifactReceipt(record) {
|
|
4276
|
-
return {
|
|
4277
|
-
artifactRef: record.artifactRef,
|
|
4278
|
-
...record.sessionId ? { sessionId: record.sessionId } : {},
|
|
4279
|
-
toolName: record.toolName,
|
|
4280
|
-
contentType: record.contentType,
|
|
4281
|
-
byteLength: record.byteLength,
|
|
4282
|
-
redacted: record.redacted,
|
|
4283
|
-
createdAt: record.createdAt,
|
|
4284
|
-
...record.metadata ? { metadata: { ...record.metadata } } : {}
|
|
4285
|
-
};
|
|
4286
|
-
}
|
|
4287
|
-
function cloneArtifactRecord(record) {
|
|
4288
|
-
return {
|
|
4289
|
-
...record,
|
|
4290
|
-
...record.metadata ? { metadata: { ...record.metadata } } : {}
|
|
4291
|
-
};
|
|
4292
|
-
}
|
|
4293
|
-
function createInMemoryNotificationHost(options = {}) {
|
|
4294
|
-
const now = options.now ?? Date.now;
|
|
4295
|
-
const notifications = [];
|
|
4296
|
-
let counter = 0;
|
|
4297
|
-
return {
|
|
4298
|
-
async notify(request) {
|
|
4299
|
-
const redaction = redactSecrets(request.body ?? "");
|
|
4300
|
-
const receipt = {
|
|
4301
|
-
notificationId: `notification:${++counter}`,
|
|
4302
|
-
...request.sessionId ? { sessionId: request.sessionId } : {},
|
|
4303
|
-
...request.topicId ? { topicId: request.topicId } : {},
|
|
4304
|
-
deliveredChannels: [...new Set(request.channels)],
|
|
4305
|
-
title: request.title,
|
|
4306
|
-
...request.body !== void 0 ? { bodyPreview: redaction.text } : {},
|
|
4307
|
-
severity: request.severity ?? "info",
|
|
4308
|
-
redacted: redaction.redacted,
|
|
4309
|
-
timestamp: now(),
|
|
4310
|
-
...request.origin ? { origin: cloneCommandOrigin(request.origin) } : {}
|
|
4311
|
-
};
|
|
4312
|
-
notifications.push(receipt);
|
|
4313
|
-
return cloneNotificationReceipt(receipt);
|
|
4314
|
-
},
|
|
4315
|
-
getSnapshot() {
|
|
4316
|
-
return { notifications: notifications.map(cloneNotificationReceipt) };
|
|
4317
|
-
}
|
|
4318
|
-
};
|
|
4319
|
-
}
|
|
4320
|
-
function cloneNotificationReceipt(receipt) {
|
|
4321
|
-
return {
|
|
4322
|
-
...receipt,
|
|
4323
|
-
deliveredChannels: [...receipt.deliveredChannels],
|
|
4324
|
-
...receipt.origin ? { origin: cloneCommandOrigin(receipt.origin) } : {}
|
|
4325
|
-
};
|
|
4326
|
-
}
|
|
4327
|
-
function createMessagingBindingStore(options = {}) {
|
|
4328
|
-
const now = options.now ?? Date.now;
|
|
4329
|
-
const bindings = /* @__PURE__ */ new Map();
|
|
4330
|
-
return {
|
|
4331
|
-
async bindTopic(input) {
|
|
4332
|
-
const timestamp = now();
|
|
4333
|
-
const existing = bindings.get(input.bindingId);
|
|
4334
|
-
const binding = {
|
|
4335
|
-
bindingId: input.bindingId,
|
|
4336
|
-
sessionId: input.sessionId,
|
|
4337
|
-
channel: input.channel,
|
|
4338
|
-
topicId: input.topicId,
|
|
4339
|
-
...input.actorId ? { actorId: input.actorId } : existing?.actorId ? { actorId: existing.actorId } : {},
|
|
4340
|
-
createdAt: existing?.createdAt ?? timestamp,
|
|
4341
|
-
updatedAt: timestamp,
|
|
4342
|
-
...input.metadata ? { metadata: { ...input.metadata } } : existing?.metadata ? { metadata: { ...existing.metadata } } : {}
|
|
4343
|
-
};
|
|
4344
|
-
bindings.set(binding.bindingId, binding);
|
|
4345
|
-
return cloneTopicBinding(binding);
|
|
4346
|
-
},
|
|
4347
|
-
async resolveTopic(lookup) {
|
|
4348
|
-
const binding = [...bindings.values()].find((candidate) => candidate.channel === lookup.channel && candidate.topicId === lookup.topicId);
|
|
4349
|
-
return binding ? cloneTopicBinding(binding) : void 0;
|
|
4350
|
-
},
|
|
4351
|
-
async listBindings(filter = {}) {
|
|
4352
|
-
return [...bindings.values()].filter((binding) => !filter.sessionId || binding.sessionId === filter.sessionId).filter((binding) => !filter.channel || binding.channel === filter.channel).map(cloneTopicBinding);
|
|
4353
|
-
},
|
|
4354
|
-
async unbindTopic(bindingId, unbindOptions = {}) {
|
|
4355
|
-
if (!bindings.has(bindingId)) return { ok: false, bindingId, reason: "not_found" };
|
|
4356
|
-
bindings.delete(bindingId);
|
|
4357
|
-
return {
|
|
4358
|
-
ok: true,
|
|
4359
|
-
bindingId,
|
|
4360
|
-
unboundAt: now(),
|
|
4361
|
-
...unbindOptions.actorId ? { actorId: unbindOptions.actorId } : {}
|
|
4362
|
-
};
|
|
4363
|
-
},
|
|
4364
|
-
getSnapshot() {
|
|
4365
|
-
return { bindings: [...bindings.values()].map(cloneTopicBinding) };
|
|
4366
|
-
}
|
|
4367
|
-
};
|
|
4368
|
-
}
|
|
4369
|
-
function cloneTopicBinding(binding) {
|
|
4370
|
-
return {
|
|
4371
|
-
...binding,
|
|
4372
|
-
...binding.metadata ? { metadata: { ...binding.metadata } } : {}
|
|
4373
|
-
};
|
|
4374
|
-
}
|
|
4375
|
-
var DEFAULT_SECRET_ENV_KEY_PATTERN = /(token|api[_-]?key|secret|password|passwd|credential|authorization|auth)$/i;
|
|
4376
|
-
function scrubLocalMcpEnvironment(env, options = {}) {
|
|
4377
|
-
const keepKeys = new Set(options.keepKeys ?? []);
|
|
4378
|
-
const removeKeyPattern = options.removeKeyPattern ?? DEFAULT_SECRET_ENV_KEY_PATTERN;
|
|
4379
|
-
const scrubbed = {};
|
|
4380
|
-
const redactedPreview = {};
|
|
4381
|
-
const removedKeys = [];
|
|
4382
|
-
for (const [key, value] of Object.entries(env)) {
|
|
4383
|
-
if (value === void 0) continue;
|
|
4384
|
-
if (!keepKeys.has(key) && removeKeyPattern.test(key)) {
|
|
4385
|
-
removedKeys.push(key);
|
|
4386
|
-
redactedPreview[key] = "[REDACTED]";
|
|
4387
|
-
continue;
|
|
4388
|
-
}
|
|
4389
|
-
scrubbed[key] = value;
|
|
4390
|
-
redactedPreview[key] = value;
|
|
4391
|
-
}
|
|
4392
|
-
removedKeys.sort();
|
|
4393
|
-
return {
|
|
4394
|
-
env: scrubbed,
|
|
4395
|
-
removedKeys,
|
|
4396
|
-
redactedPreview
|
|
4397
|
-
};
|
|
4398
|
-
}
|
|
4399
|
-
function createConfigRecoveryReceipt(input) {
|
|
4400
|
-
const migratedAliases = [...input.migratedAliases ?? []].sort();
|
|
4401
|
-
const matcherIdsBackfilled = [...input.matcherIdsBackfilled ?? []].sort();
|
|
4402
|
-
const body = {
|
|
4403
|
-
configPath: input.configPath,
|
|
4404
|
-
configKind: input.configKind,
|
|
4405
|
-
problem: input.problem,
|
|
4406
|
-
recovery: input.recovery,
|
|
4407
|
-
defaultVersion: input.defaultVersion,
|
|
4408
|
-
userConfigHash: input.userConfigHash,
|
|
4409
|
-
migratedAliases,
|
|
4410
|
-
matcherIdsBackfilled,
|
|
4411
|
-
timestamp: input.timestamp
|
|
4412
|
-
};
|
|
4413
|
-
return {
|
|
4414
|
-
...body,
|
|
4415
|
-
receiptId: `config-recovery:${stableHash(body)}`,
|
|
4416
|
-
overwroteUserConfig: false,
|
|
4417
|
-
migratedAliases,
|
|
4418
|
-
matcherIdsBackfilled
|
|
4419
|
-
};
|
|
4420
|
-
}
|
|
4421
|
-
function createResourceBundleSnapshot(input) {
|
|
4422
|
-
const resources = [...input.resources].map((resource) => ({ ...resource })).sort((left, right) => left.path.localeCompare(right.path));
|
|
4423
|
-
const body = {
|
|
4424
|
-
workspaceId: input.workspaceId,
|
|
4425
|
-
resources,
|
|
4426
|
-
timestamp: input.timestamp
|
|
4427
|
-
};
|
|
4428
|
-
return {
|
|
4429
|
-
snapshotId: `resource-bundle:${stableHash(body)}`,
|
|
4430
|
-
workspaceId: input.workspaceId,
|
|
4431
|
-
resourceCount: resources.length,
|
|
4432
|
-
resources,
|
|
4433
|
-
...input.timestamp !== void 0 ? { timestamp: input.timestamp } : {}
|
|
4434
|
-
};
|
|
4435
|
-
}
|
|
4436
|
-
function createConfigWatchEvent(input) {
|
|
4437
|
-
const body = { ...input };
|
|
4438
|
-
return {
|
|
4439
|
-
...body,
|
|
4440
|
-
eventId: `config-watch:${stableHash(body)}`
|
|
4441
|
-
};
|
|
4442
|
-
}
|
|
4443
|
-
var RUNTIME_KIND_ORDER = ["claude", "codex", "mcp", "cli"];
|
|
4444
|
-
function createToolRegistryVersion(input) {
|
|
4445
|
-
const tools = [...input.tools].map((tool) => ({
|
|
4446
|
-
...tool,
|
|
4447
|
-
...tool.featureFlags ? { featureFlags: [...tool.featureFlags].sort() } : {},
|
|
4448
|
-
runtimeSupport: { ...tool.runtimeSupport }
|
|
4449
|
-
})).sort((left, right) => left.name.localeCompare(right.name));
|
|
4450
|
-
const unsupported = collectRuntimeSupportGaps(tools, "unsupported");
|
|
4451
|
-
const degraded = collectRuntimeSupportGaps(tools, "degraded");
|
|
4452
|
-
const body = {
|
|
4453
|
-
registryVersion: input.registryVersion,
|
|
4454
|
-
tools
|
|
4455
|
-
};
|
|
4456
|
-
return {
|
|
4457
|
-
registryId: `tool-registry:${stableHash(body)}`,
|
|
4458
|
-
registryVersion: input.registryVersion,
|
|
4459
|
-
toolCount: tools.length,
|
|
4460
|
-
tools,
|
|
4461
|
-
degraded,
|
|
4462
|
-
unsupported
|
|
4463
|
-
};
|
|
4464
|
-
}
|
|
4465
|
-
function collectRuntimeSupportGaps(tools, level) {
|
|
4466
|
-
const gaps = [];
|
|
4467
|
-
for (const tool of tools) {
|
|
4468
|
-
const runtimeKinds = [...RUNTIME_KIND_ORDER].sort();
|
|
4469
|
-
for (const runtimeKind of runtimeKinds) {
|
|
4470
|
-
if (tool.runtimeSupport[runtimeKind] === level) {
|
|
4471
|
-
gaps.push({ name: tool.name, runtimeKind });
|
|
4472
|
-
}
|
|
4473
|
-
}
|
|
4474
|
-
}
|
|
4475
|
-
return gaps;
|
|
4476
|
-
}
|
|
4477
|
-
function validateStatusConfig(config, file = "statuses.json") {
|
|
4478
|
-
const errors = [];
|
|
4479
|
-
const warnings = [];
|
|
4480
|
-
const statusIds = /* @__PURE__ */ new Set();
|
|
4481
|
-
config.statuses.forEach((status, index) => {
|
|
4482
|
-
if (!status.id) {
|
|
4483
|
-
errors.push({
|
|
4484
|
-
file,
|
|
4485
|
-
path: `statuses[${index}].id`,
|
|
4486
|
-
message: "Status id is required",
|
|
4487
|
-
severity: "error"
|
|
4488
|
-
});
|
|
4489
|
-
return;
|
|
4490
|
-
}
|
|
4491
|
-
if (statusIds.has(status.id)) {
|
|
4492
|
-
errors.push({
|
|
4493
|
-
file,
|
|
4494
|
-
path: `statuses[${index}].id`,
|
|
4495
|
-
message: "Status id must be unique",
|
|
4496
|
-
severity: "error"
|
|
4497
|
-
});
|
|
4498
|
-
return;
|
|
4499
|
-
}
|
|
4500
|
-
statusIds.add(status.id);
|
|
4501
|
-
});
|
|
4502
|
-
const initial = config.statuses.find((status) => status.id === config.initialStatus);
|
|
4503
|
-
if (!initial) {
|
|
4504
|
-
errors.push({
|
|
4505
|
-
file,
|
|
4506
|
-
path: "initialStatus",
|
|
4507
|
-
message: "Initial status is not defined",
|
|
4508
|
-
severity: "error"
|
|
4509
|
-
});
|
|
4510
|
-
} else if (initial.terminal) {
|
|
4511
|
-
errors.push({
|
|
4512
|
-
file,
|
|
4513
|
-
path: "initialStatus",
|
|
4514
|
-
message: "Initial status cannot be terminal",
|
|
4515
|
-
severity: "error"
|
|
4516
|
-
});
|
|
4517
|
-
}
|
|
4518
|
-
for (const [index, transition] of (config.allowedTransitions ?? []).entries()) {
|
|
4519
|
-
if (!statusIds.has(transition.from)) {
|
|
4520
|
-
errors.push({
|
|
4521
|
-
file,
|
|
4522
|
-
path: `allowedTransitions[${index}].from`,
|
|
4523
|
-
message: "Transition source status is not defined",
|
|
4524
|
-
severity: "error"
|
|
4525
|
-
});
|
|
4526
|
-
}
|
|
4527
|
-
if (!statusIds.has(transition.to)) {
|
|
4528
|
-
errors.push({
|
|
4529
|
-
file,
|
|
4530
|
-
path: `allowedTransitions[${index}].to`,
|
|
4531
|
-
message: "Transition target status is not defined",
|
|
4532
|
-
severity: "error"
|
|
4533
|
-
});
|
|
4534
|
-
}
|
|
4535
|
-
}
|
|
4536
|
-
if (!config.statuses.some((status) => status.terminal)) {
|
|
4537
|
-
warnings.push({
|
|
4538
|
-
file,
|
|
4539
|
-
path: "statuses",
|
|
4540
|
-
message: "Status config has no terminal status",
|
|
4541
|
-
severity: "warning",
|
|
4542
|
-
suggestion: "Add at least one terminal status such as done or archived."
|
|
4543
|
-
});
|
|
4544
|
-
}
|
|
4545
|
-
return {
|
|
4546
|
-
valid: errors.length === 0,
|
|
4547
|
-
errors,
|
|
4548
|
-
warnings
|
|
4549
|
-
};
|
|
4550
|
-
}
|
|
4551
|
-
function createInMemoryHostUtilityToolRegistry(options = {}) {
|
|
4552
|
-
const now = options.now ?? Date.now;
|
|
4553
|
-
const outputPolicy = options.outputPolicy ?? createToolOutputPolicy();
|
|
4554
|
-
const tools = /* @__PURE__ */ new Map();
|
|
4555
|
-
return {
|
|
4556
|
-
register(input) {
|
|
4557
|
-
tools.set(input.descriptor.name, cloneUtilityRegistration(input));
|
|
4558
|
-
},
|
|
4559
|
-
async execute(input) {
|
|
4560
|
-
const startedAt = now();
|
|
4561
|
-
const policyDecision = input.policyDecision ?? { decision: "allow" };
|
|
4562
|
-
const descriptor = tools.get(input.toolName)?.descriptor;
|
|
4563
|
-
if (policyDecision.decision !== "allow") {
|
|
4564
|
-
return createUtilityReceipt({
|
|
4565
|
-
input,
|
|
4566
|
-
descriptor,
|
|
4567
|
-
startedAt,
|
|
4568
|
-
finishedAt: now(),
|
|
4569
|
-
text: policyDecision.reason,
|
|
4570
|
-
exitStatus: "denied",
|
|
4571
|
-
policyDecision,
|
|
4572
|
-
outputPolicy
|
|
4573
|
-
});
|
|
4574
|
-
}
|
|
4575
|
-
const registration = tools.get(input.toolName);
|
|
4576
|
-
if (!registration) {
|
|
4577
|
-
return createUtilityReceipt({
|
|
4578
|
-
input,
|
|
4579
|
-
startedAt,
|
|
4580
|
-
finishedAt: now(),
|
|
4581
|
-
text: "host utility tool is not registered",
|
|
4582
|
-
exitStatus: "unsupported",
|
|
4583
|
-
policyDecision,
|
|
4584
|
-
outputPolicy
|
|
4585
|
-
});
|
|
4586
|
-
}
|
|
4587
|
-
try {
|
|
4588
|
-
const result = await registration.handler(input.input, {
|
|
4589
|
-
callId: input.callId,
|
|
4590
|
-
...input.commandOrigin ? { commandOrigin: cloneCommandOrigin(input.commandOrigin) } : {}
|
|
4591
|
-
});
|
|
4592
|
-
return createUtilityReceipt({
|
|
4593
|
-
input,
|
|
4594
|
-
descriptor: registration.descriptor,
|
|
4595
|
-
startedAt,
|
|
4596
|
-
finishedAt: now(),
|
|
4597
|
-
text: result.text,
|
|
4598
|
-
exitStatus: result.exitStatus ?? "success",
|
|
4599
|
-
policyDecision,
|
|
4600
|
-
outputPolicy,
|
|
4601
|
-
metadata: result.metadata
|
|
4602
|
-
});
|
|
4603
|
-
} catch (error) {
|
|
4604
|
-
return createUtilityReceipt({
|
|
4605
|
-
input,
|
|
4606
|
-
descriptor: registration.descriptor,
|
|
4607
|
-
startedAt,
|
|
4608
|
-
finishedAt: now(),
|
|
4609
|
-
text: error instanceof Error ? error.message : String(error),
|
|
4610
|
-
exitStatus: "failed",
|
|
4611
|
-
policyDecision,
|
|
4612
|
-
outputPolicy
|
|
4613
|
-
});
|
|
4614
|
-
}
|
|
4615
|
-
},
|
|
4616
|
-
capabilityReport() {
|
|
4617
|
-
return createToolRegistryVersion({
|
|
4618
|
-
registryVersion: options.registryVersion ?? "in-memory",
|
|
4619
|
-
tools: [...tools.values()].map((tool) => tool.descriptor)
|
|
4620
|
-
});
|
|
4621
|
-
}
|
|
4622
|
-
};
|
|
4623
|
-
}
|
|
4624
|
-
function createUtilityReceipt(input) {
|
|
4625
|
-
const output2 = input.outputPolicy.process({
|
|
4626
|
-
callId: input.input.callId,
|
|
4627
|
-
toolName: input.input.toolName,
|
|
4628
|
-
text: input.text,
|
|
4629
|
-
intent: input.descriptor?.category
|
|
4630
|
-
});
|
|
4631
|
-
return {
|
|
4632
|
-
toolName: input.input.toolName,
|
|
4633
|
-
callId: input.input.callId,
|
|
4634
|
-
inputDigest: `sha256:${createHash3("sha256").update(stableStringify(input.input.input), "utf8").digest("hex")}`,
|
|
4635
|
-
executionMode: "host-service",
|
|
4636
|
-
...input.descriptor?.sandboxProfile ? { sandboxProfile: cloneSandboxProfile(input.descriptor.sandboxProfile) } : {},
|
|
4637
|
-
output: output2,
|
|
4638
|
-
durationMs: input.finishedAt - input.startedAt,
|
|
4639
|
-
startedAt: input.startedAt,
|
|
4640
|
-
finishedAt: input.finishedAt,
|
|
4641
|
-
exitStatus: input.exitStatus,
|
|
4642
|
-
policyDecision: { ...input.policyDecision },
|
|
4643
|
-
...input.input.commandOrigin ? { commandOrigin: cloneCommandOrigin(input.input.commandOrigin) } : {},
|
|
4644
|
-
...input.metadata ? { metadata: { ...input.metadata } } : {}
|
|
4645
|
-
};
|
|
4646
|
-
}
|
|
4647
|
-
function cloneUtilityRegistration(input) {
|
|
4648
|
-
return {
|
|
4649
|
-
descriptor: cloneHostUtilityDescriptor(input.descriptor),
|
|
4650
|
-
handler: input.handler
|
|
4651
|
-
};
|
|
4652
|
-
}
|
|
4653
|
-
function cloneHostUtilityDescriptor(descriptor) {
|
|
4654
|
-
return {
|
|
4655
|
-
...descriptor,
|
|
4656
|
-
...descriptor.featureFlags ? { featureFlags: [...descriptor.featureFlags] } : {},
|
|
4657
|
-
runtimeSupport: { ...descriptor.runtimeSupport },
|
|
4658
|
-
...descriptor.sandboxProfile ? { sandboxProfile: cloneSandboxProfile(descriptor.sandboxProfile) } : {}
|
|
4659
|
-
};
|
|
4660
|
-
}
|
|
4661
|
-
function cloneSandboxProfile(profile) {
|
|
4662
|
-
return {
|
|
4663
|
-
...profile,
|
|
4664
|
-
allowedRuntimes: [...profile.allowedRuntimes],
|
|
4665
|
-
envScrubbedKeys: [...profile.envScrubbedKeys]
|
|
4666
|
-
};
|
|
4667
|
-
}
|
|
4668
|
-
function createSecondaryLlmCallPolicy(options) {
|
|
4669
|
-
const allowedModels = new Set(options.allowedModels);
|
|
4670
|
-
const allowedAttachmentRoots = options.allowedAttachmentRoots ?? [];
|
|
4671
|
-
return {
|
|
4672
|
-
authorize(input) {
|
|
4673
|
-
if (!allowedModels.has(input.model)) {
|
|
4674
|
-
return { decision: "deny", reason: "model_not_allowed", model: input.model };
|
|
4675
|
-
}
|
|
4676
|
-
const inputBytes = Buffer.byteLength(input.prompt, "utf8");
|
|
4677
|
-
if (inputBytes > options.maxInputBytes) {
|
|
4678
|
-
return { decision: "deny", reason: "input_too_large", model: input.model };
|
|
4679
|
-
}
|
|
4680
|
-
const maxOutputBytes = input.maxOutputBytes ?? options.maxOutputBytes;
|
|
4681
|
-
if (maxOutputBytes > options.maxOutputBytes) {
|
|
4682
|
-
return { decision: "deny", reason: "output_too_large", model: input.model };
|
|
4683
|
-
}
|
|
4684
|
-
if ((input.attachments ?? []).some((path) => !isAllowedAttachmentPath(path, allowedAttachmentRoots))) {
|
|
4685
|
-
return { decision: "deny", reason: "attachment_path_not_allowed", model: input.model };
|
|
4686
|
-
}
|
|
4687
|
-
return {
|
|
4688
|
-
decision: "allow",
|
|
4689
|
-
model: input.model,
|
|
4690
|
-
inputBytes,
|
|
4691
|
-
maxOutputBytes
|
|
4692
|
-
};
|
|
4693
|
-
}
|
|
4694
|
-
};
|
|
4695
|
-
}
|
|
4696
|
-
function isAllowedAttachmentPath(path, allowedRoots) {
|
|
4697
|
-
if (allowedRoots.length === 0) return false;
|
|
4698
|
-
return allowedRoots.some((root) => path === root || path.startsWith(`${root.replace(/\/+$/, "")}/`));
|
|
4699
|
-
}
|
|
4700
|
-
function createInMemorySessionToolBridge(options = {}) {
|
|
4701
|
-
const now = options.now ?? Date.now;
|
|
4702
|
-
const sessions = /* @__PURE__ */ new Map();
|
|
4703
|
-
const plans = [];
|
|
4704
|
-
const browserActions = [];
|
|
4705
|
-
const messages = [];
|
|
4706
|
-
const spawnedSessions = [];
|
|
4707
|
-
const sourceOperations = [];
|
|
4708
|
-
const skillOperations = [];
|
|
4709
|
-
const automationConfigOperations = [];
|
|
4710
|
-
const scheduleOperations = [];
|
|
4711
|
-
let generatedSessionCounter = 0;
|
|
4712
|
-
let messageCounter = 0;
|
|
4713
|
-
function ensureSession(sessionId) {
|
|
4714
|
-
const id = sessionId ?? "default";
|
|
4715
|
-
const existing = sessions.get(id);
|
|
4716
|
-
if (existing) return existing;
|
|
4717
|
-
const created = { sessionId: id };
|
|
4718
|
-
sessions.set(id, created);
|
|
4719
|
-
return created;
|
|
4720
|
-
}
|
|
4721
|
-
function nextSessionId() {
|
|
4722
|
-
return options.nextSessionId?.() ?? `session-${++generatedSessionCounter}`;
|
|
4723
|
-
}
|
|
4724
|
-
const bridge = {
|
|
4725
|
-
async submitPlan(request) {
|
|
4726
|
-
ensureSession(request.sessionId);
|
|
4727
|
-
plans.push({
|
|
4728
|
-
sessionId: request.sessionId,
|
|
4729
|
-
planRef: request.planRef,
|
|
4730
|
-
...request.origin ? { origin: request.origin } : {},
|
|
4731
|
-
submittedAt: now()
|
|
4732
|
-
});
|
|
4733
|
-
return { accepted: true, planRef: request.planRef };
|
|
4734
|
-
},
|
|
4735
|
-
async runBrowserAction(request) {
|
|
4736
|
-
const record = {
|
|
4737
|
-
sessionId: request.sessionId,
|
|
4738
|
-
action: request.action,
|
|
4739
|
-
...request.input !== void 0 ? { input: request.input } : {},
|
|
4740
|
-
recordedAt: now()
|
|
4741
|
-
};
|
|
4742
|
-
ensureSession(request.sessionId);
|
|
4743
|
-
browserActions.push(record);
|
|
4744
|
-
return {
|
|
4745
|
-
ok: true,
|
|
4746
|
-
result: {
|
|
4747
|
-
action: record.action,
|
|
4748
|
-
...record.input !== void 0 ? { input: record.input } : {},
|
|
4749
|
-
recordedAt: record.recordedAt
|
|
4750
|
-
}
|
|
4751
|
-
};
|
|
4752
|
-
},
|
|
4753
|
-
async spawnSession(request) {
|
|
4754
|
-
const sessionId = nextSessionId();
|
|
4755
|
-
const record = {
|
|
4756
|
-
sessionId,
|
|
4757
|
-
...request.parentSessionId ? { parentSessionId: request.parentSessionId } : {},
|
|
4758
|
-
prompt: request.prompt,
|
|
4759
|
-
...request.model ? { model: request.model } : {},
|
|
4760
|
-
...request.commandOrigin ? { commandOrigin: request.commandOrigin } : {},
|
|
4761
|
-
createdAt: now()
|
|
4762
|
-
};
|
|
4763
|
-
spawnedSessions.push(record);
|
|
4764
|
-
sessions.set(sessionId, {
|
|
4765
|
-
sessionId,
|
|
4766
|
-
topic: request.prompt,
|
|
4767
|
-
status: "created"
|
|
4768
|
-
});
|
|
4769
|
-
return { sessionId };
|
|
4770
|
-
},
|
|
4771
|
-
async sendSessionMessage(request) {
|
|
4772
|
-
ensureSession(request.sessionId);
|
|
4773
|
-
const commandId = `message:${++messageCounter}`;
|
|
4774
|
-
messages.push({
|
|
4775
|
-
sessionId: request.sessionId,
|
|
4776
|
-
message: request.message,
|
|
4777
|
-
...request.attachments ? { attachments: cloneAttachments(request.attachments) } : {},
|
|
4778
|
-
...request.commandOrigin ? { commandOrigin: request.commandOrigin } : {},
|
|
4779
|
-
commandId,
|
|
4780
|
-
sentAt: now()
|
|
4781
|
-
});
|
|
4782
|
-
return { ok: true, commandId };
|
|
4783
|
-
},
|
|
4784
|
-
async updateSessionMetadata(request) {
|
|
4785
|
-
const sessionId = request.sessionId ?? "default";
|
|
4786
|
-
const current = ensureSession(sessionId);
|
|
4787
|
-
const next = {
|
|
4788
|
-
...current,
|
|
4789
|
-
sessionId,
|
|
4790
|
-
...request.labels ? { labels: uniqueSorted(request.labels) } : {},
|
|
4791
|
-
...request.status ? { status: request.status } : {},
|
|
4792
|
-
...request.flagged !== void 0 ? { flagged: request.flagged } : {},
|
|
4793
|
-
...request.topic ? { topic: request.topic } : {}
|
|
4794
|
-
};
|
|
4795
|
-
sessions.set(sessionId, next);
|
|
4796
|
-
return cloneSession(next);
|
|
4797
|
-
},
|
|
4798
|
-
async listSessions(request) {
|
|
4799
|
-
const labels = request.labels ?? [];
|
|
4800
|
-
const filtered = [...sessions.values()].filter((session) => !request.status || session.status === request.status).filter((session) => labels.every((label) => session.labels?.includes(label))).sort((left, right) => String(left.sessionId ?? "").localeCompare(String(right.sessionId ?? ""))).slice(0, request.limit ?? Number.POSITIVE_INFINITY).map(cloneSession);
|
|
4801
|
-
return { sessions: filtered };
|
|
4802
|
-
},
|
|
4803
|
-
// Sources CRUD (in-memory recording stubs)
|
|
4804
|
-
async listSources(request) {
|
|
4805
|
-
sourceOperations.push({ op: "listSources", request, ts: now() });
|
|
4806
|
-
return { sources: [] };
|
|
4807
|
-
},
|
|
4808
|
-
async getSource(request) {
|
|
4809
|
-
sourceOperations.push({ op: "getSource", request, ts: now() });
|
|
4810
|
-
return { source: { slug: request.sourceSlug, name: request.sourceSlug, provider: "stub", type: "api", enabled: true } };
|
|
4811
|
-
},
|
|
4812
|
-
async createSource(request) {
|
|
4813
|
-
sourceOperations.push({ op: "createSource", request, ts: now() });
|
|
4814
|
-
return { ok: true, source: { slug: request.name.toLowerCase().replace(/\s+/g, "-"), name: request.name, provider: request.provider, type: request.type, enabled: request.enabled ?? true } };
|
|
4815
|
-
},
|
|
4816
|
-
async updateSource(request) {
|
|
4817
|
-
sourceOperations.push({ op: "updateSource", request, ts: now() });
|
|
4818
|
-
return { ok: true, source: { slug: request.sourceSlug, name: request.name ?? request.sourceSlug, provider: "stub", type: "api", enabled: request.enabled ?? true } };
|
|
4819
|
-
},
|
|
4820
|
-
async deleteSource(request) {
|
|
4821
|
-
sourceOperations.push({ op: "deleteSource", request, ts: now() });
|
|
4822
|
-
return { ok: true, sourceSlug: request.sourceSlug };
|
|
4823
|
-
},
|
|
4824
|
-
// Skills CRUD (in-memory recording stubs)
|
|
4825
|
-
async listSkills(request) {
|
|
4826
|
-
skillOperations.push({ op: "listSkills", request, ts: now() });
|
|
4827
|
-
return { skills: [] };
|
|
4828
|
-
},
|
|
4829
|
-
async getSkill(request) {
|
|
4830
|
-
skillOperations.push({ op: "getSkill", request, ts: now() });
|
|
4831
|
-
return { skill: { slug: request.skillSlug, name: request.skillSlug, description: "stub", source: "workspace", content: "" } };
|
|
4832
|
-
},
|
|
4833
|
-
async createSkill(request) {
|
|
4834
|
-
skillOperations.push({ op: "createSkill", request, ts: now() });
|
|
4835
|
-
return { ok: true, skill: { slug: request.slug, name: request.name, description: request.description, source: "workspace" } };
|
|
4836
|
-
},
|
|
4837
|
-
async updateSkill(request) {
|
|
4838
|
-
skillOperations.push({ op: "updateSkill", request, ts: now() });
|
|
4839
|
-
return { ok: true, skill: { slug: request.skillSlug, name: request.name ?? request.skillSlug, description: request.description ?? "stub", source: "workspace" } };
|
|
4840
|
-
},
|
|
4841
|
-
async deleteSkill(request) {
|
|
4842
|
-
skillOperations.push({ op: "deleteSkill", request, ts: now() });
|
|
4843
|
-
return { ok: true, skillSlug: request.skillSlug };
|
|
4844
|
-
},
|
|
4845
|
-
// Automations Config (in-memory recording stubs)
|
|
4846
|
-
async getAutomationsConfig(request) {
|
|
4847
|
-
automationConfigOperations.push({ op: "getAutomationsConfig", request, ts: now() });
|
|
4848
|
-
return { config: null, configPath: "/stub/automations.json" };
|
|
4849
|
-
},
|
|
4850
|
-
async updateAutomationsConfig(request) {
|
|
4851
|
-
automationConfigOperations.push({ op: "updateAutomationsConfig", request, ts: now() });
|
|
4852
|
-
return { ok: true, automationCount: 0, errors: [] };
|
|
4853
|
-
},
|
|
4854
|
-
// Scheduler (in-memory recording stubs)
|
|
4855
|
-
async listSchedules(request) {
|
|
4856
|
-
scheduleOperations.push({ op: "listSchedules", request, ts: now() });
|
|
4857
|
-
return { schedules: [] };
|
|
4858
|
-
},
|
|
4859
|
-
async startSchedule(request) {
|
|
4860
|
-
scheduleOperations.push({ op: "startSchedule", request, ts: now() });
|
|
4861
|
-
return { schedulerId: request.schedulerId, state: "started", timestamp: now() };
|
|
4862
|
-
},
|
|
4863
|
-
async stopSchedule(request) {
|
|
4864
|
-
scheduleOperations.push({ op: "stopSchedule", request, ts: now() });
|
|
4865
|
-
return { ok: true, schedulerId: request.schedulerId, state: "stopped" };
|
|
4866
|
-
},
|
|
4867
|
-
...options.queryLlm ? {
|
|
4868
|
-
async queryLlm(request) {
|
|
4869
|
-
return options.queryLlm(request);
|
|
4870
|
-
}
|
|
4871
|
-
} : {}
|
|
4872
|
-
};
|
|
4873
|
-
return {
|
|
4874
|
-
bridge,
|
|
4875
|
-
getSnapshot() {
|
|
4876
|
-
return {
|
|
4877
|
-
sessions: [...sessions.values()].map(cloneSession),
|
|
4878
|
-
plans: plans.map((plan) => ({ ...plan })),
|
|
4879
|
-
browserActions: browserActions.map((action) => ({ ...action })),
|
|
4880
|
-
messages: messages.map((message) => ({
|
|
4881
|
-
...message,
|
|
4882
|
-
...message.attachments ? { attachments: cloneAttachments(message.attachments) } : {}
|
|
4883
|
-
})),
|
|
4884
|
-
spawnedSessions: spawnedSessions.map((session) => ({ ...session })),
|
|
4885
|
-
sourceOperations: sourceOperations.map((op) => ({ ...op })),
|
|
4886
|
-
skillOperations: skillOperations.map((op) => ({ ...op })),
|
|
4887
|
-
automationConfigOperations: automationConfigOperations.map((op) => ({ ...op })),
|
|
4888
|
-
scheduleOperations: scheduleOperations.map((op) => ({ ...op }))
|
|
4889
|
-
};
|
|
4890
|
-
}
|
|
4891
|
-
};
|
|
4892
|
-
}
|
|
4893
|
-
function cloneSession(session) {
|
|
4894
|
-
return {
|
|
4895
|
-
...session,
|
|
4896
|
-
...session.labels ? { labels: [...session.labels] } : {}
|
|
4897
|
-
};
|
|
4898
|
-
}
|
|
4899
|
-
function cloneAttachments(attachments) {
|
|
4900
|
-
return attachments.map((attachment) => ({ ...attachment }));
|
|
4901
|
-
}
|
|
4902
|
-
function uniqueSorted(values) {
|
|
4903
|
-
return [...new Set(values)].sort();
|
|
4904
|
-
}
|
|
4905
|
-
function createConfigWatcherManager(options) {
|
|
4906
|
-
const watchers = [];
|
|
4907
|
-
const hashes = /* @__PURE__ */ new Map();
|
|
4908
|
-
const debounceMs = options.debounceMs ?? 300;
|
|
4909
|
-
const now = options.now ?? Date.now;
|
|
4910
|
-
const debounceTimers = /* @__PURE__ */ new Map();
|
|
4911
|
-
let active = false;
|
|
4912
|
-
function hashFile(path) {
|
|
4913
|
-
try {
|
|
4914
|
-
if (!existsSync(path)) return void 0;
|
|
4915
|
-
const content = readFileSync2(path, "utf-8");
|
|
4916
|
-
return createHash4("sha256").update(content).digest("hex").slice(0, 16);
|
|
4917
|
-
} catch {
|
|
4918
|
-
return void 0;
|
|
4919
|
-
}
|
|
4920
|
-
}
|
|
4921
|
-
function handleChange(file) {
|
|
4922
|
-
const existing = debounceTimers.get(file.path);
|
|
4923
|
-
if (existing) clearTimeout(existing);
|
|
4924
|
-
debounceTimers.set(file.path, setTimeout(() => {
|
|
4925
|
-
debounceTimers.delete(file.path);
|
|
4926
|
-
if (!active) return;
|
|
4927
|
-
const previousHash = hashes.get(file.path);
|
|
4928
|
-
const nextHash = hashFile(file.path);
|
|
4929
|
-
if (previousHash === nextHash) return;
|
|
4930
|
-
let action;
|
|
4931
|
-
if (!previousHash && nextHash) action = "created";
|
|
4932
|
-
else if (previousHash && !nextHash) action = "deleted";
|
|
4933
|
-
else action = "updated";
|
|
4934
|
-
if (nextHash) {
|
|
4935
|
-
hashes.set(file.path, nextHash);
|
|
4936
|
-
} else {
|
|
4937
|
-
hashes.delete(file.path);
|
|
4938
|
-
}
|
|
4939
|
-
options.callbacks.onConfigChange({
|
|
4940
|
-
workspaceId: options.workspaceId,
|
|
4941
|
-
configPath: file.path,
|
|
4942
|
-
configKind: file.kind,
|
|
4943
|
-
action,
|
|
4944
|
-
source: "file_watcher",
|
|
4945
|
-
timestamp: now(),
|
|
4946
|
-
previousHash,
|
|
4947
|
-
nextHash
|
|
4948
|
-
});
|
|
4949
|
-
}, debounceMs));
|
|
4950
|
-
}
|
|
4951
|
-
return {
|
|
4952
|
-
get watching() {
|
|
4953
|
-
return active;
|
|
4954
|
-
},
|
|
4955
|
-
start() {
|
|
4956
|
-
if (active) return;
|
|
4957
|
-
active = true;
|
|
4958
|
-
for (const file of options.files) {
|
|
4959
|
-
const initialHash = hashFile(file.path);
|
|
4960
|
-
if (initialHash) hashes.set(file.path, initialHash);
|
|
4961
|
-
try {
|
|
4962
|
-
const watcher = watch(file.path, { persistent: false }, () => {
|
|
4963
|
-
handleChange(file);
|
|
4964
|
-
});
|
|
4965
|
-
watcher.on("error", (err) => {
|
|
4966
|
-
options.callbacks.onError?.(err);
|
|
4967
|
-
});
|
|
4968
|
-
watchers.push(watcher);
|
|
4969
|
-
} catch {
|
|
4970
|
-
}
|
|
4971
|
-
}
|
|
4972
|
-
},
|
|
4973
|
-
stop() {
|
|
4974
|
-
active = false;
|
|
4975
|
-
for (const watcher of watchers) {
|
|
4976
|
-
watcher.close();
|
|
4977
|
-
}
|
|
4978
|
-
watchers.length = 0;
|
|
4979
|
-
for (const timer of debounceTimers.values()) {
|
|
4980
|
-
clearTimeout(timer);
|
|
4981
|
-
}
|
|
4982
|
-
debounceTimers.clear();
|
|
4983
|
-
}
|
|
4984
|
-
};
|
|
4985
|
-
}
|
|
4986
|
-
export {
|
|
4987
|
-
createConfigRecoveryReceipt,
|
|
4988
|
-
createConfigWatchEvent,
|
|
4989
|
-
createConfigWatcherManager,
|
|
4990
|
-
createHostAgentRuntime,
|
|
4991
|
-
createHostRuntimeController,
|
|
4992
|
-
createInMemoryArtifactStore,
|
|
4993
|
-
createInMemoryAuditStore,
|
|
4994
|
-
createInMemoryHostUtilityToolRegistry,
|
|
4995
|
-
createInMemoryNotificationHost,
|
|
4996
|
-
createInMemorySessionToolBridge,
|
|
4997
|
-
createMessagingAccessPolicy,
|
|
4998
|
-
createMessagingBindingStore,
|
|
4999
|
-
createPrivilegedExecutionBroker,
|
|
5000
|
-
createResourceBundleSnapshot,
|
|
5001
|
-
createSecondaryLlmCallPolicy,
|
|
5002
|
-
createSourceAuthoringPlan,
|
|
5003
|
-
createToolOutputPolicy,
|
|
5004
|
-
createToolRegistryVersion,
|
|
5005
|
-
expandWebhookTemplate,
|
|
5006
|
-
scrubLocalMcpEnvironment,
|
|
5007
|
-
validateStatusConfig
|
|
5008
|
-
};
|