@elevasis/sdk 1.54.0 → 1.56.0
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 +21 -0
- package/README.md +32 -0
- package/dist/{chunk-ZAVFBZHM.js → chunk-72ZGICTR.js} +247 -6
- package/dist/{chunk-OT4CHFQJ.js → chunk-R3J6BEPO.js} +54 -5
- package/dist/cli.cjs +153 -15
- package/dist/index.d.ts +46 -16
- package/dist/index.js +1 -1
- package/dist/node/index.d.ts +1 -8
- package/dist/node/index.js +3 -14
- package/dist/test-utils/index.js +2 -2
- package/dist/worker/index.d.ts +9 -0
- package/dist/worker/index.js +2 -2
- package/package.json +4 -4
- package/reference/_navigation.md +3 -3
- package/reference/_reference-manifest.json +3 -3
- package/reference/packages/core/src/knowledge/README.md +1 -1
- package/reference/packages/ui/src/knowledge/README.md +32 -32
- package/reference/rules/error-handling.md +50 -1
- package/reference/rules/execution.md +63 -7
- package/reference/scaffold/core/organization-model.mdx +1 -1
- package/reference/scaffold/recipes/customize-organization-model.md +3 -1
- package/reference/scaffold/recipes/extend-lead-gen.md +2 -1
- package/reference/scaffold/reference/contracts.md +30 -21
- package/reference/sdk/framework/project-structure.mdx +1 -3
- package/reference/ui/exports.mdx +1 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Elevasis
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# @elevasis/sdk
|
|
2
|
+
|
|
3
|
+
SDK for building Elevasis organization resources — agents, workflows, and the CLI that deploys them.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add @elevasis/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Published surface
|
|
12
|
+
|
|
13
|
+
| Subpath | Contains |
|
|
14
|
+
| ---------------------------- | ------------------------------------------------------------------------- |
|
|
15
|
+
| `@elevasis/sdk` | Resource definitions, the registry, and its validators |
|
|
16
|
+
| `@elevasis/sdk/worker` | The worker-thread authoring surface: `Agent`, `Workflow`, collectors |
|
|
17
|
+
| `@elevasis/sdk/node` | Node-only helpers |
|
|
18
|
+
| `@elevasis/sdk/test-utils` | Test helpers for resource authors |
|
|
19
|
+
|
|
20
|
+
The package also installs an `elevasis-sdk` binary. Run `elevasis-sdk --help` for the command set;
|
|
21
|
+
every authenticated command accepts `--prod` to target production rather than a local API.
|
|
22
|
+
|
|
23
|
+
## Relationship to `@elevasis/core`
|
|
24
|
+
|
|
25
|
+
The SDK inlines the `@repo/core` types it re-exports, so no `@elevasis/core` specifier survives in
|
|
26
|
+
the shipped declarations. That makes the SDK a second sanctioned route onto capabilities core does
|
|
27
|
+
not publish directly — import them from here rather than reaching for a core subpath that does not
|
|
28
|
+
resolve.
|
|
29
|
+
|
|
30
|
+
## License
|
|
31
|
+
|
|
32
|
+
MIT — see [LICENSE](./LICENSE).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ProcessingStageStatusSchema, ListBuilderStageKeySchema, LLMResponseParseError, errorToString, getErrorDetails, ExecutionError2, validateEntryPoint, validateTerminalSteps, validateStepReferences, WorkflowTimeoutError, WorkflowStalledError, WorkflowCancellationError, logWorkflowStart, detectCycle, WorkflowStepError, logStepStart, validateTerminalOutput, logStepSuccess, determineNextStep, logStepFailure, logExecutionPath, logWorkflowSuccess, logWorkflowFailure, estimateTokens, allSettledWithConcurrency, truncationCharBudget, buildIterationResponseSchema } from './chunk-
|
|
1
|
+
import { ProcessingStageStatusSchema, ListBuilderStageKeySchema, LLMResponseParseError, errorToString, getErrorDetails, ExecutionError2, validateEntryPoint, validateTerminalSteps, validateStepReferences, WorkflowTimeoutError, WorkflowStalledError, WorkflowCancellationError, WorkflowStepTimeoutError, logWorkflowStart, detectCycle, WorkflowStepError, logStepStart, validateTerminalOutput, logStepSuccess, determineNextStep, logStepFailure, logExecutionPath, logWorkflowSuccess, logWorkflowFailure, estimateTokens, allSettledWithConcurrency, calculateCost, truncationCharBudget, buildIterationResponseSchema } from './chunk-R3J6BEPO.js';
|
|
2
2
|
import { workerData, parentPort } from 'worker_threads';
|
|
3
3
|
import { zodToJsonSchema } from '@alcyone-labs/zod-to-json-schema';
|
|
4
4
|
import { z, ZodError } from 'zod';
|
|
@@ -65,6 +65,71 @@ var Workflow = class {
|
|
|
65
65
|
}
|
|
66
66
|
return new WorkflowCancellationError("Execution cancelled by user", { stepId, executionPath });
|
|
67
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Run one step's handler, bounded by the step's own `timeout` when it declares one.
|
|
70
|
+
*
|
|
71
|
+
* Two things have to happen for a step deadline to mean anything, and doing only one of them is
|
|
72
|
+
* why this is not a bare `Promise.race`:
|
|
73
|
+
*
|
|
74
|
+
* 1. The handler is handed a context whose `signal` is the step's, chained off the execution's, so
|
|
75
|
+
* a handler that already honours `context.signal` (every `fetch`-based one does) aborts its own
|
|
76
|
+
* work at the step deadline instead of running on unattended.
|
|
77
|
+
* 2. The race rejects regardless, because a handler that ignores its signal would otherwise hold
|
|
78
|
+
* the graph open until the 2-hour execution ceiling -- which is the whole thing being fixed.
|
|
79
|
+
*
|
|
80
|
+
* The chaining direction matches `startExecutionDeadline` in the API's `lifecycle.ts`: the parent's
|
|
81
|
+
* abort reason is propagated verbatim into the child controller, so an execution-level cancel or
|
|
82
|
+
* timeout still classifies as itself rather than as a step deadline.
|
|
83
|
+
*
|
|
84
|
+
* A step with no `timeout` takes the untouched path and is handed `context` itself -- no derived
|
|
85
|
+
* controller, no listener, no race.
|
|
86
|
+
*/
|
|
87
|
+
async runStepHandler(step, input, context, executionPath) {
|
|
88
|
+
if (step.timeout === void 0) return step.handler(input, context);
|
|
89
|
+
const controller = new AbortController();
|
|
90
|
+
let deadlineFired = false;
|
|
91
|
+
const timer = setTimeout(() => {
|
|
92
|
+
deadlineFired = true;
|
|
93
|
+
controller.abort("timeout");
|
|
94
|
+
}, step.timeout);
|
|
95
|
+
let onParentAbort;
|
|
96
|
+
if (context.signal) {
|
|
97
|
+
if (context.signal.aborted) {
|
|
98
|
+
controller.abort(context.signal.reason);
|
|
99
|
+
} else {
|
|
100
|
+
onParentAbort = () => controller.abort(context.signal?.reason);
|
|
101
|
+
context.signal.addEventListener("abort", onParentAbort, { once: true });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const aborted = new Promise((_, reject) => {
|
|
105
|
+
controller.signal.addEventListener(
|
|
106
|
+
"abort",
|
|
107
|
+
() => {
|
|
108
|
+
if (deadlineFired) {
|
|
109
|
+
reject(
|
|
110
|
+
new WorkflowStepTimeoutError(`Step exceeded its timeout (${step.timeout}ms) [${step.id}:${step.name}]`, {
|
|
111
|
+
timeout: step.timeout,
|
|
112
|
+
stepId: step.id,
|
|
113
|
+
stepName: step.name,
|
|
114
|
+
executionPath
|
|
115
|
+
})
|
|
116
|
+
);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
reject(
|
|
120
|
+
this.abortErrorFor(context.signal, step.id, executionPath) ?? new WorkflowCancellationError("Execution cancelled by user", { stepId: step.id, executionPath })
|
|
121
|
+
);
|
|
122
|
+
},
|
|
123
|
+
{ once: true }
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
try {
|
|
127
|
+
return await Promise.race([step.handler(input, { ...context, signal: controller.signal }), aborted]);
|
|
128
|
+
} finally {
|
|
129
|
+
clearTimeout(timer);
|
|
130
|
+
if (onParentAbort) context.signal?.removeEventListener("abort", onParentAbort);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
68
133
|
/**
|
|
69
134
|
* Execute the workflow with graph-based flow control
|
|
70
135
|
* Context is required for execution tracking, logging, and organization isolation
|
|
@@ -98,7 +163,7 @@ var Workflow = class {
|
|
|
98
163
|
logStepStart(context, step.id, step.name, currentData, stepStartTime);
|
|
99
164
|
try {
|
|
100
165
|
const validatedInput = step.inputSchema.parse(currentData);
|
|
101
|
-
const rawOutput = await
|
|
166
|
+
const rawOutput = await this.runStepHandler(step, validatedInput, context, executionPath);
|
|
102
167
|
currentData = step.outputSchema.parse(rawOutput);
|
|
103
168
|
if (step.next === null && this.shouldGenerateOutput) {
|
|
104
169
|
validateTerminalOutput(step.id, currentData, this.contract.outputSchema);
|
|
@@ -120,6 +185,9 @@ var Workflow = class {
|
|
|
120
185
|
const stepEndTime = Date.now();
|
|
121
186
|
const duration = stepEndTime - stepStartTime;
|
|
122
187
|
logStepFailure(context, step.id, step.name, error, duration, stepStartTime, stepEndTime);
|
|
188
|
+
if (error instanceof WorkflowStepTimeoutError || error instanceof WorkflowTimeoutError || error instanceof WorkflowStalledError || error instanceof WorkflowCancellationError) {
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
123
191
|
const cause = error instanceof ExecutionError2 ? error : void 0;
|
|
124
192
|
throw new WorkflowStepError(
|
|
125
193
|
`Step failed [${step.id}:${step.name}]: ${errorToString(error)}`,
|
|
@@ -1255,6 +1323,12 @@ function normalizeSessionMessages(actions, sessionCapable) {
|
|
|
1255
1323
|
}
|
|
1256
1324
|
var NO_PROGRESS_STREAK_KEY = "agent.actions.noProgressStreak";
|
|
1257
1325
|
var MAX_CONSECUTIVE_NO_PROGRESS_ITERATIONS = 2;
|
|
1326
|
+
var LAST_PLAN_SIGNATURE_KEY = "agent.actions.lastPlanSignature";
|
|
1327
|
+
var REPEATED_PLAN_STREAK_KEY = "agent.actions.repeatedPlanStreak";
|
|
1328
|
+
var DEFAULT_MAX_IDENTICAL_ITERATIONS = 3;
|
|
1329
|
+
function planSignature(actions) {
|
|
1330
|
+
return JSON.stringify(actions);
|
|
1331
|
+
}
|
|
1258
1332
|
async function processActions(iterationContext, response) {
|
|
1259
1333
|
const normalizedActions = normalizeSessionMessages(response.nextActions, !!iterationContext.config.sessionCapable);
|
|
1260
1334
|
if (normalizedActions.length === 0) {
|
|
@@ -1278,6 +1352,31 @@ async function processActions(iterationContext, response) {
|
|
|
1278
1352
|
}
|
|
1279
1353
|
} else {
|
|
1280
1354
|
iterationContext.executionContext.store.delete(NO_PROGRESS_STREAK_KEY);
|
|
1355
|
+
const store = iterationContext.executionContext.store;
|
|
1356
|
+
const signature = planSignature(normalizedActions);
|
|
1357
|
+
const repeated = store.get(LAST_PLAN_SIGNATURE_KEY) === signature;
|
|
1358
|
+
const streak = repeated ? (store.get(REPEATED_PLAN_STREAK_KEY) ?? 0) + 1 : 0;
|
|
1359
|
+
store.set(LAST_PLAN_SIGNATURE_KEY, signature);
|
|
1360
|
+
store.set(REPEATED_PLAN_STREAK_KEY, streak);
|
|
1361
|
+
if (repeated) {
|
|
1362
|
+
const ceiling = iterationContext.config.constraints?.maxIdenticalIterations ?? DEFAULT_MAX_IDENTICAL_ITERATIONS;
|
|
1363
|
+
iterationContext.memoryManager.addToHistory({
|
|
1364
|
+
type: "error",
|
|
1365
|
+
content: JSON.stringify({
|
|
1366
|
+
error: `This iteration's plan is identical to the previous one (repeated ${streak}x). Repeating it again will produce the same result. Either act on what the last result returned, or complete.`
|
|
1367
|
+
}),
|
|
1368
|
+
turnNumber: iterationContext.executionContext.sessionTurnNumber ?? null,
|
|
1369
|
+
iterationNumber: iterationContext.iteration,
|
|
1370
|
+
source: "framework"
|
|
1371
|
+
});
|
|
1372
|
+
if (streak >= ceiling) {
|
|
1373
|
+
throw new AgentNoProgressError(`Agent repeated an identical plan for ${streak} consecutive iterations`, {
|
|
1374
|
+
iteration: iterationContext.iteration,
|
|
1375
|
+
streak,
|
|
1376
|
+
ceiling
|
|
1377
|
+
});
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1281
1380
|
}
|
|
1282
1381
|
const completeRequested = normalizedActions.some((action) => action.type === "complete");
|
|
1283
1382
|
const toolCalls = [];
|
|
@@ -2139,6 +2238,14 @@ var Agent = class {
|
|
|
2139
2238
|
while (iteration <= maxIterations) {
|
|
2140
2239
|
const abortError = this.abortErrorFor(context.signal, iteration);
|
|
2141
2240
|
if (abortError) throw abortError;
|
|
2241
|
+
const spendStop = this.spendCeilingReached(context);
|
|
2242
|
+
if (spendStop) {
|
|
2243
|
+
context.logger.warn(
|
|
2244
|
+
`Agent stopped at iteration ${iteration}: ${spendStop.limit} ceiling of ${spendStop.ceiling} reached (${spendStop.actual})`
|
|
2245
|
+
);
|
|
2246
|
+
this.stopReason = "spend_exhausted";
|
|
2247
|
+
return;
|
|
2248
|
+
}
|
|
2142
2249
|
try {
|
|
2143
2250
|
await context.onHeartbeat?.();
|
|
2144
2251
|
} catch {
|
|
@@ -2162,6 +2269,33 @@ var Agent = class {
|
|
|
2162
2269
|
}
|
|
2163
2270
|
this.stopReason = "budget_exhausted";
|
|
2164
2271
|
}
|
|
2272
|
+
/**
|
|
2273
|
+
* Whether a declared spend ceiling has been reached, and which one.
|
|
2274
|
+
*
|
|
2275
|
+
* Reads `ExecutionContext.aiUsageCollector` -- the same collector `ObservabilityService` persists
|
|
2276
|
+
* to `execution_metrics` -- so enforcement and the recorded figure can never disagree. The
|
|
2277
|
+
* collector was already being written on every LLM call and read only after the fact; this is the
|
|
2278
|
+
* first place its running total decides anything.
|
|
2279
|
+
*
|
|
2280
|
+
* Returns `null` when no ceiling is declared, or when no collector was injected. An execution
|
|
2281
|
+
* without a collector is not silently capped at zero -- it is uncapped, which is what it was
|
|
2282
|
+
* before any of this existed.
|
|
2283
|
+
*
|
|
2284
|
+
* @returns the breached ceiling, or `null` when none is.
|
|
2285
|
+
*/
|
|
2286
|
+
spendCeilingReached(context) {
|
|
2287
|
+
const { maxCostUsd, maxTotalTokens } = this.config.constraints ?? {};
|
|
2288
|
+
if (maxCostUsd === void 0 && maxTotalTokens === void 0) return null;
|
|
2289
|
+
const summary = context.aiUsageCollector?.getSummary();
|
|
2290
|
+
if (!summary) return null;
|
|
2291
|
+
if (maxCostUsd !== void 0 && summary.totalCostUsd >= maxCostUsd) {
|
|
2292
|
+
return { limit: "maxCostUsd", ceiling: maxCostUsd, actual: summary.totalCostUsd };
|
|
2293
|
+
}
|
|
2294
|
+
if (maxTotalTokens !== void 0 && summary.totalTokens >= maxTotalTokens) {
|
|
2295
|
+
return { limit: "maxTotalTokens", ceiling: maxTotalTokens, actual: summary.totalTokens };
|
|
2296
|
+
}
|
|
2297
|
+
return null;
|
|
2298
|
+
}
|
|
2165
2299
|
/**
|
|
2166
2300
|
* Classify an aborted signal into the typed error the rest of the framework expects, regardless
|
|
2167
2301
|
* of where the abort surfaced. An abort landing mid-`fetch` or mid-tool throws whatever the
|
|
@@ -2589,6 +2723,78 @@ Fix the errors and generate a valid output.
|
|
|
2589
2723
|
}
|
|
2590
2724
|
}
|
|
2591
2725
|
};
|
|
2726
|
+
|
|
2727
|
+
// ../core/src/operations/observability/ai-usage-collector.ts
|
|
2728
|
+
var AIUsageCollector = class {
|
|
2729
|
+
model = "gpt-5";
|
|
2730
|
+
// Default, will be overwritten on first record()
|
|
2731
|
+
calls = [];
|
|
2732
|
+
callSequence = 0;
|
|
2733
|
+
/**
|
|
2734
|
+
* Record a single AI call with usage metrics
|
|
2735
|
+
*
|
|
2736
|
+
* @param usage - Token usage and latency data from LLM adapter
|
|
2737
|
+
* @param callType - Type discriminator (agent-reasoning, tool, etc.)
|
|
2738
|
+
* @param context - Optional typed context specific to callType
|
|
2739
|
+
*/
|
|
2740
|
+
record(usage, callType = "other", context) {
|
|
2741
|
+
this.callSequence++;
|
|
2742
|
+
this.model = usage.model;
|
|
2743
|
+
const costUsd = usage.cost ?? calculateCost(
|
|
2744
|
+
usage.model,
|
|
2745
|
+
usage.inputTokens,
|
|
2746
|
+
usage.outputTokens,
|
|
2747
|
+
usage.cacheReadInputTokens,
|
|
2748
|
+
usage.cacheCreationInputTokens
|
|
2749
|
+
);
|
|
2750
|
+
const inputTokens = usage.inputTokens + (usage.cacheReadInputTokens ?? 0) + (usage.cacheCreationInputTokens ?? 0);
|
|
2751
|
+
this.calls.push({
|
|
2752
|
+
callSequence: this.callSequence,
|
|
2753
|
+
callType,
|
|
2754
|
+
model: usage.model,
|
|
2755
|
+
inputTokens,
|
|
2756
|
+
outputTokens: usage.outputTokens,
|
|
2757
|
+
costUsd,
|
|
2758
|
+
latencyMs: usage.latencyMs,
|
|
2759
|
+
context,
|
|
2760
|
+
...usage.cacheReadInputTokens !== void 0 ? { cacheReadInputTokens: usage.cacheReadInputTokens } : {},
|
|
2761
|
+
...usage.cacheCreationInputTokens !== void 0 ? { cacheCreationInputTokens: usage.cacheCreationInputTokens } : {},
|
|
2762
|
+
...usage.inputWarnings?.length ? { inputWarnings: usage.inputWarnings } : {},
|
|
2763
|
+
...usage.inputWarningsBySource?.length ? { inputWarningsBySource: usage.inputWarningsBySource } : {},
|
|
2764
|
+
...usage.inputBlocked ? { inputBlocked: true } : {},
|
|
2765
|
+
...usage.outputValidationError ? { outputValidationError: usage.outputValidationError } : {},
|
|
2766
|
+
...usage.unvalidatedOutput ? { unvalidatedOutput: usage.unvalidatedOutput } : {},
|
|
2767
|
+
...usage.strictStatus ? { strictStatus: usage.strictStatus } : {},
|
|
2768
|
+
...usage.strictRefusalReasons?.length ? { strictRefusalReasons: usage.strictRefusalReasons } : {},
|
|
2769
|
+
...usage.providerMs !== void 0 ? { providerMs: usage.providerMs } : {},
|
|
2770
|
+
...usage.validateMs !== void 0 ? { validateMs: usage.validateMs } : {},
|
|
2771
|
+
...usage.wallClockMs !== void 0 ? { wallClockMs: usage.wallClockMs } : {}
|
|
2772
|
+
});
|
|
2773
|
+
}
|
|
2774
|
+
/**
|
|
2775
|
+
* Get aggregated summary of all AI calls
|
|
2776
|
+
*/
|
|
2777
|
+
getSummary() {
|
|
2778
|
+
const totalInputTokens = this.calls.reduce((sum, c) => sum + c.inputTokens, 0);
|
|
2779
|
+
const totalOutputTokens = this.calls.reduce((sum, c) => sum + c.outputTokens, 0);
|
|
2780
|
+
const totalCostUsd = this.calls.reduce((sum, c) => sum + c.costUsd, 0);
|
|
2781
|
+
return {
|
|
2782
|
+
model: this.model,
|
|
2783
|
+
totalInputTokens,
|
|
2784
|
+
totalOutputTokens,
|
|
2785
|
+
totalTokens: totalInputTokens + totalOutputTokens,
|
|
2786
|
+
totalCostUsd,
|
|
2787
|
+
callCount: this.calls.length,
|
|
2788
|
+
calls: this.calls
|
|
2789
|
+
};
|
|
2790
|
+
}
|
|
2791
|
+
/**
|
|
2792
|
+
* Check if any usage has been recorded
|
|
2793
|
+
*/
|
|
2794
|
+
hasUsage() {
|
|
2795
|
+
return this.calls.length > 0;
|
|
2796
|
+
}
|
|
2797
|
+
};
|
|
2592
2798
|
var RETRYABLE_CODES = /* @__PURE__ */ new Set([
|
|
2593
2799
|
"rate_limit_exceeded",
|
|
2594
2800
|
"network_error",
|
|
@@ -2756,11 +2962,14 @@ var platform = {
|
|
|
2756
2962
|
|
|
2757
2963
|
// src/worker/llm-adapter.ts
|
|
2758
2964
|
var PostMessageLLMAdapter = class {
|
|
2759
|
-
constructor(provider, model) {
|
|
2965
|
+
constructor(provider, model, aiUsageCollector, callType) {
|
|
2760
2966
|
this.provider = provider;
|
|
2761
2967
|
this.model = model;
|
|
2968
|
+
this.aiUsageCollector = aiUsageCollector;
|
|
2969
|
+
this.callType = callType;
|
|
2762
2970
|
}
|
|
2763
2971
|
async generate(request) {
|
|
2972
|
+
const startedAt = Date.now();
|
|
2764
2973
|
const { result, usage } = await platform.callWithUsage({
|
|
2765
2974
|
tool: "llm",
|
|
2766
2975
|
method: "generate",
|
|
@@ -2777,6 +2986,22 @@ var PostMessageLLMAdapter = class {
|
|
|
2777
2986
|
maxOutputTokens: request.maxOutputTokens
|
|
2778
2987
|
}
|
|
2779
2988
|
});
|
|
2989
|
+
if (usage && this.aiUsageCollector) {
|
|
2990
|
+
this.aiUsageCollector.record(
|
|
2991
|
+
{
|
|
2992
|
+
model: usage.model ?? this.model,
|
|
2993
|
+
inputTokens: usage.inputTokens,
|
|
2994
|
+
outputTokens: usage.outputTokens,
|
|
2995
|
+
latencyMs: Date.now() - startedAt,
|
|
2996
|
+
...usage.cost !== void 0 && { cost: usage.cost },
|
|
2997
|
+
...usage.cacheReadInputTokens !== void 0 && { cacheReadInputTokens: usage.cacheReadInputTokens },
|
|
2998
|
+
...usage.cacheCreationInputTokens !== void 0 && {
|
|
2999
|
+
cacheCreationInputTokens: usage.cacheCreationInputTokens
|
|
3000
|
+
}
|
|
3001
|
+
},
|
|
3002
|
+
this.callType
|
|
3003
|
+
);
|
|
3004
|
+
}
|
|
2780
3005
|
return {
|
|
2781
3006
|
output: result,
|
|
2782
3007
|
...usage && {
|
|
@@ -2791,10 +3016,11 @@ var PostMessageLLMAdapter = class {
|
|
|
2791
3016
|
}
|
|
2792
3017
|
};
|
|
2793
3018
|
function createPostMessageAdapterFactory() {
|
|
2794
|
-
return (config) => new PostMessageLLMAdapter(config.provider, config.model);
|
|
3019
|
+
return (config, aiUsageCollector, callType) => new PostMessageLLMAdapter(config.provider, config.model, aiUsageCollector, callType);
|
|
2795
3020
|
}
|
|
3021
|
+
var FULL_TOKEN_HEX_LENGTH = 32;
|
|
2796
3022
|
function generateHmacToken(secret, data) {
|
|
2797
|
-
return createHmac("sha256", secret).update(data.toLowerCase().trim()).digest("hex").slice(0,
|
|
3023
|
+
return createHmac("sha256", secret).update(data.toLowerCase().trim()).digest("hex").slice(0, FULL_TOKEN_HEX_LENGTH);
|
|
2798
3024
|
}
|
|
2799
3025
|
function classifyPlatformToolError(err, options = {}) {
|
|
2800
3026
|
const {
|
|
@@ -4244,6 +4470,12 @@ function buildWorkerExecutionContext(params) {
|
|
|
4244
4470
|
organizationId: params.organizationId,
|
|
4245
4471
|
organizationName: params.organizationName,
|
|
4246
4472
|
resourceId: params.resourceId,
|
|
4473
|
+
// Worker-side usage accounting. The parent keeps its own collector and remains the source of the
|
|
4474
|
+
// persisted `execution_metrics` row; this one exists so the agent loop — which runs in this
|
|
4475
|
+
// thread — can read what the turn has spent so far and stop at a declared ceiling. Without it
|
|
4476
|
+
// `Agent.iterate` read `undefined` on every deployed execution and treated every agent as
|
|
4477
|
+
// uncapped, which a local dogfood run on 2026-09-02 is what caught.
|
|
4478
|
+
aiUsageCollector: new AIUsageCollector(),
|
|
4247
4479
|
sessionId: params.sessionId,
|
|
4248
4480
|
sessionTurnNumber: params.sessionTurnNumber,
|
|
4249
4481
|
conversationHistory: params.conversationHistory,
|
|
@@ -4450,19 +4682,28 @@ function startWorker(org) {
|
|
|
4450
4682
|
const durationMs = Date.now() - startTime;
|
|
4451
4683
|
const stopReason = agentInstance.getStopReason();
|
|
4452
4684
|
const budgetExhausted = stopReason === "budget_exhausted";
|
|
4685
|
+
const spendExhausted = stopReason === "spend_exhausted";
|
|
4686
|
+
const stoppedShort = budgetExhausted || spendExhausted;
|
|
4453
4687
|
if (budgetExhausted) {
|
|
4454
4688
|
console.warn(`[SDK-WORKER] Agent '${resourceId}' exhausted its iteration budget (${durationMs}ms)`);
|
|
4689
|
+
} else if (spendExhausted) {
|
|
4690
|
+
console.warn(`[SDK-WORKER] Agent '${resourceId}' reached its cost/token ceiling (${durationMs}ms)`);
|
|
4455
4691
|
} else {
|
|
4456
4692
|
console.log(`[SDK-WORKER] Agent '${resourceId}' completed (${durationMs}ms)`);
|
|
4457
4693
|
}
|
|
4458
4694
|
parentPort.postMessage({
|
|
4459
4695
|
type: "result",
|
|
4460
|
-
status:
|
|
4696
|
+
status: stoppedShort ? "failed" : "completed",
|
|
4461
4697
|
...budgetExhausted ? {
|
|
4462
4698
|
error: "Agent stopped without completing: iteration budget exhausted. Any output below was synthesized from partial work.",
|
|
4463
4699
|
errorName: "AgentBudgetExhaustedError",
|
|
4464
4700
|
errorCode: "agent_budget_exhausted"
|
|
4465
4701
|
} : {},
|
|
4702
|
+
...spendExhausted ? {
|
|
4703
|
+
error: "Agent stopped without completing: cost or token ceiling reached. Any output below was synthesized from partial work.",
|
|
4704
|
+
errorName: "AgentSpendExhaustedError",
|
|
4705
|
+
errorCode: "agent_spend_exhausted"
|
|
4706
|
+
} : {},
|
|
4466
4707
|
output,
|
|
4467
4708
|
stopReason,
|
|
4468
4709
|
// Whether the agent emitted an assistant message this turn. `Agent` has tracked this for
|
|
@@ -814,14 +814,25 @@ var SystemEntrySchema = z.object({
|
|
|
814
814
|
* position-derived paths. Both still exist on this schema for backward compat.
|
|
815
815
|
*/
|
|
816
816
|
systems: z.lazy(() => z.record(z.string().trim().min(1).max(100), SystemEntrySchema)).optional(),
|
|
817
|
-
/**
|
|
817
|
+
/**
|
|
818
|
+
* @deprecated Use systems. Accepted as a compatibility alias during the ontology bridge.
|
|
819
|
+
*
|
|
820
|
+
* Accepted on INPUT only. Parsing used to mirror `systems` into this key so that
|
|
821
|
+
* either spelling could be read off a parsed model, which meant every parsed System
|
|
822
|
+
* carried the same children twice. Readers that walked both keys then visited each
|
|
823
|
+
* nested System twice -- `getOrgOsRouteContractSystems` reported 11 checked paths
|
|
824
|
+
* against command-center's 7 real Systems and would have raised every nested-System
|
|
825
|
+
* failure as two failures -- and readers that walked this key alone looked correct
|
|
826
|
+
* while depending entirely on the mirror. Both defects were live. The mirror is gone:
|
|
827
|
+
* a parsed model now carries children under whichever key the author wrote, so read
|
|
828
|
+
* `system.systems ?? system.subsystems` (helpers.ts, validation.ts, ontology.ts,
|
|
829
|
+
* selectDeclaredSystems.ts, validateManifests.ts and SystemOpsView.tsx all do).
|
|
830
|
+
*/
|
|
818
831
|
subsystems: z.lazy(() => z.record(z.string().trim().min(1).max(100), SystemEntrySchema)).optional()
|
|
819
832
|
}).strict().refine((system) => system.label !== void 0 || system.title !== void 0, {
|
|
820
833
|
path: ["label"],
|
|
821
834
|
message: "System must provide label or title"
|
|
822
|
-
})
|
|
823
|
-
(system) => system.systems !== void 0 && system.subsystems === void 0 ? { ...system, subsystems: system.systems } : system
|
|
824
|
-
);
|
|
835
|
+
});
|
|
825
836
|
z.record(z.string(), SystemEntrySchema).refine((record) => Object.entries(record).every(([key, entry]) => entry.id === key), {
|
|
826
837
|
message: "Each system entry id must match its map key"
|
|
827
838
|
}).default({});
|
|
@@ -1992,6 +2003,8 @@ var AnthropicConfigSchema = z.discriminatedUnion("model", [
|
|
|
1992
2003
|
AnthropicClaude5ConfigSchema,
|
|
1993
2004
|
AnthropicStandardConfigSchema
|
|
1994
2005
|
]);
|
|
2006
|
+
var ANTHROPIC_CACHE_READ_RATE_MULTIPLIER = 0.1;
|
|
2007
|
+
var OPENAI_CACHE_READ_RATE_MULTIPLIER = 0.5;
|
|
1995
2008
|
var GPT56_CACHE_READ_RATE_MULTIPLIER = 0.1;
|
|
1996
2009
|
var CACHE_CREATION_RATE_MULTIPLIER = 1.25;
|
|
1997
2010
|
var MODEL_INFO = {
|
|
@@ -2188,6 +2201,30 @@ function getModelInfo(model) {
|
|
|
2188
2201
|
}
|
|
2189
2202
|
return void 0;
|
|
2190
2203
|
}
|
|
2204
|
+
function cacheReadRateMultiplier(model, info) {
|
|
2205
|
+
if (info?.cacheReadRateMultiplier !== void 0) return info.cacheReadRateMultiplier;
|
|
2206
|
+
return model.startsWith("claude-") ? ANTHROPIC_CACHE_READ_RATE_MULTIPLIER : OPENAI_CACHE_READ_RATE_MULTIPLIER;
|
|
2207
|
+
}
|
|
2208
|
+
function cacheCreationRateMultiplier(info) {
|
|
2209
|
+
return info?.cacheCreationRateMultiplier ?? CACHE_CREATION_RATE_MULTIPLIER;
|
|
2210
|
+
}
|
|
2211
|
+
function calculateCost(model, inputTokens, outputTokens, cacheReadInputTokens = 0, cacheCreationInputTokens = 0) {
|
|
2212
|
+
const info = getModelInfo(model);
|
|
2213
|
+
if (!info) {
|
|
2214
|
+
console.warn(
|
|
2215
|
+
`[CostCalculator] Unknown model '${model}' - cannot calculate cost. Available models:`,
|
|
2216
|
+
Object.keys(MODEL_INFO)
|
|
2217
|
+
);
|
|
2218
|
+
return 0;
|
|
2219
|
+
}
|
|
2220
|
+
const baseInputRate = info.inputCostPer1M / 100;
|
|
2221
|
+
const inputCostUsd = inputTokens / 1e6 * baseInputRate;
|
|
2222
|
+
const cacheReadCostUsd = cacheReadInputTokens / 1e6 * baseInputRate * cacheReadRateMultiplier(model, info);
|
|
2223
|
+
const cacheCreationCostUsd = cacheCreationInputTokens / 1e6 * baseInputRate * cacheCreationRateMultiplier(info);
|
|
2224
|
+
const outputCostUsd = outputTokens / 1e6 * (info.outputCostPer1M / 100);
|
|
2225
|
+
const totalCostUsd = inputCostUsd + cacheReadCostUsd + cacheCreationCostUsd + outputCostUsd;
|
|
2226
|
+
return totalCostUsd;
|
|
2227
|
+
}
|
|
2191
2228
|
function validateModelConfig(config) {
|
|
2192
2229
|
const model = config.model;
|
|
2193
2230
|
if (!model) {
|
|
@@ -2615,6 +2652,18 @@ var WorkflowTimeoutError = class extends ExecutionError2 {
|
|
|
2615
2652
|
return false;
|
|
2616
2653
|
}
|
|
2617
2654
|
};
|
|
2655
|
+
var WorkflowStepTimeoutError = class extends ExecutionError2 {
|
|
2656
|
+
type = "workflow_step_timeout_error";
|
|
2657
|
+
severity = "critical";
|
|
2658
|
+
category = "workflow";
|
|
2659
|
+
constructor(message, context) {
|
|
2660
|
+
super(message, context);
|
|
2661
|
+
}
|
|
2662
|
+
/** The run still had budget; a slow dependency is exactly the case a retry exists for. */
|
|
2663
|
+
isRetryable() {
|
|
2664
|
+
return true;
|
|
2665
|
+
}
|
|
2666
|
+
};
|
|
2618
2667
|
var WorkflowStalledError = class extends ExecutionError2 {
|
|
2619
2668
|
type = "workflow_stalled_error";
|
|
2620
2669
|
severity = "critical";
|
|
@@ -5962,4 +6011,4 @@ function defineWorkflowConfig(resourceId, descriptors, actionRegistry = []) {
|
|
|
5962
6011
|
}
|
|
5963
6012
|
var ListBuilderStageKeySchema = z.string().min(1);
|
|
5964
6013
|
|
|
5965
|
-
export { ActivityEventSchema, BuildPlanSnapshotStepSchema, ContractRefResolutionError, CrmStageKeySchema, CrmStateKeySchema, EmailSchema, ExecutionError, ExecutionError2, LLMResponseParseError, ListBuilderStageKeySchema, ProcessingStageStatusSchema, ProspectingBuildTemplateSchema, RegistryValidationError, ResourceRegistry, StepType, ToolingError, WorkflowCancellationError, WorkflowStalledError, WorkflowStepError, WorkflowTimeoutError, allSettledWithConcurrency, bindResourceDescriptor, buildIterationResponseSchema, compileBusinessOntologyValidationIndex, concurrentPool, createLeadGenStageValidators, defineContract, defineResource, defineResourceOntology, defineResources, defineSingleStepWorkflow, defineTopology, defineTopologyRelationship, defineWorkflowConfig, deriveActions, detectCycle, determineNextStep, diagnosticOutput, errorToString, estimateTokens, getErrorDetails, integrationInput, isBuiltInReadinessProfile, isZodType, logExecutionPath, logStepFailure, logStepStart, logStepSuccess, logWorkflowFailure, logWorkflowStart, logWorkflowSuccess, lookupReadinessProfile, parseTopologyNodeRef, registerReadinessProfile, resolveContractRef, runDiagnostic, splitName, topologyRef, topologyRelationship, truncationCharBudget, validateDeclaredSystemInterfaceReadiness, validateDeploymentSpec, validateEntryPoint, validateRelationships, validateResourceGovernance, validateStepReferences, validateTerminalOutput, validateTerminalSteps };
|
|
6014
|
+
export { ActivityEventSchema, BuildPlanSnapshotStepSchema, ContractRefResolutionError, CrmStageKeySchema, CrmStateKeySchema, EmailSchema, ExecutionError, ExecutionError2, LLMResponseParseError, ListBuilderStageKeySchema, ProcessingStageStatusSchema, ProspectingBuildTemplateSchema, RegistryValidationError, ResourceRegistry, StepType, ToolingError, WorkflowCancellationError, WorkflowStalledError, WorkflowStepError, WorkflowStepTimeoutError, WorkflowTimeoutError, allSettledWithConcurrency, bindResourceDescriptor, buildIterationResponseSchema, calculateCost, compileBusinessOntologyValidationIndex, concurrentPool, createLeadGenStageValidators, defineContract, defineResource, defineResourceOntology, defineResources, defineSingleStepWorkflow, defineTopology, defineTopologyRelationship, defineWorkflowConfig, deriveActions, detectCycle, determineNextStep, diagnosticOutput, errorToString, estimateTokens, getErrorDetails, integrationInput, isBuiltInReadinessProfile, isZodType, logExecutionPath, logStepFailure, logStepStart, logStepSuccess, logWorkflowFailure, logWorkflowStart, logWorkflowSuccess, lookupReadinessProfile, parseTopologyNodeRef, registerReadinessProfile, resolveContractRef, runDiagnostic, splitName, topologyRef, topologyRelationship, truncationCharBudget, validateDeclaredSystemInterfaceReadiness, validateDeploymentSpec, validateEntryPoint, validateRelationships, validateResourceGovernance, validateStepReferences, validateTerminalOutput, validateTerminalSteps };
|