@drisp/cli 0.4.2 → 0.4.4
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/{WorkflowInstallWizard-NDWLVIFI.js → WorkflowInstallWizard-2MC5A7W4.js} +2 -2
- package/dist/athena-gateway.js +910 -870
- package/dist/{chunk-GE6PPB6Z.js → chunk-5VK2ZMVV.js} +96 -119
- package/dist/{chunk-WHELLVBL.js → chunk-JAPBSL7D.js} +1177 -640
- package/dist/cli.js +1004 -1060
- package/dist/dashboard-daemon.js +2 -2
- package/package.json +4 -2
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
resolveActiveWorkflow,
|
|
33
33
|
resolveWorkflow,
|
|
34
34
|
resolveWorkflowPlugins
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-5VK2ZMVV.js";
|
|
36
36
|
|
|
37
37
|
// src/shared/utils/processRegistry.ts
|
|
38
38
|
var ProcessRegistry = class {
|
|
@@ -3079,8 +3079,8 @@ function spawnClaude(options) {
|
|
|
3079
3079
|
cwd: projectDir,
|
|
3080
3080
|
stdio: ["ignore", "pipe", "pipe"],
|
|
3081
3081
|
env: {
|
|
3082
|
-
//
|
|
3083
|
-
CLAUDE_CODE_AUTO_COMPACT_WINDOW: "
|
|
3082
|
+
// Claude compacts around 95% of this window; 185k keeps the trigger above 175k tokens used.
|
|
3083
|
+
CLAUDE_CODE_AUTO_COMPACT_WINDOW: "185000",
|
|
3084
3084
|
...process.env,
|
|
3085
3085
|
...extraEnv ?? {},
|
|
3086
3086
|
ATHENA_INSTANCE_ID: String(instanceId),
|
|
@@ -6555,7 +6555,7 @@ function buildCodexPromptOptions(input) {
|
|
|
6555
6555
|
agentRoots: input.workflowPlan?.agentRoots && input.workflowPlan.agentRoots.length > 0 ? input.workflowPlan.agentRoots : void 0,
|
|
6556
6556
|
plugins: resolveCodexWorkflowPlugins(input.workflowPlan),
|
|
6557
6557
|
config: {
|
|
6558
|
-
model_auto_compact_token_limit:
|
|
6558
|
+
model_auto_compact_token_limit: 175e3,
|
|
6559
6559
|
...resolveCodexMcpConfig(input.pluginMcpConfig, input.workflowPlan) ?? {}
|
|
6560
6560
|
},
|
|
6561
6561
|
ephemeral: input.ephemeral,
|
|
@@ -7297,130 +7297,6 @@ var EXEC_EXIT_CODE = {
|
|
|
7297
7297
|
import crypto2 from "crypto";
|
|
7298
7298
|
import path17 from "path";
|
|
7299
7299
|
|
|
7300
|
-
// src/core/controller/rules.ts
|
|
7301
|
-
function ruleMatches(ruleToolName, toolName) {
|
|
7302
|
-
if (ruleToolName === "*") return true;
|
|
7303
|
-
if (ruleToolName === toolName) return true;
|
|
7304
|
-
if (ruleToolName.endsWith("__*")) {
|
|
7305
|
-
const prefix = ruleToolName.slice(0, -1);
|
|
7306
|
-
return toolName.startsWith(prefix);
|
|
7307
|
-
}
|
|
7308
|
-
return false;
|
|
7309
|
-
}
|
|
7310
|
-
function matchRule(rules, toolName) {
|
|
7311
|
-
const denyMatch = rules.find(
|
|
7312
|
-
(r) => r.action === "deny" && ruleMatches(r.toolName, toolName)
|
|
7313
|
-
);
|
|
7314
|
-
if (denyMatch) return denyMatch;
|
|
7315
|
-
return rules.find(
|
|
7316
|
-
(r) => r.action === "approve" && ruleMatches(r.toolName, toolName)
|
|
7317
|
-
);
|
|
7318
|
-
}
|
|
7319
|
-
|
|
7320
|
-
// src/core/controller/permission.ts
|
|
7321
|
-
var SESSION_APPROVAL_REQUEST_HOOKS = /* @__PURE__ */ new Set([
|
|
7322
|
-
"item/commandExecution/requestApproval",
|
|
7323
|
-
"item/fileChange/requestApproval",
|
|
7324
|
-
"item/permissions/requestApproval"
|
|
7325
|
-
]);
|
|
7326
|
-
function isScopedPermissionsRequest(hookName) {
|
|
7327
|
-
return hookName === "item/permissions/requestApproval";
|
|
7328
|
-
}
|
|
7329
|
-
function supportsSessionApproval(hookName) {
|
|
7330
|
-
return hookName !== void 0 && SESSION_APPROVAL_REQUEST_HOOKS.has(hookName);
|
|
7331
|
-
}
|
|
7332
|
-
function extractPermissionSnapshot(event) {
|
|
7333
|
-
const data = event.data;
|
|
7334
|
-
const toolNameFromData = typeof data["tool_name"] === "string" ? data["tool_name"] : void 0;
|
|
7335
|
-
const toolInputFromData = typeof data["tool_input"] === "object" && data["tool_input"] !== null ? data["tool_input"] : void 0;
|
|
7336
|
-
const toolUseIdFromData = typeof data["tool_use_id"] === "string" ? data["tool_use_id"] : void 0;
|
|
7337
|
-
return {
|
|
7338
|
-
request_id: event.id,
|
|
7339
|
-
ts: event.timestamp,
|
|
7340
|
-
kind: event.kind,
|
|
7341
|
-
hookName: event.hookName,
|
|
7342
|
-
tool_name: event.toolName ?? toolNameFromData ?? "Unknown",
|
|
7343
|
-
tool_input: toolInputFromData ?? {},
|
|
7344
|
-
tool_use_id: event.toolUseId ?? toolUseIdFromData,
|
|
7345
|
-
suggestions: data.permission_suggestions
|
|
7346
|
-
};
|
|
7347
|
-
}
|
|
7348
|
-
|
|
7349
|
-
// src/core/controller/runtimeController.ts
|
|
7350
|
-
function handleEvent(event, cb) {
|
|
7351
|
-
const eventKind2 = event.kind;
|
|
7352
|
-
const isScoped = isScopedPermissionsRequest(event.hookName);
|
|
7353
|
-
const eventData = event.data;
|
|
7354
|
-
const toolName = event.toolName ?? (typeof eventData["tool_name"] === "string" ? eventData["tool_name"] : void 0);
|
|
7355
|
-
if (eventKind2 === "permission.request" && toolName === "user_input") {
|
|
7356
|
-
cb.enqueueQuestion(event.id);
|
|
7357
|
-
cb.relayQuestion?.(event);
|
|
7358
|
-
return { handled: true };
|
|
7359
|
-
}
|
|
7360
|
-
if (eventKind2 === "permission.request" && toolName) {
|
|
7361
|
-
const rule = matchRule(cb.getRules(), toolName);
|
|
7362
|
-
if (rule?.action === "deny") {
|
|
7363
|
-
return {
|
|
7364
|
-
handled: true,
|
|
7365
|
-
decision: {
|
|
7366
|
-
type: "json",
|
|
7367
|
-
source: "rule",
|
|
7368
|
-
intent: {
|
|
7369
|
-
kind: "permission_deny",
|
|
7370
|
-
reason: `Blocked by rule: ${rule.addedBy}`
|
|
7371
|
-
}
|
|
7372
|
-
}
|
|
7373
|
-
};
|
|
7374
|
-
}
|
|
7375
|
-
if (rule?.action === "approve") {
|
|
7376
|
-
return {
|
|
7377
|
-
handled: true,
|
|
7378
|
-
decision: {
|
|
7379
|
-
type: "json",
|
|
7380
|
-
source: "rule",
|
|
7381
|
-
intent: { kind: "permission_allow" },
|
|
7382
|
-
// Scoped Codex permission grants are session-scoped under
|
|
7383
|
-
// auto rules so the same capability isn't re-prompted.
|
|
7384
|
-
...isScoped ? { data: { scope: "session" } } : {}
|
|
7385
|
-
}
|
|
7386
|
-
};
|
|
7387
|
-
}
|
|
7388
|
-
cb.enqueuePermission(event);
|
|
7389
|
-
cb.relayPermission?.(event);
|
|
7390
|
-
return { handled: true };
|
|
7391
|
-
}
|
|
7392
|
-
if (eventKind2 === "tool.pre" && toolName === "AskUserQuestion") {
|
|
7393
|
-
cb.enqueueQuestion(event.id);
|
|
7394
|
-
cb.relayQuestion?.(event);
|
|
7395
|
-
return { handled: true };
|
|
7396
|
-
}
|
|
7397
|
-
if (eventKind2 === "tool.pre" && toolName) {
|
|
7398
|
-
const rule = matchRule(cb.getRules(), toolName);
|
|
7399
|
-
if (rule?.action === "deny") {
|
|
7400
|
-
return {
|
|
7401
|
-
handled: true,
|
|
7402
|
-
decision: {
|
|
7403
|
-
type: "json",
|
|
7404
|
-
source: "rule",
|
|
7405
|
-
intent: {
|
|
7406
|
-
kind: "pre_tool_deny",
|
|
7407
|
-
reason: `Blocked by rule: ${rule.addedBy}`
|
|
7408
|
-
}
|
|
7409
|
-
}
|
|
7410
|
-
};
|
|
7411
|
-
}
|
|
7412
|
-
return {
|
|
7413
|
-
handled: true,
|
|
7414
|
-
decision: {
|
|
7415
|
-
type: "json",
|
|
7416
|
-
source: "rule",
|
|
7417
|
-
intent: { kind: "pre_tool_allow" }
|
|
7418
|
-
}
|
|
7419
|
-
};
|
|
7420
|
-
}
|
|
7421
|
-
return { handled: false };
|
|
7422
|
-
}
|
|
7423
|
-
|
|
7424
7300
|
// src/core/feed/todo.ts
|
|
7425
7301
|
function isSubagentTool(toolName) {
|
|
7426
7302
|
return toolName === "Task" || toolName === "Agent";
|
|
@@ -7802,8 +7678,16 @@ function createTranscriptReader() {
|
|
|
7802
7678
|
try {
|
|
7803
7679
|
const buf = Buffer.alloc(fileSize - offset);
|
|
7804
7680
|
const bytesRead = fs15.readSync(fd, buf, 0, buf.length, offset);
|
|
7805
|
-
|
|
7806
|
-
|
|
7681
|
+
let lastNewline = -1;
|
|
7682
|
+
for (let i = bytesRead - 1; i >= 0; i--) {
|
|
7683
|
+
if (buf[i] === 10) {
|
|
7684
|
+
lastNewline = i;
|
|
7685
|
+
break;
|
|
7686
|
+
}
|
|
7687
|
+
}
|
|
7688
|
+
if (lastNewline === -1) return [];
|
|
7689
|
+
offsets.set(transcriptPath, offset + lastNewline + 1);
|
|
7690
|
+
const chunk = buf.toString("utf8", 0, lastNewline + 1);
|
|
7807
7691
|
const lines = chunk.split("\n").filter((l) => l.trim().length > 0);
|
|
7808
7692
|
const messages = [];
|
|
7809
7693
|
for (const line of lines) {
|
|
@@ -7846,55 +7730,54 @@ function createTranscriptReader() {
|
|
|
7846
7730
|
return { readNewAssistantMessages, getOffset };
|
|
7847
7731
|
}
|
|
7848
7732
|
|
|
7849
|
-
// src/core/feed/
|
|
7850
|
-
function
|
|
7851
|
-
const MAX_STREAMED_TOOL_OUTPUT_CHARS = 64e3;
|
|
7852
|
-
const STREAMED_TOOL_OUTPUT_TRUNCATED_NOTICE = "[streaming output truncated to recent content]\n";
|
|
7733
|
+
// src/core/feed/internals/runLifecycle.ts
|
|
7734
|
+
function createRunLifecycle() {
|
|
7853
7735
|
let currentSession = null;
|
|
7854
7736
|
let currentRun = null;
|
|
7855
|
-
let lastRootTasks = [];
|
|
7856
|
-
const actors = new ActorRegistry();
|
|
7857
7737
|
let seq = 0;
|
|
7858
7738
|
let runSeq = 0;
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
|
|
7868
|
-
|
|
7869
|
-
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
}
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
|
|
7885
|
-
|
|
7886
|
-
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
7891
|
-
|
|
7892
|
-
|
|
7739
|
+
function getRunId() {
|
|
7740
|
+
const sessId = currentSession?.session_id ?? "unknown";
|
|
7741
|
+
return `${sessId}:R${runSeq}`;
|
|
7742
|
+
}
|
|
7743
|
+
return {
|
|
7744
|
+
allocateSeq() {
|
|
7745
|
+
return ++seq;
|
|
7746
|
+
},
|
|
7747
|
+
getRunId,
|
|
7748
|
+
getSession() {
|
|
7749
|
+
return currentSession;
|
|
7750
|
+
},
|
|
7751
|
+
getCurrentRun() {
|
|
7752
|
+
return currentRun;
|
|
7753
|
+
},
|
|
7754
|
+
setSession(session) {
|
|
7755
|
+
currentSession = session;
|
|
7756
|
+
},
|
|
7757
|
+
endSession(ts) {
|
|
7758
|
+
if (currentSession) currentSession.ended_at = ts;
|
|
7759
|
+
},
|
|
7760
|
+
clearSession() {
|
|
7761
|
+
currentSession = null;
|
|
7762
|
+
},
|
|
7763
|
+
incrementCounter(name) {
|
|
7764
|
+
if (currentRun) currentRun.counters[name]++;
|
|
7765
|
+
},
|
|
7766
|
+
closeRun(ts, status) {
|
|
7767
|
+
if (!currentRun) return null;
|
|
7768
|
+
currentRun.status = status;
|
|
7769
|
+
currentRun.ended_at = ts;
|
|
7770
|
+
const closed = currentRun;
|
|
7771
|
+
currentRun = null;
|
|
7772
|
+
return closed;
|
|
7773
|
+
},
|
|
7774
|
+
openNewRun(ts, sessionId, triggerType, promptPreview) {
|
|
7775
|
+
runSeq++;
|
|
7893
7776
|
currentRun = {
|
|
7894
|
-
run_id:
|
|
7895
|
-
session_id:
|
|
7896
|
-
started_at:
|
|
7897
|
-
trigger:
|
|
7777
|
+
run_id: getRunId(),
|
|
7778
|
+
session_id: sessionId,
|
|
7779
|
+
started_at: ts,
|
|
7780
|
+
trigger: { type: triggerType, prompt_preview: promptPreview },
|
|
7898
7781
|
status: "running",
|
|
7899
7782
|
actors: { root_agent_id: "agent:root", subagent_ids: [] },
|
|
7900
7783
|
counters: {
|
|
@@ -7904,54 +7787,344 @@ function createFeedMapper(bootstrap) {
|
|
|
7904
7787
|
blocks: 0
|
|
7905
7788
|
}
|
|
7906
7789
|
};
|
|
7790
|
+
return currentRun;
|
|
7791
|
+
},
|
|
7792
|
+
restoreFrom(bootstrap) {
|
|
7907
7793
|
for (const e of bootstrap.feedEvents) {
|
|
7908
|
-
if (e.
|
|
7909
|
-
|
|
7910
|
-
if (
|
|
7911
|
-
|
|
7912
|
-
|
|
7794
|
+
if (e.seq > seq) seq = e.seq;
|
|
7795
|
+
const m = e.run_id.match(/:R(\d+)$/);
|
|
7796
|
+
if (m) {
|
|
7797
|
+
const n = parseInt(m[1], 10);
|
|
7798
|
+
if (n > runSeq) runSeq = n;
|
|
7799
|
+
}
|
|
7800
|
+
}
|
|
7801
|
+
const lastAdapterId = bootstrap.adapterSessionIds.at(-1);
|
|
7802
|
+
if (lastAdapterId) {
|
|
7803
|
+
currentSession = {
|
|
7804
|
+
session_id: lastAdapterId,
|
|
7805
|
+
started_at: bootstrap.createdAt,
|
|
7806
|
+
source: "resume"
|
|
7807
|
+
};
|
|
7808
|
+
}
|
|
7809
|
+
let lastRunStart;
|
|
7810
|
+
let lastRunEnd;
|
|
7811
|
+
for (const e of bootstrap.feedEvents) {
|
|
7812
|
+
if (e.kind === "run.start") lastRunStart = e;
|
|
7813
|
+
if (e.kind === "run.end") lastRunEnd = e;
|
|
7814
|
+
}
|
|
7815
|
+
if (lastRunStart && (!lastRunEnd || lastRunEnd.seq < lastRunStart.seq)) {
|
|
7816
|
+
const triggerData = lastRunStart.data;
|
|
7817
|
+
currentRun = {
|
|
7818
|
+
run_id: lastRunStart.run_id,
|
|
7819
|
+
session_id: lastRunStart.session_id,
|
|
7820
|
+
started_at: lastRunStart.ts,
|
|
7821
|
+
trigger: triggerData.trigger,
|
|
7822
|
+
status: "running",
|
|
7823
|
+
actors: { root_agent_id: "agent:root", subagent_ids: [] },
|
|
7824
|
+
counters: {
|
|
7825
|
+
tool_uses: 0,
|
|
7826
|
+
tool_failures: 0,
|
|
7827
|
+
permission_requests: 0,
|
|
7828
|
+
blocks: 0
|
|
7829
|
+
}
|
|
7830
|
+
};
|
|
7831
|
+
for (const e of bootstrap.feedEvents) {
|
|
7832
|
+
if (e.run_id !== currentRun.run_id) continue;
|
|
7833
|
+
if (e.kind === "tool.pre") currentRun.counters.tool_uses++;
|
|
7834
|
+
if (e.kind === "tool.failure") currentRun.counters.tool_failures++;
|
|
7835
|
+
if (e.kind === "permission.request")
|
|
7836
|
+
currentRun.counters.permission_requests++;
|
|
7837
|
+
}
|
|
7913
7838
|
}
|
|
7914
7839
|
}
|
|
7915
|
-
}
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7840
|
+
};
|
|
7841
|
+
}
|
|
7842
|
+
|
|
7843
|
+
// src/core/feed/internals/decisionCorrelation.ts
|
|
7844
|
+
function createDecisionCorrelation() {
|
|
7845
|
+
const eventIdByRequestId = /* @__PURE__ */ new Map();
|
|
7846
|
+
const eventKindByRequestId = /* @__PURE__ */ new Map();
|
|
7847
|
+
const resolvedRequestById = /* @__PURE__ */ new Map();
|
|
7848
|
+
return {
|
|
7849
|
+
recordRequest(requestId, eventId, kind) {
|
|
7850
|
+
eventIdByRequestId.set(requestId, eventId);
|
|
7851
|
+
eventKindByRequestId.set(requestId, kind);
|
|
7852
|
+
resolvedRequestById.set(requestId, { event_id: eventId, kind });
|
|
7853
|
+
},
|
|
7854
|
+
lookupResolved(requestId) {
|
|
7855
|
+
return resolvedRequestById.get(requestId) ?? null;
|
|
7856
|
+
},
|
|
7857
|
+
consumeForDecision(requestId) {
|
|
7858
|
+
const parentEventId = eventIdByRequestId.get(requestId);
|
|
7859
|
+
if (!parentEventId) return null;
|
|
7860
|
+
const originalKind = eventKindByRequestId.get(requestId);
|
|
7861
|
+
eventIdByRequestId.delete(requestId);
|
|
7862
|
+
eventKindByRequestId.delete(requestId);
|
|
7863
|
+
return { parentEventId, originalKind };
|
|
7864
|
+
},
|
|
7865
|
+
resetForNewRun() {
|
|
7866
|
+
eventIdByRequestId.clear();
|
|
7867
|
+
eventKindByRequestId.clear();
|
|
7868
|
+
resolvedRequestById.clear();
|
|
7929
7869
|
}
|
|
7870
|
+
};
|
|
7871
|
+
}
|
|
7872
|
+
|
|
7873
|
+
// src/core/feed/internals/toolCorrelation.ts
|
|
7874
|
+
var MAX_STREAMED_TOOL_OUTPUT_CHARS = 64e3;
|
|
7875
|
+
var STREAMED_TOOL_OUTPUT_TRUNCATED_NOTICE = "[streaming output truncated to recent content]\n";
|
|
7876
|
+
function createToolCorrelation() {
|
|
7877
|
+
const toolPreIndex = /* @__PURE__ */ new Map();
|
|
7878
|
+
const toolDeltaTextByUseId = /* @__PURE__ */ new Map();
|
|
7879
|
+
const truncatedToolDeltaUseIds = /* @__PURE__ */ new Set();
|
|
7880
|
+
return {
|
|
7881
|
+
recordPre(toolUseId, eventId) {
|
|
7882
|
+
toolPreIndex.set(toolUseId, eventId);
|
|
7883
|
+
},
|
|
7884
|
+
lookupParent(toolUseId) {
|
|
7885
|
+
return toolUseId ? toolPreIndex.get(toolUseId) : void 0;
|
|
7886
|
+
},
|
|
7887
|
+
forgetTool(toolUseId) {
|
|
7888
|
+
toolDeltaTextByUseId.delete(toolUseId);
|
|
7889
|
+
truncatedToolDeltaUseIds.delete(toolUseId);
|
|
7890
|
+
},
|
|
7891
|
+
appendDelta(toolUseId, chunk) {
|
|
7892
|
+
if (!toolUseId) return chunk;
|
|
7893
|
+
const cumulative = `${toolDeltaTextByUseId.get(toolUseId) ?? ""}${chunk}`;
|
|
7894
|
+
if (cumulative.length <= MAX_STREAMED_TOOL_OUTPUT_CHARS) {
|
|
7895
|
+
toolDeltaTextByUseId.set(toolUseId, cumulative);
|
|
7896
|
+
return truncatedToolDeltaUseIds.has(toolUseId) ? `${STREAMED_TOOL_OUTPUT_TRUNCATED_NOTICE}${cumulative}` : cumulative;
|
|
7897
|
+
}
|
|
7898
|
+
const tail2 = cumulative.slice(-MAX_STREAMED_TOOL_OUTPUT_CHARS);
|
|
7899
|
+
toolDeltaTextByUseId.set(toolUseId, tail2);
|
|
7900
|
+
truncatedToolDeltaUseIds.add(toolUseId);
|
|
7901
|
+
return `${STREAMED_TOOL_OUTPUT_TRUNCATED_NOTICE}${tail2}`;
|
|
7902
|
+
},
|
|
7903
|
+
resetForNewRun() {
|
|
7904
|
+
toolPreIndex.clear();
|
|
7905
|
+
toolDeltaTextByUseId.clear();
|
|
7906
|
+
truncatedToolDeltaUseIds.clear();
|
|
7907
|
+
}
|
|
7908
|
+
};
|
|
7909
|
+
}
|
|
7910
|
+
|
|
7911
|
+
// src/core/feed/internals/agentMessageStream.ts
|
|
7912
|
+
function normalizeAgentMessage(message) {
|
|
7913
|
+
return message.replace(/\r\n/g, "\n").trimEnd();
|
|
7914
|
+
}
|
|
7915
|
+
function agentMessageKey(actorId, scope) {
|
|
7916
|
+
return `${actorId}\0${scope}`;
|
|
7917
|
+
}
|
|
7918
|
+
function createAgentMessageStream(eventBuilder, transcriptReader) {
|
|
7919
|
+
const pendingMessages = /* @__PURE__ */ new Map();
|
|
7920
|
+
const lastAgentMessageByActorScope = /* @__PURE__ */ new Map();
|
|
7921
|
+
const reasoningSummaryByKey = /* @__PURE__ */ new Map();
|
|
7922
|
+
function emit(args) {
|
|
7923
|
+
const normalized = normalizeAgentMessage(args.message);
|
|
7924
|
+
if (!normalized) return null;
|
|
7925
|
+
const key = agentMessageKey(args.actorId, args.scope);
|
|
7926
|
+
if (lastAgentMessageByActorScope.get(key) === normalized) return null;
|
|
7927
|
+
const data = {
|
|
7928
|
+
message: normalized,
|
|
7929
|
+
source: args.source,
|
|
7930
|
+
scope: args.scope,
|
|
7931
|
+
...args.model ? { model: args.model } : {}
|
|
7932
|
+
};
|
|
7933
|
+
const event = eventBuilder(
|
|
7934
|
+
"agent.message",
|
|
7935
|
+
"info",
|
|
7936
|
+
args.actorId,
|
|
7937
|
+
data,
|
|
7938
|
+
args.runtimeEvent,
|
|
7939
|
+
args.cause
|
|
7940
|
+
);
|
|
7941
|
+
lastAgentMessageByActorScope.set(key, normalized);
|
|
7942
|
+
return event;
|
|
7930
7943
|
}
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7944
|
+
return {
|
|
7945
|
+
emit,
|
|
7946
|
+
appendPendingDelta(itemId, delta, defaultActorId, defaultScope) {
|
|
7947
|
+
if (!delta) return;
|
|
7948
|
+
const key = itemId ?? "__legacy_root__";
|
|
7949
|
+
const existing = pendingMessages.get(key);
|
|
7950
|
+
if (existing) {
|
|
7951
|
+
existing.message += delta;
|
|
7952
|
+
return;
|
|
7953
|
+
}
|
|
7954
|
+
pendingMessages.set(key, {
|
|
7955
|
+
message: delta,
|
|
7956
|
+
actorId: defaultActorId,
|
|
7957
|
+
scope: defaultScope
|
|
7958
|
+
});
|
|
7959
|
+
},
|
|
7960
|
+
emitCompleted({
|
|
7961
|
+
itemId,
|
|
7962
|
+
messageText,
|
|
7963
|
+
fallbackActorId,
|
|
7964
|
+
fallbackScope,
|
|
7965
|
+
runtimeEvent
|
|
7966
|
+
}) {
|
|
7967
|
+
const key = itemId ?? "__legacy_root__";
|
|
7968
|
+
const pending = pendingMessages.get(key);
|
|
7969
|
+
const message = messageText ?? pending?.message ?? "";
|
|
7970
|
+
pendingMessages.delete(key);
|
|
7971
|
+
if (!message) return null;
|
|
7972
|
+
const actorId = pending?.actorId ?? fallbackActorId;
|
|
7973
|
+
const scope = pending?.scope ?? fallbackScope;
|
|
7974
|
+
return emit({
|
|
7975
|
+
runtimeEvent,
|
|
7976
|
+
actorId,
|
|
7977
|
+
scope,
|
|
7978
|
+
message,
|
|
7979
|
+
source: "hook"
|
|
7980
|
+
});
|
|
7981
|
+
},
|
|
7982
|
+
flushPending(runtimeEvent) {
|
|
7983
|
+
const out = [];
|
|
7984
|
+
for (const [key, pending] of pendingMessages) {
|
|
7985
|
+
if (pending.message) {
|
|
7986
|
+
const ev = emit({
|
|
7987
|
+
runtimeEvent,
|
|
7988
|
+
actorId: pending.actorId,
|
|
7989
|
+
scope: pending.scope,
|
|
7990
|
+
message: pending.message,
|
|
7991
|
+
source: "hook"
|
|
7992
|
+
});
|
|
7993
|
+
if (ev) out.push(ev);
|
|
7994
|
+
}
|
|
7995
|
+
pendingMessages.delete(key);
|
|
7996
|
+
}
|
|
7997
|
+
return out;
|
|
7998
|
+
},
|
|
7999
|
+
emitTranscriptMessages(transcriptPath, runtimeEvent, actorId, scope) {
|
|
8000
|
+
const msgs = transcriptReader.readNewAssistantMessages(transcriptPath);
|
|
8001
|
+
const out = [];
|
|
8002
|
+
for (const msg of msgs) {
|
|
8003
|
+
const ev = emit({
|
|
8004
|
+
runtimeEvent,
|
|
8005
|
+
actorId,
|
|
8006
|
+
scope,
|
|
8007
|
+
message: msg.text,
|
|
8008
|
+
source: "transcript",
|
|
8009
|
+
model: msg.model
|
|
8010
|
+
});
|
|
8011
|
+
if (ev) out.push(ev);
|
|
8012
|
+
}
|
|
8013
|
+
return out;
|
|
8014
|
+
},
|
|
8015
|
+
drainTranscript(transcriptPath) {
|
|
8016
|
+
transcriptReader.readNewAssistantMessages(transcriptPath);
|
|
8017
|
+
},
|
|
8018
|
+
appendReasoningSummary(itemId, index, chunk) {
|
|
8019
|
+
const key = `${itemId ?? ""}:${index ?? 0}`;
|
|
8020
|
+
const next = `${reasoningSummaryByKey.get(key) ?? ""}${chunk}`;
|
|
8021
|
+
reasoningSummaryByKey.set(key, next);
|
|
8022
|
+
return next;
|
|
8023
|
+
},
|
|
8024
|
+
clearPending() {
|
|
8025
|
+
pendingMessages.clear();
|
|
8026
|
+
},
|
|
8027
|
+
resetDeduper() {
|
|
8028
|
+
lastAgentMessageByActorScope.clear();
|
|
8029
|
+
},
|
|
8030
|
+
resetForNewRun() {
|
|
8031
|
+
lastAgentMessageByActorScope.clear();
|
|
8032
|
+
reasoningSummaryByKey.clear();
|
|
7937
8033
|
}
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
8034
|
+
};
|
|
8035
|
+
}
|
|
8036
|
+
|
|
8037
|
+
// src/core/feed/internals/rootPlanTracker.ts
|
|
8038
|
+
function createRootPlanTracker() {
|
|
8039
|
+
let items = [];
|
|
8040
|
+
return {
|
|
8041
|
+
set(next) {
|
|
8042
|
+
items = next;
|
|
8043
|
+
},
|
|
8044
|
+
current() {
|
|
8045
|
+
return items;
|
|
8046
|
+
},
|
|
8047
|
+
differs(next) {
|
|
8048
|
+
if (next.length !== items.length) return true;
|
|
8049
|
+
for (let i = 0; i < next.length; i++) {
|
|
8050
|
+
if (next[i]?.content !== items[i]?.content) return true;
|
|
8051
|
+
if (next[i]?.status !== items[i]?.status) return true;
|
|
8052
|
+
}
|
|
8053
|
+
return false;
|
|
8054
|
+
}
|
|
8055
|
+
};
|
|
8056
|
+
}
|
|
8057
|
+
|
|
8058
|
+
// src/core/feed/internals/subagentTracker.ts
|
|
8059
|
+
function createSubagentTracker() {
|
|
8060
|
+
const stack = [];
|
|
8061
|
+
const descriptions = /* @__PURE__ */ new Map();
|
|
8062
|
+
let pendingDescription;
|
|
8063
|
+
return {
|
|
8064
|
+
pushActor(actorId) {
|
|
8065
|
+
stack.push(actorId);
|
|
8066
|
+
},
|
|
8067
|
+
popActor(actorId) {
|
|
8068
|
+
const idx = stack.lastIndexOf(actorId);
|
|
8069
|
+
if (idx !== -1) stack.splice(idx, 1);
|
|
8070
|
+
},
|
|
8071
|
+
peek() {
|
|
8072
|
+
return stack.length > 0 ? stack[stack.length - 1] : void 0;
|
|
8073
|
+
},
|
|
8074
|
+
clear() {
|
|
8075
|
+
stack.length = 0;
|
|
8076
|
+
},
|
|
8077
|
+
currentScope() {
|
|
8078
|
+
return stack.length > 0 ? "subagent" : "root";
|
|
8079
|
+
},
|
|
8080
|
+
recordPendingDescription(description) {
|
|
8081
|
+
pendingDescription = description;
|
|
8082
|
+
},
|
|
8083
|
+
clearPendingDescription() {
|
|
8084
|
+
pendingDescription = void 0;
|
|
8085
|
+
},
|
|
8086
|
+
consumePendingDescription() {
|
|
8087
|
+
const value = pendingDescription;
|
|
8088
|
+
pendingDescription = void 0;
|
|
8089
|
+
return value;
|
|
8090
|
+
},
|
|
8091
|
+
setDescription(agentId, description) {
|
|
8092
|
+
descriptions.set(agentId, description);
|
|
8093
|
+
},
|
|
8094
|
+
description(agentId) {
|
|
8095
|
+
return descriptions.get(agentId);
|
|
7942
8096
|
}
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
8097
|
+
};
|
|
8098
|
+
}
|
|
8099
|
+
|
|
8100
|
+
// src/core/feed/mapper.ts
|
|
8101
|
+
function extractTodoItems(toolInput) {
|
|
8102
|
+
const input = toolInput;
|
|
8103
|
+
return Array.isArray(input?.todos) ? input.todos : [];
|
|
8104
|
+
}
|
|
8105
|
+
function mapPlanStepStatus(status) {
|
|
8106
|
+
switch (status) {
|
|
8107
|
+
case "inProgress":
|
|
8108
|
+
return "in_progress";
|
|
8109
|
+
case "completed":
|
|
8110
|
+
return "completed";
|
|
8111
|
+
case void 0:
|
|
8112
|
+
default:
|
|
8113
|
+
return "pending";
|
|
7951
8114
|
}
|
|
8115
|
+
}
|
|
8116
|
+
function createFeedMapper(bootstrap) {
|
|
8117
|
+
const runLifecycle = createRunLifecycle();
|
|
8118
|
+
const decisionCorrelation = createDecisionCorrelation();
|
|
8119
|
+
const toolCorrelation = createToolCorrelation();
|
|
8120
|
+
const transcriptReader = createTranscriptReader();
|
|
8121
|
+
const actors = new ActorRegistry();
|
|
8122
|
+
const rootPlan = createRootPlanTracker();
|
|
8123
|
+
const subagents = createSubagentTracker();
|
|
7952
8124
|
function makeEvent(kind, level, actorId, data, runtimeEvent, cause) {
|
|
7953
|
-
const s =
|
|
7954
|
-
const
|
|
8125
|
+
const s = runLifecycle.allocateSeq();
|
|
8126
|
+
const runId = runLifecycle.getRunId();
|
|
8127
|
+
const eventId = `${runId}:E${s}`;
|
|
7955
8128
|
const baseCause = {
|
|
7956
8129
|
hook_request_id: runtimeEvent.id,
|
|
7957
8130
|
transcript_path: runtimeEvent.context.transcriptPath,
|
|
@@ -7962,7 +8135,7 @@ function createFeedMapper(bootstrap) {
|
|
|
7962
8135
|
seq: s,
|
|
7963
8136
|
ts: runtimeEvent.timestamp,
|
|
7964
8137
|
session_id: runtimeEvent.sessionId,
|
|
7965
|
-
run_id:
|
|
8138
|
+
run_id: runId,
|
|
7966
8139
|
kind,
|
|
7967
8140
|
level,
|
|
7968
8141
|
actor_id: actorId,
|
|
@@ -7974,57 +8147,50 @@ function createFeedMapper(bootstrap) {
|
|
|
7974
8147
|
};
|
|
7975
8148
|
fe.title = composeTitle(fe, runtimeEvent);
|
|
7976
8149
|
if (runtimeEvent.interaction.expectsDecision || kind === "permission.request" || kind === "stop.request") {
|
|
7977
|
-
|
|
7978
|
-
eventKindByRequestId.set(runtimeEvent.id, kind);
|
|
7979
|
-
resolvedRequestById.set(runtimeEvent.id, { event_id: eventId, kind });
|
|
8150
|
+
decisionCorrelation.recordRequest(runtimeEvent.id, eventId, kind);
|
|
7980
8151
|
}
|
|
7981
8152
|
return fe;
|
|
7982
8153
|
}
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
8154
|
+
const agentMessageStream = createAgentMessageStream(
|
|
8155
|
+
makeEvent,
|
|
8156
|
+
transcriptReader
|
|
8157
|
+
);
|
|
8158
|
+
if (bootstrap) {
|
|
8159
|
+
runLifecycle.restoreFrom(bootstrap);
|
|
8160
|
+
for (const e of bootstrap.feedEvents) {
|
|
8161
|
+
if (e.kind === "tool.pre" && e.actor_id === "agent:root" && e.data.tool_name === "TodoWrite") {
|
|
8162
|
+
rootPlan.set(
|
|
8163
|
+
extractTodoItems(e.data.tool_input)
|
|
8164
|
+
);
|
|
8165
|
+
}
|
|
8166
|
+
}
|
|
8167
|
+
}
|
|
8168
|
+
function closeRunIntoEvent(runtimeEvent, status) {
|
|
8169
|
+
const closed = runLifecycle.closeRun(runtimeEvent.timestamp, status);
|
|
8170
|
+
if (!closed) return null;
|
|
8171
|
+
return makeEvent(
|
|
7988
8172
|
"run.end",
|
|
7989
8173
|
"info",
|
|
7990
8174
|
"system",
|
|
7991
|
-
{ status, counters: { ...
|
|
8175
|
+
{ status, counters: { ...closed.counters } },
|
|
7992
8176
|
runtimeEvent
|
|
7993
8177
|
);
|
|
7994
|
-
currentRun = null;
|
|
7995
|
-
return evt;
|
|
7996
8178
|
}
|
|
7997
8179
|
function ensureRunArray(runtimeEvent, triggerType = "other", promptPreview) {
|
|
7998
|
-
if (
|
|
8180
|
+
if (runLifecycle.getCurrentRun() && triggerType === "other") return [];
|
|
7999
8181
|
const results = [];
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
8009
|
-
|
|
8010
|
-
|
|
8011
|
-
|
|
8012
|
-
resetAgentMessageDeduper();
|
|
8013
|
-
activeSubagentStack.length = 0;
|
|
8014
|
-
currentRun = {
|
|
8015
|
-
run_id: getRunId(),
|
|
8016
|
-
session_id: runtimeEvent.sessionId,
|
|
8017
|
-
started_at: runtimeEvent.timestamp,
|
|
8018
|
-
trigger: { type: triggerType, prompt_preview: promptPreview },
|
|
8019
|
-
status: "running",
|
|
8020
|
-
actors: { root_agent_id: "agent:root", subagent_ids: [] },
|
|
8021
|
-
counters: {
|
|
8022
|
-
tool_uses: 0,
|
|
8023
|
-
tool_failures: 0,
|
|
8024
|
-
permission_requests: 0,
|
|
8025
|
-
blocks: 0
|
|
8026
|
-
}
|
|
8027
|
-
};
|
|
8182
|
+
const closeEvt = closeRunIntoEvent(runtimeEvent, "completed");
|
|
8183
|
+
if (closeEvt) results.push(closeEvt);
|
|
8184
|
+
toolCorrelation.resetForNewRun();
|
|
8185
|
+
decisionCorrelation.resetForNewRun();
|
|
8186
|
+
agentMessageStream.resetForNewRun();
|
|
8187
|
+
subagents.clear();
|
|
8188
|
+
runLifecycle.openNewRun(
|
|
8189
|
+
runtimeEvent.timestamp,
|
|
8190
|
+
runtimeEvent.sessionId,
|
|
8191
|
+
triggerType,
|
|
8192
|
+
promptPreview
|
|
8193
|
+
);
|
|
8028
8194
|
results.push(
|
|
8029
8195
|
makeEvent(
|
|
8030
8196
|
"run.start",
|
|
@@ -8036,73 +8202,8 @@ function createFeedMapper(bootstrap) {
|
|
|
8036
8202
|
);
|
|
8037
8203
|
return results;
|
|
8038
8204
|
}
|
|
8039
|
-
const activeSubagentStack = [];
|
|
8040
|
-
let lastTaskDescription;
|
|
8041
|
-
const subagentDescriptions = /* @__PURE__ */ new Map();
|
|
8042
|
-
const pendingMessages = /* @__PURE__ */ new Map();
|
|
8043
|
-
const lastAgentMessageByActorScope = /* @__PURE__ */ new Map();
|
|
8044
|
-
const reasoningSummaryByKey = /* @__PURE__ */ new Map();
|
|
8045
|
-
const transcriptReader = createTranscriptReader();
|
|
8046
|
-
function emitTranscriptMessages(transcriptPath, runtimeEvent, actorId, scope) {
|
|
8047
|
-
const msgs = transcriptReader.readNewAssistantMessages(transcriptPath);
|
|
8048
|
-
const results = [];
|
|
8049
|
-
for (const msg of msgs) {
|
|
8050
|
-
const agentMsg = emitAgentMessage(
|
|
8051
|
-
runtimeEvent,
|
|
8052
|
-
actorId,
|
|
8053
|
-
scope,
|
|
8054
|
-
msg.text,
|
|
8055
|
-
"transcript",
|
|
8056
|
-
void 0,
|
|
8057
|
-
msg.model
|
|
8058
|
-
);
|
|
8059
|
-
if (agentMsg) {
|
|
8060
|
-
results.push(agentMsg);
|
|
8061
|
-
}
|
|
8062
|
-
}
|
|
8063
|
-
return results;
|
|
8064
|
-
}
|
|
8065
|
-
function agentMessageKey(actorId, scope) {
|
|
8066
|
-
return `${actorId}\0${scope}`;
|
|
8067
|
-
}
|
|
8068
|
-
function normalizeAgentMessage(message) {
|
|
8069
|
-
return message.replace(/\r\n/g, "\n").trimEnd();
|
|
8070
|
-
}
|
|
8071
|
-
function resetAgentMessageDeduper() {
|
|
8072
|
-
lastAgentMessageByActorScope.clear();
|
|
8073
|
-
}
|
|
8074
|
-
function appendReasoningSummary(itemId, index, chunk) {
|
|
8075
|
-
const key = `${itemId ?? ""}:${index ?? 0}`;
|
|
8076
|
-
const next = `${reasoningSummaryByKey.get(key) ?? ""}${chunk}`;
|
|
8077
|
-
reasoningSummaryByKey.set(key, next);
|
|
8078
|
-
return next;
|
|
8079
|
-
}
|
|
8080
|
-
function emitAgentMessage(runtimeEvent, actorId, scope, message, source, cause, model) {
|
|
8081
|
-
const normalized = normalizeAgentMessage(message);
|
|
8082
|
-
if (!normalized) return null;
|
|
8083
|
-
const key = agentMessageKey(actorId, scope);
|
|
8084
|
-
const previous = lastAgentMessageByActorScope.get(key);
|
|
8085
|
-
if (previous === normalized) {
|
|
8086
|
-
return null;
|
|
8087
|
-
}
|
|
8088
|
-
const event = makeEvent(
|
|
8089
|
-
"agent.message",
|
|
8090
|
-
"info",
|
|
8091
|
-
actorId,
|
|
8092
|
-
{
|
|
8093
|
-
message: normalized,
|
|
8094
|
-
source,
|
|
8095
|
-
scope,
|
|
8096
|
-
...model ? { model } : {}
|
|
8097
|
-
},
|
|
8098
|
-
runtimeEvent,
|
|
8099
|
-
cause
|
|
8100
|
-
);
|
|
8101
|
-
lastAgentMessageByActorScope.set(key, normalized);
|
|
8102
|
-
return event;
|
|
8103
|
-
}
|
|
8104
8205
|
function resolveToolActor() {
|
|
8105
|
-
return
|
|
8206
|
+
return subagents.peek() ?? "agent:root";
|
|
8106
8207
|
}
|
|
8107
8208
|
function resolveToolUseId(event, record) {
|
|
8108
8209
|
return event.toolUseId ?? record["tool_use_id"];
|
|
@@ -8145,100 +8246,44 @@ function createFeedMapper(bootstrap) {
|
|
|
8145
8246
|
};
|
|
8146
8247
|
const eventKind2 = event.kind;
|
|
8147
8248
|
const results = [];
|
|
8148
|
-
const
|
|
8149
|
-
const actorId = resolveToolActor();
|
|
8150
|
-
return {
|
|
8151
|
-
actorId,
|
|
8152
|
-
scope: activeSubagentStack.length > 0 ? "subagent" : "root"
|
|
8153
|
-
};
|
|
8154
|
-
};
|
|
8155
|
-
const appendPendingMessageDelta = (itemId, delta) => {
|
|
8156
|
-
if (!delta) return;
|
|
8157
|
-
const key = itemId ?? "__legacy_root__";
|
|
8158
|
-
const existing = pendingMessages.get(key);
|
|
8159
|
-
if (existing) {
|
|
8160
|
-
existing.message += delta;
|
|
8161
|
-
return;
|
|
8162
|
-
}
|
|
8163
|
-
const scope = resolveMessageScope();
|
|
8164
|
-
pendingMessages.set(key, {
|
|
8165
|
-
message: delta,
|
|
8166
|
-
actorId: scope.actorId,
|
|
8167
|
-
scope: scope.scope
|
|
8168
|
-
});
|
|
8169
|
-
};
|
|
8170
|
-
const emitCompletedMessage = (itemId, messageText) => {
|
|
8171
|
-
const key = itemId ?? "__legacy_root__";
|
|
8172
|
-
const pending = pendingMessages.get(key);
|
|
8173
|
-
const message = messageText ?? pending?.message ?? "";
|
|
8174
|
-
if (!message) return;
|
|
8175
|
-
const scope = pending ?? resolveMessageScope();
|
|
8176
|
-
const agentMsg = emitAgentMessage(
|
|
8177
|
-
event,
|
|
8178
|
-
scope.actorId,
|
|
8179
|
-
scope.scope,
|
|
8180
|
-
message,
|
|
8181
|
-
"hook"
|
|
8182
|
-
);
|
|
8183
|
-
if (agentMsg) {
|
|
8184
|
-
results.push(agentMsg);
|
|
8185
|
-
}
|
|
8186
|
-
pendingMessages.delete(key);
|
|
8187
|
-
};
|
|
8188
|
-
const flushPendingMessages = () => {
|
|
8189
|
-
for (const [itemId, pending] of pendingMessages) {
|
|
8190
|
-
if (!pending.message) continue;
|
|
8191
|
-
const agentMsg = emitAgentMessage(
|
|
8192
|
-
event,
|
|
8193
|
-
pending.actorId,
|
|
8194
|
-
pending.scope,
|
|
8195
|
-
pending.message,
|
|
8196
|
-
"hook"
|
|
8197
|
-
);
|
|
8198
|
-
if (agentMsg) {
|
|
8199
|
-
results.push(agentMsg);
|
|
8200
|
-
}
|
|
8201
|
-
pendingMessages.delete(itemId);
|
|
8202
|
-
}
|
|
8203
|
-
};
|
|
8249
|
+
const currentScope = () => subagents.currentScope();
|
|
8204
8250
|
function emitFallbackMessage(parentKind, actorId, scope) {
|
|
8205
8251
|
if (results.some((r) => r.kind === "agent.message")) return;
|
|
8206
8252
|
const msg = readString(d["last_assistant_message"]);
|
|
8207
8253
|
if (!msg) return;
|
|
8208
8254
|
const parentEvt = results.find((r) => r.kind === parentKind);
|
|
8209
|
-
const
|
|
8210
|
-
event,
|
|
8255
|
+
const ev = agentMessageStream.emit({
|
|
8256
|
+
runtimeEvent: event,
|
|
8211
8257
|
actorId,
|
|
8212
8258
|
scope,
|
|
8213
|
-
msg,
|
|
8214
|
-
"hook",
|
|
8215
|
-
parentEvt ? { parent_event_id: parentEvt.event_id } : void 0
|
|
8216
|
-
);
|
|
8217
|
-
if (
|
|
8218
|
-
results.push(agentMsg);
|
|
8219
|
-
}
|
|
8259
|
+
message: msg,
|
|
8260
|
+
source: "hook",
|
|
8261
|
+
cause: parentEvt ? { parent_event_id: parentEvt.event_id } : void 0
|
|
8262
|
+
});
|
|
8263
|
+
if (ev) results.push(ev);
|
|
8220
8264
|
}
|
|
8221
8265
|
const transcriptPath = event.context.transcriptPath;
|
|
8222
8266
|
const isStopEvent = eventKind2 === "stop.request" || eventKind2 === "subagent.stop";
|
|
8223
8267
|
if (transcriptPath && !isStopEvent) {
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8268
|
+
results.push(
|
|
8269
|
+
...agentMessageStream.emitTranscriptMessages(
|
|
8270
|
+
transcriptPath,
|
|
8271
|
+
event,
|
|
8272
|
+
resolveToolActor(),
|
|
8273
|
+
currentScope()
|
|
8274
|
+
)
|
|
8229
8275
|
);
|
|
8230
|
-
results.push(...transcriptMsgs);
|
|
8231
8276
|
}
|
|
8232
8277
|
switch (eventKind2) {
|
|
8233
8278
|
case "session.start": {
|
|
8234
|
-
|
|
8279
|
+
agentMessageStream.clearPending();
|
|
8235
8280
|
const source = readString(d["source"]) ?? "startup";
|
|
8236
|
-
|
|
8281
|
+
runLifecycle.setSession({
|
|
8237
8282
|
session_id: event.sessionId,
|
|
8238
8283
|
started_at: event.timestamp,
|
|
8239
8284
|
source,
|
|
8240
8285
|
agent_type: readString(d["agent_type"])
|
|
8241
|
-
};
|
|
8286
|
+
});
|
|
8242
8287
|
if (source === "resume" || source === "clear" || source === "compact") {
|
|
8243
8288
|
results.push(
|
|
8244
8289
|
...ensureRunArray(event, source)
|
|
@@ -8260,11 +8305,9 @@ function createFeedMapper(bootstrap) {
|
|
|
8260
8305
|
break;
|
|
8261
8306
|
}
|
|
8262
8307
|
case "session.end": {
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8266
|
-
if (closeEvt) results.push(closeEvt);
|
|
8267
|
-
}
|
|
8308
|
+
agentMessageStream.clearPending();
|
|
8309
|
+
const closeEvt = closeRunIntoEvent(event, "completed");
|
|
8310
|
+
if (closeEvt) results.push(closeEvt);
|
|
8268
8311
|
results.push(
|
|
8269
8312
|
makeEvent(
|
|
8270
8313
|
"session.end",
|
|
@@ -8276,9 +8319,7 @@ function createFeedMapper(bootstrap) {
|
|
|
8276
8319
|
event
|
|
8277
8320
|
)
|
|
8278
8321
|
);
|
|
8279
|
-
|
|
8280
|
-
currentSession.ended_at = event.timestamp;
|
|
8281
|
-
}
|
|
8322
|
+
runLifecycle.endSession(event.timestamp);
|
|
8282
8323
|
break;
|
|
8283
8324
|
}
|
|
8284
8325
|
case "user.prompt": {
|
|
@@ -8302,7 +8343,7 @@ function createFeedMapper(bootstrap) {
|
|
|
8302
8343
|
break;
|
|
8303
8344
|
}
|
|
8304
8345
|
case "turn.start": {
|
|
8305
|
-
|
|
8346
|
+
agentMessageStream.clearPending();
|
|
8306
8347
|
const prompt = readString(d["prompt"]);
|
|
8307
8348
|
results.push(
|
|
8308
8349
|
...ensureRunArray(
|
|
@@ -8329,23 +8370,29 @@ function createFeedMapper(bootstrap) {
|
|
|
8329
8370
|
break;
|
|
8330
8371
|
}
|
|
8331
8372
|
case "message.delta": {
|
|
8332
|
-
|
|
8373
|
+
agentMessageStream.appendPendingDelta(
|
|
8333
8374
|
readString(d["item_id"]),
|
|
8334
|
-
readString(d["delta"]) ?? ""
|
|
8375
|
+
readString(d["delta"]) ?? "",
|
|
8376
|
+
resolveToolActor(),
|
|
8377
|
+
currentScope()
|
|
8335
8378
|
);
|
|
8336
8379
|
break;
|
|
8337
8380
|
}
|
|
8338
8381
|
case "message.complete": {
|
|
8339
8382
|
results.push(...ensureRunArray(event));
|
|
8340
|
-
|
|
8341
|
-
readString(d["item_id"]),
|
|
8342
|
-
readString(d["message"])
|
|
8343
|
-
|
|
8383
|
+
const ev = agentMessageStream.emitCompleted({
|
|
8384
|
+
itemId: readString(d["item_id"]),
|
|
8385
|
+
messageText: readString(d["message"]),
|
|
8386
|
+
fallbackActorId: resolveToolActor(),
|
|
8387
|
+
fallbackScope: currentScope(),
|
|
8388
|
+
runtimeEvent: event
|
|
8389
|
+
});
|
|
8390
|
+
if (ev) results.push(ev);
|
|
8344
8391
|
break;
|
|
8345
8392
|
}
|
|
8346
8393
|
case "turn.complete": {
|
|
8347
|
-
if (!
|
|
8348
|
-
|
|
8394
|
+
if (!runLifecycle.getCurrentRun()) {
|
|
8395
|
+
agentMessageStream.clearPending();
|
|
8349
8396
|
break;
|
|
8350
8397
|
}
|
|
8351
8398
|
const stopEvt = makeEvent(
|
|
@@ -8358,39 +8405,29 @@ function createFeedMapper(bootstrap) {
|
|
|
8358
8405
|
event
|
|
8359
8406
|
);
|
|
8360
8407
|
results.push(stopEvt);
|
|
8361
|
-
const
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
};
|
|
8369
|
-
}
|
|
8370
|
-
}
|
|
8371
|
-
const closeEvt = closeRun(event, "completed");
|
|
8372
|
-
if (closeEvt) {
|
|
8373
|
-
results.push(closeEvt);
|
|
8408
|
+
const flushed = agentMessageStream.flushPending(event);
|
|
8409
|
+
for (const f of flushed) {
|
|
8410
|
+
f.cause = {
|
|
8411
|
+
...f.cause ?? {},
|
|
8412
|
+
parent_event_id: stopEvt.event_id
|
|
8413
|
+
};
|
|
8414
|
+
results.push(f);
|
|
8374
8415
|
}
|
|
8416
|
+
const closeEvt = closeRunIntoEvent(event, "completed");
|
|
8417
|
+
if (closeEvt) results.push(closeEvt);
|
|
8375
8418
|
break;
|
|
8376
8419
|
}
|
|
8377
8420
|
case "plan.delta": {
|
|
8378
8421
|
const planSteps = d["plan"];
|
|
8379
8422
|
if (Array.isArray(planSteps) && planSteps.length > 0) {
|
|
8380
|
-
const
|
|
8381
|
-
(step
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
}
|
|
8423
|
+
const next = planSteps.map(
|
|
8424
|
+
(step) => ({
|
|
8425
|
+
content: typeof step.step === "string" ? step.step : "",
|
|
8426
|
+
status: mapPlanStepStatus(step.status)
|
|
8427
|
+
})
|
|
8386
8428
|
);
|
|
8387
|
-
if (
|
|
8388
|
-
|
|
8389
|
-
(step) => ({
|
|
8390
|
-
content: typeof step.step === "string" ? step.step : "",
|
|
8391
|
-
status: mapPlanStepStatus(step.status)
|
|
8392
|
-
})
|
|
8393
|
-
);
|
|
8429
|
+
if (rootPlan.differs(next)) {
|
|
8430
|
+
rootPlan.set(next);
|
|
8394
8431
|
results.push(
|
|
8395
8432
|
makeEvent(
|
|
8396
8433
|
"todo.update",
|
|
@@ -8435,7 +8472,7 @@ function createFeedMapper(bootstrap) {
|
|
|
8435
8472
|
"info",
|
|
8436
8473
|
"agent:root",
|
|
8437
8474
|
{
|
|
8438
|
-
message: appendReasoningSummary(
|
|
8475
|
+
message: agentMessageStream.appendReasoningSummary(
|
|
8439
8476
|
readString(d["item_id"]),
|
|
8440
8477
|
summaryIndex,
|
|
8441
8478
|
readString(d["delta"]) ?? ""
|
|
@@ -8472,9 +8509,9 @@ function createFeedMapper(bootstrap) {
|
|
|
8472
8509
|
results.push(...ensureRunArray(event));
|
|
8473
8510
|
const toolUseId = resolveToolUseId(event, d);
|
|
8474
8511
|
const toolName = event.toolName ?? readString(d["tool_name"]) ?? "Unknown";
|
|
8475
|
-
const parentId =
|
|
8512
|
+
const parentId = toolCorrelation.lookupParent(toolUseId);
|
|
8476
8513
|
const chunk = readString(d["delta"]) ?? "";
|
|
8477
|
-
const cumulative =
|
|
8514
|
+
const cumulative = toolCorrelation.appendDelta(toolUseId, chunk);
|
|
8478
8515
|
results.push(
|
|
8479
8516
|
makeEvent(
|
|
8480
8517
|
"tool.delta",
|
|
@@ -8494,7 +8531,7 @@ function createFeedMapper(bootstrap) {
|
|
|
8494
8531
|
}
|
|
8495
8532
|
case "tool.pre": {
|
|
8496
8533
|
results.push(...ensureRunArray(event));
|
|
8497
|
-
|
|
8534
|
+
runLifecycle.incrementCounter("tool_uses");
|
|
8498
8535
|
const toolUseId = resolveToolUseId(event, d);
|
|
8499
8536
|
const toolName = event.toolName ?? readString(d["tool_name"]) ?? "Unknown";
|
|
8500
8537
|
const fe = makeEvent(
|
|
@@ -8510,7 +8547,7 @@ function createFeedMapper(bootstrap) {
|
|
|
8510
8547
|
toolUseId ? { tool_use_id: toolUseId } : void 0
|
|
8511
8548
|
);
|
|
8512
8549
|
if (toolUseId) {
|
|
8513
|
-
|
|
8550
|
+
toolCorrelation.recordPre(toolUseId, fe.event_id);
|
|
8514
8551
|
}
|
|
8515
8552
|
results.push(fe);
|
|
8516
8553
|
if (toolName === "WebSearch") {
|
|
@@ -8536,22 +8573,23 @@ function createFeedMapper(bootstrap) {
|
|
|
8536
8573
|
);
|
|
8537
8574
|
}
|
|
8538
8575
|
if (toolName === "TodoWrite" && fe.actor_id === "agent:root") {
|
|
8539
|
-
|
|
8576
|
+
rootPlan.set(extractTodoItems(readObject(d["tool_input"])));
|
|
8540
8577
|
}
|
|
8541
8578
|
if (isSubagentTool(toolName)) {
|
|
8542
8579
|
const input = readObject(d["tool_input"]);
|
|
8543
|
-
|
|
8580
|
+
if (typeof input["description"] === "string") {
|
|
8581
|
+
subagents.recordPendingDescription(input["description"]);
|
|
8582
|
+
} else {
|
|
8583
|
+
subagents.clearPendingDescription();
|
|
8584
|
+
}
|
|
8544
8585
|
}
|
|
8545
8586
|
break;
|
|
8546
8587
|
}
|
|
8547
8588
|
case "tool.post": {
|
|
8548
8589
|
results.push(...ensureRunArray(event));
|
|
8549
8590
|
const toolUseId = resolveToolUseId(event, d);
|
|
8550
|
-
if (toolUseId)
|
|
8551
|
-
|
|
8552
|
-
truncatedToolDeltaUseIds.delete(toolUseId);
|
|
8553
|
-
}
|
|
8554
|
-
const parentId = toolUseId ? toolPreIndex.get(toolUseId) : void 0;
|
|
8591
|
+
if (toolUseId) toolCorrelation.forgetTool(toolUseId);
|
|
8592
|
+
const parentId = toolCorrelation.lookupParent(toolUseId);
|
|
8555
8593
|
const toolName = event.toolName ?? readString(d["tool_name"]) ?? "Unknown";
|
|
8556
8594
|
const postEvent = makeEvent(
|
|
8557
8595
|
"tool.post",
|
|
@@ -8599,13 +8637,10 @@ function createFeedMapper(bootstrap) {
|
|
|
8599
8637
|
}
|
|
8600
8638
|
case "tool.failure": {
|
|
8601
8639
|
results.push(...ensureRunArray(event));
|
|
8602
|
-
|
|
8640
|
+
runLifecycle.incrementCounter("tool_failures");
|
|
8603
8641
|
const toolUseId = resolveToolUseId(event, d);
|
|
8604
|
-
if (toolUseId)
|
|
8605
|
-
|
|
8606
|
-
truncatedToolDeltaUseIds.delete(toolUseId);
|
|
8607
|
-
}
|
|
8608
|
-
const parentId = toolUseId ? toolPreIndex.get(toolUseId) : void 0;
|
|
8642
|
+
if (toolUseId) toolCorrelation.forgetTool(toolUseId);
|
|
8643
|
+
const parentId = toolCorrelation.lookupParent(toolUseId);
|
|
8609
8644
|
const toolName = event.toolName ?? readString(d["tool_name"]) ?? "Unknown";
|
|
8610
8645
|
results.push(
|
|
8611
8646
|
makeEvent(
|
|
@@ -8627,7 +8662,7 @@ function createFeedMapper(bootstrap) {
|
|
|
8627
8662
|
}
|
|
8628
8663
|
case "permission.request": {
|
|
8629
8664
|
results.push(...ensureRunArray(event));
|
|
8630
|
-
|
|
8665
|
+
runLifecycle.incrementCounter("permission_requests");
|
|
8631
8666
|
const toolName = event.toolName ?? readString(d["tool_name"]) ?? "Unknown";
|
|
8632
8667
|
results.push(
|
|
8633
8668
|
makeEvent(
|
|
@@ -8676,9 +8711,11 @@ function createFeedMapper(bootstrap) {
|
|
|
8676
8711
|
const agentType = event.agentType ?? readString(d["agent_type"]);
|
|
8677
8712
|
if (agentId) {
|
|
8678
8713
|
actors.ensureSubagent(agentId, agentType ?? "unknown");
|
|
8714
|
+
const currentRun = runLifecycle.getCurrentRun();
|
|
8679
8715
|
if (currentRun) currentRun.actors.subagent_ids.push(agentId);
|
|
8680
|
-
|
|
8716
|
+
subagents.pushActor(`subagent:${agentId}`);
|
|
8681
8717
|
}
|
|
8718
|
+
const description = subagents.consumePendingDescription() ?? readString(d["prompt"]);
|
|
8682
8719
|
results.push(
|
|
8683
8720
|
makeEvent(
|
|
8684
8721
|
"subagent.start",
|
|
@@ -8687,7 +8724,7 @@ function createFeedMapper(bootstrap) {
|
|
|
8687
8724
|
{
|
|
8688
8725
|
agent_id: agentId ?? "",
|
|
8689
8726
|
agent_type: agentType ?? "",
|
|
8690
|
-
description:
|
|
8727
|
+
description: description ?? void 0,
|
|
8691
8728
|
tool: readString(d["tool"]),
|
|
8692
8729
|
sender_thread_id: readString(d["sender_thread_id"]),
|
|
8693
8730
|
receiver_thread_id: readString(d["receiver_thread_id"]),
|
|
@@ -8697,22 +8734,16 @@ function createFeedMapper(bootstrap) {
|
|
|
8697
8734
|
event
|
|
8698
8735
|
)
|
|
8699
8736
|
);
|
|
8700
|
-
if (agentId &&
|
|
8701
|
-
|
|
8702
|
-
agentId,
|
|
8703
|
-
lastTaskDescription ?? readString(d["prompt"]) ?? ""
|
|
8704
|
-
);
|
|
8737
|
+
if (agentId && description) {
|
|
8738
|
+
subagents.setDescription(agentId, description);
|
|
8705
8739
|
}
|
|
8706
|
-
lastTaskDescription = void 0;
|
|
8707
8740
|
break;
|
|
8708
8741
|
}
|
|
8709
8742
|
case "subagent.stop": {
|
|
8710
8743
|
results.push(...ensureRunArray(event));
|
|
8711
8744
|
const agentId = event.agentId ?? readString(d["agent_id"]);
|
|
8712
8745
|
if (agentId) {
|
|
8713
|
-
|
|
8714
|
-
const idx = activeSubagentStack.lastIndexOf(actorId);
|
|
8715
|
-
if (idx !== -1) activeSubagentStack.splice(idx, 1);
|
|
8746
|
+
subagents.popActor(`subagent:${agentId}`);
|
|
8716
8747
|
}
|
|
8717
8748
|
const subStopActorId = `subagent:${agentId ?? "unknown"}`;
|
|
8718
8749
|
const subStopEvt = makeEvent(
|
|
@@ -8725,7 +8756,7 @@ function createFeedMapper(bootstrap) {
|
|
|
8725
8756
|
stop_hook_active: readBoolean(d["stop_hook_active"]) ?? false,
|
|
8726
8757
|
agent_transcript_path: readString(d["agent_transcript_path"]),
|
|
8727
8758
|
last_assistant_message: readString(d["last_assistant_message"]),
|
|
8728
|
-
description:
|
|
8759
|
+
description: subagents.description(agentId ?? ""),
|
|
8729
8760
|
tool: readString(d["tool"]),
|
|
8730
8761
|
status: readString(d["status"]),
|
|
8731
8762
|
sender_thread_id: readString(d["sender_thread_id"]),
|
|
@@ -8836,7 +8867,7 @@ function createFeedMapper(bootstrap) {
|
|
|
8836
8867
|
],
|
|
8837
8868
|
"server_request.resolved": (data, runtimeEvent, ctx) => {
|
|
8838
8869
|
const requestId = data["request_id"] !== void 0 ? String(data["request_id"]) : void 0;
|
|
8839
|
-
const resolved = requestId ?
|
|
8870
|
+
const resolved = requestId ? decisionCorrelation.lookupResolved(requestId) : null;
|
|
8840
8871
|
return [
|
|
8841
8872
|
makeEvent(
|
|
8842
8873
|
"server.request.resolved",
|
|
@@ -9174,34 +9205,30 @@ function createFeedMapper(bootstrap) {
|
|
|
9174
9205
|
}
|
|
9175
9206
|
}
|
|
9176
9207
|
if (eventKind2 === "stop.request") {
|
|
9177
|
-
if (transcriptPath)
|
|
9178
|
-
transcriptReader.readNewAssistantMessages(transcriptPath);
|
|
9179
|
-
}
|
|
9208
|
+
if (transcriptPath) agentMessageStream.drainTranscript(transcriptPath);
|
|
9180
9209
|
emitFallbackMessage("stop.request", "agent:root", "root");
|
|
9181
9210
|
}
|
|
9182
9211
|
if (eventKind2 === "subagent.stop") {
|
|
9183
9212
|
const agentId = readString(d["agent_id"]) ?? "unknown";
|
|
9184
|
-
if (transcriptPath)
|
|
9185
|
-
transcriptReader.readNewAssistantMessages(transcriptPath);
|
|
9186
|
-
}
|
|
9213
|
+
if (transcriptPath) agentMessageStream.drainTranscript(transcriptPath);
|
|
9187
9214
|
emitFallbackMessage("subagent.stop", `subagent:${agentId}`, "subagent");
|
|
9188
9215
|
}
|
|
9189
9216
|
return results;
|
|
9190
9217
|
}
|
|
9191
9218
|
function mapDecision(requestId, decision) {
|
|
9192
|
-
const
|
|
9193
|
-
if (!
|
|
9194
|
-
const originalKind =
|
|
9195
|
-
eventIdByRequestId.delete(requestId);
|
|
9196
|
-
eventKindByRequestId.delete(requestId);
|
|
9219
|
+
const consumed = decisionCorrelation.consumeForDecision(requestId);
|
|
9220
|
+
if (!consumed) return null;
|
|
9221
|
+
const { parentEventId, originalKind } = consumed;
|
|
9197
9222
|
function makeDecisionEvent(kind, data) {
|
|
9198
|
-
const s =
|
|
9223
|
+
const s = runLifecycle.allocateSeq();
|
|
9224
|
+
const runId = runLifecycle.getRunId();
|
|
9225
|
+
const session = runLifecycle.getSession();
|
|
9199
9226
|
const fe = {
|
|
9200
|
-
event_id: `${
|
|
9227
|
+
event_id: `${runId}:E${s}`,
|
|
9201
9228
|
seq: s,
|
|
9202
9229
|
ts: Date.now(),
|
|
9203
|
-
session_id:
|
|
9204
|
-
run_id:
|
|
9230
|
+
session_id: session?.session_id ?? "unknown",
|
|
9231
|
+
run_id: runId,
|
|
9205
9232
|
kind,
|
|
9206
9233
|
level: "info",
|
|
9207
9234
|
actor_id: decision.source === "user" ? "user" : "system",
|
|
@@ -9233,41 +9260,205 @@ function createFeedMapper(bootstrap) {
|
|
|
9233
9260
|
}
|
|
9234
9261
|
return makeDecisionEvent("permission.decision", data);
|
|
9235
9262
|
}
|
|
9236
|
-
if (originalKind === "stop.request") {
|
|
9237
|
-
let data;
|
|
9238
|
-
const d = decision.data;
|
|
9239
|
-
const decisionReason = typeof d?.reason === "string" ? d.reason : void 0;
|
|
9240
|
-
if (decision.source === "timeout") {
|
|
9241
|
-
data = { decision_type: "no_opinion", reason: "timeout" };
|
|
9242
|
-
} else if (decision.type === "passthrough") {
|
|
9243
|
-
data = { decision_type: "no_opinion", reason: decision.source };
|
|
9244
|
-
} else if (d?.decision === "block") {
|
|
9245
|
-
data = {
|
|
9246
|
-
decision_type: "block",
|
|
9247
|
-
reason: decisionReason ?? decision.reason ?? "Blocked"
|
|
9248
|
-
};
|
|
9249
|
-
} else if (d?.ok === false) {
|
|
9250
|
-
data = {
|
|
9251
|
-
decision_type: "block",
|
|
9252
|
-
reason: decisionReason ?? "Blocked by hook"
|
|
9253
|
-
};
|
|
9254
|
-
} else {
|
|
9255
|
-
data = { decision_type: "allow" };
|
|
9263
|
+
if (originalKind === "stop.request") {
|
|
9264
|
+
let data;
|
|
9265
|
+
const d = decision.data;
|
|
9266
|
+
const decisionReason = typeof d?.reason === "string" ? d.reason : void 0;
|
|
9267
|
+
if (decision.source === "timeout") {
|
|
9268
|
+
data = { decision_type: "no_opinion", reason: "timeout" };
|
|
9269
|
+
} else if (decision.type === "passthrough") {
|
|
9270
|
+
data = { decision_type: "no_opinion", reason: decision.source };
|
|
9271
|
+
} else if (d?.decision === "block") {
|
|
9272
|
+
data = {
|
|
9273
|
+
decision_type: "block",
|
|
9274
|
+
reason: decisionReason ?? decision.reason ?? "Blocked"
|
|
9275
|
+
};
|
|
9276
|
+
} else if (d?.ok === false) {
|
|
9277
|
+
data = {
|
|
9278
|
+
decision_type: "block",
|
|
9279
|
+
reason: decisionReason ?? "Blocked by hook"
|
|
9280
|
+
};
|
|
9281
|
+
} else {
|
|
9282
|
+
data = { decision_type: "allow" };
|
|
9283
|
+
}
|
|
9284
|
+
return makeDecisionEvent("stop.decision", data);
|
|
9285
|
+
}
|
|
9286
|
+
return null;
|
|
9287
|
+
}
|
|
9288
|
+
return {
|
|
9289
|
+
mapEvent,
|
|
9290
|
+
mapDecision,
|
|
9291
|
+
getSession: () => runLifecycle.getSession(),
|
|
9292
|
+
getCurrentRun: () => runLifecycle.getCurrentRun(),
|
|
9293
|
+
getActors: () => actors.all(),
|
|
9294
|
+
getTasks: () => rootPlan.current(),
|
|
9295
|
+
allocateSeq: () => runLifecycle.allocateSeq()
|
|
9296
|
+
};
|
|
9297
|
+
}
|
|
9298
|
+
|
|
9299
|
+
// src/core/controller/rules.ts
|
|
9300
|
+
function ruleMatches(ruleToolName, toolName) {
|
|
9301
|
+
if (ruleToolName === "*") return true;
|
|
9302
|
+
if (ruleToolName === toolName) return true;
|
|
9303
|
+
if (ruleToolName.endsWith("__*")) {
|
|
9304
|
+
const prefix = ruleToolName.slice(0, -1);
|
|
9305
|
+
return toolName.startsWith(prefix);
|
|
9306
|
+
}
|
|
9307
|
+
return false;
|
|
9308
|
+
}
|
|
9309
|
+
function matchRule(rules, toolName) {
|
|
9310
|
+
const denyMatch = rules.find(
|
|
9311
|
+
(r) => r.action === "deny" && ruleMatches(r.toolName, toolName)
|
|
9312
|
+
);
|
|
9313
|
+
if (denyMatch) return denyMatch;
|
|
9314
|
+
return rules.find(
|
|
9315
|
+
(r) => r.action === "approve" && ruleMatches(r.toolName, toolName)
|
|
9316
|
+
);
|
|
9317
|
+
}
|
|
9318
|
+
|
|
9319
|
+
// src/core/controller/permission.ts
|
|
9320
|
+
var SESSION_APPROVAL_REQUEST_HOOKS = /* @__PURE__ */ new Set([
|
|
9321
|
+
"item/commandExecution/requestApproval",
|
|
9322
|
+
"item/fileChange/requestApproval",
|
|
9323
|
+
"item/permissions/requestApproval"
|
|
9324
|
+
]);
|
|
9325
|
+
function isScopedPermissionsRequest(hookName) {
|
|
9326
|
+
return hookName === "item/permissions/requestApproval";
|
|
9327
|
+
}
|
|
9328
|
+
function supportsSessionApproval(hookName) {
|
|
9329
|
+
return hookName !== void 0 && SESSION_APPROVAL_REQUEST_HOOKS.has(hookName);
|
|
9330
|
+
}
|
|
9331
|
+
function extractPermissionSnapshot(event) {
|
|
9332
|
+
const data = event.data;
|
|
9333
|
+
const toolNameFromData = typeof data["tool_name"] === "string" ? data["tool_name"] : void 0;
|
|
9334
|
+
const toolInputFromData = typeof data["tool_input"] === "object" && data["tool_input"] !== null ? data["tool_input"] : void 0;
|
|
9335
|
+
const toolUseIdFromData = typeof data["tool_use_id"] === "string" ? data["tool_use_id"] : void 0;
|
|
9336
|
+
return {
|
|
9337
|
+
request_id: event.id,
|
|
9338
|
+
ts: event.timestamp,
|
|
9339
|
+
kind: event.kind,
|
|
9340
|
+
hookName: event.hookName,
|
|
9341
|
+
tool_name: event.toolName ?? toolNameFromData ?? "Unknown",
|
|
9342
|
+
tool_input: toolInputFromData ?? {},
|
|
9343
|
+
tool_use_id: event.toolUseId ?? toolUseIdFromData,
|
|
9344
|
+
suggestions: data.permission_suggestions
|
|
9345
|
+
};
|
|
9346
|
+
}
|
|
9347
|
+
|
|
9348
|
+
// src/core/controller/runtimeController.ts
|
|
9349
|
+
function handleEvent(event, cb) {
|
|
9350
|
+
const eventKind2 = event.kind;
|
|
9351
|
+
const isScoped = isScopedPermissionsRequest(event.hookName);
|
|
9352
|
+
const eventData = event.data;
|
|
9353
|
+
const toolName = event.toolName ?? (typeof eventData["tool_name"] === "string" ? eventData["tool_name"] : void 0);
|
|
9354
|
+
if (eventKind2 === "permission.request" && toolName === "user_input") {
|
|
9355
|
+
cb.enqueueQuestion(event.id);
|
|
9356
|
+
cb.relayQuestion?.(event);
|
|
9357
|
+
return { handled: true };
|
|
9358
|
+
}
|
|
9359
|
+
if (eventKind2 === "permission.request" && toolName) {
|
|
9360
|
+
const rule = matchRule(cb.getRules(), toolName);
|
|
9361
|
+
if (rule?.action === "deny") {
|
|
9362
|
+
return {
|
|
9363
|
+
handled: true,
|
|
9364
|
+
decision: {
|
|
9365
|
+
type: "json",
|
|
9366
|
+
source: "rule",
|
|
9367
|
+
intent: {
|
|
9368
|
+
kind: "permission_deny",
|
|
9369
|
+
reason: `Blocked by rule: ${rule.addedBy}`
|
|
9370
|
+
}
|
|
9371
|
+
}
|
|
9372
|
+
};
|
|
9373
|
+
}
|
|
9374
|
+
if (rule?.action === "approve") {
|
|
9375
|
+
return {
|
|
9376
|
+
handled: true,
|
|
9377
|
+
decision: {
|
|
9378
|
+
type: "json",
|
|
9379
|
+
source: "rule",
|
|
9380
|
+
intent: { kind: "permission_allow" },
|
|
9381
|
+
// Scoped Codex permission grants are session-scoped under
|
|
9382
|
+
// auto rules so the same capability isn't re-prompted.
|
|
9383
|
+
...isScoped ? { data: { scope: "session" } } : {}
|
|
9384
|
+
}
|
|
9385
|
+
};
|
|
9386
|
+
}
|
|
9387
|
+
cb.enqueuePermission(event);
|
|
9388
|
+
cb.relayPermission?.(event);
|
|
9389
|
+
return { handled: true };
|
|
9390
|
+
}
|
|
9391
|
+
if (eventKind2 === "tool.pre" && toolName === "AskUserQuestion") {
|
|
9392
|
+
cb.enqueueQuestion(event.id);
|
|
9393
|
+
cb.relayQuestion?.(event);
|
|
9394
|
+
return { handled: true };
|
|
9395
|
+
}
|
|
9396
|
+
if (eventKind2 === "tool.pre" && toolName) {
|
|
9397
|
+
const rule = matchRule(cb.getRules(), toolName);
|
|
9398
|
+
if (rule?.action === "deny") {
|
|
9399
|
+
return {
|
|
9400
|
+
handled: true,
|
|
9401
|
+
decision: {
|
|
9402
|
+
type: "json",
|
|
9403
|
+
source: "rule",
|
|
9404
|
+
intent: {
|
|
9405
|
+
kind: "pre_tool_deny",
|
|
9406
|
+
reason: `Blocked by rule: ${rule.addedBy}`
|
|
9407
|
+
}
|
|
9408
|
+
}
|
|
9409
|
+
};
|
|
9410
|
+
}
|
|
9411
|
+
return {
|
|
9412
|
+
handled: true,
|
|
9413
|
+
decision: {
|
|
9414
|
+
type: "json",
|
|
9415
|
+
source: "rule",
|
|
9416
|
+
intent: { kind: "pre_tool_allow" }
|
|
9256
9417
|
}
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9418
|
+
};
|
|
9419
|
+
}
|
|
9420
|
+
return { handled: false };
|
|
9421
|
+
}
|
|
9422
|
+
|
|
9423
|
+
// src/core/feed/ingest.ts
|
|
9424
|
+
function ingestRuntimeEvent(event, ctx) {
|
|
9425
|
+
const controllerResult = handleEvent(event, ctx.controllerCallbacks);
|
|
9426
|
+
const feedEvents = ctx.mapper.mapEvent(event);
|
|
9427
|
+
if (ctx.store) {
|
|
9428
|
+
persistOrDegrade(
|
|
9429
|
+
ctx.store,
|
|
9430
|
+
() => ctx.store.recordEvent(event, feedEvents),
|
|
9431
|
+
"recordEvent failed",
|
|
9432
|
+
ctx.onPersistFailure
|
|
9433
|
+
);
|
|
9260
9434
|
}
|
|
9261
9435
|
return {
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
getSession: () => currentSession,
|
|
9265
|
-
getCurrentRun: () => currentRun,
|
|
9266
|
-
getActors: () => actors.all(),
|
|
9267
|
-
getTasks: () => lastRootTasks,
|
|
9268
|
-
allocateSeq: nextSeq
|
|
9436
|
+
feedEvents,
|
|
9437
|
+
decision: controllerResult.handled && controllerResult.decision ? controllerResult.decision : null
|
|
9269
9438
|
};
|
|
9270
9439
|
}
|
|
9440
|
+
function ingestRuntimeDecision(eventId, decision, ctx) {
|
|
9441
|
+
const feedEvent = ctx.mapper.mapDecision(eventId, decision);
|
|
9442
|
+
if (feedEvent && ctx.store) {
|
|
9443
|
+
persistOrDegrade(
|
|
9444
|
+
ctx.store,
|
|
9445
|
+
() => ctx.store.recordFeedEvents([feedEvent]),
|
|
9446
|
+
"recordFeedEvents failed",
|
|
9447
|
+
ctx.onPersistFailure
|
|
9448
|
+
);
|
|
9449
|
+
}
|
|
9450
|
+
return feedEvent;
|
|
9451
|
+
}
|
|
9452
|
+
function persistOrDegrade(store, action, label, onFailure) {
|
|
9453
|
+
try {
|
|
9454
|
+
action();
|
|
9455
|
+
} catch (err) {
|
|
9456
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
9457
|
+
const message = `${label}: ${reason}`;
|
|
9458
|
+
store.markDegraded(message);
|
|
9459
|
+
onFailure?.(message);
|
|
9460
|
+
}
|
|
9461
|
+
}
|
|
9271
9462
|
|
|
9272
9463
|
// src/infra/sessions/store.ts
|
|
9273
9464
|
import fs16 from "fs";
|
|
@@ -9523,6 +9714,20 @@ function initSchema(db) {
|
|
|
9523
9714
|
}
|
|
9524
9715
|
}
|
|
9525
9716
|
|
|
9717
|
+
// src/infra/sessions/types.ts
|
|
9718
|
+
function rowToAthenaSession(row, adapterSessionIds, firstPrompt) {
|
|
9719
|
+
return {
|
|
9720
|
+
id: row.id,
|
|
9721
|
+
projectDir: row.project_dir,
|
|
9722
|
+
createdAt: row.created_at,
|
|
9723
|
+
updatedAt: row.updated_at,
|
|
9724
|
+
label: row.label ?? void 0,
|
|
9725
|
+
eventCount: row.event_count ?? 0,
|
|
9726
|
+
firstPrompt,
|
|
9727
|
+
adapterSessionIds
|
|
9728
|
+
};
|
|
9729
|
+
}
|
|
9730
|
+
|
|
9526
9731
|
// src/infra/sessions/store.ts
|
|
9527
9732
|
function createSessionStore(opts) {
|
|
9528
9733
|
if (opts.dbPath !== ":memory:") {
|
|
@@ -9639,15 +9844,7 @@ function createSessionStore(opts) {
|
|
|
9639
9844
|
const adapterRows = db.prepare("SELECT * FROM adapter_sessions ORDER BY started_at").all();
|
|
9640
9845
|
const feedRows = db.prepare("SELECT data FROM feed_events ORDER BY seq").all();
|
|
9641
9846
|
const adapterSessionIds = adapterRows.map((r) => r.session_id);
|
|
9642
|
-
const session = sessionRow ? {
|
|
9643
|
-
id: sessionRow.id,
|
|
9644
|
-
projectDir: sessionRow.project_dir,
|
|
9645
|
-
createdAt: sessionRow.created_at,
|
|
9646
|
-
updatedAt: sessionRow.updated_at,
|
|
9647
|
-
label: sessionRow.label ?? void 0,
|
|
9648
|
-
eventCount: sessionRow.event_count ?? 0,
|
|
9649
|
-
adapterSessionIds
|
|
9650
|
-
} : {
|
|
9847
|
+
const session = sessionRow ? rowToAthenaSession(sessionRow, adapterSessionIds) : {
|
|
9651
9848
|
id: opts.sessionId,
|
|
9652
9849
|
projectDir: opts.projectDir,
|
|
9653
9850
|
createdAt: now,
|
|
@@ -9677,24 +9874,17 @@ function createSessionStore(opts) {
|
|
|
9677
9874
|
function getAthenaSession() {
|
|
9678
9875
|
const sessionRow = db.prepare("SELECT * FROM session WHERE id = ?").get(opts.sessionId);
|
|
9679
9876
|
const adapterRows = db.prepare("SELECT session_id FROM adapter_sessions ORDER BY started_at").all();
|
|
9877
|
+
const adapterSessionIds = adapterRows.map((r) => r.session_id);
|
|
9680
9878
|
if (!sessionRow) {
|
|
9681
9879
|
return {
|
|
9682
9880
|
id: opts.sessionId,
|
|
9683
9881
|
projectDir: opts.projectDir,
|
|
9684
9882
|
createdAt: now,
|
|
9685
9883
|
updatedAt: now,
|
|
9686
|
-
adapterSessionIds
|
|
9884
|
+
adapterSessionIds
|
|
9687
9885
|
};
|
|
9688
9886
|
}
|
|
9689
|
-
return
|
|
9690
|
-
id: sessionRow.id,
|
|
9691
|
-
projectDir: sessionRow.project_dir,
|
|
9692
|
-
createdAt: sessionRow.created_at,
|
|
9693
|
-
updatedAt: sessionRow.updated_at,
|
|
9694
|
-
label: sessionRow.label ?? void 0,
|
|
9695
|
-
eventCount: sessionRow.event_count ?? 0,
|
|
9696
|
-
adapterSessionIds: adapterRows.map((r) => r.session_id)
|
|
9697
|
-
};
|
|
9887
|
+
return rowToAthenaSession(sessionRow, adapterSessionIds);
|
|
9698
9888
|
}
|
|
9699
9889
|
const updateTokens = db.prepare(
|
|
9700
9890
|
`UPDATE adapter_sessions SET
|
|
@@ -9847,16 +10037,11 @@ function readSessionFromDb(dbPath) {
|
|
|
9847
10037
|
firstPrompt = promptRow.prompt;
|
|
9848
10038
|
}
|
|
9849
10039
|
}
|
|
9850
|
-
return
|
|
9851
|
-
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
|
|
9855
|
-
label: row.label ?? void 0,
|
|
9856
|
-
eventCount: row.event_count ?? 0,
|
|
9857
|
-
firstPrompt,
|
|
9858
|
-
adapterSessionIds: adapters.map((a) => a.session_id)
|
|
9859
|
-
};
|
|
10040
|
+
return rowToAthenaSession(
|
|
10041
|
+
row,
|
|
10042
|
+
adapters.map((a) => a.session_id),
|
|
10043
|
+
firstPrompt
|
|
10044
|
+
);
|
|
9860
10045
|
} catch {
|
|
9861
10046
|
return null;
|
|
9862
10047
|
} finally {
|
|
@@ -10844,6 +11029,33 @@ function resolveFinalMessage(input) {
|
|
|
10844
11029
|
return { message: "", source: "empty" };
|
|
10845
11030
|
}
|
|
10846
11031
|
|
|
11032
|
+
// src/app/exec/failureLatch.ts
|
|
11033
|
+
function createFailureLatch(onRegister) {
|
|
11034
|
+
let failure;
|
|
11035
|
+
return {
|
|
11036
|
+
register(next) {
|
|
11037
|
+
if (failure) return;
|
|
11038
|
+
failure = next;
|
|
11039
|
+
onRegister(failure);
|
|
11040
|
+
},
|
|
11041
|
+
current() {
|
|
11042
|
+
return failure;
|
|
11043
|
+
},
|
|
11044
|
+
hasFailure() {
|
|
11045
|
+
return failure !== void 0;
|
|
11046
|
+
}
|
|
11047
|
+
};
|
|
11048
|
+
}
|
|
11049
|
+
function exitCodeFromFailure(failure) {
|
|
11050
|
+
if (!failure) return EXEC_EXIT_CODE.SUCCESS;
|
|
11051
|
+
if (failure.kind === "timeout") return EXEC_EXIT_CODE.TIMEOUT;
|
|
11052
|
+
if (failure.kind === "output") return EXEC_EXIT_CODE.OUTPUT;
|
|
11053
|
+
if (failure.kind === "workflow") {
|
|
11054
|
+
return failure.state === "exhausted" ? EXEC_EXIT_CODE.WORKFLOW_EXHAUSTED : EXEC_EXIT_CODE.WORKFLOW_BLOCKED;
|
|
11055
|
+
}
|
|
11056
|
+
return EXEC_EXIT_CODE.RUNTIME;
|
|
11057
|
+
}
|
|
11058
|
+
|
|
10847
11059
|
// src/app/exec/output.ts
|
|
10848
11060
|
import fs19 from "fs/promises";
|
|
10849
11061
|
import path16 from "path";
|
|
@@ -10951,7 +11163,6 @@ async function runExec(options) {
|
|
|
10951
11163
|
now
|
|
10952
11164
|
});
|
|
10953
11165
|
const rules = [];
|
|
10954
|
-
let failure;
|
|
10955
11166
|
let runtimeStarted = false;
|
|
10956
11167
|
let cumulativeTokens = { ...NULL_TOKENS3 };
|
|
10957
11168
|
let streamFinalMessage = null;
|
|
@@ -11016,28 +11227,22 @@ async function runExec(options) {
|
|
|
11016
11227
|
runtime,
|
|
11017
11228
|
spawnProcess: options.spawnProcess
|
|
11018
11229
|
});
|
|
11019
|
-
|
|
11020
|
-
if (failure) return;
|
|
11021
|
-
failure = next;
|
|
11230
|
+
const latch = createFailureLatch((next) => {
|
|
11022
11231
|
output.error(next.message);
|
|
11023
11232
|
output.emitJsonEvent("exec.error", {
|
|
11024
11233
|
kind: next.kind,
|
|
11025
11234
|
message: next.message
|
|
11026
11235
|
});
|
|
11027
11236
|
void sessionController.kill();
|
|
11028
|
-
}
|
|
11237
|
+
});
|
|
11029
11238
|
const abortListener = () => {
|
|
11030
|
-
|
|
11031
|
-
kind: "process",
|
|
11032
|
-
message: "Execution cancelled."
|
|
11033
|
-
});
|
|
11239
|
+
latch.register({ kind: "process", message: "Execution cancelled." });
|
|
11034
11240
|
};
|
|
11035
11241
|
if (options.signal?.aborted) {
|
|
11036
11242
|
abortListener();
|
|
11037
11243
|
} else {
|
|
11038
11244
|
options.signal?.addEventListener("abort", abortListener, { once: true });
|
|
11039
11245
|
}
|
|
11040
|
-
const hasFailure = () => failure !== void 0;
|
|
11041
11246
|
const currentAdapterSessionId = () => adapterSessionId;
|
|
11042
11247
|
const bridgeFactory = options.bridgeFactory ?? startSessionBridge;
|
|
11043
11248
|
const bridge = options.channels && options.channels.length > 0 ? await bridgeFactory({
|
|
@@ -11079,23 +11284,21 @@ async function runExec(options) {
|
|
|
11079
11284
|
toolName: runtimeEvent.toolName ?? null,
|
|
11080
11285
|
data: runtimeEvent.data
|
|
11081
11286
|
});
|
|
11082
|
-
if (hasFailure()) return;
|
|
11083
|
-
const
|
|
11084
|
-
|
|
11085
|
-
|
|
11287
|
+
if (latch.hasFailure()) return;
|
|
11288
|
+
const { feedEvents, decision } = ingestRuntimeEvent(runtimeEvent, {
|
|
11289
|
+
mapper,
|
|
11290
|
+
store,
|
|
11291
|
+
controllerCallbacks,
|
|
11292
|
+
onPersistFailure: (message) => output.warn(message)
|
|
11293
|
+
});
|
|
11294
|
+
if (decision) {
|
|
11295
|
+
runtime.sendDecision(runtimeEvent.id, decision);
|
|
11086
11296
|
}
|
|
11087
|
-
const
|
|
11088
|
-
for (const event of mapped) {
|
|
11297
|
+
for (const event of feedEvents) {
|
|
11089
11298
|
if (event.kind === "agent.message") {
|
|
11090
11299
|
mappedFinalMessage = event.data.message;
|
|
11091
11300
|
}
|
|
11092
11301
|
}
|
|
11093
|
-
safePersist(
|
|
11094
|
-
store,
|
|
11095
|
-
() => store.recordEvent(runtimeEvent, mapped),
|
|
11096
|
-
(message) => output.warn(message),
|
|
11097
|
-
"recordEvent failed"
|
|
11098
|
-
);
|
|
11099
11302
|
});
|
|
11100
11303
|
const unsubscribeDecision = runtime.onDecision(
|
|
11101
11304
|
(eventId, decision) => {
|
|
@@ -11103,20 +11306,17 @@ async function runExec(options) {
|
|
|
11103
11306
|
eventId,
|
|
11104
11307
|
decision
|
|
11105
11308
|
});
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
safePersist(
|
|
11309
|
+
ingestRuntimeDecision(eventId, decision, {
|
|
11310
|
+
mapper,
|
|
11109
11311
|
store,
|
|
11110
|
-
() =>
|
|
11111
|
-
|
|
11112
|
-
"recordFeedEvents failed"
|
|
11113
|
-
);
|
|
11312
|
+
onPersistFailure: (message) => output.warn(message)
|
|
11313
|
+
});
|
|
11114
11314
|
}
|
|
11115
11315
|
);
|
|
11116
11316
|
let timeoutTimer;
|
|
11117
11317
|
if (typeof options.timeoutMs === "number" && options.timeoutMs > 0) {
|
|
11118
11318
|
timeoutTimer = setTimeout(() => {
|
|
11119
|
-
|
|
11319
|
+
latch.register({
|
|
11120
11320
|
kind: "timeout",
|
|
11121
11321
|
message: `Execution timed out after ${options.timeoutMs}ms.`
|
|
11122
11322
|
});
|
|
@@ -11186,30 +11386,30 @@ async function runExec(options) {
|
|
|
11186
11386
|
activeRunId = handle.runId;
|
|
11187
11387
|
const runResult = await handle.result;
|
|
11188
11388
|
cumulativeTokens = runResult.tokens;
|
|
11189
|
-
if (!
|
|
11389
|
+
if (!latch.hasFailure()) {
|
|
11190
11390
|
if (runResult.status === "blocked") {
|
|
11191
|
-
|
|
11391
|
+
latch.register(
|
|
11192
11392
|
workflowFailure(
|
|
11193
11393
|
"blocked",
|
|
11194
11394
|
runResult.stopReason ? `Workflow blocked: ${runResult.stopReason}` : "Workflow blocked."
|
|
11195
11395
|
)
|
|
11196
11396
|
);
|
|
11197
11397
|
} else if (runResult.status === "exhausted") {
|
|
11198
|
-
|
|
11398
|
+
latch.register(
|
|
11199
11399
|
workflowFailure(
|
|
11200
11400
|
"exhausted",
|
|
11201
11401
|
`Workflow reached the maximum of ${workflow?.loop?.maxIterations ?? 0} iterations.`
|
|
11202
11402
|
)
|
|
11203
11403
|
);
|
|
11204
11404
|
} else if (runResult.status === "failed") {
|
|
11205
|
-
|
|
11405
|
+
latch.register({
|
|
11206
11406
|
kind: "process",
|
|
11207
11407
|
message: runResult.stopReason ?? "Workflow run failed."
|
|
11208
11408
|
});
|
|
11209
11409
|
}
|
|
11210
11410
|
}
|
|
11211
11411
|
} catch (error) {
|
|
11212
|
-
|
|
11412
|
+
latch.register({
|
|
11213
11413
|
kind: "process",
|
|
11214
11414
|
message: error instanceof Error ? error.message : String(error)
|
|
11215
11415
|
});
|
|
@@ -11231,39 +11431,26 @@ async function runExec(options) {
|
|
|
11231
11431
|
streamMessage: streamFinalMessage,
|
|
11232
11432
|
mappedMessage: mappedFinalMessage
|
|
11233
11433
|
});
|
|
11234
|
-
if (resolvedFinalMessage.source === "empty" && !
|
|
11434
|
+
if (resolvedFinalMessage.source === "empty" && !latch.hasFailure()) {
|
|
11235
11435
|
const warning = "No assistant message found in stream or hook events; writing empty output.";
|
|
11236
11436
|
output.warn(warning);
|
|
11237
11437
|
output.emitJsonEvent("exec.warning", { message: warning });
|
|
11238
11438
|
}
|
|
11239
|
-
if (!
|
|
11439
|
+
if (!latch.hasFailure() && options.outputLastMessagePath) {
|
|
11240
11440
|
try {
|
|
11241
11441
|
await output.writeLastMessage(
|
|
11242
11442
|
options.outputLastMessagePath,
|
|
11243
11443
|
resolvedFinalMessage.message
|
|
11244
11444
|
);
|
|
11245
11445
|
} catch (error) {
|
|
11246
|
-
|
|
11446
|
+
latch.register({
|
|
11247
11447
|
kind: "output",
|
|
11248
11448
|
message: `Failed writing --output-last-message: ${error instanceof Error ? error.message : String(error)}`
|
|
11249
|
-
};
|
|
11250
|
-
output.error(failure.message);
|
|
11251
|
-
output.emitJsonEvent("exec.error", {
|
|
11252
|
-
kind: failure.kind,
|
|
11253
|
-
message: failure.message
|
|
11254
11449
|
});
|
|
11255
11450
|
}
|
|
11256
11451
|
}
|
|
11257
|
-
|
|
11258
|
-
|
|
11259
|
-
exitCode = EXEC_EXIT_CODE.TIMEOUT;
|
|
11260
|
-
} else if (failure?.kind === "output") {
|
|
11261
|
-
exitCode = EXEC_EXIT_CODE.OUTPUT;
|
|
11262
|
-
} else if (failure?.kind === "workflow") {
|
|
11263
|
-
exitCode = failure.state === "exhausted" ? EXEC_EXIT_CODE.WORKFLOW_EXHAUSTED : EXEC_EXIT_CODE.WORKFLOW_BLOCKED;
|
|
11264
|
-
} else if (failure) {
|
|
11265
|
-
exitCode = EXEC_EXIT_CODE.RUNTIME;
|
|
11266
|
-
}
|
|
11452
|
+
const failure = latch.current();
|
|
11453
|
+
const exitCode = exitCodeFromFailure(failure);
|
|
11267
11454
|
const success = exitCode === EXEC_EXIT_CODE.SUCCESS;
|
|
11268
11455
|
const finalMessage = success ? resolvedFinalMessage.message : null;
|
|
11269
11456
|
if (success && finalMessage !== null) {
|
|
@@ -11471,6 +11658,292 @@ function createInstanceSocketClient(opts) {
|
|
|
11471
11658
|
return { connect: connect2, close, onFrame, onClose, sendRunEvent };
|
|
11472
11659
|
}
|
|
11473
11660
|
|
|
11661
|
+
// src/app/dashboard/runStreamClient.ts
|
|
11662
|
+
import { WebSocket as WebSocket3 } from "ws";
|
|
11663
|
+
var DEFAULT_RECONNECT_DELAYS_MS = [250, 1e3, 2e3, 5e3, 15e3];
|
|
11664
|
+
var DEFAULT_HEARTBEAT_MS2 = 3e4;
|
|
11665
|
+
var DEFAULT_WATCHDOG_MS = 9e4;
|
|
11666
|
+
var DEFAULT_MAX_QUEUE_SIZE = 5e3;
|
|
11667
|
+
var TERMINAL_KINDS = /* @__PURE__ */ new Set(["completion", "error"]);
|
|
11668
|
+
function createRunStreamClient(opts) {
|
|
11669
|
+
const log = opts.log ?? (() => {
|
|
11670
|
+
});
|
|
11671
|
+
const now = opts.now ?? (() => Date.now());
|
|
11672
|
+
const reconnectDelays = opts.reconnectDelaysMs ?? DEFAULT_RECONNECT_DELAYS_MS;
|
|
11673
|
+
const heartbeatMs = opts.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_MS2;
|
|
11674
|
+
const watchdogMs = opts.watchdogTimeoutMs ?? DEFAULT_WATCHDOG_MS;
|
|
11675
|
+
const maxQueueSize = opts.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE;
|
|
11676
|
+
const makeWebSocket = opts.makeWebSocket ?? ((url) => new WebSocket3(url));
|
|
11677
|
+
const setTimer = opts.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
11678
|
+
const clearTimer = opts.clearTimer ?? ((t) => clearTimeout(t));
|
|
11679
|
+
let nextSeq = 1;
|
|
11680
|
+
const queue = [];
|
|
11681
|
+
let ws = null;
|
|
11682
|
+
let connectAttempt = 0;
|
|
11683
|
+
let stopped = false;
|
|
11684
|
+
let serverTerminated = false;
|
|
11685
|
+
let heartbeatTimer = null;
|
|
11686
|
+
let watchdogTimer = null;
|
|
11687
|
+
let reconnectTimer = null;
|
|
11688
|
+
let resumeResolved = false;
|
|
11689
|
+
let firstConnect = null;
|
|
11690
|
+
const terminationWaiters = [];
|
|
11691
|
+
function trimQueueUpTo(lastAckedSeq) {
|
|
11692
|
+
while (queue.length > 0 && queue[0].seq <= lastAckedSeq) {
|
|
11693
|
+
queue.shift();
|
|
11694
|
+
}
|
|
11695
|
+
}
|
|
11696
|
+
function trimQueueUpToExclusive(expectedSeq) {
|
|
11697
|
+
while (queue.length > 0 && queue[0].seq < expectedSeq) {
|
|
11698
|
+
queue.shift();
|
|
11699
|
+
}
|
|
11700
|
+
}
|
|
11701
|
+
function clearTimers() {
|
|
11702
|
+
if (heartbeatTimer) {
|
|
11703
|
+
clearTimer(heartbeatTimer);
|
|
11704
|
+
heartbeatTimer = null;
|
|
11705
|
+
}
|
|
11706
|
+
if (watchdogTimer) {
|
|
11707
|
+
clearTimer(watchdogTimer);
|
|
11708
|
+
watchdogTimer = null;
|
|
11709
|
+
}
|
|
11710
|
+
if (reconnectTimer) {
|
|
11711
|
+
clearTimer(reconnectTimer);
|
|
11712
|
+
reconnectTimer = null;
|
|
11713
|
+
}
|
|
11714
|
+
}
|
|
11715
|
+
function startHeartbeat() {
|
|
11716
|
+
if (heartbeatMs <= 0) return;
|
|
11717
|
+
const tick = () => {
|
|
11718
|
+
if (!ws || ws.readyState !== ws.OPEN) return;
|
|
11719
|
+
try {
|
|
11720
|
+
ws.send(JSON.stringify({ type: "ping", ts: now() }));
|
|
11721
|
+
} catch (err) {
|
|
11722
|
+
log(
|
|
11723
|
+
"warn",
|
|
11724
|
+
`run-stream ping failed: ${err instanceof Error ? err.message : String(err)}`
|
|
11725
|
+
);
|
|
11726
|
+
}
|
|
11727
|
+
heartbeatTimer = setTimer(tick, heartbeatMs);
|
|
11728
|
+
heartbeatTimer.unref?.();
|
|
11729
|
+
};
|
|
11730
|
+
heartbeatTimer = setTimer(tick, heartbeatMs);
|
|
11731
|
+
heartbeatTimer.unref?.();
|
|
11732
|
+
}
|
|
11733
|
+
function bumpWatchdog() {
|
|
11734
|
+
if (watchdogMs <= 0) return;
|
|
11735
|
+
if (watchdogTimer) clearTimer(watchdogTimer);
|
|
11736
|
+
watchdogTimer = setTimer(() => {
|
|
11737
|
+
log(
|
|
11738
|
+
"warn",
|
|
11739
|
+
`run-stream watchdog: no server frames for ${watchdogMs}ms \u2014 recycling socket`
|
|
11740
|
+
);
|
|
11741
|
+
try {
|
|
11742
|
+
ws?.terminate();
|
|
11743
|
+
} catch {
|
|
11744
|
+
}
|
|
11745
|
+
}, watchdogMs);
|
|
11746
|
+
watchdogTimer.unref?.();
|
|
11747
|
+
}
|
|
11748
|
+
function flushQueue() {
|
|
11749
|
+
if (!ws || ws.readyState !== ws.OPEN || !resumeResolved) return;
|
|
11750
|
+
for (const frame of queue) {
|
|
11751
|
+
try {
|
|
11752
|
+
ws.send(JSON.stringify(frame));
|
|
11753
|
+
} catch (err) {
|
|
11754
|
+
log(
|
|
11755
|
+
"warn",
|
|
11756
|
+
`run-stream flush stopped: ${err instanceof Error ? err.message : String(err)}`
|
|
11757
|
+
);
|
|
11758
|
+
return;
|
|
11759
|
+
}
|
|
11760
|
+
}
|
|
11761
|
+
}
|
|
11762
|
+
function notifyTerminated() {
|
|
11763
|
+
serverTerminated = true;
|
|
11764
|
+
const waiters = terminationWaiters.splice(0, terminationWaiters.length);
|
|
11765
|
+
for (const w of waiters) {
|
|
11766
|
+
try {
|
|
11767
|
+
w();
|
|
11768
|
+
} catch {
|
|
11769
|
+
}
|
|
11770
|
+
}
|
|
11771
|
+
}
|
|
11772
|
+
function handleServerFrame(parsed) {
|
|
11773
|
+
bumpWatchdog();
|
|
11774
|
+
switch (parsed.type) {
|
|
11775
|
+
case "resume": {
|
|
11776
|
+
resumeResolved = true;
|
|
11777
|
+
trimQueueUpTo(parsed.lastAckedSeq);
|
|
11778
|
+
if (firstConnect) {
|
|
11779
|
+
const f = firstConnect;
|
|
11780
|
+
firstConnect = null;
|
|
11781
|
+
f.resolve();
|
|
11782
|
+
}
|
|
11783
|
+
if (parsed.terminated) {
|
|
11784
|
+
notifyTerminated();
|
|
11785
|
+
try {
|
|
11786
|
+
ws?.close(1e3, "already_terminated");
|
|
11787
|
+
} catch {
|
|
11788
|
+
}
|
|
11789
|
+
return;
|
|
11790
|
+
}
|
|
11791
|
+
flushQueue();
|
|
11792
|
+
return;
|
|
11793
|
+
}
|
|
11794
|
+
case "ack": {
|
|
11795
|
+
trimQueueUpTo(parsed.seq);
|
|
11796
|
+
return;
|
|
11797
|
+
}
|
|
11798
|
+
case "pong":
|
|
11799
|
+
return;
|
|
11800
|
+
case "error": {
|
|
11801
|
+
if (parsed.code === "sequence_gap" && typeof parsed.expected === "number") {
|
|
11802
|
+
trimQueueUpToExclusive(parsed.expected);
|
|
11803
|
+
flushQueue();
|
|
11804
|
+
log(
|
|
11805
|
+
"warn",
|
|
11806
|
+
`run-stream sequence_gap: server expected seq=${parsed.expected}; replayed ${queue.length} frames`
|
|
11807
|
+
);
|
|
11808
|
+
return;
|
|
11809
|
+
}
|
|
11810
|
+
log(
|
|
11811
|
+
"warn",
|
|
11812
|
+
`run-stream server error: ${parsed.code}${parsed.message ? ` ${parsed.message}` : ""}`
|
|
11813
|
+
);
|
|
11814
|
+
return;
|
|
11815
|
+
}
|
|
11816
|
+
}
|
|
11817
|
+
}
|
|
11818
|
+
function nextReconnectDelay() {
|
|
11819
|
+
if (reconnectDelays.length === 0) return 0;
|
|
11820
|
+
const idx = Math.min(connectAttempt, reconnectDelays.length - 1);
|
|
11821
|
+
const ms = reconnectDelays[idx] ?? 0;
|
|
11822
|
+
connectAttempt += 1;
|
|
11823
|
+
return ms;
|
|
11824
|
+
}
|
|
11825
|
+
function scheduleReconnect() {
|
|
11826
|
+
if (stopped || serverTerminated) return;
|
|
11827
|
+
const delayMs = nextReconnectDelay();
|
|
11828
|
+
log("info", `run-stream reconnect scheduled in ${delayMs}ms`);
|
|
11829
|
+
reconnectTimer = setTimer(() => {
|
|
11830
|
+
reconnectTimer = null;
|
|
11831
|
+
void openSocket();
|
|
11832
|
+
}, delayMs);
|
|
11833
|
+
reconnectTimer.unref?.();
|
|
11834
|
+
}
|
|
11835
|
+
async function openSocket() {
|
|
11836
|
+
if (stopped || serverTerminated) return;
|
|
11837
|
+
resumeResolved = false;
|
|
11838
|
+
const next = makeWebSocket(opts.wsUrl);
|
|
11839
|
+
ws = next;
|
|
11840
|
+
next.on("open", () => {
|
|
11841
|
+
connectAttempt = 0;
|
|
11842
|
+
startHeartbeat();
|
|
11843
|
+
bumpWatchdog();
|
|
11844
|
+
});
|
|
11845
|
+
next.on("message", (data) => {
|
|
11846
|
+
let parsed;
|
|
11847
|
+
try {
|
|
11848
|
+
parsed = JSON.parse(String(data));
|
|
11849
|
+
} catch {
|
|
11850
|
+
log("warn", "run-stream received non-JSON frame");
|
|
11851
|
+
return;
|
|
11852
|
+
}
|
|
11853
|
+
handleServerFrame(parsed);
|
|
11854
|
+
});
|
|
11855
|
+
next.on("close", (code, reasonBuf) => {
|
|
11856
|
+
if (next !== ws) return;
|
|
11857
|
+
ws = null;
|
|
11858
|
+
clearTimers();
|
|
11859
|
+
const reason = reasonBuf?.toString?.() || "closed";
|
|
11860
|
+
if (code === 1e3 && reason === "run_terminated") {
|
|
11861
|
+
notifyTerminated();
|
|
11862
|
+
return;
|
|
11863
|
+
}
|
|
11864
|
+
if (firstConnect) {
|
|
11865
|
+
const f = firstConnect;
|
|
11866
|
+
firstConnect = null;
|
|
11867
|
+
f.reject(
|
|
11868
|
+
new Error(`run-stream initial connect failed: ${reason} (${code})`)
|
|
11869
|
+
);
|
|
11870
|
+
return;
|
|
11871
|
+
}
|
|
11872
|
+
scheduleReconnect();
|
|
11873
|
+
});
|
|
11874
|
+
next.on("error", (err) => {
|
|
11875
|
+
log(
|
|
11876
|
+
"warn",
|
|
11877
|
+
`run-stream socket error: ${err instanceof Error ? err.message : String(err)}`
|
|
11878
|
+
);
|
|
11879
|
+
});
|
|
11880
|
+
}
|
|
11881
|
+
return {
|
|
11882
|
+
async connect() {
|
|
11883
|
+
if (firstConnect) {
|
|
11884
|
+
throw new Error("run-stream client already connecting");
|
|
11885
|
+
}
|
|
11886
|
+
await new Promise((resolve, reject) => {
|
|
11887
|
+
firstConnect = { resolve, reject };
|
|
11888
|
+
void openSocket();
|
|
11889
|
+
});
|
|
11890
|
+
},
|
|
11891
|
+
sendEvent(input) {
|
|
11892
|
+
const seq = nextSeq++;
|
|
11893
|
+
const frame = {
|
|
11894
|
+
seq,
|
|
11895
|
+
ts: input.ts,
|
|
11896
|
+
kind: input.kind,
|
|
11897
|
+
payload: input.payload ?? null
|
|
11898
|
+
};
|
|
11899
|
+
if (queue.length >= maxQueueSize) {
|
|
11900
|
+
log(
|
|
11901
|
+
"warn",
|
|
11902
|
+
`run-stream queue at cap (${maxQueueSize}); dropping oldest unacked frame seq=${queue[0].seq}`
|
|
11903
|
+
);
|
|
11904
|
+
queue.shift();
|
|
11905
|
+
}
|
|
11906
|
+
queue.push(frame);
|
|
11907
|
+
if (ws && ws.readyState === ws.OPEN && resumeResolved) {
|
|
11908
|
+
try {
|
|
11909
|
+
ws.send(JSON.stringify(frame));
|
|
11910
|
+
} catch (err) {
|
|
11911
|
+
log(
|
|
11912
|
+
"warn",
|
|
11913
|
+
`run-stream send failed (will replay): ${err instanceof Error ? err.message : String(err)}`
|
|
11914
|
+
);
|
|
11915
|
+
}
|
|
11916
|
+
}
|
|
11917
|
+
if (TERMINAL_KINDS.has(frame.kind)) {
|
|
11918
|
+
}
|
|
11919
|
+
return seq;
|
|
11920
|
+
},
|
|
11921
|
+
whenTerminated() {
|
|
11922
|
+
if (serverTerminated) return Promise.resolve();
|
|
11923
|
+
return new Promise((resolve) => {
|
|
11924
|
+
terminationWaiters.push(resolve);
|
|
11925
|
+
});
|
|
11926
|
+
},
|
|
11927
|
+
async close(reason = "client_close") {
|
|
11928
|
+
stopped = true;
|
|
11929
|
+
clearTimers();
|
|
11930
|
+
const current = ws;
|
|
11931
|
+
ws = null;
|
|
11932
|
+
if (current) {
|
|
11933
|
+
try {
|
|
11934
|
+
current.close(1e3, reason);
|
|
11935
|
+
} catch {
|
|
11936
|
+
try {
|
|
11937
|
+
current.terminate();
|
|
11938
|
+
} catch {
|
|
11939
|
+
}
|
|
11940
|
+
}
|
|
11941
|
+
}
|
|
11942
|
+
notifyTerminated();
|
|
11943
|
+
}
|
|
11944
|
+
};
|
|
11945
|
+
}
|
|
11946
|
+
|
|
11474
11947
|
// src/app/dashboard/remoteRunExecutor.ts
|
|
11475
11948
|
function parseRunSpec(value) {
|
|
11476
11949
|
if (typeof value !== "object" || value === null) return null;
|
|
@@ -11479,6 +11952,8 @@ function parseRunSpec(value) {
|
|
|
11479
11952
|
if (typeof prompt !== "string" || prompt.trim().length === 0) return null;
|
|
11480
11953
|
const env = obj["env"];
|
|
11481
11954
|
const workflow = obj["workflow"];
|
|
11955
|
+
const callbackWsUrl = obj["callbackWsUrl"];
|
|
11956
|
+
const callbackToken = obj["callbackToken"];
|
|
11482
11957
|
return {
|
|
11483
11958
|
prompt,
|
|
11484
11959
|
sessionId: typeof obj["sessionId"] === "string" && obj["sessionId"].length > 0 ? obj["sessionId"] : void 0,
|
|
@@ -11489,7 +11964,9 @@ function parseRunSpec(value) {
|
|
|
11489
11964
|
(entry) => typeof entry[1] === "string"
|
|
11490
11965
|
)
|
|
11491
11966
|
) : void 0,
|
|
11492
|
-
timeoutSec: typeof obj["timeoutSec"] === "number" && Number.isFinite(obj["timeoutSec"]) ? obj["timeoutSec"] : void 0
|
|
11967
|
+
timeoutSec: typeof obj["timeoutSec"] === "number" && Number.isFinite(obj["timeoutSec"]) ? obj["timeoutSec"] : void 0,
|
|
11968
|
+
callbackWsUrl: typeof callbackWsUrl === "string" && callbackWsUrl.length > 0 ? callbackWsUrl : void 0,
|
|
11969
|
+
callbackToken: typeof callbackToken === "string" && callbackToken.length > 0 ? callbackToken : void 0
|
|
11493
11970
|
};
|
|
11494
11971
|
}
|
|
11495
11972
|
function workflowNameFromRef(ref) {
|
|
@@ -11542,30 +12019,80 @@ async function executeRemoteAssignment({
|
|
|
11542
12019
|
runExecFn = runExec,
|
|
11543
12020
|
bootstrapRuntimeConfigFn = bootstrapRuntimeConfig,
|
|
11544
12021
|
now = Date.now,
|
|
11545
|
-
abortSignal
|
|
12022
|
+
abortSignal,
|
|
12023
|
+
createRunStreamClientFn = createRunStreamClient,
|
|
12024
|
+
runStreamConnectTimeoutMs = 5e3
|
|
11546
12025
|
}) {
|
|
11547
|
-
let seq = 0;
|
|
11548
12026
|
let lastTerminalFailureMessage = null;
|
|
11549
12027
|
let deferredFailedCompletion = null;
|
|
12028
|
+
const spec = parseRunSpec(frame.runSpec);
|
|
12029
|
+
let runStream = null;
|
|
12030
|
+
if (spec?.callbackWsUrl && spec.callbackToken) {
|
|
12031
|
+
const connect2 = createRunStreamClientFn({
|
|
12032
|
+
wsUrl: spec.callbackWsUrl,
|
|
12033
|
+
token: spec.callbackToken,
|
|
12034
|
+
log: (level, message) => log(level, `run-stream[${frame.runId}]: ${message}`),
|
|
12035
|
+
now
|
|
12036
|
+
});
|
|
12037
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
12038
|
+
const t = setTimeout(() => resolve("timeout"), runStreamConnectTimeoutMs);
|
|
12039
|
+
t.unref?.();
|
|
12040
|
+
});
|
|
12041
|
+
try {
|
|
12042
|
+
const result = await Promise.race([
|
|
12043
|
+
connect2.connect().then(() => "connected"),
|
|
12044
|
+
timeoutPromise
|
|
12045
|
+
]);
|
|
12046
|
+
if (result === "connected") {
|
|
12047
|
+
runStream = connect2;
|
|
12048
|
+
} else {
|
|
12049
|
+
log(
|
|
12050
|
+
"warn",
|
|
12051
|
+
`run-stream[${frame.runId}]: connect timed out after ${runStreamConnectTimeoutMs}ms; falling back to instance-socket relay`
|
|
12052
|
+
);
|
|
12053
|
+
void connect2.close("connect_timeout");
|
|
12054
|
+
}
|
|
12055
|
+
} catch (err) {
|
|
12056
|
+
log(
|
|
12057
|
+
"warn",
|
|
12058
|
+
`run-stream[${frame.runId}]: connect failed (${err instanceof Error ? err.message : String(err)}); falling back to instance-socket relay`
|
|
12059
|
+
);
|
|
12060
|
+
}
|
|
12061
|
+
}
|
|
12062
|
+
let legacySeq = 0;
|
|
11550
12063
|
const send = (kind, payload, ts = now()) => {
|
|
11551
|
-
seq += 1;
|
|
11552
12064
|
if (kind === "error" && typeof payload === "object" && payload !== null && typeof payload.message === "string") {
|
|
11553
12065
|
lastTerminalFailureMessage = payload.message;
|
|
11554
12066
|
}
|
|
12067
|
+
if (runStream) {
|
|
12068
|
+
runStream.sendEvent({ ts, kind, payload });
|
|
12069
|
+
return;
|
|
12070
|
+
}
|
|
12071
|
+
legacySeq += 1;
|
|
11555
12072
|
client.sendRunEvent({
|
|
11556
12073
|
runId: frame.runId,
|
|
11557
|
-
seq,
|
|
12074
|
+
seq: legacySeq,
|
|
11558
12075
|
ts,
|
|
11559
12076
|
kind,
|
|
11560
12077
|
payload
|
|
11561
12078
|
});
|
|
11562
12079
|
};
|
|
11563
12080
|
send("progress", { message: "assignment received" });
|
|
11564
|
-
const
|
|
12081
|
+
const closeRunStream = async (reason) => {
|
|
12082
|
+
if (!runStream) return;
|
|
12083
|
+
const drainTimeout = new Promise((resolve) => {
|
|
12084
|
+
const t = setTimeout(() => resolve(), 1e4);
|
|
12085
|
+
t.unref?.();
|
|
12086
|
+
});
|
|
12087
|
+
await Promise.race([runStream.whenTerminated(), drainTimeout]);
|
|
12088
|
+
await runStream.close(reason);
|
|
12089
|
+
runStream = null;
|
|
12090
|
+
};
|
|
11565
12091
|
if (!spec) {
|
|
11566
12092
|
send("error", {
|
|
11567
12093
|
message: "remote assignment missing prompt"
|
|
11568
12094
|
});
|
|
12095
|
+
await closeRunStream("done");
|
|
11569
12096
|
return;
|
|
11570
12097
|
}
|
|
11571
12098
|
const projectDir = spec.projectDir ?? fallbackProjectDir;
|
|
@@ -11581,6 +12108,7 @@ async function executeRemoteAssignment({
|
|
|
11581
12108
|
send("error", {
|
|
11582
12109
|
message: err instanceof Error ? err.message : String(err)
|
|
11583
12110
|
});
|
|
12111
|
+
await closeRunStream("done");
|
|
11584
12112
|
return;
|
|
11585
12113
|
}
|
|
11586
12114
|
for (const warning of runtimeConfig.warnings) {
|
|
@@ -11678,11 +12206,13 @@ async function executeRemoteAssignment({
|
|
|
11678
12206
|
send("error", {
|
|
11679
12207
|
message: err instanceof Error ? err.message : String(err)
|
|
11680
12208
|
});
|
|
12209
|
+
} finally {
|
|
12210
|
+
await closeRunStream("done");
|
|
11681
12211
|
}
|
|
11682
12212
|
}
|
|
11683
12213
|
|
|
11684
12214
|
// src/app/dashboard/runtimeDaemon.ts
|
|
11685
|
-
var
|
|
12215
|
+
var DEFAULT_RECONNECT_DELAYS_MS2 = [1e3, 2e3, 5e3, 1e4, 3e4];
|
|
11686
12216
|
var DEFAULT_MAX_CONCURRENT_RUNS = 1;
|
|
11687
12217
|
var DEFAULT_REFRESH_LEAD_SEC = 60;
|
|
11688
12218
|
var DEFAULT_REFRESH_FAILURE_LIMIT = 5;
|
|
@@ -11708,7 +12238,7 @@ async function runDashboardRuntimeDaemon(options = {}) {
|
|
|
11708
12238
|
const projectDir = options.projectDir ?? process.cwd();
|
|
11709
12239
|
const log = options.log ?? (() => {
|
|
11710
12240
|
});
|
|
11711
|
-
const reconnectDelays = options.reconnectDelaysMs ??
|
|
12241
|
+
const reconnectDelays = options.reconnectDelaysMs ?? DEFAULT_RECONNECT_DELAYS_MS2;
|
|
11712
12242
|
const maxConcurrentRuns = options.maxConcurrentRuns ?? DEFAULT_MAX_CONCURRENT_RUNS;
|
|
11713
12243
|
const refreshLeadSec = options.refreshLeadSec ?? DEFAULT_REFRESH_LEAD_SEC;
|
|
11714
12244
|
const refreshFailureLimit = options.refreshFailureLimit ?? DEFAULT_REFRESH_FAILURE_LIMIT;
|
|
@@ -12094,6 +12624,35 @@ function isProcessAlive(pid) {
|
|
|
12094
12624
|
// src/infra/daemon/udsIpc.ts
|
|
12095
12625
|
import fs22 from "fs";
|
|
12096
12626
|
import net2 from "net";
|
|
12627
|
+
|
|
12628
|
+
// src/infra/daemon/udsFrameCodec.ts
|
|
12629
|
+
var DEFAULT_MAX_FRAME_BYTES = 1e6;
|
|
12630
|
+
function tryReadFrame(buffer, maxBodyBytes = DEFAULT_MAX_FRAME_BYTES) {
|
|
12631
|
+
const newlineIdx = buffer.indexOf(10);
|
|
12632
|
+
if (newlineIdx < 0) return null;
|
|
12633
|
+
const lenStr = buffer.subarray(0, newlineIdx).toString("utf-8");
|
|
12634
|
+
const len = Number.parseInt(lenStr, 10);
|
|
12635
|
+
if (!Number.isFinite(len) || len < 0 || len > maxBodyBytes) {
|
|
12636
|
+
return {
|
|
12637
|
+
ok: false,
|
|
12638
|
+
error: `uds bad framing header: ${JSON.stringify(lenStr.slice(0, 32))}`,
|
|
12639
|
+
rest: buffer.subarray(newlineIdx + 1)
|
|
12640
|
+
};
|
|
12641
|
+
}
|
|
12642
|
+
if (buffer.length < newlineIdx + 1 + len) return null;
|
|
12643
|
+
const body = buffer.subarray(newlineIdx + 1, newlineIdx + 1 + len);
|
|
12644
|
+
const rest = buffer.subarray(newlineIdx + 1 + len);
|
|
12645
|
+
return { ok: true, body, rest };
|
|
12646
|
+
}
|
|
12647
|
+
function writeFrame(socket, value) {
|
|
12648
|
+
const body = JSON.stringify(value);
|
|
12649
|
+
const buf = Buffer.from(body, "utf-8");
|
|
12650
|
+
socket.write(`${buf.length}
|
|
12651
|
+
`);
|
|
12652
|
+
socket.write(buf);
|
|
12653
|
+
}
|
|
12654
|
+
|
|
12655
|
+
// src/infra/daemon/udsIpc.ts
|
|
12097
12656
|
async function startUdsServer(socketPath, handler, log) {
|
|
12098
12657
|
await unlinkStaleSocket(socketPath);
|
|
12099
12658
|
const server = net2.createServer((socket) => {
|
|
@@ -12142,21 +12701,15 @@ async function handleConnection(socket, handler, log) {
|
|
|
12142
12701
|
});
|
|
12143
12702
|
async function drain() {
|
|
12144
12703
|
for (; ; ) {
|
|
12145
|
-
const
|
|
12146
|
-
if (
|
|
12147
|
-
|
|
12148
|
-
|
|
12149
|
-
if (!Number.isFinite(len) || len < 0 || len > 1e6) {
|
|
12150
|
-
log?.(
|
|
12151
|
-
"warn",
|
|
12152
|
-
`uds bad framing header: ${JSON.stringify(lenStr.slice(0, 32))}`
|
|
12153
|
-
);
|
|
12704
|
+
const frame = tryReadFrame(buffer);
|
|
12705
|
+
if (frame === null) return;
|
|
12706
|
+
if (!frame.ok) {
|
|
12707
|
+
log?.("warn", frame.error);
|
|
12154
12708
|
socket.destroy();
|
|
12155
12709
|
return;
|
|
12156
12710
|
}
|
|
12157
|
-
|
|
12158
|
-
|
|
12159
|
-
buffer = buffer.subarray(newlineIdx + 1 + len);
|
|
12711
|
+
const { body } = frame;
|
|
12712
|
+
buffer = frame.rest;
|
|
12160
12713
|
let parsed;
|
|
12161
12714
|
try {
|
|
12162
12715
|
parsed = JSON.parse(body.toString("utf-8"));
|
|
@@ -12178,13 +12731,6 @@ async function handleConnection(socket, handler, log) {
|
|
|
12178
12731
|
}
|
|
12179
12732
|
}
|
|
12180
12733
|
}
|
|
12181
|
-
function writeFrame(socket, response) {
|
|
12182
|
-
const body = JSON.stringify(response);
|
|
12183
|
-
const buf = Buffer.from(body, "utf-8");
|
|
12184
|
-
socket.write(`${buf.length}
|
|
12185
|
-
`);
|
|
12186
|
-
socket.write(buf);
|
|
12187
|
-
}
|
|
12188
12734
|
async function sendUdsRequest(socketPath, request, options = {}) {
|
|
12189
12735
|
const timeoutMs = options.timeoutMs ?? 5e3;
|
|
12190
12736
|
return await new Promise((resolve, reject) => {
|
|
@@ -12209,30 +12755,20 @@ async function sendUdsRequest(socketPath, request, options = {}) {
|
|
|
12209
12755
|
action();
|
|
12210
12756
|
};
|
|
12211
12757
|
socket.on("connect", () => {
|
|
12212
|
-
|
|
12213
|
-
const buf = Buffer.from(body, "utf-8");
|
|
12214
|
-
socket.write(`${buf.length}
|
|
12215
|
-
`);
|
|
12216
|
-
socket.write(buf);
|
|
12758
|
+
writeFrame(socket, request);
|
|
12217
12759
|
});
|
|
12218
12760
|
socket.on("data", (chunk) => {
|
|
12219
12761
|
const chunkBuf = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
12220
12762
|
buffer = Buffer.concat([buffer, chunkBuf]);
|
|
12221
|
-
const
|
|
12222
|
-
if (
|
|
12223
|
-
|
|
12224
|
-
buffer.subarray(0, newlineIdx).toString("utf-8"),
|
|
12225
|
-
10
|
|
12226
|
-
);
|
|
12227
|
-
if (!Number.isFinite(len)) {
|
|
12763
|
+
const frame = tryReadFrame(buffer);
|
|
12764
|
+
if (frame === null) return;
|
|
12765
|
+
if (!frame.ok) {
|
|
12228
12766
|
finish(() => reject(new Error("uds reply malformed")));
|
|
12229
12767
|
return;
|
|
12230
12768
|
}
|
|
12231
|
-
if (buffer.length < newlineIdx + 1 + len) return;
|
|
12232
|
-
const body = buffer.subarray(newlineIdx + 1, newlineIdx + 1 + len).toString("utf-8");
|
|
12233
12769
|
let parsed;
|
|
12234
12770
|
try {
|
|
12235
|
-
parsed = JSON.parse(body);
|
|
12771
|
+
parsed = JSON.parse(frame.body.toString("utf-8"));
|
|
12236
12772
|
} catch (err) {
|
|
12237
12773
|
finish(
|
|
12238
12774
|
() => reject(
|
|
@@ -12301,7 +12837,8 @@ export {
|
|
|
12301
12837
|
extractPermissionSnapshot,
|
|
12302
12838
|
isSubagentTool,
|
|
12303
12839
|
createFeedMapper,
|
|
12304
|
-
|
|
12840
|
+
ingestRuntimeEvent,
|
|
12841
|
+
ingestRuntimeDecision,
|
|
12305
12842
|
generateId,
|
|
12306
12843
|
createSessionStore,
|
|
12307
12844
|
sessionsDir,
|
|
@@ -12358,4 +12895,4 @@ export {
|
|
|
12358
12895
|
startUdsServer,
|
|
12359
12896
|
sendUdsRequest
|
|
12360
12897
|
};
|
|
12361
|
-
//# sourceMappingURL=chunk-
|
|
12898
|
+
//# sourceMappingURL=chunk-JAPBSL7D.js.map
|