New:Socket for Asana Is Now Available.Learn more
Get Started

@sapiom/agent-core

Package Overview
Dependencies
Maintainers
4
Versions
36
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sapiom/agent-core - npm Package Compare versions

Comparing version
0.13.1
to
0.13.2
+36
-0
CHANGELOG.md
# @sapiom/orchestration-core
## 0.13.2
### Patch Changes
- db81e32: Step code can now tell a local trace from a deployed run, and the stub Postgres DSN no longer points at a host that might answer.
`AgentExecutionContext` gains `isLocalTrace?: boolean` — `true` under `run_local`, and set by nothing else. The deployed step runner does not set it, so `input.dryRun ?? ctx.isLocalTrace` reads as live in production; the name is qualified precisely because that absence is load-bearing.
Use it for the I/O `run_local` cannot stub — a raw Postgres socket, third-party HTTP, any client holding its own connection. Resolve it once in the entry step and carry it in `ctx.shared`, since `input` downstream is the previous step's output:
```ts
// entry step
const dryRun = input.dryRun ?? ctx.isLocalTrace ?? false;
ctx.shared.set("dryRun", dryRun);
```
An explicit `{ "dryRun": false }` still forces the live path. Capability calls (`ctx.sapiom.*`) are already stubbed locally and need no guard.
Separately, the stub `database.get` / `database.create` DSN now uses a host in the reserved `.invalid` TLD (RFC 6761) instead of `localhost`. A template that dialed the old DSN reached whatever Postgres happened to be listening on the author's own machine and failed with an opaque TLS error; it now fails at name resolution on any conforming resolver. Treat this as a backstop rather than a guard — a resolver that hijacks NXDOMAIN can still return an address — and gate the dial on `ctx.isLocalTrace`.
- 054f749: `sapiom-sandbox-preview` skill now teaches App Links. It says plainly that a
sandbox preview URL is temporary (it dies with the resource's `ttl`, along with
any bookmark or Slack message it was pasted into), and adds a "Make it durable /
share it" section that routes "share this", "send this to my team", "a permanent
link", "keep it alive", "my link died" to `sapiom_dev_app_publish` — with the
five facts an agent otherwise gets wrong: wake-on-demand cold start, org-scoped
by default (public needs `confirmPublic` + `dailySpendCapUsd`, so ask first),
republish-in-place on the same slug, text-only bundles, and the ~10 MiB cap. The
frontmatter `description` picks up the durability triggers so the skill fires on
a "link that won't die" ask.
- Updated dependencies [db81e32]
- Updated dependencies [9165f18]
- Updated dependencies [6fb3273]
- @sapiom/agent@0.12.2
- @sapiom/tools@0.33.0
## 0.13.1

@@ -4,0 +40,0 @@

+1
-0

@@ -81,2 +81,3 @@ "use strict";

sapiom,
isLocalTrace: true,
};

@@ -83,0 +84,0 @@ let stepInput = request.input;

+2
-2
export declare const VERSION_FALLBACK: {
readonly agent: "0.12.1";
readonly tools: "0.32.0";
readonly agent: "0.12.2";
readonly tools: "0.33.0";
};

@@ -5,4 +5,4 @@ "use strict";

exports.VERSION_FALLBACK = {
agent: "0.12.1",
tools: "0.32.0",
agent: "0.12.2",
tools: "0.33.0",
};

@@ -78,2 +78,3 @@ import { InMemoryContextStore, StepInputValidationError, } from "@sapiom/agent";

sapiom,
isLocalTrace: true,
};

@@ -80,0 +81,0 @@ let stepInput = request.input;

export declare const VERSION_FALLBACK: {
readonly agent: "0.12.1";
readonly tools: "0.32.0";
readonly agent: "0.12.2";
readonly tools: "0.33.0";
};
export const VERSION_FALLBACK = {
agent: "0.12.1",
tools: "0.32.0",
agent: "0.12.2",
tools: "0.33.0",
};
{
"name": "@sapiom/agent-core",
"version": "0.13.1",
"version": "0.13.2",
"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.12.1",
"@sapiom/agent": "^0.12.2",
"@sapiom/agent-runtime": "^0.7.0",
"@sapiom/tools": "^0.32.0",
"@sapiom/analytics-core": "^0.2.1"
"@sapiom/analytics-core": "^0.2.1",
"@sapiom/tools": "^0.33.0"
},

