@intentic/sandbox-contract 1.223.0 → 1.224.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/dist/chores/chores.js +1 -1
- package/dist/chores/chores.js.map +1 -1
- package/dist/chores/prompt.d.ts.map +1 -1
- package/dist/chores/prompt.js +3 -3
- package/dist/chores/prompt.js.map +1 -1
- package/dist/contracts/ports.contract.d.ts +11 -0
- package/dist/contracts/ports.contract.d.ts.map +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/schemas.d.ts +22 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +3 -0
- package/dist/schemas.js.map +1 -1
- package/package.json +4 -4
- package/src/agent-catalog.ts +78 -78
- package/src/agent-run-model.ts +8 -8
- package/src/capability-env.ts +3 -3
- package/src/capability-secrets.ts +5 -5
- package/src/chores/chores.ts +67 -67
- package/src/chores/digest.ts +3 -3
- package/src/chores/extension-update.ts +3 -3
- package/src/chores/fix-deps.ts +3 -3
- package/src/chores/probes.ts +16 -16
- package/src/chores/prompt.ts +9 -9
- package/src/chores/stack.ts +17 -17
- package/src/chores/verdict.ts +23 -23
- package/src/contract-lock.ts +7 -7
- package/src/contracts/activity.contract.ts +1 -1
- package/src/contracts/agent.contract.ts +2 -2
- package/src/contracts/agents.contract.ts +11 -11
- package/src/contracts/automations.contract.ts +5 -5
- package/src/contracts/capabilities.contract.ts +7 -7
- package/src/contracts/chores.contract.ts +3 -3
- package/src/contracts/ci.contract.ts +2 -2
- package/src/contracts/claude.contract.ts +3 -3
- package/src/contracts/drafts.contract.ts +1 -1
- package/src/contracts/endpoints.contract.ts +2 -2
- package/src/contracts/extensions.contract.ts +8 -8
- package/src/contracts/git.contract.ts +6 -6
- package/src/contracts/grok.contract.ts +4 -4
- package/src/contracts/host.contract.ts +5 -5
- package/src/contracts/intentic.contract.ts +2 -2
- package/src/contracts/logs.contract.ts +1 -1
- package/src/contracts/loops.contract.ts +8 -8
- package/src/contracts/personas.contract.ts +8 -8
- package/src/contracts/ports.contract.ts +1 -1
- package/src/contracts/prepush.contract.ts +2 -2
- package/src/contracts/providers.contract.ts +4 -4
- package/src/contracts/public.contract.ts +1 -1
- package/src/contracts/push.contract.ts +2 -2
- package/src/contracts/secrets.contract.ts +2 -2
- package/src/contracts/sessions.contract.ts +2 -2
- package/src/contracts/settings.contract.ts +3 -3
- package/src/contracts/share.contract.ts +1 -1
- package/src/contracts/skills.contract.ts +2 -2
- package/src/contracts/system.contract.ts +9 -9
- package/src/contracts/usage.contract.ts +1 -1
- package/src/contracts/vpn.contract.ts +5 -5
- package/src/contracts/workflows.contract.ts +12 -12
- package/src/contracts/workspace.contract.ts +13 -13
- package/src/conversation-ids.ts +7 -7
- package/src/events.ts +136 -136
- package/src/history-state.ts +12 -12
- package/src/host-protocol.ts +7 -7
- package/src/hostnames.ts +15 -15
- package/src/index.ts +8 -8
- package/src/listener-protocol.ts +12 -12
- package/src/model-order.ts +30 -30
- package/src/output-fields.ts +9 -9
- package/src/path-refs.ts +6 -6
- package/src/publish-drafts.ts +6 -6
- package/src/quick-model.ts +15 -15
- package/src/routes.ts +19 -19
- package/src/runtime-state.ts +22 -22
- package/src/schemas.ts +930 -918
- package/src/search-globs.ts +6 -6
- package/src/session-names.ts +5 -5
- package/src/share-paths.ts +7 -7
- package/src/sse.ts +1 -1
- package/src/state-portability.ts +8 -8
- package/src/terminal-protocol.ts +3 -3
- package/src/title.ts +20 -20
- package/src/tunnel-ids.ts +7 -7
- package/src/versions.ts +5 -5
- package/src/workflow-faults.ts +11 -11
- package/src/workspace-state.ts +119 -119
|
@@ -11,14 +11,14 @@ import {
|
|
|
11
11
|
} from "../schemas.js";
|
|
12
12
|
|
|
13
13
|
// The sandbox's automations manifest (scheduled agent wake-ups). `list` returns each automation with its recent
|
|
14
|
-
// runs + next fire time. `upsert` adds or edits by id (nothing to provision
|
|
14
|
+
// runs + next fire time. `upsert` adds or edits by id (nothing to provision, the scheduler picks it up on its
|
|
15
15
|
// next poll); `setEnabled` changes only the switch, so a list-row action never has to reconstruct the record.
|
|
16
16
|
// `remove` deletes.
|
|
17
17
|
// The `pending*` routes are the owner's approval queue: a `requireApproval` automation holds each fire here
|
|
18
18
|
// instead of waking; `approve` runs the held wake, `reject` drops it.
|
|
19
19
|
export const automationsContract = {
|
|
20
20
|
list: oc.route({ method: "GET", path: "/automations" }).output(AutomationsListSchema),
|
|
21
|
-
/* WHAT CAN WAKE AN AGENT HERE, and what to start from
|
|
21
|
+
/* WHAT CAN WAKE AN AGENT HERE, and what to start from, the daemon's own sources and templates merged with
|
|
22
22
|
* every installed extension's. The composer's entire vocabulary, so that adding a trigger to an area is a
|
|
23
23
|
* change to that area and to nothing else. `upsert` below validates against the same merge, which is what
|
|
24
24
|
* keeps the surface and the daemon from disagreeing about what is allowed. */
|
|
@@ -26,18 +26,18 @@ export const automationsContract = {
|
|
|
26
26
|
upsert: oc.route({ method: "POST", path: "/automations" }).input(AutomationSchema).output(OkSchema),
|
|
27
27
|
setEnabled: oc.route({ method: "POST", path: "/automations/{id}/enabled" }).input(AutomationEnabledInputSchema).output(OkSchema),
|
|
28
28
|
remove: oc.route({ method: "DELETE", path: "/automations/{id}" }).input(AutomationIdParamSchema).output(OkSchema),
|
|
29
|
-
/* Fire one automation NOW, by hand
|
|
29
|
+
/* Fire one automation NOW, by hand, the answer to "I wrote a 3 a.m. cron and I have no way to try it".
|
|
30
30
|
* It runs the SAME path the real trigger runs: a schedule stays a headless main-tree wake, because a
|
|
31
31
|
* test-fire that proves an isolated worktree works proves nothing about the fire it is standing in for. The
|
|
32
32
|
* guard runs too ("skipped by guard" is the most useful thing this can report); only the approval gate is
|
|
33
33
|
* skipped, since pressing the button IS the owner's approval.
|
|
34
34
|
*
|
|
35
|
-
* Owner-explicit, so a DISABLED automation fires as well
|
|
35
|
+
* Owner-explicit, so a DISABLED automation fires as well, trying a prompt before switching it on is the
|
|
36
36
|
* main reason to press this, and unlike the /automations/{id}/fire webhook there is no outside sender here to
|
|
37
37
|
* fail closed against.
|
|
38
38
|
*
|
|
39
39
|
* NOT FOR A LISTENER, which is the one trigger whose fire is nothing without the thing that fired it. A
|
|
40
|
-
* listener's prompt is a brief about handling the events riding with it, and by hand there are none
|
|
40
|
+
* listener's prompt is a brief about handling the events riding with it, and by hand there are none, so the
|
|
41
41
|
* button could only ever produce an agent told to handle events, handed none, asking where they went. Worse,
|
|
42
42
|
* that pointless run took the automation's turn: a real mention arriving while it ran had to wait behind it.
|
|
43
43
|
* Refused here rather than hidden in the UI alone, because the honest answer to "how do I test this" is to
|
|
@@ -19,14 +19,14 @@ import {
|
|
|
19
19
|
/* The sandbox's unified capability manifest. `list` returns each active capability with its live status, the
|
|
20
20
|
* non-secret echo of its config, and the NAMES of the credentials it holds. `add` upserts a capability and
|
|
21
21
|
* STREAMS its apply (devops scaffolding / service provisioning emit ndjson progress; mcp/integration emit a
|
|
22
|
-
* terminal frame), mirroring the /intentic runner. `remove` tears it down (devops refuses
|
|
22
|
+
* terminal frame), mirroring the /intentic runner. `remove` tears it down (devops refuses, deleting the repos
|
|
23
23
|
* is data loss). `status` re-probes a single capability for a lazy UI refresh. `marketplace` resolves a Claude
|
|
24
24
|
* Code plugin marketplace repo into installable plugin-capability configs.
|
|
25
25
|
*
|
|
26
26
|
* `add` IS ALSO THE EDIT, because the write is an upsert: the same id with a changed config changes that
|
|
27
27
|
* connection. A caller editing one has never been shown its credentials, so it sends VAULTED
|
|
28
28
|
* (capability-secrets.ts) for each it is leaving alone and the daemon resolves those from what is stored before
|
|
29
|
-
* anything runs
|
|
29
|
+
* anything runs, the only way to change one setting on a tunnel without re-typing its key. A marker with
|
|
30
30
|
* nothing behind it is refused rather than written. */
|
|
31
31
|
export const capabilitiesContract = {
|
|
32
32
|
list: oc.route({ method: "GET", path: "/capabilities" }).output(CapabilitiesListSchema),
|
|
@@ -34,24 +34,24 @@ export const capabilitiesContract = {
|
|
|
34
34
|
remove: oc.route({ method: "DELETE", path: "/capabilities/{id}" }).input(CapabilityIdParamSchema).output(OkSchema),
|
|
35
35
|
/* Give a connection a different name, carrying what the old one keyed: a browser profile with its logins,
|
|
36
36
|
* a connected machine's enrollment, an extension's checkout. The name is the agent's handle for the thing,
|
|
37
|
-
* so this is a migration
|
|
37
|
+
* so this is a migration, add + remove would lose exactly the state that makes the connection worth
|
|
38
38
|
* keeping. A kind whose name is part of what it IS (the scaffolders, the one-per-sandbox cards) refuses. */
|
|
39
39
|
rename: oc.route({ method: "POST", path: "/capabilities/{id}/rename" }).input(CapabilityRenameSchema).output(OkSchema),
|
|
40
40
|
// Replace just the secret in a capability's config (the /secrets page's edit) and re-run its apply.
|
|
41
41
|
setSecret: oc.route({ method: "POST", path: "/capabilities/{id}/secret" }).input(CapabilitySecretInputSchema).output(OkSchema),
|
|
42
42
|
status: oc.route({ method: "GET", path: "/capabilities/{id}/status" }).input(CapabilityIdParamSchema).output(CapabilityStatusSchema),
|
|
43
|
-
/* One capability's stored config, secrets included
|
|
43
|
+
/* One capability's stored config, secrets included, how an extension BACKEND dials the service behind a
|
|
44
44
|
* connected capability (ext-deployments reads its Komodo's key pair through this). Never a browser's: the
|
|
45
45
|
* handler refuses any caller with a member identity, so only the daemon's header grants reach it, and an
|
|
46
|
-
* extension's grant reaches it only when its manifest declares the route in `permissions.daemon
|
|
46
|
+
* extension's grant reaches it only when its manifest declares the route in `permissions.daemon`, which
|
|
47
47
|
* is the install dialog saying, in one line, "this extension can read connected credentials". */
|
|
48
48
|
connection: oc.route({ method: "GET", path: "/capabilities/{id}/connection" }).input(CapabilityIdParamSchema).output(CapabilityConnectionSchema),
|
|
49
49
|
marketplace: oc.route({ method: "POST", path: "/capabilities/marketplace" }).input(MarketplaceRequestSchema).output(MarketplaceSchema),
|
|
50
50
|
// "Not needed": stop offering this card until the workspace evidence behind it changes. Nothing is torn
|
|
51
|
-
// down and nothing is remembered about the card itself
|
|
51
|
+
// down and nothing is remembered about the card itself, only the evidence it was declined against.
|
|
52
52
|
dismiss: oc.route({ method: "DELETE", path: "/capabilities/recommendations/{card}" }).input(CapabilityCardParamSchema).output(OkSchema),
|
|
53
53
|
// Start an agent-kind capability's interactive login (its declared loginCommand) in a visible terminal
|
|
54
|
-
// session the user types into
|
|
54
|
+
// session the user types into, device-code sign-in flows. Returns the session the panel attaches to.
|
|
55
55
|
login: oc.route({ method: "POST", path: "/capabilities/{id}/login" }).input(CapabilityIdParamSchema).output(CapabilityLoginSchema),
|
|
56
56
|
// Mint one TOTP code from the capability's stored seed (a field its card marks `totp`). The one capability
|
|
57
57
|
// read the agent token is admitted to (see auth/grants): a code expires within its period and never reveals
|
|
@@ -2,19 +2,19 @@ import { oc } from "@orpc/contract";
|
|
|
2
2
|
import { ChoreLedgerWriteSchema, ChoreProbeRequestSchema, ChoresReportSchema, OkSchema } from "../schemas.js";
|
|
3
3
|
|
|
4
4
|
/* Maintenance evidence: what every repo under /work currently measures, and what has already been done about it.
|
|
5
|
-
* Three routes, because there are exactly three things the surface does
|
|
5
|
+
* Three routes, because there are exactly three things the surface does, read the evidence, ask for a
|
|
6
6
|
* measurement to be retaken, and record what a turn concluded.
|
|
7
7
|
*
|
|
8
8
|
* There is no `GET /chores/{id}` and no "run this chore" route on purpose. A chore RUN is an ordinary isolated
|
|
9
9
|
* fleet agent (`POST /agent` with a derived conversation id), the same as an acceptance run or a documentation
|
|
10
|
-
* generation
|
|
10
|
+
* generation, so the worktree, the live status, the cost, the transcript and the /agents/<id> page already
|
|
11
11
|
* exist, and adding a bespoke launcher here would be a second way to start a turn that has to be kept in step
|
|
12
12
|
* with the first. */
|
|
13
13
|
export const choresContract = {
|
|
14
14
|
// Every repo's standing evidence in one read: cached probe results (with their age and state), the cheap
|
|
15
15
|
// resident signals, the ledger, and the daemon's node version. The rail badge polls this; so does the panel.
|
|
16
16
|
list: oc.route({ method: "GET", path: "/chores" }).output(ChoresReportSchema),
|
|
17
|
-
// Re-run one repo's probe now, ignoring its TTL
|
|
17
|
+
// Re-run one repo's probe now, ignoring its TTL, the panel's per-probe refresh. An ack: the runner works in
|
|
18
18
|
// the background and the result arrives on the next `list`, because a jscpd sweep outlives any sane request.
|
|
19
19
|
probe: oc.route({ method: "POST", path: "/chores/probe" }).input(ChoreProbeRequestSchema).output(OkSchema),
|
|
20
20
|
// Record what a chore turn concluded, or snooze one. Upsert by repo+chore: a chore has one current verdict,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
// Pipelines on the workspace repos' github/gitlab remotes. `runs` serves the Pipelines rail view (cache +
|
|
13
13
|
// on-demand backfill; per-repo webhook warnings ride along); `rerun`/`cancel` proxy to the vendor; `fix`
|
|
14
14
|
// opens an isolated agent conversation seeded with the failure context. The public webhook receiver
|
|
15
|
-
// (/ci/webhook/:host) is a plain Hono route
|
|
15
|
+
// (/ci/webhook/:host) is a plain Hono route, vendors can't do Google ID tokens, so it lives outside this
|
|
16
16
|
// contract, like /automations/{id}/fire.
|
|
17
17
|
export const ciContract = {
|
|
18
18
|
runs: oc.route({ method: "GET", path: "/ci/runs" }).output(CiRunsResponseSchema),
|
|
@@ -20,7 +20,7 @@ export const ciContract = {
|
|
|
20
20
|
cancel: oc.route({ method: "POST", path: "/ci/runs/cancel" }).input(CiRunParamSchema).output(OkSchema),
|
|
21
21
|
jobs: oc.route({ method: "POST", path: "/ci/runs/jobs" }).input(CiRunParamSchema).output(CiJobsResponseSchema),
|
|
22
22
|
fix: oc.route({ method: "POST", path: "/ci/fix" }).input(CiFixParamSchema).output(CiFixResponseSchema),
|
|
23
|
-
// "I have looked at the pipelines"
|
|
23
|
+
// "I have looked at the pipelines", what silences the rail badge for breakages already read. No input:
|
|
24
24
|
// the surface is read as a whole, and the daemon stamps its own clock so a skewed browser can't mark
|
|
25
25
|
// future failures as already seen.
|
|
26
26
|
seen: oc.route({ method: "POST", path: "/ci/seen" }).output(CiSeenResponseSchema),
|
|
@@ -10,18 +10,18 @@ import {
|
|
|
10
10
|
OkSchema,
|
|
11
11
|
} from "../schemas.js";
|
|
12
12
|
|
|
13
|
-
// Claude subscription OAuth
|
|
13
|
+
// Claude subscription OAuth, the sandbox owns the credential. `start` hands the browser the authorize URL +
|
|
14
14
|
// PKCE material; `exchange` stores the resulting tokens as a new account and returns it; `accounts` lists the
|
|
15
15
|
// connected accounts; `rename` renames one; `disconnect` clears the one named by id. A sandbox can hold several
|
|
16
16
|
// Claude accounts.
|
|
17
17
|
//
|
|
18
18
|
// The model catalog is NOT here: it is the one thing every provider answers identically, so it lives on the
|
|
19
19
|
// shared /providers/{provider}/models route (providers.contract.ts). What is left is what is genuinely Claude's
|
|
20
|
-
|
|
20
|
+
//, an account handshake no other provider has.
|
|
21
21
|
export const claudeContract = {
|
|
22
22
|
start: oc.route({ method: "POST", path: "/claude/oauth/start" }).output(AuthorizeChallengeSchema),
|
|
23
23
|
exchange: oc.route({ method: "POST", path: "/claude/oauth/exchange" }).input(OauthExchangeSchema).output(OauthAccountSchema),
|
|
24
|
-
// Each account carries its plan-limit reading. `force` re-measures before answering
|
|
24
|
+
// Each account carries its plan-limit reading. `force` re-measures before answering, see
|
|
25
25
|
// AccountListQuerySchema, and USAGE_WAIT_MS in claude.routes.ts for what an ordinary read waits.
|
|
26
26
|
accounts: oc.route({ method: "GET", path: "/claude/accounts" }).input(AccountListQuerySchema).output(OauthAccountListSchema),
|
|
27
27
|
rename: oc.route({ method: "POST", path: "/claude/account/rename" }).input(AccountRenameSchema).output(OauthAccountSchema),
|
|
@@ -2,7 +2,7 @@ import { oc } from "@orpc/contract";
|
|
|
2
2
|
import { DraftIdParamSchema, DraftsListSchema, DraftSummarySchema, OkSchema } from "../schemas.js";
|
|
3
3
|
|
|
4
4
|
// The sandbox's post-drafts queue (agent-proposed posts awaiting approval). The agent creates draft files
|
|
5
|
-
// directly
|
|
5
|
+
// directly, these routes are the OWNER's side: `list` is the approval inbox, `upsert` covers approve / edit /
|
|
6
6
|
// retry (all a plain re-post with a field changed, like the automations enabled toggle), `remove` is reject.
|
|
7
7
|
export const draftsContract = {
|
|
8
8
|
list: oc.route({ method: "GET", path: "/drafts" }).output(DraftsListSchema),
|
|
@@ -2,12 +2,12 @@ import { oc } from "@orpc/contract";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { CapabilityIdParamSchema, ModelsSchema } from "../schemas.js";
|
|
4
4
|
|
|
5
|
-
// An `endpoint` capability's picker catalog
|
|
5
|
+
// An `endpoint` capability's picker catalog, the models the configured server itself publishes, read from its
|
|
6
6
|
// own /v1/models. Every other provider's catalog is one fixed route because there is one of each; endpoints are
|
|
7
7
|
// user-created and unbounded, so the id rides in the path. There is no seed floor and no static list: what a
|
|
8
8
|
// model API serves is knowable only by asking it, and an empty answer is the honest report that we could not.
|
|
9
9
|
|
|
10
|
-
/* The free trial's remaining allowance, on the endpoints contract because the trial IS an endpoint
|
|
10
|
+
/* The free trial's remaining allowance, on the endpoints contract because the trial IS an endpoint, the one
|
|
11
11
|
* the daemon provisions rather than the user (agent-catalog.ts TRIAL_ENDPOINT_ID). It is a separate read from
|
|
12
12
|
* the catalog above because it answers a different question and changes on a different clock: the model list is
|
|
13
13
|
* a property of the upstream, while this is a property of the ACCOUNT and moves with every message sent.
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
WorkspaceExtensionCreateSchema,
|
|
20
20
|
} from "../schemas.js";
|
|
21
21
|
|
|
22
|
-
// Installed extensions resolved to their approved manifests
|
|
22
|
+
// Installed extensions resolved to their approved manifests, what the web extension host boots from. The
|
|
23
23
|
// bundle itself is a plain Hono route (GET /extensions/{id}/bundle): raw ESM bytes are not an oRPC payload.
|
|
24
24
|
// `settings`/`setSettings` carry the extension's own contributes.settings values; keys the manifest never
|
|
25
25
|
// declared are refused, the same honesty rule the host applies to runtime view/command registrations.
|
|
@@ -27,28 +27,28 @@ export const extensionsContract = {
|
|
|
27
27
|
list: oc.route({ method: "GET", path: "/extensions" }).output(ExtensionsListSchema),
|
|
28
28
|
// Author a new extension in place: writes a running one into .intentic/config/workspace-extensions/<name>/. The only
|
|
29
29
|
// creating route here, and it exists because that directory is otherwise reachable exclusively through an
|
|
30
|
-
// agent's file tools
|
|
30
|
+
// agent's file tools, which is a fine way to CHANGE an extension and a poor way to meet the idea of one.
|
|
31
31
|
create: oc.route({ method: "POST", path: "/extensions/workspace" }).input(WorkspaceExtensionCreateSchema).output(WorkspaceExtensionCreatedSchema),
|
|
32
32
|
settings: oc.route({ method: "GET", path: "/extensions/{id}/settings" }).input(CapabilityIdParamSchema).output(ExtensionSettingsSchema),
|
|
33
33
|
setSettings: oc.route({ method: "POST", path: "/extensions/{id}/settings" }).input(ExtensionSettingsInputSchema).output(OkSchema),
|
|
34
34
|
// The owner's on/off switch. Disabling stops the extension's declared processes here and now; its agent
|
|
35
35
|
// plugin dir and PATH entry are rebuilt per turn, and an `environment` fragment only at the next image
|
|
36
|
-
// rebuild
|
|
36
|
+
// rebuild, the Extensions tab states which of those an extension actually has.
|
|
37
37
|
setEnabled: oc.route({ method: "POST", path: "/extensions/{id}/enabled" }).input(ExtensionEnabledInputSchema).output(OkSchema),
|
|
38
38
|
// The host reporting which declared routes it just let through. Written by the browser because that is where
|
|
39
|
-
// the permission gate runs (apiImpl.ts)
|
|
39
|
+
// the permission gate runs (apiImpl.ts), the daemon sees an extension's traffic as ordinary authenticated
|
|
40
40
|
// requests and cannot tell which extension, or which declared entry, any of it belongs to.
|
|
41
41
|
recordUsage: oc.route({ method: "POST", path: "/extensions/{id}/usage" }).input(ExtensionUsageInputSchema).output(OkSchema),
|
|
42
|
-
/* Whether this extension is fit for somebody else to run
|
|
42
|
+
/* Whether this extension is fit for somebody else to run, the checks answerable from its files alone. Read
|
|
43
43
|
* on demand rather than carried on the list: it reads the bundle off disk per extension, and it is looked at
|
|
44
44
|
* when an author is about to publish, not every time the tab renders. */
|
|
45
45
|
readiness: oc.route({ method: "GET", path: "/extensions/{id}/readiness" }).input(CapabilityIdParamSchema).output(ExtensionReadinessSchema),
|
|
46
46
|
/* The update lifecycle for a GIT-INSTALLED extension. The list carries what the periodic registry check
|
|
47
47
|
* found (update/advisory/health per row); these are the verbs around it. `checkUpdates` runs the comparison
|
|
48
48
|
* now (the tab's "check now"). `updatePreview` stages the offered sha and answers with the version story +
|
|
49
|
-
* the mechanical powers diff
|
|
49
|
+
* the mechanical powers diff, the read BEFORE the click, costing one throwaway clone like a registry
|
|
50
50
|
* browse. `applyUpdate` is the transaction: re-clone, validate, quiesce, swap (keeping the outgoing
|
|
51
|
-
* checkout one back), restart, health-watch
|
|
51
|
+
* checkout one back), restart, health-watch, on the EXISTING capability config, so a private-source token
|
|
52
52
|
* survives what a bare re-add would lose. `revert` swaps the kept-previous checkout back. Update and revert
|
|
53
53
|
* change the code that runs, so like install they are owner-only. */
|
|
54
54
|
checkUpdates: oc.route({ method: "POST", path: "/extensions/updates/check" }).output(ExtensionUpdatesCheckedSchema),
|
|
@@ -61,7 +61,7 @@ export const extensionsContract = {
|
|
|
61
61
|
.input(ExtensionUpdateActionSchema)
|
|
62
62
|
.output(ExtensionUpdateAppliedSchema),
|
|
63
63
|
revert: oc.route({ method: "POST", path: "/extensions/{id}/revert" }).input(CapabilityIdParamSchema).output(ExtensionUpdateAppliedSchema),
|
|
64
|
-
// The owner's standing answer per extension (notify / agent / auto, and the advisory opt-out)
|
|
64
|
+
// The owner's standing answer per extension (notify / agent / auto, and the advisory opt-out), see
|
|
65
65
|
// ExtensionUpdatePolicySchema for what each rung means.
|
|
66
66
|
setUpdatePolicy: oc.route({ method: "POST", path: "/extensions/{id}/update-policy" }).input(ExtensionUpdatePolicyInputSchema).output(OkSchema),
|
|
67
67
|
// Declared background processes (contributes.processes): tmux-managed through the panel machinery
|
|
@@ -54,9 +54,9 @@ export const gitContract = {
|
|
|
54
54
|
changes: oc.route({ method: "GET", path: "/git/changes" }).output(GitChangesSchema),
|
|
55
55
|
// The git-history graph over one repo's real commits: the repo list (for the tree affordance + switcher),
|
|
56
56
|
// one repo's commit log, and lazy per-commit detail (changed files, then a file's before/after AT the
|
|
57
|
-
// commit). Read-only
|
|
57
|
+
// commit). Read-only, commit/discard on the working tree stay the write path (above).
|
|
58
58
|
repos: oc.route({ method: "GET", path: "/git/repos" }).output(GitReposSchema),
|
|
59
|
-
// The same repos with the host + project their remote names
|
|
59
|
+
// The same repos with the host + project their remote names, how a caller recognises a workspace repo in a
|
|
60
60
|
// list of `owner/name` strings that came from somewhere else. Kept off `repos` (a `git remote -v` per repo).
|
|
61
61
|
remoteRepos: oc.route({ method: "GET", path: "/git/remote-repos" }).output(GitRemoteReposSchema),
|
|
62
62
|
log: oc.route({ method: "GET", path: "/git/{repo}/log" }).input(GitLogQuerySchema).output(GitLogSchema),
|
|
@@ -67,13 +67,13 @@ export const gitContract = {
|
|
|
67
67
|
// GitActionResult so a conflict/clean-apply failure is a value, not a 500. Read routes above.
|
|
68
68
|
/* The halted-operation pair. `operation` is a READ every git surface can use to explain a worktree it cannot
|
|
69
69
|
* otherwise act on; `abort` is the single way out, and it is git's own `--abort` rather than anything
|
|
70
|
-
* clever. Neither is reachable from the daemon's own verbs
|
|
70
|
+
* clever. Neither is reachable from the daemon's own verbs, those abort themselves, so this exists purely
|
|
71
71
|
* for what a terminal left behind. */
|
|
72
72
|
operation: oc.route({ method: "GET", path: "/git/{repo}/operation" }).input(RepoParamSchema).output(GitOperationStateSchema),
|
|
73
73
|
abort: oc.route({ method: "POST", path: "/git/{repo}/abort" }).input(RepoParamSchema).output(GitActionResultSchema),
|
|
74
74
|
/* Walk the current branch back to where it was before its last action, off the branch's own reflog. The
|
|
75
75
|
* complement to the Checkpoints timeline, not a duplicate of it: a checkpoint restores the working tree,
|
|
76
|
-
* this moves the ref. The read carries `previousSha`, which the write sends back as a concurrency token
|
|
76
|
+
* this moves the ref. The read carries `previousSha`, which the write sends back as a concurrency token,
|
|
77
77
|
* an undo prepared against a stale view is refused rather than landing somewhere unlooked-at. */
|
|
78
78
|
undoable: oc.route({ method: "GET", path: "/git/{repo}/undo" }).input(RepoParamSchema).output(GitUndoStateSchema),
|
|
79
79
|
undo: oc.route({ method: "POST", path: "/git/{repo}/undo" }).input(GitUndoSchema).output(GitActionResultSchema),
|
|
@@ -101,7 +101,7 @@ export const gitContract = {
|
|
|
101
101
|
status: oc.route({ method: "GET", path: "/git/{repo}/status" }).input(RepoParamSchema).output(GitStatusSchema),
|
|
102
102
|
commit: oc.route({ method: "POST", path: "/git/{repo}/commit" }).input(CommitSchema).output(CommitResultSchema),
|
|
103
103
|
discard: oc.route({ method: "POST", path: "/git/{repo}/discard" }).input(DiscardSchema).output(OkSchema),
|
|
104
|
-
// Index moves. Per-path, worktree untouched, so they need no checkpoint and can't fail destructively
|
|
104
|
+
// Index moves. Per-path, worktree untouched, so they need no checkpoint and can't fail destructively,
|
|
105
105
|
// git's own error (an unmatched pathspec) propagates.
|
|
106
106
|
stage: oc.route({ method: "POST", path: "/git/{repo}/stage" }).input(GitStageSchema).output(OkSchema),
|
|
107
107
|
unstage: oc.route({ method: "POST", path: "/git/{repo}/unstage" }).input(GitStageSchema).output(OkSchema),
|
|
@@ -113,7 +113,7 @@ export const gitContract = {
|
|
|
113
113
|
deleteBranch: oc.route({ method: "POST", path: "/git/{repo}/branches/delete" }).input(GitBranchDeleteSchema).output(OkSchema),
|
|
114
114
|
// Remote sync. All three report a GitActionResult rather than throwing: no remote, no credentials and a
|
|
115
115
|
// non-fast-forwardable pull are ORDINARY outcomes the panel renders, not 500s. `remote` is the read
|
|
116
|
-
// (ahead/behind as of the last fetch
|
|
116
|
+
// (ahead/behind as of the last fetch, hence the Fetch button) the sync bar polls.
|
|
117
117
|
remote: oc.route({ method: "GET", path: "/git/{repo}/remote" }).input(RepoParamSchema).output(GitRemoteStateSchema),
|
|
118
118
|
fetch: oc.route({ method: "POST", path: "/git/{repo}/fetch" }).input(RepoParamSchema).output(GitActionResultSchema),
|
|
119
119
|
pull: oc.route({ method: "POST", path: "/git/{repo}/pull" }).input(RepoParamSchema).output(GitActionResultSchema),
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { oc } from "@orpc/contract";
|
|
2
2
|
import { AccountIdSchema, DeviceStartSchema, OauthAccountListSchema, OkSchema } from "../schemas.js";
|
|
3
3
|
|
|
4
|
-
// xAI Grok (via OpenCode) uses subscription OAuth
|
|
4
|
+
// xAI Grok (via OpenCode) uses subscription OAuth, the sandbox owns the credential (OpenCode persists the
|
|
5
5
|
// tokens and refreshes them). `start` authorizes xAI's headless device-code method and returns the verification
|
|
6
6
|
// URL + instructions (which carry the one-time code the user enters at x.ai); OpenCode then polls to completion
|
|
7
|
-
|
|
8
|
-
// ponytail: OpenCode holds one xAI auth per data dir, so `accounts` is 0 or 1
|
|
7
|
+
//, there is no paste-back, and the UI polls `accounts` until connected. `disconnect` clears the tokens.
|
|
8
|
+
// ponytail: OpenCode holds one xAI auth per data dir, so `accounts` is 0 or 1, the list shape matches the
|
|
9
9
|
// other providers without paying for per-account OpenCode servers yet.
|
|
10
10
|
//
|
|
11
|
-
// Like Claude's, this contract is now the account handshake alone
|
|
11
|
+
// Like Claude's, this contract is now the account handshake alone, the model catalog answers on the shared
|
|
12
12
|
// /providers/{provider}/models route (providers.contract.ts).
|
|
13
13
|
export const grokContract = {
|
|
14
14
|
start: oc.route({ method: "POST", path: "/grok/oauth/start" }).output(DeviceStartSchema),
|
|
@@ -6,7 +6,7 @@ import { HostFactsSchema, HostScopesSchema, MachineFlowLineSchema, MachineSandbo
|
|
|
6
6
|
*
|
|
7
7
|
* The direction is the unusual part: the machine is the oRPC SERVER and the daemon holds the client, even though
|
|
8
8
|
* the machine is the side that dialled. A personal computer sits behind NAT with a closing lid, so it can only
|
|
9
|
-
* ever be the one that connects
|
|
9
|
+
* ever be the one that connects, but everything is asked OF it. oRPC's websocket adapter takes any socket-like
|
|
10
10
|
* object on either side, so the roles are free to be the opposite of who placed the call.
|
|
11
11
|
*
|
|
12
12
|
* No `.route()` on these: HTTP method and path are for the daemon's own REST surface, and this contract never
|
|
@@ -14,12 +14,12 @@ import { HostFactsSchema, HostScopesSchema, MachineFlowLineSchema, MachineSandbo
|
|
|
14
14
|
*
|
|
15
15
|
* `mcp` IS THE DELIBERATE HOLE in the typing, and it is worth understanding before someone "fixes" it. The agent
|
|
16
16
|
* talks to a machine in MCP, over the daemon's loopback bridge; if this contract described each tool, then the
|
|
17
|
-
* daemon would have to know every tool's schema and translate
|
|
17
|
+
* daemon would have to know every tool's schema and translate, and a machine could no longer learn a tool
|
|
18
18
|
* without a matching daemon release. Keeping one opaque procedure is what buys the machine an independent
|
|
19
19
|
* release cycle. The payload is still validated where it is understood: on the machine, against the tool's own
|
|
20
20
|
* schema, and by the agent's MCP client on the way back. */
|
|
21
21
|
export const hostContract = {
|
|
22
|
-
// What this computer is
|
|
22
|
+
// What this computer is, pulled right after the socket authenticates, and again whenever the sandbox wants
|
|
23
23
|
// it fresh. The card shows it, and the agent's skill pack is written against it.
|
|
24
24
|
describe: oc.output(HostFactsSchema),
|
|
25
25
|
// The grant, pushed down on every connect and again whenever the owner edits the card. The machine ENFORCES
|
|
@@ -28,7 +28,7 @@ export const hostContract = {
|
|
|
28
28
|
// Liveness, driven by the daemon: it doubles as the keepalive that stops an idle tunnel from reaping the
|
|
29
29
|
// connection, and as the probe whose failure means the machine is gone rather than quiet.
|
|
30
30
|
ping: oc.output(OkSchema),
|
|
31
|
-
// One MCP JSON-RPC message in, its answer out
|
|
31
|
+
// One MCP JSON-RPC message in, its answer out, forwarded verbatim in both directions. See above.
|
|
32
32
|
mcp: oc.input(z.unknown()).output(z.unknown()),
|
|
33
33
|
/* One operation on one of this machine's sandboxes, narrated as it happens.
|
|
34
34
|
*
|
|
@@ -38,7 +38,7 @@ export const hostContract = {
|
|
|
38
38
|
* container, which is minutes of silence unless the lines travel while they are produced. A stream is what
|
|
39
39
|
* the browser needs, and a stream is the one thing an MCP tool result cannot be.
|
|
40
40
|
*
|
|
41
|
-
* The scope is still checked here, on the machine, by the same functions the MCP tools call
|
|
41
|
+
* The scope is still checked here, on the machine, by the same functions the MCP tools call, this adds a
|
|
42
42
|
* way of WATCHING an operation, never a way of skipping the switch that permits it. */
|
|
43
43
|
runSandboxFlow: oc.input(MachineSandboxFlowSchema).output(eventIterator(MachineFlowLineSchema)),
|
|
44
44
|
};
|
|
@@ -7,9 +7,9 @@ import { IntenticRunSchema, OkSchema } from "../schemas.js";
|
|
|
7
7
|
export const intenticContract = {
|
|
8
8
|
run: oc.route({ method: "POST", path: "/intentic" }).input(IntenticRunSchema).output(eventIterator(IntenticLineSchema)),
|
|
9
9
|
// Launch the minutes-long apply → adopt reconcile as a one-shot tmux job (session panel-infra-apply) and
|
|
10
|
-
// return immediately
|
|
10
|
+
// return immediately, progress is followed by attaching the terminal, not by holding this request open.
|
|
11
11
|
apply: oc.route({ method: "POST", path: "/intentic/apply" }).output(OkSchema),
|
|
12
|
-
// Tail the running (or just-finished) apply's structured event stream
|
|
12
|
+
// Tail the running (or just-finished) apply's structured event stream, the same ndjson lines the tmux pane
|
|
13
13
|
// renders as text, persisted to a durable file so the UI shows per-resource progress that survives a page
|
|
14
14
|
// refresh. Replays from the run's {kind:"start"} then follows live, closing on {kind:"exit"}. GET, like
|
|
15
15
|
// /events, because it takes no input; reuses the loose IntenticLine shape so no new schema is needed.
|
|
@@ -2,7 +2,7 @@ import { oc } from "@orpc/contract";
|
|
|
2
2
|
import { LogReadQuerySchema, LogReadSchema, LogsListSchema } from "../schemas.js";
|
|
3
3
|
|
|
4
4
|
// Daemon-owned debug logs (historyRoot/logs): terminal pipe-pane captures, intentic CLI run logs, daemon.log.
|
|
5
|
-
// Read-only by design
|
|
5
|
+
// Read-only by design, the files are written by the daemon/tmux only, so the record stays trustworthy.
|
|
6
6
|
export const logsContract = {
|
|
7
7
|
list: oc.route({ method: "GET", path: "/logs" }).output(LogsListSchema),
|
|
8
8
|
read: oc.route({ method: "GET", path: "/logs/file" }).input(LogReadQuerySchema).output(LogReadSchema),
|
|
@@ -11,29 +11,29 @@ import {
|
|
|
11
11
|
OkSchema,
|
|
12
12
|
} from "../schemas.js";
|
|
13
13
|
|
|
14
|
-
/* The loop routes
|
|
14
|
+
/* The loop routes, "run this conversation again until the goal is met".
|
|
15
15
|
*
|
|
16
16
|
* TWO HALVES THAT LOOK LIKE ONE FEATURE AND ARE NOT, which is why they share a file and share nothing else.
|
|
17
17
|
*
|
|
18
18
|
* A RUNNING LOOP has no editor. It is started against a conversation, it converges or it gives up, and then it
|
|
19
|
-
* is history: `start`, `stop`, and a `list` of what has run. No upsert, no enabled toggle, no id of its own
|
|
19
|
+
* is history: `start`, `stop`, and a `list` of what has run. No upsert, no enabled toggle, no id of its own,
|
|
20
20
|
* the conversation IS the id.
|
|
21
21
|
*
|
|
22
22
|
* A SAVED LOOP is a manifest entry like a workflow, and gets the manifest treatment: list, save, remove. It is
|
|
23
23
|
* the loop's MACHINERY without its goal (LoopDesignSchema says why at length), so it is authored once and
|
|
24
|
-
* pointed at a different job every time. It has no `run` route of its own on purpose
|
|
24
|
+
* pointed at a different job every time. It has no `run` route of its own on purpose, running one is `start`
|
|
25
25
|
* with the design's fields and the composer's sentence, so there is exactly one way a loop begins and exactly
|
|
26
26
|
* one place that can refuse it.
|
|
27
27
|
*
|
|
28
28
|
* `start` acks immediately with the loop as recorded and runs detached, the same contract POST /agent keeps: the
|
|
29
29
|
* first iteration alone can take minutes, and every surface that would render progress is already attached to
|
|
30
|
-
* the conversation. What comes back is the record, not an outcome
|
|
30
|
+
* the conversation. What comes back is the record, not an outcome, the outcome arrives on the fleet card.
|
|
31
31
|
*/
|
|
32
32
|
export const loopsContract = {
|
|
33
|
-
// Every loop this workspace has run, newest first
|
|
33
|
+
// Every loop this workspace has run, newest first, the record is kept after the loop ends, because "why did
|
|
34
34
|
// it stop at iteration 4" is the question a loop is read for, and the answer is its iteration history.
|
|
35
35
|
list: oc.route({ method: "GET", path: "/loops" }).output(LoopsListSchema),
|
|
36
|
-
/* Start looping a conversation. Rejects when that conversation is already looping
|
|
36
|
+
/* Start looping a conversation. Rejects when that conversation is already looping, a second loop on one
|
|
37
37
|
* agent would have two pumps racing the same worktree and the same turn mutex, and the loser would spend a
|
|
38
38
|
* turn to discover it.
|
|
39
39
|
*
|
|
@@ -49,10 +49,10 @@ export const loopsContract = {
|
|
|
49
49
|
stop: oc.route({ method: "POST", path: "/loops/{conversationId}/stop" }).input(LoopIdParamSchema).output(OkSchema),
|
|
50
50
|
|
|
51
51
|
// Every saved loop. A literal path segment under /loops rather than a surface of its own, because a saved
|
|
52
|
-
// loop is not a different feature
|
|
52
|
+
// loop is not a different feature, it is the same loop with its goal left blank until somebody types one.
|
|
53
53
|
designs: oc.route({ method: "GET", path: "/loops/designs" }).output(LoopDesignsListSchema),
|
|
54
54
|
/* Create or replace a saved loop, with the operation explicit so a name collision cannot turn a create into
|
|
55
|
-
* a replacement. Refuses a design that could never finish
|
|
55
|
+
* a replacement. Refuses a design that could never finish, nothing to produce and nothing to check, with
|
|
56
56
|
* the same sentence `start` refuses an ad-hoc loop for, because it is the same mistake made earlier and
|
|
57
57
|
* catching it at save time is the whole advantage of saving. */
|
|
58
58
|
saveDesign: oc.route({ method: "POST", path: "/loops/designs" }).input(LoopDesignSaveSchema).output(LoopDesignSchema),
|
|
@@ -13,12 +13,12 @@ import {
|
|
|
13
13
|
|
|
14
14
|
/* The sandbox's named personas (PersonaSchema): which connected accounts each one speaks for, what a session
|
|
15
15
|
* wearing it may do, where it works, and what it is told. The card half is a plain three-verb CRUD with no apply
|
|
16
|
-
* step, because a card provisions NOTHING
|
|
16
|
+
* step, because a card provisions NOTHING, unlike a capability, saving one connects nothing, installs nothing
|
|
17
17
|
* and spends nothing. It records a decision about accounts that already exist.
|
|
18
18
|
*
|
|
19
19
|
* The file behind it is committed workspace config (personas/personas-store.ts), so these routes are one of
|
|
20
20
|
* two equally supported ways to edit them: this, and opening the file in the editor like any other project
|
|
21
|
-
* config. Neither is the "real" one
|
|
21
|
+
* config. Neither is the "real" one, which is why `save` is a whole-card upsert rather than a field patch, so
|
|
22
22
|
* a round trip through the UI leaves a diff a reviewer would recognise.
|
|
23
23
|
*
|
|
24
24
|
* THE KIT ROUTES BELOW EDIT FILES, not the card, and they are here rather than on the skills contract because
|
|
@@ -30,14 +30,14 @@ import {
|
|
|
30
30
|
* a save that shipped the whole folder would make an edit to one skill capable of deleting another. */
|
|
31
31
|
export const personasContract = {
|
|
32
32
|
list: oc.route({ method: "GET", path: "/personas" }).output(PersonasListSchema),
|
|
33
|
-
// Upsert by id
|
|
33
|
+
// Upsert by id, re-saving the same id edits that card.
|
|
34
34
|
save: oc.route({ method: "POST", path: "/personas" }).input(PersonaSchema).output(OkSchema),
|
|
35
35
|
/* Removing a card takes away a persona, never an account: the login it named stays connected and reachable from
|
|
36
|
-
* every other surface. What it CAN do is orphan a reference
|
|
37
|
-
* card that no longer exists
|
|
36
|
+
* every other surface. What it CAN do is orphan a reference, an automation pinned to this id now names a
|
|
37
|
+
* card that no longer exists, and the resolver reads that as "no accounts at all" rather than "all of
|
|
38
38
|
* them", so the automation goes quiet instead of posting as somebody unintended. */
|
|
39
39
|
/* Removing a card takes away a persona, never an account: the login it named stays connected and reachable from
|
|
40
|
-
* every other surface. It DOES take the card's kit with it
|
|
40
|
+
* every other surface. It DOES take the card's kit with it, a folder no card can reach is a folder no list
|
|
41
41
|
* shows, and leaving the owner's prompt and skills orphaned on disk is worse than deleting what they just
|
|
42
42
|
* asked to delete. */
|
|
43
43
|
remove: oc.route({ method: "DELETE", path: "/personas/{id}" }).input(PersonaIdParamSchema).output(OkSchema),
|
|
@@ -46,10 +46,10 @@ export const personasContract = {
|
|
|
46
46
|
|
|
47
47
|
kit: oc.route({ method: "GET", path: "/personas/{id}/kit" }).input(PersonaIdParamSchema).output(PersonaKitSchema),
|
|
48
48
|
// An empty prompt DELETES the file rather than storing a blank one, so "custom with nothing written" is one
|
|
49
|
-
// state instead of two
|
|
49
|
+
// state instead of two, the resolver falls back to the sandbox's prompt for it (personas.ts personaPrompt).
|
|
50
50
|
savePrompt: oc.route({ method: "POST", path: "/personas/{id}/prompt" }).input(PersonaPromptSchema).output(OkSchema),
|
|
51
51
|
readSkill: oc.route({ method: "GET", path: "/personas/{id}/skills/read" }).input(PersonaSkillNameSchema).output(PersonaSkillBodySchema),
|
|
52
|
-
// Upsert by name, like the sandbox's own skills
|
|
52
|
+
// Upsert by name, like the sandbox's own skills, and with no enabled list to write, because a kit skill is
|
|
53
53
|
// on exactly when its persona is worn. That is what "specific to that persona" has to mean.
|
|
54
54
|
saveSkill: oc.route({ method: "POST", path: "/personas/{id}/skills" }).input(PersonaSkillSchema).output(OkSchema),
|
|
55
55
|
removeSkill: oc.route({ method: "POST", path: "/personas/{id}/skills/remove" }).input(PersonaSkillNameSchema).output(OkSchema),
|
|
@@ -2,7 +2,7 @@ import { oc } from "@orpc/contract";
|
|
|
2
2
|
import { OkSchema, PortForwardResultSchema, PortParamSchema, PortsListSchema } from "../schemas.js";
|
|
3
3
|
|
|
4
4
|
// Listening TCP ports in the sandbox + explicit forwarding through the preview proxy (see the ports section in
|
|
5
|
-
// schemas.ts). `forward` is idempotent
|
|
5
|
+
// schemas.ts). `forward` is idempotent, re-forwarding a port returns its existing slot's URL; `unforward`
|
|
6
6
|
// frees the slot immediately (the hostname keeps resolving, the proxy just stops mapping it).
|
|
7
7
|
export const portsContract = {
|
|
8
8
|
list: oc.route({ method: "GET", path: "/ports" }).output(PortsListSchema),
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { oc } from "@orpc/contract";
|
|
2
2
|
import { OkSchema, PrepushRunSchema } from "../schemas.js";
|
|
3
3
|
|
|
4
|
-
// The pre-push check
|
|
4
|
+
// The pre-push check, the command the workspace runs when the user pushes, before anything leaves the machine
|
|
5
5
|
// (see PrepushRunSchema for where this sits and why). Three verbs about ONE run: the check answers about the
|
|
6
6
|
// main working tree, of which there is exactly one, so nothing here is addressed by id.
|
|
7
7
|
//
|
|
8
|
-
// `run` starts the check and returns immediately
|
|
8
|
+
// `run` starts the check and returns immediately, a suite takes minutes, and an oRPC call held open for one
|
|
9
9
|
// would die on the first proxy timeout with the work still going. The dialog that started it polls `state` for
|
|
10
10
|
// the verdict, and opens the terminals panel on the `session` the first answer names: the suite runs in a real
|
|
11
11
|
// tmux window, so watching it is the terminal's job. `cancel` kills that window; the run settles as `cancelled`
|
|
@@ -3,21 +3,21 @@ import { ModelsSchema, NativeProviderParamSchema } from "../schemas.js";
|
|
|
3
3
|
|
|
4
4
|
// EVERY NATIVE PROVIDER'S PICKER CATALOG, ON ONE ROUTE.
|
|
5
5
|
//
|
|
6
|
-
// There were five of these — /claude/models, /codex/models, /grok/models, /kimi/models, /gemini/models
|
|
6
|
+
// There were five of these — /claude/models, /codex/models, /grok/models, /kimi/models, /gemini/models, one per
|
|
7
7
|
// provider, each a contract entry, a route factory and a service field wired separately. They differed in
|
|
8
8
|
// nothing: same method, same output schema, same "the daemon's catalog for this provider, never empty". Three of
|
|
9
9
|
// the five route files were the same fifteen lines with a name swapped, and the daemon paid for that shape twice
|
|
10
10
|
// more in the branch chains that had to fan back out over the providers to ask them all the same question.
|
|
11
11
|
//
|
|
12
|
-
// So the provider is a PARAMETER, not five routes. Adding one is a row in the daemon's catalog registry
|
|
13
|
-
// same discipline the adapter registry already applies to serving a turn
|
|
12
|
+
// So the provider is a PARAMETER, not five routes. Adding one is a row in the daemon's catalog registry, the
|
|
13
|
+
// same discipline the adapter registry already applies to serving a turn, rather than a vertical slice through
|
|
14
14
|
// the contract, the router, the service container and every test double.
|
|
15
15
|
//
|
|
16
16
|
// Endpoints keep their own route (endpoints.contract.ts) and should: they are user-created and unbounded, their
|
|
17
17
|
// id names a capability that may not exist, and a missing one is a NOT_FOUND rather than an empty catalog. That
|
|
18
18
|
// is a different question with a different failure, not a sixth provider.
|
|
19
19
|
export const providersContract = {
|
|
20
|
-
// One provider's models (+ its default id), never empty
|
|
20
|
+
// One provider's models (+ its default id), never empty, live discovery with a persisted/seed floor behind
|
|
21
21
|
// it. Order is the provider's own preference order and is not re-ranked here; see ModelsSchema.
|
|
22
22
|
models: oc.route({ method: "GET", path: "/providers/{provider}/models" }).input(NativeProviderParamSchema).output(ModelsSchema),
|
|
23
23
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { oc } from "@orpc/contract";
|
|
2
2
|
import { OkSchema, PublicListSchema, PublishResultSchema, PublishSchema, UnpublishSchema } from "../schemas.js";
|
|
3
3
|
|
|
4
|
-
// The workspace outbox
|
|
4
|
+
// The workspace outbox, what `public/` currently holds and its address (see the public section in schemas.ts).
|
|
5
5
|
// `publish` copies a workspace file or directory in, creating the outbox if this is the first one; `unpublish`
|
|
6
6
|
// withdraws one and removes the outbox behind the last, so the directory's presence always means exactly "there
|
|
7
7
|
// is something published". There is no route to READ a published file: that is the whole point of the
|
|
@@ -5,10 +5,10 @@ import { OkSchema, PushChannelIdSchema, PushChannelSchema, PushConfigQuerySchema
|
|
|
5
5
|
// push/push-store.ts for why the key lives on the history volume), and sends on the three moments where the
|
|
6
6
|
// operator's attention is genuinely wanted: a turn finished, the agent is blocked on an answer, and an
|
|
7
7
|
// automation is waiting for approval. A channel is either a browser's web-push subscription or a native
|
|
8
|
-
// install reached through the platform's push relay
|
|
8
|
+
// install reached through the platform's push relay, see PushChannelSchema for the split and why.
|
|
9
9
|
//
|
|
10
10
|
// `test` exists because a notification pipeline has four independent failure points the user cannot inspect
|
|
11
|
-
// (device permission, service-worker or shell registration, the daemon's key, the push service itself)
|
|
11
|
+
// (device permission, service-worker or shell registration, the daemon's key, the push service itself), a
|
|
12
12
|
// button that proves the whole chain end-to-end is worth more than any amount of status rendering.
|
|
13
13
|
export const pushContract = {
|
|
14
14
|
config: oc.route({ method: "GET", path: "/push/config" }).input(PushConfigQuerySchema).output(PushConfigSchema),
|
|
@@ -2,9 +2,9 @@ import { oc } from "@orpc/contract";
|
|
|
2
2
|
import { OkSchema, SecretInventorySchema, SecretKeyParamSchema, SecretKeysSchema, SecretRevealSchema, SecretSetSchema } from "../schemas.js";
|
|
3
3
|
|
|
4
4
|
// User-supplied env-var secrets, written to the sandbox's gitignored desired-state/.env (which
|
|
5
|
-
// `apply` reloads each run
|
|
5
|
+
// `apply` reloads each run, no restart). `set` upserts one KEY=value, `remove` deletes it; `list` returns the
|
|
6
6
|
// keys present. `inventory` aggregates every secret store into one view (keys + status + provenance, never
|
|
7
|
-
// values). `reveal` is the single value-returning route
|
|
7
|
+
// values). `reveal` is the single value-returning route, owner-only, POST so the key never sits in a URL.
|
|
8
8
|
// set/remove/list/reveal refuse until DevOps has scaffolded the desired-state repo; inventory always answers.
|
|
9
9
|
export const secretsContract = {
|
|
10
10
|
set: oc.route({ method: "POST", path: "/secrets" }).input(SecretSetSchema).output(OkSchema),
|
|
@@ -3,12 +3,12 @@ import { z } from "zod";
|
|
|
3
3
|
import { SessionTranscriptSchema } from "../events.js";
|
|
4
4
|
import { SessionIdParamSchema, SessionsListSchema } from "../schemas.js";
|
|
5
5
|
|
|
6
|
-
// Past conversations in this workspace (the SDK-native session store, keyed on the working dir
|
|
6
|
+
// Past conversations in this workspace (the SDK-native session store, keyed on the working dir, which for a
|
|
7
7
|
// repo covers its linked worktrees too, so an isolated conversation's transcript is reachable from the
|
|
8
8
|
// workspace root). `list` returns summaries for the history menu (filtered by `query` when the search box is
|
|
9
9
|
// used); `get` restores one transcript for display.
|
|
10
10
|
//
|
|
11
|
-
// `caseSensitive` is the filter's Aa switch, on the same terms as the fleet search's
|
|
11
|
+
// `caseSensitive` is the filter's Aa switch, on the same terms as the fleet search's, off means the letters do
|
|
12
12
|
// not matter, and the two routes answer one query together (the board lists these rows under its own cards), so
|
|
13
13
|
// a switch either of them ignored would show as one field returning two different match sets.
|
|
14
14
|
export const sessionsContract = {
|