@huynle/hub 0.10.1 → 0.11.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 (129) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +36 -8
  3. package/package.json +1 -1
  4. package/packages/claude-code/package.json +1 -1
  5. package/packages/claude-code/src/sessions.ts +4 -1
  6. package/packages/cli/dist/hub-cli.js +851 -466
  7. package/packages/cli/package.json +1 -1
  8. package/packages/cli/src/cli.ts +12 -7
  9. package/packages/cli/src/decisions.ts +52 -4
  10. package/packages/cli/src/format.ts +9 -0
  11. package/packages/cli/src/grants-mcp.ts +65 -0
  12. package/packages/cli/src/mcp-operations.ts +49 -0
  13. package/packages/cli/src/mcp.ts +26 -4
  14. package/packages/cli/src/subscriptions-mcp.ts +76 -0
  15. package/packages/cli/src/team-intake-mcp.ts +2 -1
  16. package/packages/cli/src/teams-mcp.ts +9 -3
  17. package/packages/codex/package.json +1 -1
  18. package/packages/codex/src/sessions.ts +14 -3
  19. package/packages/node/package.json +1 -1
  20. package/packages/preset/package.json +1 -1
  21. package/packages/protocol/package.json +1 -1
  22. package/packages/protocol/schema/api/v1/_root.json +119 -0
  23. package/packages/protocol/schema/api/v1/account.json +419 -0
  24. package/packages/protocol/schema/api/v1/decisions.json +635 -0
  25. package/packages/protocol/schema/api/v1/discovery.json +110 -0
  26. package/packages/protocol/schema/api/v1/events.json +679 -0
  27. package/packages/protocol/schema/api/v1/session-actions.json +660 -0
  28. package/packages/protocol/schema/api/v1/sessions.json +2055 -0
  29. package/packages/protocol/schema/api/v1/speech.json +492 -0
  30. package/packages/protocol/schema/api/v1/unspecified.json +282 -0
  31. package/packages/protocol/schema/app-delivery/v1.schema.json +67 -5
  32. package/packages/protocol/schema/app-front/v1.schema.json +7 -3
  33. package/packages/protocol/src/api-v1.ts +8 -0
  34. package/packages/protocol/src/index.ts +2 -0
  35. package/packages/protocol/src/teams.ts +16 -0
  36. package/packages/protocol/src/voice-recording.ts +39 -2
  37. package/packages/runtime-openshell/image/entry.sh +3 -1
  38. package/packages/runtime-openshell/image/start.sh +3 -1
  39. package/packages/runtime-openshell/package.json +1 -1
  40. package/packages/runtime-sdk/package.json +1 -1
  41. package/packages/sdk/package.json +1 -1
  42. package/packages/sdk/src/app-front.ts +2 -2
  43. package/packages/sdk/src/ask-fields.ts +76 -0
  44. package/packages/sdk/src/client.ts +24 -2
  45. package/packages/sdk/src/envelope.ts +3 -2
  46. package/packages/sdk/src/index.ts +1 -0
  47. package/packages/sdk/src/team-intake.ts +6 -0
  48. package/packages/sdk/src/teams.ts +4 -0
  49. package/packages/sdk/src/types.ts +36 -4
  50. package/packages/sdk/src/wake.ts +9 -4
  51. package/packages/spoke-sdk/package.json +1 -1
  52. package/src/central/agent/agent-limits.ts +185 -0
  53. package/src/central/agent/app-decisions.ts +6 -5
  54. package/src/central/agent/decisions.ts +136 -9
  55. package/src/central/agent/delivery.ts +164 -22
  56. package/src/central/agent/errors.ts +2 -1
  57. package/src/central/agent/events.ts +20 -2
  58. package/src/central/agent/grant-routes.ts +14 -1
  59. package/src/central/agent/grants.ts +23 -0
  60. package/src/central/agent/launch-engine.ts +9 -6
  61. package/src/central/agent/message-limits.ts +6 -0
  62. package/src/central/agent/message-routes.ts +33 -6
  63. package/src/central/agent/messages.ts +7 -0
  64. package/src/central/agent/placement.ts +4 -2
  65. package/src/central/agent/read-routes.ts +5 -0
  66. package/src/central/agent/routes.ts +13 -0
  67. package/src/central/agent/subscriptions.ts +15 -4
  68. package/src/central/api-v1.ts +115 -0
  69. package/src/central/artifacts/store.ts +4 -2
  70. package/src/central/concierge/board-results.ts +2 -1
  71. package/src/central/concierge/notifier.ts +4 -2
  72. package/src/central/config.ts +24 -3
  73. package/src/central/health/classify.ts +209 -0
  74. package/src/central/http.ts +380 -36
  75. package/src/central/recording/export.ts +57 -3
  76. package/src/central/recording/index.ts +236 -17
  77. package/src/central/recording/ingest.ts +11 -2
  78. package/src/central/recording/routes.ts +61 -7
  79. package/src/central/recording/schema.ts +68 -3
  80. package/src/central/recording/settings.ts +19 -5
  81. package/src/central/recording/store.ts +19 -0
  82. package/src/central/recording/taps.ts +45 -1
  83. package/src/central/registry.ts +34 -3
  84. package/src/central/speech/openai.ts +4 -4
  85. package/src/central/speech/openrouter.ts +4 -4
  86. package/src/central/speech/provider.ts +32 -1
  87. package/src/central/speech/service.ts +6 -5
  88. package/src/central/teams/budgets.ts +144 -0
  89. package/src/central/teams/hop-stalls.ts +80 -0
  90. package/src/central/teams/intake.ts +38 -6
  91. package/src/central/teams/involvement.ts +95 -0
  92. package/src/central/teams/item-decisions.ts +60 -0
  93. package/src/central/teams/item-eligibility.ts +35 -0
  94. package/src/central/teams/item-stalls.ts +87 -0
  95. package/src/central/teams/loops.ts +40 -0
  96. package/src/central/teams/policy.ts +10 -0
  97. package/src/central/teams/progress.ts +154 -0
  98. package/src/central/teams/routes.ts +2 -0
  99. package/src/central/teams/seats.ts +3 -3
  100. package/src/central/teams/staffing.ts +5 -0
  101. package/src/central/teams/store.ts +20 -1
  102. package/src/central/teams/validation.ts +16 -2
  103. package/src/central/workspaces/coordinator.ts +3 -1
  104. package/src/spoke/hub-tool.ts +26 -11
  105. package/web/dist/assets/{ArtifactPage-CsPjam8D.js → ArtifactPage-TbFJi59n.js} +1 -1
  106. package/web/dist/assets/{ArtifactPane-B9weO8L6.js → ArtifactPane-7D-M-Ich.js} +1 -1
  107. package/web/dist/assets/{ArtifactWorkbench-DzKhMs3l.js → ArtifactWorkbench-DlCLEjut.js} +1 -1
  108. package/web/dist/assets/{Diagram-CuRIffka.js → Diagram-PvOPetOI.js} +1 -1
  109. package/web/dist/assets/{Library-rv5DGtrm.js → Library-Cdi83pOV.js} +2 -2
  110. package/web/dist/assets/{ProfilesDestination-DUuQ9jmd.js → ProfilesDestination-cryC7P6j.js} +1 -1
  111. package/web/dist/assets/TeamsPage-B4xrlGhp.js +1 -0
  112. package/web/dist/assets/Watchtower-60B1JX_J.js +2 -0
  113. package/web/dist/assets/{artifacts-But87Jfo.js → artifacts-DlQEYmDl.js} +1 -1
  114. package/web/dist/assets/index-DA0gKIzv.js +33 -0
  115. package/web/dist/assets/{index-rW6LKQj1.css → index-Dyq-jBeE.css} +1 -1
  116. package/web/dist/assets/live-B2IfKm37.js +5 -0
  117. package/web/dist/assets/{live-BUofsQpz.css → live-De-kd3eW.css} +1 -1
  118. package/web/dist/assets/{pane-layout-BW0RdX5a.js → pane-layout-BAuu_pA3.js} +1 -1
  119. package/web/dist/assets/{renderer2d-DyANQ1WS.js → renderer2d-CsZnF3_o.js} +1 -1
  120. package/web/dist/assets/{renderer3d-vYYKFAJq.js → renderer3d-BIMA0wFn.js} +2 -2
  121. package/web/dist/assets/{safe-area-B2Bk__FK.js → safe-area-dtrnw9NF.js} +1 -1
  122. package/web/dist/assets/{silero-worker-e802lB5T.js → silero-worker-CC2etKS3.js} +1 -1
  123. package/web/dist/assets/{source-C-De8L3s.js → source-GYayxUxS.js} +1 -1
  124. package/web/dist/assets/{toast-CXTwTvDw.js → toast-q818mjUF.js} +1 -1
  125. package/web/dist/index.html +5 -5
  126. package/web/dist/assets/TeamsPage-D_17MkvN.js +0 -1
  127. package/web/dist/assets/Watchtower-DlrNOJ7X.js +0 -2
  128. package/web/dist/assets/index-bX8B6NLo.js +0 -33
  129. package/web/dist/assets/live-BGLA-peO.js +0 -5
