@slock-ai/daemon 0.57.0 → 0.57.1-play.20260607140323
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/{chunk-DQN3TW2I.js → chunk-4PZVJXRJ.js} +1025 -346
- package/dist/core.js +5 -1
- package/dist/drivers/piSdkRunner.js +96 -0
- package/dist/index.js +5 -3
- package/package.json +1 -1
|
@@ -1980,6 +1980,19 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
|
|
|
1980
1980
|
return candidates.filter((candidate) => existsSync(candidate.path));
|
|
1981
1981
|
}
|
|
1982
1982
|
|
|
1983
|
+
// src/authEnv.ts
|
|
1984
|
+
var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
|
|
1985
|
+
var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
|
|
1986
|
+
function scrubDaemonAuthEnv(env) {
|
|
1987
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1988
|
+
return env;
|
|
1989
|
+
}
|
|
1990
|
+
function scrubDaemonChildEnv(env) {
|
|
1991
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1992
|
+
delete env[SLOCK_AGENT_TOKEN_ENV];
|
|
1993
|
+
return env;
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1983
1996
|
// src/agentCredentialProxy.ts
|
|
1984
1997
|
import { randomBytes } from "crypto";
|
|
1985
1998
|
import http from "http";
|
|
@@ -2235,6 +2248,412 @@ function reduceApmGatedFlushReadiness(state, input) {
|
|
|
2235
2248
|
};
|
|
2236
2249
|
}
|
|
2237
2250
|
|
|
2251
|
+
// src/agentInboxStateMachine.ts
|
|
2252
|
+
var DEFAULT_HELD_CONTEXT_LIMIT = 3;
|
|
2253
|
+
function planAgentInboxSideEffect(input) {
|
|
2254
|
+
const heldContextLimit = input.heldContextLimit ?? DEFAULT_HELD_CONTEXT_LIMIT;
|
|
2255
|
+
const trace = [
|
|
2256
|
+
{
|
|
2257
|
+
step: "input",
|
|
2258
|
+
data: compactTraceData({
|
|
2259
|
+
action: input.action,
|
|
2260
|
+
target: input.target,
|
|
2261
|
+
continueAnyway: input.continueAnyway,
|
|
2262
|
+
pendingCount: input.pendingMessages.length,
|
|
2263
|
+
recentCount: input.recentMessages.length,
|
|
2264
|
+
existingSeenUpToSeq: input.existingSeenUpToSeq,
|
|
2265
|
+
modelSeenSeq: input.modelSeenSeq
|
|
2266
|
+
})
|
|
2267
|
+
}
|
|
2268
|
+
];
|
|
2269
|
+
if (input.continueAnyway) {
|
|
2270
|
+
appendTrace(trace, "continue_anyway_bypass");
|
|
2271
|
+
return forwardPlan(input, {
|
|
2272
|
+
action: input.action,
|
|
2273
|
+
decision: "bypass",
|
|
2274
|
+
target: input.target,
|
|
2275
|
+
inboxTrustState: "trusted",
|
|
2276
|
+
reason: "continue_anyway"
|
|
2277
|
+
}, trace);
|
|
2278
|
+
}
|
|
2279
|
+
if (input.pendingMessages.length > 0) {
|
|
2280
|
+
appendTrace(trace, "pending_messages_found", { pendingCount: input.pendingMessages.length });
|
|
2281
|
+
const pending = sortInboxMessagesBySeq(normalizeInboxVisibleMessages(input.pendingMessages, input.target));
|
|
2282
|
+
const boundary2 = resolveFreshnessBoundary(pending);
|
|
2283
|
+
if (!boundary2.ok) {
|
|
2284
|
+
appendTrace(trace, "pending_context_missing_boundary");
|
|
2285
|
+
return forwardWithoutDecision(input, trace);
|
|
2286
|
+
}
|
|
2287
|
+
const alreadySeenPending = [];
|
|
2288
|
+
const unconsumedMessages = [];
|
|
2289
|
+
for (const message of pending) {
|
|
2290
|
+
if (isMessageModelSeen(input, message)) {
|
|
2291
|
+
alreadySeenPending.push(message);
|
|
2292
|
+
} else {
|
|
2293
|
+
unconsumedMessages.push(message);
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
appendTrace(trace, "pending_context_classified", {
|
|
2297
|
+
pendingCount: pending.length,
|
|
2298
|
+
unseenCount: unconsumedMessages.length
|
|
2299
|
+
});
|
|
2300
|
+
if (unconsumedMessages.length === 0) {
|
|
2301
|
+
const contiguousBoundary = maxKnownContiguousBoundary(input);
|
|
2302
|
+
const canAdvanceBoundary = typeof contiguousBoundary === "number" && contiguousBoundary >= boundary2.seenUpToSeq;
|
|
2303
|
+
appendTrace(trace, "pending_context_already_seen", { boundarySeq: boundary2.seenUpToSeq });
|
|
2304
|
+
return forwardPlan(input, {
|
|
2305
|
+
action: input.action,
|
|
2306
|
+
decision: "forward",
|
|
2307
|
+
target: input.target,
|
|
2308
|
+
inboxTrustState: "trusted",
|
|
2309
|
+
reason: "exact_target_pending_already_seen",
|
|
2310
|
+
pendingCount: pending.length,
|
|
2311
|
+
pendingMaxSeq: boundary2.seenUpToSeq,
|
|
2312
|
+
modelSeenSeq: contiguousBoundary,
|
|
2313
|
+
heldMessageCount: 0,
|
|
2314
|
+
omittedMessageCount: 0
|
|
2315
|
+
}, trace, {
|
|
2316
|
+
forwardSeenUpToSeq: input.action === "send" && canAdvanceBoundary ? boundary2.seenUpToSeq : void 0,
|
|
2317
|
+
consumeEffect: {
|
|
2318
|
+
type: "consume_visible_messages",
|
|
2319
|
+
target: input.target,
|
|
2320
|
+
messages: exactSeenConsumeMessages(input, pending),
|
|
2321
|
+
boundarySeq: canAdvanceBoundary ? boundary2.seenUpToSeq : void 0,
|
|
2322
|
+
source: "side_effect_preflight_context"
|
|
2323
|
+
}
|
|
2324
|
+
});
|
|
2325
|
+
}
|
|
2326
|
+
const heldBoundary = resolveFreshnessBoundary(unconsumedMessages);
|
|
2327
|
+
if (heldBoundary.ok) {
|
|
2328
|
+
const heldMessages = latestVisibleMessages(unconsumedMessages, heldContextLimit);
|
|
2329
|
+
const omittedMessageCount = Math.max(0, unconsumedMessages.length - heldMessages.length);
|
|
2330
|
+
const context = {
|
|
2331
|
+
heldMessages,
|
|
2332
|
+
newMessageCount: unconsumedMessages.length,
|
|
2333
|
+
shownMessageCount: heldMessages.length,
|
|
2334
|
+
omittedMessageCount,
|
|
2335
|
+
seenUpToSeq: heldBoundary.seenUpToSeq
|
|
2336
|
+
};
|
|
2337
|
+
appendTrace(trace, "held_context_built", {
|
|
2338
|
+
boundarySeq: boundary2.seenUpToSeq,
|
|
2339
|
+
heldBoundarySeq: heldBoundary.seenUpToSeq,
|
|
2340
|
+
heldCount: context.shownMessageCount,
|
|
2341
|
+
omittedCount: context.omittedMessageCount
|
|
2342
|
+
});
|
|
2343
|
+
return heldPlan(input, {
|
|
2344
|
+
decision: {
|
|
2345
|
+
action: input.action,
|
|
2346
|
+
decision: "local_hold",
|
|
2347
|
+
target: input.target,
|
|
2348
|
+
inboxTrustState: "trusted",
|
|
2349
|
+
reason: "exact_target_pending",
|
|
2350
|
+
pendingCount: input.pendingMessages.length,
|
|
2351
|
+
pendingMaxSeq: heldBoundary.seenUpToSeq,
|
|
2352
|
+
modelSeenSeq: input.modelSeenSeq,
|
|
2353
|
+
heldMessageCount: context.shownMessageCount,
|
|
2354
|
+
omittedMessageCount: context.omittedMessageCount
|
|
2355
|
+
},
|
|
2356
|
+
context,
|
|
2357
|
+
consumeMessages: sortInboxMessagesBySeq([
|
|
2358
|
+
...exactSeenConsumeMessages(input, alreadySeenPending),
|
|
2359
|
+
...context.heldMessages
|
|
2360
|
+
]),
|
|
2361
|
+
consumeBoundarySeq: heldBoundary.seenUpToSeq
|
|
2362
|
+
}, trace);
|
|
2363
|
+
}
|
|
2364
|
+
appendTrace(trace, "pending_unseen_context_missing_boundary");
|
|
2365
|
+
return forwardWithoutDecision(input, trace);
|
|
2366
|
+
}
|
|
2367
|
+
const boundary = Math.max(input.existingSeenUpToSeq ?? 0, input.modelSeenSeq ?? 0);
|
|
2368
|
+
appendTrace(trace, "model_boundary_checked", { boundary });
|
|
2369
|
+
if (boundary > 0) {
|
|
2370
|
+
appendTrace(trace, "model_boundary_selected", { boundary });
|
|
2371
|
+
return forwardPlan(input, {
|
|
2372
|
+
action: input.action,
|
|
2373
|
+
decision: "forward",
|
|
2374
|
+
target: input.target,
|
|
2375
|
+
inboxTrustState: "trusted",
|
|
2376
|
+
reason: "model_seen_boundary",
|
|
2377
|
+
pendingCount: 0,
|
|
2378
|
+
modelSeenSeq: boundary
|
|
2379
|
+
}, trace, { forwardSeenUpToSeq: input.action === "send" ? boundary : void 0 });
|
|
2380
|
+
}
|
|
2381
|
+
if (input.recentMessages.length > 0) {
|
|
2382
|
+
return planFirstTouchRecentContext(input, heldContextLimit, trace);
|
|
2383
|
+
}
|
|
2384
|
+
appendTrace(trace, "no_context_available");
|
|
2385
|
+
return forwardPlan(input, {
|
|
2386
|
+
action: input.action,
|
|
2387
|
+
decision: "forward",
|
|
2388
|
+
target: input.target,
|
|
2389
|
+
inboxTrustState: "trusted",
|
|
2390
|
+
reason: "no_exact_target_pending_or_recent_context",
|
|
2391
|
+
pendingCount: 0,
|
|
2392
|
+
modelSeenSeq: 0
|
|
2393
|
+
}, trace);
|
|
2394
|
+
}
|
|
2395
|
+
function normalizeInboxVisibleMessage(message, target) {
|
|
2396
|
+
const targetFields = target ? parseTargetFields(target) : {};
|
|
2397
|
+
const normalized = {
|
|
2398
|
+
...targetFields,
|
|
2399
|
+
...message,
|
|
2400
|
+
message_id: message.message_id ?? message.id,
|
|
2401
|
+
timestamp: message.timestamp ?? message.createdAt,
|
|
2402
|
+
sender_type: message.sender_type ?? message.senderType,
|
|
2403
|
+
sender_name: message.sender_name ?? message.senderName,
|
|
2404
|
+
sender_description: message.sender_description ?? message.senderDescription ?? null
|
|
2405
|
+
};
|
|
2406
|
+
const senderId = messageSenderId(message);
|
|
2407
|
+
if (senderId) normalized.sender_id = senderId;
|
|
2408
|
+
return normalized;
|
|
2409
|
+
}
|
|
2410
|
+
function normalizeInboxVisibleMessages(messages, target) {
|
|
2411
|
+
return messages.map((message) => normalizeInboxVisibleMessage(message, target));
|
|
2412
|
+
}
|
|
2413
|
+
function maxInboxMessageSeq(messages) {
|
|
2414
|
+
let maxSeq = 0;
|
|
2415
|
+
for (const message of messages) {
|
|
2416
|
+
const seq = Math.floor(messageSeq(message));
|
|
2417
|
+
if (Number.isFinite(seq) && seq > 0) maxSeq = Math.max(maxSeq, seq);
|
|
2418
|
+
}
|
|
2419
|
+
return maxSeq > 0 ? maxSeq : void 0;
|
|
2420
|
+
}
|
|
2421
|
+
function maxKnownContiguousBoundary(input) {
|
|
2422
|
+
const boundary = Math.max(input.existingSeenUpToSeq ?? 0, input.modelSeenSeq ?? 0);
|
|
2423
|
+
return boundary > 0 ? boundary : void 0;
|
|
2424
|
+
}
|
|
2425
|
+
function exactSeenConsumeMessages(input, messages) {
|
|
2426
|
+
const boundary = maxKnownContiguousBoundary(input);
|
|
2427
|
+
return messages.map((message) => {
|
|
2428
|
+
const seq = Math.floor(messageSeq(message));
|
|
2429
|
+
if (Number.isFinite(seq) && seq > 0 && typeof boundary === "number" && boundary >= seq) {
|
|
2430
|
+
return message;
|
|
2431
|
+
}
|
|
2432
|
+
const id = typeof message.message_id === "string" && message.message_id.length > 0 ? message.message_id : typeof message.id === "string" && message.id.length > 0 ? message.id : void 0;
|
|
2433
|
+
return id ? { ...message, seq: void 0 } : message;
|
|
2434
|
+
});
|
|
2435
|
+
}
|
|
2436
|
+
function sortInboxMessagesBySeq(messages) {
|
|
2437
|
+
return [...messages].sort((a, b) => messageSeq(a) - messageSeq(b));
|
|
2438
|
+
}
|
|
2439
|
+
function planFirstTouchRecentContext(input, heldContextLimit, trace) {
|
|
2440
|
+
const recent = sortInboxMessagesBySeq(normalizeInboxVisibleMessages(input.recentMessages, input.target));
|
|
2441
|
+
const unconsumedMessages = recent.filter((message) => !isMessageModelSeen(input, message));
|
|
2442
|
+
appendTrace(trace, "recent_context_loaded", {
|
|
2443
|
+
recentCount: recent.length,
|
|
2444
|
+
unseenCount: unconsumedMessages.length
|
|
2445
|
+
});
|
|
2446
|
+
const boundary = resolveFreshnessBoundary(recent);
|
|
2447
|
+
if (!boundary.ok) {
|
|
2448
|
+
appendTrace(trace, "recent_context_missing_boundary");
|
|
2449
|
+
return forwardPlan(input, {
|
|
2450
|
+
action: input.action,
|
|
2451
|
+
decision: "forward",
|
|
2452
|
+
target: input.target,
|
|
2453
|
+
inboxTrustState: "untrusted",
|
|
2454
|
+
reason: "target_first_touch_recent_context_without_seq_boundary",
|
|
2455
|
+
pendingCount: 0,
|
|
2456
|
+
modelSeenSeq: 0
|
|
2457
|
+
}, trace);
|
|
2458
|
+
}
|
|
2459
|
+
appendTrace(trace, "recent_boundary_resolved", { boundarySeq: boundary.seenUpToSeq });
|
|
2460
|
+
if (unconsumedMessages.length === 0) {
|
|
2461
|
+
appendTrace(trace, "recent_context_already_seen");
|
|
2462
|
+
return forwardPlan(input, {
|
|
2463
|
+
action: input.action,
|
|
2464
|
+
decision: "forward",
|
|
2465
|
+
target: input.target,
|
|
2466
|
+
inboxTrustState: "untrusted",
|
|
2467
|
+
reason: "target_first_touch_recent_context_already_seen",
|
|
2468
|
+
pendingCount: 0,
|
|
2469
|
+
pendingMaxSeq: boundary.seenUpToSeq,
|
|
2470
|
+
modelSeenSeq: boundary.seenUpToSeq,
|
|
2471
|
+
heldMessageCount: 0,
|
|
2472
|
+
omittedMessageCount: 0
|
|
2473
|
+
}, trace, {
|
|
2474
|
+
forwardSeenUpToSeq: input.action === "send" ? boundary.seenUpToSeq : void 0,
|
|
2475
|
+
consumeEffect: {
|
|
2476
|
+
type: "consume_visible_messages",
|
|
2477
|
+
target: input.target,
|
|
2478
|
+
messages: recent,
|
|
2479
|
+
boundarySeq: boundary.seenUpToSeq,
|
|
2480
|
+
source: "side_effect_preflight_context"
|
|
2481
|
+
}
|
|
2482
|
+
});
|
|
2483
|
+
}
|
|
2484
|
+
const heldBoundary = resolveFreshnessBoundary(unconsumedMessages);
|
|
2485
|
+
if (!heldBoundary.ok) {
|
|
2486
|
+
appendTrace(trace, "unseen_context_missing_boundary");
|
|
2487
|
+
return forwardPlan(input, {
|
|
2488
|
+
action: input.action,
|
|
2489
|
+
decision: "forward",
|
|
2490
|
+
target: input.target,
|
|
2491
|
+
inboxTrustState: "untrusted",
|
|
2492
|
+
reason: "target_first_touch_unseen_context_without_seq_boundary",
|
|
2493
|
+
pendingCount: 0,
|
|
2494
|
+
modelSeenSeq: 0
|
|
2495
|
+
}, trace);
|
|
2496
|
+
}
|
|
2497
|
+
appendTrace(trace, "unseen_boundary_resolved", { boundarySeq: heldBoundary.seenUpToSeq });
|
|
2498
|
+
const heldMessages = latestVisibleMessages(unconsumedMessages, heldContextLimit);
|
|
2499
|
+
const omittedMessageCount = Math.max(0, unconsumedMessages.length - heldMessages.length);
|
|
2500
|
+
appendTrace(trace, "unseen_hold_selected", {
|
|
2501
|
+
heldCount: heldMessages.length,
|
|
2502
|
+
omittedCount: omittedMessageCount,
|
|
2503
|
+
consumeBoundarySeq: boundary.seenUpToSeq
|
|
2504
|
+
});
|
|
2505
|
+
return heldPlan(input, {
|
|
2506
|
+
decision: {
|
|
2507
|
+
action: input.action,
|
|
2508
|
+
decision: "syncing_hold",
|
|
2509
|
+
target: input.target,
|
|
2510
|
+
inboxTrustState: "untrusted",
|
|
2511
|
+
reason: "target_first_touch_recent_context",
|
|
2512
|
+
pendingCount: 0,
|
|
2513
|
+
pendingMaxSeq: heldBoundary.seenUpToSeq,
|
|
2514
|
+
modelSeenSeq: 0,
|
|
2515
|
+
heldMessageCount: heldMessages.length,
|
|
2516
|
+
omittedMessageCount
|
|
2517
|
+
},
|
|
2518
|
+
context: {
|
|
2519
|
+
heldMessages,
|
|
2520
|
+
newMessageCount: unconsumedMessages.length,
|
|
2521
|
+
shownMessageCount: heldMessages.length,
|
|
2522
|
+
omittedMessageCount,
|
|
2523
|
+
seenUpToSeq: boundary.seenUpToSeq
|
|
2524
|
+
},
|
|
2525
|
+
consumeMessages: recent,
|
|
2526
|
+
consumeBoundarySeq: boundary.seenUpToSeq
|
|
2527
|
+
}, trace);
|
|
2528
|
+
}
|
|
2529
|
+
function heldPlan(input, held, trace) {
|
|
2530
|
+
const producerFactId = buildApmFreshnessDecisionProducerFactId(input.agentId, held.decision);
|
|
2531
|
+
const decision = { ...held.decision, producerFactId };
|
|
2532
|
+
appendTrace(trace, "plan_built", {
|
|
2533
|
+
outcome: "held",
|
|
2534
|
+
decision: decision.decision,
|
|
2535
|
+
effectCount: 2,
|
|
2536
|
+
localResponseState: "held",
|
|
2537
|
+
seenUpToSeq: held.context.seenUpToSeq
|
|
2538
|
+
});
|
|
2539
|
+
return {
|
|
2540
|
+
outcome: "held",
|
|
2541
|
+
target: input.target,
|
|
2542
|
+
effects: [
|
|
2543
|
+
{
|
|
2544
|
+
type: "consume_visible_messages",
|
|
2545
|
+
target: input.target,
|
|
2546
|
+
messages: held.consumeMessages,
|
|
2547
|
+
boundarySeq: held.consumeBoundarySeq,
|
|
2548
|
+
source: "side_effect_preflight_context"
|
|
2549
|
+
},
|
|
2550
|
+
{ type: "record_freshness_decision", decision }
|
|
2551
|
+
],
|
|
2552
|
+
trace,
|
|
2553
|
+
localResponse: projectApmHeldFreshnessEnvelope({
|
|
2554
|
+
producerFactId,
|
|
2555
|
+
action: input.action,
|
|
2556
|
+
heldMessages: held.context.heldMessages,
|
|
2557
|
+
newMessageCount: held.context.newMessageCount,
|
|
2558
|
+
omittedMessageCount: held.context.omittedMessageCount,
|
|
2559
|
+
seenUpToSeq: held.context.seenUpToSeq
|
|
2560
|
+
}).body
|
|
2561
|
+
};
|
|
2562
|
+
}
|
|
2563
|
+
function forwardPlan(input, decision, trace, options = {}) {
|
|
2564
|
+
appendTrace(trace, "plan_built", {
|
|
2565
|
+
outcome: "forward",
|
|
2566
|
+
decision: decision.decision,
|
|
2567
|
+
effectCount: options.consumeEffect ? 2 : 1,
|
|
2568
|
+
forwardSeenUpToSeq: options.forwardSeenUpToSeq
|
|
2569
|
+
});
|
|
2570
|
+
return {
|
|
2571
|
+
outcome: "forward",
|
|
2572
|
+
target: input.target,
|
|
2573
|
+
forwardSeenUpToSeq: options.forwardSeenUpToSeq,
|
|
2574
|
+
effects: [
|
|
2575
|
+
...options.consumeEffect ? [options.consumeEffect] : [],
|
|
2576
|
+
{ type: "record_freshness_decision", decision }
|
|
2577
|
+
],
|
|
2578
|
+
trace
|
|
2579
|
+
};
|
|
2580
|
+
}
|
|
2581
|
+
function forwardWithoutDecision(input, trace) {
|
|
2582
|
+
appendTrace(trace, "plan_built", {
|
|
2583
|
+
outcome: "forward",
|
|
2584
|
+
decision: "none",
|
|
2585
|
+
effectCount: 0
|
|
2586
|
+
});
|
|
2587
|
+
return {
|
|
2588
|
+
outcome: "forward",
|
|
2589
|
+
target: input.target,
|
|
2590
|
+
effects: [],
|
|
2591
|
+
trace
|
|
2592
|
+
};
|
|
2593
|
+
}
|
|
2594
|
+
function resolveFreshnessBoundary(messages) {
|
|
2595
|
+
const seenUpToSeq = maxInboxMessageSeq(messages);
|
|
2596
|
+
return typeof seenUpToSeq === "number" ? { ok: true, seenUpToSeq } : { ok: false, reason: "missing_seq_boundary" };
|
|
2597
|
+
}
|
|
2598
|
+
function latestVisibleMessages(messages, limit) {
|
|
2599
|
+
const sorted = sortInboxMessagesBySeq(messages);
|
|
2600
|
+
return sorted.slice(Math.max(0, sorted.length - limit));
|
|
2601
|
+
}
|
|
2602
|
+
function isMessageModelSeen(input, message) {
|
|
2603
|
+
const seq = Math.floor(messageSeq(message));
|
|
2604
|
+
if (Number.isFinite(seq) && seq > 0 && typeof input.modelSeenSeq === "number" && input.modelSeenSeq >= seq) return true;
|
|
2605
|
+
return input.isMessageModelSeen?.({ target: input.target, message }) === true;
|
|
2606
|
+
}
|
|
2607
|
+
function messageSeq(message) {
|
|
2608
|
+
return Number(message.seq ?? 0);
|
|
2609
|
+
}
|
|
2610
|
+
function messageSenderId(message) {
|
|
2611
|
+
if (typeof message.sender_id === "string" && message.sender_id.length > 0) return message.sender_id;
|
|
2612
|
+
if (typeof message.senderId === "string" && message.senderId.length > 0) return message.senderId;
|
|
2613
|
+
return void 0;
|
|
2614
|
+
}
|
|
2615
|
+
function appendTrace(trace, step, data) {
|
|
2616
|
+
const compacted = compactTraceData(data);
|
|
2617
|
+
trace.push(Object.keys(compacted).length > 0 ? { step, data: compacted } : { step });
|
|
2618
|
+
}
|
|
2619
|
+
function compactTraceData(data) {
|
|
2620
|
+
const compacted = {};
|
|
2621
|
+
if (!data) return compacted;
|
|
2622
|
+
for (const [key, value] of Object.entries(data)) {
|
|
2623
|
+
if (value !== void 0) compacted[key] = value;
|
|
2624
|
+
}
|
|
2625
|
+
return compacted;
|
|
2626
|
+
}
|
|
2627
|
+
function parseTargetFields(target) {
|
|
2628
|
+
if (target.startsWith("dm:@")) {
|
|
2629
|
+
const rest = target.slice("dm:@".length);
|
|
2630
|
+
const [peer, threadId] = rest.split(":", 2);
|
|
2631
|
+
if (threadId) {
|
|
2632
|
+
return {
|
|
2633
|
+
channel_type: "thread",
|
|
2634
|
+
channel_name: threadId,
|
|
2635
|
+
parent_channel_type: "dm",
|
|
2636
|
+
parent_channel_name: peer
|
|
2637
|
+
};
|
|
2638
|
+
}
|
|
2639
|
+
return { channel_type: "dm", channel_name: peer };
|
|
2640
|
+
}
|
|
2641
|
+
if (target.startsWith("#")) {
|
|
2642
|
+
const rest = target.slice(1);
|
|
2643
|
+
const [channel, threadId] = rest.split(":", 2);
|
|
2644
|
+
if (threadId) {
|
|
2645
|
+
return {
|
|
2646
|
+
channel_type: "thread",
|
|
2647
|
+
channel_name: threadId,
|
|
2648
|
+
parent_channel_type: "channel",
|
|
2649
|
+
parent_channel_name: channel
|
|
2650
|
+
};
|
|
2651
|
+
}
|
|
2652
|
+
return { channel_type: "channel", channel_name: channel };
|
|
2653
|
+
}
|
|
2654
|
+
return {};
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2238
2657
|
// src/agentInboxProjection.ts
|
|
2239
2658
|
function projectAgentInboxSnapshot(messages) {
|
|
2240
2659
|
const buckets = /* @__PURE__ */ new Map();
|
|
@@ -2264,16 +2683,16 @@ function projectBucket(target, messages) {
|
|
|
2264
2683
|
channelType: latest.channel_type,
|
|
2265
2684
|
pendingCount: messages.length,
|
|
2266
2685
|
firstPendingMsgId: messageId(first),
|
|
2267
|
-
firstPendingSeq:
|
|
2686
|
+
firstPendingSeq: messageSeq2(first),
|
|
2268
2687
|
latestMsgId: messageId(latest),
|
|
2269
|
-
latestSeq:
|
|
2688
|
+
latestSeq: messageSeq2(latest),
|
|
2270
2689
|
latestSenderName: latest.sender_name ?? latest.senderName,
|
|
2271
2690
|
latestSenderType: normalizeSenderType(latest.sender_type ?? latest.senderType),
|
|
2272
2691
|
flags: [...flags].sort()
|
|
2273
2692
|
});
|
|
2274
2693
|
}
|
|
2275
2694
|
function compareInboxMessages(a, b) {
|
|
2276
|
-
return (
|
|
2695
|
+
return (messageSeq2(a) ?? 0) - (messageSeq2(b) ?? 0) || (messageId(a) ?? "").localeCompare(messageId(b) ?? "");
|
|
2277
2696
|
}
|
|
2278
2697
|
function formatInboxMessageTarget(message) {
|
|
2279
2698
|
if (message.channel_type === "thread" && message.parent_channel_name && message.channel_name) {
|
|
@@ -2289,7 +2708,7 @@ function messageId(message) {
|
|
|
2289
2708
|
if (!message) return void 0;
|
|
2290
2709
|
return nonEmptyString(message.message_id) ?? nonEmptyString(message.id);
|
|
2291
2710
|
}
|
|
2292
|
-
function
|
|
2711
|
+
function messageSeq2(message) {
|
|
2293
2712
|
if (!message || typeof message.seq !== "number" || !Number.isFinite(message.seq) || message.seq <= 0) return void 0;
|
|
2294
2713
|
return Math.floor(message.seq);
|
|
2295
2714
|
}
|
|
@@ -2814,42 +3233,9 @@ async function readRequestBody(req) {
|
|
|
2814
3233
|
}
|
|
2815
3234
|
return Buffer.concat(chunks);
|
|
2816
3235
|
}
|
|
2817
|
-
function
|
|
3236
|
+
function messageSeq3(message) {
|
|
2818
3237
|
return Number(message.seq ?? 0);
|
|
2819
3238
|
}
|
|
2820
|
-
function maxMessageSeq(messages) {
|
|
2821
|
-
let maxSeq = 0;
|
|
2822
|
-
for (const message of messages) {
|
|
2823
|
-
const seq = Math.floor(messageSeq2(message));
|
|
2824
|
-
if (Number.isFinite(seq) && seq > 0) maxSeq = Math.max(maxSeq, seq);
|
|
2825
|
-
}
|
|
2826
|
-
return maxSeq > 0 ? maxSeq : void 0;
|
|
2827
|
-
}
|
|
2828
|
-
function messageSenderId(message) {
|
|
2829
|
-
if (typeof message.sender_id === "string" && message.sender_id.length > 0) return message.sender_id;
|
|
2830
|
-
if (typeof message.senderId === "string" && message.senderId.length > 0) return message.senderId;
|
|
2831
|
-
return void 0;
|
|
2832
|
-
}
|
|
2833
|
-
function isSelfAuthoredMessage(registration, message) {
|
|
2834
|
-
return messageSenderId(message) === registration.agentId;
|
|
2835
|
-
}
|
|
2836
|
-
function isMessageModelSeen(coordinator, target, message) {
|
|
2837
|
-
const seq = Math.floor(messageSeq2(message));
|
|
2838
|
-
const boundary = coordinator.getBoundary(target);
|
|
2839
|
-
if (Number.isFinite(seq) && seq > 0 && typeof boundary === "number" && boundary >= seq) return true;
|
|
2840
|
-
return coordinator.isMessageModelSeen?.({ target, message }) === true;
|
|
2841
|
-
}
|
|
2842
|
-
function resolveFreshnessBoundary(messages) {
|
|
2843
|
-
const seenUpToSeq = maxMessageSeq(messages);
|
|
2844
|
-
return typeof seenUpToSeq === "number" ? { ok: true, seenUpToSeq } : { ok: false, reason: "missing_seq_boundary" };
|
|
2845
|
-
}
|
|
2846
|
-
function sortBySeq(messages) {
|
|
2847
|
-
return [...messages].sort((a, b) => messageSeq2(a) - messageSeq2(b));
|
|
2848
|
-
}
|
|
2849
|
-
function latestVisibleMessages(messages, limit) {
|
|
2850
|
-
const sorted = sortBySeq(messages);
|
|
2851
|
-
return sorted.slice(Math.max(0, sorted.length - limit));
|
|
2852
|
-
}
|
|
2853
3239
|
function localAgentApiInboxResponse(registration) {
|
|
2854
3240
|
const coordinator = registration.inboxCoordinator;
|
|
2855
3241
|
if (!coordinator) return void 0;
|
|
@@ -2897,9 +3283,9 @@ function localAgentApiEventsResponse(registration, target) {
|
|
|
2897
3283
|
return { status: 400, body: parsedQuery.error };
|
|
2898
3284
|
}
|
|
2899
3285
|
if (pending.length === 0) return void 0;
|
|
2900
|
-
const normalized =
|
|
3286
|
+
const normalized = sortInboxMessagesBySeq(normalizeInboxVisibleMessages(pending));
|
|
2901
3287
|
const filtered = parsedQuery.sinceSeq !== null ? normalized.filter((message) => {
|
|
2902
|
-
const seq =
|
|
3288
|
+
const seq = messageSeq3(message);
|
|
2903
3289
|
return Number.isFinite(seq) && seq > parsedQuery.sinceSeq;
|
|
2904
3290
|
}) : normalized;
|
|
2905
3291
|
const events = filtered.slice(0, parsedQuery.limit);
|
|
@@ -2908,7 +3294,7 @@ function localAgentApiEventsResponse(registration, target) {
|
|
|
2908
3294
|
const lastSeenMsgId = newestEvent?.message_id ?? newestEvent?.id ?? null;
|
|
2909
3295
|
const lastSeenSeq = newestEvent?.seq ?? parsedQuery.sinceSeq;
|
|
2910
3296
|
if (events.length > 0) {
|
|
2911
|
-
coordinator.consumeVisibleMessages({ messages: events, source: "
|
|
3297
|
+
coordinator.consumeVisibleMessages({ messages: events, source: "agent_api_events_local" });
|
|
2912
3298
|
}
|
|
2913
3299
|
coordinator.recordDrainOutcome?.({
|
|
2914
3300
|
source: "daemon_pending",
|
|
@@ -2930,30 +3316,6 @@ function localAgentApiEventsResponse(registration, target) {
|
|
|
2930
3316
|
}
|
|
2931
3317
|
};
|
|
2932
3318
|
}
|
|
2933
|
-
function localHeldContext(input) {
|
|
2934
|
-
if (input.messages.length === 0) return { ok: false, reason: "empty_context" };
|
|
2935
|
-
const normalized = sortBySeq(normalizeVisibleMessages(input.messages, input.target));
|
|
2936
|
-
const heldMessages = latestVisibleMessages(normalized, LOCAL_HELD_CONTEXT_LIMIT);
|
|
2937
|
-
const omittedMessageCount = Math.max(0, normalized.length - heldMessages.length);
|
|
2938
|
-
const boundary = resolveFreshnessBoundary(normalized);
|
|
2939
|
-
if (!boundary.ok) return boundary;
|
|
2940
|
-
input.coordinator.consumeVisibleMessages({
|
|
2941
|
-
target: input.target,
|
|
2942
|
-
messages: heldMessages,
|
|
2943
|
-
boundarySeq: boundary.seenUpToSeq,
|
|
2944
|
-
source: input.source
|
|
2945
|
-
});
|
|
2946
|
-
return {
|
|
2947
|
-
ok: true,
|
|
2948
|
-
context: {
|
|
2949
|
-
heldMessages,
|
|
2950
|
-
newMessageCount: normalized.length,
|
|
2951
|
-
shownMessageCount: heldMessages.length,
|
|
2952
|
-
omittedMessageCount,
|
|
2953
|
-
seenUpToSeq: boundary.seenUpToSeq
|
|
2954
|
-
}
|
|
2955
|
-
};
|
|
2956
|
-
}
|
|
2957
3319
|
function recordFreshnessDecision(coordinator, decision) {
|
|
2958
3320
|
coordinator?.recordFreshnessDecision?.(decision);
|
|
2959
3321
|
}
|
|
@@ -2967,53 +3329,6 @@ function sideEffectTarget(action, body) {
|
|
|
2967
3329
|
const field = action === "send" ? body.target : body.channel;
|
|
2968
3330
|
return typeof field === "string" && field.length > 0 ? field : void 0;
|
|
2969
3331
|
}
|
|
2970
|
-
function parseTargetFields(target) {
|
|
2971
|
-
if (target.startsWith("dm:@")) {
|
|
2972
|
-
const rest = target.slice("dm:@".length);
|
|
2973
|
-
const [peer, threadId] = rest.split(":", 2);
|
|
2974
|
-
if (threadId) {
|
|
2975
|
-
return {
|
|
2976
|
-
channel_type: "thread",
|
|
2977
|
-
channel_name: threadId,
|
|
2978
|
-
parent_channel_type: "dm",
|
|
2979
|
-
parent_channel_name: peer
|
|
2980
|
-
};
|
|
2981
|
-
}
|
|
2982
|
-
return { channel_type: "dm", channel_name: peer };
|
|
2983
|
-
}
|
|
2984
|
-
if (target.startsWith("#")) {
|
|
2985
|
-
const rest = target.slice(1);
|
|
2986
|
-
const [channel, threadId] = rest.split(":", 2);
|
|
2987
|
-
if (threadId) {
|
|
2988
|
-
return {
|
|
2989
|
-
channel_type: "thread",
|
|
2990
|
-
channel_name: threadId,
|
|
2991
|
-
parent_channel_type: "channel",
|
|
2992
|
-
parent_channel_name: channel
|
|
2993
|
-
};
|
|
2994
|
-
}
|
|
2995
|
-
return { channel_type: "channel", channel_name: channel };
|
|
2996
|
-
}
|
|
2997
|
-
return {};
|
|
2998
|
-
}
|
|
2999
|
-
function normalizeVisibleMessage(message, target) {
|
|
3000
|
-
const targetFields = target ? parseTargetFields(target) : {};
|
|
3001
|
-
const normalized = {
|
|
3002
|
-
...targetFields,
|
|
3003
|
-
...message,
|
|
3004
|
-
message_id: message.message_id ?? message.id,
|
|
3005
|
-
timestamp: message.timestamp ?? message.createdAt,
|
|
3006
|
-
sender_type: message.sender_type ?? message.senderType,
|
|
3007
|
-
sender_name: message.sender_name ?? message.senderName,
|
|
3008
|
-
sender_description: message.sender_description ?? message.senderDescription ?? null
|
|
3009
|
-
};
|
|
3010
|
-
const senderId = messageSenderId(message);
|
|
3011
|
-
if (senderId) normalized.sender_id = senderId;
|
|
3012
|
-
return normalized;
|
|
3013
|
-
}
|
|
3014
|
-
function normalizeVisibleMessages(messages, target) {
|
|
3015
|
-
return messages.map((message) => normalizeVisibleMessage(message, target));
|
|
3016
|
-
}
|
|
3017
3332
|
async function loadRecentTargetMessages(registration, headers, target) {
|
|
3018
3333
|
const historyUrl = new URL2("/internal/agent-api/history", registration.serverUrl);
|
|
3019
3334
|
historyUrl.searchParams.set("channel", target);
|
|
@@ -3023,181 +3338,57 @@ async function loadRecentTargetMessages(registration, headers, target) {
|
|
|
3023
3338
|
historyHeaders.delete("content-type");
|
|
3024
3339
|
const res = await fetch(historyUrl, { method: "GET", headers: historyHeaders });
|
|
3025
3340
|
if (!res.ok) return [];
|
|
3026
|
-
const parsed = await res.json().catch(() => null);
|
|
3027
|
-
return Array.isArray(parsed?.messages) ?
|
|
3028
|
-
}
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
return { bodyText: rawBody };
|
|
3035
|
-
}
|
|
3036
|
-
const target = sideEffectTarget(action, body);
|
|
3037
|
-
const coordinator = registration.inboxCoordinator;
|
|
3038
|
-
if (!target || !coordinator) {
|
|
3039
|
-
return { bodyText: JSON.stringify(body), target };
|
|
3040
|
-
}
|
|
3041
|
-
if (action === "send" && body.continueAnyway === true) {
|
|
3042
|
-
recordFreshnessDecision(coordinator, {
|
|
3043
|
-
action,
|
|
3044
|
-
decision: "bypass",
|
|
3045
|
-
target,
|
|
3046
|
-
inboxTrustState: "trusted",
|
|
3047
|
-
reason: "continue_anyway"
|
|
3048
|
-
});
|
|
3049
|
-
return { bodyText: JSON.stringify(body), target };
|
|
3050
|
-
}
|
|
3051
|
-
const pending = coordinator.getPendingMessages(target);
|
|
3052
|
-
if (pending.length > 0) {
|
|
3053
|
-
const modelSeenSeq = coordinator.getBoundary(target);
|
|
3054
|
-
const contextResult = localHeldContext({
|
|
3055
|
-
target,
|
|
3056
|
-
messages: pending,
|
|
3057
|
-
coordinator,
|
|
3058
|
-
source: "side_effect_preflight_context"
|
|
3059
|
-
});
|
|
3060
|
-
if (contextResult.ok) {
|
|
3061
|
-
const { context } = contextResult;
|
|
3062
|
-
const decision = {
|
|
3063
|
-
action,
|
|
3064
|
-
decision: "local_hold",
|
|
3065
|
-
target,
|
|
3066
|
-
inboxTrustState: "trusted",
|
|
3067
|
-
reason: "exact_target_pending",
|
|
3068
|
-
pendingCount: pending.length,
|
|
3069
|
-
pendingMaxSeq: context.seenUpToSeq,
|
|
3070
|
-
modelSeenSeq,
|
|
3071
|
-
heldMessageCount: context.shownMessageCount,
|
|
3072
|
-
omittedMessageCount: context.omittedMessageCount
|
|
3073
|
-
};
|
|
3074
|
-
const producerFactId = buildApmFreshnessDecisionProducerFactId(registration.agentId, decision);
|
|
3075
|
-
const localResponse = projectApmHeldFreshnessEnvelope({
|
|
3076
|
-
producerFactId,
|
|
3077
|
-
action,
|
|
3078
|
-
heldMessages: context.heldMessages,
|
|
3079
|
-
newMessageCount: context.newMessageCount,
|
|
3080
|
-
omittedMessageCount: context.omittedMessageCount,
|
|
3081
|
-
seenUpToSeq: context.seenUpToSeq
|
|
3082
|
-
}).body;
|
|
3083
|
-
recordFreshnessDecision(coordinator, { ...decision, producerFactId });
|
|
3084
|
-
return {
|
|
3085
|
-
bodyText: JSON.stringify(body),
|
|
3086
|
-
target,
|
|
3087
|
-
localResponse
|
|
3088
|
-
};
|
|
3089
|
-
}
|
|
3090
|
-
return {
|
|
3091
|
-
bodyText: JSON.stringify(body),
|
|
3092
|
-
target
|
|
3093
|
-
};
|
|
3094
|
-
}
|
|
3095
|
-
const existingBoundary = typeof body.seenUpToSeq === "number" && Number.isFinite(body.seenUpToSeq) ? Math.max(0, Math.floor(body.seenUpToSeq)) : void 0;
|
|
3096
|
-
const loadedBoundary = coordinator.getBoundary(target);
|
|
3097
|
-
const boundary = Math.max(existingBoundary ?? 0, loadedBoundary ?? 0);
|
|
3098
|
-
if (boundary > 0) {
|
|
3099
|
-
if (action === "send") body.seenUpToSeq = boundary;
|
|
3100
|
-
recordFreshnessDecision(coordinator, {
|
|
3101
|
-
action,
|
|
3102
|
-
decision: "forward",
|
|
3103
|
-
target,
|
|
3104
|
-
inboxTrustState: "trusted",
|
|
3105
|
-
reason: "model_seen_boundary",
|
|
3106
|
-
pendingCount: 0,
|
|
3107
|
-
modelSeenSeq: boundary
|
|
3108
|
-
});
|
|
3109
|
-
return { bodyText: JSON.stringify(body), target };
|
|
3110
|
-
}
|
|
3111
|
-
const recent = await loadRecentTargetMessages(registration, headers, target);
|
|
3112
|
-
if (recent.length > 0) {
|
|
3113
|
-
const unconsumedCounterparty = recent.filter(
|
|
3114
|
-
(message) => !isSelfAuthoredMessage(registration, message) && !isMessageModelSeen(coordinator, target, message)
|
|
3115
|
-
);
|
|
3116
|
-
const boundary2 = resolveFreshnessBoundary(recent);
|
|
3117
|
-
if (!boundary2.ok) {
|
|
3118
|
-
recordFreshnessDecision(coordinator, {
|
|
3119
|
-
action,
|
|
3120
|
-
decision: "forward",
|
|
3121
|
-
target,
|
|
3122
|
-
inboxTrustState: "untrusted",
|
|
3123
|
-
reason: "target_first_touch_recent_context_without_seq_boundary",
|
|
3124
|
-
pendingCount: 0,
|
|
3125
|
-
modelSeenSeq: 0
|
|
3126
|
-
});
|
|
3127
|
-
return { bodyText: JSON.stringify(body), target };
|
|
3128
|
-
}
|
|
3129
|
-
if (unconsumedCounterparty.length === 0) {
|
|
3130
|
-
const { seenUpToSeq: seenUpToSeq2 } = boundary2;
|
|
3131
|
-
if (action === "send") body.seenUpToSeq = seenUpToSeq2;
|
|
3132
|
-
coordinator.consumeVisibleMessages({ target, messages: recent, boundarySeq: seenUpToSeq2, source: "side_effect_preflight_context" });
|
|
3133
|
-
recordFreshnessDecision(coordinator, {
|
|
3134
|
-
action,
|
|
3135
|
-
decision: "forward",
|
|
3136
|
-
target,
|
|
3137
|
-
inboxTrustState: "untrusted",
|
|
3138
|
-
reason: "target_first_touch_recent_context_already_seen",
|
|
3139
|
-
pendingCount: 0,
|
|
3140
|
-
pendingMaxSeq: seenUpToSeq2,
|
|
3141
|
-
modelSeenSeq: seenUpToSeq2,
|
|
3142
|
-
heldMessageCount: 0,
|
|
3143
|
-
omittedMessageCount: 0
|
|
3144
|
-
});
|
|
3145
|
-
return { bodyText: JSON.stringify(body), target };
|
|
3341
|
+
const parsed = await res.json().catch(() => null);
|
|
3342
|
+
return Array.isArray(parsed?.messages) ? normalizeInboxVisibleMessages(parsed.messages, target) : [];
|
|
3343
|
+
}
|
|
3344
|
+
function applyAgentInboxStateMachineEffects(coordinator, effects) {
|
|
3345
|
+
for (const effect of effects) {
|
|
3346
|
+
if (effect.type === "record_freshness_decision") {
|
|
3347
|
+
recordFreshnessDecision(coordinator, effect.decision);
|
|
3348
|
+
continue;
|
|
3146
3349
|
}
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
decision: "syncing_hold",
|
|
3167
|
-
target,
|
|
3168
|
-
inboxTrustState: "untrusted",
|
|
3169
|
-
reason: "target_first_touch_recent_context",
|
|
3170
|
-
pendingCount: 0,
|
|
3171
|
-
pendingMaxSeq: heldBoundary.seenUpToSeq,
|
|
3172
|
-
modelSeenSeq: 0,
|
|
3173
|
-
heldMessageCount: heldMessages.length,
|
|
3174
|
-
omittedMessageCount
|
|
3175
|
-
};
|
|
3176
|
-
const producerFactId = buildApmFreshnessDecisionProducerFactId(registration.agentId, decision);
|
|
3177
|
-
recordFreshnessDecision(coordinator, { ...decision, producerFactId });
|
|
3178
|
-
return {
|
|
3179
|
-
bodyText: JSON.stringify(body),
|
|
3180
|
-
target,
|
|
3181
|
-
localResponse: projectApmHeldFreshnessEnvelope({
|
|
3182
|
-
producerFactId,
|
|
3183
|
-
action,
|
|
3184
|
-
heldMessages,
|
|
3185
|
-
newMessageCount: unconsumedCounterparty.length,
|
|
3186
|
-
omittedMessageCount,
|
|
3187
|
-
seenUpToSeq
|
|
3188
|
-
}).body
|
|
3189
|
-
};
|
|
3350
|
+
coordinator.consumeVisibleMessages({
|
|
3351
|
+
target: effect.target,
|
|
3352
|
+
messages: effect.messages,
|
|
3353
|
+
boundarySeq: effect.boundarySeq,
|
|
3354
|
+
source: effect.source
|
|
3355
|
+
});
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
async function prepareAgentApiSideEffectForward(registration, headers, rawBody, action) {
|
|
3359
|
+
let body;
|
|
3360
|
+
try {
|
|
3361
|
+
body = rawBody ? JSON.parse(rawBody) : {};
|
|
3362
|
+
} catch {
|
|
3363
|
+
return { bodyText: rawBody };
|
|
3364
|
+
}
|
|
3365
|
+
const target = sideEffectTarget(action, body);
|
|
3366
|
+
const coordinator = registration.inboxCoordinator;
|
|
3367
|
+
if (!target || !coordinator) {
|
|
3368
|
+
return { bodyText: JSON.stringify(body), target };
|
|
3190
3369
|
}
|
|
3191
|
-
|
|
3370
|
+
const pending = coordinator.getPendingMessages(target);
|
|
3371
|
+
const existingBoundary = typeof body.seenUpToSeq === "number" && Number.isFinite(body.seenUpToSeq) ? Math.max(0, Math.floor(body.seenUpToSeq)) : void 0;
|
|
3372
|
+
const continueAnyway = action === "send" && body.continueAnyway === true;
|
|
3373
|
+
const shouldLoadRecent = pending.length === 0 && !continueAnyway && Math.max(existingBoundary ?? 0, coordinator.getBoundary(target) ?? 0) <= 0;
|
|
3374
|
+
const recent = shouldLoadRecent ? await loadRecentTargetMessages(registration, headers, target) : [];
|
|
3375
|
+
const plan = planAgentInboxSideEffect({
|
|
3376
|
+
agentId: registration.agentId,
|
|
3192
3377
|
action,
|
|
3193
|
-
decision: "forward",
|
|
3194
3378
|
target,
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3379
|
+
continueAnyway,
|
|
3380
|
+
existingSeenUpToSeq: existingBoundary,
|
|
3381
|
+
modelSeenSeq: coordinator.getBoundary(target),
|
|
3382
|
+
pendingMessages: pending,
|
|
3383
|
+
recentMessages: recent,
|
|
3384
|
+
isMessageModelSeen: (messageInput) => coordinator.isMessageModelSeen?.(messageInput) === true,
|
|
3385
|
+
heldContextLimit: LOCAL_HELD_CONTEXT_LIMIT
|
|
3199
3386
|
});
|
|
3200
|
-
|
|
3387
|
+
applyAgentInboxStateMachineEffects(coordinator, plan.effects);
|
|
3388
|
+
if (typeof plan.forwardSeenUpToSeq === "number") {
|
|
3389
|
+
if (action === "send") body.seenUpToSeq = plan.forwardSeenUpToSeq;
|
|
3390
|
+
}
|
|
3391
|
+
return { bodyText: JSON.stringify(body), target, localResponse: plan.localResponse };
|
|
3201
3392
|
}
|
|
3202
3393
|
function shouldBufferJsonResponse(upstream, pathname, registration) {
|
|
3203
3394
|
if (!registration.inboxCoordinator) return false;
|
|
@@ -3217,27 +3408,26 @@ function consumeVisibleResponse(registration, targetUrl, sendTarget, responseTex
|
|
|
3217
3408
|
if (targetUrl.pathname === "/internal/agent-api/send" && parsed.state === "held" && Array.isArray(parsed.heldMessages)) {
|
|
3218
3409
|
coordinator.consumeVisibleMessages({
|
|
3219
3410
|
target: sendTarget,
|
|
3220
|
-
messages:
|
|
3411
|
+
messages: normalizeInboxVisibleMessages(parsed.heldMessages, sendTarget),
|
|
3221
3412
|
boundarySeq: typeof parsed.seenUpToSeq === "number" ? parsed.seenUpToSeq : void 0,
|
|
3222
3413
|
source: "server_held_context"
|
|
3223
3414
|
});
|
|
3224
3415
|
return;
|
|
3225
3416
|
}
|
|
3226
3417
|
if (targetUrl.pathname === "/internal/agent-api/send" && parsed.state === "sent") {
|
|
3227
|
-
const
|
|
3228
|
-
if (sendTarget &&
|
|
3418
|
+
const messageSeq4 = typeof parsed.messageSeq === "number" && Number.isFinite(parsed.messageSeq) ? Math.floor(parsed.messageSeq) : void 0;
|
|
3419
|
+
if (sendTarget && messageSeq4 && messageSeq4 > 0) {
|
|
3229
3420
|
coordinator.consumeVisibleMessages({
|
|
3230
3421
|
target: sendTarget,
|
|
3231
|
-
messages:
|
|
3232
|
-
boundarySeq: messageSeq3,
|
|
3422
|
+
messages: normalizeInboxVisibleMessages([{ id: parsed.messageId }], sendTarget),
|
|
3233
3423
|
source: "agent_api_send_commit"
|
|
3234
3424
|
});
|
|
3235
3425
|
}
|
|
3236
3426
|
return;
|
|
3237
3427
|
}
|
|
3238
3428
|
if (targetUrl.pathname === "/internal/agent-api/events" && Array.isArray(parsed.events)) {
|
|
3239
|
-
const messages =
|
|
3240
|
-
coordinator.consumeVisibleMessages({ messages, source: "
|
|
3429
|
+
const messages = normalizeInboxVisibleMessages(parsed.events);
|
|
3430
|
+
coordinator.consumeVisibleMessages({ messages, source: "agent_api_events_server" });
|
|
3241
3431
|
coordinator.recordDrainOutcome?.({
|
|
3242
3432
|
source: "server_events",
|
|
3243
3433
|
sinceCursorKind: parseAgentApiEventsQuery(targetUrl).sinceCursorKind,
|
|
@@ -3249,8 +3439,8 @@ function consumeVisibleResponse(registration, targetUrl, sendTarget, responseTex
|
|
|
3249
3439
|
}
|
|
3250
3440
|
if (targetUrl.pathname === "/internal/agent-api/history" && Array.isArray(parsed.messages)) {
|
|
3251
3441
|
const target = targetUrl.searchParams.get("channel") ?? void 0;
|
|
3252
|
-
const messages =
|
|
3253
|
-
coordinator.consumeVisibleMessages({ target, messages, boundarySeq:
|
|
3442
|
+
const messages = normalizeInboxVisibleMessages(parsed.messages, target);
|
|
3443
|
+
coordinator.consumeVisibleMessages({ target, messages, boundarySeq: maxInboxMessageSeq(messages), source: "agent_api_history" });
|
|
3254
3444
|
}
|
|
3255
3445
|
}
|
|
3256
3446
|
async function registerAgentCredentialProxy(input) {
|
|
@@ -3289,7 +3479,9 @@ var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
|
|
|
3289
3479
|
var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
|
|
3290
3480
|
var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
3291
3481
|
var RAW_CREDENTIAL_ENV_DENYLIST = [
|
|
3292
|
-
"
|
|
3482
|
+
"SLOCK_AGENT_TOKEN",
|
|
3483
|
+
"SLOCK_AGENT_CREDENTIAL_KEY",
|
|
3484
|
+
"SLOCK_AGENT_CREDENTIAL_KEY_FILE"
|
|
3293
3485
|
];
|
|
3294
3486
|
var cachedOpencliBinPath;
|
|
3295
3487
|
function resolveOpencliBinPath() {
|
|
@@ -3504,7 +3696,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
|
|
|
3504
3696
|
...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
|
|
3505
3697
|
PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
|
|
3506
3698
|
};
|
|
3507
|
-
|
|
3699
|
+
scrubDaemonChildEnv(spawnEnv);
|
|
3508
3700
|
for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
|
|
3509
3701
|
delete spawnEnv[key];
|
|
3510
3702
|
}
|
|
@@ -3933,7 +4125,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn, existsSyncFn) {
|
|
|
3933
4125
|
}
|
|
3934
4126
|
function resolveCommandOnPath(command, deps = {}) {
|
|
3935
4127
|
const platform = deps.platform ?? process.platform;
|
|
3936
|
-
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
4128
|
+
const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
|
|
3937
4129
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
3938
4130
|
const existsSyncFn = deps.existsSyncFn ?? existsSync2;
|
|
3939
4131
|
if (platform === "win32") {
|
|
@@ -3959,7 +4151,7 @@ function firstExistingPath(candidates, deps = {}) {
|
|
|
3959
4151
|
return null;
|
|
3960
4152
|
}
|
|
3961
4153
|
function readCommandVersion(command, args = [], deps = {}) {
|
|
3962
|
-
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
4154
|
+
const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
|
|
3963
4155
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
3964
4156
|
try {
|
|
3965
4157
|
const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
|
|
@@ -5313,11 +5505,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
|
|
|
5313
5505
|
return parseCursorModelsOutput(String(result.stdout || ""));
|
|
5314
5506
|
}
|
|
5315
5507
|
function buildCursorModelProbeEnv(deps = {}) {
|
|
5316
|
-
return withWindowsUserEnvironment({
|
|
5508
|
+
return scrubDaemonChildEnv(withWindowsUserEnvironment({
|
|
5317
5509
|
...deps.env ?? process.env,
|
|
5318
5510
|
FORCE_COLOR: "0",
|
|
5319
5511
|
NO_COLOR: "1"
|
|
5320
|
-
}, deps);
|
|
5512
|
+
}, deps));
|
|
5321
5513
|
}
|
|
5322
5514
|
function runCursorModelsCommand() {
|
|
5323
5515
|
return spawnSync("cursor-agent", ["models"], {
|
|
@@ -5373,7 +5565,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
|
|
|
5373
5565
|
}
|
|
5374
5566
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
|
|
5375
5567
|
const existsSyncFn = deps.existsSyncFn ?? existsSync4;
|
|
5376
|
-
const env = deps.env ?? process.env;
|
|
5568
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
5377
5569
|
const winPath = path6.win32;
|
|
5378
5570
|
let geminiEntry = null;
|
|
5379
5571
|
try {
|
|
@@ -5513,12 +5705,15 @@ var GeminiDriver = class {
|
|
|
5513
5705
|
// src/drivers/kimi.ts
|
|
5514
5706
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
5515
5707
|
import { spawn as spawn7 } from "child_process";
|
|
5516
|
-
import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
5708
|
+
import { chmodSync, existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
5517
5709
|
import os3 from "os";
|
|
5518
5710
|
import path7 from "path";
|
|
5519
5711
|
var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
|
|
5520
5712
|
var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
|
|
5521
5713
|
var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
|
|
5714
|
+
var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
|
|
5715
|
+
var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
|
|
5716
|
+
var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
|
|
5522
5717
|
function parseToolArguments(raw) {
|
|
5523
5718
|
if (typeof raw !== "string") return raw;
|
|
5524
5719
|
try {
|
|
@@ -5527,6 +5722,73 @@ function parseToolArguments(raw) {
|
|
|
5527
5722
|
return raw;
|
|
5528
5723
|
}
|
|
5529
5724
|
}
|
|
5725
|
+
function readKimiConfigSource(home = os3.homedir(), env = process.env) {
|
|
5726
|
+
const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
5727
|
+
if (inlineConfig && inlineConfig.trim()) {
|
|
5728
|
+
return {
|
|
5729
|
+
raw: inlineConfig,
|
|
5730
|
+
explicitPath: null,
|
|
5731
|
+
sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
|
|
5732
|
+
};
|
|
5733
|
+
}
|
|
5734
|
+
const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
5735
|
+
const configPath = explicitPath && explicitPath.trim() ? explicitPath : path7.join(home, ".kimi", "config.toml");
|
|
5736
|
+
try {
|
|
5737
|
+
return {
|
|
5738
|
+
raw: readFileSync3(configPath, "utf8"),
|
|
5739
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
5740
|
+
sourcePath: configPath
|
|
5741
|
+
};
|
|
5742
|
+
} catch {
|
|
5743
|
+
return {
|
|
5744
|
+
raw: null,
|
|
5745
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
5746
|
+
sourcePath: configPath
|
|
5747
|
+
};
|
|
5748
|
+
}
|
|
5749
|
+
}
|
|
5750
|
+
function buildKimiSpawnEnv(env = process.env) {
|
|
5751
|
+
const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
|
|
5752
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
5753
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
5754
|
+
return scrubDaemonChildEnv(spawnEnv);
|
|
5755
|
+
}
|
|
5756
|
+
function buildKimiEffectiveEnv(ctx, overrideEnv) {
|
|
5757
|
+
return {
|
|
5758
|
+
...process.env,
|
|
5759
|
+
...ctx.config.envVars || {},
|
|
5760
|
+
...overrideEnv || {}
|
|
5761
|
+
};
|
|
5762
|
+
}
|
|
5763
|
+
function buildKimiLaunchOptions(ctx, opts = {}) {
|
|
5764
|
+
const env = buildKimiEffectiveEnv(ctx, opts.env);
|
|
5765
|
+
const source = readKimiConfigSource(opts.home ?? os3.homedir(), env);
|
|
5766
|
+
const args = [];
|
|
5767
|
+
let configFilePath = null;
|
|
5768
|
+
let configContent = null;
|
|
5769
|
+
if (source.explicitPath) {
|
|
5770
|
+
configFilePath = source.explicitPath;
|
|
5771
|
+
} else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
|
|
5772
|
+
configFilePath = path7.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
|
|
5773
|
+
configContent = source.raw;
|
|
5774
|
+
if (opts.writeGeneratedConfig !== false) {
|
|
5775
|
+
writeFileSync3(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
|
|
5776
|
+
chmodSync(configFilePath, 384);
|
|
5777
|
+
}
|
|
5778
|
+
}
|
|
5779
|
+
if (configFilePath) {
|
|
5780
|
+
args.push("--config-file", configFilePath);
|
|
5781
|
+
}
|
|
5782
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
5783
|
+
args.push("--model", ctx.config.model);
|
|
5784
|
+
}
|
|
5785
|
+
return {
|
|
5786
|
+
args,
|
|
5787
|
+
env: buildKimiSpawnEnv(env),
|
|
5788
|
+
configFilePath,
|
|
5789
|
+
configContent
|
|
5790
|
+
};
|
|
5791
|
+
}
|
|
5530
5792
|
function resolveKimiSpawn(commandArgs, deps = {}) {
|
|
5531
5793
|
return {
|
|
5532
5794
|
command: resolveCommandOnPath("kimi", deps) ?? "kimi",
|
|
@@ -5550,7 +5812,25 @@ var KimiDriver = class {
|
|
|
5550
5812
|
};
|
|
5551
5813
|
model = {
|
|
5552
5814
|
detectedModelsVerifiedAs: "launchable",
|
|
5553
|
-
toLaunchSpec: (modelId) =>
|
|
5815
|
+
toLaunchSpec: (modelId, ctx, opts) => {
|
|
5816
|
+
if (!ctx) return { args: ["--model", modelId] };
|
|
5817
|
+
const launchCtx = {
|
|
5818
|
+
...ctx,
|
|
5819
|
+
config: {
|
|
5820
|
+
...ctx.config,
|
|
5821
|
+
model: modelId
|
|
5822
|
+
}
|
|
5823
|
+
};
|
|
5824
|
+
const launch = buildKimiLaunchOptions(launchCtx, {
|
|
5825
|
+
home: opts?.home,
|
|
5826
|
+
writeGeneratedConfig: false
|
|
5827
|
+
});
|
|
5828
|
+
return {
|
|
5829
|
+
args: launch.args,
|
|
5830
|
+
env: launch.env,
|
|
5831
|
+
configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
|
|
5832
|
+
};
|
|
5833
|
+
}
|
|
5554
5834
|
};
|
|
5555
5835
|
supportsStdinNotification = true;
|
|
5556
5836
|
mcpToolPrefix = "";
|
|
@@ -5576,21 +5856,23 @@ var KimiDriver = class {
|
|
|
5576
5856
|
` system_prompt_path: ./${KIMI_SYSTEM_PROMPT_FILE}`,
|
|
5577
5857
|
""
|
|
5578
5858
|
].join("\n"), "utf8");
|
|
5859
|
+
const launch = buildKimiLaunchOptions(ctx);
|
|
5579
5860
|
const args = [
|
|
5580
5861
|
"--wire",
|
|
5581
5862
|
"--yolo",
|
|
5582
5863
|
"--agent-file",
|
|
5583
5864
|
agentFilePath,
|
|
5584
5865
|
"--session",
|
|
5585
|
-
this.sessionId
|
|
5866
|
+
this.sessionId,
|
|
5867
|
+
...launch.args
|
|
5586
5868
|
];
|
|
5587
5869
|
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
5588
5870
|
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
5589
5871
|
args.push("--model", launchRuntimeFields.model);
|
|
5590
5872
|
}
|
|
5591
5873
|
const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
|
|
5592
|
-
const
|
|
5593
|
-
const proc = spawn7(
|
|
5874
|
+
const spawnTarget = resolveKimiSpawn(args);
|
|
5875
|
+
const proc = spawn7(spawnTarget.command, spawnTarget.args, {
|
|
5594
5876
|
cwd: ctx.workingDirectory,
|
|
5595
5877
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5596
5878
|
env: spawnEnv,
|
|
@@ -5598,7 +5880,7 @@ var KimiDriver = class {
|
|
|
5598
5880
|
// and has an 8191-character command-line limit. Kimi's official
|
|
5599
5881
|
// installer/uv entrypoint is an executable, so launch it directly and
|
|
5600
5882
|
// keep prompts on stdin / files instead of routing through cmd.exe.
|
|
5601
|
-
shell:
|
|
5883
|
+
shell: spawnTarget.shell
|
|
5602
5884
|
});
|
|
5603
5885
|
proc.stdin?.write(JSON.stringify({
|
|
5604
5886
|
jsonrpc: "2.0",
|
|
@@ -5712,14 +5994,9 @@ var KimiDriver = class {
|
|
|
5712
5994
|
return detectKimiModels();
|
|
5713
5995
|
}
|
|
5714
5996
|
};
|
|
5715
|
-
function detectKimiModels(home = os3.homedir()) {
|
|
5716
|
-
const
|
|
5717
|
-
|
|
5718
|
-
try {
|
|
5719
|
-
raw = readFileSync3(configPath, "utf8");
|
|
5720
|
-
} catch {
|
|
5721
|
-
return null;
|
|
5722
|
-
}
|
|
5997
|
+
function detectKimiModels(home = os3.homedir(), opts = {}) {
|
|
5998
|
+
const raw = readKimiConfigSource(home, opts.env).raw;
|
|
5999
|
+
if (raw === null) return null;
|
|
5723
6000
|
const models = [];
|
|
5724
6001
|
const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
|
|
5725
6002
|
const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
|
|
@@ -5959,7 +6236,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
|
|
|
5959
6236
|
const platform = deps.platform ?? process.platform;
|
|
5960
6237
|
const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
|
|
5961
6238
|
const result = spawnSyncFn("opencode", ["models"], {
|
|
5962
|
-
env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
6239
|
+
env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
|
|
5963
6240
|
encoding: "utf8",
|
|
5964
6241
|
timeout: 5e3,
|
|
5965
6242
|
shell: platform === "win32"
|
|
@@ -7204,9 +7481,26 @@ var RuntimeProgressState = class {
|
|
|
7204
7481
|
};
|
|
7205
7482
|
|
|
7206
7483
|
// src/runtimeNotificationState.ts
|
|
7484
|
+
function computeInboxNoticeFingerprint(messages) {
|
|
7485
|
+
const keys = [];
|
|
7486
|
+
for (const m of messages) {
|
|
7487
|
+
const seq = typeof m.seq === "number" && Number.isFinite(m.seq) && m.seq > 0 ? Math.floor(m.seq) : null;
|
|
7488
|
+
if (seq !== null) {
|
|
7489
|
+
keys.push(`s:${seq}`);
|
|
7490
|
+
continue;
|
|
7491
|
+
}
|
|
7492
|
+
const id = typeof m.message_id === "string" && m.message_id.length > 0 ? m.message_id : typeof m.id === "string" && m.id.length > 0 ? m.id : "";
|
|
7493
|
+
if (id.length > 0) keys.push(`m:${id}`);
|
|
7494
|
+
}
|
|
7495
|
+
if (keys.length === 0) return "";
|
|
7496
|
+
keys.sort();
|
|
7497
|
+
return keys.join(",");
|
|
7498
|
+
}
|
|
7207
7499
|
var RuntimeNotificationState = class {
|
|
7208
7500
|
timerValue = null;
|
|
7209
7501
|
pendingCountValue = 0;
|
|
7502
|
+
lastNoticeFingerprint = null;
|
|
7503
|
+
lastNoticeSessionId = null;
|
|
7210
7504
|
get pendingCount() {
|
|
7211
7505
|
return this.pendingCountValue;
|
|
7212
7506
|
}
|
|
@@ -7237,6 +7531,25 @@ var RuntimeNotificationState = class {
|
|
|
7237
7531
|
clear() {
|
|
7238
7532
|
this.clearPending();
|
|
7239
7533
|
this.clearTimer();
|
|
7534
|
+
this.clearNoticeFingerprint();
|
|
7535
|
+
}
|
|
7536
|
+
/**
|
|
7537
|
+
* True iff this exact unread-set was already successfully written in THIS
|
|
7538
|
+
* session (per-(agent,session) scope — never suppress across a session
|
|
7539
|
+
* change). Empty fingerprint always returns false (fail toward sending).
|
|
7540
|
+
*/
|
|
7541
|
+
isDuplicateNotice(fingerprint, sessionId) {
|
|
7542
|
+
if (fingerprint.length === 0) return false;
|
|
7543
|
+
return this.lastNoticeFingerprint === fingerprint && this.lastNoticeSessionId === sessionId;
|
|
7544
|
+
}
|
|
7545
|
+
/** Register a fingerprint as written — call ONLY after a successful stdin write. */
|
|
7546
|
+
recordNoticeWritten(fingerprint, sessionId) {
|
|
7547
|
+
this.lastNoticeFingerprint = fingerprint;
|
|
7548
|
+
this.lastNoticeSessionId = sessionId;
|
|
7549
|
+
}
|
|
7550
|
+
clearNoticeFingerprint() {
|
|
7551
|
+
this.lastNoticeFingerprint = null;
|
|
7552
|
+
this.lastNoticeSessionId = null;
|
|
7240
7553
|
}
|
|
7241
7554
|
schedule(callback, delayMs) {
|
|
7242
7555
|
if (this.timerValue) return false;
|
|
@@ -7368,34 +7681,24 @@ function toLocalTime(iso) {
|
|
|
7368
7681
|
function formatChannelLabel(message) {
|
|
7369
7682
|
return message.channel_type === "dm" ? `DM:@${message.channel_name}` : `#${message.channel_name}`;
|
|
7370
7683
|
}
|
|
7371
|
-
function
|
|
7372
|
-
if (
|
|
7373
|
-
const shortId = getMessageShortId(
|
|
7374
|
-
if (
|
|
7375
|
-
return `dm:@${
|
|
7684
|
+
function computeTarget(channelType, channelName, parentChannelName, parentChannelType) {
|
|
7685
|
+
if (channelType === "thread" && parentChannelName) {
|
|
7686
|
+
const shortId = getMessageShortId(String(channelName ?? ""));
|
|
7687
|
+
if (parentChannelType === "dm") {
|
|
7688
|
+
return `dm:@${parentChannelName}:${shortId}`;
|
|
7376
7689
|
}
|
|
7377
|
-
return `#${
|
|
7690
|
+
return `#${parentChannelName}:${shortId}`;
|
|
7378
7691
|
}
|
|
7379
|
-
if (
|
|
7380
|
-
return `dm:@${
|
|
7692
|
+
if (channelType === "dm") {
|
|
7693
|
+
return `dm:@${channelName}`;
|
|
7381
7694
|
}
|
|
7382
|
-
return `#${
|
|
7695
|
+
return `#${channelName}`;
|
|
7696
|
+
}
|
|
7697
|
+
function formatMessageTarget(message) {
|
|
7698
|
+
return computeTarget(message.channel_type, message.channel_name, message.parent_channel_name, message.parent_channel_type);
|
|
7383
7699
|
}
|
|
7384
7700
|
function formatVisibleMessageTarget(message) {
|
|
7385
|
-
|
|
7386
|
-
const shortId = getMessageShortId(String(message.channel_name));
|
|
7387
|
-
if (message.parent_channel_type === "dm") {
|
|
7388
|
-
return `dm:@${message.parent_channel_name}:${shortId}`;
|
|
7389
|
-
}
|
|
7390
|
-
return `#${message.parent_channel_name}:${shortId}`;
|
|
7391
|
-
}
|
|
7392
|
-
if (message.channel_type === "dm" && message.channel_name) {
|
|
7393
|
-
return `dm:@${message.channel_name}`;
|
|
7394
|
-
}
|
|
7395
|
-
if (message.channel_name) {
|
|
7396
|
-
return `#${message.channel_name}`;
|
|
7397
|
-
}
|
|
7398
|
-
return null;
|
|
7701
|
+
return computeTarget(message.channel_type, message.channel_name, message.parent_channel_name, message.parent_channel_type);
|
|
7399
7702
|
}
|
|
7400
7703
|
function getMessageShortId(messageId2) {
|
|
7401
7704
|
return messageId2.startsWith("thread-") ? messageId2.slice(7) : messageId2.slice(0, 8);
|
|
@@ -7617,6 +7920,8 @@ var TRAJECTORY_COALESCE_MS = 350;
|
|
|
7617
7920
|
var ACTIVITY_HEARTBEAT_MS = 6e4;
|
|
7618
7921
|
var STDIN_NOTIFICATION_INITIAL_DELAY_MS = 3e3;
|
|
7619
7922
|
var STDIN_NOTIFICATION_RETRY_DELAY_MS = 15e3;
|
|
7923
|
+
var RUNTIME_ERROR_DELIVERY_BACKOFF_BASE_MS = 1e4;
|
|
7924
|
+
var RUNTIME_ERROR_DELIVERY_BACKOFF_MAX_MS = 5 * 6e4;
|
|
7620
7925
|
var COMPACTION_STALE_MS = 5 * 6e4;
|
|
7621
7926
|
var RUNTIME_PROGRESS_STALE_MS = 15 * 6e4;
|
|
7622
7927
|
var DEFAULT_RUNTIME_START_TIMEOUT_MS = 2 * 6e4;
|
|
@@ -8030,6 +8335,14 @@ function createRuntimeTraceCounters() {
|
|
|
8030
8335
|
thinkingEvents: 0
|
|
8031
8336
|
};
|
|
8032
8337
|
}
|
|
8338
|
+
function createRuntimeErrorDeliveryBackoffState() {
|
|
8339
|
+
return {
|
|
8340
|
+
attempts: 0,
|
|
8341
|
+
untilMs: 0,
|
|
8342
|
+
timer: null,
|
|
8343
|
+
reason: null
|
|
8344
|
+
};
|
|
8345
|
+
}
|
|
8033
8346
|
function cleanupAgentCredentialProxy(agentId, launchId) {
|
|
8034
8347
|
unregisterAgentCredentialProxyForLaunch({ agentId, launchId });
|
|
8035
8348
|
}
|
|
@@ -8474,6 +8787,10 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8474
8787
|
defaultAgentEnvVarsProvider;
|
|
8475
8788
|
tracer;
|
|
8476
8789
|
stdinNotificationRetryMs;
|
|
8790
|
+
runtimeErrorDeliveryBackoffBaseMs;
|
|
8791
|
+
runtimeErrorDeliveryBackoffMaxMs;
|
|
8792
|
+
runtimeErrorDeliveryBackoffJitterRatio;
|
|
8793
|
+
runtimeErrorDeliveryBackoffFailPointForTesting;
|
|
8477
8794
|
cliTransportTraceDir = null;
|
|
8478
8795
|
deliveryTraceContexts = /* @__PURE__ */ new WeakMap();
|
|
8479
8796
|
runtimeExitTraceAttrs = /* @__PURE__ */ new WeakMap();
|
|
@@ -8497,6 +8814,19 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8497
8814
|
0,
|
|
8498
8815
|
Math.floor(opts.stdinNotificationRetryMs ?? STDIN_NOTIFICATION_RETRY_DELAY_MS)
|
|
8499
8816
|
);
|
|
8817
|
+
this.runtimeErrorDeliveryBackoffBaseMs = Math.max(
|
|
8818
|
+
0,
|
|
8819
|
+
Math.floor(opts.runtimeErrorDeliveryBackoff?.baseMs ?? RUNTIME_ERROR_DELIVERY_BACKOFF_BASE_MS)
|
|
8820
|
+
);
|
|
8821
|
+
this.runtimeErrorDeliveryBackoffMaxMs = Math.max(
|
|
8822
|
+
this.runtimeErrorDeliveryBackoffBaseMs,
|
|
8823
|
+
Math.floor(opts.runtimeErrorDeliveryBackoff?.maxMs ?? RUNTIME_ERROR_DELIVERY_BACKOFF_MAX_MS)
|
|
8824
|
+
);
|
|
8825
|
+
this.runtimeErrorDeliveryBackoffJitterRatio = Math.max(
|
|
8826
|
+
0,
|
|
8827
|
+
Math.min(1, opts.runtimeErrorDeliveryBackoff?.jitterRatio ?? 0.1)
|
|
8828
|
+
);
|
|
8829
|
+
this.runtimeErrorDeliveryBackoffFailPointForTesting = opts.runtimeErrorDeliveryBackoff?.failPointForTesting ?? null;
|
|
8500
8830
|
this.maxConcurrentAgentStarts = Math.max(
|
|
8501
8831
|
1,
|
|
8502
8832
|
Math.floor(
|
|
@@ -8550,6 +8880,183 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8550
8880
|
this.sendStdinNotification(agentId);
|
|
8551
8881
|
}, delayMs);
|
|
8552
8882
|
}
|
|
8883
|
+
clearRuntimeErrorDeliveryBackoff(ap) {
|
|
8884
|
+
if (ap.runtimeErrorDeliveryBackoff.timer) {
|
|
8885
|
+
clearTimeout(ap.runtimeErrorDeliveryBackoff.timer);
|
|
8886
|
+
}
|
|
8887
|
+
ap.runtimeErrorDeliveryBackoff = createRuntimeErrorDeliveryBackoffState();
|
|
8888
|
+
}
|
|
8889
|
+
clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, eventKind) {
|
|
8890
|
+
if (ap.runtimeErrorDeliveryBackoff.attempts === 0 && ap.runtimeErrorDeliveryBackoff.untilMs === 0) return;
|
|
8891
|
+
const attempts = ap.runtimeErrorDeliveryBackoff.attempts;
|
|
8892
|
+
const reason = ap.runtimeErrorDeliveryBackoff.reason;
|
|
8893
|
+
this.clearRuntimeErrorDeliveryBackoff(ap);
|
|
8894
|
+
this.recordDaemonTrace("daemon.agent.runtime_error_delivery_backoff.reset", {
|
|
8895
|
+
agentId,
|
|
8896
|
+
runtime: ap.config.runtime,
|
|
8897
|
+
model: ap.config.model,
|
|
8898
|
+
launchId: ap.launchId || void 0,
|
|
8899
|
+
reason: reason || void 0,
|
|
8900
|
+
attempts,
|
|
8901
|
+
reset_source: eventKind
|
|
8902
|
+
});
|
|
8903
|
+
}
|
|
8904
|
+
runtimeErrorDeliveryBackoffRemainingMs(ap) {
|
|
8905
|
+
return Math.max(0, ap.runtimeErrorDeliveryBackoff.untilMs - Date.now());
|
|
8906
|
+
}
|
|
8907
|
+
scheduleRuntimeErrorDeliveryBackoffFlush(agentId, ap) {
|
|
8908
|
+
const delayMs = this.runtimeErrorDeliveryBackoffRemainingMs(ap);
|
|
8909
|
+
if (delayMs <= 0 || ap.runtimeErrorDeliveryBackoff.timer) return false;
|
|
8910
|
+
ap.runtimeErrorDeliveryBackoff.timer = setTimeout(() => {
|
|
8911
|
+
ap.runtimeErrorDeliveryBackoff.timer = null;
|
|
8912
|
+
this.flushRuntimeErrorDeliveryBackoff(agentId);
|
|
8913
|
+
}, delayMs);
|
|
8914
|
+
ap.runtimeErrorDeliveryBackoff.timer.unref?.();
|
|
8915
|
+
return true;
|
|
8916
|
+
}
|
|
8917
|
+
recoverableRuntimeDeliveryBackoffReason(message, terminalFailure, stickyTerminalFailure, reasonOverride) {
|
|
8918
|
+
if (stickyTerminalFailure || terminalFailure?.actionRequired) return null;
|
|
8919
|
+
if (reasonOverride !== void 0) return reasonOverride;
|
|
8920
|
+
if (terminalFailure) return "recoverable_terminal_runtime_error";
|
|
8921
|
+
const runtimeErrorClass = buildRuntimeErrorDiagnosticEnvelope(message).spanAttrs.runtime_error_class;
|
|
8922
|
+
switch (runtimeErrorClass) {
|
|
8923
|
+
case "RateLimitError":
|
|
8924
|
+
return "rate_limited";
|
|
8925
|
+
case "ProviderServerError":
|
|
8926
|
+
return "provider_server_error";
|
|
8927
|
+
case "ProviderConnectionError":
|
|
8928
|
+
return "provider_connection_error";
|
|
8929
|
+
case "ProviderStreamError":
|
|
8930
|
+
return "provider_stream_error";
|
|
8931
|
+
default:
|
|
8932
|
+
return null;
|
|
8933
|
+
}
|
|
8934
|
+
}
|
|
8935
|
+
noteRuntimeErrorDeliveryBackoff(agentId, ap, message, terminalFailure, stickyTerminalFailure, reasonOverride) {
|
|
8936
|
+
const reason = this.recoverableRuntimeDeliveryBackoffReason(message, terminalFailure, stickyTerminalFailure, reasonOverride);
|
|
8937
|
+
if (!reason) return false;
|
|
8938
|
+
const attempts = ap.runtimeErrorDeliveryBackoff.attempts + 1;
|
|
8939
|
+
const exponentialDelayMs = this.runtimeErrorDeliveryBackoffBaseMs * 2 ** Math.max(0, attempts - 1);
|
|
8940
|
+
const cappedDelayMs = Math.min(this.runtimeErrorDeliveryBackoffMaxMs, exponentialDelayMs);
|
|
8941
|
+
const jitterMs = Math.floor(cappedDelayMs * this.runtimeErrorDeliveryBackoffJitterRatio * Math.random());
|
|
8942
|
+
const delayMs = Math.min(this.runtimeErrorDeliveryBackoffMaxMs, cappedDelayMs + jitterMs);
|
|
8943
|
+
if (ap.runtimeErrorDeliveryBackoff.timer) {
|
|
8944
|
+
clearTimeout(ap.runtimeErrorDeliveryBackoff.timer);
|
|
8945
|
+
ap.runtimeErrorDeliveryBackoff.timer = null;
|
|
8946
|
+
}
|
|
8947
|
+
ap.runtimeErrorDeliveryBackoff.attempts = attempts;
|
|
8948
|
+
ap.runtimeErrorDeliveryBackoff.reason = reason;
|
|
8949
|
+
ap.runtimeErrorDeliveryBackoff.untilMs = Date.now() + delayMs;
|
|
8950
|
+
if (ap.inbox.length > 0) {
|
|
8951
|
+
this.scheduleRuntimeErrorDeliveryBackoffFlush(agentId, ap);
|
|
8952
|
+
}
|
|
8953
|
+
this.recordDaemonTrace("daemon.agent.runtime_error_delivery_backoff", {
|
|
8954
|
+
agentId,
|
|
8955
|
+
runtime: ap.config.runtime,
|
|
8956
|
+
model: ap.config.model,
|
|
8957
|
+
launchId: ap.launchId || void 0,
|
|
8958
|
+
reason,
|
|
8959
|
+
attempts,
|
|
8960
|
+
delay_ms: delayMs,
|
|
8961
|
+
until_ms: ap.runtimeErrorDeliveryBackoff.untilMs,
|
|
8962
|
+
inbox_count: ap.inbox.length,
|
|
8963
|
+
pending_notification_count: ap.notifications.pendingCount
|
|
8964
|
+
});
|
|
8965
|
+
return true;
|
|
8966
|
+
}
|
|
8967
|
+
queueDeliveryForRuntimeErrorBackoff(agentId, ap, message) {
|
|
8968
|
+
const remainingMs = this.runtimeErrorDeliveryBackoffRemainingMs(ap);
|
|
8969
|
+
if (remainingMs <= 0) return false;
|
|
8970
|
+
ap.inbox.push(message);
|
|
8971
|
+
if (!ap.isIdle && ap.driver.supportsStdinNotification && ap.sessionId) {
|
|
8972
|
+
ap.notifications.add();
|
|
8973
|
+
}
|
|
8974
|
+
const scheduled = this.scheduleRuntimeErrorDeliveryBackoffFlush(agentId, ap);
|
|
8975
|
+
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
8976
|
+
outcome: "queued_runtime_error_backoff",
|
|
8977
|
+
accepted: true,
|
|
8978
|
+
process_present: true,
|
|
8979
|
+
runtime: ap.config.runtime,
|
|
8980
|
+
session_id_present: Boolean(ap.sessionId),
|
|
8981
|
+
launchId: ap.launchId || void 0,
|
|
8982
|
+
is_idle: ap.isIdle,
|
|
8983
|
+
inbox_count: ap.inbox.length,
|
|
8984
|
+
pending_notification_count: ap.notifications.pendingCount,
|
|
8985
|
+
runtime_error_backoff_remaining_ms: remainingMs,
|
|
8986
|
+
runtime_error_backoff_attempts: ap.runtimeErrorDeliveryBackoff.attempts,
|
|
8987
|
+
runtime_error_backoff_reason: ap.runtimeErrorDeliveryBackoff.reason || void 0,
|
|
8988
|
+
runtime_error_backoff_timer_scheduled: scheduled || ap.runtimeErrorDeliveryBackoff.timer !== null
|
|
8989
|
+
}));
|
|
8990
|
+
return true;
|
|
8991
|
+
}
|
|
8992
|
+
flushRuntimeErrorDeliveryBackoff(agentId) {
|
|
8993
|
+
const ap = this.agents.get(agentId);
|
|
8994
|
+
if (!ap) return false;
|
|
8995
|
+
const remainingMs = this.runtimeErrorDeliveryBackoffRemainingMs(ap);
|
|
8996
|
+
if (remainingMs > 0) {
|
|
8997
|
+
this.scheduleRuntimeErrorDeliveryBackoffFlush(agentId, ap);
|
|
8998
|
+
return false;
|
|
8999
|
+
}
|
|
9000
|
+
if (ap.inbox.length === 0) return false;
|
|
9001
|
+
const reason = ap.runtimeErrorDeliveryBackoff.reason || "runtime_error_backoff";
|
|
9002
|
+
if (ap.isIdle && ap.driver.supportsStdinNotification && ap.sessionId) {
|
|
9003
|
+
const messages = [...ap.inbox];
|
|
9004
|
+
ap.notifications.clearPending();
|
|
9005
|
+
ap.notifications.clearTimer();
|
|
9006
|
+
this.commitApmIdleState(agentId, ap, false);
|
|
9007
|
+
this.startRuntimeTrace(agentId, ap, "runtime-error-backoff-idle-delivery", messages);
|
|
9008
|
+
this.broadcastActivity(agentId, "working", "Message received");
|
|
9009
|
+
const accepted2 = this.deliverInboxUpdateViaStdin(
|
|
9010
|
+
agentId,
|
|
9011
|
+
ap,
|
|
9012
|
+
messages,
|
|
9013
|
+
"idle",
|
|
9014
|
+
"runtime_error_backoff_idle_delivery"
|
|
9015
|
+
);
|
|
9016
|
+
this.recordDaemonTrace("daemon.agent.runtime_error_delivery_backoff.flush", {
|
|
9017
|
+
agentId,
|
|
9018
|
+
runtime: ap.config.runtime,
|
|
9019
|
+
model: ap.config.model,
|
|
9020
|
+
launchId: ap.launchId || void 0,
|
|
9021
|
+
reason,
|
|
9022
|
+
mode: "idle",
|
|
9023
|
+
outcome: accepted2 ? "written" : "not_written",
|
|
9024
|
+
inbox_count: ap.inbox.length,
|
|
9025
|
+
messages_count: messages.length
|
|
9026
|
+
});
|
|
9027
|
+
return accepted2;
|
|
9028
|
+
}
|
|
9029
|
+
if (!ap.driver.supportsStdinNotification || !ap.sessionId) {
|
|
9030
|
+
this.recordDaemonTrace("daemon.agent.runtime_error_delivery_backoff.flush", {
|
|
9031
|
+
agentId,
|
|
9032
|
+
runtime: ap.config.runtime,
|
|
9033
|
+
model: ap.config.model,
|
|
9034
|
+
launchId: ap.launchId || void 0,
|
|
9035
|
+
reason,
|
|
9036
|
+
outcome: "queued_without_stdin",
|
|
9037
|
+
inbox_count: ap.inbox.length,
|
|
9038
|
+
session_id_present: Boolean(ap.sessionId),
|
|
9039
|
+
supports_stdin_notification: ap.driver.supportsStdinNotification
|
|
9040
|
+
});
|
|
9041
|
+
return false;
|
|
9042
|
+
}
|
|
9043
|
+
if (ap.notifications.pendingCount === 0) {
|
|
9044
|
+
ap.notifications.add(ap.inbox.length);
|
|
9045
|
+
}
|
|
9046
|
+
const accepted = this.sendStdinNotification(agentId);
|
|
9047
|
+
this.recordDaemonTrace("daemon.agent.runtime_error_delivery_backoff.flush", {
|
|
9048
|
+
agentId,
|
|
9049
|
+
runtime: ap.config.runtime,
|
|
9050
|
+
model: ap.config.model,
|
|
9051
|
+
launchId: ap.launchId || void 0,
|
|
9052
|
+
reason,
|
|
9053
|
+
mode: "busy",
|
|
9054
|
+
outcome: accepted ? "written" : "not_written",
|
|
9055
|
+
inbox_count: ap.inbox.length,
|
|
9056
|
+
pending_notification_count: ap.notifications.pendingCount
|
|
9057
|
+
});
|
|
9058
|
+
return accepted;
|
|
9059
|
+
}
|
|
8553
9060
|
allPendingVisibleMessages(agentId) {
|
|
8554
9061
|
const collect = (messages) => (messages ?? []).filter((message) => typeof message.seq === "number" && message.seq > 0);
|
|
8555
9062
|
return [
|
|
@@ -8596,10 +9103,13 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8596
9103
|
if (byTarget.size === 0) return;
|
|
8597
9104
|
const boundaryMap = this.visibleBoundaryMap(agentId);
|
|
8598
9105
|
const visibleIds = this.visibleMessageIdMap(agentId);
|
|
9106
|
+
const advancesBoundary = input.source === "spawn_wake_message" || input.source === "agent_api_events_local" || input.source.startsWith("stdin_");
|
|
8599
9107
|
for (const [target, bucket] of byTarget) {
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
|
|
9108
|
+
if (advancesBoundary) {
|
|
9109
|
+
const highWaterSeq = Math.max(bucket.maxSeq, bucket.boundarySeq);
|
|
9110
|
+
const previous = boundaryMap.get(target) ?? 0;
|
|
9111
|
+
boundaryMap.set(target, Math.max(previous, highWaterSeq));
|
|
9112
|
+
}
|
|
8603
9113
|
if (bucket.ids.size > 0) {
|
|
8604
9114
|
let targetIds = visibleIds.get(target);
|
|
8605
9115
|
if (!targetIds) {
|
|
@@ -8663,6 +9173,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8663
9173
|
active.notifications.remove(removedActive);
|
|
8664
9174
|
if (active.inbox.length === 0) {
|
|
8665
9175
|
active.notifications.clear();
|
|
9176
|
+
this.clearRuntimeErrorDeliveryBackoff(active);
|
|
8666
9177
|
}
|
|
8667
9178
|
}
|
|
8668
9179
|
const removedCount = removedActive + removedStarting;
|
|
@@ -9177,6 +9688,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9177
9688
|
recentStdout: [],
|
|
9178
9689
|
recentStderr: [],
|
|
9179
9690
|
lastRuntimeError: null,
|
|
9691
|
+
runtimeErrorDeliveryBackoff: createRuntimeErrorDeliveryBackoffState(),
|
|
9180
9692
|
spawnError: null,
|
|
9181
9693
|
exitCode: null,
|
|
9182
9694
|
exitSignal: null,
|
|
@@ -9279,6 +9791,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9279
9791
|
const ap = this.agents.get(agentId);
|
|
9280
9792
|
if (ap.runtime !== runtime) return;
|
|
9281
9793
|
ap.notifications.clearTimer();
|
|
9794
|
+
this.clearRuntimeErrorDeliveryBackoff(ap);
|
|
9282
9795
|
if (ap.pendingTrajectory?.timer) {
|
|
9283
9796
|
clearTimeout(ap.pendingTrajectory.timer);
|
|
9284
9797
|
}
|
|
@@ -9396,6 +9909,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9396
9909
|
logger.warn(`[Agent ${agentId}] Recoverable provider stream failure (${reason}) \u2014 keeping agent wakeable`);
|
|
9397
9910
|
this.sendAgentStatus(agentId, "active", ap.launchId);
|
|
9398
9911
|
} else if (startupTimeoutTermination) {
|
|
9912
|
+
this.cacheStartupTimeoutRetryConfig(agentId, ap);
|
|
9399
9913
|
logger.warn(`[Agent ${agentId}] Startup timeout cleanup completed (${reason})`);
|
|
9400
9914
|
} else {
|
|
9401
9915
|
this.idleAgentConfigs.delete(agentId);
|
|
@@ -9466,6 +9980,23 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9466
9980
|
this.agents.delete(agentId);
|
|
9467
9981
|
this.idleAgentConfigs.delete(agentId);
|
|
9468
9982
|
}
|
|
9983
|
+
cacheStartupTimeoutRetryConfig(agentId, ap) {
|
|
9984
|
+
const retryConfig = {
|
|
9985
|
+
...stripManagedRunnerCredential(ap.config),
|
|
9986
|
+
sessionId: ap.sessionId
|
|
9987
|
+
};
|
|
9988
|
+
this.idleAgentConfigs.set(agentId, {
|
|
9989
|
+
config: retryConfig,
|
|
9990
|
+
sessionId: ap.sessionId,
|
|
9991
|
+
launchId: ap.launchId
|
|
9992
|
+
});
|
|
9993
|
+
this.recordDaemonTrace("daemon.agent.startup_timeout.retry_config_cached", {
|
|
9994
|
+
agentId,
|
|
9995
|
+
launchId: ap.launchId || void 0,
|
|
9996
|
+
runtime: ap.config.runtime,
|
|
9997
|
+
session_id_present: Boolean(ap.sessionId)
|
|
9998
|
+
});
|
|
9999
|
+
}
|
|
9469
10000
|
async buildSpawnConfig(agentId, config) {
|
|
9470
10001
|
const baseConfig = config.serverUrl === this.serverUrl ? config : { ...config, serverUrl: this.serverUrl };
|
|
9471
10002
|
const runnerConfig = await this.ensureManagedRunnerCredential(agentId, baseConfig);
|
|
@@ -9693,6 +10224,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9693
10224
|
return;
|
|
9694
10225
|
}
|
|
9695
10226
|
ap.notifications.clearTimer();
|
|
10227
|
+
this.clearRuntimeErrorDeliveryBackoff(ap);
|
|
9696
10228
|
if (ap.activityHeartbeat) {
|
|
9697
10229
|
clearInterval(ap.activityHeartbeat);
|
|
9698
10230
|
}
|
|
@@ -9747,6 +10279,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9747
10279
|
this.deliveryTraceContexts.set(message, traceContext);
|
|
9748
10280
|
}
|
|
9749
10281
|
const transientDelivery = this.isTransientDelivery(message);
|
|
10282
|
+
if (!transientDelivery && this.isVisibleMessageModelSeen(agentId, formatMessageTarget(message), message)) {
|
|
10283
|
+
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
10284
|
+
outcome: "dropped_already_consumed",
|
|
10285
|
+
accepted: true,
|
|
10286
|
+
process_present: Boolean(this.agents.get(agentId))
|
|
10287
|
+
}));
|
|
10288
|
+
return true;
|
|
10289
|
+
}
|
|
9750
10290
|
const ap = this.agents.get(agentId);
|
|
9751
10291
|
if (!ap) {
|
|
9752
10292
|
if (this.agentsStarting.has(agentId) || this.queuedAgentStarts.has(agentId)) {
|
|
@@ -9890,6 +10430,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9890
10430
|
return true;
|
|
9891
10431
|
}
|
|
9892
10432
|
}
|
|
10433
|
+
if (!transientDelivery && this.queueDeliveryForRuntimeErrorBackoff(agentId, ap, message)) {
|
|
10434
|
+
return true;
|
|
10435
|
+
}
|
|
9893
10436
|
if (ap.isIdle && ap.driver.supportsStdinNotification && ap.sessionId) {
|
|
9894
10437
|
if (transientDelivery) {
|
|
9895
10438
|
this.commitApmIdleState(agentId, ap, false);
|
|
@@ -10942,6 +11485,19 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10942
11485
|
return written;
|
|
10943
11486
|
}
|
|
10944
11487
|
case "deliver_stdin": {
|
|
11488
|
+
const runtimeErrorBackoffRemainingMs = this.runtimeErrorDeliveryBackoffRemainingMs(ap);
|
|
11489
|
+
if (runtimeErrorBackoffRemainingMs > 0) {
|
|
11490
|
+
const scheduled = this.scheduleRuntimeErrorDeliveryBackoffFlush(agentId, ap);
|
|
11491
|
+
this.recordApmGatedSteeringEffectTrace(agentId, ap, effect, {
|
|
11492
|
+
outcome: "suppressed_runtime_error_backoff",
|
|
11493
|
+
runtime_error_backoff_remaining_ms: runtimeErrorBackoffRemainingMs,
|
|
11494
|
+
runtime_error_backoff_attempts: ap.runtimeErrorDeliveryBackoff.attempts,
|
|
11495
|
+
runtime_error_backoff_reason: ap.runtimeErrorDeliveryBackoff.reason || void 0,
|
|
11496
|
+
runtime_error_backoff_timer_scheduled: scheduled || ap.runtimeErrorDeliveryBackoff.timer !== null,
|
|
11497
|
+
delivered_messages_count: 0
|
|
11498
|
+
});
|
|
11499
|
+
return false;
|
|
11500
|
+
}
|
|
10945
11501
|
const messages = [...ap.inbox];
|
|
10946
11502
|
ap.notifications.clear();
|
|
10947
11503
|
if (messages.length === 0) {
|
|
@@ -11051,7 +11607,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11051
11607
|
logger.warn(`[Agent ${agentId}] ${ap.driver.id} did not emit a startup runtime event within ${timeoutMs}ms; terminating process`);
|
|
11052
11608
|
this.broadcastActivity(agentId, "error", detail, [{ kind: "text", text: `Error: ${detail}` }], ap.launchId);
|
|
11053
11609
|
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
11054
|
-
this.
|
|
11610
|
+
this.cacheStartupTimeoutRetryConfig(agentId, ap);
|
|
11055
11611
|
try {
|
|
11056
11612
|
this.runtimeExitTraceAttrs.set(ap.runtime, {
|
|
11057
11613
|
stop_source: "startup_timeout",
|
|
@@ -11182,6 +11738,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11182
11738
|
}
|
|
11183
11739
|
if (event.kind === "internal_progress") {
|
|
11184
11740
|
ap.runtimeProgress.noteInternalProgress();
|
|
11741
|
+
this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
|
|
11185
11742
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.internal_observed", {
|
|
11186
11743
|
turn_outcome: "held",
|
|
11187
11744
|
turn_subtype: "runtime_progress",
|
|
@@ -11206,6 +11763,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11206
11763
|
this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
|
|
11207
11764
|
this.queueTrajectoryText(agentId, "thinking", event.text);
|
|
11208
11765
|
if (ap) {
|
|
11766
|
+
this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
|
|
11209
11767
|
const reduction = reduceApmGatedAssistantContinuation(ap.gatedSteering);
|
|
11210
11768
|
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "thinking" });
|
|
11211
11769
|
}
|
|
@@ -11215,6 +11773,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11215
11773
|
this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
|
|
11216
11774
|
this.queueTrajectoryText(agentId, "text", event.text);
|
|
11217
11775
|
if (ap) {
|
|
11776
|
+
this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
|
|
11218
11777
|
const reduction = reduceApmGatedAssistantContinuation(ap.gatedSteering);
|
|
11219
11778
|
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "text" });
|
|
11220
11779
|
}
|
|
@@ -11225,6 +11784,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11225
11784
|
this.flushPendingTrajectory(agentId);
|
|
11226
11785
|
const invocation = normalizeToolDisplayInvocation(event.name, event.input);
|
|
11227
11786
|
if (ap) {
|
|
11787
|
+
this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
|
|
11228
11788
|
const reduction = reduceApmGatedToolUse(ap.gatedSteering, { kind: "tool_call" });
|
|
11229
11789
|
this.recordRuntimeTraceEvent(agentId, ap, "tool.call.started", { tool: invocation.toolName });
|
|
11230
11790
|
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, {
|
|
@@ -11244,6 +11804,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11244
11804
|
case "tool_output": {
|
|
11245
11805
|
const invocation = normalizeToolDisplayInvocation(event.name, {});
|
|
11246
11806
|
if (ap) {
|
|
11807
|
+
this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
|
|
11247
11808
|
const reduction = reduceApmGatedToolUse(ap.gatedSteering, { kind: "tool_output" });
|
|
11248
11809
|
this.recordRuntimeTraceEvent(agentId, ap, "tool.output.observed", { tool: invocation.toolName });
|
|
11249
11810
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.continuation.expected");
|
|
@@ -11303,6 +11864,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11303
11864
|
this.commitApmIdleState(agentId, ap, true);
|
|
11304
11865
|
if (stickyTerminalFailure) {
|
|
11305
11866
|
this.broadcastActivity(agentId, "error", stickyTerminalFailure.detail);
|
|
11867
|
+
} else if (ap.lastRuntimeError && ap.runtimeErrorDeliveryBackoff.attempts > 0) {
|
|
11868
|
+
this.broadcastActivity(agentId, "error", ap.lastRuntimeError);
|
|
11306
11869
|
} else {
|
|
11307
11870
|
this.broadcastActivity(agentId, "online", "Idle");
|
|
11308
11871
|
}
|
|
@@ -11352,12 +11915,19 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11352
11915
|
visibleErrorMessage = formatRuntimeLoginRequiredMessage(ap.driver.id);
|
|
11353
11916
|
}
|
|
11354
11917
|
const shouldDisableToolBoundaryFlush = ap.runtime.descriptor.busyDelivery === "gated" && this.isThinkingBlockMutationError(event.message);
|
|
11355
|
-
const
|
|
11918
|
+
const backoffFailPoint = this.runtimeErrorDeliveryBackoffFailPointForTesting?.({
|
|
11919
|
+
agentId,
|
|
11920
|
+
message: event.message
|
|
11921
|
+
}) ?? null;
|
|
11922
|
+
const terminalFailure = backoffFailPoint && Object.prototype.hasOwnProperty.call(backoffFailPoint, "terminalFailure") ? backoffFailPoint.terminalFailure ?? null : classifyTerminalFailure(ap);
|
|
11923
|
+
const stickyTerminalFailure = backoffFailPoint && Object.prototype.hasOwnProperty.call(backoffFailPoint, "stickyTerminalFailure") ? backoffFailPoint.stickyTerminalFailure ?? null : classifyStickyTerminalFailure(ap);
|
|
11924
|
+
const backoffReasonOverride = backoffFailPoint && Object.prototype.hasOwnProperty.call(backoffFailPoint, "reason") ? backoffFailPoint.reason ?? null : void 0;
|
|
11356
11925
|
const reduction = reduceApmGatedError(ap.gatedSteering, {
|
|
11357
11926
|
disableToolBoundaryFlush: shouldDisableToolBoundaryFlush,
|
|
11358
11927
|
terminalWakeable: Boolean(ap.driver.supportsStdinNotification && terminalFailure && !terminalFailure.actionRequired)
|
|
11359
11928
|
});
|
|
11360
11929
|
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "error" });
|
|
11930
|
+
this.noteRuntimeErrorDeliveryBackoff(agentId, ap, event.message, terminalFailure, stickyTerminalFailure, backoffReasonOverride);
|
|
11361
11931
|
if (reduction.shouldDisableToolBoundaryFlush) {
|
|
11362
11932
|
this.recordGatedSteeringEvent(agentId, ap, "disabled", {
|
|
11363
11933
|
reason: "thinking_block_mutation_error",
|
|
@@ -11378,7 +11948,6 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11378
11948
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_error")
|
|
11379
11949
|
});
|
|
11380
11950
|
if (ap.driver.supportsStdinNotification && terminalFailure) {
|
|
11381
|
-
const stickyTerminalFailure = classifyStickyTerminalFailure(ap);
|
|
11382
11951
|
if (terminalFailure.actionRequired) {
|
|
11383
11952
|
logger.warn(`[Agent ${agentId}] ${ap.driver.id} auth requires user action; terminating runtime process`);
|
|
11384
11953
|
try {
|
|
@@ -11396,7 +11965,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11396
11965
|
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
11397
11966
|
logger.warn(`[Agent ${agentId}] ${ap.driver.id} terminal runtime error requires explicit recovery`);
|
|
11398
11967
|
} else {
|
|
11399
|
-
ap.notifications.
|
|
11968
|
+
ap.notifications.clearPending();
|
|
11969
|
+
ap.notifications.clearTimer();
|
|
11400
11970
|
logger.info(`[Agent ${agentId}] Marked ${ap.driver.id} wakeable after terminal runtime error`);
|
|
11401
11971
|
}
|
|
11402
11972
|
}
|
|
@@ -11507,6 +12077,27 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11507
12077
|
if (count === 0) return false;
|
|
11508
12078
|
if (ap.isIdle) return false;
|
|
11509
12079
|
if (!ap.sessionId) return false;
|
|
12080
|
+
const runtimeErrorBackoffRemainingMs = this.runtimeErrorDeliveryBackoffRemainingMs(ap);
|
|
12081
|
+
if (runtimeErrorBackoffRemainingMs > 0) {
|
|
12082
|
+
ap.notifications.add(count);
|
|
12083
|
+
const scheduled = this.scheduleRuntimeErrorDeliveryBackoffFlush(agentId, ap);
|
|
12084
|
+
this.recordDaemonTrace("daemon.agent.stdin_notification", {
|
|
12085
|
+
agentId,
|
|
12086
|
+
runtime: ap.config.runtime,
|
|
12087
|
+
model: ap.config.model,
|
|
12088
|
+
launchId: ap.launchId || void 0,
|
|
12089
|
+
outcome: "suppressed_runtime_error_backoff",
|
|
12090
|
+
mode: "busy",
|
|
12091
|
+
pending_notification_count: count,
|
|
12092
|
+
inbox_count: ap.inbox.length,
|
|
12093
|
+
session_id_present: true,
|
|
12094
|
+
runtime_error_backoff_remaining_ms: runtimeErrorBackoffRemainingMs,
|
|
12095
|
+
runtime_error_backoff_attempts: ap.runtimeErrorDeliveryBackoff.attempts,
|
|
12096
|
+
runtime_error_backoff_reason: ap.runtimeErrorDeliveryBackoff.reason || void 0,
|
|
12097
|
+
runtime_error_backoff_timer_scheduled: scheduled || ap.runtimeErrorDeliveryBackoff.timer !== null
|
|
12098
|
+
});
|
|
12099
|
+
return false;
|
|
12100
|
+
}
|
|
11510
12101
|
if (ap.gatedSteering.compacting) {
|
|
11511
12102
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.compaction_boundary.delivery_suppressed", {
|
|
11512
12103
|
pendingNotificationCount: count,
|
|
@@ -11522,6 +12113,22 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11522
12113
|
const inboxCount = ap.inbox.length;
|
|
11523
12114
|
if (inboxCount === 0) return false;
|
|
11524
12115
|
const changedMessages = ap.inbox.slice(Math.max(0, ap.inbox.length - count));
|
|
12116
|
+
const noticeFingerprint = computeInboxNoticeFingerprint(changedMessages);
|
|
12117
|
+
if (ap.notifications.isDuplicateNotice(noticeFingerprint, ap.sessionId)) {
|
|
12118
|
+
this.recordDaemonTrace("daemon.agent.stdin_notification", {
|
|
12119
|
+
agentId,
|
|
12120
|
+
runtime: ap.config.runtime,
|
|
12121
|
+
model: ap.config.model,
|
|
12122
|
+
launchId: ap.launchId || void 0,
|
|
12123
|
+
outcome: "suppressed_duplicate",
|
|
12124
|
+
mode: "busy",
|
|
12125
|
+
pending_notification_count: count,
|
|
12126
|
+
inbox_count: ap.inbox.length,
|
|
12127
|
+
session_id_present: true
|
|
12128
|
+
});
|
|
12129
|
+
logger.info(`[Agent ${agentId}] Suppressing duplicate stdin inbox notice (unread-set unchanged since last write); pending=${ap.inbox.length}`);
|
|
12130
|
+
return false;
|
|
12131
|
+
}
|
|
11525
12132
|
const inboxRows = projectAgentInboxSnapshot(changedMessages);
|
|
11526
12133
|
const notification = `[Slock inbox notice:
|
|
11527
12134
|
${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
|
|
@@ -11559,6 +12166,7 @@ ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
|
|
|
11559
12166
|
inbox_target_count: inboxRows.length,
|
|
11560
12167
|
session_id_present: true
|
|
11561
12168
|
});
|
|
12169
|
+
ap.notifications.recordNoticeWritten(noticeFingerprint, ap.sessionId);
|
|
11562
12170
|
return true;
|
|
11563
12171
|
} else {
|
|
11564
12172
|
ap.notifications.add(count);
|
|
@@ -11855,6 +12463,8 @@ var DaemonConnection = class {
|
|
|
11855
12463
|
lastDroppedSendLogAt = 0;
|
|
11856
12464
|
lastInboundAt = null;
|
|
11857
12465
|
lastInboundMessageKind = null;
|
|
12466
|
+
pendingActivityByAgent = /* @__PURE__ */ new Map();
|
|
12467
|
+
latestObservedLaunchIdByAgent = /* @__PURE__ */ new Map();
|
|
11858
12468
|
constructor(options) {
|
|
11859
12469
|
this.options = options;
|
|
11860
12470
|
this.clock = options.clock ?? systemClock;
|
|
@@ -11868,6 +12478,7 @@ var DaemonConnection = class {
|
|
|
11868
12478
|
}
|
|
11869
12479
|
disconnect() {
|
|
11870
12480
|
this.shouldConnect = false;
|
|
12481
|
+
this.pendingActivityByAgent.clear();
|
|
11871
12482
|
this.clearWatchdog();
|
|
11872
12483
|
if (this.reconnectTimer) {
|
|
11873
12484
|
this.clock.clearTimeout(this.reconnectTimer);
|
|
@@ -11881,9 +12492,15 @@ var DaemonConnection = class {
|
|
|
11881
12492
|
}
|
|
11882
12493
|
send(msg) {
|
|
11883
12494
|
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
12495
|
+
this.observeLaunchIdentity(msg);
|
|
12496
|
+
if (msg.type === "agent:activity") {
|
|
12497
|
+
this.pendingActivityByAgent.delete(msg.agentId);
|
|
12498
|
+
}
|
|
11884
12499
|
this.ws.send(JSON.stringify(msg));
|
|
11885
12500
|
return;
|
|
11886
12501
|
}
|
|
12502
|
+
this.observeLaunchIdentity(msg);
|
|
12503
|
+
this.queueReplayableMessage(msg);
|
|
11887
12504
|
const now = this.clock.now();
|
|
11888
12505
|
if (now - this.lastDroppedSendLogAt > 5e3) {
|
|
11889
12506
|
this.lastDroppedSendLogAt = now;
|
|
@@ -11926,6 +12543,7 @@ var DaemonConnection = class {
|
|
|
11926
12543
|
reconnect_attempt: priorReconnectAttempt,
|
|
11927
12544
|
inbound_watchdog_ms: this.options.inboundWatchdogMs ?? INBOUND_WATCHDOG_MS
|
|
11928
12545
|
});
|
|
12546
|
+
this.flushPendingActivity(ws);
|
|
11929
12547
|
this.options.onConnect();
|
|
11930
12548
|
});
|
|
11931
12549
|
ws.on("message", (data) => {
|
|
@@ -12023,6 +12641,65 @@ var DaemonConnection = class {
|
|
|
12023
12641
|
this.lastInboundAt = this.clock.now();
|
|
12024
12642
|
this.lastInboundMessageKind = messageKind;
|
|
12025
12643
|
}
|
|
12644
|
+
queueReplayableMessage(msg) {
|
|
12645
|
+
if (msg.type !== "agent:activity") return;
|
|
12646
|
+
const latestLaunchId = this.latestObservedLaunchIdByAgent.get(msg.agentId);
|
|
12647
|
+
if (msg.launchId && latestLaunchId && msg.launchId !== latestLaunchId) {
|
|
12648
|
+
this.trace("daemon.connection.pending_activity_invalidated", {
|
|
12649
|
+
reason: "launch_changed",
|
|
12650
|
+
message_type: msg.type,
|
|
12651
|
+
agentId: msg.agentId,
|
|
12652
|
+
stale_launch_id_present: true,
|
|
12653
|
+
next_launch_id_present: true
|
|
12654
|
+
});
|
|
12655
|
+
return;
|
|
12656
|
+
}
|
|
12657
|
+
this.pendingActivityByAgent.set(msg.agentId, msg);
|
|
12658
|
+
}
|
|
12659
|
+
observeLaunchIdentity(msg) {
|
|
12660
|
+
const identity = this.agentLaunchIdentity(msg);
|
|
12661
|
+
if (!identity?.launchId) return;
|
|
12662
|
+
if (msg.type !== "agent:activity") {
|
|
12663
|
+
this.latestObservedLaunchIdByAgent.set(identity.agentId, identity.launchId);
|
|
12664
|
+
}
|
|
12665
|
+
const pending = this.pendingActivityByAgent.get(identity.agentId);
|
|
12666
|
+
if (!pending || pending.launchId === identity.launchId) return;
|
|
12667
|
+
this.pendingActivityByAgent.delete(identity.agentId);
|
|
12668
|
+
this.trace("daemon.connection.pending_activity_invalidated", {
|
|
12669
|
+
reason: "launch_changed",
|
|
12670
|
+
message_type: msg.type,
|
|
12671
|
+
agentId: identity.agentId,
|
|
12672
|
+
stale_launch_id_present: Boolean(pending.launchId),
|
|
12673
|
+
next_launch_id_present: true
|
|
12674
|
+
});
|
|
12675
|
+
}
|
|
12676
|
+
agentLaunchIdentity(msg) {
|
|
12677
|
+
switch (msg.type) {
|
|
12678
|
+
case "agent:activity":
|
|
12679
|
+
case "agent:status":
|
|
12680
|
+
case "agent:session":
|
|
12681
|
+
case "agent:runtime_profile":
|
|
12682
|
+
case "agent:runtime_profile:migration:ack":
|
|
12683
|
+
case "agent:runtime_profile:migration_done":
|
|
12684
|
+
case "agent:runtime_profile:daemon_release_notice:ack":
|
|
12685
|
+
return { agentId: msg.agentId, launchId: msg.launchId };
|
|
12686
|
+
default:
|
|
12687
|
+
return null;
|
|
12688
|
+
}
|
|
12689
|
+
}
|
|
12690
|
+
flushPendingActivity(ws) {
|
|
12691
|
+
if (this.pendingActivityByAgent.size === 0) return;
|
|
12692
|
+
if (this.ws !== ws || ws.readyState !== WebSocket.OPEN) return;
|
|
12693
|
+
const pending = [...this.pendingActivityByAgent.values()];
|
|
12694
|
+
this.pendingActivityByAgent.clear();
|
|
12695
|
+
for (const msg of pending) {
|
|
12696
|
+
ws.send(JSON.stringify(msg));
|
|
12697
|
+
}
|
|
12698
|
+
this.trace("daemon.connection.outbound_replayed", {
|
|
12699
|
+
message_type: "agent:activity",
|
|
12700
|
+
message_count: pending.length
|
|
12701
|
+
});
|
|
12702
|
+
}
|
|
12026
12703
|
lastInboundAgeBucket() {
|
|
12027
12704
|
return durationMsBucket(this.lastInboundAt == null ? null : this.clock.now() - this.lastInboundAt);
|
|
12028
12705
|
}
|
|
@@ -12845,7 +13522,7 @@ var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
|
|
|
12845
13522
|
var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"];
|
|
12846
13523
|
var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
|
|
12847
13524
|
var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
|
|
12848
|
-
var DAEMON_CLI_USAGE =
|
|
13525
|
+
var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
|
|
12849
13526
|
var RunnerCredentialMintError2 = class extends Error {
|
|
12850
13527
|
code;
|
|
12851
13528
|
retryable;
|
|
@@ -12881,9 +13558,9 @@ function runnerCredentialErrorDetail2(error) {
|
|
|
12881
13558
|
async function waitForRunnerCredentialRetry2() {
|
|
12882
13559
|
await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
|
|
12883
13560
|
}
|
|
12884
|
-
function parseDaemonCliArgs(args) {
|
|
13561
|
+
function parseDaemonCliArgs(args, env = {}) {
|
|
12885
13562
|
let serverUrl = "";
|
|
12886
|
-
let apiKey = "";
|
|
13563
|
+
let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
|
|
12887
13564
|
for (let i = 0; i < args.length; i++) {
|
|
12888
13565
|
if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
|
|
12889
13566
|
if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
|
|
@@ -13652,6 +14329,8 @@ var DaemonCore = class {
|
|
|
13652
14329
|
};
|
|
13653
14330
|
|
|
13654
14331
|
export {
|
|
14332
|
+
DAEMON_API_KEY_ENV,
|
|
14333
|
+
scrubDaemonAuthEnv,
|
|
13655
14334
|
subscribeDaemonLogs,
|
|
13656
14335
|
resolveWorkspaceDirectoryPath,
|
|
13657
14336
|
scanWorkspaceDirectories,
|