@noetic-tools/core 5.3.0 → 5.4.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/README.md +8 -6
- package/dist/adapters/in-memory-subprocess/step-completion.js +1 -1
- package/dist/adapters/openrouter.d.ts +12 -1
- package/dist/adapters/openrouter.js +79 -5
- package/dist/builders/acp-agent-tool.d.ts +67 -0
- package/dist/builders/acp-agent-tool.js +87 -0
- package/dist/builders/control-flow-builders.d.ts +13 -13
- package/dist/builders/control-flow-builders.js +11 -11
- package/dist/{patterns → builders}/dynamic-workflow.d.ts +1 -1
- package/dist/{patterns → builders}/dynamic-workflow.js +20 -14
- package/dist/builders/every.d.ts +14 -14
- package/dist/builders/every.js +16 -16
- package/dist/builders/layer-provides-builders.d.ts +1 -1
- package/dist/builders/layer-provides-builders.js +1 -1
- package/dist/builders/provide-builder.d.ts +5 -12
- package/dist/builders/provide-builder.js +9 -10
- package/dist/builders/spawn-builder.d.ts +0 -2
- package/dist/builders/spawn-builder.js +2 -3
- package/dist/builders/step-builders.d.ts +103 -92
- package/dist/builders/step-builders.js +146 -163
- package/dist/builders/tool-builder.d.ts +18 -22
- package/dist/builders/tool-builder.js +6 -4
- package/dist/builders/workflow-hydrator.d.ts +11 -7
- package/dist/builders/workflow-hydrator.js +75 -87
- package/dist/builders/workflow-step.d.ts +15 -27
- package/dist/builders/workflow-step.js +14 -19
- package/dist/conditions/conditions.d.ts +2 -2
- package/dist/conditions/conditions.js +1 -1
- package/dist/harness/agent-harness.d.ts +89 -46
- package/dist/harness/agent-harness.js +93 -39
- package/dist/harness/deps/types.d.ts +1 -1
- package/dist/harness/model-call.d.ts +1 -1
- package/dist/harness/model-call.js +23 -18
- package/dist/index.d.ts +27 -35
- package/dist/index.js +13 -29
- package/dist/interpreter/acp-events.d.ts +67 -0
- package/dist/interpreter/acp-events.js +256 -0
- package/dist/interpreter/acp-history.d.ts +22 -0
- package/dist/interpreter/acp-history.js +58 -0
- package/dist/interpreter/action-types.d.ts +1 -1
- package/dist/interpreter/collect-tools.js +10 -13
- package/dist/interpreter/execute-acp-agent.d.ts +10 -0
- package/dist/interpreter/execute-acp-agent.js +505 -0
- package/dist/interpreter/execute-action.d.ts +5 -5
- package/dist/interpreter/execute-action.js +284 -218
- package/dist/interpreter/execute-control.d.ts +9 -9
- package/dist/interpreter/execute-control.js +27 -27
- package/dist/interpreter/execute.d.ts +1 -1
- package/dist/interpreter/execute.js +29 -35
- package/dist/interpreter/step-dispatcher.d.ts +1 -1
- package/dist/interpreter/step-dispatcher.js +3 -3
- package/dist/interpreter/structured-output.d.ts +14 -0
- package/dist/interpreter/structured-output.js +40 -0
- package/dist/interpreter/typeguards.d.ts +1 -1
- package/dist/interpreter/typeguards.js +1 -1
- package/dist/portable/builders.d.ts +5 -3
- package/dist/portable/builders.js +8 -5
- package/dist/portable/context-layers.d.ts +1 -1
- package/dist/portable/context-layers.js +1 -1
- package/dist/runtime/acp-session-store.d.ts +75 -0
- package/dist/runtime/acp-session-store.js +0 -0
- package/dist/runtime/broadcaster-utils.d.ts +1 -1
- package/dist/runtime/broadcaster-utils.js +1 -1
- package/dist/runtime/context-impl.d.ts +8 -10
- package/dist/runtime/context-impl.js +8 -12
- package/dist/runtime/durable/step-ledger.d.ts +2 -2
- package/dist/runtime/durable/step-ledger.js +3 -3
- package/dist/runtime/session-runner.d.ts +12 -1
- package/dist/runtime/session-runner.js +33 -1
- package/dist/runtime/tool-context.js +0 -2
- package/dist/schemas/workflow.d.ts +3 -3
- package/dist/schemas/workflow.js +2 -2
- package/dist/types/checkpoint.d.ts +11 -2
- package/dist/types/checkpoint.js +10 -1
- package/dist/{runtime → util}/detached-handle.js +1 -1
- package/package.json +6 -4
- package/schema/noetic-workflow.schema.json +239 -260
- package/dist/builders/context-option.d.ts +0 -23
- package/dist/builders/context-option.js +0 -22
- package/dist/deprecated.d.ts +0 -30
- package/dist/deprecated.js +0 -28
- package/dist/interpreter/execute-sub-harness.d.ts +0 -8
- package/dist/interpreter/execute-sub-harness.js +0 -241
- package/dist/interpreter/sub-harness-events.d.ts +0 -48
- package/dist/interpreter/sub-harness-events.js +0 -208
- package/dist/patterns/interview.d.ts +0 -53
- package/dist/patterns/interview.js +0 -113
- package/dist/patterns/plans.d.ts +0 -44
- package/dist/patterns/plans.js +0 -125
- package/dist/patterns/ralph-wiggum.d.ts +0 -19
- package/dist/patterns/ralph-wiggum.js +0 -37
- package/dist/patterns/react.d.ts +0 -19
- package/dist/patterns/react.js +0 -41
- /package/dist/{runtime → util}/detached-handle.d.ts +0 -0
|
@@ -1,33 +1,21 @@
|
|
|
1
1
|
import { ItemSchemaRegistry } from '@noetic-tools/types';
|
|
2
|
+
import { AcpSessionStore } from '../runtime/acp-session-store.js';
|
|
2
3
|
import type { LayerStateStore, RecallCache } from './deps/context.js';
|
|
3
4
|
import type { DetachedSpawnOverrides } from './deps/interpreter.js';
|
|
4
5
|
import type { CheckpointStore, EventBroadcaster, RestoreCheckpointOptions, StepLedgerRetention, StepLedgerStore } from './deps/runtime.js';
|
|
5
|
-
import type { AgentConfig, AgentHarnessContract, AgentHooks, CallModelRequest, Channel, ChannelHandle, Context, ContextCacheConfig, ContextCacheStore, ContextData, ContextLayer, CwdState, DeliveryMode, DetachedHandle, ExecuteInput, ExecuteOptions, ExternalChannel, FsAdapter, HarnessResponse, HarnessStatus, Item,
|
|
6
|
+
import type { AcpLiveSession, AcpSessionInfo, AgentConfig, AgentHarnessContract, AgentHooks, CallModelRequest, Channel, ChannelHandle, Context, ContextCacheConfig, ContextCacheStore, ContextData, ContextLayer, CwdState, DeliveryMode, DetachedHandle, ExecuteInput, ExecuteOptions, ExternalChannel, FsAdapter, HarnessResponse, HarnessStatus, Item, ItemSchemaConfig, LLMResponse, LlmProviderConfig, ProjectionPolicy, RecallLayerOutput, SessionScope, SessionUsage, ShellAdapter, Span, SteeringDecision, Step, StorageAdapter, StreamEvent, StreamingItem, SubprocessAdapter, Tool, TraceExporter, ZodType } from './deps/types.js';
|
|
6
7
|
export { createStreamIdleWatchdog } from './model-call.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
memory?: ContextLayer[];
|
|
14
|
-
storage?: StorageAdapter;
|
|
15
|
-
hooks?: AgentHooks;
|
|
8
|
+
/**
|
|
9
|
+
* Storage-scoped environment configuration for `AgentHarness`.
|
|
10
|
+
*
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
export interface StorageEnvironmentConfig {
|
|
16
14
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* Use this when the workflow step tree is static and tools are supplied
|
|
20
|
-
* per harness instance rather than baked into individual `step.llm` calls.
|
|
15
|
+
* Key-value storage adapter backing context-layer persistence and the
|
|
16
|
+
* step-completion ledger. Defaults to an in-memory adapter.
|
|
21
17
|
*/
|
|
22
|
-
|
|
23
|
-
params: TParams;
|
|
24
|
-
paramsSchema?: ZodType<TParams>;
|
|
25
|
-
/** Filesystem adapter. Defaults to local node:fs when not provided. */
|
|
26
|
-
fs?: FsAdapter;
|
|
27
|
-
/** Shell adapter. Defaults to local sh when not provided. */
|
|
28
|
-
shell?: ShellAdapter;
|
|
29
|
-
/** Subprocess adapter. Defaults to an in-memory, same-process adapter. */
|
|
30
|
-
subprocess?: SubprocessAdapter;
|
|
18
|
+
adapter?: StorageAdapter;
|
|
31
19
|
/**
|
|
32
20
|
* Checkpoint store used by `harness.checkpoint(ctx)` / `harness.restore()`.
|
|
33
21
|
* When absent, checkpoint/restore are no-ops — a zero-config harness keeps
|
|
@@ -35,6 +23,8 @@ interface AgentHarnessOpts<TParams extends Record<string, unknown> = Record<stri
|
|
|
35
23
|
* to enable durable execution.
|
|
36
24
|
*/
|
|
37
25
|
checkpointStore?: CheckpointStore;
|
|
26
|
+
/** Layer-state store override. Defaults to an in-memory store. */
|
|
27
|
+
layerStateStore?: LayerStateStore;
|
|
38
28
|
/**
|
|
39
29
|
* Bounds on the step-completion ledger that backs step-level resume. Only meaningful
|
|
40
30
|
* alongside a `checkpointStore`. Defaults to `DEFAULT_STEP_LEDGER_RETENTION` — 128 KiB
|
|
@@ -43,11 +33,45 @@ interface AgentHarnessOpts<TParams extends Record<string, unknown> = Record<stri
|
|
|
43
33
|
* never correctness.
|
|
44
34
|
*/
|
|
45
35
|
stepLedgerRetention?: StepLedgerRetention;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Execution-environment configuration for `AgentHarness`: the storage,
|
|
39
|
+
* filesystem, shell, and subprocess surfaces the agent runs against.
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
export interface AgentEnvironmentConfig {
|
|
44
|
+
/** Storage configuration: adapter, durability stores, ledger retention. */
|
|
45
|
+
storage?: StorageEnvironmentConfig;
|
|
46
|
+
/** Filesystem adapter. Defaults to an in-memory adapter. */
|
|
47
|
+
fs?: FsAdapter;
|
|
48
|
+
/** Shell adapter. Defaults to an in-memory adapter. */
|
|
49
|
+
shell?: ShellAdapter;
|
|
50
|
+
/** Subprocess adapter. Defaults to an in-memory, same-process adapter. */
|
|
51
|
+
subprocess?: SubprocessAdapter;
|
|
52
|
+
}
|
|
53
|
+
interface AgentHarnessOpts<TParams extends Record<string, unknown> = Record<string, unknown>> {
|
|
54
|
+
name: string;
|
|
55
|
+
/** Root step tree executed for every turn submitted via `execute()`. */
|
|
56
|
+
agentGraph?: Step<ContextData, string, string>;
|
|
57
|
+
/** Default context layers applied to every context created via `createContext()` / `execute()`. */
|
|
58
|
+
contextLayers?: ContextLayer[];
|
|
59
|
+
hooks?: AgentHooks;
|
|
60
|
+
/**
|
|
61
|
+
* Harness-wide tool pool. Merged (identity-deduplicated) with tools
|
|
62
|
+
* collected from `agentGraph` to form every context's `unifiedTools`.
|
|
63
|
+
* Use this when the workflow step tree is static and tools are supplied
|
|
64
|
+
* per harness instance rather than baked into individual `step.callModel` calls.
|
|
65
|
+
*/
|
|
66
|
+
tools?: Tool[];
|
|
67
|
+
params: TParams;
|
|
68
|
+
paramsSchema?: ZodType<TParams>;
|
|
69
|
+
/** Execution environment: storage, filesystem, shell, subprocess. */
|
|
70
|
+
environment?: AgentEnvironmentConfig;
|
|
71
|
+
/** Default provider configuration for model calls made through this harness. */
|
|
72
|
+
callModelDefaults?: LlmProviderConfig;
|
|
73
|
+
/** Harness-wide item schema extensions and validation strictness. */
|
|
74
|
+
itemSchemas?: ItemSchemaConfig;
|
|
51
75
|
/** Default projection policy for all LLM steps. Individual steps override via `step.projection`. */
|
|
52
76
|
projection?: ProjectionPolicy;
|
|
53
77
|
/** When true, every layer is recalled atomically regardless of its `recallMode`. */
|
|
@@ -55,15 +79,8 @@ interface AgentHarnessOpts<TParams extends Record<string, unknown> = Record<stri
|
|
|
55
79
|
/** Tuning for prompt-cache anchoring. See `ContextCacheConfig` for the defaults. */
|
|
56
80
|
contextCache?: ContextCacheConfig;
|
|
57
81
|
traceExporter?: TraceExporter;
|
|
58
|
-
layerStateStore?: LayerStateStore;
|
|
59
82
|
/** Default delivery mode for messages that don't specify one. Defaults to `next-turn`. */
|
|
60
83
|
defaultDeliveryMode?: DeliveryMode;
|
|
61
|
-
/**
|
|
62
|
-
* Abort the in-flight model call if the provider stream emits no events for this
|
|
63
|
-
* many milliseconds. Defaults to `DEFAULT_STREAM_IDLE_TIMEOUT_MS` (120s).
|
|
64
|
-
* Pass `0` or a negative number to disable the watchdog.
|
|
65
|
-
*/
|
|
66
|
-
streamIdleTimeoutMs?: number;
|
|
67
84
|
/**
|
|
68
85
|
* Initial working directory for the harness. Used as the seed value for the
|
|
69
86
|
* shared `cwdState.cwd` on every Context this harness creates, including
|
|
@@ -129,7 +146,7 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
129
146
|
* @internal
|
|
130
147
|
*/
|
|
131
148
|
readonly stepLedgerRetention: Required<StepLedgerRetention>;
|
|
132
|
-
private readonly
|
|
149
|
+
private readonly agentGraph?;
|
|
133
150
|
/** Harness-wide tool pool merged into every context's `unifiedTools`. */
|
|
134
151
|
private readonly harnessTools;
|
|
135
152
|
/** @internal Context layers configured for this harness. Exposed non-private
|
|
@@ -142,15 +159,42 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
142
159
|
private readonly rootRunDepth;
|
|
143
160
|
private readonly callModelOverride?;
|
|
144
161
|
private readonly defaultDeliveryMode;
|
|
145
|
-
private readonly streamIdleTimeoutMs;
|
|
146
162
|
private readonly sessions;
|
|
147
163
|
/**
|
|
148
|
-
* @internal
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
|
|
164
|
+
* @internal Live ACP connections. Reached by the interpreter via
|
|
165
|
+
* `frameworkCast`; use {@link listAcpSessions}, {@link getAcpSession},
|
|
166
|
+
* {@link cancelAcpSession}, and {@link closeAcpSessions} from outside core.
|
|
167
|
+
*/
|
|
168
|
+
readonly acpSessions: AcpSessionStore;
|
|
169
|
+
/**
|
|
170
|
+
* Snapshot of every live ACP sub-agent, for a UI that wants to show what is
|
|
171
|
+
* running and let a user act on it. Read-only: a session's turns are driven
|
|
172
|
+
* by steps, so nothing here can start work behind the runtime's back.
|
|
173
|
+
* @public
|
|
174
|
+
*/
|
|
175
|
+
listAcpSessions(): AcpSessionInfo[];
|
|
176
|
+
/**
|
|
177
|
+
* The live connection + session behind a handle from {@link listAcpSessions}
|
|
178
|
+
* (or a step's `session.reuse` key), or `undefined` when nothing matches.
|
|
179
|
+
* @public
|
|
180
|
+
*/
|
|
181
|
+
getAcpSession(key: string): AcpLiveSession | undefined;
|
|
182
|
+
/**
|
|
183
|
+
* Interrupt whatever the sub-agent under `key` is doing, via `session/cancel`.
|
|
184
|
+
* The connection stays open and the turn still returns — with the `cancelled`
|
|
185
|
+
* stop reason, which the running step surfaces as a `cancelled` error.
|
|
186
|
+
* Returns false when nothing matches the key.
|
|
187
|
+
* @public
|
|
188
|
+
*/
|
|
189
|
+
cancelAcpSession(key: string): Promise<boolean>;
|
|
190
|
+
/**
|
|
191
|
+
* Close every ACP connection this harness still holds, whatever its
|
|
192
|
+
* keep-alive scope. A connection owns a live agent — usually a child process
|
|
193
|
+
* whose stdio keeps the event loop alive — so a `keepAlive: 'harness'`
|
|
194
|
+
* session must be closed by its owner or the host will not exit. Idempotent.
|
|
195
|
+
* @public
|
|
152
196
|
*/
|
|
153
|
-
|
|
197
|
+
closeAcpSessions(): Promise<void>;
|
|
154
198
|
readonly layerStateStore: LayerStateStore;
|
|
155
199
|
/** Per-harness memoization cache for `recallMode: 'eventual'` layers. */
|
|
156
200
|
readonly recallCache: RecallCache;
|
|
@@ -193,6 +237,7 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
193
237
|
}): Promise<void>;
|
|
194
238
|
getStatus(scope?: SessionScope): HarnessStatus;
|
|
195
239
|
getQueueSize(scope?: SessionScope): number;
|
|
240
|
+
getUsage(scope?: SessionScope): SessionUsage;
|
|
196
241
|
seedSessionHistory(threadId: string, items: ReadonlyArray<Item>): void;
|
|
197
242
|
private getOrCreateSession;
|
|
198
243
|
private requireSession;
|
|
@@ -225,9 +270,7 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
225
270
|
state?: unknown;
|
|
226
271
|
threadId?: string;
|
|
227
272
|
resourceId?: string;
|
|
228
|
-
|
|
229
|
-
/** @deprecated Renamed to `context`. */
|
|
230
|
-
memory?: ContextLayer[];
|
|
273
|
+
contextLayers?: ContextLayer[];
|
|
231
274
|
/**
|
|
232
275
|
* Initial cwd for the new context. When set, takes precedence over both
|
|
233
276
|
* the parent snapshot and the harness root cwd — used by worktree
|
|
@@ -332,7 +375,7 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
332
375
|
*
|
|
333
376
|
* Aborting a context rejects everything blocked on it (channel `recv`
|
|
334
377
|
* waiters, parked senders) with `{ kind: 'cancelled' }`, stops its in-flight
|
|
335
|
-
* model call and
|
|
378
|
+
* model call and ACP agent turn, and makes the next step boundary throw
|
|
336
379
|
* `cancelled`.
|
|
337
380
|
*
|
|
338
381
|
* Cancellation is cooperative: a layer hook already in flight is allowed to
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { frameworkCast, ItemSchemaRegistry, NoeticConfigError } from '@noetic-tools/types';
|
|
2
2
|
import { SpanImpl } from '../observability/span-impl.js';
|
|
3
3
|
import { NoopExporter } from '../observability/trace-exporter.js';
|
|
4
|
+
import { AcpSessionStore } from '../runtime/acp-session-store.js';
|
|
4
5
|
import { createInMemoryFsAdapter, createInMemoryShellAdapter, createInMemorySubprocessAdapter, OpenRouter, } from './deps/adapters.js';
|
|
5
6
|
import { afterModelCallLayers, allocateBudgets, assembleView, beforeToolCallLayers, completeLayers, contextToExecCtx, createContextCacheStore, createLayerStateStore, createRecallCache, DEFAULT_PROJECTION, disposeLayers, executeRerender, initLayers, projectHistoryLayers, recallLayers, recallLayersAtomic, recallLayersEventual, resolveLayerTools, runAppendPipeline, storeLayers, } from './deps/context.js';
|
|
6
7
|
import { collectAllTools, deduplicateTools, dispatchStepThroughAdapter, execute, prepareBandedView, } from './deps/interpreter.js';
|
|
@@ -8,7 +9,6 @@ import { buildItemStream, ChannelStore, ContextImpl, captureCheckpoint, clearChe
|
|
|
8
9
|
import { SteeringAction } from './deps/types.js';
|
|
9
10
|
import { AgentHarnessModelCaller } from './model-call.js';
|
|
10
11
|
export { createStreamIdleWatchdog } from './model-call.js';
|
|
11
|
-
import { resolveContextOption } from '../builders/context-option.js';
|
|
12
12
|
import { buildItemSchemaRegistry } from './model-schema.js';
|
|
13
13
|
//#endregion
|
|
14
14
|
const DEFAULT_THREAD_ID = '__default__';
|
|
@@ -178,7 +178,7 @@ export class AgentHarness {
|
|
|
178
178
|
* @internal
|
|
179
179
|
*/
|
|
180
180
|
stepLedgerRetention;
|
|
181
|
-
|
|
181
|
+
agentGraph;
|
|
182
182
|
/** Harness-wide tool pool merged into every context's `unifiedTools`. */
|
|
183
183
|
harnessTools;
|
|
184
184
|
/** @internal Context layers configured for this harness. Exposed non-private
|
|
@@ -191,15 +191,55 @@ export class AgentHarness {
|
|
|
191
191
|
rootRunDepth = new Map();
|
|
192
192
|
callModelOverride;
|
|
193
193
|
defaultDeliveryMode;
|
|
194
|
-
streamIdleTimeoutMs;
|
|
195
194
|
sessions = new Map();
|
|
196
195
|
/**
|
|
197
|
-
* @internal
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
* `frameworkCast`; do not access from outside core.
|
|
196
|
+
* @internal Live ACP connections. Reached by the interpreter via
|
|
197
|
+
* `frameworkCast`; use {@link listAcpSessions}, {@link getAcpSession},
|
|
198
|
+
* {@link cancelAcpSession}, and {@link closeAcpSessions} from outside core.
|
|
201
199
|
*/
|
|
202
|
-
|
|
200
|
+
acpSessions = new AcpSessionStore();
|
|
201
|
+
/**
|
|
202
|
+
* Snapshot of every live ACP sub-agent, for a UI that wants to show what is
|
|
203
|
+
* running and let a user act on it. Read-only: a session's turns are driven
|
|
204
|
+
* by steps, so nothing here can start work behind the runtime's back.
|
|
205
|
+
* @public
|
|
206
|
+
*/
|
|
207
|
+
listAcpSessions() {
|
|
208
|
+
return this.acpSessions.list();
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* The live connection + session behind a handle from {@link listAcpSessions}
|
|
212
|
+
* (or a step's `session.reuse` key), or `undefined` when nothing matches.
|
|
213
|
+
* @public
|
|
214
|
+
*/
|
|
215
|
+
getAcpSession(key) {
|
|
216
|
+
return this.acpSessions.get(key);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Interrupt whatever the sub-agent under `key` is doing, via `session/cancel`.
|
|
220
|
+
* The connection stays open and the turn still returns — with the `cancelled`
|
|
221
|
+
* stop reason, which the running step surfaces as a `cancelled` error.
|
|
222
|
+
* Returns false when nothing matches the key.
|
|
223
|
+
* @public
|
|
224
|
+
*/
|
|
225
|
+
async cancelAcpSession(key) {
|
|
226
|
+
const entry = this.acpSessions.get(key);
|
|
227
|
+
if (!entry) {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
await entry.session.cancel();
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Close every ACP connection this harness still holds, whatever its
|
|
235
|
+
* keep-alive scope. A connection owns a live agent — usually a child process
|
|
236
|
+
* whose stdio keeps the event loop alive — so a `keepAlive: 'harness'`
|
|
237
|
+
* session must be closed by its owner or the host will not exit. Idempotent.
|
|
238
|
+
* @public
|
|
239
|
+
*/
|
|
240
|
+
async closeAcpSessions() {
|
|
241
|
+
await this.acpSessions.closeAll();
|
|
242
|
+
}
|
|
203
243
|
layerStateStore;
|
|
204
244
|
/** Per-harness memoization cache for `recallMode: 'eventual'` layers. */
|
|
205
245
|
recallCache;
|
|
@@ -225,45 +265,47 @@ export class AgentHarness {
|
|
|
225
265
|
itemSchemas;
|
|
226
266
|
constructor(opts) {
|
|
227
267
|
const validatedParams = opts.paramsSchema ? opts.paramsSchema.parse(opts.params) : opts.params;
|
|
268
|
+
const environment = opts.environment;
|
|
269
|
+
const storageEnv = environment?.storage;
|
|
228
270
|
this.config = {
|
|
229
271
|
name: opts.name,
|
|
230
|
-
storage:
|
|
272
|
+
storage: storageEnv?.adapter ?? createInMemoryStorage(),
|
|
231
273
|
hooks: opts.hooks,
|
|
232
274
|
params: validatedParams,
|
|
233
275
|
itemSchemas: opts.itemSchemas,
|
|
234
|
-
strictItemSchemas: opts.strictItemSchemas ?? true,
|
|
235
276
|
projection: opts.projection,
|
|
236
277
|
forceAtomicRecall: opts.forceAtomicRecall,
|
|
237
278
|
contextCache: opts.contextCache,
|
|
238
279
|
};
|
|
239
|
-
this.fs =
|
|
240
|
-
this.shell =
|
|
241
|
-
this.subprocess =
|
|
242
|
-
this.checkpointStore =
|
|
243
|
-
this.stepLedgerStore =
|
|
280
|
+
this.fs = environment?.fs ?? createInMemoryFsAdapter();
|
|
281
|
+
this.shell = environment?.shell ?? createInMemoryShellAdapter();
|
|
282
|
+
this.subprocess = environment?.subprocess ?? createInMemorySubprocessAdapter();
|
|
283
|
+
this.checkpointStore = storageEnv?.checkpointStore;
|
|
284
|
+
this.stepLedgerStore = storageEnv?.checkpointStore
|
|
244
285
|
? createStepLedgerStore({
|
|
245
286
|
storage: this.config.storage ?? createInMemoryStorage(),
|
|
246
287
|
})
|
|
247
288
|
: undefined;
|
|
248
|
-
this.stepLedgerRetention = resolveStepLedgerRetention(
|
|
249
|
-
this.
|
|
289
|
+
this.stepLedgerRetention = resolveStepLedgerRetention(storageEnv?.stepLedgerRetention);
|
|
290
|
+
this.agentGraph = opts.agentGraph;
|
|
250
291
|
this.harnessTools = opts.tools ?? [];
|
|
251
|
-
this._contextLayers =
|
|
292
|
+
this._contextLayers = opts.contextLayers;
|
|
252
293
|
this.callModelOverride = opts._testCallModel;
|
|
253
|
-
this.client = opts._testCallModel
|
|
294
|
+
this.client = opts._testCallModel
|
|
295
|
+
? undefined
|
|
296
|
+
: createClient(opts.callModelDefaults, opts.contextCache);
|
|
254
297
|
this.channelStore = new ChannelStore();
|
|
255
298
|
this.traceExporter = opts.traceExporter ?? new NoopExporter();
|
|
256
299
|
this.layerStateStore =
|
|
257
|
-
|
|
300
|
+
storageEnv?.layerStateStore ??
|
|
258
301
|
createLayerStateStore((layerId, hook, error) => {
|
|
259
302
|
console.warn(`[noetic] context layer '${layerId}' ${hook} error:`, error);
|
|
260
303
|
});
|
|
261
304
|
this.recallCache = createRecallCache();
|
|
262
305
|
this.contextCache = createContextCacheStore();
|
|
263
306
|
this.defaultDeliveryMode = opts.defaultDeliveryMode ?? 'next-turn';
|
|
264
|
-
this.
|
|
265
|
-
|
|
266
|
-
strictUnknownExtensions: opts.strictItemSchemas ?? true,
|
|
307
|
+
this.itemSchemas = new ItemSchemaRegistry(opts.itemSchemas?.schemas, {
|
|
308
|
+
strictUnknownExtensions: opts.itemSchemas?.strict ?? true,
|
|
267
309
|
});
|
|
268
310
|
this.rootCwdState = {
|
|
269
311
|
cwd: opts.initialCwd ?? '/',
|
|
@@ -284,11 +326,11 @@ export class AgentHarness {
|
|
|
284
326
|
}
|
|
285
327
|
//#region Session Accessors
|
|
286
328
|
execute(input, options) {
|
|
287
|
-
if (!this.
|
|
329
|
+
if (!this.agentGraph) {
|
|
288
330
|
return Promise.reject(new NoeticConfigError({
|
|
289
331
|
code: 'NO_STEP_CONFIGURED',
|
|
290
|
-
message: 'No
|
|
291
|
-
hint: 'Pass `
|
|
332
|
+
message: 'No agentGraph configured on this harness.',
|
|
333
|
+
hint: 'Pass `agentGraph` in constructor options, or use run() directly.',
|
|
292
334
|
}));
|
|
293
335
|
}
|
|
294
336
|
const threadId = options?.threadId ?? DEFAULT_THREAD_ID;
|
|
@@ -353,6 +395,17 @@ export class AgentHarness {
|
|
|
353
395
|
const session = this.sessions.get(threadId);
|
|
354
396
|
return session ? session.runner.queue.size : 0;
|
|
355
397
|
}
|
|
398
|
+
getUsage(scope) {
|
|
399
|
+
const threadId = scope?.threadId ?? DEFAULT_THREAD_ID;
|
|
400
|
+
const session = this.sessions.get(threadId);
|
|
401
|
+
if (!session) {
|
|
402
|
+
return {
|
|
403
|
+
inputTokens: 0,
|
|
404
|
+
outputTokens: 0,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
return session.runner.getUsage();
|
|
408
|
+
}
|
|
356
409
|
seedSessionHistory(threadId, items) {
|
|
357
410
|
const session = this.getOrCreateSession(threadId);
|
|
358
411
|
session.accumulatedItems = [
|
|
@@ -385,15 +438,15 @@ export class AgentHarness {
|
|
|
385
438
|
threadId,
|
|
386
439
|
resourceId: perTurnOptions.resourceId,
|
|
387
440
|
state: perTurnOptions.state,
|
|
388
|
-
|
|
441
|
+
contextLayers: perTurnOptions.contextLayers,
|
|
389
442
|
_broadcaster: session.runner.broadcaster,
|
|
390
443
|
});
|
|
391
444
|
const ext = frameworkCast(ctx);
|
|
392
445
|
ext._sessionQueue = session.runner.queue;
|
|
393
446
|
ext._sessionBetweenRounds = true;
|
|
394
447
|
ext._sessionRunnerAgentName = this.config.name;
|
|
395
|
-
if (this.
|
|
396
|
-
const stepTools = this.
|
|
448
|
+
if (this.agentGraph || this.harnessTools.length > 0) {
|
|
449
|
+
const stepTools = this.agentGraph ? collectAllTools(this.agentGraph) : [];
|
|
397
450
|
this.setUnifiedTools(ctx, [
|
|
398
451
|
...stepTools,
|
|
399
452
|
...this.harnessTools,
|
|
@@ -402,11 +455,11 @@ export class AgentHarness {
|
|
|
402
455
|
return ctx;
|
|
403
456
|
},
|
|
404
457
|
runTurn: async (ctx, _turn, signal) => {
|
|
405
|
-
if (!this.
|
|
458
|
+
if (!this.agentGraph) {
|
|
406
459
|
throw new NoeticConfigError({
|
|
407
460
|
code: 'NO_STEP_CONFIGURED',
|
|
408
|
-
message: 'No
|
|
409
|
-
hint: 'Pass `
|
|
461
|
+
message: 'No agentGraph configured on this harness.',
|
|
462
|
+
hint: 'Pass `agentGraph` in constructor options.',
|
|
410
463
|
});
|
|
411
464
|
}
|
|
412
465
|
// Wire signal-abort to context-abort so the interpreter bails cleanly.
|
|
@@ -420,7 +473,7 @@ export class AgentHarness {
|
|
|
420
473
|
once: true,
|
|
421
474
|
});
|
|
422
475
|
}
|
|
423
|
-
const result = await this.initAndRun(this.
|
|
476
|
+
const result = await this.initAndRun(this.agentGraph, '', ctx);
|
|
424
477
|
// Snapshot final items into session history for the next turn.
|
|
425
478
|
session.accumulatedItems = [
|
|
426
479
|
...ctx.itemLog.items,
|
|
@@ -450,7 +503,7 @@ export class AgentHarness {
|
|
|
450
503
|
itemSchemas: this.itemSchemas,
|
|
451
504
|
client: this.client,
|
|
452
505
|
callModelOverride: this.callModelOverride,
|
|
453
|
-
streamIdleTimeoutMs:
|
|
506
|
+
streamIdleTimeoutMs: DEFAULT_STREAM_IDLE_TIMEOUT_MS,
|
|
454
507
|
harness: this,
|
|
455
508
|
traceExporter: this.traceExporter,
|
|
456
509
|
}).callModel(request);
|
|
@@ -509,6 +562,7 @@ export class AgentHarness {
|
|
|
509
562
|
else {
|
|
510
563
|
this.rootRunDepth.delete(ctx.id);
|
|
511
564
|
this.channelStore.closeExecution(ctx.id);
|
|
565
|
+
await this.acpSessions.closeOwnedBy(ctx.id);
|
|
512
566
|
}
|
|
513
567
|
}
|
|
514
568
|
}
|
|
@@ -540,10 +594,10 @@ export class AgentHarness {
|
|
|
540
594
|
}
|
|
541
595
|
createContext(opts) {
|
|
542
596
|
const resolved = opts ?? {};
|
|
543
|
-
// `ContextImpl` takes the layers as `layers`, so
|
|
597
|
+
// `ContextImpl` takes the layers as `layers`, so the option spelling is
|
|
544
598
|
// dropped from `rest` rather than spread through under the wrong name.
|
|
545
|
-
const {
|
|
546
|
-
const effectiveLayers =
|
|
599
|
+
const { contextLayers, cwdInit, ...rest } = resolved;
|
|
600
|
+
const effectiveLayers = contextLayers ?? this._contextLayers;
|
|
547
601
|
const itemSchemas = buildItemSchemaRegistry({
|
|
548
602
|
base: this.itemSchemas,
|
|
549
603
|
layers: effectiveLayers,
|
|
@@ -696,7 +750,7 @@ export class AgentHarness {
|
|
|
696
750
|
const ctx = this.createContext({
|
|
697
751
|
items: historyItems,
|
|
698
752
|
threadId,
|
|
699
|
-
|
|
753
|
+
contextLayers: this._contextLayers,
|
|
700
754
|
});
|
|
701
755
|
const layers = ctx.layers ?? [];
|
|
702
756
|
if (layers.length === 0) {
|
|
@@ -837,7 +891,7 @@ export class AgentHarness {
|
|
|
837
891
|
*
|
|
838
892
|
* Aborting a context rejects everything blocked on it (channel `recv`
|
|
839
893
|
* waiters, parked senders) with `{ kind: 'cancelled' }`, stops its in-flight
|
|
840
|
-
* model call and
|
|
894
|
+
* model call and ACP agent turn, and makes the next step boundary throw
|
|
841
895
|
* `cancelled`.
|
|
842
896
|
*
|
|
843
897
|
* Cancellation is cooperative: a layer hook already in flight is allowed to
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export type { ContextCacheConfig, ContextCacheStore, ContextData, ContextLayer, ExecutionContext, ProjectionPolicy, StorageAdapter, } from '@noetic-tools/context';
|
|
2
|
-
export type { AgentConfig, AgentHarnessContract, AgentHooks, CallModelRequest, Channel, ChannelHandle, Context, CwdState, DeliveryMode, DetachedHandle, ExecuteInput, ExecuteOptions, ExternalChannel, FsAdapter, HarnessResponse, HarnessStatus, Item, ItemSchemaExtensions, LLMResponse, LlmProviderConfig, RecallLayerOutput, SessionScope, ShellAdapter, Span, SteeringDecision, Step, StreamEvent, StreamingItem,
|
|
2
|
+
export type { AcpKeepAlive, AcpLiveSession, AcpSessionInfo, AgentConfig, AgentHarnessContract, AgentHooks, CallModelRequest, Channel, ChannelHandle, Context, CwdState, DeliveryMode, DetachedHandle, ExecuteInput, ExecuteOptions, ExternalChannel, FsAdapter, HarnessResponse, HarnessStatus, Item, ItemSchemaConfig, ItemSchemaExtensions, LLMResponse, LlmProviderConfig, RecallLayerOutput, SessionScope, SessionUsage, ShellAdapter, Span, SteeringDecision, Step, StreamEvent, StreamingItem, SubprocessAdapter, Tool, TraceExporter, } from '@noetic-tools/types';
|
|
3
3
|
export { SteeringAction } from '@noetic-tools/types';
|
|
4
4
|
export type { ZodType } from 'zod';
|
|
@@ -10,7 +10,7 @@ interface StreamIdleWatchdog {
|
|
|
10
10
|
/** @internal Create a watchdog that aborts `controller` when no
|
|
11
11
|
* {@link StreamIdleWatchdog.reset reset} is called within `timeoutMs`. When
|
|
12
12
|
* `timeoutMs <= 0`, returns an inert no-op so callers can always call `.reset()`
|
|
13
|
-
* / `.stop()` without
|
|
13
|
+
* / `.stop()` without branching. Starts armed: the caller is responsible for
|
|
14
14
|
* `.stop()` in a `finally`. `onTimeout` runs before the abort so observers can
|
|
15
15
|
* emit a framework event with the original cause. Exported only for unit tests. */
|
|
16
16
|
export declare function createStreamIdleWatchdog(timeoutMs: number, controller: AbortController, onTimeout?: () => void): StreamIdleWatchdog;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { frameworkCast, NoeticConfigError } from '@noetic-tools/types';
|
|
2
2
|
import { serverTool } from '@openrouter/agent';
|
|
3
|
-
import {
|
|
4
|
-
import { convertTools, executeToolCall, extractOutputItems, extractSystemInstruction, extractUsage, itemsToInput, sanitizeToolNameForWire, } from '../adapters/openrouter.js';
|
|
3
|
+
import { convertTools, executeToolCall, extractOutputItems, extractSystemInstruction, extractUsage, itemsToInput, resolveWireJsonSchema, sanitizeToolNameForWire, } from '../adapters/openrouter.js';
|
|
5
4
|
import { isFunctionCall } from '../interpreter/typeguards.js';
|
|
6
5
|
import { GenAI, NoeticAttr, ToolAttr } from '../observability/genai-attributes.js';
|
|
7
6
|
import { emitFrameworkEvent, getBroadcaster, shouldEmit } from '../runtime/broadcaster-utils.js';
|
|
@@ -9,8 +8,12 @@ import { buildItemSchemaRegistry, createToolResultItem } from './model-schema.js
|
|
|
9
8
|
const MAX_TOOL_ROUNDS = 32;
|
|
10
9
|
const MAX_RECOVERY_CONTINUATIONS = 3;
|
|
11
10
|
const EPHEMERAL_CONTINUE_INPUT = 'continue';
|
|
12
|
-
function buildTextFormat(schema) {
|
|
13
|
-
const jsonSchema =
|
|
11
|
+
function buildTextFormat(schema, explicitJsonSchema) {
|
|
12
|
+
const jsonSchema = resolveWireJsonSchema({
|
|
13
|
+
schema,
|
|
14
|
+
explicitJsonSchema,
|
|
15
|
+
what: 'structured output schema',
|
|
16
|
+
});
|
|
14
17
|
return {
|
|
15
18
|
format: {
|
|
16
19
|
type: 'json_schema',
|
|
@@ -133,7 +136,7 @@ async function pipeStreamEventsToBroadcaster(opts) {
|
|
|
133
136
|
/** @internal Create a watchdog that aborts `controller` when no
|
|
134
137
|
* {@link StreamIdleWatchdog.reset reset} is called within `timeoutMs`. When
|
|
135
138
|
* `timeoutMs <= 0`, returns an inert no-op so callers can always call `.reset()`
|
|
136
|
-
* / `.stop()` without
|
|
139
|
+
* / `.stop()` without branching. Starts armed: the caller is responsible for
|
|
137
140
|
* `.stop()` in a `finally`. `onTimeout` runs before the abort so observers can
|
|
138
141
|
* emit a framework event with the original cause. Exported only for unit tests. */
|
|
139
142
|
export function createStreamIdleWatchdog(timeoutMs, controller, onTimeout) {
|
|
@@ -150,7 +153,7 @@ export function createStreamIdleWatchdog(timeoutMs, controller, onTimeout) {
|
|
|
150
153
|
if (stopped) {
|
|
151
154
|
return;
|
|
152
155
|
}
|
|
153
|
-
const reason = new Error(`
|
|
156
|
+
const reason = new Error(`model stream idle timeout after ${timeoutMs}ms`);
|
|
154
157
|
onTimeout?.();
|
|
155
158
|
controller.abort(reason);
|
|
156
159
|
}, timeoutMs);
|
|
@@ -354,7 +357,7 @@ export class AgentHarnessModelCaller {
|
|
|
354
357
|
throw new NoeticConfigError({
|
|
355
358
|
code: 'NO_LLM_PROVIDER',
|
|
356
359
|
message: 'No LLM provider configured on this harness.',
|
|
357
|
-
hint: 'Pass `
|
|
360
|
+
hint: 'Pass `callModelDefaults: { apiKey: "..." }` (defaults to the Noetic platform) or set NOETIC_API_KEY. For direct OpenRouter, use `callModelDefaults: { provider: "openrouter", apiKey: "..." }` or set OPENROUTER_API_KEY.',
|
|
358
361
|
});
|
|
359
362
|
}
|
|
360
363
|
async callModel(request) {
|
|
@@ -384,7 +387,9 @@ export class AgentHarnessModelCaller {
|
|
|
384
387
|
const rounds = [];
|
|
385
388
|
let totalCost = 0;
|
|
386
389
|
const conversationInput = itemsToInput(prepared.remaining);
|
|
387
|
-
const textFormat = request.outputSchema
|
|
390
|
+
const textFormat = request.outputSchema
|
|
391
|
+
? buildTextFormat(request.outputSchema, request.outputJsonSchema)
|
|
392
|
+
: undefined;
|
|
388
393
|
let round = 0;
|
|
389
394
|
let invalidRecoveryContinuations = 0;
|
|
390
395
|
let toolLimitRecoveryContinuations = 0;
|
|
@@ -406,7 +411,7 @@ export class AgentHarnessModelCaller {
|
|
|
406
411
|
let idleStalled = false;
|
|
407
412
|
const watchdog = createStreamIdleWatchdog(this.opts.streamIdleTimeoutMs, roundController, () => {
|
|
408
413
|
idleStalled = true;
|
|
409
|
-
prepared.emitIfAllowed('
|
|
414
|
+
prepared.emitIfAllowed('model_call_stalled', {
|
|
410
415
|
round,
|
|
411
416
|
idleTimeoutMs: this.opts.streamIdleTimeoutMs,
|
|
412
417
|
});
|
|
@@ -414,7 +419,7 @@ export class AgentHarnessModelCaller {
|
|
|
414
419
|
const modelInput = recoveryContinuation
|
|
415
420
|
? withEphemeralContinueInput(conversationInput)
|
|
416
421
|
: frameworkCast(conversationInput);
|
|
417
|
-
prepared.emitIfAllowed('
|
|
422
|
+
prepared.emitIfAllowed('model_call_started', {
|
|
418
423
|
round,
|
|
419
424
|
messageCount: modelInput.length,
|
|
420
425
|
toolCount: prepared.sdkTools?.length ?? 0,
|
|
@@ -446,7 +451,7 @@ export class AgentHarnessModelCaller {
|
|
|
446
451
|
watchdog.reset();
|
|
447
452
|
if (!firstEventSeen) {
|
|
448
453
|
firstEventSeen = true;
|
|
449
|
-
prepared.emitIfAllowed('
|
|
454
|
+
prepared.emitIfAllowed('model_call_first_event', {
|
|
450
455
|
round,
|
|
451
456
|
});
|
|
452
457
|
}
|
|
@@ -462,7 +467,7 @@ export class AgentHarnessModelCaller {
|
|
|
462
467
|
if (idleStalled) {
|
|
463
468
|
throw roundSignal.reason instanceof Error
|
|
464
469
|
? roundSignal.reason
|
|
465
|
-
: new Error(`
|
|
470
|
+
: new Error(`model stream idle timeout after ${this.opts.streamIdleTimeoutMs}ms`);
|
|
466
471
|
}
|
|
467
472
|
if (request.signal?.aborted) {
|
|
468
473
|
break;
|
|
@@ -507,7 +512,7 @@ export class AgentHarnessModelCaller {
|
|
|
507
512
|
continue;
|
|
508
513
|
}
|
|
509
514
|
invalidRecoveryContinuations = 0;
|
|
510
|
-
prepared.emitIfAllowed('
|
|
515
|
+
prepared.emitIfAllowed('model_call_completed', {
|
|
511
516
|
round,
|
|
512
517
|
itemCount: sdkResponse.output?.length ?? 0,
|
|
513
518
|
});
|
|
@@ -605,7 +610,7 @@ export class AgentHarnessModelCaller {
|
|
|
605
610
|
kind: 'ok',
|
|
606
611
|
};
|
|
607
612
|
}
|
|
608
|
-
params.emitIfAllowed('
|
|
613
|
+
params.emitIfAllowed('model_call_failed', {
|
|
609
614
|
round: params.round,
|
|
610
615
|
status: terminalError.status,
|
|
611
616
|
error: terminalError.message,
|
|
@@ -615,7 +620,7 @@ export class AgentHarnessModelCaller {
|
|
|
615
620
|
throw new Error(terminalError.message);
|
|
616
621
|
}
|
|
617
622
|
const next = params.invalidRecoveryContinuations + 1;
|
|
618
|
-
params.emitIfAllowed('
|
|
623
|
+
params.emitIfAllowed('model_call_recovery_continue', {
|
|
619
624
|
round: params.round,
|
|
620
625
|
status: terminalError.status,
|
|
621
626
|
attempt: next,
|
|
@@ -633,7 +638,7 @@ export class AgentHarnessModelCaller {
|
|
|
633
638
|
};
|
|
634
639
|
}
|
|
635
640
|
const message = 'LLM response completed with no output items';
|
|
636
|
-
params.emitIfAllowed('
|
|
641
|
+
params.emitIfAllowed('model_call_failed', {
|
|
637
642
|
round: params.round,
|
|
638
643
|
status: 'completed',
|
|
639
644
|
error: message,
|
|
@@ -643,7 +648,7 @@ export class AgentHarnessModelCaller {
|
|
|
643
648
|
throw new Error(message);
|
|
644
649
|
}
|
|
645
650
|
const next = params.invalidRecoveryContinuations + 1;
|
|
646
|
-
params.emitIfAllowed('
|
|
651
|
+
params.emitIfAllowed('model_call_recovery_continue', {
|
|
647
652
|
round: params.round,
|
|
648
653
|
status: 'completed',
|
|
649
654
|
attempt: next,
|
|
@@ -750,7 +755,7 @@ export class AgentHarnessModelCaller {
|
|
|
750
755
|
recordMalformedToolCall(params) {
|
|
751
756
|
const { fc, request, allItems, conversationInput } = params;
|
|
752
757
|
const errorOutput = `Error: malformed JSON in tool arguments: ${fc.arguments}`;
|
|
753
|
-
const toolForCall = request.tools?.find((tool) => tool.name === fc.name);
|
|
758
|
+
const toolForCall = request.tools?.find((tool) => tool.name === fc.name || sanitizeToolNameForWire(tool.name) === fc.name);
|
|
754
759
|
// Owner-scoped result validation (see executeFunctionCall).
|
|
755
760
|
const outputItem = createToolResultItem({
|
|
756
761
|
output: errorOutput,
|