@code-yeongyu/senpi 2026.8.7 → 2026.8.9-2

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 (199) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/dist/core/agent-session.d.ts +4 -0
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +35 -12
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/extensions/builtin/cache-keepalive/index.d.ts +30 -0
  7. package/dist/core/extensions/builtin/cache-keepalive/index.d.ts.map +1 -0
  8. package/dist/core/extensions/builtin/cache-keepalive/index.js +281 -0
  9. package/dist/core/extensions/builtin/cache-keepalive/index.js.map +1 -0
  10. package/dist/core/extensions/builtin/compaction/index.js +9 -9
  11. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  12. package/dist/core/extensions/builtin/goal/cache-warm-renderer.d.ts.map +1 -1
  13. package/dist/core/extensions/builtin/goal/cache-warm-renderer.js +21 -8
  14. package/dist/core/extensions/builtin/goal/cache-warm-renderer.js.map +1 -1
  15. package/dist/core/extensions/builtin/goal/cache-warm.d.ts +11 -0
  16. package/dist/core/extensions/builtin/goal/cache-warm.d.ts.map +1 -1
  17. package/dist/core/extensions/builtin/goal/cache-warm.js +16 -0
  18. package/dist/core/extensions/builtin/goal/cache-warm.js.map +1 -1
  19. package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -1
  20. package/dist/core/extensions/builtin/goal/index.js +31 -1
  21. package/dist/core/extensions/builtin/goal/index.js.map +1 -1
  22. package/dist/core/extensions/builtin/goal/monitor-continuation-types.d.ts +1 -0
  23. package/dist/core/extensions/builtin/goal/monitor-continuation-types.d.ts.map +1 -1
  24. package/dist/core/extensions/builtin/goal/monitor-continuation-types.js.map +1 -1
  25. package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts +2 -1
  26. package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts.map +1 -1
  27. package/dist/core/extensions/builtin/goal/monitor-continuation.js +153 -51
  28. package/dist/core/extensions/builtin/goal/monitor-continuation.js.map +1 -1
  29. package/dist/core/extensions/builtin/goal/prompt.d.ts +1 -1
  30. package/dist/core/extensions/builtin/goal/prompt.d.ts.map +1 -1
  31. package/dist/core/extensions/builtin/goal/prompt.js +21 -7
  32. package/dist/core/extensions/builtin/goal/prompt.js.map +1 -1
  33. package/dist/core/extensions/builtin/goal/store-changed-event.d.ts +8 -0
  34. package/dist/core/extensions/builtin/goal/store-changed-event.d.ts.map +1 -0
  35. package/dist/core/extensions/builtin/goal/store-changed-event.js +9 -0
  36. package/dist/core/extensions/builtin/goal/store-changed-event.js.map +1 -0
  37. package/dist/core/extensions/builtin/goal/wait-progress.d.ts +3 -1
  38. package/dist/core/extensions/builtin/goal/wait-progress.d.ts.map +1 -1
  39. package/dist/core/extensions/builtin/goal/wait-progress.js +27 -3
  40. package/dist/core/extensions/builtin/goal/wait-progress.js.map +1 -1
  41. package/dist/core/extensions/builtin/goal/wait-ticker.d.ts +4 -3
  42. package/dist/core/extensions/builtin/goal/wait-ticker.d.ts.map +1 -1
  43. package/dist/core/extensions/builtin/goal/wait-ticker.js +6 -6
  44. package/dist/core/extensions/builtin/goal/wait-ticker.js.map +1 -1
  45. package/dist/core/extensions/builtin/import-repro.d.ts.map +1 -1
  46. package/dist/core/extensions/builtin/import-repro.js +9 -0
  47. package/dist/core/extensions/builtin/import-repro.js.map +1 -1
  48. package/dist/core/extensions/builtin/index.d.ts.map +1 -1
  49. package/dist/core/extensions/builtin/index.js +2 -0
  50. package/dist/core/extensions/builtin/index.js.map +1 -1
  51. package/dist/core/extensions/builtin/model-fallback/settings.d.ts.map +1 -1
  52. package/dist/core/extensions/builtin/model-fallback/settings.js +21 -1
  53. package/dist/core/extensions/builtin/model-fallback/settings.js.map +1 -1
  54. package/dist/core/extensions/builtin/monitor-state-event.d.ts +16 -0
  55. package/dist/core/extensions/builtin/monitor-state-event.d.ts.map +1 -1
  56. package/dist/core/extensions/builtin/monitor-state-event.js +15 -0
  57. package/dist/core/extensions/builtin/monitor-state-event.js.map +1 -1
  58. package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
  59. package/dist/core/extensions/builtin/terminal/extension.js +20 -1
  60. package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
  61. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts +5 -1
  62. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts.map +1 -1
  63. package/dist/core/extensions/builtin/terminal/session-bundle.js +19 -2
  64. package/dist/core/extensions/builtin/terminal/session-bundle.js.map +1 -1
  65. package/dist/core/extensions/builtin/terminal/tools/bash.d.ts.map +1 -1
  66. package/dist/core/extensions/builtin/terminal/tools/bash.js +2 -0
  67. package/dist/core/extensions/builtin/terminal/tools/bash.js.map +1 -1
  68. package/dist/core/extensions/builtin/terminal/tools/context.d.ts +2 -0
  69. package/dist/core/extensions/builtin/terminal/tools/context.d.ts.map +1 -1
  70. package/dist/core/extensions/builtin/terminal/tools/context.js.map +1 -1
  71. package/dist/core/extensions/runner.d.ts +2 -0
  72. package/dist/core/extensions/runner.d.ts.map +1 -1
  73. package/dist/core/extensions/runner.js +15 -2
  74. package/dist/core/extensions/runner.js.map +1 -1
  75. package/dist/core/extensions/types.d.ts +16 -0
  76. package/dist/core/extensions/types.d.ts.map +1 -1
  77. package/dist/core/extensions/types.js.map +1 -1
  78. package/dist/core/resource-loader.d.ts +4 -0
  79. package/dist/core/resource-loader.d.ts.map +1 -1
  80. package/dist/core/resource-loader.js +6 -0
  81. package/dist/core/resource-loader.js.map +1 -1
  82. package/dist/core/retry-fallback/chains.d.ts +13 -1
  83. package/dist/core/retry-fallback/chains.d.ts.map +1 -1
  84. package/dist/core/retry-fallback/chains.js +80 -8
  85. package/dist/core/retry-fallback/chains.js.map +1 -1
  86. package/dist/core/retry-fallback/controller.d.ts +2 -0
  87. package/dist/core/retry-fallback/controller.d.ts.map +1 -1
  88. package/dist/core/retry-fallback/controller.js.map +1 -1
  89. package/dist/core/retry-fallback/expansion.d.ts +46 -0
  90. package/dist/core/retry-fallback/expansion.d.ts.map +1 -0
  91. package/dist/core/retry-fallback/expansion.js +113 -0
  92. package/dist/core/retry-fallback/expansion.js.map +1 -0
  93. package/dist/core/retry-fallback/settings.d.ts +6 -0
  94. package/dist/core/retry-fallback/settings.d.ts.map +1 -1
  95. package/dist/core/retry-fallback/settings.js +12 -10
  96. package/dist/core/retry-fallback/settings.js.map +1 -1
  97. package/dist/core/retry-fallback/validate.d.ts.map +1 -1
  98. package/dist/core/retry-fallback/validate.js +14 -1
  99. package/dist/core/retry-fallback/validate.js.map +1 -1
  100. package/dist/core/session-manager.d.ts +1 -1
  101. package/dist/core/session-manager.d.ts.map +1 -1
  102. package/dist/core/session-manager.js.map +1 -1
  103. package/dist/core/settings-manager.d.ts +10 -0
  104. package/dist/core/settings-manager.d.ts.map +1 -1
  105. package/dist/core/settings-manager.js +14 -0
  106. package/dist/core/settings-manager.js.map +1 -1
  107. package/dist/main.d.ts.map +1 -1
  108. package/dist/main.js +18 -3
  109. package/dist/main.js.map +1 -1
  110. package/dist/modes/app-server/threads/goal-handlers.d.ts.map +1 -1
  111. package/dist/modes/app-server/threads/goal-handlers.js +7 -0
  112. package/dist/modes/app-server/threads/goal-handlers.js.map +1 -1
  113. package/dist/modes/interactive/components/favorite-models-selector.d.ts +4 -0
  114. package/dist/modes/interactive/components/favorite-models-selector.d.ts.map +1 -1
  115. package/dist/modes/interactive/components/favorite-models-selector.js +23 -7
  116. package/dist/modes/interactive/components/favorite-models-selector.js.map +1 -1
  117. package/dist/modes/interactive/components/model-selector.d.ts +1 -0
  118. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  119. package/dist/modes/interactive/components/model-selector.js +11 -7
  120. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  121. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  122. package/dist/modes/interactive/interactive-mode.js +26 -6
  123. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  124. package/dist/modes/interactive/model-search-rank.d.ts +13 -0
  125. package/dist/modes/interactive/model-search-rank.d.ts.map +1 -0
  126. package/dist/modes/interactive/model-search-rank.js +200 -0
  127. package/dist/modes/interactive/model-search-rank.js.map +1 -0
  128. package/dist/modes/interactive/model-search.d.ts +0 -5
  129. package/dist/modes/interactive/model-search.d.ts.map +1 -1
  130. package/dist/modes/interactive/model-search.js +0 -9
  131. package/dist/modes/interactive/model-search.js.map +1 -1
  132. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  133. package/dist/modes/rpc/session-command-router.js +3 -2
  134. package/dist/modes/rpc/session-command-router.js.map +1 -1
  135. package/dist/modes/rpc/session-registry.d.ts +1 -1
  136. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  137. package/dist/modes/rpc/session-registry.js +3 -3
  138. package/dist/modes/rpc/session-registry.js.map +1 -1
  139. package/docs/providers.md +23 -3
  140. package/docs/settings.md +1 -1
  141. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +33 -0
  142. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  143. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/wake-source-state.ts +29 -0
  144. package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +10 -0
  145. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-manager.ts +27 -1
  146. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.d.ts.map +1 -1
  147. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.js +4 -7
  148. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.js.map +1 -1
  149. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  150. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts +3 -1
  151. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
  152. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +13 -0
  153. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  154. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  155. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +6 -3
  156. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js.map +1 -1
  157. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts.map +1 -1
  158. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +6 -2
  159. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
  160. package/node_modules/@earendil-works/pi-ai/dist/api/warm-prompt-cache.d.ts +17 -0
  161. package/node_modules/@earendil-works/pi-ai/dist/api/warm-prompt-cache.d.ts.map +1 -0
  162. package/node_modules/@earendil-works/pi-ai/dist/api/warm-prompt-cache.js +52 -0
  163. package/node_modules/@earendil-works/pi-ai/dist/api/warm-prompt-cache.js.map +1 -0
  164. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +3 -1
  165. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  166. package/node_modules/@earendil-works/pi-ai/dist/index.js +3 -1
  167. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  168. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  169. package/node_modules/@earendil-works/pi-ai/dist/providers/data/github-copilot.json +1 -1
  170. package/node_modules/@earendil-works/pi-ai/dist/providers/data/moonshotai-cn.json +1 -1
  171. package/node_modules/@earendil-works/pi-ai/dist/providers/data/moonshotai.json +1 -1
  172. package/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
  173. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  174. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  175. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  176. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  177. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/antml/recovery-stream.d.ts.map +1 -1
  178. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/antml/recovery-stream.js +120 -1
  179. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/antml/recovery-stream.js.map +1 -1
  180. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +2 -0
  181. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  182. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  183. package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.d.ts +3 -1
  184. package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.d.ts.map +1 -1
  185. package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.js +17 -3
  186. package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.js.map +1 -1
  187. package/node_modules/@earendil-works/pi-ai/dist/utils/visible-text.d.ts +4 -0
  188. package/node_modules/@earendil-works/pi-ai/dist/utils/visible-text.d.ts.map +1 -0
  189. package/node_modules/@earendil-works/pi-ai/dist/utils/visible-text.js +8 -0
  190. package/node_modules/@earendil-works/pi-ai/dist/utils/visible-text.js.map +1 -0
  191. package/node_modules/@earendil-works/pi-ai/package.json +1 -1
  192. package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.d.ts +12 -0
  193. package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.d.ts.map +1 -1
  194. package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.js +34 -2
  195. package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.js.map +1 -1
  196. package/node_modules/@earendil-works/pi-pty/native/prebuilds/darwin-arm64/senpi_pty.darwin-arm64.node +0 -0
  197. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  198. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  199. package/package.json +6 -6
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Wake-source liveness contract shared with the goal builtin.
3
+ *
4
+ * The goal builtin delays its hidden continuation while any live wake source
5
+ * can still wake the session. Each emitter publishes a full per-source
6
+ * snapshot on every liveness transition and once on `session_start`.
7
+ *
8
+ * The event name is duplicated here on purpose: senpi-codemode is a separate
9
+ * package and must not import across the packages/coding-agent boundary, so
10
+ * the sentinel test pins this literal to the exact cross-package contract.
11
+ */
12
+ export const WAKE_SOURCE_STATE_EVENT = "wake_source_state";
13
+
14
+ /** Source key identifying detached eval cells in the per-source snapshot. */
15
+ export const SENPI_CODEMODE_WAKE_SOURCE = "senpi-codemode";
16
+
17
+ /** One detached cell as broadcast on the wake-source state event. */
18
+ export interface WakeSourceStateItem {
19
+ readonly id: string;
20
+ readonly description: string;
21
+ /** Epoch milliseconds when the channel registered; lets consumers render their own elapsed labels. */
22
+ readonly startedAtMs: number;
23
+ }
24
+
25
+ export interface WakeSourceState {
26
+ readonly source: string;
27
+ readonly activeCount: number;
28
+ readonly items?: readonly WakeSourceStateItem[];
29
+ }
@@ -15,6 +15,7 @@ import {
15
15
  } from "./extension/runtime-factory.ts";
