@oh-hai/cli 0.4.3 → 0.4.5

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 (36) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/auth/token-store.d.ts +17 -0
  3. package/dist/auth/token-store.js +21 -0
  4. package/dist/auth/token-store.js.map +1 -1
  5. package/dist/commands/fleet.js +37 -29
  6. package/dist/commands/fleet.js.map +1 -1
  7. package/dist/commands/handlers.js +2 -0
  8. package/dist/commands/handlers.js.map +1 -1
  9. package/dist/commands/login.js +83 -3
  10. package/dist/commands/login.js.map +1 -1
  11. package/dist/commands/mail/http.d.ts +47 -0
  12. package/dist/commands/mail/http.js +106 -0
  13. package/dist/commands/mail/http.js.map +1 -0
  14. package/dist/commands/mail/identity.d.ts +15 -0
  15. package/dist/commands/mail/identity.js +135 -0
  16. package/dist/commands/mail/identity.js.map +1 -0
  17. package/dist/commands/mail.d.ts +2 -0
  18. package/dist/commands/mail.js +232 -0
  19. package/dist/commands/mail.js.map +1 -0
  20. package/dist/commands/messaging/capability.d.ts +0 -1
  21. package/dist/commands/messaging/capability.js +12 -13
  22. package/dist/commands/messaging/capability.js.map +1 -1
  23. package/dist/commands/messaging/http.d.ts +9 -1
  24. package/dist/commands/messaging/http.js +2 -2
  25. package/dist/commands/messaging/http.js.map +1 -1
  26. package/dist/commands/messaging/sessions-http.d.ts +16 -3
  27. package/dist/commands/messaging/sessions-http.js +15 -4
  28. package/dist/commands/messaging/sessions-http.js.map +1 -1
  29. package/dist/commands/registry.js +27 -0
  30. package/dist/commands/registry.js.map +1 -1
  31. package/dist/commands/whoami.d.ts +14 -1
  32. package/dist/commands/whoami.js +36 -5
  33. package/dist/commands/whoami.js.map +1 -1
  34. package/dist/help.js +1 -0
  35. package/dist/help.js.map +1 -1
  36. package/package.json +2 -2
