@sapiom/agent-core 0.6.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.
Files changed (114) hide show
  1. package/CHANGELOG.md +246 -0
  2. package/LICENSE +21 -0
  3. package/dist/cjs/__tests__/execution-detail.wire.fixture.json +101 -0
  4. package/dist/cjs/__tests__/execution-projection.preseam.fixture.json +37 -0
  5. package/dist/cjs/__tests__/execution-projection.with-cost.fixture.json +60 -0
  6. package/dist/cjs/bundle.d.ts +5 -0
  7. package/dist/cjs/bundle.js +134 -0
  8. package/dist/cjs/check.d.ts +10 -0
  9. package/dist/cjs/check.js +154 -0
  10. package/dist/cjs/client.d.ts +21 -0
  11. package/dist/cjs/client.js +108 -0
  12. package/dist/cjs/clone.d.ts +17 -0
  13. package/dist/cjs/clone.js +71 -0
  14. package/dist/cjs/config.d.ts +16 -0
  15. package/dist/cjs/config.js +40 -0
  16. package/dist/cjs/decode.d.ts +4 -0
  17. package/dist/cjs/decode.js +172 -0
  18. package/dist/cjs/deploy.d.ts +12 -0
  19. package/dist/cjs/deploy.js +67 -0
  20. package/dist/cjs/errors.d.ts +15 -0
  21. package/dist/cjs/errors.js +23 -0
  22. package/dist/cjs/git.d.ts +12 -0
  23. package/dist/cjs/git.js +76 -0
  24. package/dist/cjs/index.d.ts +41 -0
  25. package/dist/cjs/index.js +72 -0
  26. package/dist/cjs/inspect.d.ts +40 -0
  27. package/dist/cjs/inspect.js +99 -0
  28. package/dist/cjs/link.d.ts +15 -0
  29. package/dist/cjs/link.js +19 -0
  30. package/dist/cjs/local/dispatcher.d.ts +37 -0
  31. package/dist/cjs/local/dispatcher.js +149 -0
  32. package/dist/cjs/local/load.d.ts +6 -0
  33. package/dist/cjs/local/load.js +102 -0
  34. package/dist/cjs/local/run-local.d.ts +32 -0
  35. package/dist/cjs/local/run-local.js +95 -0
  36. package/dist/cjs/local/stubs.d.ts +8 -0
  37. package/dist/cjs/local/stubs.js +35 -0
  38. package/dist/cjs/run.d.ts +11 -0
  39. package/dist/cjs/run.js +29 -0
  40. package/dist/cjs/scaffold.d.ts +24 -0
  41. package/dist/cjs/scaffold.js +190 -0
  42. package/dist/cjs/schedule.d.ts +67 -0
  43. package/dist/cjs/schedule.js +33 -0
  44. package/dist/cjs/signal.d.ts +12 -0
  45. package/dist/cjs/signal.js +24 -0
  46. package/dist/cjs/types.d.ts +99 -0
  47. package/dist/cjs/types.js +11 -0
  48. package/dist/cjs/watch.d.ts +10 -0
  49. package/dist/cjs/watch.js +101 -0
  50. package/dist/esm/__tests__/execution-detail.wire.fixture.json +101 -0
  51. package/dist/esm/__tests__/execution-projection.preseam.fixture.json +37 -0
  52. package/dist/esm/__tests__/execution-projection.with-cost.fixture.json +60 -0
  53. package/dist/esm/bundle.d.ts +5 -0
  54. package/dist/esm/bundle.js +95 -0
  55. package/dist/esm/check.d.ts +10 -0
  56. package/dist/esm/check.js +115 -0
  57. package/dist/esm/client.d.ts +21 -0
  58. package/dist/esm/client.js +103 -0
  59. package/dist/esm/clone.d.ts +17 -0
  60. package/dist/esm/clone.js +65 -0
  61. package/dist/esm/config.d.ts +16 -0
  62. package/dist/esm/config.js +31 -0
  63. package/dist/esm/decode.d.ts +4 -0
  64. package/dist/esm/decode.js +167 -0
  65. package/dist/esm/deploy.d.ts +12 -0
  66. package/dist/esm/deploy.js +61 -0
  67. package/dist/esm/errors.d.ts +15 -0
  68. package/dist/esm/errors.js +19 -0
  69. package/dist/esm/git.d.ts +12 -0
  70. package/dist/esm/git.js +69 -0
  71. package/dist/esm/index.d.ts +41 -0
  72. package/dist/esm/index.js +21 -0
  73. package/dist/esm/inspect.d.ts +40 -0
  74. package/dist/esm/inspect.js +92 -0
  75. package/dist/esm/link.d.ts +15 -0
  76. package/dist/esm/link.js +16 -0
  77. package/dist/esm/local/dispatcher.d.ts +37 -0
  78. package/dist/esm/local/dispatcher.js +145 -0
  79. package/dist/esm/local/load.d.ts +6 -0
  80. package/dist/esm/local/load.js +63 -0
  81. package/dist/esm/local/run-local.d.ts +32 -0
  82. package/dist/esm/local/run-local.js +87 -0
  83. package/dist/esm/local/stubs.d.ts +8 -0
  84. package/dist/esm/local/stubs.js +31 -0
  85. package/dist/esm/package.json +1 -0
  86. package/dist/esm/run.d.ts +11 -0
  87. package/dist/esm/run.js +25 -0
  88. package/dist/esm/scaffold.d.ts +24 -0
  89. package/dist/esm/scaffold.js +179 -0
  90. package/dist/esm/schedule.d.ts +67 -0
  91. package/dist/esm/schedule.js +26 -0
  92. package/dist/esm/signal.d.ts +12 -0
  93. package/dist/esm/signal.js +20 -0
  94. package/dist/esm/types.d.ts +99 -0
  95. package/dist/esm/types.js +8 -0
  96. package/dist/esm/watch.d.ts +10 -0
  97. package/dist/esm/watch.js +96 -0
  98. package/dist/tsconfig.cjs.tsbuildinfo +1 -0
  99. package/dist/tsconfig.esm.tsbuildinfo +1 -0
  100. package/package.json +73 -0
  101. package/templates/coding-pause/AGENTS.md +48 -0
  102. package/templates/coding-pause/CLAUDE.md +7 -0
  103. package/templates/coding-pause/README.md +53 -0
  104. package/templates/coding-pause/_gitignore +3 -0
  105. package/templates/coding-pause/index.ts +109 -0
  106. package/templates/coding-pause/package.json +21 -0
  107. package/templates/coding-pause/tsconfig.json +12 -0
  108. package/templates/default/AGENTS.md +48 -0
  109. package/templates/default/CLAUDE.md +7 -0
  110. package/templates/default/README.md +30 -0
  111. package/templates/default/_gitignore +3 -0
  112. package/templates/default/index.ts +26 -0
  113. package/templates/default/package.json +21 -0
  114. package/templates/default/tsconfig.json +12 -0
