@huynle/hub 0.10.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +17 -3
  3. package/package.json +1 -1
  4. package/packages/claude-code/package.json +1 -1
  5. package/packages/cli/dist/hub-cli.js +622 -394
  6. package/packages/cli/package.json +1 -1
  7. package/packages/cli/src/grants-mcp.ts +65 -0
  8. package/packages/cli/src/mcp-operations.ts +49 -0
  9. package/packages/cli/src/mcp.ts +6 -0
  10. package/packages/cli/src/subscriptions-mcp.ts +76 -0
  11. package/packages/cli/src/teams-mcp.ts +5 -3
  12. package/packages/codex/package.json +1 -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/src/teams.ts +4 -0
  17. package/packages/protocol/src/voice-recording.ts +39 -2
  18. package/packages/runtime-openshell/image/entry.sh +3 -1
  19. package/packages/runtime-openshell/image/start.sh +3 -1
  20. package/packages/runtime-openshell/package.json +1 -1
  21. package/packages/runtime-sdk/package.json +1 -1
  22. package/packages/sdk/package.json +1 -1
  23. package/packages/sdk/src/client.ts +13 -0
  24. package/packages/sdk/src/teams.ts +2 -0
  25. package/packages/spoke-sdk/package.json +1 -1
  26. package/src/central/agent/decisions.ts +7 -1
  27. package/src/central/agent/delivery.ts +39 -9
  28. package/src/central/agent/grant-routes.ts +14 -1
  29. package/src/central/agent/grants.ts +23 -0
  30. package/src/central/agent/launch-engine.ts +3 -3
  31. package/src/central/agent/message-limits.ts +3 -0
  32. package/src/central/agent/routes.ts +11 -0
  33. package/src/central/agent/subscriptions.ts +3 -1
  34. package/src/central/config.ts +9 -2
  35. package/src/central/http.ts +102 -20
  36. package/src/central/recording/export.ts +57 -3
  37. package/src/central/recording/index.ts +234 -16
  38. package/src/central/recording/ingest.ts +11 -2
  39. package/src/central/recording/routes.ts +61 -7
  40. package/src/central/recording/schema.ts +66 -2
  41. package/src/central/recording/settings.ts +19 -5
  42. package/src/central/recording/store.ts +19 -0
  43. package/src/central/recording/taps.ts +45 -1
  44. package/src/central/speech/openai.ts +4 -4
  45. package/src/central/speech/openrouter.ts +4 -4
  46. package/src/central/speech/provider.ts +32 -1
  47. package/src/central/speech/service.ts +6 -5
  48. package/src/central/teams/hop-stalls.ts +72 -0
  49. package/src/central/teams/policy.ts +10 -0
  50. package/src/central/teams/routes.ts +2 -0
  51. package/src/central/teams/seats.ts +3 -3
  52. package/src/central/teams/staffing.ts +5 -0
  53. package/src/central/teams/store.ts +5 -2
  54. package/src/central/teams/validation.ts +8 -5
  55. package/web/dist/assets/{ArtifactPage-CsPjam8D.js → ArtifactPage-C5MMqXLN.js} +1 -1
  56. package/web/dist/assets/{ArtifactPane-B9weO8L6.js → ArtifactPane-CXSCmhyJ.js} +1 -1
  57. package/web/dist/assets/{ArtifactWorkbench-DzKhMs3l.js → ArtifactWorkbench-S0RxNGWy.js} +1 -1
  58. package/web/dist/assets/{Diagram-CuRIffka.js → Diagram-BGmyRb-R.js} +1 -1
  59. package/web/dist/assets/{Library-rv5DGtrm.js → Library-CFmMbXEa.js} +2 -2
  60. package/web/dist/assets/{ProfilesDestination-DUuQ9jmd.js → ProfilesDestination-7jpOc2HM.js} +1 -1
  61. package/web/dist/assets/{TeamsPage-D_17MkvN.js → TeamsPage-C1NGrUR-.js} +1 -1
  62. package/web/dist/assets/{Watchtower-DlrNOJ7X.js → Watchtower-DnvlJaU0.js} +2 -2
  63. package/web/dist/assets/{artifacts-But87Jfo.js → artifacts-DlQEYmDl.js} +1 -1
  64. package/web/dist/assets/index-Da8SKXMw.js +33 -0
  65. package/web/dist/assets/{index-rW6LKQj1.css → index-Dyq-jBeE.css} +1 -1
  66. package/web/dist/assets/live-BmqLASSo.js +5 -0
  67. package/web/dist/assets/{pane-layout-BW0RdX5a.js → pane-layout-BAuu_pA3.js} +1 -1
  68. package/web/dist/assets/{renderer2d-DyANQ1WS.js → renderer2d-B2Nh39UN.js} +1 -1
  69. package/web/dist/assets/{renderer3d-vYYKFAJq.js → renderer3d-CTYbljHH.js} +1 -1
  70. package/web/dist/assets/{safe-area-B2Bk__FK.js → safe-area-CkhEclGo.js} +1 -1
  71. package/web/dist/assets/{silero-worker-e802lB5T.js → silero-worker-CC2etKS3.js} +1 -1
  72. package/web/dist/assets/{source-C-De8L3s.js → source-GYayxUxS.js} +1 -1
  73. package/web/dist/assets/{toast-CXTwTvDw.js → toast-q818mjUF.js} +1 -1
  74. package/web/dist/index.html +5 -5
  75. package/web/dist/assets/index-bX8B6NLo.js +0 -33
  76. package/web/dist/assets/live-BGLA-peO.js +0 -5
