@autonome-research/thread-phase 3.0.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/LICENSE +21 -0
- package/README.md +226 -0
- package/dist/agent/index.d.ts +28 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +28 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/openai-adapter.d.ts +15 -0
- package/dist/agent/openai-adapter.d.ts.map +1 -0
- package/dist/agent/openai-adapter.js +57 -0
- package/dist/agent/openai-adapter.js.map +1 -0
- package/dist/agent/parse-json.d.ts +12 -0
- package/dist/agent/parse-json.d.ts.map +1 -0
- package/dist/agent/parse-json.js +31 -0
- package/dist/agent/parse-json.js.map +1 -0
- package/dist/agent/retry.d.ts +15 -0
- package/dist/agent/retry.d.ts.map +1 -0
- package/dist/agent/retry.js +35 -0
- package/dist/agent/retry.js.map +1 -0
- package/dist/agent/runner.d.ts +25 -0
- package/dist/agent/runner.d.ts.map +1 -0
- package/dist/agent/runner.js +270 -0
- package/dist/agent/runner.js.map +1 -0
- package/dist/agent/stream-consumer.d.ts +57 -0
- package/dist/agent/stream-consumer.d.ts.map +1 -0
- package/dist/agent/stream-consumer.js +126 -0
- package/dist/agent/stream-consumer.js.map +1 -0
- package/dist/agent/types.d.ts +135 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +9 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/agent-runner.d.ts +10 -0
- package/dist/agent-runner.d.ts.map +1 -0
- package/dist/agent-runner.js +10 -0
- package/dist/agent-runner.js.map +1 -0
- package/dist/agents/capability.d.ts +36 -0
- package/dist/agents/capability.d.ts.map +1 -0
- package/dist/agents/capability.js +51 -0
- package/dist/agents/capability.js.map +1 -0
- package/dist/agents/event-bus.d.ts +20 -0
- package/dist/agents/event-bus.d.ts.map +1 -0
- package/dist/agents/event-bus.js +40 -0
- package/dist/agents/event-bus.js.map +1 -0
- package/dist/agents/index.d.ts +23 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +33 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/inference-adapter.d.ts +52 -0
- package/dist/agents/inference-adapter.d.ts.map +1 -0
- package/dist/agents/inference-adapter.js +209 -0
- package/dist/agents/inference-adapter.js.map +1 -0
- package/dist/agents/job-store-bridge.d.ts +44 -0
- package/dist/agents/job-store-bridge.d.ts.map +1 -0
- package/dist/agents/job-store-bridge.js +58 -0
- package/dist/agents/job-store-bridge.js.map +1 -0
- package/dist/agents/memory.d.ts +40 -0
- package/dist/agents/memory.d.ts.map +1 -0
- package/dist/agents/memory.js +14 -0
- package/dist/agents/memory.js.map +1 -0
- package/dist/agents/protocol.d.ts +302 -0
- package/dist/agents/protocol.d.ts.map +1 -0
- package/dist/agents/protocol.js +36 -0
- package/dist/agents/protocol.js.map +1 -0
- package/dist/agents/run-helpers.d.ts +70 -0
- package/dist/agents/run-helpers.d.ts.map +1 -0
- package/dist/agents/run-helpers.js +131 -0
- package/dist/agents/run-helpers.js.map +1 -0
- package/dist/agents/serialize-error.d.ts +18 -0
- package/dist/agents/serialize-error.d.ts.map +1 -0
- package/dist/agents/serialize-error.js +27 -0
- package/dist/agents/serialize-error.js.map +1 -0
- package/dist/agents/structured-output.d.ts +90 -0
- package/dist/agents/structured-output.d.ts.map +1 -0
- package/dist/agents/structured-output.js +101 -0
- package/dist/agents/structured-output.js.map +1 -0
- package/dist/agents/test-utils/conformance.d.ts +59 -0
- package/dist/agents/test-utils/conformance.d.ts.map +1 -0
- package/dist/agents/test-utils/conformance.js +207 -0
- package/dist/agents/test-utils/conformance.js.map +1 -0
- package/dist/agents/test-utils/index.d.ts +12 -0
- package/dist/agents/test-utils/index.d.ts.map +1 -0
- package/dist/agents/test-utils/index.js +12 -0
- package/dist/agents/test-utils/index.js.map +1 -0
- package/dist/agents/test-utils/mock-agent.d.ts +66 -0
- package/dist/agents/test-utils/mock-agent.d.ts.map +1 -0
- package/dist/agents/test-utils/mock-agent.js +244 -0
- package/dist/agents/test-utils/mock-agent.js.map +1 -0
- package/dist/agents/thread.d.ts +57 -0
- package/dist/agents/thread.d.ts.map +1 -0
- package/dist/agents/thread.js +128 -0
- package/dist/agents/thread.js.map +1 -0
- package/dist/agents/turn-accumulator.d.ts +94 -0
- package/dist/agents/turn-accumulator.d.ts.map +1 -0
- package/dist/agents/turn-accumulator.js +150 -0
- package/dist/agents/turn-accumulator.js.map +1 -0
- package/dist/agents/with-memory.d.ts +55 -0
- package/dist/agents/with-memory.d.ts.map +1 -0
- package/dist/agents/with-memory.js +155 -0
- package/dist/agents/with-memory.js.map +1 -0
- package/dist/agents/with-thread.d.ts +45 -0
- package/dist/agents/with-thread.d.ts.map +1 -0
- package/dist/agents/with-thread.js +70 -0
- package/dist/agents/with-thread.js.map +1 -0
- package/dist/cache.d.ts +47 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +81 -0
- package/dist/cache.js.map +1 -0
- package/dist/context/compressor.d.ts +36 -0
- package/dist/context/compressor.d.ts.map +1 -0
- package/dist/context/compressor.js +158 -0
- package/dist/context/compressor.js.map +1 -0
- package/dist/context/index.d.ts +4 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +4 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/result-capper.d.ts +32 -0
- package/dist/context/result-capper.d.ts.map +1 -0
- package/dist/context/result-capper.js +50 -0
- package/dist/context/result-capper.js.map +1 -0
- package/dist/context/token-budget.d.ts +81 -0
- package/dist/context/token-budget.d.ts.map +1 -0
- package/dist/context/token-budget.js +99 -0
- package/dist/context/token-budget.js.map +1 -0
- package/dist/helpers/caller.d.ts +18 -0
- package/dist/helpers/caller.d.ts.map +1 -0
- package/dist/helpers/caller.js +40 -0
- package/dist/helpers/caller.js.map +1 -0
- package/dist/helpers/hook.d.ts +73 -0
- package/dist/helpers/hook.d.ts.map +1 -0
- package/dist/helpers/hook.js +244 -0
- package/dist/helpers/hook.js.map +1 -0
- package/dist/helpers/index.d.ts +12 -0
- package/dist/helpers/index.d.ts.map +1 -0
- package/dist/helpers/index.js +11 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/helpers/one-shot.d.ts +27 -0
- package/dist/helpers/one-shot.d.ts.map +1 -0
- package/dist/helpers/one-shot.js +43 -0
- package/dist/helpers/one-shot.js.map +1 -0
- package/dist/helpers/schedule.d.ts +59 -0
- package/dist/helpers/schedule.d.ts.map +1 -0
- package/dist/helpers/schedule.js +118 -0
- package/dist/helpers/schedule.js.map +1 -0
- package/dist/helpers/types.d.ts +34 -0
- package/dist/helpers/types.d.ts.map +1 -0
- package/dist/helpers/types.js +11 -0
- package/dist/helpers/types.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/inference.d.ts +27 -0
- package/dist/inference.d.ts.map +1 -0
- package/dist/inference.js +34 -0
- package/dist/inference.js.map +1 -0
- package/dist/messages.d.ts +64 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +17 -0
- package/dist/messages.js.map +1 -0
- package/dist/orchestrator.d.ts +56 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +62 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/patterns/bounded-fanout-of.d.ts +61 -0
- package/dist/patterns/bounded-fanout-of.d.ts.map +1 -0
- package/dist/patterns/bounded-fanout-of.js +142 -0
- package/dist/patterns/bounded-fanout-of.js.map +1 -0
- package/dist/patterns/bounded-fanout.d.ts +111 -0
- package/dist/patterns/bounded-fanout.d.ts.map +1 -0
- package/dist/patterns/bounded-fanout.js +151 -0
- package/dist/patterns/bounded-fanout.js.map +1 -0
- package/dist/patterns/index.d.ts +14 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/patterns/index.js +13 -0
- package/dist/patterns/index.js.map +1 -0
- package/dist/patterns/intent-gate.d.ts +27 -0
- package/dist/patterns/intent-gate.d.ts.map +1 -0
- package/dist/patterns/intent-gate.js +32 -0
- package/dist/patterns/intent-gate.js.map +1 -0
- package/dist/patterns/match.d.ts +30 -0
- package/dist/patterns/match.d.ts.map +1 -0
- package/dist/patterns/match.js +58 -0
- package/dist/patterns/match.js.map +1 -0
- package/dist/patterns/parallel-fanout.d.ts +28 -0
- package/dist/patterns/parallel-fanout.d.ts.map +1 -0
- package/dist/patterns/parallel-fanout.js +24 -0
- package/dist/patterns/parallel-fanout.js.map +1 -0
- package/dist/patterns/parallel-phases.d.ts +27 -0
- package/dist/patterns/parallel-phases.d.ts.map +1 -0
- package/dist/patterns/parallel-phases.js +77 -0
- package/dist/patterns/parallel-phases.js.map +1 -0
- package/dist/patterns/preflight-confidence.d.ts +20 -0
- package/dist/patterns/preflight-confidence.d.ts.map +1 -0
- package/dist/patterns/preflight-confidence.js +38 -0
- package/dist/patterns/preflight-confidence.js.map +1 -0
- package/dist/patterns/spot-check.d.ts +19 -0
- package/dist/patterns/spot-check.d.ts.map +1 -0
- package/dist/patterns/spot-check.js +33 -0
- package/dist/patterns/spot-check.js.map +1 -0
- package/dist/patterns/sub-pipeline.d.ts +84 -0
- package/dist/patterns/sub-pipeline.d.ts.map +1 -0
- package/dist/patterns/sub-pipeline.js +90 -0
- package/dist/patterns/sub-pipeline.js.map +1 -0
- package/dist/patterns/synthesize-with-followup.d.ts +35 -0
- package/dist/patterns/synthesize-with-followup.d.ts.map +1 -0
- package/dist/patterns/synthesize-with-followup.js +45 -0
- package/dist/patterns/synthesize-with-followup.js.map +1 -0
- package/dist/patterns/while-condition.d.ts +31 -0
- package/dist/patterns/while-condition.d.ts.map +1 -0
- package/dist/patterns/while-condition.js +59 -0
- package/dist/patterns/while-condition.js.map +1 -0
- package/dist/patterns/with-retry.d.ts +37 -0
- package/dist/patterns/with-retry.d.ts.map +1 -0
- package/dist/patterns/with-retry.js +73 -0
- package/dist/patterns/with-retry.js.map +1 -0
- package/dist/phase.d.ts +78 -0
- package/dist/phase.d.ts.map +1 -0
- package/dist/phase.js +36 -0
- package/dist/phase.js.map +1 -0
- package/dist/session/index.d.ts +5 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +4 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/job-runner.d.ts +67 -0
- package/dist/session/job-runner.d.ts.map +1 -0
- package/dist/session/job-runner.js +131 -0
- package/dist/session/job-runner.js.map +1 -0
- package/dist/session/job-store.d.ts +98 -0
- package/dist/session/job-store.d.ts.map +1 -0
- package/dist/session/job-store.js +37 -0
- package/dist/session/job-store.js.map +1 -0
- package/dist/session/sqlite-job-store.d.ts +40 -0
- package/dist/session/sqlite-job-store.d.ts.map +1 -0
- package/dist/session/sqlite-job-store.js +200 -0
- package/dist/session/sqlite-job-store.js.map +1 -0
- package/dist/session/sse.d.ts +60 -0
- package/dist/session/sse.d.ts.map +1 -0
- package/dist/session/sse.js +97 -0
- package/dist/session/sse.js.map +1 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +2 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/registry.d.ts +44 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +74 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/triggers/index.d.ts +15 -0
- package/dist/triggers/index.d.ts.map +1 -0
- package/dist/triggers/index.js +14 -0
- package/dist/triggers/index.js.map +1 -0
- package/dist/triggers/run-trigger.d.ts +86 -0
- package/dist/triggers/run-trigger.d.ts.map +1 -0
- package/dist/triggers/run-trigger.js +146 -0
- package/dist/triggers/run-trigger.js.map +1 -0
- package/dist/triggers/timer-trigger.d.ts +46 -0
- package/dist/triggers/timer-trigger.d.ts.map +1 -0
- package/dist/triggers/timer-trigger.js +74 -0
- package/dist/triggers/timer-trigger.js.map +1 -0
- package/dist/triggers/types.d.ts +61 -0
- package/dist/triggers/types.d.ts.map +1 -0
- package/dist/triggers/types.js +23 -0
- package/dist/triggers/types.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent runner — the tool-use loop primitive.
|
|
3
|
+
*
|
|
4
|
+
* Given an agent config (system prompt, tools, model tier), a starting
|
|
5
|
+
* conversation, and an executor for the tools, runs an iterated tool-use
|
|
6
|
+
* loop against an OpenAI-compatible inference endpoint until the agent
|
|
7
|
+
* produces final text or hits its round budget.
|
|
8
|
+
*
|
|
9
|
+
* Composed from focused helpers in this directory:
|
|
10
|
+
* - `./types.ts` — public surface
|
|
11
|
+
* - `./openai-adapter.ts` — Message↔OpenAI wire-format translation
|
|
12
|
+
* - `./stream-consumer.ts` — folds streaming chunks into one round's state
|
|
13
|
+
* - `./retry.ts` — error classification (retryable, abort)
|
|
14
|
+
*
|
|
15
|
+
* What the loop owns:
|
|
16
|
+
* - Round budgeting and the compress / hard-stop transitions
|
|
17
|
+
* - Streaming the request, dispatching tools, collecting results
|
|
18
|
+
* - Cumulative usage / executedToolCalls accounting across rounds
|
|
19
|
+
* - Cancellation observation (AbortSignal in options)
|
|
20
|
+
* - The verifyResult hook, the parser-mismatch warning, the retry loop
|
|
21
|
+
*/
|
|
22
|
+
import { TokenBudgetTracker, BudgetStatus, } from '../context/token-budget.js';
|
|
23
|
+
import { TruncateAndCacheResultCapper } from '../context/result-capper.js';
|
|
24
|
+
import { DeterministicCompressor, AggressiveCompressor, sanitizeToolPairs, } from '../context/compressor.js';
|
|
25
|
+
import { toOpenAIMessages, toOpenAITools } from './openai-adapter.js';
|
|
26
|
+
import { consumeStream, looksLikeToolCallText } from './stream-consumer.js';
|
|
27
|
+
import { isRetryableError, isAbortError } from './retry.js';
|
|
28
|
+
export async function runAgentWithTools(config, initialMessages, options, agentLabel) {
|
|
29
|
+
const label = agentLabel ?? config.name;
|
|
30
|
+
const activity = [];
|
|
31
|
+
const executedToolCalls = [];
|
|
32
|
+
const cumulativeUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
|
33
|
+
const cache = options.cache ?? null;
|
|
34
|
+
const maxRetries = options.maxRetries ?? 1;
|
|
35
|
+
const signal = options.signal;
|
|
36
|
+
const emit = options.onStreamEvent;
|
|
37
|
+
const budgetTracker = options.budgetTracker ?? new TokenBudgetTracker();
|
|
38
|
+
const cap = budgetTracker.getResultCap();
|
|
39
|
+
const protect = budgetTracker.getProtectCounts();
|
|
40
|
+
const resultCapper = options.resultCapper ?? new TruncateAndCacheResultCapper(cap.maxChars, cap.previewChars);
|
|
41
|
+
const compressor = options.compressor ?? new DeterministicCompressor();
|
|
42
|
+
const aggressiveCompressor = options.aggressiveCompressor ?? new AggressiveCompressor();
|
|
43
|
+
// Helper: compose the final result, run the verifyResult hook (if any),
|
|
44
|
+
// and surface a clean error result if the hook rejects.
|
|
45
|
+
const finalize = async (text, finishReason) => {
|
|
46
|
+
let result = {
|
|
47
|
+
text,
|
|
48
|
+
activity,
|
|
49
|
+
finishReason,
|
|
50
|
+
usage: cumulativeUsage,
|
|
51
|
+
executedToolCalls,
|
|
52
|
+
};
|
|
53
|
+
if (options.verifyResult) {
|
|
54
|
+
try {
|
|
55
|
+
const verified = await options.verifyResult(result);
|
|
56
|
+
if (verified)
|
|
57
|
+
result = verified;
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
const e = err;
|
|
61
|
+
activity.push({
|
|
62
|
+
agent: label,
|
|
63
|
+
action: 'verify_failed',
|
|
64
|
+
detail: e.message?.slice(0, 200),
|
|
65
|
+
});
|
|
66
|
+
return {
|
|
67
|
+
...result,
|
|
68
|
+
text: JSON.stringify({ _error: true, message: e.message ?? 'verifyResult threw' }),
|
|
69
|
+
finishReason: 'error',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
};
|
|
75
|
+
// Surface a clear cancellation result when the caller aborts before the
|
|
76
|
+
// first round.
|
|
77
|
+
if (signal?.aborted) {
|
|
78
|
+
activity.push({ agent: label, action: 'aborted', detail: 'signal aborted before first round' });
|
|
79
|
+
return finalize('', 'error');
|
|
80
|
+
}
|
|
81
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
82
|
+
try {
|
|
83
|
+
let localMessages = [...initialMessages];
|
|
84
|
+
let forceOutput = false;
|
|
85
|
+
for (let round = 0; round < config.maxToolRounds; round++) {
|
|
86
|
+
if (signal?.aborted) {
|
|
87
|
+
activity.push({ agent: label, action: 'aborted', detail: `aborted before round ${round}` });
|
|
88
|
+
return finalize([...localMessages]
|
|
89
|
+
.reverse()
|
|
90
|
+
.find((m) => m.role === 'assistant')?.content ?? '', 'error');
|
|
91
|
+
}
|
|
92
|
+
// --- Layer 2: budget check (compress / hard-stop) ---
|
|
93
|
+
const budget = budgetTracker.check(config.systemPrompt, localMessages, forceOutput ? [] : config.tools);
|
|
94
|
+
if (budget.status === BudgetStatus.HARD_STOP && !forceOutput) {
|
|
95
|
+
activity.push({
|
|
96
|
+
agent: label,
|
|
97
|
+
action: 'budget_hard_stop',
|
|
98
|
+
detail: `${budget.estimatedTokens} tokens (${(budget.budgetUsed * 100).toFixed(0)}%) — aggressive compression`,
|
|
99
|
+
});
|
|
100
|
+
localMessages = aggressiveCompressor.compress(localMessages, {
|
|
101
|
+
protectFirst: protect.protectFirst,
|
|
102
|
+
protectLast: protect.protectLastAggressive,
|
|
103
|
+
activityLog: activity,
|
|
104
|
+
});
|
|
105
|
+
localMessages = sanitizeToolPairs(localMessages);
|
|
106
|
+
forceOutput = true;
|
|
107
|
+
}
|
|
108
|
+
else if (budget.status === BudgetStatus.COMPRESS) {
|
|
109
|
+
activity.push({
|
|
110
|
+
agent: label,
|
|
111
|
+
action: 'budget_compress',
|
|
112
|
+
detail: `${budget.estimatedTokens} tokens (${(budget.budgetUsed * 100).toFixed(0)}%) — compressing old results`,
|
|
113
|
+
});
|
|
114
|
+
localMessages = compressor.compress(localMessages, {
|
|
115
|
+
protectFirst: protect.protectFirst,
|
|
116
|
+
protectLast: protect.protectLast,
|
|
117
|
+
activityLog: activity,
|
|
118
|
+
});
|
|
119
|
+
localMessages = sanitizeToolPairs(localMessages);
|
|
120
|
+
}
|
|
121
|
+
const isLastRound = round === config.maxToolRounds - 1 || forceOutput;
|
|
122
|
+
// The nudge only makes sense when the agent has tools; injecting it
|
|
123
|
+
// for a no-tools agent confuses the model into returning empty content.
|
|
124
|
+
const shouldNudge = isLastRound && config.tools.length > 0;
|
|
125
|
+
const messagesForRequest = shouldNudge
|
|
126
|
+
? [
|
|
127
|
+
...localMessages,
|
|
128
|
+
{
|
|
129
|
+
role: 'user',
|
|
130
|
+
content: 'You are running out of tool rounds. Please produce your final output now based on what you have found so far.',
|
|
131
|
+
},
|
|
132
|
+
]
|
|
133
|
+
: localMessages;
|
|
134
|
+
const openAiMessages = toOpenAIMessages(config.systemPrompt, messagesForRequest);
|
|
135
|
+
// The openai SDK serializes `tools: undefined` as `"tools": null` in
|
|
136
|
+
// the JSON body, which trips up vLLM/Qwen into a reasoning-only loop.
|
|
137
|
+
// Only set the field when we actually want tools available.
|
|
138
|
+
const sendTools = !isLastRound && config.tools.length > 0;
|
|
139
|
+
// extraBody first so anything the runner sets explicitly wins on key collision.
|
|
140
|
+
const requestBody = {
|
|
141
|
+
...config.extraBody,
|
|
142
|
+
model: config.model,
|
|
143
|
+
max_tokens: config.maxTokens,
|
|
144
|
+
messages: openAiMessages,
|
|
145
|
+
stream: true,
|
|
146
|
+
// Ask backends that support it to include usage in the terminal
|
|
147
|
+
// chunk. Backends that don't support it ignore the field.
|
|
148
|
+
stream_options: { include_usage: true },
|
|
149
|
+
};
|
|
150
|
+
if (sendTools) {
|
|
151
|
+
requestBody.tools = toOpenAITools(config.tools);
|
|
152
|
+
}
|
|
153
|
+
const stream = await options.client.chat.completions.create(requestBody, {
|
|
154
|
+
signal,
|
|
155
|
+
});
|
|
156
|
+
const round_ = await consumeStream(stream, (delta) => {
|
|
157
|
+
emit?.({ type: 'content_delta', agent: label, delta });
|
|
158
|
+
});
|
|
159
|
+
// Roll usage forward across rounds.
|
|
160
|
+
cumulativeUsage.promptTokens += round_.usage.promptTokens;
|
|
161
|
+
cumulativeUsage.completionTokens += round_.usage.completionTokens;
|
|
162
|
+
cumulativeUsage.totalTokens += round_.usage.totalTokens;
|
|
163
|
+
// Inference-provider parser-mismatch warning: tools[] was sent but
|
|
164
|
+
// the model returned plain content shaped like a tool call. Likely
|
|
165
|
+
// a missing or wrong --tool-call-parser on the backend.
|
|
166
|
+
if (sendTools &&
|
|
167
|
+
round_.toolCalls.length === 0 &&
|
|
168
|
+
looksLikeToolCallText(round_.content)) {
|
|
169
|
+
const hint = 'tools[] was sent but model returned tool-call-shaped content as plain text. ' +
|
|
170
|
+
'Likely a missing or wrong --tool-call-parser on the inference backend.';
|
|
171
|
+
activity.push({ agent: label, action: 'parser_mismatch_warning', detail: hint });
|
|
172
|
+
// eslint-disable-next-line no-console
|
|
173
|
+
console.warn(`[${label}] ${hint} Preview: ${round_.content.slice(0, 160)}`);
|
|
174
|
+
}
|
|
175
|
+
const assistantMsg = {
|
|
176
|
+
role: 'assistant',
|
|
177
|
+
content: round_.content,
|
|
178
|
+
toolCalls: round_.toolCalls,
|
|
179
|
+
};
|
|
180
|
+
localMessages.push(assistantMsg);
|
|
181
|
+
if (assistantMsg.content.trim() && assistantMsg.toolCalls.length > 0) {
|
|
182
|
+
activity.push({
|
|
183
|
+
agent: label,
|
|
184
|
+
action: 'reasoning',
|
|
185
|
+
detail: assistantMsg.content.trim().slice(0, 200),
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
emit?.({
|
|
189
|
+
type: 'round_complete',
|
|
190
|
+
agent: label,
|
|
191
|
+
round,
|
|
192
|
+
finishReason: round_.finishReason,
|
|
193
|
+
});
|
|
194
|
+
// No tool calls → final output, return.
|
|
195
|
+
if (assistantMsg.toolCalls.length === 0) {
|
|
196
|
+
return finalize(assistantMsg.content, round_.finishReason);
|
|
197
|
+
}
|
|
198
|
+
// Log tool calls + emit lifecycle events.
|
|
199
|
+
for (const tc of assistantMsg.toolCalls) {
|
|
200
|
+
const summary = Object.entries(tc.input)
|
|
201
|
+
.map(([k, v]) => `${k}=${JSON.stringify(v)}`.slice(0, 60))
|
|
202
|
+
.join(', ');
|
|
203
|
+
activity.push({
|
|
204
|
+
agent: label,
|
|
205
|
+
action: `tool:${tc.name}`,
|
|
206
|
+
detail: summary.slice(0, 120),
|
|
207
|
+
});
|
|
208
|
+
emit?.({ type: 'tool_call_started', agent: label, toolCall: tc });
|
|
209
|
+
}
|
|
210
|
+
// Execute tools in parallel.
|
|
211
|
+
const results = await Promise.all(assistantMsg.toolCalls.map((tc) => options.toolExecutor.execute(tc.name, tc.id, tc.input)));
|
|
212
|
+
// --- Layer 1: cap each tool result ---
|
|
213
|
+
for (let i = 0; i < results.length; i++) {
|
|
214
|
+
const r = results[i];
|
|
215
|
+
const tc = assistantMsg.toolCalls[i];
|
|
216
|
+
r.content = resultCapper.cap(r.content, tc.name, tc.id, cache);
|
|
217
|
+
activity.push({
|
|
218
|
+
agent: label,
|
|
219
|
+
action: `result:${tc.name}`,
|
|
220
|
+
detail: `${r.content.length} chars — ${r.content.slice(0, 80).replace(/\n/g, ' ')}`,
|
|
221
|
+
});
|
|
222
|
+
executedToolCalls.push(tc);
|
|
223
|
+
emit?.({ type: 'tool_call_complete', agent: label, toolCall: tc, result: r });
|
|
224
|
+
}
|
|
225
|
+
// Append tool-result messages.
|
|
226
|
+
for (const r of results) {
|
|
227
|
+
localMessages.push({
|
|
228
|
+
role: 'tool',
|
|
229
|
+
toolCallId: r.toolCallId,
|
|
230
|
+
content: r.content,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// Round budget exhausted with no final text output.
|
|
235
|
+
activity.push({ agent: label, action: 'max_rounds_reached' });
|
|
236
|
+
const lastAssistant = [...localMessages]
|
|
237
|
+
.reverse()
|
|
238
|
+
.find((m) => m.role === 'assistant');
|
|
239
|
+
// We ran out of rounds while the model still wanted to call tools.
|
|
240
|
+
// Surface that as 'length'-adjacent: incomplete from the model's
|
|
241
|
+
// perspective, even if the wire-level reason was 'tool_calls'.
|
|
242
|
+
return finalize(lastAssistant?.content ?? '{}', 'length');
|
|
243
|
+
}
|
|
244
|
+
catch (err) {
|
|
245
|
+
const e = err;
|
|
246
|
+
if (isAbortError(err)) {
|
|
247
|
+
activity.push({ agent: label, action: 'aborted', detail: e.message?.slice(0, 120) });
|
|
248
|
+
return finalize('', 'error');
|
|
249
|
+
}
|
|
250
|
+
activity.push({ agent: label, action: 'error', detail: e.message?.slice(0, 120) });
|
|
251
|
+
if (attempt < maxRetries && isRetryableError(err)) {
|
|
252
|
+
const delay = 2000 + Math.random() * 3000;
|
|
253
|
+
activity.push({
|
|
254
|
+
agent: label,
|
|
255
|
+
action: 'retry',
|
|
256
|
+
detail: `Retrying after ${e.message?.slice(0, 60)}`,
|
|
257
|
+
});
|
|
258
|
+
// eslint-disable-next-line no-console
|
|
259
|
+
console.warn(`[${label}] retryable error, waiting ${Math.round(delay)}ms: ${e.message?.slice(0, 100)}`);
|
|
260
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
// eslint-disable-next-line no-console
|
|
264
|
+
console.error(`[${label}] agent failed (attempt ${attempt + 1}):`, e.message);
|
|
265
|
+
return finalize(JSON.stringify({ _error: true, message: e.message }), 'error');
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return finalize('{}', 'error');
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/agent/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stream consumer — folds an OpenAI streaming chat-completion response into
|
|
3
|
+
* a single round's accumulated state (content, tool_calls, finish_reason,
|
|
4
|
+
* usage). Tool calls arrive as per-index deltas; we buffer by index and
|
|
5
|
+
* JSON-parse the args at the end.
|
|
6
|
+
*
|
|
7
|
+
* Pure data transform: takes an AsyncIterable of chunks, calls the supplied
|
|
8
|
+
* callback for each content delta, returns the assembled AccumulatedRound.
|
|
9
|
+
* No I/O, no logging, no agent state — keeps the consumer testable in
|
|
10
|
+
* isolation.
|
|
11
|
+
*/
|
|
12
|
+
import type { ChatCompletionChunk } from 'openai/resources/chat/completions.js';
|
|
13
|
+
import type { ToolCall } from '../messages.js';
|
|
14
|
+
import type { FinishReason, UsageInfo } from './types.js';
|
|
15
|
+
/**
|
|
16
|
+
* @internal
|
|
17
|
+
*
|
|
18
|
+
* Internal accumulator shape — not part of the public API. May change
|
|
19
|
+
* between minor versions.
|
|
20
|
+
*/
|
|
21
|
+
export interface AccumulatedRound {
|
|
22
|
+
content: string;
|
|
23
|
+
toolCalls: ToolCall[];
|
|
24
|
+
finishReason: FinishReason;
|
|
25
|
+
usage: UsageInfo;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* @internal
|
|
29
|
+
*
|
|
30
|
+
* Normalize OpenAI's finish_reason string into a `FinishReason` union.
|
|
31
|
+
* Exported for advanced callers (e.g. building a custom streaming consumer)
|
|
32
|
+
* but not part of the v1 stable surface.
|
|
33
|
+
*/
|
|
34
|
+
export declare function normalizeFinishReason(raw: string | null | undefined): FinishReason;
|
|
35
|
+
/**
|
|
36
|
+
* @internal
|
|
37
|
+
*
|
|
38
|
+
* Heuristic: does this content look like a tool call that leaked through
|
|
39
|
+
* as plain text? Used by the agent loop to flag a missing/wrong
|
|
40
|
+
* inference-side `--tool-call-parser` configuration. Exported for callers
|
|
41
|
+
* that want to apply the same heuristic; not part of the v1 stable surface.
|
|
42
|
+
*/
|
|
43
|
+
export declare function looksLikeToolCallText(text: string): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* @internal
|
|
46
|
+
*
|
|
47
|
+
* Consume one streaming chat-completion response.
|
|
48
|
+
*
|
|
49
|
+
* Calls `onContentDelta` synchronously for each chunk's content fragment so
|
|
50
|
+
* the caller can surface them upstream as they arrive. Returns the assembled
|
|
51
|
+
* round once the stream ends.
|
|
52
|
+
*
|
|
53
|
+
* Exported for advanced callers (e.g. building a non-loop agent that just
|
|
54
|
+
* streams once and returns); not part of the v1 stable surface.
|
|
55
|
+
*/
|
|
56
|
+
export declare function consumeStream(stream: AsyncIterable<ChatCompletionChunk>, onContentDelta: (delta: string) => void): Promise<AccumulatedRound>;
|
|
57
|
+
//# sourceMappingURL=stream-consumer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-consumer.d.ts","sourceRoot":"","sources":["../../src/agent/stream-consumer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAChF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,YAAY,EAAE,YAAY,CAAC;IAC3B,KAAK,EAAE,SAAS,CAAC;CAClB;AAQD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,YAAY,CAWlF;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAS3D;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,aAAa,CAAC,mBAAmB,CAAC,EAC1C,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GACtC,OAAO,CAAC,gBAAgB,CAAC,CA6E3B"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stream consumer — folds an OpenAI streaming chat-completion response into
|
|
3
|
+
* a single round's accumulated state (content, tool_calls, finish_reason,
|
|
4
|
+
* usage). Tool calls arrive as per-index deltas; we buffer by index and
|
|
5
|
+
* JSON-parse the args at the end.
|
|
6
|
+
*
|
|
7
|
+
* Pure data transform: takes an AsyncIterable of chunks, calls the supplied
|
|
8
|
+
* callback for each content delta, returns the assembled AccumulatedRound.
|
|
9
|
+
* No I/O, no logging, no agent state — keeps the consumer testable in
|
|
10
|
+
* isolation.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* @internal
|
|
14
|
+
*
|
|
15
|
+
* Normalize OpenAI's finish_reason string into a `FinishReason` union.
|
|
16
|
+
* Exported for advanced callers (e.g. building a custom streaming consumer)
|
|
17
|
+
* but not part of the v1 stable surface.
|
|
18
|
+
*/
|
|
19
|
+
export function normalizeFinishReason(raw) {
|
|
20
|
+
switch (raw) {
|
|
21
|
+
case 'stop':
|
|
22
|
+
case 'length':
|
|
23
|
+
case 'tool_calls':
|
|
24
|
+
case 'content_filter':
|
|
25
|
+
case 'function_call':
|
|
26
|
+
return raw;
|
|
27
|
+
default:
|
|
28
|
+
return 'unknown';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* @internal
|
|
33
|
+
*
|
|
34
|
+
* Heuristic: does this content look like a tool call that leaked through
|
|
35
|
+
* as plain text? Used by the agent loop to flag a missing/wrong
|
|
36
|
+
* inference-side `--tool-call-parser` configuration. Exported for callers
|
|
37
|
+
* that want to apply the same heuristic; not part of the v1 stable surface.
|
|
38
|
+
*/
|
|
39
|
+
export function looksLikeToolCallText(text) {
|
|
40
|
+
const trimmed = text.trim();
|
|
41
|
+
if (!trimmed)
|
|
42
|
+
return false;
|
|
43
|
+
return (/^<tool_call\b/i.test(trimmed) ||
|
|
44
|
+
/^<function_call\b/i.test(trimmed) ||
|
|
45
|
+
/^\{\s*"name"\s*:/i.test(trimmed) ||
|
|
46
|
+
/^\{\s*"function"\s*:/i.test(trimmed));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* @internal
|
|
50
|
+
*
|
|
51
|
+
* Consume one streaming chat-completion response.
|
|
52
|
+
*
|
|
53
|
+
* Calls `onContentDelta` synchronously for each chunk's content fragment so
|
|
54
|
+
* the caller can surface them upstream as they arrive. Returns the assembled
|
|
55
|
+
* round once the stream ends.
|
|
56
|
+
*
|
|
57
|
+
* Exported for advanced callers (e.g. building a non-loop agent that just
|
|
58
|
+
* streams once and returns); not part of the v1 stable surface.
|
|
59
|
+
*/
|
|
60
|
+
export async function consumeStream(stream, onContentDelta) {
|
|
61
|
+
const out = {
|
|
62
|
+
content: '',
|
|
63
|
+
toolCalls: [],
|
|
64
|
+
finishReason: 'unknown',
|
|
65
|
+
usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
66
|
+
};
|
|
67
|
+
// Tool calls stream as deltas keyed by index. Build them up by index, then
|
|
68
|
+
// flatten to ToolCall[] at the end.
|
|
69
|
+
const toolBuffers = new Map();
|
|
70
|
+
for await (const chunk of stream) {
|
|
71
|
+
// Some backends emit an extra terminal chunk with usage and no choices.
|
|
72
|
+
if (chunk.usage) {
|
|
73
|
+
out.usage = {
|
|
74
|
+
promptTokens: chunk.usage.prompt_tokens ?? 0,
|
|
75
|
+
completionTokens: chunk.usage.completion_tokens ?? 0,
|
|
76
|
+
totalTokens: chunk.usage.total_tokens ?? 0,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const choice = chunk.choices?.[0];
|
|
80
|
+
if (!choice)
|
|
81
|
+
continue;
|
|
82
|
+
const delta = choice.delta;
|
|
83
|
+
if (delta?.content) {
|
|
84
|
+
out.content += delta.content;
|
|
85
|
+
onContentDelta(delta.content);
|
|
86
|
+
}
|
|
87
|
+
if (delta?.tool_calls) {
|
|
88
|
+
for (const tcDelta of delta.tool_calls) {
|
|
89
|
+
const idx = tcDelta.index;
|
|
90
|
+
let buf = toolBuffers.get(idx);
|
|
91
|
+
if (!buf) {
|
|
92
|
+
buf = { id: '', name: '', argsBuffer: '' };
|
|
93
|
+
toolBuffers.set(idx, buf);
|
|
94
|
+
}
|
|
95
|
+
if (tcDelta.id)
|
|
96
|
+
buf.id = tcDelta.id;
|
|
97
|
+
if (tcDelta.function?.name)
|
|
98
|
+
buf.name = tcDelta.function.name;
|
|
99
|
+
if (tcDelta.function?.arguments)
|
|
100
|
+
buf.argsBuffer += tcDelta.function.arguments;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (choice.finish_reason) {
|
|
104
|
+
out.finishReason = normalizeFinishReason(choice.finish_reason);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Flatten tool buffers in index order.
|
|
108
|
+
const indices = [...toolBuffers.keys()].sort((a, b) => a - b);
|
|
109
|
+
for (const idx of indices) {
|
|
110
|
+
const buf = toolBuffers.get(idx);
|
|
111
|
+
if (!buf.id || !buf.name) {
|
|
112
|
+
// Malformed tool call delta — skip rather than corrupt the loop.
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
let input = {};
|
|
116
|
+
try {
|
|
117
|
+
input = buf.argsBuffer ? JSON.parse(buf.argsBuffer) : {};
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
input = { _raw: buf.argsBuffer };
|
|
121
|
+
}
|
|
122
|
+
out.toolCalls.push({ id: buf.id, name: buf.name, input });
|
|
123
|
+
}
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=stream-consumer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-consumer.js","sourceRoot":"","sources":["../../src/agent/stream-consumer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types for the agent runner.
|
|
3
|
+
*
|
|
4
|
+
* Kept separate from the loop so callers can import the surface without
|
|
5
|
+
* pulling in the streaming machinery, and so the type surface is easy to
|
|
6
|
+
* audit at a glance.
|
|
7
|
+
*/
|
|
8
|
+
import type OpenAI from 'openai';
|
|
9
|
+
import type { ToolCall, ToolDefinition, ToolExecutor, ToolResult } from '../messages.js';
|
|
10
|
+
import type { TokenBudgetTracker } from '../context/token-budget.js';
|
|
11
|
+
import type { ResultCapper } from '../context/result-capper.js';
|
|
12
|
+
import type { CompressorStrategy } from '../context/compressor.js';
|
|
13
|
+
import type { PipelineCache } from '../cache.js';
|
|
14
|
+
export interface AgentConfig {
|
|
15
|
+
/** Identifier surfaced in activity logs. */
|
|
16
|
+
name: string;
|
|
17
|
+
systemPrompt: string;
|
|
18
|
+
/** Model name passed to the inference endpoint. Empty string → caller-default. */
|
|
19
|
+
model: string;
|
|
20
|
+
tools: ToolDefinition[];
|
|
21
|
+
/** Max rounds of tool use before forcing a final response. */
|
|
22
|
+
maxToolRounds: number;
|
|
23
|
+
/** Max tokens for the model's response. */
|
|
24
|
+
maxTokens: number;
|
|
25
|
+
/**
|
|
26
|
+
* Extra fields merged into the chat-completions request body. Use for
|
|
27
|
+
* provider-specific extensions that aren't in the OpenAI spec — e.g. vLLM's
|
|
28
|
+
* `chat_template_kwargs: { enable_thinking: false }` to disable Qwen3 reasoning,
|
|
29
|
+
* or `top_k`, `repetition_penalty`, etc.
|
|
30
|
+
*
|
|
31
|
+
* Merged via spread; if a key collides with one the runner sets explicitly
|
|
32
|
+
* (model, max_tokens, messages, tools, stream), the runner's value wins.
|
|
33
|
+
*/
|
|
34
|
+
extraBody?: Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
export interface ActivityEntry {
|
|
37
|
+
agent: string;
|
|
38
|
+
action: string;
|
|
39
|
+
detail?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* `finishReason` mirrors OpenAI's `choices[0].finish_reason`. Callers that
|
|
43
|
+
* need to detect truncation should branch on `'length'` (= max_tokens hit,
|
|
44
|
+
* output cut off mid-stream). `'unknown'` is used when the backend never
|
|
45
|
+
* surfaced a reason (e.g. a stream that errored mid-flight).
|
|
46
|
+
*/
|
|
47
|
+
export type FinishReason = 'stop' | 'length' | 'tool_calls' | 'content_filter' | 'function_call' | 'error' | 'unknown';
|
|
48
|
+
export interface UsageInfo {
|
|
49
|
+
promptTokens: number;
|
|
50
|
+
completionTokens: number;
|
|
51
|
+
totalTokens: number;
|
|
52
|
+
}
|
|
53
|
+
export interface AgentRunResult {
|
|
54
|
+
/** Final text output from the agent. May be JSON; caller can parseJSON it. */
|
|
55
|
+
text: string;
|
|
56
|
+
activity: ActivityEntry[];
|
|
57
|
+
/**
|
|
58
|
+
* Reason the agent stopped. `'stop'` = clean finish, `'length'` = truncated
|
|
59
|
+
* (max_tokens hit), `'tool_calls'` = stopped to call tools (rare at the top
|
|
60
|
+
* level since the loop continues), `'error'` = surfaced from a thrown error
|
|
61
|
+
* inside the loop, `'unknown'` = backend never reported one.
|
|
62
|
+
*/
|
|
63
|
+
finishReason: FinishReason;
|
|
64
|
+
/** Token usage summed across every inference round in this run. */
|
|
65
|
+
usage: UsageInfo;
|
|
66
|
+
/**
|
|
67
|
+
* Every tool call the agent actually executed during this run, in order.
|
|
68
|
+
* Use this rather than parsing `activity[]` to verify what the agent did
|
|
69
|
+
* vs. what it claimed in its text/JSON output.
|
|
70
|
+
*/
|
|
71
|
+
executedToolCalls: ToolCall[];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Streaming events fed to `AgentRunnerOptions.onStreamEvent`.
|
|
75
|
+
*
|
|
76
|
+
* - `content_delta`: chunk of model content as it streams.
|
|
77
|
+
* - `tool_call_started`: assistant emitted a tool call (post-decode).
|
|
78
|
+
* - `tool_call_complete`: tool executed, result capped, content available.
|
|
79
|
+
* - `round_complete`: one tool-use round just finished.
|
|
80
|
+
*/
|
|
81
|
+
export type AgentStreamEvent = {
|
|
82
|
+
type: 'content_delta';
|
|
83
|
+
agent: string;
|
|
84
|
+
delta: string;
|
|
85
|
+
} | {
|
|
86
|
+
type: 'tool_call_started';
|
|
87
|
+
agent: string;
|
|
88
|
+
toolCall: ToolCall;
|
|
89
|
+
} | {
|
|
90
|
+
type: 'tool_call_complete';
|
|
91
|
+
agent: string;
|
|
92
|
+
toolCall: ToolCall;
|
|
93
|
+
result: ToolResult;
|
|
94
|
+
} | {
|
|
95
|
+
type: 'round_complete';
|
|
96
|
+
agent: string;
|
|
97
|
+
round: number;
|
|
98
|
+
finishReason: FinishReason;
|
|
99
|
+
};
|
|
100
|
+
export interface AgentRunnerOptions {
|
|
101
|
+
client: OpenAI;
|
|
102
|
+
toolExecutor: ToolExecutor;
|
|
103
|
+
/** Per-pipeline cache shared with the result capper for full-result retrieval. */
|
|
104
|
+
cache?: PipelineCache | null;
|
|
105
|
+
budgetTracker?: TokenBudgetTracker;
|
|
106
|
+
resultCapper?: ResultCapper;
|
|
107
|
+
compressor?: CompressorStrategy;
|
|
108
|
+
aggressiveCompressor?: CompressorStrategy;
|
|
109
|
+
/** Number of full retry attempts on retryable errors (default 1 = retry once). */
|
|
110
|
+
maxRetries?: number;
|
|
111
|
+
/**
|
|
112
|
+
* Cancellation signal. When aborted: the in-flight stream is cancelled
|
|
113
|
+
* (passed through to `client.chat.completions.create({ signal })`), the
|
|
114
|
+
* loop stops between rounds, and the runner returns whatever it has so far
|
|
115
|
+
* with `finishReason: 'error'`.
|
|
116
|
+
*/
|
|
117
|
+
signal?: AbortSignal;
|
|
118
|
+
/**
|
|
119
|
+
* Optional streaming sink. Receives content deltas as they arrive plus
|
|
120
|
+
* tool-call lifecycle events. Synchronous callbacks only — don't await
|
|
121
|
+
* inside; if you need async work, schedule it.
|
|
122
|
+
*/
|
|
123
|
+
onStreamEvent?: (event: AgentStreamEvent) => void;
|
|
124
|
+
/**
|
|
125
|
+
* Defensive validation hook called once per run, just before returning.
|
|
126
|
+
* Receives the assembled `AgentRunResult` (with `executedToolCalls` already
|
|
127
|
+
* populated) and may either return a transformed result or throw to mark
|
|
128
|
+
* the run as failed. Use this to verify the agent's claimed output against
|
|
129
|
+
* what was actually executed — the canonical way to catch silent
|
|
130
|
+
* confabulation where a small model says "I created file X" without ever
|
|
131
|
+
* calling the write tool.
|
|
132
|
+
*/
|
|
133
|
+
verifyResult?: (result: AgentRunResult) => Promise<AgentRunResult> | AgentRunResult;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/agent/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,EACV,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,UAAU,EACX,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAMjD,MAAM,WAAW,WAAW;IAC1B,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,8DAA8D;IAC9D,aAAa,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,QAAQ,GACR,YAAY,GACZ,gBAAgB,GAChB,eAAe,GACf,OAAO,GACP,SAAS,CAAC;AAEd,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B;;;;;OAKG;IACH,YAAY,EAAE,YAAY,CAAC;IAC3B,mEAAmE;IACnE,KAAK,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,iBAAiB,EAAE,QAAQ,EAAE,CAAC;CAC/B;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAChE;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GACrF;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,YAAY,CAAA;CAAE,CAAC;AAEzF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,YAAY,CAAC;IAC3B,kFAAkF;IAClF,KAAK,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAC7B,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;IAC1C,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAAC;CACrF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types for the agent runner.
|
|
3
|
+
*
|
|
4
|
+
* Kept separate from the loop so callers can import the surface without
|
|
5
|
+
* pulling in the streaming machinery, and so the type surface is easy to
|
|
6
|
+
* audit at a glance.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/agent/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backward-compat shim — the agent runner was split into focused modules
|
|
3
|
+
* under `./agent/`. This file re-exports the same public surface so existing
|
|
4
|
+
* imports continue to work.
|
|
5
|
+
*
|
|
6
|
+
* For new code, import from `thread-phase` (or directly from `./agent/*`)
|
|
7
|
+
* rather than this path.
|
|
8
|
+
*/
|
|
9
|
+
export { runAgentWithTools, parseJSON, type AgentConfig, type ActivityEntry, type AgentRunResult, type AgentRunnerOptions, type AgentStreamEvent, type FinishReason, type UsageInfo, } from './agent/index.js';
|
|
10
|
+
//# sourceMappingURL=agent-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-runner.d.ts","sourceRoot":"","sources":["../src/agent-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,SAAS,GACf,MAAM,kBAAkB,CAAC"}
|