@@ -0,0 +1,106 @@
1
+ // Hub calls for the `oh-hai mail` surface — the account owner's own inbox (issue #874).
2
+ //
3
+ // Every function here takes a HUMAN session bearer. Nothing in this module decides whether the
4
+ // caller is entitled to use it: that is `identity.ts`'s pre-flight gate, which runs before any of
5
+ // these are reached. Re-checking here would invite the response-inspection shape the gate exists to
6
+ // avoid — see that module's header.
7
+ //
8
+ // Status mapping, transport errors, and the error envelope are REUSED from the messaging layer
9
+ // rather than reimplemented: a second copy of the status table drifts, and a drifted copy maps a
10
+ // 404 or a 409 to the wrong exit code, which is exactly the machine contract callers branch on.
11
+ import { throwTransportError } from "../http.js";
12
+ import { readErrorEnvelope, readJson, redactToken, requestTimeoutMs, statusToCliError } from "../messaging/http.js";
13
+ import { redactDeep } from "../messaging/sessions-http.js";
14
+ import { CliError } from "../../envelope.js";
15
+ function isMailRow(v) {
16
+ const r = v;
17
+ return typeof r === "object" && r !== null && typeof r.id === "string" && typeof r.type === "string" && typeof r.status === "string";
18
+ }
19
+ async function get(ctx, token, path) {
20
+ const url = `${ctx.config.baseUrl}${path}`;
21
+ let res;
22
+ try {
23
+ res = await ctx.runtime.fetchImpl(url, {
24
+ method: "GET",
25
+ headers: { Authorization: `Bearer ${token}` },
26
+ signal: AbortSignal.timeout(requestTimeoutMs(ctx)),
27
+ });
28
+ }
29
+ catch (error) {
30
+ throwTransportError(error, ctx.config.baseUrl);
31
+ }
32
+ if (res.status < 200 || res.status >= 300) {
33
+ const { code, message } = await readErrorEnvelope(res);
34
+ throw statusToCliError(res.status, redactToken(message, token), code);
35
+ }
36
+ // REDACT THE BEARER FROM THE SUCCESS BODY TOO — not just from error messages (§5.4).
37
+ // A buggy or hostile Hub can echo the Authorization value into any field, and this surface
38
+ // serializes rows VERBATIM under `--json` (deliberately, so a multi-line body survives) — so an
39
+ // echoed bearer would land on stdout intact. The sibling `listMessages` already round-trips its
40
+ // rows through the same redactor; the human inbox path must not be the one that skips it.
41
+ // Fails CLOSED: a re-parse failure raises a server error rather than returning un-redacted data.
42
+ return redactDeep(await readJson(res), token, "message");
43
+ }
44
+ /** List the account inbox — full message bodies, not an index (see MailRow). `view` selects the
45
+ * triage feed; `archived` is an explicit opt-in. */
46
+ export async function listInbox(ctx, token, opts) {
47
+ const query = new URLSearchParams();
48
+ if (opts.limit !== undefined)
49
+ query.set("limit", String(opts.limit));
50
+ if (opts.offset !== undefined)
51
+ query.set("offset", String(opts.offset));
52
+ // Only the archived view carries a param; `active` is the absence of one.
53
+ if (opts.view === "archived")
54
+ query.set("archived", "true");
55
+ const suffix = query.toString();
56
+ const body = await get(ctx, token, `/v1/messages${suffix ? `?${suffix}` : ""}`);
57
+ const messages = body.messages;
58
+ if (!Array.isArray(messages) || !messages.every(isMailRow)) {
59
+ // A malformed body is a SERVER error, never a silently empty inbox — an empty table would read
60
+ // as "nothing to triage" and hide a Hub or proxy regression behind good news.
61
+ throw new CliError("server", "the Hub returned a malformed message list (expected { messages: [{ id, type, status }] }).");
62
+ }
63
+ return messages;
64
+ }
65
+ /** Pull one message by id.
66
+ *
67
+ * VALIDATED LIKE THE LIST PATH, and for the same reason. `readJson` collapses a malformed 2xx —
68
+ * non-JSON, an array, `{}` — to an empty object, so an unvalidated detail read would exit 0 and
69
+ * print `{ message: {} }` (or a row of em dashes), presenting a Hub/proxy contract failure as a
70
+ * successful read of an empty message. The list path already refuses that; leaving the detail path
71
+ * permissive made the two disagree about what a valid response is. */
72
+ export async function readMessage(ctx, token, id) {
73
+ const body = await get(ctx, token, `/v1/messages/${encodeURIComponent(id)}`);
74
+ if (!isMailRow(body)) {
75
+ throw new CliError("server", "the Hub returned a malformed message (expected { id, type, status }).");
76
+ }
77
+ // AND IT MUST BE THE MESSAGE THAT WAS ASKED FOR. A well-shaped body for a DIFFERENT id passes the
78
+ // shape check and would exit 0 having shown the wrong message — the caller then archives, quotes
79
+ // or acts on something they never requested. The agent-side `pollMessage` validates this echo for
80
+ // the same reason; a shape check alone is not an identity check.
81
+ if (body.id !== id) {
82
+ throw new CliError("server", "the Hub returned a different message than the one requested.");
83
+ }
84
+ return body;
85
+ }
86
+ /** Apply one triage verb to one message. Returns nothing — the Hub answers 2xx with no body worth
87
+ * rendering, and inventing one would imply state this command did not read back. */
88
+ export async function triage(ctx, token, id, verb) {
89
+ const url = `${ctx.config.baseUrl}/v1/messages/${encodeURIComponent(id)}/${verb}`;
90
+ let res;
91
+ try {
92
+ res = await ctx.runtime.fetchImpl(url, {
93
+ method: "POST",
94
+ headers: { Authorization: `Bearer ${token}` },
95
+ signal: AbortSignal.timeout(requestTimeoutMs(ctx)),
96
+ });
97
+ }
98
+ catch (error) {
99
+ throwTransportError(error, ctx.config.baseUrl);
100
+ }
101
+ if (res.status < 200 || res.status >= 300) {
102
+ const { code, message } = await readErrorEnvelope(res);
103
+ throw statusToCliError(res.status, redactToken(message, token), code);
104
+ }
105
+ }
106
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/commands/mail/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { CommandContext } from "../context.js";
2
+ /** The human identity a `mail` command runs as. */
3
+ export interface MailIdentity {
4
+ /** The account owner's session bearer. */
5
+ token: string;
6
+ /** The `<userId>` half of the stored `human:<userId>` id — never the raw stored key. */
7
+ userId: string;
8
+ }
9
+ /**
10
+ * Resolve the human identity for a `mail` command, or refuse.
11
+ *
12
+ * Called before any request is composed. Throws rather than returning a discriminated result: a
13
+ * caller cannot then forget to branch, which is the failure this whole module exists to prevent.
14
+ */
15
+ export declare function resolveMailIdentity(ctx: CommandContext): Promise<MailIdentity>;
@@ -0,0 +1,135 @@
1
+ // Identity resolution for the `oh-hai mail` surface — the account OWNER's own inbox (issue #874).
2
+ //
3
+ // WHY THE REFUSAL IS PRE-FLIGHT, AND WHY IT CANNOT BE A RESPONSE CHECK.
4
+ // `GET /v1/messages` is DUAL-AUDIENCE: with a human session bearer it returns the account-wide
5
+ // inbox; with an AGENT bearer it SUCCEEDS and returns that agent's own submitted messages. It does
6
+ // not error for the wrong audience — it returns different data with the same shape and the same
7
+ // 200. So a guard that inspects the RESPONSE is unsound by construction, and the sibling
8
+ // agent-side guard in `messaging/http.ts` says exactly that in its own comment:
9
+ //
10
+ // a heuristic, not a proof of the agent branch: a single-agent or empty account has no
11
+ // foreign row to trip on
12
+ //
13
+ // That hole is widest precisely where it matters most — a fresh or single-agent account — and its
14
+ // failure mode is silent: a plausible, well-formed, WRONG inbox with nothing signalling it.
15
+ // Deciding from the RESOLVED IDENTITY, before a request is composed, has no such hole: an agent
16
+ // identity never reaches the network here at all. Every `mail` command therefore calls
17
+ // `resolveMailIdentity` FIRST, and nothing in this module consults response data.
18
+ //
19
+ // FAIL CLOSED. An unresolvable identity is refused as not-human rather than attempted
20
+ // unauthenticated — "I could not tell" is never permission to send.
21
+ import { resolveIdentity } from "../../auth/resolve-token.js";
22
+ import { humanUserId, isHumanId } from "../../auth/token-store.js";
23
+ import { CliError } from "../../envelope.js";
24
+ import { sanitizeForTerminal } from "../messaging/shared.js";
25
+ import { classifyProbeStatus, probeGet, readActor, readJsonBody } from "../whoami.js";
26
+ /** How to get one, appended to every refusal so the error is actionable rather than a diagnosis. */
27
+ const REMEDY = "`oh-hai mail` reads YOUR account inbox, so it needs a human identity: run `oh-hai login --human` " +
28
+ "(or select one with --account human:<id>). To read an agent's own submitted messages instead, use `oh-hai messages list`.";
29
+ /**
30
+ * Resolve the human identity for a `mail` command, or refuse.
31
+ *
32
+ * Called before any request is composed. Throws rather than returning a discriminated result: a
33
+ * caller cannot then forget to branch, which is the failure this whole module exists to prevent.
34
+ */
35
+ export async function resolveMailIdentity(ctx) {
36
+ // FIRST, AND BEFORE THE ID IS EVEN CONSULTED: refuse an env bearer.
37
+ //
38
+ // `MA2H_AGENT_TOKEN` is an AGENT credential by name and by contract, and — the part that makes
39
+ // this a hole rather than a nicety — it is NOT BOUND TO THE CONFIGURED ACCOUNT. `resolveIdentity`
40
+ // returns it verbatim alongside whatever `--account` says (the same unbinding `requireWatchIdentity`
41
+ // documents for the drain lock). So without this arm:
42
+ //
43
+ // MA2H_AGENT_TOKEN=<agent bearer> oh-hai mail list --account human:usr_1
44
+ //
45
+ // presents a human-SHAPED id with an AGENT token behind it. The id test below would pass, the
46
+ // request would go out, and the Hub would answer with that agent's own submissions — the exact
47
+ // plausible-but-wrong inbox this module exists to prevent, reached through the env path instead
48
+ // of the store. A human identity must be STORE-resolved, where the token was looked up BY the
49
+ // human id and the two are therefore bound.
50
+ if (ctx.config.tokenSource === "env") {
51
+ throw new CliError("usage", "MA2H_AGENT_TOKEN is set, and it is an AGENT credential that is not bound to --account — it cannot be used as a human identity. " +
52
+ "Unset it and run `oh-hai login --human`, so the session token is stored under (and resolved by) your human id.");
53
+ }
54
+ const store = await ctx.runtime.openStore();
55
+ const { account, token } = await resolveIdentity(ctx.config, store);
56
+ // Order matters: classify the IDENTITY before asking whether a token exists. An agent identity
57
+ // that happens to have no stored token must still read as "wrong identity", not as "log in" —
58
+ // sending the caller to re-login would have them authenticate as the agent again and arrive back
59
+ // here, having been told the wrong thing twice.
60
+ if (account === undefined) {
61
+ throw new CliError("usage", `no identity is selected, so this command cannot tell whose inbox to read. ${REMEDY}`);
62
+ }
63
+ if (!isHumanId(account)) {
64
+ // SANITIZE THE ID BEFORE IT REACHES A TERMINAL. A device-code login stores the agent id the HUB
65
+ // chose, verbatim — `runDeviceCodeFlow` accepts any non-empty `agent_id` — so a custom or
66
+ // compromised Hub can plant control characters in it, and this message goes straight to stderr.
67
+ // The login and whoami display paths already sanitize for exactly this; the table rendering in
68
+ // mail.ts does too. The ERROR path was the one place still interpolating it raw.
69
+ throw new CliError("usage", `the selected identity "${sanitizeForTerminal(account)}" is an AGENT, not a human. ${REMEDY}`);
70
+ }
71
+ if (token === undefined) {
72
+ throw new CliError("auth", `no stored token for "${sanitizeForTerminal(account)}" — run \`oh-hai login --human\`.`);
73
+ }
74
+ // `humanUserId` re-tests the prefix rather than slicing on the strength of the check above, so
75
+ // this stays correct if the two are ever separated.
76
+ const userId = humanUserId(account);
77
+ if (userId === undefined || userId === "") {
78
+ throw new CliError("usage", `the selected identity "${sanitizeForTerminal(account)}" carries no user id. ${REMEDY}`);
79
+ }
80
+ // ATTEST THE CREDENTIAL. The checks above establish that the stored ID claims to be human; they
81
+ // cannot establish that the TOKEN is. The `human:` prefix is only reserved as of this change, and
82
+ // it never was on the server: `MA2H_AGENT_TOKENS` splits on `=` with no reserved-prefix rule, and
83
+ // an older CLI accepted `login --token-stdin --account human:bot` with any bearer. So a
84
+ // credential stored BEFORE this change — or configured on the Hub — can carry a human-shaped id
85
+ // over an AGENT token, and every local check would wave it through.
86
+ //
87
+ // This is the same rule as the two refusals above, applied honestly: an id-based check is sound
88
+ // only where the id IMPLIES the credential. History means it does not, so ask the Hub who this
89
+ // token actually is and believe THAT.
90
+ //
91
+ // ORDERING IS DELIBERATE. The local refusals run FIRST, so the common wrong-identity case still
92
+ // costs no network call at all and the "an agent identity never reaches the network" property
93
+ // holds for it. This probe runs only for a credential that already looks human, and it targets
94
+ // `/auth/whoami` — an identity endpoint that discloses nothing about the inbox — so the INBOX is
95
+ // still never touched by a non-human caller.
96
+ const res = await probeGet(ctx, "/auth/whoami", token);
97
+ if (res.status !== 200)
98
+ classifyProbeStatus(res.status);
99
+ const actor = readActor(await readJsonBody(res));
100
+ if (actor === undefined) {
101
+ throw new CliError("server", "the Hub did not attest an identity for this credential (200 without an actor); refusing rather than assuming it is yours.");
102
+ }
103
+ // NEVER ECHO AN ACTOR THAT CONTAINS THE CREDENTIAL. `sanitizeForTerminal` strips controls; it does
104
+ // not redact a PRINTABLE bearer. A Hub answering `agent:<the-token>` would otherwise have its
105
+ // actor interpolated into the refusal below and written to stderr — the §5.4 never-print contract
106
+ // broken by the very check that exists to protect the surface. The login arm refuses this exact
107
+ // shape; adding the probe here without the same refusal reproduced the hole one file over.
108
+ if (actor.includes(token)) {
109
+ throw new CliError("server", "the Hub echoed the presented credential back inside its identity response; refusing rather than reporting it.");
110
+ }
111
+ // THE ATTESTED ACTOR MUST BE **THIS** IDENTITY, not merely *a* human one.
112
+ //
113
+ // "Is it human?" is not the question. An older CLI accepted `--token-stdin --account human:A` with
114
+ // ANY bearer, so a store can hold human B's valid session token under the key `human:A`. The Hub
115
+ // then attests `human:B` — genuinely human — and a human-only check passes, after which
116
+ // `--account human:A` lists, reads and ARCHIVES B's inbox while every message claims to run as A.
117
+ // A cross-account read, reached through a check that looked like it had closed the hole.
118
+ //
119
+ // This is the third variant of one rule in this PR, and the general form is the one to hold:
120
+ // the credential must be attested as the EXACT identity being acted on, not as a member of the
121
+ // right class.
122
+ if (actor !== account) {
123
+ throw new CliError("usage", `the stored credential under "${sanitizeForTerminal(account)}" is attested by the Hub as a DIFFERENT identity. ` +
124
+ "It was stored before the `human:` prefix was reserved, or under the wrong key. Re-run `oh-hai login --human` to replace it — " +
125
+ "the attested id is not shown here because it is not yours to learn from a mismatched key.");
126
+ }
127
+ // NOTE: no separate "is the actor human?" arm here, deliberately. The equality check above already
128
+ // implies it — `account` passed `isHumanId` earlier, and `actor` must equal `account` — so such a
129
+ // branch could never execute. Leaving one would imply a live guard that is doing nothing, which is
130
+ // how a later reader concludes the class is covered when the equality check is what covers it.
131
+ // Equal to `account` by the check above; read from the ATTESTED value so the Hub remains the
132
+ // source of truth rather than the local key.
133
+ return { token, userId: humanUserId(actor) ?? userId };
134
+ }
135
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../../src/commands/mail/identity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { CommandContext } from "./context.js";
2
+ export declare function mailCommand(ctx: CommandContext): Promise<void | number>;
@@ -0,0 +1,232 @@
1
+ // `oh-hai mail` (cli spec §4.16) — the ACCOUNT OWNER's own inbox, read and triaged from a terminal
2
+ // (issue #874).
3
+ //
4
+ // A SEPARATE VERB FROM `inbox`, DELIBERATELY. `oh-hai inbox` is the AGENT's mailbox drain (the
5
+ // human→agent leg). Dispatching one noun on the resolved identity would put the human/agent
6
+ // decision in a runtime branch — the same shape as the failure this surface is built to prevent —
7
+ // so the two are separate namespaces instead, and a wrong identity reaches a command that cannot
8
+ // serve it at all.
9
+ //
10
+ // EVERY SUBCOMMAND RESOLVES ITS IDENTITY FIRST. `resolveMailIdentity` refuses an agent identity
11
+ // before a request is composed; nothing here inspects a response to decide entitlement. See
12
+ // `mail/identity.ts` for why the response cannot answer that question.
13
+ //
14
+ // Output posture: under `--json` rows keep their STRUCTURE and their newlines (a multi-line body
15
+ // must survive, which is why they are not run through `sanitizeForTerminal` — that strips C0 and
16
+ // would destroy them), while the human table renders only single-line index fields, each
17
+ // `sanitizeForTerminal`'d against a hostile Hub.
18
+ //
19
+ // `JSON.stringify` IS NOT A SANITIZER, and an earlier revision of this comment claimed it was:
20
+ // it escapes the C0 range but passes **C1** (U+0080–U+009F, including U+009B CSI) and **DEL**
21
+ // through RAW. A JSON envelope printed into a terminal therefore carries a live injection vector.
22
+ // `agents list --json` already strips these; this surface must too — see `stripJsonUnsafeControls`.
23
+ import { buildOk, CliError, serializeEnvelope } from "../envelope.js";
24
+ import { ExitCode } from "../exit-codes.js";
25
+ import { flagOnStrict, parseCommandArgs } from "./flags.js";
26
+ import { resolveMailIdentity } from "./mail/identity.js";
27
+ import { listInbox, readMessage, triage } from "./mail/http.js";
28
+ import { nonNegativeInt, positiveInt, sanitizeForTerminal, stringFlag } from "./messaging/shared.js";
29
+ const SUBCOMMANDS = ["list", "read", "archive", "unarchive"];
30
+ const OPTIONS = {
31
+ limit: { type: "string" },
32
+ offset: { type: "string" },
33
+ archived: { type: "boolean" },
34
+ };
35
+ const DEFAULT_LIMIT = 50;
36
+ /** The Hub's server-side clamps on `GET /v1/messages`. Mirrored so the paging hint is computed
37
+ * against the limit/offset the Hub ACTUALLY applied — a request above these is clamped server-side,
38
+ * so a full clamped page compared against the raw request would wrongly report "no more" and a
39
+ * scripted pager would stop early. Mirrors `messages.ts`. */
40
+ const LIST_LIMIT_MAX = 200;
41
+ const LIST_OFFSET_MAX = 10_000;
42
+ /** A parsing ceiling — an absurd `--limit`/`--offset` is a usage error (exit 2) before any call. */
43
+ const MAX_LIST_INT = 1_000_000;
44
+ /** Strip the control characters `JSON.stringify` does NOT escape — **C1** (U+0080–U+009F, e.g.
45
+ * U+009B CSI) and **DEL** (U+007F) — from every string in a value, recursively.
46
+ *
47
+ * Deliberately NOT `sanitizeForTerminal`: that also strips C0, which includes the newlines a
48
+ * message body legitimately contains, and preserving those is the entire reason this surface emits
49
+ * rows with their structure intact. C0 needs no stripping here because `JSON.stringify` escapes it
50
+ * (`\n`, `\u001b`), so it can never reach a terminal as a control. C1 and DEL are the gap. */
51
+ function stripJsonUnsafeControls(value) {
52
+ if (typeof value === "string") {
53
+ let out = "";
54
+ for (const ch of value) {
55
+ const code = ch.codePointAt(0) ?? 0;
56
+ if (code !== 0x7f && !(code >= 0x80 && code <= 0x9f))
57
+ out += ch;
58
+ }
59
+ return out;
60
+ }
61
+ if (Array.isArray(value))
62
+ return value.map((v) => stripJsonUnsafeControls(v));
63
+ if (value !== null && typeof value === "object") {
64
+ const o = {};
65
+ for (const [k, v] of Object.entries(value)) {
66
+ o[stripJsonUnsafeControls(k)] = stripJsonUnsafeControls(v);
67
+ }
68
+ return o;
69
+ }
70
+ return value;
71
+ }
72
+ const EM_DASH = "—";
73
+ export async function mailCommand(ctx) {
74
+ // Operands: the subcommand plus, for the triage verbs, one or more message ids.
75
+ const parsed = parseCommandArgs(ctx.argv, OPTIONS, { maxOperands: 64 });
76
+ const sub = parsed.positionals[1];
77
+ if (sub === undefined) {
78
+ throw new CliError("usage", `missing subcommand: expected one of ${SUBCOMMANDS.join(" | ")}.`);
79
+ }
80
+ if (!SUBCOMMANDS.includes(sub)) {
81
+ throw new CliError("usage", `unknown subcommand "${sub}": expected one of ${SUBCOMMANDS.join(" | ")}.`);
82
+ }
83
+ const ids = parsed.positionals.slice(2);
84
+ const flags = parsed.values;
85
+ // Flags are validated BEFORE credentials are touched (matches notify/messages): a bad invocation
86
+ // is exit 2 without opening the keychain.
87
+ const requestedLimit = flags.limit !== undefined ? positiveInt(stringFlag(flags.limit), "--limit", "a positive integer", MAX_LIST_INT) : DEFAULT_LIMIT;
88
+ const requestedOffset = flags.offset !== undefined ? nonNegativeInt(stringFlag(flags.offset), "--offset", "a non-negative integer", MAX_LIST_INT) : undefined;
89
+ // ARITY FIRST — before the credential lookup, not after.
90
+ // A malformed invocation (`mail read` with no id) is a USAGE error, and reporting it as an
91
+ // identity/auth error on a machine with no human identity selected sends the caller to fix the
92
+ // wrong thing entirely. It also opened — and could prompt — the OS keychain for an invocation
93
+ // that was never going to make a request. This does NOT weaken the gate below: arity is a purely
94
+ // local check, so the identity refusal still precedes every Hub call.
95
+ // `list` takes NO operands. Without this, `mail list message_1` opened credentials, contacted the
96
+ // Hub, and returned the WHOLE inbox while silently ignoring the argument — a caller who meant
97
+ // `mail read message_1` gets a plausible result for a question they did not ask.
98
+ if (sub === "list" && ids.length !== 0) {
99
+ throw new CliError("usage", "`oh-hai mail list` takes no message ids — did you mean `oh-hai mail read <id>`?");
100
+ }
101
+ if (sub === "read" && ids.length !== 1) {
102
+ throw new CliError("usage", "`oh-hai mail read` takes exactly one message id.");
103
+ }
104
+ if ((sub === "archive" || sub === "unarchive") && ids.length === 0) {
105
+ throw new CliError("usage", `\`oh-hai mail ${sub}\` takes one or more message ids.`);
106
+ }
107
+ // THE GATE. Before any request is composed, for every subcommand.
108
+ const { token } = await resolveMailIdentity(ctx);
109
+ if (sub === "list") {
110
+ // `flagOnStrict`, not `flagOn` and certainly not `=== true`. Two failure modes, one shape:
111
+ // under `strict: false` the supported `--archived=true` form arrives as the STRING "true", so an
112
+ // identity test reads it as off; and a TYPO (`--archived=treu`) is an unrecognized value that
113
+ // `flagOn` also reads as off. Both silently serve the ACTIVE inbox for an explicit request for
114
+ // the archived one — a plausible wrong view rather than an error, which is the failure this
115
+ // whole surface is built to avoid. `flagOnStrict` exists for exactly this: a value it does not
116
+ // recognize is a usage error, not a silent "off".
117
+ await mailList(ctx, token, requestedLimit, requestedOffset, flagOnStrict(flags.archived, "--archived"));
118
+ return;
119
+ }
120
+ if (sub === "read") {
121
+ await mailRead(ctx, token, ids[0]);
122
+ return;
123
+ }
124
+ return mailTriage(ctx, token, sub, ids);
125
+ }
126
+ async function mailList(ctx, token, requestedLimit, requestedOffset, archived) {
127
+ const limit = Math.min(requestedLimit, LIST_LIMIT_MAX);
128
+ const offset = requestedOffset !== undefined ? Math.min(requestedOffset, LIST_OFFSET_MAX) : undefined;
129
+ const view = archived ? "archived" : "active";
130
+ const messages = await listInbox(ctx, token, { limit, ...(offset !== undefined ? { offset } : {}), view });
131
+ const effectiveOffset = offset ?? 0;
132
+ const nextOffset = Math.min(effectiveOffset + messages.length, LIST_OFFSET_MAX);
133
+ const hasMore = messages.length === limit && effectiveOffset < LIST_OFFSET_MAX;
134
+ if (ctx.json) {
135
+ ctx.io.log(serializeEnvelope(buildOk("mail.list", stripJsonUnsafeControls({ messages, count: messages.length, view, has_more: hasMore, next_offset: nextOffset }))));
136
+ return;
137
+ }
138
+ if (messages.length === 0) {
139
+ ctx.io.log(archived ? "No archived messages." : "No messages.");
140
+ return;
141
+ }
142
+ const rows = messages.map(toRow);
143
+ const display = [{ id: "ID", type: "TYPE", status: "STATUS", title: "TITLE", created: "CREATED" }, ...rows];
144
+ const idW = colWidth(display, "id");
145
+ const typeW = colWidth(display, "type");
146
+ const statusW = colWidth(display, "status");
147
+ const titleW = colWidth(display, "title");
148
+ for (const r of display) {
149
+ ctx.io.log(`${r.id.padEnd(idW)} ${r.type.padEnd(typeW)} ${r.status.padEnd(statusW)} ${r.title.padEnd(titleW)} ${r.created}`);
150
+ }
151
+ if (hasMore)
152
+ ctx.io.err(`(more — re-run with --offset ${nextOffset})`);
153
+ }
154
+ async function mailRead(ctx, token, id) {
155
+ const message = await readMessage(ctx, token, id);
156
+ if (ctx.json) {
157
+ ctx.io.log(serializeEnvelope(buildOk("mail.read", stripJsonUnsafeControls({ message }))));
158
+ return;
159
+ }
160
+ // The human view prints the index fields plus the body, each sanitized. The body is the one
161
+ // multi-line field, so it is printed last and on its own lines rather than squeezed into a table.
162
+ const row = toRow(message);
163
+ ctx.io.log(`${row.id} ${row.type} ${row.status}`);
164
+ ctx.io.log(`title: ${row.title}`);
165
+ ctx.io.log(`created: ${row.created}`);
166
+ const body = typeof message.body === "string" ? message.body : undefined;
167
+ if (body !== undefined) {
168
+ ctx.io.log("");
169
+ for (const line of body.split("\n"))
170
+ ctx.io.log(sanitizeForTerminal(line));
171
+ }
172
+ }
173
+ async function mailTriage(ctx, token, verb, ids) {
174
+ if (ids.length === 0)
175
+ throw new CliError("usage", `\`oh-hai mail ${verb}\` takes one or more message ids.`);
176
+ // Per-id outcome, deliberately: collapsing a partial failure into one status would report a batch
177
+ // as failed when most of it succeeded, and leave the caller unable to tell which ids to retry.
178
+ const results = [];
179
+ for (const id of ids) {
180
+ try {
181
+ await triage(ctx, token, id, verb);
182
+ results.push({ id, ok: true });
183
+ }
184
+ catch (error) {
185
+ // ONLY A GENUINELY PER-MESSAGE CONDITION STAYS PER-ID — an ALLOWLIST, not a denylist.
186
+ //
187
+ // An earlier revision rethrew just `auth` and `network`, which fixed two cells of a family and
188
+ // left the rest: a timeout still collapsed to exit 1 instead of the timeout tier, a Hub 5xx
189
+ // lost the server tier, and the loop re-attempted every id against a condition that could not
190
+ // improve. The failure classes are open-ended, so enumerating the batch-wide ones can never be
191
+ // complete; enumerating the PER-ID ones can, because they are the only classes that say
192
+ // something about THIS message rather than about the request as a whole.
193
+ // The allowlist, spelled with the §7 codes that actually exist: a message that is absent, in
194
+ // a conflicting state, or named by a request the Hub rejected as malformed. Everything else —
195
+ // auth, network, server, timeout, and every code not yet invented — is about the request as a
196
+ // whole and stops the batch.
197
+ const PER_MESSAGE = new Set(["not_found", "conflict", "bad_request", "validation_error"]);
198
+ const perMessage = error instanceof CliError && PER_MESSAGE.has(error.code);
199
+ if (!perMessage)
200
+ throw error;
201
+ results.push({ id, ok: false, error: error instanceof Error ? error.message : String(error) });
202
+ }
203
+ }
204
+ const failed = results.filter((r) => !r.ok);
205
+ if (ctx.json) {
206
+ ctx.io.log(serializeEnvelope(buildOk(`mail.${verb}`, stripJsonUnsafeControls({ results, count: results.length, failed: failed.length }))));
207
+ }
208
+ else {
209
+ for (const r of results) {
210
+ if (r.ok)
211
+ ctx.io.log(`${verb}d ${sanitizeForTerminal(r.id)}`);
212
+ else
213
+ ctx.io.err(`FAILED ${sanitizeForTerminal(r.id)}: ${sanitizeForTerminal(r.error ?? "unknown error")}`);
214
+ }
215
+ }
216
+ // RETURN a non-zero code rather than THROWING, because stdout is already committed.
217
+ // Throwing here would make the dispatcher emit a SECOND `--json` envelope after the ok one, so a
218
+ // consumer reading the single-envelope contract (§8) gets two contradictory documents and
219
+ // commonly fails to parse — and the per-id results, the whole reason for reporting them, are
220
+ // absent from the error envelope that would arrive last. `doctor` returns its code for exactly
221
+ // this reason; the handler contract is `Promise<void | number>`.
222
+ return failed.length > 0 ? ExitCode.ERROR : ExitCode.SUCCESS;
223
+ }
224
+ /** Project a Hub row to sanitized, single-line index fields; absent fields render as an em dash. */
225
+ function toRow(m) {
226
+ const one = (v) => (typeof v === "string" && v !== "" ? sanitizeForTerminal(v) : EM_DASH);
227
+ return { id: one(m.id), type: one(m.type), status: one(m.status), title: one(m.title), created: one(m.created_at) };
228
+ }
229
+ function colWidth(rows, key) {
230
+ return rows.reduce((w, r) => Math.max(w, r[key].length), 0);
231
+ }
232
+ //# sourceMappingURL=mail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail.js","sourceRoot":"","sources":["../../src/commands/mail.ts"],"names":[],"mappings":"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"}
@@ -29,7 +29,6 @@ export interface HubCapability {
29
29
  min_ttl_seconds?: number;
30
30
  max_ttl_seconds?: number;
31
31
  max_live_per_agent?: number;
32
- agent_list_visibility?: boolean;
33
32
  terminal_retention_seconds?: number;
34
33
  };
