@intentface/latch-core 0.9.1

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 (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -0
  3. package/dist/agent.d.ts +200 -0
  4. package/dist/agent.d.ts.map +1 -0
  5. package/dist/agent.js +9 -0
  6. package/dist/agent.js.map +1 -0
  7. package/dist/compaction.d.ts +33 -0
  8. package/dist/compaction.d.ts.map +1 -0
  9. package/dist/compaction.js +104 -0
  10. package/dist/compaction.js.map +1 -0
  11. package/dist/connections.d.ts +16 -0
  12. package/dist/connections.d.ts.map +1 -0
  13. package/dist/connections.js +41 -0
  14. package/dist/connections.js.map +1 -0
  15. package/dist/context.d.ts +41 -0
  16. package/dist/context.d.ts.map +1 -0
  17. package/dist/context.js +25 -0
  18. package/dist/context.js.map +1 -0
  19. package/dist/current-date.d.ts +12 -0
  20. package/dist/current-date.d.ts.map +1 -0
  21. package/dist/current-date.js +25 -0
  22. package/dist/current-date.js.map +1 -0
  23. package/dist/extensions.d.ts +333 -0
  24. package/dist/extensions.d.ts.map +1 -0
  25. package/dist/extensions.js +569 -0
  26. package/dist/extensions.js.map +1 -0
  27. package/dist/harness/index.d.ts +17 -0
  28. package/dist/harness/index.d.ts.map +1 -0
  29. package/dist/harness/index.js +15 -0
  30. package/dist/harness/index.js.map +1 -0
  31. package/dist/harness/tools.d.ts +88 -0
  32. package/dist/harness/tools.d.ts.map +1 -0
  33. package/dist/harness/tools.js +296 -0
  34. package/dist/harness/tools.js.map +1 -0
  35. package/dist/harness/web-fetch.d.ts +47 -0
  36. package/dist/harness/web-fetch.d.ts.map +1 -0
  37. package/dist/harness/web-fetch.js +247 -0
  38. package/dist/harness/web-fetch.js.map +1 -0
  39. package/dist/index.d.ts +25 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +25 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/limits.d.ts +152 -0
  44. package/dist/limits.d.ts.map +1 -0
  45. package/dist/limits.js +97 -0
  46. package/dist/limits.js.map +1 -0
  47. package/dist/memory.d.ts +93 -0
  48. package/dist/memory.d.ts.map +1 -0
  49. package/dist/memory.js +13 -0
  50. package/dist/memory.js.map +1 -0
  51. package/dist/message.d.ts +46 -0
  52. package/dist/message.d.ts.map +1 -0
  53. package/dist/message.js +2 -0
  54. package/dist/message.js.map +1 -0
  55. package/dist/models/catalog.d.ts +56 -0
  56. package/dist/models/catalog.d.ts.map +1 -0
  57. package/dist/models/catalog.js +211 -0
  58. package/dist/models/catalog.js.map +1 -0
  59. package/dist/models/defaults.d.ts +23 -0
  60. package/dist/models/defaults.d.ts.map +1 -0
  61. package/dist/models/defaults.js +19 -0
  62. package/dist/models/defaults.js.map +1 -0
  63. package/dist/models/index.d.ts +23 -0
  64. package/dist/models/index.d.ts.map +1 -0
  65. package/dist/models/index.js +19 -0
  66. package/dist/models/index.js.map +1 -0
  67. package/dist/models/prompt-caching.d.ts +19 -0
  68. package/dist/models/prompt-caching.d.ts.map +1 -0
  69. package/dist/models/prompt-caching.js +18 -0
  70. package/dist/models/prompt-caching.js.map +1 -0
  71. package/dist/models/provider.d.ts +19 -0
  72. package/dist/models/provider.d.ts.map +1 -0
  73. package/dist/models/provider.js +22 -0
  74. package/dist/models/provider.js.map +1 -0
  75. package/dist/models/reasoning.d.ts +21 -0
  76. package/dist/models/reasoning.d.ts.map +1 -0
  77. package/dist/models/reasoning.js +59 -0
  78. package/dist/models/reasoning.js.map +1 -0
  79. package/dist/pricing.d.ts +52 -0
  80. package/dist/pricing.d.ts.map +1 -0
  81. package/dist/pricing.js +37 -0
  82. package/dist/pricing.js.map +1 -0
  83. package/dist/principal.d.ts +37 -0
  84. package/dist/principal.d.ts.map +1 -0
  85. package/dist/principal.js +30 -0
  86. package/dist/principal.js.map +1 -0
  87. package/dist/projections.d.ts +37 -0
  88. package/dist/projections.d.ts.map +1 -0
  89. package/dist/projections.js +128 -0
  90. package/dist/projections.js.map +1 -0
  91. package/dist/prompt-caching.d.ts +106 -0
  92. package/dist/prompt-caching.d.ts.map +1 -0
  93. package/dist/prompt-caching.js +165 -0
  94. package/dist/prompt-caching.js.map +1 -0
  95. package/dist/runtime.d.ts +670 -0
  96. package/dist/runtime.d.ts.map +1 -0
  97. package/dist/runtime.js +2425 -0
  98. package/dist/runtime.js.map +1 -0
  99. package/dist/scheduler.d.ts +31 -0
  100. package/dist/scheduler.d.ts.map +1 -0
  101. package/dist/scheduler.js +43 -0
  102. package/dist/scheduler.js.map +1 -0
  103. package/dist/storage.d.ts +389 -0
  104. package/dist/storage.d.ts.map +1 -0
  105. package/dist/storage.js +38 -0
  106. package/dist/storage.js.map +1 -0
  107. package/dist/telemetry.d.ts +155 -0
  108. package/dist/telemetry.d.ts.map +1 -0
  109. package/dist/telemetry.js +2 -0
  110. package/dist/telemetry.js.map +1 -0
  111. package/dist/vault-node.d.ts +20 -0
  112. package/dist/vault-node.d.ts.map +1 -0
  113. package/dist/vault-node.js +30 -0
  114. package/dist/vault-node.js.map +1 -0
  115. package/dist/vault.d.ts +62 -0
  116. package/dist/vault.d.ts.map +1 -0
  117. package/dist/vault.js +88 -0
  118. package/dist/vault.js.map +1 -0
  119. package/package.json +95 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Intentface
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # @intentface/latch-core
2
+
3
+ The framework core for building agents on the **AI SDK v7** — the runtime, the message/identity model, and the pluggable seams everything else plugs into. Dependency-light and host-agnostic (no HTTP framework, no DB, no provider SDKs).
4
+
5
+ 📖 **Full documentation:** [docs source](https://github.com/Intentface/intentface-latch/tree/main/apps/docs) — to read it locally, run `pnpm --filter @intentface/latch-docs dev` and open http://localhost:3100/docs.
6
+
7
+ ## In one screen
8
+
9
+ ```ts
10
+ import { createRuntime, defineAgent, defineContext } from "@intentface/latch-core";
11
+
12
+ const runtime = createRuntime<Principal, Ctx>({
13
+ storage, // chats, messages, runs, usage, schedules
14
+ context, // per-turn RuntimeContext built from the principal
15
+ agents, // name → agent factory
16
+ // …optional seams: pricing, connections, memory, cron, sandbox, telemetry
17
+ });
18
+
19
+ const response = await runtime.handleChat({ agent: "assistant", principal, message });
20
+ ```
21
+
22
+ ## What it does
23
+
24
+ - **Runtime** (`createRuntime`) — composition root + operations facade. Runs one agent turn with a `ToolLoopAgent`, streams `UIMessage` parts, persists messages/runs/usage, and handles approvals (HITL), durability (leases + reaper), subagents, and schedules.
25
+ - **Message model** — `AppMessage` = a typed AI SDK `UIMessage`. The source of truth; two axes (`visibility`, `sendToModel`) drive the client and model projections.
26
+ - **Seams** — `StorageAdapter`, `ConnectionRegistry`, `Vault`, `Pricing`, `MemoryProvider`, plus the `cron` / `sandbox` / `harnessTools` / `telemetry` hooks. `Principal` stays opaque: authz is the integrator's job.
27
+ - **Helpers** — `defineAgent`, `defineContext`, `mergeConnections`, `computeCost` / `staticPricing`, `toModelMessages` / `toClientMessages`.
28
+
29
+ ## Docs map
30
+
31
+ | Page | Covers |
32
+ | --- | --- |
33
+ | Introduction | what it is, the adoption tiers, a minimal runtime |
34
+ | Runtime | every config field, every operation, what a turn does in order |
35
+ | Agents | `defineAgent`, capability flags, subagents, dynamic agents |
36
+ | Turn inputs | principal, context, messages, projections, compaction |
37
+ | Storage | the `StorageAdapter` contract and the guarantees it must make |
38
+ | Operations | durability, schedules, human-in-the-loop |
39
+ | Seams | connections, vault, memory, pricing, telemetry, sandbox |
40
+
41
+ Runnable examples live in [`apps/example`](https://github.com/Intentface/intentface-latch/tree/main/apps/example) (source) — 23 smoke scripts covering persistence, durability, resume, approvals, MCP, vault, cost and observability.
42
+
43
+ ## Where it fits
44
+
45
+ The base of the `@intentface/latch-*` stack. Mount it over HTTP with `latch-server`; persist with `latch-drizzle`; add tools via `latch-mcp` / `latch-openapi`; reach chat platforms with `latch-channels`. Chat UI primitives live in a separate repo, [`@intentface/chat`](https://github.com/Intentface/intentface-chat).
@@ -0,0 +1,200 @@
1
+ import type { LanguageModel } from "ai";
2
+ import type { MemoryConfig } from "./memory.js";
3
+ /**
4
+ * Agent definitions.
5
+ *
6
+ * An agent definition is *config*, never an instantiated engine — that's the
7
+ * "engine portability" invariant: the executor builds a `ToolLoopAgent` (T1) or
8
+ * a `WorkflowAgent` (later) from the same config. It's a lazy factory resolved
9
+ * per request with the typed runtime context (static config is wrapped into one).
10
+ */
11
+ /**
12
+ * Minimal, engine-agnostic agent config (a subset of the SDK's agent settings).
13
+ * `tools` is kept loose for now: the SDK does export `ToolSet`, but every tool
14
+ * source (connections, MCP, OpenAPI, the platform's dynamic connections) builds
15
+ * a `Record<string, unknown>` and adopting the SDK type is one typing pass
16
+ * across them all. Until then the concrete engine constrains it at the call
17
+ * site when the run executes.
18
+ */
19
+ /** A set of tools opened for a turn, plus the teardown to run when it ends. */
20
+ export interface OpenToolSet {
21
+ tools: Record<string, unknown>;
22
+ close(): Promise<void>;
23
+ /**
24
+ * Optional per-tool approval policy contributed by this source (merged into
25
+ * the agent's `toolApproval`, object form). Lets a source gate its own tools —
26
+ * e.g. an MCP host gating a synthetic `connect_<name>` tool so an unauthorized
27
+ * connection pauses the turn for OAuth instead of failing.
28
+ */
29
+ toolApproval?: Record<string, unknown>;
30
+ /**
31
+ * Tools that stay callable even when an agent restricts `enabledTools` — e.g.
32
+ * the synthetic `connect_<name>` auth tools, which must work so the connect
33
+ * flow isn't blocked by a tool allow-list.
34
+ */
35
+ alwaysActive?: string[];
36
+ }
37
+ /**
38
+ * A dynamic, per-turn tool provider (e.g. an MCP host). The runtime calls
39
+ * `open(principal)` before the turn, merges the tools, and calls `close()` after —
40
+ * so connections are scoped to the caller and torn down when the turn ends.
41
+ * `@intentface/latch-mcp`'s `McpHost` satisfies this structurally.
42
+ */
43
+ export interface ToolSource<P> {
44
+ open(principal: P): Promise<OpenToolSet>;
45
+ }
46
+ export interface AgentConfig<P> {
47
+ model: LanguageModel;
48
+ instructions?: string;
49
+ /** Static tools, available every turn. */
50
+ tools?: Record<string, unknown>;
51
+ /** Dynamic tool sources opened per turn and closed after (e.g. MCP). */
52
+ toolSources?: ToolSource<P>[];
53
+ /**
54
+ * Names of the connections (from the runtime's connection registry) this
55
+ * agent may use — its MCP servers / integrations. The runtime resolves these
56
+ * to a per-turn tool source: each authorized connection's tools, plus a gated
57
+ * `connect_<name>` tool for any that still need OAuth. Different agents can
58
+ * carry different connections. Names with no registry entry are ignored.
59
+ */
60
+ connections?: string[];
61
+ /**
62
+ * HITL approval policy (the SDK's `toolApproval`): per-tool `'user-approval'`,
63
+ * an object with a reason, or a function. A gated call ends the turn with an
64
+ * `approval-requested` part instead of executing; the decision round-trips as
65
+ * message state via `runtime.applyApproval`. Kept loose (the SDK type is
66
+ * complex); cast at the agent boundary.
67
+ */
68
+ toolApproval?: unknown;
69
+ /**
70
+ * Smooth the visible token cadence (AI SDK `smoothStream`): even out bursty
71
+ * deltas before they stream to the client. `true` uses the SDK defaults
72
+ * (~10ms, word chunking); or tune `delayInMs`/`chunking`. Presentation only —
73
+ * it does not change what gets persisted.
74
+ */
75
+ smoothStream?: boolean | {
76
+ delayInMs?: number;
77
+ chunking?: "word" | "line";
78
+ };
79
+ /**
80
+ * Max tool-loop steps before the turn stops (the SDK `stopWhen` ceiling).
81
+ * Omitted → SDK default (20). A number caps the loop; `"unlimited"` lets it
82
+ * run until the model stops on its own (no more tool calls) — use with care,
83
+ * it removes the runaway-loop backstop.
84
+ */
85
+ maxSteps?: number | "unlimited";
86
+ /**
87
+ * Restrict which tools the model may call (the SDK's `activeTools`) — the
88
+ * namespaced names (e.g. `clockify_getTimeEntries`). Omitted → all available
89
+ * tools are active. Tools a source marks `alwaysActive` (e.g. `connect_<name>`)
90
+ * stay callable regardless.
91
+ */
92
+ enabledTools?: string[];
93
+ /**
94
+ * Per-tool description overrides (by namespaced tool name). The runtime
95
+ * replaces the resolved tool's `description` with this text before the model
96
+ * sees it — e.g. to tailor a connection tool's guidance for this agent.
97
+ */
98
+ toolDescriptions?: Record<string, string>;
99
+ /**
100
+ * Agents this one may delegate to. The runtime adds a `spawn_agent` tool that
101
+ * runs one of these as a subagent (a normal agent, run autonomously — its
102
+ * tools are auto-approved) and returns its final answer. Each subagent run is
103
+ * its own persisted thread; pass the returned `threadId` back to continue the
104
+ * same subagent with full context.
105
+ */
106
+ subagents?: string[];
107
+ /**
108
+ * Give the agent a per-session sandbox (a workspace) and the filesystem/shell
109
+ * tools that operate on it (`bash`, `read_file`, `write_file`, `glob`,
110
+ * `grep`). The runtime resolves the actual `Experimental_SandboxSession` via
111
+ * `RuntimeConfig.sandbox` and passes it to the model; the tools come from
112
+ * `RuntimeConfig.harnessTools.sandbox`. Optional `provider` picks the backend
113
+ * (e.g. `"just-bash"` | `"daytona"`); the resolver decides the default.
114
+ */
115
+ sandbox?: boolean;
116
+ /** Sandbox backend hint, passed through to `RuntimeConfig.sandbox`. */
117
+ sandboxProvider?: string;
118
+ /**
119
+ * Add the app-runtime default harness tools (`web_fetch`, `todo`,
120
+ * `ask_question`) from `RuntimeConfig.harnessTools.app`. `true` adds all;
121
+ * an array restricts to those names. These run in the app process, not the
122
+ * sandbox.
123
+ */
124
+ defaultTools?: boolean | string[];
125
+ /**
126
+ * Give the agent the platform's render tool(s) from
127
+ * `RuntimeConfig.harnessTools.render` — a headless-browser fetch for
128
+ * JS-rendered pages. A separate capability flag (like `sandbox`) rather than
129
+ * a `defaultTools` member: the platform supplies it (needs a deployed render
130
+ * service) and it must be opted into explicitly, never swept in by
131
+ * `defaultTools: true`.
132
+ */
133
+ renderTools?: boolean;
134
+ /** Names of harness/default tools to drop for this agent (eve-style opt-out). */
135
+ disableTools?: string[];
136
+ /**
137
+ * Use the model provider's NATIVE web tools (server-side web search/fetch)
138
+ * when available, supplied by `RuntimeConfig.resolveProviderTools(modelId)`.
139
+ * Provider-specific: e.g. Anthropic exposes web_search + web_fetch, OpenAI
140
+ * web_search only. No-op on providers without native web tools.
141
+ */
142
+ providerWebTools?: boolean;
143
+ /**
144
+ * Reasoning/thinking effort for this agent. Mapped to provider-specific
145
+ * options by `RuntimeConfig.resolveReasoningOptions` (OpenAI effort enum vs
146
+ * Anthropic/Google thinking-token budget) and gated to reasoning-capable
147
+ * models. Omitted → the provider default.
148
+ */
149
+ effort?: "minimal" | "low" | "medium" | "high";
150
+ /**
151
+ * Persistent cross-session memory for this agent. Requires the runtime to be
152
+ * composed with a `MemoryProvider` (`RuntimeConfig.memory`) — without one the
153
+ * declaration is ignored. When both are set, each turn gets the provider's
154
+ * compiled memory index appended to `instructions` and its memory tools
155
+ * (`memory_search`/`memory_save`) merged as capability tools; the provider's
156
+ * background jobs handle extraction and consolidation out of band.
157
+ */
158
+ memory?: MemoryConfig;
159
+ }
160
+ /** Static, display-only metadata for an agent — surfaced by `runtime.listAgents()`. */
161
+ export interface AgentMeta {
162
+ /** Human-friendly name for a picker (defaults to the registry key). */
163
+ title?: string;
164
+ /** Short description of what the agent does. */
165
+ description?: string;
166
+ /**
167
+ * Internal agent: still resolvable/runnable by name, but flagged so
168
+ * user-facing pickers can drop it. `runtime.listAgents()` returns hidden
169
+ * agents WITH the flag (internal callers — e.g. a builder validating
170
+ * subagent names — need the full set); the HTTP `GET agents` route filters
171
+ * them out.
172
+ */
173
+ hidden?: boolean;
174
+ }
175
+ /** An agent's identity for selection UIs: its registry key + display metadata. */
176
+ export interface AgentInfo extends AgentMeta {
177
+ name: string;
178
+ }
179
+ /**
180
+ * A factory that produces the agent's config for a given request/context.
181
+ * Carries optional static `meta` (read without invoking the factory) so the
182
+ * runtime can list agents for a picker.
183
+ */
184
+ export type AgentFactory<P, RuntimeContext = unknown> = ((ctx: {
185
+ /**
186
+ * The runtime CONTEXT built by `context.build` for this turn (org name, plan,
187
+ * feature flags — see `ContextDefinition`).
188
+ */
189
+ context: RuntimeContext;
190
+ principal: P;
191
+ /**
192
+ * The per-turn request payload (see `ContextDefinition.build`). Ephemeral —
193
+ * absent on resume/cron turns, so factories must not require it.
194
+ */
195
+ turnContext?: unknown;
196
+ }) => AgentConfig<P> | Promise<AgentConfig<P>>) & {
197
+ meta?: AgentMeta;
198
+ };
199
+ export declare function defineAgent<P, RuntimeContext = unknown>(def: AgentConfig<P> | AgentFactory<P, RuntimeContext>, meta?: AgentMeta): AgentFactory<P, RuntimeContext>;
200
+ //# sourceMappingURL=agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
package/dist/agent.js ADDED
@@ -0,0 +1,9 @@
1
+ export function defineAgent(def, meta) {
2
+ const factory = typeof def === "function"
3
+ ? def
4
+ : () => def;
5
+ if (meta)
6
+ factory.meta = meta;
7
+ return factory;
8
+ }
9
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAyMA,MAAM,UAAU,WAAW,CACzB,GAAqD,EACrD,IAAgB;IAEhB,MAAM,OAAO,GACX,OAAO,GAAG,KAAK,UAAU;QACvB,CAAC,CAAE,GAAuC;QAC1C,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC;IAChB,IAAI,IAAI;QAAE,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAC9B,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { type LanguageModel, type ModelMessage } from "ai";
2
+ /**
3
+ * Summarize a conversation for compaction with a single tool-free model call.
4
+ *
5
+ * Deliberately NOT a normal agent turn: with the agent's own tools and
6
+ * instructions in play the tool loop would go do work instead of summarizing.
7
+ */
8
+ export declare function summarizeForCompaction(input: {
9
+ model: LanguageModel;
10
+ messages: ModelMessage[];
11
+ /** Extra steer from the caller, e.g. `/compact keep the training plan`. */
12
+ focus?: string;
13
+ /**
14
+ * Provider options for the call. Callers should pass their "don't think"
15
+ * mapping (see `resolveReasoningOptions`): summarizing needs no reasoning
16
+ * block, and on a thinking-by-default model that block competes with the
17
+ * summary for the same output budget.
18
+ */
19
+ providerOptions?: Record<string, unknown>;
20
+ /** Abort a stalled provider (a request signal, or a caller-owned timeout). */
21
+ signal?: AbortSignal;
22
+ }): Promise<{
23
+ text: string;
24
+ /** True when even the shortened retry hit `MAX_SUMMARY_TOKENS`. */
25
+ truncated: boolean;
26
+ usage: {
27
+ inputTokens: number;
28
+ outputTokens: number;
29
+ cacheReadTokens: number;
30
+ cacheWriteTokens: number;
31
+ };
32
+ }>;
33
+ //# sourceMappingURL=compaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../src/compaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,IAAI,CAAC;AA6CzE;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,KAAK,EAAE;IAClD,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,8EAA8E;IAC9E,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE;QACL,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,MAAM,CAAC;QACxB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH,CAAC,CA+DD"}
@@ -0,0 +1,104 @@
1
+ import { generateText } from "ai";
2
+ /**
3
+ * Conversation compaction: replace a long history with a summary the model reads
4
+ * instead, so a chat that never ends (a Telegram DM, a months-old thread) stops
5
+ * growing its prompt without losing what matters.
6
+ *
7
+ * The boundary itself is a projection concern (`sliceAtCompaction` in
8
+ * `projections.ts`); this module only produces the summary text. Kept separate
9
+ * from `runtime.ts` so the prompt is easy to find and tune.
10
+ */
11
+ const COMPACTION_SYSTEM = `You compact conversations so they can continue in a smaller context window.
12
+
13
+ Write a summary that lets the assistant carry on as if it had read the whole
14
+ conversation. Preserve, in this order of priority:
15
+ 1. What the user is trying to achieve, and any decisions already made.
16
+ 2. Facts about the user and their situation that came up (names, numbers, dates,
17
+ preferences, constraints) — these are usually irrecoverable once dropped.
18
+ 3. What has been done so far, including tool results that still matter.
19
+ 4. Anything explicitly left open, pending, or promised.
20
+
21
+ Rules:
22
+ - Write in the third person about "the user" and "the assistant".
23
+ - Be specific: keep concrete values, drop pleasantries and dead ends.
24
+ - Never invent anything. If something is unclear, say so plainly.
25
+ - No preamble, no headings-for-their-own-sake, no closing remarks — just the
26
+ summary itself.`;
27
+ const instructionFor = (words) => `Summarize the conversation above as described in your system prompt, so it can be continued in a fresh context window. Keep it under ${words} words. Output only the summary.`;
28
+ /**
29
+ * Hard ceiling on the call. Generous on purpose: it is a backstop against a
30
+ * runaway summary, NOT the mechanism that keeps summaries small — that's the word
31
+ * target in the instruction. A tight ceiling here is actively harmful, because on
32
+ * a model that thinks by default the thinking block spends this budget before any
33
+ * summary text is produced, and the call ends truncated with nothing usable.
34
+ */
35
+ const MAX_SUMMARY_TOKENS = 8000;
36
+ /** Asked-for length. Big enough to carry a long conversation's specifics. */
37
+ const SUMMARY_WORD_TARGET = 600;
38
+ /** Retry target after a truncated first attempt — decisively shorter. */
39
+ const RETRY_WORD_TARGET = 250;
40
+ /**
41
+ * Summarize a conversation for compaction with a single tool-free model call.
42
+ *
43
+ * Deliberately NOT a normal agent turn: with the agent's own tools and
44
+ * instructions in play the tool loop would go do work instead of summarizing.
45
+ */
46
+ export async function summarizeForCompaction(input) {
47
+ const focus = input.focus?.trim();
48
+ const attempt = async (words) => {
49
+ const instruction = instructionFor(words);
50
+ const res = await generateText({
51
+ model: input.model,
52
+ system: COMPACTION_SYSTEM,
53
+ maxOutputTokens: MAX_SUMMARY_TOKENS,
54
+ // Cast for the same reason `buildTurn` casts its ToolLoopAgent options: the
55
+ // hook's shape is `Record<string, unknown>` (it's host-supplied and
56
+ // provider-specific), while the SDK wants `Record<string, JSONObject>`.
57
+ ...(input.providerOptions
58
+ ? { providerOptions: input.providerOptions }
59
+ : {}),
60
+ ...(input.signal ? { abortSignal: input.signal } : {}),
61
+ messages: [
62
+ ...input.messages,
63
+ {
64
+ role: "user",
65
+ content: focus
66
+ ? `${instruction}\n\nPay particular attention to: ${focus}`
67
+ : instruction,
68
+ },
69
+ ],
70
+ });
71
+ // Same shape the tool loop reads per step (`onStepEnd` in runtime.ts), so the
72
+ // cached-prompt split reaches `computeCost` the same way.
73
+ const u = res.usage;
74
+ return {
75
+ text: res.text.trim(),
76
+ truncated: res.finishReason === "length",
77
+ usage: {
78
+ inputTokens: u?.inputTokens ?? 0,
79
+ outputTokens: u?.outputTokens ?? 0,
80
+ cacheReadTokens: u?.inputTokenDetails?.cacheReadTokens ?? 0,
81
+ cacheWriteTokens: u?.inputTokenDetails?.cacheWriteTokens ?? 0,
82
+ },
83
+ };
84
+ };
85
+ const first = await attempt(SUMMARY_WORD_TARGET);
86
+ if (!first.truncated)
87
+ return first;
88
+ // Truncated: ask again for a decisively shorter summary rather than giving up.
89
+ // A truncated summary must never be stored (it becomes the permanent head of
90
+ // every later prompt), but failing outright would mean the longest
91
+ // conversations — the ones that most need compacting — could never be.
92
+ const retry = await attempt(RETRY_WORD_TARGET);
93
+ return {
94
+ ...retry,
95
+ // Both calls were paid for; report the total so cost isn't understated.
96
+ usage: {
97
+ inputTokens: first.usage.inputTokens + retry.usage.inputTokens,
98
+ outputTokens: first.usage.outputTokens + retry.usage.outputTokens,
99
+ cacheReadTokens: first.usage.cacheReadTokens + retry.usage.cacheReadTokens,
100
+ cacheWriteTokens: first.usage.cacheWriteTokens + retry.usage.cacheWriteTokens,
101
+ },
102
+ };
103
+ }
104
+ //# sourceMappingURL=compaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compaction.js","sourceRoot":"","sources":["../src/compaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAyC,MAAM,IAAI,CAAC;AAEzE;;;;;;;;GAQG;AAEH,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;kBAeR,CAAC;AAEnB,MAAM,cAAc,GAAG,CAAC,KAAa,EAAE,EAAE,CACvC,wIAAwI,KAAK,kCAAkC,CAAC;AAElL;;;;;;GAMG;AACH,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAChC,6EAA6E;AAC7E,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAChC,yEAAyE;AACzE,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAE9B;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,KAc5C;IAWC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;IAElC,MAAM,OAAO,GAAG,KAAK,EAAE,KAAa,EAAE,EAAE;QACtC,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC;YAC7B,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,iBAAiB;YACzB,eAAe,EAAE,kBAAkB;YACnC,4EAA4E;YAC5E,oEAAoE;YACpE,wEAAwE;YACxE,GAAG,CAAC,KAAK,CAAC,eAAe;gBACvB,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,eAAwC,EAAE;gBACrE,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACtD,QAAQ,EAAE;gBACR,GAAG,KAAK,CAAC,QAAQ;gBACjB;oBACE,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE,KAAK;wBACZ,CAAC,CAAC,GAAG,WAAW,oCAAoC,KAAK,EAAE;wBAC3D,CAAC,CAAC,WAAW;iBAChB;aACF;SACF,CAAC,CAAC;QACH,8EAA8E;QAC9E,0DAA0D;QAC1D,MAAM,CAAC,GAAG,GAAG,CAAC,KAID,CAAC;QACd,OAAO;YACL,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE;YACrB,SAAS,EAAE,GAAG,CAAC,YAAY,KAAK,QAAQ;YACxC,KAAK,EAAE;gBACL,WAAW,EAAE,CAAC,EAAE,WAAW,IAAI,CAAC;gBAChC,YAAY,EAAE,CAAC,EAAE,YAAY,IAAI,CAAC;gBAClC,eAAe,EAAE,CAAC,EAAE,iBAAiB,EAAE,eAAe,IAAI,CAAC;gBAC3D,gBAAgB,EAAE,CAAC,EAAE,iBAAiB,EAAE,gBAAgB,IAAI,CAAC;aAC9D;SACF,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACjD,IAAI,CAAC,KAAK,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAEnC,+EAA+E;IAC/E,6EAA6E;IAC7E,mEAAmE;IACnE,uEAAuE;IACvE,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC/C,OAAO;QACL,GAAG,KAAK;QACR,wEAAwE;QACxE,KAAK,EAAE;YACL,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,WAAW;YAC9D,YAAY,EAAE,KAAK,CAAC,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC,YAAY;YACjE,eAAe,EAAE,KAAK,CAAC,KAAK,CAAC,eAAe,GAAG,KAAK,CAAC,KAAK,CAAC,eAAe;YAC1E,gBAAgB,EAAE,KAAK,CAAC,KAAK,CAAC,gBAAgB,GAAG,KAAK,CAAC,KAAK,CAAC,gBAAgB;SAC9E;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { ToolSource } from "./agent.js";
2
+ import type { ConnectionRegistry } from "./runtime.js";
3
+ /**
4
+ * Combine tool sources into one: `open` opens all of them and merges their
5
+ * tools + approval policies; `close` tears them all down. Later sources win on
6
+ * key collisions (namespacing avoids that in practice).
7
+ */
8
+ export declare function mergeToolSources<P>(...sources: ToolSource<P>[]): ToolSource<P>;
9
+ /**
10
+ * Combine connection registries of different kinds (e.g. MCP + OpenAPI) into one
11
+ * the runtime can use. An agent's `connections: [...]` then resolves across all
12
+ * of them — each registry's `hostFor` ignores names it doesn't own, so the
13
+ * merged source is the union of whatever each kind contributes for those names.
14
+ */
15
+ export declare function mergeConnections<P>(...registries: ConnectionRegistry<P>[]): ConnectionRegistry<P>;
16
+ //# sourceMappingURL=connections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connections.d.ts","sourceRoot":"","sources":["../src/connections.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,UAAU,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC,CAuB9E;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,GAAG,UAAU,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAAE,GACrC,kBAAkB,CAAC,CAAC,CAAC,CAKvB"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Combine tool sources into one: `open` opens all of them and merges their
3
+ * tools + approval policies; `close` tears them all down. Later sources win on
4
+ * key collisions (namespacing avoids that in practice).
5
+ */
6
+ export function mergeToolSources(...sources) {
7
+ return {
8
+ async open(principal) {
9
+ const opened = await Promise.all(sources.map((s) => s.open(principal)));
10
+ const tools = {};
11
+ const toolApproval = {};
12
+ const alwaysActive = [];
13
+ for (const o of opened) {
14
+ Object.assign(tools, o.tools);
15
+ Object.assign(toolApproval, o.toolApproval ?? {});
16
+ if (o.alwaysActive)
17
+ alwaysActive.push(...o.alwaysActive);
18
+ }
19
+ return {
20
+ tools,
21
+ toolApproval,
22
+ // Preserve each source's always-callable tools (e.g. the synthetic
23
+ // connect_<name> auth tools) so a tool-restricted agent keeps them.
24
+ alwaysActive,
25
+ close: () => Promise.all(opened.map((o) => o.close().catch(() => { }))).then(() => undefined),
26
+ };
27
+ },
28
+ };
29
+ }
30
+ /**
31
+ * Combine connection registries of different kinds (e.g. MCP + OpenAPI) into one
32
+ * the runtime can use. An agent's `connections: [...]` then resolves across all
33
+ * of them — each registry's `hostFor` ignores names it doesn't own, so the
34
+ * merged source is the union of whatever each kind contributes for those names.
35
+ */
36
+ export function mergeConnections(...registries) {
37
+ return {
38
+ hostFor: (names) => mergeToolSources(...registries.map((r) => r.hostFor(names))),
39
+ };
40
+ }
41
+ //# sourceMappingURL=connections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connections.js","sourceRoot":"","sources":["../src/connections.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAI,GAAG,OAAwB;IAC7D,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,SAAS;YAClB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YACxE,MAAM,KAAK,GAA4B,EAAE,CAAC;YAC1C,MAAM,YAAY,GAA4B,EAAE,CAAC;YACjD,MAAM,YAAY,GAAa,EAAE,CAAC;YAClC,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;gBACvB,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;gBAC9B,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;gBAClD,IAAI,CAAC,CAAC,YAAY;oBAAE,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;YAC3D,CAAC;YACD,OAAO;gBACL,KAAK;gBACL,YAAY;gBACZ,mEAAmE;gBACnE,oEAAoE;gBACpE,YAAY;gBACZ,KAAK,EAAE,GAAG,EAAE,CACV,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;aAClF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,GAAG,UAAmC;IAEtC,OAAO;QACL,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CACjB,gBAAgB,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;KAC/D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Per-project context definition.
3
+ *
4
+ * The AI SDK only has raw per-call `runtimeContext` / `toolsContext`. `defineContext`
5
+ * is thin sugar (OURS, not an SDK API): a project declares its `RuntimeContext`
6
+ * shape once and builds it per request from the (opaque, customer-typed)
7
+ * `Principal`. The type then flows through `defineAgent`, prepareStep, hooks,
8
+ * and tools.
9
+ *
10
+ * Tools receive the built context automatically: the runtime hands every tool
11
+ * `{ ...runtimeContext, principal, run, writer, runtime }` as its
12
+ * `options.context` (the runtime itself included, so a harness tool can
13
+ * schedule or spawn without a host-global), so a tool that needs a credential
14
+ * resolves it itself, scoped by the principal
15
+ * (e.g. `vault.resolver(ctx.principal, "crm_api_key")`) — the model never sees
16
+ * the value.
17
+ *
18
+ * Derive runtime context from DURABLE identity (look things up by ids on the
19
+ * principal), so a resumed run or a scheduled trigger — which has no request —
20
+ * rebuilds the same context.
21
+ */
22
+ /** Reads a secret for a principal (the Vault read-side; isolation lives in the store). */
23
+ export type SecretResolver<P> = (principal: P, key: string) => Promise<string | undefined>;
24
+ export interface ContextDefinition<P, RuntimeContext> {
25
+ /**
26
+ * Build the shared runtime context for a request from its principal.
27
+ *
28
+ * `turnContext` is the per-turn request payload (e.g. the parsed POST body or
29
+ * a caller-supplied object) — EPHEMERAL by design. Unlike the principal it is
30
+ * never persisted with a run, so a resumed/cron-fired turn rebuilds context
31
+ * without it: derive anything durable from the principal, and treat
32
+ * `turnContext` as optional per-turn enrichment only.
33
+ */
34
+ build: (args: {
35
+ principal: P;
36
+ request?: Request;
37
+ turnContext?: unknown;
38
+ }) => RuntimeContext | Promise<RuntimeContext>;
39
+ }
40
+ export declare function defineContext<P, RuntimeContext>(def: ContextDefinition<P, RuntimeContext>): ContextDefinition<P, RuntimeContext>;
41
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,0FAA0F;AAC1F,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,CAC9B,SAAS,EAAE,CAAC,EACZ,GAAG,EAAE,MAAM,KACR,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;AAEjC,MAAM,WAAW,iBAAiB,CAAC,CAAC,EAAE,cAAc;IAClD;;;;;;;;OAQG;IACH,KAAK,EAAE,CAAC,IAAI,EAAE;QACZ,SAAS,EAAE,CAAC,CAAC;QACb,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,KAAK,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAChD;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,cAAc,EAC7C,GAAG,EAAE,iBAAiB,CAAC,CAAC,EAAE,cAAc,CAAC,GACxC,iBAAiB,CAAC,CAAC,EAAE,cAAc,CAAC,CAEtC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Per-project context definition.
3
+ *
4
+ * The AI SDK only has raw per-call `runtimeContext` / `toolsContext`. `defineContext`
5
+ * is thin sugar (OURS, not an SDK API): a project declares its `RuntimeContext`
6
+ * shape once and builds it per request from the (opaque, customer-typed)
7
+ * `Principal`. The type then flows through `defineAgent`, prepareStep, hooks,
8
+ * and tools.
9
+ *
10
+ * Tools receive the built context automatically: the runtime hands every tool
11
+ * `{ ...runtimeContext, principal, run, writer, runtime }` as its
12
+ * `options.context` (the runtime itself included, so a harness tool can
13
+ * schedule or spawn without a host-global), so a tool that needs a credential
14
+ * resolves it itself, scoped by the principal
15
+ * (e.g. `vault.resolver(ctx.principal, "crm_api_key")`) — the model never sees
16
+ * the value.
17
+ *
18
+ * Derive runtime context from DURABLE identity (look things up by ids on the
19
+ * principal), so a resumed run or a scheduled trigger — which has no request —
20
+ * rebuilds the same context.
21
+ */
22
+ export function defineContext(def) {
23
+ return def;
24
+ }
25
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAyBH,MAAM,UAAU,aAAa,CAC3B,GAAyC;IAEzC,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The "what day is it" line appended to every agent's instructions.
3
+ *
4
+ * Day granularity ONLY — the string may change at most once per calendar day.
5
+ * Anthropic prompt caches live minutes, not days, so a day-stable line never
6
+ * invalidates a live cache; anything finer (clock time) would change the
7
+ * system prompt every turn and bust the cache each time. The weekday is
8
+ * spelled out because models are unreliable at deriving it from a date, and
9
+ * the timezone is stated so the model never guesses whose midnight applies.
10
+ */
11
+ export declare function currentDateLine(now?: Date): string;
12
+ //# sourceMappingURL=current-date.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"current-date.d.ts","sourceRoot":"","sources":["../src/current-date.ts"],"names":[],"mappings":"AAUA;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,GAAG,GAAE,IAAiB,GAAG,MAAM,CAI9D"}
@@ -0,0 +1,25 @@
1
+ const WEEKDAYS_UTC = [
2
+ "Sunday",
3
+ "Monday",
4
+ "Tuesday",
5
+ "Wednesday",
6
+ "Thursday",
7
+ "Friday",
8
+ "Saturday",
9
+ ];
10
+ /**
11
+ * The "what day is it" line appended to every agent's instructions.
12
+ *
13
+ * Day granularity ONLY — the string may change at most once per calendar day.
14
+ * Anthropic prompt caches live minutes, not days, so a day-stable line never
15
+ * invalidates a live cache; anything finer (clock time) would change the
16
+ * system prompt every turn and bust the cache each time. The weekday is
17
+ * spelled out because models are unreliable at deriving it from a date, and
18
+ * the timezone is stated so the model never guesses whose midnight applies.
19
+ */
20
+ export function currentDateLine(now = new Date()) {
21
+ const weekday = WEEKDAYS_UTC[now.getUTCDay()];
22
+ const iso = now.toISOString().slice(0, 10);
23
+ return `Today's date is ${weekday}, ${iso} (UTC).`;
24
+ }
25
+ //# sourceMappingURL=current-date.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"current-date.js","sourceRoot":"","sources":["../src/current-date.ts"],"names":[],"mappings":"AAAA,MAAM,YAAY,GAAG;IACnB,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,WAAW;IACX,UAAU;IACV,QAAQ;IACR,UAAU;CACF,CAAC;AAEX;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,MAAY,IAAI,IAAI,EAAE;IACpD,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;IAC9C,MAAM,GAAG,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC3C,OAAO,mBAAmB,OAAO,KAAK,GAAG,SAAS,CAAC;AACrD,CAAC"}