@@ -0,0 +1,282 @@
1
+ [
2
+ "DELETE /api/admin/devices/:id",
3
+ "DELETE /api/admin/machines/:id",
4
+ "DELETE /api/agent/app-clients/:id",
5
+ "DELETE /api/agent/app-tokens/:id",
6
+ "DELETE /api/agent/artifact-host/:cap",
7
+ "DELETE /api/agent/artifacts/:id",
8
+ "DELETE /api/agent/connections/:id",
9
+ "DELETE /api/agent/grants/:id",
10
+ "DELETE /api/agent/grants/mine/:id",
11
+ "DELETE /api/agent/launches/:id",
12
+ "DELETE /api/agent/sessions/:addr/profile",
13
+ "DELETE /api/agent/subscriptions/:id",
14
+ "DELETE /api/agent/teams/:id/draft",
15
+ "DELETE /api/agent/teams/:id/draft/edges",
16
+ "DELETE /api/agent/teams/:id/draft/roles/:role",
17
+ "DELETE /api/agent/teams/:id/staffing/:staffing",
18
+ "DELETE /api/agent/webhooks/:id",
19
+ "DELETE /api/agent/workspaces/:id/bindings",
20
+ "DELETE /api/agent/workspaces/:id/members/:member",
21
+ "DELETE /api/machines/:machine",
22
+ "DELETE /api/recordings",
23
+ "DELETE /api/recordings/:id",
24
+ "GET /api/activity/mine",
25
+ "GET /api/admin/agents/acting",
26
+ "GET /api/admin/audit",
27
+ "GET /api/admin/machines",
28
+ "GET /api/admin/users",
29
+ "GET /api/agent/acting",
30
+ "GET /api/agent/agent-limits",
31
+ "GET /api/agent/app-clients",
32
+ "GET /api/agent/app-tokens",
33
+ "GET /api/agent/artifacts",
34
+ "GET /api/agent/artifacts/:id",
35
+ "GET /api/agent/artifacts/:id/jobs",
36
+ "GET /api/agent/artifacts/:id/jobs/:job",
37
+ "GET /api/agent/artifacts/:id/jobs/:job/context",
38
+ "GET /api/agent/artifacts/:id/jobs/:job/proposal",
39
+ "GET /api/agent/artifacts/:id/revisions",
40
+ "GET /api/agent/artifacts/:id/revisions/:rev",
41
+ "GET /api/agent/artifacts/:id/revisions/:rev/blobs/:hash",
42
+ "GET /api/agent/audit",
43
+ "GET /api/agent/concierge",
44
+ "GET /api/agent/concierge/arm",
45
+ "GET /api/agent/concierge/board",
46
+ "GET /api/agent/concierge/decisions",
47
+ "GET /api/agent/concierge/decisions/:id",
48
+ "GET /api/agent/concierge/machines/:id/models",
49
+ "GET /api/agent/concierge/notes",
50
+ "GET /api/agent/concierge/sessions/:addr/forms/:form",
51
+ "GET /api/agent/concierge/watchlist",
52
+ "GET /api/agent/connections",
53
+ "GET /api/agent/decisions",
54
+ "GET /api/agent/decisions/:id",
55
+ "GET /api/agent/events",
56
+ "GET /api/agent/grants",
57
+ "GET /api/agent/grants/:id",
58
+ "GET /api/agent/grants/mine",
59
+ "GET /api/agent/grants/mine/:id",
60
+ "GET /api/agent/graph",
61
+ "GET /api/agent/graph/edges/:id",
62
+ "GET /api/agent/graph/nodes/:id",
63
+ "GET /api/agent/inbox",
64
+ "GET /api/agent/launch-nodes",
65
+ "GET /api/agent/launches",
66
+ "GET /api/agent/launches/:id",
67
+ "GET /api/agent/messages/:id",
68
+ "GET /api/agent/messaging",
69
+ "GET /api/agent/profiles",
70
+ "GET /api/agent/profiles/:id",
71
+ "GET /api/agent/profiles/:id/usage",
72
+ "GET /api/agent/profiles/:id/versions",
73
+ "GET /api/agent/profiles/:id/versions/:version",
74
+ "GET /api/agent/profiles/:id/versions/:version/skills/:name",
75
+ "GET /api/agent/sessions",
76
+ "GET /api/agent/sessions/:addr",
77
+ "GET /api/agent/sessions/:addr/context-cards",
78
+ "GET /api/agent/sessions/:addr/intent",
79
+ "GET /api/agent/sessions/:addr/log",
80
+ "GET /api/agent/sessions/:addr/profile",
81
+ "GET /api/agent/sessions/:addr/profile/skills/:name",
82
+ "GET /api/agent/sessions/:addr/stream",
83
+ "GET /api/agent/side-chats/:chat",
84
+ "GET /api/agent/status",
85
+ "GET /api/agent/subscriptions",
86
+ "GET /api/agent/subscriptions/:id",
87
+ "GET /api/agent/team-settings",
88
+ "GET /api/agent/teams",
89
+ "GET /api/agent/teams/:id",
90
+ "GET /api/agent/teams/:id/diff",
91
+ "GET /api/agent/teams/:id/draft",
92
+ "GET /api/agent/teams/:id/intake",
93
+ "GET /api/agent/teams/:id/intake/:intake",
94
+ "GET /api/agent/teams/:id/roster",
95
+ "GET /api/agent/teams/:id/staffing",
96
+ "GET /api/agent/teams/:id/status",
97
+ "GET /api/agent/teams/:id/versions",
98
+ "GET /api/agent/teams/:id/versions/:version",
99
+ "GET /api/agent/watch",
100
+ "GET /api/agent/webhooks",
101
+ "GET /api/agent/webhooks/:id",
102
+ "GET /api/agent/who",
103
+ "GET /api/agent/whoami",
104
+ "GET /api/agent/workspaces",
105
+ "GET /api/agent/workspaces/:id",
106
+ "GET /api/agent/workspaces/:id/assignments",
107
+ "GET /api/agent/workspaces/:id/assignments/:assignment",
108
+ "GET /api/agent/workspaces/:id/bindings",
109
+ "GET /api/agent/workspaces/:id/board",
110
+ "GET /api/agent/workspaces/:id/checkpoints",
111
+ "GET /api/agent/workspaces/:id/master",
112
+ "GET /api/agent/workspaces/:id/members",
113
+ "GET /api/agent/workspaces/:id/side-chats",
114
+ "GET /api/agent/workspaces/:id/side-chats/:chat",
115
+ "GET /api/agent/workspaces/:id/supervision",
116
+ "GET /api/cli-logins/:code",
117
+ "GET /api/connect/flows/:flow",
118
+ "GET /api/context-cards/primary",
119
+ "GET /api/enrollments",
120
+ "GET /api/intents/mine",
121
+ "GET /api/media/remote",
122
+ "GET /api/media/unfurl",
123
+ "GET /api/push/key",
124
+ "GET /api/push/prefs",
125
+ "GET /api/recordings",
126
+ "GET /api/recordings/:id",
127
+ "GET /api/recordings/:id/clips/:n",
128
+ "GET /api/recordings/:id/export",
129
+ "GET /api/recordings/:id/replies/:n",
130
+ "GET /api/recordings/:id/segments/:n",
131
+ "GET /api/recordings/export",
132
+ "GET /api/sessions/:session/context",
133
+ "GET /api/sessions/:session/context-cards",
134
+ "GET /api/sessions/:session/diff",
135
+ "GET /api/sessions/:session/model-prefs",
136
+ "GET /api/sessions/:session/models",
137
+ "GET /api/sessions/:session/revert/preview",
138
+ "GET /api/sessions/:session/summary",
139
+ "GET /api/settings/agent-limits",
140
+ "GET /api/settings/context-cards",
141
+ "GET /api/settings/media",
142
+ "GET /api/settings/recording",
143
+ "GET /api/settings/speech/default",
144
+ "GET /api/settings/speech/models",
145
+ "GET /api/teams/:id/hops",
146
+ "PATCH /api/agent/app-clients/:id",
147
+ "PATCH /api/agent/artifacts/:id",
148
+ "PATCH /api/agent/profiles/:id",
149
+ "PATCH /api/agent/teams/:id",
150
+ "PATCH /api/agent/workspaces/:id",
151
+ "PATCH /api/agent/workspaces/:id/assignments/:assignment",
152
+ "PATCH /api/agent/workspaces/:id/members/:member",
153
+ "POST /api/agent/app-clients",
154
+ "POST /api/agent/app-clients/:id/secret",
155
+ "POST /api/agent/app-tokens",
156
+ "POST /api/agent/artifacts",
157
+ "POST /api/agent/artifacts/:id/archive",
158
+ "POST /api/agent/artifacts/:id/jobs",
159
+ "POST /api/agent/artifacts/:id/jobs/:job/apply",
160
+ "POST /api/agent/artifacts/:id/jobs/:job/cancel",
161
+ "POST /api/agent/artifacts/:id/jobs/:job/result",
162
+ "POST /api/agent/artifacts/:id/keep",
163
+ "POST /api/agent/artifacts/:id/revisions",
164
+ "POST /api/agent/artifacts/:id/revisions/:rev/host",
165
+ "POST /api/agent/artifacts/:id/unarchive",
166
+ "POST /api/agent/artifacts/blobs",
167
+ "POST /api/agent/ask",
168
+ "POST /api/agent/concierge/arm",
169
+ "POST /api/agent/concierge/disable",
170
+ "POST /api/agent/concierge/enable",
171
+ "POST /api/agent/concierge/grant-requests",
172
+ "POST /api/agent/concierge/proposals",
173
+ "POST /api/agent/concierge/restart",
174
+ "POST /api/agent/concierge/watchlist/mute",
175
+ "POST /api/agent/concierge/watchlist/unmute",
176
+ "POST /api/agent/concierge/watchlist/unwatch",
177
+ "POST /api/agent/concierge/watchlist/watch",
178
+ "POST /api/agent/decisions/:id/answer",
179
+ "POST /api/agent/decline",
180
+ "POST /api/agent/grants",
181
+ "POST /api/agent/grants/requests",
182
+ "POST /api/agent/launches",
183
+ "POST /api/agent/msg",
184
+ "POST /api/agent/mute",
185
+ "POST /api/agent/name",
186
+ "POST /api/agent/profiles",
187
+ "POST /api/agent/profiles/:id/versions",
188
+ "POST /api/agent/profiles/:id/versions/:version/commands/:name/render",
189
+ "POST /api/agent/profiles/import",
190
+ "POST /api/agent/reply",
191
+ "POST /api/agent/roles/claim",
192
+ "POST /api/agent/roles/release",
193
+ "POST /api/agent/sessions/:addr/answer",
194
+ "POST /api/agent/sessions/:addr/approve",
195
+ "POST /api/agent/sessions/:addr/conversation",
196
+ "POST /api/agent/sessions/:addr/deny",
197
+ "POST /api/agent/sessions/:addr/interrupt",
198
+ "POST /api/agent/sessions/:addr/profile/commands/:name/render",
199
+ "POST /api/agent/sessions/:addr/prompt",
200
+ "POST /api/agent/sessions/:addr/stop",
201
+ "POST /api/agent/sessions/:addr/utterance",
202
+ "POST /api/agent/sessions/archive-inactive",
203
+ "POST /api/agent/speech/speak",
204
+ "POST /api/agent/speech/transcribe",
205
+ "POST /api/agent/subscriptions",
206
+ "POST /api/agent/teams",
207
+ "POST /api/agent/teams/:id/archive",
208
+ "POST /api/agent/teams/:id/intake",
209
+ "POST /api/agent/teams/:id/intake/:intake/status",
210
+ "POST /api/agent/teams/:id/pause",
211
+ "POST /api/agent/teams/:id/publish",
212
+ "POST /api/agent/teams/:id/restore",
213
+ "POST /api/agent/teams/:id/resume",
214
+ "POST /api/agent/teams/:id/staffing/:staffing/replace",
215
+ "POST /api/agent/teams/:id/staffing/launch",
216
+ "POST /api/agent/unmute",
217
+ "POST /api/agent/webhooks",
218
+ "POST /api/agent/workspaces",
219
+ "POST /api/agent/workspaces/:id/archive",
220
+ "POST /api/agent/workspaces/:id/assignments",
221
+ "POST /api/agent/workspaces/:id/bindings",
222
+ "POST /api/agent/workspaces/:id/checkpoints",
223
+ "POST /api/agent/workspaces/:id/master",
224
+ "POST /api/agent/workspaces/:id/members",
225
+ "POST /api/agent/workspaces/:id/restore",
226
+ "POST /api/agent/workspaces/:id/side-chats",
227
+ "POST /api/agent/workspaces/:id/side-chats/:chat/refresh",
228
+ "POST /api/agent/workspaces/:id/side-chats/:chat/send",
229
+ "POST /api/cli-logins/:code/approve",
230
+ "POST /api/cli-logins/:code/deny",
231
+ "POST /api/connect/flows/:flow/approve",
232
+ "POST /api/connect/flows/:flow/deny",
233
+ "POST /api/enrollments/:code/approve",
234
+ "POST /api/enrollments/:code/deny",
235
+ "POST /api/machines/:machine/sessions",
236
+ "POST /api/push/subscribe",
237
+ "POST /api/push/test",
238
+ "POST /api/push/unsubscribe",
239
+ "POST /api/recordings",
240
+ "POST /api/recordings/:id/end",
241
+ "POST /api/recordings/:id/events",
242
+ "POST /api/sessions/:session/context-cards/:provider/refresh",
243
+ "POST /api/sessions/:session/error/dismiss",
244
+ "POST /api/sessions/:session/fork",
245
+ "POST /api/sessions/:session/previews",
246
+ "POST /api/sessions/:session/rename",
247
+ "POST /api/sessions/:session/revert",
248
+ "POST /api/sessions/:session/revert/commit",
249
+ "POST /api/sessions/:session/revert/restore",
250
+ "POST /api/settings/speech/test",
251
+ "POST /api/teams/:id/renew",
252
+ "PUT /api/admin/agents/acting",
253
+ "PUT /api/admin/users/:id",
254
+ "PUT /api/agent/acting",
255
+ "PUT /api/agent/artifacts/:id/visibility",
256
+ "PUT /api/agent/concierge/notes",
257
+ "PUT /api/agent/concierge/settings",
258
+ "PUT /api/agent/concierge/watchlist",
259
+ "PUT /api/agent/grants/:id",
260
+ "PUT /api/agent/messaging",
261
+ "PUT /api/agent/sessions/:addr/archive",
262
+ "PUT /api/agent/sessions/:addr/intent",
263
+ "PUT /api/agent/sessions/:addr/pin",
264
+ "PUT /api/agent/sessions/:addr/profile",
265
+ "PUT /api/agent/team-settings",
266
+ "PUT /api/agent/teams/:id/draft/edges",
267
+ "PUT /api/agent/teams/:id/draft/roles/:role",
268
+ "PUT /api/agent/teams/:id/staffing",
269
+ "PUT /api/agent/workspaces/:id/supervision",
270
+ "PUT /api/push/mute",
271
+ "PUT /api/push/prefs",
272
+ "PUT /api/recordings/:id/clips/:n",
273
+ "PUT /api/recordings/:id/segments/:n",
274
+ "PUT /api/sessions/:session/model",
275
+ "PUT /api/sessions/:session/model-prefs",
276
+ "PUT /api/settings/agent-limits",
277
+ "PUT /api/settings/context-cards",
278
+ "PUT /api/settings/media",
279
+ "PUT /api/settings/recording",
280
+ "PUT /api/settings/speech",
281
+ "PUT /api/settings/speech/default"
282
+ ]
@@ -99,7 +99,10 @@
99
99
  "description": "How a message reached its recipient session (Delivery in src/central/agent/messages.ts). `expired`: pending past PENDING_MS or (H2) past its notAfter; never delivered (see expiredReason).",
