@intentic/sandbox-contract 1.176.3 → 1.209.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 +65 -0
- package/dist/agent-catalog.d.ts +15 -2
- package/dist/agent-catalog.d.ts.map +1 -1
- package/dist/agent-catalog.js +50 -1
- package/dist/agent-catalog.js.map +1 -1
- package/dist/agent-run-model.d.ts +3 -0
- package/dist/agent-run-model.d.ts.map +1 -0
- package/dist/agent-run-model.js +10 -0
- package/dist/agent-run-model.js.map +1 -0
- package/dist/capability-env.d.ts +2 -0
- package/dist/capability-env.d.ts.map +1 -0
- package/dist/capability-env.js +2 -0
- package/dist/capability-env.js.map +1 -0
- package/dist/chores/chores.d.ts +9 -0
- package/dist/chores/chores.d.ts.map +1 -1
- package/dist/chores/chores.js +257 -18
- package/dist/chores/chores.js.map +1 -1
- package/dist/chores/digest.js +0 -0
- package/dist/chores/digest.js.map +1 -1
- package/dist/chores/extension-update.d.ts +9 -0
- package/dist/chores/extension-update.d.ts.map +1 -0
- package/dist/chores/extension-update.js +15 -0
- package/dist/chores/extension-update.js.map +1 -0
- package/dist/chores/fix-deps.d.ts +10 -0
- package/dist/chores/fix-deps.d.ts.map +1 -0
- package/dist/chores/fix-deps.js +18 -0
- package/dist/chores/fix-deps.js.map +1 -0
- package/dist/chores/index.d.ts +7 -2
- package/dist/chores/index.d.ts.map +1 -1
- package/dist/chores/index.js +4 -1
- package/dist/chores/index.js.map +1 -1
- package/dist/chores/probes.d.ts.map +1 -1
- package/dist/chores/probes.js +127 -3
- package/dist/chores/probes.js.map +1 -1
- package/dist/chores/stack.d.ts +27 -0
- package/dist/chores/stack.d.ts.map +1 -0
- package/dist/chores/stack.js +121 -0
- package/dist/chores/stack.js.map +1 -0
- package/dist/chores/verdict.d.ts +2 -1
- package/dist/chores/verdict.d.ts.map +1 -1
- package/dist/chores/verdict.js +53 -6
- package/dist/chores/verdict.js.map +1 -1
- package/dist/contract-lock.d.ts +3 -0
- package/dist/contract-lock.d.ts.map +1 -0
- package/dist/contract-lock.js +35 -0
- package/dist/contract-lock.js.map +1 -0
- package/dist/contracts/activity.contract.d.ts +10 -0
- package/dist/contracts/activity.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.d.ts +230 -20
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.js +2 -1
- package/dist/contracts/agent.contract.js.map +1 -1
- package/dist/contracts/agents.contract.d.ts +847 -1
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.js +3 -1
- package/dist/contracts/agents.contract.js.map +1 -1
- package/dist/contracts/automations.contract.d.ts +89 -0
- package/dist/contracts/automations.contract.d.ts.map +1 -1
- package/dist/contracts/automations.contract.js +3 -1
- package/dist/contracts/automations.contract.js.map +1 -1
- package/dist/contracts/capabilities.contract.d.ts +108 -32
- package/dist/contracts/capabilities.contract.d.ts.map +1 -1
- package/dist/contracts/capabilities.contract.js +5 -1
- package/dist/contracts/capabilities.contract.js.map +1 -1
- package/dist/contracts/chores.contract.d.ts +43 -0
- package/dist/contracts/chores.contract.d.ts.map +1 -1
- package/dist/contracts/ci.contract.d.ts +5 -0
- package/dist/contracts/ci.contract.d.ts.map +1 -1
- package/dist/contracts/ci.contract.js +2 -2
- package/dist/contracts/ci.contract.js.map +1 -1
- package/dist/contracts/claude.contract.d.ts +3 -14
- package/dist/contracts/claude.contract.d.ts.map +1 -1
- package/dist/contracts/claude.contract.js +2 -3
- package/dist/contracts/claude.contract.js.map +1 -1
- package/dist/contracts/drafts.contract.d.ts +4 -0
- package/dist/contracts/drafts.contract.d.ts.map +1 -1
- package/dist/contracts/endpoints.contract.d.ts +28 -12
- package/dist/contracts/endpoints.contract.d.ts.map +1 -1
- package/dist/contracts/endpoints.contract.js +9 -0
- package/dist/contracts/endpoints.contract.js.map +1 -1
- package/dist/contracts/extensions.contract.d.ts +399 -69
- package/dist/contracts/extensions.contract.d.ts.map +1 -1
- package/dist/contracts/extensions.contract.js +15 -1
- package/dist/contracts/extensions.contract.js.map +1 -1
- package/dist/contracts/git.contract.d.ts +209 -11
- package/dist/contracts/git.contract.d.ts.map +1 -1
- package/dist/contracts/git.contract.js +12 -2
- package/dist/contracts/git.contract.js.map +1 -1
- package/dist/contracts/grok.contract.d.ts +0 -13
- package/dist/contracts/grok.contract.d.ts.map +1 -1
- package/dist/contracts/grok.contract.js +1 -2
- package/dist/contracts/grok.contract.js.map +1 -1
- package/dist/contracts/host.contract.d.ts +39 -0
- package/dist/contracts/host.contract.d.ts.map +1 -1
- package/dist/contracts/host.contract.js +3 -2
- package/dist/contracts/host.contract.js.map +1 -1
- package/dist/contracts/loops.contract.d.ts +343 -0
- package/dist/contracts/loops.contract.d.ts.map +1 -0
- package/dist/contracts/loops.contract.js +11 -0
- package/dist/contracts/loops.contract.js.map +1 -0
- package/dist/contracts/panels.contract.d.ts +6 -0
- package/dist/contracts/panels.contract.d.ts.map +1 -1
- package/dist/contracts/personas.contract.d.ts +112 -0
- package/dist/contracts/personas.contract.d.ts.map +1 -0
- package/dist/contracts/personas.contract.js +13 -0
- package/dist/contracts/personas.contract.js.map +1 -0
- package/dist/contracts/ports.contract.d.ts +1 -0
- package/dist/contracts/ports.contract.d.ts.map +1 -1
- package/dist/contracts/prepush.contract.d.ts +1 -0
- package/dist/contracts/prepush.contract.d.ts.map +1 -1
- package/dist/contracts/prepush.contract.js.map +1 -1
- package/dist/contracts/{codex.contract.d.ts → providers.contract.d.ts} +11 -3
- package/dist/contracts/providers.contract.d.ts.map +1 -0
- package/dist/contracts/providers.contract.js +6 -0
- package/dist/contracts/providers.contract.js.map +1 -0
- package/dist/contracts/public.contract.d.ts +24 -0
- package/dist/contracts/public.contract.d.ts.map +1 -0
- package/dist/contracts/public.contract.js +8 -0
- package/dist/contracts/public.contract.js.map +1 -0
- package/dist/contracts/push.contract.d.ts +3 -3
- package/dist/contracts/secrets.contract.d.ts +9 -0
- package/dist/contracts/secrets.contract.d.ts.map +1 -1
- package/dist/contracts/sessions.contract.d.ts +16 -1
- package/dist/contracts/sessions.contract.d.ts.map +1 -1
- package/dist/contracts/sessions.contract.js +1 -1
- package/dist/contracts/sessions.contract.js.map +1 -1
- package/dist/contracts/settings.contract.d.ts +352 -58
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.js +2 -1
- package/dist/contracts/settings.contract.js.map +1 -1
- package/dist/contracts/share.contract.d.ts +55 -0
- package/dist/contracts/share.contract.d.ts.map +1 -0
- package/dist/contracts/share.contract.js +9 -0
- package/dist/contracts/share.contract.js.map +1 -0
- package/dist/contracts/skills.contract.d.ts +41 -0
- package/dist/contracts/skills.contract.d.ts.map +1 -0
- package/dist/contracts/skills.contract.js +9 -0
- package/dist/contracts/skills.contract.js.map +1 -0
- package/dist/contracts/system.contract.d.ts +204 -8
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.js +10 -2
- package/dist/contracts/system.contract.js.map +1 -1
- package/dist/contracts/workflows.contract.d.ts +575 -0
- package/dist/contracts/workflows.contract.d.ts.map +1 -0
- package/dist/contracts/workflows.contract.js +13 -0
- package/dist/contracts/workflows.contract.js.map +1 -0
- package/dist/contracts/workspace.contract.d.ts +18 -4
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/contracts/workspace.contract.js +6 -3
- package/dist/contracts/workspace.contract.js.map +1 -1
- package/dist/conversation-ids.d.ts +2 -0
- package/dist/conversation-ids.d.ts.map +1 -0
- package/dist/conversation-ids.js +113 -0
- package/dist/conversation-ids.js.map +1 -0
- package/dist/events.d.ts +523 -17
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +106 -13
- package/dist/events.js.map +1 -1
- package/dist/history-state.d.ts.map +1 -1
- package/dist/history-state.js +10 -0
- package/dist/history-state.js.map +1 -1
- package/dist/hostnames.d.ts +4 -0
- package/dist/hostnames.d.ts.map +1 -1
- package/dist/hostnames.js +7 -2
- package/dist/hostnames.js.map +1 -1
- package/dist/index.d.ts +3791 -478
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +39 -17
- package/dist/index.js.map +1 -1
- package/dist/listener-protocol.d.ts +77 -0
- package/dist/listener-protocol.d.ts.map +1 -0
- package/dist/listener-protocol.js +33 -0
- package/dist/listener-protocol.js.map +1 -0
- package/dist/model-order.d.ts +1 -0
- package/dist/model-order.d.ts.map +1 -1
- package/dist/model-order.js +16 -0
- package/dist/model-order.js.map +1 -1
- package/dist/output-fields.d.ts +30 -0
- package/dist/output-fields.d.ts.map +1 -0
- package/dist/output-fields.js +57 -0
- package/dist/output-fields.js.map +1 -0
- package/dist/path-refs.d.ts.map +1 -1
- package/dist/path-refs.js.map +1 -1
- package/dist/publish-drafts.d.ts +7 -0
- package/dist/publish-drafts.d.ts.map +1 -0
- package/dist/publish-drafts.js +20 -0
- package/dist/publish-drafts.js.map +1 -0
- package/dist/quick-model.d.ts +2 -1
- package/dist/quick-model.d.ts.map +1 -1
- package/dist/quick-model.js +18 -14
- package/dist/quick-model.js.map +1 -1
- package/dist/routes.d.ts +3 -0
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +63 -0
- package/dist/routes.js.map +1 -1
- package/dist/runtime-state.d.ts +33 -0
- package/dist/runtime-state.d.ts.map +1 -0
- package/dist/runtime-state.js +14 -0
- package/dist/runtime-state.js.map +1 -0
- package/dist/schemas.d.ts +4886 -851
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +993 -192
- package/dist/schemas.js.map +1 -1
- package/dist/search-globs.d.ts +6 -0
- package/dist/search-globs.d.ts.map +1 -0
- package/dist/search-globs.js +39 -0
- package/dist/search-globs.js.map +1 -0
- package/dist/share-paths.d.ts +8 -0
- package/dist/share-paths.d.ts.map +1 -0
- package/dist/share-paths.js +21 -0
- package/dist/share-paths.js.map +1 -0
- package/dist/title.d.ts.map +1 -1
- package/dist/title.js +1 -1
- package/dist/title.js.map +1 -1
- package/dist/tunnel-ids.d.ts +1 -0
- package/dist/tunnel-ids.d.ts.map +1 -1
- package/dist/tunnel-ids.js +1 -0
- package/dist/tunnel-ids.js.map +1 -1
- package/dist/versions.d.ts +4 -0
- package/dist/versions.d.ts.map +1 -0
- package/dist/versions.js +15 -0
- package/dist/versions.js.map +1 -0
- package/dist/workflow-faults.d.ts +4 -0
- package/dist/workflow-faults.d.ts.map +1 -0
- package/dist/workflow-faults.js +115 -0
- package/dist/workflow-faults.js.map +1 -0
- package/dist/workspace-state.d.ts +280 -1
- package/dist/workspace-state.d.ts.map +1 -1
- package/dist/workspace-state.js +163 -21
- package/dist/workspace-state.js.map +1 -1
- package/package.json +30 -6
- package/src/agent-catalog.test.ts +147 -7
- package/src/agent-catalog.ts +233 -21
- package/src/agent-run-model.test.ts +60 -0
- package/src/agent-run-model.ts +51 -0
- package/src/capability-env.ts +10 -0
- package/src/capability-ledger.test.ts +110 -0
- package/src/chores/chores.test.ts +32 -0
- package/src/chores/chores.ts +456 -32
- package/src/chores/digest.ts +0 -0
- package/src/chores/extension-update.ts +37 -0
- package/src/chores/fix-deps.ts +40 -0
- package/src/chores/index.ts +7 -2
- package/src/chores/probes.test.ts +155 -3
- package/src/chores/probes.ts +200 -4
- package/src/chores/stack.test.ts +126 -0
- package/src/chores/stack.ts +244 -0
- package/src/chores/verdict.test.ts +357 -19
- package/src/chores/verdict.ts +95 -21
- package/src/contract-lock.test.ts +17 -0
- package/src/contract-lock.ts +66 -0
- package/src/contracts/agent.contract.ts +6 -0
- package/src/contracts/agents.contract.ts +12 -0
- package/src/contracts/automations.contract.ts +17 -1
- package/src/contracts/capabilities.contract.ts +22 -0
- package/src/contracts/ci.contract.ts +10 -2
- package/src/contracts/claude.contract.ts +8 -4
- package/src/contracts/endpoints.contract.ts +21 -0
- package/src/contracts/extensions.contract.ts +42 -0
- package/src/contracts/git.contract.ts +33 -6
- package/src/contracts/grok.contract.ts +4 -3
- package/src/contracts/host.contract.ts +13 -2
- package/src/contracts/loops.contract.ts +62 -0
- package/src/contracts/personas.contract.ts +56 -0
- package/src/contracts/prepush.contract.ts +4 -3
- package/src/contracts/providers.contract.ts +23 -0
- package/src/contracts/public.contract.ts +14 -0
- package/src/contracts/sessions.contract.ts +5 -1
- package/src/contracts/settings.contract.ts +5 -0
- package/src/contracts/share.contract.ts +20 -0
- package/src/contracts/skills.contract.ts +24 -0
- package/src/contracts/system.contract.ts +27 -3
- package/src/contracts/workflows.contract.ts +78 -0
- package/src/contracts/workspace.contract.ts +10 -2
- package/src/conversation-ids.test.ts +25 -0
- package/src/conversation-ids.ts +160 -0
- package/src/events.test.ts +20 -1
- package/src/events.ts +404 -43
- package/src/history-state.ts +30 -1
- package/src/hostnames.ts +20 -6
- package/src/index.ts +58 -17
- package/src/listener-protocol.ts +96 -0
- package/src/model-order.test.ts +32 -1
- package/src/model-order.ts +56 -3
- package/src/output-fields.ts +111 -0
- package/src/path-refs.test.ts +4 -4
- package/src/path-refs.ts +5 -5
- package/src/publish-drafts.ts +59 -0
- package/src/quick-model.test.ts +70 -27
- package/src/quick-model.ts +76 -40
- package/src/routes.test.ts +91 -3
- package/src/routes.ts +135 -0
- package/src/runtime-state.test.ts +45 -0
- package/src/runtime-state.ts +124 -0
- package/src/schemas.test.ts +16 -29
- package/src/schemas.ts +3475 -552
- package/src/search-globs.test.ts +46 -0
- package/src/search-globs.ts +76 -0
- package/src/share-paths.test.ts +48 -0
- package/src/share-paths.ts +68 -0
- package/src/state-portability.ts +8 -8
- package/src/title.test.ts +20 -1
- package/src/title.ts +12 -3
- package/src/tunnel-ids.ts +15 -1
- package/src/versions.test.ts +39 -0
- package/src/versions.ts +48 -0
- package/src/workflow-faults.test.ts +127 -0
- package/src/workflow-faults.ts +188 -0
- package/src/workspace-state.test.ts +169 -15
- package/src/workspace-state.ts +427 -35
- package/dist/contracts/codex.contract.d.ts.map +0 -1
- package/dist/contracts/codex.contract.js +0 -6
- package/dist/contracts/codex.contract.js.map +0 -1
- package/dist/contracts/gemini.contract.d.ts +0 -16
- package/dist/contracts/gemini.contract.d.ts.map +0 -1
- package/dist/contracts/gemini.contract.js +0 -6
- package/dist/contracts/gemini.contract.js.map +0 -1
- package/dist/contracts/kimi.contract.d.ts +0 -16
- package/dist/contracts/kimi.contract.d.ts.map +0 -1
- package/dist/contracts/kimi.contract.js +0 -6
- package/dist/contracts/kimi.contract.js.map +0 -1
- package/dist/contracts/komodo.contract.d.ts +0 -125
- package/dist/contracts/komodo.contract.d.ts.map +0 -1
- package/dist/contracts/komodo.contract.js +0 -14
- package/dist/contracts/komodo.contract.js.map +0 -1
- package/dist/contracts/memory.contract.d.ts +0 -34
- package/dist/contracts/memory.contract.d.ts.map +0 -1
- package/dist/contracts/memory.contract.js +0 -9
- package/dist/contracts/memory.contract.js.map +0 -1
- package/src/contracts/codex.contract.ts +0 -10
- package/src/contracts/gemini.contract.ts +0 -11
- package/src/contracts/kimi.contract.ts +0 -9
- package/src/contracts/komodo.contract.ts +0 -36
- package/src/contracts/memory.contract.ts +0 -13
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { oc } from "@orpc/contract";
|
|
2
2
|
import {
|
|
3
|
-
CommitMessageDraftSchema,
|
|
4
|
-
CommitMessageSchema,
|
|
5
3
|
CommitResultSchema,
|
|
6
4
|
CommitSchema,
|
|
7
5
|
DiscardSchema,
|
|
@@ -24,15 +22,25 @@ import {
|
|
|
24
22
|
GitFileWriteSchema,
|
|
25
23
|
GitLogQuerySchema,
|
|
26
24
|
GitLogSchema,
|
|
25
|
+
GitOperationStateSchema,
|
|
26
|
+
GitUndoSchema,
|
|
27
|
+
GitUndoStateSchema,
|
|
27
28
|
GitRemoteStateSchema,
|
|
28
29
|
GitReposSchema,
|
|
29
30
|
GitResetSchema,
|
|
30
31
|
GitStageSchema,
|
|
31
32
|
GitStatusSchema,
|
|
32
33
|
GitTagCreateSchema,
|
|
34
|
+
GitTagDeleteSchema,
|
|
35
|
+
GitTagPushSchema,
|
|
33
36
|
OkSchema,
|
|
34
37
|
PushSchema,
|
|
35
38
|
RepoParamSchema,
|
|
39
|
+
StashApplySchema,
|
|
40
|
+
StashDiffQuerySchema,
|
|
41
|
+
StashListSchema,
|
|
42
|
+
StashPushSchema,
|
|
43
|
+
StashRefParamSchema,
|
|
36
44
|
} from "../schemas.js";
|
|
37
45
|
|
|
38
46
|
// Per-repo git ops over the workspace repos: "root" (the /work repo) plus every discovered repo under /work
|
|
@@ -41,10 +49,6 @@ import {
|
|
|
41
49
|
// Changes panel renders; commit/discard take optional `paths` for per-file actions.
|
|
42
50
|
export const gitContract = {
|
|
43
51
|
changes: oc.route({ method: "GET", path: "/git/changes" }).output(GitChangesSchema),
|
|
44
|
-
// Drafts a commit message for what the commit box is about to record, on the sandbox's quick model (the
|
|
45
|
-
// cheap rung — see quick-model.ts). Workspace-wide like `changes` and for the same reason: one commit box,
|
|
46
|
-
// one message, every staged repo. POST because it spends a model call, not because it writes anything.
|
|
47
|
-
commitMessage: oc.route({ method: "POST", path: "/git/commit-message" }).input(CommitMessageDraftSchema).output(CommitMessageSchema),
|
|
48
52
|
// The git-history graph over one repo's real commits: the repo list (for the tree affordance + switcher),
|
|
49
53
|
// one repo's commit log, and lazy per-commit detail (changed files, then a file's before/after AT the
|
|
50
54
|
// commit). Read-only — commit/discard on the working tree stay the write path (above).
|
|
@@ -55,8 +59,31 @@ export const gitContract = {
|
|
|
55
59
|
// Write actions from the graph's commit context menu (VSCode "Git Graph" parity). Non-destructive refs
|
|
56
60
|
// (branch/tag) return Ok and let git's errors propagate; the sequence + HEAD-moving ops return a
|
|
57
61
|
// GitActionResult so a conflict/clean-apply failure is a value, not a 500. Read routes above.
|
|
62
|
+
/* The halted-operation pair. `operation` is a READ every git surface can use to explain a worktree it cannot
|
|
63
|
+
* otherwise act on; `abort` is the single way out, and it is git's own `--abort` rather than anything
|
|
64
|
+
* clever. Neither is reachable from the daemon's own verbs — those abort themselves — so this exists purely
|
|
65
|
+
* for what a terminal left behind. */
|
|
66
|
+
operation: oc.route({ method: "GET", path: "/git/{repo}/operation" }).input(RepoParamSchema).output(GitOperationStateSchema),
|
|
67
|
+
abort: oc.route({ method: "POST", path: "/git/{repo}/abort" }).input(RepoParamSchema).output(GitActionResultSchema),
|
|
68
|
+
/* Walk the current branch back to where it was before its last action, off the branch's own reflog. The
|
|
69
|
+
* complement to the Checkpoints timeline, not a duplicate of it: a checkpoint restores the working tree,
|
|
70
|
+
* this moves the ref. The read carries `previousSha`, which the write sends back as a concurrency token —
|
|
71
|
+
* an undo prepared against a stale view is refused rather than landing somewhere unlooked-at. */
|
|
72
|
+
undoable: oc.route({ method: "GET", path: "/git/{repo}/undo" }).input(RepoParamSchema).output(GitUndoStateSchema),
|
|
73
|
+
undo: oc.route({ method: "POST", path: "/git/{repo}/undo" }).input(GitUndoSchema).output(GitActionResultSchema),
|
|
74
|
+
/* The stash. Read as a list plus a per-entry diff, mirroring the commit log and commit-diff pair above,
|
|
75
|
+
* because a stash entry is a commit and the graph renders it as one. The writes are git's own four verbs;
|
|
76
|
+
* only `drop` is unrecoverable, and the route checkpoints before it. */
|
|
77
|
+
stashes: oc.route({ method: "GET", path: "/git/{repo}/stashes" }).input(RepoParamSchema).output(StashListSchema),
|
|
78
|
+
stashDiff: oc.route({ method: "GET", path: "/git/{repo}/stash-diff" }).input(StashDiffQuerySchema).output(GitCommitDiffSchema),
|
|
79
|
+
stashPush: oc.route({ method: "POST", path: "/git/{repo}/stash" }).input(StashPushSchema).output(GitActionResultSchema),
|
|
80
|
+
stashApply: oc.route({ method: "POST", path: "/git/{repo}/stash/apply" }).input(StashApplySchema).output(GitActionResultSchema),
|
|
81
|
+
stashDrop: oc.route({ method: "POST", path: "/git/{repo}/stash/drop" }).input(StashRefParamSchema).output(OkSchema),
|
|
58
82
|
createBranch: oc.route({ method: "POST", path: "/git/{repo}/branch" }).input(GitBranchCreateSchema).output(OkSchema),
|
|
59
83
|
createTag: oc.route({ method: "POST", path: "/git/{repo}/tag" }).input(GitTagCreateSchema).output(OkSchema),
|
|
84
|
+
// The other two things one does with a tag, so the graph's tag pills are not a create-only affordance.
|
|
85
|
+
deleteTag: oc.route({ method: "POST", path: "/git/{repo}/tag/delete" }).input(GitTagDeleteSchema).output(OkSchema),
|
|
86
|
+
pushTag: oc.route({ method: "POST", path: "/git/{repo}/tag/push" }).input(GitTagPushSchema).output(GitActionResultSchema),
|
|
60
87
|
checkout: oc.route({ method: "POST", path: "/git/{repo}/checkout" }).input(GitCheckoutSchema).output(GitActionResultSchema),
|
|
61
88
|
cherryPick: oc.route({ method: "POST", path: "/git/{repo}/cherry-pick" }).input(GitCommitActionSchema).output(GitActionResultSchema),
|
|
62
89
|
revert: oc.route({ method: "POST", path: "/git/{repo}/revert" }).input(GitCommitActionSchema).output(GitActionResultSchema),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { oc } from "@orpc/contract";
|
|
2
|
-
import { AccountIdSchema, DeviceStartSchema,
|
|
2
|
+
import { AccountIdSchema, DeviceStartSchema, OauthAccountListSchema, OkSchema } from "../schemas.js";
|
|
3
3
|
|
|
4
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
|
|
@@ -7,10 +7,11 @@ import { AccountIdSchema, DeviceStartSchema, ModelsSchema, OauthAccountListSchem
|
|
|
7
7
|
// — there is no paste-back — and the UI polls `accounts` until connected. `disconnect` clears the tokens.
|
|
8
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
|
+
//
|
|
11
|
+
// Like Claude's, this contract is now the account handshake alone — the model catalog answers on the shared
|
|
12
|
+
// /providers/{provider}/models route (providers.contract.ts).
|
|
10
13
|
export const grokContract = {
|
|
11
14
|
start: oc.route({ method: "POST", path: "/grok/oauth/start" }).output(DeviceStartSchema),
|
|
12
|
-
// xAI's live models (via OpenCode) for the model picker — the source of truth for valid model ids.
|
|
13
|
-
models: oc.route({ method: "GET", path: "/grok/models" }).output(ModelsSchema),
|
|
14
15
|
accounts: oc.route({ method: "GET", path: "/grok/accounts" }).output(OauthAccountListSchema),
|
|
15
16
|
disconnect: oc.route({ method: "POST", path: "/grok/account/disconnect" }).input(AccountIdSchema).output(OkSchema),
|
|
16
17
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { oc } from "@orpc/contract";
|
|
1
|
+
import { eventIterator, oc } from "@orpc/contract";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { HostFactsSchema, HostScopesSchema, OkSchema } from "../schemas.js";
|
|
3
|
+
import { HostFactsSchema, HostScopesSchema, MachineFlowLineSchema, MachineSandboxFlowSchema, OkSchema } from "../schemas.js";
|
|
4
4
|
|
|
5
5
|
/* What a connected computer can be ASKED, over the socket it opened to this sandbox.
|
|
6
6
|
*
|
|
@@ -30,4 +30,15 @@ export const hostContract = {
|
|
|
30
30
|
ping: oc.output(OkSchema),
|
|
31
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
|
+
/* One operation on one of this machine's sandboxes, narrated as it happens.
|
|
34
|
+
*
|
|
35
|
+
* TYPED, unlike `mcp`, and the difference is who is on the other end. `mcp`'s reader is a model, which has
|
|
36
|
+
* nothing to do with a line as it arrives and everything to gain from the machine learning tools without a
|
|
37
|
+
* daemon release. This reader is a PERSON watching a progress log: an update pulls an image and recreates a
|
|
38
|
+
* container, which is minutes of silence unless the lines travel while they are produced. A stream is what
|
|
39
|
+
* the browser needs, and a stream is the one thing an MCP tool result cannot be.
|
|
40
|
+
*
|
|
41
|
+
* The scope is still checked here, on the machine, by the same functions the MCP tools call — this adds a
|
|
42
|
+
* way of WATCHING an operation, never a way of skipping the switch that permits it. */
|
|
43
|
+
runSandboxFlow: oc.input(MachineSandboxFlowSchema).output(eventIterator(MachineFlowLineSchema)),
|
|
33
44
|
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { oc } from "@orpc/contract";
|
|
2
|
+
import {
|
|
3
|
+
LoopDesignIdParamSchema,
|
|
4
|
+
LoopDesignSaveSchema,
|
|
5
|
+
LoopDesignSchema,
|
|
6
|
+
LoopDesignsListSchema,
|
|
7
|
+
LoopIdParamSchema,
|
|
8
|
+
LoopRecordSchema,
|
|
9
|
+
LoopSchema,
|
|
10
|
+
LoopsListSchema,
|
|
11
|
+
OkSchema,
|
|
12
|
+
} from "../schemas.js";
|
|
13
|
+
|
|
14
|
+
/* The loop routes — "run this conversation again until the goal is met".
|
|
15
|
+
*
|
|
16
|
+
* TWO HALVES THAT LOOK LIKE ONE FEATURE AND ARE NOT, which is why they share a file and share nothing else.
|
|
17
|
+
*
|
|
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 —
|
|
20
|
+
* the conversation IS the id.
|
|
21
|
+
*
|
|
22
|
+
* A SAVED LOOP is a manifest entry like a workflow, and gets the manifest treatment: list, save, remove. It is
|
|
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 — running one is `start`
|
|
25
|
+
* with the design's fields and the composer's sentence, so there is exactly one way a loop begins and exactly
|
|
26
|
+
* one place that can refuse it.
|
|
27
|
+
*
|
|
28
|
+
* `start` acks immediately with the loop as recorded and runs detached, the same contract POST /agent keeps: the
|
|
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 — the outcome arrives on the fleet card.
|
|
31
|
+
*/
|
|
32
|
+
export const loopsContract = {
|
|
33
|
+
// Every loop this workspace has run, newest first — the record is kept after the loop ends, because "why did
|
|
34
|
+
// it stop at iteration 4" is the question a loop is read for, and the answer is its iteration history.
|
|
35
|
+
list: oc.route({ method: "GET", path: "/loops" }).output(LoopsListSchema),
|
|
36
|
+
/* Start looping a conversation. Rejects when that conversation is already looping — a second loop on one
|
|
37
|
+
* agent would have two pumps racing the same worktree and the same turn mutex, and the loser would spend a
|
|
38
|
+
* turn to discover it.
|
|
39
|
+
*
|
|
40
|
+
* The conversation need not exist yet: a loop against a fresh id opens it, exactly as a first chat turn
|
|
41
|
+
* does, which is what lets "run this until it's green" be the FIRST thing said to a new agent. */
|
|
42
|
+
start: oc.route({ method: "POST", path: "/loops" }).input(LoopSchema).output(LoopRecordSchema),
|
|
43
|
+
/* Stop the loop, leaving the turn in flight alone.
|
|
44
|
+
*
|
|
45
|
+
* The split is deliberate and it is the one thing about this route that has to be right: stopping a LOOP
|
|
46
|
+
* means "do not start another iteration", not "kill what is running". A user watching iteration 6 do good
|
|
47
|
+
* work should be able to say "this is the last one" without throwing that work away. Killing the turn is
|
|
48
|
+
* what /agent/stop is for, and pressing both is the ordinary way to abandon a loop outright. */
|
|
49
|
+
stop: oc.route({ method: "POST", path: "/loops/{conversationId}/stop" }).input(LoopIdParamSchema).output(OkSchema),
|
|
50
|
+
|
|
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 — it is the same loop with its goal left blank until somebody types one.
|
|
53
|
+
designs: oc.route({ method: "GET", path: "/loops/designs" }).output(LoopDesignsListSchema),
|
|
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 — nothing to produce and nothing to check — with
|
|
56
|
+
* the same sentence `start` refuses an ad-hoc loop for, because it is the same mistake made earlier and
|
|
57
|
+
* catching it at save time is the whole advantage of saving. */
|
|
58
|
+
saveDesign: oc.route({ method: "POST", path: "/loops/designs" }).input(LoopDesignSaveSchema).output(LoopDesignSchema),
|
|
59
|
+
// Deleting a saved loop does NOT stop a loop running from it: a running loop copied the fields it needed
|
|
60
|
+
// when it started, so it converges or gives up on its own terms, and its record stays readable.
|
|
61
|
+
removeDesign: oc.route({ method: "DELETE", path: "/loops/designs/{id}" }).input(LoopDesignIdParamSchema).output(OkSchema),
|
|
62
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { oc } from "@orpc/contract";
|
|
2
|
+
import {
|
|
3
|
+
OkSchema,
|
|
4
|
+
PersonaIdParamSchema,
|
|
5
|
+
PersonaKitSchema,
|
|
6
|
+
PersonaPromptSchema,
|
|
7
|
+
PersonaSchema,
|
|
8
|
+
PersonasListSchema,
|
|
9
|
+
PersonaSkillBodySchema,
|
|
10
|
+
PersonaSkillNameSchema,
|
|
11
|
+
PersonaSkillSchema,
|
|
12
|
+
} from "../schemas.js";
|
|
13
|
+
|
|
14
|
+
/* The sandbox's named personas (PersonaSchema): which connected accounts each one speaks for, what a session
|
|
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 — unlike a capability, saving one connects nothing, installs nothing
|
|
17
|
+
* and spends nothing. It records a decision about accounts that already exist.
|
|
18
|
+
*
|
|
19
|
+
* The file behind it is committed workspace config (personas/personas-store.ts), so these routes are one of
|
|
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 — which is why `save` is a whole-card upsert rather than a field patch, so
|
|
22
|
+
* a round trip through the UI leaves a diff a reviewer would recognise.
|
|
23
|
+
*
|
|
24
|
+
* THE KIT ROUTES BELOW EDIT FILES, not the card, and they are here rather than on the skills contract because
|
|
25
|
+
* what they write belongs to one persona: its prompt, and the skills only its turns can reach
|
|
26
|
+
* (personas/persona-kit.ts). The sandbox's `skills` domain answers "what does the agent know" for every chat;
|
|
27
|
+
* this answers "what does this card carry", and the two lists are different questions with different answers.
|
|
28
|
+
*
|
|
29
|
+
* Per-verb routes rather than one whole-kit PUT: a kit is a directory of files somebody edits one at a time, and
|
|
30
|
+
* a save that shipped the whole folder would make an edit to one skill capable of deleting another. */
|
|
31
|
+
export const personasContract = {
|
|
32
|
+
list: oc.route({ method: "GET", path: "/personas" }).output(PersonasListSchema),
|
|
33
|
+
// Upsert by id — re-saving the same id edits that card.
|
|
34
|
+
save: oc.route({ method: "POST", path: "/personas" }).input(PersonaSchema).output(OkSchema),
|
|
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 — 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
|
+
* them", so the automation goes quiet instead of posting as somebody unintended. */
|
|
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 — a folder no card can reach is a folder no list
|
|
41
|
+
* shows, and leaving the owner's prompt and skills orphaned on disk is worse than deleting what they just
|
|
42
|
+
* asked to delete. */
|
|
43
|
+
remove: oc.route({ method: "DELETE", path: "/personas/{id}" }).input(PersonaIdParamSchema).output(OkSchema),
|
|
44
|
+
|
|
45
|
+
// ---- the kit: what this card is told, and the skills only it reaches ----
|
|
46
|
+
|
|
47
|
+
kit: oc.route({ method: "GET", path: "/personas/{id}/kit" }).input(PersonaIdParamSchema).output(PersonaKitSchema),
|
|
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 — the resolver falls back to the sandbox's prompt for it (personas.ts personaPrompt).
|
|
50
|
+
savePrompt: oc.route({ method: "POST", path: "/personas/{id}/prompt" }).input(PersonaPromptSchema).output(OkSchema),
|
|
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 — and with no enabled list to write, because a kit skill is
|
|
53
|
+
// on exactly when its persona is worn. That is what "specific to that persona" has to mean.
|
|
54
|
+
saveSkill: oc.route({ method: "POST", path: "/personas/{id}/skills" }).input(PersonaSkillSchema).output(OkSchema),
|
|
55
|
+
removeSkill: oc.route({ method: "POST", path: "/personas/{id}/skills/remove" }).input(PersonaSkillNameSchema).output(OkSchema),
|
|
56
|
+
};
|
|
@@ -6,9 +6,10 @@ import { OkSchema, PrepushRunSchema } from "../schemas.js";
|
|
|
6
6
|
// main working tree, of which there is exactly one, so nothing here is addressed by id.
|
|
7
7
|
//
|
|
8
8
|
// `run` starts the check and returns immediately — a suite takes minutes, and an oRPC call held open for one
|
|
9
|
-
// would die on the first proxy timeout with the work still going. The dialog that started it polls `state
|
|
10
|
-
//
|
|
11
|
-
//
|
|
9
|
+
// would die on the first proxy timeout with the work still going. The dialog that started it polls `state` for
|
|
10
|
+
// the verdict, and opens the terminals panel on the `session` the first answer names: the suite runs in a real
|
|
11
|
+
// tmux window, so watching it is the terminal's job. `cancel` kills that window; the run settles as `cancelled`
|
|
12
|
+
// and the push it was gating does not go.
|
|
12
13
|
export const prepushContract = {
|
|
13
14
|
state: oc.route({ method: "GET", path: "/prepush/state" }).output(PrepushRunSchema),
|
|
14
15
|
run: oc.route({ method: "POST", path: "/prepush/run" }).output(OkSchema),
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { oc } from "@orpc/contract";
|
|
2
|
+
import { ModelsSchema, NativeProviderParamSchema } from "../schemas.js";
|
|
3
|
+
|
|
4
|
+
// EVERY NATIVE PROVIDER'S PICKER CATALOG, ON ONE ROUTE.
|
|
5
|
+
//
|
|
6
|
+
// There were five of these — /claude/models, /codex/models, /grok/models, /kimi/models, /gemini/models — one per
|
|
7
|
+
// provider, each a contract entry, a route factory and a service field wired separately. They differed in
|
|
8
|
+
// nothing: same method, same output schema, same "the daemon's catalog for this provider, never empty". Three of
|
|
9
|
+
// the five route files were the same fifteen lines with a name swapped, and the daemon paid for that shape twice
|
|
10
|
+
// more in the branch chains that had to fan back out over the providers to ask them all the same question.
|
|
11
|
+
//
|
|
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
|
+
// the contract, the router, the service container and every test double.
|
|
15
|
+
//
|
|
16
|
+
// Endpoints keep their own route (endpoints.contract.ts) and should: they are user-created and unbounded, their
|
|
17
|
+
// id names a capability that may not exist, and a missing one is a NOT_FOUND rather than an empty catalog. That
|
|
18
|
+
// is a different question with a different failure, not a sixth provider.
|
|
19
|
+
export const providersContract = {
|
|
20
|
+
// One provider's models (+ its default id), never empty — live discovery with a persisted/seed floor behind
|
|
21
|
+
// it. Order is the provider's own preference order and is not re-ranked here; see ModelsSchema.
|
|
22
|
+
models: oc.route({ method: "GET", path: "/providers/{provider}/models" }).input(NativeProviderParamSchema).output(ModelsSchema),
|
|
23
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { oc } from "@orpc/contract";
|
|
2
|
+
import { OkSchema, PublicListSchema, PublishResultSchema, PublishSchema, UnpublishSchema } from "../schemas.js";
|
|
3
|
+
|
|
4
|
+
// The workspace outbox — what `public/` currently holds and its address (see the public section in schemas.ts).
|
|
5
|
+
// `publish` copies a workspace file or directory in, creating the outbox if this is the first one; `unpublish`
|
|
6
|
+
// withdraws one and removes the outbox behind the last, so the directory's presence always means exactly "there
|
|
7
|
+
// is something published". There is no route to READ a published file: that is the whole point of the
|
|
8
|
+
// unauthenticated `public-<slot>` hostname, and a second, authenticated way in would just be the workspace file
|
|
9
|
+
// API with extra steps.
|
|
10
|
+
export const publicContract = {
|
|
11
|
+
list: oc.route({ method: "GET", path: "/public" }).output(PublicListSchema),
|
|
12
|
+
publish: oc.route({ method: "POST", path: "/public/publish" }).input(PublishSchema).output(PublishResultSchema),
|
|
13
|
+
unpublish: oc.route({ method: "POST", path: "/public/unpublish" }).input(UnpublishSchema).output(OkSchema),
|
|
14
|
+
};
|
|
@@ -7,10 +7,14 @@ import { SessionIdParamSchema, SessionsListSchema } from "../schemas.js";
|
|
|
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
|
+
//
|
|
11
|
+
// `caseSensitive` is the filter's Aa switch, on the same terms as the fleet search's — off means the letters do
|
|
12
|
+
// not matter, and the two routes answer one query together (the board lists these rows under its own cards), so
|
|
13
|
+
// a switch either of them ignored would show as one field returning two different match sets.
|
|
10
14
|
export const sessionsContract = {
|
|
11
15
|
list: oc
|
|
12
16
|
.route({ method: "GET", path: "/sessions" })
|
|
13
|
-
.input(z.object({ query: z.string().optional() }))
|
|
17
|
+
.input(z.object({ query: z.string().optional(), caseSensitive: z.stringbool().optional() }))
|
|
14
18
|
.output(SessionsListSchema),
|
|
15
19
|
get: oc.route({ method: "GET", path: "/sessions/{id}" }).input(SessionIdParamSchema).output(SessionTranscriptSchema),
|
|
16
20
|
};
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
BuiltinPromptTextSchema,
|
|
5
5
|
DayWindowQuerySchema,
|
|
6
6
|
OkSchema,
|
|
7
|
+
RuleFiringsSchema,
|
|
7
8
|
SandboxSettingsSchema,
|
|
8
9
|
SavingsReportSchema,
|
|
9
10
|
} from "../schemas.js";
|
|
@@ -20,4 +21,8 @@ export const settingsContract = {
|
|
|
20
21
|
set: oc.route({ method: "POST", path: "/settings" }).input(SandboxSettingsSchema).output(OkSchema),
|
|
21
22
|
savings: oc.route({ method: "GET", path: "/settings/savings" }).input(DayWindowQuerySchema).output(SavingsReportSchema),
|
|
22
23
|
builtinPrompt: oc.route({ method: "GET", path: "/settings/system-prompt/{base}" }).input(BuiltinPromptSchema).output(BuiltinPromptTextSchema),
|
|
24
|
+
// When each rule last did something, keyed by rule id. Its own route rather than a field on the settings
|
|
25
|
+
// object because a firing is not an edit: folding it in would make every push a settings write, and would
|
|
26
|
+
// put a value that changes on its own inside the object the screen optimistically patches.
|
|
27
|
+
firings: oc.route({ method: "GET", path: "/settings/rule-firings" }).output(RuleFiringsSchema),
|
|
23
28
|
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { oc } from "@orpc/contract";
|
|
2
|
+
import { OkSchema, ShareCreateSchema, ShareListSchema, ShareRemoveSchema, ShareUpdateSchema, SharedConversationSchema } from "../schemas.js";
|
|
3
|
+
|
|
4
|
+
/* Conversations published as read-only pages (see the share section in schemas.ts).
|
|
5
|
+
*
|
|
6
|
+
* Its own group rather than two more routes on `public`, because the two speak different path spaces and mean
|
|
7
|
+
* different things by "publish". The outbox's routes take a path in the workspace and copy the bytes at it; a
|
|
8
|
+
* share takes a CONVERSATION ID and renders something that did not exist as a file until it was asked for.
|
|
9
|
+
* Folding them together would have `publish` accept two unrelated kinds of input distinguished by a flag.
|
|
10
|
+
*
|
|
11
|
+
* There is no route to read a share back. The page is the read — it answers on the unauthenticated
|
|
12
|
+
* `public-<slot>` hostname like every other published file, which is the point of having made one. */
|
|
13
|
+
export const shareContract = {
|
|
14
|
+
list: oc.route({ method: "GET", path: "/share" }).output(ShareListSchema),
|
|
15
|
+
// Answers with the row it just wrote, link included, so the dialog can show the address without re-listing.
|
|
16
|
+
create: oc.route({ method: "POST", path: "/share" }).input(ShareCreateSchema).output(SharedConversationSchema),
|
|
17
|
+
// Re-render an existing share from the conversation as it stands now. Same id, same link, new snapshot.
|
|
18
|
+
update: oc.route({ method: "POST", path: "/share/update" }).input(ShareUpdateSchema).output(SharedConversationSchema),
|
|
19
|
+
remove: oc.route({ method: "POST", path: "/share/remove" }).input(ShareRemoveSchema).output(OkSchema),
|
|
20
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { oc } from "@orpc/contract";
|
|
2
|
+
import { OkSchema, SkillBodySchema, SkillDraftSchema, SkillIdSchema, SkillRemoveSchema, SkillsListSchema } from "../schemas.js";
|
|
3
|
+
|
|
4
|
+
/* WHAT THE AGENT KNOWS RIGHT NOW, and which half of it the owner controls.
|
|
5
|
+
*
|
|
6
|
+
* Its own domain rather than three more routes on `settings`, because only one of the four sources it reads is
|
|
7
|
+
* the settings file: the rest are the owner's own skill store, the plugin checkouts a capability cloned, and the
|
|
8
|
+
* skills folders inside installed extensions. `list` is the join of all four (skill-inventory.ts).
|
|
9
|
+
*
|
|
10
|
+
* The ENABLED half still rides the settings object's `skills` array — that array is what the reconciler
|
|
11
|
+
* converges and what the boot path already reads, so a switch on this list is an ordinary settings write and
|
|
12
|
+
* there is exactly one place that decides which skills exist on disk. `save` and `remove` write the text AND
|
|
13
|
+
* that array together, which is why they are here rather than being two calls a screen has to sequence.
|
|
14
|
+
*
|
|
15
|
+
* `read` is a GET with the id in the query rather than in the path: an id can name an owner
|
|
16
|
+
* (`extension:intentic.knowledge:knowledge`), and a path template cannot carry those segments. */
|
|
17
|
+
export const skillsContract = {
|
|
18
|
+
list: oc.route({ method: "GET", path: "/skills" }).output(SkillsListSchema),
|
|
19
|
+
read: oc.route({ method: "GET", path: "/skills/read" }).input(SkillIdSchema).output(SkillBodySchema),
|
|
20
|
+
// Upsert by name: saving over an existing skill rewrites it, which is also how one is renamed (the old name
|
|
21
|
+
// is a different skill and is deleted on its own). A saved skill is switched ON — you wrote it to use it.
|
|
22
|
+
save: oc.route({ method: "POST", path: "/skills" }).input(SkillDraftSchema).output(OkSchema),
|
|
23
|
+
remove: oc.route({ method: "POST", path: "/skills/remove" }).input(SkillRemoveSchema).output(OkSchema),
|
|
24
|
+
};
|
|
@@ -5,14 +5,17 @@ import {
|
|
|
5
5
|
BrowserNameParamSchema,
|
|
6
6
|
BrowsersListSchema,
|
|
7
7
|
DaemonSessionSchema,
|
|
8
|
-
HostTunnelInputSchema,
|
|
9
|
-
HostTunnelSchema,
|
|
10
8
|
InfoSchema,
|
|
9
|
+
ManifestProblemsSchema,
|
|
10
|
+
MachineFlowLineSchema,
|
|
11
|
+
MachineSandboxFlowInputSchema,
|
|
11
12
|
OkSchema,
|
|
12
13
|
PresenceReportSchema,
|
|
13
14
|
SubagentIdParamSchema,
|
|
14
15
|
SubagentsListSchema,
|
|
15
16
|
TerminalNameParamSchema,
|
|
17
|
+
TerminalScrollbackQuerySchema,
|
|
18
|
+
TerminalScrollbackSchema,
|
|
16
19
|
TerminalsListSchema,
|
|
17
20
|
UsageSummarySchema,
|
|
18
21
|
} from "../schemas.js";
|
|
@@ -24,6 +27,10 @@ import {
|
|
|
24
27
|
// connection simply never joins the roster.
|
|
25
28
|
export const systemContract = {
|
|
26
29
|
info: oc.route({ method: "GET", path: "/info" }).output(InfoSchema),
|
|
30
|
+
// What the daemon could not read in its own `.intentic/` manifests — a file it fell back on, a key it did
|
|
31
|
+
// not recognise, an entry it skipped. Its own route rather than a field on /info because it is invalidated
|
|
32
|
+
// by a different thing: a manifest changing on disk, which the workspace-state table already broadcasts.
|
|
33
|
+
manifestProblems: oc.route({ method: "GET", path: "/system/manifest-problems" }).output(ManifestProblemsSchema),
|
|
27
34
|
// Exchange the request's verified bearer (a Google ID token — or a still-valid session, which makes this
|
|
28
35
|
// route sliding renewal) for a daemon-minted session, the credential every steady-state call presents.
|
|
29
36
|
session: oc.route({ method: "POST", path: "/system/session" }).output(DaemonSessionSchema),
|
|
@@ -33,7 +40,6 @@ export const systemContract = {
|
|
|
33
40
|
.output(eventIterator(SystemEventSchema)),
|
|
34
41
|
// A tab's activity self-report (view/session/file/idle), fanned back out to every member on /events.
|
|
35
42
|
presence: oc.route({ method: "POST", path: "/system/presence" }).input(PresenceReportSchema).output(OkSchema),
|
|
36
|
-
hostTunnel: oc.route({ method: "POST", path: "/system/host-tunnel" }).input(HostTunnelInputSchema).output(HostTunnelSchema),
|
|
37
43
|
// Per-account token/cost totals, aggregated from the activity log's turn.completed events.
|
|
38
44
|
usage: oc.route({ method: "GET", path: "/system/usage" }).output(UsageSummarySchema),
|
|
39
45
|
// The web-owned tmux sessions behind the terminal tabs. `terminals` enumerates them (the panel rebuilds a tab
|
|
@@ -42,6 +48,13 @@ export const systemContract = {
|
|
|
42
48
|
// (browser fetch sends the header), unlike the header-less WS route which app.ts exempts.
|
|
43
49
|
terminals: oc.route({ method: "GET", path: "/system/terminals" }).output(TerminalsListSchema),
|
|
44
50
|
killTerminal: oc.route({ method: "DELETE", path: "/system/terminals/{name}" }).input(TerminalNameParamSchema).output(OkSchema),
|
|
51
|
+
// One session's pane history as selectable text — the answer to "scroll back and copy that" in a surface
|
|
52
|
+
// whose live view is a tmux client on the alternate screen, where the scrollback is on the far side of the
|
|
53
|
+
// socket and the page has nothing to select. See TerminalScrollbackSchema.
|
|
54
|
+
terminalScrollback: oc
|
|
55
|
+
.route({ method: "GET", path: "/system/terminals/{name}/scrollback" })
|
|
56
|
+
.input(TerminalScrollbackQuerySchema)
|
|
57
|
+
.output(TerminalScrollbackSchema),
|
|
45
58
|
// The agent's live Chromiums and the pages each has open — the Browsers view's roster, polled while it is on
|
|
46
59
|
// screen and by the rail so its tile can appear the moment a turn starts browsing. The frames are the
|
|
47
60
|
// separate /system/browser-view WebSocket; this is the control plane, exactly as `terminals` is for tmux.
|
|
@@ -61,4 +74,15 @@ export const systemContract = {
|
|
|
61
74
|
.route({ method: "GET", path: "/system/subagents/{id}/transcript" })
|
|
62
75
|
.input(SubagentIdParamSchema)
|
|
63
76
|
.output(SessionTranscriptSchema),
|
|
77
|
+
/* Start, stop, restart, update, rebuild, roll back or remove a sandbox on one of the user's own computers —
|
|
78
|
+
* the Computers view's buttons, relayed to the machine over the socket it holds open to us.
|
|
79
|
+
*
|
|
80
|
+
* Streamed because the slowest of these takes minutes, and it is the same stream whatever the op: one door
|
|
81
|
+
* for one decision, so the view has one shape to render rather than one per duration. The daemon adds no
|
|
82
|
+
* judgement — the machine enforces its own switches and its refusal arrives as the terminal `error` line,
|
|
83
|
+
* in its own words, naming the control to flip. */
|
|
84
|
+
manageMachineSandbox: oc
|
|
85
|
+
.route({ method: "POST", path: "/system/computers/{id}/sandboxes/{slug}" })
|
|
86
|
+
.input(MachineSandboxFlowInputSchema)
|
|
87
|
+
.output(eventIterator(MachineFlowLineSchema)),
|
|
64
88
|
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { oc } from "@orpc/contract";
|
|
2
|
+
import {
|
|
3
|
+
OkSchema,
|
|
4
|
+
WorkflowIdParamSchema,
|
|
5
|
+
WorkflowRunIdParamSchema,
|
|
6
|
+
WorkflowRunSchema,
|
|
7
|
+
WorkflowRunsListSchema,
|
|
8
|
+
WorkflowRunStartSchema,
|
|
9
|
+
WorkflowSaveSchema,
|
|
10
|
+
WorkflowSchema,
|
|
11
|
+
WorkflowsListSchema,
|
|
12
|
+
} from "../schemas.js";
|
|
13
|
+
|
|
14
|
+
/* The workflow routes — "run these sessions, in this order, each handing its result to the next".
|
|
15
|
+
*
|
|
16
|
+
* SPLIT LIKE AUTOMATIONS, NOT LIKE LOOPS, and the split says what a workflow is. A loop has no editor because
|
|
17
|
+
* it is started against a conversation and then it is history; a workflow is a DESIGN — a thing the user
|
|
18
|
+
* authors once, keeps, edits, and runs repeatedly — so it gets the manifest treatment: create/update, delete,
|
|
19
|
+
* list. What it does not get is an `enabled` toggle, because nothing fires it on its own: a workflow runs when
|
|
20
|
+
* somebody (or an automation's prompt) says run it.
|
|
21
|
+
*
|
|
22
|
+
* `run` acks with the run as recorded and executes detached, the contract every turn-starting route here
|
|
23
|
+
* keeps. The first step alone can take minutes and the run may take hours; the run view is where it is watched,
|
|
24
|
+
* and the fleet board shows its steps as the ordinary agents they are.
|
|
25
|
+
*/
|
|
26
|
+
export const workflowsContract = {
|
|
27
|
+
// Every saved workflow with its run history, newest run first. One route rather than two because the list
|
|
28
|
+
// page shows both and a workflow with no runs is the interesting case, not an error.
|
|
29
|
+
list: oc.route({ method: "GET", path: "/workflows" }).output(WorkflowsListSchema),
|
|
30
|
+
/* Create or replace a workflow, with the operation made explicit so an accidental id collision cannot turn
|
|
31
|
+
* a create into replacement. Refuses a graph that cannot run — a cycle, a `needs` naming a step
|
|
32
|
+
* that is not there, a step with no way to know it is finished — with the same sentences the designer shows
|
|
33
|
+
* while you type (see workflowFaults). Validation lives in the contract precisely so those two can never
|
|
34
|
+
* disagree about what is legal. */
|
|
35
|
+
save: oc.route({ method: "POST", path: "/workflows" }).input(WorkflowSaveSchema).output(WorkflowSchema),
|
|
36
|
+
// Deleting a workflow does NOT stop a run of it that is in flight, and does not delete its history: the run
|
|
37
|
+
// snapshotted its definition when it started, so it stays readable and stays stoppable.
|
|
38
|
+
remove: oc.route({ method: "DELETE", path: "/workflows/{id}" }).input(WorkflowIdParamSchema).output(OkSchema),
|
|
39
|
+
/* Start a run, optionally pointed at a request — the sentence the user typed in the composer, which every
|
|
40
|
+
* step is handed on top of its own prompt. Every step is recorded `pending` up front, so the graph is
|
|
41
|
+
* complete from the first frame and a missing node never has to mean two things. Several runs of one
|
|
42
|
+
* workflow may be in flight at once — they derive different conversation ids, so nothing is shared and
|
|
43
|
+
* nothing can collide, which is what makes "run this design again on a different question" free. */
|
|
44
|
+
run: oc.route({ method: "POST", path: "/workflows/{id}/run" }).input(WorkflowRunStartSchema).output(WorkflowRunSchema),
|
|
45
|
+
// Every run across every workflow, newest first — the history the run view opens onto, and the only place a
|
|
46
|
+
// deleted workflow's runs are still reachable.
|
|
47
|
+
runs: oc.route({ method: "GET", path: "/workflows/runs" }).output(WorkflowRunsListSchema),
|
|
48
|
+
/* Stop a run: nothing that has not started will start, and the steps in flight are CUT OFF where they
|
|
49
|
+
* are — their turns aborted exactly as /agent/stop aborts one, so whatever they had written stays on
|
|
50
|
+
* their branches and the step settles as stopped.
|
|
51
|
+
*
|
|
52
|
+
* Not the graceful "finish the iteration you are on" a LOOP's stop performs, and the difference is the
|
|
53
|
+
* unit: a loop's iteration is a round somebody is watching, a workflow step's is an entire agent turn.
|
|
54
|
+
* Waiting for one meant a stopped run kept working, kept spending and kept asking questions for minutes
|
|
55
|
+
* after the press, which is indistinguishable from a button that does nothing.
|
|
56
|
+
*
|
|
57
|
+
* IT ALWAYS ENDS THE RUN, including one no scheduler is behind — a record left `running` by a daemon that
|
|
58
|
+
* was replaced mid-flight. That case used to be refused, which made the stuck run permanent: a Stop that
|
|
59
|
+
* could not work, a step count that would never move, and no way off the board.
|
|
60
|
+
*/
|
|
61
|
+
stopRun: oc.route({ method: "POST", path: "/workflows/runs/{runId}/stop" }).input(WorkflowRunIdParamSchema).output(OkSchema),
|
|
62
|
+
/* Take an ENDED run off the board — the run's half of `agents.archive`, and the same bargain: nothing is
|
|
63
|
+
* lost, the checkouts are reclaimed, and `unarchiveRun` puts it all back. It needs an exit of its own
|
|
64
|
+
* because nothing about a run transitions once it is over, so a failed one would sit in Attention until
|
|
65
|
+
* fifty more had rolled it off the ledger. Refused while the run is going.
|
|
66
|
+
*
|
|
67
|
+
* IT ARCHIVES THE STEPS WITH THE RUN, which is what makes it an archive rather than a dismissal. A step
|
|
68
|
+
* has no card of its own — the run's row stands for it — so merely dropping the record released the run's
|
|
69
|
+
* conversations onto the board as loose cards at the exact moment the user said they were done with the
|
|
70
|
+
* job. Every step that ran is archived, on the same terms as pointing `agents.archive` at those ids: that
|
|
71
|
+
* route archives what the user named without re-litigating whether each one was ready to go, and a run the
|
|
72
|
+
* user has archived is exactly that gesture made once for the whole graph.
|
|
73
|
+
*/
|
|
74
|
+
archiveRun: oc.route({ method: "POST", path: "/workflows/runs/{runId}/archive" }).input(WorkflowRunIdParamSchema).output(OkSchema),
|
|
75
|
+
// Put an archived run and its sessions back on the board, the inverse of the above and the run's half of
|
|
76
|
+
// `agents.unarchive`.
|
|
77
|
+
unarchiveRun: oc.route({ method: "POST", path: "/workflows/runs/{runId}/unarchive" }).input(WorkflowRunIdParamSchema).output(OkSchema),
|
|
78
|
+
};
|
|
@@ -24,11 +24,13 @@ import {
|
|
|
24
24
|
WorkspaceHealthSchema,
|
|
25
25
|
WorkspaceInstallResultSchema,
|
|
26
26
|
WorkspaceInstallSchema,
|
|
27
|
+
WorkspaceMediaTicketQuerySchema,
|
|
27
28
|
WorkspaceMediaTicketSchema,
|
|
28
29
|
WorkspaceModulesSchema,
|
|
29
30
|
WorkspaceMoveSchema,
|
|
30
31
|
WorkspaceResolveQuerySchema,
|
|
31
32
|
WorkspaceResolveSchema,
|
|
33
|
+
WorkspaceScopeSchema,
|
|
32
34
|
WorkspaceSetupSchema,
|
|
33
35
|
WorkspaceSyncSchema,
|
|
34
36
|
WorkspaceTreeSchema,
|
|
@@ -38,7 +40,10 @@ import {
|
|
|
38
40
|
// stays a plain Hono route serving raw bytes with a Content-Type header (oRPC's request/response shape doesn't
|
|
39
41
|
// fit a streamed binary body). External MCP tools moved to the unified capabilities manifest (mcp kind).
|
|
40
42
|
export const workspaceContract = {
|
|
41
|
-
|
|
43
|
+
// `agent` names whose copy of the workspace to read (WorkspaceScopeSchema); omitted is the shared /work
|
|
44
|
+
// tree. Every read route below takes it, so a link into a conversation's own checkout browses as one tree
|
|
45
|
+
// rather than as one openable file surrounded by the shared one.
|
|
46
|
+
tree: oc.route({ method: "GET", path: "/workspace/tree" }).input(WorkspaceScopeSchema).output(WorkspaceTreeSchema),
|
|
42
47
|
// Lazy-load one directory's children — the tree returns ignored dirs (node_modules, .git, …) without children,
|
|
43
48
|
// and the client fetches them here on expand so a giant node_modules can't blow the tree walk's entry budget.
|
|
44
49
|
children: oc.route({ method: "GET", path: "/workspace/children" }).input(WorkspaceChildrenQuerySchema).output(WorkspaceChildrenSchema),
|
|
@@ -49,7 +54,10 @@ export const workspaceContract = {
|
|
|
49
54
|
* it answers a streamed byte RANGE, which oRPC has no shape for). Minting is here rather than beside it so
|
|
50
55
|
* it rides the bearer middleware and the contract's route advertisement: a browser can tell whether the
|
|
51
56
|
* sandbox in front of it can stream video at all, instead of learning it from a 404 mid-playback. */
|
|
52
|
-
mediaTicket: oc
|
|
57
|
+
mediaTicket: oc
|
|
58
|
+
.route({ method: "POST", path: "/workspace/media-ticket" })
|
|
59
|
+
.input(WorkspaceMediaTicketQuerySchema)
|
|
60
|
+
.output(WorkspaceMediaTicketSchema),
|
|
53
61
|
// Which file a NAMED reference means — the lookup behind every clickable path in the UI. A path an agent
|
|
54
62
|
// wrote in prose is often only a suffix of the real one, so it is matched against the workspace tree rather
|
|
55
63
|
// than trusted as root-relative.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import { newConversationId } from "./conversation-ids.js";
|
|
3
|
+
import { ConversationIdSchema } from "./schemas.js";
|
|
4
|
+
|
|
5
|
+
// The one property that is not a matter of taste: this string becomes a git branch and a filesystem path, and
|
|
6
|
+
// the id guard is what stands between those and an injection. Held over a large sample rather than one draw,
|
|
7
|
+
// because the generator picks from three independent spaces and any of them could produce the bad character.
|
|
8
|
+
test("every generated id passes the conversation-id guard", () => {
|
|
9
|
+
for (let index = 0; index < 2_000; index += 1) {
|
|
10
|
+
expect(ConversationIdSchema.safeParse(newConversationId()).success).toBe(true);
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("an id reads as a word pair with a short tail, and stays short", () => {
|
|
15
|
+
const id = newConversationId();
|
|
16
|
+
expect(id).toMatch(/^[a-z]+-[a-z]+-[0-9a-z]{4}$/);
|
|
17
|
+
// Comfortably under a UUID's 36, which is the whole reason this exists.
|
|
18
|
+
expect(id.length).toBeLessThan(24);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// The tail is what makes the readable half safe to repeat: names may rhyme, ids may not.
|
|
22
|
+
test("ids are unique across a burst", () => {
|
|
23
|
+
const ids = new Set(Array.from({ length: 5_000 }, newConversationId));
|
|
24
|
+
expect(ids.size).toBe(5_000);
|
|
25
|
+
});
|