agent-accelerator 0.6.0 → 0.6.6
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 +62 -18
- package/package.json +1 -1
- package/src/agent/agent.ts +192 -695
- package/src/agent/architecture.md +19 -13
- package/src/agent/context.ts +1 -0
- package/src/agent/defaults.ts +88 -0
- package/src/agent/delegation.ts +8 -3
- package/src/agent/loop.ts +242 -226
- package/src/agent/named-sessions.ts +93 -0
- package/src/agent/options.ts +166 -0
- package/src/agent/run-control.ts +248 -0
- package/src/agent/subagents-tool.ts +14 -21
- package/src/agent/usage-limit.ts +92 -0
- package/src/agent/worker.ts +17 -3
- package/src/agent/workers.ts +13 -1
- package/src/architecture.md +1 -0
- package/src/index.ts +11 -11
- package/src/prompts/additional-context.md +2 -0
- package/src/prompts/architecture.md +19 -0
- package/src/prompts/prompts.ts +81 -0
- package/src/prompts/reasoning-directive.md +5 -0
- package/src/prompts/structured-output.md +2 -0
- package/src/prompts/tool-bridge.md +1 -0
- package/src/prompts/tool-interrupted.md +1 -0
- package/src/prompts/tool-not-found.md +1 -0
- package/src/prompts/tool-repeat-blocked.md +1 -0
- package/src/prompts/tool-schema-error.md +1 -0
- package/src/prompts/tool-truncated-arguments.md +1 -0
- package/src/prompts/worker-failed.md +1 -0
- package/src/prompts/worker-replied.md +2 -0
- package/src/providers/architecture.md +8 -8
- package/src/providers/google.ts +83 -13
- package/src/providers/openai-compat.ts +17 -7
- package/src/providers/openai.ts +10 -4
- package/src/providers/openrouter.ts +185 -18
- package/src/providers.ts +59 -9
- package/src/scheduler/architecture.md +7 -7
- package/src/scheduler/scheduler.ts +40 -0
- package/src/scheduler/tool.ts +5 -9
- package/src/session/journal.ts +1 -0
- package/src/session/store.ts +4 -0
- package/src/streaming/event-stream.ts +1 -1
- package/src/tools/architecture.md +7 -6
- package/src/tools/executor.ts +60 -15
- package/src/tools/tool-content.ts +32 -0
- package/src/types/agent.ts +40 -8
- package/src/types/core.ts +4 -0
- package/src/types/message.ts +7 -0
- package/src/types/model.ts +4 -1
- package/src/types/scheduler.ts +4 -4
- package/src/types/tool.ts +2 -0
- package/src/utils/errors.ts +3 -3
- package/src/utils/headers.ts +1 -1
- /package/src/{scheduler/scheduled-actions.md → prompts/scheduled-actions-tool.md} +0 -0
- /package/src/{agent/subagents.md → prompts/subagents-tool.md} +0 -0
|
@@ -2,26 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
The agent and everything that runs under it.
|
|
4
4
|
|
|
5
|
-
| File
|
|
6
|
-
|
|
|
7
|
-
| `agent.ts`
|
|
8
|
-
| `
|
|
9
|
-
| `
|
|
10
|
-
| `
|
|
11
|
-
| `
|
|
12
|
-
| `
|
|
13
|
-
| `
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
16
|
-
| `
|
|
5
|
+
| File | What it is | Used by |
|
|
6
|
+
| :------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------- |
|
|
7
|
+
| `agent.ts` | `Agent`: `run`/`stream`/`ask`, `steer`/`queue`/`interrupt`, persistence hooks, `resume()`/`interrupted()`, `usageReport()`, `fork()`, `toConfig()`/`fromConfig()`, the `workers` getter. | callers, workers, scheduler |
|
|
8
|
+
| `run-control.ts` | `RunControl`: the active run, the waiting queue and the machinery behind `steer`/`queue`/`interrupt` (`pumpQueue`, `finishCurrentRun`, urgent queueing). `Agent` keeps the public methods and calls it. | `Agent` |
|
|
9
|
+
| `options.ts` | `buildProviderOptions` (agent config + per-run options in one place), `resolveAgentLimits`, `normalizeThinking`, `resolveEffectiveThinking`, `normalizeToolChoiceOption`. | `Agent`, barrel |
|
|
10
|
+
| `named-sessions.ts` | `NamedSessions`: the in-memory contexts of `run(..., { sessionId })` (LRU 100, 1 h idle TTL). | `Agent` |
|
|
11
|
+
| `loop.ts` | `runAgentLoop` / `streamAgentLoop`: per turn budget, pairing repair, file access, provider call, tool execution, repeat guard. Also `ensureToolPairing`, `accumulateUsage`. | `Agent` |
|
|
12
|
+
| `context.ts` | `AgentContext`: the validated message list with checkpoint/rollback. | `Agent`, loops |
|
|
13
|
+
| `delegation.ts` | Fixed workers (`agentToTool`, `buildAgentTools`), the live worker trace store, session-id helpers, budget helpers, `openWorkerTrace`/`workerMetadata`. | `Agent`, `workers.ts` |
|
|
14
|
+
| `worker.ts` | Worker core: model/tool normalization, `buildDynamicWorkerConfig` (shared preset), `createLiveWorker`. | `Agent`, registry, scheduler |
|
|
15
|
+
| `workers.ts` | `WorkerRegistry` (`agent.workers`): persistent multi-turn workers. | `Agent`, `subagents` tool |
|
|
16
|
+
| `subagents-tool.ts` | The one model-facing `subagents` tool; its description is the `subagentsTool` prompt (`../prompts/subagents-tool.md`). | `Agent` constructor |
|
|
17
|
+
| `subagent.ts` | `SubAgent` class and `agentFromEnv` (thin, kept for compatibility). | callers |
|
|
18
|
+
| `turns.ts` | Pure grouping of messages into finished turns (history cut for workers). | `workers.ts` |
|
|
19
|
+
| `defaults.ts` | `configure()` and the `AGENT_ACCEL_*` env vars: developer defaults for `usageLimit` and `dynamicSubagents.scheduledActionBudget`, per field (Agent config > `configure()` > env). SDK-built workers never take them (`isWorkerConfig`). | `Agent`, barrel |
|
|
20
|
+
| `usage-limit.ts` | `UsageLimit`: the chat-wide cap (`AgentConfig.usageLimit`). The root agent owns it, registry and fixed workers share it (`agent.chat`); every model turn adds to it and is checked against it. | `Agent`, `workers.ts`, `delegation.ts` |
|
|
21
|
+
| `config-blocks.ts` | RSI blocks: only `model`, `instructions`, `tools` are editable; `allowedModels` check. | `Agent` |
|
|
17
22
|
|
|
18
23
|
## Invariants
|
|
19
24
|
|
|
20
|
-
- `runAgentLoop` and `streamAgentLoop` duplicate
|
|
25
|
+
- `runAgentLoop` and `streamAgentLoop` still duplicate the shape of a turn, but the repeated parts live once in helpers at the top of `loop.ts` (`buildTurnOptions`, `promoteThinkingIfAllowed`, `recordModelTurn`, `emitThinkingTrace`, `rollUpToolResults`, `partialProgress`, `newTurnUsage`): change those there, and any change to the turn order itself goes into both loops.
|
|
21
26
|
- An `Agent` runs one thing at a time; delegation forks a copy per call so parallel calls never queue.
|
|
22
27
|
- A worker is a plain `Agent` with a parent link and presets. Never duplicate logic between Agent and worker.
|
|
23
28
|
- A worker is never less constrained than its parent (`inheritableConfig()`); its children draw only from the tools it was granted.
|
|
24
29
|
- Exactly-once worker calls rest on the call key (parent run mark id + tool call id) and `meta.last`.
|
|
25
30
|
- Only results branded by `makeSubagentResult` have their usage metadata trusted.
|
|
31
|
+
- A chat-wide cap (`usageLimit`) counts every model turn of the root and of all workers through one shared `UsageLimit`; its starting point is read once per process from the saved sessions. Scheduled workers never get it.
|
|
26
32
|
- Each Agent records only its own model turns; never sum `AgentResponse.usage` for totals.
|
|
27
33
|
- `reset()` clears the conversation (messages, run marks, the run's saved tool results) but never the spending totals.
|
package/src/agent/context.ts
CHANGED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Developer defaults for the spending caps, set in code (`configure()`) or through env vars, so one place can cap every
|
|
3
|
+
* agent of a program. Precedence, per field (`maxCostUsd`, `maxTotalTokens`): the Agent's own config, then `configure()`,
|
|
4
|
+
* then the env var, then no cap. Only agents YOU construct take defaults: the workers and scheduled runs the SDK builds
|
|
5
|
+
* itself never do (a chat worker joins its parent's cap; a scheduled run has its own).
|
|
6
|
+
*/
|
|
7
|
+
import { ConfigError } from "../types/errors.ts";
|
|
8
|
+
import { getEnv } from "../utils/env.ts";
|
|
9
|
+
import { checkUsageLimitConfig, type UsageLimitConfig } from "./usage-limit.ts";
|
|
10
|
+
|
|
11
|
+
/** What `configure()` can set; every key is optional and has the same meaning as the Agent option of the same name. */
|
|
12
|
+
export interface AgentDefaults {
|
|
13
|
+
/** Default for `AgentConfig.usageLimit` (the chat-wide cap). Env: `AGENT_ACCEL_USAGE_LIMIT_USD`, `AGENT_ACCEL_USAGE_LIMIT_TOKENS`. */
|
|
14
|
+
usageLimit?: UsageLimitConfig;
|
|
15
|
+
/** Default for `dynamicSubagents.scheduledActionBudget`. Env: `AGENT_ACCEL_SCHEDULED_BUDGET_USD`, `AGENT_ACCEL_SCHEDULED_BUDGET_TOKENS`. */
|
|
16
|
+
scheduledActionBudget?: UsageLimitConfig;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type CapName = keyof AgentDefaults;
|
|
20
|
+
|
|
21
|
+
const ENV: Record<CapName, { maxCostUsd: string; maxTotalTokens: string }> = {
|
|
22
|
+
usageLimit: { maxCostUsd: "AGENT_ACCEL_USAGE_LIMIT_USD", maxTotalTokens: "AGENT_ACCEL_USAGE_LIMIT_TOKENS" },
|
|
23
|
+
scheduledActionBudget: {
|
|
24
|
+
maxCostUsd: "AGENT_ACCEL_SCHEDULED_BUDGET_USD",
|
|
25
|
+
maxTotalTokens: "AGENT_ACCEL_SCHEDULED_BUDGET_TOKENS",
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
let configured: AgentDefaults = {};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Sets developer defaults for the spending caps; call it once at startup, like `setHome()`. It REPLACES what an earlier
|
|
33
|
+
* call set (`configure({})` clears it). A key an Agent sets itself wins, field by field; env vars are the fallback.
|
|
34
|
+
* Throws a `ConfigError` for a value that is not a number above 0.
|
|
35
|
+
*
|
|
36
|
+
* @example `configure({ usageLimit: { maxCostUsd: 5 }, scheduledActionBudget: { maxCostUsd: 0.5 } });`
|
|
37
|
+
*/
|
|
38
|
+
export function configure(defaults: AgentDefaults = {}): void {
|
|
39
|
+
if (!defaults || typeof defaults !== "object" || Array.isArray(defaults)) {
|
|
40
|
+
throw new ConfigError("[Agent Accelerator] configure() expects an object like { usageLimit: { maxCostUsd: 5 } }.");
|
|
41
|
+
}
|
|
42
|
+
for (const key of Object.keys(defaults)) {
|
|
43
|
+
if (!(key in ENV)) {
|
|
44
|
+
throw new ConfigError(
|
|
45
|
+
`[Agent Accelerator] configure(): unknown setting "${key}" (known: ${Object.keys(ENV).join(", ")}).`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const value = (defaults as Record<string, unknown>)[key];
|
|
49
|
+
if (value === undefined) continue;
|
|
50
|
+
const problem = checkUsageLimitConfig(value);
|
|
51
|
+
if (problem) throw new ConfigError(`[Agent Accelerator] configure(): invalid ${key}: ${problem}.`);
|
|
52
|
+
}
|
|
53
|
+
configured = Object.fromEntries(
|
|
54
|
+
Object.entries(defaults)
|
|
55
|
+
.filter(([, v]) => v !== undefined)
|
|
56
|
+
.map(([k, v]) => [k, { ...(v as UsageLimitConfig) }])
|
|
57
|
+
) as AgentDefaults;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Reads one cap field from its env var: undefined when unset, a ConfigError naming the variable when it is not a number above 0; in: mergeCap, out: the value.
|
|
61
|
+
function fromEnv(variable: string): number | undefined {
|
|
62
|
+
const raw = getEnv(variable);
|
|
63
|
+
if (raw === undefined) return undefined;
|
|
64
|
+
const n = Number(raw);
|
|
65
|
+
if (!(Number.isFinite(n) && n > 0)) {
|
|
66
|
+
throw new ConfigError(
|
|
67
|
+
`[Agent Accelerator] Invalid ${variable} ${JSON.stringify(raw)}: expected a number greater than 0.`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
return n;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The cap an Agent uses: its own value, field by field over `configure()` over the env vars; undefined when nothing sets one. `withDefaults: false` ignores `configure()` and the env (SDK-built workers). */
|
|
74
|
+
export function mergeCap(
|
|
75
|
+
name: CapName,
|
|
76
|
+
own: UsageLimitConfig | undefined,
|
|
77
|
+
withDefaults: boolean
|
|
78
|
+
): UsageLimitConfig | undefined {
|
|
79
|
+
const base = withDefaults ? configured[name] : undefined;
|
|
80
|
+
const maxCostUsd = own?.maxCostUsd ?? base?.maxCostUsd ?? (withDefaults ? fromEnv(ENV[name].maxCostUsd) : undefined);
|
|
81
|
+
const maxTotalTokens =
|
|
82
|
+
own?.maxTotalTokens ?? base?.maxTotalTokens ?? (withDefaults ? fromEnv(ENV[name].maxTotalTokens) : undefined);
|
|
83
|
+
if (maxCostUsd === undefined && maxTotalTokens === undefined) return undefined;
|
|
84
|
+
return {
|
|
85
|
+
...(maxCostUsd !== undefined ? { maxCostUsd } : {}),
|
|
86
|
+
...(maxTotalTokens !== undefined ? { maxTotalTokens } : {}),
|
|
87
|
+
};
|
|
88
|
+
}
|
package/src/agent/delegation.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { nowMs } from "../utils/session.ts";
|
|
|
15
15
|
import { newRunId } from "../utils/session.ts";
|
|
16
16
|
import { newTurnId } from "../utils/session.ts";
|
|
17
17
|
import { sanitizeToolName, createLiveWorker } from "./worker.ts";
|
|
18
|
+
import type { UsageLimit } from "./usage-limit.ts";
|
|
18
19
|
|
|
19
20
|
/** Builds a random child session id that fits the 64-char provider limit, embedding a hash of the parent when it must be cut; in: a parent id + tag from scheduled runs, out: the worker's session id. */
|
|
20
21
|
export function createChildSessionId(parentId: string, tag: string): string {
|
|
@@ -631,7 +632,7 @@ export interface AgentAsToolTarget {
|
|
|
631
632
|
*
|
|
632
633
|
* @example `const researchTool = researcher.asTool("research");`
|
|
633
634
|
*/
|
|
634
|
-
export function agentToTool(input: Agent | AgentAsToolTarget): ToolDefinition {
|
|
635
|
+
export function agentToTool(input: Agent | AgentAsToolTarget, opts?: { chat?: UsageLimit }): ToolDefinition {
|
|
635
636
|
const agentInstance = "agent" in input ? input.agent : input;
|
|
636
637
|
const rawName =
|
|
637
638
|
("name" in input && (input as any).name) ||
|
|
@@ -679,6 +680,7 @@ export function agentToTool(input: Agent | AgentAsToolTarget): ToolDefinition {
|
|
|
679
680
|
typeof (agentInstance as Agent).fork === "function"
|
|
680
681
|
? agentInstance.fork(subSessionId, parentSessionId)
|
|
681
682
|
: agentInstance;
|
|
683
|
+
if (worker !== agentInstance && opts?.chat) worker.chat = opts.chat; // this call counts into the parent chat's cap
|
|
682
684
|
const workerStream = worker.stream(task, {
|
|
683
685
|
signal: ctx?.signal,
|
|
684
686
|
sessionId: subSessionId,
|
|
@@ -729,12 +731,15 @@ export function agentToTool(input: Agent | AgentAsToolTarget): ToolDefinition {
|
|
|
729
731
|
*
|
|
730
732
|
* @example `const tools = buildAgentTools([researcher, reviewer]);`
|
|
731
733
|
*/
|
|
732
|
-
export function buildAgentTools(
|
|
734
|
+
export function buildAgentTools(
|
|
735
|
+
agents?: (Agent | AgentAsToolTarget)[],
|
|
736
|
+
opts?: { chat?: UsageLimit }
|
|
737
|
+
): Record<string, ToolDefinition> {
|
|
733
738
|
if (!agents || agents.length === 0) return {};
|
|
734
739
|
|
|
735
740
|
const tools: Record<string, ToolDefinition> = {};
|
|
736
741
|
for (const item of agents) {
|
|
737
|
-
const t = agentToTool(item);
|
|
742
|
+
const t = agentToTool(item, opts);
|
|
738
743
|
if (t.name) {
|
|
739
744
|
let finalName = t.name;
|
|
740
745
|
let n = 1;
|