@slock-ai/daemon 0.55.2-alpha.0 → 0.55.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat-bridge.js +1 -1
- package/dist/{chunk-QQRU3GA6.js → chunk-DLB2KVD7.js} +903 -251
- package/dist/{chunk-VOZJ2ELH.js → chunk-M2KQBJR3.js} +37 -4
- package/dist/cli/index.js +172 -37
- package/dist/core.js +2 -2
- package/dist/index.js +2 -2
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
executeJsonRequest,
|
|
6
6
|
executeResponseRequest,
|
|
7
7
|
logger
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-M2KQBJR3.js";
|
|
9
9
|
|
|
10
10
|
// src/core.ts
|
|
11
11
|
import path16 from "path";
|
|
@@ -38,6 +38,244 @@ var CHANNEL_MESSAGE_RE = new RegExp(
|
|
|
38
38
|
"iu"
|
|
39
39
|
);
|
|
40
40
|
|
|
41
|
+
// ../shared/src/producerFactLineage.ts
|
|
42
|
+
var PRODUCER_FACT_TEXT_LABEL = "producerFactId";
|
|
43
|
+
function formatProducerFactLineageBracket(producerFactId) {
|
|
44
|
+
const id = normalizeProducerFactId(producerFactId);
|
|
45
|
+
return id ? ` [${PRODUCER_FACT_TEXT_LABEL}=${id}]` : "";
|
|
46
|
+
}
|
|
47
|
+
function normalizeProducerFactId(producerFactId) {
|
|
48
|
+
return typeof producerFactId === "string" ? producerFactId.trim() : "";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ../shared/src/apmHeldFreshness.ts
|
|
52
|
+
function buildApmFreshnessDecisionProducerFactId(agentId, input) {
|
|
53
|
+
const stableInput = {
|
|
54
|
+
agentId,
|
|
55
|
+
action: input.action,
|
|
56
|
+
decision: input.decision,
|
|
57
|
+
target: input.target ?? null,
|
|
58
|
+
reason: input.reason,
|
|
59
|
+
pendingMaxSeq: input.pendingMaxSeq ?? null,
|
|
60
|
+
modelSeenSeq: input.modelSeenSeq ?? null,
|
|
61
|
+
heldMessageCount: input.heldMessageCount ?? null,
|
|
62
|
+
omittedMessageCount: input.omittedMessageCount ?? null
|
|
63
|
+
};
|
|
64
|
+
return `freshness_decision_fact:${hashApmHeldFreshnessStable(stableInput)}`;
|
|
65
|
+
}
|
|
66
|
+
function projectApmHeldFreshnessEnvelope(input) {
|
|
67
|
+
const body = {
|
|
68
|
+
state: "held",
|
|
69
|
+
outcome: "held",
|
|
70
|
+
subtype: "freshness",
|
|
71
|
+
reason: "newer_messages_available",
|
|
72
|
+
producerFactId: input.producerFactId,
|
|
73
|
+
available_actions: apmHeldFreshnessAvailableActions(input.action),
|
|
74
|
+
heldMessages: input.heldMessages,
|
|
75
|
+
newMessageCount: input.newMessageCount,
|
|
76
|
+
shownMessageCount: input.heldMessages.length,
|
|
77
|
+
omittedMessageCount: input.omittedMessageCount,
|
|
78
|
+
seenUpToSeq: input.seenUpToSeq
|
|
79
|
+
};
|
|
80
|
+
return {
|
|
81
|
+
clauseId: "SMR-006",
|
|
82
|
+
projector: "held-envelope",
|
|
83
|
+
surface: "agent-api-held-response",
|
|
84
|
+
producerFactId: input.producerFactId,
|
|
85
|
+
body
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function projectApmHeldFreshnessActivity(input) {
|
|
89
|
+
const title = input.action === "send" ? "Send held by freshness check" : input.action === "task_claim" ? "Task claim held by freshness check" : "Task update held by freshness check";
|
|
90
|
+
const text = [
|
|
91
|
+
input.target ? `target: ${input.target}` : null,
|
|
92
|
+
`new messages: ${input.messageCount} newer message${input.messageCount === 1 ? "" : "s"}`,
|
|
93
|
+
`decision: ${input.decision === "syncing_hold" ? "syncing hold" : "local hold"}; review the newer context before retrying`
|
|
94
|
+
].filter((line) => Boolean(line)).join("\n");
|
|
95
|
+
return {
|
|
96
|
+
clauseId: "SMR-006",
|
|
97
|
+
projector: "held-envelope",
|
|
98
|
+
surface: "agent:activity",
|
|
99
|
+
producerFactId: input.producerFactId,
|
|
100
|
+
entry: {
|
|
101
|
+
kind: "slock_action",
|
|
102
|
+
producerFactId: input.producerFactId,
|
|
103
|
+
title,
|
|
104
|
+
text
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function projectApmFreshnessDecisionTrace(input) {
|
|
109
|
+
return {
|
|
110
|
+
clauseId: "SMR-006",
|
|
111
|
+
projector: "held-envelope",
|
|
112
|
+
surface: "daemon-trace",
|
|
113
|
+
producerFactId: input.producerFactId,
|
|
114
|
+
attrs: {
|
|
115
|
+
producer_fact_id: input.producerFactId,
|
|
116
|
+
action: input.decision.action,
|
|
117
|
+
decision: input.decision.decision,
|
|
118
|
+
target: input.decision.target,
|
|
119
|
+
inbox_trust_state: input.decision.inboxTrustState,
|
|
120
|
+
reason: input.decision.reason,
|
|
121
|
+
pending_count: input.decision.pendingCount,
|
|
122
|
+
pending_max_seq: input.decision.pendingMaxSeq,
|
|
123
|
+
model_seen_seq: input.decision.modelSeenSeq,
|
|
124
|
+
held_message_count: input.decision.heldMessageCount,
|
|
125
|
+
omitted_message_count: input.decision.omittedMessageCount
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function apmHeldFreshnessAvailableActions(action) {
|
|
130
|
+
return action === "send" ? ["check_messages", "send_draft", "send_anyway"] : ["check_messages", "retry_action"];
|
|
131
|
+
}
|
|
132
|
+
function hashApmHeldFreshnessStable(value) {
|
|
133
|
+
return sha256HexUtf8(stableStringifyApmHeldFreshness(value));
|
|
134
|
+
}
|
|
135
|
+
function stableStringifyApmHeldFreshness(value) {
|
|
136
|
+
return JSON.stringify(stableNormalizeApmHeldFreshness(value));
|
|
137
|
+
}
|
|
138
|
+
function stableNormalizeApmHeldFreshness(value) {
|
|
139
|
+
if (Array.isArray(value)) return value.map((item) => stableNormalizeApmHeldFreshness(item));
|
|
140
|
+
if (!value || typeof value !== "object") return value;
|
|
141
|
+
const record = value;
|
|
142
|
+
const normalized = {};
|
|
143
|
+
for (const key of Object.keys(record).sort()) {
|
|
144
|
+
const child = record[key];
|
|
145
|
+
if (child === void 0) continue;
|
|
146
|
+
normalized[key] = stableNormalizeApmHeldFreshness(child);
|
|
147
|
+
}
|
|
148
|
+
return normalized;
|
|
149
|
+
}
|
|
150
|
+
var SHA256_INITIAL_STATE = [
|
|
151
|
+
1779033703,
|
|
152
|
+
3144134277,
|
|
153
|
+
1013904242,
|
|
154
|
+
2773480762,
|
|
155
|
+
1359893119,
|
|
156
|
+
2600822924,
|
|
157
|
+
528734635,
|
|
158
|
+
1541459225
|
|
159
|
+
];
|
|
160
|
+
var SHA256_K = [
|
|
161
|
+
1116352408,
|
|
162
|
+
1899447441,
|
|
163
|
+
3049323471,
|
|
164
|
+
3921009573,
|
|
165
|
+
961987163,
|
|
166
|
+
1508970993,
|
|
167
|
+
2453635748,
|
|
168
|
+
2870763221,
|
|
169
|
+
3624381080,
|
|
170
|
+
310598401,
|
|
171
|
+
607225278,
|
|
172
|
+
1426881987,
|
|
173
|
+
1925078388,
|
|
174
|
+
2162078206,
|
|
175
|
+
2614888103,
|
|
176
|
+
3248222580,
|
|
177
|
+
3835390401,
|
|
178
|
+
4022224774,
|
|
179
|
+
264347078,
|
|
180
|
+
604807628,
|
|
181
|
+
770255983,
|
|
182
|
+
1249150122,
|
|
183
|
+
1555081692,
|
|
184
|
+
1996064986,
|
|
185
|
+
2554220882,
|
|
186
|
+
2821834349,
|
|
187
|
+
2952996808,
|
|
188
|
+
3210313671,
|
|
189
|
+
3336571891,
|
|
190
|
+
3584528711,
|
|
191
|
+
113926993,
|
|
192
|
+
338241895,
|
|
193
|
+
666307205,
|
|
194
|
+
773529912,
|
|
195
|
+
1294757372,
|
|
196
|
+
1396182291,
|
|
197
|
+
1695183700,
|
|
198
|
+
1986661051,
|
|
199
|
+
2177026350,
|
|
200
|
+
2456956037,
|
|
201
|
+
2730485921,
|
|
202
|
+
2820302411,
|
|
203
|
+
3259730800,
|
|
204
|
+
3345764771,
|
|
205
|
+
3516065817,
|
|
206
|
+
3600352804,
|
|
207
|
+
4094571909,
|
|
208
|
+
275423344,
|
|
209
|
+
430227734,
|
|
210
|
+
506948616,
|
|
211
|
+
659060556,
|
|
212
|
+
883997877,
|
|
213
|
+
958139571,
|
|
214
|
+
1322822218,
|
|
215
|
+
1537002063,
|
|
216
|
+
1747873779,
|
|
217
|
+
1955562222,
|
|
218
|
+
2024104815,
|
|
219
|
+
2227730452,
|
|
220
|
+
2361852424,
|
|
221
|
+
2428436474,
|
|
222
|
+
2756734187,
|
|
223
|
+
3204031479,
|
|
224
|
+
3329325298
|
|
225
|
+
];
|
|
226
|
+
function sha256HexUtf8(value) {
|
|
227
|
+
const bytes = new TextEncoder().encode(value);
|
|
228
|
+
const paddedLength = Math.ceil((bytes.length + 9) / 64) * 64;
|
|
229
|
+
const padded = new Uint8Array(paddedLength);
|
|
230
|
+
padded.set(bytes);
|
|
231
|
+
padded[bytes.length] = 128;
|
|
232
|
+
const bitLength = bytes.length * 8;
|
|
233
|
+
const view = new DataView(padded.buffer);
|
|
234
|
+
view.setUint32(paddedLength - 8, Math.floor(bitLength / 4294967296));
|
|
235
|
+
view.setUint32(paddedLength - 4, bitLength >>> 0);
|
|
236
|
+
const h = [...SHA256_INITIAL_STATE];
|
|
237
|
+
const words = new Array(64);
|
|
238
|
+
for (let offset = 0; offset < paddedLength; offset += 64) {
|
|
239
|
+
for (let i = 0; i < 16; i += 1) {
|
|
240
|
+
words[i] = view.getUint32(offset + i * 4);
|
|
241
|
+
}
|
|
242
|
+
for (let i = 16; i < 64; i += 1) {
|
|
243
|
+
const s0 = rotateRight(words[i - 15], 7) ^ rotateRight(words[i - 15], 18) ^ words[i - 15] >>> 3;
|
|
244
|
+
const s1 = rotateRight(words[i - 2], 17) ^ rotateRight(words[i - 2], 19) ^ words[i - 2] >>> 10;
|
|
245
|
+
words[i] = words[i - 16] + s0 + words[i - 7] + s1 >>> 0;
|
|
246
|
+
}
|
|
247
|
+
let [a, b, c, d, e, f, g, hh] = h;
|
|
248
|
+
for (let i = 0; i < 64; i += 1) {
|
|
249
|
+
const s1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);
|
|
250
|
+
const ch = e & f ^ ~e & g;
|
|
251
|
+
const temp1 = hh + s1 + ch + SHA256_K[i] + words[i] >>> 0;
|
|
252
|
+
const s0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22);
|
|
253
|
+
const maj = a & b ^ a & c ^ b & c;
|
|
254
|
+
const temp2 = s0 + maj >>> 0;
|
|
255
|
+
hh = g;
|
|
256
|
+
g = f;
|
|
257
|
+
f = e;
|
|
258
|
+
e = d + temp1 >>> 0;
|
|
259
|
+
d = c;
|
|
260
|
+
c = b;
|
|
261
|
+
b = a;
|
|
262
|
+
a = temp1 + temp2 >>> 0;
|
|
263
|
+
}
|
|
264
|
+
h[0] = h[0] + a >>> 0;
|
|
265
|
+
h[1] = h[1] + b >>> 0;
|
|
266
|
+
h[2] = h[2] + c >>> 0;
|
|
267
|
+
h[3] = h[3] + d >>> 0;
|
|
268
|
+
h[4] = h[4] + e >>> 0;
|
|
269
|
+
h[5] = h[5] + f >>> 0;
|
|
270
|
+
h[6] = h[6] + g >>> 0;
|
|
271
|
+
h[7] = h[7] + hh >>> 0;
|
|
272
|
+
}
|
|
273
|
+
return h.map((part) => part.toString(16).padStart(8, "0")).join("");
|
|
274
|
+
}
|
|
275
|
+
function rotateRight(value, bits) {
|
|
276
|
+
return value >>> bits | value << 32 - bits;
|
|
277
|
+
}
|
|
278
|
+
|
|
41
279
|
// ../shared/src/tracing/index.ts
|
|
42
280
|
var DEFAULT_TRACE_FLAGS = "00";
|
|
43
281
|
var TRACEPARENT_VERSION = "00";
|
|
@@ -954,7 +1192,7 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
954
1192
|
// src/agentProcessManager.ts
|
|
955
1193
|
import { mkdirSync as mkdirSync4, readdirSync, statSync, writeFileSync as writeFileSync7 } from "fs";
|
|
956
1194
|
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
957
|
-
import { createHash as
|
|
1195
|
+
import { createHash as createHash3 } from "crypto";
|
|
958
1196
|
import path12 from "path";
|
|
959
1197
|
import os5 from "os";
|
|
960
1198
|
|
|
@@ -1059,8 +1297,8 @@ Use the \`slock\` CLI for chat / task / attachment operations. The daemon inject
|
|
|
1059
1297
|
17. **\`slock attachment view\`** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
|
|
1060
1298
|
18. **\`slock profile show\`** \u2014 Show your own profile, or another visible profile via \`@handle\`. Mirrors the canonical Slock profile view.
|
|
1061
1299
|
19. **\`slock profile update\`** \u2014 Update your own profile. Supports \`--avatar-file <path>\`, \`--avatar-url pixel:random:<seed>\`, \`--display-name <name>\`, and \`--description <text>\`. Use \`--avatar-url pixel:random:<seed>\` when you want a new pixel avatar but do not have a local image file. Values must be non-empty. Provide at least one flag per call; multiple flags can be combined.
|
|
1062
|
-
20. **\`slock integration list\`** \u2014 List registered third-party services and this agent's active Slock Agent Logins.
|
|
1063
|
-
21. **\`slock integration login\`** \u2014 Provision or reuse this agent's login for a registered third-party service.
|
|
1300
|
+
20. **\`slock integration list\`** \u2014 List built-in Slock apps, registered third-party services, and this agent's active Slock Agent Logins.
|
|
1301
|
+
21. **\`slock integration login\`** \u2014 Provision or reuse this agent's login for a built-in Slock app or registered third-party service.
|
|
1064
1302
|
22. **\`slock integration env\`** \u2014 Print per-agent local CLI environment for a manifest-backed service that requires isolated HOME/XDG state.
|
|
1065
1303
|
23. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
|
|
1066
1304
|
24. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
|
|
@@ -1174,9 +1412,9 @@ Each channel has a **name** and optionally a **description** that define its pur
|
|
|
1174
1412
|
- If unsure where something belongs, call ${serverInfoCmd} to review channel descriptions.`;
|
|
1175
1413
|
const thirdPartyIntegrationsSection = isCli ? `### Third-party integrations
|
|
1176
1414
|
|
|
1177
|
-
If a registered third-party service requires login, use Slock Agent Login through the CLI instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app, first run \`slock integration list\` and match the app to a
|
|
1415
|
+
If a built-in Slock app or registered third-party service requires login, use Slock Agent Login through the CLI instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app or built-in Slock app, first run \`slock integration list\` and match the app to a listed service before browsing the app. Use \`slock integration login --service <service>\` to provision or reuse your agent login for that service. If the service exposes an agent behavior manifest and you need to run its local CLI, run \`slock integration env --service <service>\` before invoking that CLI; if it prints exports, apply them first so service credentials stay under a per-agent profile HOME/XDG tree instead of the host user's global HOME. If it reports that no local env is required, do not invent HOME/XDG overrides. If it fails, do not run that local CLI with the host user's HOME; report that the service manifest is unsupported. Slock does not execute commands from remote manifests automatically. If the CLI reports that the \`integration\` command is unknown, the local daemon/CLI is too old for Slock Agent Login; report that the machine must be upgraded/restarted instead of calling internal HTTP endpoints yourself. When the command returns \`Agent login ready\` or \`Already logged in\`, the agent-side login is ready. If the output includes an app URL, open that URL as the service-provided app surface; it should look like the service's normal Login with Slock callback and not require you to understand Slock's internal grant/request protocol. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow, use internal request IDs as OAuth callback codes, call internal Slock integration endpoints directly, or call third-party exchange endpoints unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use \`slock profile show\`. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the listed service / Slock Agent Login path.` : `### Third-party integrations
|
|
1178
1416
|
|
|
1179
|
-
If a registered third-party service requires login, use Slock Agent Login through the available registered-service interface instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app, first inspect the registered-service interface and match the app to a
|
|
1417
|
+
If a built-in Slock app or registered third-party service requires login, use Slock Agent Login through the available registered-service interface instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app or built-in Slock app, first inspect the registered-service interface and match the app to a listed service before browsing the app. Once the registered-service interface reports the agent login is ready, the agent-side login is ready. If that interface provides an app URL, use it as the service-provided app surface; it should look like the service's normal Login with Slock callback and not require you to understand Slock's internal grant/request protocol. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow or treat internal request IDs as OAuth callback codes unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use your Slock profile view. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the listed service / Slock Agent Login path.`;
|
|
1180
1418
|
const readingHistorySection = isCli ? `### Reading history
|
|
1181
1419
|
|
|
1182
1420
|
\`slock message read --channel "#channel-name"\` or \`slock message read --channel dm:@peer-name\` or \`slock message read --channel "#channel:shortid"\`
|
|
@@ -1544,6 +1782,258 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
|
|
|
1544
1782
|
import { randomBytes } from "crypto";
|
|
1545
1783
|
import http from "http";
|
|
1546
1784
|
import { URL as URL2 } from "url";
|
|
1785
|
+
|
|
1786
|
+
// src/apmStateMachine.ts
|
|
1787
|
+
import { createHash } from "crypto";
|
|
1788
|
+
var MAX_APM_GATED_STEERING_EVENTS = 12;
|
|
1789
|
+
function createInitialApmGatedSteeringState() {
|
|
1790
|
+
return {
|
|
1791
|
+
isIdle: false,
|
|
1792
|
+
expectedTerminationReason: null,
|
|
1793
|
+
phase: "idle",
|
|
1794
|
+
outstandingToolUses: 0,
|
|
1795
|
+
compacting: false,
|
|
1796
|
+
toolBoundaryFlushDisabled: false,
|
|
1797
|
+
lastFlushReason: null,
|
|
1798
|
+
recentEvents: []
|
|
1799
|
+
};
|
|
1800
|
+
}
|
|
1801
|
+
function reduceApmIdleState(state, input) {
|
|
1802
|
+
return {
|
|
1803
|
+
nextState: {
|
|
1804
|
+
...state,
|
|
1805
|
+
isIdle: input.isIdle
|
|
1806
|
+
}
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
function reduceApmGatedToolUse(state, input) {
|
|
1810
|
+
if (input.kind === "tool_call") {
|
|
1811
|
+
return {
|
|
1812
|
+
nextState: {
|
|
1813
|
+
isIdle: false,
|
|
1814
|
+
expectedTerminationReason: state.expectedTerminationReason,
|
|
1815
|
+
phase: "tool_wait",
|
|
1816
|
+
outstandingToolUses: state.outstandingToolUses + 1,
|
|
1817
|
+
compacting: state.compacting,
|
|
1818
|
+
toolBoundaryFlushDisabled: state.toolBoundaryFlushDisabled,
|
|
1819
|
+
lastFlushReason: state.lastFlushReason,
|
|
1820
|
+
recentEvents: state.recentEvents
|
|
1821
|
+
},
|
|
1822
|
+
hadOutstandingToolUse: state.outstandingToolUses > 0,
|
|
1823
|
+
shouldFlushToolBatch: false
|
|
1824
|
+
};
|
|
1825
|
+
}
|
|
1826
|
+
const hadOutstandingToolUse = state.outstandingToolUses > 0;
|
|
1827
|
+
const outstandingToolUses = Math.max(0, state.outstandingToolUses - 1);
|
|
1828
|
+
return {
|
|
1829
|
+
nextState: {
|
|
1830
|
+
isIdle: false,
|
|
1831
|
+
expectedTerminationReason: state.expectedTerminationReason,
|
|
1832
|
+
phase: "tool_boundary",
|
|
1833
|
+
outstandingToolUses,
|
|
1834
|
+
compacting: state.compacting,
|
|
1835
|
+
toolBoundaryFlushDisabled: state.toolBoundaryFlushDisabled,
|
|
1836
|
+
lastFlushReason: state.lastFlushReason,
|
|
1837
|
+
recentEvents: state.recentEvents
|
|
1838
|
+
},
|
|
1839
|
+
hadOutstandingToolUse,
|
|
1840
|
+
shouldFlushToolBatch: hadOutstandingToolUse && outstandingToolUses === 0
|
|
1841
|
+
};
|
|
1842
|
+
}
|
|
1843
|
+
function reduceApmGatedCompaction(state, input) {
|
|
1844
|
+
if (input.kind === "compaction_started") {
|
|
1845
|
+
return {
|
|
1846
|
+
nextState: {
|
|
1847
|
+
isIdle: false,
|
|
1848
|
+
expectedTerminationReason: state.expectedTerminationReason,
|
|
1849
|
+
phase: "compacting",
|
|
1850
|
+
outstandingToolUses: state.outstandingToolUses,
|
|
1851
|
+
compacting: true,
|
|
1852
|
+
toolBoundaryFlushDisabled: state.toolBoundaryFlushDisabled,
|
|
1853
|
+
lastFlushReason: state.lastFlushReason,
|
|
1854
|
+
recentEvents: state.recentEvents
|
|
1855
|
+
}
|
|
1856
|
+
};
|
|
1857
|
+
}
|
|
1858
|
+
if (input.kind === "compaction_interrupted") {
|
|
1859
|
+
return {
|
|
1860
|
+
nextState: {
|
|
1861
|
+
isIdle: false,
|
|
1862
|
+
expectedTerminationReason: state.expectedTerminationReason,
|
|
1863
|
+
phase: state.phase,
|
|
1864
|
+
outstandingToolUses: state.outstandingToolUses,
|
|
1865
|
+
compacting: false,
|
|
1866
|
+
toolBoundaryFlushDisabled: state.toolBoundaryFlushDisabled,
|
|
1867
|
+
lastFlushReason: state.lastFlushReason,
|
|
1868
|
+
recentEvents: state.recentEvents
|
|
1869
|
+
}
|
|
1870
|
+
};
|
|
1871
|
+
}
|
|
1872
|
+
return {
|
|
1873
|
+
nextState: {
|
|
1874
|
+
isIdle: false,
|
|
1875
|
+
expectedTerminationReason: state.expectedTerminationReason,
|
|
1876
|
+
phase: "assistant_continuation",
|
|
1877
|
+
outstandingToolUses: state.outstandingToolUses,
|
|
1878
|
+
compacting: false,
|
|
1879
|
+
toolBoundaryFlushDisabled: state.toolBoundaryFlushDisabled,
|
|
1880
|
+
lastFlushReason: state.lastFlushReason,
|
|
1881
|
+
recentEvents: state.recentEvents
|
|
1882
|
+
}
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
1885
|
+
function reduceApmGatedCompactionBoundaryFlush(_state, input) {
|
|
1886
|
+
if (!input.hasSession || !input.supportsStdinNotification || input.inboxLength === 0) {
|
|
1887
|
+
return { effects: [] };
|
|
1888
|
+
}
|
|
1889
|
+
if (input.pendingNotificationCount === 0) return { effects: [] };
|
|
1890
|
+
return {
|
|
1891
|
+
effects: [{
|
|
1892
|
+
kind: "notify_stdin",
|
|
1893
|
+
reason: "compaction_finished",
|
|
1894
|
+
stdinMode: "busy",
|
|
1895
|
+
clauseId: "SMR-002"
|
|
1896
|
+
}]
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1899
|
+
function reduceApmGatedTurnEnd(_state, input = {}) {
|
|
1900
|
+
const shouldDeliverQueuedMessages = Boolean(
|
|
1901
|
+
input.inboxLength && input.inboxLength > 0 && input.supportsStdinNotification && input.hasSession
|
|
1902
|
+
);
|
|
1903
|
+
return {
|
|
1904
|
+
nextState: {
|
|
1905
|
+
isIdle: !shouldDeliverQueuedMessages,
|
|
1906
|
+
expectedTerminationReason: input.terminateProcessOnTurnEnd === true ? "turn_end" : _state.expectedTerminationReason,
|
|
1907
|
+
phase: "idle",
|
|
1908
|
+
outstandingToolUses: 0,
|
|
1909
|
+
compacting: false,
|
|
1910
|
+
toolBoundaryFlushDisabled: _state.toolBoundaryFlushDisabled,
|
|
1911
|
+
lastFlushReason: _state.lastFlushReason,
|
|
1912
|
+
recentEvents: _state.recentEvents
|
|
1913
|
+
},
|
|
1914
|
+
effects: shouldDeliverQueuedMessages ? [{
|
|
1915
|
+
kind: "deliver_stdin",
|
|
1916
|
+
reason: "turn_end",
|
|
1917
|
+
stdinMode: "idle",
|
|
1918
|
+
clauseId: "SMR-002"
|
|
1919
|
+
}] : []
|
|
1920
|
+
};
|
|
1921
|
+
}
|
|
1922
|
+
function reduceApmGatedError(state, input = {}) {
|
|
1923
|
+
const shouldDisableToolBoundaryFlush = input.disableToolBoundaryFlush === true;
|
|
1924
|
+
return {
|
|
1925
|
+
nextState: {
|
|
1926
|
+
isIdle: input.terminalWakeable === true,
|
|
1927
|
+
expectedTerminationReason: state.expectedTerminationReason,
|
|
1928
|
+
phase: "error",
|
|
1929
|
+
outstandingToolUses: state.outstandingToolUses,
|
|
1930
|
+
compacting: false,
|
|
1931
|
+
toolBoundaryFlushDisabled: state.toolBoundaryFlushDisabled || shouldDisableToolBoundaryFlush,
|
|
1932
|
+
lastFlushReason: state.lastFlushReason,
|
|
1933
|
+
recentEvents: state.recentEvents
|
|
1934
|
+
},
|
|
1935
|
+
shouldDisableToolBoundaryFlush
|
|
1936
|
+
};
|
|
1937
|
+
}
|
|
1938
|
+
function reduceApmGatedAssistantContinuation(state) {
|
|
1939
|
+
return {
|
|
1940
|
+
nextState: {
|
|
1941
|
+
isIdle: false,
|
|
1942
|
+
expectedTerminationReason: state.expectedTerminationReason,
|
|
1943
|
+
phase: "assistant_continuation",
|
|
1944
|
+
outstandingToolUses: state.outstandingToolUses,
|
|
1945
|
+
compacting: state.compacting,
|
|
1946
|
+
toolBoundaryFlushDisabled: state.toolBoundaryFlushDisabled,
|
|
1947
|
+
lastFlushReason: state.lastFlushReason,
|
|
1948
|
+
recentEvents: state.recentEvents
|
|
1949
|
+
}
|
|
1950
|
+
};
|
|
1951
|
+
}
|
|
1952
|
+
function reduceApmStalledRecoveryTermination(state, input) {
|
|
1953
|
+
if (input.inboxLength === 0) {
|
|
1954
|
+
return { nextState: state, shouldTerminate: false, alreadyRecovering: false, blockedReason: "empty_inbox" };
|
|
1955
|
+
}
|
|
1956
|
+
if (state.expectedTerminationReason === "stalled_recovery") {
|
|
1957
|
+
return { nextState: state, shouldTerminate: false, alreadyRecovering: true, blockedReason: null };
|
|
1958
|
+
}
|
|
1959
|
+
const directStdinRuntime = input.supportsStdinNotification && input.busyDeliveryMode === "direct";
|
|
1960
|
+
const canRestartDirectStdinProcess = directStdinRuntime && input.hasSession && (state.outstandingToolUses === 0 || input.hasDirectStdinRecoveryEvidence);
|
|
1961
|
+
const canRestartStalledProcess = !input.supportsStdinNotification || canRestartDirectStdinProcess;
|
|
1962
|
+
if (!canRestartStalledProcess) {
|
|
1963
|
+
return { nextState: state, shouldTerminate: false, alreadyRecovering: false, blockedReason: "runtime_not_restartable" };
|
|
1964
|
+
}
|
|
1965
|
+
if (input.staleForMs < input.staleThresholdMs && !input.runtimeProgressIsStale) {
|
|
1966
|
+
return { nextState: state, shouldTerminate: false, alreadyRecovering: false, blockedReason: "runtime_progress_recent" };
|
|
1967
|
+
}
|
|
1968
|
+
return {
|
|
1969
|
+
nextState: {
|
|
1970
|
+
...state,
|
|
1971
|
+
expectedTerminationReason: "stalled_recovery"
|
|
1972
|
+
},
|
|
1973
|
+
shouldTerminate: true,
|
|
1974
|
+
alreadyRecovering: false,
|
|
1975
|
+
blockedReason: null
|
|
1976
|
+
};
|
|
1977
|
+
}
|
|
1978
|
+
function reduceApmStartupTimeoutTermination(state, input) {
|
|
1979
|
+
if (input.hasRuntimeProgressEvent) {
|
|
1980
|
+
return {
|
|
1981
|
+
nextState: state,
|
|
1982
|
+
shouldTerminate: false,
|
|
1983
|
+
blockedReason: "runtime_progress_started"
|
|
1984
|
+
};
|
|
1985
|
+
}
|
|
1986
|
+
return {
|
|
1987
|
+
nextState: {
|
|
1988
|
+
...state,
|
|
1989
|
+
isIdle: false,
|
|
1990
|
+
expectedTerminationReason: "startup_timeout"
|
|
1991
|
+
},
|
|
1992
|
+
shouldTerminate: true,
|
|
1993
|
+
blockedReason: null
|
|
1994
|
+
};
|
|
1995
|
+
}
|
|
1996
|
+
function reduceApmGatedFlush(state, input) {
|
|
1997
|
+
return {
|
|
1998
|
+
nextState: {
|
|
1999
|
+
...state,
|
|
2000
|
+
lastFlushReason: input.reason
|
|
2001
|
+
}
|
|
2002
|
+
};
|
|
2003
|
+
}
|
|
2004
|
+
function reduceApmGatedRecentEvent(state, input) {
|
|
2005
|
+
const summary = `${input.event}:${state.phase}:tools=${state.outstandingToolUses}:compact=${state.compacting}`;
|
|
2006
|
+
return {
|
|
2007
|
+
nextState: {
|
|
2008
|
+
...state,
|
|
2009
|
+
recentEvents: [...state.recentEvents, summary].slice(-MAX_APM_GATED_STEERING_EVENTS)
|
|
2010
|
+
}
|
|
2011
|
+
};
|
|
2012
|
+
}
|
|
2013
|
+
function reduceApmGatedFlushReadiness(state, input) {
|
|
2014
|
+
if (!input.isGated) return { shouldNotify: false, blockedReason: "non_gated", effects: [] };
|
|
2015
|
+
if (!input.hasSession) return { shouldNotify: false, blockedReason: "missing_session", effects: [] };
|
|
2016
|
+
if (input.inboxLength === 0) return { shouldNotify: false, blockedReason: "empty_inbox", effects: [] };
|
|
2017
|
+
if (state.toolBoundaryFlushDisabled) {
|
|
2018
|
+
return { shouldNotify: false, blockedReason: "tool_boundary_flush_disabled", effects: [] };
|
|
2019
|
+
}
|
|
2020
|
+
if (state.compacting) return { shouldNotify: false, blockedReason: "compacting", effects: [] };
|
|
2021
|
+
if (state.outstandingToolUses > 0) {
|
|
2022
|
+
return { shouldNotify: false, blockedReason: "outstanding_tool_uses", effects: [] };
|
|
2023
|
+
}
|
|
2024
|
+
return {
|
|
2025
|
+
shouldNotify: true,
|
|
2026
|
+
blockedReason: null,
|
|
2027
|
+
effects: [{
|
|
2028
|
+
kind: "notify_stdin",
|
|
2029
|
+
reason: input.reason,
|
|
2030
|
+
stdinMode: "busy",
|
|
2031
|
+
clauseId: "SMR-002"
|
|
2032
|
+
}]
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
// src/agentCredentialProxy.ts
|
|
1547
2037
|
var registrations = /* @__PURE__ */ new Map();
|
|
1548
2038
|
var proxyServerState = null;
|
|
1549
2039
|
var proxyServerStartPromise = null;
|
|
@@ -1682,7 +2172,7 @@ async function handleProxyRequest(req, res) {
|
|
|
1682
2172
|
const bodyBuffer = new ArrayBuffer(rawBodyBuffer.byteLength);
|
|
1683
2173
|
new Uint8Array(bodyBuffer).set(rawBodyBuffer);
|
|
1684
2174
|
body = bodyBuffer;
|
|
1685
|
-
headers.
|
|
2175
|
+
headers.delete("content-length");
|
|
1686
2176
|
}
|
|
1687
2177
|
let sendTarget;
|
|
1688
2178
|
const sideEffectAction = agentApiSideEffectAction(target.pathname);
|
|
@@ -1706,7 +2196,7 @@ async function handleProxyRequest(req, res) {
|
|
|
1706
2196
|
body = prepared.bodyText;
|
|
1707
2197
|
if (sideEffectAction === "send") sendTarget = prepared.target;
|
|
1708
2198
|
headers.set("content-type", "application/json");
|
|
1709
|
-
headers.
|
|
2199
|
+
headers.delete("content-length");
|
|
1710
2200
|
}
|
|
1711
2201
|
const upstream = await daemonFetch(target, {
|
|
1712
2202
|
method,
|
|
@@ -1895,6 +2385,24 @@ function maxMessageSeq(messages) {
|
|
|
1895
2385
|
}
|
|
1896
2386
|
return maxSeq > 0 ? maxSeq : void 0;
|
|
1897
2387
|
}
|
|
2388
|
+
function messageSenderId(message) {
|
|
2389
|
+
if (typeof message.sender_id === "string" && message.sender_id.length > 0) return message.sender_id;
|
|
2390
|
+
if (typeof message.senderId === "string" && message.senderId.length > 0) return message.senderId;
|
|
2391
|
+
return void 0;
|
|
2392
|
+
}
|
|
2393
|
+
function isSelfAuthoredMessage(registration, message) {
|
|
2394
|
+
return messageSenderId(message) === registration.agentId;
|
|
2395
|
+
}
|
|
2396
|
+
function isMessageModelSeen(coordinator, target, message) {
|
|
2397
|
+
const seq = Math.floor(messageSeq(message));
|
|
2398
|
+
const boundary = coordinator.getBoundary(target);
|
|
2399
|
+
if (Number.isFinite(seq) && seq > 0 && typeof boundary === "number" && boundary >= seq) return true;
|
|
2400
|
+
return coordinator.isMessageModelSeen?.({ target, message }) === true;
|
|
2401
|
+
}
|
|
2402
|
+
function resolveFreshnessBoundary(messages) {
|
|
2403
|
+
const seenUpToSeq = maxMessageSeq(messages);
|
|
2404
|
+
return typeof seenUpToSeq === "number" ? { ok: true, seenUpToSeq } : { ok: false, reason: "missing_seq_boundary" };
|
|
2405
|
+
}
|
|
1898
2406
|
function sortBySeq(messages) {
|
|
1899
2407
|
return [...messages].sort((a, b) => messageSeq(a) - messageSeq(b));
|
|
1900
2408
|
}
|
|
@@ -1963,35 +2471,29 @@ function localAgentApiEventsResponse(registration, target) {
|
|
|
1963
2471
|
}
|
|
1964
2472
|
};
|
|
1965
2473
|
}
|
|
1966
|
-
function
|
|
1967
|
-
|
|
1968
|
-
}
|
|
1969
|
-
function localHeldResponse(input) {
|
|
1970
|
-
if (input.messages.length === 0) return void 0;
|
|
2474
|
+
function localHeldContext(input) {
|
|
2475
|
+
if (input.messages.length === 0) return { ok: false, reason: "empty_context" };
|
|
1971
2476
|
const normalized = sortBySeq(normalizeVisibleMessages(input.messages, input.target));
|
|
1972
2477
|
const heldMessages = latestVisibleMessages(normalized, LOCAL_HELD_CONTEXT_LIMIT);
|
|
1973
2478
|
const omittedMessageCount = Math.max(0, normalized.length - heldMessages.length);
|
|
1974
|
-
const
|
|
1975
|
-
if (
|
|
2479
|
+
const boundary = resolveFreshnessBoundary(normalized);
|
|
2480
|
+
if (!boundary.ok) return boundary;
|
|
1976
2481
|
input.coordinator.consumeVisibleMessages({
|
|
1977
2482
|
target: input.target,
|
|
1978
2483
|
messages: heldMessages,
|
|
1979
|
-
boundarySeq: seenUpToSeq,
|
|
2484
|
+
boundarySeq: boundary.seenUpToSeq,
|
|
1980
2485
|
source: input.source
|
|
1981
2486
|
});
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
omittedMessageCount
|
|
2487
|
+
return {
|
|
2488
|
+
ok: true,
|
|
2489
|
+
context: {
|
|
2490
|
+
heldMessages,
|
|
2491
|
+
newMessageCount: normalized.length,
|
|
2492
|
+
shownMessageCount: heldMessages.length,
|
|
2493
|
+
omittedMessageCount,
|
|
2494
|
+
seenUpToSeq: boundary.seenUpToSeq
|
|
2495
|
+
}
|
|
1992
2496
|
};
|
|
1993
|
-
response.seenUpToSeq = seenUpToSeq;
|
|
1994
|
-
return response;
|
|
1995
2497
|
}
|
|
1996
2498
|
function recordFreshnessDecision(coordinator, decision) {
|
|
1997
2499
|
coordinator?.recordFreshnessDecision?.(decision);
|
|
@@ -2037,7 +2539,7 @@ function parseTargetFields(target) {
|
|
|
2037
2539
|
}
|
|
2038
2540
|
function normalizeVisibleMessage(message, target) {
|
|
2039
2541
|
const targetFields = target ? parseTargetFields(target) : {};
|
|
2040
|
-
|
|
2542
|
+
const normalized = {
|
|
2041
2543
|
...targetFields,
|
|
2042
2544
|
...message,
|
|
2043
2545
|
message_id: message.message_id ?? message.id,
|
|
@@ -2046,6 +2548,9 @@ function normalizeVisibleMessage(message, target) {
|
|
|
2046
2548
|
sender_name: message.sender_name ?? message.senderName,
|
|
2047
2549
|
sender_description: message.sender_description ?? message.senderDescription ?? null
|
|
2048
2550
|
};
|
|
2551
|
+
const senderId = messageSenderId(message);
|
|
2552
|
+
if (senderId) normalized.sender_id = senderId;
|
|
2553
|
+
return normalized;
|
|
2049
2554
|
}
|
|
2050
2555
|
function normalizeVisibleMessages(messages, target) {
|
|
2051
2556
|
return messages.map((message) => normalizeVisibleMessage(message, target));
|
|
@@ -2086,31 +2591,46 @@ async function prepareAgentApiSideEffectForward(registration, headers, rawBody,
|
|
|
2086
2591
|
}
|
|
2087
2592
|
const pending = coordinator.getPendingMessages(target);
|
|
2088
2593
|
if (pending.length > 0) {
|
|
2089
|
-
const
|
|
2090
|
-
|
|
2594
|
+
const modelSeenSeq = coordinator.getBoundary(target);
|
|
2595
|
+
const contextResult = localHeldContext({
|
|
2091
2596
|
target,
|
|
2092
2597
|
messages: pending,
|
|
2093
2598
|
coordinator,
|
|
2094
2599
|
source: "side_effect_preflight_context"
|
|
2095
2600
|
});
|
|
2096
|
-
if (
|
|
2097
|
-
|
|
2601
|
+
if (contextResult.ok) {
|
|
2602
|
+
const { context } = contextResult;
|
|
2603
|
+
const decision = {
|
|
2098
2604
|
action,
|
|
2099
2605
|
decision: "local_hold",
|
|
2100
2606
|
target,
|
|
2101
2607
|
inboxTrustState: "trusted",
|
|
2102
2608
|
reason: "exact_target_pending",
|
|
2103
2609
|
pendingCount: pending.length,
|
|
2104
|
-
pendingMaxSeq:
|
|
2105
|
-
modelSeenSeq
|
|
2106
|
-
heldMessageCount:
|
|
2107
|
-
omittedMessageCount:
|
|
2108
|
-
}
|
|
2610
|
+
pendingMaxSeq: context.seenUpToSeq,
|
|
2611
|
+
modelSeenSeq,
|
|
2612
|
+
heldMessageCount: context.shownMessageCount,
|
|
2613
|
+
omittedMessageCount: context.omittedMessageCount
|
|
2614
|
+
};
|
|
2615
|
+
const producerFactId = buildApmFreshnessDecisionProducerFactId(registration.agentId, decision);
|
|
2616
|
+
const localResponse = projectApmHeldFreshnessEnvelope({
|
|
2617
|
+
producerFactId,
|
|
2618
|
+
action,
|
|
2619
|
+
heldMessages: context.heldMessages,
|
|
2620
|
+
newMessageCount: context.newMessageCount,
|
|
2621
|
+
omittedMessageCount: context.omittedMessageCount,
|
|
2622
|
+
seenUpToSeq: context.seenUpToSeq
|
|
2623
|
+
}).body;
|
|
2624
|
+
recordFreshnessDecision(coordinator, { ...decision, producerFactId });
|
|
2625
|
+
return {
|
|
2626
|
+
bodyText: JSON.stringify(body),
|
|
2627
|
+
target,
|
|
2628
|
+
localResponse
|
|
2629
|
+
};
|
|
2109
2630
|
}
|
|
2110
2631
|
return {
|
|
2111
2632
|
bodyText: JSON.stringify(body),
|
|
2112
|
-
target
|
|
2113
|
-
localResponse
|
|
2633
|
+
target
|
|
2114
2634
|
};
|
|
2115
2635
|
}
|
|
2116
2636
|
const existingBoundary = typeof body.seenUpToSeq === "number" && Number.isFinite(body.seenUpToSeq) ? Math.max(0, Math.floor(body.seenUpToSeq)) : void 0;
|
|
@@ -2131,35 +2651,82 @@ async function prepareAgentApiSideEffectForward(registration, headers, rawBody,
|
|
|
2131
2651
|
}
|
|
2132
2652
|
const recent = await loadRecentTargetMessages(registration, headers, target);
|
|
2133
2653
|
if (recent.length > 0) {
|
|
2134
|
-
const
|
|
2654
|
+
const unconsumedCounterparty = recent.filter(
|
|
2655
|
+
(message) => !isSelfAuthoredMessage(registration, message) && !isMessageModelSeen(coordinator, target, message)
|
|
2656
|
+
);
|
|
2657
|
+
const boundary2 = resolveFreshnessBoundary(recent);
|
|
2658
|
+
if (!boundary2.ok) {
|
|
2659
|
+
recordFreshnessDecision(coordinator, {
|
|
2660
|
+
action,
|
|
2661
|
+
decision: "forward",
|
|
2662
|
+
target,
|
|
2663
|
+
inboxTrustState: "untrusted",
|
|
2664
|
+
reason: "target_first_touch_recent_context_without_seq_boundary",
|
|
2665
|
+
pendingCount: 0,
|
|
2666
|
+
modelSeenSeq: 0
|
|
2667
|
+
});
|
|
2668
|
+
return { bodyText: JSON.stringify(body), target };
|
|
2669
|
+
}
|
|
2670
|
+
if (unconsumedCounterparty.length === 0) {
|
|
2671
|
+
const { seenUpToSeq: seenUpToSeq2 } = boundary2;
|
|
2672
|
+
if (action === "send") body.seenUpToSeq = seenUpToSeq2;
|
|
2673
|
+
coordinator.consumeVisibleMessages({ target, messages: recent, boundarySeq: seenUpToSeq2, source: "side_effect_preflight_context" });
|
|
2674
|
+
recordFreshnessDecision(coordinator, {
|
|
2675
|
+
action,
|
|
2676
|
+
decision: "forward",
|
|
2677
|
+
target,
|
|
2678
|
+
inboxTrustState: "untrusted",
|
|
2679
|
+
reason: "target_first_touch_recent_context_already_seen",
|
|
2680
|
+
pendingCount: 0,
|
|
2681
|
+
pendingMaxSeq: seenUpToSeq2,
|
|
2682
|
+
modelSeenSeq: seenUpToSeq2,
|
|
2683
|
+
heldMessageCount: 0,
|
|
2684
|
+
omittedMessageCount: 0
|
|
2685
|
+
});
|
|
2686
|
+
return { bodyText: JSON.stringify(body), target };
|
|
2687
|
+
}
|
|
2688
|
+
const heldBoundary = resolveFreshnessBoundary(unconsumedCounterparty);
|
|
2689
|
+
if (!heldBoundary.ok) {
|
|
2690
|
+
recordFreshnessDecision(coordinator, {
|
|
2691
|
+
action,
|
|
2692
|
+
decision: "forward",
|
|
2693
|
+
target,
|
|
2694
|
+
inboxTrustState: "untrusted",
|
|
2695
|
+
reason: "target_first_touch_counterparty_context_without_seq_boundary",
|
|
2696
|
+
pendingCount: 0,
|
|
2697
|
+
modelSeenSeq: 0
|
|
2698
|
+
});
|
|
2699
|
+
return { bodyText: JSON.stringify(body), target };
|
|
2700
|
+
}
|
|
2701
|
+
const heldMessages = latestVisibleMessages(unconsumedCounterparty, LOCAL_HELD_CONTEXT_LIMIT);
|
|
2702
|
+
const omittedMessageCount = Math.max(0, unconsumedCounterparty.length - heldMessages.length);
|
|
2703
|
+
const { seenUpToSeq } = boundary2;
|
|
2135
2704
|
coordinator.consumeVisibleMessages({ target, messages: recent, boundarySeq: seenUpToSeq, source: "side_effect_preflight_context" });
|
|
2136
|
-
|
|
2705
|
+
const decision = {
|
|
2137
2706
|
action,
|
|
2138
2707
|
decision: "syncing_hold",
|
|
2139
2708
|
target,
|
|
2140
2709
|
inboxTrustState: "untrusted",
|
|
2141
2710
|
reason: "target_first_touch_recent_context",
|
|
2142
2711
|
pendingCount: 0,
|
|
2143
|
-
pendingMaxSeq: seenUpToSeq,
|
|
2712
|
+
pendingMaxSeq: heldBoundary.seenUpToSeq,
|
|
2144
2713
|
modelSeenSeq: 0,
|
|
2145
|
-
heldMessageCount:
|
|
2146
|
-
omittedMessageCount
|
|
2147
|
-
}
|
|
2714
|
+
heldMessageCount: heldMessages.length,
|
|
2715
|
+
omittedMessageCount
|
|
2716
|
+
};
|
|
2717
|
+
const producerFactId = buildApmFreshnessDecisionProducerFactId(registration.agentId, decision);
|
|
2718
|
+
recordFreshnessDecision(coordinator, { ...decision, producerFactId });
|
|
2148
2719
|
return {
|
|
2149
2720
|
bodyText: JSON.stringify(body),
|
|
2150
2721
|
target,
|
|
2151
|
-
localResponse: {
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
seenUpToSeq
|
|
2158
|
-
|
|
2159
|
-
newMessageCount: recent.length,
|
|
2160
|
-
shownMessageCount: recent.length,
|
|
2161
|
-
omittedMessageCount: 0
|
|
2162
|
-
}
|
|
2722
|
+
localResponse: projectApmHeldFreshnessEnvelope({
|
|
2723
|
+
producerFactId,
|
|
2724
|
+
action,
|
|
2725
|
+
heldMessages,
|
|
2726
|
+
newMessageCount: unconsumedCounterparty.length,
|
|
2727
|
+
omittedMessageCount,
|
|
2728
|
+
seenUpToSeq
|
|
2729
|
+
}).body
|
|
2163
2730
|
};
|
|
2164
2731
|
}
|
|
2165
2732
|
recordFreshnessDecision(coordinator, {
|
|
@@ -2512,7 +3079,7 @@ function collectResultErrorDetail(message, fallback) {
|
|
|
2512
3079
|
return parts.join(" | ") || fallback;
|
|
2513
3080
|
}
|
|
2514
3081
|
function isProviderApiFailureText(value, hasToolUse) {
|
|
2515
|
-
return !hasToolUse && /^\s*API Error:/i.test(value) && (/\b(?:ECONNRESET|EPIPE|ETIMEDOUT|ECONNREFUSED|ENOTFOUND|EAI_AGAIN)\b/i.test(value) || /\bUnable to connect to API\b/i.test(value) || /\b(?:timed out|timeout)\b/i.test(value) || /\b5\d{2}\b/.test(value));
|
|
3082
|
+
return !hasToolUse && /^\s*API Error:/i.test(value) && (/\b(?:ECONNRESET|EPIPE|ETIMEDOUT|ECONNREFUSED|ENOTFOUND|EAI_AGAIN)\b/i.test(value) || /\bUnable to connect to API\b/i.test(value) || /\b(?:timed out|timeout)\b/i.test(value) || /\b4\d{2}\b/.test(value) || /\b5\d{2}\b/.test(value));
|
|
2516
3083
|
}
|
|
2517
3084
|
var ClaudeEventNormalizer = class {
|
|
2518
3085
|
normalizeLine(line) {
|
|
@@ -3538,6 +4105,7 @@ var CodexDriver = class {
|
|
|
3538
4105
|
cwd: ctx.workingDirectory,
|
|
3539
4106
|
approvalPolicy: "never",
|
|
3540
4107
|
sandbox: "danger-full-access",
|
|
4108
|
+
sandbox_mode: "danger-full-access",
|
|
3541
4109
|
developerInstructions: ctx.standingPrompt,
|
|
3542
4110
|
// Raw response items are used only as payload-free liveness signals in
|
|
3543
4111
|
// the daemon. They replace the previous transcript-mtime heuristic.
|
|
@@ -5356,7 +5924,7 @@ async function deleteWorkspaceDirectory(dataDir, directoryName) {
|
|
|
5356
5924
|
}
|
|
5357
5925
|
|
|
5358
5926
|
// src/runtimeErrorDiagnostics.ts
|
|
5359
|
-
import { createHash } from "crypto";
|
|
5927
|
+
import { createHash as createHash2 } from "crypto";
|
|
5360
5928
|
var MAX_RUNTIME_ERROR_MESSAGE_EXCERPT_CHARS = 4096;
|
|
5361
5929
|
var RUNTIME_AUTH_ACTION_REQUIRED_PATTERNS = [
|
|
5362
5930
|
/access token could not be refreshed/i,
|
|
@@ -5506,7 +6074,7 @@ function runtimeDisplayName(runtimeId) {
|
|
|
5506
6074
|
}
|
|
5507
6075
|
function fingerprintRuntimeError(value) {
|
|
5508
6076
|
const normalized = value.toLowerCase().replace(/[0-9a-f]{12,}/g, "<hex>").replace(/\b\d+\b/g, "<num>").replace(/\s+/g, " ").trim();
|
|
5509
|
-
return
|
|
6077
|
+
return createHash2("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
5510
6078
|
}
|
|
5511
6079
|
function bucketLength(length) {
|
|
5512
6080
|
if (length === 0) return "0";
|
|
@@ -5612,6 +6180,9 @@ var RuntimeNotificationState = class {
|
|
|
5612
6180
|
var DEFAULT_MAX_CONCURRENT_AGENT_STARTS = 5;
|
|
5613
6181
|
var DEFAULT_AGENT_START_INTERVAL_MS = 500;
|
|
5614
6182
|
var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS = 3;
|
|
6183
|
+
function assertNeverApmEffect(effect) {
|
|
6184
|
+
throw new Error(`Unhandled APM gated steering effect: ${String(effect)}`);
|
|
6185
|
+
}
|
|
5615
6186
|
var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS = 250;
|
|
5616
6187
|
var WORKSPACE_TEXT_FILE_MAX_BYTES = 1048576;
|
|
5617
6188
|
var WORKSPACE_IMAGE_PREVIEW_MAX_BYTES = 5 * 1024 * 1024;
|
|
@@ -5917,7 +6488,8 @@ function formatIncomingMessage(message, driver) {
|
|
|
5917
6488
|
const senderType = formatVisibleActorType(message.sender_type);
|
|
5918
6489
|
const attachSuffix = message.attachments?.length ? ` [${message.attachments.length} attachment${message.attachments.length > 1 ? "s" : ""}: ${message.attachments.map((a) => `${a.filename} (id:${a.id})`).join(", ")} \u2014 use view_file to download]` : "";
|
|
5919
6490
|
const taskSuffix = message.task_status ? ` [task #${message.task_number} status=${message.task_status}${message.task_assignee_id ? ` assignee=${formatTaskAssigneeType(message.task_assignee_type)}:${message.task_assignee_id}` : ""}]` : "";
|
|
5920
|
-
const
|
|
6491
|
+
const lineageSuffix = formatProducerFactLineageBracket(message.producerFactId);
|
|
6492
|
+
const body = `[target=${target} msg=${msgId} time=${time}${senderType}] ${formatSenderHandle(message)}: ${message.content}${attachSuffix}${taskSuffix}${lineageSuffix}`;
|
|
5921
6493
|
return threadJoinPrefix ? `${threadJoinPrefix}
|
|
5922
6494
|
${body}` : body;
|
|
5923
6495
|
}
|
|
@@ -5959,6 +6531,8 @@ function buildUnreadSummary(messages, excludeChannel) {
|
|
|
5959
6531
|
var MAX_TRAJECTORY_TEXT = 2e3;
|
|
5960
6532
|
var TRAJECTORY_COALESCE_MS = 350;
|
|
5961
6533
|
var ACTIVITY_HEARTBEAT_MS = 6e4;
|
|
6534
|
+
var STDIN_NOTIFICATION_INITIAL_DELAY_MS = 3e3;
|
|
6535
|
+
var STDIN_NOTIFICATION_RETRY_DELAY_MS = 15e3;
|
|
5962
6536
|
var COMPACTION_STALE_MS = 5 * 6e4;
|
|
5963
6537
|
var RUNTIME_PROGRESS_STALE_MS = 15 * 6e4;
|
|
5964
6538
|
var DEFAULT_RUNTIME_START_TIMEOUT_MS = 2 * 6e4;
|
|
@@ -5967,7 +6541,6 @@ var MAX_STDOUT_LINES = 8;
|
|
|
5967
6541
|
var MAX_STDOUT_LINE_LENGTH = 240;
|
|
5968
6542
|
var MAX_STDERR_LINES = 8;
|
|
5969
6543
|
var MAX_STDERR_LINE_LENGTH = 240;
|
|
5970
|
-
var MAX_GATED_STEERING_EVENTS = 12;
|
|
5971
6544
|
var ONBOARDING_MEMORY_SEED_ENV = "SLOCK_ONBOARDING_MEMORY_SEED";
|
|
5972
6545
|
var FIRST_CINDY_SEED_MODE = "first-cindy";
|
|
5973
6546
|
function getOnboardingSeedMode(config) {
|
|
@@ -6383,13 +6956,8 @@ function stripManagedRunnerCredential(config) {
|
|
|
6383
6956
|
}
|
|
6384
6957
|
function createGatedSteeringState() {
|
|
6385
6958
|
return {
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
compacting: false,
|
|
6389
|
-
toolBoundaryFlushDisabled: process.env.SLOCK_CLAUDE_GATED_STEERING_TOOL_BOUNDARY === "0",
|
|
6390
|
-
lastFlushReason: null,
|
|
6391
|
-
recentEvents: [],
|
|
6392
|
-
inFlightBatch: null
|
|
6959
|
+
...createInitialApmGatedSteeringState(),
|
|
6960
|
+
toolBoundaryFlushDisabled: process.env.SLOCK_CLAUDE_GATED_STEERING_TOOL_BOUNDARY === "0"
|
|
6393
6961
|
};
|
|
6394
6962
|
}
|
|
6395
6963
|
var RUNTIME_PROFILE_MIGRATION_MESSAGE_PREFIX = "runtime-profile-migration-";
|
|
@@ -6408,7 +6976,7 @@ function runtimeProfileNotificationTitle(kind) {
|
|
|
6408
6976
|
}
|
|
6409
6977
|
function hashRuntimeProfileKey(key) {
|
|
6410
6978
|
if (!key) return void 0;
|
|
6411
|
-
return
|
|
6979
|
+
return createHash3("sha256").update(key).digest("hex").slice(0, 16);
|
|
6412
6980
|
}
|
|
6413
6981
|
function runtimeProfileTurnControl(kind, key, source) {
|
|
6414
6982
|
return {
|
|
@@ -6680,6 +7248,19 @@ function summarizeMessageInputBytes(messages) {
|
|
|
6680
7248
|
runtime_input_thread_context_content_bytes_bucket: bucketBytes(threadContextContentBytes)
|
|
6681
7249
|
};
|
|
6682
7250
|
}
|
|
7251
|
+
function messageProducerFactTraceAttrs(messages) {
|
|
7252
|
+
if (!messages || messages.length === 0) return {};
|
|
7253
|
+
const producerFactIds = /* @__PURE__ */ new Set();
|
|
7254
|
+
for (const message of messages) {
|
|
7255
|
+
const producerFactId = typeof message.producerFactId === "string" ? message.producerFactId.trim() : "";
|
|
7256
|
+
if (producerFactId) producerFactIds.add(producerFactId);
|
|
7257
|
+
}
|
|
7258
|
+
if (producerFactIds.size === 0) return {};
|
|
7259
|
+
return {
|
|
7260
|
+
message_producer_fact_count: producerFactIds.size,
|
|
7261
|
+
...producerFactIds.size === 1 ? { message_producer_fact_id: [...producerFactIds][0] } : {}
|
|
7262
|
+
};
|
|
7263
|
+
}
|
|
6683
7264
|
function buildRuntimeInputTraceAttrs(opts) {
|
|
6684
7265
|
return {
|
|
6685
7266
|
runtime_input_source: opts.source,
|
|
@@ -6748,10 +7329,12 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6748
7329
|
driverResolver;
|
|
6749
7330
|
defaultAgentEnvVarsProvider;
|
|
6750
7331
|
tracer;
|
|
7332
|
+
stdinNotificationRetryMs;
|
|
6751
7333
|
cliTransportTraceDir = null;
|
|
6752
7334
|
deliveryTraceContexts = /* @__PURE__ */ new WeakMap();
|
|
6753
7335
|
processExitTraceAttrs = /* @__PURE__ */ new WeakMap();
|
|
6754
7336
|
agentVisibleBoundaries = /* @__PURE__ */ new Map();
|
|
7337
|
+
agentVisibleMessageIds = /* @__PURE__ */ new Map();
|
|
6755
7338
|
constructor(chatBridgePath, sendToServer, daemonApiKey, opts) {
|
|
6756
7339
|
this.chatBridgePath = chatBridgePath;
|
|
6757
7340
|
this.slockCliPath = opts.slockCliPath ?? "";
|
|
@@ -6763,6 +7346,10 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6763
7346
|
this.driverResolver = opts.driverResolver || getDriver;
|
|
6764
7347
|
this.defaultAgentEnvVarsProvider = opts.defaultAgentEnvVarsProvider || null;
|
|
6765
7348
|
this.tracer = opts.tracer ?? noopTracer;
|
|
7349
|
+
this.stdinNotificationRetryMs = Math.max(
|
|
7350
|
+
0,
|
|
7351
|
+
Math.floor(opts.stdinNotificationRetryMs ?? STDIN_NOTIFICATION_RETRY_DELAY_MS)
|
|
7352
|
+
);
|
|
6766
7353
|
this.maxConcurrentAgentStarts = Math.max(
|
|
6767
7354
|
1,
|
|
6768
7355
|
Math.floor(
|
|
@@ -6793,6 +7380,29 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6793
7380
|
getVisibleBoundary(agentId, target) {
|
|
6794
7381
|
return this.agentVisibleBoundaries.get(agentId)?.get(target);
|
|
6795
7382
|
}
|
|
7383
|
+
visibleMessageIdMap(agentId) {
|
|
7384
|
+
let map = this.agentVisibleMessageIds.get(agentId);
|
|
7385
|
+
if (!map) {
|
|
7386
|
+
map = /* @__PURE__ */ new Map();
|
|
7387
|
+
this.agentVisibleMessageIds.set(agentId, map);
|
|
7388
|
+
}
|
|
7389
|
+
return map;
|
|
7390
|
+
}
|
|
7391
|
+
getVisibleMessageIdSet(agentId, target) {
|
|
7392
|
+
return this.agentVisibleMessageIds.get(agentId)?.get(target);
|
|
7393
|
+
}
|
|
7394
|
+
isVisibleMessageModelSeen(agentId, target, message) {
|
|
7395
|
+
const seq = Number(message.seq ?? 0);
|
|
7396
|
+
const boundary = this.getVisibleBoundary(agentId, target);
|
|
7397
|
+
if (Number.isFinite(seq) && seq > 0 && typeof boundary === "number" && boundary >= Math.floor(seq)) return true;
|
|
7398
|
+
const id = typeof message.message_id === "string" ? message.message_id : typeof message.id === "string" ? message.id : "";
|
|
7399
|
+
return id.length > 0 && this.getVisibleMessageIdSet(agentId, target)?.has(id) === true;
|
|
7400
|
+
}
|
|
7401
|
+
scheduleStdinNotification(agentId, ap, delayMs) {
|
|
7402
|
+
return ap.notifications.schedule(() => {
|
|
7403
|
+
this.sendStdinNotification(agentId);
|
|
7404
|
+
}, delayMs);
|
|
7405
|
+
}
|
|
6796
7406
|
allPendingVisibleMessages(agentId) {
|
|
6797
7407
|
const collect = (messages) => (messages ?? []).filter((message) => typeof message.seq === "number" && message.seq > 0);
|
|
6798
7408
|
return [
|
|
@@ -6822,13 +7432,14 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6822
7432
|
};
|
|
6823
7433
|
for (const message of input.messages) {
|
|
6824
7434
|
const seq = Number(message.seq ?? 0);
|
|
6825
|
-
if (!Number.isFinite(seq) || seq <= 0) continue;
|
|
6826
7435
|
const target = input.target ?? formatVisibleMessageTarget(message);
|
|
6827
7436
|
if (!target) continue;
|
|
6828
7437
|
const bucket = ensureBucket(target);
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
7438
|
+
if (Number.isFinite(seq) && seq > 0) {
|
|
7439
|
+
bucket.maxSeq = Math.max(bucket.maxSeq, Math.floor(seq));
|
|
7440
|
+
bucket.seqs.add(Math.floor(seq));
|
|
7441
|
+
}
|
|
7442
|
+
const id = typeof message.message_id === "string" ? message.message_id : typeof message.id === "string" ? message.id : null;
|
|
6832
7443
|
if (id) bucket.ids.add(id);
|
|
6833
7444
|
}
|
|
6834
7445
|
if (input.target && typeof input.boundarySeq === "number" && Number.isFinite(input.boundarySeq) && input.boundarySeq > 0) {
|
|
@@ -6837,10 +7448,19 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6837
7448
|
}
|
|
6838
7449
|
if (byTarget.size === 0) return;
|
|
6839
7450
|
const boundaryMap = this.visibleBoundaryMap(agentId);
|
|
7451
|
+
const visibleIds = this.visibleMessageIdMap(agentId);
|
|
6840
7452
|
for (const [target, bucket] of byTarget) {
|
|
6841
7453
|
const highWaterSeq = Math.max(bucket.maxSeq, bucket.boundarySeq);
|
|
6842
7454
|
const previous = boundaryMap.get(target) ?? 0;
|
|
6843
7455
|
boundaryMap.set(target, Math.max(previous, highWaterSeq));
|
|
7456
|
+
if (bucket.ids.size > 0) {
|
|
7457
|
+
let targetIds = visibleIds.get(target);
|
|
7458
|
+
if (!targetIds) {
|
|
7459
|
+
targetIds = /* @__PURE__ */ new Set();
|
|
7460
|
+
visibleIds.set(target, targetIds);
|
|
7461
|
+
}
|
|
7462
|
+
for (const id of bucket.ids) targetIds.add(id);
|
|
7463
|
+
}
|
|
6844
7464
|
}
|
|
6845
7465
|
const suppress = (messages) => {
|
|
6846
7466
|
if (!messages || messages.length === 0) return 0;
|
|
@@ -6873,6 +7493,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6873
7493
|
return {
|
|
6874
7494
|
getBoundary: (target) => this.getVisibleBoundary(agentId, target),
|
|
6875
7495
|
getPendingMessages: (target) => this.pendingVisibleMessages(agentId, target),
|
|
7496
|
+
isMessageModelSeen: ({ target, message }) => this.isVisibleMessageModelSeen(agentId, target, message),
|
|
6876
7497
|
getAllPendingMessages: () => this.allPendingVisibleMessages(agentId),
|
|
6877
7498
|
consumeVisibleMessages: (input) => this.consumeVisibleMessages(agentId, input),
|
|
6878
7499
|
recordDrainOutcome: (input) => {
|
|
@@ -6888,20 +7509,16 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6888
7509
|
recordProxyFailure: (input) => this.recordAgentProxyFailure(agentId, input),
|
|
6889
7510
|
recordTransportNormalizedError: (input) => this.recordAgentProxyTransportNormalizedError(agentId, input),
|
|
6890
7511
|
recordFreshnessDecision: (input) => {
|
|
7512
|
+
const producerFactId = input.producerFactId ?? buildApmFreshnessDecisionProducerFactId(agentId, input);
|
|
7513
|
+
const trace = projectApmFreshnessDecisionTrace({
|
|
7514
|
+
producerFactId,
|
|
7515
|
+
decision: input
|
|
7516
|
+
});
|
|
6891
7517
|
this.recordDaemonTrace("daemon.agent.inbox.freshness_decision", {
|
|
6892
7518
|
agentId,
|
|
6893
|
-
|
|
6894
|
-
decision: input.decision,
|
|
6895
|
-
target: input.target,
|
|
6896
|
-
inbox_trust_state: input.inboxTrustState,
|
|
6897
|
-
reason: input.reason,
|
|
6898
|
-
pending_count: input.pendingCount,
|
|
6899
|
-
pending_max_seq: input.pendingMaxSeq,
|
|
6900
|
-
model_seen_seq: input.modelSeenSeq,
|
|
6901
|
-
held_message_count: input.heldMessageCount,
|
|
6902
|
-
omitted_message_count: input.omittedMessageCount
|
|
7519
|
+
...trace.attrs
|
|
6903
7520
|
});
|
|
6904
|
-
this.recordFreshnessDecisionActivity(agentId, input);
|
|
7521
|
+
this.recordFreshnessDecisionActivity(agentId, input, producerFactId);
|
|
6905
7522
|
}
|
|
6906
7523
|
};
|
|
6907
7524
|
}
|
|
@@ -6931,20 +7548,17 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6931
7548
|
upstream: input.upstream
|
|
6932
7549
|
}, "error");
|
|
6933
7550
|
}
|
|
6934
|
-
recordFreshnessDecisionActivity(agentId, input) {
|
|
7551
|
+
recordFreshnessDecisionActivity(agentId, input, producerFactId) {
|
|
6935
7552
|
if (input.decision !== "local_hold" && input.decision !== "syncing_hold") return;
|
|
6936
7553
|
const ap = this.agents.get(agentId);
|
|
6937
7554
|
const messageCount = input.pendingCount ?? input.heldMessageCount ?? 0;
|
|
6938
|
-
const
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
`decision: ${input.decision === "syncing_hold" ? "syncing hold" : "local hold"}; review the newer context before retrying`
|
|
6946
|
-
].filter((line) => Boolean(line)).join("\n")
|
|
6947
|
-
};
|
|
7555
|
+
const entry = projectApmHeldFreshnessActivity({
|
|
7556
|
+
producerFactId,
|
|
7557
|
+
action: input.action,
|
|
7558
|
+
decision: input.decision,
|
|
7559
|
+
target: input.target,
|
|
7560
|
+
messageCount
|
|
7561
|
+
}).entry;
|
|
6948
7562
|
if (ap) ap.activityClientSeq += 1;
|
|
6949
7563
|
this.sendToServer({
|
|
6950
7564
|
type: "agent:activity",
|
|
@@ -7353,7 +7967,6 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7353
7967
|
notifications: new RuntimeNotificationState(),
|
|
7354
7968
|
activityHeartbeat: null,
|
|
7355
7969
|
startupTimeoutTimer: null,
|
|
7356
|
-
startupTimedOut: false,
|
|
7357
7970
|
compactionWatchdog: null,
|
|
7358
7971
|
compactionStartedAt: null,
|
|
7359
7972
|
runtimeProgress: new RuntimeProgressState(Date.now()),
|
|
@@ -7485,7 +8098,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7485
8098
|
this.clearStalledRecoverySigtermWatchdog(ap);
|
|
7486
8099
|
const finalCode = ap.exitCode ?? code;
|
|
7487
8100
|
const finalSignal = ap.exitSignal ?? signal;
|
|
7488
|
-
const
|
|
8101
|
+
const startupTimeoutTermination = ap.expectedTerminationReason === "startup_timeout";
|
|
8102
|
+
const expectedTermination = Boolean(ap.expectedTerminationReason);
|
|
7489
8103
|
const processEndedCleanly = finalCode === 0 || expectedTermination && !ap.lastRuntimeError;
|
|
7490
8104
|
const terminalFailureDetail = processEndedCleanly ? null : classifyTerminalFailure(ap);
|
|
7491
8105
|
const resumeRecoveryReason = resumeSessionRecoveryReason(ap);
|
|
@@ -7589,7 +8203,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7589
8203
|
});
|
|
7590
8204
|
logger.warn(`[Agent ${agentId}] Recoverable provider stream failure (${reason}) \u2014 keeping agent wakeable`);
|
|
7591
8205
|
this.sendAgentStatus(agentId, "active", ap.launchId);
|
|
7592
|
-
} else if (
|
|
8206
|
+
} else if (startupTimeoutTermination) {
|
|
7593
8207
|
logger.warn(`[Agent ${agentId}] Startup timeout cleanup completed (${reason})`);
|
|
7594
8208
|
} else {
|
|
7595
8209
|
this.idleAgentConfigs.delete(agentId);
|
|
@@ -7597,7 +8211,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7597
8211
|
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
7598
8212
|
}
|
|
7599
8213
|
if (terminalFailureDetail) {
|
|
7600
|
-
if (!
|
|
8214
|
+
if (!startupTimeoutTermination) {
|
|
7601
8215
|
this.broadcastActivity(
|
|
7602
8216
|
agentId,
|
|
7603
8217
|
"error",
|
|
@@ -7795,9 +8409,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7795
8409
|
pendingMessages: ap.inbox.length
|
|
7796
8410
|
});
|
|
7797
8411
|
} else if (!ap.notifications.hasTimer) {
|
|
7798
|
-
|
|
7799
|
-
this.sendStdinNotification(agentId);
|
|
7800
|
-
}, 3e3);
|
|
8412
|
+
this.scheduleStdinNotification(agentId, ap, STDIN_NOTIFICATION_INITIAL_DELAY_MS);
|
|
7801
8413
|
}
|
|
7802
8414
|
}
|
|
7803
8415
|
this.recordDaemonTrace("daemon.agent.runtime_profile.routed", {
|
|
@@ -7975,7 +8587,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7975
8587
|
if (ap.isIdle && ap.driver.supportsStdinNotification && ap.sessionId) {
|
|
7976
8588
|
const nextMessages = ap.inbox.splice(0, ap.inbox.length);
|
|
7977
8589
|
nextMessages.push(message);
|
|
7978
|
-
|
|
8590
|
+
this.commitApmIdleState(agentId, ap, false);
|
|
7979
8591
|
this.startRuntimeTrace(agentId, ap, "stdin-idle-delivery", nextMessages);
|
|
7980
8592
|
this.broadcastActivity(agentId, "working", "Message received");
|
|
7981
8593
|
const stdinAccepted = this.deliverMessagesViaStdin(agentId, ap, nextMessages, "idle");
|
|
@@ -8059,9 +8671,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8059
8671
|
if (ap.driver.busyDeliveryMode === "gated") {
|
|
8060
8672
|
ap.notifications.add();
|
|
8061
8673
|
if (!ap.notifications.hasTimer) {
|
|
8062
|
-
|
|
8063
|
-
this.sendStdinNotification(agentId);
|
|
8064
|
-
}, 3e3);
|
|
8674
|
+
this.scheduleStdinNotification(agentId, ap, STDIN_NOTIFICATION_INITIAL_DELAY_MS);
|
|
8065
8675
|
}
|
|
8066
8676
|
this.recordGatedSteeringEvent(agentId, ap, "buffer", {
|
|
8067
8677
|
reason: "busy_message",
|
|
@@ -8082,9 +8692,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8082
8692
|
}
|
|
8083
8693
|
ap.notifications.add();
|
|
8084
8694
|
if (!ap.notifications.hasTimer) {
|
|
8085
|
-
|
|
8086
|
-
this.sendStdinNotification(agentId);
|
|
8087
|
-
}, 3e3);
|
|
8695
|
+
this.scheduleStdinNotification(agentId, ap, STDIN_NOTIFICATION_INITIAL_DELAY_MS);
|
|
8088
8696
|
}
|
|
8089
8697
|
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
8090
8698
|
outcome: "queued_busy_notification",
|
|
@@ -8258,7 +8866,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8258
8866
|
return true;
|
|
8259
8867
|
}
|
|
8260
8868
|
if (ap?.sessionId && ap.driver.supportsStdinNotification && ap.isIdle) {
|
|
8261
|
-
|
|
8869
|
+
this.commitApmIdleState(agentId, ap, false);
|
|
8262
8870
|
this.startRuntimeTrace(agentId, ap, "runtime-profile", [message]);
|
|
8263
8871
|
const written = this.deliverMessagesViaStdin(agentId, ap, [message], "idle");
|
|
8264
8872
|
span.end(written ? "ok" : "error", {
|
|
@@ -8768,18 +9376,22 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8768
9376
|
if (!ap || !ap.compactionStartedAt) return;
|
|
8769
9377
|
this.clearCompactionWatchdog(ap);
|
|
8770
9378
|
this.broadcastActivity(agentId, "working", detail, [{ kind: "compaction_finished" }]);
|
|
8771
|
-
ap.gatedSteering
|
|
8772
|
-
this.
|
|
9379
|
+
const reduction = reduceApmGatedCompaction(ap.gatedSteering, { kind: "compaction_finished" });
|
|
9380
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, {
|
|
9381
|
+
event: "compaction_finished",
|
|
9382
|
+
inferred: true
|
|
9383
|
+
});
|
|
8773
9384
|
if (options.flushBoundaryMessages ?? true) {
|
|
8774
9385
|
this.flushCompactionBoundaryMessages(agentId, ap);
|
|
8775
9386
|
}
|
|
8776
|
-
|
|
9387
|
+
this.commitApmIdleState(agentId, ap, false);
|
|
8777
9388
|
}
|
|
8778
9389
|
interruptCompactionIfActive(agentId) {
|
|
8779
9390
|
const ap = this.agents.get(agentId);
|
|
8780
9391
|
if (!ap || !ap.compactionStartedAt && !ap.gatedSteering.compacting) return;
|
|
8781
9392
|
this.clearCompactionWatchdog(ap);
|
|
8782
|
-
ap.gatedSteering
|
|
9393
|
+
const reduction = reduceApmGatedCompaction(ap.gatedSteering, { kind: "compaction_interrupted" });
|
|
9394
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState);
|
|
8783
9395
|
}
|
|
8784
9396
|
messagesTraceAttrs(messages) {
|
|
8785
9397
|
if (!messages || messages.length === 0) return {};
|
|
@@ -8792,6 +9404,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8792
9404
|
message_id_present: Boolean(first.message_id),
|
|
8793
9405
|
deliveryId: context.deliveryId,
|
|
8794
9406
|
delivery_correlation_id: context.deliveryId,
|
|
9407
|
+
...messageProducerFactTraceAttrs(messages),
|
|
8795
9408
|
control_kind: notification.kind,
|
|
8796
9409
|
key_present: Boolean(notification.key),
|
|
8797
9410
|
runtime_profile_control_kind: notification.kind,
|
|
@@ -8804,7 +9417,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8804
9417
|
messageId: first.message_id,
|
|
8805
9418
|
message_id_present: Boolean(first.message_id),
|
|
8806
9419
|
deliveryId: context.deliveryId,
|
|
8807
|
-
delivery_correlation_id: context.deliveryId ?? first.message_id
|
|
9420
|
+
delivery_correlation_id: context.deliveryId ?? first.message_id,
|
|
9421
|
+
...messageProducerFactTraceAttrs(messages)
|
|
8808
9422
|
};
|
|
8809
9423
|
}
|
|
8810
9424
|
runtimeProfileTurnControlTraceAttrs(control) {
|
|
@@ -8893,10 +9507,11 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8893
9507
|
}
|
|
8894
9508
|
recordGatedSteeringEvent(agentId, ap, event, attrs = {}) {
|
|
8895
9509
|
if (ap.driver.busyDeliveryMode !== "gated") return;
|
|
8896
|
-
const
|
|
8897
|
-
ap.
|
|
8898
|
-
ap.gatedSteering.recentEvents = ap.gatedSteering.recentEvents.slice(-MAX_GATED_STEERING_EVENTS);
|
|
9510
|
+
const reduction = reduceApmGatedRecentEvent(ap.gatedSteering, { event });
|
|
9511
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState);
|
|
8899
9512
|
this.recordRuntimeTraceEvent(agentId, ap, `runtime.gated_steering.${event}`, {
|
|
9513
|
+
isIdle: ap.gatedSteering.isIdle,
|
|
9514
|
+
expectedTerminationReason: ap.gatedSteering.expectedTerminationReason,
|
|
8900
9515
|
phase: ap.gatedSteering.phase,
|
|
8901
9516
|
outstandingToolUses: ap.gatedSteering.outstandingToolUses,
|
|
8902
9517
|
compacting: ap.gatedSteering.compacting,
|
|
@@ -8905,63 +9520,110 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8905
9520
|
...attrs
|
|
8906
9521
|
});
|
|
8907
9522
|
}
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
ap.gatedSteering.
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
}
|
|
8922
|
-
|
|
8923
|
-
|
|
8924
|
-
const
|
|
8925
|
-
ap.
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
8933
|
-
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
);
|
|
8939
|
-
if (reason === "turn_end") {
|
|
8940
|
-
this.broadcastActivity(agentId, "working", "Message received");
|
|
8941
|
-
}
|
|
8942
|
-
if (this.deliverMessagesViaStdin(agentId, ap, nextMessages, reason === "turn_end" ? "idle" : "busy")) {
|
|
8943
|
-
return true;
|
|
9523
|
+
commitGatedSteeringDecisionState(agentId, ap, nextState, phaseEventAttrs) {
|
|
9524
|
+
ap.gatedSteering.phase = nextState.phase;
|
|
9525
|
+
ap.gatedSteering.outstandingToolUses = nextState.outstandingToolUses;
|
|
9526
|
+
ap.gatedSteering.compacting = nextState.compacting;
|
|
9527
|
+
ap.gatedSteering.toolBoundaryFlushDisabled = nextState.toolBoundaryFlushDisabled;
|
|
9528
|
+
ap.gatedSteering.lastFlushReason = nextState.lastFlushReason;
|
|
9529
|
+
ap.gatedSteering.recentEvents = nextState.recentEvents;
|
|
9530
|
+
ap.gatedSteering.isIdle = nextState.isIdle;
|
|
9531
|
+
ap.gatedSteering.expectedTerminationReason = nextState.expectedTerminationReason;
|
|
9532
|
+
ap.isIdle = nextState.isIdle;
|
|
9533
|
+
ap.expectedTerminationReason = nextState.expectedTerminationReason;
|
|
9534
|
+
if (phaseEventAttrs) {
|
|
9535
|
+
this.recordGatedSteeringEvent(agentId, ap, "phase", phaseEventAttrs);
|
|
9536
|
+
}
|
|
9537
|
+
}
|
|
9538
|
+
commitApmIdleState(agentId, ap, isIdle) {
|
|
9539
|
+
const reduction = reduceApmIdleState(ap.gatedSteering, { isIdle });
|
|
9540
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState);
|
|
9541
|
+
}
|
|
9542
|
+
notifyGatedSteeringBoundary(agentId, ap, reason) {
|
|
9543
|
+
const readiness = reduceApmGatedFlushReadiness(ap.gatedSteering, {
|
|
9544
|
+
isGated: ap.driver.busyDeliveryMode === "gated",
|
|
9545
|
+
hasSession: Boolean(ap.sessionId),
|
|
9546
|
+
inboxLength: ap.inbox.length,
|
|
9547
|
+
reason
|
|
9548
|
+
});
|
|
9549
|
+
if (readiness.effects.length === 0) return false;
|
|
9550
|
+
let didExecute = false;
|
|
9551
|
+
for (const effect of readiness.effects) {
|
|
9552
|
+
didExecute = this.executeApmGatedSteeringEffect(agentId, ap, effect) || didExecute;
|
|
8944
9553
|
}
|
|
8945
|
-
|
|
8946
|
-
|
|
9554
|
+
return didExecute;
|
|
9555
|
+
}
|
|
9556
|
+
recordApmGatedSteeringEffectTrace(agentId, ap, effect, attrs) {
|
|
9557
|
+
this.recordDaemonTrace("daemon.apm.gated_effect", {
|
|
9558
|
+
agentId,
|
|
9559
|
+
launchId: ap.launchId || void 0,
|
|
9560
|
+
runtime: ap.config.runtime,
|
|
9561
|
+
effect_kind: effect.kind,
|
|
9562
|
+
reason: effect.reason,
|
|
9563
|
+
target: "runtime-stdin",
|
|
9564
|
+
stdin_mode: effect.stdinMode,
|
|
9565
|
+
clause_id: effect.clauseId,
|
|
9566
|
+
pending_messages: ap.inbox.length,
|
|
9567
|
+
...attrs
|
|
9568
|
+
});
|
|
9569
|
+
}
|
|
9570
|
+
executeApmGatedSteeringEffect(agentId, ap, effect) {
|
|
9571
|
+
switch (effect.kind) {
|
|
9572
|
+
case "notify_stdin":
|
|
9573
|
+
this.recordGatedSteeringEvent(agentId, ap, "notify", {
|
|
9574
|
+
reason: effect.reason,
|
|
9575
|
+
pendingMessages: ap.inbox.length
|
|
9576
|
+
});
|
|
9577
|
+
{
|
|
9578
|
+
const written = this.sendStdinNotification(agentId);
|
|
9579
|
+
this.recordApmGatedSteeringEffectTrace(agentId, ap, effect, {
|
|
9580
|
+
outcome: written ? "written" : "not_written"
|
|
9581
|
+
});
|
|
9582
|
+
return written;
|
|
9583
|
+
}
|
|
9584
|
+
case "deliver_stdin": {
|
|
9585
|
+
const messages = ap.inbox.splice(0, ap.inbox.length);
|
|
9586
|
+
ap.notifications.clear();
|
|
9587
|
+
if (messages.length === 0) {
|
|
9588
|
+
this.recordApmGatedSteeringEffectTrace(agentId, ap, effect, {
|
|
9589
|
+
outcome: "empty",
|
|
9590
|
+
delivered_messages_count: 0
|
|
9591
|
+
});
|
|
9592
|
+
return true;
|
|
9593
|
+
}
|
|
9594
|
+
if (ap.driver.busyDeliveryMode === "gated") {
|
|
9595
|
+
const flushReduction = reduceApmGatedFlush(ap.gatedSteering, { reason: effect.reason });
|
|
9596
|
+
this.commitGatedSteeringDecisionState(agentId, ap, flushReduction.nextState);
|
|
9597
|
+
this.recordGatedSteeringEvent(agentId, ap, "flush", {
|
|
9598
|
+
reason: effect.reason,
|
|
9599
|
+
messageCount: messages.length
|
|
9600
|
+
});
|
|
9601
|
+
}
|
|
9602
|
+
this.broadcastActivity(agentId, "working", "Message received");
|
|
9603
|
+
const accepted = this.deliverMessagesViaStdin(agentId, ap, messages, effect.stdinMode);
|
|
9604
|
+
this.recordApmGatedSteeringEffectTrace(agentId, ap, effect, {
|
|
9605
|
+
outcome: accepted ? "written" : "not_written",
|
|
9606
|
+
delivered_messages_count: messages.length
|
|
9607
|
+
});
|
|
9608
|
+
return accepted;
|
|
9609
|
+
}
|
|
9610
|
+
default:
|
|
9611
|
+
return assertNeverApmEffect(effect);
|
|
8947
9612
|
}
|
|
8948
|
-
ap.notifications.add(pendingNotificationCount || pendingMessages);
|
|
8949
|
-
return false;
|
|
8950
9613
|
}
|
|
8951
9614
|
flushCompactionBoundaryMessages(agentId, ap) {
|
|
8952
|
-
|
|
8953
|
-
|
|
8954
|
-
ap.
|
|
8955
|
-
|
|
8956
|
-
|
|
9615
|
+
const reduction = reduceApmGatedCompactionBoundaryFlush(ap.gatedSteering, {
|
|
9616
|
+
hasSession: Boolean(ap.sessionId),
|
|
9617
|
+
supportsStdinNotification: ap.driver.supportsStdinNotification,
|
|
9618
|
+
inboxLength: ap.inbox.length,
|
|
9619
|
+
pendingNotificationCount: ap.notifications.pendingCount
|
|
9620
|
+
});
|
|
9621
|
+
if (reduction.effects.length === 0) return false;
|
|
9622
|
+
ap.notifications.clearTimer();
|
|
9623
|
+
for (const effect of reduction.effects) {
|
|
9624
|
+
this.executeApmGatedSteeringEffect(agentId, ap, effect);
|
|
8957
9625
|
}
|
|
8958
|
-
return
|
|
8959
|
-
}
|
|
8960
|
-
clearGatedInFlightBatch(agentId, ap, reason) {
|
|
8961
|
-
if (ap.driver.busyDeliveryMode !== "gated" || !ap.gatedSteering.inFlightBatch) return;
|
|
8962
|
-
const messageCount = ap.gatedSteering.inFlightBatch.messages.length;
|
|
8963
|
-
ap.gatedSteering.inFlightBatch = null;
|
|
8964
|
-
this.recordGatedSteeringEvent(agentId, ap, "ack", { reason, messageCount });
|
|
9626
|
+
return true;
|
|
8965
9627
|
}
|
|
8966
9628
|
startRuntimeStartupTimeout(agentId, ap) {
|
|
8967
9629
|
const timeoutMs = runtimeStartTimeoutMs();
|
|
@@ -8979,14 +9641,17 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8979
9641
|
handleRuntimeStartupTimeout(agentId, ap, timeoutMs) {
|
|
8980
9642
|
const current = this.agents.get(agentId);
|
|
8981
9643
|
if (current !== ap) return;
|
|
8982
|
-
|
|
9644
|
+
const reduction = reduceApmStartupTimeoutTermination(ap.gatedSteering, {
|
|
9645
|
+
hasRuntimeProgressEvent: Boolean(ap.runtimeProgress.lastEventKind)
|
|
9646
|
+
});
|
|
9647
|
+
if (!reduction.shouldTerminate) {
|
|
8983
9648
|
this.clearRuntimeStartupTimeout(ap);
|
|
8984
9649
|
return;
|
|
8985
9650
|
}
|
|
8986
9651
|
this.clearRuntimeStartupTimeout(ap);
|
|
9652
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState);
|
|
8987
9653
|
const terminalFailureDetail = classifyTerminalFailure(ap);
|
|
8988
9654
|
const detail = terminalFailureDetail?.detail ?? formatRuntimeStartTimeoutMessage(ap.driver.id);
|
|
8989
|
-
ap.startupTimedOut = true;
|
|
8990
9655
|
ap.lastRuntimeError = detail;
|
|
8991
9656
|
ap.runtimeProgress.markStale();
|
|
8992
9657
|
const staleForMs = Math.max(timeoutMs, ap.runtimeProgress.ageMs());
|
|
@@ -9015,6 +9680,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9015
9680
|
try {
|
|
9016
9681
|
this.processExitTraceAttrs.set(ap.process, {
|
|
9017
9682
|
stop_source: "startup_timeout",
|
|
9683
|
+
expectedTerminationReason: "startup_timeout",
|
|
9018
9684
|
timeout_ms: timeoutMs
|
|
9019
9685
|
});
|
|
9020
9686
|
ap.process.kill("SIGTERM");
|
|
@@ -9023,20 +9689,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9023
9689
|
logger.warn(`[Agent ${agentId}] Failed to terminate startup-timed-out ${ap.driver.id} process: ${reason}`);
|
|
9024
9690
|
}
|
|
9025
9691
|
}
|
|
9026
|
-
requeueGatedInFlightBatch(agentId, ap, reason) {
|
|
9027
|
-
if (ap.driver.busyDeliveryMode !== "gated" || !ap.gatedSteering.inFlightBatch) return;
|
|
9028
|
-
const batch = ap.gatedSteering.inFlightBatch;
|
|
9029
|
-
ap.gatedSteering.inFlightBatch = null;
|
|
9030
|
-
ap.inbox.unshift(...batch.messages);
|
|
9031
|
-
ap.notifications.add(batch.messages.length);
|
|
9032
|
-
this.recordGatedSteeringEvent(agentId, ap, "requeue", {
|
|
9033
|
-
reason,
|
|
9034
|
-
originalFlushReason: batch.reason,
|
|
9035
|
-
messageCount: batch.messages.length
|
|
9036
|
-
});
|
|
9037
|
-
}
|
|
9038
9692
|
isThinkingBlockMutationError(message) {
|
|
9039
|
-
return /thinking.*redacted_thinking|redacted_thinking.*thinking/i.test(message) && /cannot be modified/i.test(message);
|
|
9693
|
+
return /thinking.*redacted_thinking|redacted_thinking.*thinking/i.test(message) && /cannot be modified/i.test(message) || /messages\.\d+\.content\.\d+\.text\.start_timestamp:\s*Extra inputs are not permitted/i.test(message);
|
|
9040
9694
|
}
|
|
9041
9695
|
markRuntimeProgressStaleIfNeeded(agentId, ap) {
|
|
9042
9696
|
if (ap.lastActivity !== "working" && ap.lastActivity !== "thinking") return false;
|
|
@@ -9068,16 +9722,22 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9068
9722
|
return true;
|
|
9069
9723
|
}
|
|
9070
9724
|
recoverStaleProcessForQueuedMessageIfNeeded(agentId, ap) {
|
|
9071
|
-
|
|
9072
|
-
|
|
9725
|
+
const staleForMs = ap.runtimeProgress.ageMs();
|
|
9726
|
+
const reduction = reduceApmStalledRecoveryTermination(ap.gatedSteering, {
|
|
9727
|
+
inboxLength: ap.inbox.length,
|
|
9728
|
+
supportsStdinNotification: ap.driver.supportsStdinNotification,
|
|
9729
|
+
busyDeliveryMode: ap.driver.busyDeliveryMode,
|
|
9730
|
+
hasSession: Boolean(ap.sessionId),
|
|
9731
|
+
hasDirectStdinRecoveryEvidence: hasDirectStdinRecoveryEvidence(ap),
|
|
9732
|
+
runtimeProgressIsStale: ap.runtimeProgress.isStale,
|
|
9733
|
+
staleForMs,
|
|
9734
|
+
staleThresholdMs: RUNTIME_PROGRESS_STALE_MS
|
|
9735
|
+
});
|
|
9736
|
+
if (reduction.alreadyRecovering) {
|
|
9073
9737
|
return true;
|
|
9074
9738
|
}
|
|
9075
|
-
|
|
9076
|
-
|
|
9077
|
-
const canRestartStalledProcess = !ap.driver.supportsStdinNotification || canRestartDirectStdinProcess;
|
|
9078
|
-
if (!canRestartStalledProcess) return false;
|
|
9079
|
-
const staleForMs = ap.runtimeProgress.ageMs();
|
|
9080
|
-
if (staleForMs < RUNTIME_PROGRESS_STALE_MS && !ap.runtimeProgress.isStale) return false;
|
|
9739
|
+
if (!reduction.shouldTerminate) return false;
|
|
9740
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState);
|
|
9081
9741
|
const staleForMinutes = Math.max(1, Math.floor(staleForMs / 6e4));
|
|
9082
9742
|
ap.runtimeProgress.markStale();
|
|
9083
9743
|
const diagnostic = buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes);
|
|
@@ -9103,7 +9763,6 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9103
9763
|
...runtimeTraceCounterAttrs(ap),
|
|
9104
9764
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_stalled")
|
|
9105
9765
|
});
|
|
9106
|
-
ap.expectedTerminationReason = "stalled_recovery";
|
|
9107
9766
|
const runtimeLabel = ap.driver.id === "opencode" ? "OpenCode" : ap.driver.id;
|
|
9108
9767
|
logger.warn(
|
|
9109
9768
|
`[Agent ${agentId}] ${runtimeLabel} process stalled for ${staleForMinutes}m with ${ap.inbox.length} queued message(s); terminating for restart`
|
|
@@ -9169,31 +9828,32 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9169
9828
|
this.sendRuntimeProfileReport(agentId);
|
|
9170
9829
|
break;
|
|
9171
9830
|
case "thinking": {
|
|
9172
|
-
if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
|
|
9173
9831
|
this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
|
|
9174
9832
|
this.queueTrajectoryText(agentId, "thinking", event.text);
|
|
9175
|
-
if (ap)
|
|
9176
|
-
|
|
9833
|
+
if (ap) {
|
|
9834
|
+
const reduction = reduceApmGatedAssistantContinuation(ap.gatedSteering);
|
|
9835
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "thinking" });
|
|
9836
|
+
}
|
|
9177
9837
|
break;
|
|
9178
9838
|
}
|
|
9179
9839
|
case "text": {
|
|
9180
|
-
if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
|
|
9181
9840
|
this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
|
|
9182
9841
|
this.queueTrajectoryText(agentId, "text", event.text);
|
|
9183
|
-
if (ap)
|
|
9184
|
-
|
|
9842
|
+
if (ap) {
|
|
9843
|
+
const reduction = reduceApmGatedAssistantContinuation(ap.gatedSteering);
|
|
9844
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "text" });
|
|
9845
|
+
}
|
|
9185
9846
|
break;
|
|
9186
9847
|
}
|
|
9187
9848
|
case "tool_call": {
|
|
9188
|
-
if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
|
|
9189
9849
|
this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed tool use)");
|
|
9190
9850
|
this.flushPendingTrajectory(agentId);
|
|
9191
9851
|
const invocation = normalizeToolDisplayInvocation(event.name, event.input);
|
|
9192
9852
|
if (ap) {
|
|
9193
|
-
ap.gatedSteering
|
|
9853
|
+
const reduction = reduceApmGatedToolUse(ap.gatedSteering, { kind: "tool_call" });
|
|
9194
9854
|
this.noteRuntimeProfileToolCall(agentId, ap, invocation.toolName);
|
|
9195
9855
|
this.recordRuntimeTraceEvent(agentId, ap, "tool.call.started", { tool: invocation.toolName });
|
|
9196
|
-
this.
|
|
9856
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, {
|
|
9197
9857
|
event: "tool_call",
|
|
9198
9858
|
tool: invocation.toolName
|
|
9199
9859
|
});
|
|
@@ -9205,23 +9865,20 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9205
9865
|
toolName: invocation.toolName,
|
|
9206
9866
|
toolInput: inputSummary
|
|
9207
9867
|
}]);
|
|
9208
|
-
if (ap) ap.isIdle = false;
|
|
9209
9868
|
break;
|
|
9210
9869
|
}
|
|
9211
9870
|
case "tool_output": {
|
|
9212
9871
|
const invocation = normalizeToolDisplayInvocation(event.name, {});
|
|
9213
9872
|
if (ap) {
|
|
9214
|
-
const
|
|
9215
|
-
ap.gatedSteering.outstandingToolUses = Math.max(0, ap.gatedSteering.outstandingToolUses - 1);
|
|
9873
|
+
const reduction = reduceApmGatedToolUse(ap.gatedSteering, { kind: "tool_output" });
|
|
9216
9874
|
this.recordRuntimeTraceEvent(agentId, ap, "tool.output.observed", { tool: invocation.toolName });
|
|
9217
9875
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.continuation.expected");
|
|
9218
|
-
this.
|
|
9876
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, {
|
|
9219
9877
|
event: "tool_output",
|
|
9220
9878
|
tool: invocation.toolName
|
|
9221
9879
|
});
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
this.tryFlushGatedSteering(agentId, ap, "tool_batch_complete");
|
|
9880
|
+
if (reduction.shouldFlushToolBatch) {
|
|
9881
|
+
this.notifyGatedSteeringBoundary(agentId, ap, "tool_batch_complete");
|
|
9225
9882
|
}
|
|
9226
9883
|
}
|
|
9227
9884
|
break;
|
|
@@ -9232,9 +9889,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9232
9889
|
if (ap) this.startCompactionWatchdog(agentId, ap);
|
|
9233
9890
|
this.broadcastActivity(agentId, "working", "Compacting context", [{ kind: "compaction_started" }]);
|
|
9234
9891
|
if (ap) {
|
|
9235
|
-
ap.gatedSteering
|
|
9236
|
-
this.
|
|
9237
|
-
ap.isIdle = false;
|
|
9892
|
+
const reduction = reduceApmGatedCompaction(ap.gatedSteering, { kind: "compaction_started" });
|
|
9893
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "compaction_started" });
|
|
9238
9894
|
}
|
|
9239
9895
|
break;
|
|
9240
9896
|
case "compaction_finished":
|
|
@@ -9243,10 +9899,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9243
9899
|
if (ap) this.clearCompactionWatchdog(ap);
|
|
9244
9900
|
this.broadcastActivity(agentId, "working", "Context compaction finished", [{ kind: "compaction_finished" }]);
|
|
9245
9901
|
if (ap) {
|
|
9246
|
-
ap.gatedSteering
|
|
9247
|
-
this.
|
|
9902
|
+
const reduction = reduceApmGatedCompaction(ap.gatedSteering, { kind: "compaction_finished" });
|
|
9903
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "compaction_finished" });
|
|
9248
9904
|
this.flushCompactionBoundaryMessages(agentId, ap);
|
|
9249
|
-
ap.isIdle = false;
|
|
9250
9905
|
}
|
|
9251
9906
|
break;
|
|
9252
9907
|
case "turn_end":
|
|
@@ -9256,28 +9911,21 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9256
9911
|
});
|
|
9257
9912
|
this.flushPendingTrajectory(agentId);
|
|
9258
9913
|
if (ap) {
|
|
9259
|
-
this.clearGatedInFlightBatch(agentId, ap, "turn_end");
|
|
9260
9914
|
if (event.sessionId) ap.sessionId = event.sessionId;
|
|
9261
|
-
ap.gatedSteering
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
9272
|
-
});
|
|
9273
|
-
}
|
|
9274
|
-
this.broadcastActivity(agentId, "working", "Message received");
|
|
9275
|
-
if (!this.deliverMessagesViaStdin(agentId, ap, nextMessages, "idle")) {
|
|
9276
|
-
ap.isIdle = true;
|
|
9915
|
+
const reduction = reduceApmGatedTurnEnd(ap.gatedSteering, {
|
|
9916
|
+
inboxLength: ap.inbox.length,
|
|
9917
|
+
supportsStdinNotification: ap.driver.supportsStdinNotification,
|
|
9918
|
+
hasSession: Boolean(ap.sessionId),
|
|
9919
|
+
terminateProcessOnTurnEnd: ap.driver.terminateProcessOnTurnEnd === true
|
|
9920
|
+
});
|
|
9921
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "turn_end" });
|
|
9922
|
+
const deliverStdinEffect = reduction.effects.find((effect) => effect.kind === "deliver_stdin");
|
|
9923
|
+
if (deliverStdinEffect) {
|
|
9924
|
+
if (!this.executeApmGatedSteeringEffect(agentId, ap, deliverStdinEffect)) {
|
|
9925
|
+
this.commitApmIdleState(agentId, ap, true);
|
|
9277
9926
|
this.broadcastActivity(agentId, "online", "Idle");
|
|
9278
9927
|
}
|
|
9279
9928
|
} else {
|
|
9280
|
-
ap.isIdle = true;
|
|
9281
9929
|
if (ap.lastRuntimeError) {
|
|
9282
9930
|
this.broadcastActivity(agentId, "error", ap.lastRuntimeError);
|
|
9283
9931
|
} else {
|
|
@@ -9290,7 +9938,6 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9290
9938
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "turn_end")
|
|
9291
9939
|
});
|
|
9292
9940
|
if (ap.driver.terminateProcessOnTurnEnd) {
|
|
9293
|
-
ap.expectedTerminationReason = "turn_end";
|
|
9294
9941
|
logger.info(`[Agent ${agentId}] Turn completed; terminating ${ap.driver.id} process`);
|
|
9295
9942
|
try {
|
|
9296
9943
|
this.processExitTraceAttrs.set(ap.process, {
|
|
@@ -9319,10 +9966,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9319
9966
|
if (runtimeErrorDiagnostics.spanAttrs.runtime_error_action_required === true) {
|
|
9320
9967
|
visibleErrorMessage = formatRuntimeLoginRequiredMessage(ap.driver.id);
|
|
9321
9968
|
}
|
|
9322
|
-
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
|
|
9969
|
+
const shouldDisableToolBoundaryFlush = ap.driver.busyDeliveryMode === "gated" && this.isThinkingBlockMutationError(event.message);
|
|
9970
|
+
const terminalFailure = classifyTerminalFailure(ap);
|
|
9971
|
+
const reduction = reduceApmGatedError(ap.gatedSteering, {
|
|
9972
|
+
disableToolBoundaryFlush: shouldDisableToolBoundaryFlush,
|
|
9973
|
+
terminalWakeable: Boolean(ap.driver.supportsStdinNotification && terminalFailure && !terminalFailure.actionRequired)
|
|
9974
|
+
});
|
|
9975
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "error" });
|
|
9976
|
+
if (reduction.shouldDisableToolBoundaryFlush) {
|
|
9326
9977
|
this.recordGatedSteeringEvent(agentId, ap, "disabled", {
|
|
9327
9978
|
reason: "thinking_block_mutation_error",
|
|
9328
9979
|
lastFlushReason: ap.gatedSteering.lastFlushReason,
|
|
@@ -9341,7 +9992,6 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9341
9992
|
...runtimeTraceCounterAttrs(ap),
|
|
9342
9993
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_error")
|
|
9343
9994
|
});
|
|
9344
|
-
const terminalFailure = classifyTerminalFailure(ap);
|
|
9345
9995
|
if (ap.driver.supportsStdinNotification && terminalFailure) {
|
|
9346
9996
|
if (terminalFailure.actionRequired) {
|
|
9347
9997
|
logger.warn(`[Agent ${agentId}] ${ap.driver.id} auth requires user action; terminating runtime process`);
|
|
@@ -9356,7 +10006,6 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9356
10006
|
logger.warn(`[Agent ${agentId}] Failed to terminate ${ap.driver.id} after auth error: ${reason}`);
|
|
9357
10007
|
}
|
|
9358
10008
|
} else {
|
|
9359
|
-
ap.isIdle = true;
|
|
9360
10009
|
ap.notifications.clear();
|
|
9361
10010
|
logger.info(`[Agent ${agentId}] Marked ${ap.driver.id} wakeable after terminal runtime error`);
|
|
9362
10011
|
}
|
|
@@ -9471,6 +10120,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9471
10120
|
return true;
|
|
9472
10121
|
} else {
|
|
9473
10122
|
ap.notifications.add(count);
|
|
10123
|
+
const retryScheduled = ap.driver.busyDeliveryMode === "direct" ? this.scheduleStdinNotification(agentId, ap, this.stdinNotificationRetryMs) : false;
|
|
9474
10124
|
this.recordDaemonTrace("daemon.agent.stdin_notification", {
|
|
9475
10125
|
agentId,
|
|
9476
10126
|
runtime: ap.config.runtime,
|
|
@@ -9479,6 +10129,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9479
10129
|
outcome: "encode_failed",
|
|
9480
10130
|
mode: "busy",
|
|
9481
10131
|
pending_notification_count: count,
|
|
10132
|
+
retry_scheduled: retryScheduled,
|
|
10133
|
+
notification_timer_present: ap.notifications.hasTimer,
|
|
9482
10134
|
inbox_count: inboxCount,
|
|
9483
10135
|
session_id_present: true
|
|
9484
10136
|
}, "error");
|
|
@@ -9507,7 +10159,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9507
10159
|
});
|
|
9508
10160
|
if (messages.length === 0) {
|
|
9509
10161
|
if (mode === "idle") {
|
|
9510
|
-
|
|
10162
|
+
this.commitApmIdleState(agentId, ap, true);
|
|
9511
10163
|
}
|
|
9512
10164
|
return true;
|
|
9513
10165
|
}
|
|
@@ -9549,7 +10201,7 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
9549
10201
|
if (!encoded) {
|
|
9550
10202
|
ap.inbox.unshift(...messages);
|
|
9551
10203
|
if (mode === "idle") {
|
|
9552
|
-
|
|
10204
|
+
this.commitApmIdleState(agentId, ap, true);
|
|
9553
10205
|
}
|
|
9554
10206
|
logger.warn(
|
|
9555
10207
|
`[Agent ${agentId}] Failed to encode ${mode} stdin delivery; re-queued ${messages.length === 1 ? "message" : `${messages.length} messages`}`
|
|
@@ -9901,7 +10553,7 @@ var ReminderCache = class {
|
|
|
9901
10553
|
};
|
|
9902
10554
|
|
|
9903
10555
|
// src/machineLock.ts
|
|
9904
|
-
import { createHash as
|
|
10556
|
+
import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
|
|
9905
10557
|
import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync8 } from "fs";
|
|
9906
10558
|
import os6 from "os";
|
|
9907
10559
|
import path13 from "path";
|
|
@@ -9917,7 +10569,7 @@ var DaemonMachineLockConflictError = class extends Error {
|
|
|
9917
10569
|
}
|
|
9918
10570
|
};
|
|
9919
10571
|
function apiKeyFingerprint(apiKey) {
|
|
9920
|
-
return
|
|
10572
|
+
return createHash4("sha256").update(apiKey).digest("hex");
|
|
9921
10573
|
}
|
|
9922
10574
|
function getDaemonMachineLockId(apiKey) {
|
|
9923
10575
|
return `machine-${apiKeyFingerprint(apiKey).slice(0, 16)}`;
|
|
@@ -10180,7 +10832,7 @@ function isDiagnosticErrorAttr(key) {
|
|
|
10180
10832
|
}
|
|
10181
10833
|
|
|
10182
10834
|
// src/traceBundleUpload.ts
|
|
10183
|
-
import { createHash as
|
|
10835
|
+
import { createHash as createHash6, randomUUID as randomUUID4 } from "crypto";
|
|
10184
10836
|
import { gzipSync } from "zlib";
|
|
10185
10837
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
10186
10838
|
import path15 from "path";
|
|
@@ -10310,12 +10962,12 @@ async function uploadWithSignedCapability({
|
|
|
10310
10962
|
}
|
|
10311
10963
|
|
|
10312
10964
|
// src/traceJitter.ts
|
|
10313
|
-
import { createHash as
|
|
10965
|
+
import { createHash as createHash5 } from "crypto";
|
|
10314
10966
|
var INITIAL_UPLOAD_DELAY_SPAN_MS = 3e4;
|
|
10315
10967
|
var UPLOAD_INTERVAL_JITTER_SPAN_MS = 6e4;
|
|
10316
10968
|
var MAX_FILE_AGE_JITTER_SPAN_MS = 6e4;
|
|
10317
10969
|
function computeTraceJitter(lockId) {
|
|
10318
|
-
const seed =
|
|
10970
|
+
const seed = createHash5("sha256").update(lockId).digest();
|
|
10319
10971
|
return {
|
|
10320
10972
|
initialUploadDelayMs: seed.readUInt32BE(0) % INITIAL_UPLOAD_DELAY_SPAN_MS,
|
|
10321
10973
|
uploadIntervalJitterMs: seed.readUInt32BE(4) % UPLOAD_INTERVAL_JITTER_SPAN_MS,
|
|
@@ -10516,7 +11168,7 @@ var DaemonTraceBundleUploader = class {
|
|
|
10516
11168
|
}
|
|
10517
11169
|
};
|
|
10518
11170
|
function sha256Hex(body) {
|
|
10519
|
-
return
|
|
11171
|
+
return createHash6("sha256").update(body).digest("hex");
|
|
10520
11172
|
}
|
|
10521
11173
|
function readPositiveIntegerEnv2(name, fallback) {
|
|
10522
11174
|
const value = process.env[name];
|