@sapiom/harness 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +151 -0
- package/dist/cli/args.d.ts +16 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +80 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/bin.js +30 -66
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/consent.js +1 -1
- package/dist/cli/settings.d.ts.map +1 -1
- package/dist/cli/settings.js +33 -1
- package/dist/cli/settings.js.map +1 -1
- package/dist/core/adapters/adapter.d.ts +2 -2
- package/dist/core/adapters/claude-code.d.ts +60 -2
- package/dist/core/adapters/claude-code.d.ts.map +1 -1
- package/dist/core/adapters/claude-code.js +119 -32
- package/dist/core/adapters/claude-code.js.map +1 -1
- package/dist/core/adapters/codex.d.ts +21 -2
- package/dist/core/adapters/codex.d.ts.map +1 -1
- package/dist/core/adapters/codex.js +53 -2
- package/dist/core/adapters/codex.js.map +1 -1
- package/dist/core/adapters/detect.d.ts +9 -0
- package/dist/core/adapters/detect.d.ts.map +1 -1
- package/dist/core/adapters/detect.js +9 -0
- package/dist/core/adapters/detect.js.map +1 -1
- package/dist/core/api-key-provider.d.ts +79 -0
- package/dist/core/api-key-provider.d.ts.map +1 -0
- package/dist/core/api-key-provider.js +91 -0
- package/dist/core/api-key-provider.js.map +1 -0
- package/dist/core/canvas-body.d.ts +28 -28
- package/dist/core/canvas-body.d.ts.map +1 -1
- package/dist/core/canvas-body.js +117 -55
- package/dist/core/canvas-body.js.map +1 -1
- package/dist/core/canvas-derive.d.ts +43 -0
- package/dist/core/canvas-derive.d.ts.map +1 -0
- package/dist/core/canvas-derive.js +95 -0
- package/dist/core/canvas-derive.js.map +1 -0
- package/dist/core/canvas-enrichment.d.ts +52 -127
- package/dist/core/canvas-enrichment.d.ts.map +1 -1
- package/dist/core/canvas-enrichment.js +17 -178
- package/dist/core/canvas-enrichment.js.map +1 -1
- package/dist/core/canvas-graph.d.ts +40 -1
- package/dist/core/canvas-graph.d.ts.map +1 -1
- package/dist/core/canvas-graph.js +71 -9
- package/dist/core/canvas-graph.js.map +1 -1
- package/dist/core/canvas-interconnections.d.ts +26 -8
- package/dist/core/canvas-interconnections.d.ts.map +1 -1
- package/dist/core/canvas-interconnections.js +132 -24
- package/dist/core/canvas-interconnections.js.map +1 -1
- package/dist/core/canvas-manifest-check.d.ts.map +1 -1
- package/dist/core/canvas-manifest-check.js +15 -2
- package/dist/core/canvas-manifest-check.js.map +1 -1
- package/dist/core/canvas-render.d.ts +8 -15
- package/dist/core/canvas-render.d.ts.map +1 -1
- package/dist/core/canvas-render.js +29 -41
- package/dist/core/canvas-render.js.map +1 -1
- package/dist/core/canvas-run-state.d.ts +50 -5
- package/dist/core/canvas-run-state.d.ts.map +1 -1
- package/dist/core/canvas-run-state.js +176 -11
- package/dist/core/canvas-run-state.js.map +1 -1
- package/dist/core/canvas-svg.d.ts +9 -4
- package/dist/core/canvas-svg.d.ts.map +1 -1
- package/dist/core/canvas-svg.js +84 -13
- package/dist/core/canvas-svg.js.map +1 -1
- package/dist/core/canvas-template.d.ts +9 -10
- package/dist/core/canvas-template.d.ts.map +1 -1
- package/dist/core/canvas-template.js +57 -20
- package/dist/core/canvas-template.js.map +1 -1
- package/dist/core/canvas-watcher.d.ts +22 -1
- package/dist/core/canvas-watcher.d.ts.map +1 -1
- package/dist/core/canvas-watcher.js +111 -30
- package/dist/core/canvas-watcher.js.map +1 -1
- package/dist/core/collector/normalizer.d.ts +8 -0
- package/dist/core/collector/normalizer.d.ts.map +1 -1
- package/dist/core/collector/normalizer.js +8 -0
- package/dist/core/collector/normalizer.js.map +1 -1
- package/dist/core/collector/store.d.ts +75 -1
- package/dist/core/collector/store.d.ts.map +1 -1
- package/dist/core/collector/store.js +334 -2
- package/dist/core/collector/store.js.map +1 -1
- package/dist/core/definition-slug-resolver.d.ts +13 -0
- package/dist/core/definition-slug-resolver.d.ts.map +1 -1
- package/dist/core/definition-slug-resolver.js +29 -0
- package/dist/core/definition-slug-resolver.js.map +1 -1
- package/dist/core/errors.d.ts +10 -4
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +11 -5
- package/dist/core/errors.js.map +1 -1
- package/dist/core/example-seed.d.ts +8 -0
- package/dist/core/example-seed.d.ts.map +1 -1
- package/dist/core/example-seed.js +47 -10
- package/dist/core/example-seed.js.map +1 -1
- package/dist/core/inject/system-prompt.d.ts +8 -0
- package/dist/core/inject/system-prompt.d.ts.map +1 -1
- package/dist/core/inject/system-prompt.js +3 -1
- package/dist/core/inject/system-prompt.js.map +1 -1
- package/dist/core/macros.d.ts.map +1 -1
- package/dist/core/macros.js +5 -6
- package/dist/core/macros.js.map +1 -1
- package/dist/core/paths.d.ts +1 -0
- package/dist/core/paths.d.ts.map +1 -1
- package/dist/core/paths.js +1 -0
- package/dist/core/paths.js.map +1 -1
- package/dist/core/record-archive.d.ts +187 -0
- package/dist/core/record-archive.d.ts.map +1 -0
- package/dist/core/record-archive.js +465 -0
- package/dist/core/record-archive.js.map +1 -0
- package/dist/core/rehydration.d.ts +72 -0
- package/dist/core/rehydration.d.ts.map +1 -0
- package/dist/core/rehydration.js +72 -0
- package/dist/core/rehydration.js.map +1 -0
- package/dist/core/render-local-run.d.ts +82 -0
- package/dist/core/render-local-run.d.ts.map +1 -0
- package/dist/core/render-local-run.js +105 -0
- package/dist/core/render-local-run.js.map +1 -0
- package/dist/core/render-log-slice.d.ts +30 -0
- package/dist/core/render-log-slice.d.ts.map +1 -0
- package/dist/core/render-log-slice.js +49 -0
- package/dist/core/render-log-slice.js.map +1 -0
- package/dist/core/render-run-state.d.ts.map +1 -1
- package/dist/core/render-run-state.js +30 -49
- package/dist/core/render-run-state.js.map +1 -1
- package/dist/core/resume-brief.d.ts +130 -0
- package/dist/core/resume-brief.d.ts.map +1 -0
- package/dist/core/resume-brief.js +404 -0
- package/dist/core/resume-brief.js.map +1 -0
- package/dist/core/rolling-summary.d.ts +130 -0
- package/dist/core/rolling-summary.d.ts.map +1 -0
- package/dist/core/rolling-summary.js +227 -0
- package/dist/core/rolling-summary.js.map +1 -0
- package/dist/core/run-local-bootstrap.d.ts +93 -0
- package/dist/core/run-local-bootstrap.d.ts.map +1 -0
- package/dist/core/run-local-bootstrap.js +155 -0
- package/dist/core/run-local-bootstrap.js.map +1 -0
- package/dist/core/run-state.d.ts +16 -1
- package/dist/core/run-state.d.ts.map +1 -1
- package/dist/core/run-state.js +75 -25
- package/dist/core/run-state.js.map +1 -1
- package/dist/core/session-manager.d.ts +11 -3
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +56 -7
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-record.d.ts +165 -0
- package/dist/core/session-record.d.ts.map +1 -0
- package/dist/core/session-record.js +470 -0
- package/dist/core/session-record.js.map +1 -0
- package/dist/core/spawn-target.d.ts +12 -0
- package/dist/core/spawn-target.d.ts.map +1 -0
- package/dist/core/spawn-target.js +187 -0
- package/dist/core/spawn-target.js.map +1 -0
- package/dist/core/stub-feedback.d.ts +52 -0
- package/dist/core/stub-feedback.d.ts.map +1 -0
- package/dist/core/stub-feedback.js +33 -0
- package/dist/core/stub-feedback.js.map +1 -0
- package/dist/core/task-manager.d.ts.map +1 -1
- package/dist/core/task-manager.js +9 -1
- package/dist/core/task-manager.js.map +1 -1
- package/dist/core/template-catalog.d.ts +48 -0
- package/dist/core/template-catalog.d.ts.map +1 -0
- package/dist/core/template-catalog.js +338 -0
- package/dist/core/template-catalog.js.map +1 -0
- package/dist/core/workflow-registry.d.ts.map +1 -1
- package/dist/core/workflow-registry.js +2 -2
- package/dist/core/workflow-registry.js.map +1 -1
- package/dist/core/workspace-watcher.d.ts.map +1 -1
- package/dist/core/workspace-watcher.js +4 -3
- package/dist/core/workspace-watcher.js.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -1
- package/dist/profiles/default.d.ts.map +1 -1
- package/dist/profiles/default.js +9 -14
- package/dist/profiles/default.js.map +1 -1
- package/dist/server/actions.d.ts +139 -0
- package/dist/server/actions.d.ts.map +1 -0
- package/dist/server/actions.js +430 -0
- package/dist/server/actions.js.map +1 -0
- package/dist/server/auth-routes.d.ts +92 -0
- package/dist/server/auth-routes.d.ts.map +1 -0
- package/dist/server/auth-routes.js +151 -0
- package/dist/server/auth-routes.js.map +1 -0
- package/dist/server/canvas.d.ts.map +1 -1
- package/dist/server/canvas.js +7 -7
- package/dist/server/canvas.js.map +1 -1
- package/dist/server/fs.d.ts.map +1 -1
- package/dist/server/fs.js +17 -1
- package/dist/server/fs.js.map +1 -1
- package/dist/server/index.d.ts +12 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +0 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/ingest.d.ts +11 -0
- package/dist/server/ingest.d.ts.map +1 -1
- package/dist/server/ingest.js +1 -0
- package/dist/server/ingest.js.map +1 -1
- package/dist/server/macros.d.ts +4 -6
- package/dist/server/macros.d.ts.map +1 -1
- package/dist/server/macros.js.map +1 -1
- package/dist/server/rest.d.ts +18 -6
- package/dist/server/rest.d.ts.map +1 -1
- package/dist/server/rest.js +185 -50
- package/dist/server/rest.js.map +1 -1
- package/dist/server/runs.d.ts +16 -15
- package/dist/server/runs.d.ts.map +1 -1
- package/dist/server/runs.js +15 -80
- package/dist/server/runs.js.map +1 -1
- package/dist/server/static.d.ts +15 -1
- package/dist/server/static.d.ts.map +1 -1
- package/dist/server/static.js +54 -4
- package/dist/server/static.js.map +1 -1
- package/dist/server/templates.d.ts +24 -0
- package/dist/server/templates.d.ts.map +1 -0
- package/dist/server/templates.js +54 -0
- package/dist/server/templates.js.map +1 -0
- package/dist/shared/types.d.ts +558 -66
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js +19 -11
- package/dist/shared/types.js.map +1 -1
- package/dist/web/assets/index-DC65cskq.js +446 -0
- package/dist/web/assets/index-xtD-3FFa.css +32 -0
- package/dist/web/canvas/sess-boot/index.html +305 -0
- package/dist/web/index.html +4 -9
- package/package.json +8 -4
- package/dist/core/canvas-enrich.d.ts +0 -115
- package/dist/core/canvas-enrich.d.ts.map +0 -1
- package/dist/core/canvas-enrich.js +0 -186
- package/dist/core/canvas-enrich.js.map +0 -1
- package/dist/core/run-spend.d.ts +0 -55
- package/dist/core/run-spend.d.ts.map +0 -1
- package/dist/core/run-spend.js +0 -109
- package/dist/core/run-spend.js.map +0 -1
- package/dist/core/run-transactions.d.ts +0 -60
- package/dist/core/run-transactions.d.ts.map +0 -1
- package/dist/core/run-transactions.js +0 -129
- package/dist/core/run-transactions.js.map +0 -1
- package/dist/profiles/canvas-guidelines.d.ts +0 -20
- package/dist/profiles/canvas-guidelines.d.ts.map +0 -1
- package/dist/profiles/canvas-guidelines.js +0 -48
- package/dist/profiles/canvas-guidelines.js.map +0 -1
- package/dist/server/skills.d.ts +0 -51
- package/dist/server/skills.d.ts.map +0 -1
- package/dist/server/skills.js +0 -312
- package/dist/server/skills.js.map +0 -1
- package/dist/web/assets/index-DM84J83Y.js +0 -234
- package/dist/web/assets/index-DemtWW4L.css +0 -32
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,156 @@
|
|
|
1
1
|
# @sapiom/harness
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 3f96e37: Canvas board redesign + deterministic step/workflow metadata.
|
|
8
|
+
|
|
9
|
+
- **Board redesign** matching the new design: in-drawer step navigation removed (the chart is beside it), chat split into a standalone toggleable panel independent of the info panel, the board subheader dropped with the deployed pill and expand relocated to the tab bar, and the manual "Render diagram" button replaced by auto-render on bind/session-start.
|
|
10
|
+
- **Deterministic metadata:** the renderer surfaces per-step `description` / `inputSchema` / `capabilities` / `timeoutMs` and a workflow Overview payload, all read from the manifest (no LLM in the render path). When a step doesn't call a capability or declare a description, the field is simply absent — the shape summary still renders.
|
|
11
|
+
- **Capability auto-detect** from `sapiom.*` call sites in the workflow source, attributed to the `defineStep` block the call sits in (calls in shared helpers are left unattributed rather than mis-billed to the nearest step).
|
|
12
|
+
|
|
13
|
+
Reads the new optional `description` / `capabilities` fields from `@sapiom/agent`; workflows on an older SDK render with those fields blank.
|
|
14
|
+
|
|
15
|
+
- 7b98507: Studio: show a template's complexity band where the per-run cost estimate used to be.
|
|
16
|
+
|
|
17
|
+
The Templates dialog rendered `estCostPerRunUsd`, relayed from the same core endpoint the dashboard's Template library reads. Core stopped serving that field: it could only price capabilities metered per `call`, so the estimate was `null` for 21 of 26 templates, and a number that honest for 5 of them was not worth a slot on every card. Core now derives a **complexity band** — `Minimal` through `Advanced`, 1–5 — from each template's declared shape.
|
|
18
|
+
|
|
19
|
+
Without this change nothing errored, which is why it would have gone unnoticed: `template-catalog.ts`'s defensive narrowing turned the missing field into `null` and the formatter turned that into an em dash, so **every** template read `—` where a cost used to be.
|
|
20
|
+
|
|
21
|
+
`TemplateSummary.estCostPerRunUsd` is replaced by `TemplateSummary.complexity`, and the new `TemplateComplexity` / `TemplateComplexityBasis` types are exported alongside it. **Breaking for embedders** (hence `minor`): `src/index.ts` re-exports `./shared/types.js`, so code reading `estCostPerRunUsd` off a summary stops compiling.
|
|
22
|
+
|
|
23
|
+
The band is read, never computed. Whether core derives it (today) or serves an authored one later, this surface is unchanged.
|
|
24
|
+
|
|
25
|
+
`complexity` is typed nullable here even though core types it required, and that is deliberate rather than belt-and-braces. This is a published npm package: an old copy can point at any backend, and a fresh copy can point at a backend that predates the field — a local stack, a self-hosted one, production before a promotion. An unguarded dereference in the row renderer would take out the whole dialog, so a band that isn't there degrades that one row to an em dash instead. Note the glyph's meaning has changed: it used to mean "no cost estimate exists", the majority case; it now means "this response predates the band", and nobody should ever see it against a current backend.
|
|
26
|
+
|
|
27
|
+
On the card the band rides beside the step count, with the counts behind it in the tooltip. In the detail pane, the section retitles to "Capabilities and complexity" and the old three-state cost note collapses to a single line — the band plus what produced it ("2 model steps, 1 chained, 5 steps, 1 capability"), so it reads as an estimate of shape rather than an opaque verdict. Text only, no meter or dots, matching the dashboard's gallery.
|
|
28
|
+
|
|
29
|
+
- b199f93: Studio: read the template gallery live, and stop pitching the product at returning users.
|
|
30
|
+
|
|
31
|
+
**Templates come from the real catalog.** `web/src/lib/templates.ts` shipped a hardcoded copy of two registry entries (pinned at harness 0.1.4 / `f0e3406`) because, as its header said, no listing API exposed the gallery to any client. One exists now, so the Studio showed 2 templates while the dashboard's Template library showed 26. New `GET /api/templates` and `GET /api/templates/:id` relay core's `GET /v1/workflows/templates{,/:id}` — the same endpoint the dashboard renders, so the two surfaces can no longer drift. The dialog gains category grouping, search, and a per-template complexity band. Two contract details, both verified against a running backend: the core surface authenticates with `Authorization: Bearer` (the `x-sapiom-api-key` header the _agents_ surface takes returns 401 here), and the path carries the `/v1` prefix. The API key stays server-side, as with the runs router; a 401/403 triggers one credential refresh and retry. Signed out or with core unreachable, the dialog falls back to the bundled offline starters **and says which** — silence is what let a two-entry list read as the whole gallery.
|
|
32
|
+
|
|
33
|
+
The detail pane now projects the graph core actually serves — the engine's `DefinitionStepDto`/`DefinitionTransitionDto` shapes, where a step carries `stepName` and a singular `capabilityId`, edges reference steps by namespaced `id`, and a step's role is decided by its transition kinds rather than array order. Node kinds come from `classifyStepKind`, extracted from `canvas-graph.ts`'s `classifyNode` and now shared: the preview claims parity with the canvas, so it must not own a second copy of that precedence. All four kinds (`continue`/`pause`/`terminate`/`fail`) survive the projection — a fail-only sink renders amber "needs attention" rather than a green success exit, a `continue`-plus-`terminate` gate stays a mid-flow step, and a pause step shows its signal.
|
|
34
|
+
|
|
35
|
+
**Overview is a working surface, not a pitch.** `showWelcome` was `overviewSelected || (firstRun && !hasLiveSession)`, so the first-run hero rendered whenever the Overview tab was selected — including for someone with a rail full of workspaces. The hero is now genuine-first-run only; returning users get their recent workspaces, with the Docs / Templates / New workspace action band shared by both states.
|
|
36
|
+
|
|
37
|
+
**Workspace terminology.** A workspace is a folder, matching the rail and the editor convention users arrive with: the rail header reads "Workspaces", and "New project" / "Add project" / "Project directory" become their workspace equivalents. "Agent project" is left alone deliberately — that is the SDK's own term for a `sapiom.json` directory, and `sapiom agents init` and `AGENTS.md` both use it.
|
|
38
|
+
|
|
39
|
+
**The Sample project action is gone**, along with `POST /api/sample-project` and the exported `SampleProjectSeedResponse` type (nothing else in the repo referenced either). `core/example-seed.ts` remains — `scripts/seed-example.mjs` still uses it for demo prep.
|
|
40
|
+
|
|
41
|
+
**Breaking for embedders** (hence `minor`, not `patch`): `src/index.ts` re-exports `./shared/types.js`, so `SampleProjectSeedResponse` was part of the published surface, and `HarnessServerOptions` loses its `sampleProjectRoot` field. Code typed against either stops compiling. `TemplateStepView` also carries `kind`/`sublabel` rather than a `terminal` boolean — see above for why that collapse was wrong. No in-repo consumer is affected.
|
|
42
|
+
|
|
43
|
+
### Patch Changes
|
|
44
|
+
|
|
45
|
+
- c32f818: Session history now outlives the event log it is rebuilt from (SAP-2060).
|
|
46
|
+
|
|
47
|
+
H2 made past sessions readable by folding them out of `events.ndjson`. That file is an analytics sink with an analytics sink's retention — 50 MB / 30 days, truncated oldest-first — so a session a user could read today would quietly stop existing next month, with no way to tell "never recorded" from "swept".
|
|
48
|
+
|
|
49
|
+
- New `src/core/record-archive.ts`: at session end the folded `SessionRecord` is compacted and written to `~/.sapiom/harness/records/<harnessSessionId>.json`. Tool inputs and results are clipped hard (they are most of the bytes and the least useful part of a months-old record); prompts and assistant text — the conversation — are kept whole. Over ~64 KB it drops whole turns oldest-first, never below one. Both losses are declared as new `SessionRecord.limitations` codes (`compacted-archive`, `dropped-early-turns`) and the transcript view spells them out, alongside a note naming the record as an archived copy (`SessionRecord.archivedAt`).
|
|
50
|
+
- Bounded, not merely durable: the store enforces a 16 MB total and a 365-day age cap, oldest-first, swept after every write and once at boot. `turnCount` deliberately keeps the conversation's count even when turns were dropped, so a history row's count doesn't change when its events are swept.
|
|
51
|
+
- Written on the normal end of a session (the `SessionEnd` hook's event, via a new `onEventPersisted` ingest seam — fired from `onNormalizedEvent` it would race the append and store a record with no `endedAt`) and on an abnormal one (the session's transition to `exited`, which is all a killed pty gives us). A boot pass archives conversations the log still holds but the archive doesn't, which is what keeps history that predates this feature — and any session lost to a force-kill — from disappearing at its 30-day mark.
|
|
52
|
+
- Reads prefer whichever source still holds the WHOLE conversation. The ticket asked for "prefer the archive, fall back to events"; taken literally that serves a compacted excerpt for a session that ended a minute ago, and freezes a resumed conversation's record at its first exit. Since retention truncates oldest-first, an intact first event means the log lost nothing, so: the log when it still holds the beginning or has events newer than the archive, the archive otherwise, null when neither has anything (still an honest 404).
|
|
53
|
+
- Records live in their own root, NOT under `<generated>/<sessionId>/` as the ticket suggested: that directory is deleted the moment a session's pty exits and swept 7 days after going stale, both shorter than the 30 days of events the archive exists to outlive.
|
|
54
|
+
|
|
55
|
+
- 460bfc1: Expose the embedding surface so a second host (the Electron desktop app) can reuse the harness instead of forking it: re-export `startServer`/`HarnessServer`/`HarnessServerOptions` plus the setup helpers (`runDoctor`, `pickDefaultHarness`, `ensureAuthenticated`, `getOrCreateMachineId`, `ensureSpawnHelperExecutable`, settings, install-command constants) from the package entry. `saveSettings` is part of that surface: a host that prompts for telemetry consent natively (instead of through the TTY-shaped `ensureConsent`) must persist the answer itself, or the settings file — which the UI's analytics indicator and the next launch both read — never learns about it. No CLI behavior change.
|
|
56
|
+
|
|
57
|
+
Also run the Canvas step-graph check subprocess correctly when embedded in Electron: it spawns `process.execPath` (the Electron binary when embedded), so it now passes `ELECTRON_RUN_AS_NODE=1` — guarded by `process.versions.electron`, a no-op under the CLI's real Node. And `packageRoot()` (used as the subprocess `cwd`) now translates an `app.asar` path to its `app.asar.unpacked` twin, since a `cwd` inside the asar archive fails with `spawn ENOTDIR` (the host must `asarUnpack` the harness + its deps).
|
|
58
|
+
|
|
59
|
+
- c32f818: Portable continue: a session the agent can no longer reattach to is now continuable, by seeding a fresh session with our own reconstruction of it (SAP-2059).
|
|
60
|
+
|
|
61
|
+
H1 stopped offering Resume on rows that would fail; that left them with nowhere to go — a disabled button and "start a new session instead", even when our event log held the whole conversation. Resume-as-reattach can only ever work for the vendor that wrote the transcript, on the machine that wrote it. This adds the other half: `POST /api/sessions` takes `rehydrateFrom`, and the new session launches with a bounded markdown briefing about the old one.
|
|
62
|
+
|
|
63
|
+
- New `src/core/resume-brief.ts`: `buildResumeBrief(record, opts)` renders a `SessionRecord` as ~6k tokens of markdown — what the session was (title, cwd, branch, bound workflow + `definitionId`), the rolling summary when present, the last N turns with tool calls collapsed to name + target, and files written / commands run derived from `tool.call` inputs. Over budget it drops turns oldest-first, then the (hard-capped) digests, and clamps the summary only as a last resort.
|
|
64
|
+
- It leads with an honesty header and spells out each of the record's `limitations` in prose. A brief that reads like restored memory is worse than no brief: the agent would assert file contents and command results it never saw.
|
|
65
|
+
- Delivery is per-adapter, declared not inferred (`HarnessAdapter.systemPromptDelivery`). Both shipped adapters use `launch-flag` — the brief is appended to the generated system-prompt file, which claude-code reads via `--append-system-prompt` and codex inlines as `developer_instructions`, so one code path serves both with no adapter change. A harness with no prompt flag declares `post-ready-injection` and gets the brief through the ordinary input path once the session reports `ready`, never into a TUI sitting on a trust prompt.
|
|
66
|
+
- New `src/core/rolling-summary.ts`: opt-in via `HarnessSettings.rollingSummary` (off by default; toggle in Settings). Every 10 completed turns and once at session end, a bounded headless run (`launchTask`, cheap model, `--max-turns 1`) folds the record into `<generated>/<sessionId>/summary.md`. Fully detached from the ingest path — a turn is never slower or riskier for it. Codex has no `launchTask`, so its briefs degrade to last-N-turns, as does everyone's with the setting off.
|
|
67
|
+
- `HarnessSession.rehydratedFrom` records what actually happened, not what was asked for: a `rehydrateFrom` id our log holds nothing for still creates the session, with the field null and the UI saying the continue carried no context.
|
|
68
|
+
- UI: `resumeFromHistory` branches on the server-verified `resumeMode` instead of guessing, and the dead-session pane offers "Continue here" (with what will and won't carry over) wherever it has a record to seed from, instead of a disabled Resume.
|
|
69
|
+
|
|
70
|
+
- b7f5b02: Verify resumability before offering Resume, so no past-session row is a button that's guaranteed to fail (SAP-2057).
|
|
71
|
+
|
|
72
|
+
A row's Resume badge was `agentSessionId != null` — "our SessionStart hook fired once", not "the agent still has this conversation". Since neither Claude Code nor Codex writes any transcript for a session that never received a prompt, one in three registry rows on a real machine (16 of 49 measured) offered Resume and answered with `No conversation found with session ID: …`, exit 1, and a dead pane offering Resume again. Transcript-only rows had the inverse bug: hardcoded un-resumable even when the transcript was right there, so opening one silently started a fresh session and dropped the conversation.
|
|
73
|
+
|
|
74
|
+
- `HarnessAdapter` gains `canResume(agentSessionId, cwd)` (never throws): one `stat` on the encoded transcript path for claude-code, a `session_meta` id+cwd match for codex.
|
|
75
|
+
- Both adapters now resolve symlinked cwds. Claude Code encodes the cwd's **realpath**, so a session in `/tmp/foo` (macOS: `/tmp` → `/private/tmp`) stores its transcript under a different encoding than the registry's cwd string — history discovery silently missed those rows before, and a resumability probe would have gone further and refused a resume that works.
|
|
76
|
+
- `SessionSummary` gains `resumeMode: "agent-resume" | "rehydrate"`, resolved server-side in `GET /api/sessions/history` for both row sources. Adapters now return `PastSessionRecord`, so they can't decide it themselves.
|
|
77
|
+
- `SessionManager.resume()` pre-flights `canResume()` and throws `SessionNotResumeableError` (409, with a reason naming the agent) instead of spawning a doomed pty.
|
|
78
|
+
- New `POST /api/sessions/adopt` wires up `registerHistorical()`: a transcript-only row whose conversation the agent really holds is adopted into the registry and genuinely resumed. The server re-verifies resumability itself, and the route is idempotent.
|
|
79
|
+
- Truthful durations: a resume that never reaches a live pty no longer stamps `lastActiveAt`, so an idle session stops reporting "Ran for 6h 25m" after a failed Resume, and `formatDuration` returns null on a zero span instead of inventing "under a minute".
|
|
80
|
+
- UI: rows render the verified `resumeMode` (`resumable` / `archived`, `checking…` until known); the dead pane and past-session pane disable Resume with the real reason instead of a generic one.
|
|
81
|
+
|
|
82
|
+
- 460bfc1: Install the seeded sample project's dependencies on first creation, so the Canvas step-graph renders on first view instead of failing with "Could not resolve @sapiom/agent / zod". `seedExampleProject` now runs a best-effort `npm install` right after scaffolding (before the initial commit, keeping the gitignored `node_modules` out of history); it's non-fatal (missing/offline npm falls back to the existing "ask your agent to fix it" Canvas prompt) and skippable via the new `installDependencies` option (default true; tests pass false to stay offline). Adds `dependenciesInstalled` to `SeedExampleProjectResult`.
|
|
83
|
+
- 2d25205: Fix starting a session on Windows. An agent installed by npm is `claude.cmd`, and node-pty spawns via `CreateProcess`, which performs no `PATHEXT` resolution and cannot execute a `.cmd` at all — so every session failed with `Cannot create process, error code: 2` while `doctor` reported the agent present (detection shells `where`, which _does_ resolve `PATHEXT`). Background tasks and macros failed the same way through `child_process.spawn`.
|
|
84
|
+
|
|
85
|
+
Both paths now resolve the shim to what it really runs — Claude Code ships a native `bin\claude.exe`, other packages a `cli.js` run under node — and spawn that directly. Deliberately **not** via `cmd.exe`: node-pty escapes `"` as `\"` for `CreateProcess`, but cmd only counts raw quotes, so one embedded quote desynchronises its parser and any following `&`/`|` becomes a command separator (CVE-2024-27980's class, reachable on every session since the codex adapter passes `JSON.stringify(prompt)` as an argument). Resolving the target keeps arguments in exactly one quoting layer, with no shell involved.
|
|
86
|
+
|
|
87
|
+
Also exports `resolveSpawnTarget` and `createClaudeCodeAdapter` for hosts that spawn a pty themselves or need to point the adapter at a different binary. No change on macOS or Linux.
|
|
88
|
+
|
|
89
|
+
- Updated dependencies [3f96e37]
|
|
90
|
+
- @sapiom/agent@0.7.0
|
|
91
|
+
- @sapiom/agent-core@0.9.10
|
|
92
|
+
|
|
93
|
+
## 0.1.6
|
|
94
|
+
|
|
95
|
+
### Patch Changes
|
|
96
|
+
|
|
97
|
+
- b6b9d16: Add a server-side actions router with direct Deploy and Prod-run routes:
|
|
98
|
+
|
|
99
|
+
- `POST /api/workflows/:id/deploy` deploys a linked agent and streams build status as NDJSON (a `building` line up front, then a terminal `ready`/`error` line).
|
|
100
|
+
- `POST /api/runs` `{ definitionId, input }` starts an execution and returns `{ executionId }`.
|
|
101
|
+
|
|
102
|
+
Both run entirely server-side with the held API key (never exposed to the browser) and require no coding-agent session, so an action consumes no LLM credits.
|
|
103
|
+
|
|
104
|
+
- b6b9d16: Enrich the step debug/explain context with the run's real per-step evidence. When you ask the agent to debug or explain a step from the run inspector, the injected context now folds in the step's actual input and output, the capabilities it called (with a marker for any served by a stub), and — for offline runs — supplied stubs that matched nothing or carried the wrong shape, on top of the step's status, latency, error, and logs. Every section is emitted only when the trace carries it (no fabricated placeholders), and the context names capabilities, never a model, so "why did this step do X" carries the real evidence instead of just the step name.
|
|
105
|
+
- b6b9d16: Extend the expired/rotated API key recovery to the Deploy and Prod-run actions. When one of these actions is rejected as unauthorized, the Studio now re-reads your cached credentials and retries once — so signing in again (in the CLI or elsewhere) unblocks Deploy/Prod-run in place, matching the live-run status path, instead of every action staying stuck on the stale key until a restart.
|
|
106
|
+
- b6b9d16: The Harness Studio presents your coding agent in a terminal view.
|
|
107
|
+
- b6b9d16: Studio run and step-inspection hardening:
|
|
108
|
+
|
|
109
|
+
- Auto-bind a session to the workflow in its folder the moment the session starts, not only when a file later changes — so the canvas and Run actions light up immediately for an existing workflow.
|
|
110
|
+
- Animate the canvas board (per-step running / passed / failed status) during both local and production runs.
|
|
111
|
+
- Never let a direct action (Local Run / Prod Run / Deploy) fail silently: surface the reason on a blocked click, clear the in-flight indicator when the action settles, and distinguish "deploy failed — retry" from "not deployed yet".
|
|
112
|
+
- Enrich the step inspector: per-step input/output and logs, the capability calls a step made (with the served stub values on offline runs), and clickable preview / download / research links found in a step's output — all shown when you click into a step.
|
|
113
|
+
|
|
114
|
+
- b6b9d16: Degrade gracefully when the Studio is offline or the session drops. Losing your network connection no longer blanks the Studio: a boot failure now shows an honest, recoverable state (offline / session needs a refresh / server unreachable) with a Retry that reconnects in place, and a non-blocking banner appears if the connection drops mid-session so the app stays usable against its last-known state. A rejected credential surfaces as a recoverable "reconnect" state rather than a hard lockout. These states are wired to real signals (the browser's connectivity and the kind of the failed request).
|
|
115
|
+
- b6b9d16: Recover from an expired or rotated API key instead of getting stuck. When a live-run status request is rejected as unauthorized, the Studio now re-reads your cached credentials and retries once, so signing in again (in the CLI or elsewhere) unblocks the app in place rather than requiring a restart. Studio actions always authenticate with your held API key.
|
|
116
|
+
- b6b9d16: Remove the Studio's cost and pricing surfaces. The wallet card, the per-workflow price note, and per-step cost figures are gone; the run inspector now shows logs, latency, and pass/fail only. `StepView` no longer carries a `costUsd` field.
|
|
117
|
+
- b6b9d16: Remove the run spend and transactions endpoints (`GET /api/runs/:executionId/spend` and `/transactions`) and their supporting fetchers. The runs router now serves only run state (`GET /api/runs/:executionId/state`).
|
|
118
|
+
- b6b9d16: Restore the bundled demo canvas document (`web/public/canvas/sess-boot/`) the
|
|
119
|
+
Studio's mock/demo mode renders on first paint. The web app already references
|
|
120
|
+
it (the demo session opens on its seeded board), but the file was missing, so
|
|
121
|
+
the canvas pane stayed empty in demo mode. This is demo/mock-only content; real
|
|
122
|
+
local mode still renders the server-generated canvas.
|
|
123
|
+
- b6b9d16: Show real per-step input and output in the run inspector's "Last run" section. When a step's run recorded the value it ran on and the value it produced, each is rendered as a collapsible, inspectable payload; a step that carried no input/output shows no block at all (never a fabricated placeholder). Objects are pretty-printed, plain strings shown as-is, and a real `null`/`false`/`0` is displayed faithfully.
|
|
124
|
+
- b6b9d16: Add `POST /api/runs/local`: run an agent entirely offline against stub capabilities and stream the result back as NDJSON — one per-step trace line, then a terminal summary carrying the run outcome plus which supplied stub keys went unused or had the wrong shape. It runs in a child process, needs no sign-in, and makes no network call, so a local run works signed-out and at zero cost.
|
|
125
|
+
- b6b9d16: Serve the harness web UI from the package build and harden the design-system seam:
|
|
126
|
+
|
|
127
|
+
- `pnpm build` emits the web app to `dist/web` and the harness server serves it as the SPA (index.html, hashed assets, and client-side deep-route fallback), so `start` and `npx @sapiom/harness` launch the full UI against the real server. Adds a regression test pinning the build → serve path.
|
|
128
|
+
- The design system resolves to the real package when it's installed and falls back to a committed neutral, unbranded token set otherwise — so a public build renders legibly out of the box, with no theme source required. The stylesheet only bridges variable names onto tokens; it never redefines a token.
|
|
129
|
+
|
|
130
|
+
No behavioral or API changes to the harness server.
|
|
131
|
+
|
|
132
|
+
- b6b9d16: Surface how an offline run's stubs behaved in the run inspector. A step that ran in an offline (stub) run now shows a read-only "stubbed" chip on its row and in its detail, so it is clear its capability calls were served by stubs rather than real calls. The inspector also shows, when present, a read-only notice for supplied stubs that matched no capability call (a no-op mock — usually a typo or the wrong path) and for stubs whose value had the wrong shape — so a stub that silently did nothing is visible instead of a mystery. Nothing is shown when a run has no such issues, and the affordance names capabilities, never a model. Real (non-offline) runs are unaffected.
|
|
133
|
+
- b6b9d16: Refresh the harness web UI with a rebuilt workspace: a three-zone layout (an on-disk explorer of your agents, a per-agent workbench with a session tab strip, and session-keyed projections — Canvas / Steps / Code / Skills), a command palette, a chat/terminal view toggle, and a click-into-step run inspector. The bundle ships with a neutral, unbranded default theme so a public build renders legibly out of the box; light and dark are both supported. No behavioral or API changes to the harness server.
|
|
134
|
+
- b6b9d16: Point the harness web UI's `@shared/types` alias at the package's own shared contract instead of a vendored copy, so the web and server always build against a single source of truth. The snippet panel now reads the real deployed-agent slug and executions base URL when the server provides them, falling back cleanly when it does not. No behavioral or API changes to the harness server.
|
|
135
|
+
- b6b9d16: Wire prod and offline run logs into the Studio's click-into-step run inspector.
|
|
136
|
+
|
|
137
|
+
- **Prod runs** light up per-step in the inspector as they progress — status, latency, pass/fail, and (when the run carries them) the step's real input and output. The inspector polls the run's state after it starts and stops quietly once the run finishes or can't be found, so a click into any step shows what it actually did.
|
|
138
|
+
- **Offline stub runs** render in the SAME inspector: their streamed per-step trace is mapped into the identical step view (logs, pass/fail, and the input/output each step ran on), so an offline run reads exactly like a real one — just free and untimed, since a stub run records no cost or duration.
|
|
139
|
+
|
|
140
|
+
Both paths share one step-render shape, so the inspector can never disagree with itself about how a run looks. The inspector names the capability a step called, never a model.
|
|
141
|
+
|
|
142
|
+
- b6b9d16: Restore the `resolveCoreBaseUrl` helper that the actions router relies on to derive the core API base URL. It is now co-located with `resolveAgentsBaseUrl` (its only dependency) instead of living in a since-removed module, so the harness server builds and the actions router self-defaults its base URL again.
|
|
143
|
+
- b6b9d16: Wire the Studio's Deploy, Prod-run, and Run-local buttons to their direct routes instead of typing a command into the coding agent:
|
|
144
|
+
|
|
145
|
+
- **Deploy** streams build status and refreshes the workflow once it publishes, flipping the Draft/Deployed state.
|
|
146
|
+
- **Prod-run** starts a real execution and hands the new execution off to the run inspector, so it shows up in the Steps view.
|
|
147
|
+
- **Run-local** runs the workflow offline with capabilities stubbed and reports the outcome — no network, no spend.
|
|
148
|
+
|
|
149
|
+
These three actions now run without a coding-agent session, so they consume no LLM credits. Debug, Explain, and free-form prompts still go through the coding agent, and Visualize is unchanged.
|
|
150
|
+
|
|
151
|
+
- Updated dependencies [b6b9d16]
|
|
152
|
+
- @sapiom/agent-core@0.9.9
|
|
153
|
+
|
|
3
154
|
## 0.1.5
|
|
4
155
|
|
|
5
156
|
### Patch Changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface CliOptions {
|
|
2
|
+
dir: string;
|
|
3
|
+
port: number;
|
|
4
|
+
login: boolean;
|
|
5
|
+
noAuth: boolean;
|
|
6
|
+
noTelemetry: boolean;
|
|
7
|
+
noOpen: boolean;
|
|
8
|
+
noSession: boolean;
|
|
9
|
+
dev: boolean;
|
|
10
|
+
/** Override the state root (default `~/.sapiom/harness`). Lets a throwaway
|
|
11
|
+
* root stand in for a fresh install — the first-run flow becomes testable
|
|
12
|
+
* without moving, renaming, or losing the real one. */
|
|
13
|
+
stateRoot?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function parseArgs(argv: string[]): CliOptions;
|
|
16
|
+
//# sourceMappingURL=args.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,GAAG,EAAE,OAAO,CAAC;IACb;;4DAEwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAoEpD"}
|
package/dist/cli/args.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI argument parsing, split out of bin.ts so it can be TESTED.
|
|
3
|
+
*
|
|
4
|
+
* bin.ts self-executes (`main().catch(...)`), so importing it from a test would
|
|
5
|
+
* boot the whole CLI. That is why bin-boot.test.ts used to carry a hand-copied
|
|
6
|
+
* MIRROR of this function — a copy that could (and did) drift from the real
|
|
7
|
+
* one, and which meant a new flag could ship with passing tests that never
|
|
8
|
+
* exercised it. This module is the single implementation both use.
|
|
9
|
+
*/
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
import { DEFAULT_PORT } from "../shared/types.js";
|
|
12
|
+
export function parseArgs(argv) {
|
|
13
|
+
let dir;
|
|
14
|
+
let port = DEFAULT_PORT;
|
|
15
|
+
let login = false;
|
|
16
|
+
let noAuth = false;
|
|
17
|
+
let noTelemetry = false;
|
|
18
|
+
let noOpen = false;
|
|
19
|
+
let noSession = false;
|
|
20
|
+
let dev = false;
|
|
21
|
+
let stateRoot;
|
|
22
|
+
for (let i = 0; i < argv.length; i++) {
|
|
23
|
+
const arg = argv[i];
|
|
24
|
+
switch (arg) {
|
|
25
|
+
case "--port": {
|
|
26
|
+
const value = argv[++i];
|
|
27
|
+
if (!value || Number.isNaN(Number(value))) {
|
|
28
|
+
throw new Error("--port requires a numeric value");
|
|
29
|
+
}
|
|
30
|
+
port = Number(value);
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
case "--login":
|
|
34
|
+
login = true;
|
|
35
|
+
break;
|
|
36
|
+
case "--no-auth":
|
|
37
|
+
noAuth = true;
|
|
38
|
+
break;
|
|
39
|
+
case "--no-telemetry":
|
|
40
|
+
noTelemetry = true;
|
|
41
|
+
break;
|
|
42
|
+
case "--state-root": {
|
|
43
|
+
const value = argv[++i];
|
|
44
|
+
if (!value)
|
|
45
|
+
throw new Error("--state-root requires a directory path");
|
|
46
|
+
stateRoot = value;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
case "--no-open":
|
|
50
|
+
noOpen = true;
|
|
51
|
+
break;
|
|
52
|
+
case "--no-session":
|
|
53
|
+
noSession = true;
|
|
54
|
+
break;
|
|
55
|
+
case "--dev":
|
|
56
|
+
dev = true;
|
|
57
|
+
break;
|
|
58
|
+
default:
|
|
59
|
+
if (arg.startsWith("--")) {
|
|
60
|
+
throw new Error(`Unknown flag: ${arg}`);
|
|
61
|
+
}
|
|
62
|
+
if (dir !== undefined) {
|
|
63
|
+
throw new Error(`Unexpected extra argument: ${arg}`);
|
|
64
|
+
}
|
|
65
|
+
dir = arg;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
dir: path.resolve(dir ?? process.cwd()),
|
|
70
|
+
port,
|
|
71
|
+
login,
|
|
72
|
+
noAuth,
|
|
73
|
+
noTelemetry,
|
|
74
|
+
noOpen,
|
|
75
|
+
noSession,
|
|
76
|
+
dev,
|
|
77
|
+
...(stateRoot ? { stateRoot } : {}),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=args.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAiBlD,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,IAAI,GAAuB,CAAC;IAC5B,IAAI,IAAI,GAAG,YAAY,CAAC;IACxB,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,GAAG,GAAG,KAAK,CAAC;IAChB,IAAI,SAA6B,CAAC;IAElC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,QAAQ,GAAG,EAAE,CAAC;YACZ,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBACxB,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;oBAC1C,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;gBACrD,CAAC;gBACD,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;gBACrB,MAAM;YACR,CAAC;YACD,KAAK,SAAS;gBACZ,KAAK,GAAG,IAAI,CAAC;gBACb,MAAM;YACR,KAAK,WAAW;gBACd,MAAM,GAAG,IAAI,CAAC;gBACd,MAAM;YACR,KAAK,gBAAgB;gBACnB,WAAW,GAAG,IAAI,CAAC;gBACnB,MAAM;YACR,KAAK,cAAc,CAAC,CAAC,CAAC;gBACpB,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBACxB,IAAI,CAAC,KAAK;oBAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;gBACtE,SAAS,GAAG,KAAK,CAAC;gBAClB,MAAM;YACR,CAAC;YACD,KAAK,WAAW;gBACd,MAAM,GAAG,IAAI,CAAC;gBACd,MAAM;YACR,KAAK,cAAc;gBACjB,SAAS,GAAG,IAAI,CAAC;gBACjB,MAAM;YACR,KAAK,OAAO;gBACV,GAAG,GAAG,IAAI,CAAC;gBACX,MAAM;YACR;gBACE,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;oBACzB,MAAM,IAAI,KAAK,CAAC,iBAAiB,GAAG,EAAE,CAAC,CAAC;gBAC1C,CAAC;gBACD,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;oBACtB,MAAM,IAAI,KAAK,CAAC,8BAA8B,GAAG,EAAE,CAAC,CAAC;gBACvD,CAAC;gBACD,GAAG,GAAG,GAAG,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO;QACL,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QACvC,IAAI;QACJ,KAAK;QACL,MAAM;QACN,WAAW;QACX,MAAM;QACN,SAAS;QACT,GAAG;QACH,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpC,CAAC;AACJ,CAAC"}
|
package/dist/cli/bin.js
CHANGED
|
@@ -2,83 +2,34 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* sapiom-harness CLI entry (workstream W4).
|
|
4
4
|
*
|
|
5
|
-
* Flow: doctor → auth (reuse
|
|
6
|
-
*
|
|
5
|
+
* Flow: doctor → auth (non-blocking: reuse cached credential if present, skip
|
|
6
|
+
* browser OAuth at boot) → consent (first run) → generate boot token →
|
|
7
|
+
* startServer → open browser → print a startup banner.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
9
|
+
* The Studio launches unauthenticated when no cached credential exists; the
|
|
10
|
+
* web app drives sign-in (D4/D5). Use --login to trigger an interactive
|
|
11
|
+
* browser OAuth before the server starts.
|
|
12
|
+
*
|
|
13
|
+
* Flags: [dir] (default cwd), --port, --login, --no-auth, --no-telemetry,
|
|
14
|
+
* --no-open, --no-session, --dev, --state-root <dir>.
|
|
10
15
|
*/
|
|
11
|
-
import * as path from "node:path";
|
|
12
16
|
import * as crypto from "node:crypto";
|
|
13
17
|
import open from "open";
|
|
14
|
-
import { DEFAULT_PORT } from "../shared/types.js";
|
|
15
18
|
import { runDoctor, printDoctorReport, pickDefaultHarness, CLAUDE_INSTALL_COMMAND, CODEX_INSTALL_COMMAND, } from "./doctor.js";
|
|
16
19
|
import { ensureAuthenticated } from "./auth.js";
|
|
17
20
|
import { ensureConsent } from "./consent.js";
|
|
18
21
|
import { loadSettings, recordRecentDir } from "./settings.js";
|
|
19
22
|
import { getOrCreateMachineId } from "./machine-id.js";
|
|
23
|
+
import { resolveStatePaths } from "../core/paths.js";
|
|
24
|
+
import { parseArgs } from "./args.js";
|
|
20
25
|
import { startServer } from "../server/index.js";
|
|
21
|
-
function parseArgs(argv) {
|
|
22
|
-
let dir;
|
|
23
|
-
let port = DEFAULT_PORT;
|
|
24
|
-
let noAuth = false;
|
|
25
|
-
let noTelemetry = false;
|
|
26
|
-
let noOpen = false;
|
|
27
|
-
let noSession = false;
|
|
28
|
-
let dev = false;
|
|
29
|
-
for (let i = 0; i < argv.length; i++) {
|
|
30
|
-
const arg = argv[i];
|
|
31
|
-
switch (arg) {
|
|
32
|
-
case "--port": {
|
|
33
|
-
const value = argv[++i];
|
|
34
|
-
if (!value || Number.isNaN(Number(value))) {
|
|
35
|
-
throw new Error("--port requires a numeric value");
|
|
36
|
-
}
|
|
37
|
-
port = Number(value);
|
|
38
|
-
break;
|
|
39
|
-
}
|
|
40
|
-
case "--no-auth":
|
|
41
|
-
noAuth = true;
|
|
42
|
-
break;
|
|
43
|
-
case "--no-telemetry":
|
|
44
|
-
noTelemetry = true;
|
|
45
|
-
break;
|
|
46
|
-
case "--no-open":
|
|
47
|
-
noOpen = true;
|
|
48
|
-
break;
|
|
49
|
-
case "--no-session":
|
|
50
|
-
noSession = true;
|
|
51
|
-
break;
|
|
52
|
-
case "--dev":
|
|
53
|
-
dev = true;
|
|
54
|
-
break;
|
|
55
|
-
default:
|
|
56
|
-
if (arg.startsWith("--")) {
|
|
57
|
-
throw new Error(`Unknown flag: ${arg}`);
|
|
58
|
-
}
|
|
59
|
-
if (dir !== undefined) {
|
|
60
|
-
throw new Error(`Unexpected extra argument: ${arg}`);
|
|
61
|
-
}
|
|
62
|
-
dir = arg;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return {
|
|
66
|
-
dir: path.resolve(dir ?? process.cwd()),
|
|
67
|
-
port,
|
|
68
|
-
noAuth,
|
|
69
|
-
noTelemetry,
|
|
70
|
-
noOpen,
|
|
71
|
-
noSession,
|
|
72
|
-
dev,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
26
|
function printBanner(opts) {
|
|
76
27
|
const authLine = opts.identity
|
|
77
28
|
? `${opts.identity.organizationName} (${opts.identity.userId})${opts.identity.source === "cached" ? " — cached" : ""}`
|
|
78
29
|
: "not authenticated";
|
|
79
30
|
console.log("");
|
|
80
|
-
console.log(" Sapiom
|
|
81
|
-
console.log("
|
|
31
|
+
console.log(" Sapiom Studio");
|
|
32
|
+
console.log(" -------------");
|
|
82
33
|
console.log(` directory ${opts.dir}`);
|
|
83
34
|
console.log(` auth ${authLine}`);
|
|
84
35
|
console.log(` telemetry ${opts.telemetryOptIn ? "on" : "off"}`);
|
|
@@ -104,8 +55,20 @@ const main = async () => {
|
|
|
104
55
|
console.log(`\n⚠ Claude Code not found — install with: ${CLAUDE_INSTALL_COMMAND}\n` +
|
|
105
56
|
" Continuing with the Codex harness.");
|
|
106
57
|
}
|
|
107
|
-
|
|
108
|
-
|
|
58
|
+
// Every piece of harness state resolves through ONE root, so --state-root
|
|
59
|
+
// relocates all of it together. Without this, firstRun and recentDirs would
|
|
60
|
+
// still read and WRITE the real settings file while everything else used the
|
|
61
|
+
// throwaway root — a half-isolated run that silently mutates real state.
|
|
62
|
+
const statePaths = resolveStatePaths(options.stateRoot);
|
|
63
|
+
const machineId = await getOrCreateMachineId(statePaths.machineId);
|
|
64
|
+
// Auth is non-blocking at boot: use a cached credential if one exists, but
|
|
65
|
+
// never open a browser at startup. The Studio launches unauthenticated and
|
|
66
|
+
// the web app drives sign-in (D4/D5). Pass --login to run an interactive
|
|
67
|
+
// browser OAuth before the server starts.
|
|
68
|
+
const identity = await ensureAuthenticated({
|
|
69
|
+
interactive: options.login,
|
|
70
|
+
noAuth: options.noAuth,
|
|
71
|
+
});
|
|
109
72
|
// A cached credential signs you in with no visible prompt at all — call it
|
|
110
73
|
// out explicitly so "auth silently worked" doesn't read as "nothing
|
|
111
74
|
// happened" (a fresh login is its own visible browser flow already).
|
|
@@ -120,8 +83,8 @@ const main = async () => {
|
|
|
120
83
|
// and suppresses the auto-created boot session, so a brand-new user lands on
|
|
121
84
|
// the welcome panel rather than a bare terminal in whatever directory they
|
|
122
85
|
// happened to launch from.
|
|
123
|
-
const firstRun = (await loadSettings()).recentDirs.length === 0;
|
|
124
|
-
await recordRecentDir(options.dir);
|
|
86
|
+
const firstRun = (await loadSettings(statePaths.settings)).recentDirs.length === 0;
|
|
87
|
+
await recordRecentDir(options.dir, statePaths.settings);
|
|
125
88
|
const bootToken = crypto.randomBytes(32).toString("hex");
|
|
126
89
|
let server = null;
|
|
127
90
|
try {
|
|
@@ -134,6 +97,7 @@ const main = async () => {
|
|
|
134
97
|
identity,
|
|
135
98
|
machineId,
|
|
136
99
|
launchDir: options.dir,
|
|
100
|
+
...(options.stateRoot ? { stateRoot: options.stateRoot } : {}),
|
|
137
101
|
autoCreateSession: !options.noSession && !firstRun,
|
|
138
102
|
defaultHarnessKind,
|
|
139
103
|
availableHarnesses: doctorReport.availableHarnesses,
|
package/dist/cli/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../../src/cli/bin.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../../src/cli/bin.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/consent.js
CHANGED
|
@@ -13,7 +13,7 @@ import { hasStoredSettings, loadSettings, saveSettings } from "./settings.js";
|
|
|
13
13
|
// which surfaces this path to interactive users who did get the default.
|
|
14
14
|
const DEFAULT_TELEMETRY_OPT_IN = true;
|
|
15
15
|
const CONSENT_COPY = `
|
|
16
|
-
Sapiom
|
|
16
|
+
Sapiom Studio collects usage analytics locally and, with telemetry on,
|
|
17
17
|
sends them to Sapiom to improve the product:
|
|
18
18
|
- the prompts you send and the tool calls your agent makes
|
|
19
19
|
- session start/stop lifecycle events
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/cli/settings.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/cli/settings.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AA2DzE;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAWhF;AAED,4EAA4E;AAC5E,wBAAsB,iBAAiB,CAAC,YAAY,GAAE,MAAkC,GAAG,OAAO,CAAC,OAAO,CAAC,CAO1G;AAED,wBAAsB,YAAY,CAAC,YAAY,GAAE,MAAkC,GAAG,OAAO,CAAC,eAAe,CAAC,CAe7G;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,eAAe,EACzB,YAAY,GAAE,MAAkC,GAC/C,OAAO,CAAC,IAAI,CAAC,CASf;AAED,6FAA6F;AAC7F,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,GAAE,MAAkC,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlH;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CAAC,YAAY,GAAE,MAAkC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAkB7G"}
|
package/dist/cli/settings.js
CHANGED
|
@@ -5,6 +5,10 @@ import { expandHome } from "./paths.js";
|
|
|
5
5
|
const DEFAULT_SETTINGS = {
|
|
6
6
|
telemetryOptIn: false,
|
|
7
7
|
recentDirs: [],
|
|
8
|
+
// Opt-in: the rolling summary spends tokens on a background LLM call the
|
|
9
|
+
// user never asked for. Off, portable continue still works — its brief just
|
|
10
|
+
// degrades to the last N turns. See core/rolling-summary.ts.
|
|
11
|
+
rollingSummary: false,
|
|
8
12
|
};
|
|
9
13
|
const MAX_RECENT_DIRS = 8;
|
|
10
14
|
/** The real settings file. Every function below accepts an explicit path so
|
|
@@ -35,6 +39,25 @@ async function normalizeRecentDir(candidate) {
|
|
|
35
39
|
}
|
|
36
40
|
return resolved;
|
|
37
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Normalize a `projectRoot`: expand `~`, require absolute, resolve.
|
|
44
|
+
*
|
|
45
|
+
* Deliberately does NOT require the directory to exist, unlike
|
|
46
|
+
* normalizeRecentDir. A recent dir is a place something already happened; a
|
|
47
|
+
* project root is a place things WILL happen, and it is created when the first
|
|
48
|
+
* project lands there. Requiring existence would silently reset the user's
|
|
49
|
+
* chosen default the moment they picked a folder they hadn't made yet.
|
|
50
|
+
*
|
|
51
|
+
* Returns undefined for junk so the field simply falls back to the host default.
|
|
52
|
+
*/
|
|
53
|
+
function normalizeProjectRoot(candidate) {
|
|
54
|
+
if (typeof candidate !== "string" || !candidate.trim())
|
|
55
|
+
return undefined;
|
|
56
|
+
const expanded = expandHome(candidate.trim());
|
|
57
|
+
if (!path.isAbsolute(expanded))
|
|
58
|
+
return undefined;
|
|
59
|
+
return path.resolve(expanded);
|
|
60
|
+
}
|
|
38
61
|
/**
|
|
39
62
|
* Validates, normalizes, case-sensitively dedupes (first occurrence wins —
|
|
40
63
|
* callers order newest-first), and caps a candidate recent-dirs list.
|
|
@@ -70,16 +93,25 @@ export async function loadSettings(settingsPath = defaultSettingsFilePath()) {
|
|
|
70
93
|
try {
|
|
71
94
|
const raw = await fs.readFile(settingsPath, "utf-8");
|
|
72
95
|
const parsed = { ...DEFAULT_SETTINGS, ...JSON.parse(raw) };
|
|
73
|
-
|
|
96
|
+
const projectRoot = normalizeProjectRoot(parsed.projectRoot);
|
|
97
|
+
return {
|
|
98
|
+
...parsed,
|
|
99
|
+
recentDirs: await sanitizeRecentDirs(parsed.recentDirs),
|
|
100
|
+
// Omit rather than store undefined, so JSON.stringify on the way back out
|
|
101
|
+
// doesn't leave a dangling `"projectRoot": null`-shaped hole.
|
|
102
|
+
...(projectRoot ? { projectRoot } : { projectRoot: undefined }),
|
|
103
|
+
};
|
|
74
104
|
}
|
|
75
105
|
catch {
|
|
76
106
|
return { ...DEFAULT_SETTINGS };
|
|
77
107
|
}
|
|
78
108
|
}
|
|
79
109
|
export async function saveSettings(settings, settingsPath = defaultSettingsFilePath()) {
|
|
110
|
+
const normalizedRoot = normalizeProjectRoot(settings.projectRoot);
|
|
80
111
|
const sanitized = {
|
|
81
112
|
...settings,
|
|
82
113
|
recentDirs: await sanitizeRecentDirs(settings.recentDirs),
|
|
114
|
+
...(normalizedRoot ? { projectRoot: normalizedRoot } : { projectRoot: undefined }),
|
|
83
115
|
};
|
|
84
116
|
await fs.mkdir(path.dirname(settingsPath), { recursive: true });
|
|
85
117
|
await fs.writeFile(settingsPath, JSON.stringify(sanitized, null, 2) + "\n");
|
package/dist/cli/settings.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/cli/settings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAwB,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,gBAAgB,GAAoB;IACxC,cAAc,EAAE,KAAK;IACrB,UAAU,EAAE,EAAE;
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/cli/settings.ts"],"names":[],"mappings":"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"}
|
|
@@ -30,8 +30,8 @@ export interface HarnessAdapterBase {
|
|
|
30
30
|
/**
|
|
31
31
|
* Per-agent prompt text guiding the user through installing and
|
|
32
32
|
* configuring the Sapiom MCP server (`@sapiom/mcp`) for this harness.
|
|
33
|
-
*
|
|
34
|
-
*
|
|
33
|
+
* Rendered in the Install MCP modal. Returns the text string directly —
|
|
34
|
+
* callers may render it as preformatted text.
|
|
35
35
|
*/
|
|
36
36
|
installMcpPrompt(): string;
|
|
37
37
|
/**
|