@noetic-tools/core 5.4.2 → 5.6.0
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/adapters/openrouter.js +1 -1
- package/dist/builders/control-flow-builders.d.ts +6 -4
- package/dist/builders/control-flow-builders.js +3 -2
- package/dist/builders/decide-builders.d.ts +86 -0
- package/dist/builders/decide-builders.js +230 -0
- package/dist/builders/dynamic-workflow.js +1 -0
- package/dist/builders/tool-builder.d.ts +5 -1
- package/dist/builders/tool-builder.js +2 -0
- package/dist/builders/workflow-hydrator.js +81 -2
- package/dist/conditions/conditions.d.ts +9 -3
- package/dist/conditions/conditions.js +84 -25
- package/dist/harness/agent-harness.d.ts +19 -4
- package/dist/harness/agent-harness.js +74 -9
- package/dist/harness/decide-call.d.ts +19 -0
- package/dist/harness/decide-call.js +483 -0
- package/dist/harness/deps/adapters.d.ts +1 -0
- package/dist/harness/deps/adapters.js +1 -0
- package/dist/harness/deps/context.d.ts +1 -1
- package/dist/harness/deps/context.js +1 -1
- package/dist/harness/deps/types.d.ts +1 -1
- package/dist/harness/model-call.js +3 -1
- package/dist/index.d.ts +12 -4
- package/dist/index.js +5 -1
- package/dist/interpreter/action-deps.d.ts +1 -1
- package/dist/interpreter/action-deps.js +1 -1
- package/dist/interpreter/action-types.d.ts +1 -1
- package/dist/interpreter/collect-tools.js +2 -0
- package/dist/interpreter/execute-acp-agent.js +1 -1
- package/dist/interpreter/execute-action.js +46 -21
- package/dist/interpreter/execute-control.js +1 -1
- package/dist/interpreter/execute-decide.d.ts +10 -0
- package/dist/interpreter/execute-decide.js +417 -0
- package/dist/interpreter/execute.js +8 -3
- package/dist/observability/genai-attributes.d.ts +6 -0
- package/dist/observability/genai-attributes.js +6 -0
- package/dist/portable/context-layers.d.ts +1 -1
- package/dist/portable/context-layers.js +1 -1
- package/dist/runtime/session-runner.js +4 -0
- package/dist/schemas/workflow.d.ts +1 -1
- package/dist/util/status-code.d.ts +2 -0
- package/dist/util/status-code.js +7 -0
- package/package.json +7 -6
- package/schema/noetic-workflow.schema.json +396 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { storageGetMany } from '@noetic-tools/context';
|
|
2
|
-
import {
|
|
2
|
+
import { DecideOutputError, isNoeticError, NoeticErrorImpl, trackUsage } from '@noetic-tools/types';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { cosineSimilarity } from './cosine-similarity.js';
|
|
5
5
|
//#endregion
|
|
@@ -234,41 +234,100 @@ export function allCondition(...conditions) {
|
|
|
234
234
|
}
|
|
235
235
|
//#endregion
|
|
236
236
|
//#region aiCondition
|
|
237
|
-
const AiConditionResponseSchema = z.object({
|
|
238
|
-
answer: z.boolean(),
|
|
239
|
-
});
|
|
240
237
|
/**
|
|
241
|
-
* Creates a condition that
|
|
238
|
+
* Creates a condition that asks a model a yes/no question about the input — a
|
|
239
|
+
* one-question `decide`. Works with decision models natively and with chat
|
|
240
|
+
* models through estimated probabilities.
|
|
242
241
|
*
|
|
243
242
|
* @public
|
|
244
|
-
* @param opts -
|
|
245
|
-
* @
|
|
243
|
+
* @param opts.model - Decision or chat model id.
|
|
244
|
+
* @param opts.prompt - The yes/no question.
|
|
245
|
+
* @param opts.threshold - Probability of yes at or above which the condition holds. Default 0.5.
|
|
246
|
+
* @returns A `Condition` that is true when the model answers yes. A refusal counts as no.
|
|
247
|
+
* @throws `NoeticError` `model_parse_error` when the response is not a boolean answer.
|
|
246
248
|
*/
|
|
247
249
|
export function aiCondition(opts) {
|
|
248
250
|
return async (input, ctx) => {
|
|
249
|
-
const
|
|
250
|
-
const systemPrompt = `You are a boolean classifier. Given the user's input, answer the following question with JSON: {"answer": true} or {"answer": false}.\n\nQuestion: ${opts.prompt}`;
|
|
251
|
-
const items = [
|
|
252
|
-
createMessage(systemPrompt, 'developer'),
|
|
253
|
-
createMessage(text, 'user'),
|
|
254
|
-
];
|
|
255
|
-
const response = await ctx.harness.callModel({
|
|
251
|
+
const response = await askDecide(ctx, {
|
|
256
252
|
model: opts.model,
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
253
|
+
state: [
|
|
254
|
+
{
|
|
255
|
+
type: 'text',
|
|
256
|
+
name: 'input',
|
|
257
|
+
text: serializeInput(input),
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
questions: {
|
|
261
|
+
answer: {
|
|
262
|
+
type: 'boolean',
|
|
263
|
+
instructions: opts.prompt,
|
|
264
|
+
threshold: opts.threshold,
|
|
265
|
+
},
|
|
260
266
|
},
|
|
261
267
|
});
|
|
262
|
-
trackUsage(ctx,
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
catch {
|
|
268
|
+
trackUsage(ctx, {
|
|
269
|
+
items: [],
|
|
270
|
+
usage: response.usage,
|
|
271
|
+
cost: response.cost,
|
|
272
|
+
});
|
|
273
|
+
const answer = response.answers.answer;
|
|
274
|
+
if (answer?.type === 'refusal') {
|
|
270
275
|
return false;
|
|
271
276
|
}
|
|
277
|
+
const parsed = AiConditionAnswerSchema.safeParse(answer);
|
|
278
|
+
if (!parsed.success) {
|
|
279
|
+
throw new NoeticErrorImpl({
|
|
280
|
+
kind: 'model_parse_error',
|
|
281
|
+
stepId: 'aiCondition',
|
|
282
|
+
raw: JSON.stringify(answer),
|
|
283
|
+
schema: AiConditionAnswerSchema,
|
|
284
|
+
zodError: parsed.error,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
return parsed.data.value;
|
|
272
288
|
};
|
|
273
289
|
}
|
|
290
|
+
async function askDecide(ctx, request) {
|
|
291
|
+
try {
|
|
292
|
+
return await ctx.harness.decide(request);
|
|
293
|
+
}
|
|
294
|
+
catch (e) {
|
|
295
|
+
if (isNoeticError(e)) {
|
|
296
|
+
throw e;
|
|
297
|
+
}
|
|
298
|
+
if (ctx.aborted) {
|
|
299
|
+
throw new NoeticErrorImpl({
|
|
300
|
+
kind: 'cancelled',
|
|
301
|
+
reason: ctx.abortReason ?? 'context aborted',
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
if (e instanceof DecideOutputError) {
|
|
305
|
+
if (e.usage) {
|
|
306
|
+
trackUsage(ctx, {
|
|
307
|
+
items: [],
|
|
308
|
+
usage: e.usage,
|
|
309
|
+
cost: e.cost,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
throw new NoeticErrorImpl({
|
|
313
|
+
kind: 'model_parse_error',
|
|
314
|
+
stepId: 'aiCondition',
|
|
315
|
+
raw: e.raw,
|
|
316
|
+
schema: e.schema,
|
|
317
|
+
zodError: e.zodError,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
throw new NoeticErrorImpl({
|
|
321
|
+
kind: 'step_failed',
|
|
322
|
+
stepId: 'aiCondition',
|
|
323
|
+
cause: e instanceof Error ? e : new Error(String(e)),
|
|
324
|
+
retriesExhausted: false,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
const AiConditionAnswerSchema = z.object({
|
|
329
|
+
type: z.literal('boolean'),
|
|
330
|
+
value: z.boolean(),
|
|
331
|
+
probability: z.number().min(0).max(1),
|
|
332
|
+
});
|
|
274
333
|
//#endregion
|
|
@@ -3,7 +3,7 @@ import { AcpSessionStore } from '../runtime/acp-session-store.js';
|
|
|
3
3
|
import type { LayerStateStore, RecallCache } from './deps/context.js';
|
|
4
4
|
import type { DetachedSpawnOverrides } from './deps/interpreter.js';
|
|
5
5
|
import type { CheckpointStore, EventBroadcaster, RestoreCheckpointOptions, StepLedgerRetention, StepLedgerStore } from './deps/runtime.js';
|
|
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
|
+
import type { AcpLiveSession, AcpSessionInfo, AgentConfig, AgentHarnessContract, AgentHooks, CallModelRequest, Channel, ChannelHandle, Context, ContextCacheConfig, ContextCacheStore, ContextData, ContextLayer, CwdState, DecideRequest, DecideResponse, DecisionRecord, DeliveryMode, DetachedHandle, ExecuteInput, ExecuteOptions, ExternalChannel, FsAdapter, HarnessResponse, HarnessStatus, Item, ItemSchemaConfig, LLMResponse, LlmProviderConfig, ProjectionPolicy, RecallLayerOutput, RecallOptions, SessionScope, SessionUsage, ShellAdapter, Span, SteeringDecision, Step, StorageAdapter, StreamEvent, StreamingItem, SubprocessAdapter, Tool, TraceExporter, ZodType } from './deps/types.js';
|
|
7
7
|
export { createStreamIdleWatchdog } from './model-call.js';
|
|
8
8
|
/**
|
|
9
9
|
* Storage-scoped environment configuration for `AgentHarness`.
|
|
@@ -87,8 +87,12 @@ interface AgentHarnessOpts<TParams extends Record<string, unknown> = Record<stri
|
|
|
87
87
|
* those produced by spawn/fork. Defaults to `process.cwd()`.
|
|
88
88
|
*/
|
|
89
89
|
initialCwd?: string;
|
|
90
|
+
/** Default model for `decide` steps. */
|
|
91
|
+
decisionModel?: string;
|
|
90
92
|
/** @internal Test-only escape hatch to inject a mock callModel implementation. */
|
|
91
93
|
_testCallModel?: (request: CallModelRequest) => Promise<LLMResponse>;
|
|
94
|
+
/** @internal Test-only escape hatch to inject a mock decide implementation. */
|
|
95
|
+
_testDecide?: (request: DecideRequest) => Promise<DecideResponse>;
|
|
92
96
|
}
|
|
93
97
|
/** Env vars consulted when resolving the LLM client (read defensively so the
|
|
94
98
|
* harness also works in runtimes without a `process` global, e.g. the browser). */
|
|
@@ -154,6 +158,11 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
154
158
|
* without friend-class gymnastics. Do not access from outside core. */
|
|
155
159
|
readonly _contextLayers?: ContextLayer[];
|
|
156
160
|
private readonly client?;
|
|
161
|
+
/** SDK 1.x client for the decisions endpoint; the agent's client predates it. */
|
|
162
|
+
private readonly decisionsClient?;
|
|
163
|
+
private readonly decideOverride?;
|
|
164
|
+
/** Model id → whether it answers natively as a decision model. */
|
|
165
|
+
private readonly decisionModels;
|
|
157
166
|
private readonly channelStore;
|
|
158
167
|
/** Re-entrant `run()` depth per root context id — see `executeClosingChannels`. */
|
|
159
168
|
private readonly rootRunDepth;
|
|
@@ -207,6 +216,8 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
207
216
|
* `ctx.id` (the layer-state store's executionId).
|
|
208
217
|
*/
|
|
209
218
|
private readonly initializedExecutions;
|
|
219
|
+
/** Executions restored from a checkpoint: their first layer init keeps the restored state. */
|
|
220
|
+
private readonly restoredExecutions;
|
|
210
221
|
readonly traceExporter: TraceExporter;
|
|
211
222
|
/**
|
|
212
223
|
* Long-lived shared cwd state. The same reference is seeded into every
|
|
@@ -242,6 +253,8 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
242
253
|
private getOrCreateSession;
|
|
243
254
|
private requireSession;
|
|
244
255
|
callModel(request: CallModelRequest): Promise<LLMResponse>;
|
|
256
|
+
decide(request: DecideRequest): Promise<DecideResponse>;
|
|
257
|
+
notifyDecision(layers: ContextLayer[], record: DecisionRecord, ctx: Context): Promise<void>;
|
|
245
258
|
run<I, O>(s: Step<ContextData, I, O>, input: I, ctx: Context): Promise<O>;
|
|
246
259
|
/** Run all layer `init` hooks before the first step executes. Per spec 11,
|
|
247
260
|
* init MUST complete before any recall fires so layer state is populated. */
|
|
@@ -271,6 +284,8 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
271
284
|
threadId?: string;
|
|
272
285
|
resourceId?: string;
|
|
273
286
|
contextLayers?: ContextLayer[];
|
|
287
|
+
/** Additional layers appended AFTER the effective set — never replaces it. */
|
|
288
|
+
extraContextLayers?: ContextLayer[];
|
|
274
289
|
/**
|
|
275
290
|
* Initial cwd for the new context. When set, takes precedence over both
|
|
276
291
|
* the parent snapshot and the harness root cwd — used by worktree
|
|
@@ -293,8 +308,8 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
293
308
|
recallLayers(layers: ContextLayer[], input: string, ctx: Context): Promise<RecallLayerOutput[]>;
|
|
294
309
|
/** Allocate per-layer recall budgets from the harness projection policy. */
|
|
295
310
|
private layerBudgets;
|
|
296
|
-
recallLayersAtomic(layers: ContextLayer[], input: string, ctx: Context, budgets: Map<string, number
|
|
297
|
-
recallLayersEventual(layers: ContextLayer[], input: string, ctx: Context, budgets: Map<string, number
|
|
311
|
+
recallLayersAtomic(layers: ContextLayer[], input: string, ctx: Context, budgets: Map<string, number>, opts?: RecallOptions): Promise<RecallLayerOutput[]>;
|
|
312
|
+
recallLayersEventual(layers: ContextLayer[], input: string, ctx: Context, budgets: Map<string, number>, opts?: RecallOptions): Promise<RecallLayerOutput[]>;
|
|
298
313
|
/**
|
|
299
314
|
* Compute the items array that would be sent to the model on the next turn —
|
|
300
315
|
* the same arrangement `executeLLM` builds: harness-level context layers'
|
|
@@ -393,7 +408,7 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
393
408
|
createSpan(name: string, parent: Span | null): Span;
|
|
394
409
|
getLayerState<T>(executionId: string, layerId: string): T | undefined;
|
|
395
410
|
setLayerState<T>(executionId: string, layerId: string, state: T): void;
|
|
396
|
-
beforeToolCall(layers: ContextLayer[], toolName: string, toolArgs: unknown, ctx: Context): Promise<SteeringDecision>;
|
|
411
|
+
beforeToolCall(layers: ContextLayer[], toolName: string, toolArgs: unknown, ctx: Context, callId?: string): Promise<SteeringDecision>;
|
|
397
412
|
afterModelCall(layers: ContextLayer[], response: LLMResponse, ctx: Context): Promise<SteeringDecision>;
|
|
398
413
|
projectHistory(layers: ContextLayer[], items: ReadonlyArray<Item>, ctx: Context): Promise<ReadonlyArray<Item>>;
|
|
399
414
|
runAppendPipeline(layers: ContextLayer[], items: Item[], ctx: Context): Promise<{
|
|
@@ -2,8 +2,9 @@ import { frameworkCast, ItemSchemaRegistry, NoeticConfigError } from '@noetic-to
|
|
|
2
2
|
import { SpanImpl } from '../observability/span-impl.js';
|
|
3
3
|
import { NoopExporter } from '../observability/trace-exporter.js';
|
|
4
4
|
import { AcpSessionStore } from '../runtime/acp-session-store.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { callDecide } from './decide-call.js';
|
|
6
|
+
import { createInMemoryFsAdapter, createInMemoryShellAdapter, createInMemorySubprocessAdapter, OpenRouter, OpenRouterSdk, } from './deps/adapters.js';
|
|
7
|
+
import { afterModelCallLayers, allocateBudgets, assembleView, beforeToolCallLayers, completeLayers, contextToExecCtx, createContextCacheStore, createLayerStateStore, createRecallCache, DEFAULT_PROJECTION, disposeLayers, executeRerender, initLayers, notifyDecision, projectHistoryLayers, recallLayers, recallLayersAtomic, recallLayersEventual, resolveLayerTools, runAppendPipeline, storeLayers, } from './deps/context.js';
|
|
7
8
|
import { collectAllTools, deduplicateTools, dispatchStepThroughAdapter, execute, prepareBandedView, } from './deps/interpreter.js';
|
|
8
9
|
import { buildItemStream, ChannelStore, ContextImpl, captureCheckpoint, clearCheckpoint, collectContextTree, createInMemoryStorage, createStepLedgerStore, filterReasoningStream, filterTextStream, resolveStepLedgerRetention, restoreFromCheckpoint, SessionRunner, StepLedger, snapshotCwdState, } from './deps/runtime.js';
|
|
9
10
|
import { SteeringAction } from './deps/types.js';
|
|
@@ -144,6 +145,16 @@ function createClient(config, contextCache) {
|
|
|
144
145
|
},
|
|
145
146
|
});
|
|
146
147
|
}
|
|
148
|
+
function createDecisionsClient(config) {
|
|
149
|
+
const resolved = resolveLlmClient(config, readLlmEnv());
|
|
150
|
+
if (!resolved) {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
return new OpenRouterSdk({
|
|
154
|
+
apiKey: resolved.apiKey,
|
|
155
|
+
serverURL: resolved.serverURL,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
147
158
|
//#region AgentHarness
|
|
148
159
|
/**
|
|
149
160
|
* Default agent harness for executing agent steps with built-in channel, context, and trace support.
|
|
@@ -186,6 +197,11 @@ export class AgentHarness {
|
|
|
186
197
|
* without friend-class gymnastics. Do not access from outside core. */
|
|
187
198
|
_contextLayers;
|
|
188
199
|
client;
|
|
200
|
+
/** SDK 1.x client for the decisions endpoint; the agent's client predates it. */
|
|
201
|
+
decisionsClient;
|
|
202
|
+
decideOverride;
|
|
203
|
+
/** Model id → whether it answers natively as a decision model. */
|
|
204
|
+
decisionModels = new Map();
|
|
189
205
|
channelStore;
|
|
190
206
|
/** Re-entrant `run()` depth per root context id — see `executeClosingChannels`. */
|
|
191
207
|
rootRunDepth = new Map();
|
|
@@ -252,6 +268,8 @@ export class AgentHarness {
|
|
|
252
268
|
* `ctx.id` (the layer-state store's executionId).
|
|
253
269
|
*/
|
|
254
270
|
initializedExecutions = new Set();
|
|
271
|
+
/** Executions restored from a checkpoint: their first layer init keeps the restored state. */
|
|
272
|
+
restoredExecutions = new Set();
|
|
255
273
|
traceExporter;
|
|
256
274
|
/**
|
|
257
275
|
* Long-lived shared cwd state. The same reference is seeded into every
|
|
@@ -276,6 +294,7 @@ export class AgentHarness {
|
|
|
276
294
|
projection: opts.projection,
|
|
277
295
|
forceAtomicRecall: opts.forceAtomicRecall,
|
|
278
296
|
contextCache: opts.contextCache,
|
|
297
|
+
decisionModel: opts.decisionModel,
|
|
279
298
|
};
|
|
280
299
|
this.fs = environment?.fs ?? createInMemoryFsAdapter();
|
|
281
300
|
this.shell = environment?.shell ?? createInMemoryShellAdapter();
|
|
@@ -294,6 +313,10 @@ export class AgentHarness {
|
|
|
294
313
|
this.client = opts._testCallModel
|
|
295
314
|
? undefined
|
|
296
315
|
: createClient(opts.callModelDefaults, opts.contextCache);
|
|
316
|
+
this.decideOverride = opts._testDecide;
|
|
317
|
+
this.decisionsClient = opts._testCallModel
|
|
318
|
+
? undefined
|
|
319
|
+
: createDecisionsClient(opts.callModelDefaults);
|
|
297
320
|
this.channelStore = new ChannelStore();
|
|
298
321
|
this.traceExporter = opts.traceExporter ?? new NoopExporter();
|
|
299
322
|
this.layerStateStore =
|
|
@@ -439,6 +462,7 @@ export class AgentHarness {
|
|
|
439
462
|
resourceId: perTurnOptions.resourceId,
|
|
440
463
|
state: perTurnOptions.state,
|
|
441
464
|
contextLayers: perTurnOptions.contextLayers,
|
|
465
|
+
extraContextLayers: perTurnOptions.extraContextLayers,
|
|
442
466
|
_broadcaster: session.runner.broadcaster,
|
|
443
467
|
});
|
|
444
468
|
const ext = frameworkCast(ctx);
|
|
@@ -509,6 +533,27 @@ export class AgentHarness {
|
|
|
509
533
|
}).callModel(request);
|
|
510
534
|
}
|
|
511
535
|
//#endregion
|
|
536
|
+
//#region decide
|
|
537
|
+
async decide(request) {
|
|
538
|
+
if (this.decideOverride) {
|
|
539
|
+
return this.decideOverride(request);
|
|
540
|
+
}
|
|
541
|
+
return callDecide(request, {
|
|
542
|
+
client: this.decisionsClient,
|
|
543
|
+
callModel: (r) => this.callModel(r),
|
|
544
|
+
nativeModels: this.decisionModels,
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
async notifyDecision(layers, record, ctx) {
|
|
548
|
+
await notifyDecision({
|
|
549
|
+
layers,
|
|
550
|
+
record,
|
|
551
|
+
ctx: this.toExecCtx(ctx),
|
|
552
|
+
store: this.layerStateStore,
|
|
553
|
+
recallCache: this.recallCache,
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
//#endregion
|
|
512
557
|
async run(s, input, ctx) {
|
|
513
558
|
// Populate the unified tool pool when an embedder drives a step directly on
|
|
514
559
|
// a bare `createContext()` context (the turn pipeline does this per turn).
|
|
@@ -583,7 +628,14 @@ export class AgentHarness {
|
|
|
583
628
|
return;
|
|
584
629
|
}
|
|
585
630
|
this.initializedExecutions.add(ctx.id);
|
|
586
|
-
|
|
631
|
+
const keep = this.restoredExecutions.delete(ctx.id);
|
|
632
|
+
await initLayers({
|
|
633
|
+
layers,
|
|
634
|
+
ctx: this.toExecCtx(ctx),
|
|
635
|
+
storage,
|
|
636
|
+
store: this.layerStateStore,
|
|
637
|
+
keep,
|
|
638
|
+
});
|
|
587
639
|
}
|
|
588
640
|
detachedSpawn(s, input, parentCtx, overrides) {
|
|
589
641
|
/* NOT a checkpoint boundary, despite spec 23 listing one here: a DetachedHandle's
|
|
@@ -596,8 +648,14 @@ export class AgentHarness {
|
|
|
596
648
|
const resolved = opts ?? {};
|
|
597
649
|
// `ContextImpl` takes the layers as `layers`, so the option spelling is
|
|
598
650
|
// dropped from `rest` rather than spread through under the wrong name.
|
|
599
|
-
const { contextLayers, cwdInit, ...rest } = resolved;
|
|
600
|
-
const
|
|
651
|
+
const { contextLayers, extraContextLayers, cwdInit, ...rest } = resolved;
|
|
652
|
+
const baseLayers = contextLayers ?? this._contextLayers;
|
|
653
|
+
const effectiveLayers = extraContextLayers && extraContextLayers.length > 0
|
|
654
|
+
? [
|
|
655
|
+
...(baseLayers ?? []),
|
|
656
|
+
...extraContextLayers,
|
|
657
|
+
]
|
|
658
|
+
: baseLayers;
|
|
601
659
|
const itemSchemas = buildItemSchemaRegistry({
|
|
602
660
|
base: this.itemSchemas,
|
|
603
661
|
layers: effectiveLayers,
|
|
@@ -700,7 +758,7 @@ export class AgentHarness {
|
|
|
700
758
|
a.allocated,
|
|
701
759
|
]));
|
|
702
760
|
}
|
|
703
|
-
async recallLayersAtomic(layers, input, ctx, budgets) {
|
|
761
|
+
async recallLayersAtomic(layers, input, ctx, budgets, opts) {
|
|
704
762
|
return recallLayersAtomic({
|
|
705
763
|
layers,
|
|
706
764
|
query: input,
|
|
@@ -710,9 +768,10 @@ export class AgentHarness {
|
|
|
710
768
|
store: this.layerStateStore,
|
|
711
769
|
itemSchemas: this.itemSchemas,
|
|
712
770
|
forceAtomic: this.config.forceAtomicRecall,
|
|
771
|
+
peek: opts?.peek,
|
|
713
772
|
});
|
|
714
773
|
}
|
|
715
|
-
async recallLayersEventual(layers, input, ctx, budgets) {
|
|
774
|
+
async recallLayersEventual(layers, input, ctx, budgets, opts) {
|
|
716
775
|
return recallLayersEventual({
|
|
717
776
|
layers,
|
|
718
777
|
query: input,
|
|
@@ -723,6 +782,7 @@ export class AgentHarness {
|
|
|
723
782
|
itemSchemas: this.itemSchemas,
|
|
724
783
|
forceAtomic: this.config.forceAtomicRecall,
|
|
725
784
|
cache: this.recallCache,
|
|
785
|
+
peek: opts?.peek,
|
|
726
786
|
});
|
|
727
787
|
}
|
|
728
788
|
/**
|
|
@@ -861,7 +921,11 @@ export class AgentHarness {
|
|
|
861
921
|
* whose `id` is already the original `executionId`.
|
|
862
922
|
*/
|
|
863
923
|
async restore(executionId, opts) {
|
|
864
|
-
|
|
924
|
+
const ctx = await restoreFromCheckpoint(this, executionId, opts);
|
|
925
|
+
if (ctx) {
|
|
926
|
+
this.restoredExecutions.add(ctx.id);
|
|
927
|
+
}
|
|
928
|
+
return ctx;
|
|
865
929
|
}
|
|
866
930
|
/**
|
|
867
931
|
* Discard everything `restore(executionId)` would have used — the snapshot *and* the
|
|
@@ -948,7 +1012,7 @@ export class AgentHarness {
|
|
|
948
1012
|
setLayerState(executionId, layerId, state) {
|
|
949
1013
|
this.layerStateStore.set(executionId, layerId, state);
|
|
950
1014
|
}
|
|
951
|
-
async beforeToolCall(layers, toolName, toolArgs, ctx) {
|
|
1015
|
+
async beforeToolCall(layers, toolName, toolArgs, ctx, callId) {
|
|
952
1016
|
const hasHook = layers.some((l) => l.hooks.beforeToolCall);
|
|
953
1017
|
if (!hasHook) {
|
|
954
1018
|
return {
|
|
@@ -959,6 +1023,7 @@ export class AgentHarness {
|
|
|
959
1023
|
layers,
|
|
960
1024
|
toolName,
|
|
961
1025
|
toolArgs,
|
|
1026
|
+
callId,
|
|
962
1027
|
ctx: this.toExecCtx(ctx),
|
|
963
1028
|
store: this.layerStateStore,
|
|
964
1029
|
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CallModelRequest, DecideRequest, DecideResponse, LLMResponse } from '@noetic-tools/types';
|
|
2
|
+
import type { OpenRouter } from '@openrouter/sdk';
|
|
3
|
+
/** @internal The slice of the SDK client the decide caller uses. */
|
|
4
|
+
export type DecisionsClient = Pick<OpenRouter, 'alpha' | 'models'>;
|
|
5
|
+
/** @internal */
|
|
6
|
+
export interface DecideCallerDeps {
|
|
7
|
+
client?: DecisionsClient;
|
|
8
|
+
callModel: (request: CallModelRequest) => Promise<LLMResponse>;
|
|
9
|
+
/** Per-harness cache: model id → whether it is a decision model (`undefined` while unknown). */
|
|
10
|
+
nativeModels: Map<string, Promise<boolean | undefined>>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Answer a decide request. Decision models (those whose OpenRouter output
|
|
14
|
+
* modality is `decisions`) answer natively with their own distributions; any
|
|
15
|
+
* other model answers through a structured-output call that estimates them.
|
|
16
|
+
*
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
export declare function callDecide(request: DecideRequest, deps: DecideCallerDeps): Promise<DecideResponse>;
|