@intelligo-dev/executions 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/dist/db/schema.d.ts +340 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +68 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle.d.ts +72 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +236 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/ports.d.ts +68 -0
- package/dist/ports.d.ts.map +1 -0
- package/dist/ports.js +16 -0
- package/dist/ports.js.map +1 -0
- package/dist/pricing.d.ts +159 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +187 -0
- package/dist/pricing.js.map +1 -0
- package/dist/queries.d.ts +126 -0
- package/dist/queries.d.ts.map +1 -0
- package/dist/queries.js +117 -0
- package/dist/queries.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAiB/C,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,mBAAmB,EACnB,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC;;;;GAIG;AACH,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,EACvB,aAAa,EACb,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution lifecycle — the SaaS boundary around a native AI run.
|
|
3
|
+
*
|
|
4
|
+
* const run = await executions.begin({ workspaceId, userId, capability });
|
|
5
|
+
* if (!run.allowed) return refuse(run.reason);
|
|
6
|
+
* try {
|
|
7
|
+
* const result = await careerAgent.generate(messages); // native
|
|
8
|
+
* await run.complete({ usage: result.usage, model: result.model });
|
|
9
|
+
* return result;
|
|
10
|
+
* } catch (error) {
|
|
11
|
+
* await run.fail({ error });
|
|
12
|
+
* throw error;
|
|
13
|
+
* }
|
|
14
|
+
*
|
|
15
|
+
* The handle knows nothing about agents, tools, messages, or streams —
|
|
16
|
+
* only actor, workspace, capability, entitlement, status, usage, cost,
|
|
17
|
+
* credits, and audit (ADR-0003).
|
|
18
|
+
*
|
|
19
|
+
* Every terminal transition is idempotent: calling complete() twice, or
|
|
20
|
+
* fail() after complete(), leaves the first outcome in place. Streaming
|
|
21
|
+
* routes have several plausible finish paths (usage resolved, client
|
|
22
|
+
* abort, error) and must be able to fire whichever arrives first
|
|
23
|
+
* without racing.
|
|
24
|
+
*/
|
|
25
|
+
import type { ExecutionPorts } from "./ports";
|
|
26
|
+
/**
|
|
27
|
+
* Row lifecycle. `settling` is non-terminal and deliberate: it marks
|
|
28
|
+
* the window where usage is being charged, so a second complete() or a
|
|
29
|
+
* racing fail() cannot re-enter it, and a settlement that dies mid-way
|
|
30
|
+
* leaves a state the stale sweep reports rather than a row that claims
|
|
31
|
+
* success for usage nobody recorded.
|
|
32
|
+
*/
|
|
33
|
+
export type ExecutionStatus = "running" | "settling" | "succeeded" | "failed" | "refused";
|
|
34
|
+
export type BeginExecutionInput = {
|
|
35
|
+
workspaceId: string;
|
|
36
|
+
userId?: string | null;
|
|
37
|
+
/** Product-defined verb, e.g. "career.recommendation". */
|
|
38
|
+
capability: string;
|
|
39
|
+
/** Supply to reuse an existing correlation id; generated otherwise. */
|
|
40
|
+
requestId?: string;
|
|
41
|
+
/** Model the caller intends to use — informs the entitlement hold. */
|
|
42
|
+
model?: string;
|
|
43
|
+
metadata?: Record<string, unknown>;
|
|
44
|
+
};
|
|
45
|
+
export type CompleteExecutionInput = {
|
|
46
|
+
usage?: {
|
|
47
|
+
inputTokens?: number;
|
|
48
|
+
outputTokens?: number;
|
|
49
|
+
totalTokens?: number;
|
|
50
|
+
};
|
|
51
|
+
model?: string;
|
|
52
|
+
metadata?: Record<string, unknown>;
|
|
53
|
+
};
|
|
54
|
+
export type ExecutionRun = {
|
|
55
|
+
id: string;
|
|
56
|
+
requestId: string;
|
|
57
|
+
/** False when entitlement refused; complete()/fail() are then no-ops. */
|
|
58
|
+
allowed: boolean;
|
|
59
|
+
/** Set when allowed is false. */
|
|
60
|
+
reason?: string;
|
|
61
|
+
estimatedMnt?: number;
|
|
62
|
+
usingTrialCredits: boolean;
|
|
63
|
+
complete(input?: CompleteExecutionInput): Promise<void>;
|
|
64
|
+
fail(input: {
|
|
65
|
+
error: unknown;
|
|
66
|
+
}): Promise<void>;
|
|
67
|
+
};
|
|
68
|
+
export declare function createExecutions(ports?: ExecutionPorts): {
|
|
69
|
+
begin: (input: BeginExecutionInput) => Promise<ExecutionRun>;
|
|
70
|
+
};
|
|
71
|
+
export type Executions = ReturnType<typeof createExecutions>;
|
|
72
|
+
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAQH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAI9C;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,UAAU,GACV,WAAW,GACX,QAAQ,GACR,SAAS,CAAC;AAEd,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,CAAC,EAAE;QACN,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,OAAO,EAAE,OAAO,CAAC;IACjB,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,IAAI,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD,CAAC;AAMF,wBAAgB,gBAAgB,CAAC,KAAK,GAAE,cAAmB;mBAC7B,mBAAmB,KAAG,OAAO,CAAC,YAAY,CAAC;EAkOxE;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution lifecycle — the SaaS boundary around a native AI run.
|
|
3
|
+
*
|
|
4
|
+
* const run = await executions.begin({ workspaceId, userId, capability });
|
|
5
|
+
* if (!run.allowed) return refuse(run.reason);
|
|
6
|
+
* try {
|
|
7
|
+
* const result = await careerAgent.generate(messages); // native
|
|
8
|
+
* await run.complete({ usage: result.usage, model: result.model });
|
|
9
|
+
* return result;
|
|
10
|
+
* } catch (error) {
|
|
11
|
+
* await run.fail({ error });
|
|
12
|
+
* throw error;
|
|
13
|
+
* }
|
|
14
|
+
*
|
|
15
|
+
* The handle knows nothing about agents, tools, messages, or streams —
|
|
16
|
+
* only actor, workspace, capability, entitlement, status, usage, cost,
|
|
17
|
+
* credits, and audit (ADR-0003).
|
|
18
|
+
*
|
|
19
|
+
* Every terminal transition is idempotent: calling complete() twice, or
|
|
20
|
+
* fail() after complete(), leaves the first outcome in place. Streaming
|
|
21
|
+
* routes have several plausible finish paths (usage resolved, client
|
|
22
|
+
* abort, error) and must be able to fire whichever arrives first
|
|
23
|
+
* without racing.
|
|
24
|
+
*/
|
|
25
|
+
import { db } from "@intelligo-dev/core/db";
|
|
26
|
+
import { createLogger } from "@intelligo-dev/core/logger";
|
|
27
|
+
import { recordAuditEvent } from "@intelligo-dev/audit";
|
|
28
|
+
import { and, eq } from "drizzle-orm";
|
|
29
|
+
import { executions } from "./db/schema";
|
|
30
|
+
const log = createLogger("Executions");
|
|
31
|
+
function errorMessage(error) {
|
|
32
|
+
return error instanceof Error ? error.message : String(error);
|
|
33
|
+
}
|
|
34
|
+
export function createExecutions(ports = {}) {
|
|
35
|
+
async function begin(input) {
|
|
36
|
+
const requestId = input.requestId ?? crypto.randomUUID();
|
|
37
|
+
const id = crypto.randomUUID();
|
|
38
|
+
const startedAt = new Date();
|
|
39
|
+
const decision = ports.checkEntitlement
|
|
40
|
+
? await ports.checkEntitlement({
|
|
41
|
+
workspaceId: input.workspaceId,
|
|
42
|
+
userId: input.userId,
|
|
43
|
+
capability: input.capability,
|
|
44
|
+
requestId,
|
|
45
|
+
model: input.model,
|
|
46
|
+
})
|
|
47
|
+
: { allowed: true };
|
|
48
|
+
await db.insert(executions).values({
|
|
49
|
+
id,
|
|
50
|
+
workspaceId: input.workspaceId,
|
|
51
|
+
userId: input.userId ?? null,
|
|
52
|
+
capability: input.capability,
|
|
53
|
+
requestId,
|
|
54
|
+
status: decision.allowed ? "running" : "refused",
|
|
55
|
+
model: input.model ?? null,
|
|
56
|
+
reservedMnt: decision.estimatedMnt ?? null,
|
|
57
|
+
refusalReason: decision.allowed ? null : (decision.reason ?? "refused"),
|
|
58
|
+
startedAt,
|
|
59
|
+
finishedAt: decision.allowed ? null : startedAt,
|
|
60
|
+
durationMs: decision.allowed ? null : 0,
|
|
61
|
+
metadata: input.metadata ?? null,
|
|
62
|
+
});
|
|
63
|
+
const usingTrialCredits = decision.usingTrialCredits ?? false;
|
|
64
|
+
if (!decision.allowed) {
|
|
65
|
+
await recordAuditEvent({
|
|
66
|
+
workspaceId: input.workspaceId,
|
|
67
|
+
actorId: input.userId ?? null,
|
|
68
|
+
action: "execution.refused",
|
|
69
|
+
resourceKind: "execution",
|
|
70
|
+
resourceId: id,
|
|
71
|
+
outcome: "failed",
|
|
72
|
+
metadata: {
|
|
73
|
+
capability: input.capability,
|
|
74
|
+
requestId,
|
|
75
|
+
reason: decision.reason,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
id,
|
|
80
|
+
requestId,
|
|
81
|
+
allowed: false,
|
|
82
|
+
reason: decision.reason,
|
|
83
|
+
estimatedMnt: decision.estimatedMnt,
|
|
84
|
+
usingTrialCredits,
|
|
85
|
+
async complete() { },
|
|
86
|
+
async fail() { },
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Compare-and-swap the row's status. Returns false when another
|
|
91
|
+
* path already moved it, so the caller can skip the side effects
|
|
92
|
+
* that belong to the transition it lost.
|
|
93
|
+
*/
|
|
94
|
+
async function transition(from, to, fields = {}) {
|
|
95
|
+
const updated = await db
|
|
96
|
+
.update(executions)
|
|
97
|
+
.set({ status: to, ...fields })
|
|
98
|
+
.where(and(eq(executions.id, id), eq(executions.status, from)))
|
|
99
|
+
.returning();
|
|
100
|
+
return updated.length > 0;
|
|
101
|
+
}
|
|
102
|
+
/** Terminal transition: stamps the finish time and duration. */
|
|
103
|
+
function finishFields(fields) {
|
|
104
|
+
const finishedAt = new Date();
|
|
105
|
+
return {
|
|
106
|
+
finishedAt,
|
|
107
|
+
durationMs: finishedAt.getTime() - startedAt.getTime(),
|
|
108
|
+
...fields,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
id,
|
|
113
|
+
requestId,
|
|
114
|
+
allowed: true,
|
|
115
|
+
estimatedMnt: decision.estimatedMnt,
|
|
116
|
+
usingTrialCredits,
|
|
117
|
+
async complete(result = {}) {
|
|
118
|
+
const inputTokens = result.usage?.inputTokens ?? 0;
|
|
119
|
+
const outputTokens = result.usage?.outputTokens ?? 0;
|
|
120
|
+
const totalTokens = result.usage?.totalTokens ?? inputTokens + outputTokens;
|
|
121
|
+
const model = result.model ?? input.model;
|
|
122
|
+
// Claim the right to settle BEFORE spending money. The CAS used
|
|
123
|
+
// to happen after settleUsage, so a second complete() — or a
|
|
124
|
+
// complete() racing a fail() — charged the workspace again and
|
|
125
|
+
// then quietly discovered it had lost the race. Status and
|
|
126
|
+
// audit were idempotent; the deduction was not.
|
|
127
|
+
if (!(await transition("running", "settling")))
|
|
128
|
+
return;
|
|
129
|
+
let chargedMnt;
|
|
130
|
+
if (ports.settleUsage) {
|
|
131
|
+
try {
|
|
132
|
+
const settled = await ports.settleUsage({
|
|
133
|
+
workspaceId: input.workspaceId,
|
|
134
|
+
userId: input.userId,
|
|
135
|
+
requestId,
|
|
136
|
+
capability: input.capability,
|
|
137
|
+
model,
|
|
138
|
+
inputTokens,
|
|
139
|
+
outputTokens,
|
|
140
|
+
totalTokens,
|
|
141
|
+
usingTrialCredits,
|
|
142
|
+
metadata: result.metadata ?? input.metadata,
|
|
143
|
+
});
|
|
144
|
+
chargedMnt = settled?.chargedMnt;
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
// Usage is money: never silently drop it. The row stays
|
|
148
|
+
// `settling` — a non-terminal state the stale sweep reports
|
|
149
|
+
// — so the execution is visibly unsettled rather than
|
|
150
|
+
// recorded as a successful free turn.
|
|
151
|
+
log.error("Usage settlement failed", {
|
|
152
|
+
executionId: id,
|
|
153
|
+
requestId,
|
|
154
|
+
workspaceId: input.workspaceId,
|
|
155
|
+
error: errorMessage(error),
|
|
156
|
+
});
|
|
157
|
+
await recordAuditEvent({
|
|
158
|
+
workspaceId: input.workspaceId,
|
|
159
|
+
actorId: input.userId ?? null,
|
|
160
|
+
action: "execution.settlement_failed",
|
|
161
|
+
resourceKind: "execution",
|
|
162
|
+
resourceId: id,
|
|
163
|
+
outcome: "failed",
|
|
164
|
+
metadata: {
|
|
165
|
+
requestId,
|
|
166
|
+
capability: input.capability,
|
|
167
|
+
error: errorMessage(error),
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
await transition("settling", "succeeded", finishFields({
|
|
174
|
+
model: model ?? null,
|
|
175
|
+
inputTokens,
|
|
176
|
+
outputTokens,
|
|
177
|
+
totalTokens,
|
|
178
|
+
chargedMnt: chargedMnt ?? null,
|
|
179
|
+
}));
|
|
180
|
+
await recordAuditEvent({
|
|
181
|
+
workspaceId: input.workspaceId,
|
|
182
|
+
actorId: input.userId ?? null,
|
|
183
|
+
action: "execution.completed",
|
|
184
|
+
resourceKind: "execution",
|
|
185
|
+
resourceId: id,
|
|
186
|
+
metadata: {
|
|
187
|
+
requestId,
|
|
188
|
+
capability: input.capability,
|
|
189
|
+
model,
|
|
190
|
+
totalTokens,
|
|
191
|
+
chargedMnt,
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
},
|
|
195
|
+
async fail({ error }) {
|
|
196
|
+
// Only from `running`. Once complete() has claimed the row for
|
|
197
|
+
// settlement, a late abort must not release a hold that is
|
|
198
|
+
// about to be charged.
|
|
199
|
+
const transitioned = await transition("running", "failed", finishFields({ errorMessage: errorMessage(error).slice(0, 1000) }));
|
|
200
|
+
if (!transitioned)
|
|
201
|
+
return;
|
|
202
|
+
if (ports.releaseHold) {
|
|
203
|
+
try {
|
|
204
|
+
await ports.releaseHold({
|
|
205
|
+
workspaceId: input.workspaceId,
|
|
206
|
+
requestId,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
catch (releaseError) {
|
|
210
|
+
// Non-fatal: an unreleased hold expires on its own.
|
|
211
|
+
log.warn("Hold release failed", {
|
|
212
|
+
executionId: id,
|
|
213
|
+
requestId,
|
|
214
|
+
error: errorMessage(releaseError),
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
await recordAuditEvent({
|
|
219
|
+
workspaceId: input.workspaceId,
|
|
220
|
+
actorId: input.userId ?? null,
|
|
221
|
+
action: "execution.failed",
|
|
222
|
+
resourceKind: "execution",
|
|
223
|
+
resourceId: id,
|
|
224
|
+
outcome: "failed",
|
|
225
|
+
metadata: {
|
|
226
|
+
requestId,
|
|
227
|
+
capability: input.capability,
|
|
228
|
+
error: errorMessage(error),
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
return { begin };
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"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"}
|
package/dist/ports.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ports the execution lifecycle depends on.
|
|
3
|
+
*
|
|
4
|
+
* `executions` must not import `billing` — in the target structure
|
|
5
|
+
* entitlement decisions live in `entitlements` and the hold/settle
|
|
6
|
+
* mechanics in `credits`, neither of which exists yet. Rather than
|
|
7
|
+
* bake in a dependency that has to be unwound later, the lifecycle
|
|
8
|
+
* declares what it needs and the composition root binds today's
|
|
9
|
+
* billing implementations to it (ADR-0005).
|
|
10
|
+
*
|
|
11
|
+
* Both ports are optional: with neither bound, executions still record
|
|
12
|
+
* the lifecycle, they just don't gate or charge. That is what the
|
|
13
|
+
* reference app and any non-metered capability want.
|
|
14
|
+
*/
|
|
15
|
+
export type EntitlementDecision = {
|
|
16
|
+
allowed: boolean;
|
|
17
|
+
/** Human-readable refusal, surfaced to the caller and recorded. */
|
|
18
|
+
reason?: string;
|
|
19
|
+
/** Worst-case charge held for this execution, in MNT. */
|
|
20
|
+
estimatedMnt?: number;
|
|
21
|
+
/** True when the hold came out of the trial grant. */
|
|
22
|
+
usingTrialCredits?: boolean;
|
|
23
|
+
};
|
|
24
|
+
export type EntitlementRequest = {
|
|
25
|
+
workspaceId: string;
|
|
26
|
+
userId?: string | null;
|
|
27
|
+
capability: string;
|
|
28
|
+
/** Correlates the hold with settlement. */
|
|
29
|
+
requestId: string;
|
|
30
|
+
model?: string;
|
|
31
|
+
};
|
|
32
|
+
export type UsageSettlement = {
|
|
33
|
+
workspaceId: string;
|
|
34
|
+
userId?: string | null;
|
|
35
|
+
requestId: string;
|
|
36
|
+
capability: string;
|
|
37
|
+
model?: string;
|
|
38
|
+
inputTokens: number;
|
|
39
|
+
outputTokens: number;
|
|
40
|
+
totalTokens: number;
|
|
41
|
+
usingTrialCredits: boolean;
|
|
42
|
+
metadata?: Record<string, unknown>;
|
|
43
|
+
};
|
|
44
|
+
export type SettlementResult = {
|
|
45
|
+
/** Actual amount charged, in MNT, if the port computed one. */
|
|
46
|
+
chargedMnt?: number;
|
|
47
|
+
};
|
|
48
|
+
export type ExecutionPorts = {
|
|
49
|
+
/**
|
|
50
|
+
* Decide whether this execution may run and hold the worst-case
|
|
51
|
+
* cost. Called before the run; a refusal short-circuits it.
|
|
52
|
+
*/
|
|
53
|
+
checkEntitlement?: (request: EntitlementRequest) => Promise<EntitlementDecision>;
|
|
54
|
+
/**
|
|
55
|
+
* Record real usage and release the hold. Called after a successful
|
|
56
|
+
* run. Must be idempotent per requestId — the lifecycle may retry.
|
|
57
|
+
*/
|
|
58
|
+
settleUsage?: (settlement: UsageSettlement) => Promise<SettlementResult | void>;
|
|
59
|
+
/**
|
|
60
|
+
* Release a hold without charging (run failed before producing
|
|
61
|
+
* usage). Optional: when absent, the hold is left to expire.
|
|
62
|
+
*/
|
|
63
|
+
releaseHold?: (input: {
|
|
64
|
+
workspaceId: string;
|
|
65
|
+
requestId: string;
|
|
66
|
+
}) => Promise<void>;
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=ports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,CACjB,OAAO,EAAE,kBAAkB,KACxB,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAElC;;;OAGG;IACH,WAAW,CAAC,EAAE,CACZ,UAAU,EAAE,eAAe,KACxB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAEtC;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;KACnB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB,CAAC"}
|
package/dist/ports.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ports the execution lifecycle depends on.
|
|
3
|
+
*
|
|
4
|
+
* `executions` must not import `billing` — in the target structure
|
|
5
|
+
* entitlement decisions live in `entitlements` and the hold/settle
|
|
6
|
+
* mechanics in `credits`, neither of which exists yet. Rather than
|
|
7
|
+
* bake in a dependency that has to be unwound later, the lifecycle
|
|
8
|
+
* declares what it needs and the composition root binds today's
|
|
9
|
+
* billing implementations to it (ADR-0005).
|
|
10
|
+
*
|
|
11
|
+
* Both ports are optional: with neither bound, executions still record
|
|
12
|
+
* the lifecycle, they just don't gate or charge. That is what the
|
|
13
|
+
* reference app and any non-metered capability want.
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=ports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.js","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model registry and execution cost accounting.
|
|
3
|
+
*
|
|
4
|
+
* This lives in `executions` rather than beside the provider clients
|
|
5
|
+
* because it is not AI code: it is what an execution costs. The
|
|
6
|
+
* boundary records actor, workspace, capability, usage and cost
|
|
7
|
+
* (ADR-0003), and the price of a token is the last of those.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately a leaf module — no database, no provider SDK, no
|
|
10
|
+
* imports at all — and reachable as `@intelligo-dev/executions/pricing`
|
|
11
|
+
* so a client bundle can read a display name or a price without
|
|
12
|
+
* pulling in Drizzle. `@intelligo-dev/ai` re-exports it, so the ids that
|
|
13
|
+
* pick a provider and the ids that carry a price stay one list; two
|
|
14
|
+
* lists is how a model runs on Gemini and bills at Claude rates.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Model configuration with display information and cost tracking
|
|
18
|
+
*
|
|
19
|
+
* 6 models, 3 providers: Google (2), OpenAI (3), Anthropic (1)
|
|
20
|
+
*/
|
|
21
|
+
export declare const MODEL_CONFIGS: {
|
|
22
|
+
readonly "google/gemini-2.5-flash": {
|
|
23
|
+
readonly provider: "google";
|
|
24
|
+
readonly model: "gemini-2.5-flash";
|
|
25
|
+
readonly displayName: "Gemini 2.5 Flash";
|
|
26
|
+
readonly costPerMInputTokens: 0.3;
|
|
27
|
+
readonly costPerMOutputTokens: 2.5;
|
|
28
|
+
readonly capabilities: {
|
|
29
|
+
readonly thinking: true;
|
|
30
|
+
readonly toolCall: true;
|
|
31
|
+
readonly vision: true;
|
|
32
|
+
readonly webSearch: true;
|
|
33
|
+
readonly codeExec: true;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
readonly "google/gemini-2.5-pro": {
|
|
37
|
+
readonly provider: "google";
|
|
38
|
+
readonly model: "gemini-2.5-pro";
|
|
39
|
+
readonly displayName: "Gemini 2.5 Pro";
|
|
40
|
+
readonly costPerMInputTokens: 1.25;
|
|
41
|
+
readonly costPerMOutputTokens: 10;
|
|
42
|
+
readonly capabilities: {
|
|
43
|
+
readonly thinking: true;
|
|
44
|
+
readonly toolCall: true;
|
|
45
|
+
readonly vision: true;
|
|
46
|
+
readonly webSearch: true;
|
|
47
|
+
readonly codeExec: true;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
readonly "openai/gpt-5-mini": {
|
|
51
|
+
readonly provider: "openai";
|
|
52
|
+
readonly model: "gpt-5-mini";
|
|
53
|
+
readonly displayName: "GPT-5 Mini";
|
|
54
|
+
readonly costPerMInputTokens: 0.25;
|
|
55
|
+
readonly costPerMOutputTokens: 2;
|
|
56
|
+
readonly capabilities: {
|
|
57
|
+
readonly thinking: true;
|
|
58
|
+
readonly toolCall: true;
|
|
59
|
+
readonly vision: true;
|
|
60
|
+
readonly webSearch: false;
|
|
61
|
+
readonly codeExec: false;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
readonly "openai/gpt-5.4-mini": {
|
|
65
|
+
readonly provider: "openai";
|
|
66
|
+
readonly model: "gpt-5.4-mini";
|
|
67
|
+
readonly displayName: "GPT-5.4 Mini";
|
|
68
|
+
readonly costPerMInputTokens: 0.75;
|
|
69
|
+
readonly costPerMOutputTokens: 4.5;
|
|
70
|
+
readonly capabilities: {
|
|
71
|
+
readonly thinking: true;
|
|
72
|
+
readonly toolCall: true;
|
|
73
|
+
readonly vision: true;
|
|
74
|
+
readonly webSearch: true;
|
|
75
|
+
readonly codeExec: true;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
readonly "openai/o4-mini": {
|
|
79
|
+
readonly provider: "openai";
|
|
80
|
+
readonly model: "o4-mini";
|
|
81
|
+
readonly displayName: "o4-mini";
|
|
82
|
+
readonly costPerMInputTokens: 1.1;
|
|
83
|
+
readonly costPerMOutputTokens: 4.4;
|
|
84
|
+
readonly capabilities: {
|
|
85
|
+
readonly thinking: true;
|
|
86
|
+
readonly toolCall: true;
|
|
87
|
+
readonly vision: true;
|
|
88
|
+
readonly webSearch: true;
|
|
89
|
+
readonly codeExec: true;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
readonly "anthropic/claude-sonnet-4-6": {
|
|
93
|
+
readonly provider: "anthropic";
|
|
94
|
+
readonly model: "claude-sonnet-4-6-20260214";
|
|
95
|
+
readonly displayName: "Claude Sonnet 4.6";
|
|
96
|
+
readonly costPerMInputTokens: 3;
|
|
97
|
+
readonly costPerMOutputTokens: 15;
|
|
98
|
+
readonly capabilities: {
|
|
99
|
+
readonly thinking: true;
|
|
100
|
+
readonly toolCall: true;
|
|
101
|
+
readonly vision: true;
|
|
102
|
+
readonly webSearch: true;
|
|
103
|
+
readonly codeExec: true;
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
export type ModelId = keyof typeof MODEL_CONFIGS;
|
|
108
|
+
/**
|
|
109
|
+
* Calculate the dollar cost for a given model and token counts.
|
|
110
|
+
*/
|
|
111
|
+
export declare function calculateCost(modelId: string, inputTokens: number, outputTokens: number): number;
|
|
112
|
+
/**
|
|
113
|
+
* Default billing margin multiplier applied on top of raw model cost.
|
|
114
|
+
* Covers infra, tool overhead, FX volatility, and profit. Tunable per
|
|
115
|
+
* deploy via billing_settings.margin_multiplier; this constant is the
|
|
116
|
+
* fallback when the DB value is absent.
|
|
117
|
+
*
|
|
118
|
+
* INVARIANT — 65% gross margin floor (founder contract):
|
|
119
|
+
* gross_margin = 1 − 1/multiplier
|
|
120
|
+
* We must keep gross_margin ≥ 0.65 across every model in MODEL_CONFIGS,
|
|
121
|
+
* which means multiplier ≥ 1/0.35 ≈ 2.857. The current value of 4
|
|
122
|
+
* yields 75%, leaving comfortable buffer above the floor for FX moves
|
|
123
|
+
* and unexpected provider price hikes. The Vitest assertion in
|
|
124
|
+
* models.test.ts pins this — drop below 2.857 at your own risk.
|
|
125
|
+
*/
|
|
126
|
+
export declare const DEFAULT_BILLING_MARGIN = 4;
|
|
127
|
+
/**
|
|
128
|
+
* Default USD→MNT exchange rate fallback. Real value lives in
|
|
129
|
+
* billing_settings.usd_to_mnt_rate and is read per request.
|
|
130
|
+
*/
|
|
131
|
+
export declare const DEFAULT_USD_TO_MNT_RATE = 3450;
|
|
132
|
+
/**
|
|
133
|
+
* Per-model maximum output budget used for worst-case pre-request cost
|
|
134
|
+
* estimation in checkQuota. Numbers are conservative — cap on streamed
|
|
135
|
+
* output tokens we'd actually let a single chat turn produce.
|
|
136
|
+
*/
|
|
137
|
+
export declare const MODEL_OUTPUT_BUDGET: Record<ModelId, number>;
|
|
138
|
+
export type ChargedAmount = {
|
|
139
|
+
rawCostUsd: number;
|
|
140
|
+
chargedMnt: number;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Convert raw model cost to MNT user-facing charged amount.
|
|
144
|
+
*
|
|
145
|
+
* chargedMnt = ceil( rawCostUsd × margin × fxRate )
|
|
146
|
+
*
|
|
147
|
+
* The rounding is upward so micro-fractions never let a free request
|
|
148
|
+
* slip through; over-billing per request is at most 1 MNT.
|
|
149
|
+
*/
|
|
150
|
+
export declare function calculateChargedMnt(modelId: string, inputTokens: number, outputTokens: number, fxRate?: number, margin?: number): ChargedAmount;
|
|
151
|
+
/**
|
|
152
|
+
* Worst-case MNT cost estimate for a single chat turn against a given
|
|
153
|
+
* model. Used by checkQuota to refuse requests whose ceiling cost would
|
|
154
|
+
* exceed remaining balance, before we burn provider tokens. Uses the
|
|
155
|
+
* full MODEL_OUTPUT_BUDGET as the output side and a generous 16K input
|
|
156
|
+
* budget to cover system prompt + conversation history.
|
|
157
|
+
*/
|
|
158
|
+
export declare function estimateWorstCaseChargedMnt(modelId: string, fxRate?: number, margin?: number): number;
|
|
159
|
+
//# sourceMappingURL=pricing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;GAIG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwFhB,CAAC;AAEX,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,aAAa,CAAC;AAEjD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GACnB,MAAM,CAkBR;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC;;;GAGG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAOvD,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,MAAM,GAAE,MAAgC,EACxC,MAAM,GAAE,MAA+B,GACtC,aAAa,CAIf;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAgC,EACxC,MAAM,GAAE,MAA+B,GACtC,MAAM,CAKR"}
|