@hachej/boring-agent 0.1.110 → 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.
Files changed (156) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +262 -51
  3. package/dist/agents.d.ts +75 -0
  4. package/dist/agents.js +91 -0
  5. package/dist/approval.d.ts +71 -0
  6. package/dist/approval.js +126 -0
  7. package/dist/artifacts.d.ts +53 -0
  8. package/dist/artifacts.js +129 -0
  9. package/dist/ask-user.d.ts +40 -0
  10. package/dist/ask-user.js +134 -0
  11. package/dist/attachment.d.ts +5 -0
  12. package/dist/attachment.js +4 -0
  13. package/dist/browser-task.d.ts +32 -0
  14. package/dist/browser-task.js +119 -0
  15. package/dist/chat-transport.d.ts +80 -0
  16. package/dist/chat-transport.js +306 -0
  17. package/dist/contracts.d.ts +40 -0
  18. package/dist/contracts.js +1 -0
  19. package/dist/conversations.d.ts +136 -0
  20. package/dist/conversations.js +366 -0
  21. package/dist/definitions.d.ts +37 -0
  22. package/dist/definitions.js +99 -0
  23. package/dist/delivery-input.d.ts +35 -0
  24. package/dist/delivery-input.js +56 -0
  25. package/dist/delivery.d.ts +33 -0
  26. package/dist/delivery.js +154 -0
  27. package/dist/durable-wait.d.ts +23 -0
  28. package/dist/durable-wait.js +79 -0
  29. package/dist/file-convert.d.ts +46 -0
  30. package/dist/file-convert.js +159 -0
  31. package/dist/file-guard.d.ts +50 -0
  32. package/dist/file-guard.js +171 -0
  33. package/dist/file-types.d.ts +17 -0
  34. package/dist/file-types.js +95 -0
  35. package/dist/gateway-provider.d.ts +24 -0
  36. package/dist/gateway-provider.js +42 -0
  37. package/dist/git.d.ts +34 -0
  38. package/dist/git.js +79 -0
  39. package/dist/harness-pool.d.ts +38 -0
  40. package/dist/harness-pool.js +134 -0
  41. package/dist/index.d.ts +4 -0
  42. package/dist/index.js +1 -0
  43. package/dist/json-schema-tool.d.ts +25 -0
  44. package/dist/json-schema-tool.js +19 -0
  45. package/dist/memory/optchat-core.d.ts +118 -0
  46. package/dist/memory/optchat-core.js +317 -0
  47. package/dist/memory/optchat.d.ts +156 -0
  48. package/dist/memory/optchat.js +432 -0
  49. package/dist/mentions.d.ts +56 -0
  50. package/dist/mentions.js +140 -0
  51. package/dist/metering.d.ts +183 -0
  52. package/dist/metering.js +443 -0
  53. package/dist/model-gateway.d.ts +86 -0
  54. package/dist/model-gateway.js +241 -0
  55. package/dist/native-version.d.ts +9 -0
  56. package/dist/native-version.js +7 -0
  57. package/dist/native.d.ts +4 -0
  58. package/dist/native.js +1 -0
  59. package/dist/presentation.d.ts +17 -0
  60. package/dist/presentation.js +39 -0
  61. package/dist/projection-format.d.ts +68 -0
  62. package/dist/projection-format.js +118 -0
  63. package/dist/projection-window.d.ts +9 -0
  64. package/dist/projection-window.js +93 -0
  65. package/dist/projection.d.ts +22 -0
  66. package/dist/projection.js +153 -0
  67. package/dist/question-response.d.ts +20 -0
  68. package/dist/question-response.js +75 -0
  69. package/dist/questions.d.ts +74 -0
  70. package/dist/questions.js +199 -0
  71. package/dist/self-evolving.d.ts +55 -0
  72. package/dist/self-evolving.js +239 -0
  73. package/dist/skills.d.ts +15 -0
  74. package/dist/skills.js +51 -0
  75. package/dist/tool-evidence.d.ts +14 -0
  76. package/dist/tool-evidence.js +54 -0
  77. package/dist/validation.d.ts +38 -0
  78. package/dist/validation.js +68 -0
  79. package/dist/workspaces.d.ts +107 -0
  80. package/dist/workspaces.js +212 -0
  81. package/package.json +145 -150
  82. package/dist/DebugDrawer-JNA43SVR.js +0 -8
  83. package/dist/admin-DORVQfLn.d.ts +0 -340
  84. package/dist/chunk-2BGLZZ7T.js +0 -10
  85. package/dist/chunk-42D6KAQV.js +0 -2882
  86. package/dist/chunk-4XOW57KJ.js +0 -343
  87. package/dist/chunk-72KT6VWV.js +0 -1338
  88. package/dist/chunk-7XAOVFSN.js +0 -28
  89. package/dist/chunk-KJIEMSQP.js +0 -198
  90. package/dist/chunk-KN72XWP7.js +0 -394
  91. package/dist/chunk-N6THTH7P.js +0 -110
  92. package/dist/chunk-V3H5A3BR.js +0 -22501
  93. package/dist/chunk-YGRQ4Q5P.js +0 -20
  94. package/dist/chunk-YTUZFKB3.js +0 -78
  95. package/dist/core/index.d.ts +0 -28
  96. package/dist/core/index.js +0 -617
  97. package/dist/createHarness-DOLOHA62.js +0 -18
  98. package/dist/eval/index.d.ts +0 -247
  99. package/dist/eval/index.js +0 -541
  100. package/dist/file-search-BUgMSKZE.d.ts +0 -289
  101. package/dist/front/artifacts.d.ts +0 -34
  102. package/dist/front/artifacts.js +0 -21
  103. package/dist/front/index.d.ts +0 -1082
  104. package/dist/front/index.js +0 -12912
  105. package/dist/front/styles.css +0 -5380
  106. package/dist/harness-qHlMJhZo.d.ts +0 -276
  107. package/dist/middleware-CaAEXpi5.d.ts +0 -115
  108. package/dist/piChatEvent-DpE0lIb9.d.ts +0 -450
  109. package/dist/piChatSessionService-CBct-hmz.d.ts +0 -86
  110. package/dist/sandbox-BMhut3kh.d.ts +0 -317
  111. package/dist/server/agent-host/testing/compositionRouteProof.d.ts +0 -8
  112. package/dist/server/agent-host/testing/compositionRouteProof.js +0 -78
  113. package/dist/server/agent-host/testing/gatewayConformance.d.ts +0 -43
  114. package/dist/server/agent-host/testing/gatewayConformance.js +0 -605
  115. package/dist/server/index.d.ts +0 -2278
  116. package/dist/server/index.js +0 -274
  117. package/dist/server/pi-session-readability.d.ts +0 -12
  118. package/dist/server/pi-session-readability.js +0 -23
  119. package/dist/server/worker/index.d.ts +0 -68
  120. package/dist/server/worker/index.js +0 -377
  121. package/dist/share-entry-CxS6UJ_L.d.ts +0 -330
  122. package/dist/shared/index.d.ts +0 -2111
  123. package/dist/shared/index.js +0 -262
  124. package/dist/types-DcWoQMn8.d.ts +0 -1377
  125. package/docs/ACCESSIBILITY.md +0 -52
  126. package/docs/AGENT_GATEWAY_V0.md +0 -212
  127. package/docs/AGENT_PACKAGES.md +0 -90
  128. package/docs/API.md +0 -123
  129. package/docs/AUTHORING.md +0 -68
  130. package/docs/CSP.md +0 -41
  131. package/docs/ERROR_CODES.md +0 -229
  132. package/docs/KNOWN_LIMITATIONS.md +0 -100
  133. package/docs/MIGRATION.md +0 -50
  134. package/docs/PERFORMANCE.md +0 -68
  135. package/docs/PLUGINS.md +0 -138
  136. package/docs/README.md +0 -98
  137. package/docs/RISKS-MULTI-TAB.md +0 -46
  138. package/docs/SESSION_IDENTITY.md +0 -91
  139. package/docs/STYLING.md +0 -73
  140. package/docs/UI-SHADCN.md +0 -56
  141. package/docs/VERCEL_COSTS.md +0 -103
  142. package/docs/plans/archive/AGENT_EVAL_FRAMEWORK.md +0 -466
  143. package/docs/plans/archive/agent-package-spec.md +0 -1777
  144. package/docs/plans/archive/harness-followup-capabilities.md +0 -440
  145. package/docs/plans/archive/harness-tool-ui-capabilities.md +0 -144
  146. package/docs/plans/archive/pi-followup-history-projection.md +0 -229
  147. package/docs/plans/archive/pi-tools-migration.md +0 -1061
  148. package/docs/plans/archive/reviews/pi-followup-history-codex-review.md +0 -11
  149. package/docs/plans/archive/reviews/pi-followup-history-gpt-review.md +0 -64
  150. package/docs/plans/archive/reviews/pi-followup-history-opus-review.md +0 -43
  151. package/docs/plans/archive/reviews/pi-followup-history-xai-review.md +0 -43
  152. package/docs/plans/archive/vercel-base-snapshot-template-plan.md +0 -527
  153. package/docs/plans/archive/vercel-persistent-sandbox-adapter.md +0 -553
  154. package/docs/runtime-provisioning.md +0 -157
  155. package/docs/runtime.md +0 -296
  156. package/docs/tools.md +0 -102