16
16
  import type { CodemodeSessionManager, CreateCodemodeSessionManagerOptions } from "./extension/session-manager.ts";
17
17
  import { SessionManagerProxy } from "./extension/session-manager-proxy.ts";
18
+ import { WAKE_SOURCE_STATE_EVENT, type WakeSourceState } from "./extension/wake-source-state.ts";
18
19
  import { EvalDetachedCellManager, type EvalDetachedCellStatusEntry } from "./tool/detached-cell-manager.ts";
19
20
  import { createEvalTool } from "./tool/eval-tool.ts";
20
21
  import { renderEvalCall, renderEvalResult } from "./tool/render.ts";
@@ -38,6 +39,8 @@ export interface CodemodeExtensionAPI {
38
39
  getActiveTools(): string[];
39
40
  getAllTools(): readonly EvalSchemaToolInfo[];
40
41
  sendUserMessage(content: string, options?: { deliverAs?: "steer" | "followUp" }): void;
42
+ /** Optional host event bus; a host without one turns wake-source emission into a harmless no-op. */
43
+ events?: { emit(name: string, data: unknown): void };
41
44
  }
42
45
 
43
46
  export interface SenpiCodemodeOptions {
@@ -79,6 +82,9 @@ export default function senpiCodemode(pi: CodemodeExtensionAPI, options: SenpiCo
79
82
  const showDetachedCells = (entries: readonly EvalDetachedCellStatusEntry[]): void => {
80
83
  statusTicker.sync(entries);
81
84
  };
85
+ const emitWakeSourceState = (state: WakeSourceState): void => {
86
+ pi.events?.emit(WAKE_SOURCE_STATE_EVENT, state);
87
+ };
82
88
  const registerEvalForRuntime = (
83
89
  runtime: SessionRuntime,
84
90
  modelId: string | undefined,
@@ -126,6 +132,7 @@ export default function senpiCodemode(pi: CodemodeExtensionAPI, options: SenpiCo
126
132
  cellManager: new EvalDetachedCellManager({
127
133
  notifier,
128
134
  onStatusChange: showDetachedCells,
135
+ onWakeSourceState: emitWakeSourceState,
129
136
  ...(options.now === undefined ? {} : { now: options.now }),
130
137
  }),
131
138
  executionTracker: manager,
@@ -156,9 +163,12 @@ export default function senpiCodemode(pi: CodemodeExtensionAPI, options: SenpiCo
156
163
  artifactsDir: runtime.artifactsDir,
157
164
  notifier,
158
165
  onStatusChange: showDetachedCells,
166
+ onWakeSourceState: emitWakeSourceState,
159
167
  ...(options.now === undefined ? {} : { now: options.now }),
160
168
  });
161
169
  activeCells = cellManager;
170
+ // The goal builtin clears its per-session counts at session_start; re-publish our snapshot.
171
+ cellManager.publishWakeSourceState();
162
172
  activeRuntime = runtime;
163
173
  activeModelId = ctx.model?.id;
164
174
  registerEvalForRuntime(runtime, activeModelId, cellManager);
@@ -1,4 +1,5 @@
1
1
  import type { AgentToolResult } from "@code-yeongyu/senpi";
2
+ import { SENPI_CODEMODE_WAKE_SOURCE, type WakeSourceState } from "../extension/wake-source-state.ts";
2
3
  import { detachedNotificationSpillPath } from "./detached-cell-notification.ts";
3
4
  import { currentDetachedResult, detachedErrorResult, snapshotDetachedCell } from "./detached-cell-snapshot.ts";
4
5
  import {
@@ -58,12 +59,15 @@ export interface EvalDetachedCellManagerOptions {
58
59
  readonly artifactsDir?: string;
59
60
  readonly notifier?: EvalDetachedCellNotifier;
60
61
  readonly onStatusChange?: (entries: readonly EvalDetachedCellStatusEntry[]) => void;
62
+ /** Receives a full per-source liveness snapshot on every detached-cell transition; used by the goal builtin. */
63
+ readonly onWakeSourceState?: (state: WakeSourceState) => void;
61
64
  readonly now?: () => number;
62
65
  }
63
66
 
64
67
  export class EvalDetachedCellManager {
65
68
  readonly #artifactsDir: string | undefined;
66
69
  readonly #onStatusChange: ((entries: readonly EvalDetachedCellStatusEntry[]) => void) | undefined;
70
+ readonly #onWakeSourceState: ((state: WakeSourceState) => void) | undefined;
67
71
  readonly #cells = new Map<string, ManagedCell>();
68
72
  readonly #detachedByLanguage = new Map<EvalLanguage, ManagedCell>();
69
73
  readonly #notificationQueue: DetachedNotificationQueue;
@@ -72,6 +76,7 @@ export class EvalDetachedCellManager {
72
76
  constructor(options: EvalDetachedCellManagerOptions = {}) {
73
77
  this.#artifactsDir = options.artifactsDir;
74
78
  this.#onStatusChange = options.onStatusChange;
79
+ this.#onWakeSourceState = options.onWakeSourceState;
75
80
  this.#notificationQueue = new DetachedNotificationQueue(options.notifier, options.artifactsDir);
76
81
  this.#now = options.now ?? Date.now;
77
82
  }
@@ -155,6 +160,7 @@ export class EvalDetachedCellManager {
155
160
  await Promise.allSettled(
156
161
  detached.map(async (cell) => await this.stop(cell.cellId, "Session ended; detached eval cell cancelled")),
157
162
  );
163
+ if (detached.length === 0) this.#emitWakeSourceState([]);
158
164
  await this.#notificationQueue.flush();
159
165
  }
160
166
 
@@ -162,6 +168,11 @@ export class EvalDetachedCellManager {
162
168
  await this.#notificationQueue.flush();
163
169
  }
164
170
 
171
+ /** Re-publish the current snapshot; consumers reset their per-source counts at session_start. */
172
+ publishWakeSourceState(): void {
173
+ this.#emitWakeSourceState([...this.#detachedByLanguage.values()]);
174
+ }
175
+
165
176
  #settle(
166
177
  cell: ManagedCell,
167
178
  state: "completed" | "failed" | "cancelled",
@@ -188,14 +199,29 @@ export class EvalDetachedCellManager {
188
199
  }
189
200
 
190
201
  #emitStatus(): void {
202
+ const liveCells = [...this.#detachedByLanguage.values()];
191
203
  this.#onStatusChange?.(
192
- [...this.#detachedByLanguage.values()].map((cell) => ({
204
+ liveCells.map((cell) => ({
193
205
  cellId: cell.cellId,
194
206
  language: cell.input.language,
195
207
  startedAtMs: cell.startedAtMs,
196
208
  ...(cell.input.summary === undefined ? {} : { summary: cell.input.summary }),
197
209
  })),
198
210
  );
211
+ this.#emitWakeSourceState(liveCells);
212
+ }
213
+
214
+ #emitWakeSourceState(liveCells: readonly ManagedCell[]): void {
215
+ this.#onWakeSourceState?.({
216
+ source: SENPI_CODEMODE_WAKE_SOURCE,
217
+ activeCount: liveCells.length,
218
+ items: liveCells.map((cell) => ({
219
+ id: cell.cellId,
220
+ description:
221
+ cell.input.summary === undefined || cell.input.summary.length === 0 ? cell.cellId : cell.input.summary,
222
+ startedAtMs: cell.startedAtMs,
223
+ })),
224
+ });
199
225
  }
200
226
 
201
227
  #snapshot(cell: ManagedCell): EvalDetachedCellSnapshot {
@@ -1 +1 @@
1
- {"version":3,"file":"empty-assistant-recovery.d.ts","sourceRoot":"","sources":["../src/empty-assistant-recovery.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,GAAG,EAMR,KAAK,KAAK,EACV,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AA2G3C,wBAAgB,0BAA0B,CAAC,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,QAAQ,GAAG,QAAQ,CAMnH"}
1
+ {"version":3,"file":"empty-assistant-recovery.d.ts","sourceRoot":"","sources":["../src/empty-assistant-recovery.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,GAAG,EAQR,KAAK,KAAK,EAEV,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAqG3C,wBAAgB,0BAA0B,CAAC,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,QAAQ,GAAG,QAAQ,CAMnH"}
@@ -1,13 +1,10 @@
1
- import { createAssistantMessageEventStream, hasKimiTextToolCallRecovery, } from "@earendil-works/pi-ai";
1
+ import { createAssistantMessageEventStream, getToolCallFormat, hasVisibleAssistantContent, hasVisibleText, shouldRecoverTextToolCalls, } from "@earendil-works/pi-ai";
2
2
  const EMPTY_RESPONSE_ERROR = "Model returned an empty response twice";
3
- function hasVisibleContent(message) {
4
- return message.content.some((block) => block.type === "toolCall" || (block.type === "text" && block.text.trim().length > 0));
5
- }
6
3
  function isEmptyStop(message) {
7
- return message.stopReason === "stop" && !hasVisibleContent(message);
4
+ return message.stopReason === "stop" && !hasVisibleAssistantContent(message);
8
5
  }
9
6
  function eventStartsVisibleContent(event) {
10
- return event.type === "toolcall_start" || (event.type === "text_delta" && event.delta.trim().length > 0);
7
+ return event.type === "toolcall_start" || (event.type === "text_delta" && hasVisibleText(event.delta));
11
8
  }
12
9
  function appendRetryDiagnostic(message) {
13
10
  return {
@@ -96,7 +93,7 @@ function createRetryingStream(firstStream, createStream) {
96
93
  return outerStream;
97
94
  }
98
95
  export function withEmptyAssistantRecovery(model, streamFunction) {
99
- if (!hasKimiTextToolCallRecovery(model))
96
+ if (!shouldRecoverTextToolCalls(model) && getToolCallFormat(model) === undefined)
100
97
  return streamFunction;
101
98
  return async (requestedModel, context, options) => {
102
99
  const createStream = () => streamFunction(requestedModel, context, options);
@@ -1 +1 @@
1
- {"version":3,"file":"empty-assistant-recovery.js","sourceRoot":"","sources":["../src/empty-assistant-recovery.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype Api,\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype AssistantMessageEventStream,\n\tcreateAssistantMessageEventStream,\n\thasKimiTextToolCallRecovery,\n\ttype Model,\n} from \"@earendil-works/pi-ai\";\nimport type { StreamFn } from \"./types.ts\";\n\ntype StreamFactory = () => AssistantMessageEventStream | Promise<AssistantMessageEventStream>;\n\nconst EMPTY_RESPONSE_ERROR = \"Model returned an empty response twice\";\n\nfunction hasVisibleContent(message: AssistantMessage): boolean {\n\treturn message.content.some(\n\t\t(block) => block.type === \"toolCall\" || (block.type === \"text\" && block.text.trim().length > 0),\n\t);\n}\n\nfunction isEmptyStop(message: AssistantMessage): boolean {\n\treturn message.stopReason === \"stop\" && !hasVisibleContent(message);\n}\n\nfunction eventStartsVisibleContent(event: AssistantMessageEvent): boolean {\n\treturn event.type === \"toolcall_start\" || (event.type === \"text_delta\" && event.delta.trim().length > 0);\n}\n\nfunction appendRetryDiagnostic(message: AssistantMessage): AssistantMessage {\n\treturn {\n\t\t...message,\n\t\tdiagnostics: [\n\t\t\t...(message.diagnostics ?? []),\n\t\t\t{\n\t\t\t\ttype: \"empty_assistant_response_recovery\",\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\tdetails: { retries: 1 },\n\t\t\t},\n\t\t],\n\t};\n}\n\nfunction createEmptyResponseFailure(message: AssistantMessage): AssistantMessage {\n\treturn {\n\t\t...appendRetryDiagnostic(message),\n\t\tcontent: [{ type: \"text\", text: EMPTY_RESPONSE_ERROR }],\n\t\tstopReason: \"error\",\n\t\terrorMessage: EMPTY_RESPONSE_ERROR,\n\t};\n}\n\nfunction createRetryingStream(firstStream: AssistantMessageEventStream, createStream: StreamFactory) {\n\tconst outerStream = createAssistantMessageEventStream();\n\n\tvoid (async (): Promise<void> => {\n\t\ttry {\n\t\t\tlet stream = firstStream;\n\t\t\tlet retrying = false;\n\t\t\tfor (;;) {\n\t\t\t\tconst buffered: AssistantMessageEvent[] = [];\n\t\t\t\tlet forwarding = false;\n\t\t\t\tlet retry = false;\n\t\t\t\tfor await (const event of stream) {\n\t\t\t\t\tif (event.type === \"done\") {\n\t\t\t\t\t\tif (isEmptyStop(event.message)) {\n\t\t\t\t\t\t\tif (!retrying) {\n\t\t\t\t\t\t\t\tretry = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst error = createEmptyResponseFailure(event.message);\n\t\t\t\t\t\t\touterStream.push({ type: \"error\", reason: \"error\", error });\n\t\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst terminal = retrying ? { ...event, message: appendRetryDiagnostic(event.message) } : event;\n\t\t\t\t\t\tif (!forwarding) {\n\t\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\t}\n\t\t\t\t\t\touterStream.push(terminal);\n\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (event.type === \"error\") {\n\t\t\t\t\t\tif (!forwarding) {\n\t\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\t}\n\t\t\t\t\t\touterStream.push(event);\n\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (forwarding) {\n\t\t\t\t\t\touterStream.push(event);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbuffered.push(event);\n\t\t\t\t\tif (eventStartsVisibleContent(event)) {\n\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\tforwarding = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!retry) {\n\t\t\t\t\touterStream.end(retrying ? appendRetryDiagnostic(await stream.result()) : await stream.result());\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tretrying = true;\n\t\t\t\tstream = await createStream();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\touterStream.fail(error);\n\t\t}\n\t})();\n\n\treturn outerStream;\n}\n\nexport function withEmptyAssistantRecovery<TApi extends Api>(model: Model<TApi>, streamFunction: StreamFn): StreamFn {\n\tif (!hasKimiTextToolCallRecovery(model)) return streamFunction;\n\treturn async (requestedModel, context, options) => {\n\t\tconst createStream = (): ReturnType<StreamFn> => streamFunction(requestedModel, context, options);\n\t\treturn createRetryingStream(await createStream(), createStream);\n\t};\n}\n"]}
1
+ {"version":3,"file":"empty-assistant-recovery.js","sourceRoot":"","sources":["../src/empty-assistant-recovery.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype Api,\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype AssistantMessageEventStream,\n\tcreateAssistantMessageEventStream,\n\tgetToolCallFormat,\n\thasVisibleAssistantContent,\n\thasVisibleText,\n\ttype Model,\n\tshouldRecoverTextToolCalls,\n} from \"@earendil-works/pi-ai\";\nimport type { StreamFn } from \"./types.ts\";\n\ntype StreamFactory = () => AssistantMessageEventStream | Promise<AssistantMessageEventStream>;\n\nconst EMPTY_RESPONSE_ERROR = \"Model returned an empty response twice\";\n\nfunction isEmptyStop(message: AssistantMessage): boolean {\n\treturn message.stopReason === \"stop\" && !hasVisibleAssistantContent(message);\n}\n\nfunction eventStartsVisibleContent(event: AssistantMessageEvent): boolean {\n\treturn event.type === \"toolcall_start\" || (event.type === \"text_delta\" && hasVisibleText(event.delta));\n}\n\nfunction appendRetryDiagnostic(message: AssistantMessage): AssistantMessage {\n\treturn {\n\t\t...message,\n\t\tdiagnostics: [\n\t\t\t...(message.diagnostics ?? []),\n\t\t\t{\n\t\t\t\ttype: \"empty_assistant_response_recovery\",\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\tdetails: { retries: 1 },\n\t\t\t},\n\t\t],\n\t};\n}\n\nfunction createEmptyResponseFailure(message: AssistantMessage): AssistantMessage {\n\treturn {\n\t\t...appendRetryDiagnostic(message),\n\t\tcontent: [{ type: \"text\", text: EMPTY_RESPONSE_ERROR }],\n\t\tstopReason: \"error\",\n\t\terrorMessage: EMPTY_RESPONSE_ERROR,\n\t};\n}\n\nfunction createRetryingStream(firstStream: AssistantMessageEventStream, createStream: StreamFactory) {\n\tconst outerStream = createAssistantMessageEventStream();\n\n\tvoid (async (): Promise<void> => {\n\t\ttry {\n\t\t\tlet stream = firstStream;\n\t\t\tlet retrying = false;\n\t\t\tfor (;;) {\n\t\t\t\tconst buffered: AssistantMessageEvent[] = [];\n\t\t\t\tlet forwarding = false;\n\t\t\t\tlet retry = false;\n\t\t\t\tfor await (const event of stream) {\n\t\t\t\t\tif (event.type === \"done\") {\n\t\t\t\t\t\tif (isEmptyStop(event.message)) {\n\t\t\t\t\t\t\tif (!retrying) {\n\t\t\t\t\t\t\t\tretry = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst error = createEmptyResponseFailure(event.message);\n\t\t\t\t\t\t\touterStream.push({ type: \"error\", reason: \"error\", error });\n\t\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst terminal = retrying ? { ...event, message: appendRetryDiagnostic(event.message) } : event;\n\t\t\t\t\t\tif (!forwarding) {\n\t\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\t}\n\t\t\t\t\t\touterStream.push(terminal);\n\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (event.type === \"error\") {\n\t\t\t\t\t\tif (!forwarding) {\n\t\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\t}\n\t\t\t\t\t\touterStream.push(event);\n\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (forwarding) {\n\t\t\t\t\t\touterStream.push(event);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbuffered.push(event);\n\t\t\t\t\tif (eventStartsVisibleContent(event)) {\n\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\tforwarding = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!retry) {\n\t\t\t\t\touterStream.end(retrying ? appendRetryDiagnostic(await stream.result()) : await stream.result());\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tretrying = true;\n\t\t\t\tstream = await createStream();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\touterStream.fail(error);\n\t\t}\n\t})();\n\n\treturn outerStream;\n}\n\nexport function withEmptyAssistantRecovery<TApi extends Api>(model: Model<TApi>, streamFunction: StreamFn): StreamFn {\n\tif (!shouldRecoverTextToolCalls(model) && getToolCallFormat(model) === undefined) return streamFunction;\n\treturn async (requestedModel, context, options) => {\n\t\tconst createStream = (): ReturnType<StreamFn> => streamFunction(requestedModel, context, options);\n\t\treturn createRetryingStream(await createStream(), createStream);\n\t};\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-agent-core",
3
3
  "private": true,
4
- "version": "2026.8.7",
4
+ "version": "2026.8.9-2",
5
5
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
@@ -30,7 +30,7 @@
30
30
  "prepublishOnly": "npm run build"
31
31
  },
32
32
  "dependencies": {
33
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.8.7",
33
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.8.9-2",
34
34
  "diff": "9.0.0",
35
35
  "ignore": "7.0.6",
36
36
  "typebox": "1.3.8",
@@ -1,5 +1,6 @@
1
1
  import Anthropic from "@anthropic-ai/sdk";
2
- import type { SimpleStreamOptions, StreamFunction, StreamOptions } from "../types.ts";
2
+ import type { MessageCreateParamsNonStreaming } from "@anthropic-ai/sdk/resources/messages.js";
3
+ import type { Context, Model, SimpleStreamOptions, StreamFunction, StreamOptions } from "../types.ts";
3
4
  export { getAnthropicCompat } from "../utils/prompt-cache-ttl.ts";
4
5
  export type AnthropicEffort = "low" | "medium" | "high" | "xhigh" | "max";
5
6
  export type AnthropicThinkingDisplay = "summarized" | "omitted";
@@ -69,4 +70,5 @@ export interface AnthropicOptions extends StreamOptions {
69
70
  }
70
71
  export declare const stream: StreamFunction<"anthropic-messages", AnthropicOptions>;
71
72
  export declare const streamSimple: StreamFunction<"anthropic-messages", SimpleStreamOptions>;
73
+ export declare function buildAnthropicWarmPromptCacheParams(model: Model<"anthropic-messages">, context: Context, options?: AnthropicOptions): MessageCreateParamsNonStreaming;
72
74
  //# sourceMappingURL=anthropic-messages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAU1C,OAAO,KAAK,EAWX,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAOb,MAAM,aAAa,CAAC;AAkCrB,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AA6JlE,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AAoEhE,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACtD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACnB;AA4zBD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CA6ZzE,CAAC;AA0FF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CA2ClF,CAAC"}
1
+ {"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,KAAK,EAGX,+BAA+B,EAK/B,MAAM,yCAAyC,CAAC;AAEjD,OAAO,KAAK,EAKX,OAAO,EAGP,KAAK,EAGL,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAOb,MAAM,aAAa,CAAC;AAkCrB,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AA6JlE,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AAoEhE,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACtD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACnB;AA4zBD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CA6ZzE,CAAC;AA0FF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CA2ClF,CAAC;AAsIF,wBAAgB,mCAAmC,CAClD,KAAK,EAAE,KAAK,CAAC,oBAAoB,CAAC,EAClC,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,gBAAgB,GACxB,+BAA+B,CAkBjC"}
@@ -1438,6 +1438,19 @@ function createClient(model, apiKey, interleavedThinking, useFineGrainedToolStre
1438
1438
  });
1439
1439
  return { client, isOAuthToken: false };
1440
1440
  }
1441
+ export function buildAnthropicWarmPromptCacheParams(model, context, options) {
1442
+ const params = buildParams(model, context, false, {
1443
+ ...options,
1444
+ maxTokens: 0,
1445
+ thinkingEnabled: undefined,
1446
+ toolChoice: undefined,
1447
+ });
1448
+ const { stream: _stream, thinking: _thinking, output_config: _outputConfig, tool_choice: _toolChoice, ...nonStreaming } = params;
1449
+ return sanitizeAnthropicToolPairs({
1450
+ ...nonStreaming,
1451
+ max_tokens: 0,
1452
+ });
1453
+ }
1441
1454
  function buildParams(model, context, isOAuthToken, options, unsignedThinkingReplay = getAnthropicCompat(model).unsignedThinkingReplay) {
1442
1455
  const compat = getAnthropicCompat(model);
1443
1456
  const { cacheControl } = getCacheControl(model, options?.cacheRetention ?? model.cacheRetention, options?.env);