@code-yeongyu/senpi-agent-core 2026.10.19 → 2026.10.21-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.
package/dist/agent.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { readProviderDiagnostic, sanitizeProviderDiagnostic, } from "@earendil-works/pi-ai";
2
2
  import { buildProviderContext as buildProviderContextFromAgentContext, runAgentLoop, runAgentLoopContinue, } from "./agent-loop.js";
3
3
  import { ProviderRetryWatchdogAbortError } from "./assistant-terminal-state.js";
4
+ import { isLocalFailure, localFailurePort } from "./local-failure.js";
4
5
  import { getDefaultStreamFn } from "./stream-fn.js";
5
6
  function defaultConvertToLlm(messages) {
6
7
  return messages.filter((message) => message.role === "system" ||
@@ -396,7 +397,7 @@ export class Agent {
396
397
  */
397
398
  async takeQueuedMessages(queue) {
398
399
  const pendingQueue = queue === "steering" ? this.steeringQueue : this.followUpQueue;
399
- const port = this.queuedInputPort;
400
+ const port = this.queuedInputPort && localFailurePort(this.queuedInputPort);
400
401
  if (!port)
401
402
  return pendingQueue.drain();
402
403
  let generation = pendingQueue.getClearGeneration();
@@ -451,7 +452,7 @@ export class Agent {
451
452
  let steeringQueueGeneration = this.steeringQueue.getClearGeneration();
452
453
  let followUpQueueGeneration = this.followUpQueue.getClearGeneration();
453
454
  const finishTurn = this.finishTurn;
454
- const queuedInputPort = this.queuedInputPort;
455
+ const queuedInputPort = this.queuedInputPort && localFailurePort(this.queuedInputPort);
455
456
  const giveBack = (queue, messages) => this.giveBack(queue, messages, queue === "steering" ? steeringQueueGeneration : followUpQueueGeneration);
456
457
  const reasoning = this._state.reasoningBaseline ?? this._state.thinkingLevel;
457
458
  return {
@@ -590,6 +591,7 @@ export class Agent {
590
591
  errorMessage: error instanceof Error ? error.message : String(error),
591
592
  ...(providerDiagnostic === undefined ? {} : { providerDiagnostic }),
592
593
  ...(error instanceof ProviderRetryWatchdogAbortError ? { abortSource: "provider" } : {}),
594
+ ...(!aborted && isLocalFailure(error) ? { failureSource: "local" } : {}),
593
595
  timestamp: Date.now(),
594
596
  };
595
597
  await this.processEvents({ type: "message_start", message: failureMessage });
@@ -12,7 +12,11 @@ export function bashTool(output = {}) {
12
12
  async execute({ command, cwd }, api, ctx) {
13
13
  const started = Date.now();
14
14
  const child = spawn("bash", ["-c", command], { cwd, stdio: ["ignore", "pipe", "pipe"] });
15
- const onAbort = () => child.kill("SIGKILL");
15
+ // A child that never started has no pid; on Node < 24.22 / 26.10 its kill() signals our own process group.
16
+ const onAbort = () => {
17
+ if (child.pid !== undefined)
18
+ child.kill("SIGKILL");
19
+ };
16
20
  ctx.abortSignal?.addEventListener("abort", onAbort);
17
21
  child.stdout.on("data", (c) => api.stream(c));
18
22
  child.stderr.on("data", (c) => api.stream(c));
@@ -0,0 +1,4 @@
1
+ import type { QueuedInputPort } from "./agent.ts";
2
+ export declare function isLocalFailure(error: unknown): boolean;
3
+ export declare function localFailurePort(port: QueuedInputPort): QueuedInputPort;
4
+ //# sourceMappingURL=local-failure.d.ts.map
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Errors raised by the owner's queued-input port (claim, release, commit) inside a run. That code is local
3
+ * bookkeeping, never a provider request, so the run's failure message is marked `failureSource: "local"` (senpi#3866).
4
+ */
5
+ const localFailures = new WeakSet();
6
+ export function isLocalFailure(error) {
7
+ return typeof error === "object" && error !== null && localFailures.has(error);
8
+ }
9
+ async function local(work) {
10
+ try {
11
+ return await work();
12
+ }
13
+ catch (error) {
14
+ if (typeof error === "object" && error !== null)
15
+ localFailures.add(error);
16
+ throw error;
17
+ }
18
+ }
19
+ export function localFailurePort(port) {
20
+ return {
21
+ claim: (queue, drain) => local(() => port.claim(queue, drain)),
22
+ release: (queue, messages, giveBack) => local(() => port.release(queue, messages, giveBack)),
23
+ commitConsumption: (queue, messages) => local(() => port.commitConsumption(queue, messages)),
24
+ };
25
+ }
26
+ //# sourceMappingURL=local-failure.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi-agent-core",
3
- "version": "2026.10.19",
3
+ "version": "2026.10.21-1",
4
4
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -62,8 +62,8 @@
62
62
  },
63
63
  "dependencies": {
64
64
  "@earendil-works/chord": "0.99.1",
65
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.19",
66
- "@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.10.19",
65
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.21-1",
66
+ "@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.10.21-1",
67
67
  "diff": "9.0.0",
68
68
  "ignore": "7.0.9",
69
69
  "typebox": "1.3.34",