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.
Files changed (55) hide show
  1. package/README.md +62 -18
  2. package/package.json +1 -1
  3. package/src/agent/agent.ts +192 -695
  4. package/src/agent/architecture.md +19 -13
  5. package/src/agent/context.ts +1 -0
  6. package/src/agent/defaults.ts +88 -0
  7. package/src/agent/delegation.ts +8 -3
  8. package/src/agent/loop.ts +242 -226
  9. package/src/agent/named-sessions.ts +93 -0
  10. package/src/agent/options.ts +166 -0
  11. package/src/agent/run-control.ts +248 -0
  12. package/src/agent/subagents-tool.ts +14 -21
  13. package/src/agent/usage-limit.ts +92 -0
  14. package/src/agent/worker.ts +17 -3
  15. package/src/agent/workers.ts +13 -1
  16. package/src/architecture.md +1 -0
  17. package/src/index.ts +11 -11
  18. package/src/prompts/additional-context.md +2 -0
  19. package/src/prompts/architecture.md +19 -0
  20. package/src/prompts/prompts.ts +81 -0
  21. package/src/prompts/reasoning-directive.md +5 -0
  22. package/src/prompts/structured-output.md +2 -0
  23. package/src/prompts/tool-bridge.md +1 -0
  24. package/src/prompts/tool-interrupted.md +1 -0
  25. package/src/prompts/tool-not-found.md +1 -0
  26. package/src/prompts/tool-repeat-blocked.md +1 -0
  27. package/src/prompts/tool-schema-error.md +1 -0
  28. package/src/prompts/tool-truncated-arguments.md +1 -0
  29. package/src/prompts/worker-failed.md +1 -0
  30. package/src/prompts/worker-replied.md +2 -0
  31. package/src/providers/architecture.md +8 -8
  32. package/src/providers/google.ts +83 -13
  33. package/src/providers/openai-compat.ts +17 -7
  34. package/src/providers/openai.ts +10 -4
  35. package/src/providers/openrouter.ts +185 -18
  36. package/src/providers.ts +59 -9
  37. package/src/scheduler/architecture.md +7 -7
  38. package/src/scheduler/scheduler.ts +40 -0
  39. package/src/scheduler/tool.ts +5 -9
  40. package/src/session/journal.ts +1 -0
  41. package/src/session/store.ts +4 -0
  42. package/src/streaming/event-stream.ts +1 -1
  43. package/src/tools/architecture.md +7 -6
  44. package/src/tools/executor.ts +60 -15
  45. package/src/tools/tool-content.ts +32 -0
  46. package/src/types/agent.ts +40 -8
  47. package/src/types/core.ts +4 -0
  48. package/src/types/message.ts +7 -0
  49. package/src/types/model.ts +4 -1
  50. package/src/types/scheduler.ts +4 -4
  51. package/src/types/tool.ts +2 -0
  52. package/src/utils/errors.ts +3 -3
  53. package/src/utils/headers.ts +1 -1
  54. /package/src/{scheduler/scheduled-actions.md → prompts/scheduled-actions-tool.md} +0 -0
  55. /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 | 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
- | `loop.ts` | `runAgentLoop` / `streamAgentLoop`: per turn budget, pairing repair, file access, provider call, tool execution, repeat guard. Also `ensureToolPairing`, `accumulateUsage`. | `Agent` |
9
- | `context.ts` | `AgentContext`: the validated message list with checkpoint/rollback. | `Agent`, loops |
10
- | `delegation.ts` | Fixed workers (`agentToTool`, `buildAgentTools`), the live worker trace store, session-id helpers, budget helpers, `openWorkerTrace`/`workerMetadata`. | `Agent`, `workers.ts` |
11
- | `worker.ts` | Worker core: model/tool normalization, `buildDynamicWorkerConfig` (shared preset), `createLiveWorker`. | `Agent`, registry, scheduler |
12
- | `workers.ts` | `WorkerRegistry` (`agent.workers`): persistent multi-turn workers. | `Agent`, `subagents` tool |
13
- | `subagents-tool.ts` + `subagents.md` | The one model-facing `subagents` tool; the `.md` is its description. | `Agent` constructor |
14
- | `subagent.ts` | `SubAgent` class and `agentFromEnv` (thin, kept for compatibility). | callers |
15
- | `turns.ts` | Pure grouping of messages into finished turns (history cut for workers). | `workers.ts` |
16
- | `config-blocks.ts` | RSI blocks: only `model`, `instructions`, `tools` are editable; `allowedModels` check. | `Agent` |
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 their turn logic: every loop change goes into both.
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.
@@ -143,6 +143,7 @@ export class AgentContext {
143
143
  isError: res.isError,
144
144
  ...(res.logFile ? { logFile: res.logFile } : {}),
145
145
  ...(res.stopped ? { stopped: true } : {}),
146
+ ...(res.parts && res.parts.length > 0 ? { parts: res.parts } : {}),
146
147
  },
147
148
  ],
148
149
  });
@@ -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
+ }
@@ -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(agents?: (Agent | AgentAsToolTarget)[]): Record<string, ToolDefinition> {
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;