package/docs/README.md DELETED
@@ -1,98 +0,0 @@
1
- # @hachej/boring-agent Docs
2
-
3
- `@hachej/boring-agent` is a pane-embeddable coding agent: an LLM agent loop, a
4
- tool catalog, and a chat UI, with swappable execution backends. The same agent,
5
- tools, and UI run in three modes — `direct` (host process), `local` (bwrap
6
- sandbox), and `vercel-sandbox` (Firecracker microVM) — selected at construction.
7
- It is consumed two ways: standalone via `createAgentApp` (Fastify server +
8
- `ChatPanel`), or mounted into `@hachej/boring-workspace` for the full IDE shell.
9
-
10
- ## Architecture
11
-
12
- ```
13
- Chat UI (ChatPanel / front)
14
- │ pi-chat SSE stream
15
- Agent HTTP routes (registerAgentRoutes / createAgentApp)
16
- │
17
- Agent harness (pi-coding-agent loop) ── Tool catalog (AgentTool[])
18
- │ │
19
- Workspace (filesystem) Sandbox (command execution)
20
- ```
21
-
22
- - **front** (`src/front`) — `ChatPanel` (= `PiChatPanel`), primitives, slash
23
- commands, tool renderers, session hooks. Browser-only.
24
- - **server** (`src/server`) — `createAgentApp`, `registerAgentRoutes`, the Pi
25
- harness adapter, sandbox/workspace adapters, runtime provisioning, model
26
- config, HTTP routes. Node-only.
27
- - **shared** (`src/shared`) — platform-agnostic contracts and zod schemas
28
- (`AgentHarness`, `Workspace`, `Sandbox`, `AgentTool`, `SessionStore`, chat
29
- frames, error codes).
30
- - **eval** (`src/eval`) — agent-behavior evaluation toolkit.
31
-
32
- Data flow: the browser opens a pi-chat SSE stream; the server drives the
33
- pi-coding-agent harness; tools resolve filesystem ops through the `Workspace`
34
- adapter and shell/exec through the `Sandbox` adapter. Workspace + Sandbox must
35
- target the same filesystem substrate (the mode adapter enforces the pairing).
36
-
37
- ## Key abstractions
38
-
39
- The public surfaces a consumer or extender touches (all in
40
- `@hachej/boring-agent/shared` unless noted):
41
-
42
- - `AgentHarness` — the agent loop interface; pi-coding-agent is the v1 impl.
43
- - `Workspace` / `Sandbox` — filesystem and command-execution adapters, swapped
44
- per mode.
45
- - `AgentTool` + `CatalogDeps` — the tool contract and the deps tools bind to.
46
- - `SessionStore` — session listing/lifecycle.
47
- - `createAgentApp` / `registerAgentRoutes` (`/server`) — entry points to run or
48
- embed the server.
49
- - `ChatPanel` (`/front`) — the embeddable UI.
50
-
51
- ## Architectural decisions
52
-
53
- Locked decisions live in the root [`docs/DECISIONS.md`](../../../docs/DECISIONS.md);
54
- the agent ↔ workspace boundary is in
55
- [`docs/WORKSPACE_CONTRACT.md`](../../../docs/WORKSPACE_CONTRACT.md). Highlights:
56
-
57
- - Standalone CLI-shaped product; same code embeds into a host shell — decision 1.
58
- - pi-coding-agent as the v1 harness behind a generic `AgentHarness` — decisions 4–5.
59
- - `mode = direct | local | vercel-sandbox`; Workspace+Sandbox pairing invariant — decisions 7a–7f.
60
- - Single Anthropic provider by default, but the model layer also supports custom
61
- OpenAI-compatible and Infomaniak providers — decision 10.
62
- - Plugins extend via Pi resources or trusted server plugins, not core edits — decision 8.
63
- - Four export surfaces: top-level/`front`, `/server`, `/shared`, `/eval` — decision 15.
64
-
65
- ## Documentation
66
-
67
- **Surfaces & integration**
68
-
69
- - [API](./API.md) — the four entry points and what each exports.
70
- - [AUTHORING](./AUTHORING.md) — declarative agent directories, bounds, validation, and legacy catalog migration.
71
- - [AGENT_PACKAGES](./AGENT_PACKAGES.md) — install, seat, update, unseat, and remove plugin-shaped agent packages in a workspace.
72
- - [STYLING](./STYLING.md) — CSS-variable theming contract and public selectors.
73
- - [UI-SHADCN](./UI-SHADCN.md) — `ChatPanel` styling model and tool-renderer overrides.
74
- - [tools](./tools.md) — built-in tools, package-added tools, and how to add custom tools.
75
- - [PLUGINS](./PLUGINS.md) — the two extension paths and tool-collision rules.
76
- - [MIGRATION](./MIGRATION.md) — moving legacy integrations to the v2 split.
77
-
78
- **Runtime & provisioning**
79
-
80
- - [runtime](./runtime.md) — the three modes, the cwd invariant, and `.boring-agent/` layout.
81
- - [runtime-provisioning](./runtime-provisioning.md) — when provisioning runs, package-authoring shape, trusted server provisioning.
82
-
83
- **Contracts & operations**
84
-
85
- - [SESSION_IDENTITY](./SESSION_IDENTITY.md) — durable session stream key, owner attributes, and v1→v2 migration contract.
86
- - [ERROR_CODES](./ERROR_CODES.md) — the stable API error-code registry.
87
- - [CSP](./CSP.md) — Content-Security-Policy compatibility.
88
- - [ACCESSIBILITY](./ACCESSIBILITY.md) — a11y coverage and known gaps.
89
-
90
- **Risk & cost notes**
91
-
92
- - [KNOWN_LIMITATIONS](./KNOWN_LIMITATIONS.md) — accepted risks (orphaned sandboxes, deferred git routes).
93
- - [RISKS-MULTI-TAB](./RISKS-MULTI-TAB.md) — last-write-wins behavior for concurrent edits.
94
- - [VERCEL_COSTS](./VERCEL_COSTS.md) — Vercel sandbox cost model.
95
- - [PERFORMANCE](./PERFORMANCE.md) — historical Vercel cold-start benchmark.
96
-
97
- Historical plans and specs live under `docs/plans/archive/` — archival context only, not
98
- current truth; verify against code before relying on them.
@@ -1,46 +0,0 @@
1
- # Multi-Tab Concurrency Risk
2
-
3
- ## Scope
4
-
5
- This note documents current `@hachej/boring-agent` behavior when two browser tabs
6
- point at the same workspace and edit the same file.
7
-
8
- ## Observed Behavior (v1)
9
-
10
- - There is no workspace-level lock and no per-file lock.
11
- - `POST /api/v1/files` performs an unconditional write.
12
- - There is no optimistic concurrency precondition (no ETag/version check,
13
- no `If-Match` handling).
14
- - Result: writes are silent last-write-wins. A stale tab can overwrite a newer
15
- edit without a conflict response.
16
-
17
- This is visible in route code:
18
- - [file.ts](../src/server/http/routes/file.ts) calls `workspace.writeFile(...)`
19
- directly.
20
- - [createNodeWorkspace.ts](../../boring-sandbox/src/providers/node-workspace/createNodeWorkspace.ts)
21
- calls Node `writeFile(...)` without revision guards.
22
-
23
- Behavior is exercised by:
24
- - [file.test.ts](../src/server/http/routes/__tests__/file.test.ts)
25
- `multi-tab stale write overwrites newer content (last write wins)`.
26
-
27
- ## Session Semantics
28
-
29
- - Tabs are not treated as exclusive owners of a workspace.
30
- - Session identity is driven by `sessionId`; multiple tabs can operate against
31
- the same workspace concurrently.
32
-
33
- ## Known User Impact
34
-
35
- - Lost-update risk for simultaneous edits on the same path.
36
- - No explicit error or merge prompt when overwrite happens.
37
-
38
- ## Future Mitigation Seam
39
-
40
- Introduce optimistic write preconditions:
41
-
42
- 1. `GET /api/v1/files` returns a version token (for example ETag/content hash +
43
- mtime).
44
- 2. `POST /api/v1/files` accepts `If-Match` and rejects mismatched versions with
45
- `409 Conflict`.
46
- 3. Frontend handles `409` with refresh/retry UX instead of silent overwrite.
@@ -1,91 +0,0 @@
1
- # Session stream identity
2
-
3
- This contract governs durable agent event-stream addressing. The code in
4
- `src/shared/events.ts` is authoritative when this document and the
5
- implementation disagree.
6
-
7
- ## Durable key
8
-
9
- A session stream is keyed by `(workspaceScopeId, sessionId)`. Its path is:
10
-
11
- ```text
12
- sessions/<enc(workspaceScopeId)>/<enc(sessionId)>
13
- ```
14
-
15
- `enc` percent-encodes every byte outside `[A-Za-z0-9._-]`, using uppercase
16
- hex. Both fields are opaque, non-empty, well-formed Unicode strings; session
17
- ids need not be UUIDs.
18
- `sessionStreamPath(identity)` is the only constructor and
19
- `parseSessionStreamPath(path)` accepts only the canonical encoding.
20
- The value helpers are server-internal and are not exported from the package's
21
- shared public barrel; only the `SessionStreamIdentity` type is public there.
22
-
23
- The process-local Pi cache may still use a JSON tuple, but it is not a durable
24
- key. Gateway host maps use `(workspaceScopeId, agentTypeId, sessionId)`, and
25
- Pi transcript directories use their existing agent and workspace namespace.
26
- Neither grammar may be embedded into a durable stream path.
27
-
28
- When no event store is configured, legacy/headless callers may omit
29
- `workspaceId`. The service preserves their pre-durability JSON cache key and
30
- never constructs or validates a durable path. Once an event store is present,
31
- both identity fields are required and invalid identity or durable-read errors
32
- surface instead of being treated as a missing transcript.
33
-
34
- ## Owner attributes
35
-
36
- `boring_event_stream_owners` stores one row for each addressed session stream:
37
-
38
- - `path` is the primary key.
39
- - `workspace_scope_id` and `session_id` repeat the durable identity and are
40
- indexed together.
41
- - `agent_type_id` is required for newly opened channels but is an attribute,
42
- not part of the stream key.
43
- - `auth_subject_id` is execution attribution, never ownership.
44
- - `seat_id` and `thread_id` are reserved nullable columns; A1 defines no
45
- semantics or foreign keys for them.
46
- - `key_version` is `2` for streams created with this grammar and `1` for
47
- migrated rows.
48
-
49
- There is deliberately no foreign key. SQLite foreign-key enforcement is not
50
- enabled on this connection; migration validation and store transactions enforce
51
- the relationship instead.
52
-
53
- `auth_subject_id` uses first-writer semantics: the first successful
54
- `createSessionStream` claim records it, and later opens do not replace it.
55
- This is attribution for the opening execution, not a durable authorization or
56
- ownership decision.
57
-
58
- ## Schema v1 to v2
59
-
60
- Opening a v1 database runs one `BEGIN IMMEDIATE` migration. It preflights every
61
- old stream into `boring_event_stream_migration_v2` by parent `rowid`, raw
62
- `path`, and `typeof(path)`, validates unique targets, every parent's path and
63
- metadata, per-stream row counts and maximum sequence values, every owner row,
64
- and every child row before swapping paths across all three event tables.
65
-
66
- The collision rules are exhaustive:
67
-
68
- 1. A single old row rekeys to its v2 path.
69
- 2. For an empty-user row colliding with user-bearing rows, the empty-user row
70
- wins and the others are quarantined.
71
- 3. If several user-bearing rows collide without an empty-user row, all are
72
- quarantined because their sequence spaces cannot be merged safely.
73
- 4. Non-matching, malformed, or otherwise ambiguous old paths are quarantined.
74
-
75
- Legacy identities containing ill-formed Unicode are quarantined with reason
76
- `unencodable-identity`. Non-TEXT parent paths (including SQL `NULL`) use a
77
- rowid-safe quarantine destination; their raw original value/type remains in
78
- the manifest. No encoding failure aborts migration of the remaining rows.
79
-
80
- Ordinary quarantine destinations are `quarantine/v1/<enc(oldPath)>`; rowid-safe
81
- destinations cover values that cannot be encoded. Rows are never dropped. The
82
- swap is validated again before `schema_version` becomes `2`.
83
- Concurrent openers serialize on the immediate transaction, re-read the version
84
- under the lock, and only one migrates. A migrated owner has
85
- `agent_type_id = NULL` until the next legitimate channel open backfills it from
86
- the composition's explicit agent type.
87
-
88
- The migration is a rollback fence, not a reversible downgrade. Disable
89
- `BORING_CHAT_DURABLE_STREAM` before running an old binary; an old binary pointed
90
- at a v2 file refuses it with `EventStreamSchemaVersionError` rather than writing
91
- mixed path grammars.
package/docs/STYLING.md DELETED
@@ -1,73 +0,0 @@
1
- # STYLING
2
-
3
- Styling contract for `@hachej/boring-agent` frontend surfaces. For the full
4
- agent/workspace Tailwind isolation model, see
5
- [`docs/TAILWIND-V4-STYLE-ISOLATION.md`](../../../docs/TAILWIND-V4-STYLE-ISOLATION.md).
6
-
7
- ## Package CSS
8
-
9
- Consumers import the precompiled package stylesheet once, after host/workspace
10
- base CSS and before app overrides:
11
-
12
- ```ts
13
- import "@hachej/boring-workspace/globals.css"
14
- import "@hachej/boring-agent/front/styles.css"
15
- import "./app.css"
16
- ```
17
-
18
- The published `@hachej/boring-agent/front/styles.css` is consumer-safe: it contains no
19
- Tailwind `@source`, no Tailwind imports, and no repo-relative source paths.
20
-
21
- ## Public selectors
22
-
23
- Agent surfaces expose a package namespace root and stable part attributes:
24
-
25
- ```css
26
- [data-boring-agent] {}
27
- [data-boring-agent-part="chat"] {}
28
- [data-boring-agent-part="composer"] {}
29
- [data-boring-agent-part="tool-card"] {}
30
- [data-boring-agent-message-role="assistant"] {}
31
- [data-boring-state="selected"] {}
32
- ```
33
-
34
- `data-boring-agent-*` is the public selector namespace for agent UI. Workspace
35
- uses `data-boring-workspace-*`; do not style agent internals through workspace
36
- selectors.
37
-
38
- ## Token contract
39
-
40
- Workspace owns the public visual tokens (`--boring-*`). Agent consumes those
41
- host tokens and provides standalone fallbacks under `[data-boring-agent]`.
42
-
43
- Common tokens:
44
-
45
- - `--boring-background`
46
- - `--boring-foreground`
47
- - `--boring-card`
48
- - `--boring-muted`
49
- - `--boring-muted-foreground`
50
- - `--boring-accent`
51
- - `--boring-border`
52
- - `--boring-radius`
53
- - `--boring-font-sans`
54
- - `--boring-font-mono`
55
-
56
- Example app override:
57
-
58
- ```css
59
- [data-boring-agent-part="composer"] {
60
- --boring-accent: oklch(0.64 0.18 250);
61
- }
62
-
63
- [data-boring-agent-message-role="assistant"] {
64
- --boring-card: oklch(0.98 0.01 250);
65
- }
66
- ```
67
-
68
- ## Invariants
69
-
70
- - Keep reusable primitives driven by semantic CSS variables and public data
71
- attributes, not package-internal DOM structure.
72
- - Keep styling decisions out of `src/shared/**` and server runtime modules.
73
- - Published package CSS must remain precompiled and consumer-safe.
package/docs/UI-SHADCN.md DELETED
@@ -1,56 +0,0 @@
1
- # Agent UI
2
-
3
- `@hachej/boring-agent` exports the pane-embeddable `ChatPanel` and its frontend
4
- primitives. The default styled surface is packaged with precompiled CSS.
5
-
6
- ## Quickstart
7
-
8
- ```tsx
9
- import "@hachej/boring-workspace/globals.css"
10
- import "@hachej/boring-agent/front/styles.css"
11
- import { ChatPanel } from "@hachej/boring-agent"
12
-
13
- function App() {
14
- return <ChatPanel sessionId="my-session" />
15
- }
16
- ```
17
-
18
- Import app overrides after the package CSS when customizing:
19
-
20
- ```ts
21
- import "@hachej/boring-workspace/globals.css"
22
- import "@hachej/boring-agent/front/styles.css"
23
- import "./app.css"
24
- ```
25
-
26
- ## Styling model
27
-
28
- | Concern | Contract |
29
- |---|---|
30
- | Package CSS | `@hachej/boring-agent/front/styles.css` |
31
- | Root selector | `[data-boring-agent]` |
32
- | Parts | `[data-boring-agent-part="composer"]`, `[data-boring-agent-part="tool-card"]`, etc. |
33
- | Message role | `[data-boring-agent-message-role="assistant"]` |
34
- | State | `[data-boring-state="selected"]`, `[data-boring-state="disabled"]`, etc. |
35
- | Tokens | Consumes host `--boring-*` tokens with standalone fallbacks |
36
-
37
- The built stylesheet has no Tailwind `@source` directives, no Tailwind imports,
38
- and no repo-relative source paths.
39
-
40
- ## Dark mode
41
-
42
- Add `class="dark"` to an ancestor. Workspace owns the dark `--boring-*` token
43
- values; agent inherits them and falls back when embedded standalone.
44
-
45
- ## Custom tool renderers
46
-
47
- Same API as `ChatPanel`:
48
-
49
- ```tsx
50
- <ChatPanel
51
- sessionId="sess"
52
- toolRenderers={{
53
- write: ({ toolCall }) => <div>Wrote {toolCall.input.path}</div>,
54
- }}
55
- />
56
- ```
@@ -1,103 +0,0 @@
1
- # Vercel Sandbox Cost Model
2
-
3
- Date: 2026-04-23
4
-
5
- ## Inputs
6
-
7
- ### Pricing + limits (official)
8
-
9
- Source: Vercel Sandbox pricing and limits (last updated March 14, 2026):
10
- <https://vercel.com/docs/vercel-sandbox/pricing>
11
-
12
- | Metric | Rate / Limit |
13
- |---|---|
14
- | Active CPU | `$0.128 / vCPU-hour` |
15
- | Provisioned Memory | `$0.0212 / GB-hour` |
16
- | Sandbox creations | `$0.60 / 1M` (`$0.0000006 / create`) |
17
- | Data transfer | `$0.15 / GB` |
18
- | Snapshot storage | `$0.08 / GB-month` |
19
- | Default vCPU | `2` |
20
- | Memory per vCPU | `2 GB` |
21
- | Max runtime | Hobby `45 min`, Pro/Enterprise `5h` |
22
- | vCPU allocation rate limit | Hobby `40 vCPU / 10 min`, Pro `200 / min`, Ent `400 / min` |
23
-
24
- Snapshot defaults (official):
25
- - <https://vercel.com/docs/vercel-sandbox/concepts/snapshots>
26
- - Default snapshot expiration is `30 days` unless configured otherwise.
27
-
28
- ### Measured workload inputs (this repo)
29
-
30
- - Cold-start benchmark (`96h`): [PERFORMANCE.md](./PERFORMANCE.md)
31
- - FS + exec latency benchmark (`f4f`): historical raw JSON artifact removed; see the root `docs/PERFORMANCE.md` rollup.
32
- - Measured snapshot sizes from benchmark project: ~`279 MB` each (`0.2606 GB`).
33
-
34
- Observations:
35
- - 96h: cold-start p95 is below `3s`, so we do not need a large warm pool to hide startup.
36
- - f4f + 96h traces are I/O-heavy; CPU-active share is much lower than wall-clock. Model uses `30%` CPU-active as the baseline.
37
-
38
- ## Model
39
-
40
- For one workspace/day:
41
-
42
- ```text
43
- daily_cost =
44
- (active_cpu_vcpu_hours * 0.128) +
45
- (memory_gb * wall_clock_hours * 0.0212) +
46
- (creates_per_day * 0.0000006) +
47
- (snapshot_gb * snapshots_kept * 0.08 / 30)
48
- ```
49
-
50
- Baseline assumptions used below:
51
- - `2 vCPU`, `4 GB` memory.
52
- - one active user session/day: `30 min` wall-clock, `30%` CPU-active.
53
- - this gives `active_cpu_vcpu_hours = 2 * 0.5 * 0.3 = 0.3` vCPU-hours/day.
54
- - `1` creation/day.
55
- - snapshot retention `keep-last-2`.
56
- - idle policy changes runtime billed for memory, but not active CPU time.
57
-
58
- ## Cost per Workspace per Day
59
-
60
- Historical companion CSV artifacts were removed from the package tree; the table below preserves the cost model.
61
-
62
- | Idle policy | Runtime billed/day | CPU $/day | Memory $/day | Snapshot $/day | Create $/day | Total $/day |
63
- |---|---:|---:|---:|---:|---:|---:|
64
- | Stop on session end | 0.5 h | 0.0384 | 0.0424 | 0.0014 | 0.0000006 | **0.0822** |
65
- | Idle-stop after 60 min | 1.0 h | 0.0384 | 0.0848 | 0.0014 | 0.0000006 | **0.1246** |
66
- | No explicit stop (5h timeout) | 5.0 h | 0.0384 | 0.4240 | 0.0014 | 0.0000006 | **0.4638** |
67
- | Pinned 24h workspace | 24.0 h | 0.0384 | 2.0352 | 0.0014 | 0.0000006 | **2.0750** |
68
-
69
- Implication:
70
- - Memory wall-clock billing dominates cost. Idle lifetime policy matters far more than creation count.
71
-
72
- ## Snapshot Retention Trade-off
73
-
74
- With measured snapshot size `0.2606 GB`:
75
-
76
- | Retention | Storage/workspace/month |
77
- |---|---:|
78
- | Keep 1 | `$0.0209` |
79
- | Keep 2 (current) | `$0.0417` |
80
- | Keep 5 | `$0.1043` |
81
- | Keep 10 | `$0.2085` |
82
-
83
- Conclusion:
84
- - **Keep-last-2 is still the right default.**
85
- - Storage cost is tiny at small scale but scales linearly with workspace count.
86
-
87
- ## Idle Pool Sizing
88
-
89
- If we keep a warm 2 vCPU / 4 GB sandbox running for 24h, per slot:
90
- - Memory-only floor: `4 GB * 24h * 0.0212 = $2.0352/day`.
91
- - At 1% CPU-active overhead, total is about `~$2.10/day` per warm slot.
92
-
93
- Because 96h cold-start p95 is already `<3s`, default should be **no prewarmed idle pool**.
94
-
95
- ## Policy Recommendation
96
-
97
- 1. Keep `BORING_AGENT_SNAPSHOT_KEEP=2` (confirmed).
98
- 2. Stop sandbox proactively on session end; do not rely on timeout.
99
- 3. Add/keep idle-stop guardrail at `<= 60 min` idle.
100
- 4. Add stale-handle cleanup: force destroy after `24h` idle as a safety backstop if stop hooks are missed.
101
- 5. Keep idle pool size at `0` by default; only enable a tiny pool behind feature flag if product latency goals tighten.
102
-
103
- This policy minimizes spend while keeping current UX targets achievable.