issue-flow 0.16.0 → 0.18.0
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/agent-HMZTXCRT.js +30 -0
- package/dist/{analyze-556KCJKZ.js → analyze-Q4ONIWRZ.js} +22 -22
- package/dist/{apply-WVV7G7WN.js → apply-EEFPY2W4.js} +14 -14
- package/dist/{bench-UBFGKUHR.js → bench-WWWXLTFH.js} +23 -23
- package/dist/{chunk-GV2DABNZ.js → chunk-34OCSKOB.js} +12 -12
- package/dist/chunk-3R27MJ5R.js +25 -0
- package/dist/chunk-3R27MJ5R.js.map +1 -0
- package/dist/{chunk-PGN2VX4Y.js → chunk-45WZ6GMI.js} +32 -48
- package/dist/chunk-45WZ6GMI.js.map +1 -0
- package/dist/{chunk-H3EYBJZU.js → chunk-62GVVZQO.js} +10 -10
- package/dist/{chunk-GPLP7EPL.js → chunk-6K2LCFG6.js} +10 -10
- package/dist/{chunk-GPLP7EPL.js.map → chunk-6K2LCFG6.js.map} +1 -1
- package/dist/{chunk-OGVWJO4S.js → chunk-7F7LGMVO.js} +3 -3
- package/dist/{chunk-X6GY3CM2.js → chunk-7UMMFOL2.js} +104 -104
- package/dist/chunk-7UMMFOL2.js.map +1 -0
- package/dist/{chunk-OLUOZLU5.js → chunk-CWSQVHVV.js} +1691 -1075
- package/dist/chunk-CWSQVHVV.js.map +1 -0
- package/dist/{chunk-MXCOSPLJ.js → chunk-DGRTJD5R.js} +3 -3
- package/dist/{chunk-ROIIEVZH.js → chunk-DUS5ZNKL.js} +474 -361
- package/dist/chunk-DUS5ZNKL.js.map +1 -0
- package/dist/{chunk-BSBJXPLC.js → chunk-E4I6MMHD.js} +10 -10
- package/dist/{chunk-QZOBR6YF.js → chunk-ECU7YO7S.js} +4 -4
- package/dist/chunk-ECU7YO7S.js.map +1 -0
- package/dist/{chunk-EPFC4LHT.js → chunk-EG2MGDP5.js} +9 -9
- package/dist/chunk-HOAHMERN.js +229 -0
- package/dist/chunk-HOAHMERN.js.map +1 -0
- package/dist/{chunk-QVLIIGBU.js → chunk-ICM463EO.js} +60 -13
- package/dist/chunk-ICM463EO.js.map +1 -0
- package/dist/{chunk-4FUWWBTP.js → chunk-KMCS76ZW.js} +8 -8
- package/dist/{chunk-SW2MF47D.js → chunk-KRAGAUKD.js} +19 -19
- package/dist/{chunk-OOX3RMD3.js → chunk-LGHND47K.js} +4 -4
- package/dist/{chunk-VTF4RWS5.js → chunk-LI35PIIS.js} +7 -7
- package/dist/{chunk-Q2TZA6AC.js → chunk-LTH6XYZ3.js} +14 -14
- package/dist/{chunk-YA6FWFYW.js → chunk-NEWNXZ65.js} +62 -11
- package/dist/chunk-NEWNXZ65.js.map +1 -0
- package/dist/{chunk-2H43OVTU.js → chunk-OODXFCQV.js} +11 -11
- package/dist/{chunk-KV2K7MK2.js → chunk-PMLE7KAQ.js} +20 -12
- package/dist/chunk-PMLE7KAQ.js.map +1 -0
- package/dist/{chunk-R42YHLDV.js → chunk-PPZ3K4YM.js} +2 -2
- package/dist/{chunk-J2LJB2OH.js → chunk-QF7VEL3M.js} +739 -579
- package/dist/chunk-QF7VEL3M.js.map +1 -0
- package/dist/{chunk-VK65RNFA.js → chunk-QJ44VG5G.js} +3 -3
- package/dist/{chunk-FKWYD44M.js → chunk-QPGEFLMY.js} +2 -2
- package/dist/{chunk-NQCR3HSU.js → chunk-RNFUVKC3.js} +3 -3
- package/dist/{chunk-G5LACAXO.js → chunk-RYOUZ6EK.js} +2 -2
- package/dist/{chunk-MCLPSL6B.js → chunk-SA6HXL4Y.js} +3 -3
- package/dist/{chunk-GVOZ6NDO.js → chunk-SOM4QA6C.js} +3 -3
- package/dist/{chunk-73TS2H42.js → chunk-TZYINKCC.js} +154 -34
- package/dist/chunk-TZYINKCC.js.map +1 -0
- package/dist/{chunk-DBEMD34J.js → chunk-V7QOIQYC.js} +8 -8
- package/dist/{chunk-IAMIBZRF.js → chunk-WCCZJL7W.js} +2 -2
- package/dist/{chunk-OGN2NX57.js → chunk-XUEMATVY.js} +9 -9
- package/dist/{chunk-SVDEWOUD.js → chunk-XZUEFTUP.js} +6 -6
- package/dist/{chunk-WLJVL25O.js → chunk-YUNBOUQH.js} +6 -6
- package/dist/{chunk-WNROH2YE.js → chunk-YVPZUN6X.js} +2 -2
- package/dist/chunk-ZVZMWNCG.js +415 -0
- package/dist/chunk-ZVZMWNCG.js.map +1 -0
- package/dist/cli.js +57 -49
- package/dist/cli.js.map +1 -1
- package/dist/{config-UE7IP36J.js → config-LEBZZCJP.js} +13 -13
- package/dist/{conventions-Z3QC2NSG.js → conventions-XOT6YW5L.js} +15 -15
- package/dist/{diagnostics-2Z5HFQOO.js → diagnostics-HTCWF7F4.js} +7 -7
- package/dist/execute-LELOSR5T.js +37 -0
- package/dist/{generate-PB4L7V5N.js → generate-JGW5JSKB.js} +22 -22
- package/dist/{git-7MUOGVRG.js → git-A2IP32PQ.js} +2 -2
- package/dist/init-TO6MUXO4.js +26 -0
- package/dist/{operations-QDJAFZUN.js → operations-O4FORFIH.js} +15 -15
- package/dist/{permissions-XVXXENVP.js → permissions-G4XCAESF.js} +4 -4
- package/dist/plan-OXAT344Q.js +34 -0
- package/dist/{policy-6WQZB6B2.js → policy-U5NBFAO5.js} +14 -14
- package/dist/pr-UZHNKKZC.js +39 -0
- package/dist/pr-review-Q4ACAWDM.js +32 -0
- package/dist/prd-6CYEMLDY.js +33 -0
- package/dist/{ps-CX7756QG.js → ps-76L6ZAZL.js} +12 -12
- package/dist/{recorder-RK6XZYF3.js → recorder-2BAKPBRA.js} +7 -7
- package/dist/{registry-POSB35Q4.js → registry-XLHHYLTR.js} +5 -5
- package/dist/{resume-OQJZV3JH.js → resume-URCU6CIJ.js} +39 -38
- package/dist/{resume-OQJZV3JH.js.map → resume-URCU6CIJ.js.map} +1 -1
- package/dist/review-LVZT2TMW.js +35 -0
- package/dist/routing-EK6RBZM4.js +33 -0
- package/dist/run-2RXJCJKT.js +56 -0
- package/dist/{session-publisher-VVXOCJ2S.js → session-publisher-KAJQRHNZ.js} +3 -2
- package/dist/{state-manager-TIC73GQY.js → state-manager-UPHO2EA2.js} +4 -4
- package/dist/state-manager-UPHO2EA2.js.map +1 -0
- package/dist/{usage-WDDNSIHP.js → usage-KTMYXAPF.js} +12 -12
- package/dist/{web-IPGY52AK.js → web-B5UPHHR5.js} +20 -18
- package/dist/{web-IPGY52AK.js.map → web-B5UPHHR5.js.map} +1 -1
- package/package.json +6 -5
- package/web/AGENTS.md +125 -18
- package/web/public/app.css +420 -234
- package/web/public/app.js +371 -80
- package/web/public/index.html +106 -78
- package/dist/agent-PMWCDWOW.js +0 -30
- package/dist/chunk-73TS2H42.js.map +0 -1
- package/dist/chunk-7Z4Y3VKM.js +0 -26
- package/dist/chunk-7Z4Y3VKM.js.map +0 -1
- package/dist/chunk-AOAQU7PD.js +0 -180
- package/dist/chunk-AOAQU7PD.js.map +0 -1
- package/dist/chunk-J2LJB2OH.js.map +0 -1
- package/dist/chunk-KV2K7MK2.js.map +0 -1
- package/dist/chunk-OLUOZLU5.js.map +0 -1
- package/dist/chunk-PGN2VX4Y.js.map +0 -1
- package/dist/chunk-QVLIIGBU.js.map +0 -1
- package/dist/chunk-QZOBR6YF.js.map +0 -1
- package/dist/chunk-ROIIEVZH.js.map +0 -1
- package/dist/chunk-X6GY3CM2.js.map +0 -1
- package/dist/chunk-YA6FWFYW.js.map +0 -1
- package/dist/execute-QYLJ5L4T.js +0 -36
- package/dist/init-XBERAZ4O.js +0 -24
- package/dist/plan-RXOW32KY.js +0 -34
- package/dist/pr-3WFYAVA7.js +0 -39
- package/dist/pr-review-RK63ZEMM.js +0 -32
- package/dist/prd-LRV6IW3E.js +0 -33
- package/dist/review-AEFISJZD.js +0 -35
- package/dist/routing-EA5NJGNU.js +0 -94
- package/dist/routing-EA5NJGNU.js.map +0 -1
- package/dist/run-HSC7R33Y.js +0 -55
- /package/dist/{agent-PMWCDWOW.js.map → agent-HMZTXCRT.js.map} +0 -0
- /package/dist/{analyze-556KCJKZ.js.map → analyze-Q4ONIWRZ.js.map} +0 -0
- /package/dist/{apply-WVV7G7WN.js.map → apply-EEFPY2W4.js.map} +0 -0
- /package/dist/{bench-UBFGKUHR.js.map → bench-WWWXLTFH.js.map} +0 -0
- /package/dist/{chunk-GV2DABNZ.js.map → chunk-34OCSKOB.js.map} +0 -0
- /package/dist/{chunk-H3EYBJZU.js.map → chunk-62GVVZQO.js.map} +0 -0
- /package/dist/{chunk-OGVWJO4S.js.map → chunk-7F7LGMVO.js.map} +0 -0
- /package/dist/{chunk-MXCOSPLJ.js.map → chunk-DGRTJD5R.js.map} +0 -0
- /package/dist/{chunk-BSBJXPLC.js.map → chunk-E4I6MMHD.js.map} +0 -0
- /package/dist/{chunk-EPFC4LHT.js.map → chunk-EG2MGDP5.js.map} +0 -0
- /package/dist/{chunk-4FUWWBTP.js.map → chunk-KMCS76ZW.js.map} +0 -0
- /package/dist/{chunk-SW2MF47D.js.map → chunk-KRAGAUKD.js.map} +0 -0
- /package/dist/{chunk-OOX3RMD3.js.map → chunk-LGHND47K.js.map} +0 -0
- /package/dist/{chunk-VTF4RWS5.js.map → chunk-LI35PIIS.js.map} +0 -0
- /package/dist/{chunk-Q2TZA6AC.js.map → chunk-LTH6XYZ3.js.map} +0 -0
- /package/dist/{chunk-2H43OVTU.js.map → chunk-OODXFCQV.js.map} +0 -0
- /package/dist/{chunk-R42YHLDV.js.map → chunk-PPZ3K4YM.js.map} +0 -0
- /package/dist/{chunk-VK65RNFA.js.map → chunk-QJ44VG5G.js.map} +0 -0
- /package/dist/{chunk-FKWYD44M.js.map → chunk-QPGEFLMY.js.map} +0 -0
- /package/dist/{chunk-NQCR3HSU.js.map → chunk-RNFUVKC3.js.map} +0 -0
- /package/dist/{chunk-G5LACAXO.js.map → chunk-RYOUZ6EK.js.map} +0 -0
- /package/dist/{chunk-MCLPSL6B.js.map → chunk-SA6HXL4Y.js.map} +0 -0
- /package/dist/{chunk-GVOZ6NDO.js.map → chunk-SOM4QA6C.js.map} +0 -0
- /package/dist/{chunk-DBEMD34J.js.map → chunk-V7QOIQYC.js.map} +0 -0
- /package/dist/{chunk-IAMIBZRF.js.map → chunk-WCCZJL7W.js.map} +0 -0
- /package/dist/{chunk-OGN2NX57.js.map → chunk-XUEMATVY.js.map} +0 -0
- /package/dist/{chunk-SVDEWOUD.js.map → chunk-XZUEFTUP.js.map} +0 -0
- /package/dist/{chunk-WLJVL25O.js.map → chunk-YUNBOUQH.js.map} +0 -0
- /package/dist/{chunk-WNROH2YE.js.map → chunk-YVPZUN6X.js.map} +0 -0
- /package/dist/{config-UE7IP36J.js.map → config-LEBZZCJP.js.map} +0 -0
- /package/dist/{conventions-Z3QC2NSG.js.map → conventions-XOT6YW5L.js.map} +0 -0
- /package/dist/{diagnostics-2Z5HFQOO.js.map → diagnostics-HTCWF7F4.js.map} +0 -0
- /package/dist/{execute-QYLJ5L4T.js.map → execute-LELOSR5T.js.map} +0 -0
- /package/dist/{generate-PB4L7V5N.js.map → generate-JGW5JSKB.js.map} +0 -0
- /package/dist/{git-7MUOGVRG.js.map → git-A2IP32PQ.js.map} +0 -0
- /package/dist/{init-XBERAZ4O.js.map → init-TO6MUXO4.js.map} +0 -0
- /package/dist/{operations-QDJAFZUN.js.map → operations-O4FORFIH.js.map} +0 -0
- /package/dist/{permissions-XVXXENVP.js.map → permissions-G4XCAESF.js.map} +0 -0
- /package/dist/{plan-RXOW32KY.js.map → plan-OXAT344Q.js.map} +0 -0
- /package/dist/{policy-6WQZB6B2.js.map → policy-U5NBFAO5.js.map} +0 -0
- /package/dist/{pr-3WFYAVA7.js.map → pr-UZHNKKZC.js.map} +0 -0
- /package/dist/{pr-review-RK63ZEMM.js.map → pr-review-Q4ACAWDM.js.map} +0 -0
- /package/dist/{prd-LRV6IW3E.js.map → prd-6CYEMLDY.js.map} +0 -0
- /package/dist/{ps-CX7756QG.js.map → ps-76L6ZAZL.js.map} +0 -0
- /package/dist/{recorder-RK6XZYF3.js.map → recorder-2BAKPBRA.js.map} +0 -0
- /package/dist/{registry-POSB35Q4.js.map → registry-XLHHYLTR.js.map} +0 -0
- /package/dist/{review-AEFISJZD.js.map → review-LVZT2TMW.js.map} +0 -0
- /package/dist/{run-HSC7R33Y.js.map → routing-EK6RBZM4.js.map} +0 -0
- /package/dist/{session-publisher-VVXOCJ2S.js.map → run-2RXJCJKT.js.map} +0 -0
- /package/dist/{state-manager-TIC73GQY.js.map → session-publisher-KAJQRHNZ.js.map} +0 -0
- /package/dist/{usage-WDDNSIHP.js.map → usage-KTMYXAPF.js.map} +0 -0
|
@@ -2,14 +2,224 @@
|
|
|
2
2
|
import {
|
|
3
3
|
setTelemetrySessionId
|
|
4
4
|
} from "./chunk-P3LRUNC6.js";
|
|
5
|
+
import {
|
|
6
|
+
writeFileAtomic
|
|
7
|
+
} from "./chunk-3R27MJ5R.js";
|
|
5
8
|
|
|
6
|
-
// src/core/session
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
// src/core/session/publishers.ts
|
|
10
|
+
import { utimes } from "fs/promises";
|
|
11
|
+
|
|
12
|
+
// src/core/session/events.ts
|
|
10
13
|
var DEFAULT_LOG_LIMIT = 200;
|
|
11
14
|
var DEFAULT_THROTTLE_MS = 1e3;
|
|
12
15
|
var DEFAULT_SESSION_HEARTBEAT_MS = 1e4;
|
|
16
|
+
|
|
17
|
+
// src/core/session/derive.ts
|
|
18
|
+
function accumulate(current, delta) {
|
|
19
|
+
return delta === void 0 ? current : (current ?? 0) + delta;
|
|
20
|
+
}
|
|
21
|
+
function accumulateUsage(target, event) {
|
|
22
|
+
return {
|
|
23
|
+
...target,
|
|
24
|
+
inputTokens: accumulate(target.inputTokens, event.inputTokens),
|
|
25
|
+
outputTokens: accumulate(target.outputTokens, event.outputTokens),
|
|
26
|
+
cacheReadTokens: accumulate(target.cacheReadTokens, event.cacheReadTokens),
|
|
27
|
+
cacheCreationTokens: accumulate(target.cacheCreationTokens, event.cacheCreationTokens),
|
|
28
|
+
costUsd: accumulate(target.costUsd, event.costUsd)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function reported(value, previous) {
|
|
32
|
+
return value === void 0 ? previous : value;
|
|
33
|
+
}
|
|
34
|
+
function secondsBetween(from, to) {
|
|
35
|
+
if (from === null) return null;
|
|
36
|
+
const fromMs = Date.parse(from);
|
|
37
|
+
const toMs = Date.parse(to);
|
|
38
|
+
if (Number.isNaN(fromMs) || Number.isNaN(toMs)) return null;
|
|
39
|
+
return Math.max(0, Math.round((toMs - fromMs) / 1e3));
|
|
40
|
+
}
|
|
41
|
+
function computePercent(phasesCompleted, phasesTotal) {
|
|
42
|
+
if (phasesTotal === 0) return 0;
|
|
43
|
+
return Math.round(phasesCompleted / phasesTotal * 100);
|
|
44
|
+
}
|
|
45
|
+
function estimateRemainingSeconds(snapshot) {
|
|
46
|
+
const completions = snapshot.stories.map((story) => story.completedAt === null ? Number.NaN : Date.parse(story.completedAt)).filter((ms) => !Number.isNaN(ms)).sort((a, b) => a - b);
|
|
47
|
+
if (completions.length < 2) return null;
|
|
48
|
+
const startMs = snapshot.startedAt === null ? Number.NaN : Date.parse(snapshot.startedAt);
|
|
49
|
+
const boundaries = Number.isNaN(startMs) ? completions : [startMs, ...completions];
|
|
50
|
+
const durations = [];
|
|
51
|
+
for (let i = 1; i < boundaries.length; i++) {
|
|
52
|
+
durations.push(Math.max(0, boundaries[i] - boundaries[i - 1]));
|
|
53
|
+
}
|
|
54
|
+
if (durations.length === 0) return null;
|
|
55
|
+
const averageMs = durations.reduce((sum, d) => sum + d, 0) / durations.length;
|
|
56
|
+
const pending = snapshot.stories.filter((story) => !story.passes).length;
|
|
57
|
+
return Math.round(averageMs * pending / 1e3);
|
|
58
|
+
}
|
|
59
|
+
function deriveNextSteps(snapshot) {
|
|
60
|
+
if (snapshot.status === "completed") return [];
|
|
61
|
+
return snapshot.phases.filter((phase) => phase.status === "pending").map((phase) => phase.name);
|
|
62
|
+
}
|
|
63
|
+
function deriveStoryStatus(story, activeStory) {
|
|
64
|
+
if (story.passes) return "done";
|
|
65
|
+
if (story.status === "in_review") return "in_review";
|
|
66
|
+
if (activeStory !== null && activeStory === story.id) return "in_progress";
|
|
67
|
+
return "backlog";
|
|
68
|
+
}
|
|
69
|
+
function deriveStoryStatuses(snapshot) {
|
|
70
|
+
const activeStory = snapshot.currentActivity?.story ?? null;
|
|
71
|
+
return snapshot.stories.map((story) => {
|
|
72
|
+
const status = deriveStoryStatus(story, activeStory);
|
|
73
|
+
return status === story.status ? story : { ...story, status };
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// src/core/session/reducer-git.ts
|
|
78
|
+
function applyGitEvent(snapshot, event) {
|
|
79
|
+
switch (event.type) {
|
|
80
|
+
case "git:update":
|
|
81
|
+
return {
|
|
82
|
+
...snapshot,
|
|
83
|
+
git: {
|
|
84
|
+
branch: event.branch ?? snapshot.git.branch,
|
|
85
|
+
baseBranch: event.baseBranch ?? snapshot.git.baseBranch,
|
|
86
|
+
branchCreated: reported(event.branchCreated, snapshot.git.branchCreated),
|
|
87
|
+
startCommit: snapshot.git.startCommit,
|
|
88
|
+
commits: event.commits ?? snapshot.git.commits
|
|
89
|
+
},
|
|
90
|
+
repository: {
|
|
91
|
+
// `undefined` is "not collected", so the previous value stands; an
|
|
92
|
+
// explicit `null` is "collected and unavailable" and overwrites it.
|
|
93
|
+
name: reported(event.repositoryName, snapshot.repository.name),
|
|
94
|
+
remoteUrl: reported(event.remoteUrl, snapshot.repository.remoteUrl),
|
|
95
|
+
branch: event.branch ?? snapshot.repository.branch,
|
|
96
|
+
headCommit: reported(event.headCommit, snapshot.repository.headCommit),
|
|
97
|
+
root: reported(event.repositoryRoot, snapshot.repository.root)
|
|
98
|
+
},
|
|
99
|
+
pullRequests: event.pullRequests ?? snapshot.pullRequests
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/core/session/reducer-log.ts
|
|
105
|
+
import { stripVTControlCharacters } from "util";
|
|
106
|
+
function applyLogEvent(snapshot, event, options) {
|
|
107
|
+
switch (event.type) {
|
|
108
|
+
case "log": {
|
|
109
|
+
const limit = options?.logLimit ?? DEFAULT_LOG_LIMIT;
|
|
110
|
+
const entry = {
|
|
111
|
+
at: event.at,
|
|
112
|
+
level: event.level,
|
|
113
|
+
message: stripVTControlCharacters(event.message)
|
|
114
|
+
};
|
|
115
|
+
const logs = [...snapshot.logs, entry].slice(-Math.max(1, limit));
|
|
116
|
+
return { ...snapshot, logs };
|
|
117
|
+
}
|
|
118
|
+
case "process:output": {
|
|
119
|
+
const limit = options?.logLimit ?? DEFAULT_LOG_LIMIT;
|
|
120
|
+
const entry = {
|
|
121
|
+
at: event.at,
|
|
122
|
+
phase: event.phase,
|
|
123
|
+
executionId: event.executionId,
|
|
124
|
+
provider: event.provider,
|
|
125
|
+
stream: event.stream,
|
|
126
|
+
message: stripVTControlCharacters(event.message)
|
|
127
|
+
};
|
|
128
|
+
return {
|
|
129
|
+
...snapshot,
|
|
130
|
+
processLogs: [...snapshot.processLogs, entry].slice(-Math.max(1, limit))
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
case "execution:update": {
|
|
134
|
+
const index = snapshot.executions.findIndex((entry) => entry.id === event.execution.id);
|
|
135
|
+
const executions = [...snapshot.executions];
|
|
136
|
+
if (index === -1) executions.push(event.execution);
|
|
137
|
+
else executions[index] = event.execution;
|
|
138
|
+
return { ...snapshot, executions };
|
|
139
|
+
}
|
|
140
|
+
default: {
|
|
141
|
+
const _exhaustive = event;
|
|
142
|
+
return _exhaustive;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/core/session/reducer-metrics.ts
|
|
148
|
+
function applyMetricsEvent(snapshot, event) {
|
|
149
|
+
switch (event.type) {
|
|
150
|
+
case "metrics:update": {
|
|
151
|
+
if (event.scope === "story") {
|
|
152
|
+
if (event.storyId === void 0) return snapshot;
|
|
153
|
+
if (!snapshot.stories.some((s) => s.id === event.storyId)) return snapshot;
|
|
154
|
+
return {
|
|
155
|
+
...snapshot,
|
|
156
|
+
stories: snapshot.stories.map(
|
|
157
|
+
(story) => story.id === event.storyId ? {
|
|
158
|
+
...accumulateUsage(story, event),
|
|
159
|
+
durationSeconds: accumulate(story.durationSeconds, event.durationSeconds)
|
|
160
|
+
} : story
|
|
161
|
+
)
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (event.phase === void 0) return snapshot;
|
|
165
|
+
if (!snapshot.phases.some((p) => p.name === event.phase)) return snapshot;
|
|
166
|
+
return {
|
|
167
|
+
...snapshot,
|
|
168
|
+
// durationSeconds is deliberately untouched here: phase:start and
|
|
169
|
+
// phase:end remain the single source of a phase's wall-clock time.
|
|
170
|
+
phases: snapshot.phases.map(
|
|
171
|
+
(phase) => phase.name === event.phase ? accumulateUsage(phase, event) : phase
|
|
172
|
+
),
|
|
173
|
+
metrics: {
|
|
174
|
+
totalInputTokens: accumulate(snapshot.metrics.totalInputTokens, event.inputTokens),
|
|
175
|
+
totalOutputTokens: accumulate(snapshot.metrics.totalOutputTokens, event.outputTokens),
|
|
176
|
+
totalCacheReadTokens: accumulate(
|
|
177
|
+
snapshot.metrics.totalCacheReadTokens,
|
|
178
|
+
event.cacheReadTokens
|
|
179
|
+
),
|
|
180
|
+
totalCacheCreationTokens: accumulate(
|
|
181
|
+
snapshot.metrics.totalCacheCreationTokens,
|
|
182
|
+
event.cacheCreationTokens
|
|
183
|
+
),
|
|
184
|
+
totalCostUsd: accumulate(snapshot.metrics.totalCostUsd, event.costUsd)
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// src/core/session/reducer-phase.ts
|
|
192
|
+
import { stripVTControlCharacters as stripVTControlCharacters2 } from "util";
|
|
193
|
+
|
|
194
|
+
// src/core/session/reducer-stage.ts
|
|
195
|
+
function isTerminalStage(stage) {
|
|
196
|
+
return stage === "done" || stage === "failed";
|
|
197
|
+
}
|
|
198
|
+
function transitionStory(story, stage, at, detail) {
|
|
199
|
+
if (story.stage === stage && story.stageDetail === detail) return story;
|
|
200
|
+
return {
|
|
201
|
+
...story,
|
|
202
|
+
stage,
|
|
203
|
+
stageSince: at,
|
|
204
|
+
stageDetail: detail,
|
|
205
|
+
history: [...story.history, { at, stage, detail }]
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function deriveStageOnStoriesUpdate(story, before, at) {
|
|
209
|
+
if (!story.passes) {
|
|
210
|
+
return {
|
|
211
|
+
stage: before?.stage ?? "pending",
|
|
212
|
+
stageSince: before?.stageSince ?? at,
|
|
213
|
+
stageDetail: before?.stageDetail ?? null
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
if (before?.passes) {
|
|
217
|
+
return { stage: before.stage, stageSince: before.stageSince, stageDetail: before.stageDetail };
|
|
218
|
+
}
|
|
219
|
+
return { stage: "awaiting_review", stageSince: at, stageDetail: null };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// src/core/session/snapshot.ts
|
|
13
223
|
function emptyUsage() {
|
|
14
224
|
return {
|
|
15
225
|
inputTokens: null,
|
|
@@ -93,106 +303,192 @@ function createInitialSnapshot() {
|
|
|
93
303
|
verification: null
|
|
94
304
|
};
|
|
95
305
|
}
|
|
96
|
-
|
|
97
|
-
|
|
306
|
+
|
|
307
|
+
// src/core/session/reducer-phase.ts
|
|
308
|
+
function applyPhaseEvent(snapshot, event) {
|
|
309
|
+
switch (event.type) {
|
|
310
|
+
case "phase:start":
|
|
311
|
+
return applyPhaseStart(snapshot, event);
|
|
312
|
+
case "phase:end":
|
|
313
|
+
return applyPhaseEnd(snapshot, event);
|
|
314
|
+
case "iteration:start":
|
|
315
|
+
return applyIterationStart(snapshot, event);
|
|
316
|
+
case "iteration:end":
|
|
317
|
+
return { ...snapshot, currentActivity: null };
|
|
318
|
+
case "correction:cycle":
|
|
319
|
+
return applyCorrectionCycle(snapshot, event);
|
|
320
|
+
default: {
|
|
321
|
+
const _exhaustive = event;
|
|
322
|
+
return _exhaustive;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
98
325
|
}
|
|
99
|
-
function
|
|
326
|
+
function applyPhaseStart(snapshot, event) {
|
|
327
|
+
const known = snapshot.phases.some((p) => p.name === event.phase);
|
|
328
|
+
const phases = known ? snapshot.phases.map(
|
|
329
|
+
(p) => p.name === event.phase ? {
|
|
330
|
+
...p,
|
|
331
|
+
status: "running",
|
|
332
|
+
startedAt: event.at,
|
|
333
|
+
endedAt: null,
|
|
334
|
+
error: null
|
|
335
|
+
} : p
|
|
336
|
+
) : [
|
|
337
|
+
...snapshot.phases,
|
|
338
|
+
{
|
|
339
|
+
name: event.phase,
|
|
340
|
+
status: "running",
|
|
341
|
+
startedAt: event.at,
|
|
342
|
+
endedAt: null,
|
|
343
|
+
durationSeconds: null,
|
|
344
|
+
error: null,
|
|
345
|
+
...emptyPhaseTiming(),
|
|
346
|
+
...emptyUsage()
|
|
347
|
+
}
|
|
348
|
+
];
|
|
349
|
+
const stories = event.phase === "review" ? snapshot.stories.map(
|
|
350
|
+
(story) => story.passes ? transitionStory(story, "in_review", event.at, null) : story
|
|
351
|
+
) : snapshot.stories;
|
|
100
352
|
return {
|
|
101
|
-
...
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
costUsd: accumulate(target.costUsd, event.costUsd)
|
|
353
|
+
...snapshot,
|
|
354
|
+
currentPhase: event.phase,
|
|
355
|
+
phases,
|
|
356
|
+
stories,
|
|
357
|
+
progress: known ? snapshot.progress : { ...snapshot.progress, phasesTotal: phases.length }
|
|
107
358
|
};
|
|
108
359
|
}
|
|
109
|
-
function
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (story.status === "in_review") return "in_review";
|
|
144
|
-
if (activeStory !== null && activeStory === story.id) return "in_progress";
|
|
145
|
-
return "backlog";
|
|
360
|
+
function applyPhaseEnd(snapshot, event) {
|
|
361
|
+
const phases = snapshot.phases.map(
|
|
362
|
+
(p) => p.name === event.phase ? {
|
|
363
|
+
...p,
|
|
364
|
+
status: event.success ? "completed" : "failed",
|
|
365
|
+
endedAt: event.at,
|
|
366
|
+
durationSeconds: secondsBetween(p.startedAt, event.at),
|
|
367
|
+
error: event.error ? stripVTControlCharacters2(event.error) : null,
|
|
368
|
+
harnessExecutionMs: event.harnessExecutionMs ?? p.harnessExecutionMs,
|
|
369
|
+
orchestrationOverheadMs: event.orchestrationOverheadMs ?? p.orchestrationOverheadMs,
|
|
370
|
+
harnessStartupMs: event.harnessStartupMs ?? p.harnessStartupMs,
|
|
371
|
+
ttftMs: event.ttftMs ?? p.ttftMs,
|
|
372
|
+
attemptCount: event.attemptCount ?? p.attemptCount,
|
|
373
|
+
retryDurationMs: event.retryDurationMs ?? p.retryDurationMs
|
|
374
|
+
} : p
|
|
375
|
+
);
|
|
376
|
+
const phasesCompleted = phases.filter((p) => p.status === "completed").length;
|
|
377
|
+
const stories = !event.success ? snapshot.stories.map(
|
|
378
|
+
(story) => isTerminalStage(story.stage) ? story : transitionStory(story, "failed", event.at, null)
|
|
379
|
+
) : event.phase === "review" ? snapshot.stories.map(
|
|
380
|
+
(story) => story.passes ? transitionStory(story, "done", event.at, null) : story
|
|
381
|
+
) : snapshot.stories;
|
|
382
|
+
return {
|
|
383
|
+
...snapshot,
|
|
384
|
+
currentPhase: snapshot.currentPhase === event.phase ? null : snapshot.currentPhase,
|
|
385
|
+
currentActivity: null,
|
|
386
|
+
phases,
|
|
387
|
+
stories,
|
|
388
|
+
progress: {
|
|
389
|
+
...snapshot.progress,
|
|
390
|
+
phasesCompleted,
|
|
391
|
+
percent: computePercent(phasesCompleted, snapshot.progress.phasesTotal)
|
|
392
|
+
}
|
|
393
|
+
};
|
|
146
394
|
}
|
|
147
|
-
function
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
395
|
+
function applyIterationStart(snapshot, event) {
|
|
396
|
+
const storyId = event.storyId;
|
|
397
|
+
const stories = snapshot.stories.map((story) => {
|
|
398
|
+
if (story.passes) return story;
|
|
399
|
+
if (story.id === storyId) {
|
|
400
|
+
return transitionStory(story, "executing", event.at, null);
|
|
401
|
+
}
|
|
402
|
+
return transitionStory(story, "pending", event.at, null);
|
|
152
403
|
});
|
|
153
|
-
}
|
|
154
|
-
function reduceSessionEvent(snapshot, event, options) {
|
|
155
|
-
const next = applyEvent(snapshot, event, options);
|
|
156
|
-
if (next === snapshot) return snapshot;
|
|
157
|
-
const elapsedSeconds = secondsBetween(next.startedAt, event.at) ?? next.elapsedSeconds;
|
|
158
404
|
return {
|
|
159
|
-
...
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
405
|
+
...snapshot,
|
|
406
|
+
execution: { ...snapshot.execution, iteration: event.iteration },
|
|
407
|
+
stories,
|
|
408
|
+
// Finally populates the `story` field of the activity payload during
|
|
409
|
+
// execute — today this phase never publishes an `activity` event at
|
|
410
|
+
// all (no streaming), so `currentActivity` stays whatever an earlier
|
|
411
|
+
// phase left it at. Left untouched when storyId is absent (every
|
|
412
|
+
// story already passes, or the caller could not determine one).
|
|
413
|
+
currentActivity: storyId !== void 0 ? { story: storyId, tool: null, detail: null, since: event.at } : snapshot.currentActivity
|
|
167
414
|
};
|
|
168
415
|
}
|
|
169
|
-
function
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
416
|
+
function applyCorrectionCycle(snapshot, event) {
|
|
417
|
+
const stageDetail = `Cycle ${event.cycle}/${event.maxCycles}`;
|
|
418
|
+
const stories = snapshot.stories.map(
|
|
419
|
+
(story) => story.passes ? transitionStory(story, "in_correction", event.at, stageDetail) : story
|
|
420
|
+
);
|
|
174
421
|
return {
|
|
175
|
-
...
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
422
|
+
...snapshot,
|
|
423
|
+
execution: {
|
|
424
|
+
...snapshot.execution,
|
|
425
|
+
correctionCycle: event.cycle,
|
|
426
|
+
maxCorrectionCycles: event.maxCycles
|
|
427
|
+
},
|
|
428
|
+
stories
|
|
180
429
|
};
|
|
181
430
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
431
|
+
|
|
432
|
+
// src/core/session/reducer-resilience.ts
|
|
433
|
+
function applyResilienceEvent(snapshot, event) {
|
|
434
|
+
switch (event.type) {
|
|
435
|
+
case "retry":
|
|
436
|
+
return {
|
|
437
|
+
...snapshot,
|
|
438
|
+
execution: { ...snapshot.execution, retries: snapshot.execution.retries + 1 }
|
|
439
|
+
};
|
|
440
|
+
case "agent:attempt":
|
|
441
|
+
return {
|
|
442
|
+
...snapshot,
|
|
443
|
+
resilience: {
|
|
444
|
+
...snapshot.resilience,
|
|
445
|
+
attempt: event.attempt,
|
|
446
|
+
provider: event.provider,
|
|
447
|
+
model: event.model ?? null,
|
|
448
|
+
lastActivityAt: event.at
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
case "failover":
|
|
452
|
+
return {
|
|
453
|
+
...snapshot,
|
|
454
|
+
resilience: {
|
|
455
|
+
...snapshot.resilience,
|
|
456
|
+
provider: event.to,
|
|
457
|
+
lastFailureKind: event.reason,
|
|
458
|
+
cooldownUntil: event.cooldownUntil ?? null,
|
|
459
|
+
lastActivityAt: event.at
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
case "agent:result":
|
|
463
|
+
return {
|
|
464
|
+
...snapshot,
|
|
465
|
+
resilience: {
|
|
466
|
+
...snapshot.resilience,
|
|
467
|
+
provider: event.provider,
|
|
468
|
+
lastFailureKind: event.failureKind ?? snapshot.resilience.lastFailureKind,
|
|
469
|
+
cooldownUntil: event.cooldownUntil ?? null,
|
|
470
|
+
lastActivityAt: event.at
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
case "agent:activity":
|
|
474
|
+
return {
|
|
475
|
+
...snapshot,
|
|
476
|
+
resilience: {
|
|
477
|
+
...snapshot.resilience,
|
|
478
|
+
provider: event.provider,
|
|
479
|
+
lastActivityAt: event.at
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
default: {
|
|
483
|
+
const _exhaustive = event;
|
|
484
|
+
return _exhaustive;
|
|
485
|
+
}
|
|
192
486
|
}
|
|
193
|
-
return { stage: "awaiting_review", stageSince: at, stageDetail: null };
|
|
194
487
|
}
|
|
195
|
-
|
|
488
|
+
|
|
489
|
+
// src/core/session/reducer-session.ts
|
|
490
|
+
import { stripVTControlCharacters as stripVTControlCharacters3 } from "util";
|
|
491
|
+
function applySessionEvent(snapshot, event) {
|
|
196
492
|
switch (event.type) {
|
|
197
493
|
case "session:start": {
|
|
198
494
|
setTelemetrySessionId(event.sessionId);
|
|
@@ -255,145 +551,43 @@ function applyEvent(snapshot, event, options) {
|
|
|
255
551
|
state: event.state
|
|
256
552
|
}
|
|
257
553
|
};
|
|
258
|
-
case "
|
|
259
|
-
const known = snapshot.phases.some((p) => p.name === event.phase);
|
|
260
|
-
const phases = known ? snapshot.phases.map(
|
|
261
|
-
(p) => p.name === event.phase ? {
|
|
262
|
-
...p,
|
|
263
|
-
status: "running",
|
|
264
|
-
startedAt: event.at,
|
|
265
|
-
endedAt: null,
|
|
266
|
-
error: null
|
|
267
|
-
} : p
|
|
268
|
-
) : [
|
|
269
|
-
...snapshot.phases,
|
|
270
|
-
{
|
|
271
|
-
name: event.phase,
|
|
272
|
-
status: "running",
|
|
273
|
-
startedAt: event.at,
|
|
274
|
-
endedAt: null,
|
|
275
|
-
durationSeconds: null,
|
|
276
|
-
error: null,
|
|
277
|
-
...emptyPhaseTiming(),
|
|
278
|
-
...emptyUsage()
|
|
279
|
-
}
|
|
280
|
-
];
|
|
281
|
-
const stories = event.phase === "review" ? snapshot.stories.map(
|
|
282
|
-
(story) => story.passes ? transitionStory(story, "in_review", event.at, null) : story
|
|
283
|
-
) : snapshot.stories;
|
|
284
|
-
return {
|
|
285
|
-
...snapshot,
|
|
286
|
-
currentPhase: event.phase,
|
|
287
|
-
phases,
|
|
288
|
-
stories,
|
|
289
|
-
progress: known ? snapshot.progress : { ...snapshot.progress, phasesTotal: phases.length }
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
case "phase:end": {
|
|
293
|
-
const phases = snapshot.phases.map(
|
|
294
|
-
(p) => p.name === event.phase ? {
|
|
295
|
-
...p,
|
|
296
|
-
status: event.success ? "completed" : "failed",
|
|
297
|
-
endedAt: event.at,
|
|
298
|
-
durationSeconds: secondsBetween(p.startedAt, event.at),
|
|
299
|
-
error: event.error ? stripVTControlCharacters(event.error) : null,
|
|
300
|
-
harnessExecutionMs: event.harnessExecutionMs ?? p.harnessExecutionMs,
|
|
301
|
-
orchestrationOverheadMs: event.orchestrationOverheadMs ?? p.orchestrationOverheadMs,
|
|
302
|
-
harnessStartupMs: event.harnessStartupMs ?? p.harnessStartupMs,
|
|
303
|
-
ttftMs: event.ttftMs ?? p.ttftMs,
|
|
304
|
-
attemptCount: event.attemptCount ?? p.attemptCount,
|
|
305
|
-
retryDurationMs: event.retryDurationMs ?? p.retryDurationMs
|
|
306
|
-
} : p
|
|
307
|
-
);
|
|
308
|
-
const phasesCompleted = phases.filter((p) => p.status === "completed").length;
|
|
309
|
-
const stories = !event.success ? snapshot.stories.map(
|
|
310
|
-
(story) => isTerminalStage(story.stage) ? story : transitionStory(story, "failed", event.at, null)
|
|
311
|
-
) : event.phase === "review" ? snapshot.stories.map(
|
|
312
|
-
(story) => story.passes ? transitionStory(story, "done", event.at, null) : story
|
|
313
|
-
) : snapshot.stories;
|
|
554
|
+
case "verify:end":
|
|
314
555
|
return {
|
|
315
556
|
...snapshot,
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
progress: {
|
|
321
|
-
...snapshot.progress,
|
|
322
|
-
phasesCompleted,
|
|
323
|
-
percent: computePercent(phasesCompleted, snapshot.progress.phasesTotal)
|
|
557
|
+
verification: {
|
|
558
|
+
verdict: event.verdict,
|
|
559
|
+
level: event.level,
|
|
560
|
+
independence: event.independence
|
|
324
561
|
}
|
|
325
562
|
};
|
|
326
|
-
|
|
327
|
-
case "iteration:start": {
|
|
328
|
-
const storyId = event.storyId;
|
|
329
|
-
const stories = snapshot.stories.map((story) => {
|
|
330
|
-
if (story.passes) return story;
|
|
331
|
-
if (story.id === storyId) {
|
|
332
|
-
return transitionStory(story, "executing", event.at, null);
|
|
333
|
-
}
|
|
334
|
-
return transitionStory(story, "pending", event.at, null);
|
|
335
|
-
});
|
|
563
|
+
case "session:end":
|
|
336
564
|
return {
|
|
337
565
|
...snapshot,
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
//
|
|
343
|
-
//
|
|
344
|
-
//
|
|
345
|
-
|
|
566
|
+
status: event.status,
|
|
567
|
+
endedAt: event.at,
|
|
568
|
+
currentPhase: null,
|
|
569
|
+
currentActivity: null,
|
|
570
|
+
// Close every non-terminal stage: the run is over, so nothing can be
|
|
571
|
+
// 'executing'/'in_review'/'in_correction' any more. A story that never
|
|
572
|
+
// reached `passes` on a run that did not complete is exactly what the
|
|
573
|
+
// 'failed' stage means; anything else settles as 'done'.
|
|
574
|
+
stories: snapshot.stories.map((story) => {
|
|
575
|
+
if (isTerminalStage(story.stage)) return story;
|
|
576
|
+
const stage = event.status === "completed" && story.passes ? "done" : "failed";
|
|
577
|
+
return transitionStory(story, stage, event.at, null);
|
|
578
|
+
}),
|
|
579
|
+
lastError: event.error ? { message: stripVTControlCharacters3(event.error), at: event.at } : snapshot.lastError
|
|
346
580
|
};
|
|
581
|
+
default: {
|
|
582
|
+
const _exhaustive = event;
|
|
583
|
+
return _exhaustive;
|
|
347
584
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
};
|
|
355
|
-
case "agent:attempt":
|
|
356
|
-
return {
|
|
357
|
-
...snapshot,
|
|
358
|
-
resilience: {
|
|
359
|
-
...snapshot.resilience,
|
|
360
|
-
attempt: event.attempt,
|
|
361
|
-
provider: event.provider,
|
|
362
|
-
model: event.model ?? null,
|
|
363
|
-
lastActivityAt: event.at
|
|
364
|
-
}
|
|
365
|
-
};
|
|
366
|
-
case "failover":
|
|
367
|
-
return {
|
|
368
|
-
...snapshot,
|
|
369
|
-
resilience: {
|
|
370
|
-
...snapshot.resilience,
|
|
371
|
-
provider: event.to,
|
|
372
|
-
lastFailureKind: event.reason,
|
|
373
|
-
cooldownUntil: event.cooldownUntil ?? null,
|
|
374
|
-
lastActivityAt: event.at
|
|
375
|
-
}
|
|
376
|
-
};
|
|
377
|
-
case "agent:result":
|
|
378
|
-
return {
|
|
379
|
-
...snapshot,
|
|
380
|
-
resilience: {
|
|
381
|
-
...snapshot.resilience,
|
|
382
|
-
provider: event.provider,
|
|
383
|
-
lastFailureKind: event.failureKind ?? snapshot.resilience.lastFailureKind,
|
|
384
|
-
cooldownUntil: event.cooldownUntil ?? null,
|
|
385
|
-
lastActivityAt: event.at
|
|
386
|
-
}
|
|
387
|
-
};
|
|
388
|
-
case "agent:activity":
|
|
389
|
-
return {
|
|
390
|
-
...snapshot,
|
|
391
|
-
resilience: {
|
|
392
|
-
...snapshot.resilience,
|
|
393
|
-
provider: event.provider,
|
|
394
|
-
lastActivityAt: event.at
|
|
395
|
-
}
|
|
396
|
-
};
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// src/core/session/reducer-story.ts
|
|
589
|
+
function applyStoryEvent(snapshot, event) {
|
|
590
|
+
switch (event.type) {
|
|
397
591
|
case "stories:update": {
|
|
398
592
|
const previous = new Map(snapshot.stories.map((story) => [story.id, story]));
|
|
399
593
|
const stories = event.stories.map((story) => {
|
|
@@ -450,143 +644,68 @@ function applyEvent(snapshot, event, options) {
|
|
|
450
644
|
resilience: { ...snapshot.resilience, lastActivityAt: event.at }
|
|
451
645
|
};
|
|
452
646
|
}
|
|
453
|
-
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
at: event.at,
|
|
457
|
-
level: event.level,
|
|
458
|
-
message: stripVTControlCharacters(event.message)
|
|
459
|
-
};
|
|
460
|
-
const logs = [...snapshot.logs, entry].slice(-Math.max(1, limit));
|
|
461
|
-
return { ...snapshot, logs };
|
|
462
|
-
}
|
|
463
|
-
case "process:output": {
|
|
464
|
-
const limit = options?.logLimit ?? DEFAULT_LOG_LIMIT;
|
|
465
|
-
const entry = {
|
|
466
|
-
at: event.at,
|
|
467
|
-
phase: event.phase,
|
|
468
|
-
executionId: event.executionId,
|
|
469
|
-
provider: event.provider,
|
|
470
|
-
stream: event.stream,
|
|
471
|
-
message: stripVTControlCharacters(event.message)
|
|
472
|
-
};
|
|
473
|
-
return {
|
|
474
|
-
...snapshot,
|
|
475
|
-
processLogs: [...snapshot.processLogs, entry].slice(-Math.max(1, limit))
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
|
-
case "execution:update": {
|
|
479
|
-
const index = snapshot.executions.findIndex((entry) => entry.id === event.execution.id);
|
|
480
|
-
const executions = [...snapshot.executions];
|
|
481
|
-
if (index === -1) executions.push(event.execution);
|
|
482
|
-
else executions[index] = event.execution;
|
|
483
|
-
return { ...snapshot, executions };
|
|
484
|
-
}
|
|
485
|
-
case "git:update":
|
|
486
|
-
return {
|
|
487
|
-
...snapshot,
|
|
488
|
-
git: {
|
|
489
|
-
branch: event.branch ?? snapshot.git.branch,
|
|
490
|
-
baseBranch: event.baseBranch ?? snapshot.git.baseBranch,
|
|
491
|
-
branchCreated: reported(event.branchCreated, snapshot.git.branchCreated),
|
|
492
|
-
startCommit: snapshot.git.startCommit,
|
|
493
|
-
commits: event.commits ?? snapshot.git.commits
|
|
494
|
-
},
|
|
495
|
-
repository: {
|
|
496
|
-
// `undefined` is "not collected", so the previous value stands; an
|
|
497
|
-
// explicit `null` is "collected and unavailable" and overwrites it.
|
|
498
|
-
name: reported(event.repositoryName, snapshot.repository.name),
|
|
499
|
-
remoteUrl: reported(event.remoteUrl, snapshot.repository.remoteUrl),
|
|
500
|
-
branch: event.branch ?? snapshot.repository.branch,
|
|
501
|
-
headCommit: reported(event.headCommit, snapshot.repository.headCommit),
|
|
502
|
-
root: reported(event.repositoryRoot, snapshot.repository.root)
|
|
503
|
-
},
|
|
504
|
-
pullRequests: event.pullRequests ?? snapshot.pullRequests
|
|
505
|
-
};
|
|
506
|
-
case "correction:cycle": {
|
|
507
|
-
const stageDetail = `Cycle ${event.cycle}/${event.maxCycles}`;
|
|
508
|
-
const stories = snapshot.stories.map(
|
|
509
|
-
(story) => story.passes ? transitionStory(story, "in_correction", event.at, stageDetail) : story
|
|
510
|
-
);
|
|
511
|
-
return {
|
|
512
|
-
...snapshot,
|
|
513
|
-
execution: {
|
|
514
|
-
...snapshot.execution,
|
|
515
|
-
correctionCycle: event.cycle,
|
|
516
|
-
maxCorrectionCycles: event.maxCycles
|
|
517
|
-
},
|
|
518
|
-
stories
|
|
519
|
-
};
|
|
520
|
-
}
|
|
521
|
-
case "metrics:update": {
|
|
522
|
-
if (event.scope === "story") {
|
|
523
|
-
if (event.storyId === void 0) return snapshot;
|
|
524
|
-
if (!snapshot.stories.some((s) => s.id === event.storyId)) return snapshot;
|
|
525
|
-
return {
|
|
526
|
-
...snapshot,
|
|
527
|
-
stories: snapshot.stories.map(
|
|
528
|
-
(story) => story.id === event.storyId ? {
|
|
529
|
-
...accumulateUsage(story, event),
|
|
530
|
-
durationSeconds: accumulate(story.durationSeconds, event.durationSeconds)
|
|
531
|
-
} : story
|
|
532
|
-
)
|
|
533
|
-
};
|
|
534
|
-
}
|
|
535
|
-
if (event.phase === void 0) return snapshot;
|
|
536
|
-
if (!snapshot.phases.some((p) => p.name === event.phase)) return snapshot;
|
|
537
|
-
return {
|
|
538
|
-
...snapshot,
|
|
539
|
-
// durationSeconds is deliberately untouched here: phase:start and
|
|
540
|
-
// phase:end remain the single source of a phase's wall-clock time.
|
|
541
|
-
phases: snapshot.phases.map(
|
|
542
|
-
(phase) => phase.name === event.phase ? accumulateUsage(phase, event) : phase
|
|
543
|
-
),
|
|
544
|
-
metrics: {
|
|
545
|
-
totalInputTokens: accumulate(snapshot.metrics.totalInputTokens, event.inputTokens),
|
|
546
|
-
totalOutputTokens: accumulate(snapshot.metrics.totalOutputTokens, event.outputTokens),
|
|
547
|
-
totalCacheReadTokens: accumulate(
|
|
548
|
-
snapshot.metrics.totalCacheReadTokens,
|
|
549
|
-
event.cacheReadTokens
|
|
550
|
-
),
|
|
551
|
-
totalCacheCreationTokens: accumulate(
|
|
552
|
-
snapshot.metrics.totalCacheCreationTokens,
|
|
553
|
-
event.cacheCreationTokens
|
|
554
|
-
),
|
|
555
|
-
totalCostUsd: accumulate(snapshot.metrics.totalCostUsd, event.costUsd)
|
|
556
|
-
}
|
|
557
|
-
};
|
|
647
|
+
default: {
|
|
648
|
+
const _exhaustive = event;
|
|
649
|
+
return _exhaustive;
|
|
558
650
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// src/core/session/reducer.ts
|
|
655
|
+
function reduceSessionEvent(snapshot, event, options) {
|
|
656
|
+
const next = applyEvent(snapshot, event, options);
|
|
657
|
+
if (next === snapshot) return snapshot;
|
|
658
|
+
const elapsedSeconds = secondsBetween(next.startedAt, event.at) ?? next.elapsedSeconds;
|
|
659
|
+
return {
|
|
660
|
+
...next,
|
|
661
|
+
updatedAt: event.at,
|
|
662
|
+
elapsedSeconds,
|
|
663
|
+
stories: deriveStoryStatuses(next),
|
|
664
|
+
estimatedRemainingSeconds: estimateRemainingSeconds(next),
|
|
665
|
+
errors: next.logs.filter((entry) => entry.level === "error"),
|
|
666
|
+
warnings: next.logs.filter((entry) => entry.level === "warn"),
|
|
667
|
+
nextSteps: deriveNextSteps(next)
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
function applyEvent(snapshot, event, options) {
|
|
671
|
+
switch (event.type) {
|
|
672
|
+
case "session:start":
|
|
568
673
|
case "session:end":
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
674
|
+
case "issue:update":
|
|
675
|
+
case "verify:end":
|
|
676
|
+
return applySessionEvent(snapshot, event);
|
|
677
|
+
case "phase:start":
|
|
678
|
+
case "phase:end":
|
|
679
|
+
case "iteration:start":
|
|
680
|
+
case "iteration:end":
|
|
681
|
+
case "correction:cycle":
|
|
682
|
+
return applyPhaseEvent(snapshot, event);
|
|
683
|
+
case "stories:update":
|
|
684
|
+
case "activity":
|
|
685
|
+
return applyStoryEvent(snapshot, event);
|
|
686
|
+
case "metrics:update":
|
|
687
|
+
return applyMetricsEvent(snapshot, event);
|
|
688
|
+
case "git:update":
|
|
689
|
+
return applyGitEvent(snapshot, event);
|
|
690
|
+
case "log":
|
|
691
|
+
case "process:output":
|
|
692
|
+
case "execution:update":
|
|
693
|
+
return applyLogEvent(snapshot, event, options);
|
|
694
|
+
case "retry":
|
|
695
|
+
case "agent:attempt":
|
|
696
|
+
case "failover":
|
|
697
|
+
case "agent:result":
|
|
698
|
+
case "agent:activity":
|
|
699
|
+
return applyResilienceEvent(snapshot, event);
|
|
700
|
+
default: {
|
|
701
|
+
const _exhaustive = event;
|
|
702
|
+
void _exhaustive;
|
|
587
703
|
return snapshot;
|
|
704
|
+
}
|
|
588
705
|
}
|
|
589
706
|
}
|
|
707
|
+
|
|
708
|
+
// src/core/session/publishers.ts
|
|
590
709
|
var NullPublisher = class {
|
|
591
710
|
empty = createInitialSnapshot();
|
|
592
711
|
publish(_event) {
|
|
@@ -703,7 +822,7 @@ var FilePublisher = class extends MemoryPublisher {
|
|
|
703
822
|
const payload = `${JSON.stringify(this.state, null, 2)}
|
|
704
823
|
`;
|
|
705
824
|
try {
|
|
706
|
-
await
|
|
825
|
+
await writeFileAtomic(this.filePath, payload);
|
|
707
826
|
this.lastWrittenVersion = version;
|
|
708
827
|
} catch (err) {
|
|
709
828
|
this.warnOnce(err);
|
|
@@ -747,12 +866,6 @@ var FilePublisher = class extends MemoryPublisher {
|
|
|
747
866
|
await this.flush();
|
|
748
867
|
}
|
|
749
868
|
};
|
|
750
|
-
async function atomicWriteFile(path, content) {
|
|
751
|
-
await mkdir(dirname(path), { recursive: true });
|
|
752
|
-
const tmpFile = `${path}.tmp`;
|
|
753
|
-
await writeFile(tmpFile, content, "utf-8");
|
|
754
|
-
await rename(tmpFile, path);
|
|
755
|
-
}
|
|
756
869
|
|
|
757
870
|
// src/core/session-publisher.ts
|
|
758
871
|
var NULL_PUBLISHER = new NullPublisher();
|
|
@@ -773,4 +886,4 @@ export {
|
|
|
773
886
|
setSessionPublisher,
|
|
774
887
|
getSessionPublisher
|
|
775
888
|
};
|
|
776
|
-
//# sourceMappingURL=chunk-
|
|
889
|
+
//# sourceMappingURL=chunk-DUS5ZNKL.js.map
|