agent-accelerator 0.5.9 → 0.6.5

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 (85) hide show
  1. package/README.md +64 -16
  2. package/examples/prompts/SYSTEM_PROMPT.md +2 -0
  3. package/examples/prompts/SYSTEM_PROMPT_AGENT.md +4 -0
  4. package/examples/prompts/SYSTEM_PROMPT_TOOLS.md +1 -0
  5. package/package.json +4 -1
  6. package/src/agent/agent.ts +447 -172
  7. package/src/agent/architecture.md +16 -12
  8. package/src/agent/config-blocks.ts +39 -12
  9. package/src/agent/context.ts +9 -3
  10. package/src/agent/defaults.ts +88 -0
  11. package/src/agent/delegation.ts +86 -28
  12. package/src/agent/loop.ts +474 -374
  13. package/src/agent/subagent.ts +1 -3
  14. package/src/agent/subagents-tool.ts +50 -19
  15. package/src/agent/turns.ts +3 -1
  16. package/src/agent/usage-limit.ts +92 -0
  17. package/src/agent/worker.ts +73 -30
  18. package/src/agent/workers.ts +126 -21
  19. package/src/architecture.md +17 -15
  20. package/src/index.ts +43 -19
  21. package/src/models/architecture.md +4 -3
  22. package/src/models/catalog-cache.ts +6 -5
  23. package/src/models/catalog.ts +39 -30
  24. package/src/prompts/additional-context.md +2 -0
  25. package/src/prompts/architecture.md +19 -0
  26. package/src/prompts/prompts.ts +81 -0
  27. package/src/prompts/reasoning-directive.md +5 -0
  28. package/src/{scheduler/scheduled-actions.md → prompts/scheduled-actions-tool.md} +4 -0
  29. package/src/prompts/structured-output.md +2 -0
  30. package/src/{agent/subagents.md → prompts/subagents-tool.md} +3 -0
  31. package/src/prompts/tool-bridge.md +1 -0
  32. package/src/prompts/tool-interrupted.md +1 -0
  33. package/src/prompts/tool-not-found.md +1 -0
  34. package/src/prompts/tool-repeat-blocked.md +1 -0
  35. package/src/prompts/tool-schema-error.md +1 -0
  36. package/src/prompts/tool-truncated-arguments.md +1 -0
  37. package/src/prompts/worker-failed.md +1 -0
  38. package/src/prompts/worker-replied.md +2 -0
  39. package/src/providers/architecture.md +10 -8
  40. package/src/providers/google.ts +228 -69
  41. package/src/providers/openai-compat.ts +74 -56
  42. package/src/providers/openai.ts +64 -41
  43. package/src/providers/openrouter.ts +270 -48
  44. package/src/providers/registry.ts +6 -21
  45. package/src/providers/shared.ts +87 -33
  46. package/src/providers.ts +115 -124
  47. package/src/scheduler/architecture.md +8 -7
  48. package/src/scheduler/scheduler.ts +226 -60
  49. package/src/scheduler/store.ts +23 -10
  50. package/src/scheduler/time.ts +17 -3
  51. package/src/scheduler/tool.ts +38 -16
  52. package/src/session/architecture.md +7 -6
  53. package/src/session/journal.ts +39 -12
  54. package/src/session/store.ts +126 -61
  55. package/src/session/usage.ts +58 -9
  56. package/src/streaming/architecture.md +4 -3
  57. package/src/streaming/event-stream.ts +26 -12
  58. package/src/telemetry.ts +6 -2
  59. package/src/tools/architecture.md +9 -7
  60. package/src/tools/executor.ts +109 -62
  61. package/src/tools/schema.ts +10 -6
  62. package/src/tools/subagent-result.ts +6 -1
  63. package/src/tools/tool-content.ts +32 -0
  64. package/src/tools/tool.ts +9 -11
  65. package/src/types/agent.ts +37 -22
  66. package/src/types/architecture.md +11 -10
  67. package/src/types/core.ts +5 -9
  68. package/src/types/errors.ts +26 -4
  69. package/src/types/message.ts +7 -0
  70. package/src/types/model.ts +4 -5
  71. package/src/types/scheduler.ts +4 -4
  72. package/src/types/tool.ts +14 -2
  73. package/src/update-models.ts +5 -5
  74. package/src/utils/architecture.md +16 -15
  75. package/src/utils/cache.ts +1 -3
  76. package/src/utils/durable.ts +17 -5
  77. package/src/utils/env.ts +9 -7
  78. package/src/utils/errors.ts +9 -17
  79. package/src/utils/headers.ts +3 -10
  80. package/src/utils/media.ts +23 -8
  81. package/src/utils/raw-capture.ts +9 -2
  82. package/src/utils/retry.ts +15 -3
  83. package/src/utils/safe-io.ts +29 -7
  84. package/src/utils/serialization.ts +1 -4
  85. package/src/utils/session.ts +1 -2
