@dorokuma/herdsman 0.13.5 → 0.14.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/README.md +2 -2
- package/dist/src/agent-history/discovery.d.ts +47 -11
- package/dist/src/agent-history/discovery.d.ts.map +1 -1
- package/dist/src/agent-history/discovery.js +242 -468
- package/dist/src/agent-history/discovery.js.map +1 -1
- package/dist/src/agent-history/service.d.ts +4 -7
- package/dist/src/agent-history/service.d.ts.map +1 -1
- package/dist/src/agent-history/service.js +46 -30
- package/dist/src/agent-history/service.js.map +1 -1
- package/dist/src/daemon/agent-event-reconciler.js +6 -10
- package/dist/src/daemon/agent-event-reconciler.js.map +1 -1
- package/dist/src/daemon/herdr-session-watch-manager.js +2 -2
- package/dist/src/daemon/herdr-session-watch-manager.js.map +1 -1
- package/dist/src/daemon/observability-server.js +6 -6
- package/dist/src/daemon/observability-server.js.map +1 -1
- package/dist/src/db/agents.d.ts +0 -2
- package/dist/src/db/agents.d.ts.map +1 -1
- package/dist/src/db/agents.js +14 -57
- package/dist/src/db/agents.js.map +1 -1
- package/dist/src/herdr/pane-identity-resolver.d.ts +11 -0
- package/dist/src/herdr/pane-identity-resolver.d.ts.map +1 -1
- package/dist/src/herdr/pane-identity-resolver.js +27 -15
- package/dist/src/herdr/pane-identity-resolver.js.map +1 -1
- package/dist/src/herdr/session-list.d.ts.map +1 -1
- package/dist/src/herdr/session-list.js +4 -2
- package/dist/src/herdr/session-list.js.map +1 -1
- package/dist/src/observability/agent-context-service.d.ts +4 -10
- package/dist/src/observability/agent-context-service.d.ts.map +1 -1
- package/dist/src/observability/agent-context-service.js +37 -58
- package/dist/src/observability/agent-context-service.js.map +1 -1
- package/dist/src/observability/agent-index-service.d.ts.map +1 -1
- package/dist/src/observability/agent-index-service.js +97 -44
- package/dist/src/observability/agent-index-service.js.map +1 -1
- package/dist/src/observability/contracts.d.ts +1 -2
- package/dist/src/observability/contracts.d.ts.map +1 -1
- package/dist/src/observability/contracts.js.map +1 -1
- package/package.json +1 -1
- package/dist/src/agent-history/claude-reader.d.ts +0 -10
- package/dist/src/agent-history/claude-reader.d.ts.map +0 -1
- package/dist/src/agent-history/claude-reader.js +0 -90
- package/dist/src/agent-history/claude-reader.js.map +0 -1
- package/dist/src/agent-history/codex-reader.d.ts +0 -10
- package/dist/src/agent-history/codex-reader.d.ts.map +0 -1
- package/dist/src/agent-history/codex-reader.js +0 -119
- package/dist/src/agent-history/codex-reader.js.map +0 -1
- package/dist/src/agent-history/gemini-reader.d.ts +0 -10
- package/dist/src/agent-history/gemini-reader.d.ts.map +0 -1
- package/dist/src/agent-history/gemini-reader.js +0 -96
- package/dist/src/agent-history/gemini-reader.js.map +0 -1
- package/dist/src/agent-history/grok-reader.d.ts +0 -11
- package/dist/src/agent-history/grok-reader.d.ts.map +0 -1
- package/dist/src/agent-history/grok-reader.js +0 -54
- package/dist/src/agent-history/grok-reader.js.map +0 -1
- package/dist/src/agent-history/opencode-reader.d.ts +0 -10
- package/dist/src/agent-history/opencode-reader.d.ts.map +0 -1
- package/dist/src/agent-history/opencode-reader.js +0 -90
- package/dist/src/agent-history/opencode-reader.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-index-service.d.ts","sourceRoot":"","sources":["../../../src/observability/agent-index-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-index-service.d.ts","sourceRoot":"","sources":["../../../src/observability/agent-index-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhD,OAAO,EAAE,KAAK,mBAAmB,EAA6B,MAAM,4BAA4B,CAAC;AACjG,OAAO,EAAE,KAAK,eAAe,EAA+B,MAAM,sBAAsB,CAAC;AACzF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,mCAAmC,CAAC;AACrF,OAAO,KAAK,EAAE,UAAU,EAAkB,MAAM,gBAAgB,CAAC;AAEjE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,KAAK,EAAyB,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAE9F,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAC/E,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EAGzB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAGjF,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,mBAAmB,GAAG,IAAI,GAAG,SAAS,EAChD,OAAO,CAAC,EAAE;IAAE,sBAAsB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAAE,GACzD,OAAO,CAiBT;AAOD,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,oBAAoB,EAAE,UAAU,EAAE,CAAC;IACnC,MAAM,EAAE,gBAAgB,EAAE,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,oBAAoB,EAAE,UAAU,EAAE,CAAC;IACnC,MAAM,EAAE,gBAAgB,EAAE,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,KAAK,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACpC,oBAAoB,EAAE,UAAU,EAAE,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,qBAAqB,CAAC,EAAE,qBAAqB,CAC3C,OAAO,mBAAmB,CAC3B,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,uBAAuB,CAAC,CAAC;IACxC,WAAW,EAAE,eAAe,CAAC;IAC7B,uBAAuB,CAAC,EAAE,2BAA2B,CAAC;IACtD,iBAAiB,CAAC,EAAE,sBAAsB,CAAC;IAC3C,MAAM,EAAE,UAAU,CAAC;IACnB,aAAa,EAAE,iBAAiB,CAAC;IACjC,eAAe,EAAE,mBAAmB,CAAC;IACrC,MAAM,EAAE,YAAY,CAAC;IACrB,gBAAgB,EAAE,oBAAoB,CAAC;CACxC,CAAC;AAEF,KAAK,YAAY,GAAG;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAChD,IAAI,EAAE,WAAW,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,WAAW,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAqB3D,qBAAa,uBAAwB,SAAQ,KAAK;;CAKjD;AAqBD,MAAM,MAAM,2BAA2B,GAAG;IACxC,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,oBAAoB,EAAE,UAAU,EAAE,CAAC;IACnC,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,gBAAgB,EAAE,eAAe,EAAE,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,oBAAoB,EAAE,UAAU,EAAE,CAAC;IACnC,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,gBAAgB,EAAE,eAAe,EAAE,CAAC;CACrC,CAAC;AAKF,qBAAa,iBAAiB;;gBAwBhB,OAAO,EAAE;QACnB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;QACtC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;YACtB,UAAU,EAAE,MAAM,CAAC;SACpB,KAAK,IAAI,CAAC,iBAAiB,EAAE,OAAO,GAAG,iBAAiB,CAAC,CAAC;QAC3D,OAAO,CAAC,EAAE,mBAAmB,CAAC;QAC9B,OAAO,CAAC,EAAE,mBAAmB,CAAC;QAC9B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;QACnB,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;QACnE;;;;;WAKG;QACH,cAAc,CAAC,EAAE,WAAW,CAAC;QAC7B,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5D,MAAM,EAAE,uBAAuB,CAAC;QAChC,eAAe,CAAC,EAAE,sBAAsB,CAAC;QACzC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;KAClD;IAgCD,uBAAuB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,2BAA2B,CAAC;IAOlF,mBAAmB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,uBAAuB,CAAC;IA6B1E,oBAAoB,CAAC,KAAK,EAAE;QAC1B,KAAK,EAAE,OAAO,CAAC;QACf,gBAAgB,EAAE,MAAM,CAAC;QACzB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,4BAA4B,CAAC;IAOnC,gBAAgB,CAAC,KAAK,EAAE;QAC5B,KAAK,EAAE,OAAO,CAAC;QACf,gBAAgB,EAAE,MAAM,CAAC;QACzB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAcrC,yBAAyB,IAAI,IAAI;IAOjC;;;;;OAKG;IACH,iBAAiB,IAAI,IAAI;IAUnB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IAuJnC,sBAAsB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;IAqCpF,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAyxBxC,oBAAoB,CAAC,KAAK,EAAE;QAC1B,gBAAgB,EAAE,MAAM,CAAC;QACzB,UAAU,EAAE,eAAe,CAAC;QAC5B,UAAU,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,8BAA8B,CAAC;CAyuC5C"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { safeAllowedSessionPath, sessionPathAllowedByShape } from "../agent-history/discovery.js";
|
|
2
|
+
import { safeAllowedSessionPath, safeOfficialSessionPath, sessionPathAllowedByShape, } from "../agent-history/discovery.js";
|
|
3
3
|
import { createAgentHistoryService } from "../agent-history/service.js";
|
|
4
4
|
import { hasNonEmptyAssistantMessage } from "../db/agent-events.js";
|
|
5
5
|
import { runSqliteTransaction } from "../db/client.js";
|
|
@@ -27,6 +27,24 @@ function isTerminalAssistant(compact) {
|
|
|
27
27
|
return stopReason === "stop" || stopReason === "length";
|
|
28
28
|
}
|
|
29
29
|
export const PLAN_WAITING_HISTORY = "PLAN_WAITING_HISTORY";
|
|
30
|
+
/**
|
|
31
|
+
* How many prior terminal rows the stale-baseline duplicate guard compares
|
|
32
|
+
* against, newest first. The guard used to compare only the single latest
|
|
33
|
+
* terminal row; M2 widens the accepted body set to unconfirmed rounds, so a body
|
|
34
|
+
* that was already delivered as an earlier terminal row must be recognised too.
|
|
35
|
+
* The window keeps that comparison bounded instead of scanning every terminal row
|
|
36
|
+
* the agent ever emitted (those live up to the 7-day settled TTL).
|
|
37
|
+
*
|
|
38
|
+
* Why 200: within that 7-day TTL a single agent's delivered/acked terminal rows
|
|
39
|
+
* in production top out around 250, and the body the guard cares about is always
|
|
40
|
+
* one of the most recent deliveries, so 200 covers it with margin while keeping
|
|
41
|
+
* the read to a few hundred KB of JSON.
|
|
42
|
+
*
|
|
43
|
+
* Above the window the guard stops recognising a duplicate, so an older body that
|
|
44
|
+
* resurfaces at the tail could be re-released as this round's answer once more.
|
|
45
|
+
* That is the accepted cost of bounding the scan instead of walking every row.
|
|
46
|
+
*/
|
|
47
|
+
const STALE_DUPLICATE_GUARD_SCAN_LIMIT = 200;
|
|
30
48
|
export class PlanWaitingHistoryError extends Error {
|
|
31
49
|
constructor() {
|
|
32
50
|
super(PLAN_WAITING_HISTORY);
|
|
@@ -1124,11 +1142,12 @@ export class AgentIndexService {
|
|
|
1124
1142
|
const overlayByPane = new Map();
|
|
1125
1143
|
for (const pane of snapshot.panes) {
|
|
1126
1144
|
const value = record(pane);
|
|
1127
|
-
|
|
1145
|
+
// `pane.list` panes are official `PaneInfo` records: snake_case only.
|
|
1146
|
+
const paneId = stringValue(value.pane_id);
|
|
1128
1147
|
if (!paneId)
|
|
1129
1148
|
continue;
|
|
1130
1149
|
const revision = integerValue(value.revision);
|
|
1131
|
-
const terminalTitle = stringValue(value.terminal_title)
|
|
1150
|
+
const terminalTitle = stringValue(value.terminal_title);
|
|
1132
1151
|
if (revision === undefined && !terminalTitle)
|
|
1133
1152
|
continue;
|
|
1134
1153
|
overlayByPane.set(paneId, {
|
|
@@ -1195,7 +1214,7 @@ export class AgentIndexService {
|
|
|
1195
1214
|
const occupiedByOthers = this.#context.occupiedSessionPathsFor(agent);
|
|
1196
1215
|
const occupancyConflict = Boolean(cached?.historyRef?.path && occupiedByOthers.has(cached.historyRef.path));
|
|
1197
1216
|
const sessionReady = agent.agentSession?.kind === "path" &&
|
|
1198
|
-
|
|
1217
|
+
safeOfficialSessionPath(agent.agentSession.value) !== null;
|
|
1199
1218
|
const sessionUnbound = sessionReady &&
|
|
1200
1219
|
(cached?.historyRef?.kind !== "agent_session" ||
|
|
1201
1220
|
cached.historyRef.path !== agent.agentSession?.value);
|
|
@@ -1442,7 +1461,7 @@ export class AgentIndexService {
|
|
|
1442
1461
|
return result;
|
|
1443
1462
|
}
|
|
1444
1463
|
#isClosedPaneAgent(herdrSessionName, agent) {
|
|
1445
|
-
const paneId = stringValue(agent.pane_id)
|
|
1464
|
+
const paneId = stringValue(agent.pane_id);
|
|
1446
1465
|
if (!paneId)
|
|
1447
1466
|
return false;
|
|
1448
1467
|
return this.#stores.agents.isPaneClosed({
|
|
@@ -1643,6 +1662,13 @@ export class AgentIndexService {
|
|
|
1643
1662
|
// degraded (which is what made #runPlanRow run
|
|
1644
1663
|
// invalidateById(..., "degraded_retry") on already-written content).
|
|
1645
1664
|
const confirmedTerminal = turn.confirmed === true;
|
|
1665
|
+
// M2: the "trust the on-disk body" judgement is decoupled from
|
|
1666
|
+
// `confirmed`. That the turn-end signal arrived is already guaranteed by
|
|
1667
|
+
// the enclosing `if (turn?.received)`, so the deliverability judgement
|
|
1668
|
+
// below only has to decide whether the disk carries a deliverable body —
|
|
1669
|
+
// a round no longer needs a (systematically unreliable) client-side
|
|
1670
|
+
// `confirmed` to be released. `confirmedTerminal` keeps its own meaning
|
|
1671
|
+
// for the released/degraded logging and payload.
|
|
1646
1672
|
const degradeOrRelease = (degradedReason) => {
|
|
1647
1673
|
if (confirmedTerminal) {
|
|
1648
1674
|
// A confirmed round stays non-degraded (see above), but the reason
|
|
@@ -1701,10 +1727,27 @@ export class AgentIndexService {
|
|
|
1701
1727
|
// disk). It can also hold the answer of a *previous* round — the same
|
|
1702
1728
|
// frozen baseline is exactly what the daemon last delivered — so a
|
|
1703
1729
|
// round that writes nothing new would otherwise re-release the
|
|
1704
|
-
// previous round's body as if it were this one's.
|
|
1705
|
-
// the
|
|
1706
|
-
//
|
|
1707
|
-
|
|
1730
|
+
// previous round's body as if it were this one's. The guard compares
|
|
1731
|
+
// `advanced` against the prior terminal rows for this agent that can
|
|
1732
|
+
// have reached the orchestrator — a bounded window, newest first (the
|
|
1733
|
+
// `agent_events` query below). Two things changed from the single-row
|
|
1734
|
+
// guard: (i) it now looks at a window, because a body delivered before
|
|
1735
|
+
// the current latest terminal row would otherwise slip through and, now
|
|
1736
|
+
// that M2 accepts unconfirmed rounds, be re-released as this round's
|
|
1737
|
+
// answer; (ii) it only counts rows in `delivered`/`acked`, because a
|
|
1738
|
+
// never-deliverable row (`agent.idle` from a non-`working` status, per
|
|
1739
|
+
// `isDeliverableAgentEvent`; ~50% of ref-bearing `agent.idle` rows for a
|
|
1740
|
+
// given agent (47/94 in a 7-day sample) carry the same ref as a `done` row
|
|
1741
|
+
// of that agent; they are never deliverable) can sit in the window and must
|
|
1742
|
+
// not blank a deliverable round; the tightened guard is not a strict
|
|
1743
|
+
// superset of the old one (it fires only in the mismatch arm).
|
|
1744
|
+
const staleBaselineDuplicate = this.#stores.sqlite
|
|
1745
|
+
.prepare(`select compact_history_json from agent_events
|
|
1746
|
+
where agent_id = ? and herdr_session_name = ?
|
|
1747
|
+
and type in ('agent.idle', 'agent.done', 'agent.blocked')
|
|
1748
|
+
and status in ('delivered', 'acked')
|
|
1749
|
+
order by id desc limit ?`)
|
|
1750
|
+
.all(input.agent.id, input.agent.herdrSessionName, STALE_DUPLICATE_GUARD_SCAN_LIMIT).some((row) => sameTerminalAssistantContent(advanced, parseCompactHistoryJson(row.compact_history_json), "pi"));
|
|
1708
1751
|
// A confirmed turn carrying a non-empty terminal assistant message is
|
|
1709
1752
|
// trusted as-is, so `no_advance_from_input` stays reserved for rounds
|
|
1710
1753
|
// with genuinely no deliverable assistant text (empty or non-terminal
|
|
@@ -1729,22 +1772,30 @@ export class AgentIndexService {
|
|
|
1729
1772
|
else {
|
|
1730
1773
|
const advancedText = advanced.lastAssistantMessage?.text?.trim() ?? "";
|
|
1731
1774
|
const advancedMatchesExpected = !expectedText || advancedText.endsWith(expectedText);
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1775
|
+
// M2 deliverability judgement: the turn-end signal already arrived
|
|
1776
|
+
// (the enclosing `if (turn?.received)`), so only the disk matters — a
|
|
1777
|
+
// non-empty terminal assistant message that was not already delivered
|
|
1778
|
+
// (`diskDeliverable`) is this round's answer. The
|
|
1779
|
+
// client's `confirmed` flag is deliberately not a conjunct here: its
|
|
1780
|
+
// `expectedText` is only the extension's own guess at the final tail
|
|
1781
|
+
// and can systematically disagree with the transcript that landed on
|
|
1782
|
+
// disk (rewrite-type extensions and credential redaction both make
|
|
1783
|
+
// the text-level confirmation never match), while the disk tail is
|
|
1784
|
+
// the authoritative evidence. When the guess disagrees the body is
|
|
1785
|
+
// still released as-is — `degraded` stays off, because `degraded:
|
|
1786
|
+
// true` makes #runPlanRow call invalidateById(..., "degraded_retry")
|
|
1787
|
+
// on content that is already written — and the mismatch is recorded
|
|
1788
|
+
// as a warning (split by `confirmed`) so it stays diagnosable.
|
|
1789
|
+
const diskDeliverable = isTerminalAssistant(advanced) &&
|
|
1790
|
+
hasNonEmptyAssistantMessage(advanced) &&
|
|
1791
|
+
!staleBaselineDuplicate;
|
|
1792
|
+
if (diskDeliverable) {
|
|
1745
1793
|
if (!advancedMatchesExpected) {
|
|
1746
|
-
console.warn(
|
|
1794
|
+
console.warn(confirmedTerminal
|
|
1795
|
+
? "Herdsman accepted a confirmed pi status event despite an expectedText mismatch"
|
|
1796
|
+
: "Herdsman accepted an unconfirmed pi status event despite an expectedText mismatch", {
|
|
1747
1797
|
agentId: input.agent.id,
|
|
1798
|
+
confirmed: confirmedTerminal,
|
|
1748
1799
|
degradedReason: "expected_text_mismatch",
|
|
1749
1800
|
herdrSessionName: input.agent.herdrSessionName,
|
|
1750
1801
|
paneId: input.agent.paneId,
|
|
@@ -1760,11 +1811,11 @@ export class AgentIndexService {
|
|
|
1760
1811
|
payloadExtra = degradeOrRelease("expected_text_mismatch");
|
|
1761
1812
|
}
|
|
1762
1813
|
else if (!isTerminalAssistant(advanced) || !hasNonEmptyAssistantMessage(advanced)) {
|
|
1763
|
-
// `
|
|
1814
|
+
// `diskDeliverable` is false in this arm by construction (its
|
|
1764
1815
|
// terminal/non-empty conjuncts are exactly this condition's
|
|
1765
|
-
// negation), so a
|
|
1766
|
-
// `non_terminal_assistant` keeps its pre-existing meaning:
|
|
1767
|
-
// non-terminal or empty tail is degraded.
|
|
1816
|
+
// negation), so a round with a deliverable disk body never reaches
|
|
1817
|
+
// it and `non_terminal_assistant` keeps its pre-existing meaning:
|
|
1818
|
+
// only a non-terminal or empty tail is degraded.
|
|
1768
1819
|
compactHistory = { ...advanced, lastAssistantMessage: null };
|
|
1769
1820
|
payloadExtra = degradeOrRelease("non_terminal_assistant");
|
|
1770
1821
|
}
|
|
@@ -2127,31 +2178,23 @@ export class AgentIndexService {
|
|
|
2127
2178
|
}
|
|
2128
2179
|
function withPaneRevision(agent, overlayByPane) {
|
|
2129
2180
|
const raw = record(agent);
|
|
2130
|
-
|
|
2181
|
+
// Herdr payloads are snake_case only (`AgentInfo` / `PaneInfo`); no camelCase
|
|
2182
|
+
// alias is accepted, so a Herdr rename surfaces as a missing field instead of
|
|
2183
|
+
// a silent compatibility shim.
|
|
2184
|
+
const paneId = stringValue(raw.pane_id);
|
|
2131
2185
|
const overlay = paneId ? overlayByPane.get(paneId) : undefined;
|
|
2132
2186
|
const revision = integerValue(raw.revision) ?? overlay?.revision;
|
|
2133
|
-
const terminalTitle = stringValue(raw.terminal_title) ??
|
|
2187
|
+
const terminalTitle = stringValue(raw.terminal_title) ?? overlay?.terminalTitle;
|
|
2134
2188
|
if (revision === undefined && !terminalTitle)
|
|
2135
2189
|
return raw;
|
|
2136
|
-
// Collapse dual keys onto herdr-canonical snake_case `terminal_title`.
|
|
2137
|
-
// Incoming snapshots may carry camelCase `terminalTitle`; spreading `raw`
|
|
2138
|
-
// would otherwise leave both keys on the overlay object. Downstream
|
|
2139
|
-
// HerdrAgentLike readers (AgentStore.replaceForSession) still accept
|
|
2140
|
-
// camelCase as fallback, and AgentIndexRecord.terminalTitle is populated
|
|
2141
|
-
// later from whichever key is present.
|
|
2142
|
-
const rest = { ...raw };
|
|
2143
|
-
delete rest.terminalTitle;
|
|
2144
2190
|
return {
|
|
2145
|
-
...
|
|
2191
|
+
...raw,
|
|
2146
2192
|
...(revision === undefined ? {} : { revision }),
|
|
2147
2193
|
...(terminalTitle ? { terminal_title: terminalTitle } : {}),
|
|
2148
2194
|
};
|
|
2149
2195
|
}
|
|
2150
2196
|
function paneGenerationOf(agent) {
|
|
2151
|
-
return
|
|
2152
|
-
stringValue(agent.paneGeneration) ??
|
|
2153
|
-
stringValue(agent.creation_id) ??
|
|
2154
|
-
stringValue(agent.creationId));
|
|
2197
|
+
return stringValue(agent.pane_generation) ?? stringValue(agent.creation_id);
|
|
2155
2198
|
}
|
|
2156
2199
|
function matchingPrior(agent, previousByTerminal, previousByPane) {
|
|
2157
2200
|
const terminalMatch = agent.terminalId ? previousByTerminal.get(agent.terminalId) : undefined;
|
|
@@ -2237,7 +2280,7 @@ function record(value) {
|
|
|
2237
2280
|
return typeof value === "object" && value !== null ? value : {};
|
|
2238
2281
|
}
|
|
2239
2282
|
function paneGenerationFromEvent(event) {
|
|
2240
|
-
return stringValue(event.pane_generation)
|
|
2283
|
+
return stringValue(event.pane_generation);
|
|
2241
2284
|
}
|
|
2242
2285
|
function paneIdentityKey(paneId, paneGeneration) {
|
|
2243
2286
|
return `${paneId}\0${paneGeneration ?? ""}`;
|
|
@@ -2265,6 +2308,16 @@ function sleep(ms, signal) {
|
|
|
2265
2308
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2266
2309
|
});
|
|
2267
2310
|
}
|
|
2311
|
+
function parseCompactHistoryJson(value) {
|
|
2312
|
+
if (!value)
|
|
2313
|
+
return null;
|
|
2314
|
+
try {
|
|
2315
|
+
return JSON.parse(value);
|
|
2316
|
+
}
|
|
2317
|
+
catch {
|
|
2318
|
+
return null;
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2268
2321
|
function sameTerminalAssistantContent(current, previous, agentKind) {
|
|
2269
2322
|
const currentMsg = current?.lastAssistantMessage;
|
|
2270
2323
|
const prevMsg = previous?.lastAssistantMessage;
|
|
@@ -2276,7 +2329,7 @@ function sameTerminalAssistantContent(current, previous, agentKind) {
|
|
|
2276
2329
|
}
|
|
2277
2330
|
return Boolean(currentMsg.text && currentMsg.text === prevMsg.text);
|
|
2278
2331
|
}
|
|
2279
|
-
// Non-agy (pi
|
|
2332
|
+
// Non-agy (pi):
|
|
2280
2333
|
if (currentMsg.ref !== null && prevMsg.ref !== null) {
|
|
2281
2334
|
return currentMsg.ref === prevMsg.ref;
|
|
2282
2335
|
}
|