@dorokuma/herdsman-pi 0.15.0 → 0.15.1

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/wake.ts +29 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dorokuma/herdsman-pi",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
4
4
  "description": "Pi extension bridge for Herdsman agent history.",
5
5
  "type": "module",
6
6
  "keywords": [
package/src/wake.ts CHANGED
@@ -7,16 +7,19 @@ import { DEFAULT_WAKE_FILTER_CONFIG, isUpstreamModelError, type WakeFilterConfig
7
7
  // next microtask (0ms timer) instead of waiting out a settle window. Delivery
8
8
  // latency is owned by the bounded deferral in the extension, not by this delay.
9
9
  export const WAKE_SETTLE_MS = 0;
10
- // Mirrors `PLAN_WAITING_HISTORY` in `src/observability/contracts.ts` (the string
11
- // value is the contract; duplicated here because `@dorokuma/herdsman-pi` ships
12
- // standalone `src/` without root `src/`).
10
+ // Mirrors the `STALLED_PLAN_ERRORS` set in `src/observability/contracts.ts`
11
+ // (`PLAN_WAITING_HISTORY` / `PLAN_STALLED` / `DEGRADED_PLAN_ERROR`, the last one
12
+ // spelled `"degraded"` inline below) and the copy in `src/agy/hook.ts`: the
13
+ // string value is the contract, and the reason is duplicated here because
14
+ // `@dorokuma/herdsman-pi` ships standalone `src/` without root `src/`. All three
15
+ // places must change together.
13
16
  export const PLAN_WAITING_HISTORY = "PLAN_WAITING_HISTORY";
14
17
  export const PLAN_STALLED = "PLAN_STALLED";
15
18
 
16
19
  export type AgentOutcome = {
17
20
  agent: string;
18
21
  eventId: number;
19
- kind: "blocked" | "completed" | "failed";
22
+ kind: "blocked" | "completed" | "failed" | "stalled";
20
23
  name?: string | null;
21
24
  paneId: string | null;
22
25
  reason?: string;
@@ -99,9 +102,22 @@ function outcomeKind(event: AgentEventWireRecord): AgentOutcome["kind"] | undefi
99
102
  const isAgyOrEmpty = agent === undefined || agent === "agy";
100
103
  if (isAgyOrEmpty) {
101
104
  const reason = stringValue(payload.reason);
105
+ // A stalled plan has *settled*, which makes it unlike PLAN_WAITING_HISTORY
106
+ // / degraded ("still waiting, nothing happened yet"): the round's outcome is
107
+ // gone and the orchestrator has to see that. With the fallback marker it
108
+ // projects as the self-readable `stalled`; a legacy row written before the
109
+ // marker existed still wakes as a plain `failed` rather than vanishing.
110
+ // A `pi` discard never enters this branch (`agent === "pi"` fails
111
+ // `isAgyOrEmpty`), so pi's own PLAN_STALLED handling is unchanged.
112
+ if (reason === PLAN_STALLED) {
113
+ if (payload.fallbackOutcome === true) {
114
+ return "stalled";
115
+ }
116
+ return "failed";
117
+ }
102
118
  if (reason === PLAN_WAITING_HISTORY || reason === "degraded") {
103
119
  if (payload.fallbackOutcome === true) {
104
- return "failed";
120
+ return "stalled";
105
121
  }
106
122
  return undefined;
107
123
  }
@@ -180,7 +196,8 @@ function project(
180
196
  }
181
197
 
182
198
  const text = normalizeExcerpt(event.compactHistory?.lastAssistantMessage?.text);
183
- const reason = kind === "failed" ? normalizeExcerpt(payload.reason) : undefined;
199
+ const reason =
200
+ kind === "failed" || kind === "stalled" ? normalizeExcerpt(payload.reason) : undefined;
184
201
  // Upstream model errors are transient provider failures, not agent results:
185
202
  // they are dropped from the wake projection without an outcome (and without
186
203
  // being consumed into `seen`, so they stay visible as raw evidence).
@@ -254,6 +271,12 @@ export function formatAgentOutcomeUpdates(outcomes: AgentOutcome[]): string {
254
271
  outcome.text.length > 0 ? `\n last assistant (pre-round): ${outcome.text}` : "";
255
272
  return `- failed ${identity} ${pane}\n reason: ${reason}${body}`;
256
273
  }
274
+ if (outcome.kind === "stalled") {
275
+ const reason = outcome.reason && outcome.reason.length > 0 ? outcome.reason : "(unknown)";
276
+ const body =
277
+ outcome.text.length > 0 ? `\n last assistant (pre-round): ${outcome.text}` : "";
278
+ return `- stalled ${identity} ${pane}\n reason: ${reason} (结果未取到,需自读 pane)${body}`;
279
+ }
257
280
  const excerpt = outcome.text.length > 0 ? outcome.text : "(no assistant message)";
258
281
  return `- ${outcome.kind} ${identity} ${pane}\n last assistant: ${excerpt}\n event: ${outcome.eventId}`;
259
282
  }).join("\n");