@code-yeongyu/senpi-agent-core 2026.10.24-2 → 2026.10.24-4

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.
@@ -3,7 +3,7 @@
3
3
  * Transforms to Message[] only at the LLM call boundary.
4
4
  */
5
5
  import { createInitialSystemMessage, EventStream, getCurrentTools, getToolStateChanges, isCursorExecResolved, normalizeContext, supportsAllowedToolChoice, validateToolArguments, } from "@earendil-works/pi-ai";
6
- import { createTerminalFailureAssistantMessage, demoteToolUseWithoutToolCalls, isStreamIdleTimeoutError, normalizeTerminalAssistantMessage, promoteStopWithPendingToolCalls, shouldFinalizeIdleAsStop, shouldTerminateAssistantTurn, } from "./assistant-terminal-state.js";
6
+ import { createTerminalFailureAssistantMessage, isStreamIdleTimeoutError, normalizeTerminalAssistantMessage, settleStopReason, shouldFinalizeIdleAsStop, shouldTerminateAssistantTurn, } from "./assistant-terminal-state.js";
7
7
  import { createResponseTimingRecorder } from "./response-timing.js";
8
8
  import { getDefaultStreamFn, withEmptyAssistantRecovery } from "./stream-fn.js";
9
9
  import { prepareAgentToolCallArguments } from "./tool-arguments.js";
@@ -191,7 +191,7 @@ async function runLoop(initialContext, newMessages, initialConfig, signal, emit,
191
191
  }
192
192
  : config;
193
193
  const streamed = await streamAssistantResponse(currentContext, requestConfig, signal, emit, withEmptyAssistantRecovery(requestConfig.model, streamFunction), isInitialProviderRequest ? config.timeoutMs : requestConfig.timeoutMs);
194
- const message = demoteToolUseWithoutToolCalls(promoteStopWithPendingToolCalls(streamed.message));
194
+ const message = streamed.message;
195
195
  const providerToolResults = streamed.providerToolResults;
196
196
  newMessages.push(message);
197
197
  const toolResults = [];
@@ -568,7 +568,7 @@ async function streamAssistantResponse(context, config, signal, emit, streamFunc
568
568
  break;
569
569
  case "done":
570
570
  case "error": {
571
- const finalMessage = normalizeTerminalAssistantMessage(await result(), event);
571
+ const finalMessage = settleStopReason(normalizeTerminalAssistantMessage(await result(), event));
572
572
  stampTiming(finalMessage);
573
573
  if (addedPartial) {
574
574
  context.messages[context.messages.length - 1] = finalMessage;
@@ -591,7 +591,7 @@ async function streamAssistantResponse(context, config, signal, emit, streamFunc
591
591
  finally {
592
592
  eventReader.dispose();
593
593
  }
594
- const finalMessage = await result();
594
+ const finalMessage = settleStopReason(await result());
595
595
  stampTiming(finalMessage);
596
596
  if (addedPartial) {
597
597
  context.messages[context.messages.length - 1] = finalMessage;
@@ -605,7 +605,7 @@ async function streamAssistantResponse(context, config, signal, emit, streamFunc
605
605
  }
606
606
  catch (error) {
607
607
  if (isStreamIdleTimeoutError(error) && shouldFinalizeIdleAsStop(partialMessage, providerToolResults)) {
608
- const finalMessage = {
608
+ const finalMessage = settleStopReason({
609
609
  role: "assistant",
610
610
  content: partialMessage?.content ?? [{ type: "text", text: "" }],
611
611
  api: partialMessage?.api ?? config.model.api,
@@ -624,7 +624,7 @@ async function streamAssistantResponse(context, config, signal, emit, streamFunc
624
624
  },
625
625
  stopReason: "stop",
626
626
  timestamp: partialMessage?.timestamp ?? Date.now(),
627
- };
627
+ });
628
628
  stampTiming(finalMessage);
629
629
  if (addedPartial) {
630
630
  context.messages[context.messages.length - 1] = finalMessage;
@@ -15,6 +15,8 @@ export declare class ProviderRetryWatchdogAbortError extends Error {
15
15
  export declare const EMPTY_TOOL_USE_DEMOTION_DIAGNOSTIC = "empty_tool_use_terminal_state";
16
16
  export declare function demoteToolUseWithoutToolCalls(message: AssistantMessage): AssistantMessage;
17
17
  export declare function promoteStopWithPendingToolCalls(message: AssistantMessage): AssistantMessage;
18
+ /** The stop reason the loop acts on; applied before `message_end` so every consumer sees the same object. */
19
+ export declare function settleStopReason(message: AssistantMessage): AssistantMessage;
18
20
  export declare function shouldTerminateAssistantTurn(message: AssistantMessage): boolean;
19
21
  export declare function isStreamIdleTimeoutError(error: unknown): boolean;
20
22
  /**
@@ -42,6 +42,10 @@ export function promoteStopWithPendingToolCalls(message) {
42
42
  return message;
43
43
  return { ...message, stopReason: "toolUse" };
44
44
  }
45
+ /** The stop reason the loop acts on; applied before `message_end` so every consumer sees the same object. */
46
+ export function settleStopReason(message) {
47
+ return demoteToolUseWithoutToolCalls(promoteStopWithPendingToolCalls(message));
48
+ }
45
49
  export function shouldTerminateAssistantTurn(message) {
46
50
  return message.stopReason === "error" || message.stopReason === "aborted" || isClassifierRefusal(message);
47
51
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi-agent-core",
3
- "version": "2026.10.24-2",
3
+ "version": "2026.10.24-4",
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.24-2",
66
- "@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.10.24-2",
65
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.24-4",
66
+ "@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.10.24-4",
67
67
  "diff": "9.0.0",
68
68
  "ignore": "7.0.9",
69
69
  "typebox": "1.3.34",