100
100
  "enum": ["steer", "turn", "queued", "retained", "pending", "dropped", "expired"]
101
101
  },
102
- "WakeSkip": { "enum": ["wake_false", "subagent", "hop_cap", "budget"] },
102
+ "WakeSkip": {
103
+ "description": "Why delivery did not wake. item_budget and item_loop were added by team-autonomy phase 2; clients must treat an unknown value as delivered, not woken.",
104
+ "enum": ["wake_false", "subagent", "hop_cap", "budget", "item_budget", "item_loop"]
105
+ },
103
106
  "Message": {
104
107
  "type": "object",
105
108
  "required": ["id", "kind", "from", "to", "recipient", "text", "state", "at", "hop"],
@@ -138,7 +141,7 @@
138
141
  }
139
142
  },
140
143
  "AskRequest": {
141
- "description": "POST /api/agent/ask (scope messages:send). Optional header Idempotency-Key (H1, per sender). expiresIn and wait are seconds (wait at most 60, never with all). The Decision fields (options … about) are for asks to @me only.",
144
+ "description": "POST /api/agent/ask (scope messages:send). Optional header Idempotency-Key (H1, per sender). expiresIn and wait are seconds (wait at most 60, never with all). The Decision fields (options … about, choices, kind) are for asks to @me only. (DC-1) choices and options are exclusive.",
142
145
  "type": "object",
143
146
  "required": ["to", "text"],
144
147
  "additionalProperties": false,
@@ -156,6 +159,8 @@
156
159
  "items": { "type": "object", "required": ["id", "label"], "properties": { "id": { "type": "string" }, "label": { "type": "string" }, "safe": { "type": "boolean" } } }
157
160
  },
