@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
@@ -5,7 +5,31 @@ import { AddInventoryInputSchema, InventoryListSchema, InventoryNameParamSchema
5
5
  // commit it (mirroring an agent edit) and return the full updated list so the UI re-renders from one response.
6
6
  // Deploy-target hosts self-register out-of-band via the daemon's plain /enroll route (connect-host script).
7
7
  export const inventoryContract = {
8
- list: oc.route({ method: "GET", path: "/inventory" }).output(InventoryListSchema),
9
- add: oc.route({ method: "POST", path: "/inventory" }).input(AddInventoryInputSchema).output(InventoryListSchema),
10
- remove: oc.route({ method: "DELETE", path: "/inventory/{name}" }).input(InventoryNameParamSchema).output(InventoryListSchema),
8
+ list: oc
9
+ .route({
10
+ method: "GET",
11
+ path: "/inventory",
12
+ summary: "Machines and services you have declared",
13
+ description: "What the deployment configuration says this setup owns and what it wants provisioned.",
14
+ })
15
+ .output(InventoryListSchema),
16
+ add: oc
17
+ .route({
18
+ method: "POST",
19
+ path: "/inventory",
20
+ summary: "Declare a machine or service",
21
+ description:
22
+ "Writes the entry into the configuration file and commits it, exactly as an agent editing that file by hand would. Answers with the whole updated list, so a screen redraws from one response.",
23
+ })
24
+ .input(AddInventoryInputSchema)
25
+ .output(InventoryListSchema),
26
+ remove: oc
27
+ .route({
28
+ method: "DELETE",
29
+ path: "/inventory/{name}",
30
+ summary: "Undeclare a machine or service",
31
+ description: "Takes the entry back out of the configuration and commits that too. Answers with the whole updated list.",
32
+ })
33
+ .input(InventoryNameParamSchema)
34
+ .output(InventoryListSchema),
11
35
  };
@@ -4,6 +4,22 @@ import { LogReadQuerySchema, LogReadSchema, LogsListSchema } from "../schemas.js
4
4
  // Daemon-owned debug logs (historyRoot/logs): terminal pipe-pane captures, intentic CLI run logs, daemon.log.
5
5
  // Read-only by design, the files are written by the daemon/tmux only, so the record stays trustworthy.
6
6
  export const logsContract = {
7
- list: oc.route({ method: "GET", path: "/logs" }).output(LogsListSchema),
8
- read: oc.route({ method: "GET", path: "/logs/file" }).input(LogReadQuerySchema).output(LogReadSchema),
7
+ list: oc
8
+ .route({
9
+ method: "GET",
10
+ path: "/logs",
11
+ summary: "Logs the sandbox keeps",
12
+ description:
13
+ "Every log file the daemon owns: captured terminal output, command runs, and the daemon's own log. Read-only, because only the sandbox writes them.",
14
+ })
15
+ .output(LogsListSchema),
16
+ read: oc
17
+ .route({
18
+ method: "GET",
19
+ path: "/logs/file",
20
+ summary: "Read part of a log",
21
+ description: "A window of one log file's text. A window rather than the whole thing, because a busy log outgrows any single answer.",
22
+ })
23
+ .input(LogReadQuerySchema)
24
+ .output(LogReadSchema),
9
25
  };
@@ -32,31 +32,83 @@ import {
32
32
  export const loopsContract = {
33
33
  // Every loop this workspace has run, newest first, the record is kept after the loop ends, because "why did
34
34
  // it stop at iteration 4" is the question a loop is read for, and the answer is its iteration history.
35
- list: oc.route({ method: "GET", path: "/loops" }).output(LoopsListSchema),
35
+ list: oc
36
+ .route({
37
+ method: "GET",
38
+ path: "/loops",
39
+ summary: "Every loop that has run",
40
+ description:
41
+ "The loops this workspace has run, newest first, kept after they end. Why it stopped on the fourth round is the question a loop gets read for, and the round-by-round history is the answer.",
42
+ })
43
+ .output(LoopsListSchema),
36
44
  /* Start looping a conversation. Rejects when that conversation is already looping, a second loop on one
37
45
  * agent would have two pumps racing the same worktree and the same turn mutex, and the loser would spend a
38
46
  * turn to discover it.
39
47
  *
40
48
  * The conversation need not exist yet: a loop against a fresh id opens it, exactly as a first chat turn
41
49
  * 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),
50
+ start: oc
51
+ .route({
52
+ method: "POST",
53
+ path: "/loops",
54
+ summary: "Run a conversation until it is done",
55
+ description:
56
+ "Starts repeating a conversation towards a goal and answers straight away with the loop as recorded; the work carries on without you. The conversation need not exist yet, so run this until it passes can be the first thing you ever say to a new agent. A conversation already looping is refused.",
57
+ })
58
+ .input(LoopSchema)
59
+ .output(LoopRecordSchema),
43
60
  /* Stop the loop, leaving the turn in flight alone.
44
61
  *
45
62
  * The split is deliberate and it is the one thing about this route that has to be right: stopping a LOOP
46
63
  * means "do not start another iteration", not "kill what is running". A user watching iteration 6 do good
47
64
  * work should be able to say "this is the last one" without throwing that work away. Killing the turn is
48
65
  * 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),
66
+ stop: oc
67
+ .route({
68
+ method: "POST",
69
+ path: "/loops/{conversationId}/stop",
70
+ summary: "Make this round the last",
71
+ description:
72
+ "Means do not start another round, not stop what is running. Somebody watching the sixth round do good work can say this is the last one without throwing that work away. To cut the current round off as well, stop the conversation too.",
73
+ })
74
+ .input(LoopIdParamSchema)
75
+ .output(OkSchema),
50
76
 
51
77
  // Every saved loop. A literal path segment under /loops rather than a surface of its own, because a saved
52
78
  // 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),
79
+ designs: oc
80
+ .route({
81
+ method: "GET",
82
+ path: "/loops/designs",
83
+ summary: "Saved loop designs",
84
+ description:
85
+ "Loops somebody authored once and can point at a different job each time. A saved loop is the same loop with its goal left blank until you type one, not a different feature.",
86
+ })
87
+ .output(LoopDesignsListSchema),
54
88
  /* Create or replace a saved loop, with the operation explicit so a name collision cannot turn a create into
55
89
  * a replacement. Refuses a design that could never finish, nothing to produce and nothing to check, with
56
90
  * the same sentence `start` refuses an ad-hoc loop for, because it is the same mistake made earlier and
57
91
  * catching it at save time is the whole advantage of saving. */
58
- saveDesign: oc.route({ method: "POST", path: "/loops/designs" }).input(LoopDesignSaveSchema).output(LoopDesignSchema),
92
+ saveDesign: oc
93
+ .route({
94
+ method: "POST",
95
+ path: "/loops/designs",
96
+ summary: "Create or replace a saved loop",
97
+ description:
98
+ "Say which of the two you mean, so a name that happens to collide cannot silently overwrite somebody's work. A design that could never finish, with nothing to produce and nothing to check, is refused in the same words an ad-hoc loop would be: catching that at save time is the whole advantage of saving.",
99
+ })
100
+ .input(LoopDesignSaveSchema)
101
+ .output(LoopDesignSchema),
59
102
  // Deleting a saved loop does NOT stop a loop running from it: a running loop copied the fields it needed
60
103
  // 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),
104
+ removeDesign: oc
105
+ .route({
106
+ method: "DELETE",
107
+ path: "/loops/designs/{id}",
108
+ summary: "Delete a saved loop",
109
+ description:
110
+ "Removes the design. A loop already running from it keeps going on its own terms, because it took a copy of what it needed when it started.",
111
+ })
112
+ .input(LoopDesignIdParamSchema)
113
+ .output(OkSchema),
62
114
  };
@@ -6,7 +6,30 @@ import { OkSchema, PanelRepoParamSchema, PanelsListSchema } from "../schemas.js"
6
6
  // on the global GET /system/terminals (the web app's one terminal panel); the interactive I/O is the
7
7
  // /system/terminal WebSocket.
8
8
  export const panelsContract = {
9
- list: oc.route({ method: "GET", path: "/panels" }).output(PanelsListSchema),
10
- start: oc.route({ method: "POST", path: "/panels/{repo}/start" }).input(PanelRepoParamSchema).output(OkSchema),
11
- stop: oc.route({ method: "POST", path: "/panels/{repo}/stop" }).input(PanelRepoParamSchema).output(OkSchema),
9
+ list: oc
10
+ .route({
11
+ method: "GET",
12
+ path: "/panels",
13
+ summary: "Repos you can run and preview",
14
+ description: "Every repo with whether its dev server is up and what the sandbox worked out about its contents.",
15
+ })
16
+ .output(PanelsListSchema),
17
+ start: oc
18
+ .route({
19
+ method: "POST",
20
+ path: "/panels/{repo}/start",
21
+ summary: "Start a repo's dev server",
22
+ description: "Brings the repo's own runnable app up in a terminal you can attach to, so its preview address starts answering.",
23
+ })
24
+ .input(PanelRepoParamSchema)
25
+ .output(OkSchema),
26
+ stop: oc
27
+ .route({
28
+ method: "POST",
29
+ path: "/panels/{repo}/stop",
30
+ summary: "Stop a repo's dev server",
31
+ description: "Shuts it down and frees the port.",
32
+ })
33
+ .input(PanelRepoParamSchema)
34
+ .output(OkSchema),
12
35
  };
@@ -29,9 +29,26 @@ import {
29
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
30
  * a save that shipped the whole folder would make an edit to one skill capable of deleting another. */
31
31
  export const personasContract = {
32
- list: oc.route({ method: "GET", path: "/personas" }).output(PersonasListSchema),
32
+ list: oc
33
+ .route({
34
+ method: "GET",
35
+ path: "/personas",
36
+ summary: "The characters an agent can wear",
37
+ description:
38
+ "Each persona with the connected accounts it speaks for, what a conversation wearing it is allowed to do, and where it works.",
39
+ })
40
+ .output(PersonasListSchema),
33
41
  // Upsert by id, re-saving the same id edits that card.
34
- save: oc.route({ method: "POST", path: "/personas" }).input(PersonaSchema).output(OkSchema),
42
+ save: oc
43
+ .route({
44
+ method: "POST",
45
+ path: "/personas",
46
+ summary: "Create or edit a persona",
47
+ description:
48
+ "Writes the whole card; sending an id that exists edits it. Nothing is connected, installed or spent by saving one, because a persona only records a decision about accounts that already exist. It is stored as a file you can equally well edit by hand, which is why this writes the card whole rather than patching a field: a round trip through a screen should leave a change a reviewer recognises.",
49
+ })
50
+ .input(PersonaSchema)
51
+ .output(OkSchema),
35
52
  /* Removing a card takes away a persona, never an account: the login it named stays connected and reachable from
36
53
  * every other surface. What it CAN do is orphan a reference, an automation pinned to this id now names a
37
54
  * card that no longer exists, and the resolver reads that as "no accounts at all" rather than "all of
@@ -40,17 +57,69 @@ export const personasContract = {
40
57
  * every other surface. It DOES take the card's kit with it, a folder no card can reach is a folder no list
41
58
  * shows, and leaving the owner's prompt and skills orphaned on disk is worse than deleting what they just
42
59
  * asked to delete. */
43
- remove: oc.route({ method: "DELETE", path: "/personas/{id}" }).input(PersonaIdParamSchema).output(OkSchema),
60
+ remove: oc
61
+ .route({
62
+ method: "DELETE",
63
+ path: "/personas/{id}",
64
+ summary: "Delete a persona",
65
+ description:
66
+ "Takes away the character, never the accounts: every login it named stays connected. Its own prompt and skills go with it, since a folder nothing can reach is worse than deleting what somebody just asked to delete. Anything still pointed at it goes quiet rather than falling back to speaking as everyone.",
67
+ })
68
+ .input(PersonaIdParamSchema)
69
+ .output(OkSchema),
44
70
 
45
71
  // ---- the kit: what this card is told, and the skills only it reaches ----
46
72
 
47
- kit: oc.route({ method: "GET", path: "/personas/{id}/kit" }).input(PersonaIdParamSchema).output(PersonaKitSchema),
73
+ kit: oc
74
+ .route({
75
+ method: "GET",
76
+ path: "/personas/{id}/kit",
77
+ summary: "What one persona carries",
78
+ description:
79
+ "The instructions this persona is given and the skills only its conversations can reach. A different question from what the agent knows generally, with a different answer.",
80
+ })
81
+ .input(PersonaIdParamSchema)
82
+ .output(PersonaKitSchema),
48
83
  // An empty prompt DELETES the file rather than storing a blank one, so "custom with nothing written" is one
49
84
  // 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),
85
+ savePrompt: oc
86
+ .route({
87
+ method: "POST",
88
+ path: "/personas/{id}/prompt",
89
+ summary: "Write a persona's instructions",
90
+ description:
91
+ "Sets what this persona is told. Saving an empty one removes it entirely rather than storing a blank, so the persona simply falls back to the sandbox's own instructions.",
92
+ })
93
+ .input(PersonaPromptSchema)
94
+ .output(OkSchema),
95
+ readSkill: oc
96
+ .route({
97
+ method: "GET",
98
+ path: "/personas/{id}/skills/read",
99
+ summary: "Read one of a persona's skills",
100
+ description: "The full text of a single skill belonging to this persona.",
101
+ })
102
+ .input(PersonaSkillNameSchema)
103
+ .output(PersonaSkillBodySchema),
52
104
  // Upsert by name, like the sandbox's own skills, and with no enabled list to write, because a kit skill is
53
105
  // 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),
106
+ saveSkill: oc
107
+ .route({
108
+ method: "POST",
109
+ path: "/personas/{id}/skills",
110
+ summary: "Write one of a persona's skills",
111
+ description:
112
+ "Creates or replaces a skill by name. There is nothing to switch on: a persona's skill is available exactly when that persona is worn, which is what belonging to it has to mean.",
113
+ })
114
+ .input(PersonaSkillSchema)
115
+ .output(OkSchema),
116
+ removeSkill: oc
117
+ .route({
118
+ method: "POST",
119
+ path: "/personas/{id}/skills/remove",
120
+ summary: "Delete one of a persona's skills",
121
+ description: "Removes a single skill from this persona and leaves the rest of its kit alone.",
122
+ })
123
+ .input(PersonaSkillNameSchema)
124
+ .output(OkSchema),
56
125
  };
@@ -5,7 +5,31 @@ import { OkSchema, PortForwardResultSchema, PortParamSchema, PortsListSchema } f
5
5
  // schemas.ts). `forward` is idempotent, re-forwarding a port returns its existing slot's URL; `unforward`
6
6
  // frees the slot immediately (the hostname keeps resolving, the proxy just stops mapping it).
7
7
  export const portsContract = {
8
- list: oc.route({ method: "GET", path: "/ports" }).output(PortsListSchema),
9
- forward: oc.route({ method: "POST", path: "/ports/forward" }).input(PortParamSchema).output(PortForwardResultSchema),
10
- unforward: oc.route({ method: "POST", path: "/ports/unforward" }).input(PortParamSchema).output(OkSchema),
8
+ list: oc
9
+ .route({
10
+ method: "GET",
11
+ path: "/ports",
12
+ summary: "What is listening inside the sandbox",
13
+ description: "Every port something is answering on, and whether each one is reachable from outside.",
14
+ })
15
+ .output(PortsListSchema),
16
+ forward: oc
17
+ .route({
18
+ method: "POST",
19
+ path: "/ports/forward",
20
+ summary: "Make a port reachable",
21
+ description:
22
+ "Gives one port an address on the outside. Asking twice is harmless: the second call hands back the address the first one made.",
23
+ })
24
+ .input(PortParamSchema)
25
+ .output(PortForwardResultSchema),
26
+ unforward: oc
27
+ .route({
28
+ method: "POST",
29
+ path: "/ports/unforward",
30
+ summary: "Stop exposing a port",
31
+ description: "Frees the slot at once. The address keeps resolving; it simply stops leading anywhere.",
32
+ })
33
+ .input(PortParamSchema)
34
+ .output(OkSchema),
11
35
  };
@@ -11,7 +11,30 @@ import { OkSchema, PrepushRunSchema } from "../schemas.js";
11
11
  // tmux window, so watching it is the terminal's job. `cancel` kills that window; the run settles as `cancelled`
12
12
  // and the push it was gating does not go.
13
13
  export const prepushContract = {
14
- state: oc.route({ method: "GET", path: "/prepush/state" }).output(PrepushRunSchema),
15
- run: oc.route({ method: "POST", path: "/prepush/run" }).output(OkSchema),
16
- cancel: oc.route({ method: "POST", path: "/prepush/cancel" }).output(OkSchema),
14
+ state: oc
15
+ .route({
16
+ method: "GET",
17
+ path: "/prepush/state",
18
+ summary: "How the pre-push check is going",
19
+ description:
20
+ "The verdict, or the progress so far. Nothing is addressed by id here, because there is one working tree and so exactly one check.",
21
+ })
22
+ .output(PrepushRunSchema),
23
+ run: oc
24
+ .route({
25
+ method: "POST",
26
+ path: "/prepush/run",
27
+ summary: "Run the checks before pushing",
28
+ description:
29
+ "Starts the suite the workspace runs before anything leaves the machine, and answers immediately. A suite takes minutes, and a request held open that long dies at the first proxy. It runs in a real terminal, so watch it there and poll for the verdict.",
30
+ })
31
+ .output(OkSchema),
32
+ cancel: oc
33
+ .route({
34
+ method: "POST",
35
+ path: "/prepush/cancel",
36
+ summary: "Stop the pre-push check",
37
+ description: "Kills the run. It settles as cancelled and the push it was gating does not go.",
38
+ })
39
+ .output(OkSchema),
17
40
  };
@@ -19,5 +19,14 @@ import { ModelsSchema, NativeProviderParamSchema } from "../schemas.js";
19
19
  export const providersContract = {
20
20
  // One provider's models (+ its default id), never empty, live discovery with a persisted/seed floor behind
21
21
  // it. Order is the provider's own preference order and is not re-ranked here; see ModelsSchema.
22
- models: oc.route({ method: "GET", path: "/providers/{provider}/models" }).input(NativeProviderParamSchema).output(ModelsSchema),
22
+ models: oc
23
+ .route({
24
+ method: "GET",
25
+ path: "/providers/{provider}/models",
26
+ summary: "Models one provider offers",
27
+ description:
28
+ "Every model this provider serves and which one it defaults to. Never empty: it is discovered live with a stored list behind it. The order is the provider's own preference and is not rearranged here.",
29
+ })
30
+ .input(NativeProviderParamSchema)
31
+ .output(ModelsSchema),
23
32
  };
@@ -8,7 +8,33 @@ import { OkSchema, PublicListSchema, PublishResultSchema, PublishSchema, Unpubli
8
8
  // unauthenticated `public-<slot>` hostname, and a second, authenticated way in would just be the workspace file
9
9
  // API with extra steps.
10
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),
11
+ list: oc
12
+ .route({
13
+ method: "GET",
14
+ path: "/public",
15
+ summary: "What is published to the internet",
16
+ description:
17
+ "Everything currently in the outbox and the address it answers on. There is no call to read a published file back: it is served openly to anyone with the link, which is the entire point of having put it there.",
18
+ })
19
+ .output(PublicListSchema),
20
+ publish: oc
21
+ .route({
22
+ method: "POST",
23
+ path: "/public/publish",
24
+ summary: "Put a file on the internet",
25
+ description:
26
+ "Copies a workspace file or folder into the outbox, where it is served to anyone with the link and no sign-in. Answers with the address.",
27
+ })
28
+ .input(PublishSchema)
29
+ .output(PublishResultSchema),
30
+ unpublish: oc
31
+ .route({
32
+ method: "POST",
33
+ path: "/public/unpublish",
34
+ summary: "Take something off the internet",
35
+ description:
36
+ "Withdraws one published entry. When the last one goes, the outbox goes with it, so its existing at all always means something is published.",
37
+ })
38
+ .input(UnpublishSchema)
39
+ .output(OkSchema),
14
40
  };
@@ -11,8 +11,41 @@ import { OkSchema, PushChannelIdSchema, PushChannelSchema, PushConfigQuerySchema
11
11
  // (device permission, service-worker or shell registration, the daemon's key, the push service itself), a
12
12
  // button that proves the whole chain end-to-end is worth more than any amount of status rendering.
13
13
  export const pushContract = {
14
- config: oc.route({ method: "GET", path: "/push/config" }).input(PushConfigQuerySchema).output(PushConfigSchema),
15
- subscribe: oc.route({ method: "POST", path: "/push/subscribe" }).input(PushChannelSchema).output(OkSchema),
16
- unsubscribe: oc.route({ method: "POST", path: "/push/unsubscribe" }).input(PushChannelIdSchema).output(OkSchema),
17
- test: oc.route({ method: "POST", path: "/push/test" }).output(PushTestSchema),
14
+ config: oc
15
+ .route({
16
+ method: "GET",
17
+ path: "/push/config",
18
+ summary: "What a device needs to subscribe",
19
+ description: "The public key and settings a browser or app needs before it can register for notifications from this sandbox.",
20
+ })
21
+ .input(PushConfigQuerySchema)
22
+ .output(PushConfigSchema),
23
+ subscribe: oc
24
+ .route({
25
+ method: "POST",
26
+ path: "/push/subscribe",
27
+ summary: "Send notifications to this device",
28
+ description:
29
+ "Registers one device. The sandbox only interrupts you on the three moments where attention is genuinely wanted: a turn has finished, the agent is stuck on a question, and something is waiting for approval.",
30
+ })
31
+ .input(PushChannelSchema)
32
+ .output(OkSchema),
33
+ unsubscribe: oc
34
+ .route({
35
+ method: "POST",
36
+ path: "/push/unsubscribe",
37
+ summary: "Stop notifying a device",
38
+ description: "Removes one registered device. Others keep receiving.",
39
+ })
40
+ .input(PushChannelIdSchema)
41
+ .output(OkSchema),
42
+ test: oc
43
+ .route({
44
+ method: "POST",
45
+ path: "/push/test",
46
+ summary: "Send a test notification",
47
+ description:
48
+ "Proves the whole chain end to end. Worth having, because there are four separate places a notification can be lost that nobody can inspect from the outside: the device's permission, its registration, the sandbox's key, and the delivery service.",
49
+ })
50
+ .output(PushTestSchema),
18
51
  };
@@ -7,9 +7,50 @@ import { OkSchema, SecretInventorySchema, SecretKeyParamSchema, SecretKeysSchema
7
7
  // values). `reveal` is the single value-returning route, owner-only, POST so the key never sits in a URL.
8
8
  // set/remove/list/reveal refuse until DevOps has scaffolded the desired-state repo; inventory always answers.
9
9
  export const secretsContract = {
10
- set: oc.route({ method: "POST", path: "/secrets" }).input(SecretSetSchema).output(OkSchema),
11
- list: oc.route({ method: "GET", path: "/secrets" }).output(SecretKeysSchema),
12
- remove: oc.route({ method: "DELETE", path: "/secrets/{key}" }).input(SecretKeyParamSchema).output(OkSchema),
13
- inventory: oc.route({ method: "GET", path: "/secrets/inventory" }).output(SecretInventorySchema),
14
- reveal: oc.route({ method: "POST", path: "/secrets/reveal" }).input(SecretKeyParamSchema).output(SecretRevealSchema),
10
+ set: oc
11
+ .route({
12
+ method: "POST",
13
+ path: "/secrets",
14
+ summary: "Store a secret",
15
+ description:
16
+ "Writes one name and value into the sandbox's own store, where running processes pick it up without a restart. Refused until the sandbox has somewhere to keep them.",
17
+ })
18
+ .input(SecretSetSchema)
19
+ .output(OkSchema),
20
+ list: oc
21
+ .route({
22
+ method: "GET",
23
+ path: "/secrets",
24
+ summary: "Names of the stored secrets",
25
+ description: "Which secrets exist here. Names only, never values.",
26
+ })
27
+ .output(SecretKeysSchema),
28
+ remove: oc
29
+ .route({
30
+ method: "DELETE",
31
+ path: "/secrets/{key}",
32
+ summary: "Delete a secret",
33
+ description: "Removes one by name.",
34
+ })
35
+ .input(SecretKeyParamSchema)
36
+ .output(OkSchema),
37
+ inventory: oc
38
+ .route({
39
+ method: "GET",
40
+ path: "/secrets/inventory",
41
+ summary: "Every secret this sandbox holds, from everywhere",
42
+ description:
43
+ "One view across all the places secrets live here: what exists, where it came from and whether it is working. Never any values. This one always answers, even before there is a store to write to.",
44
+ })
45
+ .output(SecretInventorySchema),
46
+ reveal: oc
47
+ .route({
48
+ method: "POST",
49
+ path: "/secrets/reveal",
50
+ summary: "Show one secret's value",
51
+ description:
52
+ "The only call that hands a value back, and it is for the owner alone. Sent as a body rather than in the address, so the name never ends up in a log or a browser's history.",
53
+ })
54
+ .input(SecretKeyParamSchema)
55
+ .output(SecretRevealSchema),
15
56
  };
@@ -13,8 +13,22 @@ import { SessionIdParamSchema, SessionsListSchema } from "../schemas.js";
13
13
  // a switch either of them ignored would show as one field returning two different match sets.
14
14
  export const sessionsContract = {
15
15
  list: oc
16
- .route({ method: "GET", path: "/sessions" })
16
+ .route({
17
+ method: "GET",
18
+ path: "/sessions",
19
+ summary: "Past conversations in this workspace",
20
+ description:
21
+ "Summaries for a history menu, filtered when you pass a search. Covers conversations that worked in their own private copies too, so nothing is hidden just because it happened on a branch.",
22
+ })
17
23
  .input(z.object({ query: z.string().optional(), caseSensitive: z.stringbool().optional() }))
18
24
  .output(SessionsListSchema),
19
- get: oc.route({ method: "GET", path: "/sessions/{id}" }).input(SessionIdParamSchema).output(SessionTranscriptSchema),
25
+ get: oc
26
+ .route({
27
+ method: "GET",
28
+ path: "/sessions/{id}",
29
+ summary: "Read one past conversation",
30
+ description: "The full record of a single conversation, restored for display.",
31
+ })
32
+ .input(SessionIdParamSchema)
33
+ .output(SessionTranscriptSchema),
20
34
  };
@@ -17,12 +17,53 @@ import {
17
17
  // read out of the installed CLI, so the settings page can SHOW the prompt behind a mode instead of asking the
18
18
  // user to trust a description of it, and can fork either into a custom one.
19
19
  export const settingsContract = {
20
- get: oc.route({ method: "GET", path: "/settings" }).output(SandboxSettingsSchema),
21
- set: oc.route({ method: "POST", path: "/settings" }).input(SandboxSettingsSchema).output(OkSchema),
22
- savings: oc.route({ method: "GET", path: "/settings/savings" }).input(DayWindowQuerySchema).output(SavingsReportSchema),
23
- builtinPrompt: oc.route({ method: "GET", path: "/settings/system-prompt/{base}" }).input(BuiltinPromptSchema).output(BuiltinPromptTextSchema),
20
+ get: oc
21
+ .route({
22
+ method: "GET",
23
+ path: "/settings",
24
+ summary: "How this sandbox is configured",
25
+ description: "Every setting that governs how agents behave here, with the defaults filled in for anything nobody has chosen.",
26
+ })
27
+ .output(SandboxSettingsSchema),
28
+ set: oc
29
+ .route({
30
+ method: "POST",
31
+ path: "/settings",
32
+ summary: "Change the sandbox settings",
33
+ description: "Writes the settings whole, so send the complete object rather than the fields you changed.",
34
+ })
35
+ .input(SandboxSettingsSchema)
36
+ .output(OkSchema),
37
+ savings: oc
38
+ .route({
39
+ method: "GET",
40
+ path: "/settings/savings",
41
+ summary: "What the token-saving measures were worth",
42
+ description:
43
+ "Measured rather than estimated: what each mechanism actually saved over a range of days. The same day range the spending ledger takes, so one calendar filters both.",
44
+ })
45
+ .input(DayWindowQuerySchema)
46
+ .output(SavingsReportSchema),
47
+ builtinPrompt: oc
48
+ .route({
49
+ method: "GET",
50
+ path: "/settings/system-prompt/{base}",
51
+ summary: "Read a built-in system prompt",
52
+ description:
53
+ "The actual text behind one of the built-in modes, so a settings screen can show the prompt instead of asking anyone to trust a description of it, and so either can be forked into a custom one.",
54
+ })
55
+ .input(BuiltinPromptSchema)
56
+ .output(BuiltinPromptTextSchema),
24
57
  // When each rule last did something, keyed by rule id. Its own route rather than a field on the settings
25
58
  // object because a firing is not an edit: folding it in would make every push a settings write, and would
26
59
  // 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),
60
+ firings: oc
61
+ .route({
62
+ method: "GET",
63
+ path: "/settings/rule-firings",
64
+ summary: "When each rule last did something",
65
+ description:
66
+ "A separate read rather than a field on the settings, because a rule firing is not somebody editing anything: folding it in would turn every firing into a settings write and put a self-changing value inside the object a screen edits.",
67
+ })
68
+ .output(RuleFiringsSchema),
28
69
  };