@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Thrown by `createChat` when the requested chat id already exists (typically a
3
+ * client-supplied id colliding with a chat the caller doesn't own — the scoped
4
+ * `getChat` missed, so the create raced into the PK). Carries a stable `code`
5
+ * so the server layer can map it to a 409 instead of a 500.
6
+ */
7
+ export class ChatConflictError extends Error {
8
+ code = "chat_conflict";
9
+ constructor(chatId) {
10
+ super(`Chat id is not available: ${chatId}`);
11
+ this.name = "ChatConflictError";
12
+ }
13
+ }
14
+ /**
15
+ * Thrown by `createRun` / `claimRun` when the chat already has a LIVE active
16
+ * run — one turn per conversation is a storage-level invariant (a partial
17
+ * unique index in the shipped adapters), so two tabs, a webhook retry racing a
18
+ * reply, or a schedule firing mid-conversation can never interleave one
19
+ * history. `retryAfterMs` is the live run's remaining lease (or the stale
20
+ * timeout for a non-durable run). Stable `code` → the server maps it to 409.
21
+ */
22
+ export class ChatBusyError extends Error {
23
+ chatId;
24
+ retryAfterMs;
25
+ code = "chat_busy";
26
+ constructor(chatId,
27
+ /** Milliseconds until the live run's lease expires — when a retry can succeed at the latest. */
28
+ retryAfterMs) {
29
+ super(`A turn is already running in this chat; try again in ${Math.ceil(retryAfterMs / 1000)}s.`);
30
+ this.chatId = chatId;
31
+ this.retryAfterMs = retryAfterMs;
32
+ this.name = "ChatBusyError";
33
+ }
34
+ }
35
+ export function isChatBusyError(e) {
36
+ return e?.code === "chat_busy";
37
+ }
38
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AA8BA;;;;;GAKG;AACH,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IACjC,IAAI,GAAG,eAAe,CAAC;IAChC,YAAY,MAAc;QACxB,KAAK,CAAC,6BAA6B,MAAM,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,aAAc,SAAQ,KAAK;IAG3B;IAEA;IAJF,IAAI,GAAG,WAAW,CAAC;IAC5B,YACW,MAAc;IACvB,gGAAgG;IACvF,YAAoB;QAE7B,KAAK,CAAC,wDAAwD,IAAI,CAAC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;QAJzF,WAAM,GAAN,MAAM,CAAQ;QAEd,iBAAY,GAAZ,YAAY,CAAQ;QAG7B,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,UAAU,eAAe,CAAC,CAAU;IACxC,OAAQ,CAA8B,EAAE,IAAI,KAAK,WAAW,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,155 @@
1
+ import type { TelemetryOptions } from "ai";
2
+ /**
3
+ * The telemetry seam — THE observation point for a turn, kept out of core so it
4
+ * carries no OTEL/vendor dep. A host supplies an implementation via
5
+ * `RuntimeConfig.telemetry`: a full tracer (`@intentface/latch-langfuse`), or
6
+ * just the hooks it wants (`onRunFinished` for a usage ledger or an
7
+ * operational notification, `onStep` for per-tool analytics). Without one,
8
+ * nothing changes.
9
+ *
10
+ * The runtime calls it around the single model-call choke-point in
11
+ * `buildTurn`: per-run settings feed the AI SDK's `telemetry` option, the run
12
+ * span wrapper lets a tracer root all spans in one trace, `onStep` fires after
13
+ * each generation step, and `onRunFinished` fires after the turn is persisted
14
+ * (scores, judges, flush). Every hook is guarded: a throwing or rejecting hook
15
+ * is swallowed, so telemetry can never fail a turn.
16
+ *
17
+ * Opting a run out: an impl that defines `telemetryFor` and returns undefined
18
+ * for a run silences ALL hooks for that run (not just the AI SDK settings).
19
+ */
20
+ /**
21
+ * What started a turn — and, in practice, whether a human is at the other end
22
+ * of it:
23
+ *
24
+ * - `"chat"` — a user message (`handleChat`). Someone is watching the stream.
25
+ * - `"decision"` — a human answered a pending request: an approval decision or
26
+ * a client-handled tool result. Someone is watching.
27
+ * - `"schedule"` — a cron/one-shot fire (`runDue`). NOBODY is watching.
28
+ * - `"programmatic"` — the host called `runAgent` directly (a background job, a
29
+ * webhook handler, a test run). There is no client stream, and only the host
30
+ * knows whether a person is waiting on the result — so `runAgent` takes a
31
+ * `trigger` to say when it does.
32
+ * - `"resume"` — the durability sweep recovered a crashed run and drove it to
33
+ * completion server-side. Nobody is watching.
34
+ *
35
+ * That distinction is what makes `onRunFinished` usable as an operational
36
+ * notification channel and not just an analytics feed: a turn that finishes
37
+ * `awaiting_input` under `"schedule"` or `"resume"` is a person who needs to
38
+ * be told, while the same status under `"chat"` or `"decision"` is just a gate
39
+ * rendering in front of someone who can answer it.
40
+ *
41
+ * A subagent turn inherits its parent's trigger (like `rootChatId`), so a
42
+ * whole delegation tree is attributable to what set it off.
43
+ */
44
+ export type TurnTrigger = "chat" | "decision" | "programmatic" | "schedule" | "resume";
45
+ /** Identity of one run/turn, known before the model call starts. */
46
+ export interface TelemetryRunMeta<P = unknown> {
47
+ /** Per-turn run id (`latch_runs.id`) — the natural trace correlation key. */
48
+ runId: string;
49
+ /** The conversation id — the natural session id. */
50
+ chatId: string;
51
+ /** Registered agent name. */
52
+ agent: string;
53
+ /** Display model id for the turn. */
54
+ modelId: string;
55
+ /** The caller the run is scoped to (tenant/user isolation flows from this). */
56
+ principal: P;
57
+ /**
58
+ * Run start time (epoch ms, `latch_runs.startedAt`). A stable per-run clock:
59
+ * out-of-band score writers (judge verdicts, user feedback, human
60
+ * corrections) pin their Langfuse score timestamp to it so repeated writes
61
+ * for the same run+rubric upsert one score instead of duplicating across days.
62
+ */
63
+ startedAt: number;
64
+ /** Delegation depth: 0 = top-level turn, >0 = a subagent turn. */
65
+ depth: number;
66
+ /** What started this turn — see {@link TurnTrigger}. */
67
+ trigger: TurnTrigger;
68
+ /**
69
+ * The run that spawned this subagent turn, when applicable. Absent at the top
70
+ * level. Lets a tracer link a helper's trace back to the agent it ran for.
71
+ */
72
+ parentRunId?: string;
73
+ /**
74
+ * The top-level conversation id — stable across a turn AND its subagent
75
+ * turns (a subagent's own `chatId` is an internal `sub_…` thread). A tracer
76
+ * can use it as the session id so a whole delegation tree groups together.
77
+ * Falls back to `chatId` when unset (top-level turns).
78
+ */
79
+ rootChatId?: string;
80
+ }
81
+ /** Outcome of one finished turn, delivered after persistence. */
82
+ export interface TelemetryRunOutcome {
83
+ status: "completed" | "awaiting_input" | "errored";
84
+ /** Present when status is "errored". */
85
+ error?: string;
86
+ /** Plain text of the incoming user message for this turn, if any. */
87
+ inputText?: string;
88
+ /**
89
+ * The turn's persisted messages (assistant message last). Typed loosely so
90
+ * implementations stay decoupled from the message shape; cast to
91
+ * `AppMessage[]` to walk parts.
92
+ */
93
+ messages: unknown[];
94
+ usage: {
95
+ inputTokens: number;
96
+ outputTokens: number;
97
+ cacheReadTokens?: number;
98
+ cacheWriteTokens?: number;
99
+ };
100
+ /** Cost for the turn (0 without a pricing config). */
101
+ cost: number;
102
+ endedAt?: number;
103
+ }
104
+ /** One completed generation step (model output + the step's tool calls/results). */
105
+ export interface TelemetryStep {
106
+ text?: string;
107
+ finishReason?: string;
108
+ usage?: {
109
+ inputTokens?: number;
110
+ outputTokens?: number;
111
+ cacheReadTokens?: number;
112
+ cacheWriteTokens?: number;
113
+ };
114
+ toolCalls?: Array<{
115
+ toolCallId: string;
116
+ toolName: string;
117
+ input?: unknown;
118
+ }>;
119
+ toolResults?: Array<{
120
+ toolCallId: string;
121
+ toolName: string;
122
+ output?: unknown;
123
+ }>;
124
+ }
125
+ export interface LatchTelemetry<P = unknown> {
126
+ /**
127
+ * Per-run AI SDK telemetry options (integrations, functionId, …), passed as
128
+ * `telemetry` on the turn's agent. Return undefined to skip this run — every
129
+ * other hook goes silent for it too.
130
+ */
131
+ telemetryFor?(run: TelemetryRunMeta<P>): TelemetryOptions | undefined;
132
+ /**
133
+ * After each generation step (model output + that step's tool calls and
134
+ * results). Fire-and-forget: not awaited, never fails the turn. For per-tool
135
+ * analytics and step-level metrics.
136
+ */
137
+ onStep?(run: TelemetryRunMeta<P>, step: TelemetryStep): void | Promise<void>;
138
+ /**
139
+ * Wrap the model call so the impl can root it in a trace context (seeded
140
+ * trace id, session/user/tags). Identity when absent.
141
+ */
142
+ withRunSpan?<T>(run: TelemetryRunMeta<P>, fn: () => Promise<T>): Promise<T>;
143
+ /**
144
+ * Post-turn hook, fired after messages/run/usage are persisted (and on
145
+ * error/abort with status "errored", carrying the error message). Awaited,
146
+ * errors swallowed — telemetry must never fail a turn. With `run.trigger`
147
+ * this doubles as the operational notification seam: `status:
148
+ * "awaiting_input"` under `"schedule"`/`"resume"` is a fire nobody can
149
+ * answer.
150
+ */
151
+ onRunFinished?(run: TelemetryRunMeta<P>, outcome: TelemetryRunOutcome): void | Promise<void>;
152
+ /** Flush exporters (serverless). Awaited after onRunFinished. */
153
+ flush?(): Promise<void>;
154
+ }
155
+ //# sourceMappingURL=telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":""}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Derive a base64 32-byte `masterKey` from a free-form passphrase via scrypt.
3
+ *
4
+ * NOTE — Node-only entry point (`@intentface/latch-core/node`): the vault
5
+ * itself is pure Web Crypto (`crypto.subtle`) so the package root stays
6
+ * portable to edge/serverless. `scryptSync` is a Node-only KDF and is used
7
+ * deliberately here: it's a convenience for the common case where the KEK
8
+ * comes from a human passphrase env var rather than a raw base64 key, and key
9
+ * derivation happens once at startup on the server (never in an edge request
10
+ * path). Consumers on non-Node runtimes should supply a raw base64 32-byte
11
+ * `masterKey` to `createVault` directly and skip this helper.
12
+ *
13
+ * `salt` is required and must be a stable per-deployment value (deployment
14
+ * config/KMS — it need not be secret): a shared default salt would make the
15
+ * same passphrase yield the same KEK on every deployment.
16
+ */
17
+ export declare function deriveMasterKey(passphrase: string, opts: {
18
+ salt: string;
19
+ }): string;
20
+ //# sourceMappingURL=vault-node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault-node.d.ts","sourceRoot":"","sources":["../src/vault-node.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GACrB,MAAM,CAaR"}
@@ -0,0 +1,30 @@
1
+ import { scryptSync } from "node:crypto";
2
+ /**
3
+ * Derive a base64 32-byte `masterKey` from a free-form passphrase via scrypt.
4
+ *
5
+ * NOTE — Node-only entry point (`@intentface/latch-core/node`): the vault
6
+ * itself is pure Web Crypto (`crypto.subtle`) so the package root stays
7
+ * portable to edge/serverless. `scryptSync` is a Node-only KDF and is used
8
+ * deliberately here: it's a convenience for the common case where the KEK
9
+ * comes from a human passphrase env var rather than a raw base64 key, and key
10
+ * derivation happens once at startup on the server (never in an edge request
11
+ * path). Consumers on non-Node runtimes should supply a raw base64 32-byte
12
+ * `masterKey` to `createVault` directly and skip this helper.
13
+ *
14
+ * `salt` is required and must be a stable per-deployment value (deployment
15
+ * config/KMS — it need not be secret): a shared default salt would make the
16
+ * same passphrase yield the same KEK on every deployment.
17
+ */
18
+ export function deriveMasterKey(passphrase, opts) {
19
+ // Fail closed: a missing/blank passphrase or salt (an unset env var) must not
20
+ // silently derive a valid-looking key — that would encrypt the vault under a
21
+ // predictable KEK.
22
+ if (!passphrase || passphrase.trim() === "") {
23
+ throw new Error("deriveMasterKey: passphrase must be a non-empty string");
24
+ }
25
+ if (!opts?.salt || opts.salt.trim() === "") {
26
+ throw new Error("deriveMasterKey: salt must be a non-empty, deployment-stable string");
27
+ }
28
+ return scryptSync(passphrase, opts.salt, 32).toString("base64");
29
+ }
30
+ //# sourceMappingURL=vault-node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault-node.js","sourceRoot":"","sources":["../src/vault-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,eAAe,CAC7B,UAAkB,EAClB,IAAsB;IAEtB,8EAA8E;IAC9E,6EAA6E;IAC7E,mBAAmB;IACnB,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,qEAAqE,CACtE,CAAC;IACJ,CAAC;IACD,OAAO,UAAU,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAClE,CAAC"}
@@ -0,0 +1,62 @@
1
+ import type { SecretResolver } from "./context.js";
2
+ /**
3
+ * The Vault — server-side, envelope-encrypted secret storage.
4
+ *
5
+ * Connection credentials (API keys, OAuth tokens) live here, encrypted at rest
6
+ * in the customer's own database and scoped per tenant. The decrypted value is
7
+ * brokered server-side only: it reaches a tool's egress (fetch/MCP) but never
8
+ * the model, logs, or telemetry. The read-side is a `SecretResolver`, so the
9
+ * existing context/tools plumbing consumes it without knowing it's encrypted.
10
+ *
11
+ * Envelope encryption: each secret gets a random data key (DEK) that encrypts
12
+ * the value (AES-256-GCM); the DEK itself is wrapped by a master key (KEK) held
13
+ * in config/env/KMS. Rotating the KEK only requires re-wrapping DEKs, not
14
+ * re-encrypting values. Crypto is Web Crypto (`crypto.subtle`) — portable to
15
+ * Node and edge/serverless, no native deps.
16
+ */
17
+ /** The opaque encrypted record persisted by a `SecretStore`. Never holds plaintext. */
18
+ export interface SecretRecord {
19
+ key: string;
20
+ /** base64 AES-GCM ciphertext of the value (auth tag appended by Web Crypto). */
21
+ ciphertext: string;
22
+ /** base64 nonce used to encrypt the value. */
23
+ iv: string;
24
+ /** base64 AES-GCM ciphertext of the raw DEK, wrapped under the KEK. */
25
+ wrappedDek: string;
26
+ /** base64 nonce used to wrap the DEK. */
27
+ dekIv: string;
28
+ createdAt: number;
29
+ updatedAt: number;
30
+ }
31
+ /**
32
+ * Persistence seam for encrypted secret records (Drizzle impl in latch-drizzle).
33
+ * Isolation lives here: each method receives the opaque `Principal` and the store
34
+ * scopes by it (the default derives an `owner` key). The framework never reads `P`.
35
+ */
36
+ export interface SecretStore<P> {
37
+ put(principal: P, record: SecretRecord): Promise<void>;
38
+ get(principal: P, key: string): Promise<SecretRecord | null>;
39
+ delete(principal: P, key: string): Promise<void>;
40
+ /** Keys only — never values — for the caller. */
41
+ list(principal: P): Promise<string[]>;
42
+ }
43
+ /** The Vault operations. `get` returns decrypted plaintext; storage stays opaque. */
44
+ export interface Vault<P> {
45
+ set(principal: P, key: string, value: string): Promise<void>;
46
+ get(principal: P, key: string): Promise<string | undefined>;
47
+ has(principal: P, key: string): Promise<boolean>;
48
+ delete(principal: P, key: string): Promise<void>;
49
+ list(principal: P): Promise<string[]>;
50
+ /** Read-side adapter for context/tools (a `SecretResolver`). */
51
+ readonly resolver: SecretResolver<P>;
52
+ }
53
+ /**
54
+ * Build a Vault over a `SecretStore`. `masterKey` is a base64-encoded 32-byte
55
+ * (256-bit) key — generate with `crypto.getRandomValues(new Uint8Array(32))`
56
+ * and keep it in env/KMS, never in the database.
57
+ */
58
+ export declare function createVault<P>(opts: {
59
+ store: SecretStore<P>;
60
+ masterKey: string;
61
+ }): Vault<P>;
62
+ //# sourceMappingURL=vault.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../src/vault.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD;;;;;;;;;;;;;;GAcG;AAEH,uFAAuF;AACvF,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,GAAG,CAAC,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,GAAG,CAAC,SAAS,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC7D,MAAM,CAAC,SAAS,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,iDAAiD;IACjD,IAAI,CAAC,SAAS,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CACvC;AAED,qFAAqF;AACrF,MAAM,WAAW,KAAK,CAAC,CAAC;IACtB,GAAG,CAAC,SAAS,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,GAAG,CAAC,SAAS,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC5D,GAAG,CAAC,SAAS,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACjD,MAAM,CAAC,SAAS,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,CAAC,SAAS,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACtC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;CACtC;AA2BD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE;IACnC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,KAAK,CAAC,CAAC,CAAC,CA+FX"}
package/dist/vault.js ADDED
@@ -0,0 +1,88 @@
1
+ // --- base64 <-> bytes (no Buffer; works in Node + browsers/edge) ------------
2
+ function toB64(bytes) {
3
+ const u8 = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
4
+ let s = "";
5
+ for (const b of u8)
6
+ s += String.fromCharCode(b);
7
+ return btoa(s);
8
+ }
9
+ function fromB64(b64) {
10
+ const s = atob(b64);
11
+ const u8 = new Uint8Array(s.length);
12
+ for (let i = 0; i < s.length; i++)
13
+ u8[i] = s.charCodeAt(i);
14
+ return u8;
15
+ }
16
+ const enc = new TextEncoder();
17
+ const dec = new TextDecoder();
18
+ // `deriveMasterKey` (passphrase → KEK via scrypt) lives in
19
+ // `@intentface/latch-core/node` — it needs `node:crypto`, and importing it here
20
+ // would make the package root unusable on edge/serverless runtimes.
21
+ // Web Crypto's lib types narrow BufferSource to ArrayBuffer-backed views; our
22
+ // Uint8Arrays are structurally fine, so coerce at the crypto call boundary.
23
+ const bs = (u) => u;
24
+ /**
25
+ * Build a Vault over a `SecretStore`. `masterKey` is a base64-encoded 32-byte
26
+ * (256-bit) key — generate with `crypto.getRandomValues(new Uint8Array(32))`
27
+ * and keep it in env/KMS, never in the database.
28
+ */
29
+ export function createVault(opts) {
30
+ const { store } = opts;
31
+ const rawKek = fromB64(opts.masterKey);
32
+ if (rawKek.length !== 32) {
33
+ throw new Error("Vault masterKey must be base64 of 32 bytes (256-bit)");
34
+ }
35
+ // Import the KEK once; reused to wrap/unwrap per-secret DEKs.
36
+ const kekPromise = crypto.subtle.importKey("raw", bs(rawKek), { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
37
+ async function encryptValue(value) {
38
+ const kek = await kekPromise;
39
+ const dek = await crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"]);
40
+ const iv = crypto.getRandomValues(new Uint8Array(12));
41
+ const ciphertext = await crypto.subtle.encrypt({ name: "AES-GCM", iv: bs(iv) }, dek, bs(enc.encode(value)));
42
+ const rawDek = await crypto.subtle.exportKey("raw", dek);
43
+ const dekIv = crypto.getRandomValues(new Uint8Array(12));
44
+ const wrappedDek = await crypto.subtle.encrypt({ name: "AES-GCM", iv: bs(dekIv) }, kek, rawDek);
45
+ return {
46
+ ciphertext: toB64(ciphertext),
47
+ iv: toB64(iv),
48
+ wrappedDek: toB64(wrappedDek),
49
+ dekIv: toB64(dekIv),
50
+ };
51
+ }
52
+ async function decryptValue(record) {
53
+ const kek = await kekPromise;
54
+ const rawDek = await crypto.subtle.decrypt({ name: "AES-GCM", iv: bs(fromB64(record.dekIv)) }, kek, bs(fromB64(record.wrappedDek)));
55
+ const dek = await crypto.subtle.importKey("raw", rawDek, { name: "AES-GCM" }, false, ["decrypt"]);
56
+ const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv: bs(fromB64(record.iv)) }, dek, bs(fromB64(record.ciphertext)));
57
+ return dec.decode(plaintext);
58
+ }
59
+ const get = async (principal, key) => {
60
+ const record = await store.get(principal, key);
61
+ if (!record)
62
+ return undefined;
63
+ return decryptValue(record);
64
+ };
65
+ return {
66
+ async set(principal, key, value) {
67
+ const now = Date.now();
68
+ await store.put(principal, {
69
+ key,
70
+ ...(await encryptValue(value)),
71
+ createdAt: now,
72
+ updatedAt: now,
73
+ });
74
+ },
75
+ get,
76
+ async has(principal, key) {
77
+ return (await store.get(principal, key)) != null;
78
+ },
79
+ async delete(principal, key) {
80
+ await store.delete(principal, key);
81
+ },
82
+ list(principal) {
83
+ return store.list(principal);
84
+ },
85
+ resolver: (principal, key) => get(principal, key),
86
+ };
87
+ }
88
+ //# sourceMappingURL=vault.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault.js","sourceRoot":"","sources":["../src/vault.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,95 @@
1
+ {
2
+ "name": "@intentface/latch-core",
3
+ "version": "0.9.1",
4
+ "description": "Latch core — runtime, types, and seams for building agents on the AI SDK. Subpaths: /models (models.dev catalog + provider hooks) and /harness (default sandbox/app tools); their extra deps are optional peers, loaded lazily.",
5
+ "keywords": [
6
+ "ai",
7
+ "ai-sdk",
8
+ "agents",
9
+ "llm",
10
+ "agent-runtime",
11
+ "durable-execution",
12
+ "tool-calling"
13
+ ],
14
+ "license": "MIT",
15
+ "type": "module",
16
+ "main": "./dist/index.js",
17
+ "module": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js"
23
+ },
24
+ "./node": {
25
+ "types": "./dist/vault-node.d.ts",
26
+ "import": "./dist/vault-node.js"
27
+ },
28
+ "./models": {
29
+ "types": "./dist/models/index.d.ts",
30
+ "import": "./dist/models/index.js"
31
+ },
32
+ "./harness": {
33
+ "types": "./dist/harness/index.d.ts",
34
+ "import": "./dist/harness/index.js"
35
+ }
36
+ },
37
+ "files": [
38
+ "dist"
39
+ ],
40
+ "sideEffects": false,
41
+ "publishConfig": {
42
+ "access": "public",
43
+ "registry": "https://registry.npmjs.org"
44
+ },
45
+ "dependencies": {
46
+ "zod": "^4.0.0"
47
+ },
48
+ "peerDependencies": {
49
+ "ai": ">=7.0.0",
50
+ "@ai-sdk/anthropic": ">=4.0.0",
51
+ "@ai-sdk/google": ">=4.0.0",
52
+ "@ai-sdk/openai": ">=4.0.0",
53
+ "turndown": ">=7.0.0",
54
+ "@intentface/latch-net": "^0.9.1"
55
+ },
56
+ "devDependencies": {
57
+ "@ai-sdk/anthropic": "^4.0.0",
58
+ "@ai-sdk/google": "^4.0.0",
59
+ "@ai-sdk/openai": "^4.0.0",
60
+ "@types/node": "^22",
61
+ "@types/turndown": "^5.0.6",
62
+ "ai": "^7.0.2",
63
+ "turndown": "^7.2.4",
64
+ "typescript": "^5.7.0",
65
+ "vitest": "^4",
66
+ "@intentface/latch-net": "^0.9.1"
67
+ },
68
+ "repository": {
69
+ "type": "git",
70
+ "url": "git+https://github.com/Intentface/intentface-latch.git",
71
+ "directory": "packages/core"
72
+ },
73
+ "peerDependenciesMeta": {
74
+ "@ai-sdk/anthropic": {
75
+ "optional": true
76
+ },
77
+ "@ai-sdk/google": {
78
+ "optional": true
79
+ },
80
+ "@ai-sdk/openai": {
81
+ "optional": true
82
+ },
83
+ "turndown": {
84
+ "optional": true
85
+ },
86
+ "@intentface/latch-net": {
87
+ "optional": true
88
+ }
89
+ },
90
+ "scripts": {
91
+ "build": "tsc -p tsconfig.json",
92
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json",
93
+ "test": "vitest run"
94
+ }
95
+ }