158
161
  "recommendation": { "type": "object", "required": ["option"], "properties": { "option": { "type": "string" }, "why": { "type": "string" } } },
162
+ "choices": { "type": "array", "minItems": 1, "maxItems": 12, "items": { "$ref": "#/$defs/DecisionChoice" } },
163
+ "kind": { "$ref": "#/$defs/DecisionKind" },
159
164
  "urgency": { "$ref": "#/$defs/DecisionUrgency" },
160
165
  "tier": { "$ref": "#/$defs/DecisionTier" },
161
166
  "deadline": { "type": "number", "minimum": 0 },
@@ -171,7 +176,8 @@
171
176
  "message": { "$ref": "#/$defs/Message" },
172
177
  "delivery": { "$ref": "#/$defs/MessageDelivery" },
173
178
  "woke": { "type": "boolean" },
174
- "wakeSkipped": { "$ref": "#/$defs/WakeSkip" }
179
+ "wakeSkipped": { "$ref": "#/$defs/WakeSkip" },
180
+ "wakeLimit": { "type": "number", "minimum": 1, "description": "Effective hop cap, wake budget or item wake budget when the hub provides one." }
175
181
  }
176
182
  },
177
183
  "SentAll": {
@@ -214,6 +220,8 @@
214
220
  "items": { "type": "object", "required": ["id", "label"], "properties": { "id": { "type": "string" }, "label": { "type": "string" }, "safe": { "const": true } } }
215
221
  },
