@tangle-network/agent-app 0.1.9 → 0.1.10

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.
@@ -14,8 +14,8 @@
14
14
  * SDK (structural contracts only, like `../tangle`). The `@tangle-network/tcloud`
15
15
  * SDK is the provisioner a product passes in; it is not a dependency here.
16
16
  */
17
- /** The key-provisioning operations this needs the `@tangle-network/tcloud`
18
- * SDK's `TCloudClient` satisfies it structurally; pass it in. */
17
+ /** The key-provisioning operations the key manager needs. Wire it from the
18
+ * platform via {@link createTcloudKeyProvisioner} rather than casting. */
19
19
  interface KeyProvisioner {
20
20
  createKey(input: {
21
21
  name: string;
@@ -33,6 +33,40 @@ interface KeyProvisioner {
33
33
  expiresAt?: string | null;
34
34
  }>;
35
35
  }
36
+ /**
37
+ * The subset of the `@tangle-network/tcloud` `TCloudClient` the provisioner uses
38
+ * — declared with METHOD syntax so the real client (whose `product` is a narrow
39
+ * union and whose budgets are `number | null`) is assignable bivariantly. The
40
+ * real SDK client satisfies this; pass it straight in.
41
+ */
42
+ interface TcloudKeyClient {
43
+ createKey(opts: {
44
+ name: string;
45
+ product?: string;
46
+ budgetUsd?: number;
47
+ expiresAt?: string;
48
+ parentKeyId?: string;
49
+ allowedModels?: string[];
50
+ rpmLimit?: number;
51
+ }): Promise<{
52
+ id: string;
53
+ key: string;
54
+ }>;
55
+ getKey(id: string): Promise<{
56
+ budgetUsd?: number | null;
57
+ budgetSpent?: number;
58
+ expiresAt?: string | null;
59
+ }>;
60
+ revokeKey(id: string): Promise<unknown>;
61
+ }
62
+ /**
63
+ * Adapt the tcloud SDK client to {@link KeyProvisioner} — the typed seam that
64
+ * replaces the `as unknown as KeyProvisioner` cast every consumer otherwise
65
+ * repeats. The platform already exposes child-key minting (parent→child key,
66
+ * per-key USD budget, expiry); this maps its shapes (`product` union,
67
+ * `number | null` budgets) onto the manager's contract (`null → undefined`).
68
+ */
69
+ declare function createTcloudKeyProvisioner(client: TcloudKeyClient): KeyProvisioner;
36
70
  /** A stored child-key record (the app's row, shape-normalized). */
37
71
  interface WorkspaceKeyRecord {
38
72
  /** App row id (opaque). */
@@ -198,4 +232,4 @@ interface PlatformBalanceManager<Plan extends string> {
198
232
  declare function createPlatformBalanceManager<Plan extends string>(opts: PlatformBalanceManagerOptions<Plan>): PlatformBalanceManager<Plan>;
199
233
  declare function createWorkspaceKeyManager(opts: WorkspaceKeyManagerOptions): WorkspaceKeyManager;
200
234
 
201
- export { type KeyCrypto, type KeyProvisioner, type PlanLimit, type PlatformBalanceInfo, type PlatformBalanceManager, type PlatformBalanceManagerOptions, type PlatformBillingClient, type PlatformIdentity, type PlatformProductUsage, type SharedBillingState, type WorkspaceKeyManager, type WorkspaceKeyManagerOptions, type WorkspaceKeyRecord, type WorkspaceKeyStore, type WorkspaceModelKeyUsage, createPlatformBalanceManager, createWorkspaceKeyManager };
235
+ export { type KeyCrypto, type KeyProvisioner, type PlanLimit, type PlatformBalanceInfo, type PlatformBalanceManager, type PlatformBalanceManagerOptions, type PlatformBillingClient, type PlatformIdentity, type PlatformProductUsage, type SharedBillingState, type TcloudKeyClient, type WorkspaceKeyManager, type WorkspaceKeyManagerOptions, type WorkspaceKeyRecord, type WorkspaceKeyStore, type WorkspaceModelKeyUsage, createPlatformBalanceManager, createTcloudKeyProvisioner, createWorkspaceKeyManager };
@@ -1,9 +1,11 @@
1
1
  import {
2
2
  createPlatformBalanceManager,
3
+ createTcloudKeyProvisioner,
3
4
  createWorkspaceKeyManager
4
- } from "../chunk-EAJSWUU5.js";
5
+ } from "../chunk-YS6A6G57.js";
5
6
  export {
6
7
  createPlatformBalanceManager,
8
+ createTcloudKeyProvisioner,
7
9
  createWorkspaceKeyManager
8
10
  };
9
11
  //# sourceMappingURL=index.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createWorkspaceKeyManager
3
- } from "./chunk-EAJSWUU5.js";
3
+ } from "./chunk-YS6A6G57.js";
4
4
  import {
5
5
  createFieldCrypto
6
6
  } from "./chunk-TA5Q4I2K.js";
@@ -315,4 +315,4 @@ export {
315
315
  createPresetWorkspaceKeyStore,
316
316
  createPresetWorkspaceKeyManager
317
317
  };
318
- //# sourceMappingURL=chunk-MTJXFHYD.js.map
318
+ //# sourceMappingURL=chunk-EYXTDVDY.js.map
@@ -1,4 +1,21 @@
1
1
  // src/billing/index.ts
2
+ function createTcloudKeyProvisioner(client) {
3
+ return {
4
+ createKey: async (input) => {
5
+ const created = await client.createKey(input);
6
+ return { id: created.id, key: created.key };
7
+ },
8
+ revokeKey: (keyId) => client.revokeKey(keyId),
9
+ getKey: async (keyId) => {
10
+ const info = await client.getKey(keyId);
11
+ return {
12
+ budgetUsd: info.budgetUsd ?? void 0,
13
+ budgetSpent: info.budgetSpent ?? void 0,
14
+ expiresAt: info.expiresAt ?? null
15
+ };
16
+ }
17
+ };
18
+ }
2
19
  function nextPeriodEnd(now) {
3
20
  return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1, 0, 0, 0, 0));
4
21
  }
