@tangle-network/agent-app 0.46.53 → 0.46.55
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.
- package/README.md +4 -4
- package/dist/billing/crypto-probe.d.ts +3 -0
- package/dist/billing/identity-bound.d.ts +183 -0
- package/dist/billing/index.d.ts +1 -0
- package/dist/billing/index.js +3 -1
- package/dist/chat-routes/index.js +1 -1
- package/dist/{chunk-T374KO42.js → chunk-LKZBMDYM.js} +4 -3
- package/dist/chunk-LKZBMDYM.js.map +1 -0
- package/dist/chunk-TS2ZIB53.js +701 -0
- package/dist/chunk-TS2ZIB53.js.map +1 -0
- package/dist/preset-cloudflare/index.js +1 -1
- package/dist/sandbox/index.d.ts +2 -2
- package/dist/sandbox/index.js +1 -1
- package/dist/turn-stream/adapters.d.ts +10 -10
- package/dist/turn-stream/core.d.ts +15 -12
- package/dist/turn-stream/do.d.ts +2 -2
- package/dist/turn-stream/index.d.ts +5 -4
- package/dist/turn-stream/index.js.map +1 -1
- package/package.json +5 -5
- package/dist/chunk-GXE6KHTS.js +0 -168
- package/dist/chunk-GXE6KHTS.js.map +0 -1
- package/dist/chunk-T374KO42.js.map +0 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ The substrate packages — `@tangle-network/agent-runtime`, `agent-eval`, `agent
|
|
|
16
16
|
- **Bounded tool loop** — `runAppToolLoop` / `streamAppToolLoop`: stream a turn → collect tool calls → dispatch → fold results back → re-run, capped. These are 1:1 aliases of `runToolLoop` / `streamToolLoop` from `@tangle-network/agent-runtime/tool-loop` (the engine owns the loop; this package adds no logic on that path). Substrate-free behind a `streamTurn` seam, so it drives a sandboxed agent, a Worker, or an in-browser copilot unchanged.
|
|
17
17
|
- **Assembled chat vertical** — `createChatTurnRoutes` wires auth → thread/message store → streaming turn with buffered replay → uploads → sidecar question answering into one route factory, over `authorize` / `produce` / `store` / `interactions` seams. No hand-rolled orchestration. See [`examples/chat-app.md`](./examples/chat-app.md).
|
|
18
18
|
- **Sandbox-optional** — the same tools, billing, eval, and loop work without a container. A `fetch`-only adapter maps any OpenAI-compatible stream (Tangle Router, tcloud) into the loop. See [`examples/browser-copilot.md`](./examples/browser-copilot.md).
|
|
19
|
-
- **Resumable turns** — buffer a turn so a dropped tab loses nothing and a reconnecting client replays the tail. Two niches: a browser/edge copilot streaming the Router directly (no sandbox session to attach to), and any
|
|
19
|
+
- **Resumable turns** — buffer a turn so a dropped tab loses nothing and a reconnecting client replays the tail. Two niches: a browser/edge copilot streaming the Router directly (no sandbox session to attach to), and any detached sandbox run driven through `dispatchPrompt({ detach: true })` / `streamPrompt` that a browser must tail. Sandbox 0.37 `driveTurn` uses the message lane and can be tailed through the session gateway. **An INTERACTIVE sandbox turn doesn't need it** — drive it on the message lane (`box.session(id).sendMessage()`) and attach the tab with `box.mintScopedToken()` + `SessionGatewayClient`, or let the worker resume with `box.streamPrompt('', { executionId, lastEventId })`. See [`examples/resumable-turns.md`](./examples/resumable-turns.md).
|
|
20
20
|
- **Composes the engine, never forks it** — `/eval` re-exports `@tangle-network/agent-eval`'s verifier; `/integrations` wraps the hub; `/tangle` and `/billing` take the tcloud client as a structural contract. Engines are **peer dependencies** — you pin the version, nothing is bundled.
|
|
21
21
|
- **ESM, typed, zero runtime deps** in the substrate-free modules (`/runtime`, `/web`, `/crypto`, `/redact`, `/stream`). Ships with `.d.ts` and npm [provenance](https://www.npmjs.com/package/@tangle-network/agent-app#provenance).
|
|
22
22
|
|
|
@@ -36,10 +36,10 @@ pnpm add @tangle-network/agent-eval @tangle-network/agent-integrations
|
|
|
36
36
|
| Peer | Required by | Range |
|
|
37
37
|
|---|---|---|
|
|
38
38
|
| `@tangle-network/agent-eval` | `/eval`, `/eval-campaign`, `/profile`, `/knowledge` | `>=0.172.1 <0.174.0` |
|
|
39
|
-
| `@tangle-network/agent-runtime` | `/runtime`, `/chat-routes` | `>=0.
|
|
39
|
+
| `@tangle-network/agent-runtime` | `/runtime`, `/chat-routes` | `>=0.191.0 <0.193.0` |
|
|
40
40
|
| `@tangle-network/agent-integrations` | `/integrations` | `>=0.53.55 <0.54.0` |
|
|
41
41
|
| `@tangle-network/agent-interface` | `/interactions`, `/chat-store`, `/harness` | `^2.2.0` |
|
|
42
|
-
| `@tangle-network/sandbox` | `/sandbox` | `>=0.36.
|
|
42
|
+
| `@tangle-network/sandbox` | `/sandbox` | `>=0.36.4 <0.38.0` |
|
|
43
43
|
| `@tangle-network/agent-knowledge` | `/knowledge-loop` | `^13.0.0` |
|
|
44
44
|
| `@tangle-network/agent-profile-materialize` | `/skills-placement` | `>=0.18.1 <0.20.0` |
|
|
45
45
|
| `@tangle-network/sandbox-ui` | `/brand`, `/work-product-react`, `/workspace-react` | `>=0.111.2 <0.114.0` |
|
|
@@ -130,7 +130,7 @@ Each primitive is written `package → symbol`; three packages ship similarly-na
|
|
|
130
130
|
| Your turn | Use | Why |
|
|
131
131
|
|---|---|---|
|
|
132
132
|
| **Interactive** — a user is watching a chat or copilot | sandbox → `box.streamPrompt()` held open for the turn, wrapped here as `streamSandboxPrompt` (`/sandbox`); for the browser leg, sandbox → `box.mintScopedToken()` + `SessionGatewayClient` (`@tangle-network/sandbox/session-gateway`) attaches the tab directly | Worker lifetime ≈ turn length; a dropped tab replays the buffered tail on reconnect. |
|
|
133
|
-
| **Autonomous** — a mission step, queue job, cron, or inbound email, with nobody watching | sandbox → `box.driveTurn()`, wrapped here as `driveSandboxTurn` (`/sandbox`), ticked from a durable driver; drop to raw `box.dispatchPrompt({ detach: true })` + `box.findCompletedTurn(turnId, { sessionId })` only when one pass is too coarse. `
|
|
133
|
+
| **Autonomous** — a mission step, queue job, cron, or inbound email, with nobody watching | sandbox → `box.driveTurn()`, wrapped here as `driveSandboxTurn` (`/sandbox`), ticked from a durable driver; drop to raw `box.dispatchPrompt({ detach: true })` + `box.findCompletedTurn(turnId, { sessionId })` only when one pass is too coarse. Sandbox 0.37 `driveTurn` uses the message lane and a browser can attach through the session gateway; `runDetachedTurn` (`/chat-routes`) remains the buffer bridge for stream/dispatch detached runs | Workers die in minutes, so the platform runs the turn server-side and a crash re-dispatch is a lookup, not a second run. |
|
|
134
134
|
| **Eval / CI** — a long-lived harness process | sandbox → `box.streamPrompt()` for a sandboxed harness; agent-runtime `/tool-loop` → `runToolLoop` / `streamToolLoop` for an in-process model turn — `runAppToolLoop` / `streamAppToolLoop` (`/runtime`) are 1:1 aliases of those, not a second implementation | The process outlives the run; durability adds nothing — a failed run is re-run, not resumed. |
|
|
135
135
|
|
|
136
136
|
**2. Assembled or à la carte?** `createChatTurnRoutes` (`/chat-routes`) wires the whole server chat turn — auth, store, streaming, replay, uploads, interactions — over typed seams. Reach for the individual modules (`/stream`, `/chat-store`, `/interactions`) only to compose something the assembled route doesn't cover.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import type { KeyCrypto, KeyProvisioner } from './index';
|
|
2
|
+
/** Lifecycle state for a durable delegated key row. */
|
|
3
|
+
export type WorkspaceKeyStatus = 'legacy' | 'provisioning' | 'active' | 'revocation_pending' | 'revoked' | 'orphaned';
|
|
4
|
+
/** Product partition used by the key store and remote provisioner. */
|
|
5
|
+
export type WorkspaceKeyProduct = string;
|
|
6
|
+
/** The identity fields that bind a child key to its paying owner and source. */
|
|
7
|
+
export interface WorkspaceKeyIdentity {
|
|
8
|
+
workspaceId: string;
|
|
9
|
+
ownerUserId: string;
|
|
10
|
+
platformUserId: string;
|
|
11
|
+
sourceKeyId?: string | null;
|
|
12
|
+
/** A non-secret digest of the source credential. */
|
|
13
|
+
sourceKeyFingerprint: string;
|
|
14
|
+
}
|
|
15
|
+
/** A product/workspace/owner lookup scope. */
|
|
16
|
+
export interface DurableWorkspaceKeyScope {
|
|
17
|
+
workspaceId: string;
|
|
18
|
+
ownerUserId: string;
|
|
19
|
+
product: WorkspaceKeyProduct;
|
|
20
|
+
}
|
|
21
|
+
/** A persisted child-key row owned by this manager. */
|
|
22
|
+
export interface DurableWorkspaceKeyRecord {
|
|
23
|
+
id: string;
|
|
24
|
+
workspaceId: string;
|
|
25
|
+
ownerUserId: string;
|
|
26
|
+
product: WorkspaceKeyProduct;
|
|
27
|
+
platformUserId: string;
|
|
28
|
+
sourceKeyId: string | null;
|
|
29
|
+
sourceKeyFingerprint: string;
|
|
30
|
+
/** The persisted name used to recover a remote create after a crash. Null only for pre-name rows. */
|
|
31
|
+
name: string | null;
|
|
32
|
+
keyId: string;
|
|
33
|
+
keyEncrypted: string;
|
|
34
|
+
budgetUsd: number;
|
|
35
|
+
expiresAt: Date;
|
|
36
|
+
status: WorkspaceKeyStatus;
|
|
37
|
+
revocationAttempts: number;
|
|
38
|
+
nextRevocationAt: Date | null;
|
|
39
|
+
lastRevocationError: string | null;
|
|
40
|
+
createdAt: Date;
|
|
41
|
+
}
|
|
42
|
+
/** A row that has been written before its remote child key exists. */
|
|
43
|
+
export interface DurableWorkspaceKeyProvisioningRecord extends DurableWorkspaceKeyRecord {
|
|
44
|
+
status: 'provisioning';
|
|
45
|
+
}
|
|
46
|
+
type DurableWorkspaceKeyCreateResult = Awaited<ReturnType<KeyProvisioner['createKey']>> & {
|
|
47
|
+
budgetUsd?: number | null;
|
|
48
|
+
budgetRemaining?: number;
|
|
49
|
+
expiresAt?: string | null;
|
|
50
|
+
};
|
|
51
|
+
/** The remote operations required by the durable manager. */
|
|
52
|
+
export interface DurableWorkspaceKeyProvisioner {
|
|
53
|
+
createKey(input: Parameters<KeyProvisioner['createKey']>[0]): Promise<DurableWorkspaceKeyCreateResult>;
|
|
54
|
+
getKey(id: string): Promise<{
|
|
55
|
+
budgetUsd?: number | null;
|
|
56
|
+
budgetSpent?: number;
|
|
57
|
+
expiresAt?: string | null;
|
|
58
|
+
}>;
|
|
59
|
+
revokeKey: KeyProvisioner['revokeKey'];
|
|
60
|
+
/** Find remote keys for a specific persisted provisioning attempt. */
|
|
61
|
+
findCreatedKeys(input: {
|
|
62
|
+
name: string;
|
|
63
|
+
product: string;
|
|
64
|
+
sourceKeyId?: string | null;
|
|
65
|
+
}): Promise<Array<{
|
|
66
|
+
id: string;
|
|
67
|
+
}>>;
|
|
68
|
+
}
|
|
69
|
+
/** Persistence operations for identity-bound key lifecycle state. */
|
|
70
|
+
export interface DurableWorkspaceKeyStore {
|
|
71
|
+
/** Return the active row for this exact product scope. */
|
|
72
|
+
getActive(scope: DurableWorkspaceKeyScope): Promise<DurableWorkspaceKeyRecord | null>;
|
|
73
|
+
/** Return every unfinished provisioning row for this exact product scope. */
|
|
74
|
+
listProvisioning(scope: DurableWorkspaceKeyScope): Promise<DurableWorkspaceKeyProvisioningRecord[]>;
|
|
75
|
+
/** Insert before the remote create so a crashed create can be recovered. */
|
|
76
|
+
insertProvisioning(record: DurableWorkspaceKeyProvisioningRecord): Promise<void>;
|
|
77
|
+
/** Save the remote id while the row remains in provisioning state. */
|
|
78
|
+
markProvisioningRemote(input: {
|
|
79
|
+
id: string;
|
|
80
|
+
keyId: string;
|
|
81
|
+
}): Promise<void>;
|
|
82
|
+
/** Promote a fully encrypted row to active state. */
|
|
83
|
+
markActive(input: {
|
|
84
|
+
id: string;
|
|
85
|
+
keyId: string;
|
|
86
|
+
keyEncrypted: string;
|
|
87
|
+
expiresAt: Date;
|
|
88
|
+
budgetUsd: number;
|
|
89
|
+
}): Promise<void>;
|
|
90
|
+
/** Keep a failed cleanup visible and schedule a later retry. */
|
|
91
|
+
markRevocationPending(input: {
|
|
92
|
+
id: string;
|
|
93
|
+
error?: string | null;
|
|
94
|
+
nextAttemptAt: Date;
|
|
95
|
+
incrementAttempts: boolean;
|
|
96
|
+
}): Promise<void>;
|
|
97
|
+
/** Mark a remote key as revoked. Missing remote keys count as revoked. */
|
|
98
|
+
markRevoked(id: string, now: Date): Promise<void>;
|
|
99
|
+
/** Mark a provisioning row terminal when no remote key remains to clean. */
|
|
100
|
+
markOrphaned(id: string, error: string): Promise<void>;
|
|
101
|
+
/** Query due cleanup rows. The implementation should filter by the supplied scope. */
|
|
102
|
+
listPendingRevocations(input: {
|
|
103
|
+
product: WorkspaceKeyProduct;
|
|
104
|
+
workspaceId?: string;
|
|
105
|
+
ownerUserId?: string;
|
|
106
|
+
now: Date;
|
|
107
|
+
limit: number;
|
|
108
|
+
/** Include rows scheduled for a later retry when checking before issuance. */
|
|
109
|
+
includeFuture?: boolean;
|
|
110
|
+
}): Promise<DurableWorkspaceKeyRecord[]>;
|
|
111
|
+
/** Acquire a cross-process lease for one product/workspace/owner scope. */
|
|
112
|
+
acquireLease(scope: string, operationId: string, now: Date, leaseMs: number): Promise<boolean>;
|
|
113
|
+
/** Renew an acquired lease before its expiry. */
|
|
114
|
+
renewLease(scope: string, operationId: string, now: Date, leaseMs: number): Promise<boolean>;
|
|
115
|
+
/** Release an acquired lease. */
|
|
116
|
+
releaseLease(scope: string, operationId: string): Promise<void>;
|
|
117
|
+
}
|
|
118
|
+
/** Live budget information for a child key. */
|
|
119
|
+
export interface WorkspaceKeyUsage {
|
|
120
|
+
keyId: string;
|
|
121
|
+
budgetUsd: number;
|
|
122
|
+
budgetSpent: number;
|
|
123
|
+
budgetRemaining: number;
|
|
124
|
+
expiresAt: string;
|
|
125
|
+
exhausted: boolean;
|
|
126
|
+
}
|
|
127
|
+
/** The decrypted key plus the usage snapshot used to issue it. */
|
|
128
|
+
export interface WorkspaceRuntimeKey {
|
|
129
|
+
key: string;
|
|
130
|
+
usage: WorkspaceKeyUsage;
|
|
131
|
+
/** True when this call minted a new remote key. */
|
|
132
|
+
refreshed: boolean;
|
|
133
|
+
}
|
|
134
|
+
/** Identity-bound durable key manager API. */
|
|
135
|
+
export interface DurableWorkspaceKeyManager {
|
|
136
|
+
/** Reuse a matching active key or reconcile and mint one under a lease. */
|
|
137
|
+
ensureKey(identity: WorkspaceKeyIdentity, options?: {
|
|
138
|
+
budgetUsd?: number;
|
|
139
|
+
}): Promise<WorkspaceRuntimeKey>;
|
|
140
|
+
/** Read usage without returning the child secret. */
|
|
141
|
+
getUsage(identity: WorkspaceKeyIdentity): Promise<WorkspaceKeyUsage | null>;
|
|
142
|
+
/** Retry due cleanup rows. A supplied owner/workspace limits the retry to its scope. */
|
|
143
|
+
retryPendingRevocations(scope?: Pick<WorkspaceKeyIdentity, 'ownerUserId' | 'workspaceId'>): Promise<number>;
|
|
144
|
+
}
|
|
145
|
+
/** Configuration for {@link createIdentityBoundWorkspaceKeyManager}. */
|
|
146
|
+
export interface DurableWorkspaceKeyManagerOptions {
|
|
147
|
+
store: DurableWorkspaceKeyStore;
|
|
148
|
+
provisioner: DurableWorkspaceKeyProvisioner;
|
|
149
|
+
crypto: KeyCrypto;
|
|
150
|
+
/** Product partition. Products must use separate values. */
|
|
151
|
+
product: WorkspaceKeyProduct;
|
|
152
|
+
defaultBudgetUsd: number;
|
|
153
|
+
now?: () => Date;
|
|
154
|
+
/** Local lifetime used when the remote response omits an expiry. */
|
|
155
|
+
keyLifetimeMs?: number;
|
|
156
|
+
/** Lease lifetime for remote operations. */
|
|
157
|
+
leaseMs?: number;
|
|
158
|
+
/** Maximum time to wait for another issuer to release the scope lease. */
|
|
159
|
+
leaseWaitMs?: number;
|
|
160
|
+
/** Poll delay while waiting for a scope lease. */
|
|
161
|
+
leasePollMs?: number;
|
|
162
|
+
/** Renewal interval. It must be shorter than `leaseMs`. */
|
|
163
|
+
leaseRenewIntervalMs?: number;
|
|
164
|
+
/** Initial delay for a failed remote revoke. */
|
|
165
|
+
revocationRetryBaseMs?: number;
|
|
166
|
+
/** Maximum delay for a failed remote revoke. */
|
|
167
|
+
revocationRetryMaxMs?: number;
|
|
168
|
+
/** Stable remote name. The operation id makes the default crash-safe. */
|
|
169
|
+
nameForIdentity?: (identity: WorkspaceKeyIdentity, operationId: string) => string;
|
|
170
|
+
/** Resolve names for rows written before `name` became durable. Null blocks issuance safely. */
|
|
171
|
+
legacyNameForRecord?: (record: DurableWorkspaceKeyRecord) => string | null | undefined;
|
|
172
|
+
/** Recognize a missing remote key without depending on a provider SDK. */
|
|
173
|
+
isRemoteMissing?: (error: unknown) => boolean;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Create a product-neutral manager for child-key issuance and cleanup.
|
|
177
|
+
*
|
|
178
|
+
* The provisioner must already be authenticated for the current source
|
|
179
|
+
* credential. The manager persists only the source id and its non-secret
|
|
180
|
+
* fingerprint, then refuses to reuse a row from another identity.
|
|
181
|
+
*/
|
|
182
|
+
export declare function createIdentityBoundWorkspaceKeyManager(options: DurableWorkspaceKeyManagerOptions): DurableWorkspaceKeyManager;
|
|
183
|
+
export {};
|
package/dist/billing/index.d.ts
CHANGED
|
@@ -239,3 +239,4 @@ export interface PlatformBalanceManager<Plan extends string> {
|
|
|
239
239
|
export declare function createPlatformBalanceManager<Plan extends string>(opts: PlatformBalanceManagerOptions<Plan>): PlatformBalanceManager<Plan>;
|
|
240
240
|
/** Create a workspace key manager that handles key provisioning and budget tracking */
|
|
241
241
|
export declare function createWorkspaceKeyManager(opts: WorkspaceKeyManagerOptions): WorkspaceKeyManager;
|
|
242
|
+
export { createIdentityBoundWorkspaceKeyManager, type DurableWorkspaceKeyManager, type DurableWorkspaceKeyManagerOptions, type DurableWorkspaceKeyProvisioner, type DurableWorkspaceKeyProvisioningRecord, type DurableWorkspaceKeyRecord, type DurableWorkspaceKeyScope, type DurableWorkspaceKeyStore, type WorkspaceKeyIdentity, type WorkspaceKeyProduct, type WorkspaceKeyStatus, type WorkspaceKeyUsage, type WorkspaceRuntimeKey, } from './identity-bound';
|
package/dist/billing/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
+
createIdentityBoundWorkspaceKeyManager,
|
|
2
3
|
createPlatformBalanceManager,
|
|
3
4
|
createTcloudKeyProvisioner,
|
|
4
5
|
createWorkspaceKeyManager
|
|
5
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-TS2ZIB53.js";
|
|
6
7
|
export {
|
|
8
|
+
createIdentityBoundWorkspaceKeyManager,
|
|
7
9
|
createPlatformBalanceManager,
|
|
8
10
|
createTcloudKeyProvisioner,
|
|
9
11
|
createWorkspaceKeyManager
|
|
@@ -112,7 +112,7 @@ import {
|
|
|
112
112
|
flattenHistory,
|
|
113
113
|
readSandboxBinaryBytes,
|
|
114
114
|
statSandboxFileSize
|
|
115
|
-
} from "../chunk-
|
|
115
|
+
} from "../chunk-LKZBMDYM.js";
|
|
116
116
|
import "../chunk-MCYJON3F.js";
|
|
117
117
|
import "../chunk-LWSJK546.js";
|
|
118
118
|
import "../chunk-P5PIAQVS.js";
|
|
@@ -2674,8 +2674,9 @@ async function driveSandboxTurn(shell, box, message, options) {
|
|
|
2674
2674
|
...options.wallCapMs !== void 0 ? { wallCapMs: options.wallCapMs } : {},
|
|
2675
2675
|
...options.timeoutMs !== void 0 ? { timeoutMs: options.timeoutMs } : {},
|
|
2676
2676
|
...options.signal ? { signal: options.signal } : {},
|
|
2677
|
-
//
|
|
2678
|
-
//
|
|
2677
|
+
// Sandbox 0.37 drives through the session message lane, so a gateway
|
|
2678
|
+
// consumer can handle these events. Callers without one should omit
|
|
2679
|
+
// interactions so the run stays unattended.
|
|
2679
2680
|
backend: {
|
|
2680
2681
|
type: harness,
|
|
2681
2682
|
profile,
|
|
@@ -2833,4 +2834,4 @@ export {
|
|
|
2833
2834
|
isTerminalPromptEvent,
|
|
2834
2835
|
detectInteractiveQuestion
|
|
2835
2836
|
};
|
|
2836
|
-
//# sourceMappingURL=chunk-
|
|
2837
|
+
//# sourceMappingURL=chunk-LKZBMDYM.js.map
|