Polyweave

@polyweave/agent-loop (1.0.1)

Published 2026-07-10 19:58:38 +00:00 by Dvorak

Installation

@polyweave:registry=https://hub.kl1.tenere.ai/api/packages/Polyweave/npm/
npm install @polyweave/agent-loop@1.0.1
"@polyweave/agent-loop": "1.0.1"

About this package

agent-loop

Reusable agent->evaluator execution loop for LLM harnesses. Agent does work, evaluator judges, symbolic state propagates, retry until done.

Published as @polyweave/agent-loop.

Synopsis

import { createAgentLoop, createDefaultEvaluatorParser, NodeStatus } from '@polyweave/agent-loop';

const loop = createAgentLoop({
  agentAdapter: myAgentAdapter,
  evaluatorAdapter: myEvalAdapter,
  buildAgentPrompt: (ctx) => ({ type: 'TEXT', streamId: 's1', content: { text: 'Do task: ' + ctx.task } }),
  buildEvaluatorPrompt: (ctx, history) => ({ type: 'TEXT', streamId: 's2', content: { text: 'Judge this work' } }),
  parseEvaluatorOutput: createDefaultEvaluatorParser(),
  toolAdapters: { my_tool: myToolAdapter },
  maxRetries: 5
});

loop.source.pipe(mySink);
loop.sink.write({ type: 'START' });

Install

npm install @polyweave/agent-loop

Description

The agent-loop package implements a reusable agent-evaluator execution loop over Polyweave push-stream frames. An LLM agent performs work using registered tools, then an evaluator LLM judges the output. Symbolic state propagates between iterations, and the loop retries until the evaluator signals SUCCESS or FAILURE, or the retry limit is reached.

The agent-loop handles lifecycle wrapping (max iterations, aborts), tool routing through @polyweave/tool-adapter, and frame collection from agent and evaluator streams. The loop emits PROGRESS frames for each phase transition so downstream observers can react to node_started, node_completed, node_failed, node_retry, and symbolic_asserted events.

Two loop variants are provided: createAgentLoop runs a single agent-evaluator cycle (with retries), and createLoopingAgentLoop chains multiple inner loops together, advancing through nodes provided by a nodeProvider while a shouldContinue predicate evaluates the shared context.

API

createAgentLoop(options)

Creates an agent-evaluator execution loop.

Parameters:

  • options.agentAdapter (required) — LLM adapter duplex { source, sink } for the agent.
  • options.evaluatorAdapter (required) — LLM adapter duplex { source, sink } for the evaluator.
  • options.buildAgentPrompt(ctx) (required) — Function returning a frame prompt for the agent given cycle context.
  • options.buildEvaluatorPrompt(ctx, agentHistory) (required) — Function returning a frame prompt for the evaluator.
  • options.parseEvaluatorOutput(frames) (required) — Function parsing evaluator frames into { status, evidence, symbolicUpdates }.
  • options.toolAdapters — Object mapping tool names to adapter duplexes.
  • options.context — Initial context including symbolics, runtime, node.
  • options.initialSymbolics — Initial values for the symbolic state.
  • options.maxRetries — Maximum deterministic-check retries (default 5).
  • options.maxIterations — Maximum agent-evaluator cycles (default 10).
  • options.maxBufferedOutputFrames — Output buffer size cap.
  • options.outputOverflowStrategy — Buffer overflow strategy ('drop-oldest').
  • options.onProgress(type, data) — Progress callback.
  • options.deterministicChecks(checksList, toolTrace) — Synchronous check function.
  • options.checksList — Array of check strings (e.g. 'file_exists:output.txt', 'symbolic:done=true', 'stdout_contains:OK', 'exit_code:0').
  • options.toolTimeoutMs — Tool call timeout (default 30000).

Returns: { source, sink, symbolics, context, abort }

createLoopingAgentLoop(options)

Creates a loop that chains multiple inner agent-loop invocations.

