@drisp/cli 0.5.22 → 0.5.24
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-IPLYZPLS.js → WorkflowInstallWizard-IMU47QWB.js} +2 -2
- package/dist/athena-gateway.js +26 -39
- package/dist/{chunk-MRAM6EYI.js → chunk-7GEQJQMR.js} +2 -2
- package/dist/{chunk-DSWRVUOO.js → chunk-7UUPLAP4.js} +163 -178
- package/dist/{chunk-HXW5N4GE.js → chunk-QBMYQJFX.js} +734 -838
- package/dist/{chunk-BTY7MYYT.js → chunk-YU2WMPRC.js} +54 -8
- package/dist/cli.js +140 -135
- package/dist/dashboard-daemon.js +3 -3
- package/package.json +1 -1
|
@@ -17,13 +17,14 @@ import {
|
|
|
17
17
|
TransportUnreachableError,
|
|
18
18
|
createUdsClientTransport,
|
|
19
19
|
generateChannelRequestId,
|
|
20
|
+
openVersionedDb,
|
|
20
21
|
readDashboardClientConfig,
|
|
21
22
|
refreshDashboardAccessToken,
|
|
22
23
|
resolveGatewayPaths,
|
|
23
24
|
traceGatewayFrame,
|
|
24
25
|
trackGatewayTransportReconnect,
|
|
25
26
|
writeGatewayTrace
|
|
26
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-YU2WMPRC.js";
|
|
27
28
|
import {
|
|
28
29
|
compileWorkflowPlan,
|
|
29
30
|
createWorkflowRunner,
|
|
@@ -34,7 +35,7 @@ import {
|
|
|
34
35
|
resolveWorkflow,
|
|
35
36
|
resolveWorkflowInstall,
|
|
36
37
|
resolveWorkflowPlugins
|
|
37
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-7UUPLAP4.js";
|
|
38
39
|
|
|
39
40
|
// src/infra/daemon/stateDir.ts
|
|
40
41
|
import fs from "fs";
|
|
@@ -3374,7 +3375,7 @@ function createAssistantMessageAccumulator() {
|
|
|
3374
3375
|
};
|
|
3375
3376
|
}
|
|
3376
3377
|
|
|
3377
|
-
// src/harnesses/claude/
|
|
3378
|
+
// src/harnesses/claude/session/turnConfig.ts
|
|
3378
3379
|
function mergeIsolation(base, pluginMcpConfig, perCommand) {
|
|
3379
3380
|
if (!pluginMcpConfig && !perCommand) return base;
|
|
3380
3381
|
return {
|
|
@@ -3383,6 +3384,17 @@ function mergeIsolation(base, pluginMcpConfig, perCommand) {
|
|
|
3383
3384
|
...pluginMcpConfig ? { mcpConfig: pluginMcpConfig } : {}
|
|
3384
3385
|
};
|
|
3385
3386
|
}
|
|
3387
|
+
function resolveClaudeSessionId(continuation) {
|
|
3388
|
+
if (!continuation || continuation.mode === "fresh") {
|
|
3389
|
+
return void 0;
|
|
3390
|
+
}
|
|
3391
|
+
if (continuation.mode === "resume") {
|
|
3392
|
+
return continuation.handle;
|
|
3393
|
+
}
|
|
3394
|
+
throw new Error("Claude harness does not support reuse-current continuation");
|
|
3395
|
+
}
|
|
3396
|
+
|
|
3397
|
+
// src/harnesses/claude/process/useProcess.ts
|
|
3386
3398
|
var MAX_OUTPUT = 1e3;
|
|
3387
3399
|
var KILL_TIMEOUT_MS = 3e3;
|
|
3388
3400
|
var NULL_TOKENS = {
|
|
@@ -3433,17 +3445,6 @@ function tokenUsageEquals(a, b) {
|
|
|
3433
3445
|
return a.input === b.input && a.output === b.output && a.cacheRead === b.cacheRead && a.cacheWrite === b.cacheWrite && a.total === b.total && a.contextSize === b.contextSize && a.contextWindowSize === b.contextWindowSize;
|
|
3434
3446
|
}
|
|
3435
3447
|
var JQ_ASSISTANT_TEXT_FILTER = 'select(.type == "message" and .role == "assistant") | .content[] | select(.type == "text") | .text';
|
|
3436
|
-
function resolveClaudeSessionId(continuation) {
|
|
3437
|
-
if (!continuation || continuation.mode === "fresh") {
|
|
3438
|
-
return void 0;
|
|
3439
|
-
}
|
|
3440
|
-
if (continuation.mode === "resume") {
|
|
3441
|
-
return continuation.handle;
|
|
3442
|
-
}
|
|
3443
|
-
throw new Error(
|
|
3444
|
-
"Claude process hook does not support reuse-current continuation"
|
|
3445
|
-
);
|
|
3446
|
-
}
|
|
3447
3448
|
function useClaudeProcess(projectDir, instanceId, isolation, pluginMcpConfig, verbose, workflow, options) {
|
|
3448
3449
|
const processRef = useRef(null);
|
|
3449
3450
|
const abortRef = useRef(new AbortController());
|
|
@@ -3728,25 +3729,6 @@ function useClaudeProcess(projectDir, instanceId, isolation, pluginMcpConfig, ve
|
|
|
3728
3729
|
}
|
|
3729
3730
|
|
|
3730
3731
|
// src/harnesses/claude/session/controller.ts
|
|
3731
|
-
function mergeIsolation2(base, pluginMcpConfig, overrides) {
|
|
3732
|
-
if (!pluginMcpConfig && !overrides) return base;
|
|
3733
|
-
return {
|
|
3734
|
-
...resolveIsolationConfig(base),
|
|
3735
|
-
...overrides ?? {},
|
|
3736
|
-
...pluginMcpConfig ? { mcpConfig: pluginMcpConfig } : {}
|
|
3737
|
-
};
|
|
3738
|
-
}
|
|
3739
|
-
function resolveClaudeSessionId2(continuation) {
|
|
3740
|
-
if (!continuation || continuation.mode === "fresh") {
|
|
3741
|
-
return void 0;
|
|
3742
|
-
}
|
|
3743
|
-
if (continuation.mode === "resume") {
|
|
3744
|
-
return continuation.handle;
|
|
3745
|
-
}
|
|
3746
|
-
throw new Error(
|
|
3747
|
-
"Claude session controller does not support reuse-current continuation"
|
|
3748
|
-
);
|
|
3749
|
-
}
|
|
3750
3732
|
function createClaudeSessionController(input) {
|
|
3751
3733
|
const spawnProcess = input.spawnProcess ?? spawnClaude;
|
|
3752
3734
|
const processConfig = input.processConfig;
|
|
@@ -3791,8 +3773,8 @@ function createClaudeSessionController(input) {
|
|
|
3791
3773
|
prompt,
|
|
3792
3774
|
projectDir: input.projectDir,
|
|
3793
3775
|
instanceId: input.instanceId,
|
|
3794
|
-
sessionId:
|
|
3795
|
-
isolation:
|
|
3776
|
+
sessionId: resolveClaudeSessionId(continuation),
|
|
3777
|
+
isolation: mergeIsolation(
|
|
3796
3778
|
processConfig,
|
|
3797
3779
|
input.pluginMcpConfig,
|
|
3798
3780
|
configOverride
|
|
@@ -6751,6 +6733,74 @@ function buildCodexPromptOptions(input) {
|
|
|
6751
6733
|
};
|
|
6752
6734
|
}
|
|
6753
6735
|
|
|
6736
|
+
// src/harnesses/codex/session/turnRunner.ts
|
|
6737
|
+
function createCodexTurnEventCollector() {
|
|
6738
|
+
let message = "";
|
|
6739
|
+
let tokenDelta = { ...NULL_TOKENS2 };
|
|
6740
|
+
let turnStatus;
|
|
6741
|
+
let turnErrorMessage;
|
|
6742
|
+
return {
|
|
6743
|
+
handle(event) {
|
|
6744
|
+
const data = typeof event.data === "object" ? event.data : {};
|
|
6745
|
+
if (event.kind === "message.delta") {
|
|
6746
|
+
const delta = typeof data["delta"] === "string" ? data["delta"] : "";
|
|
6747
|
+
message += delta;
|
|
6748
|
+
}
|
|
6749
|
+
if (event.kind === "usage.update") {
|
|
6750
|
+
tokenDelta = readTokenUsage(data["delta"]);
|
|
6751
|
+
}
|
|
6752
|
+
if (event.kind === "turn.complete") {
|
|
6753
|
+
turnStatus = typeof data["status"] === "string" ? data["status"] : turnStatus;
|
|
6754
|
+
}
|
|
6755
|
+
if (event.kind === "unknown" && event.hookName === "error") {
|
|
6756
|
+
const payload = typeof data["payload"] === "object" && data["payload"] !== null ? data["payload"] : null;
|
|
6757
|
+
const errorValue = typeof payload?.["error"] === "object" && payload["error"] !== null ? payload["error"] : null;
|
|
6758
|
+
if (typeof errorValue?.["message"] === "string") {
|
|
6759
|
+
turnErrorMessage = errorValue["message"];
|
|
6760
|
+
}
|
|
6761
|
+
}
|
|
6762
|
+
},
|
|
6763
|
+
result() {
|
|
6764
|
+
if (turnStatus === "failed") {
|
|
6765
|
+
return {
|
|
6766
|
+
exitCode: 1,
|
|
6767
|
+
error: new Error(turnErrorMessage ?? "Codex turn failed"),
|
|
6768
|
+
tokens: tokenDelta,
|
|
6769
|
+
streamMessage: message || null
|
|
6770
|
+
};
|
|
6771
|
+
}
|
|
6772
|
+
return {
|
|
6773
|
+
exitCode: 0,
|
|
6774
|
+
error: null,
|
|
6775
|
+
tokens: tokenDelta,
|
|
6776
|
+
streamMessage: message || null
|
|
6777
|
+
};
|
|
6778
|
+
},
|
|
6779
|
+
errorResult(error) {
|
|
6780
|
+
return {
|
|
6781
|
+
exitCode: null,
|
|
6782
|
+
error,
|
|
6783
|
+
tokens: tokenDelta,
|
|
6784
|
+
streamMessage: message || null
|
|
6785
|
+
};
|
|
6786
|
+
}
|
|
6787
|
+
};
|
|
6788
|
+
}
|
|
6789
|
+
async function runCodexTurn(runtime, prompt, optionsInput, hooks) {
|
|
6790
|
+
const collector = createCodexTurnEventCollector();
|
|
6791
|
+
const unsubscribe = runtime.onEvent(collector.handle);
|
|
6792
|
+
try {
|
|
6793
|
+
await runtime.sendPrompt(prompt, buildCodexPromptOptions(optionsInput));
|
|
6794
|
+
return collector.result();
|
|
6795
|
+
} catch (error) {
|
|
6796
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
6797
|
+
hooks?.onError?.(normalized);
|
|
6798
|
+
return collector.errorResult(normalized);
|
|
6799
|
+
} finally {
|
|
6800
|
+
unsubscribe();
|
|
6801
|
+
}
|
|
6802
|
+
}
|
|
6803
|
+
|
|
6754
6804
|
// src/harnesses/codex/session/controller.ts
|
|
6755
6805
|
function createCodexSessionController(input) {
|
|
6756
6806
|
const runtime = input.runtime;
|
|
@@ -6770,73 +6820,19 @@ function createCodexSessionController(input) {
|
|
|
6770
6820
|
streamMessage: null
|
|
6771
6821
|
};
|
|
6772
6822
|
}
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
const delta = typeof data["delta"] === "string" ? data["delta"] : "";
|
|
6781
|
-
message += delta;
|
|
6782
|
-
}
|
|
6783
|
-
if (event.kind === "usage.update") {
|
|
6784
|
-
tokenDelta = readTokenUsage(data["delta"]);
|
|
6785
|
-
}
|
|
6786
|
-
if (event.kind === "turn.complete") {
|
|
6787
|
-
turnStatus = typeof data["status"] === "string" ? data["status"] : turnStatus;
|
|
6788
|
-
}
|
|
6789
|
-
if (event.kind === "unknown" && event.hookName === "error") {
|
|
6790
|
-
const payload = typeof data["payload"] === "object" && data["payload"] !== null ? data["payload"] : null;
|
|
6791
|
-
const errorValue = typeof payload?.["error"] === "object" && payload["error"] !== null ? payload["error"] : null;
|
|
6792
|
-
if (typeof errorValue?.["message"] === "string") {
|
|
6793
|
-
turnErrorMessage = errorValue["message"];
|
|
6794
|
-
}
|
|
6795
|
-
}
|
|
6823
|
+
const turnPromise = runCodexTurn(runtime, prompt, {
|
|
6824
|
+
processConfig,
|
|
6825
|
+
continuation,
|
|
6826
|
+
configOverride,
|
|
6827
|
+
workflowPlan: input.workflowPlan,
|
|
6828
|
+
pluginMcpConfig: input.pluginMcpConfig,
|
|
6829
|
+
ephemeral: input.ephemeral
|
|
6796
6830
|
});
|
|
6797
|
-
const turnPromise = (async () => {
|
|
6798
|
-
try {
|
|
6799
|
-
await runtime.sendPrompt(
|
|
6800
|
-
prompt,
|
|
6801
|
-
buildCodexPromptOptions({
|
|
6802
|
-
processConfig,
|
|
6803
|
-
continuation,
|
|
6804
|
-
configOverride,
|
|
6805
|
-
workflowPlan: input.workflowPlan,
|
|
6806
|
-
pluginMcpConfig: input.pluginMcpConfig,
|
|
6807
|
-
ephemeral: input.ephemeral
|
|
6808
|
-
})
|
|
6809
|
-
);
|
|
6810
|
-
if (turnStatus === "failed") {
|
|
6811
|
-
return {
|
|
6812
|
-
exitCode: 1,
|
|
6813
|
-
error: new Error(turnErrorMessage ?? "Codex turn failed"),
|
|
6814
|
-
tokens: tokenDelta,
|
|
6815
|
-
streamMessage: message || null
|
|
6816
|
-
};
|
|
6817
|
-
}
|
|
6818
|
-
return {
|
|
6819
|
-
exitCode: 0,
|
|
6820
|
-
error: null,
|
|
6821
|
-
tokens: tokenDelta,
|
|
6822
|
-
streamMessage: message || null
|
|
6823
|
-
};
|
|
6824
|
-
} catch (error) {
|
|
6825
|
-
return {
|
|
6826
|
-
exitCode: null,
|
|
6827
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
6828
|
-
tokens: tokenDelta,
|
|
6829
|
-
streamMessage: message || null
|
|
6830
|
-
};
|
|
6831
|
-
} finally {
|
|
6832
|
-
activeTurnPromise = null;
|
|
6833
|
-
}
|
|
6834
|
-
})();
|
|
6835
6831
|
activeTurnPromise = turnPromise;
|
|
6836
6832
|
try {
|
|
6837
6833
|
return await turnPromise;
|
|
6838
6834
|
} finally {
|
|
6839
|
-
|
|
6835
|
+
activeTurnPromise = null;
|
|
6840
6836
|
}
|
|
6841
6837
|
},
|
|
6842
6838
|
interrupt() {
|
|
@@ -6893,81 +6889,38 @@ function useCodexSessionController(runtime, processConfig, workflowPlan, ephemer
|
|
|
6893
6889
|
};
|
|
6894
6890
|
}
|
|
6895
6891
|
setIsRunning(true);
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
turnErrorMessage = errorValue["message"];
|
|
6917
|
-
}
|
|
6918
|
-
}
|
|
6919
|
-
});
|
|
6920
|
-
const turnPromise = (async () => {
|
|
6921
|
-
try {
|
|
6922
|
-
await codexRuntime.sendPrompt(
|
|
6923
|
-
prompt,
|
|
6924
|
-
buildCodexPromptOptions({
|
|
6925
|
-
processConfig,
|
|
6926
|
-
continuation,
|
|
6927
|
-
configOverride: _configOverride,
|
|
6928
|
-
workflowPlan,
|
|
6929
|
-
pluginMcpConfig,
|
|
6930
|
-
ephemeral
|
|
6931
|
-
})
|
|
6932
|
-
);
|
|
6933
|
-
if (turnStatus === "failed") {
|
|
6934
|
-
return {
|
|
6935
|
-
exitCode: 1,
|
|
6936
|
-
error: new Error(turnErrorMessage ?? "Codex turn failed"),
|
|
6937
|
-
tokens: turnTokens,
|
|
6938
|
-
streamMessage: streamedMessage || null
|
|
6939
|
-
};
|
|
6940
|
-
}
|
|
6941
|
-
return {
|
|
6942
|
-
exitCode: 0,
|
|
6943
|
-
error: null,
|
|
6944
|
-
tokens: turnTokens,
|
|
6945
|
-
streamMessage: streamedMessage || null
|
|
6946
|
-
};
|
|
6947
|
-
} catch (error) {
|
|
6948
|
-
if (!abortRef.current.signal.aborted) {
|
|
6949
|
-
onLifecycleEventRef.current?.({
|
|
6950
|
-
type: "spawn_error",
|
|
6951
|
-
message: error instanceof Error ? error.message : "Unknown Codex error",
|
|
6952
|
-
failureCode: "spawn_error"
|
|
6953
|
-
});
|
|
6954
|
-
}
|
|
6955
|
-
return {
|
|
6956
|
-
exitCode: null,
|
|
6957
|
-
error: error instanceof Error ? error : new Error("Unknown Codex error"),
|
|
6958
|
-
tokens: turnTokens,
|
|
6959
|
-
streamMessage: streamedMessage || null
|
|
6960
|
-
};
|
|
6961
|
-
} finally {
|
|
6962
|
-
activeTurnPromiseRef.current = null;
|
|
6963
|
-
unsubscribe();
|
|
6964
|
-
if (!abortRef.current.signal.aborted) {
|
|
6965
|
-
setIsRunning(false);
|
|
6892
|
+
const turnPromise = runCodexTurn(
|
|
6893
|
+
codexRuntime,
|
|
6894
|
+
prompt,
|
|
6895
|
+
{
|
|
6896
|
+
processConfig,
|
|
6897
|
+
continuation,
|
|
6898
|
+
configOverride: _configOverride,
|
|
6899
|
+
workflowPlan,
|
|
6900
|
+
pluginMcpConfig,
|
|
6901
|
+
ephemeral
|
|
6902
|
+
},
|
|
6903
|
+
{
|
|
6904
|
+
onError: (error) => {
|
|
6905
|
+
if (!abortRef.current.signal.aborted) {
|
|
6906
|
+
onLifecycleEventRef.current?.({
|
|
6907
|
+
type: "spawn_error",
|
|
6908
|
+
message: error.message,
|
|
6909
|
+
failureCode: "spawn_error"
|
|
6910
|
+
});
|
|
6911
|
+
}
|
|
6966
6912
|
}
|
|
6967
6913
|
}
|
|
6968
|
-
|
|
6914
|
+
);
|
|
6969
6915
|
activeTurnPromiseRef.current = turnPromise;
|
|
6970
|
-
|
|
6916
|
+
try {
|
|
6917
|
+
return await turnPromise;
|
|
6918
|
+
} finally {
|
|
6919
|
+
activeTurnPromiseRef.current = null;
|
|
6920
|
+
if (!abortRef.current.signal.aborted) {
|
|
6921
|
+
setIsRunning(false);
|
|
6922
|
+
}
|
|
6923
|
+
}
|
|
6971
6924
|
},
|
|
6972
6925
|
[codexRuntime, processConfig, workflowPlan, pluginMcpConfig, ephemeral]
|
|
6973
6926
|
);
|
|
@@ -7491,7 +7444,7 @@ var EXEC_EXIT_CODE = {
|
|
|
7491
7444
|
|
|
7492
7445
|
// src/app/exec/runner.ts
|
|
7493
7446
|
import crypto2 from "crypto";
|
|
7494
|
-
import
|
|
7447
|
+
import path17 from "path";
|
|
7495
7448
|
|
|
7496
7449
|
// src/core/feed/entities.ts
|
|
7497
7450
|
var ActorRegistry = class {
|
|
@@ -8336,28 +8289,11 @@ function createAgentMessageStream(eventBuilder, transcriptReader) {
|
|
|
8336
8289
|
};
|
|
8337
8290
|
}
|
|
8338
8291
|
|
|
8339
|
-
// src/core/feed/internals/
|
|
8340
|
-
function
|
|
8341
|
-
|
|
8342
|
-
return
|
|
8343
|
-
set(next) {
|
|
8344
|
-
items = next;
|
|
8345
|
-
},
|
|
8346
|
-
current() {
|
|
8347
|
-
return items;
|
|
8348
|
-
},
|
|
8349
|
-
differs(next) {
|
|
8350
|
-
if (next.length !== items.length) return true;
|
|
8351
|
-
for (let i = 0; i < next.length; i++) {
|
|
8352
|
-
if (next[i]?.content !== items[i]?.content) return true;
|
|
8353
|
-
if (next[i]?.status !== items[i]?.status) return true;
|
|
8354
|
-
}
|
|
8355
|
-
return false;
|
|
8356
|
-
}
|
|
8357
|
-
};
|
|
8292
|
+
// src/core/feed/internals/taskStateTracker.ts
|
|
8293
|
+
function extractTodoItems(toolInput) {
|
|
8294
|
+
const input = toolInput;
|
|
8295
|
+
return Array.isArray(input?.todos) ? input.todos : [];
|
|
8358
8296
|
}
|
|
8359
|
-
|
|
8360
|
-
// src/core/feed/internals/taskLifecycleTracker.ts
|
|
8361
8297
|
function coerceTaskStatus(status) {
|
|
8362
8298
|
switch (status) {
|
|
8363
8299
|
case "pending":
|
|
@@ -8369,57 +8305,6 @@ function coerceTaskStatus(status) {
|
|
|
8369
8305
|
return null;
|
|
8370
8306
|
}
|
|
8371
8307
|
}
|
|
8372
|
-
function createTaskLifecycleTracker() {
|
|
8373
|
-
let items = [];
|
|
8374
|
-
return {
|
|
8375
|
-
current() {
|
|
8376
|
-
return items;
|
|
8377
|
-
},
|
|
8378
|
-
upsertCreated({ taskId, subject, description, activeForm }) {
|
|
8379
|
-
const task = {
|
|
8380
|
-
taskId,
|
|
8381
|
-
content: subject,
|
|
8382
|
-
status: "pending",
|
|
8383
|
-
activeForm: activeForm ?? description
|
|
8384
|
-
};
|
|
8385
|
-
const existingIndex = items.findIndex((item) => item.taskId === taskId);
|
|
8386
|
-
if (existingIndex === -1) {
|
|
8387
|
-
items = [...items, task];
|
|
8388
|
-
return;
|
|
8389
|
-
}
|
|
8390
|
-
items = items.map(
|
|
8391
|
-
(item, index) => index === existingIndex ? {
|
|
8392
|
-
...item,
|
|
8393
|
-
taskId,
|
|
8394
|
-
content: subject,
|
|
8395
|
-
activeForm: task.activeForm ?? item.activeForm
|
|
8396
|
-
} : item
|
|
8397
|
-
);
|
|
8398
|
-
},
|
|
8399
|
-
markCompleted({ taskId, subject }) {
|
|
8400
|
-
const existingIndex = items.findIndex((item) => item.taskId === taskId);
|
|
8401
|
-
if (existingIndex === -1) {
|
|
8402
|
-
if (!subject) return;
|
|
8403
|
-
items = [...items, { taskId, content: subject, status: "completed" }];
|
|
8404
|
-
return;
|
|
8405
|
-
}
|
|
8406
|
-
items = items.map(
|
|
8407
|
-
(item, index) => index === existingIndex ? { ...item, status: "completed" } : item
|
|
8408
|
-
);
|
|
8409
|
-
},
|
|
8410
|
-
updateStatus({ taskId, status }) {
|
|
8411
|
-
items = items.map(
|
|
8412
|
-
(item) => item.taskId === taskId ? { ...item, status } : item
|
|
8413
|
-
);
|
|
8414
|
-
}
|
|
8415
|
-
};
|
|
8416
|
-
}
|
|
8417
|
-
|
|
8418
|
-
// src/core/feed/internals/taskStateTracker.ts
|
|
8419
|
-
function extractTodoItems(toolInput) {
|
|
8420
|
-
const input = toolInput;
|
|
8421
|
-
return Array.isArray(input?.todos) ? input.todos : [];
|
|
8422
|
-
}
|
|
8423
8308
|
function mapPlanStepStatus(status) {
|
|
8424
8309
|
switch (status) {
|
|
8425
8310
|
case "inProgress":
|
|
@@ -8432,18 +8317,69 @@ function mapPlanStepStatus(status) {
|
|
|
8432
8317
|
}
|
|
8433
8318
|
}
|
|
8434
8319
|
function createTaskStateTracker() {
|
|
8435
|
-
|
|
8436
|
-
|
|
8320
|
+
let rootPlanItems = [];
|
|
8321
|
+
let taskItems = [];
|
|
8322
|
+
function rootPlanDiffers(next) {
|
|
8323
|
+
if (next.length !== rootPlanItems.length) return true;
|
|
8324
|
+
for (let i = 0; i < next.length; i++) {
|
|
8325
|
+
if (next[i]?.content !== rootPlanItems[i]?.content) return true;
|
|
8326
|
+
if (next[i]?.status !== rootPlanItems[i]?.status) return true;
|
|
8327
|
+
}
|
|
8328
|
+
return false;
|
|
8329
|
+
}
|
|
8330
|
+
function upsertCreatedTask(input) {
|
|
8331
|
+
const { taskId, subject, description, activeForm } = input;
|
|
8332
|
+
const task = {
|
|
8333
|
+
taskId,
|
|
8334
|
+
content: subject,
|
|
8335
|
+
status: "pending",
|
|
8336
|
+
activeForm: activeForm ?? description
|
|
8337
|
+
};
|
|
8338
|
+
const existingIndex = taskItems.findIndex((item) => item.taskId === taskId);
|
|
8339
|
+
if (existingIndex === -1) {
|
|
8340
|
+
taskItems = [...taskItems, task];
|
|
8341
|
+
return;
|
|
8342
|
+
}
|
|
8343
|
+
taskItems = taskItems.map(
|
|
8344
|
+
(item, index) => index === existingIndex ? {
|
|
8345
|
+
...item,
|
|
8346
|
+
taskId,
|
|
8347
|
+
content: subject,
|
|
8348
|
+
activeForm: task.activeForm ?? item.activeForm
|
|
8349
|
+
} : item
|
|
8350
|
+
);
|
|
8351
|
+
}
|
|
8352
|
+
function markTaskCompleted(input) {
|
|
8353
|
+
const { taskId, subject } = input;
|
|
8354
|
+
const existingIndex = taskItems.findIndex((item) => item.taskId === taskId);
|
|
8355
|
+
if (existingIndex === -1) {
|
|
8356
|
+
if (!subject) return;
|
|
8357
|
+
taskItems = [
|
|
8358
|
+
...taskItems,
|
|
8359
|
+
{ taskId, content: subject, status: "completed" }
|
|
8360
|
+
];
|
|
8361
|
+
return;
|
|
8362
|
+
}
|
|
8363
|
+
taskItems = taskItems.map(
|
|
8364
|
+
(item, index) => index === existingIndex ? { ...item, status: "completed" } : item
|
|
8365
|
+
);
|
|
8366
|
+
}
|
|
8367
|
+
function updateTaskStatus(input) {
|
|
8368
|
+
const { taskId, status } = input;
|
|
8369
|
+
taskItems = taskItems.map(
|
|
8370
|
+
(item) => item.taskId === taskId ? { ...item, status } : item
|
|
8371
|
+
);
|
|
8372
|
+
}
|
|
8437
8373
|
function applyToolPre(input) {
|
|
8438
8374
|
const { toolName, toolInput, actorId } = input;
|
|
8439
8375
|
if (toolName === "TodoWrite" && actorId === "agent:root") {
|
|
8440
|
-
|
|
8376
|
+
rootPlanItems = extractTodoItems(toolInput);
|
|
8441
8377
|
}
|
|
8442
8378
|
if (toolName === "TaskUpdate") {
|
|
8443
8379
|
const taskId = readString(toolInput["taskId"], toolInput["task_id"]);
|
|
8444
8380
|
const status = coerceTaskStatus(toolInput["status"]);
|
|
8445
8381
|
if (taskId && status) {
|
|
8446
|
-
|
|
8382
|
+
updateTaskStatus({ taskId, status });
|
|
8447
8383
|
}
|
|
8448
8384
|
}
|
|
8449
8385
|
}
|
|
@@ -8455,7 +8391,7 @@ function createTaskStateTracker() {
|
|
|
8455
8391
|
const taskId = readString(task["id"], task["task_id"]);
|
|
8456
8392
|
const subject = readString(task["subject"], toolInput["subject"]);
|
|
8457
8393
|
if (taskId && subject) {
|
|
8458
|
-
|
|
8394
|
+
upsertCreatedTask({
|
|
8459
8395
|
taskId,
|
|
8460
8396
|
subject,
|
|
8461
8397
|
description: readString(toolInput["description"]),
|
|
@@ -8475,7 +8411,7 @@ function createTaskStateTracker() {
|
|
|
8475
8411
|
readObject(response["statusChange"])["to"] ?? toolInput["status"]
|
|
8476
8412
|
);
|
|
8477
8413
|
if (taskId && status) {
|
|
8478
|
-
|
|
8414
|
+
updateTaskStatus({ taskId, status });
|
|
8479
8415
|
}
|
|
8480
8416
|
}
|
|
8481
8417
|
}
|
|
@@ -8484,19 +8420,19 @@ function createTaskStateTracker() {
|
|
|
8484
8420
|
const subject = readString(data["task_subject"]);
|
|
8485
8421
|
const description = readString(data["task_description"]);
|
|
8486
8422
|
if (taskId && subject) {
|
|
8487
|
-
|
|
8423
|
+
upsertCreatedTask({ taskId, subject, description });
|
|
8488
8424
|
}
|
|
8489
8425
|
}
|
|
8490
8426
|
function applyTaskCompletedEvent(data) {
|
|
8491
8427
|
const taskId = readString(data["task_id"]);
|
|
8492
8428
|
const subject = readString(data["task_subject"]);
|
|
8493
8429
|
if (taskId) {
|
|
8494
|
-
|
|
8430
|
+
markTaskCompleted({ taskId, subject });
|
|
8495
8431
|
}
|
|
8496
8432
|
}
|
|
8497
8433
|
return {
|
|
8498
8434
|
current() {
|
|
8499
|
-
return [...
|
|
8435
|
+
return [...rootPlanItems, ...taskItems];
|
|
8500
8436
|
},
|
|
8501
8437
|
applyToolPre,
|
|
8502
8438
|
applyToolPost,
|
|
@@ -8506,8 +8442,8 @@ function createTaskStateTracker() {
|
|
|
8506
8442
|
content: typeof step.step === "string" ? step.step : "",
|
|
8507
8443
|
status: mapPlanStepStatus(step.status)
|
|
8508
8444
|
}));
|
|
8509
|
-
if (!
|
|
8510
|
-
|
|
8445
|
+
if (!rootPlanDiffers(next)) return false;
|
|
8446
|
+
rootPlanItems = next;
|
|
8511
8447
|
return true;
|
|
8512
8448
|
},
|
|
8513
8449
|
applyTaskCreatedEvent,
|
|
@@ -8538,48 +8474,6 @@ function createTaskStateTracker() {
|
|
|
8538
8474
|
};
|
|
8539
8475
|
}
|
|
8540
8476
|
|
|
8541
|
-
// src/core/feed/internals/subagentTracker.ts
|
|
8542
|
-
function createSubagentTracker() {
|
|
8543
|
-
const stack = [];
|
|
8544
|
-
const descriptions = /* @__PURE__ */ new Map();
|
|
8545
|
-
let pendingDescription;
|
|
8546
|
-
return {
|
|
8547
|
-
pushActor(actorId) {
|
|
8548
|
-
stack.push(actorId);
|
|
8549
|
-
},
|
|
8550
|
-
popActor(actorId) {
|
|
8551
|
-
const idx = stack.lastIndexOf(actorId);
|
|
8552
|
-
if (idx !== -1) stack.splice(idx, 1);
|
|
8553
|
-
},
|
|
8554
|
-
peek() {
|
|
8555
|
-
return stack.length > 0 ? stack[stack.length - 1] : void 0;
|
|
8556
|
-
},
|
|
8557
|
-
clear() {
|
|
8558
|
-
stack.length = 0;
|
|
8559
|
-
},
|
|
8560
|
-
currentScope() {
|
|
8561
|
-
return stack.length > 0 ? "subagent" : "root";
|
|
8562
|
-
},
|
|
8563
|
-
recordPendingDescription(description) {
|
|
8564
|
-
pendingDescription = description;
|
|
8565
|
-
},
|
|
8566
|
-
clearPendingDescription() {
|
|
8567
|
-
pendingDescription = void 0;
|
|
8568
|
-
},
|
|
8569
|
-
consumePendingDescription() {
|
|
8570
|
-
const value = pendingDescription;
|
|
8571
|
-
pendingDescription = void 0;
|
|
8572
|
-
return value;
|
|
8573
|
-
},
|
|
8574
|
-
setDescription(agentId, description) {
|
|
8575
|
-
descriptions.set(agentId, description);
|
|
8576
|
-
},
|
|
8577
|
-
description(agentId) {
|
|
8578
|
-
return descriptions.get(agentId);
|
|
8579
|
-
}
|
|
8580
|
-
};
|
|
8581
|
-
}
|
|
8582
|
-
|
|
8583
8477
|
// src/core/feed/todo.ts
|
|
8584
8478
|
function isSubagentTool(toolName) {
|
|
8585
8479
|
return toolName === "Task" || toolName === "Agent";
|
|
@@ -8588,44 +8482,48 @@ function isSubagentTool(toolName) {
|
|
|
8588
8482
|
// src/core/feed/internals/subagentLifecycle.ts
|
|
8589
8483
|
function createSubagentLifecycle(args) {
|
|
8590
8484
|
const { actors, runLifecycle } = args;
|
|
8591
|
-
const
|
|
8485
|
+
const stack = [];
|
|
8486
|
+
const descriptions = /* @__PURE__ */ new Map();
|
|
8487
|
+
let pendingDescription;
|
|
8592
8488
|
const actorIdFor = (agentId) => `subagent:${agentId}`;
|
|
8593
8489
|
return {
|
|
8594
8490
|
observeToolInput(toolName, toolInput) {
|
|
8595
8491
|
if (!isSubagentTool(toolName)) return;
|
|
8596
|
-
|
|
8597
|
-
tracker.recordPendingDescription(toolInput["description"]);
|
|
8598
|
-
} else {
|
|
8599
|
-
tracker.clearPendingDescription();
|
|
8600
|
-
}
|
|
8492
|
+
pendingDescription = typeof toolInput["description"] === "string" ? toolInput["description"] : void 0;
|
|
8601
8493
|
},
|
|
8602
8494
|
startSubagent({ agentId, agentType, fallbackDescription }) {
|
|
8603
|
-
const
|
|
8495
|
+
const consumed = pendingDescription;
|
|
8496
|
+
pendingDescription = void 0;
|
|
8497
|
+
const description = consumed ?? fallbackDescription;
|
|
8604
8498
|
if (agentId) {
|
|
8605
8499
|
actors.ensureSubagent(agentId, agentType ?? "unknown");
|
|
8606
8500
|
const currentRun = runLifecycle.getCurrentRun();
|
|
8607
8501
|
if (currentRun) currentRun.actors.subagent_ids.push(agentId);
|
|
8608
|
-
|
|
8609
|
-
if (description)
|
|
8502
|
+
stack.push(actorIdFor(agentId));
|
|
8503
|
+
if (description) descriptions.set(agentId, description);
|
|
8610
8504
|
}
|
|
8611
8505
|
return { actorId: "agent:root", description: description ?? void 0 };
|
|
8612
8506
|
},
|
|
8613
8507
|
stopSubagent(agentId) {
|
|
8614
|
-
if (agentId)
|
|
8508
|
+
if (agentId) {
|
|
8509
|
+
const actorId = actorIdFor(agentId);
|
|
8510
|
+
const idx = stack.lastIndexOf(actorId);
|
|
8511
|
+
if (idx !== -1) stack.splice(idx, 1);
|
|
8512
|
+
}
|
|
8615
8513
|
return {
|
|
8616
8514
|
actorId: actorIdFor(agentId ?? "unknown"),
|
|
8617
|
-
description:
|
|
8515
|
+
description: descriptions.get(agentId ?? "")
|
|
8618
8516
|
};
|
|
8619
8517
|
},
|
|
8620
8518
|
currentActor() {
|
|
8621
|
-
return
|
|
8519
|
+
return stack.at(-1) ?? "agent:root";
|
|
8622
8520
|
},
|
|
8623
8521
|
currentScope() {
|
|
8624
|
-
return
|
|
8522
|
+
return stack.length > 0 ? "subagent" : "root";
|
|
8625
8523
|
},
|
|
8626
8524
|
actorIdFor,
|
|
8627
8525
|
clear() {
|
|
8628
|
-
|
|
8526
|
+
stack.length = 0;
|
|
8629
8527
|
}
|
|
8630
8528
|
};
|
|
8631
8529
|
}
|
|
@@ -9978,193 +9876,18 @@ function createFeedMapper(bootstrap) {
|
|
|
9978
9876
|
};
|
|
9979
9877
|
}
|
|
9980
9878
|
|
|
9981
|
-
// src/
|
|
9982
|
-
|
|
9983
|
-
|
|
9984
|
-
|
|
9985
|
-
|
|
9986
|
-
|
|
9987
|
-
|
|
9988
|
-
|
|
9989
|
-
|
|
9990
|
-
|
|
9991
|
-
|
|
9992
|
-
|
|
9993
|
-
(r) => r.action === "deny" && ruleMatches(r.toolName, toolName)
|
|
9994
|
-
);
|
|
9995
|
-
if (denyMatch) return denyMatch;
|
|
9996
|
-
return rules.find(
|
|
9997
|
-
(r) => r.action === "approve" && ruleMatches(r.toolName, toolName)
|
|
9998
|
-
);
|
|
9999
|
-
}
|
|
10000
|
-
|
|
10001
|
-
// src/core/controller/permission.ts
|
|
10002
|
-
var SESSION_APPROVAL_REQUEST_HOOKS = /* @__PURE__ */ new Set([
|
|
10003
|
-
"item/commandExecution/requestApproval",
|
|
10004
|
-
"item/fileChange/requestApproval",
|
|
10005
|
-
"item/permissions/requestApproval"
|
|
10006
|
-
]);
|
|
10007
|
-
function isScopedPermissionsRequest(hookName) {
|
|
10008
|
-
return hookName === "item/permissions/requestApproval";
|
|
10009
|
-
}
|
|
10010
|
-
function supportsSessionApproval(hookName) {
|
|
10011
|
-
return hookName !== void 0 && SESSION_APPROVAL_REQUEST_HOOKS.has(hookName);
|
|
10012
|
-
}
|
|
10013
|
-
function extractPermissionSnapshot(event) {
|
|
10014
|
-
const data = event.data;
|
|
10015
|
-
const toolNameFromData = typeof data["tool_name"] === "string" ? data["tool_name"] : void 0;
|
|
10016
|
-
const toolInputFromData = typeof data["tool_input"] === "object" && data["tool_input"] !== null ? data["tool_input"] : void 0;
|
|
10017
|
-
const toolUseIdFromData = typeof data["tool_use_id"] === "string" ? data["tool_use_id"] : void 0;
|
|
10018
|
-
return {
|
|
10019
|
-
request_id: event.id,
|
|
10020
|
-
ts: event.timestamp,
|
|
10021
|
-
kind: event.kind,
|
|
10022
|
-
hookName: event.hookName,
|
|
10023
|
-
tool_name: event.toolName ?? toolNameFromData ?? "Unknown",
|
|
10024
|
-
tool_input: toolInputFromData ?? {},
|
|
10025
|
-
tool_use_id: event.toolUseId ?? toolUseIdFromData,
|
|
10026
|
-
suggestions: data.permission_suggestions
|
|
10027
|
-
};
|
|
10028
|
-
}
|
|
10029
|
-
|
|
10030
|
-
// src/core/controller/runtimeController.ts
|
|
10031
|
-
function handleEvent(event, cb) {
|
|
10032
|
-
const eventKind2 = event.kind;
|
|
10033
|
-
const isScoped = isScopedPermissionsRequest(event.hookName);
|
|
10034
|
-
const eventData = event.data;
|
|
10035
|
-
const toolName = event.toolName ?? (typeof eventData["tool_name"] === "string" ? eventData["tool_name"] : void 0);
|
|
10036
|
-
if (eventKind2 === "permission.request" && toolName === "user_input") {
|
|
10037
|
-
cb.enqueueQuestion(event.id);
|
|
10038
|
-
cb.relayQuestion?.(event);
|
|
10039
|
-
return { handled: true };
|
|
10040
|
-
}
|
|
10041
|
-
if (eventKind2 === "permission.request" && toolName) {
|
|
10042
|
-
const rule = matchRule(cb.getRules(), toolName);
|
|
10043
|
-
if (rule?.action === "deny") {
|
|
10044
|
-
return {
|
|
10045
|
-
handled: true,
|
|
10046
|
-
decision: {
|
|
10047
|
-
type: "json",
|
|
10048
|
-
source: "rule",
|
|
10049
|
-
intent: {
|
|
10050
|
-
kind: "permission_deny",
|
|
10051
|
-
reason: `Blocked by rule: ${rule.addedBy}`
|
|
10052
|
-
}
|
|
10053
|
-
}
|
|
10054
|
-
};
|
|
10055
|
-
}
|
|
10056
|
-
if (rule?.action === "approve") {
|
|
10057
|
-
return {
|
|
10058
|
-
handled: true,
|
|
10059
|
-
decision: {
|
|
10060
|
-
type: "json",
|
|
10061
|
-
source: "rule",
|
|
10062
|
-
intent: { kind: "permission_allow" },
|
|
10063
|
-
// Scoped Codex permission grants are session-scoped under
|
|
10064
|
-
// auto rules so the same capability isn't re-prompted.
|
|
10065
|
-
...isScoped ? { data: { scope: "session" } } : {}
|
|
10066
|
-
}
|
|
10067
|
-
};
|
|
10068
|
-
}
|
|
10069
|
-
cb.enqueuePermission(event);
|
|
10070
|
-
cb.relayPermission?.(event);
|
|
10071
|
-
return { handled: true };
|
|
10072
|
-
}
|
|
10073
|
-
if (eventKind2 === "tool.pre" && toolName === "AskUserQuestion") {
|
|
10074
|
-
cb.enqueueQuestion(event.id);
|
|
10075
|
-
cb.relayQuestion?.(event);
|
|
10076
|
-
return { handled: true };
|
|
10077
|
-
}
|
|
10078
|
-
if (eventKind2 === "tool.pre" && toolName) {
|
|
10079
|
-
const rule = matchRule(cb.getRules(), toolName);
|
|
10080
|
-
if (rule?.action === "deny") {
|
|
10081
|
-
return {
|
|
10082
|
-
handled: true,
|
|
10083
|
-
decision: {
|
|
10084
|
-
type: "json",
|
|
10085
|
-
source: "rule",
|
|
10086
|
-
intent: {
|
|
10087
|
-
kind: "pre_tool_deny",
|
|
10088
|
-
reason: `Blocked by rule: ${rule.addedBy}`
|
|
10089
|
-
}
|
|
10090
|
-
}
|
|
10091
|
-
};
|
|
10092
|
-
}
|
|
10093
|
-
return {
|
|
10094
|
-
handled: true,
|
|
10095
|
-
decision: {
|
|
10096
|
-
type: "json",
|
|
10097
|
-
source: "rule",
|
|
10098
|
-
intent: { kind: "pre_tool_allow" }
|
|
10099
|
-
}
|
|
10100
|
-
};
|
|
10101
|
-
}
|
|
10102
|
-
return { handled: false };
|
|
10103
|
-
}
|
|
10104
|
-
|
|
10105
|
-
// src/core/feed/ingest.ts
|
|
10106
|
-
function ingestRuntimeEvent(event, ctx) {
|
|
10107
|
-
const controllerResult = handleEvent(event, ctx.controllerCallbacks);
|
|
10108
|
-
const feedEvents = ctx.mapper.mapEvent(event);
|
|
10109
|
-
if (ctx.store) {
|
|
10110
|
-
persistOrDegrade(
|
|
10111
|
-
ctx.store,
|
|
10112
|
-
() => ctx.store.recordEvent(event, feedEvents),
|
|
10113
|
-
"recordEvent failed",
|
|
10114
|
-
ctx.onPersistFailure
|
|
10115
|
-
);
|
|
10116
|
-
}
|
|
10117
|
-
return {
|
|
10118
|
-
feedEvents,
|
|
10119
|
-
decision: controllerResult.handled && controllerResult.decision ? controllerResult.decision : null
|
|
10120
|
-
};
|
|
10121
|
-
}
|
|
10122
|
-
function ingestRuntimeDecision(eventId, decision, ctx) {
|
|
10123
|
-
const feedEvent = ctx.mapper.mapDecision(eventId, decision);
|
|
10124
|
-
if (feedEvent && ctx.store) {
|
|
10125
|
-
persistOrDegrade(
|
|
10126
|
-
ctx.store,
|
|
10127
|
-
() => ctx.store.recordFeedEvents([feedEvent]),
|
|
10128
|
-
"recordFeedEvents failed",
|
|
10129
|
-
ctx.onPersistFailure
|
|
10130
|
-
);
|
|
10131
|
-
}
|
|
10132
|
-
return feedEvent;
|
|
10133
|
-
}
|
|
10134
|
-
function persistOrDegrade(store, action, label, onFailure) {
|
|
10135
|
-
try {
|
|
10136
|
-
action();
|
|
10137
|
-
} catch (err) {
|
|
10138
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
10139
|
-
const message = `${label}: ${reason}`;
|
|
10140
|
-
store.markDegraded(message);
|
|
10141
|
-
onFailure?.(message);
|
|
10142
|
-
}
|
|
10143
|
-
}
|
|
10144
|
-
|
|
10145
|
-
// src/infra/sessions/store.ts
|
|
10146
|
-
import fs17 from "fs";
|
|
10147
|
-
import path14 from "path";
|
|
10148
|
-
import Database from "better-sqlite3";
|
|
10149
|
-
|
|
10150
|
-
// src/infra/sessions/schema.ts
|
|
10151
|
-
var SCHEMA_VERSION = 6;
|
|
10152
|
-
function initSchema(db) {
|
|
10153
|
-
db.exec("PRAGMA journal_mode = WAL");
|
|
10154
|
-
db.exec("PRAGMA foreign_keys = ON");
|
|
10155
|
-
db.exec(`
|
|
10156
|
-
CREATE TABLE IF NOT EXISTS schema_version (
|
|
10157
|
-
version INTEGER NOT NULL
|
|
10158
|
-
);
|
|
10159
|
-
|
|
10160
|
-
CREATE TABLE IF NOT EXISTS session (
|
|
10161
|
-
id TEXT PRIMARY KEY,
|
|
10162
|
-
project_dir TEXT NOT NULL,
|
|
10163
|
-
created_at INTEGER NOT NULL,
|
|
10164
|
-
updated_at INTEGER NOT NULL,
|
|
10165
|
-
label TEXT,
|
|
10166
|
-
event_count INTEGER DEFAULT 0
|
|
10167
|
-
);
|
|
9879
|
+
// src/infra/sessions/schema.ts
|
|
9880
|
+
var SCHEMA_VERSION = 6;
|
|
9881
|
+
var applySessionSchema = (db, fromVersion) => {
|
|
9882
|
+
db.exec(`
|
|
9883
|
+
CREATE TABLE IF NOT EXISTS session (
|
|
9884
|
+
id TEXT PRIMARY KEY,
|
|
9885
|
+
project_dir TEXT NOT NULL,
|
|
9886
|
+
created_at INTEGER NOT NULL,
|
|
9887
|
+
updated_at INTEGER NOT NULL,
|
|
9888
|
+
label TEXT,
|
|
9889
|
+
event_count INTEGER DEFAULT 0
|
|
9890
|
+
);
|
|
10168
9891
|
|
|
10169
9892
|
CREATE TABLE IF NOT EXISTS runtime_events (
|
|
10170
9893
|
id TEXT PRIMARY KEY,
|
|
@@ -10216,45 +9939,6 @@ function initSchema(db) {
|
|
|
10216
9939
|
FOREIGN KEY (session_id) REFERENCES session(id)
|
|
10217
9940
|
);
|
|
10218
9941
|
|
|
10219
|
-
-- Channel I/O ledger: every inbound chat normalized by an adapter and every
|
|
10220
|
-
-- outbound chat dispatched back to a provider gets one row. Idempotency
|
|
10221
|
-
-- key prevents double-delivery on retry/restart; session_id ties chat to a
|
|
10222
|
-
-- particular Athena interactive runtime when known.
|
|
10223
|
-
CREATE TABLE IF NOT EXISTS channel_messages (
|
|
10224
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
10225
|
-
channel_id TEXT NOT NULL,
|
|
10226
|
-
account_id TEXT NOT NULL,
|
|
10227
|
-
peer_id TEXT,
|
|
10228
|
-
room_id TEXT,
|
|
10229
|
-
thread_id TEXT,
|
|
10230
|
-
provider_message_id TEXT NOT NULL,
|
|
10231
|
-
direction TEXT NOT NULL CHECK(direction IN ('in','out')),
|
|
10232
|
-
session_id TEXT REFERENCES adapter_sessions(session_id),
|
|
10233
|
-
agent_id TEXT,
|
|
10234
|
-
idempotency_key TEXT,
|
|
10235
|
-
feed_event_id TEXT,
|
|
10236
|
-
created_at INTEGER NOT NULL
|
|
10237
|
-
);
|
|
10238
|
-
|
|
10239
|
-
-- Audit log for cloud function invocations brokered by the gateway. One
|
|
10240
|
-
-- row per invocation across all callers (agent tool, /run channel cmd,
|
|
10241
|
-
-- hook helper). Idempotency cache is in-memory + write-through here.
|
|
10242
|
-
CREATE TABLE IF NOT EXISTS gateway_function_invocations (
|
|
10243
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
10244
|
-
name TEXT NOT NULL,
|
|
10245
|
-
caller_kind TEXT NOT NULL CHECK(caller_kind IN ('agent','channel','hook')),
|
|
10246
|
-
session_id TEXT REFERENCES adapter_sessions(session_id),
|
|
10247
|
-
agent_id TEXT,
|
|
10248
|
-
idempotency_key TEXT,
|
|
10249
|
-
request_hash TEXT NOT NULL,
|
|
10250
|
-
status TEXT NOT NULL CHECK(status IN ('pending','ok','error','timeout')),
|
|
10251
|
-
http_status INTEGER,
|
|
10252
|
-
duration_ms INTEGER,
|
|
10253
|
-
error TEXT,
|
|
10254
|
-
started_at INTEGER NOT NULL,
|
|
10255
|
-
completed_at INTEGER
|
|
10256
|
-
);
|
|
10257
|
-
|
|
10258
9942
|
-- Durable retry queue for outbound channel sends. Drained by the gateway
|
|
10259
9943
|
-- daemon on startup and after transient send failures.
|
|
10260
9944
|
CREATE TABLE IF NOT EXISTS channel_outbox (
|
|
@@ -10274,127 +9958,78 @@ function initSchema(db) {
|
|
|
10274
9958
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_feed_seq ON feed_events(seq);
|
|
10275
9959
|
CREATE INDEX IF NOT EXISTS idx_runtime_seq ON runtime_events(seq);
|
|
10276
9960
|
CREATE INDEX IF NOT EXISTS idx_workflow_runs_session ON workflow_runs(session_id);
|
|
10277
|
-
CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_messages_idem
|
|
10278
|
-
ON channel_messages(channel_id, account_id, idempotency_key)
|
|
10279
|
-
WHERE idempotency_key IS NOT NULL;
|
|
10280
|
-
CREATE INDEX IF NOT EXISTS idx_channel_messages_session_key
|
|
10281
|
-
ON channel_messages(channel_id, account_id, peer_id, room_id, thread_id, created_at);
|
|
10282
|
-
CREATE UNIQUE INDEX IF NOT EXISTS idx_fn_idem
|
|
10283
|
-
ON gateway_function_invocations(name, idempotency_key)
|
|
10284
|
-
WHERE idempotency_key IS NOT NULL;
|
|
10285
9961
|
CREATE INDEX IF NOT EXISTS idx_outbox_due ON channel_outbox(next_attempt_at);
|
|
10286
9962
|
`);
|
|
10287
|
-
|
|
10288
|
-
if (
|
|
9963
|
+
if (fromVersion === void 0) return;
|
|
9964
|
+
if (fromVersion >= SCHEMA_VERSION) return;
|
|
9965
|
+
if (fromVersion < 2) {
|
|
10289
9966
|
throw new Error(
|
|
10290
|
-
`
|
|
9967
|
+
`Session database is at schema version ${fromVersion} which predates the first release. Delete the session database and start fresh.`
|
|
10291
9968
|
);
|
|
10292
9969
|
}
|
|
10293
|
-
if (
|
|
10294
|
-
db.
|
|
10295
|
-
|
|
10296
|
-
|
|
10297
|
-
|
|
10298
|
-
|
|
10299
|
-
|
|
10300
|
-
|
|
10301
|
-
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10307
|
-
|
|
10308
|
-
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
|
|
10322
|
-
|
|
10323
|
-
|
|
10324
|
-
|
|
10325
|
-
|
|
10326
|
-
|
|
10327
|
-
|
|
10328
|
-
|
|
10329
|
-
|
|
10330
|
-
|
|
10331
|
-
|
|
10332
|
-
|
|
10333
|
-
|
|
10334
|
-
|
|
10335
|
-
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
|
|
10341
|
-
|
|
10342
|
-
|
|
10343
|
-
|
|
10344
|
-
|
|
10345
|
-
|
|
10346
|
-
|
|
10347
|
-
account_id TEXT NOT NULL,
|
|
10348
|
-
peer_id TEXT,
|
|
10349
|
-
room_id TEXT,
|
|
10350
|
-
thread_id TEXT,
|
|
10351
|
-
provider_message_id TEXT NOT NULL,
|
|
10352
|
-
direction TEXT NOT NULL CHECK(direction IN ('in','out')),
|
|
10353
|
-
session_id TEXT REFERENCES adapter_sessions(session_id),
|
|
10354
|
-
agent_id TEXT,
|
|
10355
|
-
idempotency_key TEXT,
|
|
10356
|
-
feed_event_id TEXT,
|
|
10357
|
-
created_at INTEGER NOT NULL
|
|
10358
|
-
);
|
|
10359
|
-
CREATE TABLE IF NOT EXISTS gateway_function_invocations (
|
|
10360
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
10361
|
-
name TEXT NOT NULL,
|
|
10362
|
-
caller_kind TEXT NOT NULL CHECK(caller_kind IN ('agent','channel','hook')),
|
|
10363
|
-
session_id TEXT REFERENCES adapter_sessions(session_id),
|
|
10364
|
-
agent_id TEXT,
|
|
10365
|
-
idempotency_key TEXT,
|
|
10366
|
-
request_hash TEXT NOT NULL,
|
|
10367
|
-
status TEXT NOT NULL CHECK(status IN ('pending','ok','error','timeout')),
|
|
10368
|
-
http_status INTEGER,
|
|
10369
|
-
duration_ms INTEGER,
|
|
10370
|
-
error TEXT,
|
|
10371
|
-
started_at INTEGER NOT NULL,
|
|
10372
|
-
completed_at INTEGER
|
|
10373
|
-
);
|
|
10374
|
-
CREATE TABLE IF NOT EXISTS channel_outbox (
|
|
10375
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
10376
|
-
channel_id TEXT NOT NULL,
|
|
10377
|
-
account_id TEXT NOT NULL,
|
|
10378
|
-
payload_json TEXT NOT NULL,
|
|
10379
|
-
attempt INTEGER NOT NULL DEFAULT 0,
|
|
10380
|
-
next_attempt_at INTEGER NOT NULL,
|
|
10381
|
-
last_error TEXT,
|
|
10382
|
-
created_at INTEGER NOT NULL
|
|
10383
|
-
);
|
|
10384
|
-
CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_messages_idem
|
|
10385
|
-
ON channel_messages(channel_id, account_id, idempotency_key)
|
|
10386
|
-
WHERE idempotency_key IS NOT NULL;
|
|
10387
|
-
CREATE INDEX IF NOT EXISTS idx_channel_messages_session_key
|
|
10388
|
-
ON channel_messages(channel_id, account_id, peer_id, room_id, thread_id, created_at);
|
|
10389
|
-
CREATE UNIQUE INDEX IF NOT EXISTS idx_fn_idem
|
|
10390
|
-
ON gateway_function_invocations(name, idempotency_key)
|
|
10391
|
-
WHERE idempotency_key IS NOT NULL;
|
|
10392
|
-
CREATE INDEX IF NOT EXISTS idx_outbox_due ON channel_outbox(next_attempt_at);
|
|
10393
|
-
UPDATE schema_version SET version = 6;
|
|
10394
|
-
`);
|
|
10395
|
-
}
|
|
9970
|
+
if (fromVersion === 2) {
|
|
9971
|
+
db.exec(`
|
|
9972
|
+
ALTER TABLE adapter_sessions ADD COLUMN tokens_input INTEGER;
|
|
9973
|
+
ALTER TABLE adapter_sessions ADD COLUMN tokens_output INTEGER;
|
|
9974
|
+
ALTER TABLE adapter_sessions ADD COLUMN tokens_cache_read INTEGER;
|
|
9975
|
+
ALTER TABLE adapter_sessions ADD COLUMN tokens_cache_write INTEGER;
|
|
9976
|
+
ALTER TABLE adapter_sessions ADD COLUMN tokens_context_size INTEGER;
|
|
9977
|
+
ALTER TABLE adapter_sessions ADD COLUMN tokens_context_window_size INTEGER;
|
|
9978
|
+
UPDATE schema_version SET version = 4;
|
|
9979
|
+
`);
|
|
9980
|
+
}
|
|
9981
|
+
if (fromVersion === 3) {
|
|
9982
|
+
db.exec(`
|
|
9983
|
+
ALTER TABLE adapter_sessions ADD COLUMN tokens_context_window_size INTEGER;
|
|
9984
|
+
UPDATE schema_version SET version = 4;
|
|
9985
|
+
`);
|
|
9986
|
+
}
|
|
9987
|
+
const currentVersion = db.prepare("SELECT version FROM schema_version").get().version;
|
|
9988
|
+
if (currentVersion === 4) {
|
|
9989
|
+
db.exec(`
|
|
9990
|
+
CREATE TABLE IF NOT EXISTS workflow_runs (
|
|
9991
|
+
id TEXT PRIMARY KEY,
|
|
9992
|
+
session_id TEXT NOT NULL,
|
|
9993
|
+
workflow_name TEXT,
|
|
9994
|
+
started_at INTEGER NOT NULL,
|
|
9995
|
+
ended_at INTEGER,
|
|
9996
|
+
iteration INTEGER NOT NULL DEFAULT 0,
|
|
9997
|
+
max_iterations INTEGER NOT NULL DEFAULT 1,
|
|
9998
|
+
status TEXT NOT NULL DEFAULT 'running',
|
|
9999
|
+
stop_reason TEXT,
|
|
10000
|
+
tracker_path TEXT,
|
|
10001
|
+
FOREIGN KEY (session_id) REFERENCES session(id)
|
|
10002
|
+
);
|
|
10003
|
+
CREATE INDEX IF NOT EXISTS idx_workflow_runs_session ON workflow_runs(session_id);
|
|
10004
|
+
ALTER TABLE adapter_sessions ADD COLUMN run_id TEXT REFERENCES workflow_runs(id);
|
|
10005
|
+
UPDATE schema_version SET version = 5;
|
|
10006
|
+
`);
|
|
10007
|
+
}
|
|
10008
|
+
const versionAfterV5 = db.prepare("SELECT version FROM schema_version").get().version;
|
|
10009
|
+
if (versionAfterV5 === 5) {
|
|
10010
|
+
db.exec(`
|
|
10011
|
+
CREATE TABLE IF NOT EXISTS channel_outbox (
|
|
10012
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
10013
|
+
channel_id TEXT NOT NULL,
|
|
10014
|
+
account_id TEXT NOT NULL,
|
|
10015
|
+
payload_json TEXT NOT NULL,
|
|
10016
|
+
attempt INTEGER NOT NULL DEFAULT 0,
|
|
10017
|
+
next_attempt_at INTEGER NOT NULL,
|
|
10018
|
+
last_error TEXT,
|
|
10019
|
+
created_at INTEGER NOT NULL
|
|
10020
|
+
);
|
|
10021
|
+
CREATE INDEX IF NOT EXISTS idx_outbox_due ON channel_outbox(next_attempt_at);
|
|
10022
|
+
UPDATE schema_version SET version = 6;
|
|
10023
|
+
`);
|
|
10396
10024
|
}
|
|
10397
|
-
}
|
|
10025
|
+
};
|
|
10026
|
+
var SESSION_SCHEMA = {
|
|
10027
|
+
version: SCHEMA_VERSION,
|
|
10028
|
+
migrate: applySessionSchema,
|
|
10029
|
+
onNewerVersion: (found, expected) => new Error(
|
|
10030
|
+
`Database has newer schema version ${found} (expected <= ${expected}). Update athena-cli to open this session.`
|
|
10031
|
+
)
|
|
10032
|
+
};
|
|
10398
10033
|
|
|
10399
10034
|
// src/infra/sessions/types.ts
|
|
10400
10035
|
function rowToAthenaSession(row, adapterSessionIds, firstPrompt) {
|
|
@@ -10412,11 +10047,11 @@ function rowToAthenaSession(row, adapterSessionIds, firstPrompt) {
|
|
|
10412
10047
|
|
|
10413
10048
|
// src/infra/sessions/store.ts
|
|
10414
10049
|
function createSessionStore(opts) {
|
|
10415
|
-
|
|
10416
|
-
|
|
10417
|
-
|
|
10418
|
-
|
|
10419
|
-
|
|
10050
|
+
const db = openVersionedDb(opts.dbPath, {
|
|
10051
|
+
...SESSION_SCHEMA,
|
|
10052
|
+
foreignKeys: true,
|
|
10053
|
+
ensureDir: true
|
|
10054
|
+
});
|
|
10420
10055
|
if (opts.dbPath !== ":memory:") {
|
|
10421
10056
|
db.pragma("locking_mode = EXCLUSIVE");
|
|
10422
10057
|
db.exec("BEGIN IMMEDIATE; COMMIT");
|
|
@@ -10687,9 +10322,6 @@ function createSessionStore(opts) {
|
|
|
10687
10322
|
};
|
|
10688
10323
|
}
|
|
10689
10324
|
|
|
10690
|
-
// src/infra/sessions/hookAudit.ts
|
|
10691
|
-
import Database2 from "better-sqlite3";
|
|
10692
|
-
|
|
10693
10325
|
// src/core/feed/filter.ts
|
|
10694
10326
|
var TASK_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
10695
10327
|
"TodoWrite",
|
|
@@ -11365,8 +10997,8 @@ var PRIMARY_INPUT_EXTRACTORS = {
|
|
|
11365
10997
|
return compactText(colonIdx >= 0 ? name.slice(colonIdx + 1) : name, 80);
|
|
11366
10998
|
},
|
|
11367
10999
|
NotebookEdit: (input) => {
|
|
11368
|
-
const
|
|
11369
|
-
return
|
|
11000
|
+
const path21 = String(input.notebook_path ?? "");
|
|
11001
|
+
return path21 ? shortenPath(path21) : "";
|
|
11370
11002
|
},
|
|
11371
11003
|
AskUserQuestion: (input) => {
|
|
11372
11004
|
const questions = input.questions;
|
|
@@ -12637,16 +12269,16 @@ function extractDomain(url) {
|
|
|
12637
12269
|
}
|
|
12638
12270
|
}
|
|
12639
12271
|
function filePathSegments(input) {
|
|
12640
|
-
const
|
|
12641
|
-
if (typeof
|
|
12642
|
-
const { prefix, filename } = shortenPathStructured(
|
|
12272
|
+
const path21 = prop2(input, "file_path") ?? prop2(input, "notebook_path") ?? "";
|
|
12273
|
+
if (typeof path21 !== "string" || !path21) return [];
|
|
12274
|
+
const { prefix, filename } = shortenPathStructured(path21);
|
|
12643
12275
|
if (prefix && filename) {
|
|
12644
12276
|
return [
|
|
12645
12277
|
{ text: prefix, role: "target" },
|
|
12646
12278
|
{ text: filename, role: "filename" }
|
|
12647
12279
|
];
|
|
12648
12280
|
}
|
|
12649
|
-
return [{ text: filename ||
|
|
12281
|
+
return [{ text: filename || path21, role: "filename" }];
|
|
12650
12282
|
}
|
|
12651
12283
|
function grepSegments(input) {
|
|
12652
12284
|
const pattern = String(prop2(input, "pattern") ?? "");
|
|
@@ -13779,57 +13411,93 @@ ${details}` : entry.summary;
|
|
|
13779
13411
|
}
|
|
13780
13412
|
};
|
|
13781
13413
|
|
|
13414
|
+
// src/infra/sessions/sessionDbReader.ts
|
|
13415
|
+
import Database from "better-sqlite3";
|
|
13416
|
+
function openSessionDbReadonly(dbPath, options = {}) {
|
|
13417
|
+
const db = new Database(dbPath, {
|
|
13418
|
+
readonly: true,
|
|
13419
|
+
...options.fileMustExist ? { fileMustExist: true } : {}
|
|
13420
|
+
});
|
|
13421
|
+
return {
|
|
13422
|
+
schemaVersion() {
|
|
13423
|
+
const row = db.prepare("SELECT version FROM schema_version").get();
|
|
13424
|
+
return row?.version;
|
|
13425
|
+
},
|
|
13426
|
+
sessionRow() {
|
|
13427
|
+
return db.prepare("SELECT * FROM session LIMIT 1").get();
|
|
13428
|
+
},
|
|
13429
|
+
adapterSessionIds() {
|
|
13430
|
+
const rows = db.prepare("SELECT session_id FROM adapter_sessions ORDER BY started_at").all();
|
|
13431
|
+
return rows.map((r) => r.session_id);
|
|
13432
|
+
},
|
|
13433
|
+
firstUserPrompt() {
|
|
13434
|
+
const row = db.prepare(
|
|
13435
|
+
`SELECT json_extract(payload, '$.data.prompt') as prompt FROM runtime_events WHERE hook_name = 'UserPromptSubmit' ORDER BY seq ASC LIMIT 1`
|
|
13436
|
+
).get();
|
|
13437
|
+
return row?.prompt ?? void 0;
|
|
13438
|
+
},
|
|
13439
|
+
runtimeHookCounts() {
|
|
13440
|
+
const rows = db.prepare(
|
|
13441
|
+
"SELECT hook_name, COUNT(*) AS count FROM runtime_events GROUP BY hook_name"
|
|
13442
|
+
).all();
|
|
13443
|
+
const counts = {};
|
|
13444
|
+
for (const row of rows) counts[row.hook_name] = row.count;
|
|
13445
|
+
return counts;
|
|
13446
|
+
},
|
|
13447
|
+
feedEvents() {
|
|
13448
|
+
const rows = db.prepare("SELECT data FROM feed_events ORDER BY seq").all();
|
|
13449
|
+
return rows.map((r) => JSON.parse(r.data));
|
|
13450
|
+
},
|
|
13451
|
+
close() {
|
|
13452
|
+
db.close();
|
|
13453
|
+
}
|
|
13454
|
+
};
|
|
13455
|
+
}
|
|
13456
|
+
|
|
13782
13457
|
// src/infra/sessions/registry.ts
|
|
13783
|
-
import
|
|
13458
|
+
import fs17 from "fs";
|
|
13784
13459
|
import os11 from "os";
|
|
13785
|
-
import
|
|
13786
|
-
import Database3 from "better-sqlite3";
|
|
13460
|
+
import path14 from "path";
|
|
13787
13461
|
function sessionsDir() {
|
|
13788
|
-
return
|
|
13462
|
+
return path14.join(os11.homedir(), ".config", "athena", "sessions");
|
|
13789
13463
|
}
|
|
13790
13464
|
function sessionDbPath(sessionId) {
|
|
13791
|
-
return
|
|
13465
|
+
return path14.join(sessionsDir(), sessionId, "session.db");
|
|
13792
13466
|
}
|
|
13793
13467
|
function readSessionFromDb(dbPath) {
|
|
13794
|
-
if (!
|
|
13795
|
-
let
|
|
13468
|
+
if (!fs17.existsSync(dbPath)) return null;
|
|
13469
|
+
let reader;
|
|
13796
13470
|
try {
|
|
13797
|
-
|
|
13798
|
-
const
|
|
13799
|
-
if (
|
|
13471
|
+
reader = openSessionDbReadonly(dbPath);
|
|
13472
|
+
const version = reader.schemaVersion();
|
|
13473
|
+
if (version !== void 0 && version > SCHEMA_VERSION) {
|
|
13800
13474
|
return null;
|
|
13801
13475
|
}
|
|
13802
|
-
const row =
|
|
13476
|
+
const row = reader.sessionRow();
|
|
13803
13477
|
if (!row) return null;
|
|
13804
|
-
const
|
|
13478
|
+
const adapterSessionIds = reader.adapterSessionIds();
|
|
13805
13479
|
let firstPrompt;
|
|
13806
13480
|
if (!row.label && (row.event_count ?? 0) > 0) {
|
|
13807
|
-
const
|
|
13808
|
-
|
|
13809
|
-
|
|
13810
|
-
if (promptRow?.prompt) {
|
|
13811
|
-
firstPrompt = promptRow.prompt;
|
|
13481
|
+
const prompt = reader.firstUserPrompt();
|
|
13482
|
+
if (prompt) {
|
|
13483
|
+
firstPrompt = prompt;
|
|
13812
13484
|
}
|
|
13813
13485
|
}
|
|
13814
|
-
return rowToAthenaSession(
|
|
13815
|
-
row,
|
|
13816
|
-
adapters.map((a) => a.session_id),
|
|
13817
|
-
firstPrompt
|
|
13818
|
-
);
|
|
13486
|
+
return rowToAthenaSession(row, adapterSessionIds, firstPrompt);
|
|
13819
13487
|
} catch {
|
|
13820
13488
|
return null;
|
|
13821
13489
|
} finally {
|
|
13822
|
-
|
|
13490
|
+
reader?.close();
|
|
13823
13491
|
}
|
|
13824
13492
|
}
|
|
13825
13493
|
function listSessions(projectDir) {
|
|
13826
13494
|
const dir = sessionsDir();
|
|
13827
|
-
if (!
|
|
13828
|
-
const entries =
|
|
13495
|
+
if (!fs17.existsSync(dir)) return [];
|
|
13496
|
+
const entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
13829
13497
|
const sessions = [];
|
|
13830
13498
|
for (const entry of entries) {
|
|
13831
13499
|
if (!entry.isDirectory()) continue;
|
|
13832
|
-
const dbPath =
|
|
13500
|
+
const dbPath = path14.join(dir, entry.name, "session.db");
|
|
13833
13501
|
const session = readSessionFromDb(dbPath);
|
|
13834
13502
|
if (session && (session.eventCount ?? 0) > 0) {
|
|
13835
13503
|
if (!projectDir || session.projectDir === projectDir) {
|
|
@@ -14276,9 +13944,9 @@ function defaultLoadToken(tokenPath) {
|
|
|
14276
13944
|
}
|
|
14277
13945
|
|
|
14278
13946
|
// src/infra/config/gatewayClient.ts
|
|
14279
|
-
import
|
|
13947
|
+
import fs18 from "fs";
|
|
14280
13948
|
import os12 from "os";
|
|
14281
|
-
import
|
|
13949
|
+
import path15 from "path";
|
|
14282
13950
|
|
|
14283
13951
|
// src/shared/gateway-protocol/endpoint.ts
|
|
14284
13952
|
function parseRuntimeEndpoint(value) {
|
|
@@ -14334,16 +14002,16 @@ function isRecord2(value) {
|
|
|
14334
14002
|
// src/infra/config/gatewayClient.ts
|
|
14335
14003
|
function resolveGatewayClientConfigPath(env = process.env) {
|
|
14336
14004
|
const home = env["HOME"] ?? os12.homedir();
|
|
14337
|
-
return
|
|
14005
|
+
return path15.join(home, ".config", "athena", "gateway.json");
|
|
14338
14006
|
}
|
|
14339
14007
|
function readGatewayClientConfig(env = process.env) {
|
|
14340
14008
|
const configPath = resolveGatewayClientConfigPath(env);
|
|
14341
|
-
if (!
|
|
14009
|
+
if (!fs18.existsSync(configPath)) {
|
|
14342
14010
|
return { mode: "local" };
|
|
14343
14011
|
}
|
|
14344
14012
|
let parsed;
|
|
14345
14013
|
try {
|
|
14346
|
-
parsed = JSON.parse(
|
|
14014
|
+
parsed = JSON.parse(fs18.readFileSync(configPath, "utf-8"));
|
|
14347
14015
|
} catch (err) {
|
|
14348
14016
|
throw new Error(
|
|
14349
14017
|
`gateway client config ${configPath} is invalid JSON: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -14360,16 +14028,16 @@ function readGatewayClientConfig(env = process.env) {
|
|
|
14360
14028
|
function writeGatewayClientConfig(config, env = process.env) {
|
|
14361
14029
|
const parsed = parseRuntimeEndpoint(config);
|
|
14362
14030
|
const configPath = resolveGatewayClientConfigPath(env);
|
|
14363
|
-
const dir =
|
|
14364
|
-
|
|
14365
|
-
|
|
14031
|
+
const dir = path15.dirname(configPath);
|
|
14032
|
+
fs18.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
14033
|
+
fs18.writeFileSync(configPath, JSON.stringify(parsed, null, 2) + "\n", {
|
|
14366
14034
|
encoding: "utf-8",
|
|
14367
14035
|
mode: 384
|
|
14368
14036
|
});
|
|
14369
14037
|
if (process.platform !== "win32") {
|
|
14370
14038
|
try {
|
|
14371
|
-
|
|
14372
|
-
|
|
14039
|
+
fs18.chmodSync(dir, 448);
|
|
14040
|
+
fs18.chmodSync(configPath, 384);
|
|
14373
14041
|
} catch {
|
|
14374
14042
|
}
|
|
14375
14043
|
}
|
|
@@ -14797,14 +14465,11 @@ function sleepOrAbort(ms, signal) {
|
|
|
14797
14465
|
}
|
|
14798
14466
|
|
|
14799
14467
|
// src/app/dashboard/dashboardFeedPublisher.ts
|
|
14800
|
-
import Database4 from "better-sqlite3";
|
|
14801
14468
|
function dashboardFeedOutboxPath() {
|
|
14802
14469
|
return `${ensureDaemonStateDir().dir}/dashboard-feed-outbox.db`;
|
|
14803
14470
|
}
|
|
14804
14471
|
function initOutboxSchema(db) {
|
|
14805
14472
|
db.exec(`
|
|
14806
|
-
PRAGMA journal_mode = WAL;
|
|
14807
|
-
|
|
14808
14473
|
CREATE TABLE IF NOT EXISTS dashboard_feed_outbox (
|
|
14809
14474
|
delivery_seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
14810
14475
|
instance_id TEXT NOT NULL,
|
|
@@ -14838,8 +14503,9 @@ function makeEnvelope(input) {
|
|
|
14838
14503
|
};
|
|
14839
14504
|
}
|
|
14840
14505
|
function createDashboardFeedOutbox(options = {}) {
|
|
14841
|
-
const db =
|
|
14842
|
-
|
|
14506
|
+
const db = openVersionedDb(options.dbPath ?? dashboardFeedOutboxPath(), {
|
|
14507
|
+
migrate: initOutboxSchema
|
|
14508
|
+
});
|
|
14843
14509
|
const insert = db.prepare(`
|
|
14844
14510
|
INSERT OR IGNORE INTO dashboard_feed_outbox (
|
|
14845
14511
|
instance_id,
|
|
@@ -15038,6 +14704,257 @@ function createPairedFeedPublisher(options = {}) {
|
|
|
15038
14704
|
};
|
|
15039
14705
|
}
|
|
15040
14706
|
|
|
14707
|
+
// src/core/controller/rules.ts
|
|
14708
|
+
function ruleMatches(ruleToolName, toolName) {
|
|
14709
|
+
if (ruleToolName === "*") return true;
|
|
14710
|
+
if (ruleToolName === toolName) return true;
|
|
14711
|
+
if (ruleToolName.endsWith("__*")) {
|
|
14712
|
+
const prefix = ruleToolName.slice(0, -1);
|
|
14713
|
+
return toolName.startsWith(prefix);
|
|
14714
|
+
}
|
|
14715
|
+
return false;
|
|
14716
|
+
}
|
|
14717
|
+
function matchRule(rules, toolName) {
|
|
14718
|
+
const denyMatch = rules.find(
|
|
14719
|
+
(r) => r.action === "deny" && ruleMatches(r.toolName, toolName)
|
|
14720
|
+
);
|
|
14721
|
+
if (denyMatch) return denyMatch;
|
|
14722
|
+
return rules.find(
|
|
14723
|
+
(r) => r.action === "approve" && ruleMatches(r.toolName, toolName)
|
|
14724
|
+
);
|
|
14725
|
+
}
|
|
14726
|
+
|
|
14727
|
+
// src/core/controller/permission.ts
|
|
14728
|
+
var SESSION_APPROVAL_REQUEST_HOOKS = /* @__PURE__ */ new Set([
|
|
14729
|
+
"item/commandExecution/requestApproval",
|
|
14730
|
+
"item/fileChange/requestApproval",
|
|
14731
|
+
"item/permissions/requestApproval"
|
|
14732
|
+
]);
|
|
14733
|
+
function isScopedPermissionsRequest(hookName) {
|
|
14734
|
+
return hookName === "item/permissions/requestApproval";
|
|
14735
|
+
}
|
|
14736
|
+
function supportsSessionApproval(hookName) {
|
|
14737
|
+
return hookName !== void 0 && SESSION_APPROVAL_REQUEST_HOOKS.has(hookName);
|
|
14738
|
+
}
|
|
14739
|
+
function extractPermissionSnapshot(event) {
|
|
14740
|
+
const data = event.data;
|
|
14741
|
+
const toolNameFromData = typeof data["tool_name"] === "string" ? data["tool_name"] : void 0;
|
|
14742
|
+
const toolInputFromData = typeof data["tool_input"] === "object" && data["tool_input"] !== null ? data["tool_input"] : void 0;
|
|
14743
|
+
const toolUseIdFromData = typeof data["tool_use_id"] === "string" ? data["tool_use_id"] : void 0;
|
|
14744
|
+
return {
|
|
14745
|
+
request_id: event.id,
|
|
14746
|
+
ts: event.timestamp,
|
|
14747
|
+
kind: event.kind,
|
|
14748
|
+
hookName: event.hookName,
|
|
14749
|
+
tool_name: event.toolName ?? toolNameFromData ?? "Unknown",
|
|
14750
|
+
tool_input: toolInputFromData ?? {},
|
|
14751
|
+
tool_use_id: event.toolUseId ?? toolUseIdFromData,
|
|
14752
|
+
suggestions: data.permission_suggestions
|
|
14753
|
+
};
|
|
14754
|
+
}
|
|
14755
|
+
|
|
14756
|
+
// src/core/controller/runtimeController.ts
|
|
14757
|
+
function handleEvent(event, cb) {
|
|
14758
|
+
const eventKind2 = event.kind;
|
|
14759
|
+
const isScoped = isScopedPermissionsRequest(event.hookName);
|
|
14760
|
+
const eventData = event.data;
|
|
14761
|
+
const toolName = event.toolName ?? (typeof eventData["tool_name"] === "string" ? eventData["tool_name"] : void 0);
|
|
14762
|
+
if (eventKind2 === "permission.request" && toolName === "user_input") {
|
|
14763
|
+
cb.enqueueQuestion(event.id);
|
|
14764
|
+
cb.relayQuestion?.(event);
|
|
14765
|
+
return { handled: true };
|
|
14766
|
+
}
|
|
14767
|
+
if (eventKind2 === "permission.request" && toolName) {
|
|
14768
|
+
const rule = matchRule(cb.getRules(), toolName);
|
|
14769
|
+
if (rule?.action === "deny") {
|
|
14770
|
+
return {
|
|
14771
|
+
handled: true,
|
|
14772
|
+
decision: {
|
|
14773
|
+
type: "json",
|
|
14774
|
+
source: "rule",
|
|
14775
|
+
intent: {
|
|
14776
|
+
kind: "permission_deny",
|
|
14777
|
+
reason: `Blocked by rule: ${rule.addedBy}`
|
|
14778
|
+
}
|
|
14779
|
+
}
|
|
14780
|
+
};
|
|
14781
|
+
}
|
|
14782
|
+
if (rule?.action === "approve") {
|
|
14783
|
+
return {
|
|
14784
|
+
handled: true,
|
|
14785
|
+
decision: {
|
|
14786
|
+
type: "json",
|
|
14787
|
+
source: "rule",
|
|
14788
|
+
intent: { kind: "permission_allow" },
|
|
14789
|
+
// Scoped Codex permission grants are session-scoped under
|
|
14790
|
+
// auto rules so the same capability isn't re-prompted.
|
|
14791
|
+
...isScoped ? { data: { scope: "session" } } : {}
|
|
14792
|
+
}
|
|
14793
|
+
};
|
|
14794
|
+
}
|
|
14795
|
+
cb.enqueuePermission(event);
|
|
14796
|
+
cb.relayPermission?.(event);
|
|
14797
|
+
return { handled: true };
|
|
14798
|
+
}
|
|
14799
|
+
if (eventKind2 === "tool.pre" && toolName === "AskUserQuestion") {
|
|
14800
|
+
cb.enqueueQuestion(event.id);
|
|
14801
|
+
cb.relayQuestion?.(event);
|
|
14802
|
+
return { handled: true };
|
|
14803
|
+
}
|
|
14804
|
+
if (eventKind2 === "tool.pre" && toolName) {
|
|
14805
|
+
const rule = matchRule(cb.getRules(), toolName);
|
|
14806
|
+
if (rule?.action === "deny") {
|
|
14807
|
+
return {
|
|
14808
|
+
handled: true,
|
|
14809
|
+
decision: {
|
|
14810
|
+
type: "json",
|
|
14811
|
+
source: "rule",
|
|
14812
|
+
intent: {
|
|
14813
|
+
kind: "pre_tool_deny",
|
|
14814
|
+
reason: `Blocked by rule: ${rule.addedBy}`
|
|
14815
|
+
}
|
|
14816
|
+
}
|
|
14817
|
+
};
|
|
14818
|
+
}
|
|
14819
|
+
return {
|
|
14820
|
+
handled: true,
|
|
14821
|
+
decision: {
|
|
14822
|
+
type: "json",
|
|
14823
|
+
source: "rule",
|
|
14824
|
+
intent: { kind: "pre_tool_allow" }
|
|
14825
|
+
}
|
|
14826
|
+
};
|
|
14827
|
+
}
|
|
14828
|
+
return { handled: false };
|
|
14829
|
+
}
|
|
14830
|
+
|
|
14831
|
+
// src/core/feed/ingest.ts
|
|
14832
|
+
function ingestRuntimeEvent(event, ctx) {
|
|
14833
|
+
const controllerResult = handleEvent(event, ctx.controllerCallbacks);
|
|
14834
|
+
const feedEvents = ctx.mapper.mapEvent(event);
|
|
14835
|
+
if (ctx.store) {
|
|
14836
|
+
persistOrDegrade(
|
|
14837
|
+
ctx.store,
|
|
14838
|
+
() => ctx.store.recordEvent(event, feedEvents),
|
|
14839
|
+
"recordEvent failed",
|
|
14840
|
+
ctx.onPersistFailure
|
|
14841
|
+
);
|
|
14842
|
+
}
|
|
14843
|
+
return {
|
|
14844
|
+
feedEvents,
|
|
14845
|
+
decision: controllerResult.handled && controllerResult.decision ? controllerResult.decision : null
|
|
14846
|
+
};
|
|
14847
|
+
}
|
|
14848
|
+
function ingestRuntimeDecision(eventId, decision, ctx) {
|
|
14849
|
+
const feedEvent = ctx.mapper.mapDecision(eventId, decision);
|
|
14850
|
+
if (feedEvent && ctx.store) {
|
|
14851
|
+
persistOrDegrade(
|
|
14852
|
+
ctx.store,
|
|
14853
|
+
() => ctx.store.recordFeedEvents([feedEvent]),
|
|
14854
|
+
"recordFeedEvents failed",
|
|
14855
|
+
ctx.onPersistFailure
|
|
14856
|
+
);
|
|
14857
|
+
}
|
|
14858
|
+
return feedEvent;
|
|
14859
|
+
}
|
|
14860
|
+
function persistOrDegrade(store, action, label, onFailure) {
|
|
14861
|
+
try {
|
|
14862
|
+
action();
|
|
14863
|
+
} catch (err) {
|
|
14864
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
14865
|
+
const message = `${label}: ${reason}`;
|
|
14866
|
+
store.markDegraded(message);
|
|
14867
|
+
onFailure?.(message);
|
|
14868
|
+
}
|
|
14869
|
+
}
|
|
14870
|
+
|
|
14871
|
+
// src/app/runtime/runtimeEventLoop.ts
|
|
14872
|
+
var DASHBOARD_DECISION_POLL_LIMIT = 25;
|
|
14873
|
+
var DASHBOARD_DECISION_POLL_INTERVAL_MS = 1e3;
|
|
14874
|
+
function resolveIngest(source) {
|
|
14875
|
+
return typeof source === "function" ? source() : source;
|
|
14876
|
+
}
|
|
14877
|
+
function attachRuntimeEventLoop(options) {
|
|
14878
|
+
const { runtime } = options;
|
|
14879
|
+
const runEvent = (event) => {
|
|
14880
|
+
options.onEventReceived?.(event);
|
|
14881
|
+
if (options.skipEvent?.(event)) return;
|
|
14882
|
+
const { feedEvents, decision } = ingestRuntimeEvent(
|
|
14883
|
+
event,
|
|
14884
|
+
resolveIngest(options.ingest)
|
|
14885
|
+
);
|
|
14886
|
+
if (decision) {
|
|
14887
|
+
runtime.sendDecision(event.id, decision);
|
|
14888
|
+
}
|
|
14889
|
+
options.emitEventFeed(feedEvents, event);
|
|
14890
|
+
};
|
|
14891
|
+
const runDecision = (eventId, decision) => {
|
|
14892
|
+
options.onDecisionReceived?.(eventId, decision);
|
|
14893
|
+
if (options.skipDecision?.(eventId, decision)) return;
|
|
14894
|
+
options.beforeDecisionIngest?.(eventId, decision);
|
|
14895
|
+
const feedEvent = ingestRuntimeDecision(
|
|
14896
|
+
eventId,
|
|
14897
|
+
decision,
|
|
14898
|
+
resolveIngest(options.ingest)
|
|
14899
|
+
);
|
|
14900
|
+
options.emitDecisionFeed(feedEvent, eventId, decision);
|
|
14901
|
+
};
|
|
14902
|
+
const unsubscribeEvent = runtime.onEvent((event) => {
|
|
14903
|
+
if (options.wrapEvent) {
|
|
14904
|
+
options.wrapEvent(event, () => runEvent(event));
|
|
14905
|
+
} else {
|
|
14906
|
+
runEvent(event);
|
|
14907
|
+
}
|
|
14908
|
+
});
|
|
14909
|
+
const unsubscribeDecision = runtime.onDecision((eventId, decision) => {
|
|
14910
|
+
if (options.wrapDecision) {
|
|
14911
|
+
options.wrapDecision(
|
|
14912
|
+
eventId,
|
|
14913
|
+
decision,
|
|
14914
|
+
() => runDecision(eventId, decision)
|
|
14915
|
+
);
|
|
14916
|
+
} else {
|
|
14917
|
+
runDecision(eventId, decision);
|
|
14918
|
+
}
|
|
14919
|
+
});
|
|
14920
|
+
return {
|
|
14921
|
+
stop() {
|
|
14922
|
+
unsubscribeEvent();
|
|
14923
|
+
unsubscribeDecision();
|
|
14924
|
+
}
|
|
14925
|
+
};
|
|
14926
|
+
}
|
|
14927
|
+
function startDashboardDecisionDrain(options) {
|
|
14928
|
+
const { runtime, inbox, athenaSessionId } = options;
|
|
14929
|
+
const drainOnce = () => {
|
|
14930
|
+
const rows = inbox.pendingForSession({
|
|
14931
|
+
athenaSessionId,
|
|
14932
|
+
limit: DASHBOARD_DECISION_POLL_LIMIT
|
|
14933
|
+
});
|
|
14934
|
+
for (const row of rows) {
|
|
14935
|
+
try {
|
|
14936
|
+
runtime.sendDecision(row.requestId, row.decision);
|
|
14937
|
+
inbox.markConsumed({ id: row.id });
|
|
14938
|
+
} catch (error) {
|
|
14939
|
+
if (!options.onError) throw error;
|
|
14940
|
+
options.onError(error);
|
|
14941
|
+
}
|
|
14942
|
+
}
|
|
14943
|
+
};
|
|
14944
|
+
drainOnce();
|
|
14945
|
+
const timer = setInterval(
|
|
14946
|
+
drainOnce,
|
|
14947
|
+
options.pollIntervalMs ?? DASHBOARD_DECISION_POLL_INTERVAL_MS
|
|
14948
|
+
);
|
|
14949
|
+
options.configureTimer?.(timer);
|
|
14950
|
+
return {
|
|
14951
|
+
drainOnce,
|
|
14952
|
+
stop() {
|
|
14953
|
+
clearInterval(timer);
|
|
14954
|
+
}
|
|
14955
|
+
};
|
|
14956
|
+
}
|
|
14957
|
+
|
|
15041
14958
|
// src/app/exec/finalMessage.ts
|
|
15042
14959
|
function findLastMappedAgentMessage(feedEvents) {
|
|
15043
14960
|
for (let i = feedEvents.length - 1; i >= 0; i--) {
|
|
@@ -15088,8 +15005,8 @@ function exitCodeFromFailure(failure) {
|
|
|
15088
15005
|
}
|
|
15089
15006
|
|
|
15090
15007
|
// src/app/exec/output.ts
|
|
15091
|
-
import
|
|
15092
|
-
import
|
|
15008
|
+
import fs19 from "fs/promises";
|
|
15009
|
+
import path16 from "path";
|
|
15093
15010
|
|
|
15094
15011
|
// src/app/exec/jsonl.ts
|
|
15095
15012
|
function createExecJsonlEvent(type, data, ts = Date.now()) {
|
|
@@ -15128,9 +15045,9 @@ function createExecOutputWriter(options) {
|
|
|
15128
15045
|
writeLine(options.stdout, message);
|
|
15129
15046
|
},
|
|
15130
15047
|
async writeLastMessage(filePath, message) {
|
|
15131
|
-
const absPath =
|
|
15132
|
-
await
|
|
15133
|
-
await
|
|
15048
|
+
const absPath = path16.resolve(filePath);
|
|
15049
|
+
await fs19.mkdir(path16.dirname(absPath), { recursive: true });
|
|
15050
|
+
await fs19.writeFile(absPath, message, "utf-8");
|
|
15134
15051
|
}
|
|
15135
15052
|
};
|
|
15136
15053
|
}
|
|
@@ -15209,7 +15126,7 @@ async function runExec(options) {
|
|
|
15209
15126
|
store = sessionStoreFactory({
|
|
15210
15127
|
sessionId: athenaSessionId,
|
|
15211
15128
|
projectDir: options.projectDir,
|
|
15212
|
-
dbPath: options.ephemeral ? ":memory:" :
|
|
15129
|
+
dbPath: options.ephemeral ? ":memory:" : path17.join(sessionsDir(), athenaSessionId, "session.db")
|
|
15213
15130
|
});
|
|
15214
15131
|
} catch (error) {
|
|
15215
15132
|
const message = `Failed to initialize session store: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -15300,23 +15217,6 @@ async function runExec(options) {
|
|
|
15300
15217
|
...options.signal ? { signal: options.signal } : {}
|
|
15301
15218
|
};
|
|
15302
15219
|
const dashboardDecisionInbox = options.dashboardDecisionInbox;
|
|
15303
|
-
const applyPendingDashboardDecisions = () => {
|
|
15304
|
-
if (!dashboardDecisionInbox) return;
|
|
15305
|
-
const rows = dashboardDecisionInbox.pendingForSession({
|
|
15306
|
-
athenaSessionId,
|
|
15307
|
-
limit: 25
|
|
15308
|
-
});
|
|
15309
|
-
for (const row of rows) {
|
|
15310
|
-
try {
|
|
15311
|
-
runtime.sendDecision(row.requestId, row.decision);
|
|
15312
|
-
dashboardDecisionInbox.markConsumed({ id: row.id });
|
|
15313
|
-
} catch (error) {
|
|
15314
|
-
output.warn(
|
|
15315
|
-
`dashboard decision failed: ${error instanceof Error ? error.message : String(error)}`
|
|
15316
|
-
);
|
|
15317
|
-
}
|
|
15318
|
-
}
|
|
15319
|
-
};
|
|
15320
15220
|
const linkedAdapterSessions = /* @__PURE__ */ new Set();
|
|
15321
15221
|
function publishFeedEvents(feedEvents) {
|
|
15322
15222
|
if (feedEvents.length === 0) return;
|
|
@@ -15377,60 +15277,57 @@ async function runExec(options) {
|
|
|
15377
15277
|
});
|
|
15378
15278
|
}
|
|
15379
15279
|
};
|
|
15380
|
-
const
|
|
15381
|
-
|
|
15382
|
-
|
|
15383
|
-
linkedAdapterSessions.add(runtimeEvent.sessionId);
|
|
15384
|
-
safePersist(
|
|
15385
|
-
store,
|
|
15386
|
-
() => store.linkAdapterSession(runtimeEvent.sessionId, activeRunId),
|
|
15387
|
-
(message) => output.warn(message),
|
|
15388
|
-
"linkAdapterSession failed"
|
|
15389
|
-
);
|
|
15390
|
-
}
|
|
15391
|
-
output.emitJsonEvent("runtime.event", {
|
|
15392
|
-
id: runtimeEvent.id,
|
|
15393
|
-
kind: runtimeEvent.kind,
|
|
15394
|
-
hookName: runtimeEvent.hookName,
|
|
15395
|
-
sessionId: runtimeEvent.sessionId,
|
|
15396
|
-
toolName: runtimeEvent.toolName ?? null,
|
|
15397
|
-
data: runtimeEvent.data
|
|
15398
|
-
});
|
|
15399
|
-
if (latch.hasFailure()) return;
|
|
15400
|
-
const { feedEvents, decision } = ingestRuntimeEvent(runtimeEvent, {
|
|
15280
|
+
const runtimeEventLoop = attachRuntimeEventLoop({
|
|
15281
|
+
runtime,
|
|
15282
|
+
ingest: {
|
|
15401
15283
|
mapper,
|
|
15402
15284
|
store,
|
|
15403
15285
|
controllerCallbacks,
|
|
15404
15286
|
onPersistFailure: (message) => output.warn(message)
|
|
15405
|
-
}
|
|
15406
|
-
|
|
15407
|
-
|
|
15408
|
-
|
|
15409
|
-
|
|
15410
|
-
|
|
15411
|
-
|
|
15287
|
+
},
|
|
15288
|
+
onEventReceived: (runtimeEvent) => {
|
|
15289
|
+
adapterSessionId = runtimeEvent.sessionId;
|
|
15290
|
+
if (runtimeEvent.sessionId && activeRunId && !linkedAdapterSessions.has(runtimeEvent.sessionId)) {
|
|
15291
|
+
linkedAdapterSessions.add(runtimeEvent.sessionId);
|
|
15292
|
+
safePersist(
|
|
15293
|
+
store,
|
|
15294
|
+
() => store.linkAdapterSession(runtimeEvent.sessionId, activeRunId),
|
|
15295
|
+
(message) => output.warn(message),
|
|
15296
|
+
"linkAdapterSession failed"
|
|
15297
|
+
);
|
|
15412
15298
|
}
|
|
15413
|
-
|
|
15414
|
-
|
|
15415
|
-
|
|
15416
|
-
|
|
15417
|
-
|
|
15299
|
+
output.emitJsonEvent("runtime.event", {
|
|
15300
|
+
id: runtimeEvent.id,
|
|
15301
|
+
kind: runtimeEvent.kind,
|
|
15302
|
+
hookName: runtimeEvent.hookName,
|
|
15303
|
+
sessionId: runtimeEvent.sessionId,
|
|
15304
|
+
toolName: runtimeEvent.toolName ?? null,
|
|
15305
|
+
data: runtimeEvent.data
|
|
15306
|
+
});
|
|
15307
|
+
},
|
|
15308
|
+
skipEvent: () => latch.hasFailure(),
|
|
15309
|
+
emitEventFeed: (feedEvents) => {
|
|
15310
|
+
for (const event of feedEvents) {
|
|
15311
|
+
if (event.kind === "agent.message") {
|
|
15312
|
+
mappedFinalMessage = event.data.message;
|
|
15313
|
+
}
|
|
15314
|
+
}
|
|
15315
|
+
publishFeedEvents(feedEvents);
|
|
15316
|
+
},
|
|
15317
|
+
onDecisionReceived: (eventId, decision) => {
|
|
15418
15318
|
output.emitJsonEvent("runtime.decision", {
|
|
15419
15319
|
eventId,
|
|
15420
15320
|
decision
|
|
15421
15321
|
});
|
|
15422
|
-
|
|
15423
|
-
|
|
15424
|
-
store,
|
|
15425
|
-
onPersistFailure: (message) => output.warn(message)
|
|
15426
|
-
});
|
|
15322
|
+
},
|
|
15323
|
+
emitDecisionFeed: (feedEvent) => {
|
|
15427
15324
|
if (feedEvent) {
|
|
15428
15325
|
publishFeedEvents([feedEvent]);
|
|
15429
15326
|
}
|
|
15430
15327
|
}
|
|
15431
|
-
);
|
|
15328
|
+
});
|
|
15432
15329
|
let timeoutTimer;
|
|
15433
|
-
let
|
|
15330
|
+
let dashboardDecisionDrain;
|
|
15434
15331
|
if (typeof options.timeoutMs === "number" && options.timeoutMs > 0) {
|
|
15435
15332
|
timeoutTimer = setTimeout(() => {
|
|
15436
15333
|
latch.register({
|
|
@@ -15451,12 +15348,16 @@ async function runExec(options) {
|
|
|
15451
15348
|
status: runtime.getStatus()
|
|
15452
15349
|
});
|
|
15453
15350
|
if (dashboardDecisionInbox) {
|
|
15454
|
-
|
|
15455
|
-
|
|
15456
|
-
|
|
15457
|
-
|
|
15458
|
-
|
|
15459
|
-
|
|
15351
|
+
dashboardDecisionDrain = startDashboardDecisionDrain({
|
|
15352
|
+
runtime,
|
|
15353
|
+
inbox: dashboardDecisionInbox,
|
|
15354
|
+
athenaSessionId,
|
|
15355
|
+
...options.dashboardDecisionPollIntervalMs !== void 0 ? { pollIntervalMs: options.dashboardDecisionPollIntervalMs } : {},
|
|
15356
|
+
onError: (error) => output.warn(
|
|
15357
|
+
`dashboard decision failed: ${error instanceof Error ? error.message : String(error)}`
|
|
15358
|
+
),
|
|
15359
|
+
configureTimer: (timer) => timer.unref()
|
|
15360
|
+
});
|
|
15460
15361
|
}
|
|
15461
15362
|
const workflow = options.workflow;
|
|
15462
15363
|
output.emitJsonEvent("run.started", {
|
|
@@ -15543,14 +15444,11 @@ async function runExec(options) {
|
|
|
15543
15444
|
if (timeoutTimer) {
|
|
15544
15445
|
clearTimeout(timeoutTimer);
|
|
15545
15446
|
}
|
|
15546
|
-
|
|
15547
|
-
clearInterval(dashboardDecisionTimer);
|
|
15548
|
-
}
|
|
15447
|
+
dashboardDecisionDrain?.stop();
|
|
15549
15448
|
await writeLastMessageBeforeTerminalCompletion();
|
|
15550
15449
|
await runBeforeTerminalCompletion();
|
|
15551
15450
|
await sessionController.kill();
|
|
15552
|
-
|
|
15553
|
-
unsubscribeDecision();
|
|
15451
|
+
runtimeEventLoop.stop();
|
|
15554
15452
|
if (runtimeStarted) {
|
|
15555
15453
|
runtime.stop();
|
|
15556
15454
|
}
|
|
@@ -16169,8 +16067,8 @@ async function createRemoteRunEventPublisher({
|
|
|
16169
16067
|
// src/app/dashboard/artifactCapture.ts
|
|
16170
16068
|
import { execFile } from "child_process";
|
|
16171
16069
|
import crypto3 from "crypto";
|
|
16172
|
-
import
|
|
16173
|
-
import
|
|
16070
|
+
import fs20 from "fs/promises";
|
|
16071
|
+
import path18 from "path";
|
|
16174
16072
|
import { promisify } from "util";
|
|
16175
16073
|
var execFileAsync = promisify(execFile);
|
|
16176
16074
|
function parseArtifactUploadSpec(value) {
|
|
@@ -16353,21 +16251,21 @@ async function collectArtifactPayloads(input) {
|
|
|
16353
16251
|
return payloads;
|
|
16354
16252
|
}
|
|
16355
16253
|
async function readWorkspaceFile(projectDir, rel) {
|
|
16356
|
-
const absolute =
|
|
16357
|
-
const workspaceRoot = await
|
|
16254
|
+
const absolute = path18.resolve(projectDir, rel);
|
|
16255
|
+
const workspaceRoot = await fs20.realpath(projectDir);
|
|
16358
16256
|
let stat;
|
|
16359
16257
|
try {
|
|
16360
|
-
stat = await
|
|
16258
|
+
stat = await fs20.lstat(absolute);
|
|
16361
16259
|
} catch {
|
|
16362
16260
|
return null;
|
|
16363
16261
|
}
|
|
16364
16262
|
if (!stat.isFile() || stat.isSymbolicLink()) return null;
|
|
16365
|
-
const real = await
|
|
16366
|
-
const relativeToRoot =
|
|
16367
|
-
if (relativeToRoot === ".." || relativeToRoot.startsWith(`..${
|
|
16263
|
+
const real = await fs20.realpath(absolute);
|
|
16264
|
+
const relativeToRoot = path18.relative(workspaceRoot, real);
|
|
16265
|
+
if (relativeToRoot === ".." || relativeToRoot.startsWith(`..${path18.sep}`) || path18.isAbsolute(relativeToRoot)) {
|
|
16368
16266
|
return null;
|
|
16369
16267
|
}
|
|
16370
|
-
return
|
|
16268
|
+
return fs20.readFile(absolute);
|
|
16371
16269
|
}
|
|
16372
16270
|
async function gitDiffForAllowedPaths(input) {
|
|
16373
16271
|
const paths = splitNul(await git(input.projectDir, input.nameArgs)).filter(
|
|
@@ -16458,13 +16356,13 @@ var DEFAULT_HARD_DENY = [
|
|
|
16458
16356
|
".ssh/**"
|
|
16459
16357
|
];
|
|
16460
16358
|
function isAllowedRelativePath(rel, hardDeny) {
|
|
16461
|
-
if (
|
|
16462
|
-
const normalized =
|
|
16359
|
+
if (path18.isAbsolute(rel) || rel.includes("\0")) return false;
|
|
16360
|
+
const normalized = path18.posix.normalize(rel.replaceAll(path18.sep, "/"));
|
|
16463
16361
|
if (normalized === ".." || normalized.startsWith("../")) return false;
|
|
16464
16362
|
return !hardDeny.some((pattern) => matchesDenyPattern(normalized, pattern));
|
|
16465
16363
|
}
|
|
16466
16364
|
function matchesDenyPattern(rel, pattern) {
|
|
16467
|
-
const normalized = pattern.replaceAll(
|
|
16365
|
+
const normalized = pattern.replaceAll(path18.sep, "/");
|
|
16468
16366
|
if (normalized.includes("*") || normalized.includes("?")) {
|
|
16469
16367
|
return globToRegExp(normalized).test(rel);
|
|
16470
16368
|
}
|
|
@@ -16861,18 +16759,18 @@ async function executeRemoteAssignment({
|
|
|
16861
16759
|
|
|
16862
16760
|
// src/infra/config/attachmentMirror.ts
|
|
16863
16761
|
import crypto4 from "crypto";
|
|
16864
|
-
import
|
|
16762
|
+
import fs21 from "fs";
|
|
16865
16763
|
import os13 from "os";
|
|
16866
|
-
import
|
|
16764
|
+
import path19 from "path";
|
|
16867
16765
|
function attachmentMirrorPath(env = process.env) {
|
|
16868
16766
|
const home = env["HOME"] ?? os13.homedir();
|
|
16869
|
-
return
|
|
16767
|
+
return path19.join(home, ".config", "athena", "attachments.json");
|
|
16870
16768
|
}
|
|
16871
16769
|
function readAttachmentMirror(env = process.env) {
|
|
16872
16770
|
const file = attachmentMirrorPath(env);
|
|
16873
16771
|
let raw;
|
|
16874
16772
|
try {
|
|
16875
|
-
raw =
|
|
16773
|
+
raw = fs21.readFileSync(file, "utf-8");
|
|
16876
16774
|
} catch (err) {
|
|
16877
16775
|
if (err.code === "ENOENT") return null;
|
|
16878
16776
|
throw err;
|
|
@@ -16896,29 +16794,29 @@ function readAttachmentMirror(env = process.env) {
|
|
|
16896
16794
|
function writeAttachmentMirror(mirror, env = process.env) {
|
|
16897
16795
|
const validated = parseAttachmentMirror(mirror);
|
|
16898
16796
|
const file = attachmentMirrorPath(env);
|
|
16899
|
-
const dir =
|
|
16900
|
-
|
|
16797
|
+
const dir = path19.dirname(file);
|
|
16798
|
+
fs21.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
16901
16799
|
const tmp = `${file}.${process.pid}.${crypto4.randomBytes(4).toString("hex")}.tmp`;
|
|
16902
|
-
const fd =
|
|
16800
|
+
const fd = fs21.openSync(tmp, "w", 384);
|
|
16903
16801
|
try {
|
|
16904
|
-
|
|
16905
|
-
|
|
16802
|
+
fs21.writeSync(fd, JSON.stringify(validated, null, 2) + "\n");
|
|
16803
|
+
fs21.fsyncSync(fd);
|
|
16906
16804
|
} finally {
|
|
16907
|
-
|
|
16805
|
+
fs21.closeSync(fd);
|
|
16908
16806
|
}
|
|
16909
16807
|
try {
|
|
16910
|
-
|
|
16808
|
+
fs21.renameSync(tmp, file);
|
|
16911
16809
|
} catch (err) {
|
|
16912
16810
|
try {
|
|
16913
|
-
|
|
16811
|
+
fs21.unlinkSync(tmp);
|
|
16914
16812
|
} catch {
|
|
16915
16813
|
}
|
|
16916
16814
|
throw err;
|
|
16917
16815
|
}
|
|
16918
16816
|
if (process.platform !== "win32") {
|
|
16919
16817
|
try {
|
|
16920
|
-
|
|
16921
|
-
|
|
16818
|
+
fs21.chmodSync(dir, 448);
|
|
16819
|
+
fs21.chmodSync(file, 384);
|
|
16922
16820
|
} catch {
|
|
16923
16821
|
}
|
|
16924
16822
|
}
|
|
@@ -16926,7 +16824,7 @@ function writeAttachmentMirror(mirror, env = process.env) {
|
|
|
16926
16824
|
function removeAttachmentMirror(env = process.env) {
|
|
16927
16825
|
const file = attachmentMirrorPath(env);
|
|
16928
16826
|
try {
|
|
16929
|
-
|
|
16827
|
+
fs21.unlinkSync(file);
|
|
16930
16828
|
} catch (err) {
|
|
16931
16829
|
if (err.code !== "ENOENT") throw err;
|
|
16932
16830
|
}
|
|
@@ -17054,7 +16952,6 @@ function createAttachmentReconciler(options) {
|
|
|
17054
16952
|
}
|
|
17055
16953
|
|
|
17056
16954
|
// src/app/dashboard/dashboardDecisionInbox.ts
|
|
17057
|
-
import Database5 from "better-sqlite3";
|
|
17058
16955
|
function dashboardDecisionInboxPath() {
|
|
17059
16956
|
return `${ensureDaemonStateDir().dir}/dashboard-decision-inbox.db`;
|
|
17060
16957
|
}
|
|
@@ -17099,8 +16996,6 @@ function migrateLegacyUniqueConstraint(db) {
|
|
|
17099
16996
|
}
|
|
17100
16997
|
function initInboxSchema(db) {
|
|
17101
16998
|
db.exec(`
|
|
17102
|
-
PRAGMA journal_mode = WAL;
|
|
17103
|
-
|
|
17104
16999
|
CREATE TABLE IF NOT EXISTS dashboard_decision_inbox (
|
|
17105
17000
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
17106
17001
|
athena_session_id TEXT NOT NULL,
|
|
@@ -17122,8 +17017,9 @@ function initInboxSchema(db) {
|
|
|
17122
17017
|
`);
|
|
17123
17018
|
}
|
|
17124
17019
|
function createDashboardDecisionInbox(options = {}) {
|
|
17125
|
-
const db =
|
|
17126
|
-
|
|
17020
|
+
const db = openVersionedDb(options.dbPath ?? dashboardDecisionInboxPath(), {
|
|
17021
|
+
migrate: initInboxSchema
|
|
17022
|
+
});
|
|
17127
17023
|
const upsertUnconsumed = db.prepare(`
|
|
17128
17024
|
INSERT INTO dashboard_decision_inbox (
|
|
17129
17025
|
athena_session_id,
|
|
@@ -17332,9 +17228,9 @@ function createDashboardPairedExecution(options) {
|
|
|
17332
17228
|
}
|
|
17333
17229
|
|
|
17334
17230
|
// src/app/dashboard/remoteWorkspaceResolver.ts
|
|
17335
|
-
import
|
|
17231
|
+
import fs22 from "fs";
|
|
17336
17232
|
import os14 from "os";
|
|
17337
|
-
import
|
|
17233
|
+
import path20 from "path";
|
|
17338
17234
|
function resolveRemoteWorkspace(assignment, options = {}) {
|
|
17339
17235
|
const { spec, runId, runnerId } = assignment;
|
|
17340
17236
|
if (spec.projectDir) {
|
|
@@ -17343,14 +17239,14 @@ function resolveRemoteWorkspace(assignment, options = {}) {
|
|
|
17343
17239
|
const sessionId = spec.athenaSessionId ?? spec.sessionId;
|
|
17344
17240
|
const deploymentSlug = deploymentSlugFromUrl(options.dashboardUrl);
|
|
17345
17241
|
const stateDir = daemonStatePaths(options.env).dir;
|
|
17346
|
-
const projectDir = sessionId ?
|
|
17242
|
+
const projectDir = sessionId ? path20.join(
|
|
17347
17243
|
stateDir,
|
|
17348
17244
|
"remote-workspaces",
|
|
17349
17245
|
deploymentSlug,
|
|
17350
17246
|
sanitizePathSegment(runnerId),
|
|
17351
17247
|
"sessions",
|
|
17352
17248
|
sanitizePathSegment(sessionId)
|
|
17353
|
-
) :
|
|
17249
|
+
) : path20.join(
|
|
17354
17250
|
stateDir,
|
|
17355
17251
|
"remote-workspaces",
|
|
17356
17252
|
deploymentSlug,
|
|
@@ -17359,10 +17255,10 @@ function resolveRemoteWorkspace(assignment, options = {}) {
|
|
|
17359
17255
|
sanitizePathSegment(runId)
|
|
17360
17256
|
);
|
|
17361
17257
|
try {
|
|
17362
|
-
|
|
17258
|
+
fs22.mkdirSync(projectDir, { recursive: true, mode: 448 });
|
|
17363
17259
|
if (process.platform !== "win32") {
|
|
17364
17260
|
try {
|
|
17365
|
-
|
|
17261
|
+
fs22.chmodSync(projectDir, 448);
|
|
17366
17262
|
} catch {
|
|
17367
17263
|
}
|
|
17368
17264
|
}
|
|
@@ -17378,8 +17274,8 @@ function resolveRemoteWorkspace(assignment, options = {}) {
|
|
|
17378
17274
|
return validateProjectDir(projectDir, options.env);
|
|
17379
17275
|
}
|
|
17380
17276
|
function validateProjectDir(projectDir, env = process.env) {
|
|
17381
|
-
const resolved =
|
|
17382
|
-
if (!
|
|
17277
|
+
const resolved = path20.resolve(projectDir);
|
|
17278
|
+
if (!path20.isAbsolute(projectDir)) {
|
|
17383
17279
|
return {
|
|
17384
17280
|
kind: "rejected",
|
|
17385
17281
|
rejection: {
|
|
@@ -17388,7 +17284,7 @@ function validateProjectDir(projectDir, env = process.env) {
|
|
|
17388
17284
|
}
|
|
17389
17285
|
};
|
|
17390
17286
|
}
|
|
17391
|
-
const home =
|
|
17287
|
+
const home = path20.resolve(env["HOME"] ?? os14.homedir());
|
|
17392
17288
|
if (resolved === home) {
|
|
17393
17289
|
return {
|
|
17394
17290
|
kind: "rejected",
|
|
@@ -17400,7 +17296,7 @@ function validateProjectDir(projectDir, env = process.env) {
|
|
|
17400
17296
|
}
|
|
17401
17297
|
let stat;
|
|
17402
17298
|
try {
|
|
17403
|
-
stat =
|
|
17299
|
+
stat = fs22.statSync(resolved);
|
|
17404
17300
|
} catch {
|
|
17405
17301
|
return {
|
|
17406
17302
|
kind: "rejected",
|
|
@@ -17845,18 +17741,18 @@ async function runDashboardRuntimeDaemon(options = {}) {
|
|
|
17845
17741
|
}
|
|
17846
17742
|
|
|
17847
17743
|
// src/infra/daemon/pidLock.ts
|
|
17848
|
-
import
|
|
17744
|
+
import fs23 from "fs";
|
|
17849
17745
|
function acquirePidLock(pidPath) {
|
|
17850
17746
|
const ownPid = process.pid;
|
|
17851
17747
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
17852
17748
|
try {
|
|
17853
|
-
const fd =
|
|
17749
|
+
const fd = fs23.openSync(pidPath, "wx", 384);
|
|
17854
17750
|
try {
|
|
17855
|
-
|
|
17751
|
+
fs23.writeSync(fd, `${ownPid}
|
|
17856
17752
|
`);
|
|
17857
|
-
|
|
17753
|
+
fs23.fsyncSync(fd);
|
|
17858
17754
|
} finally {
|
|
17859
|
-
|
|
17755
|
+
fs23.closeSync(fd);
|
|
17860
17756
|
}
|
|
17861
17757
|
return makeHandle(pidPath, ownPid);
|
|
17862
17758
|
} catch (err) {
|
|
@@ -17870,7 +17766,7 @@ function acquirePidLock(pidPath) {
|
|
|
17870
17766
|
}
|
|
17871
17767
|
if (existing.state === "stale") {
|
|
17872
17768
|
try {
|
|
17873
|
-
|
|
17769
|
+
fs23.unlinkSync(pidPath);
|
|
17874
17770
|
} catch (err) {
|
|
17875
17771
|
if (err.code !== "ENOENT") throw err;
|
|
17876
17772
|
}
|
|
@@ -17884,7 +17780,7 @@ function acquirePidLock(pidPath) {
|
|
|
17884
17780
|
function readPidLock(pidPath) {
|
|
17885
17781
|
let raw;
|
|
17886
17782
|
try {
|
|
17887
|
-
raw =
|
|
17783
|
+
raw = fs23.readFileSync(pidPath, "utf-8");
|
|
17888
17784
|
} catch (err) {
|
|
17889
17785
|
if (err.code === "ENOENT") {
|
|
17890
17786
|
return { state: "absent" };
|
|
@@ -17908,9 +17804,9 @@ function makeHandle(pidPath, pid) {
|
|
|
17908
17804
|
if (released) return;
|
|
17909
17805
|
released = true;
|
|
17910
17806
|
try {
|
|
17911
|
-
const raw =
|
|
17807
|
+
const raw = fs23.readFileSync(pidPath, "utf-8").trim();
|
|
17912
17808
|
if (raw === String(pid)) {
|
|
17913
|
-
|
|
17809
|
+
fs23.unlinkSync(pidPath);
|
|
17914
17810
|
}
|
|
17915
17811
|
} catch (err) {
|
|
17916
17812
|
if (err.code !== "ENOENT") {
|
|
@@ -17936,7 +17832,7 @@ function isProcessAlive(pid) {
|
|
|
17936
17832
|
}
|
|
17937
17833
|
|
|
17938
17834
|
// src/infra/daemon/udsIpc.ts
|
|
17939
|
-
import
|
|
17835
|
+
import fs24 from "fs";
|
|
17940
17836
|
import net2 from "net";
|
|
17941
17837
|
|
|
17942
17838
|
// src/infra/daemon/udsFrameCodec.ts
|
|
@@ -17981,7 +17877,7 @@ async function startUdsServer(socketPath, handler, log) {
|
|
|
17981
17877
|
});
|
|
17982
17878
|
if (process.platform !== "win32") {
|
|
17983
17879
|
try {
|
|
17984
|
-
|
|
17880
|
+
fs24.chmodSync(socketPath, 384);
|
|
17985
17881
|
} catch {
|
|
17986
17882
|
}
|
|
17987
17883
|
}
|
|
@@ -17991,7 +17887,7 @@ async function startUdsServer(socketPath, handler, log) {
|
|
|
17991
17887
|
server.close(() => resolve());
|
|
17992
17888
|
});
|
|
17993
17889
|
try {
|
|
17994
|
-
|
|
17890
|
+
fs24.unlinkSync(socketPath);
|
|
17995
17891
|
} catch (err) {
|
|
17996
17892
|
if (err.code !== "ENOENT") {
|
|
17997
17893
|
}
|
|
@@ -18109,7 +18005,7 @@ async function sendUdsRequest(socketPath, request, options = {}) {
|
|
|
18109
18005
|
async function unlinkStaleSocket(socketPath) {
|
|
18110
18006
|
let stat;
|
|
18111
18007
|
try {
|
|
18112
|
-
stat =
|
|
18008
|
+
stat = fs24.statSync(socketPath);
|
|
18113
18009
|
} catch (err) {
|
|
18114
18010
|
if (err.code === "ENOENT") return;
|
|
18115
18011
|
throw err;
|
|
@@ -18136,7 +18032,7 @@ async function unlinkStaleSocket(socketPath) {
|
|
|
18136
18032
|
`uds path ${socketPath} is in use by another process; aborting`
|
|
18137
18033
|
);
|
|
18138
18034
|
}
|
|
18139
|
-
|
|
18035
|
+
fs24.unlinkSync(socketPath);
|
|
18140
18036
|
}
|
|
18141
18037
|
|
|
18142
18038
|
export {
|
|
@@ -18154,8 +18050,8 @@ export {
|
|
|
18154
18050
|
mergeFeedItems,
|
|
18155
18051
|
buildPostByToolUseId,
|
|
18156
18052
|
createFeedMapper,
|
|
18157
|
-
|
|
18158
|
-
|
|
18053
|
+
attachRuntimeEventLoop,
|
|
18054
|
+
startDashboardDecisionDrain,
|
|
18159
18055
|
generateId,
|
|
18160
18056
|
daemonStatePaths,
|
|
18161
18057
|
ensureDaemonStateDir,
|
|
@@ -18237,4 +18133,4 @@ export {
|
|
|
18237
18133
|
startUdsServer,
|
|
18238
18134
|
sendUdsRequest
|
|
18239
18135
|
};
|
|
18240
|
-
//# sourceMappingURL=chunk-
|
|
18136
|
+
//# sourceMappingURL=chunk-QBMYQJFX.js.map
|