@oh-hai/cli 0.1.0-beta.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 (110) hide show
  1. package/README.md +154 -0
  2. package/dist/auth/file-backend.d.ts +16 -0
  3. package/dist/auth/file-backend.js +98 -0
  4. package/dist/auth/file-backend.js.map +1 -0
  5. package/dist/auth/keychain.d.ts +54 -0
  6. package/dist/auth/keychain.js +232 -0
  7. package/dist/auth/keychain.js.map +1 -0
  8. package/dist/auth/resolve-token.d.ts +34 -0
  9. package/dist/auth/resolve-token.js +91 -0
  10. package/dist/auth/resolve-token.js.map +1 -0
  11. package/dist/auth/secure-write.d.ts +2 -0
  12. package/dist/auth/secure-write.js +30 -0
  13. package/dist/auth/secure-write.js.map +1 -0
  14. package/dist/auth/token-store.d.ts +104 -0
  15. package/dist/auth/token-store.js +208 -0
  16. package/dist/auth/token-store.js.map +1 -0
  17. package/dist/cli.d.ts +16 -0
  18. package/dist/cli.js +238 -0
  19. package/dist/cli.js.map +1 -0
  20. package/dist/commands/agents.d.ts +2 -0
  21. package/dist/commands/agents.js +370 -0
  22. package/dist/commands/agents.js.map +1 -0
  23. package/dist/commands/ask.d.ts +2 -0
  24. package/dist/commands/ask.js +246 -0
  25. package/dist/commands/ask.js.map +1 -0
  26. package/dist/commands/context.d.ts +72 -0
  27. package/dist/commands/context.js +7 -0
  28. package/dist/commands/context.js.map +1 -0
  29. package/dist/commands/doctor.d.ts +2 -0
  30. package/dist/commands/doctor.js +237 -0
  31. package/dist/commands/doctor.js.map +1 -0
  32. package/dist/commands/flags.d.ts +25 -0
  33. package/dist/commands/flags.js +100 -0
  34. package/dist/commands/flags.js.map +1 -0
  35. package/dist/commands/handlers.d.ts +2 -0
  36. package/dist/commands/handlers.js +26 -0
  37. package/dist/commands/handlers.js.map +1 -0
  38. package/dist/commands/http.d.ts +8 -0
  39. package/dist/commands/http.js +19 -0
  40. package/dist/commands/http.js.map +1 -0
  41. package/dist/commands/inbox.d.ts +2 -0
  42. package/dist/commands/inbox.js +111 -0
  43. package/dist/commands/inbox.js.map +1 -0
  44. package/dist/commands/login.d.ts +2 -0
  45. package/dist/commands/login.js +272 -0
  46. package/dist/commands/login.js.map +1 -0
  47. package/dist/commands/logout.d.ts +2 -0
  48. package/dist/commands/logout.js +35 -0
  49. package/dist/commands/logout.js.map +1 -0
  50. package/dist/commands/messaging/await.d.ts +43 -0
  51. package/dist/commands/messaging/await.js +125 -0
  52. package/dist/commands/messaging/await.js.map +1 -0
  53. package/dist/commands/messaging/build.d.ts +46 -0
  54. package/dist/commands/messaging/build.js +66 -0
  55. package/dist/commands/messaging/build.js.map +1 -0
  56. package/dist/commands/messaging/http.d.ts +22 -0
  57. package/dist/commands/messaging/http.js +270 -0
  58. package/dist/commands/messaging/http.js.map +1 -0
  59. package/dist/commands/messaging/identity.d.ts +29 -0
  60. package/dist/commands/messaging/identity.js +63 -0
  61. package/dist/commands/messaging/identity.js.map +1 -0
  62. package/dist/commands/messaging/shared.d.ts +53 -0
  63. package/dist/commands/messaging/shared.js +135 -0
  64. package/dist/commands/messaging/shared.js.map +1 -0
  65. package/dist/commands/messaging/state.d.ts +26 -0
  66. package/dist/commands/messaging/state.js +82 -0
  67. package/dist/commands/messaging/state.js.map +1 -0
  68. package/dist/commands/messaging/validate.d.ts +40 -0
  69. package/dist/commands/messaging/validate.js +193 -0
  70. package/dist/commands/messaging/validate.js.map +1 -0
  71. package/dist/commands/messaging/wire.d.ts +133 -0
  72. package/dist/commands/messaging/wire.js +16 -0
  73. package/dist/commands/messaging/wire.js.map +1 -0
  74. package/dist/commands/notify.d.ts +2 -0
  75. package/dist/commands/notify.js +68 -0
  76. package/dist/commands/notify.js.map +1 -0
  77. package/dist/commands/registry.d.ts +14 -0
  78. package/dist/commands/registry.js +144 -0
  79. package/dist/commands/registry.js.map +1 -0
  80. package/dist/commands/stub.d.ts +1 -0
  81. package/dist/commands/stub.js +9 -0
  82. package/dist/commands/stub.js.map +1 -0
  83. package/dist/commands/task.d.ts +2 -0
  84. package/dist/commands/task.js +223 -0
  85. package/dist/commands/task.js.map +1 -0
  86. package/dist/commands/whoami.d.ts +6 -0
  87. package/dist/commands/whoami.js +90 -0
  88. package/dist/commands/whoami.js.map +1 -0
  89. package/dist/config-file.d.ts +38 -0
  90. package/dist/config-file.js +233 -0
  91. package/dist/config-file.js.map +1 -0
  92. package/dist/config.d.ts +64 -0
  93. package/dist/config.js +97 -0
  94. package/dist/config.js.map +1 -0
  95. package/dist/envelope.d.ts +25 -0
  96. package/dist/envelope.js +41 -0
  97. package/dist/envelope.js.map +1 -0
  98. package/dist/exit-codes.d.ts +51 -0
  99. package/dist/exit-codes.js +57 -0
  100. package/dist/exit-codes.js.map +1 -0
  101. package/dist/help.d.ts +1 -0
  102. package/dist/help.js +17 -0
  103. package/dist/help.js.map +1 -0
  104. package/dist/terminal.d.ts +5 -0
  105. package/dist/terminal.js +18 -0
  106. package/dist/terminal.js.map +1 -0
  107. package/dist/version.d.ts +8 -0
  108. package/dist/version.js +23 -0
  109. package/dist/version.js.map +1 -0
  110. package/package.json +38 -0
