@tangle-network/agent-app 0.45.25 → 0.45.27
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/dist/assistant/index.js +5 -5
- package/dist/chat-routes/index.js +21 -21
- package/dist/chat-store/index.js +2 -2
- package/dist/{chunk-B2UEW62O.js → chunk-5ZTFZBS6.js} +4 -4
- package/dist/chunk-N3G3FSM4.js +509 -0
- package/dist/chunk-N3G3FSM4.js.map +1 -0
- package/dist/chunk-NEJ7OMQS.js +351 -0
- package/dist/chunk-NEJ7OMQS.js.map +1 -0
- package/dist/{chunk-KOMP6NDW.js → chunk-ODYE4A7L.js} +6 -6
- package/dist/{chunk-7V2I3JGZ.js → chunk-OTLEYHOG.js} +50 -12
- package/dist/chunk-OTLEYHOG.js.map +1 -0
- package/dist/chunk-WD2B6HGY.js +1046 -0
- package/dist/chunk-WD2B6HGY.js.map +1 -0
- package/dist/design-canvas-react/index.js +4 -4
- package/dist/run-tEsZUhAf.d.ts +40 -0
- package/dist/sandbox/index.d.ts +53 -1
- package/dist/sandbox/index.js +1 -1
- package/dist/signoff/cli.d.ts +12 -0
- package/dist/signoff/cli.js +166 -0
- package/dist/signoff/cli.js.map +1 -0
- package/dist/signoff/index.d.ts +430 -0
- package/dist/signoff/index.js +63 -0
- package/dist/signoff/index.js.map +1 -0
- package/dist/signoff/proof-cli.d.ts +1 -0
- package/dist/signoff/proof-cli.js +92 -0
- package/dist/signoff/proof-cli.js.map +1 -0
- package/dist/signoff/proof.d.ts +515 -0
- package/dist/signoff/proof.js +148 -0
- package/dist/signoff/proof.js.map +1 -0
- package/dist/spend/cli.d.ts +1 -0
- package/dist/spend/cli.js +119 -0
- package/dist/spend/cli.js.map +1 -0
- package/dist/spend/index.d.ts +640 -0
- package/dist/spend/index.js +196 -0
- package/dist/spend/index.js.map +1 -0
- package/dist/stream/index.js +14 -14
- package/dist/teams/index.js +9 -9
- package/dist/teams/invitations-api.js +7 -7
- package/dist/types-U7Nz-txa.d.ts +233 -0
- package/dist/web-react/index.js +11 -11
- package/package.json +22 -3
- package/dist/chunk-7V2I3JGZ.js.map +0 -1
- /package/dist/{chunk-B2UEW62O.js.map → chunk-5ZTFZBS6.js.map} +0 -0
- /package/dist/{chunk-KOMP6NDW.js.map → chunk-ODYE4A7L.js.map} +0 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_CEILING_TOLERANCE_MS,
|
|
3
|
+
SPEND_CHECKS,
|
|
4
|
+
chargeNanoUsd,
|
|
5
|
+
computeExpectedCeiling,
|
|
6
|
+
formatSpendReport,
|
|
7
|
+
isCharge,
|
|
8
|
+
parseSandboxGroupKey,
|
|
9
|
+
parseSettlementReference,
|
|
10
|
+
reconcileSpend,
|
|
11
|
+
settlementSandboxId,
|
|
12
|
+
spendReportToJson
|
|
13
|
+
} from "../chunk-NEJ7OMQS.js";
|
|
14
|
+
|
|
15
|
+
// src/spend/store.ts
|
|
16
|
+
function foldSpendBoxRecord(record, patch) {
|
|
17
|
+
let lastActivityAt = record.lastActivityAt;
|
|
18
|
+
let stoppedAt = record.stoppedAt;
|
|
19
|
+
let openDetachedRunIds = record.openDetachedRunIds;
|
|
20
|
+
if (patch.observedActivityAt !== void 0) {
|
|
21
|
+
lastActivityAt = Math.max(lastActivityAt, patch.observedActivityAt);
|
|
22
|
+
if (stoppedAt !== null && patch.observedActivityAt > stoppedAt) stoppedAt = null;
|
|
23
|
+
}
|
|
24
|
+
if (patch.openDetachedRunAdd !== void 0 && !openDetachedRunIds.includes(patch.openDetachedRunAdd)) {
|
|
25
|
+
openDetachedRunIds = [...openDetachedRunIds, patch.openDetachedRunAdd];
|
|
26
|
+
}
|
|
27
|
+
if (patch.openDetachedRunRemove !== void 0) {
|
|
28
|
+
openDetachedRunIds = openDetachedRunIds.filter((id) => id !== patch.openDetachedRunRemove);
|
|
29
|
+
}
|
|
30
|
+
if (patch.stoppedAt !== void 0) {
|
|
31
|
+
stoppedAt = patch.stoppedAt >= lastActivityAt ? patch.stoppedAt : stoppedAt;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
...record,
|
|
35
|
+
lastActivityAt,
|
|
36
|
+
stoppedAt,
|
|
37
|
+
openDetachedRunIds,
|
|
38
|
+
// Set-once: a deleted sandbox id never comes back, so a second observation
|
|
39
|
+
// is a duplicate delivery, not a second deletion.
|
|
40
|
+
deletedAt: record.deletedAt ?? patch.deletedAt ?? null
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function createInMemorySpendLedgerStore() {
|
|
44
|
+
const rows = /* @__PURE__ */ new Map();
|
|
45
|
+
return {
|
|
46
|
+
async load(sandboxId) {
|
|
47
|
+
const row = rows.get(sandboxId);
|
|
48
|
+
return row ? structuredClone(row) : null;
|
|
49
|
+
},
|
|
50
|
+
async insert(record) {
|
|
51
|
+
const stored = structuredClone(record);
|
|
52
|
+
rows.set(record.sandboxId, stored);
|
|
53
|
+
return structuredClone(stored);
|
|
54
|
+
},
|
|
55
|
+
async update(sandboxId, patch) {
|
|
56
|
+
const current = rows.get(sandboxId);
|
|
57
|
+
if (!current) return null;
|
|
58
|
+
const next = foldSpendBoxRecord(current, patch);
|
|
59
|
+
rows.set(sandboxId, next);
|
|
60
|
+
return structuredClone(next);
|
|
61
|
+
},
|
|
62
|
+
records() {
|
|
63
|
+
return [...rows.values()].map((row) => structuredClone(row));
|
|
64
|
+
},
|
|
65
|
+
put(record) {
|
|
66
|
+
rows.set(record.sandboxId, structuredClone(record));
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function createSpendLedger(options) {
|
|
71
|
+
const { store } = options;
|
|
72
|
+
const clock = options.now ?? Date.now;
|
|
73
|
+
return {
|
|
74
|
+
async observeSandbox(input) {
|
|
75
|
+
const at = input.at ?? clock();
|
|
76
|
+
const existing = await store.load(input.sandboxId);
|
|
77
|
+
if (existing) {
|
|
78
|
+
const updated = await store.update(input.sandboxId, { observedActivityAt: at });
|
|
79
|
+
return updated ?? existing;
|
|
80
|
+
}
|
|
81
|
+
return await store.insert(
|
|
82
|
+
{
|
|
83
|
+
sandboxId: input.sandboxId,
|
|
84
|
+
workspaceId: input.workspaceId,
|
|
85
|
+
createdAt: at,
|
|
86
|
+
idleTimeoutSeconds: input.idleTimeoutSeconds,
|
|
87
|
+
maxLifetimeSeconds: input.maxLifetimeSeconds ?? null,
|
|
88
|
+
lastActivityAt: at,
|
|
89
|
+
openDetachedRunIds: [],
|
|
90
|
+
stoppedAt: null,
|
|
91
|
+
deletedAt: null
|
|
92
|
+
},
|
|
93
|
+
options.extras
|
|
94
|
+
);
|
|
95
|
+
},
|
|
96
|
+
async recordActivity(sandboxId, at) {
|
|
97
|
+
return await store.update(sandboxId, { observedActivityAt: at ?? clock() });
|
|
98
|
+
},
|
|
99
|
+
async recordDetachedRunStarted(sandboxId, runId, at) {
|
|
100
|
+
return await store.update(sandboxId, {
|
|
101
|
+
observedActivityAt: at ?? clock(),
|
|
102
|
+
openDetachedRunAdd: runId
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
async recordDetachedRunEnded(sandboxId, runId, at) {
|
|
106
|
+
return await store.update(sandboxId, {
|
|
107
|
+
observedActivityAt: at ?? clock(),
|
|
108
|
+
openDetachedRunRemove: runId
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
async recordStopped(sandboxId, at) {
|
|
112
|
+
return await store.update(sandboxId, { stoppedAt: at ?? clock() });
|
|
113
|
+
},
|
|
114
|
+
async recordDeleted(sandboxId, at) {
|
|
115
|
+
return await store.update(sandboxId, { deletedAt: at ?? clock() });
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// src/spend/budget.ts
|
|
121
|
+
var ComputeBudgetExceededError = class extends Error {
|
|
122
|
+
workspaceId;
|
|
123
|
+
limitNanoUsd;
|
|
124
|
+
settledNanoUsd;
|
|
125
|
+
overageNanoUsd;
|
|
126
|
+
constructor(refusal) {
|
|
127
|
+
super(
|
|
128
|
+
`Compute budget exceeded for workspace ${refusal.workspaceId}: $${(refusal.settledNanoUsd / 1e9).toFixed(2)} settled against a cap of $${(refusal.limitNanoUsd / 1e9).toFixed(2)} (over by $${(refusal.overageNanoUsd / 1e9).toFixed(2)}). No sandbox was provisioned. Raise the cap or reconcile the spend before retrying.`
|
|
129
|
+
);
|
|
130
|
+
this.name = "ComputeBudgetExceededError";
|
|
131
|
+
this.workspaceId = refusal.workspaceId;
|
|
132
|
+
this.limitNanoUsd = refusal.limitNanoUsd;
|
|
133
|
+
this.settledNanoUsd = refusal.settledNanoUsd;
|
|
134
|
+
this.overageNanoUsd = refusal.overageNanoUsd;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
async function assertComputeBudget(budget, workspaceId) {
|
|
138
|
+
if (!budget) return;
|
|
139
|
+
const settledNanoUsd = await budget.settledNanoUsd(workspaceId);
|
|
140
|
+
if (settledNanoUsd < budget.limitNanoUsd) return;
|
|
141
|
+
const refusal = {
|
|
142
|
+
workspaceId,
|
|
143
|
+
limitNanoUsd: budget.limitNanoUsd,
|
|
144
|
+
settledNanoUsd,
|
|
145
|
+
overageNanoUsd: settledNanoUsd - budget.limitNanoUsd,
|
|
146
|
+
at: (budget.now ?? Date.now)()
|
|
147
|
+
};
|
|
148
|
+
budget.onRefusal?.(refusal);
|
|
149
|
+
throw new ComputeBudgetExceededError(refusal);
|
|
150
|
+
}
|
|
151
|
+
function createSandboxSpendHooks(options) {
|
|
152
|
+
const { ledger, budget, onError } = options;
|
|
153
|
+
return {
|
|
154
|
+
async beforeProvision(input) {
|
|
155
|
+
await assertComputeBudget(budget, input.workspaceId);
|
|
156
|
+
},
|
|
157
|
+
async onProvisioned(observation) {
|
|
158
|
+
if (!ledger) return;
|
|
159
|
+
try {
|
|
160
|
+
await ledger.observeSandbox({
|
|
161
|
+
sandboxId: observation.sandboxId,
|
|
162
|
+
workspaceId: observation.workspaceId,
|
|
163
|
+
idleTimeoutSeconds: observation.idleTimeoutSeconds,
|
|
164
|
+
maxLifetimeSeconds: observation.maxLifetimeSeconds ?? null,
|
|
165
|
+
at: observation.at
|
|
166
|
+
});
|
|
167
|
+
} catch (err) {
|
|
168
|
+
onError?.(err);
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
onActivity(input) {
|
|
172
|
+
if (!ledger) return;
|
|
173
|
+
void ledger.recordActivity(input.sandboxId, input.at).catch((err) => onError?.(err));
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
export {
|
|
178
|
+
ComputeBudgetExceededError,
|
|
179
|
+
DEFAULT_CEILING_TOLERANCE_MS,
|
|
180
|
+
SPEND_CHECKS,
|
|
181
|
+
assertComputeBudget,
|
|
182
|
+
chargeNanoUsd,
|
|
183
|
+
computeExpectedCeiling,
|
|
184
|
+
createInMemorySpendLedgerStore,
|
|
185
|
+
createSandboxSpendHooks,
|
|
186
|
+
createSpendLedger,
|
|
187
|
+
foldSpendBoxRecord,
|
|
188
|
+
formatSpendReport,
|
|
189
|
+
isCharge,
|
|
190
|
+
parseSandboxGroupKey,
|
|
191
|
+
parseSettlementReference,
|
|
192
|
+
reconcileSpend,
|
|
193
|
+
settlementSandboxId,
|
|
194
|
+
spendReportToJson
|
|
195
|
+
};
|
|
196
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/spend/store.ts","../../src/spend/budget.ts"],"sourcesContent":["import type { SpendBoxPatch, SpendBoxRecord } from './types'\n\n/**\n * Persistence seam for the expectation ledger — the product implements it over\n * its own tables.\n *\n * Deliberately NOT compare-and-set, unlike `MissionStorePort`. A mission has one\n * serialized owner and a lost write corrupts a state machine; a box record is a\n * MONOTONIC FOLD (activity takes a max, a detached-run id joins or leaves a set,\n * delete is set-once) so concurrent writers converge no matter what order they\n * land in. The worst a lost race can do here is leave `lastActivityAt` behind\n * the truth — which makes the derived ceiling TIGHTER, so the failure mode is a\n * false alarm a human dismisses, never a missed charge. That asymmetry is the\n * whole reason the fold is shaped this way.\n *\n * `update` returns null when the row does not exist, never a throw.\n */\nexport interface SpendLedgerStorePort {\n load(sandboxId: string): Promise<SpendBoxRecord | null>\n /** `extras` are the opaque product-column values — write them in the SAME\n * statement as the record, or ignore them if the table has no extra columns. */\n insert(record: SpendBoxRecord, extras?: Record<string, unknown>): Promise<SpendBoxRecord>\n update(sandboxId: string, patch: SpendBoxPatch): Promise<SpendBoxRecord | null>\n}\n\n/**\n * Apply one fold step. Exported so a SQL implementation and an in-memory one\n * reach the same record, and so a product can unit-test its own store against\n * the canonical answer.\n *\n * The two rules worth stating out loud:\n *\n * - `observedActivityAt` only ever moves `lastActivityAt` FORWARD. A replayed\n * or out-of-order event cannot rewind the ceiling.\n * - activity later than a recorded `stoppedAt` CLEARS the stop. A box that\n * worked after the product thought it stopped is running again, and keeping\n * the stale stop would make the ceiling too tight — inventing an over-ceiling\n * finding out of the product's own bookkeeping rather than the platform's.\n */\nexport function foldSpendBoxRecord(record: SpendBoxRecord, patch: SpendBoxPatch): SpendBoxRecord {\n let lastActivityAt = record.lastActivityAt\n let stoppedAt = record.stoppedAt\n let openDetachedRunIds = record.openDetachedRunIds\n\n if (patch.observedActivityAt !== undefined) {\n lastActivityAt = Math.max(lastActivityAt, patch.observedActivityAt)\n if (stoppedAt !== null && patch.observedActivityAt > stoppedAt) stoppedAt = null\n }\n if (patch.openDetachedRunAdd !== undefined && !openDetachedRunIds.includes(patch.openDetachedRunAdd)) {\n openDetachedRunIds = [...openDetachedRunIds, patch.openDetachedRunAdd]\n }\n if (patch.openDetachedRunRemove !== undefined) {\n openDetachedRunIds = openDetachedRunIds.filter((id) => id !== patch.openDetachedRunRemove)\n }\n if (patch.stoppedAt !== undefined) {\n // Latest-wins, but never behind observed activity: a stop we are told about\n // that predates work we watched is not the stop that closed this box.\n stoppedAt = patch.stoppedAt >= lastActivityAt ? patch.stoppedAt : stoppedAt\n }\n\n return {\n ...record,\n lastActivityAt,\n stoppedAt,\n openDetachedRunIds,\n // Set-once: a deleted sandbox id never comes back, so a second observation\n // is a duplicate delivery, not a second deletion.\n deletedAt: record.deletedAt ?? patch.deletedAt ?? null,\n }\n}\n\n/** An in-memory store that also lets a test inspect and force state. */\nexport interface InMemorySpendLedgerStore extends SpendLedgerStorePort {\n /** Every record, insertion order. */\n records(): SpendBoxRecord[]\n /** Unguarded direct write — simulates a crash-shaped or platform-seeded row. */\n put(record: SpendBoxRecord): void\n}\n\n/** Create an in-memory expectation ledger. Production writers use the same port. */\nexport function createInMemorySpendLedgerStore(): InMemorySpendLedgerStore {\n const rows = new Map<string, SpendBoxRecord>()\n return {\n async load(sandboxId) {\n const row = rows.get(sandboxId)\n return row ? structuredClone(row) : null\n },\n async insert(record) {\n const stored = structuredClone(record)\n rows.set(record.sandboxId, stored)\n return structuredClone(stored)\n },\n async update(sandboxId, patch) {\n const current = rows.get(sandboxId)\n if (!current) return null\n const next = foldSpendBoxRecord(current, patch)\n rows.set(sandboxId, next)\n return structuredClone(next)\n },\n records() {\n return [...rows.values()].map((row) => structuredClone(row))\n },\n put(record) {\n rows.set(record.sandboxId, structuredClone(record))\n },\n }\n}\n\n/** What the product tells the ledger when it first sees a box. */\nexport interface ObserveSandboxInput {\n readonly sandboxId: string\n readonly workspaceId: string\n /** The idle timeout the product asked the platform for, seconds. */\n readonly idleTimeoutSeconds: number\n /** The max lifetime the product asked for, seconds, when it asked for one. */\n readonly maxLifetimeSeconds?: number | null\n /** Defaults to the ledger's clock. */\n readonly at?: number\n}\n\nexport interface SpendLedgerOptions {\n readonly store: SpendLedgerStorePort\n /** Injectable clock (epoch ms). Default `Date.now`. */\n readonly now?: () => number\n /** Product columns written verbatim on every insert. */\n readonly extras?: Record<string, unknown>\n}\n\n/**\n * The recording half of spend verification: the product's own account of what\n * it asked the platform for.\n *\n * Every method is best-effort from the caller's point of view — a product wires\n * these into paths that must not fail because bookkeeping failed. They still\n * reject on a store error rather than swallowing it, so a caller that wants\n * fire-and-forget says so at the call site (`/sandbox`'s hook does).\n */\nexport interface SpendLedger {\n /**\n * Record that a box exists and is billable from now. Inserts on first sight,\n * and otherwise records activity — reuse and resume are both \"the platform is\n * charging for this box again\", and the record's own existence is what\n * distinguishes them, so no caller has to know which happened.\n */\n observeSandbox(input: ObserveSandboxInput): Promise<SpendBoxRecord>\n /** Record that the product saw this box do work. */\n recordActivity(sandboxId: string, at?: number): Promise<SpendBoxRecord | null>\n /**\n * Record that the product handed the platform work it will NOT watch finish.\n * Until the matching end is recorded, this box's ceiling cannot rest on\n * observed activity — see `computeExpectedCeiling`.\n */\n recordDetachedRunStarted(sandboxId: string, runId: string, at?: number): Promise<SpendBoxRecord | null>\n /** Record that a detached run was confirmed finished. */\n recordDetachedRunEnded(sandboxId: string, runId: string, at?: number): Promise<SpendBoxRecord | null>\n /** Record that the product knows this box stopped. */\n recordStopped(sandboxId: string, at?: number): Promise<SpendBoxRecord | null>\n /** Record that the product knows this box was deleted. */\n recordDeleted(sandboxId: string, at?: number): Promise<SpendBoxRecord | null>\n}\n\n/** Create the recording half over a product-supplied store. */\nexport function createSpendLedger(options: SpendLedgerOptions): SpendLedger {\n const { store } = options\n const clock = options.now ?? Date.now\n\n return {\n async observeSandbox(input) {\n const at = input.at ?? clock()\n const existing = await store.load(input.sandboxId)\n if (existing) {\n const updated = await store.update(input.sandboxId, { observedActivityAt: at })\n return updated ?? existing\n }\n return await store.insert(\n {\n sandboxId: input.sandboxId,\n workspaceId: input.workspaceId,\n createdAt: at,\n idleTimeoutSeconds: input.idleTimeoutSeconds,\n maxLifetimeSeconds: input.maxLifetimeSeconds ?? null,\n lastActivityAt: at,\n openDetachedRunIds: [],\n stoppedAt: null,\n deletedAt: null,\n },\n options.extras,\n )\n },\n async recordActivity(sandboxId, at) {\n return await store.update(sandboxId, { observedActivityAt: at ?? clock() })\n },\n async recordDetachedRunStarted(sandboxId, runId, at) {\n return await store.update(sandboxId, {\n observedActivityAt: at ?? clock(),\n openDetachedRunAdd: runId,\n })\n },\n async recordDetachedRunEnded(sandboxId, runId, at) {\n return await store.update(sandboxId, {\n observedActivityAt: at ?? clock(),\n openDetachedRunRemove: runId,\n })\n },\n async recordStopped(sandboxId, at) {\n return await store.update(sandboxId, { stoppedAt: at ?? clock() })\n },\n async recordDeleted(sandboxId, at) {\n return await store.update(sandboxId, { deletedAt: at ?? clock() })\n },\n }\n}\n","import type { SpendLedger } from './store'\n\n/** Why provisioning was refused, with every number the decision used. */\nexport interface ComputeBudgetRefusal {\n readonly workspaceId: string\n /** The cap, unsigned nanodollars. */\n readonly limitNanoUsd: number\n /** Cumulative settled compute spend for this workspace, unsigned nanodollars. */\n readonly settledNanoUsd: number\n /** How far past the cap it already is. */\n readonly overageNanoUsd: number\n readonly at: number\n}\n\n/**\n * Provisioning refused because the workspace is already past its compute cap.\n *\n * Correctable by design: every number the decision used is on the error, so a\n * product can render \"this workspace has spent $X of its $Y compute budget\" and\n * an operator can raise the cap or investigate without reading logs.\n *\n * This is the failure mode the module exists to produce. A platform billing\n * defect that used to end in a silent negative balance now ends in provisioning\n * stopping and something loud happening instead.\n */\nexport class ComputeBudgetExceededError extends Error {\n readonly workspaceId: string\n readonly limitNanoUsd: number\n readonly settledNanoUsd: number\n readonly overageNanoUsd: number\n\n constructor(refusal: ComputeBudgetRefusal) {\n super(\n `Compute budget exceeded for workspace ${refusal.workspaceId}: ` +\n `$${(refusal.settledNanoUsd / 1_000_000_000).toFixed(2)} settled against a cap of ` +\n `$${(refusal.limitNanoUsd / 1_000_000_000).toFixed(2)} ` +\n `(over by $${(refusal.overageNanoUsd / 1_000_000_000).toFixed(2)}). ` +\n 'No sandbox was provisioned. Raise the cap or reconcile the spend before retrying.',\n )\n this.name = 'ComputeBudgetExceededError'\n this.workspaceId = refusal.workspaceId\n this.limitNanoUsd = refusal.limitNanoUsd\n this.settledNanoUsd = refusal.settledNanoUsd\n this.overageNanoUsd = refusal.overageNanoUsd\n }\n}\n\n/**\n * A per-workspace cap on sandbox compute.\n *\n * `/billing`'s budget primitive caps MODEL keys, and it works because the\n * platform enforces the cap at the key it minted. Sandbox compute has no such\n * key: a box bills the shared company wallet, so nothing upstream refuses. This\n * carries the same shape to the one place a consumer can still act — the moment\n * before it asks for another box.\n *\n * `settledNanoUsd` is a callback rather than a number because the authority is\n * the platform ledger, not this package: the product reads the same rows it\n * hands the reconciler. Cache it if the read is expensive; a cap is a\n * coarse-grained control and a slightly stale total still refuses.\n */\nexport interface ComputeBudget {\n /** The cap, unsigned nanodollars. */\n readonly limitNanoUsd: number\n /** Cumulative settled compute spend for the workspace, unsigned nanodollars. */\n readonly settledNanoUsd: (workspaceId: string) => Promise<number> | number\n /**\n * Called on every refusal, before the error is thrown. This is the alert\n * seam: a refusal nobody hears is a product that silently stopped working.\n */\n readonly onRefusal?: (refusal: ComputeBudgetRefusal) => void\n /** Injectable clock (epoch ms). Default `Date.now`. */\n readonly now?: () => number\n}\n\n/**\n * Throw {@link ComputeBudgetExceededError} when the workspace is already past\n * its cap. Returns normally — and reads nothing — when no budget is configured.\n *\n * Deliberately a pre-check against spend ALREADY SETTLED, not a reservation\n * against spend about to happen: settlement lags provisioning by design (the\n * platform's durable settlement queue), so there is no instant at which a\n * consumer could hold an accurate running total. The cap therefore overshoots by\n * at most the unsettled tail, which is bounded by the box's own idle timeout.\n * A cap that refuses one box late is worth far more than one that cannot be\n * implemented honestly.\n */\nexport async function assertComputeBudget(\n budget: ComputeBudget | undefined,\n workspaceId: string,\n): Promise<void> {\n if (!budget) return\n const settledNanoUsd = await budget.settledNanoUsd(workspaceId)\n if (settledNanoUsd < budget.limitNanoUsd) return\n\n const refusal: ComputeBudgetRefusal = {\n workspaceId,\n limitNanoUsd: budget.limitNanoUsd,\n settledNanoUsd,\n overageNanoUsd: settledNanoUsd - budget.limitNanoUsd,\n at: (budget.now ?? Date.now)(),\n }\n budget.onRefusal?.(refusal)\n throw new ComputeBudgetExceededError(refusal)\n}\n\n// ── the /sandbox seam ─────────────────────────────────────────────────────────\n\n/**\n * What `/sandbox` reports once a box is provisioned, reused or resumed.\n *\n * Structurally identical to `SandboxProvisionedObservation` in `/sandbox`, and\n * deliberately re-declared rather than imported: `/spend` composes `/sandbox`,\n * so a type import in the other direction would invert the dependency. The two\n * are pinned together by a compile-time assignment in this module's tests.\n */\nexport interface SpendProvisionObservation {\n readonly workspaceId: string\n readonly userId?: string\n readonly sandboxId: string\n readonly boxKey?: string | undefined\n readonly idleTimeoutSeconds: number\n readonly maxLifetimeSeconds?: number | undefined\n readonly at: number\n}\n\n/**\n * The optional seam `EnsureWorkspaceSandboxOptions.spend` and the turn\n * primitives' `spend` option both accept. One object, wired in both places.\n */\nexport interface SandboxSpendSeam {\n beforeProvision?(input: { workspaceId: string; userId?: string }): Promise<void> | void\n onProvisioned?(observation: SpendProvisionObservation): Promise<void> | void\n /** Synchronous by contract — it sits on the turn path. See `createSandboxSpendHooks`. */\n onActivity?(input: { sandboxId: string; at: number }): void\n}\n\nexport interface SandboxSpendHooksOptions {\n /** Records box lifecycle. Omit to run the budget guard alone. */\n readonly ledger?: SpendLedger\n /** Refuses provisioning past a cap. Omit to record alone. */\n readonly budget?: ComputeBudget\n /**\n * Called when RECORDING fails. Recording is best-effort — a bookkeeping\n * failure must never take down the provisioning it is bookkeeping — so this\n * is the only place such a failure is visible. A refusal is NOT routed here;\n * refusals throw, by design.\n */\n readonly onError?: (error: unknown) => void\n}\n\n/**\n * Build the object to hand `ensureWorkspaceSandbox`'s `spend` option.\n *\n * Wiring it is the entire adoption cost: one field, and the product's boxes are\n * both budget-capped and recorded.\n */\nexport function createSandboxSpendHooks(options: SandboxSpendHooksOptions): SandboxSpendSeam {\n const { ledger, budget, onError } = options\n return {\n async beforeProvision(input) {\n await assertComputeBudget(budget, input.workspaceId)\n },\n async onProvisioned(observation) {\n if (!ledger) return\n try {\n await ledger.observeSandbox({\n sandboxId: observation.sandboxId,\n workspaceId: observation.workspaceId,\n idleTimeoutSeconds: observation.idleTimeoutSeconds,\n maxLifetimeSeconds: observation.maxLifetimeSeconds ?? null,\n at: observation.at,\n })\n } catch (err) {\n onError?.(err)\n }\n },\n onActivity(input) {\n if (!ledger) return\n // The turn path calls this synchronously and does not await it, so the\n // promise is settled here rather than escaping as an unhandled rejection.\n // Recording activity is a monotonic max, so a write that lands late — or\n // out of order against another turn's — still converges.\n void ledger.recordActivity(input.sandboxId, input.at).catch((err: unknown) => onError?.(err))\n },\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/stream/index.js
CHANGED
|
@@ -4,20 +4,6 @@ import {
|
|
|
4
4
|
normalizeClientTurnId,
|
|
5
5
|
resolveChatTurn
|
|
6
6
|
} from "../chunk-JEZJ6HTF.js";
|
|
7
|
-
import {
|
|
8
|
-
DEFAULT_RUNNING_TURN_LEASE_MS,
|
|
9
|
-
DEFAULT_RUNNING_TURN_RENEW_INTERVAL_MS,
|
|
10
|
-
TURN_EVENTS_MIGRATION_SQL,
|
|
11
|
-
TURN_STATUS_SCOPE_MIGRATION_SQL,
|
|
12
|
-
coalesceChatStreamEvents,
|
|
13
|
-
coalesceDeltas,
|
|
14
|
-
createBufferedTurnTap,
|
|
15
|
-
createD1TurnEventStore,
|
|
16
|
-
createMemoryTurnEventStore,
|
|
17
|
-
pumpBufferedTurn,
|
|
18
|
-
replayTurnEvents,
|
|
19
|
-
stampReplaySeq
|
|
20
|
-
} from "../chunk-SDEADFHZ.js";
|
|
21
7
|
import {
|
|
22
8
|
MISSING_TOOL_TERMINAL_ERROR,
|
|
23
9
|
MISSING_TOOL_TERMINAL_REASON,
|
|
@@ -42,6 +28,20 @@ import {
|
|
|
42
28
|
} from "../chunk-XZ27ENOZ.js";
|
|
43
29
|
import "../chunk-M3K2HVQD.js";
|
|
44
30
|
import "../chunk-YJMCRXQQ.js";
|
|
31
|
+
import {
|
|
32
|
+
DEFAULT_RUNNING_TURN_LEASE_MS,
|
|
33
|
+
DEFAULT_RUNNING_TURN_RENEW_INTERVAL_MS,
|
|
34
|
+
TURN_EVENTS_MIGRATION_SQL,
|
|
35
|
+
TURN_STATUS_SCOPE_MIGRATION_SQL,
|
|
36
|
+
coalesceChatStreamEvents,
|
|
37
|
+
coalesceDeltas,
|
|
38
|
+
createBufferedTurnTap,
|
|
39
|
+
createD1TurnEventStore,
|
|
40
|
+
createMemoryTurnEventStore,
|
|
41
|
+
pumpBufferedTurn,
|
|
42
|
+
replayTurnEvents,
|
|
43
|
+
stampReplaySeq
|
|
44
|
+
} from "../chunk-SDEADFHZ.js";
|
|
45
45
|
export {
|
|
46
46
|
DEFAULT_RUNNING_TURN_LEASE_MS,
|
|
47
47
|
DEFAULT_RUNNING_TURN_RENEW_INTERVAL_MS,
|
package/dist/teams/index.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
INVITATION_EXPIRY_DAYS,
|
|
3
|
+
generateInvitationToken,
|
|
4
|
+
getInvitationExpiresAt,
|
|
5
|
+
inviteUrlForToken,
|
|
6
|
+
normalizeInvitationEmail,
|
|
7
|
+
parseInvitationPermission,
|
|
8
|
+
renderInvitationEmail
|
|
9
|
+
} from "../chunk-2DRYTJHI.js";
|
|
1
10
|
import {
|
|
2
11
|
generateInviteToken,
|
|
3
12
|
isInviteTokenShape,
|
|
@@ -18,15 +27,6 @@ import {
|
|
|
18
27
|
workspaceRoleToCollaborationAccess,
|
|
19
28
|
workspaceRoleToSandboxRole
|
|
20
29
|
} from "../chunk-6XIAPIW6.js";
|
|
21
|
-
import {
|
|
22
|
-
INVITATION_EXPIRY_DAYS,
|
|
23
|
-
generateInvitationToken,
|
|
24
|
-
getInvitationExpiresAt,
|
|
25
|
-
inviteUrlForToken,
|
|
26
|
-
normalizeInvitationEmail,
|
|
27
|
-
parseInvitationPermission,
|
|
28
|
-
renderInvitationEmail
|
|
29
|
-
} from "../chunk-2DRYTJHI.js";
|
|
30
30
|
export {
|
|
31
31
|
ASSIGNABLE_WORKSPACE_ROLES,
|
|
32
32
|
INVITATION_EXPIRY_DAYS,
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SeatLimitError
|
|
3
|
-
} from "../chunk-MEUNTJL5.js";
|
|
4
|
-
import "../chunk-DJ4VJIH5.js";
|
|
5
|
-
import {
|
|
6
|
-
hasWorkspaceRole
|
|
7
|
-
} from "../chunk-6XIAPIW6.js";
|
|
8
1
|
import {
|
|
9
2
|
generateInvitationToken,
|
|
10
3
|
getInvitationExpiresAt,
|
|
@@ -12,6 +5,13 @@ import {
|
|
|
12
5
|
normalizeInvitationEmail,
|
|
13
6
|
parseInvitationPermission
|
|
14
7
|
} from "../chunk-2DRYTJHI.js";
|
|
8
|
+
import {
|
|
9
|
+
SeatLimitError
|
|
10
|
+
} from "../chunk-MEUNTJL5.js";
|
|
11
|
+
import "../chunk-DJ4VJIH5.js";
|
|
12
|
+
import {
|
|
13
|
+
hasWorkspaceRole
|
|
14
|
+
} from "../chunk-6XIAPIW6.js";
|
|
15
15
|
|
|
16
16
|
// src/teams/invitations-api.ts
|
|
17
17
|
import { and, eq, lte, sql } from "drizzle-orm";
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vocabulary of a local sign-off run: what a repo declares, and what the
|
|
3
|
+
* run produces as proof.
|
|
4
|
+
*
|
|
5
|
+
* A sign-off gate replaces CI as the merge gate, so it has to reproduce what CI
|
|
6
|
+
* does structurally (a pristine dependency tree) and then do more than CI does
|
|
7
|
+
* (randomized suite order, recorded seeds, a dependency graph run in parallel).
|
|
8
|
+
* Both halves are declared here rather than hardcoded, because the three
|
|
9
|
+
* workflows this must express — agent-app, legal-agent, tax-agent — differ in
|
|
10
|
+
* install filter, step list, language (one has a Python step) and repo-specific
|
|
11
|
+
* gates.
|
|
12
|
+
*/
|
|
13
|
+
/** Which bytes get verified. */
|
|
14
|
+
type SignoffSource =
|
|
15
|
+
/** Exactly the commit that would merge. Uncommitted work is NOT included. */
|
|
16
|
+
'head'
|
|
17
|
+
/** HEAD plus the working tree: tracked modifications applied as a patch and
|
|
18
|
+
* untracked, non-ignored files copied in. What you are about to commit. */
|
|
19
|
+
| 'working-tree';
|
|
20
|
+
/**
|
|
21
|
+
* How a step is re-run under different suite orders.
|
|
22
|
+
*
|
|
23
|
+
* CI runs one arbitrary order. The `node:sqlite` bundling failure that started
|
|
24
|
+
* this was scheduling-dependent: it reproduced in CI's clean install under CI's
|
|
25
|
+
* worker sharding and not in a warm local run, so a single fixed order can miss
|
|
26
|
+
* it in either direction. Every seed used is recorded in the report, which is
|
|
27
|
+
* what makes a shuffled failure reproducible rather than folklore.
|
|
28
|
+
*/
|
|
29
|
+
interface SignoffShuffleSpec {
|
|
30
|
+
/** How many times to run the step, each with its own seed. Default 2. */
|
|
31
|
+
readonly runs?: number;
|
|
32
|
+
/** Exact seeds to use. Wins over `runs`; use it to replay a known failure. */
|
|
33
|
+
readonly seeds?: readonly number[];
|
|
34
|
+
/** Arguments appended to the command, with `{seed}` substituted. Defaults to
|
|
35
|
+
* vitest's file-order shuffle; override for another runner. */
|
|
36
|
+
readonly args?: readonly string[];
|
|
37
|
+
}
|
|
38
|
+
/** One verification step — a CI step, declared by the repo. */
|
|
39
|
+
interface SignoffStepSpec {
|
|
40
|
+
/** Unique within the config. Names the failure in the report. */
|
|
41
|
+
readonly name: string;
|
|
42
|
+
/** Shell command, run from `cwd` inside the clean tree. */
|
|
43
|
+
readonly run: string;
|
|
44
|
+
/** Relative to the clean tree root. Defaults to the root. */
|
|
45
|
+
readonly cwd?: string;
|
|
46
|
+
/** Extra environment for this step only. */
|
|
47
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
48
|
+
/** Step names that must pass first. Anything not named here may run in
|
|
49
|
+
* parallel with this step — that is the whole speed lever, so an omitted
|
|
50
|
+
* dependency is a correctness bug, not a tuning knob. */
|
|
51
|
+
readonly needs?: readonly string[];
|
|
52
|
+
/** Kill the step after this long. No default — an unbounded step is the
|
|
53
|
+
* repo's choice to make. */
|
|
54
|
+
readonly timeoutMs?: number;
|
|
55
|
+
/** `true` for the default shuffle spec, or an explicit one. */
|
|
56
|
+
readonly shuffle?: boolean | SignoffShuffleSpec;
|
|
57
|
+
}
|
|
58
|
+
/** The hermetic install that precedes every step. */
|
|
59
|
+
interface SignoffInstallSpec {
|
|
60
|
+
/** Default `pnpm install --frozen-lockfile`. tax-agent needs
|
|
61
|
+
* `--filter web...` because `server/` depends on a sibling repo by `file:`. */
|
|
62
|
+
readonly run?: string;
|
|
63
|
+
/** Flag used to point the package manager at the pristine store. Default
|
|
64
|
+
* `--store-dir`. `null` passes no flag (the env var still applies). */
|
|
65
|
+
readonly storeDirFlag?: string | null;
|
|
66
|
+
/** Env var used for the same. Default `NPM_CONFIG_STORE_DIR`, which is what
|
|
67
|
+
* the tax-agent and legal-agent workflows set. `null` sets none. */
|
|
68
|
+
readonly storeEnv?: string | null;
|
|
69
|
+
/** Relative to the clean tree root. Defaults to the root. */
|
|
70
|
+
readonly cwd?: string;
|
|
71
|
+
readonly timeoutMs?: number;
|
|
72
|
+
/** Extra environment for the install only. */
|
|
73
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
74
|
+
}
|
|
75
|
+
/** What a repo declares in `signoff.config.mjs` or a package.json `signoff` key. */
|
|
76
|
+
interface SignoffConfig {
|
|
77
|
+
readonly install?: SignoffInstallSpec;
|
|
78
|
+
readonly steps: readonly SignoffStepSpec[];
|
|
79
|
+
/** Cap on concurrently running steps. Defaults to the host's parallelism. */
|
|
80
|
+
readonly maxParallel?: number;
|
|
81
|
+
/** Environment applied to every step and the install. */
|
|
82
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
83
|
+
/** Node version the product ships on, e.g. `'22'`. Falls back to `.nvmrc`.
|
|
84
|
+
* A mismatch REFUSES the run — see `node-version.ts`. */
|
|
85
|
+
readonly nodeVersion?: string;
|
|
86
|
+
/** Gitignored files the run genuinely needs (a private-registry `.npmrc`).
|
|
87
|
+
* Explicit and fail-loud: a missing one aborts rather than installing from
|
|
88
|
+
* a different registry than the developer thinks. */
|
|
89
|
+
readonly carryFiles?: readonly string[];
|
|
90
|
+
/** Root for the clean tree and the pristine stores. Default
|
|
91
|
+
* `~/.cache/agent-app-signoff`. Both live under it so they share a
|
|
92
|
+
* filesystem — pnpm hardlinks from the store into `node_modules`, and a
|
|
93
|
+
* cross-device store silently degrades to copying. */
|
|
94
|
+
readonly cacheDir?: string;
|
|
95
|
+
/** Pristine store generations to keep. Default 4, so flipping between a
|
|
96
|
+
* branch and main stays warm on both. */
|
|
97
|
+
readonly storeGenerations?: number;
|
|
98
|
+
}
|
|
99
|
+
/** Where a config came from. Printed in the proof — a run against a derived
|
|
100
|
+
* default is a weaker claim than one against a declared step list. */
|
|
101
|
+
type SignoffConfigOrigin = {
|
|
102
|
+
readonly kind: 'file';
|
|
103
|
+
readonly path: string;
|
|
104
|
+
} | {
|
|
105
|
+
readonly kind: 'package-json';
|
|
106
|
+
readonly path: string;
|
|
107
|
+
} | {
|
|
108
|
+
readonly kind: 'derived';
|
|
109
|
+
readonly path: string;
|
|
110
|
+
readonly scripts: readonly string[];
|
|
111
|
+
};
|
|
112
|
+
interface LoadedSignoffConfig {
|
|
113
|
+
readonly config: SignoffConfig;
|
|
114
|
+
readonly origin: SignoffConfigOrigin;
|
|
115
|
+
}
|
|
116
|
+
/** One execution of a step's command. A shuffled step has several. */
|
|
117
|
+
interface SignoffAttempt {
|
|
118
|
+
readonly command: string;
|
|
119
|
+
/** The suite-order seed, or `null` for an unshuffled step. */
|
|
120
|
+
readonly seed: number | null;
|
|
121
|
+
readonly exitCode: number;
|
|
122
|
+
readonly signal: string | null;
|
|
123
|
+
readonly durationMs: number;
|
|
124
|
+
readonly timedOut: boolean;
|
|
125
|
+
/** Captured stdout+stderr, interleaved. Retained on failure; on success only
|
|
126
|
+
* the tail is kept, so a passing 3,000-test run does not bloat the proof. */
|
|
127
|
+
readonly output: string;
|
|
128
|
+
readonly outputTruncated: boolean;
|
|
129
|
+
}
|
|
130
|
+
type SignoffStepStatus = 'passed' | 'failed'
|
|
131
|
+
/** Never started — an earlier failure stopped the schedule. */
|
|
132
|
+
| 'skipped'
|
|
133
|
+
/** Started, then killed when another step failed under fail-fast. */
|
|
134
|
+
| 'cancelled'
|
|
135
|
+
/** A dependency failed, so this step could not be judged. */
|
|
136
|
+
| 'blocked';
|
|
137
|
+
interface SignoffStepResult {
|
|
138
|
+
readonly name: string;
|
|
139
|
+
readonly status: SignoffStepStatus;
|
|
140
|
+
readonly attempts: readonly SignoffAttempt[];
|
|
141
|
+
readonly durationMs: number;
|
|
142
|
+
/** Milliseconds from schedule start, so the report can show the real overlap
|
|
143
|
+
* rather than asserting a speedup it did not measure. */
|
|
144
|
+
readonly startedAtMs: number | null;
|
|
145
|
+
readonly finishedAtMs: number | null;
|
|
146
|
+
}
|
|
147
|
+
interface SignoffInstallResult {
|
|
148
|
+
readonly command: string;
|
|
149
|
+
readonly storeDir: string;
|
|
150
|
+
readonly cacheKey: string;
|
|
151
|
+
readonly cacheHit: boolean;
|
|
152
|
+
/** The files whose bytes produced `cacheKey`. Named so a surprise cold
|
|
153
|
+
* install is explainable rather than mysterious. */
|
|
154
|
+
readonly keyedOn: readonly string[];
|
|
155
|
+
readonly exitCode: number;
|
|
156
|
+
readonly durationMs: number;
|
|
157
|
+
readonly output: string;
|
|
158
|
+
readonly outputTruncated: boolean;
|
|
159
|
+
}
|
|
160
|
+
interface SignoffRepoFacts {
|
|
161
|
+
readonly root: string;
|
|
162
|
+
readonly head: string;
|
|
163
|
+
readonly branch: string;
|
|
164
|
+
readonly source: SignoffSource;
|
|
165
|
+
readonly dirty: boolean;
|
|
166
|
+
/** sha256 of the applied working-tree patch, `null` when nothing was applied.
|
|
167
|
+
* This is what makes the proof specific to the bytes that were verified. */
|
|
168
|
+
readonly diffSha256: string | null;
|
|
169
|
+
readonly untrackedFiles: readonly string[];
|
|
170
|
+
readonly carriedFiles: readonly string[];
|
|
171
|
+
}
|
|
172
|
+
interface SignoffHostFacts {
|
|
173
|
+
readonly node: string;
|
|
174
|
+
/** The pin the repo declares, and where it came from. `null` when the repo
|
|
175
|
+
* pins nothing — stated in the proof, because an unpinned runtime is a
|
|
176
|
+
* weaker claim than a pinned one. */
|
|
177
|
+
readonly nodePinned: string | null;
|
|
178
|
+
readonly nodePinSource: string | null;
|
|
179
|
+
readonly packageManager: string;
|
|
180
|
+
readonly platform: string;
|
|
181
|
+
readonly arch: string;
|
|
182
|
+
readonly cpus: number;
|
|
183
|
+
}
|
|
184
|
+
interface SignoffReport {
|
|
185
|
+
readonly ok: boolean;
|
|
186
|
+
readonly startedAt: string;
|
|
187
|
+
readonly repo: SignoffRepoFacts;
|
|
188
|
+
readonly configOrigin: SignoffConfigOrigin;
|
|
189
|
+
readonly workspace: string;
|
|
190
|
+
readonly workspaceRetained: boolean;
|
|
191
|
+
readonly host: SignoffHostFacts;
|
|
192
|
+
readonly install: SignoffInstallResult;
|
|
193
|
+
readonly steps: readonly SignoffStepResult[];
|
|
194
|
+
/** Base seed. Passing it back via `--seed` reproduces every step's seeds. */
|
|
195
|
+
readonly seedBase: number;
|
|
196
|
+
readonly wallClockMs: number;
|
|
197
|
+
/** Install + the sum of step durations: what the same work costs in series. */
|
|
198
|
+
readonly serialMs: number;
|
|
199
|
+
readonly keepGoing: boolean;
|
|
200
|
+
/** The exact command that reproduces this run. */
|
|
201
|
+
readonly reproduce: string;
|
|
202
|
+
}
|
|
203
|
+
/** Progress, for a CLI that prints as it goes rather than at the end. */
|
|
204
|
+
type SignoffEvent = {
|
|
205
|
+
readonly kind: 'tree';
|
|
206
|
+
readonly path: string;
|
|
207
|
+
readonly head: string;
|
|
208
|
+
readonly dirty: boolean;
|
|
209
|
+
} | {
|
|
210
|
+
readonly kind: 'store';
|
|
211
|
+
readonly storeDir: string;
|
|
212
|
+
readonly cacheHit: boolean;
|
|
213
|
+
readonly cacheKey: string;
|
|
214
|
+
} | {
|
|
215
|
+
readonly kind: 'install-start';
|
|
216
|
+
readonly command: string;
|
|
217
|
+
} | {
|
|
218
|
+
readonly kind: 'install-end';
|
|
219
|
+
readonly exitCode: number;
|
|
220
|
+
readonly durationMs: number;
|
|
221
|
+
} | {
|
|
222
|
+
readonly kind: 'step-start';
|
|
223
|
+
readonly name: string;
|
|
224
|
+
readonly command: string;
|
|
225
|
+
readonly seed: number | null;
|
|
226
|
+
} | {
|
|
227
|
+
readonly kind: 'step-end';
|
|
228
|
+
readonly name: string;
|
|
229
|
+
readonly status: SignoffStepStatus;
|
|
230
|
+
readonly durationMs: number;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
export type { LoadedSignoffConfig as L, SignoffSource as S, SignoffEvent as a, SignoffReport as b, SignoffConfig as c, SignoffStepResult as d, SignoffStepSpec as e, SignoffRepoFacts as f, SignoffAttempt as g, SignoffConfigOrigin as h, SignoffHostFacts as i, SignoffInstallResult as j, SignoffInstallSpec as k, SignoffShuffleSpec as l, SignoffStepStatus as m };
|
package/dist/web-react/index.js
CHANGED
|
@@ -117,16 +117,8 @@ import {
|
|
|
117
117
|
withoutRecordGridCreated,
|
|
118
118
|
withoutRecordGridRemoved,
|
|
119
119
|
withoutRecordGridUpdate
|
|
120
|
-
} from "../chunk-
|
|
120
|
+
} from "../chunk-ODYE4A7L.js";
|
|
121
121
|
import "../chunk-FBVLEGEG.js";
|
|
122
|
-
import {
|
|
123
|
-
useComposerAttachments
|
|
124
|
-
} from "../chunk-E4DHYENH.js";
|
|
125
|
-
import {
|
|
126
|
-
tabTerminalConnectionId,
|
|
127
|
-
useSandboxTerminalConnection
|
|
128
|
-
} from "../chunk-BATKJP3P.js";
|
|
129
|
-
import "../chunk-PC2WYTK7.js";
|
|
130
122
|
import {
|
|
131
123
|
EvidenceLineageTable,
|
|
132
124
|
ExceptionList,
|
|
@@ -142,6 +134,14 @@ import {
|
|
|
142
134
|
import {
|
|
143
135
|
parseReviewQueueItem
|
|
144
136
|
} from "../chunk-GEYACSFW.js";
|
|
137
|
+
import {
|
|
138
|
+
useComposerAttachments
|
|
139
|
+
} from "../chunk-E4DHYENH.js";
|
|
140
|
+
import {
|
|
141
|
+
tabTerminalConnectionId,
|
|
142
|
+
useSandboxTerminalConnection
|
|
143
|
+
} from "../chunk-BATKJP3P.js";
|
|
144
|
+
import "../chunk-PC2WYTK7.js";
|
|
145
145
|
import "../chunk-QY4BRKRJ.js";
|
|
146
146
|
import {
|
|
147
147
|
ATTACHMENT_ACCEPT
|
|
@@ -153,7 +153,8 @@ import {
|
|
|
153
153
|
isChatAttachmentPart,
|
|
154
154
|
mentionInputToPart,
|
|
155
155
|
mentionPartsFromMessageParts
|
|
156
|
-
} from "../chunk-
|
|
156
|
+
} from "../chunk-5ZTFZBS6.js";
|
|
157
|
+
import "../chunk-ZVEEWGDK.js";
|
|
157
158
|
import {
|
|
158
159
|
DISPATCH_MAX_MEDIA_PARTS,
|
|
159
160
|
DISPATCH_MAX_PARTS,
|
|
@@ -166,7 +167,6 @@ import {
|
|
|
166
167
|
mediaTypeForMentionPath,
|
|
167
168
|
mentionKindForPath
|
|
168
169
|
} from "../chunk-KWXUBMXU.js";
|
|
169
|
-
import "../chunk-ZVEEWGDK.js";
|
|
170
170
|
import {
|
|
171
171
|
attachmentPartKey
|
|
172
172
|
} from "../chunk-XZ27ENOZ.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-app",
|
|
3
|
-
"version": "0.45.
|
|
3
|
+
"version": "0.45.27",
|
|
4
4
|
"packageManager": "pnpm@11.17.0",
|
|
5
5
|
"description": "Build agent applications with typed chat, tools, sandboxes, integrations, billing, and evaluation.",
|
|
6
6
|
"keywords": [
|
|
@@ -35,10 +35,23 @@
|
|
|
35
35
|
"agent-app-theme-check": "./dist/theme-contract/cli.js",
|
|
36
36
|
"agent-app-legibility-check": "./dist/legibility/cli.js",
|
|
37
37
|
"agent-app-preflight": "./dist/preflight/cli.js",
|
|
38
|
-
"agent-app-peer-check": "./dist/peer-floors/cli.js"
|
|
38
|
+
"agent-app-peer-check": "./dist/peer-floors/cli.js",
|
|
39
|
+
"agent-app-spend-check": "./dist/spend/cli.js",
|
|
40
|
+
"agent-app-signoff": "./dist/signoff/cli.js",
|
|
41
|
+
"agent-app-verify-proof": "./dist/signoff/proof-cli.js"
|
|
39
42
|
},
|
|
40
43
|
"exports": {
|
|
41
44
|
"./package.json": "./package.json",
|
|
45
|
+
"./signoff": {
|
|
46
|
+
"types": "./dist/signoff/index.d.ts",
|
|
47
|
+
"import": "./dist/signoff/index.js",
|
|
48
|
+
"default": "./dist/signoff/index.js"
|
|
49
|
+
},
|
|
50
|
+
"./signoff/proof": {
|
|
51
|
+
"types": "./dist/signoff/proof.d.ts",
|
|
52
|
+
"import": "./dist/signoff/proof.js",
|
|
53
|
+
"default": "./dist/signoff/proof.js"
|
|
54
|
+
},
|
|
42
55
|
"./tools": {
|
|
43
56
|
"types": "./dist/tools/index.d.ts",
|
|
44
57
|
"import": "./dist/tools/index.js",
|
|
@@ -470,6 +483,11 @@
|
|
|
470
483
|
"types": "./dist/peer-floors/check.d.ts",
|
|
471
484
|
"import": "./dist/peer-floors/check.js",
|
|
472
485
|
"default": "./dist/peer-floors/check.js"
|
|
486
|
+
},
|
|
487
|
+
"./spend": {
|
|
488
|
+
"types": "./dist/spend/index.d.ts",
|
|
489
|
+
"import": "./dist/spend/index.js",
|
|
490
|
+
"default": "./dist/spend/index.js"
|
|
473
491
|
}
|
|
474
492
|
},
|
|
475
493
|
"scripts": {
|
|
@@ -482,7 +500,8 @@
|
|
|
482
500
|
"test:watch": "vitest",
|
|
483
501
|
"typecheck": "tsc --noEmit",
|
|
484
502
|
"docs:gen": "agent-docs",
|
|
485
|
-
"knip": "knip"
|
|
503
|
+
"knip": "knip",
|
|
504
|
+
"signoff": "node dist/signoff/cli.js"
|
|
486
505
|
},
|
|
487
506
|
"devDependencies": {
|
|
488
507
|
"@cloudflare/workers-types": "5.20260722.1",
|