@@ -111,7 +128,8 @@ function createWorkspaceKeyManager(opts) {
111
128
  }
112
129
 
113
130
  export {
131
+ createTcloudKeyProvisioner,
114
132
  createPlatformBalanceManager,
115
133
  createWorkspaceKeyManager
116
134
  };
117
- //# sourceMappingURL=chunk-EAJSWUU5.js.map
135
+ //# sourceMappingURL=chunk-YS6A6G57.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/billing/index.ts"],"sourcesContent":["/**\n * Per-workspace budget-capped model keys — app-owned billing, metered on Tangle.\n *\n * Each workspace (the paying entity) runs the agent on its OWN child API key\n * minted from the platform parent key. The child carries a hard USD budget the\n * Tangle Router enforces AT THE KEY — model spend can't exceed the allowance,\n * zero app-side accounting. The app charges its own subscription (e.g. 5× the\n * allowance) and re-provisions each period. Child budgets are IMMUTABLE on the\n * platform, so a new budget = a fresh key + revoke the prior (rotate).\n *\n * The mint / rotate / rollover / usage LOGIC is generic and lives here.\n * Persistence (which D1 table), secret encryption, and key provisioning are\n * SEAMS each product supplies — so this module imports no DB and no key-mgmt\n * SDK (structural contracts only, like `../tangle`). The `@tangle-network/tcloud`\n * SDK is the provisioner a product passes in; it is not a dependency here.\n */\n\n/** The key-provisioning operations the key manager needs. Wire it from the\n * platform via {@link createTcloudKeyProvisioner} rather than casting. */\nexport interface KeyProvisioner {\n createKey(input: { name: string; product: string; budgetUsd: number; expiresAt: string }): Promise<{ id?: string; key?: string }>\n revokeKey(keyId: string): Promise<unknown>\n getKey(keyId: string): Promise<{ budgetUsd?: number; budgetSpent?: number; expiresAt?: string | null }>\n}\n\n/**\n * The subset of the `@tangle-network/tcloud` `TCloudClient` the provisioner uses\n * — declared with METHOD syntax so the real client (whose `product` is a narrow\n * union and whose budgets are `number | null`) is assignable bivariantly. The\n * real SDK client satisfies this; pass it straight in.\n */\nexport interface TcloudKeyClient {\n createKey(opts: {\n name: string\n product?: string\n budgetUsd?: number\n expiresAt?: string\n parentKeyId?: string\n allowedModels?: string[]\n rpmLimit?: number\n }): Promise<{ id: string; key: string }>\n getKey(id: string): Promise<{ budgetUsd?: number | null; budgetSpent?: number; expiresAt?: string | null }>\n revokeKey(id: string): Promise<unknown>\n}\n\n/**\n * Adapt the tcloud SDK client to {@link KeyProvisioner} — the typed seam that\n * replaces the `as unknown as KeyProvisioner` cast every consumer otherwise\n * repeats. The platform already exposes child-key minting (parent→child key,\n * per-key USD budget, expiry); this maps its shapes (`product` union,\n * `number | null` budgets) onto the manager's contract (`null → undefined`).\n */\nexport function createTcloudKeyProvisioner(client: TcloudKeyClient): KeyProvisioner {\n return {\n createKey: async (input) => {\n const created = await client.createKey(input)\n return { id: created.id, key: created.key }\n },\n revokeKey: (keyId) => client.revokeKey(keyId),\n getKey: async (keyId) => {\n const info = await client.getKey(keyId)\n return {\n budgetUsd: info.budgetUsd ?? undefined,\n budgetSpent: info.budgetSpent ?? undefined,\n expiresAt: info.expiresAt ?? null,\n }\n },\n }\n}\n\n/** A stored child-key record (the app's row, shape-normalized). */\nexport interface WorkspaceKeyRecord {\n /** App row id (opaque). */\n id: string\n keyId: string\n /** The encrypted secret — decrypted via {@link KeyCrypto.decrypt}. */\n keyEncrypted: string\n budgetUsd: number\n expiresAt: Date | null\n}\n\n/** Persistence seam — the product implements this against its own D1 table. */\nexport interface WorkspaceKeyStore {\n /** Most-recent active key for the workspace, or null. */\n getActive(workspaceId: string): Promise<WorkspaceKeyRecord | null>\n /** All active keys (to revoke priors on rotate). */\n listActive(workspaceId: string): Promise<Array<{ id: string; keyId: string }>>\n /** Persist a freshly minted active key. */\n insert(record: { workspaceId: string; keyId: string; keyEncrypted: string; budgetUsd: number; expiresAt: Date }): Promise<void>\n /** Mark a prior row revoked. */\n markRevoked(id: string, now: Date): Promise<void>\n}\n\n/** Secret encryption seam (the app's at-rest crypto). */\nexport interface KeyCrypto {\n encrypt(secret: string): Promise<string>\n decrypt(encrypted: string): Promise<string>\n}\n\nexport interface WorkspaceKeyManagerOptions {\n provisioner: KeyProvisioner\n store: WorkspaceKeyStore\n crypto: KeyCrypto\n /** Default monthly allowance (USD) when a call doesn't specify one. */\n defaultBudgetUsd: number\n /** Injectable clock. Default `() => new Date()`. */\n now?: () => Date\n /** tcloud product the key is scoped to. Default `'router'`. */\n product?: string\n}\n\nexport interface WorkspaceModelKeyUsage {\n keyId: string\n budgetUsd: number\n budgetSpent: number\n budgetRemaining: number\n expiresAt: string | null\n exhausted: boolean\n}\n\nexport interface WorkspaceKeyManager {\n /** The workspace's active child-key secret, provisioning one if absent/expired. */\n ensureKey(workspaceId: string, opts?: { budgetUsd?: number }): Promise<string>\n /** Mint a fresh key + revoke priors (period renewal / top-up). `rollover`\n * carries the prior key's unused budget into the new one, bounded by\n * `rolloverCapUsd`. Returns the new secret. */\n rotateKey(workspaceId: string, opts?: { budgetUsd?: number; rollover?: boolean; rolloverCapUsd?: number }): Promise<string>\n /** Live budget usage for the active key (drives the \"$X of $Y used\" panel). */\n getUsage(workspaceId: string): Promise<WorkspaceModelKeyUsage | null>\n}\n\n/** Period end = first day of next month, midnight UTC. Keys expire at the period\n * boundary so a forgotten rotation fails closed rather than running free. */\nfunction nextPeriodEnd(now: Date): Date {\n return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1, 0, 0, 0, 0))\n}\n\n// ---------------------------------------------------------------------------\n// Shared-platform-balance billing\n//\n// A DIFFERENT model from the per-workspace child-key manager above: here every\n// user runs against a SHARED platform balance (id.tangle.tools), keyed by the\n// user's platform identity. The app owns no key minting — it reads the balance,\n// gates a billable turn, and deducts spend through the platform billing API.\n// Plan limits, the platform transport, and identity resolution are SEAMS the\n// product supplies; this module imports no DB and no HTTP client.\n// ---------------------------------------------------------------------------\n\n/** A user's resolved platform identity (from the app's SSO account store). */\nexport interface PlatformIdentity {\n platformUserId: string\n /** The user's per-user platform API key (reads), or null when unlinked. */\n apiKey: string | null\n}\n\n/** Spendable balance for a platform user. */\nexport interface PlatformBalanceInfo {\n balance: number\n lifetimeSpent: number\n}\n\n/** Per-product spend aggregate. */\nexport interface PlatformProductUsage {\n product: string | null\n totalSpent: number\n count: number\n}\n\n/** Plan limits — a PARAMETER per product (dollar allowance, concurrency,\n * overage policy). Never baked into the framework. */\nexport interface PlanLimit {\n monthlyBalanceUsd: number\n concurrency: number\n overageAllowed: boolean\n}\n\n/**\n * The platform billing transport — the product wires these to id.tangle.tools\n * (or any balance backend). Reads authenticate as the user (their `apiKey`);\n * the deduct write is a service-token call naming the target user. This module\n * never touches HTTP — it only sequences these calls.\n */\nexport interface PlatformBillingClient<Plan extends string> {\n /** Resolve the user's platform identity, or null when there is no SSO account. */\n resolveIdentity(userId: string): Promise<PlatformIdentity | null>\n /** Subscription plan for the user (via their platform key). */\n getPlan(apiKey: string): Promise<Plan>\n /** Spendable balance for the user (via their platform key). */\n getBalance(apiKey: string): Promise<PlatformBalanceInfo>\n /** Per-product usage rows for the user (via their platform key). */\n getUsageByProduct(apiKey: string): Promise<PlatformProductUsage[]>\n /** Deduct spend against the user's balance (service-token write). */\n deduct(input: { platformUserId: string; amountUsd: number; type: string; description: string; referenceId: string }): Promise<void>\n}\n\nexport interface SharedBillingState<Plan extends string> {\n /** Platform user id, or null when the user has no Tangle SSO account. */\n platformUserId: string | null\n plan: Plan\n monthlyBalanceUsd: number\n remainingBalanceUsd: number\n lifetimeSpentUsd: number\n concurrency: number\n overageAllowed: boolean\n}\n\nexport interface PlatformBalanceManagerOptions<Plan extends string> {\n client: PlatformBillingClient<Plan>\n /** Plan → limits map (the product's pricing). */\n planLimits: Record<Plan, PlanLimit>\n /** The plan an unlinked / outage user falls to (fails CLOSED). */\n freePlan: Plan\n /** The product slug to attribute usage to (for `getProductUsage`). */\n productSlug: string\n}\n\nexport interface PlatformBalanceManager<Plan extends string> {\n /** Resolve the user's plan + balance. Unlinked or platform-outage users fail\n * CLOSED: free plan, zero remaining balance — a billable run is never started\n * against an unknown balance. */\n getState(userId: string): Promise<SharedBillingState<Plan>>\n /** Gate a billable turn: allowed when the plan permits overage or remaining\n * balance is positive. Returns the state so the caller deducts against it. */\n canStartBillableTurn(userId: string): Promise<{ allowed: boolean; state: SharedBillingState<Plan> }>\n /** Deduct `amountUsd` against the user's platform balance. Throws when the\n * user is not platform-linked. */\n deduct(userId: string, params: { amountUsd: number; type: string; description: string; referenceId: string }): Promise<void>\n /** This product's spend for the user (drives a usage panel). */\n getProductUsage(userId: string): Promise<{ spentUsd: number; transactionCount: number }>\n}\n\nexport function createPlatformBalanceManager<Plan extends string>(\n opts: PlatformBalanceManagerOptions<Plan>,\n): PlatformBalanceManager<Plan> {\n const { client, planLimits, freePlan, productSlug } = opts\n\n const getState: PlatformBalanceManager<Plan>['getState'] = async (userId) => {\n const identity = await client.resolveIdentity(userId)\n // No SSO account, or linked without a platform key: unlinked free tier with\n // zero balance. Reads require the user's key — never call them empty.\n if (!identity || !identity.apiKey) {\n const limits = planLimits[freePlan]\n return {\n platformUserId: identity?.platformUserId ?? null,\n plan: freePlan,\n monthlyBalanceUsd: limits.monthlyBalanceUsd,\n remainingBalanceUsd: 0,\n lifetimeSpentUsd: 0,\n concurrency: limits.concurrency,\n overageAllowed: limits.overageAllowed,\n }\n }\n const [plan, balance] = await Promise.all([client.getPlan(identity.apiKey), client.getBalance(identity.apiKey)])\n const limits = planLimits[plan]\n return {\n platformUserId: identity.platformUserId,\n plan,\n monthlyBalanceUsd: limits.monthlyBalanceUsd,\n remainingBalanceUsd: balance.balance,\n lifetimeSpentUsd: balance.lifetimeSpent,\n concurrency: limits.concurrency,\n overageAllowed: limits.overageAllowed,\n }\n }\n\n const canStartBillableTurn: PlatformBalanceManager<Plan>['canStartBillableTurn'] = async (userId) => {\n const state = await getState(userId)\n if (!state.platformUserId) return { allowed: false, state }\n const allowed = state.overageAllowed || state.remainingBalanceUsd > 0\n return { allowed, state }\n }\n\n const deduct: PlatformBalanceManager<Plan>['deduct'] = async (userId, params) => {\n const identity = await client.resolveIdentity(userId)\n if (!identity) throw new Error('Shared billing requires a platform-linked user')\n await client.deduct({\n platformUserId: identity.platformUserId,\n amountUsd: params.amountUsd,\n type: params.type,\n description: params.description,\n referenceId: params.referenceId,\n })\n }\n\n const getProductUsage: PlatformBalanceManager<Plan>['getProductUsage'] = async (userId) => {\n const identity = await client.resolveIdentity(userId)\n if (!identity?.apiKey) return { spentUsd: 0, transactionCount: 0 }\n const rows = await client.getUsageByProduct(identity.apiKey)\n const product = rows.find((row) => row.product === productSlug)\n return { spentUsd: product?.totalSpent ?? 0, transactionCount: product?.count ?? 0 }\n }\n\n return { getState, canStartBillableTurn, deduct, getProductUsage }\n}\n\nexport function createWorkspaceKeyManager(opts: WorkspaceKeyManagerOptions): WorkspaceKeyManager {\n const clock = opts.now ?? (() => new Date())\n const product = opts.product ?? 'router'\n\n const getUsage: WorkspaceKeyManager['getUsage'] = async (workspaceId) => {\n const active = await opts.store.getActive(workspaceId)\n if (!active) return null\n const info = await opts.provisioner.getKey(active.keyId)\n const budgetUsd = info.budgetUsd ?? active.budgetUsd\n const budgetSpent = info.budgetSpent ?? 0\n const budgetRemaining = Math.max(0, budgetUsd - budgetSpent)\n return {\n keyId: active.keyId,\n budgetUsd,\n budgetSpent,\n budgetRemaining,\n expiresAt: info.expiresAt ?? (active.expiresAt ? active.expiresAt.toISOString() : null),\n exhausted: budgetRemaining <= 0,\n }\n }\n\n const rotateKey: WorkspaceKeyManager['rotateKey'] = async (workspaceId, ropts) => {\n const now = clock()\n const allowance = ropts?.budgetUsd ?? opts.defaultBudgetUsd\n\n let budgetUsd = allowance\n if (ropts?.rollover) {\n const prior = await getUsage(workspaceId).catch(() => null)\n budgetUsd = allowance + (prior?.budgetRemaining ?? 0)\n if (ropts.rolloverCapUsd != null) budgetUsd = Math.min(budgetUsd, ropts.rolloverCapUsd)\n }\n\n const expiresAt = nextPeriodEnd(now)\n const created = await opts.provisioner.createKey({ name: `ws:${workspaceId}`, product, budgetUsd, expiresAt: expiresAt.toISOString() })\n if (!created.key || !created.id) throw new Error('tcloud createKey returned no key')\n const keyEncrypted = await opts.crypto.encrypt(created.key)\n\n const priors = await opts.store.listActive(workspaceId)\n await opts.store.insert({ workspaceId, keyId: created.id, keyEncrypted, budgetUsd, expiresAt })\n for (const p of priors) {\n await opts.store.markRevoked(p.id, now)\n // Best-effort upstream revoke — the row is already revoked and an expired\n // key fails closed regardless, so a transient error is non-fatal.\n try {\n await opts.provisioner.revokeKey(p.keyId)\n } catch {\n /* non-fatal */\n }\n }\n return created.key\n }\n\n const ensureKey: WorkspaceKeyManager['ensureKey'] = async (workspaceId, eopts) => {\n const now = clock()\n const active = await opts.store.getActive(workspaceId)\n if (active && (!active.expiresAt || active.expiresAt.getTime() > now.getTime())) {\n return opts.crypto.decrypt(active.keyEncrypted)\n }\n return rotateKey(workspaceId, { budgetUsd: eopts?.budgetUsd })\n }\n\n return { ensureKey, rotateKey, getUsage }\n}\n"],"mappings":"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","names":["limits"]}
package/dist/index.d.ts CHANGED
@@ -9,7 +9,7 @@ export { CreateKnowledgeLoopDeps, KnowledgeCandidate, KnowledgeDecider, Knowledg
9
9
  export { DEFAULT_HARNESS, Harness, KNOWN_HARNESSES, ResolveSessionHarnessInput, ResolvedSessionHarness, coerceHarness, isHarness, resolveSessionHarness } from './harness/index.js';
10
10
  export { AgentAppConfig, AgentDelegationConfig, AgentIdentityConfig, AgentIntegrationsConfig, AgentKnowledgeConfig, AgentTaxonomyConfig, AgentUiConfig, KnowledgeLoopConfig, KnowledgeSourceSpec, agentAppConfigJsonSchema, defineAgentApp } from './config/index.js';
11
11
  export { D1Like, D1PreparedLike, DrizzleColumnLike, DrizzleSqliteCoreLike, PRESET_MIGRATION_SQL, PRESET_TABLES, PresetBillingOptions, PresetKnowledgeAccessorOptions, PresetToolHandlerOptions, VaultKv, createD1KnowledgeStateAccessor, createPresetDrizzleSchema, createPresetFieldCrypto, createPresetToolHandlers, createPresetWorkspaceKeyManager, createPresetWorkspaceKeyStore } from './preset-cloudflare/index.js';
12
- export { KeyCrypto, KeyProvisioner, PlanLimit, PlatformBalanceInfo, PlatformBalanceManager, PlatformBalanceManagerOptions, PlatformBillingClient, PlatformIdentity, PlatformProductUsage, SharedBillingState, WorkspaceKeyManager, WorkspaceKeyManagerOptions, WorkspaceKeyRecord, WorkspaceKeyStore, WorkspaceModelKeyUsage, createPlatformBalanceManager, createWorkspaceKeyManager } from './billing/index.js';
12
+ export { KeyCrypto, KeyProvisioner, PlanLimit, PlatformBalanceInfo, PlatformBalanceManager, PlatformBalanceManagerOptions, PlatformBillingClient, PlatformIdentity, PlatformProductUsage, SharedBillingState, TcloudKeyClient, WorkspaceKeyManager, WorkspaceKeyManagerOptions, WorkspaceKeyRecord, WorkspaceKeyStore, WorkspaceModelKeyUsage, createPlatformBalanceManager, createTcloudKeyProvisioner, createWorkspaceKeyManager } from './billing/index.js';
13
13
  export { DeriveKeyOptions, createFieldCrypto, decodeHexKey, decryptAesGcm, decryptBytes, decryptWithKey, deriveKey, encryptAesGcm, encryptBytes, encryptWithKey } from './crypto/index.js';
14
14
  export { JsonRecord, PersistedChatMessageForTurn, ResolvedChatTurn, StreamEvent, asRecord, asString, buildUserTextParts, encodeEvent, finalizeAssistantParts, getPartKey, mergePersistedPart, messageHasTurnId, normalizeClientTurnId, normalizePersistedPart, normalizeTime, normalizeToolEvent, resolveChatTurn, resolveToolId, resolveToolName } from './stream/index.js';
15
15
  export { HubExecClient, HubExecClientOptions, HubExecErrorCode, HubExecResult, HubInvokeDeps, HubInvokeInput, HubInvokeOutcome, ParsedIntegrationAction, invokeIntegrationHub, resolveIntegrationAction } from './integrations/index.js';
package/dist/index.js CHANGED
@@ -26,11 +26,12 @@ import {
26
26
  createPresetToolHandlers,
27
27
  createPresetWorkspaceKeyManager,
28
28
  createPresetWorkspaceKeyStore
29
- } from "./chunk-MTJXFHYD.js";
29
+ } from "./chunk-EYXTDVDY.js";
30
30
  import {
31
31
  createPlatformBalanceManager,
32
+ createTcloudKeyProvisioner,
32
33
  createWorkspaceKeyManager
33
- } from "./chunk-EAJSWUU5.js";
34
+ } from "./chunk-YS6A6G57.js";
34
35
  import {
35
36
  createFieldCrypto,
36
37
  decodeHexKey,
@@ -168,6 +169,7 @@ export {
168
169
  createPresetWorkspaceKeyManager,
169
170
  createPresetWorkspaceKeyStore,
170
171
  createReviewerDecider,
172
+ createTcloudKeyProvisioner,
171
173
  createTokenRecallChecker,
172
174
  createWorkspaceKeyManager,
173
175
  decodeHexKey,
@@ -7,8 +7,8 @@ import {
7
7
  createPresetToolHandlers,
8
8
  createPresetWorkspaceKeyManager,
9
9
  createPresetWorkspaceKeyStore
10
- } from "../chunk-MTJXFHYD.js";
11
- import "../chunk-EAJSWUU5.js";
10
+ } from "../chunk-EYXTDVDY.js";
11
+ import "../chunk-YS6A6G57.js";
12
12
  import "../chunk-TA5Q4I2K.js";
13
13
  export {
14
14
  PRESET_MIGRATION_SQL,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-app",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "packageManager": "pnpm@10.33.4",
5
5
  "description": "Application-shell framework for Tangle agent products: a bounded tool loop, the structured agent→app tool side channel, integration-hub client, per-workspace billing, and crypto — composed over the Tangle agent substrate through typed seams.",
6
6
  "keywords": [
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/billing/index.ts"],"sourcesContent":["/**\n * Per-workspace budget-capped model keys — app-owned billing, metered on Tangle.\n *\n * Each workspace (the paying entity) runs the agent on its OWN child API key\n * minted from the platform parent key. The child carries a hard USD budget the\n * Tangle Router enforces AT THE KEY — model spend can't exceed the allowance,\n * zero app-side accounting. The app charges its own subscription (e.g. 5× the\n * allowance) and re-provisions each period. Child budgets are IMMUTABLE on the\n * platform, so a new budget = a fresh key + revoke the prior (rotate).\n *\n * The mint / rotate / rollover / usage LOGIC is generic and lives here.\n * Persistence (which D1 table), secret encryption, and key provisioning are\n * SEAMS each product supplies — so this module imports no DB and no key-mgmt\n * SDK (structural contracts only, like `../tangle`). The `@tangle-network/tcloud`\n * SDK is the provisioner a product passes in; it is not a dependency here.\n */\n\n/** The key-provisioning operations this needs — the `@tangle-network/tcloud`\n * SDK's `TCloudClient` satisfies it structurally; pass it in. */\nexport interface KeyProvisioner {\n createKey(input: { name: string; product: string; budgetUsd: number; expiresAt: string }): Promise<{ id?: string; key?: string }>\n revokeKey(keyId: string): Promise<unknown>\n getKey(keyId: string): Promise<{ budgetUsd?: number; budgetSpent?: number; expiresAt?: string | null }>\n}\n\n/** A stored child-key record (the app's row, shape-normalized). */\nexport interface WorkspaceKeyRecord {\n /** App row id (opaque). */\n id: string\n keyId: string\n /** The encrypted secret — decrypted via {@link KeyCrypto.decrypt}. */\n keyEncrypted: string\n budgetUsd: number\n expiresAt: Date | null\n}\n\n/** Persistence seam — the product implements this against its own D1 table. */\nexport interface WorkspaceKeyStore {\n /** Most-recent active key for the workspace, or null. */\n getActive(workspaceId: string): Promise<WorkspaceKeyRecord | null>\n /** All active keys (to revoke priors on rotate). */\n listActive(workspaceId: string): Promise<Array<{ id: string; keyId: string }>>\n /** Persist a freshly minted active key. */\n insert(record: { workspaceId: string; keyId: string; keyEncrypted: string; budgetUsd: number; expiresAt: Date }): Promise<void>\n /** Mark a prior row revoked. */\n markRevoked(id: string, now: Date): Promise<void>\n}\n\n/** Secret encryption seam (the app's at-rest crypto). */\nexport interface KeyCrypto {\n encrypt(secret: string): Promise<string>\n decrypt(encrypted: string): Promise<string>\n}\n\nexport interface WorkspaceKeyManagerOptions {\n provisioner: KeyProvisioner\n store: WorkspaceKeyStore\n crypto: KeyCrypto\n /** Default monthly allowance (USD) when a call doesn't specify one. */\n defaultBudgetUsd: number\n /** Injectable clock. Default `() => new Date()`. */\n now?: () => Date\n /** tcloud product the key is scoped to. Default `'router'`. */\n product?: string\n}\n\nexport interface WorkspaceModelKeyUsage {\n keyId: string\n budgetUsd: number\n budgetSpent: number\n budgetRemaining: number\n expiresAt: string | null\n exhausted: boolean\n}\n\nexport interface WorkspaceKeyManager {\n /** The workspace's active child-key secret, provisioning one if absent/expired. */\n ensureKey(workspaceId: string, opts?: { budgetUsd?: number }): Promise<string>\n /** Mint a fresh key + revoke priors (period renewal / top-up). `rollover`\n * carries the prior key's unused budget into the new one, bounded by\n * `rolloverCapUsd`. Returns the new secret. */\n rotateKey(workspaceId: string, opts?: { budgetUsd?: number; rollover?: boolean; rolloverCapUsd?: number }): Promise<string>\n /** Live budget usage for the active key (drives the \"$X of $Y used\" panel). */\n getUsage(workspaceId: string): Promise<WorkspaceModelKeyUsage | null>\n}\n\n/** Period end = first day of next month, midnight UTC. Keys expire at the period\n * boundary so a forgotten rotation fails closed rather than running free. */\nfunction nextPeriodEnd(now: Date): Date {\n return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1, 0, 0, 0, 0))\n}\n\n// ---------------------------------------------------------------------------\n// Shared-platform-balance billing\n//\n// A DIFFERENT model from the per-workspace child-key manager above: here every\n// user runs against a SHARED platform balance (id.tangle.tools), keyed by the\n// user's platform identity. The app owns no key minting — it reads the balance,\n// gates a billable turn, and deducts spend through the platform billing API.\n// Plan limits, the platform transport, and identity resolution are SEAMS the\n// product supplies; this module imports no DB and no HTTP client.\n// ---------------------------------------------------------------------------\n\n/** A user's resolved platform identity (from the app's SSO account store). */\nexport interface PlatformIdentity {\n platformUserId: string\n /** The user's per-user platform API key (reads), or null when unlinked. */\n apiKey: string | null\n}\n\n/** Spendable balance for a platform user. */\nexport interface PlatformBalanceInfo {\n balance: number\n lifetimeSpent: number\n}\n\n/** Per-product spend aggregate. */\nexport interface PlatformProductUsage {\n product: string | null\n totalSpent: number\n count: number\n}\n\n/** Plan limits — a PARAMETER per product (dollar allowance, concurrency,\n * overage policy). Never baked into the framework. */\nexport interface PlanLimit {\n monthlyBalanceUsd: number\n concurrency: number\n overageAllowed: boolean\n}\n\n/**\n * The platform billing transport — the product wires these to id.tangle.tools\n * (or any balance backend). Reads authenticate as the user (their `apiKey`);\n * the deduct write is a service-token call naming the target user. This module\n * never touches HTTP — it only sequences these calls.\n */\nexport interface PlatformBillingClient<Plan extends string> {\n /** Resolve the user's platform identity, or null when there is no SSO account. */\n resolveIdentity(userId: string): Promise<PlatformIdentity | null>\n /** Subscription plan for the user (via their platform key). */\n getPlan(apiKey: string): Promise<Plan>\n /** Spendable balance for the user (via their platform key). */\n getBalance(apiKey: string): Promise<PlatformBalanceInfo>\n /** Per-product usage rows for the user (via their platform key). */\n getUsageByProduct(apiKey: string): Promise<PlatformProductUsage[]>\n /** Deduct spend against the user's balance (service-token write). */\n deduct(input: { platformUserId: string; amountUsd: number; type: string; description: string; referenceId: string }): Promise<void>\n}\n\nexport interface SharedBillingState<Plan extends string> {\n /** Platform user id, or null when the user has no Tangle SSO account. */\n platformUserId: string | null\n plan: Plan\n monthlyBalanceUsd: number\n remainingBalanceUsd: number\n lifetimeSpentUsd: number\n concurrency: number\n overageAllowed: boolean\n}\n\nexport interface PlatformBalanceManagerOptions<Plan extends string> {\n client: PlatformBillingClient<Plan>\n /** Plan → limits map (the product's pricing). */\n planLimits: Record<Plan, PlanLimit>\n /** The plan an unlinked / outage user falls to (fails CLOSED). */\n freePlan: Plan\n /** The product slug to attribute usage to (for `getProductUsage`). */\n productSlug: string\n}\n\nexport interface PlatformBalanceManager<Plan extends string> {\n /** Resolve the user's plan + balance. Unlinked or platform-outage users fail\n * CLOSED: free plan, zero remaining balance — a billable run is never started\n * against an unknown balance. */\n getState(userId: string): Promise<SharedBillingState<Plan>>\n /** Gate a billable turn: allowed when the plan permits overage or remaining\n * balance is positive. Returns the state so the caller deducts against it. */\n canStartBillableTurn(userId: string): Promise<{ allowed: boolean; state: SharedBillingState<Plan> }>\n /** Deduct `amountUsd` against the user's platform balance. Throws when the\n * user is not platform-linked. */\n deduct(userId: string, params: { amountUsd: number; type: string; description: string; referenceId: string }): Promise<void>\n /** This product's spend for the user (drives a usage panel). */\n getProductUsage(userId: string): Promise<{ spentUsd: number; transactionCount: number }>\n}\n\nexport function createPlatformBalanceManager<Plan extends string>(\n opts: PlatformBalanceManagerOptions<Plan>,\n): PlatformBalanceManager<Plan> {\n const { client, planLimits, freePlan, productSlug } = opts\n\n const getState: PlatformBalanceManager<Plan>['getState'] = async (userId) => {\n const identity = await client.resolveIdentity(userId)\n // No SSO account, or linked without a platform key: unlinked free tier with\n // zero balance. Reads require the user's key — never call them empty.\n if (!identity || !identity.apiKey) {\n const limits = planLimits[freePlan]\n return {\n platformUserId: identity?.platformUserId ?? null,\n plan: freePlan,\n monthlyBalanceUsd: limits.monthlyBalanceUsd,\n remainingBalanceUsd: 0,\n lifetimeSpentUsd: 0,\n concurrency: limits.concurrency,\n overageAllowed: limits.overageAllowed,\n }\n }\n const [plan, balance] = await Promise.all([client.getPlan(identity.apiKey), client.getBalance(identity.apiKey)])\n const limits = planLimits[plan]\n return {\n platformUserId: identity.platformUserId,\n plan,\n monthlyBalanceUsd: limits.monthlyBalanceUsd,\n remainingBalanceUsd: balance.balance,\n lifetimeSpentUsd: balance.lifetimeSpent,\n concurrency: limits.concurrency,\n overageAllowed: limits.overageAllowed,\n }\n }\n\n const canStartBillableTurn: PlatformBalanceManager<Plan>['canStartBillableTurn'] = async (userId) => {\n const state = await getState(userId)\n if (!state.platformUserId) return { allowed: false, state }\n const allowed = state.overageAllowed || state.remainingBalanceUsd > 0\n return { allowed, state }\n }\n\n const deduct: PlatformBalanceManager<Plan>['deduct'] = async (userId, params) => {\n const identity = await client.resolveIdentity(userId)\n if (!identity) throw new Error('Shared billing requires a platform-linked user')\n await client.deduct({\n platformUserId: identity.platformUserId,\n amountUsd: params.amountUsd,\n type: params.type,\n description: params.description,\n referenceId: params.referenceId,\n })\n }\n\n const getProductUsage: PlatformBalanceManager<Plan>['getProductUsage'] = async (userId) => {\n const identity = await client.resolveIdentity(userId)\n if (!identity?.apiKey) return { spentUsd: 0, transactionCount: 0 }\n const rows = await client.getUsageByProduct(identity.apiKey)\n const product = rows.find((row) => row.product === productSlug)\n return { spentUsd: product?.totalSpent ?? 0, transactionCount: product?.count ?? 0 }\n }\n\n return { getState, canStartBillableTurn, deduct, getProductUsage }\n}\n\nexport function createWorkspaceKeyManager(opts: WorkspaceKeyManagerOptions): WorkspaceKeyManager {\n const clock = opts.now ?? (() => new Date())\n const product = opts.product ?? 'router'\n\n const getUsage: WorkspaceKeyManager['getUsage'] = async (workspaceId) => {\n const active = await opts.store.getActive(workspaceId)\n if (!active) return null\n const info = await opts.provisioner.getKey(active.keyId)\n const budgetUsd = info.budgetUsd ?? active.budgetUsd\n const budgetSpent = info.budgetSpent ?? 0\n const budgetRemaining = Math.max(0, budgetUsd - budgetSpent)\n return {\n keyId: active.keyId,\n budgetUsd,\n budgetSpent,\n budgetRemaining,\n expiresAt: info.expiresAt ?? (active.expiresAt ? active.expiresAt.toISOString() : null),\n exhausted: budgetRemaining <= 0,\n }\n }\n\n const rotateKey: WorkspaceKeyManager['rotateKey'] = async (workspaceId, ropts) => {\n const now = clock()\n const allowance = ropts?.budgetUsd ?? opts.defaultBudgetUsd\n\n let budgetUsd = allowance\n if (ropts?.rollover) {\n const prior = await getUsage(workspaceId).catch(() => null)\n budgetUsd = allowance + (prior?.budgetRemaining ?? 0)\n if (ropts.rolloverCapUsd != null) budgetUsd = Math.min(budgetUsd, ropts.rolloverCapUsd)\n }\n\n const expiresAt = nextPeriodEnd(now)\n const created = await opts.provisioner.createKey({ name: `ws:${workspaceId}`, product, budgetUsd, expiresAt: expiresAt.toISOString() })\n if (!created.key || !created.id) throw new Error('tcloud createKey returned no key')\n const keyEncrypted = await opts.crypto.encrypt(created.key)\n\n const priors = await opts.store.listActive(workspaceId)\n await opts.store.insert({ workspaceId, keyId: created.id, keyEncrypted, budgetUsd, expiresAt })\n for (const p of priors) {\n await opts.store.markRevoked(p.id, now)\n // Best-effort upstream revoke — the row is already revoked and an expired\n // key fails closed regardless, so a transient error is non-fatal.\n try {\n await opts.provisioner.revokeKey(p.keyId)\n } catch {\n /* non-fatal */\n }\n }\n return created.key\n }\n\n const ensureKey: WorkspaceKeyManager['ensureKey'] = async (workspaceId, eopts) => {\n const now = clock()\n const active = await opts.store.getActive(workspaceId)\n if (active && (!active.expiresAt || active.expiresAt.getTime() > now.getTime())) {\n return opts.crypto.decrypt(active.keyEncrypted)\n }\n return rotateKey(workspaceId, { budgetUsd: eopts?.budgetUsd })\n }\n\n return { ensureKey, rotateKey, getUsage }\n}\n"],"mappings":";AAwFA,SAAS,cAAc,KAAiB;AACtC,SAAO,IAAI,KAAK,KAAK,IAAI,IAAI,eAAe,GAAG,IAAI,YAAY,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;AACtF;AAgGO,SAAS,6BACd,MAC8B;AAC9B,QAAM,EAAE,QAAQ,YAAY,UAAU,YAAY,IAAI;AAEtD,QAAM,WAAqD,OAAO,WAAW;AAC3E,UAAM,WAAW,MAAM,OAAO,gBAAgB,MAAM;AAGpD,QAAI,CAAC,YAAY,CAAC,SAAS,QAAQ;AACjC,YAAMA,UAAS,WAAW,QAAQ;AAClC,aAAO;AAAA,QACL,gBAAgB,UAAU,kBAAkB;AAAA,QAC5C,MAAM;AAAA,QACN,mBAAmBA,QAAO;AAAA,QAC1B,qBAAqB;AAAA,QACrB,kBAAkB;AAAA,QAClB,aAAaA,QAAO;AAAA,QACpB,gBAAgBA,QAAO;AAAA,MACzB;AAAA,IACF;AACA,UAAM,CAAC,MAAM,OAAO,IAAI,MAAM,QAAQ,IAAI,CAAC,OAAO,QAAQ,SAAS,MAAM,GAAG,OAAO,WAAW,SAAS,MAAM,CAAC,CAAC;AAC/G,UAAM,SAAS,WAAW,IAAI;AAC9B,WAAO;AAAA,MACL,gBAAgB,SAAS;AAAA,MACzB;AAAA,MACA,mBAAmB,OAAO;AAAA,MAC1B,qBAAqB,QAAQ;AAAA,MAC7B,kBAAkB,QAAQ;AAAA,MAC1B,aAAa,OAAO;AAAA,MACpB,gBAAgB,OAAO;AAAA,IACzB;AAAA,EACF;AAEA,QAAM,uBAA6E,OAAO,WAAW;AACnG,UAAM,QAAQ,MAAM,SAAS,MAAM;AACnC,QAAI,CAAC,MAAM,eAAgB,QAAO,EAAE,SAAS,OAAO,MAAM;AAC1D,UAAM,UAAU,MAAM,kBAAkB,MAAM,sBAAsB;AACpE,WAAO,EAAE,SAAS,MAAM;AAAA,EAC1B;AAEA,QAAM,SAAiD,OAAO,QAAQ,WAAW;AAC/E,UAAM,WAAW,MAAM,OAAO,gBAAgB,MAAM;AACpD,QAAI,CAAC,SAAU,OAAM,IAAI,MAAM,gDAAgD;AAC/E,UAAM,OAAO,OAAO;AAAA,MAClB,gBAAgB,SAAS;AAAA,MACzB,WAAW,OAAO;AAAA,MAClB,MAAM,OAAO;AAAA,MACb,aAAa,OAAO;AAAA,MACpB,aAAa,OAAO;AAAA,IACtB,CAAC;AAAA,EACH;AAEA,QAAM,kBAAmE,OAAO,WAAW;AACzF,UAAM,WAAW,MAAM,OAAO,gBAAgB,MAAM;AACpD,QAAI,CAAC,UAAU,OAAQ,QAAO,EAAE,UAAU,GAAG,kBAAkB,EAAE;AACjE,UAAM,OAAO,MAAM,OAAO,kBAAkB,SAAS,MAAM;AAC3D,UAAM,UAAU,KAAK,KAAK,CAAC,QAAQ,IAAI,YAAY,WAAW;AAC9D,WAAO,EAAE,UAAU,SAAS,cAAc,GAAG,kBAAkB,SAAS,SAAS,EAAE;AAAA,EACrF;AAEA,SAAO,EAAE,UAAU,sBAAsB,QAAQ,gBAAgB;AACnE;AAEO,SAAS,0BAA0B,MAAuD;AAC/F,QAAM,QAAQ,KAAK,QAAQ,MAAM,oBAAI,KAAK;AAC1C,QAAM,UAAU,KAAK,WAAW;AAEhC,QAAM,WAA4C,OAAO,gBAAgB;AACvE,UAAM,SAAS,MAAM,KAAK,MAAM,UAAU,WAAW;AACrD,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,OAAO,MAAM,KAAK,YAAY,OAAO,OAAO,KAAK;AACvD,UAAM,YAAY,KAAK,aAAa,OAAO;AAC3C,UAAM,cAAc,KAAK,eAAe;AACxC,UAAM,kBAAkB,KAAK,IAAI,GAAG,YAAY,WAAW;AAC3D,WAAO;AAAA,MACL,OAAO,OAAO;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,KAAK,cAAc,OAAO,YAAY,OAAO,UAAU,YAAY,IAAI;AAAA,MAClF,WAAW,mBAAmB;AAAA,IAChC;AAAA,EACF;AAEA,QAAM,YAA8C,OAAO,aAAa,UAAU;AAChF,UAAM,MAAM,MAAM;AAClB,UAAM,YAAY,OAAO,aAAa,KAAK;AAE3C,QAAI,YAAY;AAChB,QAAI,OAAO,UAAU;AACnB,YAAM,QAAQ,MAAM,SAAS,WAAW,EAAE,MAAM,MAAM,IAAI;AAC1D,kBAAY,aAAa,OAAO,mBAAmB;AACnD,UAAI,MAAM,kBAAkB,KAAM,aAAY,KAAK,IAAI,WAAW,MAAM,cAAc;AAAA,IACxF;AAEA,UAAM,YAAY,cAAc,GAAG;AACnC,UAAM,UAAU,MAAM,KAAK,YAAY,UAAU,EAAE,MAAM,MAAM,WAAW,IAAI,SAAS,WAAW,WAAW,UAAU,YAAY,EAAE,CAAC;AACtI,QAAI,CAAC,QAAQ,OAAO,CAAC,QAAQ,GAAI,OAAM,IAAI,MAAM,kCAAkC;AACnF,UAAM,eAAe,MAAM,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAE1D,UAAM,SAAS,MAAM,KAAK,MAAM,WAAW,WAAW;AACtD,UAAM,KAAK,MAAM,OAAO,EAAE,aAAa,OAAO,QAAQ,IAAI,cAAc,WAAW,UAAU,CAAC;AAC9F,eAAW,KAAK,QAAQ;AACtB,YAAM,KAAK,MAAM,YAAY,EAAE,IAAI,GAAG;AAGtC,UAAI;AACF,cAAM,KAAK,YAAY,UAAU,EAAE,KAAK;AAAA,MAC1C,QAAQ;AAAA,MAER;AAAA,IACF;AACA,WAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,YAA8C,OAAO,aAAa,UAAU;AAChF,UAAM,MAAM,MAAM;AAClB,UAAM,SAAS,MAAM,KAAK,MAAM,UAAU,WAAW;AACrD,QAAI,WAAW,CAAC,OAAO,aAAa,OAAO,UAAU,QAAQ,IAAI,IAAI,QAAQ,IAAI;AAC/E,aAAO,KAAK,OAAO,QAAQ,OAAO,YAAY;AAAA,IAChD;AACA,WAAO,UAAU,aAAa,EAAE,WAAW,OAAO,UAAU,CAAC;AAAA,EAC/D;AAEA,SAAO,EAAE,WAAW,WAAW,SAAS;AAC1C;","names":["limits"]}