@huynle/hub 0.9.2 → 0.10.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 (141) hide show
  1. package/CHANGELOG.md +320 -0
  2. package/Dockerfile +5 -1
  3. package/README.md +154 -77
  4. package/package.json +7 -4
  5. package/packages/claude-code/package.json +1 -1
  6. package/packages/cli/dist/hub-cli.js +534 -261
  7. package/packages/cli/package.json +1 -1
  8. package/packages/cli/src/cli.ts +36 -1
  9. package/packages/cli/src/subscriptions.ts +49 -0
  10. package/packages/codex/package.json +1 -1
  11. package/packages/codex/src/sessions.ts +10 -6
  12. package/packages/codex/src/spoke.ts +2 -1
  13. package/packages/node/package.json +1 -1
  14. package/packages/preset/package.json +1 -1
  15. package/packages/protocol/package.json +1 -1
  16. package/packages/protocol/schema/app-delivery/v1.schema.json +7 -5
  17. package/packages/protocol/schema/app-front/v1.schema.json +410 -0
  18. package/packages/protocol/src/app-front.ts +293 -0
  19. package/packages/protocol/src/concierge.ts +10 -1
  20. package/packages/protocol/src/context-cards.ts +59 -0
  21. package/packages/protocol/src/credentials.ts +39 -2
  22. package/packages/protocol/src/index.ts +81 -11
  23. package/packages/runtime-openshell/package.json +1 -1
  24. package/packages/runtime-sdk/package.json +1 -1
  25. package/packages/sdk/package.json +1 -1
  26. package/packages/sdk/src/app-front.ts +524 -0
  27. package/packages/sdk/src/client.ts +37 -4
  28. package/packages/sdk/src/envelope.ts +9 -2
  29. package/packages/sdk/src/errors.ts +1 -0
  30. package/packages/sdk/src/index.ts +1 -0
  31. package/packages/sdk/src/types.ts +39 -1
  32. package/packages/spoke-sdk/package.json +1 -1
  33. package/packages/spoke-sdk/src/delivery.ts +17 -1
  34. package/packages/spoke-sdk/src/harness-spoke.ts +4 -3
  35. package/packages/voice-client/README.md +146 -0
  36. package/packages/voice-client/src/vad/LICENSE +21 -0
  37. package/src/central/agent/acting-routes.ts +17 -3
  38. package/src/central/agent/acting.ts +12 -3
  39. package/src/central/agent/actor.ts +6 -1
  40. package/src/central/agent/app-decisions.ts +146 -0
  41. package/src/central/agent/app-tokens.ts +9 -3
  42. package/src/central/agent/audit.ts +3 -0
  43. package/src/central/agent/connect-routes.ts +64 -0
  44. package/src/central/agent/connect.ts +868 -0
  45. package/src/central/agent/conversation.ts +625 -0
  46. package/src/central/agent/decisions.ts +71 -6
  47. package/src/central/agent/delivery.ts +3 -0
  48. package/src/central/agent/directory.ts +5 -0
  49. package/src/central/agent/errors.ts +2 -0
  50. package/src/central/agent/grant-requests.ts +46 -9
  51. package/src/central/agent/grants.ts +13 -2
  52. package/src/central/agent/message-limits.ts +2 -2
  53. package/src/central/agent/message-routes.ts +13 -4
  54. package/src/central/agent/read-routes.ts +16 -2
  55. package/src/central/agent/routes.ts +108 -6
  56. package/src/central/agent/speech-routes.ts +316 -0
  57. package/src/central/agent/subscription-routes.ts +174 -0
  58. package/src/central/agent/subscriptions.ts +980 -0
  59. package/src/central/agent-data.ts +43 -1
  60. package/src/central/backend/memory-counters.ts +2 -2
  61. package/src/central/backend/types.ts +5 -2
  62. package/src/central/backend/valkey-counters.ts +5 -4
  63. package/src/central/client-address.ts +16 -0
  64. package/src/central/concierge/board-render.ts +52 -1
  65. package/src/central/concierge/board-routes.ts +25 -2
  66. package/src/central/concierge/board.ts +11 -1
  67. package/src/central/concierge/context.ts +29 -8
  68. package/src/central/concierge/grant-request.ts +216 -0
  69. package/src/central/concierge/prompt.ts +49 -2
  70. package/src/central/concierge/session-routes.ts +3 -0
  71. package/src/central/config.ts +64 -3
  72. package/src/central/context-cards/budget.ts +121 -0
  73. package/src/central/context-cards/changes.ts +29 -0
  74. package/src/central/context-cards/config.ts +145 -0
  75. package/src/central/context-cards/engine.ts +379 -0
  76. package/src/central/context-cards/inputs.ts +111 -0
  77. package/src/central/context-cards/pickup.ts +134 -0
  78. package/src/central/context-cards/registry.ts +89 -0
  79. package/src/central/context-cards/routes.ts +37 -0
  80. package/src/central/context-cards/service.ts +367 -0
  81. package/src/central/context-cards/store.ts +67 -0
  82. package/src/central/context-cards/types.ts +88 -0
  83. package/src/central/fetcher.ts +10 -2
  84. package/src/central/front-relays.ts +423 -0
  85. package/src/central/http.ts +547 -58
  86. package/src/central/human-activity.ts +453 -0
  87. package/src/central/llm/chat.ts +133 -0
  88. package/src/central/registry.ts +162 -9
  89. package/src/central/speech/service.ts +9 -0
  90. package/src/central/spoke-ws.ts +13 -0
  91. package/src/central/store.ts +9 -12
  92. package/src/config.ts +30 -13
  93. package/src/index.ts +45 -43
  94. package/src/rpc.ts +33 -4
  95. package/src/spoke/concierge-agent.ts +1 -1
  96. package/src/spoke/conversation.ts +6 -75
  97. package/src/spoke/deliver.ts +18 -8
  98. package/src/spoke/hub-rpc.ts +7 -2
  99. package/src/spoke/hub-tool-subscriptions.ts +82 -0
  100. package/src/spoke/hub-tool-title.ts +89 -0
  101. package/src/spoke/hub-tool.ts +56 -18
  102. package/src/spoke/session-context.ts +10 -2
  103. package/src/spoke/spoke.ts +227 -237
  104. package/src/spoke/summary.ts +36 -1
  105. package/src/spoke/taskboard.ts +5 -6
  106. package/src/spoke/voice-front.ts +323 -30
  107. package/src/spoke/who-teams.ts +51 -2
  108. package/src/tui-messaging.ts +24 -10
  109. package/src/tui.ts +6 -6
  110. package/web/dist/assets/{ArtifactPage-BKISN-Qj.js → ArtifactPage-CsPjam8D.js} +1 -1
  111. package/web/dist/assets/{ArtifactPane-CKOiAUNH.js → ArtifactPane-B9weO8L6.js} +1 -1
  112. package/web/dist/assets/ArtifactWorkbench-DzKhMs3l.js +6 -0
  113. package/web/dist/assets/{Diagram-vdpmp3xu.css → Diagram-7y8kg3og.css} +1 -1
  114. package/web/dist/assets/Diagram-CuRIffka.js +1 -0
  115. package/web/dist/assets/{Library-CcjZXsfF.js → Library-rv5DGtrm.js} +2 -2
  116. package/web/dist/assets/Lightbox-BlbivCkd.js +1 -0
  117. package/web/dist/assets/MediaImage-Ch3ryIdR.js +4 -0
  118. package/web/dist/assets/ProfilesDestination-DUuQ9jmd.js +1 -0
  119. package/web/dist/assets/{TeamsPage-CDyjrK-I.js → TeamsPage-D_17MkvN.js} +1 -1
  120. package/web/dist/assets/{Watchtower-DYwoUWME.js → Watchtower-DlrNOJ7X.js} +2 -2
  121. package/web/dist/assets/{artifacts-DU9uRgs1.js → artifacts-But87Jfo.js} +1 -1
  122. package/web/dist/assets/index-bX8B6NLo.js +33 -0
  123. package/web/dist/assets/index-rW6LKQj1.css +1 -0
  124. package/web/dist/assets/{live-ycZ_aQZO.js → live-BGLA-peO.js} +2 -2
  125. package/web/dist/assets/{pane-layout-0RrLHGDr.js → pane-layout-BW0RdX5a.js} +1 -1
  126. package/web/dist/assets/{renderer2d-BIwbgRoX.js → renderer2d-DyANQ1WS.js} +1 -1
  127. package/web/dist/assets/{renderer3d-C1a7eYGQ.js → renderer3d-vYYKFAJq.js} +1 -1
  128. package/web/dist/assets/{safe-area-DHhPy61Q.js → safe-area-B2Bk__FK.js} +1 -1
  129. package/web/dist/assets/{source-BWlvaODa.js → source-C-De8L3s.js} +1 -1
  130. package/web/dist/assets/toast-CXTwTvDw.js +9 -0
  131. package/web/dist/assets/vad-worklet-BfxJhrXy.js +27 -0
  132. package/web/dist/index.html +6 -6
  133. package/web/dist/assets/ArtifactWorkbench-d7R1o-rs.js +0 -6
  134. package/web/dist/assets/Diagram-azw2B_QH.js +0 -1
  135. package/web/dist/assets/Lightbox-Du56MXGX.js +0 -1
  136. package/web/dist/assets/MediaImage-Ds_JUx3a.js +0 -4
  137. package/web/dist/assets/ProfilesDestination-DdzbzmeK.js +0 -1
  138. package/web/dist/assets/index-C3ci5UtY.css +0 -1
  139. package/web/dist/assets/index-LNMZCqgE.js +0 -36
  140. package/web/dist/assets/toast-Cuu2K0hx.js +0 -1
  141. /package/{web/src/voice → packages/voice-client/src}/turn/LICENSE +0 -0
@@ -7,7 +7,7 @@ import { createInterface } from "readline";
7
7
  // packages/cli/package.json