@@ -0,0 +1,48 @@
1
+ # Working in this orchestration
2
+
3
+ This project defines exactly one Sapiom orchestration in `index.ts`, authored against `@sapiom/agent`. Inside a step's `run`, Sapiom capabilities are on `ctx.sapiom` (e.g. `ctx.sapiom.repositories.list()`, `repo.pushFromSandbox(...)`).
4
+
5
+ ## Authoring
6
+
7
+ - An orchestration is `defineAgent({ entry, steps })`; each step is `defineStep({ name, next, run })`. Keep exactly one `defineAgent(...)` export.
8
+ - **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.
9
+
10
+ ## Validating
11
+
12
+ When you've made a coherent change and want to validate it — the same point you'd run tests in any project — this project ships a near-complete local suite. Reach for it then; you don't need to run it after every small edit.
13
+
14
+ - **`npm run typecheck`** — types, and confirms every `ctx.sapiom.*` capability/method you used exists.
15
+ - **check** — typecheck + bundle + manifest + step-graph validation. The full local pre-flight before deploy.
16
+ - **run_local** — runs your **real** step code locally against **stub capabilities**: every `ctx.sapiom.*` call (namespace calls *and* handle methods like `repo.pushFromSandbox`) returns a built-in default, so a workflow runs end-to-end with zero setup. Returns a per-step trace.
17
+ - **deploy** — ship it.
18
+
19
+ > Write each step the way it should run in production. `run_local` adapts to your code (stub capabilities), not the other way around — never weaken or drop real logic to shape a local run.
20
+
21
+ ## Stubs (overrides)
22
+
23
+ `run_local` works with **no stubs** — capabilities return sensible defaults. Add an override only when a step's logic branches on a specific result (e.g. "if `repositories.list()` already contains my repo, skip create"). Put overrides in `.sapiom-dev/stubs.json` (committed and human-reviewable); `run_local` reads it automatically. Shape:
24
+
25
+ ```jsonc
26
+ { "version": 1, "steps": { "<stepName>": { "<capability.path>": <response> } } }
27
+ ```
28
+
29
+ - Capability paths are namespace methods (`repositories.list`, `repositories.create`, `agent.coding.run`) or handle methods, which use the **singular** handle type (`repository.pushFromSandbox`, `sandbox.exec`) — not the plural namespace.
30
+ - `<response>` is returned **verbatim** — it is the value that call would return, so match its real shape. `repositories.list` takes the array `list()` returns: `[{ "slug": "...", "cloneUrl": "..." }]` (each element a repository — *not* `[[ … ]]`). `repositories.create`/`get`/`attach` take a single `{ "slug", "cloneUrl" }`.
31
+ - `run_local` reports **`unusedStubs`** (a key that matched no call — usually a typo or the plural/singular mistake) and **`stubWarnings`** (a key matched but the value was the wrong shape). A green run with either non-empty means a stub silently didn't take effect — check them.
32
+
33
+ ## Dispatched runs: pause & resume
34
+
35
+ A long-running capability (today the coding agent) is launched fire-and-forget and the workflow suspends until it finishes:
36
+
37
+ ```ts
38
+ const run = await ctx.sapiom.models.coding.launch({ task, gitRepository: repo }); // returns a handle, not a result
39
+ return pauseUntilSignal(run, { resumeStep: "finalize" }); // suspend on the run's result signal
40
+ ```
41
+
42
+ - **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.
43
+ - 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). Anything else the resumed step needs, stash in `ctx.shared` before pausing.
44
+ - **To stub the resume payload** (e.g. to exercise the failure branch), override `agent.coding.run` *in the launching step* — that one value is both the `run()` result and the payload the paused step resumes with. `agent.coding.launch` is accepted there too.
45
+
46
+ ## Determinism
47
+
48
+ A step body runs **once** on the happy path; it re-runs only on retry (after a throw). Don't rely on a value being recomputed identically across a pause/resume — capture non-deterministic values (timestamps, ids) once and pass them forward via the `goto(...)` input or `ctx.shared`.
@@ -0,0 +1,7 @@
1
+ # Working in this orchestration
2
+
3
+ Follow [AGENTS.md](./AGENTS.md) for authoring and validation.
4
+
5
+ This project ships a near-complete local suite — `npm run typecheck`, the **check** tool (typecheck + bundle + manifest + graph), and **run_local** (runs your real step code against stub capabilities, including handle methods like `repo.pushFromSandbox`). Reach for it when you'd normally validate a change (not after every small edit), and rely on `check` / `typecheck` to confirm the `ctx.sapiom.*` capabilities you used exist.
6
+
7
+ `run_local` works with no stubs (capabilities return defaults); add overrides in `.sapiom-dev/stubs.json` only for results a step branches on. Write each step the way it should run in production — never weaken or drop real logic to shape a local run.
@@ -0,0 +1,53 @@
1
+ # __PROJECT_NAME__
2
+
3
+ A Sapiom orchestration that runs a **coding agent without blocking the workflow**:
4
+ it launches the agent fire-and-forget, suspends on the run's result signal, and
5
+ resumes once the run finishes — authored as code against
6
+ [`@sapiom/agent`](https://www.npmjs.com/package/@sapiom/agent).
7
+
8
+ ```
9
+ prepare → kickoff ──pause(models.coding.result)──▶ finalize
10
+ ```
11
+
12
+ - **kickoff** calls `agent.coding.launch(...)` (which returns a handle, *not* a
13
+ result) and returns `pauseUntilSignal(handle, { resumeStep: "finalize" })`. The
14
+ workflow suspends — a long run holds no worker.
15
+ - **finalize** is resumed by the engine when the run reaches a terminal state.
16
+ Its `input` **is** the run's result signal payload (typed `CodingResultPayload`).
17
+ Because that payload crossed the pause/resume boundary, `sandbox` is plain data
18
+ — re-attach a live handle with `ctx.sapiom.sandboxes.attach(name)` to push.
19
+
20
+ State that must survive the pause goes in `ctx.shared`. To test the **failure**
21
+ branch locally, stub a failed result under the *launching* step in
22
+ `.sapiom-dev/stubs.json` — that value is also the resume payload:
23
+
24
+ ```jsonc
25
+ { "version": 1, "steps": { "kickoff": { "agent.coding.launch": { "status": "failed", "result": { "success": false }, "error": { "stage": "run", "message": "…" } } } } }
26
+ ```
27
+
28
+ ## Getting started
29
+
30
+ ```sh
31
+ npm install
32
+ ```
33
+
34
+ Then open `index.ts`. The orchestration is defined with `defineAgent({ steps })`; each step is a `defineStep({ name, next, run })`. The `run` body is ordinary code — and inside it, the full Sapiom tool catalog is available, pre-auth'd and tenant-scoped, on `ctx.sapiom`:
35
+
36
+ ```ts
37
+ const box = await ctx.sapiom.sandboxes.create({ name: 'demo' });
38
+ const repo = await ctx.sapiom.repositories.create('my-repo');
39
+ ```
40
+
41
+ No credentials to wire — a per-execution tenant credential is injected when your orchestration runs.
42
+
43
+ ## The loop
44
+
45
+ Author and run this orchestration with the Sapiom dev tools:
46
+
47
+ - **check** — validate locally (bundle, manifest, step graph). Offline.
48
+ - **run_local** — execute the steps locally against stubs (no real capability calls), iterating until it completes.
49
+ - **deploy** — build and ship.
50
+
51
+ `npm run typecheck` and `npm run format` are also available for editor-level checks.
52
+
53
+ See `AGENTS.md` for the full authoring loop.
@@ -0,0 +1,3 @@
1
+ node_modules
2
+ dist
3
+ *.tsbuildinfo
@@ -0,0 +1,109 @@
1
+ import {
2
+ defineAgent,
3
+ defineStep,
4
+ goto,
5
+ terminate,
6
+ fail,
7
+ pauseUntilSignal,
8
+ } from "@sapiom/agent";
9
+ import { CODING_RESULT_SIGNAL, type CodingResultPayload } from "@sapiom/tools";
10
+
11
+ /**
12
+ * __PROJECT_NAME__ — a non-blocking coding-agent workflow.
13
+ *
14
+ * prepare → kickoff ──pause(models.coding.result)──▶ finalize
15
+ *
16
+ * `kickoff` *launches* the coding agent (fire-and-forget) and returns
17
+ * `pauseUntilSignal(handle, …)`, suspending the workflow on the run's result
18
+ * signal — so a long run holds no worker. When the run reaches a terminal state
19
+ * the engine resumes `finalize` with the run result as its input.
20
+ *
21
+ * Key idea: the resumed step's `input` IS the signal payload (typed here as
22
+ * `CodingResultPayload`). It crossed a wire boundary, so there are no live
23
+ * handles — re-attach the run's sandbox from `executionEnvironment` with
24
+ * `ctx.sapiom.sandboxes.attach(executionEnvironment.id)`. Anything else the
25
+ * resumed step needs is stashed in `ctx.shared` before pausing.
26
+ */
27
+
28
+ const REPO_SLUG = "__PROJECT_NAME__-notes";
29
+
30
+ interface Shared extends Record<string, unknown> {
31
+ slug: string;
32
+ cloneUrl: string;
33
+ }
34
+
35
+ /** Find the repo, or create it on the first run. */
36
+ const prepare = defineStep({
37
+ name: "prepare",
38
+ next: ["kickoff"],
39
+ async run(_input, ctx) {
40
+ const existing = await ctx.sapiom.repositories.list();
41
+ const repo =
42
+ existing.find((r) => r.slug === REPO_SLUG) ??
43
+ (await ctx.sapiom.repositories.create(REPO_SLUG));
44
+ ctx.shared.set("slug", repo.slug);
45
+ ctx.shared.set("cloneUrl", repo.cloneUrl);
46
+ return goto("kickoff", {});
47
+ },
48
+ });
49
+
50
+ /** Launch the agent and suspend until it finishes — do NOT await the run. */
51
+ const kickoff = defineStep({
52
+ name: "kickoff",
53
+ next: [],
54
+ pause: { signal: CODING_RESULT_SIGNAL, resumeStep: "finalize" },
55
+ async run(_input, ctx) {
56
+ const repo = ctx.sapiom.repositories.attach(
57
+ ctx.shared.get("slug") as string,
58
+ ctx.shared.get("cloneUrl") as string,
59
+ );
60
+ const run = await ctx.sapiom.models.coding.launch({
61
+ task: "Make a small, self-contained change to this repository and commit it.",
62
+ gitRepository: repo, // auto-cloned into the sandbox at /workspace/<slug>
63
+ });
64
+ ctx.logger.info("agent launched; suspending until it finishes", {
65
+ runId: run.runId,
66
+ });
67
+ return pauseUntilSignal(run, { resumeStep: "finalize" });
68
+ },
69
+ });
70
+
71
+ /** Resumed once the run is done. The `input` is the coding result payload. */
72
+ const finalize = defineStep({
73
+ name: "finalize",
74
+ next: [],
75
+ terminal: true,
76
+ canFail: true,
77
+ async run(run: CodingResultPayload, ctx) {
78
+ if (run.status !== "completed" || !run.result?.success) {
79
+ return fail(
80
+ `coding agent did not succeed: ${run.error?.message ?? run.status}`,
81
+ );
82
+ }
83
+ // Re-attach live handles from plain values, then publish the agent's work.
84
+ const repo = ctx.sapiom.repositories.attach(
85
+ ctx.shared.get("slug") as string,
86
+ ctx.shared.get("cloneUrl") as string,
87
+ );
88
+ const env = run.executionEnvironment;
89
+ if (!env) {
90
+ return fail("coding run did not provision an execution environment");
91
+ }
92
+ const sandbox = ctx.sapiom.sandboxes.attach(env.id);
93
+ const push = await repo.pushFromSandbox(sandbox, {
94
+ message: "chore: automated change",
95
+ });
96
+ return terminate({
97
+ runId: run.runId,
98
+ pushed: push.pushed,
99
+ sha: push.sha,
100
+ summary: run.summary,
101
+ });
102
+ },
103
+ });
104
+
105
+ export const agent = defineAgent<unknown, Shared>({
106
+ name: "__PROJECT_NAME__",
107
+ entry: "prepare",
108
+ steps: { prepare, kickoff, finalize },
109
+ });
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "typecheck": "tsc --noEmit",
8
+ "format": "prettier --write \"**/*.ts\"",
9
+ "format:check": "prettier --check \"**/*.ts\""
10
+ },
11
+ "dependencies": {
12
+ "@sapiom/agent": "__AGENT_VERSION__",
13
+ "@sapiom/tools": "__TOOLS_VERSION__",
14
+ "zod": "__ZOD_VERSION__"
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^20.11.30",
18
+ "prettier": "^3.2.5",
19
+ "typescript": "^5.4.2"
20
+ }
21
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "esnext",
4
+ "moduleResolution": "bundler",
5
+ "target": "es2022",
6
+ "strict": true,
7
+ "skipLibCheck": true,
8
+ "types": ["node"],
9
+ "noEmit": true
10
+ },
11
+ "include": ["**/*.ts"]
12
+ }
@@ -0,0 +1,48 @@
1
+ # Working in this orchestration
2
+
3
+ This project defines exactly one Sapiom orchestration in `index.ts`, authored against `@sapiom/agent`. Inside a step's `run`, Sapiom capabilities are on `ctx.sapiom` (e.g. `ctx.sapiom.repositories.list()`, `repo.pushFromSandbox(...)`).
4
+
5
+ ## Authoring
6
+
7
+ - An orchestration is `defineAgent({ entry, steps })`; each step is `defineStep({ name, next, run })`. Keep exactly one `defineAgent(...)` export.
8
+ - **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.
9
+
10
+ ## Validating
11
+
12
+ When you've made a coherent change and want to validate it — the same point you'd run tests in any project — this project ships a near-complete local suite. Reach for it then; you don't need to run it after every small edit.
13
+
14
+ - **`npm run typecheck`** — types, and confirms every `ctx.sapiom.*` capability/method you used exists.
15
+ - **check** — typecheck + bundle + manifest + step-graph validation. The full local pre-flight before deploy.
16
+ - **run_local** — runs your **real** step code locally against **stub capabilities**: every `ctx.sapiom.*` call (namespace calls *and* handle methods like `repo.pushFromSandbox`) returns a built-in default, so a workflow runs end-to-end with zero setup. Returns a per-step trace.
17
+ - **deploy** — ship it.
18
+
19
+ > Write each step the way it should run in production. `run_local` adapts to your code (stub capabilities), not the other way around — never weaken or drop real logic to shape a local run.
20
+
21
+ ## Stubs (overrides)
22
+
23
+ `run_local` works with **no stubs** — capabilities return sensible defaults. Add an override only when a step's logic branches on a specific result (e.g. "if `repositories.list()` already contains my repo, skip create"). Put overrides in `.sapiom-dev/stubs.json` (committed and human-reviewable); `run_local` reads it automatically. Shape:
24
+
25
+ ```jsonc
26
+ { "version": 1, "steps": { "<stepName>": { "<capability.path>": <response> } } }
27
+ ```
28
+
29
+ - Capability paths are namespace methods (`repositories.list`, `repositories.create`, `agent.coding.run`) or handle methods, which use the **singular** handle type (`repository.pushFromSandbox`, `sandbox.exec`) — not the plural namespace.
30
+ - `<response>` is returned **verbatim** — it is the value that call would return, so match its real shape. `repositories.list` takes the array `list()` returns: `[{ "slug": "...", "cloneUrl": "..." }]` (each element a repository — *not* `[[ … ]]`). `repositories.create`/`get`/`attach` take a single `{ "slug", "cloneUrl" }`.
31
+ - `run_local` reports **`unusedStubs`** (a key that matched no call — usually a typo or the plural/singular mistake) and **`stubWarnings`** (a key matched but the value was the wrong shape). A green run with either non-empty means a stub silently didn't take effect — check them.
32
+
33
+ ## Dispatched runs: pause & resume
34
+
35
+ A long-running capability (today the coding agent) is launched fire-and-forget and the workflow suspends until it finishes:
36
+
37
+ ```ts
38
+ const run = await ctx.sapiom.models.coding.launch({ task, gitRepository: repo }); // returns a handle, not a result
39
+ return pauseUntilSignal(run, { resumeStep: "finalize" }); // suspend on the run's result signal
40
+ ```
41
+
42
+ - **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.
43
+ - 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). Anything else the resumed step needs, stash in `ctx.shared` before pausing.
44
+ - **To stub the resume payload** (e.g. to exercise the failure branch), override `agent.coding.run` *in the launching step* — that one value is both the `run()` result and the payload the paused step resumes with. `agent.coding.launch` is accepted there too.
45
+
46
+ ## Determinism
47
+
48
+ A step body runs **once** on the happy path; it re-runs only on retry (after a throw). Don't rely on a value being recomputed identically across a pause/resume — capture non-deterministic values (timestamps, ids) once and pass them forward via the `goto(...)` input or `ctx.shared`.
@@ -0,0 +1,7 @@
1
+ # Working in this orchestration
2
+
3
+ Follow [AGENTS.md](./AGENTS.md) for authoring and validation.
4
+
5
+ This project ships a near-complete local suite — `npm run typecheck`, the **check** tool (typecheck + bundle + manifest + graph), and **run_local** (runs your real step code against stub capabilities, including handle methods like `repo.pushFromSandbox`). Reach for it when you'd normally validate a change (not after every small edit), and rely on `check` / `typecheck` to confirm the `ctx.sapiom.*` capabilities you used exist.
6
+
7
+ `run_local` works with no stubs (capabilities return defaults); add overrides in `.sapiom-dev/stubs.json` only for results a step branches on. Write each step the way it should run in production — never weaken or drop real logic to shape a local run.
@@ -0,0 +1,30 @@
1
+ # __PROJECT_NAME__
2
+
3
+ A Sapiom orchestration, authored as code against [`@sapiom/agent`](https://www.npmjs.com/package/@sapiom/agent).
4
+
5
+ ## Getting started
6
+
7
+ ```sh
8
+ npm install
9
+ ```
10
+
11
+ Then open `index.ts`. The orchestration is defined with `defineAgent({ steps })`; each step is a `defineStep({ name, next, run })`. The `run` body is ordinary code — and inside it, the full Sapiom tool catalog is available, pre-auth'd and tenant-scoped, on `ctx.sapiom`:
12
+
13
+ ```ts
14
+ const box = await ctx.sapiom.sandboxes.create({ name: 'demo' });
15
+ const repo = await ctx.sapiom.repositories.create('my-repo');
16
+ ```
17
+
18
+ No credentials to wire — a per-execution tenant credential is injected when your orchestration runs.
19
+
20
+ ## The loop
21
+
22
+ Author and run this orchestration with the Sapiom dev tools:
23
+
24
+ - **check** — validate locally (bundle, manifest, step graph). Offline.
25
+ - **run_local** — execute the steps locally against stubs (no real capability calls), iterating until it completes.
26
+ - **deploy** — build and ship.
27
+
28
+ `npm run typecheck` and `npm run format` are also available for editor-level checks.
29
+
30
+ See `AGENTS.md` for the full authoring loop.
@@ -0,0 +1,3 @@
1
+ node_modules
2
+ dist
3
+ *.tsbuildinfo
@@ -0,0 +1,26 @@
1
+ import { defineAgent, defineStep, goto, terminate } from '@sapiom/agent';
2
+
3
+ const start = defineStep({
4
+ name: 'start',
5
+ next: ['finish'],
6
+ async run(input, ctx) {
7
+ ctx.logger.info('starting', { input });
8
+ // Sapiom capabilities: ctx.sapiom.sandboxes.create(), ctx.sapiom.repositories.create()
9
+ return goto('finish', { greeting: 'hello from Sapiom' });
10
+ },
11
+ });
12
+
13
+ const finish = defineStep({
14
+ name: 'finish',
15
+ next: [],
16
+ terminal: true,
17
+ async run() {
18
+ return terminate({ done: true });
19
+ },
20
+ });
21
+
22
+ export const agent = defineAgent({
23
+ name: '__PROJECT_NAME__',
24
+ entry: 'start',
25
+ steps: { start, finish },
26
+ });
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "typecheck": "tsc --noEmit",
8
+ "format": "prettier --write \"**/*.ts\"",
9
+ "format:check": "prettier --check \"**/*.ts\""
10
+ },
11
+ "dependencies": {
12
+ "@sapiom/agent": "__AGENT_VERSION__",
13
+ "@sapiom/tools": "__TOOLS_VERSION__",
14
+ "zod": "__ZOD_VERSION__"
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^20.11.30",
18
+ "prettier": "^3.2.5",
19
+ "typescript": "^5.4.2"
20
+ }
21
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "esnext",
4
+ "moduleResolution": "bundler",
5
+ "target": "es2022",
6
+ "strict": true,
7
+ "skipLibCheck": true,
8
+ "types": ["node"],
9
+ "noEmit": true
10
+ },
11
+ "include": ["**/*.ts"]
12
+ }