216
222
  "recommendation": { "type": "object", "required": ["option"], "properties": { "option": { "type": "string" }, "why": { "type": "string" } } },
223
+ "choices": { "type": "array", "items": { "$ref": "#/$defs/DecisionChoice" } },
224
+ "kind": { "$ref": "#/$defs/DecisionKind" },
217
225
  "urgency": { "$ref": "#/$defs/DecisionUrgency" },
218
226
  "tier": { "$ref": "#/$defs/DecisionTier" },
219
227
  "deadline": { "$ref": "#/$defs/EpochMs" },
@@ -226,9 +234,37 @@
226
234
  "type": "object",
227
235
  "required": ["answeredBy", "at"],
228
236
  "description": "answeredBy: user (the console), timeout (the safe onTimeout option), or (AF4, app-front contract §6) app: an app holding decisions:answer answered a confirm-tier Decision of its own session; then `app` is its party (app:<tokenID>) and `appLabel` its label.",
229
- "properties": { "option": { "type": "string" }, "text": { "type": "string" }, "answeredBy": { "enum": ["user", "timeout", "app"] }, "at": { "$ref": "#/$defs/EpochMs" }, "app": { "type": "string", "pattern": "^app:[0-9a-f]{16}$" }, "appLabel": { "type": "string" } }
237
+ "properties": {
238
+ "option": { "type": "string" },
239
+ "picks": { "description": "(DC-1) A choices Decision: the option picked in each group.", "type": "object", "additionalProperties": { "type": "string" } },
240
+ "text": { "type": "string" },
241
+ "answeredBy": { "enum": ["user", "timeout", "app"] },
242
+ "at": { "$ref": "#/$defs/EpochMs" },
243
+ "deviceID": { "type": "string" },
244
+ "via": { "enum": ["voice", "text"] },
245
+ "app": { "type": "string", "pattern": "^app:[0-9a-f]{16}$" },
246
+ "appLabel": { "type": "string" }
247
+ }
230
248
  },
231
- "closed": { "type": "object", "required": ["at", "reason"], "properties": { "at": { "$ref": "#/$defs/EpochMs" }, "reason": { "type": "string" } } }
249
+ "closed": { "type": "object", "required": ["at", "reason"], "properties": { "at": { "$ref": "#/$defs/EpochMs" }, "reason": { "type": "string" } } },
250
+ "action": { "description": "Set by the hub only (an escalated permission, a grant request, a Concierge proposal, a team hop renewal): its kind and subject.", "type": "object", "required": ["kind"], "properties": { "kind": { "type": "string" } } },
251
+ "as": { "description": "concierge: asked by one of the user's Concierge sessions.", "const": "concierge" }
252
+ }
253
+ },
254
+ "DecisionKind": { "description": "(DC-1) What an ask is about (absent: a question).", "enum": ["question", "plan", "merge", "release"] },
255
+ "DecisionChoice": {
256
+ "description": "(DC-1) One point of a structured ask to @me: 2–6 options, at most one recommended. The answer picks one option per group.",
257
+ "type": "object",
258
+ "required": ["id", "label", "options"],
259
+ "properties": {
260
+ "id": { "type": "string", "pattern": "^[A-Za-z0-9_-]{1,8}$" },
261
+ "label": { "type": "string", "minLength": 1 },
262
+ "options": {
263
+ "type": "array",
264
+ "minItems": 2,
265
+ "maxItems": 6,
266
+ "items": { "type": "object", "required": ["id", "label"], "properties": { "id": { "type": "string" }, "label": { "type": "string" }, "recommended": { "type": "boolean" } } }
267
+ }
232
268
  }
233
269
  },
