@nathapp/nax 0.68.8 → 0.69.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/nax.js +253 -126
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -20944,12 +20944,21 @@ function parseAcpxJsonLine(line, state) {
|
|
|
20944
20944
|
}
|
|
20945
20945
|
if (update.sessionUpdate === "usage_update") {
|
|
20946
20946
|
const activity = { kind: "usage_update" };
|
|
20947
|
-
|
|
20947
|
+
const metaUsage = update._meta != null && typeof update._meta === "object" ? update._meta.usage : undefined;
|
|
20948
|
+
if (metaUsage != null && typeof metaUsage === "object") {
|
|
20949
|
+
const inp = metaUsage.inputTokens ?? metaUsage.input_tokens;
|
|
20950
|
+
if (typeof inp === "number")
|
|
20951
|
+
activity.inputTokens = inp;
|
|
20952
|
+
const out = metaUsage.outputTokens ?? metaUsage.output_tokens;
|
|
20953
|
+
if (typeof out === "number")
|
|
20954
|
+
activity.outputTokens = out;
|
|
20955
|
+
}
|
|
20956
|
+
if (activity.outputTokens == null && typeof update.used === "number") {
|
|
20948
20957
|
activity.outputTokens = update.used;
|
|
20949
20958
|
}
|
|
20950
20959
|
if (typeof update.cost?.amount === "number") {
|
|
20951
20960
|
activity.costUsd = update.cost.amount;
|
|
20952
|
-
state.exactCostUsd =
|
|
20961
|
+
state.exactCostUsd = activity.costUsd;
|
|
20953
20962
|
}
|
|
20954
20963
|
return activity;
|
|
20955
20964
|
}
|
|
@@ -34337,7 +34346,25 @@ async function readSpecDriftViolations(input) {
|
|
|
34337
34346
|
return [];
|
|
34338
34347
|
}
|
|
34339
34348
|
}
|
|
34340
|
-
|
|
34349
|
+
function collectUpstreamProducedFiles(story, byId) {
|
|
34350
|
+
const produced = new Set;
|
|
34351
|
+
const seen = new Set;
|
|
34352
|
+
const stack = [...story.dependencies ?? []];
|
|
34353
|
+
while (stack.length > 0) {
|
|
34354
|
+
const depId = stack.pop();
|
|
34355
|
+
if (!depId || seen.has(depId))
|
|
34356
|
+
continue;
|
|
34357
|
+
seen.add(depId);
|
|
34358
|
+
const dep = byId.get(depId);
|
|
34359
|
+
if (!dep)
|
|
34360
|
+
continue;
|
|
34361
|
+
for (const filePath of getExpectedFiles(dep))
|
|
34362
|
+
produced.add(filePath);
|
|
34363
|
+
stack.push(...dep.dependencies ?? []);
|
|
34364
|
+
}
|
|
34365
|
+
return produced;
|
|
34366
|
+
}
|
|
34367
|
+
async function normalizeStoryFiles(story, workdir, fileExists, upstreamProduced) {
|
|
34341
34368
|
const contextFiles = story.contextFiles ?? [];
|
|
34342
34369
|
if (contextFiles.length === 0)
|
|
34343
34370
|
return { story, changed: false };
|
|
@@ -34352,6 +34379,14 @@ async function normalizeStoryFiles(story, workdir, fileExists) {
|
|
|
34352
34379
|
kept.push(entry);
|
|
34353
34380
|
continue;
|
|
34354
34381
|
}
|
|
34382
|
+
if (upstreamProduced.has(filePath)) {
|
|
34383
|
+
kept.push(entry);
|
|
34384
|
+
logger?.debug("plan", "Kept cross-story produced file in contextFiles (upstream dependency creates it)", {
|
|
34385
|
+
storyId: story.id,
|
|
34386
|
+
filePath
|
|
34387
|
+
});
|
|
34388
|
+
continue;
|
|
34389
|
+
}
|
|
34355
34390
|
if (factId) {
|
|
34356
34391
|
logger?.warn("plan", "Context file cites a manifest fact but is absent on disk", {
|
|
34357
34392
|
storyId: story.id,
|
|
@@ -34379,7 +34414,8 @@ async function normalizeStoryFiles(story, workdir, fileExists) {
|
|
|
34379
34414
|
async function normalizeCreatedContextFiles(prd, workdir, fileExists) {
|
|
34380
34415
|
if (!workdir)
|
|
34381
34416
|
return prd;
|
|
34382
|
-
const
|
|
34417
|
+
const byId = new Map(prd.userStories.map((story) => [story.id, story]));
|
|
34418
|
+
const results = await Promise.all(prd.userStories.map((story) => normalizeStoryFiles(story, workdir, fileExists, collectUpstreamProducedFiles(story, byId))));
|
|
34383
34419
|
if (!results.some((r) => r.changed))
|
|
34384
34420
|
return prd;
|
|
34385
34421
|
return { ...prd, userStories: results.map((r) => r.story) };
|
|
@@ -42566,9 +42602,9 @@ ${rows.join(`
|
|
|
42566
42602
|
`)}
|
|
42567
42603
|
`;
|
|
42568
42604
|
}
|
|
42569
|
-
var CONTEXT_VS_EXPECTED_FILES_RULE = `**\`contextFiles\` rule \u2014
|
|
42605
|
+
var CONTEXT_VS_EXPECTED_FILES_RULE = `**\`contextFiles\` rule \u2014 files readable when this story runs.** List paths that already exist in the repo today, PLUS any file an UPSTREAM dependency story creates (it does not exist now but will exist by the time this story runs, because dependencies execute first). The pipeline verifies every \`contextFiles\` entry against the filesystem; a path that exists neither on disk nor in an upstream dependency's outputs is treated as a missing-context warning.
|
|
42570
42606
|
|
|
42571
|
-
**\`expectedFiles\` rule \u2014 files
|
|
42607
|
+
**\`expectedFiles\` rule \u2014 files THIS story CREATES.** List every NEW file this story authors (relative paths). A file this story creates belongs here, NEVER in \`contextFiles\` \u2014 these are the story's outputs, not files to read first. A file created by an upstream dependency and only read/modified here belongs in \`contextFiles\`, NOT here (this story does not author it). A single path may appear in \`contextFiles\` (an existing sibling to mirror) AND \`expectedFiles\` (the new file itself), but the same path must never be in both.`, EXPECTED_FILES_SCHEMA_FIELD = `"expectedFiles": ["string \u2014 NEW files this story creates (relative paths, omit if none)"],`;
|
|
42572
42608
|
var init_plan_builder = __esm(() => {
|
|
42573
42609
|
init_config();
|
|
42574
42610
|
});
|
|
@@ -58668,7 +58704,7 @@ var package_default;
|
|
|
58668
58704
|
var init_package = __esm(() => {
|
|
58669
58705
|
package_default = {
|
|
58670
58706
|
name: "@nathapp/nax",
|
|
58671
|
-
version: "0.
|
|
58707
|
+
version: "0.69.0",
|
|
58672
58708
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
58673
58709
|
type: "module",
|
|
58674
58710
|
bin: {
|
|
@@ -58763,8 +58799,8 @@ var init_version = __esm(() => {
|
|
|
58763
58799
|
NAX_VERSION = package_default.version;
|
|
58764
58800
|
NAX_COMMIT = (() => {
|
|
58765
58801
|
try {
|
|
58766
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
58767
|
-
return "
|
|
58802
|
+
if (/^[0-9a-f]{6,10}$/.test("ce4d8f0e"))
|
|
58803
|
+
return "ce4d8f0e";
|
|
58768
58804
|
} catch {}
|
|
58769
58805
|
try {
|
|
58770
58806
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -103850,7 +103886,8 @@ function LiveActivityPanel({
|
|
|
103850
103886
|
storySteps,
|
|
103851
103887
|
runSummary,
|
|
103852
103888
|
runErrored,
|
|
103853
|
-
escalationLog = []
|
|
103889
|
+
escalationLog = [],
|
|
103890
|
+
currentStage
|
|
103854
103891
|
}) {
|
|
103855
103892
|
const borderColor = focused ? "cyan" : "gray";
|
|
103856
103893
|
const activeCallList = activeCalls ? Array.from(activeCalls.values()) : [];
|
|
@@ -103907,7 +103944,8 @@ function LiveActivityPanel({
|
|
|
103907
103944
|
paddingY: 1,
|
|
103908
103945
|
children: activeCallList.map((call) => /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(ActiveCallRow, {
|
|
103909
103946
|
call,
|
|
103910
|
-
step: call.storyId ? storySteps?.[call.storyId] : undefined
|
|
103947
|
+
step: call.storyId ? storySteps?.[call.storyId] : undefined,
|
|
103948
|
+
currentStage
|
|
103911
103949
|
}, call.callId, false, undefined, this))
|
|
103912
103950
|
}, undefined, false, undefined, this),
|
|
103913
103951
|
hasEscalations && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
@@ -103948,7 +103986,24 @@ function LiveActivityPanel({
|
|
|
103948
103986
|
!hasActiveCalls && !hasSummary && !hasError && (!storySteps || Object.keys(storySteps).length === 0) && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
103949
103987
|
paddingX: 1,
|
|
103950
103988
|
paddingY: 1,
|
|
103951
|
-
children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(
|
|
103989
|
+
children: currentStage ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
103990
|
+
flexDirection: "row",
|
|
103991
|
+
gap: 1,
|
|
103992
|
+
children: [
|
|
103993
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
103994
|
+
color: "yellow",
|
|
103995
|
+
children: [
|
|
103996
|
+
"[",
|
|
103997
|
+
currentStage,
|
|
103998
|
+
"]"
|
|
103999
|
+
]
|
|
104000
|
+
}, undefined, true, undefined, this),
|
|
104001
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
104002
|
+
dimColor: true,
|
|
104003
|
+
children: "preparing..."
|
|
104004
|
+
}, undefined, false, undefined, this)
|
|
104005
|
+
]
|
|
104006
|
+
}, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
103952
104007
|
dimColor: true,
|
|
103953
104008
|
children: "Waiting for agent..."
|
|
103954
104009
|
}, undefined, false, undefined, this)
|
|
@@ -103956,7 +104011,29 @@ function LiveActivityPanel({
|
|
|
103956
104011
|
]
|
|
103957
104012
|
}, undefined, true, undefined, this);
|
|
103958
104013
|
}
|
|
103959
|
-
function ActiveCallRow({ call, step }) {
|
|
104014
|
+
function ActiveCallRow({ call, step, currentStage }) {
|
|
104015
|
+
const stageLabel = step ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
104016
|
+
color: "yellow",
|
|
104017
|
+
children: [
|
|
104018
|
+
"[",
|
|
104019
|
+
step,
|
|
104020
|
+
"]"
|
|
104021
|
+
]
|
|
104022
|
+
}, undefined, true, undefined, this) : call.stage ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
104023
|
+
dimColor: true,
|
|
104024
|
+
children: [
|
|
104025
|
+
"[",
|
|
104026
|
+
call.stage,
|
|
104027
|
+
"]"
|
|
104028
|
+
]
|
|
104029
|
+
}, undefined, true, undefined, this) : currentStage ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
104030
|
+
dimColor: true,
|
|
104031
|
+
children: [
|
|
104032
|
+
"[",
|
|
104033
|
+
currentStage,
|
|
104034
|
+
"]"
|
|
104035
|
+
]
|
|
104036
|
+
}, undefined, true, undefined, this) : null;
|
|
103960
104037
|
return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
103961
104038
|
flexDirection: "column",
|
|
103962
104039
|
marginBottom: 1,
|
|
@@ -103972,21 +104049,7 @@ function ActiveCallRow({ call, step }) {
|
|
|
103972
104049
|
call.storyId && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
103973
104050
|
children: call.storyId
|
|
103974
104051
|
}, undefined, false, undefined, this),
|
|
103975
|
-
|
|
103976
|
-
color: "yellow",
|
|
103977
|
-
children: [
|
|
103978
|
-
"[",
|
|
103979
|
-
step,
|
|
103980
|
-
"]"
|
|
103981
|
-
]
|
|
103982
|
-
}, undefined, true, undefined, this) : call.stage ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
103983
|
-
dimColor: true,
|
|
103984
|
-
children: [
|
|
103985
|
-
"[",
|
|
103986
|
-
call.stage,
|
|
103987
|
-
"]"
|
|
103988
|
-
]
|
|
103989
|
-
}, undefined, true, undefined, this) : null
|
|
104052
|
+
stageLabel
|
|
103990
104053
|
]
|
|
103991
104054
|
}, undefined, true, undefined, this),
|
|
103992
104055
|
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
@@ -104193,7 +104256,14 @@ function getStatusIcon(status) {
|
|
|
104193
104256
|
return "\u23F8\uFE0F";
|
|
104194
104257
|
}
|
|
104195
104258
|
}
|
|
104196
|
-
function StoriesPanel({
|
|
104259
|
+
function StoriesPanel({
|
|
104260
|
+
stories,
|
|
104261
|
+
preRunPhases,
|
|
104262
|
+
postRunPhases,
|
|
104263
|
+
width,
|
|
104264
|
+
compact: compact2 = false,
|
|
104265
|
+
maxHeight
|
|
104266
|
+
}) {
|
|
104197
104267
|
const maxVisible = compact2 ? COMPACT_MAX_VISIBLE_STORIES : MAX_VISIBLE_STORIES;
|
|
104198
104268
|
const needsScrolling = stories.length > maxVisible;
|
|
104199
104269
|
const [scrollOffset, setScrollOffset] = import_react30.useState(0);
|
|
@@ -104225,7 +104295,7 @@ function StoriesPanel({ stories, postRunPhases, width, compact: compact2 = false
|
|
|
104225
104295
|
children: [
|
|
104226
104296
|
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
104227
104297
|
bold: true,
|
|
104228
|
-
children: "
|
|
104298
|
+
children: "Progress"
|
|
104229
104299
|
}, undefined, false, undefined, this),
|
|
104230
104300
|
needsScrolling && /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
104231
104301
|
dimColor: true,
|
|
@@ -104237,6 +104307,16 @@ function StoriesPanel({ stories, postRunPhases, width, compact: compact2 = false
|
|
|
104237
104307
|
}, undefined, true, undefined, this)
|
|
104238
104308
|
]
|
|
104239
104309
|
}, undefined, true, undefined, this),
|
|
104310
|
+
preRunPhases && Object.keys(preRunPhases).length > 0 && /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
104311
|
+
flexDirection: "column",
|
|
104312
|
+
paddingX: 1,
|
|
104313
|
+
paddingTop: 1,
|
|
104314
|
+
children: Object.entries(preRunPhases).map(([name, phase]) => /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(PreRunPhaseRow, {
|
|
104315
|
+
label: name,
|
|
104316
|
+
phase,
|
|
104317
|
+
compact: compact2
|
|
104318
|
+
}, name, false, undefined, this))
|
|
104319
|
+
}, undefined, false, undefined, this),
|
|
104240
104320
|
needsScrolling && canScrollUp && /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
104241
104321
|
paddingX: 1,
|
|
104242
104322
|
children: /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
@@ -104315,11 +104395,6 @@ function StoriesPanel({ stories, postRunPhases, width, compact: compact2 = false
|
|
|
104315
104395
|
paddingX: 1,
|
|
104316
104396
|
paddingTop: 1,
|
|
104317
104397
|
children: [
|
|
104318
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
104319
|
-
borderStyle: "single",
|
|
104320
|
-
borderTop: true,
|
|
104321
|
-
borderColor: "gray"
|
|
104322
|
-
}, undefined, false, undefined, this),
|
|
104323
104398
|
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
104324
104399
|
dimColor: true,
|
|
104325
104400
|
children: "Post-Run"
|
|
@@ -104344,6 +104419,21 @@ function StoriesPanel({ stories, postRunPhases, width, compact: compact2 = false
|
|
|
104344
104419
|
]
|
|
104345
104420
|
}, undefined, true, undefined, this);
|
|
104346
104421
|
}
|
|
104422
|
+
function PreRunPhaseRow({ label, phase, compact: compact2 }) {
|
|
104423
|
+
const icon = phase.status === "running" ? "\u25CF" : phase.status === "passed" ? "\u2713" : "\u2717";
|
|
104424
|
+
const color = phase.status === "running" ? "yellow" : phase.status === "passed" ? "green" : "red";
|
|
104425
|
+
const displayLabel = compact2 ? label.slice(0, 6) : label;
|
|
104426
|
+
return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
104427
|
+
children: /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
104428
|
+
color,
|
|
104429
|
+
children: [
|
|
104430
|
+
icon,
|
|
104431
|
+
" ",
|
|
104432
|
+
displayLabel
|
|
104433
|
+
]
|
|
104434
|
+
}, undefined, true, undefined, this)
|
|
104435
|
+
}, undefined, false, undefined, this);
|
|
104436
|
+
}
|
|
104347
104437
|
function PostRunPhaseRow({ label, phase, compact: compact2 }) {
|
|
104348
104438
|
const icon = phase.status === "running" ? ">" : phase.status === "passed" ? "[OK]" : "[X]";
|
|
104349
104439
|
const color = phase.status === "running" ? "cyan" : phase.status === "passed" ? "green" : "red";
|
|
@@ -104361,110 +104451,120 @@ function PostRunPhaseRow({ label, phase, compact: compact2 }) {
|
|
|
104361
104451
|
|
|
104362
104452
|
// src/tui/hooks/useAgentStreamEvents.ts
|
|
104363
104453
|
var import_react31 = __toESM(require_react(), 1);
|
|
104454
|
+
var RENDER_INTERVAL_MS = 150;
|
|
104364
104455
|
function useAgentStreamEvents(bus) {
|
|
104456
|
+
const activeCallsRef = import_react31.useRef(new Map);
|
|
104457
|
+
const inputTokensRef = import_react31.useRef(0);
|
|
104458
|
+
const outputTokensRef = import_react31.useRef(0);
|
|
104459
|
+
const lastTokensRef = import_react31.useRef(new Map);
|
|
104460
|
+
const dirtyRef = import_react31.useRef(false);
|
|
104365
104461
|
const [activeCalls, setActiveCalls] = import_react31.useState(new Map);
|
|
104366
104462
|
const [inputTokens, setInputTokens] = import_react31.useState(0);
|
|
104367
104463
|
const [outputTokens, setOutputTokens] = import_react31.useState(0);
|
|
104368
|
-
const lastTokensRef = import_react31.useRef(new Map);
|
|
104369
104464
|
import_react31.useEffect(() => {
|
|
104370
104465
|
if (!bus)
|
|
104371
104466
|
return;
|
|
104372
104467
|
const unsubscribe = bus.onAgentStream((event) => {
|
|
104373
|
-
|
|
104374
|
-
|
|
104375
|
-
|
|
104376
|
-
|
|
104377
|
-
|
|
104468
|
+
const next = new Map(activeCallsRef.current);
|
|
104469
|
+
switch (event.kind) {
|
|
104470
|
+
case "agent.call_started": {
|
|
104471
|
+
next.set(event.callId, {
|
|
104472
|
+
callId: event.callId,
|
|
104473
|
+
agentName: event.agentName,
|
|
104474
|
+
storyId: event.storyId,
|
|
104475
|
+
stage: event.stage,
|
|
104476
|
+
startedAt: event.timestamp,
|
|
104477
|
+
lastActivityAt: event.timestamp,
|
|
104478
|
+
messageUpdates: 0,
|
|
104479
|
+
thinkingUpdates: 0,
|
|
104480
|
+
usageUpdates: 0,
|
|
104481
|
+
toolCallUpdates: 0,
|
|
104482
|
+
status: "active",
|
|
104483
|
+
model: event.model
|
|
104484
|
+
});
|
|
104485
|
+
break;
|
|
104486
|
+
}
|
|
104487
|
+
case "agent.message_update": {
|
|
104488
|
+
const state = next.get(event.callId);
|
|
104489
|
+
if (state) {
|
|
104378
104490
|
next.set(event.callId, {
|
|
104379
|
-
|
|
104380
|
-
|
|
104381
|
-
|
|
104382
|
-
stage: event.stage,
|
|
104383
|
-
startedAt: now3,
|
|
104384
|
-
lastActivityAt: now3,
|
|
104385
|
-
messageUpdates: 0,
|
|
104386
|
-
thinkingUpdates: 0,
|
|
104387
|
-
usageUpdates: 0,
|
|
104388
|
-
toolCallUpdates: 0,
|
|
104389
|
-
status: "active",
|
|
104390
|
-
model: event.model
|
|
104491
|
+
...state,
|
|
104492
|
+
messageUpdates: state.messageUpdates + 1,
|
|
104493
|
+
lastActivityAt: event.timestamp
|
|
104391
104494
|
});
|
|
104392
|
-
break;
|
|
104393
|
-
}
|
|
104394
|
-
case "agent.message_update": {
|
|
104395
|
-
const state = next.get(event.callId);
|
|
104396
|
-
if (state) {
|
|
104397
|
-
next.set(event.callId, {
|
|
104398
|
-
...state,
|
|
104399
|
-
messageUpdates: state.messageUpdates + 1,
|
|
104400
|
-
lastActivityAt: event.timestamp
|
|
104401
|
-
});
|
|
104402
|
-
}
|
|
104403
|
-
break;
|
|
104404
104495
|
}
|
|
104405
|
-
|
|
104406
|
-
|
|
104407
|
-
|
|
104408
|
-
|
|
104409
|
-
|
|
104410
|
-
|
|
104411
|
-
|
|
104412
|
-
|
|
104413
|
-
|
|
104414
|
-
|
|
104496
|
+
break;
|
|
104497
|
+
}
|
|
104498
|
+
case "agent.thinking_update": {
|
|
104499
|
+
const state = next.get(event.callId);
|
|
104500
|
+
if (state) {
|
|
104501
|
+
next.set(event.callId, {
|
|
104502
|
+
...state,
|
|
104503
|
+
thinkingUpdates: state.thinkingUpdates + 1,
|
|
104504
|
+
lastActivityAt: event.timestamp
|
|
104505
|
+
});
|
|
104415
104506
|
}
|
|
104416
|
-
|
|
104417
|
-
|
|
104418
|
-
|
|
104419
|
-
|
|
104420
|
-
|
|
104421
|
-
|
|
104422
|
-
|
|
104423
|
-
|
|
104424
|
-
|
|
104425
|
-
|
|
104426
|
-
const last2 = lastTokensRef.current.get(event.callId) ?? { input: 0, output: 0 };
|
|
104427
|
-
const newInput = event.inputTokens ?? last2.input;
|
|
104428
|
-
const newOutput = event.outputTokens ?? last2.output;
|
|
104429
|
-
const deltaIn = newInput - last2.input;
|
|
104430
|
-
const deltaOut = newOutput - last2.output;
|
|
104431
|
-
lastTokensRef.current.set(event.callId, { input: newInput, output: newOutput });
|
|
104432
|
-
if (deltaIn > 0)
|
|
104433
|
-
setInputTokens((prev2) => prev2 + deltaIn);
|
|
104434
|
-
if (deltaOut > 0)
|
|
104435
|
-
setOutputTokens((prev2) => prev2 + deltaOut);
|
|
104436
|
-
}
|
|
104437
|
-
break;
|
|
104507
|
+
break;
|
|
104508
|
+
}
|
|
104509
|
+
case "agent.usage_update": {
|
|
104510
|
+
const state = next.get(event.callId);
|
|
104511
|
+
if (state) {
|
|
104512
|
+
next.set(event.callId, {
|
|
104513
|
+
...state,
|
|
104514
|
+
usageUpdates: state.usageUpdates + 1,
|
|
104515
|
+
lastActivityAt: event.timestamp
|
|
104516
|
+
});
|
|
104438
104517
|
}
|
|
104439
|
-
|
|
104440
|
-
const
|
|
104441
|
-
|
|
104442
|
-
|
|
104443
|
-
|
|
104444
|
-
|
|
104445
|
-
|
|
104446
|
-
|
|
104447
|
-
|
|
104448
|
-
|
|
104449
|
-
|
|
104518
|
+
{
|
|
104519
|
+
const last2 = lastTokensRef.current.get(event.callId) ?? { input: 0, output: 0 };
|
|
104520
|
+
const newInput = event.inputTokens ?? last2.input;
|
|
104521
|
+
const newOutput = event.outputTokens ?? last2.output;
|
|
104522
|
+
const deltaIn = newInput - last2.input;
|
|
104523
|
+
const deltaOut = newOutput - last2.output;
|
|
104524
|
+
lastTokensRef.current.set(event.callId, { input: newInput, output: newOutput });
|
|
104525
|
+
if (deltaIn > 0)
|
|
104526
|
+
inputTokensRef.current += deltaIn;
|
|
104527
|
+
if (deltaOut > 0)
|
|
104528
|
+
outputTokensRef.current += deltaOut;
|
|
104450
104529
|
}
|
|
104451
|
-
|
|
104452
|
-
|
|
104453
|
-
|
|
104454
|
-
|
|
104455
|
-
|
|
104456
|
-
|
|
104457
|
-
|
|
104458
|
-
|
|
104530
|
+
break;
|
|
104531
|
+
}
|
|
104532
|
+
case "agent.tool_call_update": {
|
|
104533
|
+
const state = next.get(event.callId);
|
|
104534
|
+
if (state) {
|
|
104535
|
+
next.set(event.callId, {
|
|
104536
|
+
...state,
|
|
104537
|
+
toolCallUpdates: state.toolCallUpdates + 1,
|
|
104538
|
+
lastActivityAt: event.timestamp,
|
|
104539
|
+
lastToolName: event.toolName
|
|
104540
|
+
});
|
|
104459
104541
|
}
|
|
104460
|
-
|
|
104461
|
-
break;
|
|
104542
|
+
break;
|
|
104462
104543
|
}
|
|
104463
|
-
|
|
104464
|
-
|
|
104544
|
+
case "agent.call_ended": {
|
|
104545
|
+
next.delete(event.callId);
|
|
104546
|
+
lastTokensRef.current.delete(event.callId);
|
|
104547
|
+
break;
|
|
104548
|
+
}
|
|
104549
|
+
default:
|
|
104550
|
+
break;
|
|
104551
|
+
}
|
|
104552
|
+
activeCallsRef.current = next;
|
|
104553
|
+
dirtyRef.current = true;
|
|
104465
104554
|
});
|
|
104466
104555
|
return unsubscribe;
|
|
104467
104556
|
}, [bus]);
|
|
104557
|
+
import_react31.useEffect(() => {
|
|
104558
|
+
const interval = setInterval(() => {
|
|
104559
|
+
if (!dirtyRef.current)
|
|
104560
|
+
return;
|
|
104561
|
+
dirtyRef.current = false;
|
|
104562
|
+
setActiveCalls(new Map(activeCallsRef.current));
|
|
104563
|
+
setInputTokens(inputTokensRef.current);
|
|
104564
|
+
setOutputTokens(outputTokensRef.current);
|
|
104565
|
+
}, RENDER_INTERVAL_MS);
|
|
104566
|
+
return () => clearInterval(interval);
|
|
104567
|
+
}, []);
|
|
104468
104568
|
return { activeCalls, inputTokens, outputTokens };
|
|
104469
104569
|
}
|
|
104470
104570
|
|
|
@@ -104660,14 +104760,33 @@ function usePipelineBusEvents(initialStories) {
|
|
|
104660
104760
|
|
|
104661
104761
|
// src/tui/hooks/usePipelineEvents.ts
|
|
104662
104762
|
var import_react33 = __toESM(require_react(), 1);
|
|
104763
|
+
var PRE_RUN_STAGES = new Set(["acceptance-setup"]);
|
|
104663
104764
|
function usePipelineEvents(events) {
|
|
104664
104765
|
const [currentStage, setCurrentStage] = import_react33.useState(undefined);
|
|
104766
|
+
const [preRunPhases, setPreRunPhases] = import_react33.useState({});
|
|
104665
104767
|
import_react33.useEffect(() => {
|
|
104666
|
-
const onStageEnter = (stage) =>
|
|
104768
|
+
const onStageEnter = (stage) => {
|
|
104769
|
+
setCurrentStage(stage);
|
|
104770
|
+
if (PRE_RUN_STAGES.has(stage)) {
|
|
104771
|
+
setPreRunPhases((prev) => ({ ...prev, [stage]: { status: "running" } }));
|
|
104772
|
+
}
|
|
104773
|
+
};
|
|
104774
|
+
const onStageExit = (stage, result2) => {
|
|
104775
|
+
if (PRE_RUN_STAGES.has(stage)) {
|
|
104776
|
+
setPreRunPhases((prev) => ({
|
|
104777
|
+
...prev,
|
|
104778
|
+
[stage]: { status: result2.action === "fail" ? "failed" : "passed" }
|
|
104779
|
+
}));
|
|
104780
|
+
}
|
|
104781
|
+
};
|
|
104667
104782
|
events.on("stage:enter", onStageEnter);
|
|
104668
|
-
|
|
104783
|
+
events.on("stage:exit", onStageExit);
|
|
104784
|
+
return () => {
|
|
104785
|
+
events.off("stage:enter", onStageEnter);
|
|
104786
|
+
events.off("stage:exit", onStageExit);
|
|
104787
|
+
};
|
|
104669
104788
|
}, [events]);
|
|
104670
|
-
return { currentStage };
|
|
104789
|
+
return { currentStage, preRunPhases };
|
|
104671
104790
|
}
|
|
104672
104791
|
|
|
104673
104792
|
// src/tui/hooks/usePty.ts
|
|
@@ -104799,7 +104918,7 @@ function App2({
|
|
|
104799
104918
|
}) {
|
|
104800
104919
|
const layout = useLayout();
|
|
104801
104920
|
const busState = usePipelineBusEvents(initialStories);
|
|
104802
|
-
const { currentStage } = usePipelineEvents(events);
|
|
104921
|
+
const { currentStage, preRunPhases } = usePipelineEvents(events);
|
|
104803
104922
|
const { exit } = use_app_default();
|
|
104804
104923
|
const startTimeRef = import_react35.useRef(Date.now());
|
|
104805
104924
|
const [elapsedMs, setElapsedMs] = import_react35.useState(0);
|
|
@@ -104820,6 +104939,8 @@ function App2({
|
|
|
104820
104939
|
const runningStories = busState.stories.filter((s) => s.status === "running");
|
|
104821
104940
|
const isParallel = runningStories.length > 1;
|
|
104822
104941
|
const currentRunningStory = runningStories[0];
|
|
104942
|
+
const runningPostRunPhase = busState.postRunPhases.acceptance?.status === "running" ? "post-run: acceptance" : busState.postRunPhases.regression?.status === "running" ? "post-run: regression" : busState.postRunPhases.review?.status === "running" ? "post-run: review" : undefined;
|
|
104943
|
+
const currentPhaseLabel = runningPostRunPhase ?? currentStage;
|
|
104823
104944
|
const runErroredForPanel = busState.runErrored ? "Run encountered an error" : undefined;
|
|
104824
104945
|
const handleKeyboardAction = async (action) => {
|
|
104825
104946
|
switch (action.type) {
|
|
@@ -104903,7 +105024,11 @@ function App2({
|
|
|
104903
105024
|
const isTooSmall = layout.width < MIN_TERMINAL_WIDTH;
|
|
104904
105025
|
const activeCount = runningStories.length;
|
|
104905
105026
|
const displayElapsed = busState.runSummary ? busState.runSummary.durationMs : elapsedMs;
|
|
104906
|
-
const
|
|
105027
|
+
const tokenParts = [
|
|
105028
|
+
inputTokens > 0 ? `${formatTokens(inputTokens)} in` : null,
|
|
105029
|
+
outputTokens > 0 ? `${formatTokens(outputTokens)} out` : null
|
|
105030
|
+
].filter(Boolean);
|
|
105031
|
+
const tokensStr = tokenParts.length > 0 ? tokenParts.join(" / ") : null;
|
|
104907
105032
|
const headerRight = [
|
|
104908
105033
|
activeCount > 0 ? `${activeCount} running` : null,
|
|
104909
105034
|
formatCost3(busState.totalCost),
|
|
@@ -104964,6 +105089,7 @@ function App2({
|
|
|
104964
105089
|
children: [
|
|
104965
105090
|
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(MemoStoriesPanel, {
|
|
104966
105091
|
stories: busState.stories,
|
|
105092
|
+
preRunPhases,
|
|
104967
105093
|
postRunPhases: busState.postRunPhases,
|
|
104968
105094
|
width: layout.mode === "single" ? layout.width : layout.storiesPanelWidth,
|
|
104969
105095
|
compact: layout.mode === "single",
|
|
@@ -104975,7 +105101,8 @@ function App2({
|
|
|
104975
105101
|
storySteps: busState.storySteps,
|
|
104976
105102
|
runSummary: busState.runSummary,
|
|
104977
105103
|
runErrored: runErroredForPanel,
|
|
104978
|
-
escalationLog: busState.escalationLog
|
|
105104
|
+
escalationLog: busState.escalationLog,
|
|
105105
|
+
currentStage: currentPhaseLabel
|
|
104979
105106
|
}, undefined, false, undefined, this)
|
|
104980
105107
|
]
|
|
104981
105108
|
}, undefined, true, undefined, this),
|