@drisp/cli 0.4.2 → 0.4.5
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 +1045 -970
- package/dist/{chunk-GE6PPB6Z.js → chunk-5VK2ZMVV.js} +96 -119
- package/dist/{chunk-6TJHAUNB.js → chunk-M44KEGM7.js} +15 -3
- package/dist/{chunk-WHELLVBL.js → chunk-PJUDHH4R.js} +1498 -834
- package/dist/cli.js +1205 -1153
- 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;
|
|
7942
8054
|
}
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
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);
|
|
8096
|
+
}
|
|
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,100 +9205,260 @@ 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
|
-
return results;
|
|
9216
|
+
return results;
|
|
9217
|
+
}
|
|
9218
|
+
function mapDecision(requestId, decision) {
|
|
9219
|
+
const consumed = decisionCorrelation.consumeForDecision(requestId);
|
|
9220
|
+
if (!consumed) return null;
|
|
9221
|
+
const { parentEventId, originalKind } = consumed;
|
|
9222
|
+
function makeDecisionEvent(kind, data) {
|
|
9223
|
+
const s = runLifecycle.allocateSeq();
|
|
9224
|
+
const runId = runLifecycle.getRunId();
|
|
9225
|
+
const session = runLifecycle.getSession();
|
|
9226
|
+
const fe = {
|
|
9227
|
+
event_id: `${runId}:E${s}`,
|
|
9228
|
+
seq: s,
|
|
9229
|
+
ts: Date.now(),
|
|
9230
|
+
session_id: session?.session_id ?? "unknown",
|
|
9231
|
+
run_id: runId,
|
|
9232
|
+
kind,
|
|
9233
|
+
level: "info",
|
|
9234
|
+
actor_id: decision.source === "user" ? "user" : "system",
|
|
9235
|
+
cause: {
|
|
9236
|
+
parent_event_id: parentEventId,
|
|
9237
|
+
hook_request_id: requestId
|
|
9238
|
+
},
|
|
9239
|
+
title: "",
|
|
9240
|
+
data
|
|
9241
|
+
};
|
|
9242
|
+
fe.title = generateTitle(fe);
|
|
9243
|
+
return fe;
|
|
9244
|
+
}
|
|
9245
|
+
if (originalKind === "permission.request") {
|
|
9246
|
+
let data;
|
|
9247
|
+
if (decision.source === "timeout") {
|
|
9248
|
+
data = { decision_type: "no_opinion", reason: "timeout" };
|
|
9249
|
+
} else if (decision.type === "passthrough") {
|
|
9250
|
+
data = { decision_type: "no_opinion", reason: decision.source };
|
|
9251
|
+
} else if (decision.intent?.kind === "permission_allow") {
|
|
9252
|
+
data = { decision_type: "allow" };
|
|
9253
|
+
} else if (decision.intent?.kind === "permission_deny") {
|
|
9254
|
+
data = {
|
|
9255
|
+
decision_type: "deny",
|
|
9256
|
+
message: decision.intent.reason
|
|
9257
|
+
};
|
|
9258
|
+
} else {
|
|
9259
|
+
data = { decision_type: "no_opinion", reason: "unknown" };
|
|
9260
|
+
}
|
|
9261
|
+
return makeDecisionEvent("permission.decision", data);
|
|
9262
|
+
}
|
|
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 };
|
|
9190
9390
|
}
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
parent_event_id: parentEventId,
|
|
9210
|
-
hook_request_id: requestId
|
|
9211
|
-
},
|
|
9212
|
-
title: "",
|
|
9213
|
-
data
|
|
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
|
+
}
|
|
9214
9409
|
};
|
|
9215
|
-
fe.title = generateTitle(fe);
|
|
9216
|
-
return fe;
|
|
9217
|
-
}
|
|
9218
|
-
if (originalKind === "permission.request") {
|
|
9219
|
-
let data;
|
|
9220
|
-
if (decision.source === "timeout") {
|
|
9221
|
-
data = { decision_type: "no_opinion", reason: "timeout" };
|
|
9222
|
-
} else if (decision.type === "passthrough") {
|
|
9223
|
-
data = { decision_type: "no_opinion", reason: decision.source };
|
|
9224
|
-
} else if (decision.intent?.kind === "permission_allow") {
|
|
9225
|
-
data = { decision_type: "allow" };
|
|
9226
|
-
} else if (decision.intent?.kind === "permission_deny") {
|
|
9227
|
-
data = {
|
|
9228
|
-
decision_type: "deny",
|
|
9229
|
-
message: decision.intent.reason
|
|
9230
|
-
};
|
|
9231
|
-
} else {
|
|
9232
|
-
data = { decision_type: "no_opinion", reason: "unknown" };
|
|
9233
|
-
}
|
|
9234
|
-
return makeDecisionEvent("permission.decision", data);
|
|
9235
9410
|
}
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
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" };
|
|
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) {
|
|
@@ -11421,54 +11608,335 @@ function createInstanceSocketClient(opts) {
|
|
|
11421
11608
|
next.terminate();
|
|
11422
11609
|
} catch {
|
|
11423
11610
|
}
|
|
11424
|
-
throw err;
|
|
11611
|
+
throw err;
|
|
11612
|
+
}
|
|
11613
|
+
ws = next;
|
|
11614
|
+
startHeartbeat();
|
|
11615
|
+
next.on("message", (data) => {
|
|
11616
|
+
let parsed;
|
|
11617
|
+
try {
|
|
11618
|
+
parsed = JSON.parse(String(data));
|
|
11619
|
+
} catch (err) {
|
|
11620
|
+
log(
|
|
11621
|
+
"warn",
|
|
11622
|
+
`instance socket frame parse failed: ${err instanceof Error ? err.message : String(err)}`
|
|
11623
|
+
);
|
|
11624
|
+
return;
|
|
11625
|
+
}
|
|
11626
|
+
handleFrame(parsed);
|
|
11627
|
+
});
|
|
11628
|
+
next.on("close", (_code, reasonBuf) => {
|
|
11629
|
+
if (next !== ws) return;
|
|
11630
|
+
ws = null;
|
|
11631
|
+
const reason = reasonBuf.toString() || "closed";
|
|
11632
|
+
emitClose(reason);
|
|
11633
|
+
});
|
|
11634
|
+
next.on("error", (err) => {
|
|
11635
|
+
log("warn", `instance socket error: ${err.message}`);
|
|
11636
|
+
});
|
|
11637
|
+
}
|
|
11638
|
+
function close(reason) {
|
|
11639
|
+
stopHeartbeat();
|
|
11640
|
+
if (ws) {
|
|
11641
|
+
try {
|
|
11642
|
+
ws.close(1e3, reason ?? "client closed");
|
|
11643
|
+
} catch {
|
|
11644
|
+
ws.terminate();
|
|
11645
|
+
}
|
|
11646
|
+
}
|
|
11647
|
+
ws = null;
|
|
11648
|
+
}
|
|
11649
|
+
function onFrame(handler) {
|
|
11650
|
+
frameHandlers.add(handler);
|
|
11651
|
+
}
|
|
11652
|
+
function onClose(handler) {
|
|
11653
|
+
closeHandlers.add(handler);
|
|
11654
|
+
}
|
|
11655
|
+
function sendRunEvent(event) {
|
|
11656
|
+
send({ type: "run_event", ...event });
|
|
11657
|
+
}
|
|
11658
|
+
return { connect: connect2, close, onFrame, onClose, sendRunEvent };
|
|
11659
|
+
}
|
|
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
|
+
function createRunStreamClient(opts) {
|
|
11668
|
+
const log = opts.log ?? (() => {
|
|
11669
|
+
});
|
|
11670
|
+
const now = opts.now ?? (() => Date.now());
|
|
11671
|
+
const reconnectDelays = opts.reconnectDelaysMs ?? DEFAULT_RECONNECT_DELAYS_MS;
|
|
11672
|
+
const heartbeatMs = opts.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_MS2;
|
|
11673
|
+
const watchdogMs = opts.watchdogTimeoutMs ?? DEFAULT_WATCHDOG_MS;
|
|
11674
|
+
const maxQueueSize = opts.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE;
|
|
11675
|
+
const makeWebSocket = opts.makeWebSocket ?? ((url) => new WebSocket3(url));
|
|
11676
|
+
const setTimer = opts.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
11677
|
+
const clearTimer = opts.clearTimer ?? ((t) => clearTimeout(t));
|
|
11678
|
+
let nextSeq = 1;
|
|
11679
|
+
const queue = [];
|
|
11680
|
+
let ws = null;
|
|
11681
|
+
let connectAttempt = 0;
|
|
11682
|
+
let stopped = false;
|
|
11683
|
+
let serverTerminated = false;
|
|
11684
|
+
let heartbeatTimer = null;
|
|
11685
|
+
let watchdogTimer = null;
|
|
11686
|
+
let reconnectTimer = null;
|
|
11687
|
+
let resumeResolved = false;
|
|
11688
|
+
let firstConnect = null;
|
|
11689
|
+
const terminationWaiters = [];
|
|
11690
|
+
function trimQueueUpTo(lastAckedSeq) {
|
|
11691
|
+
while (queue.length > 0 && queue[0].seq <= lastAckedSeq) {
|
|
11692
|
+
queue.shift();
|
|
11693
|
+
}
|
|
11694
|
+
}
|
|
11695
|
+
function trimQueueUpToExclusive(expectedSeq) {
|
|
11696
|
+
while (queue.length > 0 && queue[0].seq < expectedSeq) {
|
|
11697
|
+
queue.shift();
|
|
11698
|
+
}
|
|
11699
|
+
}
|
|
11700
|
+
function clearTimers() {
|
|
11701
|
+
if (heartbeatTimer) {
|
|
11702
|
+
clearTimer(heartbeatTimer);
|
|
11703
|
+
heartbeatTimer = null;
|
|
11704
|
+
}
|
|
11705
|
+
if (watchdogTimer) {
|
|
11706
|
+
clearTimer(watchdogTimer);
|
|
11707
|
+
watchdogTimer = null;
|
|
11708
|
+
}
|
|
11709
|
+
if (reconnectTimer) {
|
|
11710
|
+
clearTimer(reconnectTimer);
|
|
11711
|
+
reconnectTimer = null;
|
|
11712
|
+
}
|
|
11713
|
+
}
|
|
11714
|
+
function startHeartbeat() {
|
|
11715
|
+
if (heartbeatMs <= 0) return;
|
|
11716
|
+
const tick = () => {
|
|
11717
|
+
if (!ws || ws.readyState !== ws.OPEN) return;
|
|
11718
|
+
try {
|
|
11719
|
+
ws.send(JSON.stringify({ type: "ping", ts: now() }));
|
|
11720
|
+
} catch (err) {
|
|
11721
|
+
log(
|
|
11722
|
+
"warn",
|
|
11723
|
+
`run-stream ping failed: ${err instanceof Error ? err.message : String(err)}`
|
|
11724
|
+
);
|
|
11725
|
+
}
|
|
11726
|
+
heartbeatTimer = setTimer(tick, heartbeatMs);
|
|
11727
|
+
heartbeatTimer.unref?.();
|
|
11728
|
+
};
|
|
11729
|
+
heartbeatTimer = setTimer(tick, heartbeatMs);
|
|
11730
|
+
heartbeatTimer.unref?.();
|
|
11731
|
+
}
|
|
11732
|
+
function bumpWatchdog() {
|
|
11733
|
+
if (watchdogMs <= 0) return;
|
|
11734
|
+
if (watchdogTimer) clearTimer(watchdogTimer);
|
|
11735
|
+
watchdogTimer = setTimer(() => {
|
|
11736
|
+
log(
|
|
11737
|
+
"warn",
|
|
11738
|
+
`run-stream watchdog: no server frames for ${watchdogMs}ms \u2014 recycling socket`
|
|
11739
|
+
);
|
|
11740
|
+
try {
|
|
11741
|
+
ws?.terminate();
|
|
11742
|
+
} catch {
|
|
11743
|
+
}
|
|
11744
|
+
}, watchdogMs);
|
|
11745
|
+
watchdogTimer.unref?.();
|
|
11746
|
+
}
|
|
11747
|
+
function flushQueue() {
|
|
11748
|
+
if (!ws || ws.readyState !== ws.OPEN || !resumeResolved) return;
|
|
11749
|
+
for (const frame of queue) {
|
|
11750
|
+
try {
|
|
11751
|
+
ws.send(JSON.stringify(frame));
|
|
11752
|
+
} catch (err) {
|
|
11753
|
+
log(
|
|
11754
|
+
"warn",
|
|
11755
|
+
`run-stream flush stopped: ${err instanceof Error ? err.message : String(err)}`
|
|
11756
|
+
);
|
|
11757
|
+
return;
|
|
11758
|
+
}
|
|
11759
|
+
}
|
|
11760
|
+
}
|
|
11761
|
+
function notifyTerminated() {
|
|
11762
|
+
serverTerminated = true;
|
|
11763
|
+
const waiters = terminationWaiters.splice(0, terminationWaiters.length);
|
|
11764
|
+
for (const w of waiters) {
|
|
11765
|
+
try {
|
|
11766
|
+
w();
|
|
11767
|
+
} catch {
|
|
11768
|
+
}
|
|
11769
|
+
}
|
|
11770
|
+
}
|
|
11771
|
+
function handleServerFrame(parsed) {
|
|
11772
|
+
bumpWatchdog();
|
|
11773
|
+
switch (parsed.type) {
|
|
11774
|
+
case "resume": {
|
|
11775
|
+
resumeResolved = true;
|
|
11776
|
+
trimQueueUpTo(parsed.lastAckedSeq);
|
|
11777
|
+
if (firstConnect) {
|
|
11778
|
+
const f = firstConnect;
|
|
11779
|
+
firstConnect = null;
|
|
11780
|
+
f.resolve();
|
|
11781
|
+
}
|
|
11782
|
+
if (parsed.terminated) {
|
|
11783
|
+
notifyTerminated();
|
|
11784
|
+
try {
|
|
11785
|
+
ws?.close(1e3, "already_terminated");
|
|
11786
|
+
} catch {
|
|
11787
|
+
}
|
|
11788
|
+
return;
|
|
11789
|
+
}
|
|
11790
|
+
flushQueue();
|
|
11791
|
+
return;
|
|
11792
|
+
}
|
|
11793
|
+
case "ack": {
|
|
11794
|
+
trimQueueUpTo(parsed.seq);
|
|
11795
|
+
return;
|
|
11796
|
+
}
|
|
11797
|
+
case "pong":
|
|
11798
|
+
return;
|
|
11799
|
+
case "error": {
|
|
11800
|
+
if (parsed.code === "sequence_gap" && typeof parsed.expected === "number") {
|
|
11801
|
+
trimQueueUpToExclusive(parsed.expected);
|
|
11802
|
+
flushQueue();
|
|
11803
|
+
log(
|
|
11804
|
+
"warn",
|
|
11805
|
+
`run-stream sequence_gap: server expected seq=${parsed.expected}; replayed ${queue.length} frames`
|
|
11806
|
+
);
|
|
11807
|
+
return;
|
|
11808
|
+
}
|
|
11809
|
+
log(
|
|
11810
|
+
"warn",
|
|
11811
|
+
`run-stream server error: ${parsed.code}${parsed.message ? ` ${parsed.message}` : ""}`
|
|
11812
|
+
);
|
|
11813
|
+
return;
|
|
11814
|
+
}
|
|
11425
11815
|
}
|
|
11816
|
+
}
|
|
11817
|
+
function nextReconnectDelay() {
|
|
11818
|
+
if (reconnectDelays.length === 0) return 0;
|
|
11819
|
+
const idx = Math.min(connectAttempt, reconnectDelays.length - 1);
|
|
11820
|
+
const ms = reconnectDelays[idx] ?? 0;
|
|
11821
|
+
connectAttempt += 1;
|
|
11822
|
+
return ms;
|
|
11823
|
+
}
|
|
11824
|
+
function scheduleReconnect() {
|
|
11825
|
+
if (stopped || serverTerminated) return;
|
|
11826
|
+
const delayMs = nextReconnectDelay();
|
|
11827
|
+
log("info", `run-stream reconnect scheduled in ${delayMs}ms`);
|
|
11828
|
+
reconnectTimer = setTimer(() => {
|
|
11829
|
+
reconnectTimer = null;
|
|
11830
|
+
void openSocket();
|
|
11831
|
+
}, delayMs);
|
|
11832
|
+
reconnectTimer.unref?.();
|
|
11833
|
+
}
|
|
11834
|
+
async function openSocket() {
|
|
11835
|
+
if (stopped || serverTerminated) return;
|
|
11836
|
+
resumeResolved = false;
|
|
11837
|
+
const next = makeWebSocket(opts.wsUrl);
|
|
11426
11838
|
ws = next;
|
|
11427
|
-
|
|
11839
|
+
next.on("open", () => {
|
|
11840
|
+
connectAttempt = 0;
|
|
11841
|
+
startHeartbeat();
|
|
11842
|
+
bumpWatchdog();
|
|
11843
|
+
});
|
|
11428
11844
|
next.on("message", (data) => {
|
|
11429
11845
|
let parsed;
|
|
11430
11846
|
try {
|
|
11431
11847
|
parsed = JSON.parse(String(data));
|
|
11432
|
-
} catch
|
|
11433
|
-
log(
|
|
11434
|
-
"warn",
|
|
11435
|
-
`instance socket frame parse failed: ${err instanceof Error ? err.message : String(err)}`
|
|
11436
|
-
);
|
|
11848
|
+
} catch {
|
|
11849
|
+
log("warn", "run-stream received non-JSON frame");
|
|
11437
11850
|
return;
|
|
11438
11851
|
}
|
|
11439
|
-
|
|
11852
|
+
handleServerFrame(parsed);
|
|
11440
11853
|
});
|
|
11441
|
-
next.on("close", (
|
|
11854
|
+
next.on("close", (code, reasonBuf) => {
|
|
11442
11855
|
if (next !== ws) return;
|
|
11443
11856
|
ws = null;
|
|
11444
|
-
|
|
11445
|
-
|
|
11857
|
+
clearTimers();
|
|
11858
|
+
const reason = reasonBuf?.toString?.() || "closed";
|
|
11859
|
+
if (code === 1e3 && reason === "run_terminated") {
|
|
11860
|
+
notifyTerminated();
|
|
11861
|
+
return;
|
|
11862
|
+
}
|
|
11863
|
+
if (firstConnect) {
|
|
11864
|
+
const f = firstConnect;
|
|
11865
|
+
firstConnect = null;
|
|
11866
|
+
f.reject(
|
|
11867
|
+
new Error(`run-stream initial connect failed: ${reason} (${code})`)
|
|
11868
|
+
);
|
|
11869
|
+
return;
|
|
11870
|
+
}
|
|
11871
|
+
scheduleReconnect();
|
|
11446
11872
|
});
|
|
11447
11873
|
next.on("error", (err) => {
|
|
11448
|
-
log(
|
|
11874
|
+
log(
|
|
11875
|
+
"warn",
|
|
11876
|
+
`run-stream socket error: ${err instanceof Error ? err.message : String(err)}`
|
|
11877
|
+
);
|
|
11449
11878
|
});
|
|
11450
11879
|
}
|
|
11451
|
-
|
|
11452
|
-
|
|
11453
|
-
|
|
11454
|
-
|
|
11455
|
-
|
|
11456
|
-
|
|
11457
|
-
|
|
11880
|
+
return {
|
|
11881
|
+
async connect() {
|
|
11882
|
+
if (firstConnect) {
|
|
11883
|
+
throw new Error("run-stream client already connecting");
|
|
11884
|
+
}
|
|
11885
|
+
await new Promise((resolve, reject) => {
|
|
11886
|
+
firstConnect = { resolve, reject };
|
|
11887
|
+
void openSocket();
|
|
11888
|
+
});
|
|
11889
|
+
},
|
|
11890
|
+
sendEvent(input) {
|
|
11891
|
+
const frame = {
|
|
11892
|
+
seq: nextSeq++,
|
|
11893
|
+
ts: input.ts,
|
|
11894
|
+
kind: input.kind,
|
|
11895
|
+
payload: input.payload ?? null
|
|
11896
|
+
};
|
|
11897
|
+
if (queue.length >= maxQueueSize) {
|
|
11898
|
+
log(
|
|
11899
|
+
"warn",
|
|
11900
|
+
`run-stream queue at cap (${maxQueueSize}); dropping oldest unacked frame seq=${queue[0].seq}`
|
|
11901
|
+
);
|
|
11902
|
+
queue.shift();
|
|
11903
|
+
}
|
|
11904
|
+
queue.push(frame);
|
|
11905
|
+
if (ws && ws.readyState === ws.OPEN && resumeResolved) {
|
|
11906
|
+
try {
|
|
11907
|
+
ws.send(JSON.stringify(frame));
|
|
11908
|
+
} catch (err) {
|
|
11909
|
+
log(
|
|
11910
|
+
"warn",
|
|
11911
|
+
`run-stream send failed (will replay): ${err instanceof Error ? err.message : String(err)}`
|
|
11912
|
+
);
|
|
11913
|
+
}
|
|
11914
|
+
}
|
|
11915
|
+
},
|
|
11916
|
+
whenTerminated() {
|
|
11917
|
+
if (serverTerminated) return Promise.resolve();
|
|
11918
|
+
return new Promise((resolve) => {
|
|
11919
|
+
terminationWaiters.push(resolve);
|
|
11920
|
+
});
|
|
11921
|
+
},
|
|
11922
|
+
async close(reason = "client_close") {
|
|
11923
|
+
stopped = true;
|
|
11924
|
+
clearTimers();
|
|
11925
|
+
const current = ws;
|
|
11926
|
+
ws = null;
|
|
11927
|
+
if (current) {
|
|
11928
|
+
try {
|
|
11929
|
+
current.close(1e3, reason);
|
|
11930
|
+
} catch {
|
|
11931
|
+
try {
|
|
11932
|
+
current.terminate();
|
|
11933
|
+
} catch {
|
|
11934
|
+
}
|
|
11935
|
+
}
|
|
11458
11936
|
}
|
|
11937
|
+
notifyTerminated();
|
|
11459
11938
|
}
|
|
11460
|
-
|
|
11461
|
-
}
|
|
11462
|
-
function onFrame(handler) {
|
|
11463
|
-
frameHandlers.add(handler);
|
|
11464
|
-
}
|
|
11465
|
-
function onClose(handler) {
|
|
11466
|
-
closeHandlers.add(handler);
|
|
11467
|
-
}
|
|
11468
|
-
function sendRunEvent(event) {
|
|
11469
|
-
send({ type: "run_event", ...event });
|
|
11470
|
-
}
|
|
11471
|
-
return { connect: connect2, close, onFrame, onClose, sendRunEvent };
|
|
11939
|
+
};
|
|
11472
11940
|
}
|
|
11473
11941
|
|
|
11474
11942
|
// src/app/dashboard/remoteRunExecutor.ts
|
|
@@ -11479,6 +11947,8 @@ function parseRunSpec(value) {
|
|
|
11479
11947
|
if (typeof prompt !== "string" || prompt.trim().length === 0) return null;
|
|
11480
11948
|
const env = obj["env"];
|
|
11481
11949
|
const workflow = obj["workflow"];
|
|
11950
|
+
const callbackWsUrl = obj["callbackWsUrl"];
|
|
11951
|
+
const callbackToken = obj["callbackToken"];
|
|
11482
11952
|
return {
|
|
11483
11953
|
prompt,
|
|
11484
11954
|
sessionId: typeof obj["sessionId"] === "string" && obj["sessionId"].length > 0 ? obj["sessionId"] : void 0,
|
|
@@ -11489,7 +11959,9 @@ function parseRunSpec(value) {
|
|
|
11489
11959
|
(entry) => typeof entry[1] === "string"
|
|
11490
11960
|
)
|
|
11491
11961
|
) : void 0,
|
|
11492
|
-
timeoutSec: typeof obj["timeoutSec"] === "number" && Number.isFinite(obj["timeoutSec"]) ? obj["timeoutSec"] : void 0
|
|
11962
|
+
timeoutSec: typeof obj["timeoutSec"] === "number" && Number.isFinite(obj["timeoutSec"]) ? obj["timeoutSec"] : void 0,
|
|
11963
|
+
callbackWsUrl: typeof callbackWsUrl === "string" && callbackWsUrl.length > 0 ? callbackWsUrl : void 0,
|
|
11964
|
+
callbackToken: typeof callbackToken === "string" && callbackToken.length > 0 ? callbackToken : void 0
|
|
11493
11965
|
};
|
|
11494
11966
|
}
|
|
11495
11967
|
function workflowNameFromRef(ref) {
|
|
@@ -11542,112 +12014,165 @@ async function executeRemoteAssignment({
|
|
|
11542
12014
|
runExecFn = runExec,
|
|
11543
12015
|
bootstrapRuntimeConfigFn = bootstrapRuntimeConfig,
|
|
11544
12016
|
now = Date.now,
|
|
11545
|
-
abortSignal
|
|
12017
|
+
abortSignal,
|
|
12018
|
+
createRunStreamClientFn = createRunStreamClient,
|
|
12019
|
+
runStreamConnectTimeoutMs = 5e3
|
|
11546
12020
|
}) {
|
|
11547
|
-
let seq = 0;
|
|
11548
12021
|
let lastTerminalFailureMessage = null;
|
|
11549
12022
|
let deferredFailedCompletion = null;
|
|
12023
|
+
const spec = parseRunSpec(frame.runSpec);
|
|
12024
|
+
let runStream = null;
|
|
12025
|
+
if (spec?.callbackWsUrl && spec.callbackToken) {
|
|
12026
|
+
const connect2 = createRunStreamClientFn({
|
|
12027
|
+
wsUrl: spec.callbackWsUrl,
|
|
12028
|
+
token: spec.callbackToken,
|
|
12029
|
+
log: (level, message) => log(level, `run-stream[${frame.runId}]: ${message}`),
|
|
12030
|
+
now
|
|
12031
|
+
});
|
|
12032
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
12033
|
+
const t = setTimeout(() => resolve("timeout"), runStreamConnectTimeoutMs);
|
|
12034
|
+
t.unref?.();
|
|
12035
|
+
});
|
|
12036
|
+
try {
|
|
12037
|
+
const result = await Promise.race([
|
|
12038
|
+
connect2.connect().then(() => "connected"),
|
|
12039
|
+
timeoutPromise
|
|
12040
|
+
]);
|
|
12041
|
+
if (result === "connected") {
|
|
12042
|
+
runStream = connect2;
|
|
12043
|
+
} else {
|
|
12044
|
+
log(
|
|
12045
|
+
"warn",
|
|
12046
|
+
`run-stream[${frame.runId}]: connect timed out after ${runStreamConnectTimeoutMs}ms; falling back to instance-socket relay`
|
|
12047
|
+
);
|
|
12048
|
+
void connect2.close("connect_timeout");
|
|
12049
|
+
}
|
|
12050
|
+
} catch (err) {
|
|
12051
|
+
log(
|
|
12052
|
+
"warn",
|
|
12053
|
+
`run-stream[${frame.runId}]: connect failed (${err instanceof Error ? err.message : String(err)}); falling back to instance-socket relay`
|
|
12054
|
+
);
|
|
12055
|
+
}
|
|
12056
|
+
}
|
|
12057
|
+
let legacySeq = 0;
|
|
11550
12058
|
const send = (kind, payload, ts = now()) => {
|
|
11551
|
-
seq += 1;
|
|
11552
12059
|
if (kind === "error" && typeof payload === "object" && payload !== null && typeof payload.message === "string") {
|
|
11553
12060
|
lastTerminalFailureMessage = payload.message;
|
|
11554
12061
|
}
|
|
12062
|
+
if (runStream) {
|
|
12063
|
+
runStream.sendEvent({ ts, kind, payload });
|
|
12064
|
+
return;
|
|
12065
|
+
}
|
|
12066
|
+
legacySeq += 1;
|
|
11555
12067
|
client.sendRunEvent({
|
|
11556
12068
|
runId: frame.runId,
|
|
11557
|
-
seq,
|
|
12069
|
+
seq: legacySeq,
|
|
11558
12070
|
ts,
|
|
11559
12071
|
kind,
|
|
11560
12072
|
payload
|
|
11561
12073
|
});
|
|
11562
12074
|
};
|
|
11563
12075
|
send("progress", { message: "assignment received" });
|
|
11564
|
-
const spec = parseRunSpec(frame.runSpec);
|
|
11565
|
-
if (!spec) {
|
|
11566
|
-
send("error", {
|
|
11567
|
-
message: "remote assignment missing prompt"
|
|
11568
|
-
});
|
|
11569
|
-
return;
|
|
11570
|
-
}
|
|
11571
|
-
const projectDir = spec.projectDir ?? fallbackProjectDir;
|
|
11572
|
-
let runtimeConfig;
|
|
11573
12076
|
try {
|
|
11574
|
-
|
|
11575
|
-
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
|
|
11587
|
-
|
|
11588
|
-
|
|
11589
|
-
|
|
11590
|
-
|
|
11591
|
-
|
|
11592
|
-
|
|
11593
|
-
|
|
11594
|
-
|
|
11595
|
-
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
|
|
11603
|
-
|
|
12077
|
+
if (!spec) {
|
|
12078
|
+
send("error", { message: "remote assignment missing prompt" });
|
|
12079
|
+
return;
|
|
12080
|
+
}
|
|
12081
|
+
const projectDir = spec.projectDir ?? fallbackProjectDir;
|
|
12082
|
+
let runtimeConfig;
|
|
12083
|
+
try {
|
|
12084
|
+
runtimeConfig = bootstrapRuntimeConfigFn({
|
|
12085
|
+
projectDir,
|
|
12086
|
+
showSetup: false,
|
|
12087
|
+
isolationPreset: "minimal",
|
|
12088
|
+
workflowOverride: workflowNameFromRef(spec.workflow?.ref)
|
|
12089
|
+
});
|
|
12090
|
+
} catch (err) {
|
|
12091
|
+
send("error", {
|
|
12092
|
+
message: err instanceof Error ? err.message : String(err)
|
|
12093
|
+
});
|
|
12094
|
+
return;
|
|
12095
|
+
}
|
|
12096
|
+
for (const warning of runtimeConfig.warnings) {
|
|
12097
|
+
send("warning", { message: warning });
|
|
12098
|
+
}
|
|
12099
|
+
let buffered = "";
|
|
12100
|
+
const stdout = {
|
|
12101
|
+
write(chunk) {
|
|
12102
|
+
buffered += chunk;
|
|
12103
|
+
let newline = buffered.indexOf("\n");
|
|
12104
|
+
while (newline >= 0) {
|
|
12105
|
+
const line = buffered.slice(0, newline).trim();
|
|
12106
|
+
buffered = buffered.slice(newline + 1);
|
|
12107
|
+
if (line.length > 0) {
|
|
12108
|
+
try {
|
|
12109
|
+
const event = JSON.parse(line);
|
|
12110
|
+
const data = event.data;
|
|
12111
|
+
if (event.type === "exec.completed" && data?.success === false) {
|
|
12112
|
+
deferredFailedCompletion = event;
|
|
12113
|
+
continue;
|
|
12114
|
+
}
|
|
12115
|
+
send(eventKind(event), eventPayload(event), now());
|
|
12116
|
+
} catch (err) {
|
|
12117
|
+
send("progress", { line });
|
|
12118
|
+
log(
|
|
12119
|
+
"warn",
|
|
12120
|
+
`remote run emitted malformed JSONL: ${err instanceof Error ? err.message : String(err)}`
|
|
12121
|
+
);
|
|
11604
12122
|
}
|
|
11605
|
-
send(eventKind(event), eventPayload(event), now());
|
|
11606
|
-
} catch (err) {
|
|
11607
|
-
send("progress", { line });
|
|
11608
|
-
log(
|
|
11609
|
-
"warn",
|
|
11610
|
-
`remote run emitted malformed JSONL: ${err instanceof Error ? err.message : String(err)}`
|
|
11611
|
-
);
|
|
11612
12123
|
}
|
|
12124
|
+
newline = buffered.indexOf("\n");
|
|
11613
12125
|
}
|
|
11614
|
-
|
|
12126
|
+
return true;
|
|
11615
12127
|
}
|
|
11616
|
-
|
|
11617
|
-
|
|
11618
|
-
|
|
11619
|
-
|
|
11620
|
-
|
|
11621
|
-
|
|
11622
|
-
|
|
11623
|
-
|
|
11624
|
-
|
|
11625
|
-
|
|
11626
|
-
|
|
11627
|
-
|
|
11628
|
-
|
|
11629
|
-
|
|
11630
|
-
|
|
11631
|
-
|
|
11632
|
-
|
|
11633
|
-
|
|
11634
|
-
|
|
11635
|
-
|
|
11636
|
-
|
|
11637
|
-
|
|
11638
|
-
|
|
11639
|
-
|
|
11640
|
-
|
|
11641
|
-
|
|
11642
|
-
|
|
11643
|
-
|
|
11644
|
-
|
|
11645
|
-
|
|
11646
|
-
|
|
11647
|
-
|
|
11648
|
-
|
|
11649
|
-
|
|
11650
|
-
|
|
12128
|
+
};
|
|
12129
|
+
const stderr = {
|
|
12130
|
+
write(chunk) {
|
|
12131
|
+
const text = chunk.trim();
|
|
12132
|
+
if (text.length > 0) send("stderr", { text });
|
|
12133
|
+
return true;
|
|
12134
|
+
}
|
|
12135
|
+
};
|
|
12136
|
+
try {
|
|
12137
|
+
await withEnv(spec.env, async () => {
|
|
12138
|
+
const result = await runExecFn({
|
|
12139
|
+
prompt: spec.prompt,
|
|
12140
|
+
projectDir,
|
|
12141
|
+
harness: runtimeConfig.harness,
|
|
12142
|
+
athenaSessionId: spec.sessionId ?? `athena-${frame.runId}`,
|
|
12143
|
+
isolationConfig: runtimeConfig.isolationConfig,
|
|
12144
|
+
pluginMcpConfig: runtimeConfig.pluginMcpConfig,
|
|
12145
|
+
workflow: runtimeConfig.workflow,
|
|
12146
|
+
workflowPlan: runtimeConfig.workflowPlan,
|
|
12147
|
+
json: true,
|
|
12148
|
+
verbose: false,
|
|
12149
|
+
ephemeral: false,
|
|
12150
|
+
timeoutMs: spec.timeoutSec ? spec.timeoutSec * 1e3 : void 0,
|
|
12151
|
+
signal: abortSignal,
|
|
12152
|
+
stdout,
|
|
12153
|
+
stderr
|
|
12154
|
+
});
|
|
12155
|
+
if (deferredFailedCompletion) {
|
|
12156
|
+
const data = typeof deferredFailedCompletion.data === "object" && deferredFailedCompletion.data !== null ? deferredFailedCompletion.data : {};
|
|
12157
|
+
send(
|
|
12158
|
+
"error",
|
|
12159
|
+
{
|
|
12160
|
+
...data,
|
|
12161
|
+
success: result.success,
|
|
12162
|
+
exitCode: result.exitCode,
|
|
12163
|
+
athenaSessionId: result.athenaSessionId,
|
|
12164
|
+
adapterSessionId: result.adapterSessionId,
|
|
12165
|
+
finalMessage: result.finalMessage,
|
|
12166
|
+
tokens: result.tokens,
|
|
12167
|
+
durationMs: result.durationMs,
|
|
12168
|
+
message: result.failure?.message ?? eventPayload(deferredFailedCompletion).message ?? "remote execution failed"
|
|
12169
|
+
},
|
|
12170
|
+
typeof deferredFailedCompletion.ts === "number" ? deferredFailedCompletion.ts : now()
|
|
12171
|
+
);
|
|
12172
|
+
return;
|
|
12173
|
+
}
|
|
12174
|
+
if (result.failure && result.failure.message !== lastTerminalFailureMessage) {
|
|
12175
|
+
send("error", {
|
|
11651
12176
|
success: result.success,
|
|
11652
12177
|
exitCode: result.exitCode,
|
|
11653
12178
|
athenaSessionId: result.athenaSessionId,
|
|
@@ -11655,34 +12180,142 @@ async function executeRemoteAssignment({
|
|
|
11655
12180
|
finalMessage: result.finalMessage,
|
|
11656
12181
|
tokens: result.tokens,
|
|
11657
12182
|
durationMs: result.durationMs,
|
|
11658
|
-
message: result.failure
|
|
11659
|
-
}
|
|
11660
|
-
|
|
11661
|
-
|
|
11662
|
-
|
|
11663
|
-
|
|
11664
|
-
|
|
11665
|
-
|
|
11666
|
-
|
|
11667
|
-
|
|
11668
|
-
|
|
11669
|
-
|
|
11670
|
-
|
|
11671
|
-
|
|
11672
|
-
|
|
11673
|
-
|
|
11674
|
-
|
|
11675
|
-
|
|
11676
|
-
|
|
12183
|
+
message: result.failure.message
|
|
12184
|
+
});
|
|
12185
|
+
}
|
|
12186
|
+
});
|
|
12187
|
+
} catch (err) {
|
|
12188
|
+
send("error", {
|
|
12189
|
+
message: err instanceof Error ? err.message : String(err)
|
|
12190
|
+
});
|
|
12191
|
+
}
|
|
12192
|
+
} finally {
|
|
12193
|
+
if (runStream) {
|
|
12194
|
+
const drainTimeout = new Promise((resolve) => {
|
|
12195
|
+
const t = setTimeout(() => resolve(), 1e4);
|
|
12196
|
+
t.unref?.();
|
|
12197
|
+
});
|
|
12198
|
+
await Promise.race([runStream.whenTerminated(), drainTimeout]);
|
|
12199
|
+
await runStream.close("done");
|
|
12200
|
+
}
|
|
12201
|
+
}
|
|
12202
|
+
}
|
|
12203
|
+
|
|
12204
|
+
// src/infra/config/attachmentMirror.ts
|
|
12205
|
+
import crypto3 from "crypto";
|
|
12206
|
+
import fs20 from "fs";
|
|
12207
|
+
import os11 from "os";
|
|
12208
|
+
import path18 from "path";
|
|
12209
|
+
function attachmentMirrorPath(env = process.env) {
|
|
12210
|
+
const home = env["HOME"] ?? os11.homedir();
|
|
12211
|
+
return path18.join(home, ".config", "athena", "attachments.json");
|
|
12212
|
+
}
|
|
12213
|
+
function readAttachmentMirror(env = process.env) {
|
|
12214
|
+
const file = attachmentMirrorPath(env);
|
|
12215
|
+
let raw;
|
|
12216
|
+
try {
|
|
12217
|
+
raw = fs20.readFileSync(file, "utf-8");
|
|
11677
12218
|
} catch (err) {
|
|
11678
|
-
|
|
11679
|
-
|
|
11680
|
-
|
|
12219
|
+
if (err.code === "ENOENT") return null;
|
|
12220
|
+
throw err;
|
|
12221
|
+
}
|
|
12222
|
+
let parsed;
|
|
12223
|
+
try {
|
|
12224
|
+
parsed = JSON.parse(raw);
|
|
12225
|
+
} catch (err) {
|
|
12226
|
+
throw new Error(
|
|
12227
|
+
`attachment mirror ${file} is invalid JSON: ${err instanceof Error ? err.message : String(err)}`
|
|
12228
|
+
);
|
|
12229
|
+
}
|
|
12230
|
+
try {
|
|
12231
|
+
return parseAttachmentMirror(parsed);
|
|
12232
|
+
} catch (err) {
|
|
12233
|
+
throw new Error(
|
|
12234
|
+
`attachment mirror ${file} is invalid: ${err instanceof Error ? err.message : String(err)}`
|
|
12235
|
+
);
|
|
12236
|
+
}
|
|
12237
|
+
}
|
|
12238
|
+
function writeAttachmentMirror(mirror, env = process.env) {
|
|
12239
|
+
const validated = parseAttachmentMirror(mirror);
|
|
12240
|
+
const file = attachmentMirrorPath(env);
|
|
12241
|
+
const dir = path18.dirname(file);
|
|
12242
|
+
fs20.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
12243
|
+
const tmp = `${file}.${process.pid}.${crypto3.randomBytes(4).toString("hex")}.tmp`;
|
|
12244
|
+
const fd = fs20.openSync(tmp, "w", 384);
|
|
12245
|
+
try {
|
|
12246
|
+
fs20.writeSync(fd, JSON.stringify(validated, null, 2) + "\n");
|
|
12247
|
+
fs20.fsyncSync(fd);
|
|
12248
|
+
} finally {
|
|
12249
|
+
fs20.closeSync(fd);
|
|
12250
|
+
}
|
|
12251
|
+
try {
|
|
12252
|
+
fs20.renameSync(tmp, file);
|
|
12253
|
+
} catch (err) {
|
|
12254
|
+
try {
|
|
12255
|
+
fs20.unlinkSync(tmp);
|
|
12256
|
+
} catch {
|
|
12257
|
+
}
|
|
12258
|
+
throw err;
|
|
12259
|
+
}
|
|
12260
|
+
if (process.platform !== "win32") {
|
|
12261
|
+
try {
|
|
12262
|
+
fs20.chmodSync(dir, 448);
|
|
12263
|
+
fs20.chmodSync(file, 384);
|
|
12264
|
+
} catch {
|
|
12265
|
+
}
|
|
12266
|
+
}
|
|
12267
|
+
}
|
|
12268
|
+
function removeAttachmentMirror(env = process.env) {
|
|
12269
|
+
const file = attachmentMirrorPath(env);
|
|
12270
|
+
try {
|
|
12271
|
+
fs20.unlinkSync(file);
|
|
12272
|
+
} catch (err) {
|
|
12273
|
+
if (err.code !== "ENOENT") throw err;
|
|
12274
|
+
}
|
|
12275
|
+
}
|
|
12276
|
+
function parseAttachmentMirror(raw) {
|
|
12277
|
+
if (typeof raw !== "object" || raw === null) {
|
|
12278
|
+
throw new Error("root must be an object");
|
|
12279
|
+
}
|
|
12280
|
+
const obj = raw;
|
|
12281
|
+
if (typeof obj["instanceId"] !== "string" || obj["instanceId"].length === 0) {
|
|
12282
|
+
throw new Error("instanceId must be a non-empty string");
|
|
12283
|
+
}
|
|
12284
|
+
if (typeof obj["fetchedAt"] !== "number") {
|
|
12285
|
+
throw new Error("fetchedAt must be a number");
|
|
11681
12286
|
}
|
|
12287
|
+
if (!Array.isArray(obj["attachments"])) {
|
|
12288
|
+
throw new Error("attachments must be an array");
|
|
12289
|
+
}
|
|
12290
|
+
const attachments = obj["attachments"].map((entry, idx) => {
|
|
12291
|
+
if (typeof entry !== "object" || entry === null) {
|
|
12292
|
+
throw new Error(`attachments[${idx}] must be an object`);
|
|
12293
|
+
}
|
|
12294
|
+
const e = entry;
|
|
12295
|
+
if (typeof e["runnerId"] !== "string" || e["runnerId"].length === 0) {
|
|
12296
|
+
throw new Error(
|
|
12297
|
+
`attachments[${idx}].runnerId must be a non-empty string`
|
|
12298
|
+
);
|
|
12299
|
+
}
|
|
12300
|
+
const out = { runnerId: e["runnerId"] };
|
|
12301
|
+
if (typeof e["name"] === "string") out.name = e["name"];
|
|
12302
|
+
if (typeof e["executionTarget"] === "string") {
|
|
12303
|
+
out.executionTarget = e["executionTarget"];
|
|
12304
|
+
}
|
|
12305
|
+
if (typeof e["remoteInstanceId"] === "string") {
|
|
12306
|
+
out.remoteInstanceId = e["remoteInstanceId"];
|
|
12307
|
+
}
|
|
12308
|
+
return out;
|
|
12309
|
+
});
|
|
12310
|
+
return {
|
|
12311
|
+
instanceId: obj["instanceId"],
|
|
12312
|
+
fetchedAt: obj["fetchedAt"],
|
|
12313
|
+
attachments
|
|
12314
|
+
};
|
|
11682
12315
|
}
|
|
11683
12316
|
|
|
11684
12317
|
// src/app/dashboard/runtimeDaemon.ts
|
|
11685
|
-
var
|
|
12318
|
+
var DEFAULT_RECONNECT_DELAYS_MS2 = [1e3, 2e3, 5e3, 1e4, 3e4];
|
|
11686
12319
|
var DEFAULT_MAX_CONCURRENT_RUNS = 1;
|
|
11687
12320
|
var DEFAULT_REFRESH_LEAD_SEC = 60;
|
|
11688
12321
|
var DEFAULT_REFRESH_FAILURE_LIMIT = 5;
|
|
@@ -11708,7 +12341,7 @@ async function runDashboardRuntimeDaemon(options = {}) {
|
|
|
11708
12341
|
const projectDir = options.projectDir ?? process.cwd();
|
|
11709
12342
|
const log = options.log ?? (() => {
|
|
11710
12343
|
});
|
|
11711
|
-
const reconnectDelays = options.reconnectDelaysMs ??
|
|
12344
|
+
const reconnectDelays = options.reconnectDelaysMs ?? DEFAULT_RECONNECT_DELAYS_MS2;
|
|
11712
12345
|
const maxConcurrentRuns = options.maxConcurrentRuns ?? DEFAULT_MAX_CONCURRENT_RUNS;
|
|
11713
12346
|
const refreshLeadSec = options.refreshLeadSec ?? DEFAULT_REFRESH_LEAD_SEC;
|
|
11714
12347
|
const refreshFailureLimit = options.refreshFailureLimit ?? DEFAULT_REFRESH_FAILURE_LIMIT;
|
|
@@ -11716,6 +12349,7 @@ async function runDashboardRuntimeDaemon(options = {}) {
|
|
|
11716
12349
|
const refreshCooldownMs = options.refreshCooldownMs ?? DEFAULT_REFRESH_COOLDOWN_MS;
|
|
11717
12350
|
const runHistoryLimit = options.runHistoryLimit ?? DEFAULT_RUN_HISTORY_LIMIT;
|
|
11718
12351
|
const now = options.now ?? (() => Date.now());
|
|
12352
|
+
const writeMirror = options.writeMirror ?? writeAttachmentMirror;
|
|
11719
12353
|
const startedAt = now();
|
|
11720
12354
|
let stopped = false;
|
|
11721
12355
|
let reconnectAttempt = 0;
|
|
@@ -11849,6 +12483,26 @@ async function runDashboardRuntimeDaemon(options = {}) {
|
|
|
11849
12483
|
});
|
|
11850
12484
|
next.onFrame((frame) => {
|
|
11851
12485
|
lastFrameAt = now();
|
|
12486
|
+
if (frame.type === "attachments.changed") {
|
|
12487
|
+
try {
|
|
12488
|
+
writeMirror({
|
|
12489
|
+
instanceId: token.instanceId,
|
|
12490
|
+
fetchedAt: now(),
|
|
12491
|
+
attachments: frame.attachments.map((a) => ({
|
|
12492
|
+
runnerId: a.runnerId,
|
|
12493
|
+
...a.name !== void 0 ? { name: a.name } : {},
|
|
12494
|
+
...a.executionTarget !== void 0 ? { executionTarget: a.executionTarget } : {},
|
|
12495
|
+
...a.remoteInstanceId !== void 0 ? { remoteInstanceId: a.remoteInstanceId } : {}
|
|
12496
|
+
}))
|
|
12497
|
+
});
|
|
12498
|
+
} catch (err) {
|
|
12499
|
+
log(
|
|
12500
|
+
"warn",
|
|
12501
|
+
`runtime daemon: failed to write attachment mirror: ${err instanceof Error ? err.message : String(err)}`
|
|
12502
|
+
);
|
|
12503
|
+
}
|
|
12504
|
+
return;
|
|
12505
|
+
}
|
|
11852
12506
|
if (frame.type === "cancel") {
|
|
11853
12507
|
const entry = active.get(frame.runId);
|
|
11854
12508
|
if (entry) {
|
|
@@ -11973,27 +12627,27 @@ async function runDashboardRuntimeDaemon(options = {}) {
|
|
|
11973
12627
|
}
|
|
11974
12628
|
|
|
11975
12629
|
// src/infra/daemon/stateDir.ts
|
|
11976
|
-
import
|
|
11977
|
-
import
|
|
11978
|
-
import
|
|
12630
|
+
import fs21 from "fs";
|
|
12631
|
+
import os12 from "os";
|
|
12632
|
+
import path19 from "path";
|
|
11979
12633
|
function daemonStatePaths(env = process.env) {
|
|
11980
12634
|
const xdg = env["XDG_STATE_HOME"];
|
|
11981
|
-
const home = env["HOME"] ??
|
|
11982
|
-
const base = xdg && xdg.length > 0 ? xdg :
|
|
11983
|
-
const dir =
|
|
12635
|
+
const home = env["HOME"] ?? os12.homedir();
|
|
12636
|
+
const base = xdg && xdg.length > 0 ? xdg : path19.join(home, ".local", "state");
|
|
12637
|
+
const dir = path19.join(base, "drisp");
|
|
11984
12638
|
return {
|
|
11985
12639
|
dir,
|
|
11986
|
-
pidPath:
|
|
11987
|
-
logPath:
|
|
11988
|
-
socketPath:
|
|
12640
|
+
pidPath: path19.join(dir, "dashboard-daemon.pid"),
|
|
12641
|
+
logPath: path19.join(dir, "dashboard-daemon.log"),
|
|
12642
|
+
socketPath: path19.join(dir, "dashboard-daemon.sock")
|
|
11989
12643
|
};
|
|
11990
12644
|
}
|
|
11991
12645
|
function ensureDaemonStateDir(env = process.env) {
|
|
11992
12646
|
const paths = daemonStatePaths(env);
|
|
11993
|
-
|
|
12647
|
+
fs21.mkdirSync(paths.dir, { recursive: true, mode: 448 });
|
|
11994
12648
|
if (process.platform !== "win32") {
|
|
11995
12649
|
try {
|
|
11996
|
-
|
|
12650
|
+
fs21.chmodSync(paths.dir, 448);
|
|
11997
12651
|
} catch {
|
|
11998
12652
|
}
|
|
11999
12653
|
}
|
|
@@ -12001,18 +12655,18 @@ function ensureDaemonStateDir(env = process.env) {
|
|
|
12001
12655
|
}
|
|
12002
12656
|
|
|
12003
12657
|
// src/infra/daemon/pidLock.ts
|
|
12004
|
-
import
|
|
12658
|
+
import fs22 from "fs";
|
|
12005
12659
|
function acquirePidLock(pidPath) {
|
|
12006
12660
|
const ownPid = process.pid;
|
|
12007
12661
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
12008
12662
|
try {
|
|
12009
|
-
const fd =
|
|
12663
|
+
const fd = fs22.openSync(pidPath, "wx", 384);
|
|
12010
12664
|
try {
|
|
12011
|
-
|
|
12665
|
+
fs22.writeSync(fd, `${ownPid}
|
|
12012
12666
|
`);
|
|
12013
|
-
|
|
12667
|
+
fs22.fsyncSync(fd);
|
|
12014
12668
|
} finally {
|
|
12015
|
-
|
|
12669
|
+
fs22.closeSync(fd);
|
|
12016
12670
|
}
|
|
12017
12671
|
return makeHandle(pidPath, ownPid);
|
|
12018
12672
|
} catch (err) {
|
|
@@ -12026,7 +12680,7 @@ function acquirePidLock(pidPath) {
|
|
|
12026
12680
|
}
|
|
12027
12681
|
if (existing.state === "stale") {
|
|
12028
12682
|
try {
|
|
12029
|
-
|
|
12683
|
+
fs22.unlinkSync(pidPath);
|
|
12030
12684
|
} catch (err) {
|
|
12031
12685
|
if (err.code !== "ENOENT") throw err;
|
|
12032
12686
|
}
|
|
@@ -12040,7 +12694,7 @@ function acquirePidLock(pidPath) {
|
|
|
12040
12694
|
function readPidLock(pidPath) {
|
|
12041
12695
|
let raw;
|
|
12042
12696
|
try {
|
|
12043
|
-
raw =
|
|
12697
|
+
raw = fs22.readFileSync(pidPath, "utf-8");
|
|
12044
12698
|
} catch (err) {
|
|
12045
12699
|
if (err.code === "ENOENT") {
|
|
12046
12700
|
return { state: "absent" };
|
|
@@ -12064,9 +12718,9 @@ function makeHandle(pidPath, pid) {
|
|
|
12064
12718
|
if (released) return;
|
|
12065
12719
|
released = true;
|
|
12066
12720
|
try {
|
|
12067
|
-
const raw =
|
|
12721
|
+
const raw = fs22.readFileSync(pidPath, "utf-8").trim();
|
|
12068
12722
|
if (raw === String(pid)) {
|
|
12069
|
-
|
|
12723
|
+
fs22.unlinkSync(pidPath);
|
|
12070
12724
|
}
|
|
12071
12725
|
} catch (err) {
|
|
12072
12726
|
if (err.code !== "ENOENT") {
|
|
@@ -12092,8 +12746,37 @@ function isProcessAlive(pid) {
|
|
|
12092
12746
|
}
|
|
12093
12747
|
|
|
12094
12748
|
// src/infra/daemon/udsIpc.ts
|
|
12095
|
-
import
|
|
12749
|
+
import fs23 from "fs";
|
|
12096
12750
|
import net2 from "net";
|
|
12751
|
+
|
|
12752
|
+
// src/infra/daemon/udsFrameCodec.ts
|
|
12753
|
+
var DEFAULT_MAX_FRAME_BYTES = 1e6;
|
|
12754
|
+
function tryReadFrame(buffer, maxBodyBytes = DEFAULT_MAX_FRAME_BYTES) {
|
|
12755
|
+
const newlineIdx = buffer.indexOf(10);
|
|
12756
|
+
if (newlineIdx < 0) return null;
|
|
12757
|
+
const lenStr = buffer.subarray(0, newlineIdx).toString("utf-8");
|
|
12758
|
+
const len = Number.parseInt(lenStr, 10);
|
|
12759
|
+
if (!Number.isFinite(len) || len < 0 || len > maxBodyBytes) {
|
|
12760
|
+
return {
|
|
12761
|
+
ok: false,
|
|
12762
|
+
error: `uds bad framing header: ${JSON.stringify(lenStr.slice(0, 32))}`,
|
|
12763
|
+
rest: buffer.subarray(newlineIdx + 1)
|
|
12764
|
+
};
|
|
12765
|
+
}
|
|
12766
|
+
if (buffer.length < newlineIdx + 1 + len) return null;
|
|
12767
|
+
const body = buffer.subarray(newlineIdx + 1, newlineIdx + 1 + len);
|
|
12768
|
+
const rest = buffer.subarray(newlineIdx + 1 + len);
|
|
12769
|
+
return { ok: true, body, rest };
|
|
12770
|
+
}
|
|
12771
|
+
function writeFrame(socket, value) {
|
|
12772
|
+
const body = JSON.stringify(value);
|
|
12773
|
+
const buf = Buffer.from(body, "utf-8");
|
|
12774
|
+
socket.write(`${buf.length}
|
|
12775
|
+
`);
|
|
12776
|
+
socket.write(buf);
|
|
12777
|
+
}
|
|
12778
|
+
|
|
12779
|
+
// src/infra/daemon/udsIpc.ts
|
|
12097
12780
|
async function startUdsServer(socketPath, handler, log) {
|
|
12098
12781
|
await unlinkStaleSocket(socketPath);
|
|
12099
12782
|
const server = net2.createServer((socket) => {
|
|
@@ -12108,7 +12791,7 @@ async function startUdsServer(socketPath, handler, log) {
|
|
|
12108
12791
|
});
|
|
12109
12792
|
if (process.platform !== "win32") {
|
|
12110
12793
|
try {
|
|
12111
|
-
|
|
12794
|
+
fs23.chmodSync(socketPath, 384);
|
|
12112
12795
|
} catch {
|
|
12113
12796
|
}
|
|
12114
12797
|
}
|
|
@@ -12118,7 +12801,7 @@ async function startUdsServer(socketPath, handler, log) {
|
|
|
12118
12801
|
server.close(() => resolve());
|
|
12119
12802
|
});
|
|
12120
12803
|
try {
|
|
12121
|
-
|
|
12804
|
+
fs23.unlinkSync(socketPath);
|
|
12122
12805
|
} catch (err) {
|
|
12123
12806
|
if (err.code !== "ENOENT") {
|
|
12124
12807
|
}
|
|
@@ -12142,21 +12825,15 @@ async function handleConnection(socket, handler, log) {
|
|
|
12142
12825
|
});
|
|
12143
12826
|
async function drain() {
|
|
12144
12827
|
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
|
-
);
|
|
12828
|
+
const frame = tryReadFrame(buffer);
|
|
12829
|
+
if (frame === null) return;
|
|
12830
|
+
if (!frame.ok) {
|
|
12831
|
+
log?.("warn", frame.error);
|
|
12154
12832
|
socket.destroy();
|
|
12155
12833
|
return;
|
|
12156
12834
|
}
|
|
12157
|
-
|
|
12158
|
-
|
|
12159
|
-
buffer = buffer.subarray(newlineIdx + 1 + len);
|
|
12835
|
+
const { body } = frame;
|
|
12836
|
+
buffer = frame.rest;
|
|
12160
12837
|
let parsed;
|
|
12161
12838
|
try {
|
|
12162
12839
|
parsed = JSON.parse(body.toString("utf-8"));
|
|
@@ -12178,13 +12855,6 @@ async function handleConnection(socket, handler, log) {
|
|
|
12178
12855
|
}
|
|
12179
12856
|
}
|
|
12180
12857
|
}
|
|
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
12858
|
async function sendUdsRequest(socketPath, request, options = {}) {
|
|
12189
12859
|
const timeoutMs = options.timeoutMs ?? 5e3;
|
|
12190
12860
|
return await new Promise((resolve, reject) => {
|
|
@@ -12209,30 +12879,20 @@ async function sendUdsRequest(socketPath, request, options = {}) {
|
|
|
12209
12879
|
action();
|
|
12210
12880
|
};
|
|
12211
12881
|
socket.on("connect", () => {
|
|
12212
|
-
|
|
12213
|
-
const buf = Buffer.from(body, "utf-8");
|
|
12214
|
-
socket.write(`${buf.length}
|
|
12215
|
-
`);
|
|
12216
|
-
socket.write(buf);
|
|
12882
|
+
writeFrame(socket, request);
|
|
12217
12883
|
});
|
|
12218
12884
|
socket.on("data", (chunk) => {
|
|
12219
12885
|
const chunkBuf = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
12220
12886
|
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)) {
|
|
12887
|
+
const frame = tryReadFrame(buffer);
|
|
12888
|
+
if (frame === null) return;
|
|
12889
|
+
if (!frame.ok) {
|
|
12228
12890
|
finish(() => reject(new Error("uds reply malformed")));
|
|
12229
12891
|
return;
|
|
12230
12892
|
}
|
|
12231
|
-
if (buffer.length < newlineIdx + 1 + len) return;
|
|
12232
|
-
const body = buffer.subarray(newlineIdx + 1, newlineIdx + 1 + len).toString("utf-8");
|
|
12233
12893
|
let parsed;
|
|
12234
12894
|
try {
|
|
12235
|
-
parsed = JSON.parse(body);
|
|
12895
|
+
parsed = JSON.parse(frame.body.toString("utf-8"));
|
|
12236
12896
|
} catch (err) {
|
|
12237
12897
|
finish(
|
|
12238
12898
|
() => reject(
|
|
@@ -12259,7 +12919,7 @@ async function sendUdsRequest(socketPath, request, options = {}) {
|
|
|
12259
12919
|
async function unlinkStaleSocket(socketPath) {
|
|
12260
12920
|
let stat;
|
|
12261
12921
|
try {
|
|
12262
|
-
stat =
|
|
12922
|
+
stat = fs23.statSync(socketPath);
|
|
12263
12923
|
} catch (err) {
|
|
12264
12924
|
if (err.code === "ENOENT") return;
|
|
12265
12925
|
throw err;
|
|
@@ -12286,7 +12946,7 @@ async function unlinkStaleSocket(socketPath) {
|
|
|
12286
12946
|
`uds path ${socketPath} is in use by another process; aborting`
|
|
12287
12947
|
);
|
|
12288
12948
|
}
|
|
12289
|
-
|
|
12949
|
+
fs23.unlinkSync(socketPath);
|
|
12290
12950
|
}
|
|
12291
12951
|
|
|
12292
12952
|
export {
|
|
@@ -12301,7 +12961,8 @@ export {
|
|
|
12301
12961
|
extractPermissionSnapshot,
|
|
12302
12962
|
isSubagentTool,
|
|
12303
12963
|
createFeedMapper,
|
|
12304
|
-
|
|
12964
|
+
ingestRuntimeEvent,
|
|
12965
|
+
ingestRuntimeDecision,
|
|
12305
12966
|
generateId,
|
|
12306
12967
|
createSessionStore,
|
|
12307
12968
|
sessionsDir,
|
|
@@ -12350,6 +13011,9 @@ export {
|
|
|
12350
13011
|
bootstrapRuntimeConfig,
|
|
12351
13012
|
EXEC_EXIT_CODE,
|
|
12352
13013
|
runExec,
|
|
13014
|
+
readAttachmentMirror,
|
|
13015
|
+
writeAttachmentMirror,
|
|
13016
|
+
removeAttachmentMirror,
|
|
12353
13017
|
runDashboardRuntimeDaemon,
|
|
12354
13018
|
daemonStatePaths,
|
|
12355
13019
|
ensureDaemonStateDir,
|
|
@@ -12358,4 +13022,4 @@ export {
|
|
|
12358
13022
|
startUdsServer,
|
|
12359
13023
|
sendUdsRequest
|
|
12360
13024
|
};
|
|
12361
|
-
//# sourceMappingURL=chunk-
|
|
13025
|
+
//# sourceMappingURL=chunk-PJUDHH4R.js.map
|