Parameters:

  • options.innerLoopFactory(innerOpts) — Factory returning an agent loop (defaults to createAgentLoop).
  • options.shouldContinue(context) — Predicate; when false, the loop completes.
  • options.nodeProvider(context, index) — Returns the next node or null.
  • options.maxIterations — Maximum total loop iterations (default 10).
  • options.context or options.initialContext — Shared context across inner loops.
  • Plus all options forwarded to each inner loop.

Returns: { source, sink, context, abort }

createSymbolicState(initial)

Creates a subscription-based symbolic key-value store.

Returns: { assertValue, retractValue, getValue, getAll, applyUpdate, applyUpdates, subscribe }

createDefaultDeterministicChecks(options)

Returns a function that evaluates check strings for deterministic verification.

Check formats:

  • file_exists:path — Checks file presence in workspace directory.
  • symbolic:name=value — Checks symbolic state value.
  • stdout_contains:pattern — Searches tool trace stdout.
  • exit_code:N — Checks tool trace exit codes.

Returns: { allPassed: boolean, evidence: string }

createDefaultEvaluatorParser()

Returns a function that parses evaluator TEXT frames as JSON into { status, evidence, symbolicUpdates }. Expects JSON with status ('SUCCESS', 'FAILURE', 'RUNNING'), optional evidence, and optional symbolicUpdates array.

NodeStatus

Enum: { PENDING, RUNNING, SUCCESS, FAILURE }.

Examples

// With deterministic checks and file verification
const loop = createAgentLoop({
  agentAdapter,
  evaluatorAdapter,
  toolAdapters: { bash: bashTool },
  buildAgentPrompt: (ctx) => textFrame('Write file output.txt with "hello"'),
  buildEvaluatorPrompt: () => textFrame('Did the agent write the file?'),
  parseEvaluatorOutput: createDefaultEvaluatorParser(),
  deterministicChecks: createDefaultDeterministicChecks({ workspaceDir: '/tmp' }),
  checksList: ['file_exists:output.txt', 'symbolic:done=true'],
  maxRetries: 3
});
// Looping variant with sequential nodes
const loop = createLoopingAgentLoop({
  context: { symbolics: createSymbolicState() },
  nodeProvider: (ctx, i) => nodes[i] || null,
  shouldContinue: (ctx) => ctx.symbolics.getValue('done') !== true,
  maxIterations: 5,
  buildAgentPrompt: (ctx) => textFrame('Step for node: ' + ctx.node),
  buildEvaluatorPrompt: (ctx, h) => textFrame('Review the step'),
  parseEvaluatorOutput: createDefaultEvaluatorParser(),
  agentAdapter,
  evaluatorAdapter
});

Tests

npm test

Tests cover the agent-evaluator loop lifecycle, retry logic, deterministic check evaluation, symbolic state propagation, abort/cancel semantics, frame collection, and the looping agent loop variant.

Requirements

Node.js 18 or later. ESM only.

Caveats

  • Both agentAdapter and evaluatorAdapter must implement the Polyweave frame duplex interface ({ source, sink }).
  • The loop is single-cycle-at-a-time internally (re-entrant via executeFullCycle), but the sink interface serializes starts.
  • Deterministic checks run synchronously and block the agent cycle; for async checks, handle externally.
  • Excessive maxRetries combined with large maxIterations may produce many LLM calls.

License

MIT

Dependencies

Dependencies

ID Version
@polyweave/harness-base *
@polyweave/tool-adapter *
@polyweave/tools *

Development Dependencies

ID Version
@rigor/core *

Peer Dependencies

ID Version
@polyweave/core *

Keywords

polyweave agent-loop evaluator execution-loop push-stream
Details
npm
2026-07-10 19:58:38 +00:00
12
John Dvorak
SEE LICENSE IN LICENSE
8.0 KiB
Assets (1)
Versions (4) View all
1.0.4 2026-07-17
1.0.5 2026-07-11
1.0.2 2026-07-10
1.0.1 2026-07-10