234
270
 
@@ -418,6 +454,18 @@
418
454
  "lastError": { "type": "string" },
419
455
  "updatedAt": { "$ref": "#/$defs/EpochMs" },
420
456
  "routedAt": { "$ref": "#/$defs/EpochMs" },
457
+ "startedAt": { "$ref": "#/$defs/EpochMs", "description": "First active work; pauses and blocked time continue counting against wall-clock budget." },
458
+ "parentItem": { "type": "string", "description": "Optional root item of a session-submitted child; old stored rows omit this field." },
459
+ "usage": {
460
+ "type": "object", "required": ["wakes", "hours", "generation", "budget"], "additionalProperties": false,
461
+ "properties": {
462
+ "wakes": { "type": "integer", "minimum": 0 }, "hours": { "type": "number", "minimum": 0 },
463
+ "generation": { "type": "integer", "minimum": 1 },
464
+ "chargedTo": { "type": "string" },
465
+ "budget": { "type": "object", "required": ["wakes", "hours"], "additionalProperties": false,
466
+ "properties": { "wakes": { "type": "integer", "minimum": 1 }, "hours": { "type": "integer", "minimum": 1 } } }
467
+ }
468
+ },
421
469
  "closedAt": { "$ref": "#/$defs/EpochMs" },
422
470
  "notAfter": { "description": "(H4) As submitted.", "$ref": "#/$defs/EpochMs" }
423
471
  }
@@ -459,6 +507,20 @@
459
507
  }
460
508
  },
461
509
 
510
+ "TeamItemBudgetExhaustedSubject": {
511
+ "description": "Journal-only team.item_budget.exhausted; webhook subscriptions cannot select this type.",
512
+ "type": "object", "required": ["teamID"], "additionalProperties": false,
513
+ "properties": { "teamID": { "type": "string" }, "intakeID": { "type": "string" } }
514
+ },
515
+ "TeamItemBudgetExhaustedData": {
516
+ "description": "One exhaustion per item/reason/generation or once per unattributed pool's rolling 24 h; flat and text-free.",
517
+ "type": "object", "required": ["scope", "reason", "limit", "usageWakes", "usageHours"], "additionalProperties": false,
518
+ "properties": {
519
+ "scope": { "enum": ["item", "pool"] }, "reason": { "enum": ["wakes", "hours"] },
520
+ "limit": { "type": "number", "minimum": 1 }, "usageWakes": { "type": "integer", "minimum": 0 }, "usageHours": { "type": "number", "minimum": 0 }
521
+ }
522
+ },
523
+
462
524
  "LaunchChangedSubject": {
463
525
  "type": "object",
464
526
  "required": ["launchID"],
@@ -394,17 +394,21 @@
394
394
 
395
395
  "AnswerableTier": { "description": "(AF4) APP_ANSWERABLE_TIERS: the only tier an app answers (decision w6lfbfaq 1).", "enum": ["confirm"] },
396
396
  "DecisionAnswerRequest": {
397
- "description": "(AF4) POST /api/agent/decisions/:id/answer (decisions:answer): an option (and an optional note) or text for a Decision without options. No `via`.",
397
+ "description": "(AF4) POST /api/agent/decisions/:id/answer (decisions:answer): an option (and an optional note) or text for a Decision without options. (DC-1) A choices Decision: picks, one option per group (and an optional note), or text alone. No `via`.",
398
398
  "type": "object",
399
399
  "additionalProperties": false,
400
400
  "minProperties": 1,
401
- "properties": { "option": { "type": "string", "minLength": 1, "maxLength": 64 }, "text": { "type": "string", "minLength": 1, "maxLength": 4000 } }
401
+ "properties": {
402
+ "option": { "type": "string", "minLength": 1, "maxLength": 64 },
403
+ "picks": { "type": "object", "minProperties": 1, "maxProperties": 12, "additionalProperties": { "type": "string", "minLength": 1, "maxLength": 64 } },
404
+ "text": { "type": "string", "minLength": 1, "maxLength": 4000 }
405
+ }
402
406
  },
403
407
  "AppDecisionAnswer": {
404
408
  "description": "(AF4) Decision.answer when an app answered: answeredBy app, the app's party and (when it has one) its label.",
405
409
  "type": "object",
406
410
  "required": ["answeredBy", "app", "at"],
407
- "properties": { "answeredBy": { "const": "app" }, "app": { "type": "string", "pattern": "^app:[0-9a-f]{16}$" }, "appLabel": { "type": "string" }, "option": { "type": "string" }, "text": { "type": "string" }, "at": { "$ref": "#/$defs/EpochMs" } }
411
+ "properties": { "answeredBy": { "const": "app" }, "app": { "type": "string", "pattern": "^app:[0-9a-f]{16}$" }, "appLabel": { "type": "string" }, "option": { "type": "string" }, "picks": { "type": "object", "additionalProperties": { "type": "string" } }, "text": { "type": "string" }, "at": { "$ref": "#/$defs/EpochMs" } }
408
412
  }
409
413
  }
410
414
  }
@@ -0,0 +1,8 @@
1
+ // The versioned HTTP API (docs/plans/2026-11-12-api-v1-contract-and-native-app-design.md): what native apps and
2
+ // other clients build against. Pure: names shared by the hub, the spec tooling and the tests.
3
+
4
+ /**
5
+ * Every route of the hub's console and agent tables also answers under this prefix: `/api/v1/<rest>` is
6
+ * `/api/<rest>`, with one error envelope (`{error: {code, message, ...}}`). v1 changes only by addition.
7
+ */
8
+ export const API_V1_PREFIX = "/api/v1"
@@ -14,6 +14,7 @@
14
14
  // preset.ts (the harness preset schema), webhook.ts (webhook signature format), wire.ts (shared frame primitives).
