ContextProcessingNodeDefinition
SUPPORT · type · Type contract
Definition accepted by createContextProcessingNode.
It is a normal Core WorkflowNode except that the node body is declared here: plan builds the context, update decides the committed state, and policy/handlers/timeout are validated when the node is created.
Import
import type { ContextProcessingNodeDefinition } from '@jangts/agent-core/support';/** @typedef {import('@jangts/agent-core/support').ContextProcessingNodeDefinition} ContextProcessingNodeDefinition */Other public entry points:@jangts/agent-core/support, @jangts/agent-core/support/context.
Definition
Type: ContextProcessingNodeDefinition<State, Result, App>
Type expression
type ContextProcessingNodeDefinition<State, Result, App> = Omit<
WorkflowNode<State, Result, App>,
'run' | 'report'
> & {
/** Builds the context plan; must be synchronous so budget failures surface before any I/O. */
readonly plan: (state: Readonly<State>, context: WorkflowContext<App>) => ContextPlan;
/** Model preparation request, either static or derived from the current node state. */
readonly request?:
| (ModelPrepareRequest<string | ModelTransportDefinition> & { modelId?: string })
| ((
state: Readonly<State>,
context: WorkflowContext<App>,
) => ModelPrepareRequest<string | ModelTransportDefinition> & { modelId?: string });
/** Maps the node result to the state fields to commit; return only changed fields. */
readonly update: (result: ContextNodeResult, state: Readonly<State>) => Partial<State>;
/** Optional prebuilt model call, used instead of preparing `request`. */
readonly prepared?: (state: Readonly<State>, context: WorkflowContext<App>) => PreparedModelCall;
/** Processing policy; omitted means `validate` (no reduction, audit checks only). */
readonly policy?: ContextPolicy;
/** Compressors referenced by compression steps; validated against the policy at creation. */
readonly compressors?: readonly ContextCompressor[];
/** Selectors referenced by model-driven selection; validated against the policy at creation. */
readonly selectors?: readonly ContextSelector[];
/** Per-node timeout in milliseconds; defaults to the Core call timeout. */
readonly timeoutMs?: number;
};Type parameters
| Type parameter | Constraint | Default type |
|---|---|---|
State | — | — |
Result | — | — |
App | — | — |
Field structure
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Required | Read-only. Stable node id referenced by routes and error handling. |
description | string | Required | Read-only. Required explanation of what the node does; also used in diagnostics. |
name | string | undefined | Optional | Read-only. Display name; defaults to id when omitted. |
requires | readonly (keyof State & string)[] | undefined | Optional | Read-only. State keys that must be present before this node runs; enforced by the engine. |
provides | readonly (keyof State & string)[] | undefined | Optional | Read-only. State keys this node is expected to produce; validated after run. |
routes | readonly WorkflowRoute<State, Result, App>[] | Required | Read-only. Candidate successors, evaluated in order. A getter must be pure; registration reads it once. |
startsRound | boolean | undefined | Optional | Read-only. Reserves a fresh round on entry. Use it for steps that may legitimately repeat; entering such a node when the round budget is exhausted fails with the configured exhausted error. |
onErrorNodeId | string | undefined | Optional | Read-only. Node entered when run/provides/route fails; contract errors always propagate instead. |
onErrorRouteId | string | undefined | Optional | Read-only. Route id reported with the recovery transition. |
plan | (state: Readonly<State>, context: WorkflowContext<App>) => ContextPlan | Required | Read-only. Builds the context plan; must be synchronous so budget failures surface before any I/O. |
request | (ModelPrepareRequest<string | ModelTransportDefinition<unknown>> & { modelId?: string; }) | ((state: Readonly<State>, context: WorkflowContext<App>) => ModelPrepareRequest<string | ModelTransportDefinition> & { modelId?: string; }) | undefined | Optional | Read-only. Model preparation request, either static or derived from the current node state. |
update | (result: ContextNodeResult, state: Readonly<State>) => Partial<State> | Required | Read-only. Maps the node result to the state fields to commit; return only changed fields. |
prepared | ((state: Readonly<State>, context: WorkflowContext<App>) => PreparedModelCall) | undefined | Optional | Read-only. Optional prebuilt model call, used instead of preparing request. |
policy | ContextPolicy | undefined | Optional | Read-only. Processing policy; omitted means validate (no reduction, audit checks only). |
compressors | readonly ContextCompressor[] | undefined | Optional | Read-only. Compressors referenced by compression steps; validated against the policy at creation. |
selectors | readonly ContextSelector[] | undefined | Optional | Read-only. Selectors referenced by model-driven selection; validated against the policy at creation. |
timeoutMs | number | undefined | Optional | Read-only. Per-node timeout in milliseconds; defaults to the Core call timeout. |
Member index
| Member | Purpose |
|---|---|
plan | Builds the context plan; must be synchronous so budget failures surface before any I/O. |
update | Maps the node result to the state fields to commit; return only changed fields. |
Member details
plan
Builds the context plan; must be synchronous so budget failures surface before any I/O.
ContextProcessingNodeDefinition.plan(state: Readonly<State>, context: WorkflowContext<App>): ContextPlan| Parameter | Type | Passing | Description |
|---|---|---|---|
state | Readonly<State> | Required | — |
context | WorkflowContext<App> | Required | — |
Fields of context
| Field | Type | Required | Description |
|---|---|---|---|
roundIndex | number | Required | Read-only. Zero-based bounded round; phases within a round do not consume another round. |
report | (update: WorkflowNodeReport) => void | Required | Report progress while this node is active; Core supplies its execution identity. |
diagnostics | DiagnosticContext | undefined | Optional | Read-only. Diagnostic scope of this node visit, when recording is enabled. |
model | WorkflowModel | Required | Read-only. Model access limited by the workflow's configured calls and budget. |
tools | { invoke(request: Omit<import("../capabilities/tools.js").ToolInvocationRequest, "diagnostics">): Promise<import("../invocation.js").ToolResult<unknown>>; } | Required | Read-only. See the type definition and usage context. |
error | unknown | Optional | Read-only. Failure that led into this node, present only while entering an onErrorNodeId target. |
errorEventId | string | undefined | Optional | Read-only. Event id of that failure, for correlation in host logs. |
app | unknown extends App ? App : Readonly<App> | Required | Read-only. See the type definition and usage context. |
logger | SubFlowLog | undefined | Optional | Read-only. Explicit optional recorder for this operation; never inferred from diagnostics. |
signal | AbortSignal | Required | Read-only. Non-optional cancellation signal; never undefined once execution has started. |
createSubFlowLogger | () => SubFlowLogger | Required | Bound to this context and owned by runtime; repeated calls in one visit return the same instance. |
cache | NodeCache | Required | Read-only. See the type definition and usage context. |
agentCache | NodeAgentCache | undefined | Optional | Read-only. See the type definition and usage context. |
Returns: ContextPlan.
Return value structure
| Field | Type | Required | Description |
|---|---|---|---|
fragments | readonly ContextFragment[] | Required | Read-only. Fragments in the order supplied by the App; assembly preserves it. |
maxInputChars | number | Required | Character budget Core validates after assembly. |
additionalMessages | readonly (ModelMessage & { role: "user"; })[] | undefined | Optional | Protected user messages (for example image inputs); text counts toward the same budget. |
originalFragments | readonly ContextFragment[] | undefined | Optional | Trusted originals for structural mutation auditing; never sent to the model. |
mutableFragmentIds | readonly string[] | undefined | Optional | Explicit value-mutation allowance when originals are supplied. |
update
Maps the node result to the state fields to commit; return only changed fields.
ContextProcessingNodeDefinition.update(result: ContextNodeResult, state: Readonly<State>): Partial<State>| Parameter | Type | Passing | Description |
|---|---|---|---|
result | ContextNodeResult | Required | — |
state | Readonly<State> | Required | — |
Fields of result
| Field | Type | Required | Description |
|---|---|---|---|
input | BuiltContext | Required | Read-only. Context built from the processed fragments, sized against the full input limit. |
prepared | PreparedModelCall<unknown> | Required | Read-only. Model call prepared for this stage; carries measurement and remaining call budget. |
report | ContextProcessingReport | Required | Read-only. What processing changed: characters, token usage, selection decisions and reductions. |
Returns: Partial<State>.
Examples
- Context processing is an ordinary Workflow node: Integrate selection, trimming, and compression through normal nodes and routes; Core records and schedules them uniformly.
Validate and assemble only, without compression yet
Beginner · Node.js
import { AgentCoreEngine, defineWorkflow, route } from '@jangts/agent-core/engine';
import { Support } from '@jangts/agent-core';
import type { BuiltContext, PreparedModelCall } from '@jangts/agent-core/types';
type State = { question: string; input?: BuiltContext; prepared?: PreparedModelCall; answer?: string };
const builder = Support.defineContextFragments<State>([
{ id: 'question', sourceId: 'request', trust: 'user', lifetime: 'turn', role: 'user', maxChars: 1000, format: state => state.question },
], { maxInputChars: 2000 });
const prepare = Support.createContextProcessingNode<State, string>({
id: 'prepare', description: '装配并校验输入', startsRound: true,
plan: state => builder.plan({ input: state }),
update: result => ({ input: result.input, prepared: result.prepared }),
routes: [route.to('reply')],
});
const workflow = defineWorkflow<State, string>({ id: 'chat', nodes: [prepare, {
id: 'reply', description: '使用同次准备回答', requires: ['input', 'prepared'],
async run(state, { model }) { const { value } = await model.invoke({ input: state.input!, prepared: state.prepared! }); return { answer: String(value) }; },
routes: [route.deliver('answer')],
}] });
const agent = new AgentCoreEngine({ workflow, model: { provider: { async *stream() { yield '服务正常'; } } } });
console.log(await agent.run({ question: '状态?' }));import { AgentCoreEngine, defineWorkflow, route } from '@jangts/agent-core/engine';
import { Support } from '@jangts/agent-core';
const builder = Support.defineContextFragments([
{ id: 'question', sourceId: 'request', trust: 'user', lifetime: 'turn', role: 'user', maxChars: 1000, format: state => state.question },
], { maxInputChars: 2000 });
const prepare = Support.createContextProcessingNode({
id: 'prepare', description: '装配并校验输入', startsRound: true,
plan: state => builder.plan({ input: state }),
update: result => ({ input: result.input, prepared: result.prepared }),
routes: [route.to('reply')],
});
const workflow = defineWorkflow({ id: 'chat', nodes: [prepare, {
id: 'reply', description: '使用同次准备回答', requires: ['input', 'prepared'],
async run(state, { model }) { const { value } = await model.invoke({ input: state.input, prepared: state.prepared }); return { answer: String(value) }; },
routes: [route.deliver('answer')],
}] });
const agent = new AgentCoreEngine({ workflow, model: { provider: { async *stream() { yield '服务正常'; } } } });
console.log(await agent.run({ question: '状态?' }));Add a history trimming policy to the same node contract
Configuration · Node.js
Put this node back into the same Workflow as the previous example; each run just adds history to State, and the model reply node does not change its interface. In a complete flow the reply target must exist.
import { Support } from '@jangts/agent-core';
import { route } from '@jangts/agent-core/engine';
import type { BuiltContext, PreparedModelCall } from '@jangts/agent-core/types';
type State = { question: string; history: { role: string; content: string }[]; input?: BuiltContext; prepared?: PreparedModelCall };
const builder = Support.defineContextFragments<State>([
{ id: 'question', sourceId: 'request', trust: 'user', lifetime: 'turn', role: 'user', maxChars: 1000, format: state => state.question },
{ id: 'history', sourceId: 'conversation', trust: 'user', lifetime: 'turn', role: 'user', maxChars: 10000, format: state => ({ messages: state.history }) },
], { maxInputChars: 1200 });
const prepare = Support.createContextProcessingNode<State, string>({
id: 'prepare', description: '按需要裁切旧历史', startsRound: true,
plan: state => builder.plan({ input: state }),
policy: { mode: 'reduce', steps: [{ id: 'recent', kind: 'trim', fragmentId: 'history', field: 'messages',
targetChars: 700, grouping: { kind: 'user-turn' }, keepGroups: 1 }] },
update: result => ({ input: result.input, prepared: result.prepared }),
routes: [route.to('reply')],
});
console.log(prepare.id);import { Support } from '@jangts/agent-core';
import { route } from '@jangts/agent-core/engine';
const builder = Support.defineContextFragments([
{ id: 'question', sourceId: 'request', trust: 'user', lifetime: 'turn', role: 'user', maxChars: 1000, format: state => state.question },
{ id: 'history', sourceId: 'conversation', trust: 'user', lifetime: 'turn', role: 'user', maxChars: 10000, format: state => ({ messages: state.history }) },
], { maxInputChars: 1200 });
const prepare = Support.createContextProcessingNode({
id: 'prepare', description: '按需要裁切旧历史', startsRound: true,
plan: state => builder.plan({ input: state }),
policy: { mode: 'reduce', steps: [{ id: 'recent', kind: 'trim', fragmentId: 'history', field: 'messages',
targetChars: 700, grouping: { kind: 'user-turn' }, keepGroups: 1 }] },
update: result => ({ input: result.input, prepared: result.prepared }),
routes: [route.to('reply')],
});
console.log(prepare.id);