@@ -0,0 +1,270 @@
1
+ // Shared Hub HTTP for the messaging commands (notify / ask / task) — submit an envelope and
2
+ // poll a message, over the INJECTABLE `ctx.runtime.fetchImpl` so command tests stay hermetic
3
+ // (never a live Hub). The one thing worth centralizing is the HTTP-status → §7 exit-code
4
+ // mapping, so every messaging command surfaces the same codes/messages (cli spec §7/§8).
5
+ //
6
+ // The tiny defensive body readers + terminal sanitizer mirror the private copies in
7
+ // `commands/login.ts`; they are duplicated (not imported) so this stays a self-contained layer
8
+ // with FULL status coverage (409 conflict / 422 validation_error), which the device-code flow
9
+ // in login.ts never needs. A future shared CLI-core package could unify them (distinct from
10
+ // `@oh-hai/ma2h-core`, the protocol crypto/lifecycle/validation core, not these HTTP readers).
11
+ import { CliError } from "../../envelope.js";
12
+ import { exitCodeForError, isErrorCode } from "../../exit-codes.js";
13
+ import { throwTransportError } from "../http.js";
14
+ import { MAX_TIMER_MS, sanitizeForTerminal } from "./shared.js";
15
+ /** Default per-request bound when no `--timeout`/`MA2H_TIMEOUT_MS` is set — mirrors the scripts
16
+ * layer's DEFAULT_TIMEOUT_MS so an unresponsive Hub can't hang a command indefinitely. */
17
+ const DEFAULT_TIMEOUT_MS = 10_000;
18
+ /** The per-request bound for a single Hub call: `config.timeoutMs` (from `--timeout` /
19
+ * `MA2H_TIMEOUT_MS`, §6), defaulted and clamped to the timer ceiling so an oversized/negative value
20
+ * can't overflow `AbortSignal.timeout` and fail the request spuriously. */
21
+ function requestTimeoutMs(ctx) {
22
+ const t = ctx.config.timeoutMs;
23
+ return Math.min(t !== undefined && t > 0 ? t : DEFAULT_TIMEOUT_MS, MAX_TIMER_MS);
24
+ }
25
+ /** Redact any occurrence of the bearer from a Hub-supplied string before it can reach
26
+ * stdout/stderr/logs/the `--json` envelope (§5.4) — a hostile/buggy Hub could echo the
27
+ * Authorization value back in an error message, an ack field, or (inbox watch) a directive. */
28
+ export function redactToken(message, token) {
29
+ if (message === undefined || token === "")
30
+ return message;
31
+ return message.split(token).join("[redacted]");
32
+ }
33
+ /** POST an envelope to the Hub's ingest endpoint (agent bearer). `202` → the SubmitAck; every
34
+ * other status maps to the right §7 exit code (via the Hub's error envelope when present). */
35
+ export async function submitEnvelope(ctx, token, envelope) {
36
+ const url = `${ctx.config.baseUrl}/v1/messages`;
37
+ const init = {
38
+ method: "POST",
39
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
40
+ body: JSON.stringify(envelope),
41
+ signal: AbortSignal.timeout(requestTimeoutMs(ctx)),
42
+ };
43
+ let res;
44
+ try {
45
+ res = await ctx.runtime.fetchImpl(url, init);
46
+ }
47
+ catch (error) {
48
+ throwTransportError(error, ctx.config.baseUrl);
49
+ }
50
+ if (res.status !== 202) {
51
+ const { code, message } = await readErrorEnvelope(res);
52
+ throw hubStatusToCliError(res.status, redactToken(message, token), code);
53
+ }
54
+ const body = await readJson(res);
55
+ // A conformant Hub returns `{ id, status, poll_url, review_url? }` for every message type.
56
+ // Validate the fields the submit paths consume — `id` + `status` (printSubmitAck echoes status,
57
+ // which would throw on `undefined`) + `poll_url` (the ack contract + ask `--json`'s promised
58
+ // resume URL), and a present `review_url` must be a string too (else printSubmitAck /--json break)
59
+ // — rather than casting a malformed 202 body through and crashing / emitting an incomplete payload
60
+ // AFTER the message was accepted.
61
+ if (typeof body.id !== "string" || body.id === "" ||
62
+ typeof body.status !== "string" || body.status === "" ||
63
+ typeof body.poll_url !== "string" || body.poll_url === "" ||
64
+ (body.review_url !== undefined && typeof body.review_url !== "string")) {
65
+ throw new CliError("server", "the Hub accepted the message but returned an incomplete or malformed ack.");
66
+ }
67
+ // Redact the bearer from every ack field before returning — a hostile/buggy Hub could echo the
68
+ // Authorization value into id/status/poll_url/review_url, which the submit handlers print to
69
+ // stdout / the --json envelope (§5.4). Build a clean ack (drops any extra Hub fields too).
70
+ const ack = {
71
+ id: redactToken(body.id, token),
72
+ status: redactToken(body.status, token),
73
+ poll_url: redactToken(body.poll_url, token),
74
+ ...(typeof body.review_url === "string" ? { review_url: redactToken(body.review_url, token) } : {}),
75
+ };
76
+ return ack;
77
+ }
78
+ /** GET one message (the submitter-bound pull, agent bearer). Embeds the terminal Response.
79
+ * A `2xx` returns the parsed body; every other status maps to a §7 exit code. */
80
+ export async function pollMessage(ctx, token, id, timeoutMs) {
81
+ const url = `${ctx.config.baseUrl}/v1/messages/${encodeURIComponent(id)}`;
82
+ const init = {
83
+ method: "GET",
84
+ headers: { Authorization: `Bearer ${token}` },
85
+ signal: AbortSignal.timeout(timeoutMs),
86
+ };
87
+ let res;
88
+ try {
89
+ res = await ctx.runtime.fetchImpl(url, init);
90
+ }
91
+ catch (error) {
92
+ throwTransportError(error, ctx.config.baseUrl);
93
+ }
94
+ if (res.status < 200 || res.status >= 300) {
95
+ const { code, message } = await readErrorEnvelope(res);
96
+ throw hubStatusToCliError(res.status, redactToken(message, token), code);
97
+ }
98
+ const body = await readJson(res);
99
+ // Validate the body shape before the await loop keys terminality on it: the `id` must be present
100
+ // AND match the requested id (a proxy returning a different message would otherwise report the
101
+ // wrong outcome under the requested id), and a `status` valid globally but IMPOSSIBLE for the
102
+ // message's `type` (e.g. an ask-only `cancelled` on a task) would be reported as a real outcome.
103
+ // A malformed / mismatched poll body is a server error, not a completed await.
104
+ if (typeof body.id !== "string" || body.id !== id || !isValidTypeStatus(body.type, body.status)) {
105
+ throw new CliError("server", "the Hub returned a malformed or mismatched message (bad/other id, or a status invalid for its type).");
106
+ }
107
+ // A TERMINAL ask/task body must embed its Response (the pull contract) — else `await` would exit
108
+ // "successfully" with no resolution value/actor. `open`/`delivered` are pre-terminal (no Response).
109
+ if (body.status !== "open" && body.status !== "delivered" && (typeof body.response !== "object" || body.response === null)) {
110
+ throw new CliError("server", "the Hub returned a resolved message with no embedded Response.");
111
+ }
112
+ return body;
113
+ }
114
+ /** GET the agent's mailbox — the human→agent drain (spec §13, agent bearer). A `2xx` returns the
115
+ * signed deliveries; every other status maps to a §7 exit code. `max`, when set, caps the batch via
116
+ * `?max=`. The drain also touches `agent_seen` server-side, so an authenticated poll is itself the
117
+ * presence heartbeat (§15) — nothing extra to send. */
118
+ export async function drainInbox(ctx, token, max) {
119
+ const query = max !== undefined ? `?max=${max}` : "";
120
+ const url = `${ctx.config.baseUrl}/v1/inbox${query}`;
121
+ const init = {
122
+ method: "GET",
123
+ headers: { Authorization: `Bearer ${token}` },
124
+ signal: AbortSignal.timeout(requestTimeoutMs(ctx)),
125
+ };
126
+ let res;
127
+ try {
128
+ res = await ctx.runtime.fetchImpl(url, init);
129
+ }
130
+ catch (error) {
131
+ throwTransportError(error, ctx.config.baseUrl);
132
+ }
133
+ if (res.status < 200 || res.status >= 300) {
134
+ const { code, message } = await readErrorEnvelope(res);
135
+ throw hubStatusToCliError(res.status, redactToken(message, token), code);
136
+ }
137
+ const body = await readJson(res);
138
+ // Validate the drain shape before the loop trusts it: `directives` MUST be an array, and every
139
+ // element MUST carry an object `directive` with a string `id` (the dedup + ack key) and a string
140
+ // `signature` (forwarded to the runtime). A malformed body is a server error, not an empty drain —
141
+ // silently treating garbage as "no directives" would strand real mail. Mirrors submitEnvelope's
142
+ // defensive 202 validation.
143
+ const directives = body.directives;
144
+ if (!Array.isArray(directives) || !directives.every(isInboundDelivery)) {
145
+ throw new CliError("server", "the Hub returned a malformed inbox drain (expected { directives: [{ directive: { id }, signature }] }).");
146
+ }
147
+ return { directives: directives };
148
+ }
149
+ /** POST a single batched consume-ack for the drained ids — the §14 directive receipt (agent bearer).
150
+ * A `2xx` returns the consumed count; every other status maps to a §7 exit code. */
151
+ export async function ackInbox(ctx, token, ids) {
152
+ const url = `${ctx.config.baseUrl}/v1/inbox/ack`;
153
+ const init = {
154
+ method: "POST",
155
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
156
+ body: JSON.stringify({ ids }),
157
+ signal: AbortSignal.timeout(requestTimeoutMs(ctx)),
158
+ };
159
+ let res;
160
+ try {
161
+ res = await ctx.runtime.fetchImpl(url, init);
162
+ }
163
+ catch (error) {
164
+ throwTransportError(error, ctx.config.baseUrl);
165
+ }
166
+ if (res.status < 200 || res.status >= 300) {
167
+ const { code, message } = await readErrorEnvelope(res);
168
+ throw hubStatusToCliError(res.status, redactToken(message, token), code);
169
+ }
170
+ const body = await readJson(res);
171
+ // The Hub returns the ids it actually consumed (spec §14; server `ackDirectives` → string[]), not a
172
+ // count — validate that shape so a contract drift surfaces as a clean server error, not a silent cast.
173
+ const acked = body.acked;
174
+ if (!Array.isArray(acked) || !acked.every((id) => typeof id === "string")) {
175
+ throw new CliError("server", "the Hub consumed the directives but returned a malformed ack result.");
176
+ }
177
+ return { acked: acked };
178
+ }
179
+ /** Structural guard for one drained delivery: an object `directive` carrying string `id` / `from` /
180
+ * `title` (and, when present, a string `priority`), plus a string `signature`. `id` is the dedup +
181
+ * ack key; `from`/`title`/`priority` are dereferenced on the human emit path (`sanitizeForTerminal`
182
+ * would throw on a non-string), so a directive whose value for any of them isn't a string must be
183
+ * rejected here as a `server` error at drain — never handed to the loop to crash mid-stream. */
184
+ function isInboundDelivery(value) {
185
+ if (value === null || typeof value !== "object")
186
+ return false;
187
+ const { directive, signature } = value;
188
+ if (typeof signature !== "string")
189
+ return false;
190
+ if (directive === null || typeof directive !== "object")
191
+ return false;
192
+ const d = directive;
193
+ if (typeof d.id !== "string" || typeof d.from !== "string" || typeof d.title !== "string")
194
+ return false;
195
+ return d.priority === undefined || typeof d.priority === "string";
196
+ }
197
+ /** Valid lifecycle statuses per message type (spec §7). A status valid for one type but not the
198
+ * message's actual type is a malformed body. `delivered` is terminal-on-acceptance for `notify`
199
+ * ONLY — ask/task go from `open` straight to a resolution, so a `{type:"ask", status:"delivered"}`
200
+ * must be rejected (else the await loop would treat it as non-terminal and hang to timeout). */
201
+ const STATUSES_BY_TYPE = {
202
+ notify: new Set(["open", "delivered"]),
203
+ ask: new Set(["open", "answered", "declined", "cancelled", "expired"]),
204
+ task: new Set(["open", "completed", "dismissed", "expired"]),
205
+ };
206
+ function isValidTypeStatus(type, status) {
207
+ if (typeof type !== "string" || typeof status !== "string")
208
+ return false;
209
+ const allowed = STATUSES_BY_TYPE[type];
210
+ return allowed !== undefined && allowed.has(status);
211
+ }
212
+ /** Map a Hub error response to a CliError. The exit code comes from the HTTP status; the stable
213
+ * `error.code` string (§8) prefers the Hub's own `error.code` when it maps to the SAME exit tier
214
+ * — so a documented code like `version_not_supported` survives instead of being flattened to
215
+ * `bad_request` (both exit 9), without letting a Hub-supplied code change the exit semantics. The
216
+ * server message is stripped of terminal control chars (the top-level catch prints it). */
217
+ function hubStatusToCliError(status, message, hubCode) {
218
+ const detail = message !== undefined ? sanitizeForTerminal(message) : `Hub returned ${status}.`;
219
+ const base = statusToErrorCode(status);
220
+ // Preserve the Hub's code ONLY when it is a documented §8 code AND maps to the same exit tier —
221
+ // an unrecognized code (e.g. `rate_limited` on a 429) must not leak into the CLI envelope, and a
222
+ // recognized code from a different tier must not change the exit semantics.
223
+ if (hubCode !== undefined && isErrorCode(hubCode) && exitCodeForError(hubCode) === exitCodeForError(base)) {
224
+ return new CliError(hubCode, detail);
225
+ }
226
+ return new CliError(base, detail);
227
+ }
228
+ /** The stable `error.code` implied by an HTTP status (§7/§8). */
229
+ function statusToErrorCode(status) {
230
+ if (status === 401 || status === 403)
231
+ return "auth";
232
+ if (status === 404)
233
+ return "not_found";
234
+ if (status === 409)
235
+ return "conflict";
236
+ if (status === 413)
237
+ return "payload_too_large";
238
+ if (status === 422)
239
+ return "validation_error";
240
+ if (status === 400)
241
+ return "bad_request";
242
+ if (status >= 500)
243
+ return "server";
244
+ return "error";
245
+ }
246
+ /** Parse a response body as a JSON object, defensively — a fake without `json()`, a non-JSON
247
+ * body, or a non-object all collapse to `{}`, so callers read fields off a plain record. */
248
+ async function readJson(res) {
249
+ if (typeof res.json !== "function")
250
+ return {};
251
+ try {
252
+ const parsed = await res.json();
253
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)
254
+ ? parsed
255
+ : {};
256
+ }
257
+ catch {
258
+ return {};
259
+ }
260
+ }
261
+ /** Read the A2H error envelope `{ error: { code, message } }` from a non-2xx response. */
262
+ async function readErrorEnvelope(res) {
263
+ const body = await readJson(res);
264
+ const error = body.error;
265
+ const record = error !== null && typeof error === "object" ? error : {};
266
+ const message = typeof record.message === "string" && record.message.trim() !== "" ? record.message.trim() : undefined;
267
+ const code = typeof record.code === "string" && record.code.trim() !== "" ? record.code.trim() : undefined;
268
+ return { code, message };
269
+ }
270
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/commands/messaging/http.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,6FAA6F;AAC7F,yFAAyF;AACzF,yFAAyF;AACzF,EAAE;AACF,oFAAoF;AACpF,+FAA+F;AAC/F,8FAA8F;AAC9F,4FAA4F;AAC5F,+FAA+F;AAE/F,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAkB,MAAM,qBAAqB,CAAC;AAEpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGhE;2FAC2F;AAC3F,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC;;4EAE4E;AAC5E,SAAS,gBAAgB,CAAC,GAAmB;IAC3C,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC;IAC/B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,kBAAkB,EAAE,YAAY,CAAC,CAAC;AACnF,CAAC;AAED;;gGAEgG;AAChG,MAAM,UAAU,WAAW,CAAC,OAA2B,EAAE,KAAa;IACpE,IAAI,OAAO,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,OAAO,CAAC;IAC1D,OAAO,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;AACjD,CAAC;AAED;+FAC+F;AAC/F,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAmB,EACnB,KAAa,EACb,QAAiB;IAEjB,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,cAAc,CAAC;IAChD,MAAM,IAAI,GAAG;QACX,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;QACjF,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;QAC9B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;KACnD,CAAC;IACF,IAAI,GAAiB,CAAC;IACtB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mBAAmB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACvB,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,CAAC;QACvD,MAAM,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;IACjC,2FAA2F;IAC3F,gGAAgG;IAChG,6FAA6F;IAC7F,mGAAmG;IACnG,mGAAmG;IACnG,kCAAkC;IAClC,IACE,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE;QAC7C,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,EAAE;QACrD,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,EAAE;QACzD,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,CAAC,EACtE,CAAC;QACD,MAAM,IAAI,QAAQ,CAAC,QAAQ,EAAE,2EAA2E,CAAC,CAAC;IAC5G,CAAC;IACD,+FAA+F;IAC/F,6FAA6F;IAC7F,2FAA2F;IAC3F,MAAM,GAAG,GAAc;QACrB,EAAE,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAW;QACzC,MAAM,EAAE,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAwB;QAC9D,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAW;QACrD,GAAG,CAAC,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9G,CAAC;IACF,OAAO,GAAG,CAAC;AACb,CAAC;AAED;kFACkF;AAClF,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAAmB,EACnB,KAAa,EACb,EAAU,EACV,SAAiB;IAEjB,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,gBAAgB,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC;IAC1E,MAAM,IAAI,GAAG;QACX,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;QAC7C,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;KACvC,CAAC;IACF,IAAI,GAAiB,CAAC;IACtB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mBAAmB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;QAC1C,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,CAAC;QACvD,MAAM,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;IACjC,iGAAiG;IACjG,+FAA+F;IAC/F,8FAA8F;IAC9F,iGAAiG;IACjG,+EAA+E;IAC/E,IAAI,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAChG,MAAM,IAAI,QAAQ,CAAC,QAAQ,EAAE,sGAAsG,CAAC,CAAC;IACvI,CAAC;IACD,iGAAiG;IACjG,oGAAoG;IACpG,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,KAAK,WAAW,IAAI,CAAC,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,EAAE,CAAC;QAC3H,MAAM,IAAI,QAAQ,CAAC,QAAQ,EAAE,gEAAgE,CAAC,CAAC;IACjG,CAAC;IACD,OAAO,IAAiC,CAAC;AAC3C,CAAC;AAED;;;wDAGwD;AACxD,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAmB,EACnB,KAAa,EACb,GAAY;IAEZ,MAAM,KAAK,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACrD,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,YAAY,KAAK,EAAE,CAAC;IACrD,MAAM,IAAI,GAAG;QACX,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;QAC7C,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;KACnD,CAAC;IACF,IAAI,GAAiB,CAAC;IACtB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mBAAmB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;QAC1C,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,CAAC;QACvD,MAAM,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;IACjC,+FAA+F;IAC/F,iGAAiG;IACjG,mGAAmG;IACnG,gGAAgG;IAChG,4BAA4B;IAC5B,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;IACnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACvE,MAAM,IAAI,QAAQ,CAAC,QAAQ,EAAE,yGAAyG,CAAC,CAAC;IAC1I,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,UAA+B,EAAE,CAAC;AACzD,CAAC;AAED;qFACqF;AACrF,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,GAAmB,EAAE,KAAa,EAAE,GAAa;IAC9E,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,eAAe,CAAC;IACjD,MAAM,IAAI,GAAG;QACX,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;QACjF,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;QAC7B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;KACnD,CAAC;IACF,IAAI,GAAiB,CAAC;IACtB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mBAAmB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;QAC1C,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,CAAC;QACvD,MAAM,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;IACjC,oGAAoG;IACpG,uGAAuG;IACvG,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACzB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,CAAC,EAAE,CAAC;QAC1E,MAAM,IAAI,QAAQ,CAAC,QAAQ,EAAE,sEAAsE,CAAC,CAAC;IACvG,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,KAAiB,EAAE,CAAC;AACtC,CAAC;AAED;;;;iGAIiG;AACjG,SAAS,iBAAiB,CAAC,KAAc;IACvC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,KAAqD,CAAC;IACvF,IAAI,OAAO,SAAS,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAChD,IAAI,SAAS,KAAK,IAAI,IAAI,OAAO,SAAS,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtE,MAAM,CAAC,GAAG,SAAkF,CAAC;IAC7F,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACxG,OAAO,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC;AACpE,CAAC;AAED;;;iGAGiG;AACjG,MAAM,gBAAgB,GAAgC;IACpD,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACtC,GAAG,EAAE,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;IACtE,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;CAC7D,CAAC;AACF,SAAS,iBAAiB,CAAC,IAAa,EAAE,MAAe;IACvD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACzE,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AACtD,CAAC;AAED;;;;4FAI4F;AAC5F,SAAS,mBAAmB,CAAC,MAAc,EAAE,OAA2B,EAAE,OAAgB;IACxF,MAAM,MAAM,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,gBAAgB,MAAM,GAAG,CAAC;IAChG,MAAM,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACvC,gGAAgG;IAChG,iGAAiG;IACjG,4EAA4E;IAC5E,IAAI,OAAO,KAAK,SAAS,IAAI,WAAW,CAAC,OAAO,CAAC,IAAI,gBAAgB,CAAC,OAAO,CAAC,KAAK,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1G,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC;AAED,iEAAiE;AACjE,SAAS,iBAAiB,CAAC,MAAc;IACvC,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,MAAM,CAAC;IACpD,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,WAAW,CAAC;IACvC,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,UAAU,CAAC;IACtC,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,mBAAmB,CAAC;IAC/C,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,kBAAkB,CAAC;IAC9C,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,aAAa,CAAC;IACzC,IAAI,MAAM,IAAI,GAAG;QAAE,OAAO,QAAQ,CAAC;IACnC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;6FAC6F;AAC7F,KAAK,UAAU,QAAQ,CAAC,GAAiB;IACvC,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,UAAU;QAAE,OAAO,EAAE,CAAC;IAC9C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAChC,OAAO,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAC5E,CAAC,CAAE,MAAkC;YACrC,CAAC,CAAC,EAAE,CAAC;IACT,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,0FAA0F;AAC1F,KAAK,UAAU,iBAAiB,CAAC,GAAiB;IAChD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACzB,MAAM,MAAM,GAAG,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAE,KAAiC,CAAC,CAAC,CAAC,EAAE,CAAC;IACrG,MAAM,OAAO,GAAG,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACvH,MAAM,IAAI,GAAG,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3G,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { CommandContext } from "../context.js";
2
+ /** Resolve the effective bearer, opening the local store only when the CI env token is absent
3
+ * (matches `whoami` — skip probing the OS keychain when `MA2H_AGENT_TOKEN` already resolves it).
4
+ * `accountOverride`, when given, is the AUTHORITATIVE store lookup key and wins over
5
+ * `ctx.config.account` — used by `--envelope` replay: a verbatim replay authenticates as the
6
+ * captured envelope's `agent.id` (the Hub 403s if the bearer's agent ≠ the envelope's), so the
7
+ * token must be looked up by that id, not by a configured `default_account`. The env token (CI)
8
+ * still wins over both. */
9
+ export declare function resolveBearer(ctx: CommandContext, accountOverride?: string): Promise<string | undefined>;
10
+ /** The bearer + submitting agent id required for a REAL (non-dry-run) submit. Uses the same
11
+ * `resolveIdentity` helper `whoami`/`agents` use — so besides an explicit `--account`/`MA2H_AGENT_ID`
12
+ * it also DISCOVERS the sole stored identity for the Hub (the zero-config path after a device-code
13
+ * `oh-hai login`), letting `notify`/`ask`/`task` work without redundantly naming the account. A
14
+ * missing/ambiguous account is a usage error (exit 2, "pass --account") — actionable, and it beats
15
+ * an auth error that would wrongly send the caller to re-login; a known account with no token is
16
+ * the auth error (exit 3). The submitting `agent.id` MUST equal the bound bearer or the Hub 403s. */
17
+ export declare function resolveSubmitIdentity(ctx: CommandContext): Promise<{
18
+ token: string;
19
+ agentId: string;
20
+ }>;
21
+ /** The bearer required when no resolved account is needed — an `await` (poll keyed by message id
22
+ * + the submitter-bound bearer). Uses `resolveIdentity` so a zero-config single stored identity is
23
+ * discovered for the token lookup. A missing token is an auth error (exit 3). */
24
+ export declare function requireToken(ctx: CommandContext): Promise<string>;
25
+ /** The bearer for an `--envelope` replay. The captured envelope already carries the submitting
26
+ * `agent.id`, so fall back to it for the store lookup when no explicit `--account` was given —
27
+ * otherwise a user with a stored login but no `default_account` following the replay example
28
+ * would hit a spurious auth error. A missing token is an auth error (exit 3). */
29
+ export declare function requireReplayToken(ctx: CommandContext, envelopeAgentId: string): Promise<string>;
@@ -0,0 +1,63 @@
1
+ // Account-aware credential resolution for the messaging commands (spec §5/§6). This is the
2
+ // productization delta over `scripts/*`: instead of reading `MA2H_AGENT_TOKEN`/`MA2H_AGENT_ID`
3
+ // straight from env, the bearer is resolved through the same layered store `whoami` uses —
4
+ // `MA2H_AGENT_TOKEN` (CI path) wins, else the keychain, else the 0600 file — and the submitting
5
+ // agent id comes from resolved config (`--account`/`--agent`/`MA2H_AGENT_ID`/`default_account`).
6
+ import { resolveIdentity, resolveToken } from "../../auth/resolve-token.js";
7
+ import { CliError } from "../../envelope.js";
8
+ /** The "no token" auth error (exit 3), shared by the submit and await/replay resolvers. */
9
+ const NOT_AUTHENTICATED = "not authenticated — no stored token (run `oh-hai login`, or set MA2H_AGENT_TOKEN).";
10
+ /** Resolve the effective bearer, opening the local store only when the CI env token is absent
11
+ * (matches `whoami` — skip probing the OS keychain when `MA2H_AGENT_TOKEN` already resolves it).
12
+ * `accountOverride`, when given, is the AUTHORITATIVE store lookup key and wins over
13
+ * `ctx.config.account` — used by `--envelope` replay: a verbatim replay authenticates as the
14
+ * captured envelope's `agent.id` (the Hub 403s if the bearer's agent ≠ the envelope's), so the
15
+ * token must be looked up by that id, not by a configured `default_account`. The env token (CI)
16
+ * still wins over both. */
17
+ export async function resolveBearer(ctx, accountOverride) {
18
+ const store = ctx.config.tokenSource === "env" ? undefined : await ctx.runtime.openStore();
19
+ const config = accountOverride !== undefined ? { ...ctx.config, account: accountOverride } : ctx.config;
20
+ const { token } = await resolveToken(config, store);
21
+ return token;
22
+ }
23
+ /** The bearer + submitting agent id required for a REAL (non-dry-run) submit. Uses the same
24
+ * `resolveIdentity` helper `whoami`/`agents` use — so besides an explicit `--account`/`MA2H_AGENT_ID`
25
+ * it also DISCOVERS the sole stored identity for the Hub (the zero-config path after a device-code
26
+ * `oh-hai login`), letting `notify`/`ask`/`task` work without redundantly naming the account. A
27
+ * missing/ambiguous account is a usage error (exit 2, "pass --account") — actionable, and it beats
28
+ * an auth error that would wrongly send the caller to re-login; a known account with no token is
29
+ * the auth error (exit 3). The submitting `agent.id` MUST equal the bound bearer or the Hub 403s. */
30
+ export async function resolveSubmitIdentity(ctx) {
31
+ const store = ctx.config.tokenSource === "env" ? undefined : await ctx.runtime.openStore();
32
+ const { account, token } = await resolveIdentity(ctx.config, store);
33
+ if (account === undefined) {
34
+ throw new CliError("usage", "an agent id is required — pass --account/--agent, set MA2H_AGENT_ID, or log in so a single stored identity is discovered.");
35
+ }
36
+ if (token === undefined) {
37
+ throw new CliError("auth", NOT_AUTHENTICATED);
38
+ }
39
+ return { token, agentId: account };
40
+ }
41
+ /** The bearer required when no resolved account is needed — an `await` (poll keyed by message id
42
+ * + the submitter-bound bearer). Uses `resolveIdentity` so a zero-config single stored identity is
43
+ * discovered for the token lookup. A missing token is an auth error (exit 3). */
44
+ export async function requireToken(ctx) {
45
+ const store = ctx.config.tokenSource === "env" ? undefined : await ctx.runtime.openStore();
46
+ const { token } = await resolveIdentity(ctx.config, store);
47
+ if (token === undefined) {
48
+ throw new CliError("auth", NOT_AUTHENTICATED);
49
+ }
50
+ return token;
51
+ }
52
+ /** The bearer for an `--envelope` replay. The captured envelope already carries the submitting
53
+ * `agent.id`, so fall back to it for the store lookup when no explicit `--account` was given —
54
+ * otherwise a user with a stored login but no `default_account` following the replay example
55
+ * would hit a spurious auth error. A missing token is an auth error (exit 3). */
56
+ export async function requireReplayToken(ctx, envelopeAgentId) {
57
+ const token = await resolveBearer(ctx, envelopeAgentId);
58
+ if (token === undefined) {
59
+ throw new CliError("auth", NOT_AUTHENTICATED);
60
+ }
61
+ return token;
62
+ }
63
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../../src/commands/messaging/identity.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,+FAA+F;AAC/F,2FAA2F;AAC3F,gGAAgG;AAChG,iGAAiG;AAEjG,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAG7C,2FAA2F;AAC3F,MAAM,iBAAiB,GAAG,oFAAoF,CAAC;AAE/G;;;;;;4BAM4B;AAC5B,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAmB,EAAE,eAAwB;IAC/E,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;IAC3F,MAAM,MAAM,GAAG,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC;IACxG,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACpD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;sGAMsG;AACtG,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,GAAmB;IAC7D,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;IAC3F,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACpE,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,QAAQ,CAChB,OAAO,EACP,2HAA2H,CAC5H,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AACrC,CAAC;AAED;;kFAEkF;AAClF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAmB;IACpD,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;IAC3F,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC3D,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;kFAGkF;AAClF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAmB,EAAE,eAAuB;IACnF,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IACxD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,53 @@
1
+ import type { CommandContext } from "../context.js";
2
+ import type { SubmitAck } from "./wire.js";
3
+ /** The connection flags to append to a printed `await` resume hint so it is copy-paste-runnable
4
+ * regardless of how the submit resolved them: a non-default `--base-url` and a set `--account`.
5
+ * Without these, a hint after a CLI-routed submit (`--base-url … --account …`, no env/config) would
6
+ * poll the default Hub / have no account for the token lookup. These are LOCAL config (not
7
+ * Hub-supplied), so no terminal-sanitization is needed. */
8
+ export declare function resumeRoutingSuffix(ctx: CommandContext): string;
9
+ /** Strip terminal control characters (C0/C1 + DEL) from a Hub-supplied string before echoing it
10
+ * to the terminal on a HUMAN output path — a malicious/compromised Hub could otherwise inject
11
+ * ANSI/escape sequences via an ack field or a resolution's value/comment/actor. The `--json`
12
+ * path is unaffected (JSON.stringify escapes control chars). Mirrors login.ts / http.ts; built by
13
+ * code point to avoid a control-char regex literal. */
14
+ export declare function sanitizeForTerminal(value: string): string;
15
+ /** Node's setTimeout / AbortSignal.timeout ceiling (max 32-bit signed int) — a larger delay
16
+ * overflows to ~immediate. Shared by every ms-flag validator so the ceiling lives in one place. */
17
+ export declare const MAX_TIMER_MS = 2147483647;
18
+ /** Parse a strictly-positive INTEGER flag; a NaN / fractional / 0 / negative / over-`ceiling` value is
19
+ * a usage error (exit 2). `ceiling` defaults to the timer range (correct for delay flags like
20
+ * `--interval` that would overflow setTimeout if huge); pass a smaller cap for count flags like
21
+ * `--max`. `detail` tailors the message to the flag's unit (e.g. "a positive integer number of
22
+ * milliseconds"). */
23
+ export declare function positiveInt(raw: string | undefined, flag: string, detail?: string, ceiling?: number): number;
24
+ /** Coerce a `parseArgs` value (string | boolean | string[] | undefined under strict:false) to a
25
+ * plain string, or undefined. A repeated single-value flag arrives as an array — take the last. */
26
+ export declare function stringFlag(value: unknown): string | undefined;
27
+ /** Strictly parse a boolean switch. Unlike the lenient `flagOn`, an unrecognized explicit value
28
+ * (`--flag=treu`) is a USAGE error rather than silently `false` — otherwise a typo on a
29
+ * behavior-changing switch falls through to the wrong mode (a LIVE submit, or an infinite loop
30
+ * instead of a one-shot). Absent → false; bare or a recognized on/off token → the boolean;
31
+ * anything else → exit 2. */
32
+ export declare function strictBoolFlag(value: unknown, flag: string): boolean;
33
+ /** The `--dry-run` safety switch — a `strictBoolFlag` so a typo (`--dry-run=treu`) is a usage error,
34
+ * not a silent fall-through to a LIVE submit. */
35
+ export declare function dryRunFlag(value: unknown): boolean;
36
+ /** A value flag that, when present, must be non-empty. An explicit `--flag=` would otherwise ship
37
+ * a blank `idempotency_key`/`created_at`; the Hub dedupes on the key, so a blank collides (and a
38
+ * blank timestamp is invalid). Returns undefined when the flag is absent. Shared by ask + task. */
39
+ export declare function requireNonEmpty(flags: Record<string, unknown>, name: string): string | undefined;
40
+ /** Reject flags that belong to a DIFFERENT subcommand (e.g. `ask await --resolver`, `ask submit
41
+ * --interval`) rather than silently ignoring them (spec's loud-fail discipline). `values` is the
42
+ * parsed flag map; `keys` are the foreign flag names to reject for this subcommand. */
43
+ export declare function assertNoForeignFlags(values: Record<string, unknown>, keys: readonly string[], context: string): void;
44
+ /** The run id stamped on the envelope — `MA2H_AGENT_RUN_ID` (env-only, kept stable across an
45
+ * idempotent retry, §6) or a fresh `run_<uuid>`. Matches the scripts layer. */
46
+ export declare function resolveRunId(): string;
47
+ /** Print the common human-readable submit acknowledgement (headline + id + status, plus the
48
+ * poll URL when relevant and a review URL when the Hub returned one). Commands append their own
49
+ * next-step hint after calling this. Never prints the token (§5.4). */
50
+ export declare function printSubmitAck(ctx: CommandContext, headline: string, ack: SubmitAck, includePoll?: boolean): void;
51
+ /** Print a composed envelope under `--dry-run`: human → pretty JSON; `--json` → the uniform
52
+ * envelope wrapping it (`{ dry_run: true, envelope }`). Posts nothing. */
53
+ export declare function printDryRunEnvelope(ctx: CommandContext, command: string, envelope: unknown): void;
@@ -0,0 +1,135 @@
1
+ // Small helpers shared by the notify / ask / task command handlers: flag coercion, the
2
+ // deferred-`--state` rejection, the submitting run id, and the human-readable submit
3
+ // acknowledgement. Kept here so the three commands stay consistent (cli spec §4.4 / §8).
4
+ import { randomUUID } from "node:crypto";
5
+ import { DEFAULT_BASE_URL } from "../../config.js";
6
+ import { CliError, buildOk, serializeEnvelope } from "../../envelope.js";
7
+ /** The connection flags to append to a printed `await` resume hint so it is copy-paste-runnable
8
+ * regardless of how the submit resolved them: a non-default `--base-url` and a set `--account`.
9
+ * Without these, a hint after a CLI-routed submit (`--base-url … --account …`, no env/config) would
10
+ * poll the default Hub / have no account for the token lookup. These are LOCAL config (not
11
+ * Hub-supplied), so no terminal-sanitization is needed. */
12
+ export function resumeRoutingSuffix(ctx) {
13
+ const parts = [];
14
+ if (ctx.config.baseUrl !== DEFAULT_BASE_URL)
15
+ parts.push(`--base-url ${ctx.config.baseUrl}`);
16
+ if (ctx.config.account !== undefined)
17
+ parts.push(`--account ${ctx.config.account}`);
18
+ return parts.length > 0 ? ` ${parts.join(" ")}` : "";
19
+ }
20
+ /** Strip terminal control characters (C0/C1 + DEL) from a Hub-supplied string before echoing it
21
+ * to the terminal on a HUMAN output path — a malicious/compromised Hub could otherwise inject
22
+ * ANSI/escape sequences via an ack field or a resolution's value/comment/actor. The `--json`
23
+ * path is unaffected (JSON.stringify escapes control chars). Mirrors login.ts / http.ts; built by
24
+ * code point to avoid a control-char regex literal. */
25
+ export function sanitizeForTerminal(value) {
26
+ let out = "";
27
+ for (const ch of value) {
28
+ const code = ch.codePointAt(0) ?? 0;
29
+ if (code > 0x1f && code !== 0x7f && !(code >= 0x80 && code <= 0x9f))
30
+ out += ch;
31
+ }
32
+ return out;
33
+ }
34
+ /** Node's setTimeout / AbortSignal.timeout ceiling (max 32-bit signed int) — a larger delay
35
+ * overflows to ~immediate. Shared by every ms-flag validator so the ceiling lives in one place. */
36
+ export const MAX_TIMER_MS = 2_147_483_647;
37
+ /** Parse a strictly-positive INTEGER flag; a NaN / fractional / 0 / negative / over-`ceiling` value is
38
+ * a usage error (exit 2). `ceiling` defaults to the timer range (correct for delay flags like
39
+ * `--interval` that would overflow setTimeout if huge); pass a smaller cap for count flags like
40
+ * `--max`. `detail` tailors the message to the flag's unit (e.g. "a positive integer number of
41
+ * milliseconds"). */
42
+ export function positiveInt(raw, flag, detail = "a positive integer", ceiling = MAX_TIMER_MS) {
43
+ const n = raw !== undefined ? Number(raw) : NaN;
44
+ if (!Number.isInteger(n) || n <= 0 || n > ceiling) {
45
+ throw new CliError("usage", `${flag} must be ${detail} (at most ${ceiling}).`);
46
+ }
47
+ return n;
48
+ }
49
+ /** Coerce a `parseArgs` value (string | boolean | string[] | undefined under strict:false) to a
50
+ * plain string, or undefined. A repeated single-value flag arrives as an array — take the last. */
51
+ export function stringFlag(value) {
52
+ if (typeof value === "string")
53
+ return value;
54
+ if (Array.isArray(value)) {
55
+ const last = value[value.length - 1];
56
+ return typeof last === "string" ? last : undefined;
57
+ }
58
+ return undefined;
59
+ }
60
+ /** Strictly parse a boolean switch. Unlike the lenient `flagOn`, an unrecognized explicit value
61
+ * (`--flag=treu`) is a USAGE error rather than silently `false` — otherwise a typo on a
62
+ * behavior-changing switch falls through to the wrong mode (a LIVE submit, or an infinite loop
63
+ * instead of a one-shot). Absent → false; bare or a recognized on/off token → the boolean;
64
+ * anything else → exit 2. */
65
+ export function strictBoolFlag(value, flag) {
66
+ if (value === undefined)
67
+ return false;
68
+ if (value === true)
69
+ return true;
70
+ if (Array.isArray(value))
71
+ return value.length > 0 ? strictBoolFlag(value[value.length - 1], flag) : false;
72
+ if (typeof value === "string") {
73
+ const v = value.trim().toLowerCase();
74
+ if (v === "" || v === "true" || v === "1" || v === "yes" || v === "on")
75
+ return true;
76
+ if (v === "false" || v === "0" || v === "no" || v === "off")
77
+ return false;
78
+ }
79
+ throw new CliError("usage", `${flag} does not accept the value ${JSON.stringify(value)}; use ${flag} or ${flag}=true|false.`);
80
+ }
81
+ /** The `--dry-run` safety switch — a `strictBoolFlag` so a typo (`--dry-run=treu`) is a usage error,
82
+ * not a silent fall-through to a LIVE submit. */
83
+ export function dryRunFlag(value) {
84
+ return strictBoolFlag(value, "--dry-run");
85
+ }
86
+ /** A value flag that, when present, must be non-empty. An explicit `--flag=` would otherwise ship
87
+ * a blank `idempotency_key`/`created_at`; the Hub dedupes on the key, so a blank collides (and a
88
+ * blank timestamp is invalid). Returns undefined when the flag is absent. Shared by ask + task. */
89
+ export function requireNonEmpty(flags, name) {
90
+ const v = stringFlag(flags[name]);
91
+ if (v !== undefined && v.trim() === "") {
92
+ throw new CliError("usage", `--${name} was given an empty value — omit the flag for a fresh key, or pass a concrete value.`);
93
+ }
94
+ return v;
95
+ }
96
+ /** Reject flags that belong to a DIFFERENT subcommand (e.g. `ask await --resolver`, `ask submit
97
+ * --interval`) rather than silently ignoring them (spec's loud-fail discipline). `values` is the
98
+ * parsed flag map; `keys` are the foreign flag names to reject for this subcommand. */
99
+ export function assertNoForeignFlags(values, keys, context) {
100
+ const present = keys.filter((k) => values[k] !== undefined);
101
+ if (present.length > 0) {
102
+ const list = present.map((k) => `--${k}`).join(", ");
103
+ throw new CliError("usage", `${list} ${present.length > 1 ? "are" : "is"} not valid for '${context}'.`);
104
+ }
105
+ }
106
+ /** The run id stamped on the envelope — `MA2H_AGENT_RUN_ID` (env-only, kept stable across an
107
+ * idempotent retry, §6) or a fresh `run_<uuid>`. Matches the scripts layer. */
108
+ export function resolveRunId() {
109
+ const raw = process.env.MA2H_AGENT_RUN_ID?.trim();
110
+ return raw !== undefined && raw !== "" ? raw : `run_${randomUUID()}`;
111
+ }
112
+ /** Print the common human-readable submit acknowledgement (headline + id + status, plus the
113
+ * poll URL when relevant and a review URL when the Hub returned one). Commands append their own
114
+ * next-step hint after calling this. Never prints the token (§5.4). */
115
+ export function printSubmitAck(ctx, headline, ack, includePoll = false) {
116
+ // ack fields are Hub-supplied — sanitize before echoing to the terminal (§5.4 posture).
117
+ ctx.io.log(`✅ ${headline}`);
118
+ ctx.io.log(` id: ${sanitizeForTerminal(ack.id)}`);
119
+ ctx.io.log(` status: ${sanitizeForTerminal(ack.status)}`);
120
+ if (includePoll && ack.poll_url)
121
+ ctx.io.log(` poll_url: ${sanitizeForTerminal(ack.poll_url)}`);
122
+ if (ack.review_url)
123
+ ctx.io.log(` review_url: ${sanitizeForTerminal(ack.review_url)}`);
124
+ }
125
+ /** Print a composed envelope under `--dry-run`: human → pretty JSON; `--json` → the uniform
126
+ * envelope wrapping it (`{ dry_run: true, envelope }`). Posts nothing. */
127
+ export function printDryRunEnvelope(ctx, command, envelope) {
128
+ if (ctx.json) {
129
+ ctx.io.log(serializeEnvelope(buildOk(command, { dry_run: true, envelope: envelope })));
130
+ }
131
+ else {
132
+ ctx.io.log(JSON.stringify(envelope, null, 2));
133
+ }
134
+ }
135
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/commands/messaging/shared.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,qFAAqF;AACrF,yFAAyF;AAEzF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAIzE;;;;4DAI4D;AAC5D,MAAM,UAAU,mBAAmB,CAAC,GAAmB;IACrD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,KAAK,gBAAgB;QAAE,KAAK,CAAC,IAAI,CAAC,cAAc,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAC5F,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IACpF,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACvD,CAAC;AAED;;;;wDAIwD;AACxD,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC;YAAE,GAAG,IAAI,EAAE,CAAC;IACjF,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;oGACoG;AACpG,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAAC;AAE1C;;;;sBAIsB;AACtB,MAAM,UAAU,WAAW,CAAC,GAAuB,EAAE,IAAY,EAAE,MAAM,GAAG,oBAAoB,EAAE,OAAO,GAAG,YAAY;IACtH,MAAM,CAAC,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAChD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,EAAE,CAAC;QAClD,MAAM,IAAI,QAAQ,CAAC,OAAO,EAAE,GAAG,IAAI,YAAY,MAAM,aAAa,OAAO,IAAI,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;oGACoG;AACpG,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACrC,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACrD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;8BAI8B;AAC9B,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,IAAY;IACzD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC1G,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACpF,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,KAAK;YAAE,OAAO,KAAK,CAAC;IAC5E,CAAC;IACD,MAAM,IAAI,QAAQ,CAAC,OAAO,EAAE,GAAG,IAAI,8BAA8B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,IAAI,OAAO,IAAI,cAAc,CAAC,CAAC;AAChI,CAAC;AAED;kDACkD;AAClD,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,cAAc,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;AAC5C,CAAC;AAED;;oGAEoG;AACpG,MAAM,UAAU,eAAe,CAAC,KAA8B,EAAE,IAAY;IAC1E,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAClC,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACvC,MAAM,IAAI,QAAQ,CAAC,OAAO,EAAE,KAAK,IAAI,sFAAsF,CAAC,CAAC;IAC/H,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;wFAEwF;AACxF,MAAM,UAAU,oBAAoB,CAClC,MAA+B,EAC/B,IAAuB,EACvB,OAAe;IAEf,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;IAC5D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrD,MAAM,IAAI,QAAQ,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,mBAAmB,OAAO,IAAI,CAAC,CAAC;IAC1G,CAAC;AACH,CAAC;AAED;gFACgF;AAChF,MAAM,UAAU,YAAY;IAC1B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,CAAC;IAClD,OAAO,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,UAAU,EAAE,EAAE,CAAC;AACvE,CAAC;AAED;;wEAEwE;AACxE,MAAM,UAAU,cAAc,CAC5B,GAAmB,EACnB,QAAgB,EAChB,GAAc,EACd,WAAW,GAAG,KAAK;IAEnB,wFAAwF;IACxF,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC;IAC5B,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,kBAAkB,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5D,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,kBAAkB,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAChE,IAAI,WAAW,IAAI,GAAG,CAAC,QAAQ;QAAE,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,kBAAkB,mBAAmB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACnG,IAAI,GAAG,CAAC,UAAU;QAAE,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,kBAAkB,mBAAmB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;AAC1F,CAAC;AAED;2EAC2E;AAC3E,MAAM,UAAU,mBAAmB,CAAC,GAAmB,EAAE,OAAe,EAAE,QAAiB;IACzF,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QACb,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,iBAAiB,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAmC,EAAE,CAAC,CAAC,CAAC,CAAC;IACpH,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC;AACH,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type { A2hResponse, GetMessageBody, JsonObject } from "./wire.js";
2
+ /** The env var carrying the agent's 32-byte state-seal key (base64url). Agent-runtime-provisioned,
3
+ * Hub-invisible, distinct from the bearer, and never embedded in `state` (spec §9.3). */
4
+ export declare const STATE_SEAL_KEY_ENV = "MA2H_STATE_SEAL_KEY";
5
+ /** Read + validate the agent state-seal key from the env. Returns undefined when unset; throws a
6
+ * usage error (exit 2) when set but not exactly 32 bytes. */
7
+ export declare function readSealKey(): Buffer | undefined;
8
+ /** Wrap an agent-owned resume object as a sealed envelope `state` value (`{ sealed: "MA2HSEALv1.…" }`).
9
+ * The seal key is Hub-invisible (spec §9.3). */
10
+ export declare function sealResumeState(resume: JsonObject, key: Buffer): JsonObject;
11
+ /** Open the agent-owned sealed `state` round-tripped in a Response (`{ sealed: "MA2HSEALv1.…" }`).
12
+ * Returns undefined when the Response carries no sealed state. Throws on tamper / wrong key. */
13
+ export declare function openSealedState(response: A2hResponse, key: Buffer): JsonObject | undefined;
14
+ /** Await-side opener shared by `ask await` / `task await`: when the resolved message carries a
15
+ * sealed `state` AND `MA2H_STATE_SEAL_KEY` is set, open it. Returns undefined when there is no
16
+ * sealed blob or the key is unset (opening is opt-in — a missing key is not an error). Only the
17
+ * presence of a sealed blob makes the key relevant, so the key is read *after* confirming there is
18
+ * something to open: a set-but-malformed key then fails only the awaits that actually need to open
19
+ * state (a usage error, exit 2) instead of breaking every await. An open failure on a present
20
+ * blob (tamper / wrong key) is a generic error (non-zero) rather than a silent drop of the resume
21
+ * context. (This defers the key check the scripts layer does eagerly — a deliberate CLI nicety.) */
22
+ export declare function openResumeState(body: GetMessageBody): JsonObject | undefined;
23
+ /** Submit-side resolver shared by notify / ask / task: parse the `--state` flag (must be a JSON
24
+ * object), require the seal key, and return the sealed envelope `state`. Returns undefined when
25
+ * `--state` is absent. A non-object payload or a missing key is a usage error (exit 2). */
26
+ export declare function resolveState(raw: string | undefined): JsonObject | undefined;