15
15
  // Workspaces/artifacts/teams contracts (HWA-02, docs/plans/2026-10-30-hub-workspaces-artifacts-contracts.md):
16
16
  // contracts.ts (shared IDs, limits, revisions), hub-workspace.ts, profiles.ts, artifacts.ts, teams.ts, graph.ts.
17
+ // The versioned HTTP API (docs/plans/2026-11-12-api-v1-contract-and-native-app-design.md): api-v1.ts.
17
18
  // All are re-exported here.
18
19
 
19
20
  import { featureList, HUB_URL_MAX_CHARS, ID, isByeFrame as isBye, isHubURL, isObj, isResFrame as isRes, isWelcomeFrame, MAX_FRAME, MAX_RETRY_AFTER_MS, nonEmpty, num, parseFrame as parse, str, token, UNSAFE_CHARS } from "./wire.ts"
@@ -43,6 +44,7 @@ export * from "./teams.ts"
43
44
  export * from "./graph.ts"
44
45
  export * from "./voice-recording.ts"
45
46
  export * from "./app-front.ts"
47
+ export * from "./api-v1.ts"
46
48
  export { HUB_URL_MAX_CHARS, isHubURL, MAX_FRAME, MAX_RETRY_AFTER_MS }
47
49
 
48
50
  export const PROTOCOL_VERSION = 1
@@ -19,8 +19,18 @@ export const TEAM_LIMITS = {
19
19
  sessionsPerRole: 16,
20
20
  /** Open intake items per team. */
21
21
  openIntakePerTeam: 500,
22
+ /** The absolute bound of any hop limit (HOP_ABSOLUTE_MAX); the hub checks team limits against its live ceiling. */
23
+ hopLimit: 10_000,
22
24
  } as const
23
25
 
26
+ /**
27
+ * (HC-1) The largest hop limit or ceiling a hub accepts: about 10 days of continuous agent-only traffic at the rate
28
+ * #hub-dev measured (~40 hops/h). Past that a cap bounds nothing; "no cap" would be its own decision.
29
+ */
30
+ export const HOP_ABSOLUTE_MAX = 10_000
31
+ /** (HC-1) The hub's hop ceiling when neither hub.json nor the console sets one. */
32
+ export const HOP_CEILING_DEFAULT = 100
33
+
24
34
  export const TEAM_SCOPES = ["teams:read", "teams:manage", "teams:submit"] as const
25
35
 
26
36
  /** "open": every member may msg/ask every other member (two-way). "restricted": only the drawn edges. */
