@sapiom/agent-core
Advanced tools
+33
-0
| # @sapiom/orchestration-core | ||
| ## 0.13.0 | ||
| ### Minor Changes | ||
| - 555475d: Make `ctx.shared.set()` on the SDK's `InMemoryContextStore` an atomic | ||
| whole-snapshot quota gate. `runLocal` now constructs this store with step | ||
| context; hosts that have not adopted this store version may enforce the contract | ||
| only at execution boundaries during rollout. The store measures the complete | ||
| candidate as compact JSON UTF-8 before committing it, so oversized writes throw | ||
| `CTX_SHARED_SIZE_LIMIT_EXCEEDED` and retain the previous state. | ||
| Publish the bounded `CTX_SHARED_SERIALIZATION_FAILED` terminal error contract | ||
| for circular references, BigInt values, throwing `toJSON` methods, and other | ||
| `JSON.stringify` failures. Completion serializers and runners recognize the | ||
| new code through the closed platform-error registry and fail the step on its | ||
| current attempt without retrying. Ordinary JSON omission and coercion semantics | ||
| remain unchanged. | ||
| **Breaking:** `InMemoryContextStore.set()` can now throw synchronously for an | ||
| oversized or unserializable candidate. Existing local agents that wrote such | ||
| state now fail non-retryably on attempt 0. Migrate bulk state to durable storage | ||
| and keep only compact, JSON-compatible values or references in `ctx.shared`. If | ||
| a store is seeded with legacy invalid state, replace an offending key with a | ||
| value small enough to bring the complete candidate within the quota; | ||
| `TypedContextStore` has no `delete()` operation. | ||
| ### Patch Changes | ||
| - 52efab3: `sapiom-agent-authoring` skill + scaffold `AGENTS.md`: system-design teaching for multi-stage builds. New "Composing Deployed Agents" section — one agent per PROJECT; a multi-stage system is several small projects composed via `ctx.sapiom.agents.run`, with a worked coordinator example — and the scaffold's "keep exactly one `defineAgent` export" rule now says so inline, so it reads as a per-project rule rather than a design instruction to inline every stage. Also drops the "pass `smart` if you must pin" no-op from the label rule (omitting `model` is the recommendation; `smart` already is the default). | ||
| - Updated dependencies [555475d] | ||
| - @sapiom/agent@0.12.0 | ||
| - @sapiom/agent-runtime@0.7.0 | ||
| ## 0.12.2 | ||
@@ -4,0 +37,0 @@ |
@@ -53,3 +53,5 @@ "use strict"; | ||
| }); | ||
| const sharedStore = new agent_1.InMemoryContextStore(request.shared); | ||
| const sharedStore = new agent_1.InMemoryContextStore(request.shared, { | ||
| stepName: request.stepName, | ||
| }); | ||
| const overrides = (this.stubs.steps[request.stepName] ?? {}); | ||
@@ -56,0 +58,0 @@ let usedKeys = this.usedKeysByStep.get(request.stepName); |
| export declare const VERSION_FALLBACK: { | ||
| readonly agent: "0.11.0"; | ||
| readonly agent: "0.12.0"; | ||
| readonly tools: "0.31.0"; | ||
| }; |
@@ -5,4 +5,4 @@ "use strict"; | ||
| exports.VERSION_FALLBACK = { | ||
| agent: "0.11.0", | ||
| agent: "0.12.0", | ||
| tools: "0.31.0", | ||
| }; |
@@ -50,3 +50,5 @@ import { InMemoryContextStore, StepInputValidationError, } from "@sapiom/agent"; | ||
| }); | ||
| const sharedStore = new InMemoryContextStore(request.shared); | ||
| const sharedStore = new InMemoryContextStore(request.shared, { | ||
| stepName: request.stepName, | ||
| }); | ||
| const overrides = (this.stubs.steps[request.stepName] ?? {}); | ||
@@ -53,0 +55,0 @@ let usedKeys = this.usedKeysByStep.get(request.stepName); |
| export declare const VERSION_FALLBACK: { | ||
| readonly agent: "0.11.0"; | ||
| readonly agent: "0.12.0"; | ||
| readonly tools: "0.31.0"; | ||
| }; |
| export const VERSION_FALLBACK = { | ||
| agent: "0.11.0", | ||
| agent: "0.12.0", | ||
| tools: "0.31.0", | ||
| }; |
+3
-3
| { | ||
| "name": "@sapiom/agent-core", | ||
| "version": "0.12.2", | ||
| "version": "0.13.0", | ||
| "description": "Pure, stateless core functions for scaffolding, validating, and operating Sapiom agents — shared by the CLI and MCP packages.", | ||
@@ -40,4 +40,4 @@ "license": "MIT", | ||
| "esbuild": "^0.28.1", | ||
| "@sapiom/agent": "^0.11.0", | ||
| "@sapiom/agent-runtime": "^0.6.0", | ||
| "@sapiom/agent": "^0.12.0", | ||
| "@sapiom/agent-runtime": "^0.7.0", | ||
| "@sapiom/analytics-core": "^0.2.1", | ||
@@ -44,0 +44,0 @@ "@sapiom/tools": "^0.31.0" |
@@ -243,5 +243,14 @@ --- | ||
| documents, or research data in durable storage and carry only the ID/reference. | ||
| The SDK contract does not by itself make `ctx.shared.set()` a synchronous size | ||
| gate. Hosts enforce it at execution boundaries, and older hosts may temporarily | ||
| enforce a smaller legacy limit during rollout. | ||
| Hosts that construct this SDK version's `InMemoryContextStore` get setter-time | ||
| validation: `ctx.shared.set()` synchronously measures the complete candidate | ||
| snapshot before committing it. An oversized or unserializable write throws and | ||
| leaves the previous snapshot unchanged. Measurement follows `JSON.stringify`: | ||
| circular references, BigInt values, and throwing `toJSON` methods fail, while | ||
| values JSON normally omits or coerces retain those semantics. Hosts that have | ||
| not adopted this store version may temporarily enforce the contract only at | ||
| execution boundaries during rollout. If you catch a failure, use the exported | ||
| structural payload guards rather than `instanceof`, because host and definition | ||
| bundles can contain separate SDK copies. There is no `delete()` operation; to | ||
| recover from legacy invalid state, replace an offending key with a compact, | ||
| JSON-compatible value that brings the complete candidate within the quota. | ||
@@ -350,4 +359,5 @@ **A step's `run(input, ctx)` first argument is its inbound input** — the entry input at the | ||
| }, | ||
| // No `model` — omit it and let the platform choose (recommended). To pin | ||
| // instead: `model: "smart"` (a label, never a raw provider model id). | ||
| // No `model` — omit it and let the platform choose (recommended; passing | ||
| // "smart" would be a no-op — it already is the default — and a raw provider | ||
| // model id is never honored). | ||
| output: { | ||
@@ -382,4 +392,4 @@ name: "classify_ticket", | ||
| 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 | ||
| surface). Omit it entirely to let the platform choose (the recommended default) — passing | ||
| `"smart"` is a no-op: it already is the default. The result discloses what actually served, in the platform's own | ||
| vocabulary — `servedClass` (the billing size the label resolved to) and `lane` (the billing | ||
@@ -395,2 +405,49 @@ lane it executed in) — never a model or provider id. | ||
| ## Composing Deployed Agents (System Design) | ||
| **One agent per project — but a system is several projects.** The "keep exactly one | ||
| `defineAgent(...)` export" rule is a statement about a PROJECT, not about your system. A | ||
| multi-stage system ("research → write script → voiceover → assemble → post") is not one | ||
| big agent with five steps: it is several small agents, each its own project, deployed | ||
| separately, composed by a thin coordinator that dispatches them by slug. A small deployed | ||
| agent is independently testable, versioned, and reusable from more than one caller; a | ||
| monolith couples every stage into a single deploy unit and step graph, so any stage change | ||
| redeploys — and risks — all of them. | ||
| **Wrong** — one project inlining every stage as a step: | ||
| ```typescript | ||
| // DON'T: video-pipeline/index.ts with research, script, voiceover, assemble, | ||
| // post as five steps of ONE defineAgent. No stage is reusable or | ||
| // independently testable, and every stage change redeploys all five. | ||
| ``` | ||
| **Right** — each stage its own deployed project; a coordinator composes them: | ||
| ```typescript | ||
| // research-topic/, write-script/, generate-voiceover/, assemble-video/, | ||
| // post-video/: five small projects, each deployed on its own slug. | ||
| // video-pipeline/ is then just the coordinator: | ||
| const research = await ctx.sapiom.agents.run({ | ||
| definition: "research-topic", // the deployed child's slug | ||
| input: { topic: input.topic }, | ||
| }); | ||
| // agents.run resolves on ANY terminal status (completed | failed | cancelled) | ||
| // and does NOT throw — a non-completed child is data the coordinator must | ||
| // branch on, or a failed stage silently feeds `null` downstream. | ||
| if (research.status !== "completed") { | ||
| // (fail() requires this step to declare canFail: true) | ||
| return fail(`research-topic ${research.status}: ${String(research.error)}`); | ||
| } | ||
| const script = await ctx.sapiom.agents.run({ | ||
| definition: "write-script", | ||
| input: { research: research.output }, | ||
| }); | ||
| // …and so on. Use agents.launch + pauseUntilSignal for a long-running child | ||
| // so the coordinator's step doesn't time out. | ||
| ``` | ||
| Building a system in one session? Scaffold the stages as separate projects and deploy | ||
| bottom-up — children first, the coordinator last (it dispatches them by their slugs). | ||
| ## Naming Conventions | ||
@@ -397,0 +454,0 @@ |
@@ -243,5 +243,14 @@ --- | ||
| documents, or research data in durable storage and carry only the ID/reference. | ||
| The SDK contract does not by itself make `ctx.shared.set()` a synchronous size | ||
| gate. Hosts enforce it at execution boundaries, and older hosts may temporarily | ||
| enforce a smaller legacy limit during rollout. | ||
| Hosts that construct this SDK version's `InMemoryContextStore` get setter-time | ||
| validation: `ctx.shared.set()` synchronously measures the complete candidate | ||
| snapshot before committing it. An oversized or unserializable write throws and | ||
| leaves the previous snapshot unchanged. Measurement follows `JSON.stringify`: | ||
| circular references, BigInt values, and throwing `toJSON` methods fail, while | ||
| values JSON normally omits or coerces retain those semantics. Hosts that have | ||
| not adopted this store version may temporarily enforce the contract only at | ||
| execution boundaries during rollout. If you catch a failure, use the exported | ||
| structural payload guards rather than `instanceof`, because host and definition | ||
| bundles can contain separate SDK copies. There is no `delete()` operation; to | ||
| recover from legacy invalid state, replace an offending key with a compact, | ||
| JSON-compatible value that brings the complete candidate within the quota. | ||
@@ -350,4 +359,5 @@ **A step's `run(input, ctx)` first argument is its inbound input** — the entry input at the | ||
| }, | ||
| // No `model` — omit it and let the platform choose (recommended). To pin | ||
| // instead: `model: "smart"` (a label, never a raw provider model id). | ||
| // No `model` — omit it and let the platform choose (recommended; passing | ||
| // "smart" would be a no-op — it already is the default — and a raw provider | ||
| // model id is never honored). | ||
| output: { | ||
@@ -382,4 +392,4 @@ name: "classify_ticket", | ||
| 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 | ||
| surface). Omit it entirely to let the platform choose (the recommended default) — passing | ||
| `"smart"` is a no-op: it already is the default. The result discloses what actually served, in the platform's own | ||
| vocabulary — `servedClass` (the billing size the label resolved to) and `lane` (the billing | ||
@@ -395,2 +405,49 @@ lane it executed in) — never a model or provider id. | ||
| ## Composing Deployed Agents (System Design) | ||
| **One agent per project — but a system is several projects.** The "keep exactly one | ||
| `defineAgent(...)` export" rule is a statement about a PROJECT, not about your system. A | ||
| multi-stage system ("research → write script → voiceover → assemble → post") is not one | ||
| big agent with five steps: it is several small agents, each its own project, deployed | ||
| separately, composed by a thin coordinator that dispatches them by slug. A small deployed | ||
| agent is independently testable, versioned, and reusable from more than one caller; a | ||
| monolith couples every stage into a single deploy unit and step graph, so any stage change | ||
| redeploys — and risks — all of them. | ||
| **Wrong** — one project inlining every stage as a step: | ||
| ```typescript | ||
| // DON'T: video-pipeline/index.ts with research, script, voiceover, assemble, | ||
| // post as five steps of ONE defineAgent. No stage is reusable or | ||
| // independently testable, and every stage change redeploys all five. | ||
| ``` | ||
| **Right** — each stage its own deployed project; a coordinator composes them: | ||
| ```typescript | ||
| // research-topic/, write-script/, generate-voiceover/, assemble-video/, | ||
| // post-video/: five small projects, each deployed on its own slug. | ||
| // video-pipeline/ is then just the coordinator: | ||
| const research = await ctx.sapiom.agents.run({ | ||
| definition: "research-topic", // the deployed child's slug | ||
| input: { topic: input.topic }, | ||
| }); | ||
| // agents.run resolves on ANY terminal status (completed | failed | cancelled) | ||
| // and does NOT throw — a non-completed child is data the coordinator must | ||
| // branch on, or a failed stage silently feeds `null` downstream. | ||
| if (research.status !== "completed") { | ||
| // (fail() requires this step to declare canFail: true) | ||
| return fail(`research-topic ${research.status}: ${String(research.error)}`); | ||
| } | ||
| const script = await ctx.sapiom.agents.run({ | ||
| definition: "write-script", | ||
| input: { research: research.output }, | ||
| }); | ||
| // …and so on. Use agents.launch + pauseUntilSignal for a long-running child | ||
| // so the coordinator's step doesn't time out. | ||
| ``` | ||
| Building a system in one session? Scaffold the stages as separate projects and deploy | ||
| bottom-up — children first, the coordinator last (it dispatches them by their slugs). | ||
| ## Naming Conventions | ||
@@ -397,0 +454,0 @@ |
@@ -9,3 +9,3 @@ # Working in this agent project | ||
| - An agent is `defineAgent({ entry, steps })`; each step is `defineStep({ name, next, run })`. Keep exactly one `defineAgent(...)` export. | ||
| - An agent is `defineAgent({ entry, steps })`; each step is `defineStep({ name, next, run })`. Keep exactly one `defineAgent(...)` export — one agent per project. A multi-stage system is several small projects composed with `ctx.sapiom.agents.run` (see the sapiom-agent-authoring skill’s "Composing Deployed Agents"). | ||
| - **Capabilities come from the types.** What's available on `ctx.sapiom` is defined by `@sapiom/tools` — read the types / use autocomplete rather than guessing. A wrong capability or method name fails typecheck. | ||
@@ -76,3 +76,3 @@ | ||
| - **The resumed step's `input` IS the run's result signal payload.** Annotate it with `CodingResultPayload` (from `@sapiom/tools`) — you don't have to hand-roll the shape. | ||
| - That payload crossed a wire boundary, so it carries **no live handles** — to act on the run's sandbox, re-attach one from **`executionEnvironment`** with `ctx.sapiom.sandboxes.attach(result.executionEnvironment.id)` (`executionEnvironment` is `null` when the run provisioned none, e.g. a launch failure). Before pausing, stash only compact state, IDs, or durable-storage references in `ctx.shared`; its whole compact-JSON snapshot has an inclusive 256 KiB UTF-8 quota. The SDK contract does not make `ctx.shared.set()` a synchronous size gate by itself; hosts enforce it at execution boundaries, and older hosts may temporarily enforce a smaller legacy limit during rollout. | ||
| - That payload crossed a wire boundary, so it carries **no live handles** — to act on the run's sandbox, re-attach one from **`executionEnvironment`** with `ctx.sapiom.sandboxes.attach(result.executionEnvironment.id)` (`executionEnvironment` is `null` when the run provisioned none, e.g. a launch failure). Before pausing, stash only compact state, IDs, or durable-storage references in `ctx.shared`; its whole compact-JSON snapshot has an inclusive 256 KiB UTF-8 quota. Hosts that construct this SDK version's `InMemoryContextStore` get synchronous complete-candidate validation and retain the previous snapshot after an oversized or unserializable write; hosts that have not adopted it may enforce only at execution boundaries during rollout. | ||
| - **To stub the resume payload** (e.g. to exercise the failure branch), override `models.coding.run` _in the launching step_ — that one value is both the `run()` result and the payload the paused step resumes with. `models.coding.launch` is accepted there too. | ||
@@ -79,0 +79,0 @@ - `gitRepository` accepts a Sapiom repository returned by `repositories.create`, `get`, or `list`; `repositories.attach` only rehydrates such a handle. |
@@ -243,5 +243,14 @@ --- | ||
| documents, or research data in durable storage and carry only the ID/reference. | ||
| The SDK contract does not by itself make `ctx.shared.set()` a synchronous size | ||
| gate. Hosts enforce it at execution boundaries, and older hosts may temporarily | ||
| enforce a smaller legacy limit during rollout. | ||
| Hosts that construct this SDK version's `InMemoryContextStore` get setter-time | ||
| validation: `ctx.shared.set()` synchronously measures the complete candidate | ||
| snapshot before committing it. An oversized or unserializable write throws and | ||
| leaves the previous snapshot unchanged. Measurement follows `JSON.stringify`: | ||
| circular references, BigInt values, and throwing `toJSON` methods fail, while | ||
| values JSON normally omits or coerces retain those semantics. Hosts that have | ||
| not adopted this store version may temporarily enforce the contract only at | ||
| execution boundaries during rollout. If you catch a failure, use the exported | ||
| structural payload guards rather than `instanceof`, because host and definition | ||
| bundles can contain separate SDK copies. There is no `delete()` operation; to | ||
| recover from legacy invalid state, replace an offending key with a compact, | ||
| JSON-compatible value that brings the complete candidate within the quota. | ||
@@ -350,4 +359,5 @@ **A step's `run(input, ctx)` first argument is its inbound input** — the entry input at the | ||
| }, | ||
| // No `model` — omit it and let the platform choose (recommended). To pin | ||
| // instead: `model: "smart"` (a label, never a raw provider model id). | ||
| // No `model` — omit it and let the platform choose (recommended; passing | ||
| // "smart" would be a no-op — it already is the default — and a raw provider | ||
| // model id is never honored). | ||
| output: { | ||
@@ -382,4 +392,4 @@ name: "classify_ticket", | ||
| 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 | ||
| surface). Omit it entirely to let the platform choose (the recommended default) — passing | ||
| `"smart"` is a no-op: it already is the default. The result discloses what actually served, in the platform's own | ||
| vocabulary — `servedClass` (the billing size the label resolved to) and `lane` (the billing | ||
@@ -395,2 +405,49 @@ lane it executed in) — never a model or provider id. | ||
| ## Composing Deployed Agents (System Design) | ||
| **One agent per project — but a system is several projects.** The "keep exactly one | ||
| `defineAgent(...)` export" rule is a statement about a PROJECT, not about your system. A | ||
| multi-stage system ("research → write script → voiceover → assemble → post") is not one | ||
| big agent with five steps: it is several small agents, each its own project, deployed | ||
| separately, composed by a thin coordinator that dispatches them by slug. A small deployed | ||
| agent is independently testable, versioned, and reusable from more than one caller; a | ||
| monolith couples every stage into a single deploy unit and step graph, so any stage change | ||
| redeploys — and risks — all of them. | ||
| **Wrong** — one project inlining every stage as a step: | ||
| ```typescript | ||
| // DON'T: video-pipeline/index.ts with research, script, voiceover, assemble, | ||
| // post as five steps of ONE defineAgent. No stage is reusable or | ||
| // independently testable, and every stage change redeploys all five. | ||
| ``` | ||
| **Right** — each stage its own deployed project; a coordinator composes them: | ||
| ```typescript | ||
| // research-topic/, write-script/, generate-voiceover/, assemble-video/, | ||
| // post-video/: five small projects, each deployed on its own slug. | ||
| // video-pipeline/ is then just the coordinator: | ||
| const research = await ctx.sapiom.agents.run({ | ||
| definition: "research-topic", // the deployed child's slug | ||
| input: { topic: input.topic }, | ||
| }); | ||
| // agents.run resolves on ANY terminal status (completed | failed | cancelled) | ||
| // and does NOT throw — a non-completed child is data the coordinator must | ||
| // branch on, or a failed stage silently feeds `null` downstream. | ||
| if (research.status !== "completed") { | ||
| // (fail() requires this step to declare canFail: true) | ||
| return fail(`research-topic ${research.status}: ${String(research.error)}`); | ||
| } | ||
| const script = await ctx.sapiom.agents.run({ | ||
| definition: "write-script", | ||
| input: { research: research.output }, | ||
| }); | ||
| // …and so on. Use agents.launch + pauseUntilSignal for a long-running child | ||
| // so the coordinator's step doesn't time out. | ||
| ``` | ||
| Building a system in one session? Scaffold the stages as separate projects and deploy | ||
| bottom-up — children first, the coordinator last (it dispatches them by their slugs). | ||
| ## Naming Conventions | ||
@@ -397,0 +454,0 @@ |
@@ -9,3 +9,3 @@ # Working in this agent project | ||
| - An agent is `defineAgent({ entry, steps })`; each step is `defineStep({ name, next, run })`. Keep exactly one `defineAgent(...)` export. | ||
| - An agent is `defineAgent({ entry, steps })`; each step is `defineStep({ name, next, run })`. Keep exactly one `defineAgent(...)` export — one agent per project. A multi-stage system is several small projects composed with `ctx.sapiom.agents.run` (see the sapiom-agent-authoring skill’s "Composing Deployed Agents"). | ||
| - **Capabilities come from the types.** What's available on `ctx.sapiom` is defined by `@sapiom/tools` — read the types / use autocomplete rather than guessing. A wrong capability or method name fails typecheck. | ||
@@ -76,3 +76,3 @@ | ||
| - **The resumed step's `input` IS the run's result signal payload.** Annotate it with `CodingResultPayload` (from `@sapiom/tools`) — you don't have to hand-roll the shape. | ||
| - That payload crossed a wire boundary, so it carries **no live handles** — to act on the run's sandbox, re-attach one from **`executionEnvironment`** with `ctx.sapiom.sandboxes.attach(result.executionEnvironment.id)` (`executionEnvironment` is `null` when the run provisioned none, e.g. a launch failure). Before pausing, stash only compact state, IDs, or durable-storage references in `ctx.shared`; its whole compact-JSON snapshot has an inclusive 256 KiB UTF-8 quota. The SDK contract does not make `ctx.shared.set()` a synchronous size gate by itself; hosts enforce it at execution boundaries, and older hosts may temporarily enforce a smaller legacy limit during rollout. | ||
| - That payload crossed a wire boundary, so it carries **no live handles** — to act on the run's sandbox, re-attach one from **`executionEnvironment`** with `ctx.sapiom.sandboxes.attach(result.executionEnvironment.id)` (`executionEnvironment` is `null` when the run provisioned none, e.g. a launch failure). Before pausing, stash only compact state, IDs, or durable-storage references in `ctx.shared`; its whole compact-JSON snapshot has an inclusive 256 KiB UTF-8 quota. Hosts that construct this SDK version's `InMemoryContextStore` get synchronous complete-candidate validation and retain the previous snapshot after an oversized or unserializable write; hosts that have not adopted it may enforce only at execution boundaries during rollout. | ||
| - **To stub the resume payload** (e.g. to exercise the failure branch), override `models.coding.run` _in the launching step_ — that one value is both the `run()` result and the payload the paused step resumes with. `models.coding.launch` is accepted there too. | ||
@@ -79,0 +79,0 @@ - `gitRepository` accepts a Sapiom repository returned by `repositories.create`, `get`, or `list`; `repositories.attach` only rehydrates such a handle. |
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.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
574675
2.18%6255
0.06%+ Added
+ Added
+ Added
- Removed
- Removed
Updated
Updated