@@ -0,0 +1,19 @@
1
+ # src/prompts/
2
+
3
+ The prompts the SDK itself puts in front of the model, as plain `.md` files, so they can be read, copied and replaced.
4
+
5
+ | File | What it is |
6
+ | :-------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
7
+ | `prompts.ts` | `DEFAULT_PROMPTS` (the shipped text, read once at load, final newline removed), `resolvePrompts` (defaults + `AgentConfig.prompts`, validated), `fillPrompt` (`{{name}}` markers). |
8
+ | `reasoning-directive.md`, `structured-output.md`, `additional-context.md`, `tool-bridge.md` | Text added to the system prompt, a user turn or a history gap. |
9
+ | `tool-truncated-arguments.md`, `tool-not-found.md`, `tool-schema-error.md`, `tool-repeat-blocked.md`, `tool-interrupted.md` | The refusals the executor and the loop send back as tool results. |
10
+ | `worker-replied.md`, `worker-failed.md` | The header of a `subagents` answer. |
11
+ | `subagents-tool.md`, `scheduled-actions-tool.md` | The manuals that are the descriptions of the `subagents` and `scheduled_actions` tools. |
12
+
13
+ ## Invariants
14
+
15
+ - One file per prompt; the name in `FILES` (`prompts.ts`) is the public name. Add a prompt by adding a file, a `FILES` entry, its use, a row in `docs/guides/prompts.md` and a pinned-text assertion in `test/prompts.test.ts`.
16
+ - The default text is pinned exactly by a test: reflowing a file (an editor or formatter) changes what the model sees and the prompt cache, so the test must fail.
17
+ - Every user of a prompt reads it from the Agent's resolved prompts (`agent.prompts`, `loop` config `prompts`, `ProviderRequestOptions.prompts`, executor `prompts`), never from `DEFAULT_PROMPTS` directly, except as the default of an optional parameter.
18
+ - Machine-readable parts stay in code: the `repeat_blocked` `errorCode`, the `[Structured Output]` marker line.
19
+ - The system prompt carries no delegation text (cache-stable): tool manuals are tool descriptions.
@@ -0,0 +1,81 @@
1
+ /**
2
+ * The prompts the SDK itself puts in front of the model, kept as `.md` files in this folder so they can be read,
3
+ * copied and replaced. `DEFAULT_PROMPTS` holds the shipped text; `AgentConfig.prompts` overrides single entries per
4
+ * Agent. Keeping the original is recommended: it is what the SDK was tested with, and a good starting point (and the
5
+ * reference) for writing your own. `{{name}}` marks a value the SDK fills in at use time.
6
+ */
7
+ import { readFileSync } from "node:fs";
8
+ import { ConfigError } from "../types/errors.ts";
9
+
10
+ /** One entry per prompt file: its public name and the file that holds the default text. */
11
+ const FILES = {
12
+ reasoningDirective: "reasoning-directive.md",
13
+ structuredOutput: "structured-output.md",
14
+ additionalContext: "additional-context.md",
15
+ toolBridge: "tool-bridge.md",
16
+ toolTruncatedArguments: "tool-truncated-arguments.md",
17
+ toolNotFound: "tool-not-found.md",
18
+ toolSchemaError: "tool-schema-error.md",
19
+ toolRepeatBlocked: "tool-repeat-blocked.md",
20
+ toolInterrupted: "tool-interrupted.md",
21
+ workerReplied: "worker-replied.md",
22
+ workerFailed: "worker-failed.md",
23
+ subagentsTool: "subagents-tool.md",
24
+ scheduledActionsTool: "scheduled-actions-tool.md",
25
+ } as const;
26
+
27
+ /** The name of one SDK prompt (see `DEFAULT_PROMPTS`). */
28
+ export type PromptName = keyof typeof FILES;
29
+
30
+ /** The text of every prompt, by name. */
31
+ export type Prompts = Record<PromptName, string>;
32
+
33
+ /** Per-Agent replacements for single prompts (`AgentConfig.prompts`); every other prompt keeps its default. */
34
+ export type PromptOverrides = Partial<Prompts>;
35
+
36
+ // Reads one prompt file without its final newline; in: DEFAULT_PROMPTS at load, out: the default text.
37
+ function readPrompt(file: string): string {
38
+ return readFileSync(new URL(`./${file}`, import.meta.url), "utf8").replace(/\n$/, "");
39
+ }
40
+
41
+ /**
42
+ * The prompts as shipped. Placeholders (`{{name}}`) are filled in by the SDK: `reasoningDirective` none;
43
+ * `structuredOutput` `{{schema}}`; `additionalContext` `{{context}}`; `toolTruncatedArguments`, `toolRepeatBlocked`
44
+ * `{{tool}}`; `toolNotFound` `{{tool}}`, `{{suggestion}}`; `toolSchemaError` `{{tool}}`, `{{detail}}`, `{{keys}}`,
45
+ * `{{expected}}`; `toolInterrupted` `{{tool}}`, `{{id}}`; `workerReplied` `{{id}}`, `{{text}}`; `workerFailed` `{{id}}`,
46
+ * `{{reason}}`; `subagentsTool` `{{TOOLS}}`, `{{LIMIT}}`; `scheduledActionsTool` `{{TOOLS}}`.
47
+ */
48
+ export const DEFAULT_PROMPTS: Readonly<Prompts> = Object.freeze(
49
+ Object.fromEntries(Object.entries(FILES).map(([name, file]) => [name, readPrompt(file)])) as Prompts
50
+ );
51
+
52
+ /** Defaults with `overrides` applied. Throws a `ConfigError` for an unknown name or an empty or non-string text. */
53
+ export function resolvePrompts(overrides?: PromptOverrides): Prompts {
54
+ const out: Prompts = { ...DEFAULT_PROMPTS };
55
+ if (overrides === undefined) return out;
56
+ if (!overrides || typeof overrides !== "object" || Array.isArray(overrides)) {
57
+ throw new ConfigError(
58
+ '[Agent Accelerator] Invalid prompts: expected an object like { reasoningDirective: "..." } (see DEFAULT_PROMPTS for the names).'
59
+ );
60
+ }
61
+ for (const [name, text] of Object.entries(overrides)) {
62
+ if (text === undefined) continue;
63
+ if (!Object.hasOwn(FILES, name)) {
64
+ throw new ConfigError(
65
+ `[Agent Accelerator] Unknown prompt "${name}". Valid names: ${Object.keys(FILES).join(", ")}.`
66
+ );
67
+ }
68
+ if (typeof text !== "string" || !text.trim()) {
69
+ throw new ConfigError(`[Agent Accelerator] Invalid prompt "${name}": expected a non-empty string.`);
70
+ }
71
+ out[name as PromptName] = text;
72
+ }
73
+ return out;
74
+ }
75
+
76
+ /** Fills `{{name}}` markers in a prompt from `values`; a marker with no value is left as written. */
77
+ export function fillPrompt(template: string, values: Record<string, string | number>): string {
78
+ return template.replace(/\{\{(\w+)\}\}/g, (whole, key: string) =>
79
+ Object.hasOwn(values, key) ? String(values[key]) : whole
80
+ );
81
+ }
@@ -0,0 +1,5 @@
1
+ [Reasoning Directive]
2
+
3
+ 1. Use internal reasoning strictly for private planning and step-by-step thinking.
4
+ 2. Never attempt to execute tools or output final user deliverables inside reasoning.
5
+ 3. Once your reasoning is complete, output your final response or function calls directly in the standard response output.
@@ -3,6 +3,7 @@ Schedules a sub-agent (a "scheduled action") to run LATER, once or on repeat, on
3
3
  Call get_datetime first whenever you create or reschedule, then pick DD from today's date.