@@ -46,0 +46,0 @@ "devDependencies": {

@@ -274,2 +274,3 @@ ---

| `ctx.sapiom` | `Sapiom` | The typed capability client — the `Sapiom` interface from `@sapiom/tools`, installed in your `node_modules` (see "Capabilities" below) |
| `ctx.isLocalTrace` | `boolean \| undefined` | `true` under `run_local`; **absent** on a deployed run. Gate raw I/O `run_local` cannot stub (see "Testing with `run_local`") |
| `ctx.organizationId` | `string \| null` | Tenant org |

@@ -602,2 +603,28 @@ | `ctx.tenantId` | `string \| null` | Tenant id |

### Gate I/O the stub cannot see
`run_local` stubs `ctx.sapiom.*` and nothing else. Anything holding its own connection — a
`postgres` client, raw HTTP to a third party — still runs for real. `database.get` succeeds
and hands back a DSN, but that DSN's host is in the reserved `.invalid` TLD and does not
resolve, so dialing it fails.
Resolve a `dryRun` gate **once, in the entry step**, and carry it in `ctx.shared`:
```ts
// entry step — `input` is the execution's entry input
const dryRun = input.dryRun ?? ctx.isLocalTrace ?? false;
ctx.shared.set("dryRun", dryRun);
if (!dryRun) {
// raw sockets, third-party HTTP — the things run_local cannot stub
}
// any later step: `input` is the previous step's output, so re-deriving it here
// would read live on a deployed run the caller asked to be dry.
const dryRun = ctx.shared.get("dryRun") ?? false;
```
`ctx.isLocalTrace` is absent on a deployed run, so `?? ctx.isLocalTrace` supplies the default
without changing production behaviour — and an explicit `{ "dryRun": false }` still forces the
live path when you want to exercise it.
`run_local` works with **no stubs** — capabilities return sensible defaults. Add

@@ -604,0 +631,0 @@ `.sapiom-dev/stubs.json` overrides only when a step branches on a specific result:

---
name: sapiom-sandbox-preview
description: Deploy a live preview of a web app from the current project to a
Sapiom sandbox. Use when the user wants to preview, host, or deploy a web
app, dev server, API, or static site to a live URL ("preview this app",
"give me a live link", "host this server"), or mentions sapiom.json sandbox
resources. Do NOT use for deploying Sapiom agents (that's
sapiom_dev_agents_deploy) or for one-off capability calls.
description: Deploy a web app from the current project to a live Sapiom URL —
either a throwaway sandbox preview or a durable App Link. Use when the user
wants to preview, host, deploy, or share a web app, dashboard, dev server,
API, or static site ("preview this app", "give me a live link", "host this
server"), and especially when they want that link to last ("a permanent
link", "something I can share with my team", "keep it alive", "my link
died"), or mentions sapiom.json sandbox resources or App Links. Do NOT use
for deploying Sapiom agents (that's sapiom_dev_agents_deploy) or for one-off
capability calls.
---
# Sandbox Previews
# Sandbox Previews & App Links
Deploy the web app in the current project directory to a Sapiom **sandbox** and get a
**live public URL** — provisioned, uploaded, built, and started in one tool call. Driven
by the sapiom-dev MCP (`npx -y @sapiom/mcp`; see the [Get Started guide](https://docs.sapiom.ai/)
if it isn't connected).
Two ways to put the web app in the current project on a live URL, both driven by the
sapiom-dev MCP (`npx -y @sapiom/mcp`; see the [Get Started guide](https://docs.sapiom.ai/)
if it isn't connected) and both reading the same `sapiom.json` resource:
**This is not agent deployment.** Sapiom *agents* deploy with `sapiom_dev_agents_deploy`;
sandbox previews host an ordinary app (Node server, static site, API) from your working
directory.
| You want | Tool | The URL |
| --------------------------------------------- | ---------------------------- | --------------------------------------------------- |
| To look at it now, iterate, throw it away | `sapiom_dev_sandbox_preview` | **Temporary** — dies with the sandbox's `ttl` |
| A link that lasts, or that someone else opens | `sapiom_dev_app_publish` | **Durable** — `https://apps.sapiom.ai/{org}/{slug}` |
**This is not agent deployment.** Sapiom _agents_ deploy with `sapiom_dev_agents_deploy`;
both tools here host an ordinary app (Node server, static site, API, dashboard) from your
working directory.
## Prerequisite
Run `sapiom_authenticate` once (browser login; caches a key in `~/.sapiom/credentials.json`).
`sapiom_dev_sandbox_preview` returns a structured not-authenticated error otherwise;
`configure` and `check` only touch local files and work signed-out. Check with
`sapiom_status`.
`sapiom_dev_sandbox_preview` and `sapiom_dev_app_publish` return a structured
not-authenticated error otherwise; `configure` and `check` only touch local files and work
signed-out. Check with `sapiom_status`.

@@ -35,3 +42,3 @@ ## The lifecycle

the stored config.
2. **`sapiom_dev_sandbox_check`** *(optional)* — statically validates the resources without
2. **`sapiom_dev_sandbox_check`** _(optional)_ — statically validates the resources without
deploying. Returns `{ ok, sandboxes, issues }`; fix any `issues` before previewing.

@@ -42,2 +49,3 @@ 3. **`sapiom_dev_sandbox_preview`** — reads `sapiom.json`, provisions the sandbox if

resource.
4. **`sapiom_dev_app_publish`** — when the link needs to outlive the sandbox. See below.

@@ -48,2 +56,45 @@ **A `failed` status is not an error** — it carries the build/start logs so you can fix the

**The preview URL is temporary.** It belongs to a sandbox that expires with the resource's
`ttl` (default `1h`), and it goes away with it — along with any bookmark, Slack message, or
doc anyone pasted it into. Say so when you hand it over, and reach for
`sapiom_dev_app_publish` instead whenever the link is meant to survive.
## Make it durable / share it
Route to **`sapiom_dev_app_publish`** whenever the ask is about the link lasting or leaving
your machine — "share this", "send this to my team", "a permanent link", "keep it alive",
"can I bookmark this", "put this somewhere", "my link died", "the URL stopped working".
```
sapiom_dev_app_publish { slug: "dash", name: "Dash" }
→ { url: "https://apps.sapiom.ai/{org}/dash", appLinkId, bundleSha256, manifest }
```
It reads the **same** `sapiom.json` sandbox resource (source dir, `start`, `port`, optional
`build`/`env`), uploads the source as a stored bundle, and activates it. What to know:
- **Wake on demand, not always-on.** Nothing runs until someone visits. The first visit
after a publish cold-starts the app — tens of seconds behind a "Starting…" page — then
it's fast until it idles out again. Tell the user that, so a slow first load doesn't read
as broken.
- **Org-scoped by default.** Only logged-in members of the organization can open it.
`visibility: "public"` (anyone with the link) additionally needs `confirmPublic: true`
and a `dailySpendCapUsd` — the org pays for every wake, so **ask the user before setting
either**.
- **Republish in place.** Publishing the same `slug` again replaces the app at the _same_
URL and returns a new `bundleSha256`. That is how you ship an update; never mint a second
slug for v2.
- **Text-only bundles.** UTF-8 files only — no images, fonts, or archives. A binary is
rejected **by name** before anything is uploaded; drop it, or use inline SVG, a data URL,
or a CDN reference. `node_modules`, `.git`, dotfiles, symlinks and the project's own
`sapiom.json` are never uploaded, so install dependencies at wake with the resource's
`build` command.
- **~10 MiB** per bundle over this path, checked locally before the upload. Drop generated
output (`dist`, build artifacts, vendored assets) rather than shipping it.
- Pass `resource` only when the project defines more than one sandbox resource. `slug`
(`[a-z0-9-]{1,63}`) is the app's identity; `name` is what the "Starting…" page shows.
The canonical link is the identity — share `https://apps.sapiom.ai/{org}/{slug}`, never the
sandbox address the browser lands on after a wake.
## The `sapiom.json` resource

@@ -66,11 +117,11 @@

| Field | Required | Notes |
|---|---|---|
| `source` | yes | `{ "kind": "upload", "path"? }` (upload the local dir) or `{ "kind": "git", "slug", "path"? }` (server checks out a Sapiom repo) |
| `start` | yes | The server command (e.g. `node server.js`) |
| `port` | yes | 1–65535 — the port your app listens on |
| `build` | no | Build command run before start (e.g. `npm run build`) |
| `tier` | no | Sandbox size: `xs` \| `s` \| `m` \| `l` \| `xl` |
| `ttl` | no | Sandbox lifetime, e.g. `"1h"`, `"24h"`, `"7d"` |
| `env` | no | Environment variables (string map) |
| Field | Required | Notes |
| -------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `source` | yes | `{ "kind": "upload", "path"? }` (upload the local dir) or `{ "kind": "git", "slug", "path"? }` (server checks out a Sapiom repo) |
| `start` | yes | The server command (e.g. `node server.js`) |
| `port` | yes | 1–65535 — the port your app listens on |
| `build` | no | Build command run before start (e.g. `npm run build`) |
| `tier` | no | Sandbox size: `xs` \| `s` \| `m` \| `l` \| `xl` |
| `ttl` | no | Sandbox lifetime, e.g. `"1h"`, `"24h"`, `"7d"` — **preview only**; an App Link's URL is not bounded by it |
| `env` | no | Environment variables (string map) |

@@ -77,0 +128,0 @@ Uploads skip `node_modules`, `.git`, and dotfiles — dependencies install in the sandbox at

@@ -274,2 +274,3 @@ ---

| `ctx.sapiom` | `Sapiom` | The typed capability client — the `Sapiom` interface from `@sapiom/tools`, installed in your `node_modules` (see "Capabilities" below) |
| `ctx.isLocalTrace` | `boolean \| undefined` | `true` under `run_local`; **absent** on a deployed run. Gate raw I/O `run_local` cannot stub (see "Testing with `run_local`") |
| `ctx.organizationId` | `string \| null` | Tenant org |

@@ -602,2 +603,28 @@ | `ctx.tenantId` | `string \| null` | Tenant id |

### Gate I/O the stub cannot see
`run_local` stubs `ctx.sapiom.*` and nothing else. Anything holding its own connection — a
`postgres` client, raw HTTP to a third party — still runs for real. `database.get` succeeds
and hands back a DSN, but that DSN's host is in the reserved `.invalid` TLD and does not
resolve, so dialing it fails.
Resolve a `dryRun` gate **once, in the entry step**, and carry it in `ctx.shared`:
```ts
// entry step — `input` is the execution's entry input
const dryRun = input.dryRun ?? ctx.isLocalTrace ?? false;
ctx.shared.set("dryRun", dryRun);
if (!dryRun) {
// raw sockets, third-party HTTP — the things run_local cannot stub
}
// any later step: `input` is the previous step's output, so re-deriving it here
// would read live on a deployed run the caller asked to be dry.
const dryRun = ctx.shared.get("dryRun") ?? false;
```
`ctx.isLocalTrace` is absent on a deployed run, so `?? ctx.isLocalTrace` supplies the default
without changing production behaviour — and an explicit `{ "dryRun": false }` still forces the
live path when you want to exercise it.
`run_local` works with **no stubs** — capabilities return sensible defaults. Add

@@ -604,0 +631,0 @@ `.sapiom-dev/stubs.json` overrides only when a step branches on a specific result:

@@ -274,2 +274,3 @@ ---

| `ctx.sapiom` | `Sapiom` | The typed capability client — the `Sapiom` interface from `@sapiom/tools`, installed in your `node_modules` (see "Capabilities" below) |
| `ctx.isLocalTrace` | `boolean \| undefined` | `true` under `run_local`; **absent** on a deployed run. Gate raw I/O `run_local` cannot stub (see "Testing with `run_local`") |
| `ctx.organizationId` | `string \| null` | Tenant org |

@@ -602,2 +603,28 @@ | `ctx.tenantId` | `string \| null` | Tenant id |

### Gate I/O the stub cannot see
`run_local` stubs `ctx.sapiom.*` and nothing else. Anything holding its own connection — a
`postgres` client, raw HTTP to a third party — still runs for real. `database.get` succeeds
and hands back a DSN, but that DSN's host is in the reserved `.invalid` TLD and does not
resolve, so dialing it fails.
Resolve a `dryRun` gate **once, in the entry step**, and carry it in `ctx.shared`:
```ts
// entry step — `input` is the execution's entry input
const dryRun = input.dryRun ?? ctx.isLocalTrace ?? false;
ctx.shared.set("dryRun", dryRun);
if (!dryRun) {
// raw sockets, third-party HTTP — the things run_local cannot stub
}
// any later step: `input` is the previous step's output, so re-deriving it here
// would read live on a deployed run the caller asked to be dry.
const dryRun = ctx.shared.get("dryRun") ?? false;
```
`ctx.isLocalTrace` is absent on a deployed run, so `?? ctx.isLocalTrace` supplies the default
without changing production behaviour — and an explicit `{ "dryRun": false }` still forces the
live path when you want to exercise it.
`run_local` works with **no stubs** — capabilities return sensible defaults. Add

@@ -604,0 +631,0 @@ `.sapiom-dev/stubs.json` overrides only when a step branches on a specific result:

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet