@mindstudio-ai/remy 0.1.209 → 0.1.210
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/headless.js
CHANGED
|
@@ -100,6 +100,323 @@ function resolveConfig(flags) {
|
|
|
100
100
|
return { apiKey, baseUrl: baseUrl2, appId };
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
// src/api.ts
|
|
104
|
+
var log2 = createLogger("api");
|
|
105
|
+
async function* streamChat(params) {
|
|
106
|
+
const { baseUrl: baseUrl2, apiKey, signal, requestId, model, ...rest } = params;
|
|
107
|
+
const url = `${baseUrl2}/_internal/v2/agent/remy/chat`;
|
|
108
|
+
const startTime = Date.now();
|
|
109
|
+
const subAgentId = rest.subAgentId;
|
|
110
|
+
const requestBody = { ...rest, modelId: model };
|
|
111
|
+
log2.info("API request", {
|
|
112
|
+
requestId,
|
|
113
|
+
...subAgentId && { subAgentId },
|
|
114
|
+
model,
|
|
115
|
+
messageCount: rest.messages.length,
|
|
116
|
+
toolCount: rest.tools.length
|
|
117
|
+
});
|
|
118
|
+
let res;
|
|
119
|
+
try {
|
|
120
|
+
res = await fetch(url, {
|
|
121
|
+
method: "POST",
|
|
122
|
+
headers: {
|
|
123
|
+
"Content-Type": "application/json",
|
|
124
|
+
Authorization: `Bearer ${apiKey}`
|
|
125
|
+
},
|
|
126
|
+
body: JSON.stringify(requestBody),
|
|
127
|
+
signal
|
|
128
|
+
});
|
|
129
|
+
} catch (err) {
|
|
130
|
+
if (signal?.aborted) {
|
|
131
|
+
log2.warn("Request aborted", {
|
|
132
|
+
requestId,
|
|
133
|
+
...subAgentId && { subAgentId }
|
|
134
|
+
});
|
|
135
|
+
throw err;
|
|
136
|
+
}
|
|
137
|
+
log2.error("Network error", {
|
|
138
|
+
requestId,
|
|
139
|
+
...subAgentId && { subAgentId },
|
|
140
|
+
error: err.message
|
|
141
|
+
});
|
|
142
|
+
yield { type: "error", error: `Network error: ${err.message}` };
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const ttfb = Date.now() - startTime;
|
|
146
|
+
log2.info("API response", {
|
|
147
|
+
requestId,
|
|
148
|
+
...subAgentId && { subAgentId },
|
|
149
|
+
status: res.status,
|
|
150
|
+
ttfbMs: ttfb
|
|
151
|
+
});
|
|
152
|
+
if (!res.ok) {
|
|
153
|
+
let errorMessage = `HTTP ${res.status}`;
|
|
154
|
+
let errorCode;
|
|
155
|
+
let badModelId;
|
|
156
|
+
try {
|
|
157
|
+
const body = await res.json();
|
|
158
|
+
if (body.error) {
|
|
159
|
+
errorMessage = body.error;
|
|
160
|
+
}
|
|
161
|
+
if (body.errorMessage) {
|
|
162
|
+
errorMessage = body.errorMessage;
|
|
163
|
+
}
|
|
164
|
+
if (typeof body.code === "string") {
|
|
165
|
+
errorCode = body.code;
|
|
166
|
+
}
|
|
167
|
+
if (typeof body.modelId === "string") {
|
|
168
|
+
badModelId = body.modelId;
|
|
169
|
+
}
|
|
170
|
+
} catch {
|
|
171
|
+
}
|
|
172
|
+
log2.error("API error", {
|
|
173
|
+
requestId,
|
|
174
|
+
...subAgentId && { subAgentId },
|
|
175
|
+
status: res.status,
|
|
176
|
+
error: errorMessage,
|
|
177
|
+
...errorCode && { code: errorCode },
|
|
178
|
+
...badModelId && { badModelId }
|
|
179
|
+
});
|
|
180
|
+
yield {
|
|
181
|
+
type: "error",
|
|
182
|
+
error: errorMessage,
|
|
183
|
+
...errorCode && { code: errorCode },
|
|
184
|
+
...badModelId && { badModelId }
|
|
185
|
+
};
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const STALL_TIMEOUT_MS = 3e5;
|
|
189
|
+
const reader = res.body.getReader();
|
|
190
|
+
const decoder = new TextDecoder();
|
|
191
|
+
let buffer = "";
|
|
192
|
+
let receivedDone = false;
|
|
193
|
+
while (true) {
|
|
194
|
+
let stallTimer;
|
|
195
|
+
let readResult;
|
|
196
|
+
try {
|
|
197
|
+
readResult = await Promise.race([
|
|
198
|
+
reader.read(),
|
|
199
|
+
new Promise((_, reject) => {
|
|
200
|
+
stallTimer = setTimeout(
|
|
201
|
+
() => reject(new Error("stream_stall")),
|
|
202
|
+
STALL_TIMEOUT_MS
|
|
203
|
+
);
|
|
204
|
+
})
|
|
205
|
+
]);
|
|
206
|
+
clearTimeout(stallTimer);
|
|
207
|
+
} catch (err) {
|
|
208
|
+
clearTimeout(stallTimer);
|
|
209
|
+
try {
|
|
210
|
+
await reader.cancel();
|
|
211
|
+
} catch {
|
|
212
|
+
}
|
|
213
|
+
const isStall = err?.message === "stream_stall";
|
|
214
|
+
const errorMessage = isStall ? "Stream stalled \u2014 no data received for 5 minutes" : `Network error: stream interrupted \u2014 ${err?.message ?? "unknown"}`;
|
|
215
|
+
log2.error(isStall ? "Stream stalled" : "Stream interrupted", {
|
|
216
|
+
requestId,
|
|
217
|
+
...subAgentId && { subAgentId },
|
|
218
|
+
durationMs: Date.now() - startTime,
|
|
219
|
+
error: errorMessage
|
|
220
|
+
});
|
|
221
|
+
yield { type: "error", error: errorMessage };
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const { done, value } = readResult;
|
|
225
|
+
if (done) {
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
buffer += decoder.decode(value, { stream: true });
|
|
229
|
+
const lines = buffer.split("\n");
|
|
230
|
+
buffer = lines.pop() ?? "";
|
|
231
|
+
for (const line of lines) {
|
|
232
|
+
if (!line.startsWith("data: ")) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
try {
|
|
236
|
+
const event = JSON.parse(line.slice(6));
|
|
237
|
+
if (event.type === "done") {
|
|
238
|
+
const elapsed = Date.now() - startTime;
|
|
239
|
+
receivedDone = true;
|
|
240
|
+
log2.info("Stream complete", {
|
|
241
|
+
requestId,
|
|
242
|
+
...subAgentId && { subAgentId },
|
|
243
|
+
durationMs: elapsed,
|
|
244
|
+
stopReason: event.stopReason,
|
|
245
|
+
modelId: event.modelId,
|
|
246
|
+
inputTokens: event.usage.inputTokens,
|
|
247
|
+
outputTokens: event.usage.outputTokens,
|
|
248
|
+
cost: event.cost
|
|
249
|
+
});
|
|
250
|
+
} else if (event.type === "error") {
|
|
251
|
+
log2.error("SSE error event", {
|
|
252
|
+
requestId,
|
|
253
|
+
...subAgentId && { subAgentId },
|
|
254
|
+
error: event.error,
|
|
255
|
+
durationMs: Date.now() - startTime
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
yield event;
|
|
259
|
+
} catch {
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (buffer.startsWith("data: ")) {
|
|
264
|
+
try {
|
|
265
|
+
yield JSON.parse(buffer.slice(6));
|
|
266
|
+
} catch {
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (!receivedDone) {
|
|
270
|
+
log2.warn("Stream ended without done event", {
|
|
271
|
+
requestId,
|
|
272
|
+
...subAgentId && { subAgentId },
|
|
273
|
+
durationMs: Date.now() - startTime,
|
|
274
|
+
remainingBuffer: buffer.slice(0, 200)
|
|
275
|
+
});
|
|
276
|
+
yield {
|
|
277
|
+
type: "error",
|
|
278
|
+
error: "Network error: stream ended before completion"
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
var MAX_RETRIES = 5;
|
|
283
|
+
var INITIAL_BACKOFF_MS = 1e3;
|
|
284
|
+
function isRetryableError(error) {
|
|
285
|
+
return /Network error/i.test(error) || /HTTP 5\d\d/i.test(error) || /Stream stalled/i.test(error) || /overloaded/i.test(error) || /terminated/i.test(error);
|
|
286
|
+
}
|
|
287
|
+
function sleep(ms) {
|
|
288
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
289
|
+
}
|
|
290
|
+
async function* streamChatWithRetry(params, options) {
|
|
291
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
292
|
+
const buffer = [];
|
|
293
|
+
let retryableFailure = false;
|
|
294
|
+
for await (const event of streamChat(params)) {
|
|
295
|
+
if (event.type === "error") {
|
|
296
|
+
if (isRetryableError(event.error) && attempt < MAX_RETRIES - 1) {
|
|
297
|
+
options?.onRetry?.(attempt, event.error);
|
|
298
|
+
retryableFailure = true;
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
yield event;
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
buffer.push(event);
|
|
305
|
+
}
|
|
306
|
+
if (retryableFailure) {
|
|
307
|
+
if (params.signal?.aborted) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const backoff = INITIAL_BACKOFF_MS * 2 ** attempt;
|
|
311
|
+
log2.warn("Retrying", {
|
|
312
|
+
requestId: params.requestId,
|
|
313
|
+
attempt: attempt + 1,
|
|
314
|
+
maxRetries: MAX_RETRIES,
|
|
315
|
+
backoffMs: backoff
|
|
316
|
+
});
|
|
317
|
+
await sleep(backoff);
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
for (const event of buffer) {
|
|
321
|
+
yield event;
|
|
322
|
+
}
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
var FALLBACK_ACK = "[Message sent to agent. Agent is working in the background and will report back with its results when finished.]";
|
|
327
|
+
async function generateBackgroundAck(params) {
|
|
328
|
+
const url = `${params.apiConfig.baseUrl}/_internal/v2/agent/remy/generate-ack`;
|
|
329
|
+
try {
|
|
330
|
+
const res = await fetch(url, {
|
|
331
|
+
method: "POST",
|
|
332
|
+
headers: {
|
|
333
|
+
"Content-Type": "application/json",
|
|
334
|
+
Authorization: `Bearer ${params.apiConfig.apiKey}`
|
|
335
|
+
},
|
|
336
|
+
body: JSON.stringify({
|
|
337
|
+
appId: params.apiConfig.appId,
|
|
338
|
+
agentName: params.agentName,
|
|
339
|
+
task: params.task
|
|
340
|
+
}),
|
|
341
|
+
signal: AbortSignal.timeout(2e4)
|
|
342
|
+
});
|
|
343
|
+
if (!res.ok) {
|
|
344
|
+
return FALLBACK_ACK;
|
|
345
|
+
}
|
|
346
|
+
const data = await res.json();
|
|
347
|
+
return data.message || FALLBACK_ACK;
|
|
348
|
+
} catch (err) {
|
|
349
|
+
return FALLBACK_ACK;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
async function fetchRemyContext(config) {
|
|
353
|
+
if (!config.appId) {
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
const url = `${config.baseUrl}/developer/v2/helpers/remy-context?appId=${encodeURIComponent(config.appId)}`;
|
|
357
|
+
try {
|
|
358
|
+
const res = await fetch(url, {
|
|
359
|
+
method: "GET",
|
|
360
|
+
headers: {
|
|
361
|
+
Authorization: `Bearer ${config.apiKey}`
|
|
362
|
+
},
|
|
363
|
+
signal: AbortSignal.timeout(2e4)
|
|
364
|
+
});
|
|
365
|
+
if (!res.ok) {
|
|
366
|
+
log2.debug("remy-context fetch non-OK", { status: res.status });
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
const data = await res.json();
|
|
370
|
+
if (!data || typeof data !== "object" || typeof data.auth !== "object") {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
return data;
|
|
374
|
+
} catch (err) {
|
|
375
|
+
log2.debug("remy-context fetch failed", { error: err.message });
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// src/orgContext.ts
|
|
381
|
+
var log3 = createLogger("orgContext");
|
|
382
|
+
var cached = null;
|
|
383
|
+
async function initOrgContext(config) {
|
|
384
|
+
try {
|
|
385
|
+
cached = await fetchRemyContext(config);
|
|
386
|
+
log3.debug("org context loaded", {
|
|
387
|
+
delegatedAvailable: cached?.auth?.delegatedAvailable ?? false,
|
|
388
|
+
requireDelegatedOnly: cached?.auth?.requireDelegatedOnly ?? false,
|
|
389
|
+
hasOrgName: !!cached?.org?.name
|
|
390
|
+
});
|
|
391
|
+
} catch (err) {
|
|
392
|
+
cached = null;
|
|
393
|
+
log3.debug("org context init failed", { error: err.message });
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function renderOrgContextBlock() {
|
|
397
|
+
const ctx = cached;
|
|
398
|
+
const auth = ctx?.auth;
|
|
399
|
+
if (!auth || !auth.delegatedAvailable && !auth.requireDelegatedOnly) {
|
|
400
|
+
return "";
|
|
401
|
+
}
|
|
402
|
+
const lines = ["<org_auth_context>"];
|
|
403
|
+
if (ctx?.org?.name && ctx?.org?.name !== "Personal Workspace") {
|
|
404
|
+
lines.push(`This app is owned by the organization "${ctx.org.name}".`);
|
|
405
|
+
}
|
|
406
|
+
if (auth.delegatedAvailable) {
|
|
407
|
+
lines.push(
|
|
408
|
+
'"Sign in with Remy" (platform-delegated sign-in) is an available auth type for this app: organization members can sign in without a verification code.'
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
if (auth.requireDelegatedOnly) {
|
|
412
|
+
lines.push(
|
|
413
|
+
"This organization requires delegated sign-in: non-delegated human auth methods (email-code, sms-code) are blocked at the platform edge for its apps."
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
lines.push("</org_auth_context>");
|
|
417
|
+
return lines.join("\n");
|
|
418
|
+
}
|
|
419
|
+
|
|
103
420
|
// src/assets.ts
|
|
104
421
|
import fs3 from "fs";
|
|
105
422
|
import path2 from "path";
|
|
@@ -391,6 +708,8 @@ New projects progress through three onboarding states. The user might skip this
|
|
|
391
708
|
${projectContext}
|
|
392
709
|
</project_context>
|
|
393
710
|
|
|
711
|
+
${renderOrgContextBlock()}
|
|
712
|
+
|
|
394
713
|
<view_context>
|
|
395
714
|
The user is currently in ${viewContext?.mode ?? "code"} mode.
|
|
396
715
|
${viewContext?.activeFile ? `Active file: ${viewContext.activeFile}` : ""}
|
|
@@ -401,256 +720,6 @@ ${loadPlanStatus()}
|
|
|
401
720
|
return resolveIncludes(template);
|
|
402
721
|
}
|
|
403
722
|
|
|
404
|
-
// src/api.ts
|
|
405
|
-
var log2 = createLogger("api");
|
|
406
|
-
async function* streamChat(params) {
|
|
407
|
-
const { baseUrl: baseUrl2, apiKey, signal, requestId, model, ...rest } = params;
|
|
408
|
-
const url = `${baseUrl2}/_internal/v2/agent/remy/chat`;
|
|
409
|
-
const startTime = Date.now();
|
|
410
|
-
const subAgentId = rest.subAgentId;
|
|
411
|
-
const requestBody = { ...rest, modelId: model };
|
|
412
|
-
log2.info("API request", {
|
|
413
|
-
requestId,
|
|
414
|
-
...subAgentId && { subAgentId },
|
|
415
|
-
model,
|
|
416
|
-
messageCount: rest.messages.length,
|
|
417
|
-
toolCount: rest.tools.length
|
|
418
|
-
});
|
|
419
|
-
let res;
|
|
420
|
-
try {
|
|
421
|
-
res = await fetch(url, {
|
|
422
|
-
method: "POST",
|
|
423
|
-
headers: {
|
|
424
|
-
"Content-Type": "application/json",
|
|
425
|
-
Authorization: `Bearer ${apiKey}`
|
|
426
|
-
},
|
|
427
|
-
body: JSON.stringify(requestBody),
|
|
428
|
-
signal
|
|
429
|
-
});
|
|
430
|
-
} catch (err) {
|
|
431
|
-
if (signal?.aborted) {
|
|
432
|
-
log2.warn("Request aborted", {
|
|
433
|
-
requestId,
|
|
434
|
-
...subAgentId && { subAgentId }
|
|
435
|
-
});
|
|
436
|
-
throw err;
|
|
437
|
-
}
|
|
438
|
-
log2.error("Network error", {
|
|
439
|
-
requestId,
|
|
440
|
-
...subAgentId && { subAgentId },
|
|
441
|
-
error: err.message
|
|
442
|
-
});
|
|
443
|
-
yield { type: "error", error: `Network error: ${err.message}` };
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
const ttfb = Date.now() - startTime;
|
|
447
|
-
log2.info("API response", {
|
|
448
|
-
requestId,
|
|
449
|
-
...subAgentId && { subAgentId },
|
|
450
|
-
status: res.status,
|
|
451
|
-
ttfbMs: ttfb
|
|
452
|
-
});
|
|
453
|
-
if (!res.ok) {
|
|
454
|
-
let errorMessage = `HTTP ${res.status}`;
|
|
455
|
-
let errorCode;
|
|
456
|
-
let badModelId;
|
|
457
|
-
try {
|
|
458
|
-
const body = await res.json();
|
|
459
|
-
if (body.error) {
|
|
460
|
-
errorMessage = body.error;
|
|
461
|
-
}
|
|
462
|
-
if (body.errorMessage) {
|
|
463
|
-
errorMessage = body.errorMessage;
|
|
464
|
-
}
|
|
465
|
-
if (typeof body.code === "string") {
|
|
466
|
-
errorCode = body.code;
|
|
467
|
-
}
|
|
468
|
-
if (typeof body.modelId === "string") {
|
|
469
|
-
badModelId = body.modelId;
|
|
470
|
-
}
|
|
471
|
-
} catch {
|
|
472
|
-
}
|
|
473
|
-
log2.error("API error", {
|
|
474
|
-
requestId,
|
|
475
|
-
...subAgentId && { subAgentId },
|
|
476
|
-
status: res.status,
|
|
477
|
-
error: errorMessage,
|
|
478
|
-
...errorCode && { code: errorCode },
|
|
479
|
-
...badModelId && { badModelId }
|
|
480
|
-
});
|
|
481
|
-
yield {
|
|
482
|
-
type: "error",
|
|
483
|
-
error: errorMessage,
|
|
484
|
-
...errorCode && { code: errorCode },
|
|
485
|
-
...badModelId && { badModelId }
|
|
486
|
-
};
|
|
487
|
-
return;
|
|
488
|
-
}
|
|
489
|
-
const STALL_TIMEOUT_MS = 3e5;
|
|
490
|
-
const reader = res.body.getReader();
|
|
491
|
-
const decoder = new TextDecoder();
|
|
492
|
-
let buffer = "";
|
|
493
|
-
let receivedDone = false;
|
|
494
|
-
while (true) {
|
|
495
|
-
let stallTimer;
|
|
496
|
-
let readResult;
|
|
497
|
-
try {
|
|
498
|
-
readResult = await Promise.race([
|
|
499
|
-
reader.read(),
|
|
500
|
-
new Promise((_, reject) => {
|
|
501
|
-
stallTimer = setTimeout(
|
|
502
|
-
() => reject(new Error("stream_stall")),
|
|
503
|
-
STALL_TIMEOUT_MS
|
|
504
|
-
);
|
|
505
|
-
})
|
|
506
|
-
]);
|
|
507
|
-
clearTimeout(stallTimer);
|
|
508
|
-
} catch (err) {
|
|
509
|
-
clearTimeout(stallTimer);
|
|
510
|
-
try {
|
|
511
|
-
await reader.cancel();
|
|
512
|
-
} catch {
|
|
513
|
-
}
|
|
514
|
-
const isStall = err?.message === "stream_stall";
|
|
515
|
-
const errorMessage = isStall ? "Stream stalled \u2014 no data received for 5 minutes" : `Network error: stream interrupted \u2014 ${err?.message ?? "unknown"}`;
|
|
516
|
-
log2.error(isStall ? "Stream stalled" : "Stream interrupted", {
|
|
517
|
-
requestId,
|
|
518
|
-
...subAgentId && { subAgentId },
|
|
519
|
-
durationMs: Date.now() - startTime,
|
|
520
|
-
error: errorMessage
|
|
521
|
-
});
|
|
522
|
-
yield { type: "error", error: errorMessage };
|
|
523
|
-
return;
|
|
524
|
-
}
|
|
525
|
-
const { done, value } = readResult;
|
|
526
|
-
if (done) {
|
|
527
|
-
break;
|
|
528
|
-
}
|
|
529
|
-
buffer += decoder.decode(value, { stream: true });
|
|
530
|
-
const lines = buffer.split("\n");
|
|
531
|
-
buffer = lines.pop() ?? "";
|
|
532
|
-
for (const line of lines) {
|
|
533
|
-
if (!line.startsWith("data: ")) {
|
|
534
|
-
continue;
|
|
535
|
-
}
|
|
536
|
-
try {
|
|
537
|
-
const event = JSON.parse(line.slice(6));
|
|
538
|
-
if (event.type === "done") {
|
|
539
|
-
const elapsed = Date.now() - startTime;
|
|
540
|
-
receivedDone = true;
|
|
541
|
-
log2.info("Stream complete", {
|
|
542
|
-
requestId,
|
|
543
|
-
...subAgentId && { subAgentId },
|
|
544
|
-
durationMs: elapsed,
|
|
545
|
-
stopReason: event.stopReason,
|
|
546
|
-
modelId: event.modelId,
|
|
547
|
-
inputTokens: event.usage.inputTokens,
|
|
548
|
-
outputTokens: event.usage.outputTokens,
|
|
549
|
-
cost: event.cost
|
|
550
|
-
});
|
|
551
|
-
} else if (event.type === "error") {
|
|
552
|
-
log2.error("SSE error event", {
|
|
553
|
-
requestId,
|
|
554
|
-
...subAgentId && { subAgentId },
|
|
555
|
-
error: event.error,
|
|
556
|
-
durationMs: Date.now() - startTime
|
|
557
|
-
});
|
|
558
|
-
}
|
|
559
|
-
yield event;
|
|
560
|
-
} catch {
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
if (buffer.startsWith("data: ")) {
|
|
565
|
-
try {
|
|
566
|
-
yield JSON.parse(buffer.slice(6));
|
|
567
|
-
} catch {
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
if (!receivedDone) {
|
|
571
|
-
log2.warn("Stream ended without done event", {
|
|
572
|
-
requestId,
|
|
573
|
-
...subAgentId && { subAgentId },
|
|
574
|
-
durationMs: Date.now() - startTime,
|
|
575
|
-
remainingBuffer: buffer.slice(0, 200)
|
|
576
|
-
});
|
|
577
|
-
yield {
|
|
578
|
-
type: "error",
|
|
579
|
-
error: "Network error: stream ended before completion"
|
|
580
|
-
};
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
var MAX_RETRIES = 5;
|
|
584
|
-
var INITIAL_BACKOFF_MS = 1e3;
|
|
585
|
-
function isRetryableError(error) {
|
|
586
|
-
return /Network error/i.test(error) || /HTTP 5\d\d/i.test(error) || /Stream stalled/i.test(error) || /overloaded/i.test(error) || /terminated/i.test(error);
|
|
587
|
-
}
|
|
588
|
-
function sleep(ms) {
|
|
589
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
590
|
-
}
|
|
591
|
-
async function* streamChatWithRetry(params, options) {
|
|
592
|
-
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
593
|
-
const buffer = [];
|
|
594
|
-
let retryableFailure = false;
|
|
595
|
-
for await (const event of streamChat(params)) {
|
|
596
|
-
if (event.type === "error") {
|
|
597
|
-
if (isRetryableError(event.error) && attempt < MAX_RETRIES - 1) {
|
|
598
|
-
options?.onRetry?.(attempt, event.error);
|
|
599
|
-
retryableFailure = true;
|
|
600
|
-
break;
|
|
601
|
-
}
|
|
602
|
-
yield event;
|
|
603
|
-
return;
|
|
604
|
-
}
|
|
605
|
-
buffer.push(event);
|
|
606
|
-
}
|
|
607
|
-
if (retryableFailure) {
|
|
608
|
-
if (params.signal?.aborted) {
|
|
609
|
-
return;
|
|
610
|
-
}
|
|
611
|
-
const backoff = INITIAL_BACKOFF_MS * 2 ** attempt;
|
|
612
|
-
log2.warn("Retrying", {
|
|
613
|
-
requestId: params.requestId,
|
|
614
|
-
attempt: attempt + 1,
|
|
615
|
-
maxRetries: MAX_RETRIES,
|
|
616
|
-
backoffMs: backoff
|
|
617
|
-
});
|
|
618
|
-
await sleep(backoff);
|
|
619
|
-
continue;
|
|
620
|
-
}
|
|
621
|
-
for (const event of buffer) {
|
|
622
|
-
yield event;
|
|
623
|
-
}
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
var FALLBACK_ACK = "[Message sent to agent. Agent is working in the background and will report back with its results when finished.]";
|
|
628
|
-
async function generateBackgroundAck(params) {
|
|
629
|
-
const url = `${params.apiConfig.baseUrl}/_internal/v2/agent/remy/generate-ack`;
|
|
630
|
-
try {
|
|
631
|
-
const res = await fetch(url, {
|
|
632
|
-
method: "POST",
|
|
633
|
-
headers: {
|
|
634
|
-
"Content-Type": "application/json",
|
|
635
|
-
Authorization: `Bearer ${params.apiConfig.apiKey}`
|
|
636
|
-
},
|
|
637
|
-
body: JSON.stringify({
|
|
638
|
-
appId: params.apiConfig.appId,
|
|
639
|
-
agentName: params.agentName,
|
|
640
|
-
task: params.task
|
|
641
|
-
}),
|
|
642
|
-
signal: AbortSignal.timeout(2e4)
|
|
643
|
-
});
|
|
644
|
-
if (!res.ok) {
|
|
645
|
-
return FALLBACK_ACK;
|
|
646
|
-
}
|
|
647
|
-
const data = await res.json();
|
|
648
|
-
return data.message || FALLBACK_ACK;
|
|
649
|
-
} catch (err) {
|
|
650
|
-
return FALLBACK_ACK;
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
|
|
654
723
|
// src/tools/spec/readSpec.ts
|
|
655
724
|
import fs5 from "fs/promises";
|
|
656
725
|
|
|
@@ -2332,11 +2401,11 @@ var editsFinishedTool = {
|
|
|
2332
2401
|
};
|
|
2333
2402
|
|
|
2334
2403
|
// src/tools/_helpers/sidecar.ts
|
|
2335
|
-
var
|
|
2404
|
+
var log4 = createLogger("sidecar");
|
|
2336
2405
|
var baseUrl = null;
|
|
2337
2406
|
function setSidecarBaseUrl(url) {
|
|
2338
2407
|
baseUrl = url;
|
|
2339
|
-
|
|
2408
|
+
log4.info("Configured", { url });
|
|
2340
2409
|
}
|
|
2341
2410
|
async function sidecarRequest(endpoint, body = {}, options) {
|
|
2342
2411
|
if (!baseUrl) {
|
|
@@ -2351,7 +2420,7 @@ async function sidecarRequest(endpoint, body = {}, options) {
|
|
|
2351
2420
|
signal: options?.timeout ? AbortSignal.timeout(options.timeout) : void 0
|
|
2352
2421
|
});
|
|
2353
2422
|
if (!res.ok) {
|
|
2354
|
-
|
|
2423
|
+
log4.error("Sidecar error", { endpoint, status: res.status });
|
|
2355
2424
|
throw new Error(`Sidecar error: ${res.status}`);
|
|
2356
2425
|
}
|
|
2357
2426
|
const data = await res.json();
|
|
@@ -2364,7 +2433,7 @@ async function sidecarRequest(endpoint, body = {}, options) {
|
|
|
2364
2433
|
if (err.message.startsWith("Sidecar error")) {
|
|
2365
2434
|
throw err;
|
|
2366
2435
|
}
|
|
2367
|
-
|
|
2436
|
+
log4.error("Sidecar connection error", { endpoint, error: err.message });
|
|
2368
2437
|
throw new Error(`Sidecar connection error: ${err.message}`);
|
|
2369
2438
|
}
|
|
2370
2439
|
}
|
|
@@ -2655,7 +2724,7 @@ function acquireBrowserLock() {
|
|
|
2655
2724
|
}
|
|
2656
2725
|
|
|
2657
2726
|
// src/toolRegistry.ts
|
|
2658
|
-
var
|
|
2727
|
+
var log5 = createLogger("tool-registry");
|
|
2659
2728
|
var USER_CANCELLED_RESULT = "[USER CANCELLED] The user manually cancelled this tool. Do not retry it automatically \u2014 wait for the user\u2019s next message for direction.";
|
|
2660
2729
|
var ToolRegistry = class {
|
|
2661
2730
|
entries = /* @__PURE__ */ new Map();
|
|
@@ -2682,7 +2751,7 @@ var ToolRegistry = class {
|
|
|
2682
2751
|
if (!entry) {
|
|
2683
2752
|
return false;
|
|
2684
2753
|
}
|
|
2685
|
-
|
|
2754
|
+
log5.info("Tool stopped", { toolCallId: id, name: entry.name, mode });
|
|
2686
2755
|
entry.abortController.abort(mode);
|
|
2687
2756
|
if (mode === "graceful") {
|
|
2688
2757
|
const partial = entry.getPartialResult?.() ?? "";
|
|
@@ -2715,7 +2784,7 @@ ${partial}` : "[INTERRUPTED] Tool execution was stopped.";
|
|
|
2715
2784
|
if (!entry) {
|
|
2716
2785
|
return false;
|
|
2717
2786
|
}
|
|
2718
|
-
|
|
2787
|
+
log5.info("Tool restarted", { toolCallId: id, name: entry.name });
|
|
2719
2788
|
entry.abortController.abort("restart");
|
|
2720
2789
|
const newInput = patchedInput ? { ...entry.input, ...patchedInput } : entry.input;
|
|
2721
2790
|
this.onEvent?.({
|
|
@@ -2963,7 +3032,7 @@ ${content}` : attachmentHeader;
|
|
|
2963
3032
|
}
|
|
2964
3033
|
|
|
2965
3034
|
// src/subagents/runner.ts
|
|
2966
|
-
var
|
|
3035
|
+
var log6 = createLogger("sub-agent");
|
|
2967
3036
|
async function runSubAgent(config) {
|
|
2968
3037
|
const {
|
|
2969
3038
|
system,
|
|
@@ -2990,7 +3059,7 @@ async function runSubAgent(config) {
|
|
|
2990
3059
|
const signal = background ? bgAbort.signal : parentSignal;
|
|
2991
3060
|
const agentName = subAgentId || "sub-agent";
|
|
2992
3061
|
const runStart = Date.now();
|
|
2993
|
-
|
|
3062
|
+
log6.info("Sub-agent started", { requestId, parentToolId, agentName });
|
|
2994
3063
|
const emit = (e) => {
|
|
2995
3064
|
onEvent({ ...e, parentToolId });
|
|
2996
3065
|
};
|
|
@@ -3207,7 +3276,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
|
|
|
3207
3276
|
...hasArtifacts ? { artifacts } : {}
|
|
3208
3277
|
};
|
|
3209
3278
|
}
|
|
3210
|
-
|
|
3279
|
+
log6.info("Tools executing", {
|
|
3211
3280
|
requestId,
|
|
3212
3281
|
parentToolId,
|
|
3213
3282
|
count: toolCalls.length,
|
|
@@ -3284,7 +3353,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
|
|
|
3284
3353
|
run2(tc.input);
|
|
3285
3354
|
const r = await resultPromise;
|
|
3286
3355
|
toolRegistry?.unregister(tc.id);
|
|
3287
|
-
|
|
3356
|
+
log6.info("Tool completed", {
|
|
3288
3357
|
requestId,
|
|
3289
3358
|
parentToolId,
|
|
3290
3359
|
toolCallId: tc.id,
|
|
@@ -3335,7 +3404,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
|
|
|
3335
3404
|
const wrapRun = async () => {
|
|
3336
3405
|
try {
|
|
3337
3406
|
const result = await run();
|
|
3338
|
-
|
|
3407
|
+
log6.info("Sub-agent complete", {
|
|
3339
3408
|
requestId,
|
|
3340
3409
|
parentToolId,
|
|
3341
3410
|
agentName,
|
|
@@ -3344,7 +3413,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
|
|
|
3344
3413
|
});
|
|
3345
3414
|
return result;
|
|
3346
3415
|
} catch (err) {
|
|
3347
|
-
|
|
3416
|
+
log6.warn("Sub-agent error", {
|
|
3348
3417
|
requestId,
|
|
3349
3418
|
parentToolId,
|
|
3350
3419
|
agentName,
|
|
@@ -3356,7 +3425,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
|
|
|
3356
3425
|
if (!background) {
|
|
3357
3426
|
return wrapRun();
|
|
3358
3427
|
}
|
|
3359
|
-
|
|
3428
|
+
log6.info("Sub-agent backgrounded", { requestId, parentToolId, agentName });
|
|
3360
3429
|
toolRegistry?.register({
|
|
3361
3430
|
id: parentToolId,
|
|
3362
3431
|
name: agentName,
|
|
@@ -3648,7 +3717,7 @@ function resolveModel(surfaceId, models, fallback) {
|
|
|
3648
3717
|
}
|
|
3649
3718
|
|
|
3650
3719
|
// src/subagents/browserAutomation/index.ts
|
|
3651
|
-
var
|
|
3720
|
+
var log7 = createLogger("browser-automation");
|
|
3652
3721
|
async function runBrowserAutomation(task, context, opts) {
|
|
3653
3722
|
const release = await acquireBrowserLock();
|
|
3654
3723
|
try {
|
|
@@ -3747,7 +3816,7 @@ async function runBrowserAutomation(task, context, opts) {
|
|
|
3747
3816
|
}
|
|
3748
3817
|
}
|
|
3749
3818
|
} catch {
|
|
3750
|
-
|
|
3819
|
+
log7.debug("Failed to parse batch analysis result", {
|
|
3751
3820
|
batchResult
|
|
3752
3821
|
});
|
|
3753
3822
|
}
|
|
@@ -4828,7 +4897,7 @@ async function executeDesignExpertTool(name, input, context, toolCallId, onLog)
|
|
|
4828
4897
|
// src/subagents/designExpert/data/sampleCache.ts
|
|
4829
4898
|
import fs18 from "fs";
|
|
4830
4899
|
var SAMPLE_FILE = ".remy-design-sample.json";
|
|
4831
|
-
var
|
|
4900
|
+
var cached2 = null;
|
|
4832
4901
|
function generateIndices(poolSize, sampleSize) {
|
|
4833
4902
|
const n = Math.min(sampleSize, poolSize);
|
|
4834
4903
|
const indices = Array.from({ length: poolSize }, (_, i) => i);
|
|
@@ -4852,8 +4921,8 @@ function save(indices) {
|
|
|
4852
4921
|
}
|
|
4853
4922
|
}
|
|
4854
4923
|
function getSampleIndices(pools, sizes) {
|
|
4855
|
-
if (
|
|
4856
|
-
return
|
|
4924
|
+
if (cached2) {
|
|
4925
|
+
return cached2;
|
|
4857
4926
|
}
|
|
4858
4927
|
const loaded = load();
|
|
4859
4928
|
if (loaded) {
|
|
@@ -4868,10 +4937,10 @@ function getSampleIndices(pools, sizes) {
|
|
|
4868
4937
|
if (dirty2) {
|
|
4869
4938
|
save(loaded);
|
|
4870
4939
|
}
|
|
4871
|
-
|
|
4872
|
-
return
|
|
4940
|
+
cached2 = loaded;
|
|
4941
|
+
return cached2;
|
|
4873
4942
|
}
|
|
4874
|
-
|
|
4943
|
+
cached2 = {
|
|
4875
4944
|
uiInspiration: generateIndices(pools.uiInspiration, sizes.uiInspiration),
|
|
4876
4945
|
designReferences: generateIndices(
|
|
4877
4946
|
pools.designReferences,
|
|
@@ -4879,8 +4948,8 @@ function getSampleIndices(pools, sizes) {
|
|
|
4879
4948
|
),
|
|
4880
4949
|
fonts: generateIndices(pools.fonts, sizes.fonts)
|
|
4881
4950
|
};
|
|
4882
|
-
save(
|
|
4883
|
-
return
|
|
4951
|
+
save(cached2);
|
|
4952
|
+
return cached2;
|
|
4884
4953
|
}
|
|
4885
4954
|
function pickByIndices(arr, indices) {
|
|
4886
4955
|
return indices.filter((i) => i < arr.length).map((i) => arr[i]);
|
|
@@ -5642,7 +5711,7 @@ function executeTool(name, input, context) {
|
|
|
5642
5711
|
}
|
|
5643
5712
|
|
|
5644
5713
|
// src/compaction/index.ts
|
|
5645
|
-
var
|
|
5714
|
+
var log8 = createLogger("compaction");
|
|
5646
5715
|
var CONVERSATION_SUMMARY_PROMPT = readAsset("compaction", "conversation.md");
|
|
5647
5716
|
var SUBAGENT_SUMMARY_PROMPT = readAsset("compaction", "subagent.md");
|
|
5648
5717
|
var SUMMARIZABLE_SUBAGENTS = ["visualDesignExpert", "productVision"];
|
|
@@ -5708,7 +5777,7 @@ async function compactConversation(messages, apiConfig, system, tools2, model) {
|
|
|
5708
5777
|
}
|
|
5709
5778
|
]
|
|
5710
5779
|
}));
|
|
5711
|
-
|
|
5780
|
+
log8.info("Compaction complete", { summaries: summaries.length });
|
|
5712
5781
|
return checkpointMessages;
|
|
5713
5782
|
}
|
|
5714
5783
|
function findSafeInsertionPoint(messages) {
|
|
@@ -5848,7 +5917,7 @@ async function generateSummary(apiConfig, name, compactionPrompt, messagesToSumm
|
|
|
5848
5917
|
}
|
|
5849
5918
|
if (serialized.length > CHUNK_CHAR_LIMIT && messagesToSummarize.length > 1) {
|
|
5850
5919
|
const mid = Math.floor(messagesToSummarize.length / 2);
|
|
5851
|
-
|
|
5920
|
+
log8.info("Chunking summary", {
|
|
5852
5921
|
name,
|
|
5853
5922
|
messageCount: messagesToSummarize.length,
|
|
5854
5923
|
serializedLength: serialized.length
|
|
@@ -5876,7 +5945,7 @@ async function generateSummary(apiConfig, name, compactionPrompt, messagesToSumm
|
|
|
5876
5945
|
const parts = [first, second].filter((p) => !!p);
|
|
5877
5946
|
return parts.length > 0 ? parts.join("\n\n---\n\n") : null;
|
|
5878
5947
|
}
|
|
5879
|
-
|
|
5948
|
+
log8.info("Generating summary", {
|
|
5880
5949
|
name,
|
|
5881
5950
|
messageCount: messagesToSummarize.length,
|
|
5882
5951
|
cacheReuse: !!mainSystem
|
|
@@ -5918,20 +5987,20 @@ ${serialized}` : serialized;
|
|
|
5918
5987
|
toolNames: []
|
|
5919
5988
|
});
|
|
5920
5989
|
} else if (event.type === "error") {
|
|
5921
|
-
|
|
5990
|
+
log8.error("Summary generation failed", { name, error: event.error });
|
|
5922
5991
|
return null;
|
|
5923
5992
|
}
|
|
5924
5993
|
}
|
|
5925
5994
|
if (!summaryText.trim()) {
|
|
5926
|
-
|
|
5995
|
+
log8.warn("Empty summary generated", { name });
|
|
5927
5996
|
return null;
|
|
5928
5997
|
}
|
|
5929
|
-
|
|
5998
|
+
log8.info("Summary generated", { name, summaryLength: summaryText.length });
|
|
5930
5999
|
return summaryText.trim();
|
|
5931
6000
|
}
|
|
5932
6001
|
|
|
5933
6002
|
// src/compaction/trigger.ts
|
|
5934
|
-
var
|
|
6003
|
+
var log9 = createLogger("compaction:trigger");
|
|
5935
6004
|
var pendingSummaries = [];
|
|
5936
6005
|
var inflightCompaction = null;
|
|
5937
6006
|
function getPendingSummaries() {
|
|
@@ -5958,11 +6027,11 @@ function triggerCompaction(state, apiConfig, opts = {}) {
|
|
|
5958
6027
|
).then((summaries) => {
|
|
5959
6028
|
pendingSummaries.push(...summaries);
|
|
5960
6029
|
listener?.({ type: "complete", requestId });
|
|
5961
|
-
|
|
6030
|
+
log9.info("Compaction complete");
|
|
5962
6031
|
}).catch((err) => {
|
|
5963
6032
|
const message = err.message || "Compaction failed";
|
|
5964
6033
|
listener?.({ type: "complete", error: message, requestId });
|
|
5965
|
-
|
|
6034
|
+
log9.error("Compaction failed", { error: message });
|
|
5966
6035
|
throw err;
|
|
5967
6036
|
}).finally(() => {
|
|
5968
6037
|
inflightCompaction = null;
|
|
@@ -5974,25 +6043,25 @@ function triggerCompaction(state, apiConfig, opts = {}) {
|
|
|
5974
6043
|
import fs21 from "fs";
|
|
5975
6044
|
import path10 from "path";
|
|
5976
6045
|
import { createHash } from "crypto";
|
|
5977
|
-
var
|
|
6046
|
+
var log10 = createLogger("brandExtraction");
|
|
5978
6047
|
var EXTRACT_PROMPT = readAsset("brandExtraction", "extract.md");
|
|
5979
6048
|
var BRAND_FILE = ".remy-brand.json";
|
|
5980
6049
|
var CACHE_FILE = ".remy-brand.cache.json";
|
|
5981
6050
|
async function runExtraction(apiConfig, model) {
|
|
5982
6051
|
const inputHash = computeInputHash();
|
|
5983
|
-
const
|
|
5984
|
-
if (
|
|
5985
|
-
|
|
6052
|
+
const cached3 = readCache();
|
|
6053
|
+
if (cached3 && cached3.inputHash === inputHash) {
|
|
6054
|
+
log10.debug("Brand inputs unchanged \u2014 skipping extraction", { inputHash });
|
|
5986
6055
|
return null;
|
|
5987
6056
|
}
|
|
5988
|
-
|
|
6057
|
+
log10.info("Extracting brand", { inputHash });
|
|
5989
6058
|
const brand = await extractBrand(apiConfig, model);
|
|
5990
6059
|
if (!brand) {
|
|
5991
|
-
|
|
6060
|
+
log10.warn("Brand extraction failed \u2014 leaving cache untouched");
|
|
5992
6061
|
return null;
|
|
5993
6062
|
}
|
|
5994
6063
|
persistBrand(brand, inputHash);
|
|
5995
|
-
|
|
6064
|
+
log10.info("Brand persisted", { inputHash });
|
|
5996
6065
|
return brand;
|
|
5997
6066
|
}
|
|
5998
6067
|
function computeInputHash() {
|
|
@@ -6058,7 +6127,7 @@ function parseFrontmatter3(filePath) {
|
|
|
6058
6127
|
async function extractBrand(apiConfig, model) {
|
|
6059
6128
|
const corpus = buildCorpus();
|
|
6060
6129
|
if (!corpus.trim()) {
|
|
6061
|
-
|
|
6130
|
+
log10.debug("No spec corpus \u2014 emitting empty brand");
|
|
6062
6131
|
return { version: 1 };
|
|
6063
6132
|
}
|
|
6064
6133
|
let responseText = "";
|
|
@@ -6089,17 +6158,17 @@ async function extractBrand(apiConfig, model) {
|
|
|
6089
6158
|
toolNames: []
|
|
6090
6159
|
});
|
|
6091
6160
|
} else if (event.type === "error") {
|
|
6092
|
-
|
|
6161
|
+
log10.error("Brand extraction stream error", { error: event.error });
|
|
6093
6162
|
return null;
|
|
6094
6163
|
}
|
|
6095
6164
|
}
|
|
6096
6165
|
} catch (err) {
|
|
6097
|
-
|
|
6166
|
+
log10.error("Brand extraction threw", { error: err?.message });
|
|
6098
6167
|
return null;
|
|
6099
6168
|
}
|
|
6100
6169
|
const parsed = parseJsonResponse(responseText);
|
|
6101
6170
|
if (!parsed) {
|
|
6102
|
-
|
|
6171
|
+
log10.warn("Brand extraction returned unparseable JSON", {
|
|
6103
6172
|
preview: responseText.slice(0, 200)
|
|
6104
6173
|
});
|
|
6105
6174
|
return null;
|
|
@@ -6241,7 +6310,7 @@ function readCache() {
|
|
|
6241
6310
|
}
|
|
6242
6311
|
|
|
6243
6312
|
// src/brandExtraction/trigger.ts
|
|
6244
|
-
var
|
|
6313
|
+
var log11 = createLogger("brandExtraction:trigger");
|
|
6245
6314
|
var inflight = false;
|
|
6246
6315
|
var dirty = false;
|
|
6247
6316
|
function triggerBrandExtraction(apiConfig, model) {
|
|
@@ -6251,7 +6320,7 @@ function triggerBrandExtraction(apiConfig, model) {
|
|
|
6251
6320
|
}
|
|
6252
6321
|
inflight = true;
|
|
6253
6322
|
void runExtraction(apiConfig, model).catch((err) => {
|
|
6254
|
-
|
|
6323
|
+
log11.error("Brand extraction failed", { error: err?.message });
|
|
6255
6324
|
}).finally(() => {
|
|
6256
6325
|
inflight = false;
|
|
6257
6326
|
if (dirty) {
|
|
@@ -6264,7 +6333,7 @@ function triggerBrandExtraction(apiConfig, model) {
|
|
|
6264
6333
|
// src/session.ts
|
|
6265
6334
|
import fs22 from "fs";
|
|
6266
6335
|
import path11 from "path";
|
|
6267
|
-
var
|
|
6336
|
+
var log12 = createLogger("session");
|
|
6268
6337
|
var SESSION_FILE = ".remy-session.json";
|
|
6269
6338
|
var ARCHIVE_DIR = ".logs/sessions";
|
|
6270
6339
|
function loadSession(state) {
|
|
@@ -6276,7 +6345,7 @@ function loadSession(state) {
|
|
|
6276
6345
|
}
|
|
6277
6346
|
if (Array.isArray(data.messages) && data.messages.length > 0) {
|
|
6278
6347
|
state.messages = sanitizeMessages(data.messages);
|
|
6279
|
-
|
|
6348
|
+
log12.info("Session loaded", {
|
|
6280
6349
|
messageCount: state.messages.length,
|
|
6281
6350
|
...state.models && { models: state.models }
|
|
6282
6351
|
});
|
|
@@ -6329,9 +6398,9 @@ function saveSession(state) {
|
|
|
6329
6398
|
payload.models = state.models;
|
|
6330
6399
|
}
|
|
6331
6400
|
fs22.writeFileSync(SESSION_FILE, JSON.stringify(payload, null, 2), "utf-8");
|
|
6332
|
-
|
|
6401
|
+
log12.info("Session saved", { messageCount: state.messages.length });
|
|
6333
6402
|
} catch (err) {
|
|
6334
|
-
|
|
6403
|
+
log12.warn("Session save failed", { error: err.message });
|
|
6335
6404
|
}
|
|
6336
6405
|
}
|
|
6337
6406
|
function clearSession(state) {
|
|
@@ -6342,10 +6411,10 @@ function clearSession(state) {
|
|
|
6342
6411
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
6343
6412
|
const dest = path11.join(ARCHIVE_DIR, `cleared-${ts}.json`);
|
|
6344
6413
|
fs22.renameSync(SESSION_FILE, dest);
|
|
6345
|
-
|
|
6414
|
+
log12.info("Session archived on clear", { dest });
|
|
6346
6415
|
}
|
|
6347
6416
|
} catch (err) {
|
|
6348
|
-
|
|
6417
|
+
log12.warn("Session archive on clear failed, deleting instead", {
|
|
6349
6418
|
error: err.message
|
|
6350
6419
|
});
|
|
6351
6420
|
try {
|
|
@@ -6549,7 +6618,7 @@ function friendlyError(raw) {
|
|
|
6549
6618
|
}
|
|
6550
6619
|
|
|
6551
6620
|
// src/agent.ts
|
|
6552
|
-
var
|
|
6621
|
+
var log13 = createLogger("agent");
|
|
6553
6622
|
var BRAND_TRIGGERING_TOOLS = /* @__PURE__ */ new Set(["writeSpec", "editSpec"]);
|
|
6554
6623
|
function getTextContent(blocks) {
|
|
6555
6624
|
return blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
@@ -6598,7 +6667,7 @@ async function runTurn(params) {
|
|
|
6598
6667
|
} = params;
|
|
6599
6668
|
const tools2 = getToolDefinitions(onboardingState);
|
|
6600
6669
|
const excludeToolsFromClearing = tools2.filter((t) => !CLEARABLE_TOOLS.has(t.name)).map((t) => t.name);
|
|
6601
|
-
|
|
6670
|
+
log13.info("Turn started", {
|
|
6602
6671
|
requestId,
|
|
6603
6672
|
model,
|
|
6604
6673
|
toolCount: tools2.length,
|
|
@@ -6851,7 +6920,7 @@ async function runTurn(params) {
|
|
|
6851
6920
|
const tool = getToolByName(event.name);
|
|
6852
6921
|
const wasStreamed = acc?.started ?? false;
|
|
6853
6922
|
const isInputStreaming = !!tool?.streaming?.partialInput;
|
|
6854
|
-
|
|
6923
|
+
log13.info("Tool received", {
|
|
6855
6924
|
requestId,
|
|
6856
6925
|
toolCallId: event.id,
|
|
6857
6926
|
name: event.name
|
|
@@ -6969,7 +7038,7 @@ async function runTurn(params) {
|
|
|
6969
7038
|
});
|
|
6970
7039
|
return;
|
|
6971
7040
|
}
|
|
6972
|
-
|
|
7041
|
+
log13.info("Tools executing", {
|
|
6973
7042
|
requestId,
|
|
6974
7043
|
count: toolCalls.length,
|
|
6975
7044
|
tools: toolCalls.map((tc) => tc.name)
|
|
@@ -7016,7 +7085,7 @@ async function runTurn(params) {
|
|
|
7016
7085
|
let result;
|
|
7017
7086
|
if (EXTERNAL_TOOLS.has(tc.name) && resolveExternalTool) {
|
|
7018
7087
|
saveSession(state);
|
|
7019
|
-
|
|
7088
|
+
log13.info("Waiting for external tool result", {
|
|
7020
7089
|
requestId,
|
|
7021
7090
|
toolCallId: tc.id,
|
|
7022
7091
|
name: tc.name
|
|
@@ -7084,7 +7153,7 @@ async function runTurn(params) {
|
|
|
7084
7153
|
if (!tc.input.background) {
|
|
7085
7154
|
toolRegistry?.unregister(tc.id);
|
|
7086
7155
|
}
|
|
7087
|
-
|
|
7156
|
+
log13.info("Tool completed", {
|
|
7088
7157
|
requestId,
|
|
7089
7158
|
toolCallId: tc.id,
|
|
7090
7159
|
name: tc.name,
|
|
@@ -7148,7 +7217,7 @@ async function runTurn(params) {
|
|
|
7148
7217
|
import { mkdirSync, existsSync } from "fs";
|
|
7149
7218
|
import { writeFile } from "fs/promises";
|
|
7150
7219
|
import { basename, join, extname } from "path";
|
|
7151
|
-
var
|
|
7220
|
+
var log14 = createLogger("headless:attachments");
|
|
7152
7221
|
var UPLOADS_DIR = "src/.user-uploads";
|
|
7153
7222
|
function filenameFromUrl(url) {
|
|
7154
7223
|
try {
|
|
@@ -7196,7 +7265,7 @@ async function persistAttachments(attachments) {
|
|
|
7196
7265
|
}
|
|
7197
7266
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
7198
7267
|
await writeFile(localPath, buffer);
|
|
7199
|
-
|
|
7268
|
+
log14.info("Attachment saved", {
|
|
7200
7269
|
filename: name,
|
|
7201
7270
|
path: localPath,
|
|
7202
7271
|
bytes: buffer.length
|
|
@@ -7210,7 +7279,7 @@ async function persistAttachments(attachments) {
|
|
|
7210
7279
|
if (textRes.ok) {
|
|
7211
7280
|
extractedTextPath = `${localPath}.txt`;
|
|
7212
7281
|
await writeFile(extractedTextPath, await textRes.text(), "utf-8");
|
|
7213
|
-
|
|
7282
|
+
log14.info("Extracted text saved", { path: extractedTextPath });
|
|
7214
7283
|
}
|
|
7215
7284
|
} catch {
|
|
7216
7285
|
}
|
|
@@ -7433,7 +7502,7 @@ function getActionChain(startName) {
|
|
|
7433
7502
|
}
|
|
7434
7503
|
|
|
7435
7504
|
// src/headless/index.ts
|
|
7436
|
-
var
|
|
7505
|
+
var log15 = createLogger("headless");
|
|
7437
7506
|
var EXTERNAL_TOOL_TIMEOUT_MS = 3e5;
|
|
7438
7507
|
var USER_FACING_TOOLS = /* @__PURE__ */ new Set([
|
|
7439
7508
|
"promptUser",
|
|
@@ -7500,6 +7569,7 @@ var HeadlessSession = class {
|
|
|
7500
7569
|
apiKey: this.opts.apiKey,
|
|
7501
7570
|
baseUrl: this.opts.baseUrl
|
|
7502
7571
|
});
|
|
7572
|
+
await initOrgContext(this.config);
|
|
7503
7573
|
const resumed = loadSession(this.state);
|
|
7504
7574
|
this.queue = new MessageQueue(loadQueue(), () => {
|
|
7505
7575
|
this.persistStats();
|
|
@@ -7577,7 +7647,7 @@ var HeadlessSession = class {
|
|
|
7577
7647
|
}
|
|
7578
7648
|
const line = JSON.stringify(payload) + "\n";
|
|
7579
7649
|
if (event === "history") {
|
|
7580
|
-
|
|
7650
|
+
log15.info("Wrote history event to stdout", {
|
|
7581
7651
|
requestId,
|
|
7582
7652
|
bytes: line.length
|
|
7583
7653
|
});
|
|
@@ -7654,7 +7724,7 @@ var HeadlessSession = class {
|
|
|
7654
7724
|
if (this.sessionStats.lastContextSize <= FORCED_COMPACTION_THRESHOLD_TOKENS) {
|
|
7655
7725
|
return;
|
|
7656
7726
|
}
|
|
7657
|
-
|
|
7727
|
+
log15.info("Forced compaction gate triggered", {
|
|
7658
7728
|
contextSize: this.sessionStats.lastContextSize,
|
|
7659
7729
|
threshold: FORCED_COMPACTION_THRESHOLD_TOKENS,
|
|
7660
7730
|
requestId
|
|
@@ -7681,7 +7751,7 @@ var HeadlessSession = class {
|
|
|
7681
7751
|
}
|
|
7682
7752
|
onBackgroundComplete = (toolCallId, name, result, subAgentMessages) => {
|
|
7683
7753
|
this.pendingBlockUpdates.push({ toolCallId, result, subAgentMessages });
|
|
7684
|
-
|
|
7754
|
+
log15.info("Background complete", {
|
|
7685
7755
|
toolCallId,
|
|
7686
7756
|
name,
|
|
7687
7757
|
requestId: this.currentRequestId
|
|
@@ -7903,7 +7973,7 @@ var HeadlessSession = class {
|
|
|
7903
7973
|
await this.runForcedCompactionIfNeeded(requestId);
|
|
7904
7974
|
const attachments = parsed.attachments;
|
|
7905
7975
|
if (attachments?.length) {
|
|
7906
|
-
|
|
7976
|
+
log15.info("Message has attachments", {
|
|
7907
7977
|
count: attachments.length,
|
|
7908
7978
|
urls: attachments.map((a) => a.url)
|
|
7909
7979
|
});
|
|
@@ -7919,7 +7989,7 @@ var HeadlessSession = class {
|
|
|
7919
7989
|
attachmentHeader = header;
|
|
7920
7990
|
}
|
|
7921
7991
|
} catch (err) {
|
|
7922
|
-
|
|
7992
|
+
log15.warn("Attachment persistence failed", { error: err.message });
|
|
7923
7993
|
}
|
|
7924
7994
|
}
|
|
7925
7995
|
let resolved = null;
|
|
@@ -7980,7 +8050,7 @@ var HeadlessSession = class {
|
|
|
7980
8050
|
error: "Turn ended unexpectedly"
|
|
7981
8051
|
});
|
|
7982
8052
|
}
|
|
7983
|
-
|
|
8053
|
+
log15.info("Turn complete", {
|
|
7984
8054
|
requestId,
|
|
7985
8055
|
durationMs: Date.now() - this.turnStart
|
|
7986
8056
|
});
|
|
@@ -7992,7 +8062,7 @@ var HeadlessSession = class {
|
|
|
7992
8062
|
error: err.message
|
|
7993
8063
|
});
|
|
7994
8064
|
}
|
|
7995
|
-
|
|
8065
|
+
log15.warn("Command failed", {
|
|
7996
8066
|
action: "message",
|
|
7997
8067
|
requestId,
|
|
7998
8068
|
error: err.message
|
|
@@ -8142,7 +8212,7 @@ var HeadlessSession = class {
|
|
|
8142
8212
|
try {
|
|
8143
8213
|
parsed = JSON.parse(line);
|
|
8144
8214
|
} catch (err) {
|
|
8145
|
-
|
|
8215
|
+
log15.warn("Invalid JSON on stdin", {
|
|
8146
8216
|
error: err.message,
|
|
8147
8217
|
lineLength: line.length,
|
|
8148
8218
|
preview: line.slice(0, 200)
|
|
@@ -8151,7 +8221,7 @@ var HeadlessSession = class {
|
|
|
8151
8221
|
return;
|
|
8152
8222
|
}
|
|
8153
8223
|
const { action, requestId } = parsed;
|
|
8154
|
-
|
|
8224
|
+
log15.info("Command received", { action, requestId });
|
|
8155
8225
|
if (action === "tool_result" && parsed.id) {
|
|
8156
8226
|
const id = parsed.id;
|
|
8157
8227
|
const result = parsed.result ?? "";
|
|
@@ -8160,7 +8230,7 @@ var HeadlessSession = class {
|
|
|
8160
8230
|
this.pendingTools.delete(id);
|
|
8161
8231
|
pending.resolve(result);
|
|
8162
8232
|
} else if (!this.running) {
|
|
8163
|
-
|
|
8233
|
+
log15.info("Late tool_result while idle, dismissing", { id });
|
|
8164
8234
|
this.emit("completed", { success: true }, requestId);
|
|
8165
8235
|
} else {
|
|
8166
8236
|
this.earlyResults.set(id, result);
|
|
@@ -8179,7 +8249,7 @@ var HeadlessSession = class {
|
|
|
8179
8249
|
startIndex--;
|
|
8180
8250
|
}
|
|
8181
8251
|
const endIndex = before;
|
|
8182
|
-
|
|
8252
|
+
log15.info("History response", {
|
|
8183
8253
|
requestId,
|
|
8184
8254
|
startIndex,
|
|
8185
8255
|
endIndex,
|