@@ -54,6 +64,11 @@ export interface TeamDefinition extends Revisioned {
54
64
  readonly description?: string
55
65
  readonly state: RecordState
56
66
  readonly mode: TeamMode
67
+ /** Optional limit for messages between this team's seated sessions. */
68
+ readonly hopLimit?: number
69
+ /** Optional per-team overrides of the hub-wide item budget and no-progress loop threshold. */
70
+ readonly itemBudget?: { readonly wakes: number; readonly hours: number }
71
+ readonly loop?: { readonly messages: number; readonly windowMinutes: number }
57
72
  readonly roles: readonly TeamRole[]
58
73
  /** The intake role; receives submitted work and supervises members (via workspace supervision). */
59
74
  readonly entryRole: string
@@ -182,6 +197,7 @@ export function teamInputProblem(v: unknown): string | undefined {
182
197
  if (!isLabel(v.title)) return "title must be 1–120 characters of text"
183
198
  if (v.description !== undefined && !isText(v.description, DESCRIPTION_MAX)) return `description must be at most ${DESCRIPTION_MAX} characters`
184
199
  if (v.mode !== "open" && v.mode !== "restricted") return "mode must be open or restricted"
200
+ if (v.hopLimit !== undefined && (!Number.isSafeInteger(v.hopLimit) || (v.hopLimit as number) < 1 || (v.hopLimit as number) > TEAM_LIMITS.hopLimit)) return `hopLimit must be a whole number from 1 to ${TEAM_LIMITS.hopLimit}`
185
201
  if (!Array.isArray(v.roles) || v.roles.length === 0 || v.roles.length > TEAM_LIMITS.rolesPerTeam) return `roles must list 1–${TEAM_LIMITS.rolesPerTeam} roles`
186
202
  const names = new Set<string>()
187
203
  for (const role of v.roles) {
@@ -2,7 +2,8 @@ export const RECORDING_FORMAT = "hub-voice-recording" as const
2
2
  export const RECORDING_FORMAT_VERSION = 1
3
3
  export const RECORDING_AUDIO_TYPE = "audio/wav" // 16 kHz mono PCM16, exactly as sent to STT
4
4
  export const RECORDING_PAGE_TYPE = "application/x-ndjson" // event pages (blobs)
5
- export const RECORDING_MEDIA_TYPES = [RECORDING_AUDIO_TYPE, RECORDING_PAGE_TYPE] as const
5
+ export const RECORDING_REPLY_TYPE = "audio/mpeg" // Developer mode: a reply sentence's TTS audio, as synthesized
6
+ export const RECORDING_MEDIA_TYPES = [RECORDING_AUDIO_TYPE, RECORDING_PAGE_TYPE, RECORDING_REPLY_TYPE] as const
6
7
  export const RECORDING_HEADER = "x-hub-recording" // on /api/transcribe and /api/speak: the recording ID
7
8
  export const RECORDING_SEGMENT_HEADER = "x-hub-segment" // on /api/transcribe: the segment number
8
9
  export const RECORDING_REF_HEADER = "x-hub-tts-ref" // on /api/speak: "<messageID>:<ordinal>:<seq>:<i>"
@@ -11,8 +12,12 @@ export const RECORDING_AUDIO_RESULT_HEADER = "x-hub-recording-audio" // transcri
11
12
  * Console kinds and values added after version 1, listed in the start answer (`features`): a console sends them only
12
13
  * to a hub that lists them (an older hub refuses a whole batch with an unknown kind or value).
13
14
  * "turn.predict": the turn.predict kind and utterance.sent reasons "smart" / "smart-fallback" (Smart Turn).
15
+ * "capture.v2": the kinds vad.near, vad.summary, stt.slow and mark, the utterance.command command "mark", and the
16
+ * stt.received fields junkReason, held and speechMs (an older hub refuses an unknown field too).
17
+ * "dev.v1": Developer mode's kinds vad.trace, audio.clip, device.info, resource.timing and console.log (sent only
18
+ * when the start answer also says `developer: true`), and the clip upload route.
14
19
  */
15
- export const RECORDING_FEATURES = ["turn.predict"] as const
20
+ export const RECORDING_FEATURES = ["turn.predict", "capture.v2", "dev.v1"] as const
16
21
  export type RecordingFeature = (typeof RECORDING_FEATURES)[number]
17
22
 
18
23
  export const RECORDING_LIMITS = {
@@ -36,8 +41,29 @@ export const RECORDING_LIMITS = {
36
41
  hubFlushMs: 2_000, hubFlushEvents: 100,
37
42
  } as const
38
43
 
44
+ /**
45
+ * Developer mode (feature "dev.v1"): the extras' own limits. Their audio (clips, reply audio) counts against the same
46
+ * caps but stops first: never within the headroom kept for segments below the user's and the recording's caps.
47
+ */
48
+ export const DEV_LIMITS = {
49
+ clipsPerRecording: 200,
50
+ clipPreMs: 3000,
51
+ clipPostMs: 2000,
52
+ clipBytes: 192 * 1024, // 5 s at 16 kHz PCM16 = 160,044 B
53
+ /** At most one clip per this much time (console), the rest are not cut. */
54
+ clipGapMs: 2000,
55
+ repliesPerRecording: 2000,
56
+ replyBytes: 1024 * 1024, // one sentence's MP3
57
+ userHeadroomBytes: 50 * 1024 * 1024, // extras stop at userBytes − this; segments go on to userBytes
58
+ recordingHeadroomBytes: 15 * 1024 * 1024,
59
+ tracesPerMinute: 30,
60
+ logsPerMinute: 20,
61
+ } as const
62
+
39
63
  export interface RecordingSettings {
40
64
  readonly enabled: boolean // default false
65
+ /** Developer mode (implies enabled; turning recording off turns it off). Absent: off. */
66
+ readonly developer?: boolean
41
67
  readonly revision: number
42
68
  readonly updatedAt?: number
43
69
  }
@@ -75,6 +101,11 @@ export interface RecordingSummary {
75
101
  readonly events: number
76
102
  readonly audio: boolean // false once a cap stopped audio
77
103
  readonly redactions: number
104
+ /** Developer mode: this recording keeps the extras (until Developer mode is turned off). */
105
+ readonly developer?: true
106
+ /** Developer mode: audio clips and reply audio stored. */
107
+ readonly clips?: number
108
+ readonly replies?: number
78
109
  }
79
110
 
80
111
  export interface RecordingUsage { readonly bytes: number; readonly recordings: number; readonly capBytes: number }
@@ -90,11 +121,17 @@ export type ConsoleEventKind =
90
121
  | "tts.sentence.ended" | "barge.in" | "cue.played" | "mic" | "control" | "turn.timing" | "decision.voice"
91
122
  | "clock.sync" | "error"
92
123
  | "turn.predict" // feature "turn.predict"
124
+ | "vad.near" | "vad.summary" | "stt.slow" | "mark" // feature "capture.v2"
125
+ | "vad.trace" | "audio.clip" | "device.info" | "resource.timing" | "console.log" // feature "dev.v1" (Developer mode)
93
126
  export type HubEventKind =
94
127
  | "recording.started" | "recording.ended" | "conversation" | "stt.request" | "stt.result" | "tts.synth"
95
128
  | "utterance.received" | "utterance.delivered" | "turn.started" | "first.token" | "say.sent" | "reply.full"
96
129
  | "turn.ended" | "tool" | "interrupt" | "wake.arrived" | "concierge.tool" | "decision.opened" | "decision.answered"
97
130
  | "cap.reached" | "error"
131
+ | "turn.detail" | "request" | "reply.audio" // Developer mode
132
+
133
+ /** Console kinds of Developer mode: stored only in a developer recording. */
134
+ export const DEV_CONSOLE_KINDS: readonly ConsoleEventKind[] = ["vad.trace", "audio.clip", "device.info", "resource.timing", "console.log"]
98
135
 
99
136
  /** As stored and exported (events.jsonl). */
100
137
  export interface RecordingEvent {
@@ -29,7 +29,9 @@ poll="${HUB_POLL_INTERVAL:-0.5}"
29
29
  say() { echo "[hub-sandbox] $*" >&2; }
30
30
 
31
31
  mkdir -p "$hub"
32
- echo "$$" >"$hub/entry.pid"
32
+ pid_file=$(mktemp "$hub/entry.pid.XXXXXX") || exit 1
33
+ printf '%s\n' "$$" >"$pid_file"
34
+ mv -f "$pid_file" "$hub/entry.pid"
33
35
  rm -f "$hub/exited"
34
36
 
35
37
  # 1. Wait for the start op.
@@ -20,5 +20,7 @@ else
20
20
  nohup "$here/hub-sandbox-entry" >>"$log" 2>&1 </dev/null &
21
21
  fi
22
22
  # The entry rewrites this with its own pid; written here too so a start right after sees it.
23
- echo "$!" >"$hub/entry.pid"
23
+ pid_file=$(mktemp "$hub/entry.pid.XXXXXX") || exit 1
24
+ printf '%s\n' "$!" >"$pid_file"
25
+ mv -f "$pid_file" "$hub/entry.pid"
24
26
  echo "hub-sandbox: started (pid $!, log $log)"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huynle/hub-runtime-openshell",
3
- "version": "0.10.1",
3
+ "version": "0.11.1",
4
4
  "private": true,
5
5
  "description": "The `openshell` runtime of `hub node`: launches in NVIDIA OpenShell sandboxes, driven through the openshell CLI (internal; shipped inside @huynle/hub, not published on its own).",
6
6
  "license": "MIT",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huynle/hub-runtime-sdk",
3
- "version": "0.10.1",
3
+ "version": "0.11.1",
4
4
  "private": true,
5
5
  "description": "The Runtime interface `hub node` runtimes implement (internal; shipped inside @huynle/hub, not published on its own).",
6
6
  "license": "MIT",