@intentic/sandbox-contract 1.225.0 → 1.226.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/dist/agent-catalog.d.ts +2 -0
  2. package/dist/agent-catalog.d.ts.map +1 -1
  3. package/dist/agent-catalog.js +16 -0
  4. package/dist/agent-catalog.js.map +1 -1
  5. package/dist/contracts/activity.contract.d.ts.map +1 -1
  6. package/dist/contracts/activity.contract.js +17 -2
  7. package/dist/contracts/activity.contract.js.map +1 -1
  8. package/dist/contracts/agent.contract.d.ts.map +1 -1
  9. package/dist/contracts/agent.contract.js +71 -8
  10. package/dist/contracts/agent.contract.js.map +1 -1
  11. package/dist/contracts/agents.contract.d.ts +1 -0
  12. package/dist/contracts/agents.contract.d.ts.map +1 -1
  13. package/dist/contracts/agents.contract.js +164 -19
  14. package/dist/contracts/agents.contract.js.map +1 -1
  15. package/dist/contracts/automations.contract.d.ts.map +1 -1
  16. package/dist/contracts/automations.contract.js +78 -9
  17. package/dist/contracts/automations.contract.js.map +1 -1
  18. package/dist/contracts/capabilities.contract.d.ts.map +1 -1
  19. package/dist/contracts/capabilities.contract.js +98 -11
  20. package/dist/contracts/capabilities.contract.js.map +1 -1
  21. package/dist/contracts/chores.contract.d.ts.map +1 -1
  22. package/dist/contracts/chores.contract.js +26 -3
  23. package/dist/contracts/chores.contract.js.map +1 -1
  24. package/dist/contracts/ci.contract.d.ts.map +1 -1
  25. package/dist/contracts/ci.contract.js +52 -6
  26. package/dist/contracts/ci.contract.js.map +1 -1
  27. package/dist/contracts/claude.contract.d.ts.map +1 -1
  28. package/dist/contracts/claude.contract.js +44 -5
  29. package/dist/contracts/claude.contract.js.map +1 -1
  30. package/dist/contracts/drafts.contract.d.ts.map +1 -1
  31. package/dist/contracts/drafts.contract.js +26 -3
  32. package/dist/contracts/drafts.contract.js.map +1 -1
  33. package/dist/contracts/endpoints.contract.d.ts.map +1 -1
  34. package/dist/contracts/endpoints.contract.js +17 -2
  35. package/dist/contracts/endpoints.contract.js.map +1 -1
  36. package/dist/contracts/exit.contract.d.ts.map +1 -1
  37. package/dist/contracts/exit.contract.js +62 -7
  38. package/dist/contracts/exit.contract.js.map +1 -1
  39. package/dist/contracts/extensions.contract.d.ts.map +1 -1
  40. package/dist/contracts/extensions.contract.js +124 -15
  41. package/dist/contracts/extensions.contract.js.map +1 -1
  42. package/dist/contracts/git.contract.d.ts.map +1 -1
  43. package/dist/contracts/git.contract.js +384 -43
  44. package/dist/contracts/git.contract.js.map +1 -1
  45. package/dist/contracts/grok.contract.d.ts.map +1 -1
  46. package/dist/contracts/grok.contract.js +25 -3
  47. package/dist/contracts/grok.contract.js.map +1 -1
  48. package/dist/contracts/history.contract.d.ts.map +1 -1
  49. package/dist/contracts/history.contract.js +35 -4
  50. package/dist/contracts/history.contract.js.map +1 -1
  51. package/dist/contracts/intentic.contract.d.ts.map +1 -1
  52. package/dist/contracts/intentic.contract.js +25 -3
  53. package/dist/contracts/intentic.contract.js.map +1 -1
  54. package/dist/contracts/inventory.contract.d.ts.map +1 -1
  55. package/dist/contracts/inventory.contract.js +26 -3
  56. package/dist/contracts/inventory.contract.js.map +1 -1
  57. package/dist/contracts/logs.contract.d.ts.map +1 -1
  58. package/dist/contracts/logs.contract.js +17 -2
  59. package/dist/contracts/logs.contract.js.map +1 -1
  60. package/dist/contracts/loops.contract.d.ts.map +1 -1
  61. package/dist/contracts/loops.contract.js +52 -6
  62. package/dist/contracts/loops.contract.js.map +1 -1
  63. package/dist/contracts/panels.contract.d.ts.map +1 -1
  64. package/dist/contracts/panels.contract.js +26 -3
  65. package/dist/contracts/panels.contract.js.map +1 -1
  66. package/dist/contracts/personas.contract.d.ts.map +1 -1
  67. package/dist/contracts/personas.contract.js +71 -8
  68. package/dist/contracts/personas.contract.js.map +1 -1
  69. package/dist/contracts/ports.contract.d.ts.map +1 -1
  70. package/dist/contracts/ports.contract.js +26 -3
  71. package/dist/contracts/ports.contract.js.map +1 -1
  72. package/dist/contracts/prepush.contract.d.ts.map +1 -1
  73. package/dist/contracts/prepush.contract.js +24 -3
  74. package/dist/contracts/prepush.contract.js.map +1 -1
  75. package/dist/contracts/providers.contract.d.ts.map +1 -1
  76. package/dist/contracts/providers.contract.js +9 -1
  77. package/dist/contracts/providers.contract.js.map +1 -1
  78. package/dist/contracts/public.contract.d.ts.map +1 -1
  79. package/dist/contracts/public.contract.js +26 -3
  80. package/dist/contracts/public.contract.js.map +1 -1
  81. package/dist/contracts/push.contract.d.ts.map +1 -1
  82. package/dist/contracts/push.contract.js +35 -4
  83. package/dist/contracts/push.contract.js.map +1 -1
  84. package/dist/contracts/secrets.contract.d.ts.map +1 -1
  85. package/dist/contracts/secrets.contract.js +43 -5
  86. package/dist/contracts/secrets.contract.js.map +1 -1
  87. package/dist/contracts/sessions.contract.d.ts.map +1 -1
  88. package/dist/contracts/sessions.contract.js +15 -2
  89. package/dist/contracts/sessions.contract.js.map +1 -1
  90. package/dist/contracts/settings.contract.d.ts.map +1 -1
  91. package/dist/contracts/settings.contract.js +43 -5
  92. package/dist/contracts/settings.contract.js.map +1 -1
  93. package/dist/contracts/share.contract.d.ts.map +1 -1
  94. package/dist/contracts/share.contract.js +35 -4
  95. package/dist/contracts/share.contract.js.map +1 -1
  96. package/dist/contracts/skills.contract.d.ts.map +1 -1
  97. package/dist/contracts/skills.contract.js +35 -4
  98. package/dist/contracts/skills.contract.js.map +1 -1
  99. package/dist/contracts/system.contract.d.ts.map +1 -1
  100. package/dist/contracts/system.contract.js +107 -14
  101. package/dist/contracts/system.contract.js.map +1 -1
  102. package/dist/contracts/translator.contract.d.ts.map +1 -1
  103. package/dist/contracts/translator.contract.js +29 -4
  104. package/dist/contracts/translator.contract.js.map +1 -1
  105. package/dist/contracts/usage.contract.d.ts.map +1 -1
  106. package/dist/contracts/usage.contract.js +9 -1
  107. package/dist/contracts/usage.contract.js.map +1 -1
  108. package/dist/contracts/vpn.contract.d.ts.map +1 -1
  109. package/dist/contracts/vpn.contract.js +32 -4
  110. package/dist/contracts/vpn.contract.js.map +1 -1
  111. package/dist/contracts/workflows.contract.d.ts.map +1 -1
  112. package/dist/contracts/workflows.contract.js +70 -8
  113. package/dist/contracts/workflows.contract.js.map +1 -1
  114. package/dist/contracts/workspace.contract.d.ts.map +1 -1
  115. package/dist/contracts/workspace.contract.js +216 -25
  116. package/dist/contracts/workspace.contract.js.map +1 -1
  117. package/dist/events.d.ts.map +1 -1
  118. package/dist/events.js +179 -88
  119. package/dist/events.js.map +1 -1
  120. package/dist/history-state.d.ts.map +1 -1
  121. package/dist/history-state.js +1 -0
  122. package/dist/history-state.js.map +1 -1
  123. package/dist/index.d.ts +1 -0
  124. package/dist/index.d.ts.map +1 -1
  125. package/dist/schemas.d.ts +1 -0
  126. package/dist/schemas.d.ts.map +1 -1
  127. package/dist/schemas.js +2856 -1276
  128. package/dist/schemas.js.map +1 -1
  129. package/package.json +4 -4
  130. package/src/agent-catalog.test.ts +48 -3
  131. package/src/agent-catalog.ts +88 -0
  132. package/src/capability-ledger.test.ts +21 -2
  133. package/src/contracts/activity.contract.ts +18 -2
  134. package/src/contracts/agent.contract.ts +78 -8
  135. package/src/contracts/agents.contract.ts +180 -19
  136. package/src/contracts/automations.contract.ts +80 -9
  137. package/src/contracts/capabilities.contract.ts +106 -11
  138. package/src/contracts/chores.contract.ts +29 -3
  139. package/src/contracts/ci.contract.ts +55 -6
  140. package/src/contracts/claude.contract.ts +47 -5
  141. package/src/contracts/drafts.contract.ts +26 -3
  142. package/src/contracts/endpoints.contract.ts +19 -2
  143. package/src/contracts/exit.contract.ts +69 -7
  144. package/src/contracts/extensions.contract.ts +134 -15
  145. package/src/contracts/git.contract.ts +421 -43
  146. package/src/contracts/grok.contract.ts +26 -3
  147. package/src/contracts/history.contract.ts +37 -4
  148. package/src/contracts/intentic.contract.ts +28 -3
  149. package/src/contracts/inventory.contract.ts +27 -3
  150. package/src/contracts/logs.contract.ts +18 -2
  151. package/src/contracts/loops.contract.ts +58 -6
  152. package/src/contracts/panels.contract.ts +26 -3
  153. package/src/contracts/personas.contract.ts +77 -8
  154. package/src/contracts/ports.contract.ts +27 -3
  155. package/src/contracts/prepush.contract.ts +26 -3
  156. package/src/contracts/providers.contract.ts +10 -1
  157. package/src/contracts/public.contract.ts +29 -3
  158. package/src/contracts/push.contract.ts +37 -4
  159. package/src/contracts/secrets.contract.ts +46 -5
  160. package/src/contracts/sessions.contract.ts +16 -2
  161. package/src/contracts/settings.contract.ts +46 -5
  162. package/src/contracts/share.contract.ts +37 -4
  163. package/src/contracts/skills.contract.ts +39 -4
  164. package/src/contracts/system.contract.ts +119 -14
  165. package/src/contracts/translator.contract.ts +32 -4
  166. package/src/contracts/usage.contract.ts +10 -1
  167. package/src/contracts/vpn.contract.ts +35 -4
  168. package/src/contracts/workflows.contract.ts +77 -8
  169. package/src/contracts/workspace.contract.ts +231 -25
  170. package/src/events.ts +207 -88
  171. package/src/history-state.ts +6 -0
  172. package/src/schemas.ts +3506 -1260
