antpath 0.1.1 → 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/README.md +40 -4
- package/dist/client.js +4 -1
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/platform/client.d.ts +138 -7
- package/dist/platform/client.js +98 -2
- package/dist/platform/client.js.map +1 -1
- package/dist/providers/known-events.d.ts +60 -0
- package/dist/providers/known-events.js +64 -0
- package/dist/providers/known-events.js.map +1 -0
- package/dist/run/controller.d.ts +4 -1
- package/dist/run/controller.js +103 -13
- package/dist/run/controller.js.map +1 -1
- package/dist/template/index.d.ts +1 -1
- package/dist/types.d.ts +20 -0
- package/dist/utils/events.d.ts +21 -0
- package/dist/utils/events.js +97 -18
- package/dist/utils/events.js.map +1 -1
- package/docs/credentials.md +84 -2
- package/docs/events.md +129 -0
- package/docs/skills.md +2 -0
- package/package.json +1 -1
- package/references/architecture-decisions.md +110 -64
- package/references/implementation-plan.md +66 -44
package/docs/events.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Events
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Events
|
|
6
|
+
|
|
7
|
+
Claude Managed Agents sessions run autonomously on Anthropic's infrastructure.
|
|
8
|
+
They are **non-blocking**: the SDK opens a long-lived SSE stream while the
|
|
9
|
+
agent thinks, calls tools, and emits messages on its own schedule. The SDK
|
|
10
|
+
cannot intercept a tool call to return a value — `permission_policy` is
|
|
11
|
+
`always_allow`. This guide covers the observe-only event surface.
|
|
12
|
+
|
|
13
|
+
## Two ways to consume events
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
// Push: register a callback in RunOptions.
|
|
17
|
+
const handle = await client.run(template, {
|
|
18
|
+
onEvent: async (event) => {
|
|
19
|
+
if (event.type === "provider.event") {
|
|
20
|
+
// event.event is a ProviderEvent (typed via a type guard below).
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
await handle.wait();
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
// Pull: iterate the stream concurrently with handle.wait().
|
|
29
|
+
const handle = await client.run(template);
|
|
30
|
+
const collect = (async () => {
|
|
31
|
+
for await (const event of handle.streamEvents()) {
|
|
32
|
+
// ...
|
|
33
|
+
}
|
|
34
|
+
})();
|
|
35
|
+
const result = await handle.wait();
|
|
36
|
+
await collect;
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Both surfaces observe the same events. They can be used together; every
|
|
40
|
+
subscriber sees every event. A subscriber attached after `client.run()`
|
|
41
|
+
returns replays the events it missed (including the initial
|
|
42
|
+
`sdk.status` emitted while provider resources were being created), then
|
|
43
|
+
continues live.
|
|
44
|
+
|
|
45
|
+
## Event shape
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
type RunEvent =
|
|
49
|
+
| { type: "sdk.status"; status: RunStatus; at: string }
|
|
50
|
+
| { type: "sdk.message_sent"; index: number; at: string }
|
|
51
|
+
| { type: "sdk.cleanup"; ... } // see "Cleanup events" below
|
|
52
|
+
| { type: "provider.event"; event: ProviderEvent; at: string };
|
|
53
|
+
|
|
54
|
+
interface ProviderEvent {
|
|
55
|
+
type: string; // documented Claude event type, e.g. "agent.tool_use"
|
|
56
|
+
payload: unknown; // raw provider payload, redacted
|
|
57
|
+
receivedAt: string;
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
All events pass through the SDK's secret redaction before reaching any
|
|
62
|
+
subscriber, so payloads do not contain the Anthropic key or MCP credentials
|
|
63
|
+
that were supplied to `client.run()`.
|
|
64
|
+
|
|
65
|
+
## Drain-before-`wait()` guarantee
|
|
66
|
+
|
|
67
|
+
By the time `await handle.wait()` resolves, every `onEvent` invocation
|
|
68
|
+
triggered by events emitted before the terminal `sdk.status` has itself
|
|
69
|
+
resolved or rejected. Async handlers are invoked serially in event order on
|
|
70
|
+
a single per-listener promise chain, so callers can persist events in order
|
|
71
|
+
without their own queue.
|
|
72
|
+
|
|
73
|
+
The same drain applies to handlers that throw — both modes (warn-only and
|
|
74
|
+
abort-on-error, see below) wait for the offending invocation to settle
|
|
75
|
+
before resolving.
|
|
76
|
+
|
|
77
|
+
## Error semantics
|
|
78
|
+
|
|
79
|
+
Default: a handler that throws (or returns a rejecting promise) is logged
|
|
80
|
+
via the SDK's configured `logger` at `warn`. The run continues, and the
|
|
81
|
+
final result is unaffected.
|
|
82
|
+
|
|
83
|
+
Opt-in: `onEventAbortOnError: true` upgrades the **first** pre-terminal
|
|
84
|
+
handler error into a `RunStateError` that fails the run. Once terminal
|
|
85
|
+
status has been reached, listener errors are always logged only — they
|
|
86
|
+
never mutate the result, and they cannot trigger recursion when the
|
|
87
|
+
terminal `sdk.status: failed` event itself causes another error.
|
|
88
|
+
|
|
89
|
+
`RunStateError.message` and its `cause` field pass through the same
|
|
90
|
+
redaction layer used for events.
|
|
91
|
+
|
|
92
|
+
## Cleanup events
|
|
93
|
+
|
|
94
|
+
`sdk.cleanup` events are part of the `RunEvent` union, but they are
|
|
95
|
+
emitted by `handle.cleanup()` after `handle.wait()` has already resolved.
|
|
96
|
+
By that time the event bus has been closed; subscribers attached during
|
|
97
|
+
the run do **not** see them. Inspect cleanup outcomes via the
|
|
98
|
+
`CleanupResult.operations` array returned by `cleanup()` instead.
|
|
99
|
+
|
|
100
|
+
## Typed helpers
|
|
101
|
+
|
|
102
|
+
`packages/sdk/src/providers/known-events.ts` exports conservative type
|
|
103
|
+
guards that narrow `ProviderEvent.type` to documented Claude event strings.
|
|
104
|
+
They do **not** assert payload field shapes — the raw provider payload
|
|
105
|
+
stays `unknown` until callers parse it themselves.
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import {
|
|
109
|
+
isAgentMessage,
|
|
110
|
+
isAgentToolUse,
|
|
111
|
+
isAgentToolResult,
|
|
112
|
+
isAgentMcpToolUse,
|
|
113
|
+
isAgentMcpToolResult,
|
|
114
|
+
isAgentCustomToolUse,
|
|
115
|
+
isAgentThinking,
|
|
116
|
+
isUserMessage,
|
|
117
|
+
isSessionStatusRunning,
|
|
118
|
+
isSessionStatusIdle,
|
|
119
|
+
isSessionStatusTerminated,
|
|
120
|
+
isSessionError,
|
|
121
|
+
isAgentEvent,
|
|
122
|
+
isUserEvent,
|
|
123
|
+
isSessionEvent,
|
|
124
|
+
isSpanEvent
|
|
125
|
+
} from "antpath";
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The official list of event types is maintained at
|
|
129
|
+
[`platform.claude.com/docs/en/managed-agents/events-and-streaming`](https://platform.claude.com/docs/en/managed-agents/events-and-streaming).
|
package/docs/skills.md
CHANGED
|
@@ -14,3 +14,5 @@ MVP skill inputs:
|
|
|
14
14
|
Local directories are packaged as zip files and mounted under `/antpath/skills/` unless overridden.
|
|
15
15
|
|
|
16
16
|
Inline skills are uploaded as markdown files and mounted under `/antpath/skills/` unless overridden.
|
|
17
|
+
|
|
18
|
+
The platform also mounts the `antpath` CLI at `/antpath/antpath` and a per-run manifest at `/antpath/index.json` on **every** run. Skills can invoke the managed HTTP proxy via `node /antpath/antpath proxy …` — see `credentials.md` for the policy/auth model.
|
package/package.json
CHANGED
|
@@ -20,9 +20,9 @@ This supersedes the earlier SDK-only MVP boundary.
|
|
|
20
20
|
- Minimal tenant-scoped dashboard metadata for monitoring run execution.
|
|
21
21
|
- Durable worker recovery after restarts, deploys, crashes, or missed notifications.
|
|
22
22
|
- Horizontal worker scaling without duplicate run ownership.
|
|
23
|
-
- Provider cleanup by default after terminal states, with optional retention when configured per-request or per-deployment.
|
|
24
|
-
- BYO provider key custody with encrypted
|
|
25
|
-
-
|
|
23
|
+
- Provider cleanup by default after terminal states, with optional retention of Claude-side resources when configured per-request or per-deployment.
|
|
24
|
+
- Per-session BYO provider key custody: keys arrive inline with each run, are encrypted in Supabase Vault for the lifetime of that single run, and are deleted at cleanup. Persistent workspace-level provider key custody is backlog.
|
|
25
|
+
- Unconditional capture of every session-scoped Claude artifact into private Supabase Storage, bounded only by the workspace storage cap.
|
|
26
26
|
- Programmatic SDK access for submitting and observing platform runs.
|
|
27
27
|
- Test-driven implementation across SDK, dashboard, worker, database, storage, and provider adapters.
|
|
28
28
|
|
|
@@ -34,6 +34,8 @@ This supersedes the earlier SDK-only MVP boundary.
|
|
|
34
34
|
- No direct browser access to provider APIs.
|
|
35
35
|
- No direct browser Supabase data access.
|
|
36
36
|
- No raw prompt, raw model output, raw provider event payload, MCP credential, provider key, or output file content in normal application tables.
|
|
37
|
+
- No persistent workspace-level provider connections (no "saved Anthropic keys"). Every run carries its own secrets bundle; nothing about the user's Anthropic key persists past run cleanup. Persistent BYO custody is backlog.
|
|
38
|
+
- No user-configurable output capture: every artifact Claude exposes on the run's session is captured. There are no globs, no `capture: false`, no per-file or per-run user-visible caps.
|
|
37
39
|
- No provider Agent/Environment caching in MVP.
|
|
38
40
|
- No provider webhooks in MVP.
|
|
39
41
|
- No Supabase Realtime in MVP.
|
|
@@ -44,7 +46,7 @@ This supersedes the earlier SDK-only MVP boundary.
|
|
|
44
46
|
| Area | Decision |
|
|
45
47
|
| --- | --- |
|
|
46
48
|
| Product surface | Platform plus SDK. |
|
|
47
|
-
| Repository | Convert the repository to
|
|
49
|
+
| Repository | Convert the repository to a pnpm TypeScript workspace. |
|
|
48
50
|
| SDK location | Move the existing SDK package to `packages/sdk`. |
|
|
49
51
|
| SDK role | The SDK submits runs and observes status, metadata, outputs, cancellation, and deletion through the platform API. |
|
|
50
52
|
| Dashboard | Build a first-party minimal dashboard. |
|
|
@@ -59,8 +61,8 @@ This supersedes the earlier SDK-only MVP boundary.
|
|
|
59
61
|
| Service credentials | Supabase service-role credentials are server/worker only. |
|
|
60
62
|
| SDK auth | SDK uses hashed, workspace-scoped API tokens. Dashboard uses Auth.js sessions. |
|
|
61
63
|
| API token attribution | Token-authenticated runs are attributed to the token creator at submission time unless a future service-account model is introduced. |
|
|
62
|
-
| Provider key custody |
|
|
63
|
-
| Secret lifetime | Worker resolves
|
|
64
|
+
| Provider key custody | BYO Anthropic key arrives inline with every run submission. The BFF stores the secrets bundle in Supabase Vault, attaches the secret id to the run row, and deletes the Vault entry as part of cleanup. Persistent workspace-level provider connections are backlog. |
|
|
65
|
+
| Secret lifetime | Worker resolves the per-run secrets bundle once per claimed lifecycle step, keeps the decoded payload only in memory for that step, and drops it before lease release. The Vault entry itself is destroyed when the run reaches terminal cleanup. |
|
|
64
66
|
| Provider resources | Create provider Agent, Environment, Vault/Credential, Session, and file resources per run in MVP. |
|
|
65
67
|
| Provider resource caching | Backlog; later cache Agent/Environment by Template/config hash. |
|
|
66
68
|
| Worker wakeup | Poll due rows from the runs table. Postgres `NOTIFY` is a latency optimization only. |
|
|
@@ -72,12 +74,13 @@ This supersedes the earlier SDK-only MVP boundary.
|
|
|
72
74
|
| MCP approvals | Disallow approval-required tools. Worker must not approve tools or return custom tool results. |
|
|
73
75
|
| Template boundary | Templates are code-first, secret-free snapshots with stable hashes. |
|
|
74
76
|
| Idempotency | Run submission idempotency is scoped by `(workspace_id, idempotency_key)` and request hash. Same key and same hash returns the existing run; same key and different hash returns conflict. |
|
|
75
|
-
| Quotas | Enforce workspace
|
|
76
|
-
| Run caps | Plan-based caps cover duration, concurrency,
|
|
77
|
+
| Quotas | Enforce workspace storage and concurrency quotas. Storage is the only user-visible cap on output capture. |
|
|
78
|
+
| Run caps | Plan-based caps cover duration, concurrency, polling, retries, and workspace storage. There are no user-visible per-file or per-run output caps. |
|
|
77
79
|
| Operational config | Exact cap/tier values are environment-configurable defaults with conservative fallbacks and run-level snapshots. |
|
|
78
|
-
|
|
|
80
|
+
| Boot-time env validation | Worker and dashboard validate required environment variables at startup through a shared role-scoped helper and exit/abort with an aggregated error when any are missing. No silent defaults for identity, credentials, or required URLs. |
|
|
81
|
+
| Output capture | Unconditional. Every file Claude exposes on the run's session is captured into private Supabase Storage. The workspace storage cap is the only user-visible quota; capture failures (quota exceeded, download failed) are persisted as `output_capture_failures` rows and surfaced in the dashboard alongside successful outputs. |
|
|
79
82
|
| Output access | BFF returns signed links only after workspace authorization. |
|
|
80
|
-
| Cleanup | Clean up Claude provider resources by default after terminal state and output capture; explicit run policy or worker default can retain
|
|
83
|
+
| Cleanup | Clean up Claude provider resources by default after terminal state and output capture; explicit run policy or worker default can retain Claude-side resources for inspection. The per-run Vault secret is always deleted at cleanup regardless of the Claude-side retention knob. |
|
|
81
84
|
| Delete semantics | User delete is soft/pending while execution, cleanup, or storage deletion is active. Hard purge happens only after cleanup/storage deletion succeeds. |
|
|
82
85
|
| Retention | Run metadata and stored outputs remain until user deletion in MVP. |
|
|
83
86
|
| Realtime | Defer from MVP; use BFF-mediated refresh/polling first. Revisit with custom Supabase JWT/RLS or a server-mediated realtime bridge. |
|
|
@@ -125,35 +128,44 @@ packages/
|
|
|
125
128
|
|
|
126
129
|
### Identity and tenancy
|
|
127
130
|
|
|
128
|
-
- `users`
|
|
129
|
-
- Auth.js
|
|
131
|
+
- Auth.js adapter tables: `users`, `accounts`, `sessions`, and `verification_token`
|
|
132
|
+
- Auth.js owns OAuth account linking and email magic-link verification persistence in the same Supabase Postgres database.
|
|
133
|
+
- Active application sessions use Auth.js JWT strategy; the `sessions` table exists for adapter compatibility but is not the active session store.
|
|
134
|
+
- Auth.js OAuth tokens remain confined to Auth.js adapter tables and never become antpath provider credentials.
|
|
135
|
+
- `app_users`
|
|
136
|
+
- Antpath-owned user identity row linked to Auth.js `users.id` through `auth_user_id`.
|
|
137
|
+
- Workspace membership, run attribution, API-token ownership, disable state, and JWT token-version checks use `app_users.id`.
|
|
138
|
+
- `disabled_at` and `token_version` are checked by privileged BFF routes to reject disabled or stale JWT users.
|
|
130
139
|
- No provider secrets.
|
|
131
140
|
- `workspaces`
|
|
132
141
|
- Tenant boundary.
|
|
133
142
|
- Plan, quota, status, and retention settings.
|
|
134
143
|
- `workspace_memberships`
|
|
135
|
-
- Workspace,
|
|
144
|
+
- Workspace, `app_user_id`, role, and membership status.
|
|
136
145
|
- Every dashboard/API operation must prove membership/scope.
|
|
137
146
|
- `api_tokens`
|
|
138
147
|
- Workspace-scoped SDK credentials.
|
|
139
|
-
- Store hashed token material only, plus scopes,
|
|
148
|
+
- Store hashed token material only, plus scopes, `creator_app_user_id`, last-used timestamp, and revoked timestamp.
|
|
140
149
|
- API-token submissions freeze the attributed user on the run row at creation time.
|
|
141
150
|
|
|
142
|
-
###
|
|
151
|
+
### Per-run secrets
|
|
143
152
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
153
|
+
There is no `provider_connections` table in MVP. Every run carries its own secrets.
|
|
154
|
+
|
|
155
|
+
- The submission contract has a top-level `secrets` block: `{ anthropic: { apiKey }, mcpServers?: [{ name, url, headers? }], skills?: [...] }`. The shared parser enforces that **no other field** carries secret-bearing keys; the `secrets` block is the single allowlisted carrier and never serializes into normal metadata, logs, events, audit rows, or stored snapshots.
|
|
156
|
+
- The dashboard BFF stores the JSON-encoded bundle in Supabase Vault at submission time and writes the resulting `vault_secret_id` into `runs.execution_secret_id`. The bundle is the only durable trace of the user's Anthropic key, MCP credentials, and skill references for that run.
|
|
157
|
+
- Worker code reads `runs.execution_secret_id` once per claimed step, decodes the bundle in memory, builds the Claude provider, and drops the decoded values before lease release.
|
|
158
|
+
- At terminal cleanup the worker deletes the Vault entry and clears `runs.execution_secret_id`. Cleanup of the Vault secret happens regardless of the Claude-side retention knob.
|
|
159
|
+
- If the user revokes (rotates) the Anthropic key on Anthropic's side mid-run, antpath has no knowledge of this until the next provider call fails. Active runs surface a `tenant_permanent` failure with a redacted reason; cleanup proceeds with the previously vaulted key while it remains valid and gives up gracefully if it does not.
|
|
149
160
|
|
|
150
161
|
### Runs
|
|
151
162
|
|
|
152
163
|
- `runs`
|
|
153
164
|
- User-visible logical run.
|
|
154
|
-
- Workspace,
|
|
165
|
+
- Workspace, `creator_app_user_id`, template snapshot/hash, status, lifecycle phase, plan caps, timestamps.
|
|
155
166
|
- Submission idempotency key and request hash.
|
|
156
167
|
- Unique constraint: `(workspace_id, idempotency_key)`.
|
|
168
|
+
- `execution_secret_id` — Supabase Vault secret id for the per-run secrets bundle (Anthropic key + MCP credentials + skill references). Cleared at cleanup.
|
|
157
169
|
- Lease fields: `lease_owner`, `lease_token`, `lease_expires_at`, `attempt_count`, `next_check_at`, `priority`.
|
|
158
170
|
- Cancellation/deletion fields: `cancel_requested_at`, `pending_delete_at`, `deleted_at`.
|
|
159
171
|
- Provider observation cursor/watermark where available.
|
|
@@ -174,24 +186,34 @@ packages/
|
|
|
174
186
|
|
|
175
187
|
- `output_objects`
|
|
176
188
|
- Workspace/run owner, storage bucket/path, size, checksum if available, content type, provider file id.
|
|
189
|
+
- One row per artifact successfully captured from the run's session into private Supabase Storage.
|
|
177
190
|
- Used for quota accounting and signed-link generation.
|
|
191
|
+
- `output_capture_failures`
|
|
192
|
+
- Workspace/run owner, provider file id, filename, byte size as reported by the provider, reason (`workspace_quota_exceeded`, `download_failed`, etc.), redacted error message.
|
|
193
|
+
- Surfaced in the dashboard alongside `output_objects` so the user can see exactly which session artifacts didn't make it to storage and why.
|
|
178
194
|
- `cleanup_attempts`
|
|
179
195
|
- Per-resource cleanup action, status, retry count, redacted error code/message, timestamps.
|
|
180
196
|
- `usage_ledger`
|
|
181
197
|
- Token/cost/storage attribution by workspace and attributed user.
|
|
182
198
|
- Written transactionally with source event/output rows to avoid drift.
|
|
199
|
+
- `audit_logs`
|
|
200
|
+
- Safe security audit trail for sign-in user upsert, API-token changes, run cancellation/deletion, signed-link creation, and cleanup failures.
|
|
201
|
+
- Stores safe identifiers, action names, redacted errors, and non-secret metadata only.
|
|
202
|
+
- `rate_limit_buckets`
|
|
203
|
+
- Durable token-bucket-style counters for abuse-sensitive BFF/API operations.
|
|
204
|
+
- Used before side effects for run submission, run cancellation/deletion, signed-link creation, API-token creation/revocation, and email-auth throttling where practical.
|
|
183
205
|
|
|
184
206
|
## Run lifecycle
|
|
185
207
|
|
|
186
|
-
1. SDK/dashboard submits a run to the BFF.
|
|
208
|
+
1. SDK/dashboard submits a run to the BFF. The submission carries a top-level `secrets` bundle (Anthropic key, optional MCP credentials, optional skill references) plus the non-secret template/variables/cleanup payload.
|
|
187
209
|
2. BFF validates Auth.js session or SDK API token.
|
|
188
210
|
3. BFF resolves active workspace membership/scope.
|
|
189
|
-
4. BFF validates Template/request shape, plan limits, and idempotency key/request hash.
|
|
190
|
-
5. BFF writes a `runs` row with execution-affecting cap values snapshotted from plan/env defaults.
|
|
211
|
+
4. BFF validates Template/request shape, plan limits, and idempotency key/request hash. The shared parser rejects every secret-bearing key outside the allowlisted `secrets` block.
|
|
212
|
+
5. BFF stores the JSON-encoded `secrets` bundle in Supabase Vault and writes a `runs` row with execution-affecting cap values snapshotted from plan/env defaults plus `execution_secret_id` pointing at the Vault entry.
|
|
191
213
|
6. BFF emits Postgres `NOTIFY` for fast wakeup.
|
|
192
214
|
7. Worker claims due runs with row locking, `SKIP LOCKED`, lease token, and lease expiry.
|
|
193
|
-
8. Worker resolves the
|
|
194
|
-
9. Worker validates platform constraints: no approval-required tools, no custom antpath-executed tools
|
|
215
|
+
8. Worker resolves the per-run secrets bundle from Supabase Vault for the claimed step.
|
|
216
|
+
9. Worker validates platform constraints: no approval-required tools, no custom antpath-executed tools.
|
|
195
217
|
10. Worker pre-journals intended provider resources.
|
|
196
218
|
11. Worker creates provider resources and records provider IDs immediately after successful calls.
|
|
197
219
|
12. Worker creates provider session and sends the initial user event.
|
|
@@ -199,8 +221,8 @@ packages/
|
|
|
199
221
|
14. Worker later reclaims the run and polls provider session status plus event list.
|
|
200
222
|
15. Worker stores only metadata events and usage, deduped by provider event id.
|
|
201
223
|
16. Before every side effect, worker checks lease token and cancellation/deletion requests.
|
|
202
|
-
17. On terminal provider state or antpath timeout, worker
|
|
203
|
-
18. Worker cleans up provider resources by default, or records them as retained when the run/deployment policy asks to retain them.
|
|
224
|
+
17. On terminal provider state or antpath timeout, worker lists session-scoped files via the Claude Files API and unconditionally captures every file the workspace storage quota still has room for, persisting `output_capture_failures` rows for any file that would exceed the cap.
|
|
225
|
+
18. Worker cleans up Claude-side provider resources by default, or records them as retained when the run/deployment policy asks to retain them. The per-run Vault entry is deleted regardless.
|
|
204
226
|
19. Worker marks the run terminal and releases the lease.
|
|
205
227
|
20. Dashboard/SDK read tenant-scoped metadata and signed output links through BFF APIs.
|
|
206
228
|
|
|
@@ -259,51 +281,59 @@ BFF/server actions must:
|
|
|
259
281
|
- return only metadata allowed by the user's role/scope;
|
|
260
282
|
- keep Supabase service-role credentials out of browser bundles.
|
|
261
283
|
|
|
284
|
+
Auth.js and antpath authorization stay separate in the database. Auth.js owns `users`, `accounts`, `sessions`, and `verification_token`; antpath owns `app_users` and links each row to Auth.js `users.id`. JWT callbacks expose only safe identity fields such as `app_user_id` and token version. BFF routes must load the current `app_users` row and reject disabled users or stale token versions before privileged actions.
|
|
285
|
+
|
|
286
|
+
All platform and Auth.js tables have RLS enabled and `anon`/`authenticated` table privileges revoked. This is a defense-in-depth guard against accidental Supabase Data API exposure; direct `pg` repository code remains the primary BFF/worker authorization boundary for this phase.
|
|
287
|
+
|
|
262
288
|
Supabase Realtime is backlog until antpath either mints short-lived Supabase-compatible JWTs with RLS policies or exposes a server-mediated realtime bridge.
|
|
263
289
|
|
|
264
290
|
## Secret handling
|
|
265
291
|
|
|
266
|
-
- Provider keys
|
|
267
|
-
- MCP credentials
|
|
268
|
-
- Normal app tables store only secret
|
|
269
|
-
- Worker resolves
|
|
270
|
-
-
|
|
292
|
+
- Provider keys arrive inline with each submission and are stored in Supabase Vault tied to a single run via `runs.execution_secret_id`.
|
|
293
|
+
- MCP credentials and any skill payloads referenced by the run live alongside the provider key inside the same per-run Vault bundle.
|
|
294
|
+
- Normal app tables store only the Vault secret id, not decoded provider keys, MCP credentials, or skill payloads.
|
|
295
|
+
- Worker resolves the decoded bundle only for a claimed lifecycle step and drops the decoded values before lease release.
|
|
296
|
+
- The shared submission parser keeps an allowlist of exactly one carrier (`secrets`) and rejects every other field that looks like a secret-bearing key (`apiKey`, `accessToken`, `password`, `mcpCredentials`, etc.).
|
|
297
|
+
- Secret values use explicit redacted wrappers in code so they cannot serialize into logs, metrics, errors, events, or fixtures.
|
|
271
298
|
- Secret redaction applies to logs, errors, run metadata, provider events, tests, fixtures, and docs.
|
|
272
|
-
-
|
|
273
|
-
-
|
|
299
|
+
- The per-run Vault entry is deleted as part of run cleanup. Cleanup of the Vault entry happens regardless of the Claude-side retention knob.
|
|
300
|
+
- If a provider key is revoked or rotated on the provider side mid-run, the next provider call fails with a `tenant_permanent` error. The worker fails the affected run, attempts cleanup with whatever credentials are still valid, and surfaces a redacted reason on the dashboard.
|
|
301
|
+
- There is no platform-side rotation/revocation flow because there is no persistent provider connection. To "rotate" the user simply submits the next run with a different key.
|
|
274
302
|
|
|
275
303
|
## Output storage and quotas
|
|
276
304
|
|
|
305
|
+
Output capture is unconditional. Every artifact the user's Claude session exposes is downloaded to private Supabase Storage. There are no user-facing knobs to opt out, no globs, and no per-file or per-run user-visible caps.
|
|
306
|
+
|
|
277
307
|
Output flow:
|
|
278
308
|
|
|
279
|
-
1. Worker lists
|
|
280
|
-
2.
|
|
281
|
-
3.
|
|
282
|
-
4.
|
|
283
|
-
5.
|
|
284
|
-
6.
|
|
285
|
-
7. Worker cleans up provider resources by default, or retains them when explicitly configured.
|
|
286
|
-
8. Dashboard/SDK request signed links through the BFF.
|
|
309
|
+
1. Worker lists files scoped to the run's Claude session: `GET /v1/files?scope_id=<session_id>`. Each entry includes `id`, `filename`, and `size_bytes`.
|
|
310
|
+
2. For each file, before downloading, the worker checks `(workspace.storage_used_bytes + size_bytes)` against the workspace storage cap.
|
|
311
|
+
3. If the file would exceed the cap, the worker writes an `output_capture_failures` row (`reason = "workspace_quota_exceeded"`, with filename and reported byte size) and moves on without downloading.
|
|
312
|
+
4. Otherwise the worker downloads via `GET /v1/files/{file_id}/content` with a hard streaming safety cap so a malformed listing response cannot OOM the worker, uploads to private Supabase Storage at a workspace-scoped path, and writes an `output_objects` row.
|
|
313
|
+
5. Other download/upload failures (network, storage, unexpected size) are recorded as `output_capture_failures` with the redacted reason.
|
|
314
|
+
6. Dashboard/SDK request signed links through the BFF for any `output_objects` row.
|
|
287
315
|
|
|
288
316
|
Quota rules:
|
|
289
317
|
|
|
290
|
-
- Workspace
|
|
291
|
-
-
|
|
292
|
-
-
|
|
293
|
-
-
|
|
318
|
+
- Workspace storage cap is the only hard enforcement boundary. Default is generous (configurable via env, see [`environment-variables.md`](./environment-variables.md)).
|
|
319
|
+
- There is no per-user quota. Per-user attribution still freezes on the run row at submission time for billing/audit purposes, but it is not a quota dimension.
|
|
320
|
+
- There are no user-visible per-file or per-run caps. A worker-internal streaming safety cap protects against malformed listings.
|
|
321
|
+
- Capture failures are first-class data: the dashboard renders `<filename> (<size>) — skipped: workspace quota exceeded` next to successful outputs so the user can see exactly what didn't make it.
|
|
294
322
|
|
|
295
323
|
## Cleanup and reconciliation
|
|
296
324
|
|
|
297
|
-
Claude provider-resource cleanup is mandatory by default after terminal provider state and output capture so antpath does not leave behind provider state. Retention is opt-in
|
|
325
|
+
Claude provider-resource cleanup is mandatory by default after terminal provider state and output capture so antpath does not leave behind provider state. Retention is opt-in for **Claude-side resources only** through `cleanup.claudeSession = "retain"` or `ANTPATH_WORKER_CLAUDE_SESSION_CLEANUP_DEFAULT=retain`. Retained resources are recorded in `provider_resources.cleanup_status = retained` and remain reachable via the provider API. Antpath-side metadata, output objects, and the per-run Vault entry are not affected by this knob — Vault entries are always deleted at cleanup; metadata and outputs persist until the user explicitly deletes the run.
|
|
298
326
|
|
|
299
327
|
Explicit cleanup order:
|
|
300
328
|
|
|
301
|
-
1.
|
|
302
|
-
2.
|
|
303
|
-
3. Agent/archive.
|
|
304
|
-
4. Environment/archive or delete where allowed.
|
|
305
|
-
5.
|
|
306
|
-
6.
|
|
329
|
+
1. Capture session-scoped files into private Supabase Storage (with quota pre-check; failures persisted).
|
|
330
|
+
2. Provider session files / session where supported.
|
|
331
|
+
3. Agent / archive.
|
|
332
|
+
4. Environment / archive or delete where allowed.
|
|
333
|
+
5. Skills uploaded for this session and any other ephemeral provider resources created for this run.
|
|
334
|
+
6. Provider Vault/Credentials created on Claude's side for MCP wiring.
|
|
335
|
+
7. The antpath per-run Vault entry: `vault.deleteSecret(runs.execution_secret_id)`, then clear the column.
|
|
336
|
+
8. Local output metadata/storage only when the user deletes a run.
|
|
307
337
|
|
|
308
338
|
Cleanup properties:
|
|
309
339
|
|
|
@@ -319,10 +349,19 @@ Resource leak recovery:
|
|
|
319
349
|
- A reconciliation sweeper reviews unfinished intended rows, expired leases, and provider-listable resources tagged with antpath metadata.
|
|
320
350
|
- If provider create succeeded but provider id was not persisted before a crash, the sweeper attempts to match by deterministic name/metadata and attach the provider id for cleanup.
|
|
321
351
|
|
|
322
|
-
Workspace
|
|
352
|
+
Workspace deletion:
|
|
323
353
|
|
|
324
354
|
- Workspace deletion blocks new runs, requests cancellation for active runs, drains cleanup, deletes stored outputs, then purges metadata.
|
|
325
|
-
-
|
|
355
|
+
- There is no separate "provider key revocation" flow because there is no persistent provider key. Submitting the next run with a different key is the rotation mechanism.
|
|
356
|
+
|
|
357
|
+
## Audit, rate limits, quotas, and deletion
|
|
358
|
+
|
|
359
|
+
- Abuse-sensitive actions are rate-limited before provider, Vault, Storage, or mutation side effects where practical.
|
|
360
|
+
- Audit logs must never include provider keys, OAuth tokens, MCP credentials, raw prompts, raw outputs, signed URLs, or service-role credentials.
|
|
361
|
+
- Workspace storage quota is enforced before output download/upload when file size is known, with streaming hard caps for unknown-size files.
|
|
362
|
+
- App-user deletion is soft/disable-first; hard deletion must not break historical run, usage, output, or audit references.
|
|
363
|
+
- Workspace deletion is pending-first: block new submissions, mark active runs pending-delete, allow cleanup/storage deletion to drain, then purge only safe metadata.
|
|
364
|
+
- Recovery operations should be driven by explicit cleanup/deletion state and audit logs, not manual DB guessing.
|
|
326
365
|
|
|
327
366
|
## Run state model
|
|
328
367
|
|
|
@@ -364,6 +403,8 @@ Error classes:
|
|
|
364
403
|
- Database: Supabase Postgres.
|
|
365
404
|
- Storage: Supabase Storage private bucket.
|
|
366
405
|
- Secrets: Vercel/Railway environment variables plus Supabase Vault for tenant provider keys.
|
|
406
|
+
- CI: GitHub Actions with pnpm, default lint/test/build/package checks, and a separate local Supabase integration job.
|
|
407
|
+
- Local dev stack: `pnpm dev:stack` starts Supabase without printing local keys, validates local config, checks migrations, builds shared packages, and runs the dashboard and worker together. Missing Auth.js providers do not block local service startup unless `ANTPATH_DEV_STACK_REQUIRE_AUTH_PROVIDER=1` is set.
|
|
367
408
|
|
|
368
409
|
Required runtime config includes:
|
|
369
410
|
|
|
@@ -374,7 +415,10 @@ Required runtime config includes:
|
|
|
374
415
|
- Auth.js secret/providers;
|
|
375
416
|
- SDK token hashing secret/pepper;
|
|
376
417
|
- worker identity;
|
|
377
|
-
- provider API base/version settings
|
|
418
|
+
- provider API base/version settings;
|
|
419
|
+
- dashboard BFF rate-limit defaults;
|
|
420
|
+
- worker polling/lease/metrics settings.
|
|
421
|
+
- dev stack ports for dashboard and worker.
|
|
378
422
|
|
|
379
423
|
Environment-configurable defaults include:
|
|
380
424
|
|
|
@@ -396,26 +440,28 @@ Environment-configurable defaults include:
|
|
|
396
440
|
- workspace storage cap;
|
|
397
441
|
- signed URL TTL;
|
|
398
442
|
- free user allowance;
|
|
399
|
-
- metadata retention toggles
|
|
443
|
+
- metadata retention toggles;
|
|
444
|
+
- audit/rate-limit windows for sensitive BFF actions.
|
|
445
|
+
- live e2e opt-in flags for provider sessions, public MCP endpoints, Anthropic skill IDs, and timeouts.
|
|
400
446
|
|
|
401
447
|
Missing optional env vars must fall back to conservative low limits. Missing required secret/connectivity env vars must fail service startup.
|
|
402
448
|
|
|
449
|
+
Live public MCP/skills e2e tests are intentionally opt-in because they depend on external provider and public MCP availability. Deterministic unit/component tests validate request wiring for MCP server declarations, tool policies, skill references, and secret non-leakage; the live e2e validates the real provider accepts and runs that configuration end to end.
|
|
450
|
+
|
|
451
|
+
No-cost provider resilience and load testing uses sanitized replay snapshots from real Managed Agents sessions. Replay tests inject network disconnects, transient HTTP failures, provider latency, slow SSE chunks, cleanup failures, and concurrent full-session load around those snapshots. Idempotent read/delete-style calls may retry with bounded backoff; provider create/send POST calls are not retried by default because their side effects may already have happened.
|
|
452
|
+
|
|
403
453
|
## Open implementation details
|
|
404
454
|
|
|
405
455
|
These are not architecture blockers, but must be pinned during implementation planning:
|
|
406
456
|
|
|
407
|
-
-
|
|
408
|
-
- Exact Auth.js providers.
|
|
409
|
-
- Auth.js adapter/session mode and user mirror lifecycle.
|
|
410
|
-
- Supabase Vault access method and SQL privilege wrapper shape.
|
|
411
|
-
- Signed URL TTL and whether storage paths include random unguessable components in addition to workspace/run ids.
|
|
412
|
-
- Deletion audit requirements.
|
|
457
|
+
- Production plan tier values for duration, concurrency, storage, polling, and free user allowance.
|
|
413
458
|
- Provider metadata naming convention.
|
|
414
459
|
- Provider list/search capabilities and reconciliation coverage for each resource type.
|
|
415
460
|
- Exact Claude Managed Agents event pagination/filter semantics and bounded polling fallback.
|
|
416
461
|
|
|
417
462
|
## Backlog
|
|
418
463
|
|
|
464
|
+
- Persistent workspace-level provider connections (BYO Anthropic key custody across runs, with rotation, revocation, and re-use). The MVP submission contract carries the key inline per run instead.
|
|
419
465
|
- Provider webhooks as wakeup/reconciliation accelerator.
|
|
420
466
|
- SSE live event stream for richer UI.
|
|
421
467
|
- Supabase Realtime with explicit Auth.js-to-Supabase authorization design.
|