35
34
  inter_agent?: {
@@ -75,20 +75,19 @@ export async function fetchCapability(ctx) {
75
75
  return undefined;
76
76
  }
77
77
  const doc = body;
78
- // `agent_list_visibility` is a GRANT and the only field a caller ACTS on `fleet ls` turns it into
79
- // the scope caption and into `data.scope` for scripts. A blind cast would read the STRING "false"
80
- // as truthy and report the whole fleet for a list the Hub never said was account-wide. Anything
81
- // that is not a real boolean is dropped, so it reads `unknown`: the honest answer for a document
82
- // that did not say.
78
+ // `sessions` is read for its `enabled` flag (the §8.7.1 session-drain gate below), so a value that
79
+ // is not an object is dropped rather than cast a non-object would make `sessions?.enabled` throw
80
+ // or silently read undefined off a string.
81
+ //
82
+ // The per-field `agent_list_visibility` sanitizer that used to live here is GONE with #860: it
83
+ // existed only because `fleet ls` ACTED on that field. The Hub still advertises it — it states the
84
+ // deployment ceiling — but it is only half the §16.4 answer, so the CLI reads the authenticated
85
+ // `scope` on `GET /v1/sessions` instead and ignores this field entirely. Nothing here reads it, so
86
+ // there is nothing to sanitize.
83
87
  const sessions = doc.sessions;
84
- if (sessions !== null && typeof sessions === "object" && !Array.isArray(sessions)) {
85
- const visibility = sessions.agent_list_visibility;
86
- if (visibility !== undefined && typeof visibility !== "boolean") {
87
- delete sessions.agent_list_visibility;
88
- }
89
- }
90
- else if (sessions !== undefined) {
91
- delete doc.sessions;
88
+ if (sessions === null || typeof sessions !== "object" || Array.isArray(sessions)) {
89
+ if (sessions !== undefined)
90
+ delete doc.sessions;
92
91
  }
93
92
  const capability = doc;
94
93
  CACHE.set(key, capability);
@@ -1 +1 @@
1
- {"version":3,"file":"capability.js","sourceRoot":"","sources":["../../../src/commands/messaging/capability.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"capability.js","sourceRoot":"","sources":["../../../src/commands/messaging/capability.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { type HubTouchpoint } from "@oh-hai/ma2h-core/errors";
2
2
  import { CliError } from "../../envelope.js";
3
- import type { CommandContext } from "../context.js";
3
+ import type { CommandContext, HttpResponse } from "../context.js";
4
4
  import type { GetMessageBody, InboundDelivery, SubmitAck } from "./wire.js";
5
5
  /** POST /v1/inbox/ack — the ids of the directives consumed by this call (spec §14; the server's
6
6
  * `ackDirectives` returns the id array, not a count). A TRANSPORT wrapper shape of this Hub's
@@ -67,3 +67,11 @@ export declare function ackInbox(ctx: CommandContext, token: string, ids: string
67
67
  * the vendored §8.5 vocabulary — see `cliErrorCode`. The server message is stripped of terminal
68
68
  * control chars (the top-level catch prints it). */
69
69
  export declare function statusToCliError(status: number, message: string | undefined, hubCode?: string, hint?: string, touchpoint?: HubTouchpoint): CliError;
70
+ /** Parse a response body as a JSON object, defensively — a fake without `json()`, a non-JSON
71
+ * body, or a non-object all collapse to `{}`, so callers read fields off a plain record. */
72
+ export declare function readJson(res: HttpResponse): Promise<Record<string, unknown>>;
73
+ /** Read the A2H error envelope `{ error: { code, message } }` from a non-2xx response. */
74
+ export declare function readErrorEnvelope(res: HttpResponse): Promise<{
75
+ code: string | undefined;
76
+ message: string | undefined;
77
+ }>;