8
8
  var package_default = {
9
9
  name: "@huynle/hub-cli",
10
- version: "0.9.2",
10
+ version: "0.10.1",
11
11
  private: true,
12
12
  description: "The hub CLI (`hub` / `hubctl`) for agents and scripts (internal; bundled into @huynle/hub, not published on its own).",
13
13
  license: "MIT",
@@ -419,138 +419,6 @@ function parseAddress(addr) {
419
419
  const scope = project ? { project } : {};
420
420
  return role ? { kind: "role", role: match[1], ...scope } : { kind: "name", name: match[1], ...scope };
421
421
  }
422
- // packages/sdk/src/artifact-jobs.ts
423
- var PATH = "/api/agent/artifacts";
424
- var seg = (value) => encodeURIComponent(value);
425
- var sig = (options = {}) => options.signal ? { signal: options.signal } : {};
426
- function artifactJobsApi(hub) {
427
- const jobs = (id) => `${PATH}/${seg(id)}/jobs`;
428
- const job = (id, jobID, tail = "") => `${jobs(id)}/${seg(jobID)}${tail}`;
429
- return {
430
- create: async (artifactID, input, options = {}) => {
431
- const response = await hub.request("POST", jobs(artifactID), { body: input, ...options.idempotencyKey ? { headers: { "idempotency-key": options.idempotencyKey } } : {}, ...sig(options) });
432
- const answer = await response.json();
433
- return { job: answer.job, created: response.status === 201 };
434
- },
435
- list: (artifactID, options = {}) => hub.call("GET", jobs(artifactID), { query: { limit: options.limit, cursor: options.cursor }, ...sig(options) }),
436
- get: (artifactID, jobID, options) => hub.call("GET", job(artifactID, jobID), sig(options)),
437
- context: (artifactID, jobID, options) => hub.call("GET", job(artifactID, jobID, "/context"), sig(options)),
438
- proposal: (artifactID, jobID, options) => hub.call("GET", job(artifactID, jobID, "/proposal"), sig(options)),
439
- submit: (artifactID, jobID, result, options) => hub.call("POST", job(artifactID, jobID, "/result"), { body: result, ...sig(options) }),
440
- apply: (artifactID, jobID, expectedRevision, options) => hub.call("POST", job(artifactID, jobID, "/apply"), { body: { expectedRevision }, ...sig(options) }),
441
- cancel: (artifactID, jobID, expectedRevision, options) => hub.call("POST", job(artifactID, jobID, "/cancel"), { body: { expectedRevision }, ...sig(options) })
442
- };
443
- }
444
-
445
- // packages/sdk/src/artifacts.ts
446
- var PATH2 = "/api/agent/artifacts";
447
- var seg2 = (value) => encodeURIComponent(value);
448
- var sig2 = (options = {}) => options.signal ? { signal: options.signal } : {};
449
- function artifactsApi(hub) {
450
- const publish = async (pathname, body, options = {}) => {
451
- const response = await hub.request("POST", pathname, { body, ...options.idempotencyKey ? { headers: { "idempotency-key": options.idempotencyKey } } : {}, ...sig2(options) });
452
- const answer = await response.json();
453
- return { artifact: answer.artifact, revision: answer.revision, created: response.status === 201 };
454
- };
455
- return {
456
- list: (filter = {}) => hub.call("GET", PATH2, { query: { limit: filter.limit, cursor: filter.cursor, state: filter.state, workspace: filter.workspace }, ...sig2(filter) }),
457
- get: (id, options) => hub.call("GET", `${PATH2}/${seg2(id)}`, sig2(options)),
458
- create: (input, options) => publish(PATH2, input, options),
459
- revise: (id, input, options) => publish(`${PATH2}/${seg2(id)}/revisions`, input, options),
460
- revisions: (id, options = {}) => hub.call("GET", `${PATH2}/${seg2(id)}/revisions`, { query: { limit: options.limit, cursor: options.cursor }, ...sig2(options) }),
461
- revision: (id, revisionID, options) => hub.call("GET", `${PATH2}/${seg2(id)}/revisions/${seg2(revisionID)}`, sig2(options)),
462
- uploadBlob: async (body, mediaType, options = {}) => {
463
- const response = await hub.request("POST", `${PATH2}/blobs`, { raw: { body, contentType: mediaType }, ...options.hash ? { headers: { "x-blob-hash": options.hash } } : {}, ...sig2(options) });
464
- return await response.json();
465
- },
466
- download: (id, revisionID, hash, options) => hub.request("GET", `${PATH2}/${seg2(id)}/revisions/${seg2(revisionID)}/blobs/${seg2(hash)}`, sig2(options)),
467
- update: (id, input, options) => hub.call("PATCH", `${PATH2}/${seg2(id)}`, { body: input, ...sig2(options) }),
468
- setVisibility: (id, input, options) => hub.call("PUT", `${PATH2}/${seg2(id)}/visibility`, { body: input, ...sig2(options) }),
469
- archive: (id, expectedRevision, options) => hub.call("POST", `${PATH2}/${seg2(id)}/archive`, { body: { expectedRevision }, ...sig2(options) }),
470
- unarchive: (id, expectedRevision, options) => hub.call("POST", `${PATH2}/${seg2(id)}/unarchive`, { body: { expectedRevision }, ...sig2(options) }),
471
- delete: async (id, expectedRevision, options) => {
472
- await hub.call("DELETE", `${PATH2}/${seg2(id)}`, { body: { expectedRevision }, ...sig2(options) });
473
- },
474
- jobs: artifactJobsApi(hub)
475
- };
476
- }
477
- // packages/sdk/src/team-intake.ts
478
- var seg3 = (value) => encodeURIComponent(value);
479
- var sig3 = (options = {}) => options.signal ? { signal: options.signal } : {};
480
- var path3 = (team) => `/api/agent/teams/${seg3(team)}/intake`;
481
- function teamIntakeApi(hub) {
482
- return {
483
- submit: async (team, input, options = {}) => {
484
- const response = await hub.request("POST", path3(team), { body: input, ...options.idempotencyKey ? { headers: { "idempotency-key": options.idempotencyKey } } : {}, ...sig3(options) });
485
- const answer = await response.json();
486
- return { intake: answer.intake, created: response.status === 201 };
487
- },
488
- intake: (team, intakeID, options) => hub.call("GET", `${path3(team)}/${seg3(intakeID)}`, sig3(options)),
489
- intakes: (team, filter = {}) => hub.call("GET", path3(team), { query: { status: filter.status, limit: filter.limit, cursor: filter.cursor }, ...sig3(filter) }),
490
- setIntakeStatus: (team, intakeID, input, options) => hub.call("POST", `${path3(team)}/${seg3(intakeID)}/status`, { body: input, ...sig3(options) })
491
- };
492
- }
493
- var TEAM_INTAKE_STATUS_INPUTS = ["open", "in_progress", "blocked", "done", "failed", "cancelled"];
494
- async function reportIntakeStatus(api, team, intakeID, input, options = {}) {
495
- const expectedRevision = input.expectedRevision ?? (await api.intake(team, intakeID, options)).intake.revision;
496
- return api.setIntakeStatus(team, intakeID, { status: input.status, expectedRevision, ...input.reason !== undefined ? { reason: input.reason } : {} }, options);
497
- }
498
- // packages/sdk/src/teams.ts
499
- var seg4 = (value) => encodeURIComponent(value);
500
- var sig4 = (options = {}) => options.signal ? { signal: options.signal } : {};
501
- var key = (options = {}) => options.idempotencyKey ? { headers: { "idempotency-key": options.idempotencyKey } } : {};
502
- var base = (team) => `/api/agent/teams/${seg4(team)}`;
503
- function teamsApi(hub) {
504
- const created = async (method, pathname, body, options = {}) => {
505
- const response = await hub.request(method, pathname, { body, ...key(options), ...sig4(options) });
506
- return { ...await response.json(), created: response.status === 201 };
507
- };
508
- const revised = (verb) => (team, expectedRevision, options) => hub.call("POST", `${base(team)}/${verb}`, { body: { expectedRevision }, ...sig4(options) });
509
- return {
510
- ...teamIntakeApi(hub),
511
- create: (spec, options) => created("POST", "/api/agent/teams", spec, options),
512
- list: (filter = {}) => hub.call("GET", "/api/agent/teams", {
513
- query: {
514
- state: filter.state,
515
- tag: filter.tags,
516
- project: filter.project,
517
- metadata: filter.metadata ? Object.entries(filter.metadata).map(([k, v]) => `${k}=${v}`) : undefined,
518
- paused: filter.paused === undefined ? undefined : String(filter.paused),
519
- limit: filter.limit,
520
- cursor: filter.cursor
521
- },
522
- ...sig4(filter)
523
- }),
524
- get: (team, options) => hub.call("GET", base(team), sig4(options)),
525
- update: (team, patch, expectedRevision, options = {}) => hub.call("PATCH", base(team), { body: { ...patch, expectedRevision, ...options.publish ? { publish: true } : {} }, ...sig4(options) }),
526
- draft: (team, options) => hub.call("GET", `${base(team)}/draft`, sig4(options)),
527
- discardDraft: (team, expectedRevision, options) => hub.call("DELETE", `${base(team)}/draft`, { query: { expectedRevision }, ...sig4(options) }),
528
- setRole: (team, role, expectedRevision, options) => {
529
- const { name, ...fields } = role;
530
- return hub.call("PUT", `${base(team)}/draft/roles/${seg4(name)}`, { body: { ...fields, expectedRevision }, ...sig4(options) });
531
- },
532
- removeRole: (team, role, expectedRevision, options) => hub.call("DELETE", `${base(team)}/draft/roles/${seg4(role)}`, { query: { expectedRevision }, ...sig4(options) }),
533
- setEdge: (team, edge, expectedRevision, options) => hub.call("PUT", `${base(team)}/draft/edges`, { body: { ...edge, expectedRevision }, ...sig4(options) }),
534
- removeEdge: (team, from, to, expectedRevision, options) => hub.call("DELETE", `${base(team)}/draft/edges`, { query: { from, to, expectedRevision }, ...sig4(options) }),
535
- publish: (team, expectedRevision, options) => hub.call("POST", `${base(team)}/publish`, { body: { expectedRevision }, ...sig4(options) }),
536
- pause: revised("pause"),
537
- resume: revised("resume"),
538
- archive: revised("archive"),
539
- restore: revised("restore"),
540
- versions: (team, options = {}) => hub.call("GET", `${base(team)}/versions`, { query: { limit: options.limit, cursor: options.cursor }, ...sig4(options) }),
541
- version: (team, version, options) => hub.call("GET", `${base(team)}/versions/${version}`, sig4(options)),
542
- diff: (team, options = {}) => hub.call("GET", `${base(team)}/diff`, { query: { from: options.from, to: options.to }, ...sig4(options) }),
543
- staffing: (team, options = {}) => hub.call("GET", `${base(team)}/staffing`, { query: { ended: options.ended ? 1 : undefined }, ...sig4(options) }),
544
- staff: (team, input, options) => created("PUT", `${base(team)}/staffing`, input, options),
545
- launchMember: (team, input, options) => created("POST", `${base(team)}/staffing/launch`, input, options),
546
- replace: (team, staffingID, input, options) => hub.call("POST", `${base(team)}/staffing/${seg4(staffingID)}/replace`, { body: input, ...sig4(options) }),
547
- unstaff: (team, staffingID, expectedRevision, options) => hub.call("DELETE", `${base(team)}/staffing/${seg4(staffingID)}`, { query: { expectedRevision }, ...sig4(options) }),
548
- roster: (team, options) => hub.call("GET", `${base(team)}/roster`, sig4(options)),
549
- status: (team, options) => hub.call("GET", `${base(team)}/status`, sig4(options)),
550
- settings: (options) => hub.call("GET", "/api/agent/team-settings", sig4(options)),
551
- setSettings: (input, expectedRevision, options) => hub.call("PUT", "/api/agent/team-settings", { body: { teamIsolation: input.teamIsolation, expectedRevision }, ...sig4(options) })
552
- };
553
- }
554
422
  // packages/sdk/src/errors.ts
555
423
  class HubError extends Error {
556
424
  status;
@@ -667,15 +535,288 @@ async function* sseEvents(response, signal) {
667
535
  }
668
536
  }
669
537
 
538
+ // packages/sdk/src/app-front.ts
539
+ var AGENT = "/api/agent";
540
+ var sig = (options = {}) => options.signal ? { signal: options.signal } : {};
541
+ var seg = (value) => encodeURIComponent(value).replace(/%40/g, "@");
542
+ var RESUMABLE = new Set(["slow", "token_expired"]);
543
+ function speechApi(hub) {
544
+ return {
545
+ async transcribe(audio, options = {}) {
546
+ const type = options.type ?? (audio instanceof Blob && audio.type ? audio.type : "audio/webm");
547
+ const response = await hub.request("POST", `${AGENT}/speech/transcribe`, { raw: { body: audio, contentType: type }, ...sig(options) });
548
+ return await response.json();
549
+ },
550
+ async speak(text, options = {}) {
551
+ const response = await hub.request("POST", `${AGENT}/speech/speak`, { body: { text }, ...sig(options) });
552
+ if (!response.body)
553
+ throw new HubTransportError("bad_response", "the hub sent no audio");
554
+ return response.body;
555
+ }
556
+ };
557
+ }
558
+ function conversationApi(hub) {
559
+ const at = (addr, verb) => `${AGENT}/sessions/${seg(addr)}/${verb}`;
560
+ return {
561
+ set: (addr, on, options = {}) => hub.call("POST", at(addr, "conversation"), { body: options.front === undefined ? { on } : { on, front: options.front }, ...sig(options) }),
562
+ utterance: (addr, text, options = {}) => hub.call("POST", at(addr, "utterance"), { body: { text }, ...sig(options) }),
563
+ interrupt: async (addr, options = {}) => {
564
+ await hub.request("POST", at(addr, "interrupt"), sig(options));
565
+ },
566
+ stream: (addr, options = {}) => streamFrames(hub, at(addr, "stream"), options)
567
+ };
568
+ }
569
+ function appDecisionsApi(hub) {
570
+ return {
571
+ list: (options = {}) => hub.call("GET", `${AGENT}/decisions`, { query: { answerable: 1, state: options.state }, ...sig(options) }),
572
+ answer: (id, answer) => {
573
+ const { signal: _signal, ...body } = answer;
574
+ return hub.call("POST", `${AGENT}/decisions/${seg(id)}/answer`, { body, ...sig(answer) });
575
+ }
576
+ };
577
+ }
578
+ function retryable(cause) {
579
+ if (cause instanceof HubError)
580
+ return cause.status === 429 || cause.status >= 500;
581
+ if (cause instanceof HubTransportError)
582
+ return cause.kind === "unreachable" || cause.kind === "stream" || cause.kind === "http" && (cause.status === undefined || cause.status === 429 || cause.status >= 500);
583
+ return false;
584
+ }
585
+ var wait = (ms, signal) => new Promise((resolve) => {
586
+ if (signal?.aborted)
587
+ return resolve();
588
+ const done = () => {
589
+ clearTimeout(timer);
590
+ signal?.removeEventListener("abort", done);
591
+ resolve();
592
+ };
593
+ const timer = setTimeout(done, ms);
594
+ signal?.addEventListener("abort", done, { once: true });
595
+ });
596
+ async function* streamFrames(hub, pathname, options) {
597
+ const resume = options.resume ?? true;
598
+ const maxReconnects = options.maxReconnects ?? 5;
599
+ const base = options.reconnectDelayMs ?? 1000;
600
+ const cap = options.maxReconnectDelayMs ?? 30000;
601
+ let cursor = options.cursor;
602
+ let misses = 0;
603
+ let lastFailure;
604
+ let retryAfterMs = 0;
605
+ for (let attempt = 0;; attempt++) {
606
+ if (options.signal?.aborted)
607
+ return;
608
+ let response;
609
+ try {
610
+ response = await hub.request("GET", pathname, { headers: { accept: "text/event-stream", ...cursor ? { "last-event-id": cursor } : {} }, ...sig(options) });
611
+ } catch (cause) {
612
+ if (attempt === 0 || options.signal?.aborted || !retryable(cause))
613
+ throw cause;
614
+ lastFailure = cause;
615
+ retryAfterMs = cause instanceof HubError && cause.retryAfter !== undefined ? cause.retryAfter * 1000 : 0;
616
+ }
617
+ if (response) {
618
+ retryAfterMs = 0;
619
+ if (!response.body)
620
+ throw new HubTransportError("bad_response", "the hub sent no stream");
621
+ const reader = response.body.getReader();
622
+ const stop = () => void reader.cancel().catch(() => {});
623
+ options.signal?.addEventListener("abort", stop, { once: true });
624
+ const parser = new SseParser;
625
+ const decoder = new TextDecoder;
626
+ lastFailure = undefined;
627
+ try {
628
+ read:
629
+ for (;; ) {
630
+ let chunk;
631
+ try {
632
+ chunk = await reader.read();
633
+ } catch (cause) {
634
+ lastFailure = cause;
635
+ break;
636
+ }
637
+ if (chunk.done || options.signal?.aborted)
638
+ break;
639
+ for (const event of parser.push(decoder.decode(chunk.value, { stream: true }))) {
640
+ if (event.id)
641
+ cursor = event.id;
642
+ if (event.event !== "message")
643
+ continue;
644
+ let frame;
645
+ try {
646
+ frame = JSON.parse(event.data);
647
+ } catch {
648
+ continue;
649
+ }
650
+ if (frame.type === "end") {
651
+ if (resume && RESUMABLE.has(frame.reason)) {
652
+ lastFailure = new HubTransportError("stream", `the stream ended (${frame.reason})`);
653
+ break read;
654
+ }
655
+ yield { frame };
656
+ return;
657
+ }
658
+ if (frame.type !== "ready")
659
+ misses = 0;
660
+ yield cursor ? { frame, cursor } : { frame };
661
+ if (options.signal?.aborted)
662
+ return;
663
+ }
664
+ }
665
+ } finally {
666
+ options.signal?.removeEventListener("abort", stop);
667
+ stop();
668
+ }
669
+ if (options.signal?.aborted || !resume)
670
+ return;
671
+ }
672
+ if (++misses > maxReconnects) {
673
+ throw new HubTransportError("stream", `the conversation stream gave up after ${maxReconnects} reconnects${lastFailure instanceof Error ? ` (${lastFailure.message})` : ""}`, { cause: lastFailure });
674
+ }
675
+ const backoff = Math.min(cap, base * 2 ** (misses - 1));
676
+ await wait(Math.max(retryAfterMs, backoff * (0.5 + Math.random() * 0.5)), options.signal);
677
+ }
678
+ }
679
+ // packages/sdk/src/artifact-jobs.ts
680
+ var PATH = "/api/agent/artifacts";
681
+ var seg2 = (value) => encodeURIComponent(value);
682
+ var sig2 = (options = {}) => options.signal ? { signal: options.signal } : {};
683
+ function artifactJobsApi(hub) {
684
+ const jobs = (id) => `${PATH}/${seg2(id)}/jobs`;
685
+ const job = (id, jobID, tail = "") => `${jobs(id)}/${seg2(jobID)}${tail}`;
686
+ return {
687
+ create: async (artifactID, input, options = {}) => {
688
+ const response = await hub.request("POST", jobs(artifactID), { body: input, ...options.idempotencyKey ? { headers: { "idempotency-key": options.idempotencyKey } } : {}, ...sig2(options) });
689
+ const answer = await response.json();
690
+ return { job: answer.job, created: response.status === 201 };
691
+ },
692
+ list: (artifactID, options = {}) => hub.call("GET", jobs(artifactID), { query: { limit: options.limit, cursor: options.cursor }, ...sig2(options) }),
693
+ get: (artifactID, jobID, options) => hub.call("GET", job(artifactID, jobID), sig2(options)),
694
+ context: (artifactID, jobID, options) => hub.call("GET", job(artifactID, jobID, "/context"), sig2(options)),
695
+ proposal: (artifactID, jobID, options) => hub.call("GET", job(artifactID, jobID, "/proposal"), sig2(options)),
696
+ submit: (artifactID, jobID, result, options) => hub.call("POST", job(artifactID, jobID, "/result"), { body: result, ...sig2(options) }),
697
+ apply: (artifactID, jobID, expectedRevision, options) => hub.call("POST", job(artifactID, jobID, "/apply"), { body: { expectedRevision }, ...sig2(options) }),
698
+ cancel: (artifactID, jobID, expectedRevision, options) => hub.call("POST", job(artifactID, jobID, "/cancel"), { body: { expectedRevision }, ...sig2(options) })
699
+ };
700
+ }
701
+
702
+ // packages/sdk/src/artifacts.ts
703
+ var PATH2 = "/api/agent/artifacts";
704
+ var seg3 = (value) => encodeURIComponent(value);
705
+ var sig3 = (options = {}) => options.signal ? { signal: options.signal } : {};
706
+ function artifactsApi(hub) {
707
+ const publish = async (pathname, body, options = {}) => {
708
+ const response = await hub.request("POST", pathname, { body, ...options.idempotencyKey ? { headers: { "idempotency-key": options.idempotencyKey } } : {}, ...sig3(options) });
709
+ const answer = await response.json();
710
+ return { artifact: answer.artifact, revision: answer.revision, created: response.status === 201 };
711
+ };
712
+ return {
713
+ list: (filter = {}) => hub.call("GET", PATH2, { query: { limit: filter.limit, cursor: filter.cursor, state: filter.state, workspace: filter.workspace }, ...sig3(filter) }),
714
+ get: (id, options) => hub.call("GET", `${PATH2}/${seg3(id)}`, sig3(options)),
715
+ create: (input, options) => publish(PATH2, input, options),
716
+ revise: (id, input, options) => publish(`${PATH2}/${seg3(id)}/revisions`, input, options),
717
+ revisions: (id, options = {}) => hub.call("GET", `${PATH2}/${seg3(id)}/revisions`, { query: { limit: options.limit, cursor: options.cursor }, ...sig3(options) }),
718
+ revision: (id, revisionID, options) => hub.call("GET", `${PATH2}/${seg3(id)}/revisions/${seg3(revisionID)}`, sig3(options)),
719
+ uploadBlob: async (body, mediaType, options = {}) => {
720
+ const response = await hub.request("POST", `${PATH2}/blobs`, { raw: { body, contentType: mediaType }, ...options.hash ? { headers: { "x-blob-hash": options.hash } } : {}, ...sig3(options) });
721
+ return await response.json();
722
+ },
723
+ download: (id, revisionID, hash, options) => hub.request("GET", `${PATH2}/${seg3(id)}/revisions/${seg3(revisionID)}/blobs/${seg3(hash)}`, sig3(options)),
724
+ update: (id, input, options) => hub.call("PATCH", `${PATH2}/${seg3(id)}`, { body: input, ...sig3(options) }),
725
+ setVisibility: (id, input, options) => hub.call("PUT", `${PATH2}/${seg3(id)}/visibility`, { body: input, ...sig3(options) }),
726
+ archive: (id, expectedRevision, options) => hub.call("POST", `${PATH2}/${seg3(id)}/archive`, { body: { expectedRevision }, ...sig3(options) }),
727
+ unarchive: (id, expectedRevision, options) => hub.call("POST", `${PATH2}/${seg3(id)}/unarchive`, { body: { expectedRevision }, ...sig3(options) }),
728
+ delete: async (id, expectedRevision, options) => {
729
+ await hub.call("DELETE", `${PATH2}/${seg3(id)}`, { body: { expectedRevision }, ...sig3(options) });
730
+ },
731
+ jobs: artifactJobsApi(hub)
732
+ };
733
+ }
734
+ // packages/sdk/src/team-intake.ts
735
+ var seg4 = (value) => encodeURIComponent(value);
736
+ var sig4 = (options = {}) => options.signal ? { signal: options.signal } : {};
737
+ var path3 = (team) => `/api/agent/teams/${seg4(team)}/intake`;
738
+ function teamIntakeApi(hub) {
739
+ return {
740
+ submit: async (team, input, options = {}) => {
741
+ const response = await hub.request("POST", path3(team), { body: input, ...options.idempotencyKey ? { headers: { "idempotency-key": options.idempotencyKey } } : {}, ...sig4(options) });
742
+ const answer = await response.json();
743
+ return { intake: answer.intake, created: response.status === 201 };
744
+ },
745
+ intake: (team, intakeID, options) => hub.call("GET", `${path3(team)}/${seg4(intakeID)}`, sig4(options)),
746
+ intakes: (team, filter = {}) => hub.call("GET", path3(team), { query: { status: filter.status, limit: filter.limit, cursor: filter.cursor }, ...sig4(filter) }),
747
+ setIntakeStatus: (team, intakeID, input, options) => hub.call("POST", `${path3(team)}/${seg4(intakeID)}/status`, { body: input, ...sig4(options) })
748
+ };
749
+ }
750
+ var TEAM_INTAKE_STATUS_INPUTS = ["open", "in_progress", "blocked", "done", "failed", "cancelled"];
751
+ async function reportIntakeStatus(api, team, intakeID, input, options = {}) {
752
+ const expectedRevision = input.expectedRevision ?? (await api.intake(team, intakeID, options)).intake.revision;
753
+ return api.setIntakeStatus(team, intakeID, { status: input.status, expectedRevision, ...input.reason !== undefined ? { reason: input.reason } : {} }, options);
754
+ }
755
+ // packages/sdk/src/teams.ts
756
+ var seg5 = (value) => encodeURIComponent(value);
757
+ var sig5 = (options = {}) => options.signal ? { signal: options.signal } : {};
758
+ var key = (options = {}) => options.idempotencyKey ? { headers: { "idempotency-key": options.idempotencyKey } } : {};
759
+ var base = (team) => `/api/agent/teams/${seg5(team)}`;
760
+ function teamsApi(hub) {
761
+ const created = async (method, pathname, body, options = {}) => {
762
+ const response = await hub.request(method, pathname, { body, ...key(options), ...sig5(options) });
763
+ return { ...await response.json(), created: response.status === 201 };
764
+ };
765
+ const revised = (verb) => (team, expectedRevision, options) => hub.call("POST", `${base(team)}/${verb}`, { body: { expectedRevision }, ...sig5(options) });
766
+ return {
767
+ ...teamIntakeApi(hub),
768
+ create: (spec, options) => created("POST", "/api/agent/teams", spec, options),
769
+ list: (filter = {}) => hub.call("GET", "/api/agent/teams", {
770
+ query: {
771
+ state: filter.state,
772
+ tag: filter.tags,
773
+ project: filter.project,
774
+ metadata: filter.metadata ? Object.entries(filter.metadata).map(([k, v]) => `${k}=${v}`) : undefined,
775
+ paused: filter.paused === undefined ? undefined : String(filter.paused),
776
+ limit: filter.limit,
777
+ cursor: filter.cursor
778
+ },
779
+ ...sig5(filter)
780
+ }),
781
+ get: (team, options) => hub.call("GET", base(team), sig5(options)),
782
+ update: (team, patch, expectedRevision, options = {}) => hub.call("PATCH", base(team), { body: { ...patch, expectedRevision, ...options.publish ? { publish: true } : {} }, ...sig5(options) }),
783
+ draft: (team, options) => hub.call("GET", `${base(team)}/draft`, sig5(options)),
784
+ discardDraft: (team, expectedRevision, options) => hub.call("DELETE", `${base(team)}/draft`, { query: { expectedRevision }, ...sig5(options) }),
785
+ setRole: (team, role, expectedRevision, options) => {
786
+ const { name, ...fields } = role;
787
+ return hub.call("PUT", `${base(team)}/draft/roles/${seg5(name)}`, { body: { ...fields, expectedRevision }, ...sig5(options) });
788
+ },
789
+ removeRole: (team, role, expectedRevision, options) => hub.call("DELETE", `${base(team)}/draft/roles/${seg5(role)}`, { query: { expectedRevision }, ...sig5(options) }),
790
+ setEdge: (team, edge, expectedRevision, options) => hub.call("PUT", `${base(team)}/draft/edges`, { body: { ...edge, expectedRevision }, ...sig5(options) }),
791
+ removeEdge: (team, from, to, expectedRevision, options) => hub.call("DELETE", `${base(team)}/draft/edges`, { query: { from, to, expectedRevision }, ...sig5(options) }),
792
+ publish: (team, expectedRevision, options) => hub.call("POST", `${base(team)}/publish`, { body: { expectedRevision }, ...sig5(options) }),
793
+ pause: revised("pause"),
794
+ resume: revised("resume"),
795
+ archive: revised("archive"),
796
+ restore: revised("restore"),
797
+ versions: (team, options = {}) => hub.call("GET", `${base(team)}/versions`, { query: { limit: options.limit, cursor: options.cursor }, ...sig5(options) }),
798
+ version: (team, version, options) => hub.call("GET", `${base(team)}/versions/${version}`, sig5(options)),
799
+ diff: (team, options = {}) => hub.call("GET", `${base(team)}/diff`, { query: { from: options.from, to: options.to }, ...sig5(options) }),
800
+ staffing: (team, options = {}) => hub.call("GET", `${base(team)}/staffing`, { query: { ended: options.ended ? 1 : undefined }, ...sig5(options) }),
801
+ staff: (team, input, options) => created("PUT", `${base(team)}/staffing`, input, options),
802
+ launchMember: (team, input, options) => created("POST", `${base(team)}/staffing/launch`, input, options),
803
+ replace: (team, staffingID, input, options) => hub.call("POST", `${base(team)}/staffing/${seg5(staffingID)}/replace`, { body: input, ...sig5(options) }),
804
+ unstaff: (team, staffingID, expectedRevision, options) => hub.call("DELETE", `${base(team)}/staffing/${seg5(staffingID)}`, { query: { expectedRevision }, ...sig5(options) }),
805
+ roster: (team, options) => hub.call("GET", `${base(team)}/roster`, sig5(options)),
806
+ status: (team, options) => hub.call("GET", `${base(team)}/status`, sig5(options)),
807
+ settings: (options) => hub.call("GET", "/api/agent/team-settings", sig5(options)),
808
+ setSettings: (input, expectedRevision, options) => hub.call("PUT", "/api/agent/team-settings", { body: { teamIsolation: input.teamIsolation, expectedRevision }, ...sig5(options) })
809
+ };
810
+ }
670
811
  // packages/sdk/src/concierge.ts
671
812
  var WATCHLIST = "/api/agent/concierge/watchlist";
672
- var sig5 = (options = {}) => options.signal ? { signal: options.signal } : {};
813
+ var sig6 = (options = {}) => options.signal ? { signal: options.signal } : {};
673
814
  function conciergeApi(hub) {
674
- const verb = (name) => (addr, options) => hub.call("POST", `${WATCHLIST}/${name}`, { body: { session: addr }, ...sig5(options) });
815
+ const verb = (name) => (addr, options) => hub.call("POST", `${WATCHLIST}/${name}`, { body: { session: addr }, ...sig6(options) });
675
816
  return {
676
817
  watchlist: {
677
- get: (options) => hub.call("GET", WATCHLIST, sig5(options)),
678
- update: (patch, expectedRevision, options) => hub.call("PUT", WATCHLIST, { body: { ...patch, expectedRevision }, ...sig5(options) }),
818
+ get: (options) => hub.call("GET", WATCHLIST, sig6(options)),
819
+ update: (patch, expectedRevision, options) => hub.call("PUT", WATCHLIST, { body: { ...patch, expectedRevision }, ...sig6(options) }),
679
820
  watch: verb("watch"),
680
821
  unwatch: verb("unwatch"),
681
822
  mute: verb("mute"),
@@ -685,7 +826,7 @@ function conciergeApi(hub) {
685
826
  }
686
827
 
687
828
  // packages/sdk/src/client.ts
688
- var AGENT = "/api/agent";
829
+ var AGENT2 = "/api/agent";
689
830
  var WAIT_POLL_MAX_S = 60;
690
831
  function queryString(query = {}) {
691
832
  const params = new URLSearchParams;
@@ -724,69 +865,72 @@ class HubClient {
724
865
  #fetch;
725
866
  launches = {
726
867
  create: async (spec, options = {}) => {
727
- const response = await this.request("POST", `${AGENT}/launches`, { body: spec, ...signalOf(options) });
728
- const body = await this.#json(response, `${AGENT}/launches`);
868
+ const response = await this.request("POST", `${AGENT2}/launches`, { body: spec, ...signalOf(options) });
869
+ const body = await this.#json(response, `${AGENT2}/launches`);
729
870
  return { launch: body.launch, created: response.status === 201 };
730
871
  },
731
872
  list: (filter = {}) => {
732
873
  const state = typeof filter.state === "string" ? filter.state : filter.state?.join(",");
733
- return this.call("GET", `${AGENT}/launches`, { query: { state, label: filter.label }, ...signalOf(filter) });
874
+ return this.call("GET", `${AGENT2}/launches`, { query: { state, label: filter.label }, ...signalOf(filter) });
734
875
  },
735
- get: (id, options = {}) => this.call("GET", `${AGENT}/launches/${segment(id)}`, signalOf(options)),
736
- stop: (id, options = {}) => this.call("DELETE", `${AGENT}/launches/${segment(id)}`, signalOf(options))
876
+ get: (id, options = {}) => this.call("GET", `${AGENT2}/launches/${segment(id)}`, signalOf(options)),
877
+ stop: (id, options = {}) => this.call("DELETE", `${AGENT2}/launches/${segment(id)}`, signalOf(options))
737
878
  };
738
879
  profiles = {
739
- create: (input, options = {}) => this.call("POST", `${AGENT}/profiles`, { body: input, ...idempotent(options) }),
740
- import: (input, options = {}) => this.call("POST", `${AGENT}/profiles/import`, { body: input, ...idempotent(options) }),
741
- list: (filter = {}) => this.call("GET", `${AGENT}/profiles`, { query: { state: filter.state, limit: filter.limit, cursor: filter.cursor, include: filter.include }, ...signalOf(filter) }),
742
- get: (id, options = {}) => this.call("GET", `${AGENT}/profiles/${segment(id)}`, signalOf(options)),
743
- update: (id, patch, options = {}) => this.call("PATCH", `${AGENT}/profiles/${segment(id)}`, { body: patch, ...signalOf(options) }),
744
- usage: (id, options = {}) => this.call("GET", `${AGENT}/profiles/${segment(id)}/usage`, { query: { limit: options.limit, cursor: options.cursor }, ...signalOf(options) }),
745
- versions: (id, options = {}) => this.call("GET", `${AGENT}/profiles/${segment(id)}/versions`, { query: { limit: options.limit, cursor: options.cursor }, ...signalOf(options) }),
746
- createVersion: (id, input, options = {}) => this.call("POST", `${AGENT}/profiles/${segment(id)}/versions`, { body: input, ...idempotent(options) }),
747
- version: (id, version, options = {}) => this.call("GET", `${AGENT}/profiles/${segment(id)}/versions/${version}`, signalOf(options)),
748
- skill: (id, version, name, options = {}) => this.call("GET", `${AGENT}/profiles/${segment(id)}/versions/${version}/skills/${segment(name)}`, signalOf(options)),
749
- render: (id, version, name, params, options = {}) => this.call("POST", `${AGENT}/profiles/${segment(id)}/versions/${version}/commands/${segment(name)}/render`, { body: { params }, ...signalOf(options) }),
750
- ofSession: (addr, options = {}) => this.call("GET", `${AGENT}/sessions/${segment(addr)}/profile`, signalOf(options)),
751
- assign: (addr, input, options = {}) => this.call("PUT", `${AGENT}/sessions/${segment(addr)}/profile`, { body: input, ...signalOf(options) }),
752
- unassign: (addr, expectedRevision, options = {}) => this.call("DELETE", `${AGENT}/sessions/${segment(addr)}/profile`, { query: { expectedRevision }, ...signalOf(options) }),
753
- sessionSkill: (addr, name, options = {}) => this.call("GET", `${AGENT}/sessions/${segment(addr)}/profile/skills/${segment(name)}`, signalOf(options)),
754
- sessionRender: (addr, name, params, options = {}) => this.call("POST", `${AGENT}/sessions/${segment(addr)}/profile/commands/${segment(name)}/render`, { body: { params }, ...signalOf(options) })
880
+ create: (input, options = {}) => this.call("POST", `${AGENT2}/profiles`, { body: input, ...idempotent(options) }),
881
+ import: (input, options = {}) => this.call("POST", `${AGENT2}/profiles/import`, { body: input, ...idempotent(options) }),
882
+ list: (filter = {}) => this.call("GET", `${AGENT2}/profiles`, { query: { state: filter.state, limit: filter.limit, cursor: filter.cursor, include: filter.include }, ...signalOf(filter) }),
883
+ get: (id, options = {}) => this.call("GET", `${AGENT2}/profiles/${segment(id)}`, signalOf(options)),
884
+ update: (id, patch, options = {}) => this.call("PATCH", `${AGENT2}/profiles/${segment(id)}`, { body: patch, ...signalOf(options) }),
885
+ usage: (id, options = {}) => this.call("GET", `${AGENT2}/profiles/${segment(id)}/usage`, { query: { limit: options.limit, cursor: options.cursor }, ...signalOf(options) }),
886
+ versions: (id, options = {}) => this.call("GET", `${AGENT2}/profiles/${segment(id)}/versions`, { query: { limit: options.limit, cursor: options.cursor }, ...signalOf(options) }),
887
+ createVersion: (id, input, options = {}) => this.call("POST", `${AGENT2}/profiles/${segment(id)}/versions`, { body: input, ...idempotent(options) }),
888
+ version: (id, version, options = {}) => this.call("GET", `${AGENT2}/profiles/${segment(id)}/versions/${version}`, signalOf(options)),
889
+ skill: (id, version, name, options = {}) => this.call("GET", `${AGENT2}/profiles/${segment(id)}/versions/${version}/skills/${segment(name)}`, signalOf(options)),
890
+ render: (id, version, name, params, options = {}) => this.call("POST", `${AGENT2}/profiles/${segment(id)}/versions/${version}/commands/${segment(name)}/render`, { body: { params }, ...signalOf(options) }),
891
+ ofSession: (addr, options = {}) => this.call("GET", `${AGENT2}/sessions/${segment(addr)}/profile`, signalOf(options)),
892
+ assign: (addr, input, options = {}) => this.call("PUT", `${AGENT2}/sessions/${segment(addr)}/profile`, { body: input, ...signalOf(options) }),
893
+ unassign: (addr, expectedRevision, options = {}) => this.call("DELETE", `${AGENT2}/sessions/${segment(addr)}/profile`, { query: { expectedRevision }, ...signalOf(options) }),
894
+ sessionSkill: (addr, name, options = {}) => this.call("GET", `${AGENT2}/sessions/${segment(addr)}/profile/skills/${segment(name)}`, signalOf(options)),
895
+ sessionRender: (addr, name, params, options = {}) => this.call("POST", `${AGENT2}/sessions/${segment(addr)}/profile/commands/${segment(name)}/render`, { body: { params }, ...signalOf(options) })
755
896
  };
756
897
  workspaces = {
757
- create: (input, options = {}) => this.call("POST", `${AGENT}/workspaces`, { body: input, ...idempotent(options) }),
758
- list: (filter = {}) => this.call("GET", `${AGENT}/workspaces`, { query: { state: filter.state, limit: filter.limit, cursor: filter.cursor }, ...signalOf(filter) }),
759
- get: (id, options = {}) => this.call("GET", `${AGENT}/workspaces/${segment(id)}`, signalOf(options)),
760
- update: (id, patch, options = {}) => this.call("PATCH", `${AGENT}/workspaces/${segment(id)}`, { body: patch, ...signalOf(options) }),
761
- archive: (id, expectedRevision, options = {}) => this.call("POST", `${AGENT}/workspaces/${segment(id)}/archive`, { body: { expectedRevision }, ...signalOf(options) }),
762
- restore: (id, expectedRevision, options = {}) => this.call("POST", `${AGENT}/workspaces/${segment(id)}/restore`, { body: { expectedRevision }, ...signalOf(options) }),
763
- bindings: (id, options = {}) => this.call("GET", `${AGENT}/workspaces/${segment(id)}/bindings`, signalOf(options)),
764
- bind: (id, input, options = {}) => this.call("POST", `${AGENT}/workspaces/${segment(id)}/bindings`, { body: input, ...idempotent(options) }),
765
- unbind: (id, bindingID, expectedRevision, options = {}) => this.call("DELETE", `${AGENT}/workspaces/${segment(id)}/bindings`, { query: { bindingID, expectedRevision }, ...signalOf(options) }),
766
- members: (id, options = {}) => this.call("GET", `${AGENT}/workspaces/${segment(id)}/members`, { query: { includeLeft: options.includeLeft ? 1 : undefined }, ...signalOf(options) }),
767
- join: (id, input, options = {}) => this.call("POST", `${AGENT}/workspaces/${segment(id)}/members`, { body: input, ...idempotent(options) }),
768
- setRole: (id, memberID, role, expectedRevision, options = {}) => this.call("PATCH", `${AGENT}/workspaces/${segment(id)}/members/${segment(memberID)}`, { body: { role, expectedRevision }, ...signalOf(options) }),
769
- leave: (id, memberID, expectedRevision, options = {}) => this.call("DELETE", `${AGENT}/workspaces/${segment(id)}/members/${segment(memberID)}`, { query: { expectedRevision }, ...signalOf(options) }),
770
- assignments: (id, filter = {}) => this.call("GET", `${AGENT}/workspaces/${segment(id)}/assignments`, { query: { memberID: filter.memberID, status: filter.status, limit: filter.limit, cursor: filter.cursor }, ...signalOf(filter) }),
771
- assignment: (id, assignmentID, options = {}) => this.call("GET", `${AGENT}/workspaces/${segment(id)}/assignments/${segment(assignmentID)}`, signalOf(options)),
772
- assign: (id, input, options = {}) => this.call("POST", `${AGENT}/workspaces/${segment(id)}/assignments`, { body: input, ...idempotent(options) }),
773
- setAssignmentStatus: (id, assignmentID, status, expectedRevision, options = {}) => this.call("PATCH", `${AGENT}/workspaces/${segment(id)}/assignments/${segment(assignmentID)}`, { body: { status, expectedRevision }, ...signalOf(options) }),
774
- reassign: (id, assignmentID, memberID, expectedRevision, options = {}) => this.call("PATCH", `${AGENT}/workspaces/${segment(id)}/assignments/${segment(assignmentID)}`, { body: { memberID, expectedRevision }, ...signalOf(options) }),
775
- supervision: (id, options = {}) => this.call("GET", `${AGENT}/workspaces/${segment(id)}/supervision`, signalOf(options)),
776
- supervise: (id, input, options = {}) => this.call("PUT", `${AGENT}/workspaces/${segment(id)}/supervision`, { body: input, ...signalOf(options) }),
777
- board: (id, options = {}) => this.call("GET", `${AGENT}/workspaces/${segment(id)}/board`, signalOf(options)),
778
- master: (id, options = {}) => this.call("GET", `${AGENT}/workspaces/${segment(id)}/master`, signalOf(options)),
779
- replaceMaster: (id, input, options = {}) => this.call("POST", `${AGENT}/workspaces/${segment(id)}/master`, { body: input, ...idempotent(options) }),
780
- createSideChat: (id, input, options = {}) => this.call("POST", `${AGENT}/workspaces/${segment(id)}/side-chats`, { body: input, ...idempotent(options) }),
781
- sideChats: (id, filter = {}) => this.call("GET", `${AGENT}/workspaces/${segment(id)}/side-chats`, { query: { limit: filter.limit, cursor: filter.cursor }, ...signalOf(filter) }),
782
- sideChat: (id, sideChatID, options = {}) => this.call("GET", `${AGENT}/workspaces/${segment(id)}/side-chats/${segment(sideChatID)}`, signalOf(options)),
783
- sideChatByID: (sideChatID, options = {}) => this.call("GET", `${AGENT}/side-chats/${segment(sideChatID)}`, signalOf(options)),
784
- refreshSideChat: (id, sideChatID, expectedRevision, options = {}) => this.call("POST", `${AGENT}/workspaces/${segment(id)}/side-chats/${segment(sideChatID)}/refresh`, { body: { expectedRevision }, ...signalOf(options) }),
785
- sendToMain: (id, sideChatID, text, options = {}) => this.call("POST", `${AGENT}/workspaces/${segment(id)}/side-chats/${segment(sideChatID)}/send`, { body: { text, confirmed: true }, ...signalOf(options) })
898
+ create: (input, options = {}) => this.call("POST", `${AGENT2}/workspaces`, { body: input, ...idempotent(options) }),
899
+ list: (filter = {}) => this.call("GET", `${AGENT2}/workspaces`, { query: { state: filter.state, limit: filter.limit, cursor: filter.cursor }, ...signalOf(filter) }),
900
+ get: (id, options = {}) => this.call("GET", `${AGENT2}/workspaces/${segment(id)}`, signalOf(options)),
901
+ update: (id, patch, options = {}) => this.call("PATCH", `${AGENT2}/workspaces/${segment(id)}`, { body: patch, ...signalOf(options) }),
902
+ archive: (id, expectedRevision, options = {}) => this.call("POST", `${AGENT2}/workspaces/${segment(id)}/archive`, { body: { expectedRevision }, ...signalOf(options) }),
903
+ restore: (id, expectedRevision, options = {}) => this.call("POST", `${AGENT2}/workspaces/${segment(id)}/restore`, { body: { expectedRevision }, ...signalOf(options) }),
904
+ bindings: (id, options = {}) => this.call("GET", `${AGENT2}/workspaces/${segment(id)}/bindings`, signalOf(options)),
905
+ bind: (id, input, options = {}) => this.call("POST", `${AGENT2}/workspaces/${segment(id)}/bindings`, { body: input, ...idempotent(options) }),
906
+ unbind: (id, bindingID, expectedRevision, options = {}) => this.call("DELETE", `${AGENT2}/workspaces/${segment(id)}/bindings`, { query: { bindingID, expectedRevision }, ...signalOf(options) }),
907
+ members: (id, options = {}) => this.call("GET", `${AGENT2}/workspaces/${segment(id)}/members`, { query: { includeLeft: options.includeLeft ? 1 : undefined }, ...signalOf(options) }),
908
+ join: (id, input, options = {}) => this.call("POST", `${AGENT2}/workspaces/${segment(id)}/members`, { body: input, ...idempotent(options) }),
909
+ setRole: (id, memberID, role, expectedRevision, options = {}) => this.call("PATCH", `${AGENT2}/workspaces/${segment(id)}/members/${segment(memberID)}`, { body: { role, expectedRevision }, ...signalOf(options) }),
910
+ leave: (id, memberID, expectedRevision, options = {}) => this.call("DELETE", `${AGENT2}/workspaces/${segment(id)}/members/${segment(memberID)}`, { query: { expectedRevision }, ...signalOf(options) }),
911
+ assignments: (id, filter = {}) => this.call("GET", `${AGENT2}/workspaces/${segment(id)}/assignments`, { query: { memberID: filter.memberID, status: filter.status, limit: filter.limit, cursor: filter.cursor }, ...signalOf(filter) }),
912
+ assignment: (id, assignmentID, options = {}) => this.call("GET", `${AGENT2}/workspaces/${segment(id)}/assignments/${segment(assignmentID)}`, signalOf(options)),
913
+ assign: (id, input, options = {}) => this.call("POST", `${AGENT2}/workspaces/${segment(id)}/assignments`, { body: input, ...idempotent(options) }),
914
+ setAssignmentStatus: (id, assignmentID, status, expectedRevision, options = {}) => this.call("PATCH", `${AGENT2}/workspaces/${segment(id)}/assignments/${segment(assignmentID)}`, { body: { status, expectedRevision }, ...signalOf(options) }),
915
+ reassign: (id, assignmentID, memberID, expectedRevision, options = {}) => this.call("PATCH", `${AGENT2}/workspaces/${segment(id)}/assignments/${segment(assignmentID)}`, { body: { memberID, expectedRevision }, ...signalOf(options) }),
916
+ supervision: (id, options = {}) => this.call("GET", `${AGENT2}/workspaces/${segment(id)}/supervision`, signalOf(options)),
917
+ supervise: (id, input, options = {}) => this.call("PUT", `${AGENT2}/workspaces/${segment(id)}/supervision`, { body: input, ...signalOf(options) }),
918
+ board: (id, options = {}) => this.call("GET", `${AGENT2}/workspaces/${segment(id)}/board`, signalOf(options)),
919
+ master: (id, options = {}) => this.call("GET", `${AGENT2}/workspaces/${segment(id)}/master`, signalOf(options)),
920
+ replaceMaster: (id, input, options = {}) => this.call("POST", `${AGENT2}/workspaces/${segment(id)}/master`, { body: input, ...idempotent(options) }),
921
+ createSideChat: (id, input, options = {}) => this.call("POST", `${AGENT2}/workspaces/${segment(id)}/side-chats`, { body: input, ...idempotent(options) }),
922
+ sideChats: (id, filter = {}) => this.call("GET", `${AGENT2}/workspaces/${segment(id)}/side-chats`, { query: { limit: filter.limit, cursor: filter.cursor }, ...signalOf(filter) }),
923
+ sideChat: (id, sideChatID, options = {}) => this.call("GET", `${AGENT2}/workspaces/${segment(id)}/side-chats/${segment(sideChatID)}`, signalOf(options)),
924
+ sideChatByID: (sideChatID, options = {}) => this.call("GET", `${AGENT2}/side-chats/${segment(sideChatID)}`, signalOf(options)),
925
+ refreshSideChat: (id, sideChatID, expectedRevision, options = {}) => this.call("POST", `${AGENT2}/workspaces/${segment(id)}/side-chats/${segment(sideChatID)}/refresh`, { body: { expectedRevision }, ...signalOf(options) }),
926
+ sendToMain: (id, sideChatID, text, options = {}) => this.call("POST", `${AGENT2}/workspaces/${segment(id)}/side-chats/${segment(sideChatID)}/send`, { body: { text, confirmed: true }, ...signalOf(options) })
786
927
  };
787
928
  artifacts = artifactsApi(this);
788
929
  teams = teamsApi(this);
789
930
  concierge = conciergeApi(this);
931
+ speech = speechApi(this);
932
+ conversation = conversationApi(this);
933
+ appDecisions = appDecisionsApi(this);
790
934
  constructor(options) {
791
935
  this.url = options.url;
792
936
  this.#base = options.url.replace(/\/+$/, "");
@@ -795,7 +939,8 @@ class HubClient {
795
939
  }
796
940
  async request(method, pathname, options = {}) {
797
941
  const target = `${pathname}${queryString(options.query)}`;
798
- const headers = { ...options.headers, authorization: `Bearer ${this.#token}`, accept: "application/json" };
942
+ const token = typeof this.#token === "string" ? this.#token : await this.#token();
943
+ const headers = { ...options.headers, authorization: `Bearer ${token}`, accept: "application/json" };
799
944
  if (options.body !== undefined)
800
945
  headers["content-type"] = "application/json";
801
946
  if (options.raw)
@@ -842,26 +987,26 @@ class HubClient {
842
987
  }
843
988
  }
844
989
  whoami(options = {}) {
845
- return this.call("GET", `${AGENT}/whoami`, signalOf(options));
990
+ return this.call("GET", `${AGENT2}/whoami`, signalOf(options));
846
991
  }
847
992
  status(options = {}) {
848
- return this.call("GET", `${AGENT}/status`, signalOf(options));
993
+ return this.call("GET", `${AGENT2}/status`, signalOf(options));
849
994
  }
850
995
  sessions(filter = {}) {
851
996
  const state = typeof filter.state === "string" ? filter.state : filter.state?.join(",");
852
- return this.call("GET", `${AGENT}/sessions`, {
997
+ return this.call("GET", `${AGENT2}/sessions`, {
853
998
  query: { project: filter.project, state, machine: filter.machine, label: filter.label, q: filter.q },
854
999
  ...signalOf(filter)
855
1000
  });
856
1001
  }
857
1002
  show(addr, options = {}) {
858
- return this.call("GET", `${AGENT}/sessions/${segment(addr)}`, signalOf(options));
1003
+ return this.call("GET", `${AGENT2}/sessions/${segment(addr)}`, signalOf(options));
859
1004
  }
860
1005
  log(addr, options = {}) {
861
- return this.call("GET", `${AGENT}/sessions/${segment(addr)}/log`, { query: { n: options.n }, ...signalOf(options) });
1006
+ return this.call("GET", `${AGENT2}/sessions/${segment(addr)}/log`, { query: { n: options.n }, ...signalOf(options) });
862
1007
  }
863
1008
  who(options = {}) {
864
- return this.call("GET", `${AGENT}/who`, { query: { project: options.project, role: options.role === undefined ? undefined : bareRole(options.role), team: options.team }, ...signalOf(options) });
1009
+ return this.call("GET", `${AGENT2}/who`, { query: { project: options.project, role: options.role === undefined ? undefined : bareRole(options.role), team: options.team }, ...signalOf(options) });
865
1010
  }
866
1011
  async* watch(options = {}) {
867
1012
  const { signal } = options;
@@ -869,7 +1014,7 @@ class HubClient {
869
1014
  return;
870
1015
  let response;
871
1016
  try {
872
- response = await this.request("GET", `${AGENT}/watch`, { query: { blocked: options.blocked ? "1" : undefined, session: options.session, cursor: options.cursor }, ...signalOf(options) });
1017
+ response = await this.request("GET", `${AGENT2}/watch`, { query: { blocked: options.blocked ? "1" : undefined, session: options.session, cursor: options.cursor }, ...signalOf(options) });
873
1018
  } catch (cause) {
874
1019
  if (signal?.aborted)
875
1020
  return;
@@ -878,49 +1023,61 @@ class HubClient {
878
1023
  yield* sseEvents(response, signal);
879
1024
  }
880
1025
  events(options = {}) {
881
- return this.call("GET", `${AGENT}/events`, { query: { cursor: options.cursor, wait: options.wait, limit: options.limit, type: options.types, session: options.sessions }, ...signalOf(options) });
1026
+ return this.call("GET", `${AGENT2}/events`, { query: { cursor: options.cursor, wait: options.wait, limit: options.limit, type: options.types, session: options.sessions }, ...signalOf(options) });
882
1027
  }
883
1028
  webhookCreate(input, options = {}) {
884
- return this.call("POST", `${AGENT}/webhooks`, { body: input, ...signalOf(options) });
1029
+ return this.call("POST", `${AGENT2}/webhooks`, { body: input, ...signalOf(options) });
885
1030
  }
886
1031
  webhooks(options = {}) {
887
- return this.call("GET", `${AGENT}/webhooks`, signalOf(options));
1032
+ return this.call("GET", `${AGENT2}/webhooks`, signalOf(options));
888
1033
  }
889
1034
  webhook(id, options = {}) {
890
- return this.call("GET", `${AGENT}/webhooks/${segment(id)}`, signalOf(options));
1035
+ return this.call("GET", `${AGENT2}/webhooks/${segment(id)}`, signalOf(options));
891
1036
  }
892
1037
  webhookDelete(id, options = {}) {
893
- return this.call("DELETE", `${AGENT}/webhooks/${segment(id)}`, signalOf(options));
1038
+ return this.call("DELETE", `${AGENT2}/webhooks/${segment(id)}`, signalOf(options));
1039
+ }
1040
+ subscribe(input, options = {}) {
1041
+ return this.call("POST", `${AGENT2}/subscriptions`, { body: input, ...signalOf(options) });
1042
+ }
1043
+ subscriptions(options = {}) {
1044
+ return this.call("GET", `${AGENT2}/subscriptions`, { query: { session: options.session }, ...signalOf(options) });
1045
+ }
1046
+ subscription(id, options = {}) {
1047
+ return this.call("GET", `${AGENT2}/subscriptions/${segment(id)}`, signalOf(options));
1048
+ }
1049
+ unsubscribe(id, options = {}) {
1050
+ return this.call("DELETE", `${AGENT2}/subscriptions/${segment(id)}`, signalOf(options));
894
1051
  }
895
1052
  setArchived(addr, archived, options = {}) {
896
- return this.call("PUT", `${AGENT}/sessions/${segment(addr)}/archive`, { body: { archived }, ...signalOf(options) });
1053
+ return this.call("PUT", `${AGENT2}/sessions/${segment(addr)}/archive`, { body: { archived }, ...signalOf(options) });
897
1054
  }
898
1055
  setPinned(addr, pinned, options = {}) {
899
- return this.call("PUT", `${AGENT}/sessions/${segment(addr)}/pin`, { body: { pinned }, ...signalOf(options) });
1056
+ return this.call("PUT", `${AGENT2}/sessions/${segment(addr)}/pin`, { body: { pinned }, ...signalOf(options) });
900
1057
  }
901
1058
  intent(addr, options = {}) {
902
- return this.call("GET", `${AGENT}/sessions/${segment(addr)}/intent`, signalOf(options));
1059
+ return this.call("GET", `${AGENT2}/sessions/${segment(addr)}/intent`, signalOf(options));
903
1060
  }
904
1061
  setIntent(addr, text, expectedRevision, options = {}) {
905
- return this.call("PUT", `${AGENT}/sessions/${segment(addr)}/intent`, { body: { text, expectedRevision }, ...signalOf(options) });
1062
+ return this.call("PUT", `${AGENT2}/sessions/${segment(addr)}/intent`, { body: { text, expectedRevision }, ...signalOf(options) });
906
1063
  }
907
1064
  previewArchiveInactive(inactiveFor, limit, options = {}) {
908
- return this.call("POST", `${AGENT}/sessions/archive-inactive`, { body: { preview: true, inactiveFor, limit }, ...signalOf(options) });
1065
+ return this.call("POST", `${AGENT2}/sessions/archive-inactive`, { body: { preview: true, inactiveFor, limit }, ...signalOf(options) });
909
1066
  }
910
1067
  archiveInactive(previewToken, options = {}) {
911
- return this.call("POST", `${AGENT}/sessions/archive-inactive`, { body: { previewToken, confirm: true }, ...signalOf(options) });
1068
+ return this.call("POST", `${AGENT2}/sessions/archive-inactive`, { body: { previewToken, confirm: true }, ...signalOf(options) });
912
1069
  }
913
1070
  name(name, options = {}) {
914
- return this.call("POST", `${AGENT}/name`, { body: { name, ...options.session ? { session: options.session } : {} }, ...signalOf(options) });
1071
+ return this.call("POST", `${AGENT2}/name`, { body: { name, ...options.session ? { session: options.session } : {} }, ...signalOf(options) });
915
1072
  }
916
1073
  claimRole(role, options = {}) {
917
- return this.call("POST", `${AGENT}/roles/claim`, { body: { role: bareRole(role), ...options.exclusive ? { exclusive: true } : {}, ...roleTarget(options) }, ...signalOf(options) });
1074
+ return this.call("POST", `${AGENT2}/roles/claim`, { body: { role: bareRole(role), ...options.exclusive ? { exclusive: true } : {}, ...roleTarget(options) }, ...signalOf(options) });
918
1075
  }
919
1076
  releaseRole(role, options = {}) {
920
- return this.call("POST", `${AGENT}/roles/release`, { body: { role: bareRole(role), ...roleTarget(options) }, ...signalOf(options) });
1077
+ return this.call("POST", `${AGENT2}/roles/release`, { body: { role: bareRole(role), ...roleTarget(options) }, ...signalOf(options) });
921
1078
  }
922
1079
  msg(to, text, options = {}) {
923
- return this.call("POST", `${AGENT}/msg`, { body: { to, text, ...sendFields(options), ...options.all ? { all: true } : {} }, ...idempotent(options) });
1080
+ return this.call("POST", `${AGENT2}/msg`, { body: { to, text, ...sendFields(options), ...options.all ? { all: true } : {} }, ...idempotent(options) });
924
1081
  }
925
1082
  async ask(to, text, options = {}) {
926
1083
  if (options.all && options.wait !== undefined)
@@ -937,7 +1094,7 @@ class HubClient {
937
1094
  ...options.wait !== undefined ? { wait: first } : {},
938
1095
  ...decisionFields(options)
939
1096
  };
940
- const asked = await this.call("POST", `${AGENT}/ask`, { body, ...idempotent(options) });
1097
+ const asked = await this.call("POST", `${AGENT2}/ask`, { body, ...idempotent(options) });
941
1098
  if ("sent" in asked)
942
1099
  return asked;
943
1100
  let current = asked;
@@ -949,28 +1106,28 @@ class HubClient {
949
1106
  return current;
950
1107
  }
951
1108
  message(id, options = {}) {
952
- return this.call("GET", `${AGENT}/messages/${segment(id)}`, { query: { wait: options.wait }, ...signalOf(options) });
1109
+ return this.call("GET", `${AGENT2}/messages/${segment(id)}`, { query: { wait: options.wait }, ...signalOf(options) });
953
1110
  }
954
1111
  reply(id, text, options = {}) {
955
- return this.call("POST", `${AGENT}/reply`, { body: { replyTo: id, text, ...options.wake !== undefined ? { wake: options.wake } : {} }, ...signalOf(options) });
1112
+ return this.call("POST", `${AGENT2}/reply`, { body: { replyTo: id, text, ...options.wake !== undefined ? { wake: options.wake } : {} }, ...signalOf(options) });
956
1113
  }
957
1114
  decline(id, why, options = {}) {
958
- return this.call("POST", `${AGENT}/decline`, { body: { replyTo: id, why }, ...signalOf(options) });
1115
+ return this.call("POST", `${AGENT2}/decline`, { body: { replyTo: id, why }, ...signalOf(options) });
959
1116
  }
960
1117
  inbox(options = {}) {
961
- return this.call("GET", `${AGENT}/inbox`, signalOf(options));
1118
+ return this.call("GET", `${AGENT2}/inbox`, signalOf(options));
962
1119
  }
963
1120
  mute(session, options = {}) {
964
- return this.call("POST", `${AGENT}/mute`, { body: { session }, ...signalOf(options) });
1121
+ return this.call("POST", `${AGENT2}/mute`, { body: { session }, ...signalOf(options) });
965
1122
  }
966
1123
  unmute(session, options = {}) {
967
- return this.call("POST", `${AGENT}/unmute`, { body: { session }, ...signalOf(options) });
1124
+ return this.call("POST", `${AGENT2}/unmute`, { body: { session }, ...signalOf(options) });
968
1125
  }
969
1126
  decisions(options = {}) {
970
- return this.call("GET", `${AGENT}/decisions`, { query: { state: options.state }, ...signalOf(options) });
1127
+ return this.call("GET", `${AGENT2}/decisions`, { query: { state: options.state }, ...signalOf(options) });
971
1128
  }
972
1129
  decision(id, options = {}) {
973
- return this.call("GET", `${AGENT}/decisions/${segment(id)}`, signalOf(options));
1130
+ return this.call("GET", `${AGENT2}/decisions/${segment(id)}`, signalOf(options));
974
1131
  }
975
1132
  answerDecision(id, answer) {
976
1133
  const { signal: _signal, ...body } = answer;
@@ -978,16 +1135,16 @@ class HubClient {
978
1135
  }
979
1136
  approve(addr, requestID, options = {}) {
980
1137
  const body = { requestID, ...options.always ? { always: true } : {}, ...options.recommend ? { recommend: true } : {}, ...whyField(options) };
981
- return this.call("POST", `${AGENT}/sessions/${segment(addr)}/approve`, { body, ...signalOf(options) });
1138
+ return this.call("POST", `${AGENT2}/sessions/${segment(addr)}/approve`, { body, ...signalOf(options) });
982
1139
  }
983
1140
  deny(addr, requestID, options = {}) {
984
- return this.call("POST", `${AGENT}/sessions/${segment(addr)}/deny`, { body: { requestID, ...whyField(options) }, ...signalOf(options) });
1141
+ return this.call("POST", `${AGENT2}/sessions/${segment(addr)}/deny`, { body: { requestID, ...whyField(options) }, ...signalOf(options) });
985
1142
  }
986
1143
  answer(addr, formID, answer, options = {}) {
987
- return this.call("POST", `${AGENT}/sessions/${segment(addr)}/answer`, { body: { formID, answer, ...whyField(options) }, ...signalOf(options) });
1144
+ return this.call("POST", `${AGENT2}/sessions/${segment(addr)}/answer`, { body: { formID, answer, ...whyField(options) }, ...signalOf(options) });
988
1145
  }
989
1146
  cancelForm(addr, formID, options = {}) {
990
- return this.call("POST", `${AGENT}/sessions/${segment(addr)}/answer`, { body: { formID, cancel: true, ...whyField(options) }, ...signalOf(options) });
1147
+ return this.call("POST", `${AGENT2}/sessions/${segment(addr)}/answer`, { body: { formID, cancel: true, ...whyField(options) }, ...signalOf(options) });
991
1148
  }
992
1149
  prompt(addr, text, options = {}) {
993
1150
  const body = {
@@ -997,38 +1154,38 @@ class HubClient {
997
1154
  ...options.wake !== undefined ? { wake: options.wake } : {},
998
1155
  ...whyField(options)
999
1156
  };
1000
- return this.call("POST", `${AGENT}/sessions/${segment(addr)}/prompt`, { body, ...signalOf(options) });
1157
+ return this.call("POST", `${AGENT2}/sessions/${segment(addr)}/prompt`, { body, ...signalOf(options) });
1001
1158
  }
1002
1159
  stop(addr, options = {}) {
1003
- return this.call("POST", `${AGENT}/sessions/${segment(addr)}/stop`, { body: whyField(options), ...signalOf(options) });
1160
+ return this.call("POST", `${AGENT2}/sessions/${segment(addr)}/stop`, { body: whyField(options), ...signalOf(options) });
1004
1161
  }
1005
1162
  grants(options = {}) {
1006
- return this.call("GET", `${AGENT}/grants`, signalOf(options));
1163
+ return this.call("GET", `${AGENT2}/grants`, signalOf(options));
1007
1164
  }
1008
1165
  grant(id, options = {}) {
1009
- return this.call("GET", `${AGENT}/grants/${segment(id)}`, signalOf(options));
1166
+ return this.call("GET", `${AGENT2}/grants/${segment(id)}`, signalOf(options));
1010
1167
  }
1011
1168
  createGrant(spec, options = {}) {
1012
- return this.call("POST", `${AGENT}/grants`, { body: spec, ...signalOf(options) });
1169
+ return this.call("POST", `${AGENT2}/grants`, { body: spec, ...signalOf(options) });
1013
1170
  }
1014
1171
  updateGrant(id, spec, options = {}) {
1015
- return this.call("PUT", `${AGENT}/grants/${segment(id)}`, { body: spec, ...signalOf(options) });
1172
+ return this.call("PUT", `${AGENT2}/grants/${segment(id)}`, { body: spec, ...signalOf(options) });
1016
1173
  }
1017
1174
  revokeGrant(id, options = {}) {
1018
- return this.call("DELETE", `${AGENT}/grants/${segment(id)}`, signalOf(options));
1175
+ return this.call("DELETE", `${AGENT2}/grants/${segment(id)}`, signalOf(options));
1019
1176
  }
1020
1177
  requestGrant(grant, why, options = {}) {
1021
- return this.call("POST", `${AGENT}/grants/requests`, { body: { grant, why }, ...signalOf(options) });
1178
+ return this.call("POST", `${AGENT2}/grants/requests`, { body: { grant, why }, ...signalOf(options) });
1022
1179
  }
1023
1180
  createAppToken(input = {}) {
1024
1181
  const { signal: _signal, ...body } = input;
1025
- return this.call("POST", `${AGENT}/app-tokens`, { body, ...signalOf(input) });
1182
+ return this.call("POST", `${AGENT2}/app-tokens`, { body, ...signalOf(input) });
1026
1183
  }
1027
1184
  appTokens(options = {}) {
1028
- return this.call("GET", `${AGENT}/app-tokens`, signalOf(options));
1185
+ return this.call("GET", `${AGENT2}/app-tokens`, signalOf(options));
1029
1186
  }
1030
1187
  revokeAppToken(id, options = {}) {
1031
- return this.call("DELETE", `${AGENT}/app-tokens/${segment(id)}`, signalOf(options));
1188
+ return this.call("DELETE", `${AGENT2}/app-tokens/${segment(id)}`, signalOf(options));
1032
1189
  }
1033
1190
  }
1034
1191
  // packages/sdk/src/envelope.ts
@@ -1065,13 +1222,15 @@ function escapeBody(text) {
1065
1222
  `).map((line) => FORGEABLE.test(line) ? `\\${line}` : line).join(`
1066
1223
  `);
1067
1224
  }
1225
+ var NOTICE_ONLY_APPS = new Set(["subscriptions"]);
1226
+ var noticeOnly = (from) => from.kind === "app" && NOTICE_ONLY_APPS.has(from.tokenID);
1068
1227
  function renderEnvelope(input) {
1069
1228
  const { id, kind } = input;
1070
1229
  const to = sanitizeLabel(input.to);
1071
1230
  const target = kind === "reply" || kind === "decline" ? `to you · re ${sanitizeLabel(input.replyTo ?? "")}` : to.startsWith("@@") ? `to ${to} → you` : "to you";
1072
1231
  const header = `[hub] ${kind} from ${partyLabel(input.from)} ${target} · ${stamp(input.at)} · ${id}`;
1073
1232
  const boundary = `----- ${id} -----`;
1074
- const hint = kind === "ask" ? `↩ hub reply ${id} "…" · hub decline ${id} --why "…"${input.expiresAt === undefined ? "" : ` · expires ${stamp(input.expiresAt)}`}` : `↩ hub reply ${id} "…"`;
1233
+ const hint = noticeOnly(input.from) ? "↩ a notice: no reply needed (hub unsubscribe <sub_…> stops it)" : kind === "ask" ? `↩ hub reply ${id} "…" · hub decline ${id} --why "…"${input.expiresAt === undefined ? "" : ` · expires ${stamp(input.expiresAt)}`}` : `↩ hub reply ${id} "…"`;
1075
1234
  return [header, boundary, escapeBody(input.text), boundary, hint].join(`
1076
1235
  `);
1077
1236
  }
@@ -3591,7 +3750,7 @@ function profileTemplateParts(template) {
3591
3750
 
3592
3751
  // packages/protocol/src/concierge.ts
3593
3752
  var SESSION_CONTEXT_FEATURE = "sessionContext";
3594
- var SESSION_CONTEXT_KEYS = ["instructions", "board", "notes"];
3753
+ var SESSION_CONTEXT_KEYS = ["instructions", "board", "notes", "frontBoard"];
3595
3754
  var SESSION_CONTEXT_LIMITS = { partChars: 40000, denyGlobs: 50, globChars: 100, allowTools: 50 };
3596
3755
  var isRecord3 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
3597
3756
  var TOOL_GLOB = /^[A-Za-z0-9_.*-]+$/;
@@ -4166,6 +4325,19 @@ var RECORDING_LIMITS = {
4166
4325
  hubFlushMs: 2000,
4167
4326
  hubFlushEvents: 100
4168
4327
  };
4328
+ // packages/protocol/src/app-front.ts
4329
+ var ACCESS_TOKEN_TTL_MS = 60 * 60000;
4330
+ var REFRESH_TOKEN_IDLE_MS = 30 * 24 * 60 * 60000;
4331
+ var CONNECT_FLOW_TTL_MS = 10 * 60000;
4332
+ var APP_STREAM_LIMITS = {
4333
+ heartbeatMs: 20000,
4334
+ retryMs: 2000,
4335
+ recheckMs: 30000,
4336
+ bufferMaxBytes: 1 << 20,
4337
+ streamsPerApp: 4,
4338
+ streamsPerUser: 20
4339
+ };
4340
+ var LOOPBACK_HOSTS2 = new Set(["127.0.0.1", "[::1]", "localhost"]);
4169
4341
  // packages/protocol/src/index.ts
4170
4342
  var PROTOCOL_VERSION = 1;
4171
4343
  var FORM_ANSWER_MAX_KEYS = 50;
@@ -4249,10 +4421,12 @@ var IMPLIED_OPENCODE_FEATURES = [
4249
4421
  "expose",
4250
4422
  "create"
4251
4423
  ];
4252
- var OPTIONAL_SPOKE_FEATURES = [...IMPLIED_OPENCODE_FEATURES, "autoName", "sessionTokens", "deliver", "hubPrompt", "permissionMessage", "actTokens", "launch", "fork", "revert", "dismissError", "timelineMedia", PREVIEW_BUNDLE_FEATURE, "voiceFront"];
4424
+ var OPTIONAL_SPOKE_FEATURES = [...IMPLIED_OPENCODE_FEATURES, "autoName", "sessionTokens", "deliver", "hubPrompt", "permissionMessage", "actTokens", "launch", "fork", "revert", "dismissError", "timelineMedia", PREVIEW_BUNDLE_FEATURE, "voiceFront", "conciergeFront", "inboxCarried", "setModel"];
4253
4425
  var CORE_OPS = ["turn", "steer", "interrupt", "stop", "permission", "form"];
4254
4426
  var KNOWN_SPOKE_FEATURES = [...CORE_OPS, ...OPTIONAL_SPOKE_FEATURES, "turnEvents", PROFILE_APPLY_FEATURE, SESSION_CONTEXT_FEATURE];
4255
4427
  var SPOKE_FEATURES = [...OPTIONAL_SPOKE_FEATURES, PROFILE_APPLY_FEATURE, SESSION_CONTEXT_FEATURE];
4428
+ var RELAY_TEXT_MAX = 4000;
4429
+ var FRONT_UPDATE_MAX = 8000;
4256
4430
  var SESSION_TOKENS_MAX = 500;
4257
4431
  var SESSION_TOKEN_MAX_CHARS = 4096;
4258
4432
  var MESSAGE_KINDS = ["msg", "ask", "reply", "decline", "primer", "decision", "prompt"];
@@ -4269,6 +4443,8 @@ function featureForOp(op) {
4269
4443
  return "revert";
4270
4444
  case "removeFront":
4271
4445
  return "voiceFront";
4446
+ case "frontUpdate":
4447
+ return "conciergeFront";
4272
4448
  default:
4273
4449
  return op;
4274
4450
  }
@@ -4359,6 +4535,9 @@ function isSpokeRequest(v) {
4359
4535
  return nonEmpty(v.sessionID);
4360
4536
  if (v.op === "openLink")
4361
4537
  return (v.sessionID === undefined || nonEmpty(v.sessionID)) && (v.voice === undefined || typeof v.voice === "boolean");
4538
+ if (v.op === "relayToSession") {
4539
+ return nonEmpty(v.frontID) && v.frontID.length <= 300 && nonEmpty(v.session) && v.session.length <= 300 && str2(v.text) && v.text.trim().length > 0 && v.text.length <= RELAY_TEXT_MAX && (v.previous === undefined || typeof v.previous === "boolean") && (v.confirm === undefined || typeof v.confirm === "boolean");
4540
+ }
4362
4541
  return false;
4363
4542
  }
4364
4543
  var PROMPT_IMAGE_MIMES = ["image/png", "image/jpeg", "image/webp", "image/gif"];
@@ -4441,6 +4620,8 @@ function isServerRequest(v) {
4441
4620
  return nonEmpty(v.sessionID);
4442
4621
  case "setModelPrefs":
4443
4622
  return nonEmpty(v.sessionID) && (v.voice === undefined || v.voice === null || isModelSelection(v.voice)) && (v.worker === undefined || v.worker === null || isModelSelection(v.worker)) && (v.voice !== undefined || v.worker !== undefined);
4623
+ case "setModel":
4624
+ return nonEmpty(v.sessionID) && isModelSelection(v.model);
4444
4625
  case "timeline":
4445
4626
  return nonEmpty(v.sessionID) && (v.limit === undefined || Number.isInteger(v.limit) && Number(v.limit) >= 1 && Number(v.limit) <= 200) && (v.cursor === undefined || str2(v.cursor) && v.cursor.length <= 4096) && (v.order === undefined || v.order === "asc" || v.order === "desc") && (v.before === undefined || nonEmpty(v.before) && v.before.length <= 4096) && (v.cursor === undefined || v.before === undefined && v.order !== "desc");
4446
4627
  case "timelinePart":
@@ -4483,6 +4664,8 @@ function isServerRequest(v) {
4483
4664
  case "dismissError":
4484
4665
  case "removeFront":
4485
4666
  return nonEmpty(v.sessionID);
4667
+ case "frontUpdate":
4668
+ return nonEmpty(v.sessionID) && str2(v.text) && v.text.trim().length > 0 && v.text.length <= FRONT_UPDATE_MAX && (v.finished === undefined || typeof v.finished === "boolean");
4486
4669
  default:
4487
4670
  return false;
4488
4671
  }
@@ -4591,8 +4774,22 @@ var JOB_STATUSES = new Set(["running", "exited", "timeout", "killed"]);
4591
4774
  var DEFAULT_LIST = Object.freeze({ days: 30, max: 100 });
4592
4775
  var LIST_LIMITS = { days: 365, max: 500 };
4593
4776
  var DEFAULT_SERVER = "http://127.0.0.1:4097";
4594
- var DEFAULT_SUBAGENT = "general";
4595
- var AGENT_ID = /^[A-Za-z0-9_.-]{1,64}$/;
4777
+ var OBSOLETE_CONFIG_KEYS = ["subagent", "workModel"];
4778
+ var OBSOLETE_WHY = {
4779
+ subagent: "the voice agent that delegated to it is gone",
4780
+ workModel: "the voice agent whose workers used it is gone"
4781
+ };
4782
+ var warnedKeys = new Set;
4783
+ function warnObsoleteKeys(raw, log = console.warn, seen = warnedKeys) {
4784
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw))
4785
+ return;
4786
+ for (const key of OBSOLETE_CONFIG_KEYS) {
4787
+ if (!(key in raw) || seen.has(key))
4788
+ continue;
4789
+ seen.add(key);
4790
+ log(`[hub] config.json: "${key}" is obsolete (${OBSOLETE_WHY[key]}: Voice mode always uses the voice front now); it is ignored and removed on the next save`);
4791
+ }
4792
+ }
4596
4793
  var MODEL_REF = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.:\/-]+(?:#[A-Za-z0-9_.-]+)?$/;
4597
4794
  var modelRef = (v) => typeof v === "string" && v.length <= 200 && MODEL_REF.test(v) ? v : undefined;
4598
4795
  function configPath(env = process.env, home = homedir()) {
@@ -4609,7 +4806,7 @@ function prepareConfigPath(env = process.env, home = homedir(), log) {
4609
4806
  return file;
4610
4807
  }
4611
4808
  function defaults(machineName) {
4612
- return { server: DEFAULT_SERVER, mode: "off", sessions: [], machineName, subagent: DEFAULT_SUBAGENT, autoStartLocalHub: true, herdr: true, list: { ...DEFAULT_LIST } };
4809
+ return { server: DEFAULT_SERVER, mode: "off", sessions: [], machineName, autoStartLocalHub: true, herdr: true, list: { ...DEFAULT_LIST } };
4613
4810
  }
4614
4811
  function normalize(raw, machineName) {
4615
4812
  const base = defaults(machineName);
@@ -4624,12 +4821,10 @@ function normalize(raw, machineName) {
4624
4821
  mode,
4625
4822
  sessions,
4626
4823
  machineName: typeof r.machineName === "string" && r.machineName ? r.machineName : machineName,
4627
- subagent: typeof r.subagent === "string" && AGENT_ID.test(r.subagent) ? r.subagent : base.subagent,
4628
4824
  autoStartLocalHub: r.autoStartLocalHub !== false,
4629
4825
  herdr: r.herdr !== false,
4630
4826
  list: listWindow(r.list),
4631
4827
  ...modelRef(r.voiceModel) ? { voiceModel: modelRef(r.voiceModel) } : {},
4632
- ...modelRef(r.workModel) ? { workModel: modelRef(r.workModel) } : {},
4633
4828
  ...typeof r.machineID === "string" && r.machineID ? { machineID: r.machineID } : {},
4634
4829
  ...typeof r.token === "string" && r.token ? { token: r.token } : {}
4635
4830
  };
@@ -4644,7 +4839,9 @@ function listWindow(raw) {
4644
4839
  }
4645
4840
  function readConfig(file, machineName) {
4646
4841
  try {
4647
- return normalize(JSON.parse(readFileSync3(file, "utf8")), machineName);
4842
+ const raw = JSON.parse(readFileSync3(file, "utf8"));
4843
+ warnObsoleteKeys(raw);
4844
+ return normalize(raw, machineName);
4648
4845
  } catch {
4649
4846
  return defaults(machineName);
4650
4847
  }
@@ -5721,7 +5918,7 @@ function normalizeDirectories(raw, field, problems, options) {
5721
5918
  }
5722
5919
  return out;
5723
5920
  }
5724
- var LOOPBACK_HOSTS2 = new Set(["127.0.0.1", "[::1]", "localhost"]);
5921
+ var LOOPBACK_HOSTS3 = new Set(["127.0.0.1", "[::1]", "localhost"]);
5725
5922
  function loopbackOrigin(raw) {
5726
5923
  if (typeof raw !== "string" || raw.length > 2048)
5727
5924
  return;
@@ -5733,7 +5930,7 @@ function loopbackOrigin(raw) {
5733
5930
  }
5734
5931
  if (url.protocol !== "http:" && url.protocol !== "https:")
5735
5932
  return;
5736
- if (url.username || url.password || !LOOPBACK_HOSTS2.has(url.hostname))
5933
+ if (url.username || url.password || !LOOPBACK_HOSTS3.has(url.hostname))
5737
5934
  return;
5738
5935
  if (url.pathname !== "/" && url.pathname !== "" || url.search || url.hash)
5739
5936
  return;
@@ -9227,7 +9424,7 @@ import { arch as osArch, platform as osPlatform } from "node:os";
9227
9424
  // packages/node/package.json
9228
9425
  var package_default2 = {
9229
9426
  name: "@huynle/hub-node",
9230
- version: "0.9.2",
9427
+ version: "0.10.1",
9231
9428
  private: true,
9232
9429
  description: "`hub node`: the per-machine daemon that prepares, collects and cleans up launch workspaces (internal; shipped inside @huynle/hub, not published on its own).",
9233
9430
  license: "MIT",
@@ -10682,7 +10879,7 @@ import path14 from "node:path";
10682
10879
  // packages/runtime-openshell/package.json
10683
10880
  var package_default3 = {
10684
10881
  name: "@huynle/hub-runtime-openshell",
10685
- version: "0.9.2",
10882
+ version: "0.10.1",
10686
10883
  private: true,
10687
10884
  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).",
10688
10885
  license: "MIT",
@@ -12337,6 +12534,46 @@ function openShellCheckText(report) {
12337
12534
  `);
12338
12535
  }
12339
12536
 
12537
+ // packages/cli/src/subscriptions.ts
12538
+ var SUBSCRIBE_FLAGS = { type: "multi", delivery: "string", batch: "string", expires: "string", "max-per-hour": "string", subscriber: "string", note: "string" };
12539
+ var DELIVERIES2 = ["queue", "steer", "silent"];
12540
+ var safe2 = (v, max = 120) => sanitizeLabel(String(v), max);
12541
+ function subscribeInput(addrs, flags) {
12542
+ if (!addrs.length)
12543
+ throw new UsageError("needs at least one address (a session, @@role, #team or children)");
12544
+ const delivery = str(flags, "delivery");
12545
+ if (delivery !== undefined && !DELIVERIES2.includes(delivery))
12546
+ throw new UsageError(`--delivery is one of ${DELIVERIES2.join(", ")}`);
12547
+ const batch = str(flags, "batch");
12548
+ const expires = str(flags, "expires");
12549
+ const max = str(flags, "max-per-hour");
12550
+ if (max !== undefined && !/^\d+$/.test(max))
12551
+ throw new UsageError("--max-per-hour takes a whole number (1–120)");
12552
+ const types = flags.type;
12553
+ const subscriber = str(flags, "subscriber");
12554
+ const note = str(flags, "note");
12555
+ return {
12556
+ to: addrs.length === 1 ? addrs[0] : [...addrs],
12557
+ ...types?.length ? { types: types.flatMap((t) => t.split(",")).map((t) => t.trim()).filter(Boolean) } : {},
12558
+ ...delivery !== undefined ? { delivery } : {},
12559
+ ...batch !== undefined ? { batch: durationArg(batch, "--batch") } : {},
12560
+ ...expires !== undefined ? { expires: durationArg(expires, "--expires") } : {},
12561
+ ...max !== undefined ? { maxPerHour: Number(max) } : {},
12562
+ ...subscriber !== undefined ? { subscriber } : {},
12563
+ ...note !== undefined ? { note } : {}
12564
+ };
12565
+ }
12566
+ var targets = (s) => s.targets.map((t) => t.kind === "children" ? "its subagents" : safe2(t.addr, 80)).join(", ");
12567
+ var iso4 = (ms) => new Date(ms).toISOString().slice(0, 16) + "Z";
12568
+ function subscriptionLine(s) {
12569
+ const extra = [safe2(s.delivery, 10), `batch ${Math.round(s.batchMs / 1000)}s`, `≤${s.maxPerHour}/h`, `expires ${iso4(s.expiresAt)}`, `${s.stats.delivered} delivered`, ...s.pending ? [`${s.pending} pending`] : [], ...s.stats.dropped ? [`${s.stats.dropped} dropped`] : [], ...s.stats.suppressed ? [`${s.stats.suppressed} loop-suppressed`] : []];
12570
+ return `${safe2(s.id, 40)} ${safe2(s.subscriber, 80)} ← ${targets(s)} [${s.types.map((t) => safe2(t, 40)).join(",")}] ${extra.join(" · ")}${s.note ? ` "${safe2(s.note, 200)}"` : ""}`;
12571
+ }
12572
+ var subscribedText = (b) => `subscribed ${subscriptionLine(b.subscription)}
12573
+ the subscriber gets one hub message per batch window; stop with \`hub unsubscribe ${safe2(b.subscription.id, 40)}\``;
12574
+ var subscriptionsText = (b) => b.subscriptions.length ? b.subscriptions.map(subscriptionLine).join(`
12575
+ `) : "no subscriptions";
12576
+
12340
12577
  // packages/claude-code/src/main.ts
12341
12578
  import { spawnSync as spawnSync2 } from "node:child_process";
12342
12579
  import { realpathSync as realpathSync5 } from "node:fs";
@@ -13022,7 +13259,7 @@ function buildHarnessHello(options) {
13022
13259
  }
13023
13260
  // packages/spoke-sdk/src/harness-spoke.ts
13024
13261
  var INSPECT2 = Symbol.for("nodejs.util.inspect.custom");
13025
- var MODIFIER_NEEDS = { permissionMessage: "permission", launch: "create", hubPrompt: "deliver", turnEvents: null };
13262
+ var MODIFIER_NEEDS = { permissionMessage: "permission", launch: "create", hubPrompt: "deliver", turnEvents: null, inboxCarried: null };
13026
13263
  var ALWAYS = ["sessionTokens", "actTokens"];
13027
13264
  var CORE2 = CORE_OPS;
13028
13265
 
@@ -13445,7 +13682,7 @@ var LINE_MAX = 16 * 1024 * 1024;
13445
13682
  var STDERR_KEEP = 2000;
13446
13683
  var record2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v) ? v : {};
13447
13684
  var sleep6 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
13448
- var AGENT2 = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/;
13685
+ var AGENT3 = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/;
13449
13686
  var MODEL = /^[A-Za-z0-9][A-Za-z0-9._:@/\[\]-]{0,299}$/;
13450
13687
  var PROVIDERS = new Set(["anthropic", "claude-code"]);
13451
13688
  function launchDirectory(directory, roots) {
@@ -13475,7 +13712,7 @@ function launchDirectory(directory, roots) {
13475
13712
  function launchAgent(agent) {
13476
13713
  if (agent === undefined)
13477
13714
  return;
13478
- if (!AGENT2.test(agent))
13715
+ if (!AGENT3.test(agent))
13479
13716
  throw new Error(`Claude Code cannot take agent ${JSON.stringify(agent)} (letters, digits, ".", "_" and "-")`);
13480
13717
  return agent;
13481
13718
  }
@@ -14640,7 +14877,7 @@ import path23 from "node:path";
14640
14877
  // packages/claude-code/package.json
14641
14878
  var package_default4 = {
14642
14879
  name: "@huynle/hub-claude-code",
14643
- version: "0.9.2",
14880
+ version: "0.10.1",
14644
14881
  private: true,
14645
14882
  description: "The Claude Code hooks spoke: per-user daemon, hook bridge, plugin dir, `hub claude` wrapper and daemon-backed MCP server (internal; shipped inside @huynle/hub, not published on its own).",
14646
14883
  license: "MIT",
@@ -14929,7 +15166,7 @@ import path26 from "node:path";
14929
15166
  // packages/preset/package.json
14930
15167
  var package_default5 = {
14931
15168
  name: "@huynle/hub-preset",
14932
- version: "0.9.2",
15169
+ version: "0.10.1",
14933
15170
  private: true,
14934
15171
  description: "The preset spoke: drives a harness without a spoke of its own through a terminal pane (tmux), described by a data-only preset; official presets in presets/ (internal; shipped inside @huynle/hub, not published on its own).",
14935
15172
  license: "MIT",
@@ -15454,7 +15691,7 @@ import { openSync as openSync6, closeSync as closeSync6, readFileSync as readFil
15454
15691
  // packages/codex/package.json
15455
15692
  var package_default6 = {
15456
15693
  name: "@huynle/hub-codex",
15457
- version: "0.9.2",
15694
+ version: "0.10.1",
15458
15695
  private: true,
15459
15696
  description: "The Codex app-server spoke: attaches to Codex's app-server (its own over stdio, and the shared daemon's socket when present), and the `hub codex` wrapper (internal; shipped inside @huynle/hub, not published on its own).",
15460
15697
  license: "MIT",
@@ -16026,7 +16263,7 @@ class CodexSessions {
16026
16263
  const thread = this.#thread(req.sessionID);
16027
16264
  if (thread.turnID)
16028
16265
  throw new Error("The Codex session is busy (a turn is running); steer it instead");
16029
- const turnID = await this.#startTurn(thread, input(req.text, req.images), "user");
16266
+ const turnID = await this.#startTurn(thread, input(req.text, req.images), "carried");
16030
16267
  const end = await this.#waitTurn(thread, turnID);
16031
16268
  const reply = thread.reply?.text ?? "";
16032
16269
  if (!end)
@@ -16038,7 +16275,7 @@ class CodexSessions {
16038
16275
  async steer(req) {
16039
16276
  const thread = this.#thread(req.sessionID);
16040
16277
  const id = inboxID();
16041
- await this.#steerOrStart(thread, input(req.text, req.images), "user", id);
16278
+ await this.#steerOrStart(thread, input(req.text, req.images), "carried", id);
16042
16279
  return { inboxID: id };
16043
16280
  }
16044
16281
  async interrupt(req) {
@@ -16224,7 +16461,9 @@ class CodexSessions {
16224
16461
  const origin = clientID ? thread.inputs.get(clientID) : undefined;
16225
16462
  if (clientID)
16226
16463
  thread.inputs.delete(clientID);
16227
- this.#emit({ type: "session.inbox.delivered", data: { sessionID: thread.hubID, inboxID: plainID3(clientID && origin ? clientID : item.id), origin: origin ?? "user" } });
16464
+ const inboxID = plainID3(clientID && origin ? clientID : item.id);
16465
+ const data = origin === "hub" ? { origin: "hub" } : origin === "carried" ? { origin: "user", carried: true } : { origin: "user" };
16466
+ this.#emit({ type: "session.inbox.delivered", data: { sessionID: thread.hubID, inboxID, ...data } });
16228
16467
  return;
16229
16468
  }
16230
16469
  if (!TOOL_ITEMS.has(item.type))
@@ -16610,7 +16849,7 @@ function startCodexSpoke(options) {
16610
16849
  profileApply: profiles.handle
16611
16850
  },
16612
16851
  deliver: (req) => sessions.deliver(req),
16613
- features: ["hubPrompt", "launch", "turnEvents"],
16852
+ features: ["hubPrompt", "launch", "turnEvents", "inboxCarried"],
16614
16853
  onState: (state, error) => log(`hub connection ${state}${error ? `: ${error}` : ""}`),
16615
16854
  ...options.socket ? { socket: options.socket } : {},
16616
16855
  ...options.backoff ? { backoff: options.backoff } : {}
@@ -23277,7 +23516,7 @@ var PROVIDER_TIMEOUT_MS = 90000;
23277
23516
  var MAX_OUTPUT3 = 1024 * 1024;
23278
23517
  var VERSION_IN = /(?:^|[^0-9.])v?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)(?![0-9A-Za-z.-])/;
23279
23518
  var MODEL_LINE = /^[A-Za-z0-9][A-Za-z0-9._-]*\/\S+$/;
23280
- var safe2 = (detail) => isSecretValue(detail) ? "(details withheld: they looked like they carried a credential)" : detail.replace(/\s+/g, " ").trim().slice(0, 200);
23519
+ var safe3 = (detail) => isSecretValue(detail) ? "(details withheld: they looked like they carried a credential)" : detail.replace(/\s+/g, " ").trim().slice(0, 200);
23281
23520
  var errorText5 = (error) => error instanceof Error ? error.message : String(error);
23282
23521
  async function runOnce(options, context, argv, timeoutMs) {
23283
23522
  try {
@@ -23352,7 +23591,7 @@ async function verifyOpencode(context, options) {
23352
23591
  results = [await versionProbe2(options, context), await providerProbe(options, context), await spokeProbe2(options, context)];
23353
23592
  }
23354
23593
  const observedAt = context.now();
23355
- return { harness: "opencode", probes: results.map((p) => ({ ...p, observedAt, ...p.detail !== undefined ? { detail: safe2(p.detail) } : {} })) };
23594
+ return { harness: "opencode", probes: results.map((p) => ({ ...p, observedAt, ...p.detail !== undefined ? { detail: safe3(p.detail) } : {} })) };
23356
23595
  }
23357
23596
  function locateOpencodeBinary(probe) {
23358
23597
  const snap = discover(probe, { now: 0 });
@@ -25999,7 +26238,7 @@ Reading
25999
26238
 
26000
26239
  Identity
26001
26240
  hub whoami who this credential is
26002
- hub who [--project P] [--role R] [--team T] named and role-holding sessions (team members: their teams)
26241
+ hub who [--project P] [--role R] [--team T] online, named and role-holding sessions (team members: their teams)
26003
26242
  hub name <@name> [--session <addr>]
26004
26243
  hub intent <addr> [text… | --clear] what the session is for (one line, ≤ 280 characters, untrusted);
26005
26244
  no text: show it (else its title); a session token sets only its own
@@ -26116,6 +26355,15 @@ Codex (app-server spoke)
26116
26355
  hub codex-spoke install|uninstall [--dry-run] | status
26117
26356
  the spoke service as a launchd agent / systemd user unit (printed, never loaded)
26118
26357
 
26358
+ Subscriptions (a session hears other sessions' events as batched hub messages; docs/subscriptions.md)
26359
+ hub subscribe <addr|@@role|#team|children>... [--type T]... [--delivery queue|steer|silent] [--batch 30s]
26360
+ [--expires 1d] [--max-per-hour N] [--subscriber <addr>] [--note "…"]
26361
+ types: state, state:idle, blocker, message, launch, naming, presence,
26362
+ manage, exact types or * (default state, blocker); in a session's shell it
26363
+ subscribes itself, a login names --subscriber (app tokens: events:subscribe)
26364
+ hub subscriptions [--session <addr>] yours (or what a session subscribes to and what follows it)
26365
+ hub unsubscribe <subID>
26366
+
26119
26367
  Concierge watchlist (users; app tokens: sessions:read to show, sessions:manage to change)
26120
26368
  hub watchlist [show] what the Concierge watches now, and why; its config and revision
26121
26369
  hub watchlist watch|unwatch <addr> watch a session (a subagent: its root), or stop
@@ -26699,6 +26947,31 @@ ${openedText(body.decision)}` : outcome.text);
26699
26947
  answer(context, body, (b) => watchlistVerbText("unmute", b));
26700
26948
  }
26701
26949
  },
26950
+ subscribe: {
26951
+ args: ["addr..."],
26952
+ flags: SUBSCRIBE_FLAGS,
26953
+ async handle(context) {
26954
+ const input = subscribeInput(context.parsed.args, context.parsed.flags);
26955
+ const body = await context.hub((hub) => hub.subscribe(input));
26956
+ answer(context, body, subscribedText);
26957
+ }
26958
+ },
26959
+ subscriptions: {
26960
+ flags: { session: "string" },
26961
+ async handle(context) {
26962
+ const session = str(context.parsed.flags, "session");
26963
+ const body = await context.hub((hub) => hub.subscriptions(session !== undefined ? { session } : {}));
26964
+ answer(context, body, subscriptionsText);
26965
+ }
26966
+ },
26967
+ unsubscribe: {
26968
+ args: ["<subID>"],
26969
+ async handle(context) {
26970
+ const id = context.parsed.args[0];
26971
+ await context.hub((hub) => hub.unsubscribe(id));
26972
+ answer(context, { removed: id }, () => `removed ${id}`);
26973
+ }
26974
+ },
26702
26975
  logout: {
26703
26976
  async handle(context) {
26704
26977
  const file = loginFile(context.io.env, context.io.home);