@@ -11,10 +11,43 @@ import { OkSchema, ShareCreateSchema, ShareListSchema, ShareRemoveSchema, ShareU
11
11
  * There is no route to read a share back. The page is the read, it answers on the unauthenticated
12
12
  * `public-<slot>` hostname like every other published file, which is the point of having made one. */
13
13
  export const shareContract = {
14
- list: oc.route({ method: "GET", path: "/share" }).output(ShareListSchema),
14
+ list: oc
15
+ .route({
16
+ method: "GET",
17
+ path: "/share",
18
+ summary: "Conversations published as pages",
19
+ description:
20
+ "Every conversation that has been turned into a read-only page, with its link. There is no call to read one back: the page itself is the read, and it answers to anyone who has the link.",
21
+ })
22
+ .output(ShareListSchema),
15
23
  // 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),
24
+ create: oc
25
+ .route({
26
+ method: "POST",
27
+ path: "/share",
28
+ summary: "Publish a conversation",
29
+ description:
30
+ "Renders a conversation into a page anybody with the link can read, without signing in. Answers with the link, so nothing has to be listed again to find it.",
31
+ })
32
+ .input(ShareCreateSchema)
33
+ .output(SharedConversationSchema),
17
34
  // 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),
35
+ update: oc
36
+ .route({
37
+ method: "POST",
38
+ path: "/share/update",
39
+ summary: "Refresh a published page",
40
+ description: "Re-renders an existing page from the conversation as it stands now. Same link, newer contents.",
41
+ })
42
+ .input(ShareUpdateSchema)
43
+ .output(SharedConversationSchema),
44
+ remove: oc
45
+ .route({
46
+ method: "POST",
47
+ path: "/share/remove",
48
+ summary: "Unpublish a conversation",
49
+ description: "Takes the page down, so the link stops answering.",
50
+ })
51
+ .input(ShareRemoveSchema)
52
+ .output(OkSchema),
20
53
  };
@@ -15,10 +15,45 @@ import { OkSchema, SkillBodySchema, SkillDraftSchema, SkillIdSchema, SkillRemove
15
15
  * `read` is a GET with the id in the query rather than in the path: an id can name an owner
16
16
  * (`extension:intentic.knowledge:knowledge`), and a path template cannot carry those segments. */
17
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),
18
+ list: oc
19
+ .route({
20
+ method: "GET",
21
+ path: "/skills",
22
+ summary: "What the agent knows how to do",
23
+ description:
24
+ "Every skill available here and whether it is switched on, joined from all four places they come from: the owner's own, the settings, plugins a connection installed, and folders inside extensions.",
25
+ })
26
+ .output(SkillsListSchema),
27
+ read: oc
28
+ .route({
29
+ method: "GET",
30
+ path: "/skills/read",
31
+ summary: "Read one skill",
32
+ description:
33
+ "The full text of a single skill. The name travels in the query rather than the address, because a name can carry the owner it came from and that will not fit in a path.",
34
+ })
35
+ .input(SkillIdSchema)
36
+ .output(SkillBodySchema),
20
37
  // Upsert by name: saving over an existing skill rewrites it, which is also how one is renamed (the old name
21
38
  // 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),
39
+ save: oc
40
+ .route({
41
+ method: "POST",
42
+ path: "/skills",
43
+ summary: "Write a skill",
44
+ description:
45
+ "Creates or rewrites a skill by name, and switches it on, because you wrote it in order to use it. Renaming is saving under the new name and deleting the old.",
46
+ })
47
+ .input(SkillDraftSchema)
48
+ .output(OkSchema),
49
+ remove: oc
50
+ .route({
51
+ method: "POST",
52
+ path: "/skills/remove",
53
+ summary: "Delete a skill",
54
+ description:
55
+ "Removes the text and takes it off the enabled list in one step, so a screen never has to sequence two calls and never leaves one half done.",
56
+ })
57
+ .input(SkillRemoveSchema)
58
+ .output(OkSchema),
24
59
  };
