@sapiom/agent-core
Advanced tools
| { | ||
| "executionId": "exec_0001", | ||
| "id": "step_0002", | ||
| "stepName": "render", | ||
| "stepOrder": 1, | ||
| "attempt": 2, | ||
| "status": "failed", | ||
| "input": { | ||
| "items": 12 | ||
| }, | ||
| "output": null, | ||
| "error": { | ||
| "message": "template not found", | ||
| "trace": { | ||
| "frames": [ | ||
| { | ||
| "function": "render", | ||
| "file": "src/steps/render.ts", | ||
| "line": 12, | ||
| "column": 11 | ||
| }, | ||
| { | ||
| "function": "run", | ||
| "file": "src/engine/runner.ts", | ||
| "line": 88, | ||
| "column": 5 | ||
| } | ||
| ], | ||
| "sourceMapped": true, | ||
| "raw": "Error: template not found\n at render (dist/steps/render.mjs:8:9)" | ||
| }, | ||
| "traceUnavailableReason": null | ||
| }, | ||
| "logs": [ | ||
| { | ||
| "ts": "2026-01-01T00:00:46.000Z", | ||
| "level": "error", | ||
| "msg": "template not found" | ||
| } | ||
| ], | ||
| "startedAt": "2026-01-01T00:00:45.000Z", | ||
| "finishedAt": "2026-01-01T00:02:30.000Z" | ||
| } |
| { | ||
| "executionId": "exec_0001", | ||
| "id": "step_0002", | ||
| "stepName": "render", | ||
| "stepOrder": 1, | ||
| "attempt": 2, | ||
| "status": "failed", | ||
| "input": { | ||
| "items": 12 | ||
| }, | ||
| "output": null, | ||
| "error": { | ||
| "message": "template not found", | ||
| "trace": { | ||
| "frames": [ | ||
| { | ||
| "function": "render", | ||
| "file": "src/steps/render.ts", | ||
| "line": 12, | ||
| "column": 11 | ||
| }, | ||
| { | ||
| "function": "run", | ||
| "file": "src/engine/runner.ts", | ||
| "line": 88, | ||
| "column": 5 | ||
| } | ||
| ], | ||
| "sourceMapped": true, | ||
| "raw": "Error: template not found\n at render (dist/steps/render.mjs:8:9)" | ||
| }, | ||
| "traceUnavailableReason": null | ||
| }, | ||
| "logs": [ | ||
| { | ||
| "ts": "2026-01-01T00:00:46.000Z", | ||
| "level": "error", | ||
| "msg": "template not found" | ||
| } | ||
| ], | ||
| "startedAt": "2026-01-01T00:00:45.000Z", | ||
| "finishedAt": "2026-01-01T00:02:30.000Z" | ||
| } |
+26
-0
| # @sapiom/orchestration-core | ||
| ## 0.12.0 | ||
| ### Minor Changes | ||
| - 00b8814: Adds `inspectStep(opts, client)` — fetches one step attempt's full-fidelity `input`/`output`/`error`/`logs` (`GET /executions/:id/steps/:stepId/io`), at a higher size cap than `inspect()`'s own `steps[]` bounds its aggregate read to. Reach for it when a step's fields on the execution projection look truncated. | ||
| - New exported `inspectStep`, `InspectStepOptions`, `StepIoDetail`, and `decodeStepIoDetail` (the tolerant decoder, mirroring `decodeExecutionProjection`'s degradation posture). | ||
| - `StepProjection` gains an optional `id` field (the step-attempt row id) — previously decoded from the wire but silently dropped; needed to call `inspectStep` from an `inspect()` read. `null` on a read from a server that doesn't report it — existing consumers are unaffected. | ||
| ### Patch Changes | ||
| - 5a8eeea: `sapiom-agent-authoring` skill: teaches the LLM call-surface rule from step | ||
| code (`llm.run` one-shot vs `models.run` platform-driven loop vs `agents.run` | ||
| deployed-agent dispatch, with a worked example against the "reply with only | ||
| JSON" + string-parsing mistake) and settles the platform's naming | ||
| conventions (the overloaded "agent"/"run"/"task"/"session"/"dispatch" terms, | ||
| and "label" as the author-facing term for a `model:` value). Synced across | ||
| the canonical source, both scaffold templates, and the Claude Code plugin | ||
| copy. `@sapiom/tools`: corrected stale `agent.run`/`agent.coding` naming in | ||
| `models/index.ts`'s doc comments — the actual exported namespace is | ||
| `models`. | ||
| - Updated dependencies [5a8eeea] | ||
| - Updated dependencies [5a8eeea] | ||
| - @sapiom/tools@0.30.0 | ||
| - @sapiom/agent@0.10.1 | ||
| ## 0.11.4 | ||
@@ -4,0 +30,0 @@ |
@@ -1,4 +0,5 @@ | ||
| import type { CostNode, ExecutionProjection, ExecutionRef } from "./types.js"; | ||
| import type { CostNode, ExecutionProjection, ExecutionRef, StepIoDetail } from "./types.js"; | ||
| export declare function decodeCostNode(raw: unknown): CostNode | null; | ||
| export declare function decodeExecutionRef(raw: unknown): ExecutionRef; | ||
| export declare function decodeStepIoDetail(raw: unknown): StepIoDetail; | ||
| export declare function decodeExecutionProjection(raw: unknown): ExecutionProjection; |
+19
-0
@@ -5,2 +5,3 @@ "use strict"; | ||
| exports.decodeExecutionRef = decodeExecutionRef; | ||
| exports.decodeStepIoDetail = decodeStepIoDetail; | ||
| exports.decodeExecutionProjection = decodeExecutionProjection; | ||
@@ -118,2 +119,3 @@ function isRecord(v) { | ||
| return { | ||
| id: strOrNull(r.id), | ||
| stepName: str("", r.stepName), | ||
@@ -137,2 +139,19 @@ stepOrder: numOr(0, r.stepOrder), | ||
| } | ||
| function decodeStepIoDetail(raw) { | ||
| const r = rec(raw); | ||
| return { | ||
| executionId: str("", r.executionId), | ||
| id: str("", r.id), | ||
| stepName: str("", r.stepName), | ||
| stepOrder: numOr(0, r.stepOrder), | ||
| attempt: numOr(0, r.attempt), | ||
| status: str("", r.status), | ||
| input: r.input ?? null, | ||
| output: r.output ?? null, | ||
| error: decodeStepError(r.error), | ||
| logs: r.logs ?? null, | ||
| startedAt: strOrNull(r.startedAt), | ||
| finishedAt: strOrNull(r.finishedAt), | ||
| }; | ||
| } | ||
| function decodeExecutionProjection(raw) { | ||
@@ -139,0 +158,0 @@ const r = rec(raw); |
@@ -23,7 +23,7 @@ export { AgentOperationError } from "./errors.js"; | ||
| export type { RunOptions, RunResult } from "./run.js"; | ||
| export type { ExecutionProjection, StepProjection, CostNode, SettleState, ExecutionRef, DispatchRef, StepError, StepErrorTrace, StepErrorFrame, StepEvent, SseEvent, SseEventType, } from "./types.js"; | ||
| export type { ExecutionProjection, StepProjection, StepIoDetail, CostNode, SettleState, ExecutionRef, DispatchRef, StepError, StepErrorTrace, StepErrorFrame, StepEvent, SseEvent, SseEventType, } from "./types.js"; | ||
| export { SSE_EVENT_TYPES } from "./types.js"; | ||
| export { decodeExecutionProjection } from "./decode.js"; | ||
| export { inspect, listExecutions, inspectBuild, waitForExecution, isExecutionTerminal, } from "./inspect.js"; | ||
| export type { InspectOptions, InspectBuildOptions, InspectBuildResult, BuildDetail, WaitForExecutionOptions, WaitForExecutionResult, WaitStopReason, } from "./inspect.js"; | ||
| export { decodeExecutionProjection, decodeStepIoDetail } from "./decode.js"; | ||
| export { inspect, inspectStep, listExecutions, inspectBuild, waitForExecution, isExecutionTerminal, } from "./inspect.js"; | ||
| export type { InspectOptions, InspectStepOptions, InspectBuildOptions, InspectBuildResult, BuildDetail, WaitForExecutionOptions, WaitForExecutionResult, WaitStopReason, } from "./inspect.js"; | ||
| export { watchExecution, parseSseFrame, parseSseEvent } from "./watch.js"; | ||
@@ -30,0 +30,0 @@ export type { WatchExecutionOptions } from "./watch.js"; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.loadDefinition = exports.STUBS_FILE = exports.runLocalFromDir = exports.runLocal = exports.STUB_FILE_VERSION = exports.parseStubFile = exports.redactCredentials = exports.cloneRepo = exports.pushHead = exports.assertDeployable = exports.previewCron = exports.cancelSchedule = exports.getSchedule = exports.listSchedules = exports.createSchedule = exports.sendFeedback = exports.parseSignalPayload = exports.signal = exports.parseSseEvent = exports.parseSseFrame = exports.watchExecution = exports.isExecutionTerminal = exports.waitForExecution = exports.inspectBuild = exports.listExecutions = exports.inspect = exports.decodeExecutionProjection = exports.SSE_EVENT_TYPES = exports.parseJsonInput = exports.run = exports.deploy = exports.clone = exports.link = exports.bundleForDeploy = exports.check = exports.describeBundleFailure = exports.installProjectDependencies = exports.DEFAULT_TEMPLATE = exports.listTemplates = exports.resolveTemplate = exports.resolveVersions = exports.scaffold = exports.CONFIG_FILE = exports.writeConfig = exports.requireConfig = exports.readConfig = exports.DEFAULT_WORKFLOWS_HOST = exports.createClient = exports.GatewayClient = exports.AgentOperationError = void 0; | ||
| exports.LocalStubDispatcher = void 0; | ||
| exports.runLocalFromDir = exports.runLocal = exports.STUB_FILE_VERSION = exports.parseStubFile = exports.redactCredentials = exports.cloneRepo = exports.pushHead = exports.assertDeployable = exports.previewCron = exports.cancelSchedule = exports.getSchedule = exports.listSchedules = exports.createSchedule = exports.sendFeedback = exports.parseSignalPayload = exports.signal = exports.parseSseEvent = exports.parseSseFrame = exports.watchExecution = exports.isExecutionTerminal = exports.waitForExecution = exports.inspectBuild = exports.listExecutions = exports.inspectStep = exports.inspect = exports.decodeStepIoDetail = exports.decodeExecutionProjection = exports.SSE_EVENT_TYPES = exports.parseJsonInput = exports.run = exports.deploy = exports.clone = exports.link = exports.bundleForDeploy = exports.check = exports.describeBundleFailure = exports.installProjectDependencies = exports.DEFAULT_TEMPLATE = exports.listTemplates = exports.resolveTemplate = exports.resolveVersions = exports.scaffold = exports.CONFIG_FILE = exports.writeConfig = exports.requireConfig = exports.readConfig = exports.DEFAULT_WORKFLOWS_HOST = exports.createClient = exports.GatewayClient = exports.AgentOperationError = void 0; | ||
| exports.LocalStubDispatcher = exports.loadDefinition = exports.STUBS_FILE = void 0; | ||
| var errors_js_1 = require("./errors.js"); | ||
@@ -43,4 +43,6 @@ Object.defineProperty(exports, "AgentOperationError", { enumerable: true, get: function () { return errors_js_1.AgentOperationError; } }); | ||
| Object.defineProperty(exports, "decodeExecutionProjection", { enumerable: true, get: function () { return decode_js_1.decodeExecutionProjection; } }); | ||
| Object.defineProperty(exports, "decodeStepIoDetail", { enumerable: true, get: function () { return decode_js_1.decodeStepIoDetail; } }); | ||
| var inspect_js_1 = require("./inspect.js"); | ||
| Object.defineProperty(exports, "inspect", { enumerable: true, get: function () { return inspect_js_1.inspect; } }); | ||
| Object.defineProperty(exports, "inspectStep", { enumerable: true, get: function () { return inspect_js_1.inspectStep; } }); | ||
| Object.defineProperty(exports, "listExecutions", { enumerable: true, get: function () { return inspect_js_1.listExecutions; } }); | ||
@@ -47,0 +49,0 @@ Object.defineProperty(exports, "inspectBuild", { enumerable: true, get: function () { return inspect_js_1.inspectBuild; } }); |
| import { GatewayClient } from "./client.js"; | ||
| import type { ExecutionProjection, ExecutionRef, SseEvent } from "./types.js"; | ||
| import type { ExecutionProjection, ExecutionRef, SseEvent, StepIoDetail } from "./types.js"; | ||
| export interface BuildDetail { | ||
@@ -12,2 +12,7 @@ id?: string; | ||
| export declare function inspect(opts: InspectOptions, client: GatewayClient): Promise<ExecutionProjection>; | ||
| export interface InspectStepOptions { | ||
| executionId: string; | ||
| stepExecutionId: string; | ||
| } | ||
| export declare function inspectStep(opts: InspectStepOptions, client: GatewayClient): Promise<StepIoDetail>; | ||
| export declare function isExecutionTerminal(status: string): boolean; | ||
@@ -14,0 +19,0 @@ export type WaitStopReason = "terminal" | "needs-signal" | "timeout"; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.inspect = inspect; | ||
| exports.inspectStep = inspectStep; | ||
| exports.isExecutionTerminal = isExecutionTerminal; | ||
@@ -14,2 +15,6 @@ exports.waitForExecution = waitForExecution; | ||
| } | ||
| async function inspectStep(opts, client) { | ||
| const raw = await client.get(`/executions/${opts.executionId}/steps/${opts.stepExecutionId}/io`); | ||
| return (0, decode_js_1.decodeStepIoDetail)(raw); | ||
| } | ||
| const TERMINAL_STATUSES = new Set([ | ||
@@ -16,0 +21,0 @@ "completed", |
+15
-0
@@ -51,2 +51,3 @@ export type SettleState = "pending" | "settling" | "final"; | ||
| export interface StepProjection { | ||
| id?: string | null; | ||
| stepName: string; | ||
@@ -101,1 +102,15 @@ stepOrder: number; | ||
| } | ||
| export interface StepIoDetail { | ||
| executionId: string; | ||
| id: string; | ||
| stepName: string; | ||
| stepOrder: number; | ||
| attempt: number; | ||
| status: string; | ||
| input: unknown; | ||
| output: unknown; | ||
| error: StepError | null; | ||
| logs: unknown; | ||
| startedAt: string | null; | ||
| finishedAt: string | null; | ||
| } |
| export declare const VERSION_FALLBACK: { | ||
| readonly agent: "0.10.0"; | ||
| readonly tools: "0.29.0"; | ||
| readonly agent: "0.10.1"; | ||
| readonly tools: "0.30.0"; | ||
| }; |
@@ -5,4 +5,4 @@ "use strict"; | ||
| exports.VERSION_FALLBACK = { | ||
| agent: "0.10.0", | ||
| tools: "0.29.0", | ||
| agent: "0.10.1", | ||
| tools: "0.30.0", | ||
| }; |
@@ -1,4 +0,5 @@ | ||
| import type { CostNode, ExecutionProjection, ExecutionRef } from "./types.js"; | ||
| import type { CostNode, ExecutionProjection, ExecutionRef, StepIoDetail } from "./types.js"; | ||
| export declare function decodeCostNode(raw: unknown): CostNode | null; | ||
| export declare function decodeExecutionRef(raw: unknown): ExecutionRef; | ||
| export declare function decodeStepIoDetail(raw: unknown): StepIoDetail; | ||
| export declare function decodeExecutionProjection(raw: unknown): ExecutionProjection; |
+18
-0
@@ -112,2 +112,3 @@ function isRecord(v) { | ||
| return { | ||
| id: strOrNull(r.id), | ||
| stepName: str("", r.stepName), | ||
@@ -131,2 +132,19 @@ stepOrder: numOr(0, r.stepOrder), | ||
| } | ||
| export function decodeStepIoDetail(raw) { | ||
| const r = rec(raw); | ||
| return { | ||
| executionId: str("", r.executionId), | ||
| id: str("", r.id), | ||
| stepName: str("", r.stepName), | ||
| stepOrder: numOr(0, r.stepOrder), | ||
| attempt: numOr(0, r.attempt), | ||
| status: str("", r.status), | ||
| input: r.input ?? null, | ||
| output: r.output ?? null, | ||
| error: decodeStepError(r.error), | ||
| logs: r.logs ?? null, | ||
| startedAt: strOrNull(r.startedAt), | ||
| finishedAt: strOrNull(r.finishedAt), | ||
| }; | ||
| } | ||
| export function decodeExecutionProjection(raw) { | ||
@@ -133,0 +151,0 @@ const r = rec(raw); |
@@ -23,7 +23,7 @@ export { AgentOperationError } from "./errors.js"; | ||
| export type { RunOptions, RunResult } from "./run.js"; | ||
| export type { ExecutionProjection, StepProjection, CostNode, SettleState, ExecutionRef, DispatchRef, StepError, StepErrorTrace, StepErrorFrame, StepEvent, SseEvent, SseEventType, } from "./types.js"; | ||
| export type { ExecutionProjection, StepProjection, StepIoDetail, CostNode, SettleState, ExecutionRef, DispatchRef, StepError, StepErrorTrace, StepErrorFrame, StepEvent, SseEvent, SseEventType, } from "./types.js"; | ||
| export { SSE_EVENT_TYPES } from "./types.js"; | ||
| export { decodeExecutionProjection } from "./decode.js"; | ||
| export { inspect, listExecutions, inspectBuild, waitForExecution, isExecutionTerminal, } from "./inspect.js"; | ||
| export type { InspectOptions, InspectBuildOptions, InspectBuildResult, BuildDetail, WaitForExecutionOptions, WaitForExecutionResult, WaitStopReason, } from "./inspect.js"; | ||
| export { decodeExecutionProjection, decodeStepIoDetail } from "./decode.js"; | ||
| export { inspect, inspectStep, listExecutions, inspectBuild, waitForExecution, isExecutionTerminal, } from "./inspect.js"; | ||
| export type { InspectOptions, InspectStepOptions, InspectBuildOptions, InspectBuildResult, BuildDetail, WaitForExecutionOptions, WaitForExecutionResult, WaitStopReason, } from "./inspect.js"; | ||
| export { watchExecution, parseSseFrame, parseSseEvent } from "./watch.js"; | ||
@@ -30,0 +30,0 @@ export type { WatchExecutionOptions } from "./watch.js"; |
@@ -14,4 +14,4 @@ export { AgentOperationError } from "./errors.js"; | ||
| export { SSE_EVENT_TYPES } from "./types.js"; | ||
| export { decodeExecutionProjection } from "./decode.js"; | ||
| export { inspect, listExecutions, inspectBuild, waitForExecution, isExecutionTerminal, } from "./inspect.js"; | ||
| export { decodeExecutionProjection, decodeStepIoDetail } from "./decode.js"; | ||
| export { inspect, inspectStep, listExecutions, inspectBuild, waitForExecution, isExecutionTerminal, } from "./inspect.js"; | ||
| export { watchExecution, parseSseFrame, parseSseEvent } from "./watch.js"; | ||
@@ -18,0 +18,0 @@ export { signal, parseSignalPayload } from "./signal.js"; |
| import { GatewayClient } from "./client.js"; | ||
| import type { ExecutionProjection, ExecutionRef, SseEvent } from "./types.js"; | ||
| import type { ExecutionProjection, ExecutionRef, SseEvent, StepIoDetail } from "./types.js"; | ||
| export interface BuildDetail { | ||
@@ -12,2 +12,7 @@ id?: string; | ||
| export declare function inspect(opts: InspectOptions, client: GatewayClient): Promise<ExecutionProjection>; | ||
| export interface InspectStepOptions { | ||
| executionId: string; | ||
| stepExecutionId: string; | ||
| } | ||
| export declare function inspectStep(opts: InspectStepOptions, client: GatewayClient): Promise<StepIoDetail>; | ||
| export declare function isExecutionTerminal(status: string): boolean; | ||
@@ -14,0 +19,0 @@ export type WaitStopReason = "terminal" | "needs-signal" | "timeout"; |
@@ -1,2 +0,2 @@ | ||
| import { decodeExecutionProjection, decodeExecutionRef } from "./decode.js"; | ||
| import { decodeExecutionProjection, decodeExecutionRef, decodeStepIoDetail, } from "./decode.js"; | ||
| import { watchExecution } from "./watch.js"; | ||
@@ -7,2 +7,6 @@ export async function inspect(opts, client) { | ||
| } | ||
| export async function inspectStep(opts, client) { | ||
| const raw = await client.get(`/executions/${opts.executionId}/steps/${opts.stepExecutionId}/io`); | ||
| return decodeStepIoDetail(raw); | ||
| } | ||
| const TERMINAL_STATUSES = new Set([ | ||
@@ -9,0 +13,0 @@ "completed", |
+15
-0
@@ -51,2 +51,3 @@ export type SettleState = "pending" | "settling" | "final"; | ||
| export interface StepProjection { | ||
| id?: string | null; | ||
| stepName: string; | ||
@@ -101,1 +102,15 @@ stepOrder: number; | ||
| } | ||
| export interface StepIoDetail { | ||
| executionId: string; | ||
| id: string; | ||
| stepName: string; | ||
| stepOrder: number; | ||
| attempt: number; | ||
| status: string; | ||
| input: unknown; | ||
| output: unknown; | ||
| error: StepError | null; | ||
| logs: unknown; | ||
| startedAt: string | null; | ||
| finishedAt: string | null; | ||
| } |
| export declare const VERSION_FALLBACK: { | ||
| readonly agent: "0.10.0"; | ||
| readonly tools: "0.29.0"; | ||
| readonly agent: "0.10.1"; | ||
| readonly tools: "0.30.0"; | ||
| }; |
| export const VERSION_FALLBACK = { | ||
| agent: "0.10.0", | ||
| tools: "0.29.0", | ||
| agent: "0.10.1", | ||
| tools: "0.30.0", | ||
| }; |
+3
-3
| { | ||
| "name": "@sapiom/agent-core", | ||
| "version": "0.11.4", | ||
| "version": "0.12.0", | ||
| "description": "Pure, stateless core functions for scaffolding, validating, and operating Sapiom agents — shared by the CLI and MCP packages.", | ||
@@ -40,6 +40,6 @@ "license": "MIT", | ||
| "esbuild": "^0.28.1", | ||
| "@sapiom/agent": "^0.10.0", | ||
| "@sapiom/agent": "^0.10.1", | ||
| "@sapiom/agent-runtime": "^0.5.0", | ||
| "@sapiom/analytics-core": "^0.2.1", | ||
| "@sapiom/tools": "^0.29.0" | ||
| "@sapiom/tools": "^0.30.0" | ||
| }, | ||
@@ -46,0 +46,0 @@ "devDependencies": { |
@@ -308,2 +308,104 @@ --- | ||
| ## Calling LLMs from Steps | ||
| Three DIFFERENT capabilities call an LLM from step code — picking the wrong one for the | ||
| job is the most common mistake in authored agents: | ||
| | Capability | Use for | Never for | | ||
| | ------------------------ | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | | ||
| | `ctx.sapiom.llm.run` | ONE LLM call — summarize, extract, classify, one-shot generate | A multi-turn task, or anything needing its own tool-calling loop | | ||
| | `ctx.sapiom.models.run` | A platform-driven multi-turn reasoning + tool-calling loop (minutes, not seconds). `models.coding.run` for sandboxed coding tasks. | A one-shot completion — it will loop and overthink | | ||
| | `ctx.sapiom.agents.run` | Dispatching a DEPLOYED agent by slug — composing systems from small deployed agents | Anything that isn't itself a deployed agent | | ||
| **⚠️ The mistake to never repeat:** sending single-shot, fixed-shape intent through | ||
| `models.run`'s multi-turn loop instead of one `llm.run` call. The symptom: the run takes | ||
| far longer than the task needs, "overthinks" a trivial extraction, and — if the caller just | ||
| grabs the first content block hoping it's the answer — returns a `thinking` block instead. | ||
| ### Worked example: a trivial fixed-shape-JSON task | ||
| **Wrong** — one-shot intent sent through the multi-turn loop, then the answer string-parsed | ||
| out of free text: | ||
| ```typescript | ||
| // DON'T: models.run for a one-shot extraction — it loops and overthinks, and | ||
| // "reply with only JSON" is brittle (prose, invalid JSON, or a leading | ||
| // `thinking` block all break a naive `JSON.parse(content[0])`). | ||
| const run = await ctx.sapiom.models.run({ | ||
| prompt: `Reply with ONLY JSON: {"priority": "...", "category": "..."} for: ${input.text}`, | ||
| }); | ||
| const parsed = JSON.parse(run.output ?? "{}"); // brittle, and pays for a reasoning loop | ||
| ``` | ||
| **Right** — `llm.run` with `output` for the fixed shape, read back with `structuredOf` | ||
| (forced tool-use output has no `text` block — the result lives in `tool_use`, never | ||
| `content[0]`): | ||
| ```typescript | ||
| const response = await ctx.sapiom.llm.run({ | ||
| request: { | ||
| messages: [{ role: "user", content: `Classify this support ticket: ${input.text}` }], | ||
| max_tokens: 256, | ||
| }, | ||
| // No `model` — omit it and let the platform choose (recommended). To pin | ||
| // instead: `model: "smart"` (a label, never a raw provider model id). | ||
| output: { | ||
| name: "classify_ticket", | ||
| schema: { | ||
| type: "object", | ||
| properties: { | ||
| priority: { type: "string", enum: ["low", "medium", "high"] }, | ||
| category: { type: "string" }, | ||
| }, | ||
| required: ["priority", "category"], | ||
| }, | ||
| }, | ||
| }); | ||
| const { priority, category } = ctx.sapiom.llm.structuredOf<{ | ||
| priority: string; | ||
| category: string; | ||
| }>(response)!; | ||
| ``` | ||
| `output` automates the forced tool call and its `tool_choice` wiring; `structuredOf` reads | ||
| the result back out. For a **plain-text** reply instead, use | ||
| `ctx.sapiom.llm.textOf(response)` — it reads only the `type === 'text'` block, skipping a | ||
| `thinking` block that may precede it. | ||
| ### The label rule | ||
| **You never pick a model.** Every `model`/`label` field across `llm.*`, `models.run`, and | ||
| `models.coding.run` takes a **routing label** (e.g. `"smart"`) that the platform resolves | ||
| against its configured label set — never a raw provider model id (never honored, on any | ||
| surface). Omit it entirely to let the platform choose (the recommended default); pass | ||
| `"smart"` if you must pin. The result discloses what actually served, in the platform's own | ||
| vocabulary — `servedClass` (the billing size the label resolved to) and `lane` (the billing | ||
| lane it executed in) — never a model or provider id. | ||
| ### Debugging a run | ||
| Find the run in the dashboard's run detail view, find the suspicious step's row id, then | ||
| open the **Run Inspector** for that step's full-fidelity input/output/error/logs. | ||
| Full guide: [Choose a call surface](https://docs.sapiom.ai/guides/choose-a-call-surface). | ||
| ## Naming Conventions | ||
| Several words are overloaded across this platform. Know which meaning a given context | ||
| uses — conflating two costs you a wrong capability choice, not just a wrong word: | ||
| | Term | Meaning(s) on this platform | | ||
| | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | **agent** | (1) `@sapiom/agent` — this authoring framework (`defineAgent`, this skill). (2) A **deployed agent** — one project's compiled definition, dispatched by `ctx.sapiom.agents.run`/`launch` (addressed by its slug via `AgentRunSpec.definition`) — the customer-facing name is always "agent." (3) `ctx.sapiom.models.run`'s managed multi-turn loop — a managed loop the platform runs for you; you call it, you never author it. (4) A **Claude Code subagent** — an unrelated feature of the coding tool itself, not part of the Sapiom SDK. | | ||
| | **run** | (1) `ctx.sapiom.llm.run` — one synchronous LLM call. (2) `ctx.sapiom.models.run` / `agents.run` — `launch()` + `wait()`, blocking until terminal (vs. `launch()` alone, which returns a pausable handle). (3) An execution instance/row of any of the above — the thing you inspect/debug. (4) The dashboard's Run button / Local Run / Prod Run (Studio UI actions, not an API call). | | ||
| | **task** | `CodingRunSpec.task` — the coding agent's prompt-equivalent field. Deliberately not called `prompt`: it's handed to a sandboxed coding agent, not a bare LLM call. | | ||
| | **session** | (1) `ctx.sapiom.llm.createSession`/`callSession` — reserved LLM capacity accepting repeated drop-in calls until its TTL/budget ends it (replacing the deferred `submit`/`redeem` lane). (2) A Studio harness terminal session — unrelated, no LLM-capacity semantics. | | ||
| | **dispatch** | The structural contract (`DispatchHandle`) a long-running capability's `launch()` handle satisfies so a step can `pauseUntilSignal(handle, …)` and resume on completion. Every dispatched capability (coding, `models.run`, `agents.run`, more later) shares this ONE contract — "dispatch" always means this pattern, never anything else. | | ||
| | **label** | The author-facing term for a `model:`/`label:` *input* value (e.g. `"smart"`) — never a raw provider model id (never honored, on any surface). Not a contradiction that a result's `servedClass` field says "class": that field *reports* the billing class the platform resolved your label to — it's a disclosure field, not author-facing input vocabulary. You still write `label`; the platform still reports back `servedClass`. | | ||
| **The rule new capabilities must follow:** don't re-overload "agent" or "run" further. If a | ||
| new capability needs its own verb, name it something else (`dispatch`, `launch`, `submit`, | ||
| `create*`) rather than adding a sixth meaning to a word that already has five. | ||
| ## Failure Handling & Retries | ||
@@ -526,2 +628,3 @@ | ||
| | [Capabilities](https://docs.sapiom.ai/capabilities) | The full `ctx.sapiom.*` catalog with pricing | | ||
| | [Choose a call surface](https://docs.sapiom.ai/guides/choose-a-call-surface) | `llm.run` vs `models.run` vs `agents.run` — which to call and why | | ||
| | `AGENTS.md` in your scaffold | The quick in-project reference | |
@@ -308,2 +308,104 @@ --- | ||
| ## Calling LLMs from Steps | ||
| Three DIFFERENT capabilities call an LLM from step code — picking the wrong one for the | ||
| job is the most common mistake in authored agents: | ||
| | Capability | Use for | Never for | | ||
| | ------------------------ | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | | ||
| | `ctx.sapiom.llm.run` | ONE LLM call — summarize, extract, classify, one-shot generate | A multi-turn task, or anything needing its own tool-calling loop | | ||
| | `ctx.sapiom.models.run` | A platform-driven multi-turn reasoning + tool-calling loop (minutes, not seconds). `models.coding.run` for sandboxed coding tasks. | A one-shot completion — it will loop and overthink | | ||
| | `ctx.sapiom.agents.run` | Dispatching a DEPLOYED agent by slug — composing systems from small deployed agents | Anything that isn't itself a deployed agent | | ||
| **⚠️ The mistake to never repeat:** sending single-shot, fixed-shape intent through | ||
| `models.run`'s multi-turn loop instead of one `llm.run` call. The symptom: the run takes | ||
| far longer than the task needs, "overthinks" a trivial extraction, and — if the caller just | ||
| grabs the first content block hoping it's the answer — returns a `thinking` block instead. | ||
| ### Worked example: a trivial fixed-shape-JSON task | ||
| **Wrong** — one-shot intent sent through the multi-turn loop, then the answer string-parsed | ||
| out of free text: | ||
| ```typescript | ||
| // DON'T: models.run for a one-shot extraction — it loops and overthinks, and | ||
| // "reply with only JSON" is brittle (prose, invalid JSON, or a leading | ||
| // `thinking` block all break a naive `JSON.parse(content[0])`). | ||
| const run = await ctx.sapiom.models.run({ | ||
| prompt: `Reply with ONLY JSON: {"priority": "...", "category": "..."} for: ${input.text}`, | ||
| }); | ||
| const parsed = JSON.parse(run.output ?? "{}"); // brittle, and pays for a reasoning loop | ||
| ``` | ||
| **Right** — `llm.run` with `output` for the fixed shape, read back with `structuredOf` | ||
| (forced tool-use output has no `text` block — the result lives in `tool_use`, never | ||
| `content[0]`): | ||
| ```typescript | ||
| const response = await ctx.sapiom.llm.run({ | ||
| request: { | ||
| messages: [{ role: "user", content: `Classify this support ticket: ${input.text}` }], | ||
| max_tokens: 256, | ||
| }, | ||
| // No `model` — omit it and let the platform choose (recommended). To pin | ||
| // instead: `model: "smart"` (a label, never a raw provider model id). | ||
| output: { | ||
| name: "classify_ticket", | ||
| schema: { | ||
| type: "object", | ||
| properties: { | ||
| priority: { type: "string", enum: ["low", "medium", "high"] }, | ||
| category: { type: "string" }, | ||
| }, | ||
| required: ["priority", "category"], | ||
| }, | ||
| }, | ||
| }); | ||
| const { priority, category } = ctx.sapiom.llm.structuredOf<{ | ||
| priority: string; | ||
| category: string; | ||
| }>(response)!; | ||
| ``` | ||
| `output` automates the forced tool call and its `tool_choice` wiring; `structuredOf` reads | ||
| the result back out. For a **plain-text** reply instead, use | ||
| `ctx.sapiom.llm.textOf(response)` — it reads only the `type === 'text'` block, skipping a | ||
| `thinking` block that may precede it. | ||
| ### The label rule | ||
| **You never pick a model.** Every `model`/`label` field across `llm.*`, `models.run`, and | ||
| `models.coding.run` takes a **routing label** (e.g. `"smart"`) that the platform resolves | ||
| against its configured label set — never a raw provider model id (never honored, on any | ||
| surface). Omit it entirely to let the platform choose (the recommended default); pass | ||
| `"smart"` if you must pin. The result discloses what actually served, in the platform's own | ||
| vocabulary — `servedClass` (the billing size the label resolved to) and `lane` (the billing | ||
| lane it executed in) — never a model or provider id. | ||
| ### Debugging a run | ||
| Find the run in the dashboard's run detail view, find the suspicious step's row id, then | ||
| open the **Run Inspector** for that step's full-fidelity input/output/error/logs. | ||
| Full guide: [Choose a call surface](https://docs.sapiom.ai/guides/choose-a-call-surface). | ||
| ## Naming Conventions | ||
| Several words are overloaded across this platform. Know which meaning a given context | ||
| uses — conflating two costs you a wrong capability choice, not just a wrong word: | ||
| | Term | Meaning(s) on this platform | | ||
| | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | **agent** | (1) `@sapiom/agent` — this authoring framework (`defineAgent`, this skill). (2) A **deployed agent** — one project's compiled definition, dispatched by `ctx.sapiom.agents.run`/`launch` (addressed by its slug via `AgentRunSpec.definition`) — the customer-facing name is always "agent." (3) `ctx.sapiom.models.run`'s managed multi-turn loop — a managed loop the platform runs for you; you call it, you never author it. (4) A **Claude Code subagent** — an unrelated feature of the coding tool itself, not part of the Sapiom SDK. | | ||
| | **run** | (1) `ctx.sapiom.llm.run` — one synchronous LLM call. (2) `ctx.sapiom.models.run` / `agents.run` — `launch()` + `wait()`, blocking until terminal (vs. `launch()` alone, which returns a pausable handle). (3) An execution instance/row of any of the above — the thing you inspect/debug. (4) The dashboard's Run button / Local Run / Prod Run (Studio UI actions, not an API call). | | ||
| | **task** | `CodingRunSpec.task` — the coding agent's prompt-equivalent field. Deliberately not called `prompt`: it's handed to a sandboxed coding agent, not a bare LLM call. | | ||
| | **session** | (1) `ctx.sapiom.llm.createSession`/`callSession` — reserved LLM capacity accepting repeated drop-in calls until its TTL/budget ends it (replacing the deferred `submit`/`redeem` lane). (2) A Studio harness terminal session — unrelated, no LLM-capacity semantics. | | ||
| | **dispatch** | The structural contract (`DispatchHandle`) a long-running capability's `launch()` handle satisfies so a step can `pauseUntilSignal(handle, …)` and resume on completion. Every dispatched capability (coding, `models.run`, `agents.run`, more later) shares this ONE contract — "dispatch" always means this pattern, never anything else. | | ||
| | **label** | The author-facing term for a `model:`/`label:` *input* value (e.g. `"smart"`) — never a raw provider model id (never honored, on any surface). Not a contradiction that a result's `servedClass` field says "class": that field *reports* the billing class the platform resolved your label to — it's a disclosure field, not author-facing input vocabulary. You still write `label`; the platform still reports back `servedClass`. | | ||
| **The rule new capabilities must follow:** don't re-overload "agent" or "run" further. If a | ||
| new capability needs its own verb, name it something else (`dispatch`, `launch`, `submit`, | ||
| `create*`) rather than adding a sixth meaning to a word that already has five. | ||
| ## Failure Handling & Retries | ||
@@ -526,2 +628,3 @@ | ||
| | [Capabilities](https://docs.sapiom.ai/capabilities) | The full `ctx.sapiom.*` catalog with pricing | | ||
| | [Choose a call surface](https://docs.sapiom.ai/guides/choose-a-call-surface) | `llm.run` vs `models.run` vs `agents.run` — which to call and why | | ||
| | `AGENTS.md` in your scaffold | The quick in-project reference | |
@@ -308,2 +308,104 @@ --- | ||
| ## Calling LLMs from Steps | ||
| Three DIFFERENT capabilities call an LLM from step code — picking the wrong one for the | ||
| job is the most common mistake in authored agents: | ||
| | Capability | Use for | Never for | | ||
| | ------------------------ | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | | ||
| | `ctx.sapiom.llm.run` | ONE LLM call — summarize, extract, classify, one-shot generate | A multi-turn task, or anything needing its own tool-calling loop | | ||
| | `ctx.sapiom.models.run` | A platform-driven multi-turn reasoning + tool-calling loop (minutes, not seconds). `models.coding.run` for sandboxed coding tasks. | A one-shot completion — it will loop and overthink | | ||
| | `ctx.sapiom.agents.run` | Dispatching a DEPLOYED agent by slug — composing systems from small deployed agents | Anything that isn't itself a deployed agent | | ||
| **⚠️ The mistake to never repeat:** sending single-shot, fixed-shape intent through | ||
| `models.run`'s multi-turn loop instead of one `llm.run` call. The symptom: the run takes | ||
| far longer than the task needs, "overthinks" a trivial extraction, and — if the caller just | ||
| grabs the first content block hoping it's the answer — returns a `thinking` block instead. | ||
| ### Worked example: a trivial fixed-shape-JSON task | ||
| **Wrong** — one-shot intent sent through the multi-turn loop, then the answer string-parsed | ||
| out of free text: | ||
| ```typescript | ||
| // DON'T: models.run for a one-shot extraction — it loops and overthinks, and | ||
| // "reply with only JSON" is brittle (prose, invalid JSON, or a leading | ||
| // `thinking` block all break a naive `JSON.parse(content[0])`). | ||
| const run = await ctx.sapiom.models.run({ | ||
| prompt: `Reply with ONLY JSON: {"priority": "...", "category": "..."} for: ${input.text}`, | ||
| }); | ||
| const parsed = JSON.parse(run.output ?? "{}"); // brittle, and pays for a reasoning loop | ||
| ``` | ||
| **Right** — `llm.run` with `output` for the fixed shape, read back with `structuredOf` | ||
| (forced tool-use output has no `text` block — the result lives in `tool_use`, never | ||
| `content[0]`): | ||
| ```typescript | ||
| const response = await ctx.sapiom.llm.run({ | ||
| request: { | ||
| messages: [{ role: "user", content: `Classify this support ticket: ${input.text}` }], | ||
| max_tokens: 256, | ||
| }, | ||
| // No `model` — omit it and let the platform choose (recommended). To pin | ||
| // instead: `model: "smart"` (a label, never a raw provider model id). | ||
| output: { | ||
| name: "classify_ticket", | ||
| schema: { | ||
| type: "object", | ||
| properties: { | ||
| priority: { type: "string", enum: ["low", "medium", "high"] }, | ||
| category: { type: "string" }, | ||
| }, | ||
| required: ["priority", "category"], | ||
| }, | ||
| }, | ||
| }); | ||
| const { priority, category } = ctx.sapiom.llm.structuredOf<{ | ||
| priority: string; | ||
| category: string; | ||
| }>(response)!; | ||
| ``` | ||
| `output` automates the forced tool call and its `tool_choice` wiring; `structuredOf` reads | ||
| the result back out. For a **plain-text** reply instead, use | ||
| `ctx.sapiom.llm.textOf(response)` — it reads only the `type === 'text'` block, skipping a | ||
| `thinking` block that may precede it. | ||
| ### The label rule | ||
| **You never pick a model.** Every `model`/`label` field across `llm.*`, `models.run`, and | ||
| `models.coding.run` takes a **routing label** (e.g. `"smart"`) that the platform resolves | ||
| against its configured label set — never a raw provider model id (never honored, on any | ||
| surface). Omit it entirely to let the platform choose (the recommended default); pass | ||
| `"smart"` if you must pin. The result discloses what actually served, in the platform's own | ||
| vocabulary — `servedClass` (the billing size the label resolved to) and `lane` (the billing | ||
| lane it executed in) — never a model or provider id. | ||
| ### Debugging a run | ||
| Find the run in the dashboard's run detail view, find the suspicious step's row id, then | ||
| open the **Run Inspector** for that step's full-fidelity input/output/error/logs. | ||
| Full guide: [Choose a call surface](https://docs.sapiom.ai/guides/choose-a-call-surface). | ||
| ## Naming Conventions | ||
| Several words are overloaded across this platform. Know which meaning a given context | ||
| uses — conflating two costs you a wrong capability choice, not just a wrong word: | ||
| | Term | Meaning(s) on this platform | | ||
| | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | **agent** | (1) `@sapiom/agent` — this authoring framework (`defineAgent`, this skill). (2) A **deployed agent** — one project's compiled definition, dispatched by `ctx.sapiom.agents.run`/`launch` (addressed by its slug via `AgentRunSpec.definition`) — the customer-facing name is always "agent." (3) `ctx.sapiom.models.run`'s managed multi-turn loop — a managed loop the platform runs for you; you call it, you never author it. (4) A **Claude Code subagent** — an unrelated feature of the coding tool itself, not part of the Sapiom SDK. | | ||
| | **run** | (1) `ctx.sapiom.llm.run` — one synchronous LLM call. (2) `ctx.sapiom.models.run` / `agents.run` — `launch()` + `wait()`, blocking until terminal (vs. `launch()` alone, which returns a pausable handle). (3) An execution instance/row of any of the above — the thing you inspect/debug. (4) The dashboard's Run button / Local Run / Prod Run (Studio UI actions, not an API call). | | ||
| | **task** | `CodingRunSpec.task` — the coding agent's prompt-equivalent field. Deliberately not called `prompt`: it's handed to a sandboxed coding agent, not a bare LLM call. | | ||
| | **session** | (1) `ctx.sapiom.llm.createSession`/`callSession` — reserved LLM capacity accepting repeated drop-in calls until its TTL/budget ends it (replacing the deferred `submit`/`redeem` lane). (2) A Studio harness terminal session — unrelated, no LLM-capacity semantics. | | ||
| | **dispatch** | The structural contract (`DispatchHandle`) a long-running capability's `launch()` handle satisfies so a step can `pauseUntilSignal(handle, …)` and resume on completion. Every dispatched capability (coding, `models.run`, `agents.run`, more later) shares this ONE contract — "dispatch" always means this pattern, never anything else. | | ||
| | **label** | The author-facing term for a `model:`/`label:` *input* value (e.g. `"smart"`) — never a raw provider model id (never honored, on any surface). Not a contradiction that a result's `servedClass` field says "class": that field *reports* the billing class the platform resolved your label to — it's a disclosure field, not author-facing input vocabulary. You still write `label`; the platform still reports back `servedClass`. | | ||
| **The rule new capabilities must follow:** don't re-overload "agent" or "run" further. If a | ||
| new capability needs its own verb, name it something else (`dispatch`, `launch`, `submit`, | ||
| `create*`) rather than adding a sixth meaning to a word that already has five. | ||
| ## Failure Handling & Retries | ||
@@ -526,2 +628,3 @@ | ||
| | [Capabilities](https://docs.sapiom.ai/capabilities) | The full `ctx.sapiom.*` catalog with pricing | | ||
| | [Choose a call surface](https://docs.sapiom.ai/guides/choose-a-call-surface) | `llm.run` vs `models.run` vs `agents.run` — which to call and why | | ||
| | `AGENTS.md` in your scaffold | The quick in-project reference | |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
560932
6.19%140
1.45%6251
2.9%+ Added
- Removed
Updated
Updated