@@ -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.10.1",
10
+ version: "0.11.0",
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",
@@ -1162,6 +1162,15 @@ class HubClient {
1162
1162
  grants(options = {}) {
1163
1163
  return this.call("GET", `${AGENT2}/grants`, signalOf(options));
1164
1164
  }
1165
+ heldGrants(options = {}) {
1166
+ return this.call("GET", `${AGENT2}/grants/mine`, signalOf(options));
1167
+ }
1168
+ heldGrant(id, options = {}) {
1169
+ return this.call("GET", `${AGENT2}/grants/mine/${segment(id)}`, signalOf(options));
1170
+ }
1171
+ revokeHeldGrant(id, options = {}) {
1172
+ return this.call("DELETE", `${AGENT2}/grants/mine/${segment(id)}`, signalOf(options));
1173
+ }
1165
1174
  grant(id, options = {}) {
1166
1175
  return this.call("GET", `${AGENT2}/grants/${segment(id)}`, signalOf(options));
1167
1176
  }
@@ -4325,6 +4334,19 @@ var RECORDING_LIMITS = {
4325
4334
  hubFlushMs: 2000,
4326
4335
  hubFlushEvents: 100
4327
4336
  };
4337
+ var DEV_LIMITS = {
4338
+ clipsPerRecording: 200,
4339
+ clipPreMs: 3000,
4340
+ clipPostMs: 2000,
4341
+ clipBytes: 192 * 1024,
4342
+ clipGapMs: 2000,
4343
+ repliesPerRecording: 2000,
4344
+ replyBytes: 1024 * 1024,
4345
+ userHeadroomBytes: 50 * 1024 * 1024,
4346
+ recordingHeadroomBytes: 15 * 1024 * 1024,
4347
+ tracesPerMinute: 30,
4348
+ logsPerMinute: 20
4349
+ };
4328
4350
  // packages/protocol/src/app-front.ts
4329
4351
  var ACCESS_TOKEN_TTL_MS = 60 * 60000;
4330
4352
  var REFRESH_TOKEN_IDLE_MS = 30 * 24 * 60 * 60000;
@@ -6984,35 +7006,213 @@ ${intakeText(intake)}`;
6984
7006
  }
6985
7007
  ];
6986
7008
 
6987
- // packages/cli/src/teams-mcp.ts
7009
+ // packages/cli/src/subscriptions.ts
7010
+ var SUBSCRIBE_FLAGS = { type: "multi", delivery: "string", batch: "string", expires: "string", "max-per-hour": "string", subscriber: "string", note: "string" };
7011
+ var DELIVERIES2 = ["queue", "steer", "silent"];
7012
+ var safe = (v, max = 120) => sanitizeLabel(String(v), max);
7013
+ function subscribeInput(addrs, flags) {
7014
+ if (!addrs.length)
7015
+ throw new UsageError("needs at least one address (a session, @@role, #team or children)");
7016
+ const delivery = str(flags, "delivery");
7017
+ if (delivery !== undefined && !DELIVERIES2.includes(delivery))
7018
+ throw new UsageError(`--delivery is one of ${DELIVERIES2.join(", ")}`);
7019
+ const batch = str(flags, "batch");
7020
+ const expires = str(flags, "expires");
7021
+ const max = str(flags, "max-per-hour");
7022
+ if (max !== undefined && !/^\d+$/.test(max))
7023
+ throw new UsageError("--max-per-hour takes a whole number (1–120)");
7024
+ const types = flags.type;
7025
+ const subscriber = str(flags, "subscriber");
7026
+ const note = str(flags, "note");
7027
+ return {
7028
+ to: addrs.length === 1 ? addrs[0] : [...addrs],
7029
+ ...types?.length ? { types: types.flatMap((t) => t.split(",")).map((t) => t.trim()).filter(Boolean) } : {},
7030
+ ...delivery !== undefined ? { delivery } : {},
7031
+ ...batch !== undefined ? { batch: durationArg(batch, "--batch") } : {},
7032
+ ...expires !== undefined ? { expires: durationArg(expires, "--expires") } : {},
7033
+ ...max !== undefined ? { maxPerHour: Number(max) } : {},
7034
+ ...subscriber !== undefined ? { subscriber } : {},
7035
+ ...note !== undefined ? { note } : {}
7036
+ };
7037
+ }
7038
+ var targets = (s) => s.targets.map((t) => t.kind === "children" ? "its subagents" : safe(t.addr, 80)).join(", ");
7039
+ var iso3 = (ms) => new Date(ms).toISOString().slice(0, 16) + "Z";
7040
+ function subscriptionLine(s) {
7041
+ const extra = [safe(s.delivery, 10), `batch ${Math.round(s.batchMs / 1000)}s`, `≤${s.maxPerHour}/h`, `expires ${iso3(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`] : []];
7042
+ return `${safe(s.id, 40)} ${safe(s.subscriber, 80)} ← ${targets(s)} [${s.types.map((t) => safe(t, 40)).join(",")}] ${extra.join(" · ")}${s.note ? ` "${safe(s.note, 200)}"` : ""}`;
7043
+ }
7044
+ var subscribedText = (b) => `subscribed ${subscriptionLine(b.subscription)}
7045
+ the subscriber gets one hub message per batch window; stop with \`hub unsubscribe ${safe(b.subscription.id, 40)}\``;
7046
+ var subscriptionsText = (b) => b.subscriptions.length ? b.subscriptions.map(subscriptionLine).join(`
7047
+ `) : "no subscriptions";
7048
+
7049
+ // packages/cli/src/subscriptions-mcp.ts
6988
7050
  var STRING4 = { type: "string" };
6989
- var STRINGS = { type: "array", items: STRING4 };
7051
+ var need4 = (args, key) => {
7052
+ const value = args[key];
7053
+ if (typeof value !== "string" || !value.trim())
7054
+ throw new ToolArgError(`"${key}" is required (a non-empty string)`);
7055
+ return value;
7056
+ };
7057
+ var optional5 = (args, key) => {
7058
+ const value = args[key];
7059
+ if (value === undefined || value === null)
7060
+ return;
7061
+ if (typeof value !== "string")
7062
+ throw new ToolArgError(`"${key}" must be a string`);
7063
+ return value.trim() || undefined;
7064
+ };
7065
+ var array = (args, key) => {
7066
+ const value = args[key];
7067
+ if (value === undefined || value === null)
7068
+ return;
7069
+ if (!Array.isArray(value) || !value.every((v) => typeof v === "string"))
7070
+ throw new ToolArgError(`"${key}" must be a list of strings`);
7071
+ return value;
7072
+ };
7073
+ var number = (args, key, min, max) => {
7074
+ const value = args[key];
7075
+ if (value === undefined || value === null)
7076
+ return;
7077
+ if (typeof value !== "number" || !Number.isInteger(value) || value < min || value > max)
7078
+ throw new ToolArgError(`"${key}" must be a whole number from ${min} to ${max}`);
7079
+ return value;
7080
+ };
7081
+ var subscriptionTools = [
7082
+ {
7083
+ name: "subscribe",
7084
+ description: "Follow journal events for a subscriber session. A session subscribes itself; users and apps name subscriber. Apps need events:subscribe, sessions:read, relevant type scopes and messages:send unless silent. Hub checks visibility, rates and loops.",
7085
+ properties: {
7086
+ to: { type: ["string", "array"], items: STRING4, description: "One or more session/role/team addresses, or children." },
7087
+ subscriber: STRING4,
7088
+ types: { type: "array", items: STRING4 },
7089
+ delivery: { type: "string", enum: ["queue", "steer", "silent"] },
7090
+ batch: { type: "number", description: "Seconds per batch (1–3600)." },
7091
+ expires: { type: "number", description: "Seconds until expiry." },
7092
+ maxPerHour: { type: "integer", minimum: 1, maximum: 120 },
7093
+ note: STRING4
7094
+ },
7095
+ required: ["to"],
7096
+ readOnly: false,
7097
+ run: async (hub, args, signal) => {
7098
+ const to = typeof args.to === "string" && args.to.trim() ? args.to : array(args, "to");
7099
+ if (!to || Array.isArray(to) && !to.length)
7100
+ throw new ToolArgError('"to" needs at least one address');
7101
+ const delivery = optional5(args, "delivery");
7102
+ if (delivery && !["queue", "steer", "silent"].includes(delivery))
7103
+ throw new ToolArgError('"delivery" is queue, steer or silent');
7104
+ const subscriber = optional5(args, "subscriber");
7105
+ const types = array(args, "types");
7106
+ const batch = number(args, "batch", 1, 3600);
7107
+ const expires = number(args, "expires", 1, 30 * 86400);
7108
+ const maxPerHour = number(args, "maxPerHour", 1, 120);
7109
+ const note = optional5(args, "note");
7110
+ const input = { to, ...subscriber ? { subscriber } : {}, ...types ? { types } : {}, ...delivery ? { delivery } : {}, ...batch ? { batch } : {}, ...expires ? { expires } : {}, ...maxPerHour ? { maxPerHour } : {}, ...note ? { note } : {} };
7111
+ return subscribedText(await hub.subscribe(input, { signal }));
7112
+ }
7113
+ },
7114
+ {
7115
+ name: "subscriptions",
7116
+ description: "List subscriptions visible to this credential (session: its own; app: created by it; user: all). Optional session filter; route enforces scopes and tenancy.",
7117
+ properties: { session: STRING4 },
7118
+ readOnly: true,
7119
+ run: async (hub, args, signal) => subscriptionsText(await hub.subscriptions({ ...optional5(args, "session") ? { session: optional5(args, "session") } : {}, signal }))
7120
+ },
7121
+ {
7122
+ name: "unsubscribe",
7123
+ description: "Remove a subscription this credential owns. An unknown or another caller's ID answers not_found.",
7124
+ properties: { id: STRING4 },
7125
+ required: ["id"],
7126
+ readOnly: false,
7127
+ organizes: true,
7128
+ run: async (hub, args, signal) => {
7129
+ const id = need4(args, "id");
7130
+ await hub.unsubscribe(id, { signal });
7131
+ return `unsubscribed ${id}`;
7132
+ }
7133
+ }
7134
+ ];
7135
+
7136
+ // packages/cli/src/mcp-operations.ts
7137
+ var STRING5 = { type: "string" };
7138
+ var need5 = (args, key) => {
7139
+ const v = args[key];
7140
+ if (typeof v !== "string" || !v.trim())
7141
+ throw new ToolArgError(`"${key}" is required (a non-empty string)`);
7142
+ return v;
7143
+ };
7144
+ var userOnly = async (hub, signal) => {
7145
+ if ((await hub.whoami({ signal })).actor.kind !== "user")
7146
+ throw new CliError(EXIT.forbidden, "only the user can use this tool", "Use the user's device credential");
7147
+ };
7148
+ var operationTools = [
7149
+ {
7150
+ name: "session_model_set",
7151
+ description: "Set one existing session's model (providerID, modelID and optional variant), exactly as the console Model chip does. User device credential only; the console route checks ownership, catalog, availability and audit. An app or session cannot set it.",
7152
+ properties: { session: STRING5, model: { type: "object", properties: { providerID: STRING5, modelID: STRING5, variant: STRING5 }, required: ["providerID", "modelID"] } },
7153
+ required: ["session", "model"],
7154
+ readOnly: false,
7155
+ run: async (hub, args, signal) => {
7156
+ await userOnly(hub, signal);
7157
+ const model = args.model;
7158
+ if (typeof model !== "object" || model === null || Array.isArray(model))
7159
+ throw new ToolArgError('"model" is required (providerID, modelID, variant?)');
7160
+ const result = await hub.call("PUT", `/api/sessions/${encodeURIComponent(need5(args, "session"))}/model`, { body: { model }, signal });
7161
+ return JSON.stringify(result);
7162
+ }
7163
+ },
7164
+ {
7165
+ name: "machines",
7166
+ description: "The user's own enrolled machines, online status and processes. User device credential only; wraps GET /api/machines. No machine tokens or secrets.",
7167
+ properties: {},
7168
+ readOnly: true,
7169
+ run: async (hub, _args, signal) => {
7170
+ await userOnly(hub, signal);
7171
+ return JSON.stringify(await hub.call("GET", "/api/machines", { signal }));
7172
+ }
7173
+ },
7174
+ {
7175
+ name: "launch_nodes",
7176
+ description: "The user's connected launch nodes: machine, workspace roots, capacity/used/free slots, runtimes (harnesses) and labels. User device credential only; no tokens, connection IDs or mutation.",
7177
+ properties: {},
7178
+ readOnly: true,
7179
+ run: async (hub, _args, signal) => {
7180
+ await userOnly(hub, signal);
7181
+ return JSON.stringify(await hub.call("GET", "/api/agent/launch-nodes", { signal }));
7182
+ }
7183
+ }
7184
+ ];
7185
+
7186
+ // packages/cli/src/teams-mcp.ts
7187
+ var STRING6 = { type: "string" };
7188
+ var STRINGS = { type: "array", items: STRING6 };
6990
7189
  var TEAM2 = { type: "string", description: "The team: its ID (team_…) or its name." };
6991
7190
  var REVISION2 = { type: "integer", minimum: 0, description: "The team's revision you read (team_show); a stale one answers conflict with the current revision." };
6992
7191
  var IDEMPOTENCY = { type: "string", description: "Idempotency-Key: the same key and arguments within 24 h answer the first result again." };
6993
7192
  var KINDS3 = { type: "array", items: { type: "string", enum: ["msg", "ask"] }, description: "What the path permits: msg and/or ask (replies to a permitted ask never need a path)." };
6994
7193
  var DIRECTION = { type: "string", enum: ["one-way", "two-way"], description: "two-way: `to` may also contact `from` with the same kinds." };
6995
7194
  var ROLE_FIELDS = {
6996
- label: { ...STRING4, description: "A display label (confers no authority)." },
6997
- description: STRING4,
7195
+ label: { ...STRING6, description: "A display label (confers no authority)." },
7196
+ description: STRING6,
6998
7197
  externalContact: { type: "boolean", description: "Holders may contact sessions outside all their teams (default false)." },
6999
7198
  responsibilities: { ...STRINGS, description: "Short lines (at most 16)." },
7000
- profile: { type: "object", properties: { profileID: STRING4, version: { type: "integer", minimum: 1 } }, required: ["profileID"], description: "The agent profile members should run (reported, never pinned by staffing)." },
7001
- prefs: { type: "object", properties: { harness: STRING4, model: STRING4, machineID: STRING4 }, description: "Placement preferences for launched members (advisory)." },
7199
+ profile: { type: "object", properties: { profileID: STRING6, version: { type: "integer", minimum: 1 } }, required: ["profileID"], description: "The agent profile members should run (reported, never pinned by staffing)." },
7200
+ prefs: { type: "object", properties: { harness: STRING6, model: STRING6, machineID: STRING6 }, description: "Placement preferences for launched members (advisory)." },
7002
7201
  minMembers: { type: "integer", minimum: 0, maximum: 16 },
7003
7202
  maxMembers: { type: "integer", minimum: 1, maximum: 16 }
7004
7203
  };
7005
- var ROLE = { type: "object", properties: { name: { ...STRING4, description: "A slug: lowercase letters, digits, dashes." }, ...ROLE_FIELDS }, required: ["name"] };
7006
- var EDGE = { type: "object", properties: { from: STRING4, to: STRING4, kinds: KINDS3, direction: DIRECTION }, required: ["from", "to", "kinds", "direction"] };
7204
+ var ROLE = { type: "object", properties: { name: { ...STRING6, description: "A slug: lowercase letters, digits, dashes." }, ...ROLE_FIELDS }, required: ["name"] };
7205
+ var EDGE = { type: "object", properties: { from: STRING6, to: STRING6, kinds: KINDS3, direction: DIRECTION }, required: ["from", "to", "kinds", "direction"] };
7007
7206
  var SPEC_FIELDS = {
7008
- title: { ...STRING4, description: "1–120 characters." },
7009
- description: STRING4,
7207
+ title: { ...STRING6, description: "1–120 characters." },
7208
+ description: STRING6,
7010
7209
  mode: { type: "string", enum: ["open", "restricted"], description: "open: every member may msg/ask every other member. restricted: only the drawn paths (edges)." },
7210
+ hopLimit: { type: "integer", minimum: 1, maximum: 100, description: "Agent-only wake hop limit between this team's seats; may not exceed the hub-wide agents.hopLimit. Unset uses the global limit." },
7011
7211
  roles: { type: "array", items: ROLE, description: "1–32 roles." },
7012
- entryRole: { ...STRING4, description: "The role that receives submitted work (e.g. pm) and supervises members." },
7212
+ entryRole: { ...STRING6, description: "The role that receives submitted work (e.g. pm) and supervises members." },
7013
7213
  edges: { type: "array", items: EDGE, description: "Communication paths between roles (restricted mode)." },
7014
7214
  tags: { ...STRINGS, description: "Slugs." },
7015
- metadata: { type: "object", additionalProperties: STRING4, description: "String key/value pairs (keys are slugs)." },
7215
+ metadata: { type: "object", additionalProperties: STRING6, description: "String key/value pairs (keys are slugs)." },
7016
7216
  projects: { ...STRINGS, description: "Project names the team works on." }
7017
7217
  };
7018
7218
  var LAUNCH = {
@@ -7023,11 +7223,11 @@ var LAUNCH = {
7023
7223
  type: "object",
7024
7224
  description: "Where the member works on its machine.",
7025
7225
  properties: {
7026
- repo: { ...STRING4, description: "The repository's absolute path on the machine." },
7226
+ repo: { ...STRING6, description: "The repository's absolute path on the machine." },
7027
7227
  mode: { type: "string", enum: ["worktree", "checkout", "clone"], description: "worktree (default; needs branch), checkout, or clone (needs remote)." },
7028
- branch: { ...STRING4, description: "The branch to create or check out (a safe git branch name; required for worktree)." },
7029
- base: { ...STRING4, description: "What a new branch starts from." },
7030
- remote: { ...STRING4, description: "An https or ssh git remote, without credentials (required for clone)." }
7228
+ branch: { ...STRING6, description: "The branch to create or check out (a safe git branch name; required for worktree)." },
7229
+ base: { ...STRING6, description: "What a new branch starts from." },
7230
+ remote: { ...STRING6, description: "An https or ssh git remote, without credentials (required for clone)." }
7031
7231
  },
7032
7232
  required: ["repo"]
7033
7233
  },
@@ -7035,8 +7235,8 @@ var LAUNCH = {
7035
7235
  type: "object",
7036
7236
  description: "Which machine runs it.",
7037
7237
  properties: {
7038
- machine: { ...STRING4, description: "A machine ID or name." },
7039
- labels: { type: "object", additionalProperties: STRING4, description: "Node labels to match." },
7238
+ machine: { ...STRING6, description: "A machine ID or name." },
7239
+ labels: { type: "object", additionalProperties: STRING6, description: "Node labels to match." },
7040
7240
  mode: { type: "string", enum: ["require", "prefer"], description: "require (default) or prefer the machine/labels." }
7041
7241
  }
7042
7242
  },
@@ -7044,7 +7244,7 @@ var LAUNCH = {
7044
7244
  type: "object",
7045
7245
  properties: {
7046
7246
  kind: { type: "string", enum: ["host", "openshell"], description: "host (default) or openshell (clone workspaces only)." },
7047
- profile: { ...STRING4, description: "An openshell sandbox profile (openshell only)." }
7247
+ profile: { ...STRING6, description: "An openshell sandbox profile (openshell only)." }
7048
7248
  }
7049
7249
  },
7050
7250
  end: {
@@ -7056,27 +7256,27 @@ var LAUNCH = {
7056
7256
  cleanup: { type: "string", enum: ["keep", "remove"], description: "keep (default) or remove the workspace at the end." }
7057
7257
  }
7058
7258
  },
7059
- name: { ...STRING4, description: "The session's @name, without the @." },
7259
+ name: { ...STRING6, description: "The session's @name, without the @." },
7060
7260
  roles: { ...STRINGS, description: "Roles the session claims (up to 10)." },
7061
- harness: { ...STRING4, description: "The harness (default opencode)." },
7062
- agent: { ...STRING4, description: "The harness agent." },
7063
- model: { ...STRING4, description: "provider/model." },
7064
- prompt: { ...STRING4, description: "The first prompt (up to 10000 characters)." },
7065
- intent: { ...STRING4, description: "What the session is for (one line)." },
7066
- labels: { type: "object", additionalProperties: STRING4, description: "Free-form app labels." },
7067
- agentProfile: { type: "object", properties: { profileID: STRING4, version: { type: "integer", minimum: 1 } }, required: ["profileID", "version"], description: "The profile version to pin (needs profiles:assign for an app)." }
7261
+ harness: { ...STRING6, description: "The harness (default opencode)." },
7262
+ agent: { ...STRING6, description: "The harness agent." },
7263
+ model: { ...STRING6, description: "provider/model." },
7264
+ prompt: { ...STRING6, description: "The first prompt (up to 10000 characters)." },
7265
+ intent: { ...STRING6, description: "What the session is for (one line)." },
7266
+ labels: { type: "object", additionalProperties: STRING6, description: "Free-form app labels." },
7267
+ agentProfile: { type: "object", properties: { profileID: STRING6, version: { type: "integer", minimum: 1 } }, required: ["profileID", "version"], description: "The profile version to pin (needs profiles:assign for an app)." }
7068
7268
  },
7069
7269
  required: ["workspace"]
7070
7270
  };
7071
7271
  var SCOPES = "App tokens need scope teams:read (reads) or teams:manage (changes); seats, status and staffing also need sessions:read. Session tokens read only teams they are seated in and never change definitions. Team roles confer no authority.";
7072
7272
  var ArgError = ToolArgError;
7073
- var need4 = (args, key) => {
7273
+ var need6 = (args, key) => {
7074
7274
  const value = args[key];
7075
7275
  if (typeof value !== "string" || !value.trim())
7076
7276
  throw new ArgError(`"${key}" is required (a non-empty string)`);
7077
7277
  return value;
7078
7278
  };
7079
- var optional5 = (args, key) => {
7279
+ var optional6 = (args, key) => {
7080
7280
  const value = args[key];
7081
7281
  if (value === undefined || value === null)
7082
7282
  return;
@@ -7131,6 +7331,7 @@ function specLines(spec) {
7131
7331
  const role = (r) => ` ${r.name}${r.name === spec.entryRole ? " (entry)" : ""}${r.label ? ` "${one(r.label)}"` : ""}${r.externalContact ? " external contact" : ""}${r.minMembers !== undefined || r.maxMembers !== undefined ? ` members ${r.minMembers ?? 0}–${r.maxMembers ?? 16}` : ""}${r.profile ? ` profile ${r.profile.profileID}${r.profile.version ? `@${r.profile.version}` : ""}` : ""}`;
7132
7332
  return [
7133
7333
  `mode: ${spec.mode}${spec.mode === "open" ? " (every member may msg/ask every other)" : " (only the paths below)"}`,
7334
+ ...spec.hopLimit !== undefined ? [`hop limit: ${spec.hopLimit}`] : [],
7134
7335
  `entry role: ${spec.entryRole}`,
7135
7336
  "roles:",
7136
7337
  ...spec.roles.map(role),
@@ -7149,7 +7350,7 @@ function rosterLines(roster) {
7149
7350
  const flags = [r.vacant ? "vacant" : "", r.understaffed ? `understaffed (min ${r.minMembers})` : "", r.launching ? `${r.launching} launching` : ""].filter(Boolean);
7150
7351
  lines.push(` ${r.role}${r.entry ? " (entry)" : ""} ${r.seats.length}/${r.maxMembers} ${r.online} online${flags.length ? ` ${flags.join(", ")}` : ""}`);
7151
7352
  for (const s of r.seats)
7152
- lines.push(` ${s.staffing.id} ${s.staffing.sessionID} ${s.presence} gen ${s.staffing.generation} r${s.staffing.revision}${s.profile ? ` profile ${s.profile.status}${s.profile.matches ? "" : " (mismatch)"}` : ""}`);
7353
+ lines.push(` ${s.staffing.id} ${s.staffing.sessionID} ${s.presence}${s.stalled ? ` stalled: ${s.stalled}` : ""} gen ${s.staffing.generation} r${s.staffing.revision}${s.profile ? ` profile ${s.profile.status}${s.profile.matches ? "" : " (mismatch)"}` : ""}`);
7153
7354
  }
7154
7355
  const failed = roster.requests.filter((q) => q.state !== "staffed").slice(-5);
7155
7356
  for (const q of failed)
@@ -7166,7 +7367,7 @@ var statusTool = (verb, description, organizes) => ({
7166
7367
  idempotent: true,
7167
7368
  run: async (hub, args, signal) => {
7168
7369
  const expectedRevision = revision(args);
7169
- const { team } = await hub.teams[verb](need4(args, "team"), expectedRevision, { signal });
7370
+ const { team } = await hub.teams[verb](need6(args, "team"), expectedRevision, { signal });
7170
7371
  return `${team.revision === expectedRevision ? "unchanged" : { pause: "paused", resume: "resumed", archive: "archived", restore: "restored" }[verb]}: ${teamLine(team)}`;
7171
7372
  }
7172
7373
  });
@@ -7180,7 +7381,7 @@ var draftTool = (name, description, properties, required, edit) => ({
7180
7381
  idempotent: true,
7181
7382
  run: async (hub, args, signal) => {
7182
7383
  const expectedRevision = revision(args);
7183
- const { team, draft } = await edit(hub.teams, need4(args, "team"), args, expectedRevision, signal);
7384
+ const { team, draft } = await edit(hub.teams, need6(args, "team"), args, expectedRevision, signal);
7184
7385
  return draftText("draft saved", team, draft);
7185
7386
  }
7186
7387
  });
@@ -7191,22 +7392,22 @@ var teamTools = [
7191
7392
  properties: {
7192
7393
  state: { type: "string", enum: ["active", "archived", "all"] },
7193
7394
  tags: STRINGS,
7194
- project: STRING4,
7195
- metadata: { type: "object", additionalProperties: STRING4 },
7395
+ project: STRING6,
7396
+ metadata: { type: "object", additionalProperties: STRING6 },
7196
7397
  paused: { type: "boolean" },
7197
7398
  limit: { type: "integer", minimum: 1, maximum: 200 },
7198
- cursor: STRING4
7399
+ cursor: STRING6
7199
7400
  },
7200
7401
  readOnly: true,
7201
7402
  run: async (hub, args, signal) => {
7202
- const state = optional5(args, "state");
7403
+ const state = optional6(args, "state");
7203
7404
  if (state !== undefined && !["active", "archived", "all"].includes(state))
7204
7405
  throw new ArgError(`"state" is one of active, archived, all`);
7205
7406
  const tags = strings3(args, "tags");
7206
- const project = optional5(args, "project");
7407
+ const project = optional6(args, "project");
7207
7408
  const metadata = record(args, "metadata");
7208
7409
  const limit = int3(args, "limit", 1, 200);
7209
- const cursor = optional5(args, "cursor");
7410
+ const cursor = optional6(args, "cursor");
7210
7411
  if (args.paused !== undefined && typeof args.paused !== "boolean")
7211
7412
  throw new ArgError(`"paused" must be true or false`);
7212
7413
  const page = await hub.teams.list({
@@ -7232,7 +7433,7 @@ var teamTools = [
7232
7433
  required: ["team"],
7233
7434
  readOnly: true,
7234
7435
  run: async (hub, args, signal) => {
7235
- const { team, draft } = await hub.teams.get(need4(args, "team"), { signal });
7436
+ const { team, draft } = await hub.teams.get(need6(args, "team"), { signal });
7236
7437
  return [teamLine(team), ...specLines(team), ...draft ? [`draft pending (on v${draft.baseVersion}, saved ${when(draft.savedAt)}): see team_diff`] : []].join(`
7237
7438
  `);
7238
7439
  }
@@ -7244,7 +7445,7 @@ var teamTools = [
7244
7445
  required: ["team"],
7245
7446
  readOnly: true,
7246
7447
  run: async (hub, args, signal) => {
7247
- const { team, roster, intake } = await hub.teams.status(need4(args, "team"), { signal });
7448
+ const { team, roster, intake } = await hub.teams.status(need6(args, "team"), { signal });
7248
7449
  const lines = [teamLine(team), ...rosterLines(roster)];
7249
7450
  if (intake)
7250
7451
  lines.push(`intake: ${intake.queued} queued, ${intake.routed} routed (${intake.open} open, ${intake.inProgress} in progress, ${intake.blocked} blocked), ${intake.done} done, ${intake.failed} failed, ${intake.cancelled} cancelled${intake.oldestQueuedAt !== undefined ? `; oldest queued ${when(intake.oldestQueuedAt)}` : ""}`);
@@ -7255,12 +7456,12 @@ var teamTools = [
7255
7456
  {
7256
7457
  name: "team_history",
7257
7458
  description: `A team's retained published versions, newest first (version, digest, when). ${SCOPES}`,
7258
- properties: { team: TEAM2, cursor: STRING4 },
7459
+ properties: { team: TEAM2, cursor: STRING6 },
7259
7460
  required: ["team"],
7260
7461
  readOnly: true,
7261
7462
  run: async (hub, args, signal) => {
7262
- const cursor = optional5(args, "cursor");
7263
- const page = await hub.teams.versions(need4(args, "team"), { signal, ...cursor ? { cursor } : {} });
7463
+ const cursor = optional6(args, "cursor");
7464
+ const page = await hub.teams.versions(need6(args, "team"), { signal, ...cursor ? { cursor } : {} });
7264
7465
  return [...page.items.map((v) => `v${v.version} ${v.digest.slice(0, 12)} ${when(v.publishedAt)}`), ...page.cursor ? [`more: cursor ${page.cursor}`] : []].join(`
7265
7466
  `);
7266
7467
  }
@@ -7274,7 +7475,7 @@ var teamTools = [
7274
7475
  run: async (hub, args, signal) => {
7275
7476
  const from = int3(args, "from", 1, 1e9);
7276
7477
  const to = args.to === undefined || args.to === null || args.to === "draft" ? "draft" : int3(args, "to", 1, 1e9);
7277
- const { from: a, to: b, diff } = await hub.teams.diff(need4(args, "team"), { signal, ...from !== undefined ? { from } : {}, to });
7478
+ const { from: a, to: b, diff } = await hub.teams.diff(need6(args, "team"), { signal, ...from !== undefined ? { from } : {}, to });
7278
7479
  const part = (label, d) => ["added", "removed", "changed"].flatMap((k) => d[k]?.length ? [`${label} ${k}: ${d[k].join(", ")}`] : []);
7279
7480
  const lines = [
7280
7481
  ...diff.fields.length ? [`fields changed: ${diff.fields.join(", ")}`] : [],
@@ -7291,16 +7492,16 @@ var teamTools = [
7291
7492
  {
7292
7493
  name: "team_create",
7293
7494
  description: `Assemble a persistent team: roles (one is the entry role that receives submitted work, e.g. a project manager), open or restricted communication with one-way/two-way paths, tags, projects and metadata. Published as version 1 at once; the name is the team's address (@@role#name) and never changes. ${SCOPES}`,
7294
- properties: { name: { ...STRING4, description: "A slug (2–40 lowercase letters, digits, dashes), unique per user." }, ...SPEC_FIELDS, idempotencyKey: IDEMPOTENCY },
7495
+ properties: { name: { ...STRING6, description: "A slug (2–40 lowercase letters, digits, dashes), unique per user." }, ...SPEC_FIELDS, idempotencyKey: IDEMPOTENCY },
7295
7496
  required: ["name", "title", "mode", "roles", "entryRole"],
7296
7497
  readOnly: false,
7297
7498
  organizes: true,
7298
7499
  idempotent: true,
7299
7500
  run: async (hub, args, signal) => {
7300
- need4(args, "name");
7301
- need4(args, "title");
7302
- const key = optional5(args, "idempotencyKey");
7303
- const spec = picked(args, ["name", "title", "description", "mode", "roles", "entryRole", "edges", "tags", "metadata", "projects"]);
7501
+ need6(args, "name");
7502
+ need6(args, "title");
7503
+ const key = optional6(args, "idempotencyKey");
7504
+ const spec = picked(args, ["name", "title", "description", "mode", "hopLimit", "roles", "entryRole", "edges", "tags", "metadata", "projects"]);
7304
7505
  const { team, created } = await hub.teams.create({ ...spec, edges: spec.edges ?? [] }, { signal, ...key ? { idempotencyKey: key } : {} });
7305
7506
  return [`${created ? "created" : "already created (same key)"}: ${teamLine(team)}`, ...specLines(team)].join(`
7306
7507
  `);
@@ -7316,24 +7517,24 @@ var teamTools = [
7316
7517
  idempotent: true,
7317
7518
  run: async (hub, args, signal) => {
7318
7519
  const expectedRevision = revision(args);
7319
- const patch = picked(args, ["title", "description", "mode", "roles", "entryRole", "edges", "tags", "metadata", "projects"]);
7520
+ const patch = picked(args, ["title", "description", "mode", "hopLimit", "roles", "entryRole", "edges", "tags", "metadata", "projects"]);
7320
7521
  if (Object.keys(patch).length === 0)
7321
7522
  throw new ArgError("give at least one field to change");
7322
- const id = need4(args, "team");
7523
+ const id = need6(args, "team");
7323
7524
  const { team } = await hub.teams.update(id, patch, expectedRevision, { signal });
7324
7525
  return draftText("draft saved", team, (await hub.teams.draft(team.id, { signal })).draft);
7325
7526
  }
7326
7527
  },
7327
- draftTool("team_role_set", "Add a role to a team's draft, or replace the role of that name (all its fields).", { role: { ...STRING4, description: "The role's name (a slug)." }, ...ROLE_FIELDS }, ["role"], (teams, team, args, rev, signal) => teams.setRole(team, { name: need4(args, "role"), ...picked(args, Object.keys(ROLE_FIELDS)) }, rev, { signal })),
7328
- draftTool("team_role_remove", "Remove a role (not the entry role) and every path touching it from a team's draft.", { role: STRING4 }, ["role"], (teams, team, args, rev, signal) => teams.removeRole(team, need4(args, "role"), rev, { signal })),
7329
- draftTool("team_edge_set", "Draw a communication path from one role to another in a team's draft (replaces the path drawn between them).", { from: STRING4, to: STRING4, kinds: KINDS3, direction: DIRECTION }, ["from", "to", "kinds", "direction"], (teams, team, args, rev, signal) => {
7528
+ draftTool("team_role_set", "Add a role to a team's draft, or replace the role of that name (all its fields).", { role: { ...STRING6, description: "The role's name (a slug)." }, ...ROLE_FIELDS }, ["role"], (teams, team, args, rev, signal) => teams.setRole(team, { name: need6(args, "role"), ...picked(args, Object.keys(ROLE_FIELDS)) }, rev, { signal })),
7529
+ draftTool("team_role_remove", "Remove a role (not the entry role) and every path touching it from a team's draft.", { role: STRING6 }, ["role"], (teams, team, args, rev, signal) => teams.removeRole(team, need6(args, "role"), rev, { signal })),
7530
+ draftTool("team_edge_set", "Draw a communication path from one role to another in a team's draft (replaces the path drawn between them).", { from: STRING6, to: STRING6, kinds: KINDS3, direction: DIRECTION }, ["from", "to", "kinds", "direction"], (teams, team, args, rev, signal) => {
7330
7531
  const kinds = strings3(args, "kinds");
7331
7532
  if (!kinds?.length)
7332
7533
  throw new ArgError(`"kinds" is a list of msg and/or ask`);
7333
- const direction = need4(args, "direction");
7334
- return teams.setEdge(team, { from: need4(args, "from"), to: need4(args, "to"), kinds, direction }, rev, { signal });
7534
+ const direction = need6(args, "direction");
7535
+ return teams.setEdge(team, { from: need6(args, "from"), to: need6(args, "to"), kinds, direction }, rev, { signal });
7335
7536
  }),
7336
- draftTool("team_edge_remove", "Remove the path drawn from one role to another (or a two-way path between them) from a team's draft.", { from: STRING4, to: STRING4 }, ["from", "to"], (teams, team, args, rev, signal) => teams.removeEdge(team, need4(args, "from"), need4(args, "to"), rev, { signal })),
7537
+ draftTool("team_edge_remove", "Remove the path drawn from one role to another (or a two-way path between them) from a team's draft.", { from: STRING6, to: STRING6 }, ["from", "to"], (teams, team, args, rev, signal) => teams.removeEdge(team, need6(args, "from"), need6(args, "to"), rev, { signal })),
7337
7538
  {
7338
7539
  name: "team_discard_draft",
7339
7540
  description: `Drop a team's pending draft (unpublished edits are lost; the published version stays). ${SCOPES}`,
@@ -7341,7 +7542,7 @@ var teamTools = [
7341
7542
  required: ["team", "expectedRevision"],
7342
7543
  readOnly: false,
7343
7544
  idempotent: true,
7344
- run: async (hub, args, signal) => `draft discarded: ${teamLine((await hub.teams.discardDraft(need4(args, "team"), revision(args), { signal })).team)}`
7545
+ run: async (hub, args, signal) => `draft discarded: ${teamLine((await hub.teams.discardDraft(need6(args, "team"), revision(args), { signal })).team)}`
7345
7546
  },
7346
7547
  {
7347
7548
  name: "team_publish",
@@ -7351,7 +7552,7 @@ var teamTools = [
7351
7552
  readOnly: false,
7352
7553
  idempotent: true,
7353
7554
  run: async (hub, args, signal) => {
7354
- const { team, published } = await hub.teams.publish(need4(args, "team"), revision(args), { signal });
7555
+ const { team, published } = await hub.teams.publish(need6(args, "team"), revision(args), { signal });
7355
7556
  return `${published ? `published version ${team.version}` : "the draft equalled the published version; dropped"}: ${teamLine(team)}`;
7356
7557
  }
7357
7558
  },
@@ -7362,17 +7563,17 @@ var teamTools = [
7362
7563
  {
7363
7564
  name: "team_staff",
7364
7565
  description: `Staff a role: attach an existing session (sessionID, any machine) or launch a new member (launch: a launch spec as the launch tool takes). An agent needs the user's grants (roles for the session's project; launch for a repo) or the hub answers needs_user; staffing never issues grants. ${SCOPES}`,
7365
- properties: { team: TEAM2, role: STRING4, sessionID: { ...STRING4, description: "An existing session (ses_…)." }, launch: LAUNCH, idempotencyKey: IDEMPOTENCY },
7566
+ properties: { team: TEAM2, role: STRING6, sessionID: { ...STRING6, description: "An existing session (ses_…)." }, launch: LAUNCH, idempotencyKey: IDEMPOTENCY },
7366
7567
  required: ["team", "role"],
7367
7568
  readOnly: false,
7368
7569
  run: async (hub, args, signal) => {
7369
- const team = need4(args, "team");
7370
- const role = need4(args, "role");
7371
- const sessionID = optional5(args, "sessionID");
7570
+ const team = need6(args, "team");
7571
+ const role = need6(args, "role");
7572
+ const sessionID = optional6(args, "sessionID");
7372
7573
  const launch = launchOf(args);
7373
7574
  if (sessionID === undefined === (launch === undefined))
7374
7575
  throw new ArgError(`give exactly one of sessionID or launch`);
7375
- const key = optional5(args, "idempotencyKey");
7576
+ const key = optional6(args, "idempotencyKey");
7376
7577
  const options = { signal, ...key ? { idempotencyKey: key } : {} };
7377
7578
  if (sessionID !== undefined) {
7378
7579
  const r = await hub.teams.staff(team, { role, sessionID }, options);
@@ -7385,29 +7586,29 @@ var teamTools = [
7385
7586
  {
7386
7587
  name: "team_unstaff",
7387
7588
  description: `End a seat (the session leaves the role and loses the team's policy rights). Send the seat's revision (team_status). ${SCOPES}`,
7388
- properties: { team: TEAM2, staffingID: { ...STRING4, description: "The seat (stf_…)." }, expectedRevision: { ...REVISION2, description: "The seat's revision you read (team_status)." } },
7589
+ properties: { team: TEAM2, staffingID: { ...STRING6, description: "The seat (stf_…)." }, expectedRevision: { ...REVISION2, description: "The seat's revision you read (team_status)." } },
7389
7590
  required: ["team", "staffingID", "expectedRevision"],
7390
7591
  readOnly: false,
7391
7592
  idempotent: true,
7392
7593
  run: async (hub, args, signal) => {
7393
- const { staffing } = await hub.teams.unstaff(need4(args, "team"), need4(args, "staffingID"), revision(args), { signal });
7594
+ const { staffing } = await hub.teams.unstaff(need6(args, "team"), need6(args, "staffingID"), revision(args), { signal });
7394
7595
  return `unstaffed ${staffing.id} (${staffing.sessionID} as ${staffing.role})`;
7395
7596
  }
7396
7597
  },
7397
7598
  {
7398
7599
  name: "team_replace",
7399
7600
  description: `Replace a seat by an existing session (sessionID) or a new launch (launch): the old seat ends at once and the new member gets the slot's next generation. Send the seat's revision (team_status). ${SCOPES}`,
7400
- properties: { team: TEAM2, staffingID: STRING4, sessionID: STRING4, launch: LAUNCH, expectedRevision: { ...REVISION2, description: "The seat's revision you read (team_status)." } },
7601
+ properties: { team: TEAM2, staffingID: STRING6, sessionID: STRING6, launch: LAUNCH, expectedRevision: { ...REVISION2, description: "The seat's revision you read (team_status)." } },
7401
7602
  required: ["team", "staffingID", "expectedRevision"],
7402
7603
  readOnly: false,
7403
7604
  run: async (hub, args, signal) => {
7404
7605
  const expectedRevision = revision(args);
7405
- const sessionID = optional5(args, "sessionID");
7606
+ const sessionID = optional6(args, "sessionID");
7406
7607
  const launch = launchOf(args);
7407
7608
  if (sessionID === undefined === (launch === undefined))
7408
7609
  throw new ArgError(`give exactly one of sessionID or launch`);
7409
7610
  const input = sessionID !== undefined ? { expectedRevision, sessionID } : { expectedRevision, launch };
7410
- const r = await hub.teams.replace(need4(args, "team"), need4(args, "staffingID"), input, { signal });
7611
+ const r = await hub.teams.replace(need6(args, "team"), need6(args, "staffingID"), input, { signal });
7411
7612
  const next = r.staffing ? `${r.staffing.sessionID} (${r.staffing.id}, generation ${r.staffing.generation})` : `launch ${r.launch?.id ?? "?"} (request ${r.request?.id ?? "?"})`;
7412
7613
  return `replaced ${r.ended.sessionID} in ${r.ended.role} by ${next}`;
7413
7614
  }
@@ -7415,13 +7616,13 @@ var teamTools = [
7415
7616
  ];
7416
7617
 
7417
7618
  // packages/cli/src/profiles-mcp.ts
7418
- var STRING5 = { type: "string" };
7419
- var STRINGS2 = { type: "array", items: STRING5 };
7619
+ var STRING7 = { type: "string" };
7620
+ var STRINGS2 = { type: "array", items: STRING7 };
7420
7621
  var ID4 = { type: "string", description: "The profile's ID (prof_…; see profiles)." };
7421
7622
  var REVISION3 = { type: "integer", minimum: 0, description: "The profile's revision you read (profile_show); a stale one answers conflict with the current revision." };
7422
7623
  var IDEMPOTENCY2 = { type: "string", description: "Idempotency-Key: the same key and arguments within 24 h answer the first result again." };
7423
- var SKILL = { type: "object", properties: { name: { ...STRING5, description: "A slug." }, description: STRING5, body: { ...STRING5, description: "The skill's full text (loaded on demand)." } }, required: ["name", "description", "body"] };
7424
- var COMMAND3 = { type: "object", properties: { name: { ...STRING5, description: "A slug." }, description: STRING5, template: { ...STRING5, description: "Text with {{name}} parameters and {{secret:NAME}} references (never resolved by the hub)." } }, required: ["name", "description", "template"] };
7624
+ var SKILL = { type: "object", properties: { name: { ...STRING7, description: "A slug." }, description: STRING7, body: { ...STRING7, description: "The skill's full text (loaded on demand)." } }, required: ["name", "description", "body"] };
7625
+ var COMMAND3 = { type: "object", properties: { name: { ...STRING7, description: "A slug." }, description: STRING7, template: { ...STRING7, description: "Text with {{name}} parameters and {{secret:NAME}} references (never resolved by the hub)." } }, required: ["name", "description", "template"] };
7425
7626
  var CONTENT_FIELDS = {
7426
7627
  instructions: { type: ["string", "null"], description: "The profile's instructions (AGENTS.md-like text)." },
7427
7628
  model: { type: ["string", "null"], description: "provider/model the profile suggests." },
@@ -7429,21 +7630,21 @@ var CONTENT_FIELDS = {
7429
7630
  commands: { type: "array", items: COMMAND3, description: "Commands, wholesale." }
7430
7631
  };
7431
7632
  var META_FIELDS = {
7432
- title: { ...STRING5, description: "1–120 characters." },
7433
- description: STRING5,
7633
+ title: { ...STRING7, description: "1–120 characters." },
7634
+ description: STRING7,
7434
7635
  tags: { ...STRINGS2, description: "Slugs." },
7435
- metadata: { type: "object", additionalProperties: STRING5, description: "String key/value pairs (≤ 32 slug keys)." },
7636
+ metadata: { type: "object", additionalProperties: STRING7, description: "String key/value pairs (≤ 32 slug keys)." },
7436
7637
  projects: { ...STRINGS2, description: "Project names the profile is meant for (≤ 16)." }
7437
7638
  };
7438
7639
  var SCOPES2 = "App tokens need scope profiles:read (reads) or profiles:manage (changes; profile_version_create with content fields and the skill/command set/remove tools read the latest version first, so they also need profiles:read); pins need profiles:assign and sessions:read (an app also needs a prompt grant on the session). Session tokens never change profiles. A profile confers no authority.";
7439
7640
  var ArgError2 = ToolArgError;
7440
- var need5 = (args, key) => {
7641
+ var need7 = (args, key) => {
7441
7642
  const value = args[key];
7442
7643
  if (typeof value !== "string" || !value.trim())
7443
7644
  throw new ArgError2(`"${key}" is required (a non-empty string)`);
7444
7645
  return value;
7445
7646
  };
7446
- var optional6 = (args, key) => {
7647
+ var optional7 = (args, key) => {
7447
7648
  const value = args[key];
7448
7649
  if (value === undefined || value === null)
7449
7650
  return;
@@ -7459,7 +7660,7 @@ var whole2 = (args, key, min, what) => {
7459
7660
  };
7460
7661
  var revision2 = (args) => whole2(args, "expectedRevision", 0, "the revision you read (profile_show; 0 for a session with no pin)");
7461
7662
  var idempotency = (args) => {
7462
- const key = optional6(args, "idempotencyKey");
7663
+ const key = optional7(args, "idempotencyKey");
7463
7664
  return key ? { idempotencyKey: key } : {};
7464
7665
  };
7465
7666
  var picked2 = (args, keys) => Object.fromEntries(keys.filter((k) => args[k] !== undefined).map((k) => [k, args[k]]));
@@ -7492,7 +7693,7 @@ var contentTool = (name, description, properties, required, change) => ({
7492
7693
  idempotent: true,
7493
7694
  run: async (hub, args, signal) => {
7494
7695
  const expectedRevision = revision2(args);
7495
- const id = need5(args, "id");
7696
+ const id = need7(args, "id");
7496
7697
  const { latest, content } = await latestContent(hub.profiles, id, signal);
7497
7698
  return versionText(await hub.profiles.createVersion(id, { expectedRevision, content: change(content, args, latest) }, { signal }));
7498
7699
  }
@@ -7512,7 +7713,7 @@ var stateTool = (verb, description) => ({
7512
7713
  idempotent: true,
7513
7714
  run: async (hub, args, signal) => {
7514
7715
  const expectedRevision = revision2(args);
7515
- const { profile } = await hub.profiles.update(need5(args, "id"), { expectedRevision, state: verb === "archive" ? "archived" : "active" }, { signal });
7716
+ const { profile } = await hub.profiles.update(need7(args, "id"), { expectedRevision, state: verb === "archive" ? "archived" : "active" }, { signal });
7516
7717
  return `${verb === "archive" ? "archived" : "restored"}: ${profileLine(profile)}`;
7517
7718
  }
7518
7719
  });
@@ -7524,7 +7725,7 @@ var profileLifecycleTools = [
7524
7725
  required: ["id"],
7525
7726
  readOnly: true,
7526
7727
  run: async (hub, args, signal) => {
7527
- const { profile, latest } = await hub.profiles.get(need5(args, "id"), { signal });
7728
+ const { profile, latest } = await hub.profiles.get(need7(args, "id"), { signal });
7528
7729
  return [
7529
7730
  profileLine(profile),
7530
7731
  ...profile.description ? [`description: ${one2(profile.description)}`] : [],
@@ -7539,13 +7740,13 @@ var profileLifecycleTools = [
7539
7740
  {
7540
7741
  name: "profile_versions",
7541
7742
  description: `A profile's retained versions, newest first (version, digest, bytes, when, skill and command counts). ${SCOPES2}`,
7542
- properties: { id: ID4, limit: { type: "integer", minimum: 1, maximum: 200 }, cursor: STRING5 },
7743
+ properties: { id: ID4, limit: { type: "integer", minimum: 1, maximum: 200 }, cursor: STRING7 },
7543
7744
  required: ["id"],
7544
7745
  readOnly: true,
7545
7746
  run: async (hub, args, signal) => {
7546
7747
  const limit = args.limit === undefined ? undefined : whole2(args, "limit", 1, "a whole number from 1 to 200");
7547
- const cursor = optional6(args, "cursor");
7548
- const page = await hub.profiles.versions(need5(args, "id"), { signal, ...limit !== undefined ? { limit } : {}, ...cursor ? { cursor } : {} });
7748
+ const cursor = optional7(args, "cursor");
7749
+ const page = await hub.profiles.versions(need7(args, "id"), { signal, ...limit !== undefined ? { limit } : {}, ...cursor ? { cursor } : {} });
7549
7750
  const counts = (v) => v.skills !== undefined ? ` ${v.skills} skills, ${v.commands ?? 0} commands${v.instructions ? ", instructions" : ""}` : "";
7550
7751
  return [...page.items.map((v) => `v${v.version} ${v.digest.slice(0, 12)} ${v.bytes} bytes ${new Date(v.createdAt).toISOString()}${counts(v)}`), ...page.cursor ? [`more: cursor ${page.cursor}`] : []].join(`
7551
7752
  `) || "no versions";
@@ -7554,14 +7755,14 @@ var profileLifecycleTools = [
7554
7755
  {
7555
7756
  name: "profile_create",
7556
7757
  description: `Create a profile: a versioned bundle of instructions, skills and commands, published as version 1 at once. The name is unique per user. Attach it to a team role with team_role_set (profile), pin it with profile_assign. ${SCOPES2}`,
7557
- properties: { name: { ...STRING5, description: "A slug, unique per user." }, ...META_FIELDS, ...CONTENT_FIELDS, idempotencyKey: IDEMPOTENCY2 },
7758
+ properties: { name: { ...STRING7, description: "A slug, unique per user." }, ...META_FIELDS, ...CONTENT_FIELDS, idempotencyKey: IDEMPOTENCY2 },
7558
7759
  required: ["name", "title"],
7559
7760
  readOnly: false,
7560
7761
  organizes: true,
7561
7762
  idempotent: true,
7562
7763
  run: async (hub, args, signal) => {
7563
- need5(args, "name");
7564
- need5(args, "title");
7764
+ need7(args, "name");
7765
+ need7(args, "title");
7565
7766
  const meta = picked2(args, ["name", "title", "description", "tags", "metadata", "projects"]);
7566
7767
  const text = Object.entries(picked2(args, ["instructions", "model"])).filter(([, v]) => v !== null);
7567
7768
  const content = { ...Object.fromEntries(text), skills: args.skills ?? [], commands: args.commands ?? [] };
@@ -7583,7 +7784,7 @@ var profileLifecycleTools = [
7583
7784
  const patch = picked2(args, ["title", "description", "tags", "metadata", "projects"]);
7584
7785
  if (Object.keys(patch).length === 0)
7585
7786
  throw new ArgError2("give at least one field to change");
7586
- const { profile } = await hub.profiles.update(need5(args, "id"), { expectedRevision, ...patch }, { signal });
7787
+ const { profile } = await hub.profiles.update(need7(args, "id"), { expectedRevision, ...patch }, { signal });
7587
7788
  return `updated: ${profileLine(profile)}`;
7588
7789
  }
7589
7790
  },
@@ -7597,7 +7798,7 @@ var profileLifecycleTools = [
7597
7798
  idempotent: true,
7598
7799
  run: async (hub, args, signal) => {
7599
7800
  const expectedRevision = revision2(args);
7600
- const id = need5(args, "id");
7801
+ const id = need7(args, "id");
7601
7802
  const given = Object.keys(picked2(args, Object.keys(CONTENT_FIELDS)));
7602
7803
  const options = { signal, ...idempotency(args) };
7603
7804
  if (args.fromVersion !== undefined) {
@@ -7618,77 +7819,225 @@ var profileLifecycleTools = [
7618
7819
  return versionText(await hub.profiles.createVersion(id, { expectedRevision, content: next }, options));
7619
7820
  }
7620
7821
  },
7621
- contentTool("profile_skill_set", "Attach a skill to a profile, or replace the skill of that name.", { name: { ...STRING5, description: "The skill's name (a slug)." }, description: STRING5, body: { ...STRING5, description: "The skill's full text." } }, ["name", "description", "body"], (content, args) => ({
7822
+ contentTool("profile_skill_set", "Attach a skill to a profile, or replace the skill of that name.", { name: { ...STRING7, description: "The skill's name (a slug)." }, description: STRING7, body: { ...STRING7, description: "The skill's full text." } }, ["name", "description", "body"], (content, args) => ({
7622
7823
  ...content,
7623
- skills: setNamed(content.skills, { name: need5(args, "name"), description: need5(args, "description"), body: need5(args, "body") })
7824
+ skills: setNamed(content.skills, { name: need7(args, "name"), description: need7(args, "description"), body: need7(args, "body") })
7624
7825
  })),
7625
- contentTool("profile_skill_remove", "Detach a skill from a profile.", { name: { ...STRING5, description: "The skill's name." } }, ["name"], (content, args, latest) => ({
7826
+ contentTool("profile_skill_remove", "Detach a skill from a profile.", { name: { ...STRING7, description: "The skill's name." } }, ["name"], (content, args, latest) => ({
7626
7827
  ...content,
7627
- skills: removeNamed(content.skills, need5(args, "name"), "skill", latest)
7828
+ skills: removeNamed(content.skills, need7(args, "name"), "skill", latest)
7628
7829
  })),
7629
- contentTool("profile_command_set", "Attach a command to a profile, or replace the command of that name.", { name: { ...STRING5, description: "The command's name (a slug)." }, description: STRING5, template: COMMAND3.properties.template }, ["name", "description", "template"], (content, args) => ({ ...content, commands: setNamed(content.commands, { name: need5(args, "name"), description: need5(args, "description"), template: need5(args, "template") }) })),
7630
- contentTool("profile_command_remove", "Detach a command from a profile.", { name: { ...STRING5, description: "The command's name." } }, ["name"], (content, args, latest) => ({
7830
+ contentTool("profile_command_set", "Attach a command to a profile, or replace the command of that name.", { name: { ...STRING7, description: "The command's name (a slug)." }, description: STRING7, template: COMMAND3.properties.template }, ["name", "description", "template"], (content, args) => ({ ...content, commands: setNamed(content.commands, { name: need7(args, "name"), description: need7(args, "description"), template: need7(args, "template") }) })),
7831
+ contentTool("profile_command_remove", "Detach a command from a profile.", { name: { ...STRING7, description: "The command's name." } }, ["name"], (content, args, latest) => ({
7631
7832
  ...content,
7632
- commands: removeNamed(content.commands, need5(args, "name"), "command", latest)
7833
+ commands: removeNamed(content.commands, need7(args, "name"), "command", latest)
7633
7834
  })),
7634
7835
  stateTool("archive", "Archive a profile: it takes no new versions or pins; pinned sessions keep theirs; reversible with profile_restore."),
7635
7836
  stateTool("restore", "Restore an archived profile (it takes versions and pins again)."),
7636
7837
  {
7637
7838
  name: "profile_assign",
7638
7839
  description: `Pin a profile version to a session (applied through its machine; the ack tells whether it took). Send the session's pin revision (0 when it has none; see profile). Pinning confers no authority. ${SCOPES2}`,
7639
- properties: { addr: { ...STRING5, description: "The session: @name, ses_… ID or other address." }, profileID: ID4, version: { type: "integer", minimum: 1 }, expectedRevision: { ...REVISION3, description: "The session's pin revision (0 when it has none)." } },
7840
+ properties: { addr: { ...STRING7, description: "The session: @name, ses_… ID or other address." }, profileID: ID4, version: { type: "integer", minimum: 1 }, expectedRevision: { ...REVISION3, description: "The session's pin revision (0 when it has none)." } },
7640
7841
  required: ["addr", "profileID", "version", "expectedRevision"],
7641
7842
  readOnly: false,
7642
7843
  idempotent: true,
7643
7844
  run: async (hub, args, signal) => {
7644
7845
  const expectedRevision = revision2(args);
7645
- const { assignment: a } = await hub.profiles.assign(need5(args, "addr"), { profileID: need5(args, "profileID"), version: whole2(args, "version", 1, "a version number"), expectedRevision }, { signal });
7846
+ const { assignment: a } = await hub.profiles.assign(need7(args, "addr"), { profileID: need7(args, "profileID"), version: whole2(args, "version", 1, "a version number"), expectedRevision }, { signal });
7646
7847
  return `${a.sessionID} pins ${a.profileID} v${a.version} r${a.revision}${a.ack ? `, ack ${a.ack.status}${a.ack.offline ? " (offline)" : ""}` : ""}`;
7647
7848
  }
7648
7849
  },
7649
7850
  {
7650
7851
  name: "profile_unassign",
7651
7852
  description: `Remove a session's pin (what it already applied stays until it restarts). Send the pin's revision (profile_assign's answer). ${SCOPES2}`,
7652
- properties: { addr: { ...STRING5, description: "The session." }, expectedRevision: { ...REVISION3, description: "The session's pin revision." } },
7853
+ properties: { addr: { ...STRING7, description: "The session." }, expectedRevision: { ...REVISION3, description: "The session's pin revision." } },
7653
7854
  required: ["addr", "expectedRevision"],
7654
7855
  readOnly: false,
7655
7856
  idempotent: true,
7656
7857
  run: async (hub, args, signal) => {
7657
- const addr = need5(args, "addr");
7858
+ const addr = need7(args, "addr");
7658
7859
  await hub.profiles.unassign(addr, revision2(args), { signal });
7659
7860
  return `unpinned ${addr}`;
7660
7861
  }
7661
7862
  }
7662
7863
  ];
7663
7864
 
7865
+ // packages/cli/src/grants.ts
7866
+ var GRANT_FLAGS = {
7867
+ json: "json",
7868
+ holder: "string",
7869
+ project: "multi",
7870
+ allow: "multi",
7871
+ permit: "multi",
7872
+ never: "multi",
7873
+ expires: "string",
7874
+ "max-per-hour": "string",
7875
+ "allow-always": "bool"
7876
+ };
7877
+ var GRANT_REQUEST_FLAGS = { ...GRANT_FLAGS, "permission-allow": "multi", why: "string" };
7878
+ var GRANT_USER_HINT = "only the user manages grants: use the console, or `hub login` with the user's device token";
7879
+ function userOnly2(source, what) {
7880
+ if (source === "env")
7881
+ throw new CliError(EXIT.forbidden, `only the user can ${what} a grant, not a session`, GRANT_USER_HINT);
7882
+ }
7883
+ function grantError(error) {
7884
+ if (error instanceof CliError && error.exit === EXIT.forbidden)
7885
+ return new CliError(error.exit, error.message, [error.hint, GRANT_USER_HINT].filter(Boolean).join("; "), error.envelope);
7886
+ return error;
7887
+ }
7888
+ var listOf = (flags, name) => (flags[name] ?? []).flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
7889
+ async function grantSpecArg(flags, stdin) {
7890
+ return await grantArg(flags, stdin, true);
7891
+ }
7892
+ function grantRequestArg(flags, stdin) {
7893
+ return grantArg(flags, stdin, false);
7894
+ }
7895
+ async function grantArg(flags, stdin, holderRequired) {
7896
+ const json = typeof flags.json === "string" ? flags.json : undefined;
7897
+ const flagged = Object.keys(GRANT_REQUEST_FLAGS).filter((name) => name !== "json" && name !== "why" && flags[name] !== undefined);
7898
+ if (json !== undefined) {
7899
+ if (flagged.length)
7900
+ throw new UsageError(`give either --json '{…}' or ${flagged.map((f) => `--${f}`).join(", ")}`);
7901
+ return await jsonObjectArg(json, stdin, "grant");
7902
+ }
7903
+ const holder = str(flags, "holder");
7904
+ if (holder === undefined && holderRequired)
7905
+ throw new UsageError("needs --holder (ses_…, @name, @@orchestrator…, or app:<token id>)");
7906
+ const projects = listOf(flags, "project");
7907
+ if (!projects.length)
7908
+ throw new UsageError("needs --project <path> (repeatable)");
7909
+ const allow = listOf(flags, "allow");
7910
+ if (!allow.length)
7911
+ throw new UsageError("needs --allow <verbs> (approve, deny, answer, prompt, stop, roles, launch)");
7912
+ const permit = [...listOf(flags, "permit"), ...listOf(flags, "permission-allow")];
7913
+ const never = listOf(flags, "never");
7914
+ const expires = str(flags, "expires");
7915
+ const cap = str(flags, "max-per-hour");
7916
+ if (cap !== undefined && !/^\d+$/.test(cap))
7917
+ throw new UsageError("--max-per-hour is a whole number");
7918
+ return {
7919
+ ...holder !== undefined ? { holder } : {},
7920
+ scope: { projects },
7921
+ allow,
7922
+ ...permit.length || never.length ? { permissions: { ...permit.length ? { allow: permit } : {}, ...never.length ? { never } : {} } } : {},
7923
+ ...expires !== undefined ? { expires } : {},
7924
+ ...cap !== undefined ? { maxActionsPerHour: Number(cap) } : {},
7925
+ ...flags["allow-always"] === true ? { allowAlways: true } : {}
7926
+ };
7927
+ }
7928
+ var iso4 = (ms) => new Date(ms).toISOString().replace(/\.\d{3}Z$/, "Z");
7929
+ var grantLine = (g) => [g.id, g.state, g.holder, g.allow.join(","), g.scope.projects.join(","), `until ${iso4(g.expiresAt)}`].join(" ");
7930
+ var grantsText = (grants) => grants.length ? grants.map(grantLine).join(`
7931
+ `) : "no grants";
7932
+ function grantText2(g) {
7933
+ const list = (values) => values.length ? values.join(", ") : "none";
7934
+ return [
7935
+ `${g.id} ${g.state}`,
7936
+ `holder: ${g.holder}${g.holderSessionID && g.holderSessionID !== g.holder ? ` (${g.holderSessionID})` : ""}`,
7937
+ `projects: ${list(g.scope.projects)}`,
7938
+ `verbs: ${list(g.allow)}`,
7939
+ `permissions allow: ${list(g.permissions.allow)}`,
7940
+ `permissions never: ${list(g.permissions.never)}`,
7941
+ `always: ${g.allowAlways ? "yes" : "no"}`,
7942
+ `max actions per hour: ${g.maxActionsPerHour}`,
7943
+ g.revokedAt !== undefined ? `revoked: ${iso4(g.revokedAt)}` : `expires: ${iso4(g.expiresAt)}`
7944
+ ].join(`
7945
+ `);
7946
+ }
7947
+ var requestedText = (body) => `requested; waiting for the user (Decision ${body.decision.id})`;
7948
+
7949
+ // packages/cli/src/grants-mcp.ts
7950
+ var STRING8 = { type: "string" };
7951
+ var GRANT = { type: "object", description: "The grant spec accepted by POST /api/agent/grants: holder (optional for a request), scope.projects, allow, permissions, allowAlways, maxActionsPerHour and expires. The server validates all fields.", additionalProperties: true };
7952
+ var need8 = (args, key) => {
7953
+ if (typeof args[key] !== "string" || !args[key].trim())
7954
+ throw new ToolArgError(`"${key}" is required (a non-empty string)`);
7955
+ return args[key];
7956
+ };
7957
+ var spec = (args) => {
7958
+ if (typeof args.grant !== "object" || args.grant === null || Array.isArray(args.grant))
7959
+ throw new ToolArgError('"grant" is required (an object)');
7960
+ return args.grant;
7961
+ };
7962
+ var grantTools = [
7963
+ {
7964
+ name: "grant_request",
7965
+ description: "Ask the user for a grant: opens one tap Decision for @me (approve or deny). Sessions and apps with messages:send only; one open request and at most five per hour. Only the user's answer creates authority.",
7966
+ properties: { grant: GRANT, why: STRING8 },
7967
+ required: ["grant", "why"],
7968
+ readOnly: false,
7969
+ run: async (hub, args, signal) => requestedText(await hub.requestGrant(spec(args), need8(args, "why"), { signal }))
7970
+ },
7971
+ {
7972
+ name: "grant_list",
7973
+ description: "List grants (active, expired or revoked) whose holder is this session or app, including grants for roles it holds now. Apps need messages:read. A user credential has no holder: use the console or hub grant list for administration.",
7974
+ properties: {},
7975
+ readOnly: true,
7976
+ run: async (hub, _args, signal) => grantsText((await hub.heldGrants({ signal })).grants)
7977
+ },
7978
+ {
7979
+ name: "grant_show",
7980
+ description: "Show one grant only if this credential currently holds it. Apps need messages:read; another holder's ID answers not_found.",
7981
+ properties: { id: STRING8 },
7982
+ required: ["id"],
7983
+ readOnly: true,
7984
+ run: async (hub, args, signal) => grantText2((await hub.heldGrant(need8(args, "id"), { signal })).grant)
7985
+ },
7986
+ {
7987
+ name: "grant_revoke",
7988
+ description: "Irreversibly revoke a grant held by this session or app (including a held role shared with other holders). Apps need messages:send; another holder's ID answers not_found.",
7989
+ properties: { id: STRING8 },
7990
+ required: ["id"],
7991
+ readOnly: false,
7992
+ run: async (hub, args, signal) => grantText2((await hub.revokeHeldGrant(need8(args, "id"), { signal })).grant)
7993
+ },
7994
+ {
7995
+ name: "grant_create",
7996
+ description: `Create a grant directly ONLY with the user's device credential. Sessions and apps must use grant_request and wait for the user's tap. ${GRANT_USER_HINT}.`,
7997
+ properties: { grant: GRANT },
7998
+ required: ["grant"],
7999
+ readOnly: false,
8000
+ run: async (hub, args, signal) => {
8001
+ const actor = (await hub.whoami({ signal })).actor;
8002
+ if (actor.kind !== "user")
8003
+ throw new CliError(EXIT.forbidden, "only the user can create a grant, not a session or app", GRANT_USER_HINT);
8004
+ try {
8005
+ return grantText2((await hub.createGrant(spec(args), { signal })).grant);
8006
+ } catch (cause) {
8007
+ throw grantError(cause);
8008
+ }
8009
+ }
8010
+ }
8011
+ ];
8012
+
7664
8013
  // packages/cli/src/watchlist.ts
7665
- var safe = (value) => sanitizeLabel(String(value), 1024);
7666
- var list2 = (items) => items.length ? items.map(safe).join(", ") : "none";
8014
+ var safe2 = (value) => sanitizeLabel(String(value), 1024);
8015
+ var list2 = (items) => items.length ? items.map(safe2).join(", ") : "none";
7667
8016
  function watchlistConfigText(c) {
7668
8017
  return [
7669
- `revision ${safe(c.revision)}`,
7670
- `auto-active: ${c.autoActive ? `on (within ${safe(c.activeWithinHours)} h)` : "off"} pins: ${c.usePins ? "on" : "off"}`,
8018
+ `revision ${safe2(c.revision)}`,
8019
+ `auto-active: ${c.autoActive ? `on (within ${safe2(c.activeWithinHours)} h)` : "off"} pins: ${c.usePins ? "on" : "off"}`,
7671
8020
  `sessions: ${list2(c.sessions)}`,
7672
8021
  `projects: ${list2(c.projects)}`,
7673
- `labels: ${c.labels.length ? c.labels.map((l) => Object.entries(l).map(([k, v]) => `${safe(k)}=${safe(v)}`).join(",")).join("; ") : "none"}`,
8022
+ `labels: ${c.labels.length ? c.labels.map((l) => Object.entries(l).map(([k, v]) => `${safe2(k)}=${safe2(v)}`).join(",")).join("; ") : "none"}`,
7674
8023
  `muted: ${list2(c.muted)}`,
7675
8024
  `muted projects: ${list2(c.mutedProjects)}`
7676
8025
  ].join(`
7677
8026
  `);
7678
8027
  }
7679
8028
  function watchlistText(view) {
7680
- const rows = view.entries.map((e) => `${safe(e.sessionID)} ${e.reasons.map(safe).join(",")}${e.lastActiveAt !== undefined ? ` active ${new Date(e.lastActiveAt).toISOString()}` : ""}`);
8029
+ const rows = view.entries.map((e) => `${safe2(e.sessionID)} ${e.reasons.map(safe2).join(",")}${e.lastActiveAt !== undefined ? ` active ${new Date(e.lastActiveAt).toISOString()}` : ""}`);
7681
8030
  return [watchlistConfigText(view.config), "", view.entries.length ? `watching ${view.entries.length}${view.truncated ? " (truncated)" : ""}:` : "watching nothing", ...rows].join(`
7682
8031
  `);
7683
8032
  }
7684
8033
  function watchlistChangedText(verb, r) {
7685
- return `${r.changed ? `${verb}` : `already ${verb}`}: ${safe(r.sessionID)} (revision ${safe(r.config.revision)})`;
8034
+ return `${r.changed ? `${verb}` : `already ${verb}`}: ${safe2(r.sessionID)} (revision ${safe2(r.config.revision)})`;
7686
8035
  }
7687
8036
  var PAST = { watch: "watching", unwatch: "not watching", mute: "muted", unmute: "unmuted" };
7688
8037
  var watchlistVerbText = (verb, r) => watchlistChangedText(PAST[verb], r);
7689
8038
  var ADDR = { type: "string", description: "Session address: ses_… (or a unique prefix), @name, @@role, @name.project." };
7690
8039
  var STRINGS3 = { type: "array", items: { type: "string" } };
7691
- var need6 = (args, key) => {
8040
+ var need9 = (args, key) => {
7692
8041
  const value = args[key];
7693
8042
  if (typeof value !== "string" || !value.trim())
7694
8043
  throw new ToolArgError(`"${key}" is required (a non-empty string)`);
@@ -7734,13 +8083,13 @@ var watchlistTools = [
7734
8083
  throw new ToolArgError(`"expectedRevision" is required (the revision you read)`);
7735
8084
  const unknown = Object.keys(args).filter((k) => k !== "expectedRevision" && !UPDATE_FIELDS.includes(k));
7736
8085
  if (unknown.length)
7737
- throw new ToolArgError(`Unknown argument${unknown.length > 1 ? "s" : ""} ${unknown.map((k) => `"${safe(k).slice(0, 40)}"`).join(", ")}: use ${UPDATE_FIELDS.join(", ")} and expectedRevision`);
8086
+ throw new ToolArgError(`Unknown argument${unknown.length > 1 ? "s" : ""} ${unknown.map((k) => `"${safe2(k).slice(0, 40)}"`).join(", ")}: use ${UPDATE_FIELDS.join(", ")} and expectedRevision`);
7738
8087
  const patch = {};
7739
8088
  for (const field of UPDATE_FIELDS)
7740
8089
  if (args[field] !== undefined && args[field] !== null)
7741
8090
  patch[field] = args[field];
7742
8091
  const r = await hub.concierge.watchlist.update(patch, expected, { signal });
7743
- const head = r.config.revision === expected ? `No change (revision ${safe(expected)})` : `Updated the watchlist (revision ${safe(expected)} → ${safe(r.config.revision)})`;
8092
+ const head = r.config.revision === expected ? `No change (revision ${safe2(expected)})` : `Updated the watchlist (revision ${safe2(expected)} → ${safe2(r.config.revision)})`;
7744
8093
  return `${head}
7745
8094
  ${watchlistConfigText(r.config)}`;
7746
8095
  }
@@ -7755,7 +8104,7 @@ ${watchlistConfigText(r.config)}`;
7755
8104
  idempotent: true,
7756
8105
  run: async (hub, args, signal) => {
7757
8106
  const on = flag(args, "watch", true);
7758
- const addr = need6(args, "addr");
8107
+ const addr = need9(args, "addr");
7759
8108
  const r = on ? await hub.concierge.watchlist.watch(addr, { signal }) : await hub.concierge.watchlist.unwatch(addr, { signal });
7760
8109
  return watchlistVerbText(on ? "watch" : "unwatch", r);
7761
8110
  }
@@ -7770,7 +8119,7 @@ ${watchlistConfigText(r.config)}`;
7770
8119
  idempotent: true,
7771
8120
  run: async (hub, args, signal) => {
7772
8121
  const on = flag(args, "mute", true);
7773
- const addr = need6(args, "addr");
8122
+ const addr = need9(args, "addr");
7774
8123
  const r = on ? await hub.concierge.watchlist.mute(addr, { signal }) : await hub.concierge.watchlist.unmute(addr, { signal });
7775
8124
  return watchlistVerbText(on ? "mute" : "unmute", r);
7776
8125
  }
@@ -7781,13 +8130,13 @@ ${watchlistConfigText(r.config)}`;
7781
8130
  var MCP_PROTOCOL_VERSIONS = ["2025-06-18", "2025-03-26", "2024-11-05"];
7782
8131
  var RPC = { parse: -32700, invalidRequest: -32600, methodNotFound: -32601, invalidParams: -32602, internal: -32603 };
7783
8132
  var ArgError3 = ToolArgError;
7784
- var need7 = (args, key) => {
8133
+ var need10 = (args, key) => {
7785
8134
  const value = args[key];
7786
8135
  if (typeof value !== "string" || !value.trim())
7787
8136
  throw new ArgError3(`"${key}" is required (a non-empty string)`);
7788
8137
  return value;
7789
8138
  };
7790
- var optional7 = (args, key) => {
8139
+ var optional8 = (args, key) => {
7791
8140
  const value = args[key];
7792
8141
  if (value === undefined || value === null)
7793
8142
  return;
@@ -7868,7 +8217,7 @@ var sendOptions = (args) => {
7868
8217
  return { ...wake !== undefined ? { wake } : {}, ...urgent !== undefined ? { urgent } : {}, ...notAfter !== undefined ? { notAfter } : {} };
7869
8218
  };
7870
8219
  var whyOption = (args) => {
7871
- const why = optional7(args, "why");
8220
+ const why = optional8(args, "why");
7872
8221
  return why !== undefined ? { why } : {};
7873
8222
  };
7874
8223
  var optionalDelivery = (args) => {
@@ -7911,35 +8260,35 @@ var definedFields = (fields) => {
7911
8260
  return Object.keys(out).length ? out : undefined;
7912
8261
  };
7913
8262
  function launchSpec(args) {
7914
- const repo = need7(args, "repo");
8263
+ const repo = need10(args, "repo");
7915
8264
  if (!repo.startsWith("/"))
7916
8265
  throw new ArgError3(`"repo" must be an absolute path on the node`);
7917
8266
  const kind = optionalEnum(args, "runtime", RUNTIME_KINDS);
7918
- const profile = optional7(args, "profile");
8267
+ const profile = optional8(args, "profile");
7919
8268
  if (profile !== undefined && kind === undefined)
7920
8269
  throw new ArgError3(`"profile" needs "runtime" (e.g. openshell)`);
7921
8270
  const nodeLabels = optionalLabels(args, "nodeLabels");
7922
- const machine = optional7(args, "machine");
8271
+ const machine = optional8(args, "machine");
7923
8272
  const prefer = optionalBool(args, "prefer") === true;
7924
8273
  if (prefer && machine === undefined && nodeLabels === undefined)
7925
8274
  throw new ArgError3(`"prefer" needs "machine" or "nodeLabels"`);
7926
- const workspace = { repo, ...definedFields({ mode: optionalEnum(args, "mode", WORKSPACE_MODES), branch: optional7(args, "branch"), base: optional7(args, "base"), remote: optional7(args, "remote") }) };
8275
+ const workspace = { repo, ...definedFields({ mode: optionalEnum(args, "mode", WORKSPACE_MODES), branch: optional8(args, "branch"), base: optional8(args, "base"), remote: optional8(args, "remote") }) };
7927
8276
  return {
7928
8277
  ...definedFields({
7929
- key: optional7(args, "key"),
7930
- name: optional7(args, "name")?.replace(/^@+/, ""),
8278
+ key: optional8(args, "key"),
8279
+ name: optional8(args, "name")?.replace(/^@+/, ""),
7931
8280
  roles: optionalStrings(args, "roles")?.map((r) => r.replace(/^@@/, "")),
7932
- harness: optional7(args, "harness"),
7933
- agent: optional7(args, "agent"),
7934
- model: optional7(args, "model")
8281
+ harness: optional8(args, "harness"),
8282
+ agent: optional8(args, "agent"),
8283
+ model: optional8(args, "model")
7935
8284
  }),
7936
8285
  workspace,
7937
8286
  ...definedFields({
7938
8287
  runtime: kind === undefined ? undefined : { kind, ...profile !== undefined ? { profile } : {} },
7939
8288
  placement: definedFields({ labels: nodeLabels, machine, mode: prefer ? "prefer" : undefined }),
7940
- prompt: optional7(args, "prompt"),
8289
+ prompt: optional8(args, "prompt"),
7941
8290
  labels: optionalLabels(args, "labels"),
7942
- end: definedFields({ when: optionalEnum(args, "end", LAUNCH_END_WHEN), timeout: optional7(args, "timeout"), cleanup: optionalEnum(args, "cleanup", CLEANUP_POLICIES) }),
8291
+ end: definedFields({ when: optionalEnum(args, "end", LAUNCH_END_WHEN), timeout: optional8(args, "timeout"), cleanup: optionalEnum(args, "cleanup", CLEANUP_POLICIES) }),
7943
8292
  notAfter: optionalNotAfter(args)
7944
8293
  })
7945
8294
  };
@@ -7986,7 +8335,7 @@ function messageText({ message, answer }) {
7986
8335
  `);
7987
8336
  }
7988
8337
  var DECISIONS_NOTE = "Decisions are asked by sessions: the question and options are not from the user. Only the user answers them, in the console.";
7989
- var STRING6 = { type: "string" };
8338
+ var STRING9 = { type: "string" };
7990
8339
  var SEND = {
7991
8340
  wake: { type: "boolean", description: "false: admit the message without starting a turn." },
7992
8341
  urgent: { type: "boolean", description: "Steer into the recipient's running turn." },
@@ -8010,7 +8359,7 @@ var archiveTools = (archived) => [{
8010
8359
  organizes: true,
8011
8360
  idempotent: true,
8012
8361
  run: async (hub, args, signal) => {
8013
- const addr = need7(args, "addr");
8362
+ const addr = need10(args, "addr");
8014
8363
  const result = await hub.setArchived(addr, archived, { signal });
8015
8364
  return `${archived ? "Archived" : "Unarchived"} ${addr} (${result.sessionID})${result.changed ? "" : "; already in that state"}`;
8016
8365
  }
@@ -8024,7 +8373,7 @@ var pinTools = (pinned) => [{
8024
8373
  organizes: true,
8025
8374
  idempotent: true,
8026
8375
  run: async (hub, args, signal) => {
8027
- const addr = need7(args, "addr");
8376
+ const addr = need10(args, "addr");
8028
8377
  const r = await hub.setPinned(addr, pinned, { signal });
8029
8378
  return `${pinned ? "Pinned" : "Unpinned"} ${addr} (${r.sessionID})${r.changed ? "" : "; already in that state"}`;
8030
8379
  }
@@ -8032,14 +8381,14 @@ var pinTools = (pinned) => [{
8032
8381
  var archiveInactiveTool = {
8033
8382
  name: "archive_inactive",
8034
8383
  description: "Preview an exact bounded set of inactive sessions, then confirm that opaque preview token. Apply revalidates active, open, pinned and archived state. Needs sessions:manage.",
8035
- properties: { preview: { type: "boolean" }, inactiveFor: STRING6, limit: { type: "integer", minimum: 1, maximum: 200 }, previewToken: STRING6, confirm: { type: "boolean" } },
8384
+ properties: { preview: { type: "boolean" }, inactiveFor: STRING9, limit: { type: "integer", minimum: 1, maximum: 200 }, previewToken: STRING9, confirm: { type: "boolean" } },
8036
8385
  readOnly: false,
8037
8386
  organizes: true,
8038
8387
  run: async (hub, args, signal) => {
8039
8388
  if (args.preview === true)
8040
- return JSON.stringify(await hub.previewArchiveInactive(need7(args, "inactiveFor"), optionalInt(args, "limit", 1, 200) ?? 50, { signal }));
8389
+ return JSON.stringify(await hub.previewArchiveInactive(need10(args, "inactiveFor"), optionalInt(args, "limit", 1, 200) ?? 50, { signal }));
8041
8390
  if (args.confirm === true)
8042
- return JSON.stringify(await hub.archiveInactive(need7(args, "previewToken"), { signal }));
8391
+ return JSON.stringify(await hub.archiveInactive(need10(args, "previewToken"), { signal }));
8043
8392
  throw new ArgError3("Use preview:true with inactiveFor, then confirm:true with previewToken");
8044
8393
  }
8045
8394
  };
@@ -8050,19 +8399,19 @@ var intentTools = [
8050
8399
  properties: { addr: ADDR2 },
8051
8400
  required: ["addr"],
8052
8401
  readOnly: true,
8053
- run: async (hub, args, signal) => intentLine(need7(args, "addr"), (await hub.intent(need7(args, "addr"), { signal })).intent)
8402
+ run: async (hub, args, signal) => intentLine(need10(args, "addr"), (await hub.intent(need10(args, "addr"), { signal })).intent)
8054
8403
  },
8055
8404
  {
8056
8405
  name: "session_intent_set",
8057
8406
  description: "Set (text, one line, at most 280 characters) or clear (clear: true) a session's intent. Users and apps (sessions:manage) set the user's intent; a session token only its own session's. expectedRevision: the revision read (read first when absent); a stale one is a conflict.",
8058
- properties: { addr: ADDR2, text: STRING6, clear: { type: "boolean" }, expectedRevision: { type: "integer", minimum: 0 } },
8407
+ properties: { addr: ADDR2, text: STRING9, clear: { type: "boolean" }, expectedRevision: { type: "integer", minimum: 0 } },
8059
8408
  required: ["addr"],
8060
8409
  readOnly: false,
8061
8410
  organizes: true,
8062
8411
  idempotent: true,
8063
8412
  run: async (hub, args, signal) => {
8064
- const addr = need7(args, "addr");
8065
- const text = optional7(args, "text");
8413
+ const addr = need10(args, "addr");
8414
+ const text = optional8(args, "text");
8066
8415
  const clear = args.clear === true;
8067
8416
  if (clear === (text !== undefined))
8068
8417
  throw new ArgError3('Give "text" or "clear": true');
@@ -8072,22 +8421,22 @@ var intentTools = [
8072
8421
  }
8073
8422
  ];
8074
8423
  var TARGET = { ...ADDR2, description: `${ADDR2.description} Required for an app token; a session token defaults to itself.` };
8075
- var ROLE2 = { ...STRING6, description: "A role (reviewer, @@reviewer or reviewer.project)." };
8076
- var ROLE_PROJECT = { ...STRING6, description: "Scope the role to this project (as role.project)." };
8424
+ var ROLE2 = { ...STRING9, description: "A role (reviewer, @@reviewer or reviewer.project)." };
8425
+ var ROLE_PROJECT = { ...STRING9, description: "Scope the role to this project (as role.project)." };
8077
8426
  var MANAGE_NOTE = "Needs scope sessions:manage for an app token";
8078
8427
  var roleText2 = (r) => `@@${r.role}${r.project !== undefined ? `.${r.project}` : ""}`;
8079
8428
  var nameTools = [
8080
8429
  {
8081
8430
  name: "name",
8082
8431
  description: `Name a session (@name); as a session, leave addr out to name yourself (a session token names only its own session). Its previous name stays an alias for 24 h. A taken name fails (name_taken); @me is reserved. ${MANAGE_NOTE}.`,
8083
- properties: { addr: TARGET, name: { ...STRING6, description: "The name: 2–40 characters a-z, 0-9 and - (a leading @ is fine)." } },
8432
+ properties: { addr: TARGET, name: { ...STRING9, description: "The name: 2–40 characters a-z, 0-9 and - (a leading @ is fine)." } },
8084
8433
  required: ["name"],
8085
8434
  readOnly: false,
8086
8435
  organizes: true,
8087
8436
  idempotent: true,
8088
8437
  run: async (hub, args, signal) => {
8089
- const session = optional7(args, "addr");
8090
- const r = await hub.name(need7(args, "name"), { ...session ? { session } : {}, signal });
8438
+ const session = optional8(args, "addr");
8439
+ const r = await hub.name(need10(args, "name"), { ...session ? { session } : {}, signal });
8091
8440
  return `Named ${r.sessionID} @${r.name}${r.alias ? ` (@${r.alias} stays an alias for 24 h)` : ""}`;
8092
8441
  }
8093
8442
  },
@@ -8100,9 +8449,9 @@ var nameTools = [
8100
8449
  organizes: true,
8101
8450
  idempotent: true,
8102
8451
  run: async (hub, args, signal) => {
8103
- const session = optional7(args, "addr");
8104
- const project = optional7(args, "project");
8105
- const r = await hub.claimRole(need7(args, "role"), { ...session ? { session } : {}, ...project !== undefined ? { project } : {}, ...optionalBool(args, "exclusive") ? { exclusive: true } : {}, signal });
8452
+ const session = optional8(args, "addr");
8453
+ const project = optional8(args, "project");
8454
+ const r = await hub.claimRole(need10(args, "role"), { ...session ? { session } : {}, ...project !== undefined ? { project } : {}, ...optionalBool(args, "exclusive") ? { exclusive: true } : {}, signal });
8106
8455
  return `${r.sessionID} holds ${roleText2(r)} (generation ${r.generation}${r.exclusive ? ", exclusive" : ""})`;
8107
8456
  }
8108
8457
  },
@@ -8114,9 +8463,9 @@ var nameTools = [
8114
8463
  readOnly: false,
8115
8464
  organizes: true,
8116
8465
  run: async (hub, args, signal) => {
8117
- const session = optional7(args, "addr");
8118
- const project = optional7(args, "project");
8119
- const r = await hub.releaseRole(need7(args, "role"), { ...session ? { session } : {}, ...project !== undefined ? { project } : {}, signal });
8466
+ const session = optional8(args, "addr");
8467
+ const project = optional8(args, "project");
8468
+ const r = await hub.releaseRole(need10(args, "role"), { ...session ? { session } : {}, ...project !== undefined ? { project } : {}, signal });
8120
8469
  return `${r.sessionID} released ${roleText2(r)}`;
8121
8470
  }
8122
8471
  }
@@ -8125,16 +8474,16 @@ var eventsTool = {
8125
8474
  name: "events",
8126
8475
  description: "Compact Hub events (session.*, blocker.*, machine.changed, message.changed, launch.changed, session.archived) after a cursor, as JSON {events, cursor, more, reset, refresh?}. Call without cursor to start at the latest, then pass back the returned cursor. reset:true means the cursor expired: re-read the listed snapshots (status, sessions, inbox, …) and continue with the new cursor. Unknown event types are valid; ignore them. Needs scope events:subscribe (plus each family's scope) for an app token.",
8127
8476
  properties: {
8128
- cursor: { ...STRING6, description: "The cursor from the previous events answer (hev1_…); omit to start at the latest." },
8477
+ cursor: { ...STRING9, description: "The cursor from the previous events answer (hev1_…); omit to start at the latest." },
8129
8478
  wait: { type: "integer", minimum: 0, maximum: WAIT_POLL_MAX_S, description: `Seconds to wait while nothing matches (0–${WAIT_POLL_MAX_S}).` },
8130
8479
  limit: { type: "integer", minimum: 1, maximum: 200, description: "At most this many events (default 50)." },
8131
- types: { type: "array", items: STRING6, description: "Only these event types (exact, or a family like launch.*)." },
8132
- sessions: { type: "array", items: STRING6, description: "Only events of these sessions (ses_…, @name, @@role)." }
8480
+ types: { type: "array", items: STRING9, description: "Only these event types (exact, or a family like launch.*)." },
8481
+ sessions: { type: "array", items: STRING9, description: "Only events of these sessions (ses_…, @name, @@role)." }
8133
8482
  },
8134
8483
  readOnly: true,
8135
8484
  run: async (hub, args, signal) => {
8136
8485
  const page = await hub.events({
8137
- cursor: optional7(args, "cursor"),
8486
+ cursor: optional8(args, "cursor"),
8138
8487
  wait: optionalInt(args, "wait", 0, WAIT_POLL_MAX_S),
8139
8488
  limit: optionalInt(args, "limit", 1, 200),
8140
8489
  types: optionalStrings(args, "types"),
@@ -8149,13 +8498,13 @@ var webhookTools = [
8149
8498
  {
8150
8499
  name: "webhook_create",
8151
8500
  description: `Create a durable signed webhook for canonical compact events (e.g. launch.*, session.*, message.changed, team.intake). The signing secret is returned only here. Needs events:subscribe plus every selected family's scope; events ["team.intake"] alone needs only teams:submit (an app gets only its own submissions).`,
8152
- properties: { url: STRING6, events: { type: "array", items: STRING6 }, label: STRING6 },
8501
+ properties: { url: STRING9, events: { type: "array", items: STRING9 }, label: STRING9 },
8153
8502
  required: ["url"],
8154
8503
  readOnly: false,
8155
8504
  run: async (hub, args, signal) => {
8156
8505
  const events = optionalStrings(args, "events");
8157
- const label = optional7(args, "label");
8158
- const r = await hub.webhookCreate({ url: need7(args, "url"), ...events ? { events } : {}, ...label ? { label } : {} }, { signal });
8506
+ const label = optional8(args, "label");
8507
+ const r = await hub.webhookCreate({ url: need10(args, "url"), ...events ? { events } : {}, ...label ? { label } : {} }, { signal });
8159
8508
  return `${webhookText(r.webhook)}
8160
8509
  signing secret (shown once): ${r.secret}`;
8161
8510
  }
@@ -8174,11 +8523,11 @@ signing secret (shown once): ${r.secret}`;
8174
8523
  {
8175
8524
  name: "webhook_show",
8176
8525
  description: "Show one webhook and its retained delivery attempts; never its secret.",
8177
- properties: { id: STRING6 },
8526
+ properties: { id: STRING9 },
8178
8527
  required: ["id"],
8179
8528
  readOnly: true,
8180
8529
  run: async (hub, args, signal) => {
8181
- const r = await hub.webhook(need7(args, "id"), { signal });
8530
+ const r = await hub.webhook(need10(args, "id"), { signal });
8182
8531
  return `${webhookText(r.webhook)}
8183
8532
  ${JSON.stringify(r.deliveries)}`;
8184
8533
  }
@@ -8186,20 +8535,20 @@ ${JSON.stringify(r.deliveries)}`;
8186
8535
  {
8187
8536
  name: "webhook_delete",
8188
8537
  description: "Delete one webhook and its pending deliveries.",
8189
- properties: { id: STRING6 },
8538
+ properties: { id: STRING9 },
8190
8539
  required: ["id"],
8191
8540
  readOnly: false,
8192
8541
  organizes: true,
8193
8542
  idempotent: false,
8194
8543
  run: async (hub, args, signal) => {
8195
- const id = need7(args, "id");
8544
+ const id = need10(args, "id");
8196
8545
  await hub.webhookDelete(id, { signal });
8197
8546
  return `Deleted webhook ${id}`;
8198
8547
  }
8199
8548
  }
8200
8549
  ];
8201
8550
  async function profileTarget(hub, args, signal) {
8202
- const addr = optional7(args, "addr");
8551
+ const addr = optional8(args, "addr");
8203
8552
  if (addr)
8204
8553
  return addr;
8205
8554
  const { actor } = await hub.whoami({ signal });
@@ -8238,11 +8587,11 @@ var profileTools = [
8238
8587
  {
8239
8588
  name: "profile_skill",
8240
8589
  description: "Load one skill's full text from the profile pinned to a session (your own by default), on demand.",
8241
- properties: { name: STRING6, addr: ADDR2 },
8590
+ properties: { name: STRING9, addr: ADDR2 },
8242
8591
  required: ["name"],
8243
8592
  readOnly: true,
8244
8593
  run: async (hub, args, signal) => {
8245
- const r = await hub.profiles.sessionSkill(await profileTarget(hub, args, signal), need7(args, "name"), { signal });
8594
+ const r = await hub.profiles.sessionSkill(await profileTarget(hub, args, signal), need10(args, "name"), { signal });
8246
8595
  return `skill ${r.skill.name} (${r.profileID} v${r.version}):
8247
8596
  ${sanitizeText(r.skill.body)}`;
8248
8597
  }
@@ -8250,11 +8599,11 @@ ${sanitizeText(r.skill.body)}`;
8250
8599
  {
8251
8600
  name: "profile_command",
8252
8601
  description: "Render one command of the profile pinned to a session (your own by default) with its {{name}} parameters. Returns the text only: nothing is sent. {{secret:NAME}} references stay unresolved.",
8253
- properties: { name: STRING6, params: { type: "object", additionalProperties: STRING6, description: "Parameter name → text value." }, addr: ADDR2 },
8602
+ properties: { name: STRING9, params: { type: "object", additionalProperties: STRING9, description: "Parameter name → text value." }, addr: ADDR2 },
8254
8603
  required: ["name"],
8255
8604
  readOnly: true,
8256
8605
  run: async (hub, args, signal) => {
8257
- const r = await hub.profiles.sessionRender(await profileTarget(hub, args, signal), need7(args, "name"), optionalParams(args), { signal });
8606
+ const r = await hub.profiles.sessionRender(await profileTarget(hub, args, signal), need10(args, "name"), optionalParams(args), { signal });
8258
8607
  return r.prompt;
8259
8608
  }
8260
8609
  },
@@ -8273,13 +8622,13 @@ ${sanitizeText(r.skill.body)}`;
8273
8622
  {
8274
8623
  name: "profile_usage",
8275
8624
  description: "Where one profile is used: pinned sessions (requested vs running version, ack, presence, harness), team roles naming it, launches that named it and the sessions' workspaces, with counts. An app token needs profiles:read, plus sessions:read / teams:read / sessions:launch (its own launches) / workspaces:read for each part; parts it may not read are listed as hidden.",
8276
- properties: { id: STRING6, limit: { type: "integer", minimum: 1, maximum: 200, description: "Sessions per page (default 50)." }, cursor: STRING6 },
8625
+ properties: { id: STRING9, limit: { type: "integer", minimum: 1, maximum: 200, description: "Sessions per page (default 50)." }, cursor: STRING9 },
8277
8626
  required: ["id"],
8278
8627
  readOnly: true,
8279
8628
  run: async (hub, args, signal) => {
8280
8629
  const limit = optionalInt(args, "limit", 1, 200);
8281
- const cursor = optional7(args, "cursor");
8282
- const u = await hub.profiles.usage(need7(args, "id"), { ...limit !== undefined ? { limit } : {}, ...cursor ? { cursor } : {}, signal });
8630
+ const cursor = optional8(args, "cursor");
8631
+ const u = await hub.profiles.usage(need10(args, "id"), { ...limit !== undefined ? { limit } : {}, ...cursor ? { cursor } : {}, signal });
8283
8632
  const c = u.counts;
8284
8633
  const plural = (n, word, many = `${word}s`) => `${n} ${n === 1 ? word : many}`;
8285
8634
  const lines = [`${u.profileID} (latest v${u.latestVersion})`];
@@ -8322,7 +8671,7 @@ var needRevision = (args, key = "expectedRevision") => {
8322
8671
  return value;
8323
8672
  };
8324
8673
  var idemOption = (args) => {
8325
- const key = optional7(args, "idempotencyKey");
8674
+ const key = optional8(args, "idempotencyKey");
8326
8675
  return key !== undefined ? { idempotencyKey: key } : {};
8327
8676
  };
8328
8677
  var WORKSPACE = { type: "string", description: "The workspace's ID (ws_…)." };
@@ -8352,7 +8701,7 @@ var workspaceTools = [
8352
8701
  required: ["workspace"],
8353
8702
  readOnly: true,
8354
8703
  run: async (hub, args, signal) => {
8355
- const id = need7(args, "workspace");
8704
+ const id = need10(args, "workspace");
8356
8705
  const [{ workspace, bindings }, { members }, graph] = await Promise.all([hub.workspaces.get(id, { signal }), hub.workspaces.members(id, { signal }), hub.workspaces.supervision(id, { signal })]);
8357
8706
  const lines = [wsLine(workspace)];
8358
8707
  if (workspace.masterMemberID)
@@ -8368,27 +8717,27 @@ var workspaceTools = [
8368
8717
  {
8369
8718
  name: "workspace_create",
8370
8719
  description: `Create a coordination workspace. ${WS_SCOPES}`,
8371
- properties: { title: STRING6, description: STRING6, tags: { type: "array", items: STRING6 }, idempotencyKey: IDEMPOTENCY3 },
8720
+ properties: { title: STRING9, description: STRING9, tags: { type: "array", items: STRING9 }, idempotencyKey: IDEMPOTENCY3 },
8372
8721
  required: ["title"],
8373
8722
  readOnly: false,
8374
8723
  run: async (hub, args, signal) => {
8375
- const description = optional7(args, "description");
8724
+ const description = optional8(args, "description");
8376
8725
  const tags = optionalStrings(args, "tags");
8377
- const { workspace } = await hub.workspaces.create({ title: need7(args, "title"), ...description ? { description } : {}, ...tags ? { tags } : {} }, { ...idemOption(args), signal });
8726
+ const { workspace } = await hub.workspaces.create({ title: need10(args, "title"), ...description ? { description } : {}, ...tags ? { tags } : {} }, { ...idemOption(args), signal });
8378
8727
  return `created ${wsLine(workspace)}`;
8379
8728
  }
8380
8729
  },
8381
8730
  {
8382
8731
  name: "workspace_update",
8383
8732
  description: `Change a workspace's title, description or tags under its revision. ${WS_SCOPES}`,
8384
- properties: { workspace: WORKSPACE, expectedRevision: REVISION4, title: STRING6, description: STRING6, tags: { type: "array", items: STRING6 } },
8733
+ properties: { workspace: WORKSPACE, expectedRevision: REVISION4, title: STRING9, description: STRING9, tags: { type: "array", items: STRING9 } },
8385
8734
  required: ["workspace", "expectedRevision"],
8386
8735
  readOnly: false,
8387
8736
  run: async (hub, args, signal) => {
8388
- const patch = definedFields({ title: optional7(args, "title"), description: optional7(args, "description"), tags: optionalStrings(args, "tags") });
8737
+ const patch = definedFields({ title: optional8(args, "title"), description: optional8(args, "description"), tags: optionalStrings(args, "tags") });
8389
8738
  if (!patch)
8390
8739
  throw new ArgError3(`give "title", "description" or "tags"`);
8391
- const { workspace } = await hub.workspaces.update(need7(args, "workspace"), { expectedRevision: needRevision(args), ...patch }, { signal });
8740
+ const { workspace } = await hub.workspaces.update(need10(args, "workspace"), { expectedRevision: needRevision(args), ...patch }, { signal });
8392
8741
  return `updated ${wsLine(workspace)}`;
8393
8742
  }
8394
8743
  },
@@ -8400,7 +8749,7 @@ var workspaceTools = [
8400
8749
  readOnly: false,
8401
8750
  idempotent: true,
8402
8751
  run: async (hub, args, signal) => {
8403
- const id = need7(args, "workspace");
8752
+ const id = need10(args, "workspace");
8404
8753
  if (verb === "archive") {
8405
8754
  const r = await hub.workspaces.archive(id, needRevision(args), { signal });
8406
8755
  return `archived ${wsLine(r.workspace)}; ${r.departed} member(s) departed, ${r.supervision.detached} supervision link(s) detached`;
@@ -8411,31 +8760,31 @@ var workspaceTools = [
8411
8760
  {
8412
8761
  name: "workspace_bind",
8413
8762
  description: `Bind a workspace to a repository (absolute path) on one of the user's machines. ${WS_SCOPES}`,
8414
- properties: { workspace: WORKSPACE, machineID: STRING6, repo: STRING6, label: STRING6, idempotencyKey: IDEMPOTENCY3 },
8763
+ properties: { workspace: WORKSPACE, machineID: STRING9, repo: STRING9, label: STRING9, idempotencyKey: IDEMPOTENCY3 },
8415
8764
  required: ["workspace", "machineID", "repo"],
8416
8765
  readOnly: false,
8417
8766
  idempotent: true,
8418
8767
  run: async (hub, args, signal) => {
8419
- const label = optional7(args, "label");
8420
- const r = await hub.workspaces.bind(need7(args, "workspace"), { machineID: need7(args, "machineID"), repo: need7(args, "repo"), ...label ? { label } : {} }, { ...idemOption(args), signal });
8768
+ const label = optional8(args, "label");
8769
+ const r = await hub.workspaces.bind(need10(args, "workspace"), { machineID: need10(args, "machineID"), repo: need10(args, "repo"), ...label ? { label } : {} }, { ...idemOption(args), signal });
8421
8770
  return `${r.created ? "bound" : "already bound"} ${r.binding.id} ${r.binding.machineID}:${r.binding.repo} r${r.binding.revision}`;
8422
8771
  }
8423
8772
  },
8424
8773
  {
8425
8774
  name: "workspace_unbind",
8426
8775
  description: `Remove a repository binding under the binding's revision. ${WS_SCOPES}`,
8427
- properties: { workspace: WORKSPACE, bindingID: STRING6, expectedRevision: REVISION4 },
8776
+ properties: { workspace: WORKSPACE, bindingID: STRING9, expectedRevision: REVISION4 },
8428
8777
  required: ["workspace", "bindingID", "expectedRevision"],
8429
8778
  readOnly: false,
8430
8779
  run: async (hub, args, signal) => {
8431
- await hub.workspaces.unbind(need7(args, "workspace"), need7(args, "bindingID"), needRevision(args), { signal });
8432
- return `unbound ${need7(args, "bindingID")}`;
8780
+ await hub.workspaces.unbind(need10(args, "workspace"), need10(args, "bindingID"), needRevision(args), { signal });
8781
+ return `unbound ${need10(args, "bindingID")}`;
8433
8782
  }
8434
8783
  },
8435
8784
  {
8436
8785
  name: "workspace_join",
8437
8786
  description: `Add a session (ses_…) to a workspace as worker or observer (the master is set by explicit replacement). ${WS_SCOPES}`,
8438
- properties: { workspace: WORKSPACE, sessionID: STRING6, role: { type: "string", enum: [...MEMBER_ROLES] }, idempotencyKey: IDEMPOTENCY3 },
8787
+ properties: { workspace: WORKSPACE, sessionID: STRING9, role: { type: "string", enum: [...MEMBER_ROLES] }, idempotencyKey: IDEMPOTENCY3 },
8439
8788
  required: ["workspace", "sessionID", "role"],
8440
8789
  readOnly: false,
8441
8790
  idempotent: true,
@@ -8443,45 +8792,45 @@ var workspaceTools = [
8443
8792
  const role = optionalEnum(args, "role", MEMBER_ROLES);
8444
8793
  if (!role)
8445
8794
  throw new ArgError3(`"role" is one of ${MEMBER_ROLES.join(", ")}`);
8446
- const { member, created } = await hub.workspaces.join(need7(args, "workspace"), { sessionID: need7(args, "sessionID"), role }, { ...idemOption(args), signal });
8795
+ const { member, created } = await hub.workspaces.join(need10(args, "workspace"), { sessionID: need10(args, "sessionID"), role }, { ...idemOption(args), signal });
8447
8796
  return `member ${member.id} (${member.sessionID}, ${member.role}, generation ${member.generation}, r${member.revision}) ${created ? "joined" : "was already a member"}`;
8448
8797
  }
8449
8798
  },
8450
8799
  {
8451
8800
  name: "workspace_leave",
8452
8801
  description: `Remove a member under its revision; its supervision links are detached. ${WS_SCOPES}`,
8453
- properties: { workspace: WORKSPACE, memberID: STRING6, expectedRevision: REVISION4 },
8802
+ properties: { workspace: WORKSPACE, memberID: STRING9, expectedRevision: REVISION4 },
8454
8803
  required: ["workspace", "memberID", "expectedRevision"],
8455
8804
  readOnly: false,
8456
8805
  idempotent: true,
8457
8806
  run: async (hub, args, signal) => {
8458
- const r = await hub.workspaces.leave(need7(args, "workspace"), need7(args, "memberID"), needRevision(args), { signal });
8807
+ const r = await hub.workspaces.leave(need10(args, "workspace"), need10(args, "memberID"), needRevision(args), { signal });
8459
8808
  return `member ${r.member.id} left (${r.member.leftReason ?? "removed"}); ${r.supervision.detached} supervision link(s) detached`;
8460
8809
  }
8461
8810
  },
8462
8811
  {
8463
8812
  name: "workspace_assign",
8464
8813
  description: `Give a current member an assignment (title and optional outcome criteria; instructions travel as messages). ${WS_SCOPES}`,
8465
- properties: { workspace: WORKSPACE, memberID: STRING6, title: STRING6, criteria: { type: "array", items: STRING6 }, ref: STRING6, idempotencyKey: IDEMPOTENCY3 },
8814
+ properties: { workspace: WORKSPACE, memberID: STRING9, title: STRING9, criteria: { type: "array", items: STRING9 }, ref: STRING9, idempotencyKey: IDEMPOTENCY3 },
8466
8815
  required: ["workspace", "memberID", "title"],
8467
8816
  readOnly: false,
8468
8817
  run: async (hub, args, signal) => {
8469
8818
  const criteria = optionalStrings(args, "criteria");
8470
- const ref = optional7(args, "ref");
8471
- const { assignment } = await hub.workspaces.assign(need7(args, "workspace"), { memberID: need7(args, "memberID"), title: need7(args, "title"), ...criteria ? { criteria } : {}, ...ref ? { ref } : {} }, { ...idemOption(args), signal });
8819
+ const ref = optional8(args, "ref");
8820
+ const { assignment } = await hub.workspaces.assign(need10(args, "workspace"), { memberID: need10(args, "memberID"), title: need10(args, "title"), ...criteria ? { criteria } : {}, ...ref ? { ref } : {} }, { ...idemOption(args), signal });
8472
8821
  return `assignment ${assignment.id} → ${assignment.memberID}: ${assignment.status}, r${assignment.revision}`;
8473
8822
  }
8474
8823
  },
8475
8824
  {
8476
8825
  name: "workspace_assignments",
8477
8826
  description: `A workspace's assignments (id, member, status, revision, title), optionally one member's or one status. ${WS_SCOPES}`,
8478
- properties: { workspace: WORKSPACE, memberID: STRING6, status: { type: "string", enum: [...ASSIGNMENT_STATUSES] } },
8827
+ properties: { workspace: WORKSPACE, memberID: STRING9, status: { type: "string", enum: [...ASSIGNMENT_STATUSES] } },
8479
8828
  required: ["workspace"],
8480
8829
  readOnly: true,
8481
8830
  run: async (hub, args, signal) => {
8482
- const memberID = optional7(args, "memberID");
8831
+ const memberID = optional8(args, "memberID");
8483
8832
  const status = optionalEnum(args, "status", ASSIGNMENT_STATUSES);
8484
- const r = await hub.workspaces.assignments(need7(args, "workspace"), { ...memberID ? { memberID } : {}, ...status ? { status } : {}, limit: 200, signal });
8833
+ const r = await hub.workspaces.assignments(need10(args, "workspace"), { ...memberID ? { memberID } : {}, ...status ? { status } : {}, limit: 200, signal });
8485
8834
  return r.items.length ? r.items.map((v) => `${v.assignment.id} ${v.assignment.memberID} (${v.member}, ${v.presence}) ${v.assignment.status} r${v.assignment.revision} ${oneLine(sanitizeText(v.assignment.title))}`).join(`
8486
8835
  `) : "no assignments";
8487
8836
  }
@@ -8489,16 +8838,16 @@ var workspaceTools = [
8489
8838
  {
8490
8839
  name: "workspace_assignment_update",
8491
8840
  description: `Set an assignment's status (done and cancelled are final) or move it to another current member, under its revision. ${WS_SCOPES}`,
8492
- properties: { workspace: WORKSPACE, assignmentID: STRING6, expectedRevision: REVISION4, status: { type: "string", enum: [...ASSIGNMENT_STATUSES] }, memberID: STRING6 },
8841
+ properties: { workspace: WORKSPACE, assignmentID: STRING9, expectedRevision: REVISION4, status: { type: "string", enum: [...ASSIGNMENT_STATUSES] }, memberID: STRING9 },
8493
8842
  required: ["workspace", "assignmentID", "expectedRevision"],
8494
8843
  readOnly: false,
8495
8844
  run: async (hub, args, signal) => {
8496
8845
  const status = optionalEnum(args, "status", ASSIGNMENT_STATUSES);
8497
- const memberID = optional7(args, "memberID");
8846
+ const memberID = optional8(args, "memberID");
8498
8847
  if (status === undefined === (memberID === undefined))
8499
8848
  throw new ArgError3(`give exactly one of "status" or "memberID"`);
8500
- const id = need7(args, "workspace");
8501
- const assignmentID = need7(args, "assignmentID");
8849
+ const id = need10(args, "workspace");
8850
+ const assignmentID = need10(args, "assignmentID");
8502
8851
  const { assignment } = status !== undefined ? await hub.workspaces.setAssignmentStatus(id, assignmentID, status, needRevision(args), { signal }) : await hub.workspaces.reassign(id, assignmentID, memberID, needRevision(args), { signal });
8503
8852
  return `assignment ${assignment.id} → ${assignment.memberID}: ${assignment.status}, r${assignment.revision}`;
8504
8853
  }
@@ -8506,16 +8855,16 @@ var workspaceTools = [
8506
8855
  {
8507
8856
  name: "workspace_supervise",
8508
8857
  description: `Set who supervises a member (supervisor null detaches), under the supervision graph's revision (workspace_show). Acyclic, at most 6 levels. ${WS_SCOPES}`,
8509
- properties: { workspace: WORKSPACE, memberID: STRING6, supervisor: { type: ["string", "null"], description: "The supervising member's ID, or null to detach." }, order: { type: "number" }, expectedRevision: REVISION4 },
8858
+ properties: { workspace: WORKSPACE, memberID: STRING9, supervisor: { type: ["string", "null"], description: "The supervising member's ID, or null to detach." }, order: { type: "number" }, expectedRevision: REVISION4 },
8510
8859
  required: ["workspace", "memberID", "supervisor", "expectedRevision"],
8511
8860
  readOnly: false,
8512
8861
  run: async (hub, args, signal) => {
8513
- const supervisor = args.supervisor === null ? null : need7(args, "supervisor");
8862
+ const supervisor = args.supervisor === null ? null : need10(args, "supervisor");
8514
8863
  const order = args.order;
8515
8864
  if (order !== undefined && (typeof order !== "number" || !Number.isFinite(order)))
8516
8865
  throw new ArgError3(`"order" must be a number`);
8517
- const r = await hub.workspaces.supervise(need7(args, "workspace"), { memberID: need7(args, "memberID"), supervisorMemberID: supervisor, ...order !== undefined ? { order } : {}, expectedRevision: needRevision(args) }, { signal });
8518
- return `${r.change}: ${need7(args, "memberID")}${r.link ? ` ← ${r.link.supervisorMemberID} (order ${r.link.order})` : ""}; supervision revision ${r.revision}${r.pruned ? `, ${r.pruned} stale link(s) dropped` : ""}`;
8866
+ const r = await hub.workspaces.supervise(need10(args, "workspace"), { memberID: need10(args, "memberID"), supervisorMemberID: supervisor, ...order !== undefined ? { order } : {}, expectedRevision: needRevision(args) }, { signal });
8867
+ return `${r.change}: ${need10(args, "memberID")}${r.link ? ` ← ${r.link.supervisorMemberID} (order ${r.link.order})` : ""}; supervision revision ${r.revision}${r.pruned ? `, ${r.pruned} stale link(s) dropped` : ""}`;
8519
8868
  }
8520
8869
  },
8521
8870
  {
@@ -8525,7 +8874,7 @@ var workspaceTools = [
8525
8874
  required: ["workspace"],
8526
8875
  readOnly: true,
8527
8876
  run: async (hub, args, signal) => {
8528
- const v = await hub.workspaces.master(need7(args, "workspace"), { signal });
8877
+ const v = await hub.workspaces.master(need10(args, "workspace"), { signal });
8529
8878
  const line = (m) => `${m.member.id} ${m.member.sessionID} generation ${m.member.generation} ${m.presence}${m.member.leftReason ? ` (${m.member.leftReason})` : ""}`;
8530
8879
  return [
8531
8880
  `workspace ${v.workspaceID} r${v.revision}`,
@@ -8539,17 +8888,17 @@ var workspaceTools = [
8539
8888
  {
8540
8889
  name: "workspace_master_replace",
8541
8890
  description: `Replace (or first set) a workspace's master under the workspace revision: a session (ses_…) or a running launch's session (launchID). The old master departs as replaced; its supervised members move to the new one. No grants move: the new master's session needs its own grants. ${WS_SCOPES}`,
8542
- properties: { workspace: WORKSPACE, sessionID: STRING6, launchID: STRING6, expectedRevision: REVISION4, idempotencyKey: IDEMPOTENCY3 },
8891
+ properties: { workspace: WORKSPACE, sessionID: STRING9, launchID: STRING9, expectedRevision: REVISION4, idempotencyKey: IDEMPOTENCY3 },
8543
8892
  required: ["workspace", "expectedRevision"],
8544
8893
  readOnly: false,
8545
8894
  idempotent: true,
8546
8895
  run: async (hub, args, signal) => {
8547
- const sessionID = optional7(args, "sessionID");
8548
- const launchID = optional7(args, "launchID");
8896
+ const sessionID = optional8(args, "sessionID");
8897
+ const launchID = optional8(args, "launchID");
8549
8898
  if (sessionID === undefined === (launchID === undefined))
8550
8899
  throw new ArgError3(`give exactly one of "sessionID" or "launchID"`);
8551
8900
  const target = sessionID !== undefined ? { sessionID } : { launchID };
8552
- const r = await hub.workspaces.replaceMaster(need7(args, "workspace"), { ...target, expectedRevision: needRevision(args) }, { ...idemOption(args), signal });
8901
+ const r = await hub.workspaces.replaceMaster(need10(args, "workspace"), { ...target, expectedRevision: needRevision(args) }, { ...idemOption(args), signal });
8553
8902
  if (!r.changed)
8554
8903
  return `${r.master.sessionID} already is the master (${r.master.id}); nothing changed`;
8555
8904
  return [
@@ -8567,7 +8916,7 @@ var workspaceTools = [
8567
8916
  required: ["workspace"],
8568
8917
  readOnly: true,
8569
8918
  run: async (hub, args, signal) => {
8570
- const b = await hub.workspaces.board(need7(args, "workspace"), { signal });
8919
+ const b = await hub.workspaces.board(need10(args, "workspace"), { signal });
8571
8920
  const more = (truncated) => truncated ? " (more not shown)" : "";
8572
8921
  const section = (name, s, line) => s.status === "ok" ? [`${name}:${more(s.truncated)}`, ...s.items.map((i) => ` ${line(i)}`)] : [`${name}: ${s.status}${s.status === "omitted" ? ` (needs ${s.scope})` : ""}`];
8573
8922
  const lines = [wsLine(b.workspace), ...b.workspace.masterMemberID ? [`master: ${b.workspace.masterMemberID}`] : []];
@@ -8593,10 +8942,13 @@ var TOOLS = [
8593
8942
  ...webhookTools,
8594
8943
  ...profileTools,
8595
8944
  ...profileLifecycleTools,
8945
+ ...grantTools,
8596
8946
  ...workspaceTools,
8597
8947
  ...artifactTools,
8598
8948
  ...artifactJobTools,
8599
8949
  ...teamIntakeTools,
8950
+ ...subscriptionTools,
8951
+ ...operationTools,
8600
8952
  ...teamTools,
8601
8953
  ...watchlistTools,
8602
8954
  {
@@ -8617,14 +8969,14 @@ var TOOLS = [
8617
8969
  name: "sessions",
8618
8970
  description: "The sessions as a flat list, optionally filtered.",
8619
8971
  properties: {
8620
- project: { ...STRING6, description: "Only this project." },
8621
- state: { ...STRING6, description: "Only these states (comma-separated, e.g. busy,blocked)." },
8622
- machine: { ...STRING6, description: "Only this machine." },
8623
- label: { type: "array", items: STRING6, description: "key=value labels that must all match." }
8972
+ project: { ...STRING9, description: "Only this project." },
8973
+ state: { ...STRING9, description: "Only these states (comma-separated, e.g. busy,blocked)." },
8974
+ machine: { ...STRING9, description: "Only this machine." },
8975
+ label: { type: "array", items: STRING9, description: "key=value labels that must all match." }
8624
8976
  },
8625
8977
  readOnly: true,
8626
8978
  run: async (hub, args, signal) => {
8627
- const body = await hub.sessions({ project: optional7(args, "project"), state: optional7(args, "state"), machine: optional7(args, "machine"), label: optionalStrings(args, "label"), signal });
8979
+ const body = await hub.sessions({ project: optional8(args, "project"), state: optional8(args, "state"), machine: optional8(args, "machine"), label: optionalStrings(args, "label"), signal });
8628
8980
  return sessionsText(body.sessions ?? []);
8629
8981
  }
8630
8982
  },
@@ -8634,7 +8986,7 @@ var TOOLS = [
8634
8986
  properties: { addr: ADDR2 },
8635
8987
  required: ["addr"],
8636
8988
  readOnly: true,
8637
- run: async (hub, args, signal) => showText(await hub.show(need7(args, "addr"), { signal }))
8989
+ run: async (hub, args, signal) => showText(await hub.show(need10(args, "addr"), { signal }))
8638
8990
  },
8639
8991
  {
8640
8992
  name: "log",
@@ -8643,7 +8995,7 @@ var TOOLS = [
8643
8995
  required: ["addr"],
8644
8996
  readOnly: true,
8645
8997
  run: async (hub, args, signal) => {
8646
- const addr = need7(args, "addr");
8998
+ const addr = need10(args, "addr");
8647
8999
  const n = optionalInt(args, "n", 1, 200);
8648
9000
  return logText((await hub.log(addr, { ...n !== undefined ? { n } : {}, signal })).rows ?? []);
8649
9001
  }
@@ -8652,25 +9004,25 @@ var TOOLS = [
8652
9004
  name: "who",
8653
9005
  description: "Named and role-holding sessions; a team member sees its teams, grouped by team with its reach to each.",
8654
9006
  properties: {
8655
- project: { ...STRING6, description: "Only this project." },
8656
- role: { ...STRING6, description: "Only holders of this role (reviewer, @@reviewer or reviewer#team)." },
8657
- team: { ...STRING6, description: "Only this team (name or ID)." }
9007
+ project: { ...STRING9, description: "Only this project." },
9008
+ role: { ...STRING9, description: "Only holders of this role (reviewer, @@reviewer or reviewer#team)." },
9009
+ team: { ...STRING9, description: "Only this team (name or ID)." }
8658
9010
  },
8659
9011
  readOnly: true,
8660
9012
  run: async (hub, args, signal) => {
8661
- const body = await hub.who({ project: optional7(args, "project"), role: optional7(args, "role"), team: optional7(args, "team"), signal });
9013
+ const body = await hub.who({ project: optional8(args, "project"), role: optional8(args, "role"), team: optional8(args, "team"), signal });
8662
9014
  return whoText(body.sessions ?? [], body);
8663
9015
  }
8664
9016
  },
8665
9017
  {
8666
9018
  name: "msg",
8667
9019
  description: "Tell a session something (fire and forget). Sent as this server's credential, not as an OpenCode session.",
8668
- properties: { to: TO, text: { ...STRING6, description: "The message." }, ...SEND, idempotencyKey: IDEMPOTENCY3 },
9020
+ properties: { to: TO, text: { ...STRING9, description: "The message." }, ...SEND, idempotencyKey: IDEMPOTENCY3 },
8669
9021
  required: ["to", "text"],
8670
9022
  readOnly: false,
8671
9023
  run: async (hub, args, signal) => {
8672
- const to = need7(args, "to");
8673
- const text = need7(args, "text");
9024
+ const to = need10(args, "to");
9025
+ const text = need10(args, "text");
8674
9026
  const options = { ...sendOptions(args), ...idemOption(args), signal };
8675
9027
  if (optionalBool(args, "all"))
8676
9028
  return sentAllText(await hub.msg(to, text, { ...options, all: true }));
@@ -8682,7 +9034,7 @@ var TOOLS = [
8682
9034
  description: `Ask a session a question, tracked until answered, declined or expired. wait (at most ${WAIT_POLL_MAX_S} s) blocks for the answer; otherwise it shows up in the inbox tool.`,
8683
9035
  properties: {
8684
9036
  to: TO,
8685
- text: { ...STRING6, description: "The question." },
9037
+ text: { ...STRING9, description: "The question." },
8686
9038
  wait: { type: "number", minimum: 0, maximum: WAIT_POLL_MAX_S, description: `Seconds to wait for the answer (0–${WAIT_POLL_MAX_S}).` },
8687
9039
  expires: { type: ["number", "string"], description: "When the ask expires: seconds, or 30s, 5m, 2h, 1d (1 minute to 7 days; default 24 h)." },
8688
9040
  ...SEND,
@@ -8691,8 +9043,8 @@ var TOOLS = [
8691
9043
  required: ["to", "text"],
8692
9044
  readOnly: false,
8693
9045
  run: async (hub, args, signal) => {
8694
- const to = need7(args, "to");
8695
- const text = need7(args, "text");
9046
+ const to = need10(args, "to");
9047
+ const text = need10(args, "text");
8696
9048
  const wait = optionalWait(args);
8697
9049
  const expires = optionalExpires(args);
8698
9050
  const options = { ...sendOptions(args), ...expires !== undefined ? { expires } : {}, ...idemOption(args), signal };
@@ -8710,12 +9062,12 @@ ${openedText(asked.decision)}` : outcome;
8710
9062
  {
8711
9063
  name: "reply",
8712
9064
  description: "Reply to a message you received; it goes to the message's real sender.",
8713
- properties: { id: MSG_ID, text: { ...STRING6, description: "The reply." }, wake: SEND.wake },
9065
+ properties: { id: MSG_ID, text: { ...STRING9, description: "The reply." }, wake: SEND.wake },
8714
9066
  required: ["id", "text"],
8715
9067
  readOnly: false,
8716
9068
  run: async (hub, args, signal) => {
8717
- const id = need7(args, "id");
8718
- const text = need7(args, "text");
9069
+ const id = need10(args, "id");
9070
+ const text = need10(args, "text");
8719
9071
  const wake = optionalBool(args, "wake");
8720
9072
  return repliedText(await hub.reply(id, text, { ...wake !== undefined ? { wake } : {}, signal }));
8721
9073
  }
@@ -8723,12 +9075,12 @@ ${openedText(asked.decision)}` : outcome;
8723
9075
  {
8724
9076
  name: "decline",
8725
9077
  description: "Decline an ask you received, with a reason.",
8726
- properties: { id: MSG_ID, why: { ...STRING6, description: "The reason." } },
9078
+ properties: { id: MSG_ID, why: { ...STRING9, description: "The reason." } },
8727
9079
  required: ["id", "why"],
8728
9080
  readOnly: false,
8729
9081
  run: async (hub, args, signal) => {
8730
- const id = need7(args, "id");
8731
- return declinedText(await hub.decline(id, need7(args, "why"), { signal }));
9082
+ const id = need10(args, "id");
9083
+ return declinedText(await hub.decline(id, need10(args, "why"), { signal }));
8732
9084
  }
8733
9085
  },
8734
9086
  {
@@ -8748,7 +9100,7 @@ ${openedText(asked.decision)}` : outcome;
8748
9100
  required: ["id"],
8749
9101
  readOnly: true,
8750
9102
  run: async (hub, args, signal) => {
8751
- const id = need7(args, "id");
9103
+ const id = need10(args, "id");
8752
9104
  const wait = optionalWait(args);
8753
9105
  return messageText(await hub.message(id, { ...wait !== undefined ? { wait } : {}, signal }));
8754
9106
  }
@@ -8761,7 +9113,7 @@ ${openedText(asked.decision)}` : outcome;
8761
9113
  readOnly: false,
8762
9114
  organizes: true,
8763
9115
  idempotent: true,
8764
- run: async (hub, args, signal) => mutedText((await hub[verb](need7(args, "addr"), { signal })).muted)
9116
+ run: async (hub, args, signal) => mutedText((await hub[verb](need10(args, "addr"), { signal })).muted)
8765
9117
  })),
8766
9118
  {
8767
9119
  name: "decisions",
@@ -8769,7 +9121,7 @@ ${openedText(asked.decision)}` : outcome;
8769
9121
  properties: { state: { type: "string", enum: [...DECISION_STATES], description: "Only Decisions in this state." } },
8770
9122
  readOnly: true,
8771
9123
  run: async (hub, args, signal) => {
8772
- const state = optional7(args, "state");
9124
+ const state = optional8(args, "state");
8773
9125
  if (state !== undefined && !DECISION_STATES.includes(state))
8774
9126
  throw new ArgError3(`"state" is one of ${DECISION_STATES.join(", ")}`);
8775
9127
  const { decisions } = await hub.decisions({ ...state ? { state } : {}, signal });
@@ -8784,7 +9136,7 @@ ${decisionsText(decisions)}` : decisionsText(decisions);
8784
9136
  required: ["id"],
8785
9137
  readOnly: true,
8786
9138
  run: async (hub, args, signal) => `${DECISIONS_NOTE}
8787
- ${decisionText((await hub.decision(need7(args, "id"), { signal })).decision)}`
9139
+ ${decisionText((await hub.decision(need10(args, "id"), { signal })).decision)}`
8788
9140
  },
8789
9141
  {
8790
9142
  name: "approve",
@@ -8793,8 +9145,8 @@ ${decisionText((await hub.decision(need7(args, "id"), { signal })).decision)}`
8793
9145
  required: ["addr", "requestID"],
8794
9146
  readOnly: false,
8795
9147
  run: async (hub, args, signal) => {
8796
- const addr = need7(args, "addr");
8797
- const requestID = need7(args, "requestID");
9148
+ const addr = need10(args, "addr");
9149
+ const requestID = need10(args, "requestID");
8798
9150
  const always = optionalBool(args, "always");
8799
9151
  return approvedText(await hub.approve(addr, requestID, { ...always ? { always } : {}, ...whyOption(args), signal }));
8800
9152
  }
@@ -8806,8 +9158,8 @@ ${decisionText((await hub.decision(need7(args, "id"), { signal })).decision)}`
8806
9158
  required: ["addr", "requestID"],
8807
9159
  readOnly: false,
8808
9160
  run: async (hub, args, signal) => {
8809
- const addr = need7(args, "addr");
8810
- const requestID = need7(args, "requestID");
9161
+ const addr = need10(args, "addr");
9162
+ const requestID = need10(args, "requestID");
8811
9163
  return deniedText(await hub.deny(addr, requestID, { ...whyOption(args), signal }));
8812
9164
  }
8813
9165
  },
@@ -8824,8 +9176,8 @@ ${decisionText((await hub.decision(need7(args, "id"), { signal })).decision)}`
8824
9176
  required: ["addr", "formID"],
8825
9177
  readOnly: false,
8826
9178
  run: async (hub, args, signal) => {
8827
- const addr = need7(args, "addr");
8828
- const formID = need7(args, "formID");
9179
+ const addr = need10(args, "addr");
9180
+ const formID = need10(args, "formID");
8829
9181
  const given = formAnswer(args);
8830
9182
  const options = { ...whyOption(args), signal };
8831
9183
  return answeredText(given === "cancel" ? await hub.cancelForm(addr, formID, options) : await hub.answer(addr, formID, given, options), given === "cancel");
@@ -8836,15 +9188,15 @@ ${decisionText((await hub.decision(need7(args, "id"), { signal })).decision)}`
8836
9188
  description: `Prompt a session (an agent's prompt carries the hub's provenance). ${NEEDS_USER}`,
8837
9189
  properties: {
8838
9190
  addr: ADDR2,
8839
- text: { ...STRING6, description: "The prompt." },
9191
+ text: { ...STRING9, description: "The prompt." },
8840
9192
  delivery: { type: "string", enum: [...DELIVERIES], description: "How it enters a busy session (the spoke picks when unset)." },
8841
9193
  why: WHY
8842
9194
  },
8843
9195
  required: ["addr", "text"],
8844
9196
  readOnly: false,
8845
9197
  run: async (hub, args, signal) => {
8846
- const addr = need7(args, "addr");
8847
- const text = need7(args, "text");
9198
+ const addr = need10(args, "addr");
9199
+ const text = need10(args, "text");
8848
9200
  const delivery = optionalDelivery(args);
8849
9201
  return promptedText(await hub.prompt(addr, text, { ...delivery ? { delivery } : {}, ...whyOption(args), signal }));
8850
9202
  }
@@ -8855,32 +9207,32 @@ ${decisionText((await hub.decision(need7(args, "id"), { signal })).decision)}`
8855
9207
  properties: { addr: ADDR2, why: WHY },
8856
9208
  required: ["addr"],
8857
9209
  readOnly: false,
8858
- run: async (hub, args, signal) => stoppedText(await hub.stop(need7(args, "addr"), { ...whyOption(args), signal }))
9210
+ run: async (hub, args, signal) => stoppedText(await hub.stop(need10(args, "addr"), { ...whyOption(args), signal }))
8859
9211
  },
8860
9212
  {
8861
9213
  name: "launch",
8862
9214
  description: "Start a new session on a node (a hub node with the repo under its workspace roots). The same key again answers the same launch. With notAfter, a launch still waiting for a machine at that time is never started (it ends expired). Needs an app token with scope sessions:launch and a grant with verb launch covering the repo.",
8863
9215
  properties: {
8864
- repo: { ...STRING6, description: "Absolute path of the repository on the node (or where a clone goes)." },
9216
+ repo: { ...STRING9, description: "Absolute path of the repository on the node (or where a clone goes)." },
8865
9217
  mode: { type: "string", enum: [...WORKSPACE_MODES], description: "worktree (default: <repo>/.worktrees/<branch>, needs branch), checkout, or clone (needs remote)." },
8866
- branch: { ...STRING6, description: "The branch to create or check out." },
8867
- base: { ...STRING6, description: "What a new branch starts from." },
8868
- remote: { ...STRING6, description: "An https or ssh git remote (for clone)." },
8869
- key: { ...STRING6, description: "Idempotency key: retrying with the same key and arguments answers the same launch." },
8870
- name: { ...STRING6, description: "The session's @name." },
8871
- roles: { type: "array", items: STRING6, description: "Roles the session claims (e.g. reviewer.hub)." },
8872
- harness: { ...STRING6, description: "The harness (default opencode)." },
8873
- agent: { ...STRING6, description: "The harness agent (e.g. build)." },
8874
- model: { ...STRING6, description: "provider/model." },
9218
+ branch: { ...STRING9, description: "The branch to create or check out." },
9219
+ base: { ...STRING9, description: "What a new branch starts from." },
9220
+ remote: { ...STRING9, description: "An https or ssh git remote (for clone)." },
9221
+ key: { ...STRING9, description: "Idempotency key: retrying with the same key and arguments answers the same launch." },
9222
+ name: { ...STRING9, description: "The session's @name." },
9223
+ roles: { type: "array", items: STRING9, description: "Roles the session claims (e.g. reviewer.hub)." },
9224
+ harness: { ...STRING9, description: "The harness (default opencode)." },
9225
+ agent: { ...STRING9, description: "The harness agent (e.g. build)." },
9226
+ model: { ...STRING9, description: "provider/model." },
8875
9227
  runtime: { type: "string", enum: [...RUNTIME_KINDS], description: "host (default) or openshell (a sandbox)." },
8876
- profile: { ...STRING6, description: "The runtime's sandbox profile (needs runtime)." },
8877
- machine: { ...STRING6, description: "The machine (name or ID) to run on." },
9228
+ profile: { ...STRING9, description: "The runtime's sandbox profile (needs runtime)." },
9229
+ machine: { ...STRING9, description: "The machine (name or ID) to run on." },
8878
9230
  nodeLabels: { ...LABELS, description: 'Node labels to place on (e.g. {"os": "darwin"}).' },
8879
9231
  prefer: { type: "boolean", description: "machine and nodeLabels are preferences, not requirements." },
8880
9232
  labels: { ...LABELS, description: "Free-form labels for finding the launch and its session later (never interpreted by the hub)." },
8881
- prompt: { ...STRING6, description: "The session's first prompt." },
9233
+ prompt: { ...STRING9, description: "The session's first prompt." },
8882
9234
  end: { type: "string", enum: [...LAUNCH_END_WHEN], description: "When it ends: idle (default; idle with nothing open for 30 s), asked (with a question open: needs_user), never." },
8883
- timeout: { ...STRING6, description: "Longest run, e.g. 4h or 90m." },
9235
+ timeout: { ...STRING9, description: "Longest run, e.g. 4h or 90m." },
8884
9236
  cleanup: { type: "string", enum: [...CLEANUP_POLICIES], description: "keep (default) or remove the workspace when it ends." },
8885
9237
  notAfter: { type: ["number", "string"], description: "Do not start after this time: epoch milliseconds or an ISO 8601 date-time (at most 30 days ahead). Still waiting for a machine then, the launch ends expired; once placed it runs (timeout bounds it). Already passed: nothing is launched (expired)." }
8886
9238
  },
@@ -8896,12 +9248,12 @@ ${decisionText((await hub.decision(need7(args, "id"), { signal })).decision)}`
8896
9248
  name: "launches",
8897
9249
  description: "Your launches, oldest first (an app or session sees the ones it started).",
8898
9250
  properties: {
8899
- state: { ...STRING6, description: "Only these states (comma-separated: queued, placing, provisioning, running, ended)." },
8900
- label: { type: "array", items: STRING6, description: "key=value launch labels that must all match." }
9251
+ state: { ...STRING9, description: "Only these states (comma-separated: queued, placing, provisioning, running, ended)." },
9252
+ label: { type: "array", items: STRING9, description: "key=value launch labels that must all match." }
8901
9253
  },
8902
9254
  readOnly: true,
8903
9255
  run: async (hub, args, signal) => {
8904
- const state = optional7(args, "state")?.split(",").filter(Boolean);
9256
+ const state = optional8(args, "state")?.split(",").filter(Boolean);
8905
9257
  const label = optionalStrings(args, "label");
8906
9258
  return launchesText((await hub.launches.list({ ...state?.length ? { state } : {}, ...label ? { label } : {}, signal })).launches ?? []);
8907
9259
  }
@@ -8912,7 +9264,7 @@ ${decisionText((await hub.decision(need7(args, "id"), { signal })).decision)}`
8912
9264
  properties: { id: LAUNCH_ID },
8913
9265
  required: ["id"],
8914
9266
  readOnly: true,
8915
- run: async (hub, args, signal) => launchText((await hub.launches.get(need7(args, "id"), { signal })).launch)
9267
+ run: async (hub, args, signal) => launchText((await hub.launches.get(need10(args, "id"), { signal })).launch)
8916
9268
  },
8917
9269
  {
8918
9270
  name: "launch_stop",
@@ -8920,7 +9272,7 @@ ${decisionText((await hub.decision(need7(args, "id"), { signal })).decision)}`
8920
9272
  properties: { id: LAUNCH_ID },
8921
9273
  required: ["id"],
8922
9274
  readOnly: false,
8923
- run: async (hub, args, signal) => stoppedLaunchText((await hub.launches.stop(need7(args, "id"), { signal })).launch)
9275
+ run: async (hub, args, signal) => stoppedLaunchText((await hub.launches.stop(need10(args, "id"), { signal })).launch)
8924
9276
  }
8925
9277
  ];
8926
9278
  var toolList = () => TOOLS.map((t) => ({
@@ -9115,90 +9467,6 @@ async function watch(credentials, options, write, signal) {
9115
9467
  throw new CliError(EXIT.other, "the watch stream ended (the hub closed it)", AGAIN);
9116
9468
  }
9117
9469
 
9118
- // packages/cli/src/grants.ts
9119
- var GRANT_FLAGS = {
9120
- json: "json",
9121
- holder: "string",
9122
- project: "multi",
9123
- allow: "multi",
9124
- permit: "multi",
9125
- never: "multi",
9126
- expires: "string",
9127
- "max-per-hour": "string",
9128
- "allow-always": "bool"
9129
- };
9130
- var GRANT_REQUEST_FLAGS = { ...GRANT_FLAGS, "permission-allow": "multi", why: "string" };
9131
- var GRANT_USER_HINT = "only the user manages grants: use the console, or `hub login` with the user's device token";
9132
- function userOnly(source, what) {
9133
- if (source === "env")
9134
- throw new CliError(EXIT.forbidden, `only the user can ${what} a grant, not a session`, GRANT_USER_HINT);
9135
- }
9136
- function grantError(error) {
9137
- if (error instanceof CliError && error.exit === EXIT.forbidden)
9138
- return new CliError(error.exit, error.message, [error.hint, GRANT_USER_HINT].filter(Boolean).join("; "), error.envelope);
9139
- return error;
9140
- }
9141
- var listOf = (flags, name) => (flags[name] ?? []).flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
9142
- async function grantSpecArg(flags, stdin) {
9143
- return await grantArg(flags, stdin, true);
9144
- }
9145
- function grantRequestArg(flags, stdin) {
9146
- return grantArg(flags, stdin, false);
9147
- }
9148
- async function grantArg(flags, stdin, holderRequired) {
9149
- const json = typeof flags.json === "string" ? flags.json : undefined;
9150
- const flagged = Object.keys(GRANT_REQUEST_FLAGS).filter((name) => name !== "json" && name !== "why" && flags[name] !== undefined);
9151
- if (json !== undefined) {
9152
- if (flagged.length)
9153
- throw new UsageError(`give either --json '{…}' or ${flagged.map((f) => `--${f}`).join(", ")}`);
9154
- return await jsonObjectArg(json, stdin, "grant");
9155
- }
9156
- const holder = str(flags, "holder");
9157
- if (holder === undefined && holderRequired)
9158
- throw new UsageError("needs --holder (ses_…, @name, @@orchestrator…, or app:<token id>)");
9159
- const projects = listOf(flags, "project");
9160
- if (!projects.length)
9161
- throw new UsageError("needs --project <path> (repeatable)");
9162
- const allow = listOf(flags, "allow");
9163
- if (!allow.length)
9164
- throw new UsageError("needs --allow <verbs> (approve, deny, answer, prompt, stop, roles, launch)");
9165
- const permit = [...listOf(flags, "permit"), ...listOf(flags, "permission-allow")];
9166
- const never = listOf(flags, "never");
9167
- const expires = str(flags, "expires");
9168
- const cap = str(flags, "max-per-hour");
9169
- if (cap !== undefined && !/^\d+$/.test(cap))
9170
- throw new UsageError("--max-per-hour is a whole number");
9171
- return {
9172
- ...holder !== undefined ? { holder } : {},
9173
- scope: { projects },
9174
- allow,
9175
- ...permit.length || never.length ? { permissions: { ...permit.length ? { allow: permit } : {}, ...never.length ? { never } : {} } } : {},
9176
- ...expires !== undefined ? { expires } : {},
9177
- ...cap !== undefined ? { maxActionsPerHour: Number(cap) } : {},
9178
- ...flags["allow-always"] === true ? { allowAlways: true } : {}
9179
- };
9180
- }
9181
- var iso3 = (ms) => new Date(ms).toISOString().replace(/\.\d{3}Z$/, "Z");
9182
- var grantLine = (g) => [g.id, g.state, g.holder, g.allow.join(","), g.scope.projects.join(","), `until ${iso3(g.expiresAt)}`].join(" ");
9183
- var grantsText = (grants) => grants.length ? grants.map(grantLine).join(`
9184
- `) : "no grants";
9185
- function grantText2(g) {
9186
- const list = (values) => values.length ? values.join(", ") : "none";
9187
- return [
9188
- `${g.id} ${g.state}`,
9189
- `holder: ${g.holder}${g.holderSessionID && g.holderSessionID !== g.holder ? ` (${g.holderSessionID})` : ""}`,
9190
- `projects: ${list(g.scope.projects)}`,
9191
- `verbs: ${list(g.allow)}`,
9192
- `permissions allow: ${list(g.permissions.allow)}`,
9193
- `permissions never: ${list(g.permissions.never)}`,
9194
- `always: ${g.allowAlways ? "yes" : "no"}`,
9195
- `max actions per hour: ${g.maxActionsPerHour}`,
9196
- g.revokedAt !== undefined ? `revoked: ${iso3(g.revokedAt)}` : `expires: ${iso3(g.expiresAt)}`
9197
- ].join(`
9198
- `);
9199
- }
9200
- var requestedText = (body) => `requested; waiting for the user (Decision ${body.decision.id})`;
9201
-
9202
9470
  // packages/cli/src/node.ts
9203
9471
  import { appendFileSync, existsSync as existsSync5, mkdirSync as mkdirSync7, readFileSync as readFileSync8, realpathSync as realpathSync3, renameSync as renameSync5, rmSync as rmSync8, statSync as statSync4, writeFileSync as writeFileSync4 } from "node:fs";
9204
9472
  import path16 from "node:path";
@@ -9424,7 +9692,7 @@ import { arch as osArch, platform as osPlatform } from "node:os";
9424
9692
  // packages/node/package.json
9425
9693
  var package_default2 = {
9426
9694
  name: "@huynle/hub-node",
9427
- version: "0.10.1",
9695
+ version: "0.11.0",
9428
9696
  private: true,
9429
9697
  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).",
9430
9698
  license: "MIT",
@@ -10879,7 +11147,7 @@ import path14 from "node:path";
10879
11147
  // packages/runtime-openshell/package.json
10880
11148
  var package_default3 = {
10881
11149
  name: "@huynle/hub-runtime-openshell",
10882
- version: "0.10.1",
11150
+ version: "0.11.0",
10883
11151
  private: true,
10884
11152
  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).",
10885
11153
  license: "MIT",
@@ -12534,46 +12802,6 @@ function openShellCheckText(report) {
12534
12802
  `);
12535
12803
  }
12536
12804
 
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
-
12577
12805
  // packages/claude-code/src/main.ts
12578
12806
  import { spawnSync as spawnSync2 } from "node:child_process";
12579
12807
  import { realpathSync as realpathSync5 } from "node:fs";
@@ -14877,7 +15105,7 @@ import path23 from "node:path";
14877
15105
  // packages/claude-code/package.json
14878
15106
  var package_default4 = {
14879
15107
  name: "@huynle/hub-claude-code",
14880
- version: "0.10.1",
15108
+ version: "0.11.0",
14881
15109
  private: true,
14882
15110
  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).",
14883
15111
  license: "MIT",
@@ -15166,7 +15394,7 @@ import path26 from "node:path";
15166
15394
  // packages/preset/package.json
15167
15395
  var package_default5 = {
15168
15396
  name: "@huynle/hub-preset",
15169
- version: "0.10.1",
15397
+ version: "0.11.0",
15170
15398
  private: true,
15171
15399
  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).",
15172
15400
  license: "MIT",
@@ -15691,7 +15919,7 @@ import { openSync as openSync6, closeSync as closeSync6, readFileSync as readFil
15691
15919
  // packages/codex/package.json
15692
15920
  var package_default6 = {
15693
15921
  name: "@huynle/hub-codex",
15694
- version: "0.10.1",
15922
+ version: "0.11.0",
15695
15923
  private: true,
15696
15924
  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).",
15697
15925
  license: "MIT",
@@ -26715,7 +26943,7 @@ ${openedText(body.decision)}` : outcome.text);
26715
26943
  "grant create": {
26716
26944
  flags: GRANT_FLAGS,
26717
26945
  async handle(context) {
26718
- userOnly(context.credentials().source, "create");
26946
+ userOnly2(context.credentials().source, "create");
26719
26947
  const spec = await grantSpecArg(context.parsed.flags, context.io.stdin);
26720
26948
  const body = await grantHub(context, (hub) => hub.createGrant(spec));
26721
26949
  answer(context, body, (b) => `created ${grantText2(b.grant)}`);
@@ -26735,7 +26963,7 @@ ${openedText(body.decision)}` : outcome.text);
26735
26963
  "grant revoke": {
26736
26964
  args: ["<grantID>"],
26737
26965
  async handle(context) {
26738
- userOnly(context.credentials().source, "revoke");
26966
+ userOnly2(context.credentials().source, "revoke");
26739
26967
  const body = await grantHub(context, (hub) => hub.revokeGrant(context.parsed.args[0]));
26740
26968
  answer(context, body, (b) => `revoked ${b.grant.id} (${b.grant.holder})`);
26741
26969
  }