4
4
 
5
5
  COMMANDS (one tool, the `command` field)
6
+
6
7
  - create: schedule a new action. Needs instructions, task and scheduled_action.{name, id, timeToDeliver, meridiem, startCookingInAdvance}. Optional: role, tools, timeoutMs, scheduled_action.repeatEvery, scheduled_action.retries, scheduled_action.onMissed.
7
8
  - list: every action with its metadata and state (waiting, cooking, missed, done).
8
9
  - get: one action by `id`, with its latest run result.
@@ -12,6 +13,7 @@ COMMANDS (one tool, the `command` field)
12
13
  - skip: ignore a missed run of `id` and wait for the next scheduled time.
13
14
 
14
15
  SCHEDULED_ACTION FIELDS
16
+
15
17
  - name: human label.
16
18
  - id: lowercase kebab-case, at most 48 characters, e.g. "daily-ai-brief". The SDK appends 6 characters ("daily-ai-brief-d64aj2"); use that full id in every later command.
17
19
  - timeToDeliver: "DD:HH:MM". DD = days from today (00 today, 01 tomorrow, 55 = 55 days ahead). HH:MM = clock time on a 12-hour clock, HH 01-12.
@@ -30,12 +32,14 @@ Weekly report, every Monday at 9:00 AM (use get_datetime to count the days to th
30
32
  {"command":"create","instructions":"You review a codebase and report its health.","task":"Report test health, open risks and notable changes this week.","scheduled_action":{"name":"Weekly Codebase Health Report","id":"weekly-code-health","timeToDeliver":"03:09:00","meridiem":"AM","startCookingInAdvance":"00:02:00","repeatEvery":"07:00:00"}}
31
33
 
32
34
  TIMING ADVICE (do not guess; talk to the user)
35
+
33
36
  - A run must finish before the next one is due, so startCookingInAdvance must be shorter than repeatEvery.
34
37
  - Pick the lead time from the job: a quick lookup fits the 10-minute lead ("00:00:10"); research with many tools needs much more.
35
38
  - If the job looks too heavy for the gap the user asked for, do not schedule it blindly. Say so and suggest a wider gap, an earlier start, or a lighter task.
36
39
  - A run still cooking when the next one is due is skipped, never stacked, so a gap that is too small means lost runs.
37
40
 
38
41
  RULES
42
+
39
43
  - Each run starts blank and remembers nothing from earlier runs: put everything the worker needs in instructions and task.
40
44
  - Results go to files, not into this chat. Tell the user the action is scheduled and when it will be delivered.
41
45
  - Never put secrets in instructions or task: they are stored as plain text.
@@ -0,0 +1,2 @@
1
+ Return ONLY valid JSON matching this JSON Schema, with no markdown fences, no commentary, no extra text.
2
+ Schema: {{schema}}
@@ -1,12 +1,14 @@
1
1
  Starts helpers ("workers") that work on a task for you and can be asked again later. A worker is a separate agent with its own instructions, its own tools and its own memory: it knows only what you give it (plus the earlier conversation you choose to copy). Use workers for parallel research, a second opinion, or a long side task you do not want in your own context.
2
2
 
3
3
  COMMANDS (one tool, the `command` field)
4
+
4
5
  - spawn: start a worker. Needs `name` and `task`. Optional: `instructions` (the worker's own system prompt), `role`, `tools`, `history`. It waits for the worker's first reply and gives you the worker's `id`.
5
6
  - message: send `task` to the open worker `id`. The worker remembers everything said to it before. It waits for the reply.
6
7
  - close: end the worker `id` when you no longer need it. A closed worker cannot be messaged again.
7
8
  - list: your open workers, plus the numbered turns of this conversation that `history` can copy.
8
9
 
9
10
  FIELDS
11
+
10
12
  - name: a short label such as "researcher". The SDK adds a suffix; use the full `id` it returns ("researcher-1a2b3c4d") in message and close.
11
13
  - instructions: the worker's own system prompt: its role, rules and the format you want back.
12
14
  - task: for spawn, the first assignment; for message, the next message to that worker.
@@ -23,6 +25,7 @@ Ask it a follow-up:
23
25
  {"command":"message","id":"researcher-1a2b3c4d","task":"Now focus on write-heavy workloads."}
24
26
 
25
27
  RULES
28
+
26
29
  - Several workers at once: make several spawn calls in the same turn. They run in parallel.
27
30
  - A worker handles one call at a time. Wait for its reply before messaging it again.
28
31
  - {{LIMIT}}
@@ -0,0 +1 @@
1
+ I have processed the tool results.
@@ -0,0 +1 @@
1
+ Error: tool_call '{{tool}}' ({{id}}) has no result because the run was interrupted before it completed (recovered by ensureToolPairing). It may have partially executed: check the tool trace before repeating side-effecting work.
@@ -0,0 +1 @@
1
+ Error: Tool '{{tool}}' not found.{{suggestion}}
@@ -0,0 +1 @@
1
+ repeat_blocked: Tool '{{tool}}' was called again with identical arguments. The previous result is already available; reuse it or call the tool with different arguments.
@@ -0,0 +1 @@
1
+ Schema validation error for tool '{{tool}}': {{detail}} Received argument keys: [{{keys}}].{{expected}} Fix the arguments and call the tool again with corrected JSON. Do not explain the error, just retry the call.
@@ -0,0 +1 @@
1
+ Error: Tool '{{tool}}' arguments are truncated or incomplete and could not be parsed. Call the tool again with the complete JSON arguments. Do not explain the error, just retry the call.
@@ -0,0 +1 @@
1
+ [worker {{id}}] failed: {{reason}}
@@ -0,0 +1,2 @@
1
+ [worker {{id}}] replied:
2
+ {{text}}
@@ -2,19 +2,21 @@
2
2
 
3
3
  All HTTP and wire parsing. Canonical mappers (thinking, cache, tool choice, structured output) live in `../providers.ts`, never here.
4
4
 
5
- | File | Wire | Notes |
6
- | :-- | :-- | :-- |
7
- | `google.ts` | Google Interactions | Stateful chaining (TTL gate, self-heal on `not_found`), exact replay of raw `steps`, media consts at the top, Gemini 4 long decode as background interactions. |
8
- | `openai.ts` | OpenAI Responses | Stateless (`store:false`), full history every turn. |
9
- | `openrouter.ts` | OpenRouter Chat Completions | Every request streams (so it can be cancelled); `generate()` collects its own stream. |
10
- | `openai-compat.ts` | OpenAI-compatible Chat Completions | Serves every custom prefix; strict subset of fields; no default base URL. |
11
- | `registry.ts` | - | Routes `provider/model` to an adapter; the registry is immutable; `ModelProvider` helpers. |
12
- | `shared.ts` | - | Helpers every adapter uses: signals and timers, error payloads, ids, retry-after, SSE scaffold (`linkStreamAbort`, `withAbortTimer`, `readSseData`, `incompleteStreamCheck`, `failStream`). |
5
+ | File | Wire | Notes |
6
+ | :----------------- | :--------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
7
+ | `google.ts` | Google Interactions | Stateful chaining (TTL gate, self-heal on `not_found`), exact replay of raw `steps`, media consts at the top, Gemini 4 long decode as background interactions. The inline size checks (`assertGoogleInlineSizes`) exist but are not called yet. A video part's `processing` (`static`/`agentic`) is sent as the block's `processing` field. |
8
+ | `openai.ts` | OpenAI Responses | Stateless (`store:false`), full history every turn. |
9
+ | `openrouter.ts` | OpenRouter Chat Completions | Every request streams (so it can be cancelled); `generate()` collects its own stream. A turn's `reasoning_details` blocks are kept as `providerSteps` and sent back verbatim on the next request; parsed-PDF `file` annotations travel the same way, so a PDF is not parsed twice. Routing, `parallel_tool_calls`, `reasoning.exclude` and `user` come from `OPENROUTER_*` env vars (see the file header), never from AgentConfig. |
10
+ | `openai-compat.ts` | OpenAI-compatible Chat Completions | Serves every custom prefix; strict subset of fields; no default base URL. |
11
+ | `registry.ts` | - | Routes `provider/model` to an adapter; the registry is immutable; `ModelProvider` helpers. |
12
+ | `shared.ts` | - | Helpers every adapter uses: signals and timers, error payloads, ids, retry-after, SSE scaffold (`linkStreamAbort`, `withAbortTimer`, `readSseData`, `incompleteStreamCheck`, `failStream`). |
13
13
 
14
14
  ## Contract (`Provider`, in `../types/model.ts`)
15
+
15
16
  `generate(model, context, options)` returns a `ProviderGenerateResult`; `stream(...)` returns an `AssistantMessageEventStream` that ends with an `AgentResponse`; optional `isLongRunning()` tells the loop to skip its whole-call timeouts.
16
17
 
17
18
  ## Invariants
19
+
18
20
  - Fix shared streaming behaviour in `shared.ts`, never per adapter. Event parsing and request building stay in each adapter.
19
21
  - OpenRouter and compat chat chunk handlers are deliberately not merged.
20
22
  - A custom prefix never gets another provider's key or base URL.