@@ -26,33 +26,101 @@ import {
26
26
  // aborts. `clientId` is the tab's per-connection presence key; absent (an older web client) means the
27
27
  // connection simply never joins the roster.
28
28
  export const systemContract = {
29
- info: oc.route({ method: "GET", path: "/info" }).output(InfoSchema),
29
+ info: oc
30
+ .route({
31
+ method: "GET",
32
+ path: "/info",
33
+ summary: "What this sandbox is",
34
+ description:
35
+ "The sandbox's own identity and state: which workspace it holds, which image it runs, what it is called, and the list of calls it actually implements. Start here, because a browser is routinely newer than the sandbox it is talking to and this is how it finds out what is there.",
36
+ })
37
+ .output(InfoSchema),
30
38
  // What the daemon could not read in its own `.intentic/` manifests, a file it fell back on, a key it did
31
39
  // not recognise, an entry it skipped. Its own route rather than a field on /info because it is invalidated
32
40
  // 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),
41
+ manifestProblems: oc
42
+ .route({
43
+ method: "GET",
44
+ path: "/system/manifest-problems",
45
+ summary: "Settings files the sandbox could not read",
46
+ description:
47
+ "Anything the daemon tripped over in its own configuration on disk: a file it had to fall back from, a key it did not recognise, an entry it skipped. Separate from the identity call because it goes stale for a different reason, namely a file changing.",
48
+ })
49
+ .output(ManifestProblemsSchema),
34
50
  // Exchange the request's verified bearer (a Google ID token, or a still-valid session, which makes this
35
51
  // route sliding renewal) for a daemon-minted session, the credential every steady-state call presents.
36
- session: oc.route({ method: "POST", path: "/system/session" }).output(DaemonSessionSchema),
52
+ session: oc
53
+ .route({
54
+ method: "POST",
55
+ path: "/system/session",
56
+ summary: "Trade a sign-in for a session",
57
+ description:
58
+ "Exchanges a verified sign-in, or a session that has not expired yet, for a fresh session the daemon minted. That session is the credential every other call carries, and calling this again with a live one renews it.",
59
+ })
60
+ .output(DaemonSessionSchema),
37
61
  events: oc
38
- .route({ method: "GET", path: "/events" })
62
+ .route({
63
+ method: "GET",
64
+ path: "/events",
65
+ summary: "The live event stream",
66
+ description:
67
+ "A stream held open for as long as you want it, carrying heartbeats so a caller notices the sandbox dying at once, batches of file changes so a tree or an editor can refresh itself, and the roster of who else is looking. Give it an id for this connection to appear in that roster; leave it out and you watch without being seen.",
68
+ })
39
69
  .input(z.object({ clientId: z.string().optional() }))
40
70
  .output(eventIterator(SystemEventSchema)),
41
71
  // A tab's activity self-report (view/session/file/idle), fanned back out to every member on /events.
42
- presence: oc.route({ method: "POST", path: "/system/presence" }).input(PresenceReportSchema).output(OkSchema),
72
+ presence: oc
73
+ .route({
74
+ method: "POST",
75
+ path: "/system/presence",
76
+ summary: "Say what you are looking at",
77
+ description:
78
+ "Reports which view, conversation or file this connection is on, or that it has gone idle. The daemon fans it back out on the event stream so everyone else's roster updates.",
79
+ })
80
+ .input(PresenceReportSchema)
81
+ .output(OkSchema),
43
82
  // Per-account token/cost totals, aggregated from the activity log's turn.completed events.
44
- usage: oc.route({ method: "GET", path: "/system/usage" }).output(UsageSummarySchema),
83
+ usage: oc
84
+ .route({
85
+ method: "GET",
86
+ path: "/system/usage",
87
+ summary: "What has been spent",
88
+ description: "Token and cost totals per account, added up from the record of every finished turn.",
89
+ })
90
+ .output(UsageSummarySchema),
45
91
  // The web-owned tmux sessions behind the terminal tabs. `terminals` enumerates them (the panel rebuilds a tab
46
92
  // per name on load/reload); `killTerminal` destroys one when its tab's close button is clicked. The live I/O
47
93
  // is the separate /system/terminal WebSocket, these are just the control plane. Bearer-authed like the rest
48
94
  // (browser fetch sends the header), unlike the header-less WS route which app.ts exempts.
49
- terminals: oc.route({ method: "GET", path: "/system/terminals" }).output(TerminalsListSchema),
50
- killTerminal: oc.route({ method: "DELETE", path: "/system/terminals/{name}" }).input(TerminalNameParamSchema).output(OkSchema),
95
+ terminals: oc
96
+ .route({
97
+ method: "GET",
98
+ path: "/system/terminals",
99
+ summary: "Open terminals",
100
+ description:
101
+ "The terminal sessions this sandbox is holding, which is what a terminal panel rebuilds its tabs from after a reload. The live typing and output run over a separate socket; this is the list.",
102
+ })
103
+ .output(TerminalsListSchema),
104
+ killTerminal: oc
105
+ .route({
106
+ method: "DELETE",
107
+ path: "/system/terminals/{name}",
108
+ summary: "Close a terminal",
109
+ description: "Destroys one terminal session and whatever was running inside it.",
110
+ })
111
+ .input(TerminalNameParamSchema)
112
+ .output(OkSchema),
51
113
  // One session's pane history as selectable text, the answer to "scroll back and copy that" in a surface
52
114
  // whose live view is a tmux client on the alternate screen, where the scrollback is on the far side of the
53
115
  // socket and the page has nothing to select. See TerminalScrollbackSchema.
54
116
  terminalScrollback: oc
55
- .route({ method: "GET", path: "/system/terminals/{name}/scrollback" })
117
+ .route({
118
+ method: "GET",
119
+ path: "/system/terminals/{name}/scrollback",
120
+ summary: "A terminal's history as plain text",
121
+ description:
122
+ "What has scrolled past in one terminal, as text you can select and copy. The live view is a picture of a screen on the far side of a socket, with nothing in the page to select, so scrolling back and copying is this call rather than a gesture.",
123
+ })
56
124
  .input(TerminalScrollbackQuerySchema)
57
125
  .output(TerminalScrollbackSchema),
58
126
  // The agent's live Chromiums and the pages each has open, the Browsers view's roster, polled while it is on
@@ -60,8 +128,25 @@ export const systemContract = {
60
128
  // separate /system/browser-view WebSocket; this is the control plane, exactly as `terminals` is for tmux.
61
129
  // `closeBrowser` shuts one Chromium down: the agent's next browser tool call then fails as if it had crashed,
62
130
  // which is the honest account of the owner pulling the plug.
63
- browsers: oc.route({ method: "GET", path: "/system/browsers" }).output(BrowsersListSchema),
64
- closeBrowser: oc.route({ method: "DELETE", path: "/system/browsers/{name}" }).input(BrowserNameParamSchema).output(OkSchema),
131
+ browsers: oc
132
+ .route({
133
+ method: "GET",
134
+ path: "/system/browsers",
135
+ summary: "Browsers the agent has open",
136
+ description:
137
+ "Every browser a conversation currently has running and the pages inside each one. The picture of what they are showing comes over a separate socket; this is the roster.",
138
+ })
139
+ .output(BrowsersListSchema),
140
+ closeBrowser: oc
141
+ .route({
142
+ method: "DELETE",
143
+ path: "/system/browsers/{name}",
144
+ summary: "Shut a browser down",
145
+ description:
146
+ "Closes one of the agent's browsers. Its next attempt to use that browser then fails as though it had crashed, which is the honest account of somebody pulling the plug.",
147
+ })
148
+ .input(BrowserNameParamSchema)
149
+ .output(OkSchema),
65
150
  // The agents this sandbox's agents started. SDK subagents and delegated Codex/Grok runs alike (see
66
151
  // SubagentSessionSchema). Same two-route shape as the browsers above, and same division of labour: the list
67
152
  // is polled by the Subagents area while it is on screen and loosely by the rail, so its tile can appear the
@@ -69,9 +154,23 @@ export const systemContract = {
69
154
  // what you watch it through is its TRANSCRIPT, which `subagentTranscript` serves in the one shape every
70
155
  // other transcript route already answers in: live from the parent turn's frame log while it runs, off the
71
156
  // provider's own store once it has finished.
72
- subagents: oc.route({ method: "GET", path: "/system/subagents" }).output(SubagentsListSchema),
157
+ subagents: oc
158
+ .route({
159
+ method: "GET",
160
+ path: "/system/subagents",
161
+ summary: "Helpers the agents have started",
162
+ description:
163
+ "Every helper agent this sandbox's conversations have delegated work to, whichever tool started it, with what each one is doing.",
164
+ })
165
+ .output(SubagentsListSchema),
73
166
  subagentTranscript: oc
74
- .route({ method: "GET", path: "/system/subagents/{id}/transcript" })
167
+ .route({
168
+ method: "GET",
169
+ path: "/system/subagents/{id}/transcript",
170
+ summary: "A helper agent's record",
171
+ description:
172
+ "The full record of one delegated helper, in the same shape as any other conversation. It comes live from the parent turn while the helper works, and from stored history once it has finished.",
173
+ })
75
174
  .input(SubagentIdParamSchema)
76
175
  .output(SessionTranscriptSchema),
77
176
  /* Start, stop, restart, update, rebuild, roll back or remove a sandbox on one of the user's own computers,
@@ -82,7 +181,13 @@ export const systemContract = {
82
181
  * judgement, the machine enforces its own switches and its refusal arrives as the terminal `error` line,
83
182
  * in its own words, naming the control to flip. */
84
183
  manageMachineSandbox: oc
85
- .route({ method: "POST", path: "/system/computers/{id}/sandboxes/{slug}" })
184
+ .route({
185
+ method: "POST",
186
+ path: "/system/computers/{id}/sandboxes/{slug}",
187
+ summary: "Drive a sandbox on one of your own computers",
188
+ description:
189
+ "Start, stop, restart, update, rebuild, roll back or remove a sandbox running on a machine you own, relayed over the connection that machine holds open. The answer is a stream because the slowest of these takes minutes, and it is the same stream whichever you ask for. The daemon adds no opinion: the machine enforces its own permissions and a refusal arrives as the last line, in the machine's words, naming the switch to flip.",
190
+ })
86
191
  .input(MachineSandboxFlowInputSchema)
87
192
  .output(eventIterator(MachineFlowLineSchema)),
88
193
  };
@@ -12,14 +12,42 @@ import { KeyedProviderSchema, OkSchema, TranslatorAccountsSchema, TranslatorComp
12
12
  // URL this sandbox can't receive, so `complete` hands the landing URL to the translator. `connect.flow` tells the
13
13
  // card which mechanic it received without inferring it from whether an optional device code happened to exist.
14
14
  export const translatorContract = {
15
- accounts: oc.route({ method: "GET", path: "/translator/accounts" }).output(TranslatorAccountsSchema),
15
+ accounts: oc
16
+ .route({
17
+ method: "GET",
18
+ path: "/translator/accounts",
19
+ summary: "Subscriptions connected through the translator",
20
+ description:
21
+ "What is signed in per provider. Each provider can hold several accounts at once, and the translator spreads work across them.",
22
+ })
23
+ .output(TranslatorAccountsSchema),
16
24
  connect: oc
17
- .route({ method: "POST", path: "/translator/{provider}/connect" })
25
+ .route({
26
+ method: "POST",
27
+ path: "/translator/{provider}/connect",
28
+ summary: "Start connecting a subscription",
29
+ description:
30
+ "Begins the sign-in for one provider and says which of the two shapes it is: a code you type into a device page, which finishes by itself in the background, or a redirect whose landing address you hand back afterwards.",
31
+ })
18
32
  .input(z.object({ provider: KeyedProviderSchema }))
19
33
  .output(TranslatorStartSchema),
20
- complete: oc.route({ method: "POST", path: "/translator/{provider}/complete" }).input(TranslatorCompleteSchema).output(OkSchema),
34
+ complete: oc
35
+ .route({
36
+ method: "POST",
37
+ path: "/translator/{provider}/complete",
38
+ summary: "Finish a redirect sign-in",
39
+ description:
40
+ "For the providers that redirect somewhere this sandbox cannot receive: hand back the address you landed on and the connection completes.",
41
+ })
42
+ .input(TranslatorCompleteSchema)
43
+ .output(OkSchema),
21
44
  disconnect: oc
22
- .route({ method: "POST", path: "/translator/{provider}/disconnect" })
45
+ .route({
46
+ method: "POST",
47
+ path: "/translator/{provider}/disconnect",
48
+ summary: "Disconnect one subscription",
49
+ description: "Clears a single account by name. Any others under the same provider stay connected.",
50
+ })
23
51
  .input(z.object({ provider: KeyedProviderSchema, name: z.string().min(1) }))
24
52
  .output(OkSchema),
25
53
  };
@@ -6,5 +6,14 @@ import { DayWindowQuerySchema, UsageRollupSchema } from "../schemas.js";
6
6
  // activity log. `rollup` serves every cost/usage panel: it groups by day × provider × account × model, and the
7
7
  // browser re-projects from there (spend per day, cost by model, cache hit rate) without another round trip.
8
8
  export const usageContract = {
9
- rollup: oc.route({ method: "GET", path: "/usage/rollup" }).input(DayWindowQuerySchema).output(UsageRollupSchema),
9
+ rollup: oc
10
+ .route({
11
+ method: "GET",
12
+ path: "/usage/rollup",
13
+ summary: "What was spent, grouped",
14
+ description:
15
+ "The spending record over a range of days, grouped by day, provider, account and model. Everything a cost screen shows is a rearrangement of this one answer, so nothing needs a second call. Read-only: rows are written by the sandbox as turns end, which is what makes it worth trusting.",
16
+ })
17
+ .input(DayWindowQuerySchema)
18
+ .output(UsageRollupSchema),
10
19
  };
@@ -19,17 +19,48 @@ import {
19
19
  // tunnel from a shell and the UI dropping it are the same event, and a daemon restart observes the truth.
20
20
  export const vpnContract = {
21
21
  // Every configured VPN with its live link state, the Status card, the rail indicator, and `vpn list`.
22
- list: oc.route({ method: "GET", path: "/vpn" }).output(VpnListSchema),
22
+ list: oc
23
+ .route({
24
+ method: "GET",
25
+ path: "/vpn",
26
+ summary: "Configured tunnels and which are up",
27
+ description:
28
+ "Every stored VPN with its live link state, read back from the operating system rather than from memory, so a tunnel dropped from a shell and one dropped from a screen look the same here.",
29
+ })
30
+ .output(VpnListSchema),
23
31
  // Dial a stored VPN. Streams the client's progress (auth, then routing) because a dial takes seconds and
24
32
  // can fail with something the user must read, a wrong password, an untrusted gateway certificate, or a
25
33
  // required 2FA code. Idempotent: connecting an already-up tunnel reports it and stops.
26
- connect: oc.route({ method: "POST", path: "/vpn/{id}/connect" }).input(VpnConnectInputSchema).output(eventIterator(IntenticLineSchema)),
34
+ connect: oc
35
+ .route({
36
+ method: "POST",
37
+ path: "/vpn/{id}/connect",
38
+ summary: "Dial a VPN",
39
+ description:
40
+ "Brings a stored tunnel up, streaming the client's progress as it authenticates and then sets up routing. Streamed because a dial takes seconds and can fail with something you have to read: a wrong password, a gateway certificate nobody trusts, a code it wants. Connecting one that is already up simply says so.",
41
+ })
42
+ .input(VpnConnectInputSchema)
43
+ .output(eventIterator(IntenticLineSchema)),
27
44
  // Drop a tunnel. Tolerates an already-down one, "make it not be up" is the contract, not "it was up".
28
- disconnect: oc.route({ method: "POST", path: "/vpn/{id}/disconnect" }).input(VpnIdParamSchema).output(OkSchema),
45
+ disconnect: oc
46
+ .route({
47
+ method: "POST",
48
+ path: "/vpn/{id}/disconnect",
49
+ summary: "Drop a tunnel",
50
+ description: "Takes the tunnel down. One that was already down is fine: the promise is that it is not up afterwards.",
51
+ })
52
+ .input(VpnIdParamSchema)
53
+ .output(OkSchema),
29
54
  // Parse an exported FortiClient configuration into addable connections, so a user with that file fills the
30
55
  // add form by picking a connection instead of re-keying host/port/protocol per tunnel.
31
56
  importForticlient: oc
32
- .route({ method: "POST", path: "/vpn/import-forticlient" })
57
+ .route({
58
+ method: "POST",
59
+ path: "/vpn/import-forticlient",
60
+ summary: "Read connections out of an exported config",
61
+ description:
62
+ "Turns an exported FortiClient configuration into a list of connections you can add, so somebody holding that file picks from a list instead of retyping a host and port for every tunnel.",
63
+ })
33
64
  .input(ForticlientImportInputSchema)
34
65
  .output(ForticlientImportSchema),
35
66
  };
@@ -26,25 +26,68 @@ import {
26
26
  export const workflowsContract = {
27
27
  // Every saved workflow with its run history, newest run first. One route rather than two because the list
28
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),
29
+ list: oc
30
+ .route({
31
+ method: "GET",
32
+ path: "/workflows",
33
+ summary: "Saved workflows and their runs",
34
+ description:
35
+ "Every workflow somebody has designed, each with its own run history, newest first. One answer rather than two, because a workflow that has never been run is the interesting case rather than a mistake.",
36
+ })
37
+ .output(WorkflowsListSchema),
30
38
  /* Create or replace a workflow, with the operation made explicit so an accidental id collision cannot turn
31
39
  * a create into replacement. Refuses a graph that cannot run, a cycle, a `needs` naming a step
32
40
  * that is not there, a step with no way to know it is finished, with the same sentences the designer shows
33
41
  * while you type (see workflowFaults). Validation lives in the contract precisely so those two can never
34
42
  * disagree about what is legal. */
35
- save: oc.route({ method: "POST", path: "/workflows" }).input(WorkflowSaveSchema).output(WorkflowSchema),
43
+ save: oc
44
+ .route({
45
+ method: "POST",
46
+ path: "/workflows",
47
+ summary: "Create or replace a workflow",
48
+ description:
49
+ "Writes a workflow design. Say which of the two you mean, so an id that happens to collide cannot silently overwrite somebody's work. A design that could never run is refused, in the same words the editor shows while you type: a loop in the steps, a step waiting on one that is not there, a step with no way of knowing it is finished.",
50
+ })
51
+ .input(WorkflowSaveSchema)
52
+ .output(WorkflowSchema),
36
53
  // Deleting a workflow does NOT stop a run of it that is in flight, and does not delete its history: the run
37
54
  // 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),
55
+ remove: oc
56
+ .route({
57
+ method: "DELETE",
58
+ path: "/workflows/{id}",
59
+ summary: "Delete a workflow",
60
+ description:
61
+ "Removes the design. A run of it that is already going keeps going and stays readable and stoppable, because a run takes its own copy of the design when it starts.",
62
+ })
63
+ .input(WorkflowIdParamSchema)
64
+ .output(OkSchema),
39
65
  /* Start a run, optionally pointed at a request, the sentence the user typed in the composer, which every
40
66
  * step is handed on top of its own prompt. Every step is recorded `pending` up front, so the graph is
41
67
  * complete from the first frame and a missing node never has to mean two things. Several runs of one
42
68
  * workflow may be in flight at once, they derive different conversation ids, so nothing is shared and
43
69
  * 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),
70
+ run: oc
71
+ .route({
72
+ method: "POST",
73
+ path: "/workflows/{id}/run",
74
+ summary: "Start a workflow",
75
+ description:
76
+ "Kicks a workflow off and answers immediately with the run as recorded; the work carries on without you. Point it at a question and every step gets that on top of its own instructions. Every step is written down as waiting up front, so the picture is complete from the first frame. Several runs of one design can be in flight at once without colliding.",
77
+ })
78
+ .input(WorkflowRunStartSchema)
79
+ .output(WorkflowRunSchema),
45
80
  // Every run across every workflow, newest first, the history the run view opens onto, and the only place a
46
81
  // deleted workflow's runs are still reachable.
47
- runs: oc.route({ method: "GET", path: "/workflows/runs" }).output(WorkflowRunsListSchema),
82
+ runs: oc
83
+ .route({
84
+ method: "GET",
85
+ path: "/workflows/runs",
86
+ summary: "Every workflow run",
87
+ description:
88
+ "All runs across all workflows, newest first. This is also the only place the runs of a deleted workflow are still reachable.",
89
+ })
90
+ .output(WorkflowRunsListSchema),
48
91
  /* Stop a run: nothing that has not started will start, and the steps in flight are CUT OFF where they
49
92
  * are, their turns aborted exactly as /agent/stop aborts one, so whatever they had written stays on
50
93
  * their branches and the step settles as stopped.
@@ -58,7 +101,16 @@ export const workflowsContract = {
58
101
  * was replaced mid-flight. That case used to be refused, which made the stuck run permanent: a Stop that
59
102
  * could not work, a step count that would never move, and no way off the board.
60
103
  */
61
- stopRun: oc.route({ method: "POST", path: "/workflows/runs/{runId}/stop" }).input(WorkflowRunIdParamSchema).output(OkSchema),
104
+ stopRun: oc
105
+ .route({
106
+ method: "POST",
107
+ path: "/workflows/runs/{runId}/stop",
108
+ summary: "Stop a run now",
109
+ description:
110
+ "Nothing further starts, and the steps already going are cut off where they stand. Whatever they had written stays on their branches. Deliberately abrupt rather than letting the current step finish: a step is a whole agent turn, and a stop that kept spending for minutes afterwards is indistinguishable from a button that does nothing. It always ends the run, including one left stranded by a daemon that was replaced mid-flight.",
111
+ })
112
+ .input(WorkflowRunIdParamSchema)
113
+ .output(OkSchema),
62
114
  /* Take an ENDED run off the board, the run's half of `agents.archive`, and the same bargain: nothing is
63
115
  * lost, the checkouts are reclaimed, and `unarchiveRun` puts it all back. It needs an exit of its own
64
116
  * because nothing about a run transitions once it is over, so a failed one would sit in Attention until
@@ -71,8 +123,25 @@ export const workflowsContract = {
71
123
  * route archives what the user named without re-litigating whether each one was ready to go, and a run the
72
124
  * user has archived is exactly that gesture made once for the whole graph.
73
125
  */
74
- archiveRun: oc.route({ method: "POST", path: "/workflows/runs/{runId}/archive" }).input(WorkflowRunIdParamSchema).output(OkSchema),
126
+ archiveRun: oc
127
+ .route({
128
+ method: "POST",
129
+ path: "/workflows/runs/{runId}/archive",
130
+ summary: "Take a finished run off the board",
131
+ description:
132
+ "Nothing is lost and the working copies are reclaimed. Every conversation the run started is put away with it, which is what makes this an archive rather than a dismissal: a step has no card of its own, so merely dropping the run would spill its conversations onto the board at the moment somebody said they were done. Refused while the run is still going.",
133
+ })
134
+ .input(WorkflowRunIdParamSchema)
135
+ .output(OkSchema),
75
136
  // Put an archived run and its sessions back on the board, the inverse of the above and the run's half of
76
137
  // `agents.unarchive`.
77
- unarchiveRun: oc.route({ method: "POST", path: "/workflows/runs/{runId}/unarchive" }).input(WorkflowRunIdParamSchema).output(OkSchema),
138
+ unarchiveRun: oc
139
+ .route({
140
+ method: "POST",
141
+ path: "/workflows/runs/{runId}/unarchive",
142
+ summary: "Bring an archived run back",
143
+ description: "Puts a run and every conversation it started back on the board.",
144
+ })
145
+ .input(WorkflowRunIdParamSchema)
146
+ .output(OkSchema),
78
147
  };