@code-yeongyu/senpi-agent-core 2026.10.31-2 → 2026.11.2-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-loop.js +26 -5
- package/dist/agent.d.ts +7 -0
- package/dist/agent.js +6 -0
- package/dist/conformance/loop-core-manifest.d.ts +14 -0
- package/dist/conformance/loop-core-manifest.js +38 -0
- package/dist/execution/tool-host.d.ts +32 -0
- package/dist/execution/tool-host.js +2 -0
- package/dist/types.d.ts +6 -0
- package/package.json +3 -3
package/dist/agent-loop.js
CHANGED
|
@@ -99,7 +99,10 @@ export class StreamStartTimeoutError extends Error {
|
|
|
99
99
|
*/
|
|
100
100
|
async function runLoop(initialContext, newMessages, initialConfig, signal, emit, streamFunction) {
|
|
101
101
|
let currentContext = initialContext;
|
|
102
|
-
let config =
|
|
102
|
+
let config = {
|
|
103
|
+
...initialConfig,
|
|
104
|
+
executionScope: initialConfig.executionScope ?? { id: crypto.randomUUID(), root: "" },
|
|
105
|
+
};
|
|
103
106
|
let firstTurn = true;
|
|
104
107
|
let firstProviderRequest = true;
|
|
105
108
|
let drainedTerminatingQueue;
|
|
@@ -922,10 +925,28 @@ async function executeToolCalls(currentContext, assistantMessage, config, signal
|
|
|
922
925
|
// channel and its result buffered; running it again would duplicate a
|
|
923
926
|
// side-effecting tool.
|
|
924
927
|
const toolCalls = assistantMessage.content.filter((c) => c.type === "toolCall" && !isCursorExecResolved(c));
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
928
|
+
const host = config.toolHost ?? {
|
|
929
|
+
execute: async (call, options) => {
|
|
930
|
+
// Only yield when a witness sink is configured and returns a promise: an unconditional `await` adds a
|
|
931
|
+
// microtask per call and reorders lifecycle vs message events against today's fused path (EQ E03).
|
|
932
|
+
const recordWitness = options.recordWitness;
|
|
933
|
+
if (recordWitness) {
|
|
934
|
+
for (const raw of call.calls) {
|
|
935
|
+
const pending = recordWitness({ pipeline: "legacy", toolCallId: raw.id });
|
|
936
|
+
if (pending)
|
|
937
|
+
await pending;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
return call.mode === "sequential"
|
|
941
|
+
? executeToolCallsSequential(currentContext, assistantMessage, toolCalls, config, options.signal, options.emit)
|
|
942
|
+
: executeToolCallsParallel(currentContext, assistantMessage, toolCalls, config, options.signal, options.emit);
|
|
943
|
+
},
|
|
944
|
+
};
|
|
945
|
+
return host.execute({
|
|
946
|
+
scope: config.executionScope ?? { id: crypto.randomUUID(), root: "" },
|
|
947
|
+
calls: toolCalls,
|
|
948
|
+
mode: config.toolExecution ?? "parallel",
|
|
949
|
+
}, { signal, emit, recordWitness: config.onExecutionPipeline });
|
|
929
950
|
}
|
|
930
951
|
async function executeToolCallsSequential(currentContext, assistantMessage, toolCalls, config, signal, emit) {
|
|
931
952
|
const finalizedCalls = [];
|
package/dist/agent.d.ts
CHANGED
|
@@ -37,6 +37,9 @@ export interface AgentOptions {
|
|
|
37
37
|
streamKeepAliveTimeoutMs?: number;
|
|
38
38
|
maxRetryDelayMs?: number;
|
|
39
39
|
toolExecution?: ToolExecutionMode;
|
|
40
|
+
toolHost?: AgentLoopConfig["toolHost"];
|
|
41
|
+
executionScope?: AgentLoopConfig["executionScope"];
|
|
42
|
+
onExecutionPipeline?: AgentLoopConfig["onExecutionPipeline"];
|
|
40
43
|
removedToolHints?: Record<string, string>;
|
|
41
44
|
resolveUnknownToolCall?: AgentLoopConfig["resolveUnknownToolCall"];
|
|
42
45
|
abortServerSideFallback?: boolean;
|
|
@@ -113,6 +116,10 @@ export declare class Agent {
|
|
|
113
116
|
maxRetryDelayMs?: number;
|
|
114
117
|
/** Tool execution strategy for assistant messages that contain multiple tool calls. */
|
|
115
118
|
toolExecution: ToolExecutionMode;
|
|
119
|
+
toolHost?: AgentLoopConfig["toolHost"];
|
|
120
|
+
executionScope: NonNullable<AgentLoopConfig["executionScope"]>;
|
|
121
|
+
getExecutionScope?: () => NonNullable<AgentLoopConfig["executionScope"]>;
|
|
122
|
+
onExecutionPipeline?: AgentLoopConfig["onExecutionPipeline"];
|
|
116
123
|
/** Migration guidance returned when a removed tool name is called. */
|
|
117
124
|
removedToolHints: Record<string, string>;
|
|
118
125
|
/** Optional call-time resolver for tools absent from the request context. */
|
package/dist/agent.js
CHANGED
|
@@ -167,6 +167,9 @@ export class Agent {
|
|
|
167
167
|
this.streamKeepAliveTimeoutMs = runtimeOptions.streamKeepAliveTimeoutMs;
|
|
168
168
|
this.maxRetryDelayMs = runtimeOptions.maxRetryDelayMs;
|
|
169
169
|
this.toolExecution = runtimeOptions.toolExecution ?? "parallel";
|
|
170
|
+
this.toolHost = runtimeOptions.toolHost;
|
|
171
|
+
this.executionScope = runtimeOptions.executionScope ?? { id: crypto.randomUUID(), root: "" };
|
|
172
|
+
this.onExecutionPipeline = runtimeOptions.onExecutionPipeline;
|
|
170
173
|
this.removedToolHints = runtimeOptions.removedToolHints ?? {};
|
|
171
174
|
this.resolveUnknownToolCall = runtimeOptions.resolveUnknownToolCall;
|
|
172
175
|
this.abortServerSideFallback = runtimeOptions.abortServerSideFallback;
|
|
@@ -485,6 +488,9 @@ export class Agent {
|
|
|
485
488
|
abortServerSideFallback: this.abortServerSideFallback,
|
|
486
489
|
cursorExecHandlers: this.cursorExecHandlers,
|
|
487
490
|
toolExecution: this.toolExecution,
|
|
491
|
+
toolHost: this.toolHost,
|
|
492
|
+
executionScope: this.getExecutionScope?.() ?? this.executionScope,
|
|
493
|
+
onExecutionPipeline: this.onExecutionPipeline,
|
|
488
494
|
removedToolHints: this.removedToolHints,
|
|
489
495
|
resolveUnknownToolCall: this.resolveUnknownToolCall,
|
|
490
496
|
beforeToolCall: this.beforeToolCall,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S06: the host-independent loop boundary, relative to packages/agent/src.
|
|
3
|
+
* Every local dependency (including type imports) must also be listed and checked.
|
|
4
|
+
* Host adapters, carriers, persistence implementations and the Node harness stay outside.
|
|
5
|
+
* Extend this closure when adding loop modules; never add a host implementation to it.
|
|
6
|
+
*/
|
|
7
|
+
export declare const LOOP_CORE_MODULES: readonly ["agent.ts", "agent-loop.ts", "assistant-terminal-state.ts", "empty-assistant-recovery.ts", "internal-events.ts", "local-failure.ts", "response-timing.ts", "stream-fn.ts", "stream-keepalive.ts", "tool-arguments.ts", "tool-name-alias.ts", "types.ts", "durable/store.ts", "execution/tool-host.ts", "durable/store-admission.ts", "durable/wake-host.ts", "transport/transport.ts"];
|
|
8
|
+
/**
|
|
9
|
+
* Existing portable model/message utilities and schema types, not provider runtimes.
|
|
10
|
+
* Exact specifiers deliberately exclude compat, OAuth, SDKs and host-specific subpaths.
|
|
11
|
+
* The model implementation enters the loop through StreamFn, not a provider import.
|
|
12
|
+
*/
|
|
13
|
+
export declare const LOOP_CORE_EXTERNAL_MODULES: readonly ["@earendil-works/pi-ai", "@earendil-works/pi-ai/utils/oauth-refresh-error", "@earendil-works/pi-ai/utils/retry", "@earendil-works/pi-ai/utils/tool-name-match", "typebox"];
|
|
14
|
+
//# sourceMappingURL=loop-core-manifest.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S06: the host-independent loop boundary, relative to packages/agent/src.
|
|
3
|
+
* Every local dependency (including type imports) must also be listed and checked.
|
|
4
|
+
* Host adapters, carriers, persistence implementations and the Node harness stay outside.
|
|
5
|
+
* Extend this closure when adding loop modules; never add a host implementation to it.
|
|
6
|
+
*/
|
|
7
|
+
export const LOOP_CORE_MODULES = [
|
|
8
|
+
"agent.ts",
|
|
9
|
+
"agent-loop.ts",
|
|
10
|
+
"assistant-terminal-state.ts",
|
|
11
|
+
"empty-assistant-recovery.ts",
|
|
12
|
+
"internal-events.ts",
|
|
13
|
+
"local-failure.ts",
|
|
14
|
+
"response-timing.ts",
|
|
15
|
+
"stream-fn.ts",
|
|
16
|
+
"stream-keepalive.ts",
|
|
17
|
+
"tool-arguments.ts",
|
|
18
|
+
"tool-name-alias.ts",
|
|
19
|
+
"types.ts",
|
|
20
|
+
"durable/store.ts",
|
|
21
|
+
"execution/tool-host.ts",
|
|
22
|
+
"durable/store-admission.ts",
|
|
23
|
+
"durable/wake-host.ts",
|
|
24
|
+
"transport/transport.ts",
|
|
25
|
+
];
|
|
26
|
+
/**
|
|
27
|
+
* Existing portable model/message utilities and schema types, not provider runtimes.
|
|
28
|
+
* Exact specifiers deliberately exclude compat, OAuth, SDKs and host-specific subpaths.
|
|
29
|
+
* The model implementation enters the loop through StreamFn, not a provider import.
|
|
30
|
+
*/
|
|
31
|
+
export const LOOP_CORE_EXTERNAL_MODULES = [
|
|
32
|
+
"@earendil-works/pi-ai",
|
|
33
|
+
"@earendil-works/pi-ai/utils/oauth-refresh-error",
|
|
34
|
+
"@earendil-works/pi-ai/utils/retry",
|
|
35
|
+
"@earendil-works/pi-ai/utils/tool-name-match",
|
|
36
|
+
"typebox",
|
|
37
|
+
];
|
|
38
|
+
//# sourceMappingURL=loop-core-manifest.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ToolResultMessage } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { AgentEvent, AgentToolCall, ToolExecutionMode } from "../types.ts";
|
|
3
|
+
/** Brain-minted identity and execution root; the executor treats the identity as opaque. */
|
|
4
|
+
export interface ExecutionScope {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly root: string;
|
|
7
|
+
}
|
|
8
|
+
/** The raw model calls, before name resolution, argument preparation, validation or hooks. */
|
|
9
|
+
export interface ToolHostCall {
|
|
10
|
+
readonly scope: ExecutionScope;
|
|
11
|
+
readonly calls: readonly AgentToolCall[];
|
|
12
|
+
readonly mode: ToolExecutionMode;
|
|
13
|
+
}
|
|
14
|
+
export interface ExecutionPipelineWitness {
|
|
15
|
+
readonly pipeline: "legacy" | "prepared";
|
|
16
|
+
readonly toolCallId: string;
|
|
17
|
+
}
|
|
18
|
+
export interface ToolHostResult {
|
|
19
|
+
readonly messages: ToolResultMessage[];
|
|
20
|
+
readonly terminate: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface ToolHostOptions {
|
|
23
|
+
readonly signal?: AbortSignal;
|
|
24
|
+
/** Preserve tool events and ordered result-message events. The loop appends the returned messages to context. */
|
|
25
|
+
readonly emit: (event: AgentEvent) => Promise<void> | void;
|
|
26
|
+
/** Receipt metadata only: never an AgentEvent, tool result, provider payload or EQ observable. */
|
|
27
|
+
readonly recordWitness?: (witness: ExecutionPipelineWitness) => Promise<void> | void;
|
|
28
|
+
}
|
|
29
|
+
export interface ToolHost {
|
|
30
|
+
execute(call: ToolHostCall, options: ToolHostOptions): Promise<ToolHostResult>;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=tool-host.d.ts.map
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Api, AssistantMessage, AssistantMessageEvent, AssistantMessageEventStream, CursorExecHandlers, ImageContent, JsonValue, Message, Model, ProviderDiagnostic, SimpleStreamOptions, TextContent, ThinkingSelection, Tool, ToolResultMessage, TranscriptContext, Usage } from "@earendil-works/pi-ai";
|
|
2
2
|
import type { Static, TSchema } from "typebox";
|
|
3
|
+
import type { ExecutionPipelineWitness, ExecutionScope, ToolHost } from "./execution/tool-host.ts";
|
|
4
|
+
export type { ExecutionPipelineWitness, ExecutionScope, ToolHost, ToolHostCall, ToolHostOptions, ToolHostResult, } from "./execution/tool-host.ts";
|
|
3
5
|
/**
|
|
4
6
|
* Stream function used by the agent loop. `Models.streamSimple` satisfies
|
|
5
7
|
* this shape.
|
|
@@ -322,6 +324,10 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
322
324
|
* Default: "parallel"
|
|
323
325
|
*/
|
|
324
326
|
toolExecution?: ToolExecutionMode;
|
|
327
|
+
/** Unprepared batch boundary. Omitted selects the existing in-process legacy scheduler. */
|
|
328
|
+
toolHost?: ToolHost;
|
|
329
|
+
executionScope?: ExecutionScope;
|
|
330
|
+
onExecutionPipeline?: (witness: ExecutionPipelineWitness) => Promise<void> | void;
|
|
325
331
|
/**
|
|
326
332
|
* Optional migration guidance for tool names intentionally removed by an extension.
|
|
327
333
|
* The matching hint is appended to the normal unknown-tool error before hooks run.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi-agent-core",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.11.02-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",
|
|
@@ -70,8 +70,8 @@
|
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"@earendil-works/chord": "0.99.1",
|
|
73
|
-
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.
|
|
74
|
-
"@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.
|
|
73
|
+
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.11.02-1",
|
|
74
|
+
"@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.11.02-1",
|
|
75
75
|
"diff": "9.0.0",
|
|
76
76
|
"ignore": "7.0.9",
|
|
77
77
|
"typebox": "1.3.34",
|