@mnemom/mnemom 0.17.31 → 0.17.32-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/base-url-proxy.mjs +30 -0
  2. package/dist/commands/agent-fuel.d.ts +75 -0
  3. package/dist/commands/agent-fuel.js +298 -0
  4. package/dist/commands/agent-implicit-notice.d.ts +31 -0
  5. package/dist/commands/agent-implicit-notice.js +73 -0
  6. package/dist/commands/agent-session.d.ts +2 -1
  7. package/dist/commands/agent-session.js +2 -1
  8. package/dist/commands/agent-share-fixture.d.ts +20 -0
  9. package/dist/commands/agent-share-fixture.js +134 -0
  10. package/dist/commands/agent-share.d.ts +87 -0
  11. package/dist/commands/agent-share.js +251 -0
  12. package/dist/commands/agent.d.ts +75 -2
  13. package/dist/commands/agent.js +165 -28
  14. package/dist/commands/status.js +1 -1
  15. package/dist/commands/update.js +3 -1
  16. package/dist/commands/validate.d.ts +1 -1
  17. package/dist/commands/validate.js +1 -1
  18. package/dist/index.js +68 -12
  19. package/dist/lib/agent.d.ts +34 -19
  20. package/dist/lib/agent.js +41 -32
  21. package/dist/lib/api.d.ts +66 -0
  22. package/dist/lib/api.js +50 -10
  23. package/dist/lib/canonical-json.d.ts +12 -0
  24. package/dist/lib/canonical-json.js +22 -0
  25. package/dist/lib/commit-report.d.ts +109 -0
  26. package/dist/lib/commit-report.js +205 -0
  27. package/dist/lib/conduct-receipt.d.ts +81 -0
  28. package/dist/lib/conduct-receipt.js +177 -0
  29. package/dist/lib/contract-hash.d.ts +61 -0
  30. package/dist/lib/contract-hash.js +126 -0
  31. package/dist/lib/experimental.d.ts +10 -1
  32. package/dist/lib/experimental.js +35 -1
  33. package/dist/lib/oauth.d.ts +2 -1
  34. package/dist/lib/oauth.js +2 -1
  35. package/dist/lib/statusline-bin.d.ts +2 -2
  36. package/dist/lib/statusline-bin.js +4 -7
  37. package/dist/lib/statusline-feed.d.ts +33 -0
  38. package/dist/lib/statusline-feed.js +68 -0
  39. package/dist/lib/statusline.d.ts +39 -88
  40. package/dist/lib/statusline.js +58 -250
  41. package/dist/lib/update/decide.d.ts +22 -3
  42. package/dist/lib/update/decide.js +35 -12
  43. package/dist/lib/update/doctor.js +5 -1
  44. package/dist/lib/update/run.d.ts +2 -0
  45. package/dist/lib/update/run.js +22 -8
  46. package/package.json +1 -1
@@ -0,0 +1,134 @@
1
+ /**
2
+ * FIXTURE-ONLY statement source and card issuer for `mnemom agent share` (MNE-9037).
3
+ *
4
+ * Selected only when MNEMOM_AGENT_SHARE=1 AND MNEMOM_AGENT_SHARE_FIXTURE=1. It holds no key
5
+ * and signs nothing: the cards below are the two TEST cards committed in mnemom/aip
6
+ * (packages/verifier/test/fixtures/sharecard/{valid,period-revert}.json, key id
7
+ * test-sharecard-key-1) that the /share page's fixture verifier (MNE-9031) recognises. The
8
+ * issuer returns one of them only when the picked lines are exactly that card's lines; any
9
+ * other pick is an error. Two extra candidates (an estimate and an identifying label) exist so
10
+ * the refusal path can be shown. This is not a real issuer: a production source and signer
11
+ * are a separate, human-gated change.
12
+ */
13
+ export const AGENT_SHARE_FIXTURE_ENV = "MNEMOM_AGENT_SHARE_FIXTURE";
14
+ export function agentShareFixtureEnabled(env = process.env) {
15
+ return env.MNEMOM_AGENT_SHARE === "1" && env[AGENT_SHARE_FIXTURE_ENV] === "1";
16
+ }
17
+ export const FIXTURE_CARD_VALID = {
18
+ typ: "AAP-ShareCard/v1",
19
+ card_id: "card-valid-0001",
20
+ period: {
21
+ start: "2026-10-01",
22
+ end: "2026-10-07",
23
+ },
24
+ issued_at: "2026-10-08T00:00:00.000Z",
25
+ signing_key_id: "test-sharecard-key-1",
26
+ statements: [
27
+ {
28
+ typ: "AAP-Statement/v1",
29
+ hash: "1111111111111111111111111111111111111111111111111111111111111111",
30
+ },
31
+ {
32
+ typ: "AAP-Statement/v1",
33
+ hash: "2222222222222222222222222222222222222222222222222222222222222222",
34
+ },
35
+ ],
36
+ lines: [
37
+ {
38
+ id: "l1",
39
+ kind: "count",
40
+ label: "Changes reviewed",
41
+ value: 42,
42
+ statement_hash: "1111111111111111111111111111111111111111111111111111111111111111",
43
+ },
44
+ {
45
+ id: "l2",
46
+ kind: "count",
47
+ label: "Checks passed",
48
+ value: 40,
49
+ statement_hash: "2222222222222222222222222222222222222222222222222222222222222222",
50
+ },
51
+ ],
52
+ signature: "qsHSChAxBEnEyNoQQLQMFXZ6LKxruMyijxA5s3h9oPKl4OijJqxFm3QC0rteDXJsYwJCMeKyk_0xzGTDc3wDAQ",
53
+ };
54
+ export const FIXTURE_CARD_PERIOD_REVERT = {
55
+ typ: "AAP-ShareCard/v1",
56
+ card_id: "card-period-0001",
57
+ period: {
58
+ start: "2026-10-01",
59
+ end: "2026-10-07",
60
+ },
61
+ issued_at: "2026-10-08T00:00:00.000Z",
62
+ signing_key_id: "test-sharecard-key-1",
63
+ statements: [
64
+ {
65
+ typ: "AAP-Statement/v1",
66
+ hash: "1111111111111111111111111111111111111111111111111111111111111111",
67
+ },
68
+ {
69
+ typ: "AAP-Statement/v1",
70
+ hash: "2222222222222222222222222222222222222222222222222222222222222222",
71
+ },
72
+ {
73
+ typ: "AAP-Statement/v1",
74
+ hash: "3333333333333333333333333333333333333333333333333333333333333333",
75
+ },
76
+ ],
77
+ lines: [
78
+ {
79
+ id: "l1",
80
+ kind: "count",
81
+ label: "Changes reviewed",
82
+ value: 42,
83
+ statement_hash: "1111111111111111111111111111111111111111111111111111111111111111",
84
+ },
85
+ {
86
+ id: "l2",
87
+ kind: "count",
88
+ label: "Checks passed",
89
+ value: 40,
90
+ statement_hash: "2222222222222222222222222222222222222222222222222222222222222222",
91
+ },
92
+ {
93
+ id: "l3",
94
+ kind: "revert",
95
+ label: "Changes reverted",
96
+ value: 2,
97
+ statement_hash: "3333333333333333333333333333333333333333333333333333333333333333",
98
+ },
99
+ ],
100
+ signature: "OQf-4xgUQbIxOef_BsJeTehfpqLPPR2GTv8_y6EbxPX9UkFp2PpE2pnETh2VqcwDfV1U_4bSR8Pwrpsch3DwCw",
101
+ };
102
+ const FIXTURE_CARDS = [FIXTURE_CARD_VALID, FIXTURE_CARD_PERIOD_REVERT];
103
+ const HASH_E = "e".repeat(64);
104
+ const HASH_F = "f".repeat(64);
105
+ /** Real candidate lines plus two that must be refused. */
106
+ export const FIXTURE_CANDIDATES = [
107
+ ...FIXTURE_CARD_PERIOD_REVERT.lines,
108
+ {
109
+ id: "e1",
110
+ kind: "count",
111
+ label: "Hours saved",
112
+ value: 12,
113
+ statement_hash: HASH_E,
114
+ estimated: true,
115
+ },
116
+ { id: "r1", kind: "count", label: "Repo mnemom/aip", value: 3, statement_hash: HASH_F },
117
+ ];
118
+ const same = (a, b) => a.length === b.length &&
119
+ a.every((l) => {
120
+ const m = b.find((x) => x.id === l.id);
121
+ return m !== undefined && JSON.stringify(m) === JSON.stringify(l);
122
+ });
123
+ export const fixtureAgentShareParts = {
124
+ source: { listLines: async () => FIXTURE_CANDIDATES },
125
+ issuer: {
126
+ issue: async (lines) => {
127
+ const card = FIXTURE_CARDS.find((c) => same(c.lines, lines));
128
+ if (!card) {
129
+ throw new Error("fixture issuer only issues the two test cards: pick l1,l2 or l1,l2,l3");
130
+ }
131
+ return structuredClone(card);
132
+ },
133
+ },
134
+ };
@@ -0,0 +1,87 @@
1
+ /**
2
+ * `mnemom agent share` (MNE-9037, parent MNE-9014) — build a shareable work card from the
3
+ * lines of your own signed statements and print a link to the static share page with the
4
+ * card in the URL fragment (`/share#card=<base64url(JSON)>`). The fragment is never sent to
5
+ * a server. The command only PRINTS the link: it never posts, uploads or opens it anywhere
6
+ * (W6.2 — the share offer never auto-posts).
7
+ *
8
+ * Gated by MNEMOM_AGENT_SHARE=1 (not experimental-registry; off by default, so default CLI
9
+ * behaviour is unchanged). Everything outside the process is injected:
10
+ * - `source` lists candidate lines from the user's signed statements,
11
+ * - `issuer` returns the SIGNED AAP-ShareCard/v1 for the chosen lines (the CLI never holds
12
+ * a signing key),
13
+ * - `renderer` the aip `renderLines` (MNE-9026, unpublished) behind a small interface.
14
+ * The CLI refuses, before anything is issued, lines that are estimates or that carry
15
+ * identifying fields, and re-checks the returned card for the same.
16
+ */
17
+ export declare const AGENT_SHARE_ENV = "MNEMOM_AGENT_SHARE";
18
+ export declare function agentShareEnabled(env?: Record<string, string | undefined>): boolean;
19
+ /** A line as offered by the statement source. Extra keys are kept so they can be refused. */
20
+ export interface CandidateLine {
21
+ id: string;
22
+ kind: string;
23
+ label: string;
24
+ value: number;
25
+ statement_hash: string;
26
+ [extra: string]: unknown;
27
+ }
28
+ export interface SharedCardLine {
29
+ id: string;
30
+ kind: "count" | "revert";
31
+ label: string;
32
+ value: number;
33
+ statement_hash: string;
34
+ }
35
+ /** Shape of AAP-ShareCard/v1 as far as the CLI needs it (full verification is the page's job). */
36
+ export interface ShareCardLike {
37
+ typ: string;
38
+ lines: SharedCardLine[];
39
+ [key: string]: unknown;
40
+ }
41
+ export interface RenderedLinePreview {
42
+ text: string;
43
+ coverage?: string;
44
+ kind: string;
45
+ }
46
+ /** The aip `renderLines` contract, injected (not yet published). */
47
+ export interface LineRenderer {
48
+ renderLines(card: unknown): RenderedLinePreview[];
49
+ }
50
+ export interface StatementSource {
51
+ listLines(): Promise<CandidateLine[]>;
52
+ }
53
+ export interface CardIssuer {
54
+ /** Signed card committing to exactly these lines. */
55
+ issue(lines: SharedCardLine[]): Promise<ShareCardLike>;
56
+ }
57
+ export interface AgentShareDeps {
58
+ source: StatementSource;
59
+ issuer: CardIssuer;
60
+ renderer: LineRenderer;
61
+ /** Interactive picker: returns the chosen line ids. */
62
+ pick: (lines: SharedCardLine[]) => Promise<string[]>;
63
+ isTTY: () => boolean;
64
+ websiteBase: () => string;
65
+ out: (line: string) => void;
66
+ err: (line: string) => void;
67
+ }
68
+ export interface AgentShareFlags {
69
+ lines?: string;
70
+ list?: boolean;
71
+ json?: boolean;
72
+ }
73
+ export type RefusalReason = "estimate" | "identifying" | "malformed";
74
+ export interface Refusal {
75
+ id: string;
76
+ reason: RefusalReason;
77
+ detail: string;
78
+ }
79
+ /** Null when the line may be shared; otherwise why it is refused. Fails closed. */
80
+ export declare function refuseReason(line: CandidateLine): Refusal | null;
81
+ export declare function toSharedLine(line: CandidateLine): SharedCardLine;
82
+ /** Card → `<base>/share#card=<base64url(UTF-8 JSON)>` (the format the share page reads). */
83
+ export declare function buildShareLink(card: unknown, websiteBase: string): string;
84
+ /** Inverse of buildShareLink's fragment, for tests and `--json` consumers. */
85
+ export declare function decodeShareFragment(link: string): unknown;
86
+ export declare function agentShareCommand(flags: AgentShareFlags, deps: AgentShareDeps, env?: Record<string, string | undefined>): Promise<number>;
87
+ export declare const liveAgentShareDeps: AgentShareDeps;
@@ -0,0 +1,251 @@
1
+ /**
2
+ * `mnemom agent share` (MNE-9037, parent MNE-9014) — build a shareable work card from the
3
+ * lines of your own signed statements and print a link to the static share page with the
4
+ * card in the URL fragment (`/share#card=<base64url(JSON)>`). The fragment is never sent to
5
+ * a server. The command only PRINTS the link: it never posts, uploads or opens it anywhere
6
+ * (W6.2 — the share offer never auto-posts).
7
+ *
8
+ * Gated by MNEMOM_AGENT_SHARE=1 (not experimental-registry; off by default, so default CLI
9
+ * behaviour is unchanged). Everything outside the process is injected:
10
+ * - `source` lists candidate lines from the user's signed statements,
11
+ * - `issuer` returns the SIGNED AAP-ShareCard/v1 for the chosen lines (the CLI never holds
12
+ * a signing key),
13
+ * - `renderer` the aip `renderLines` (MNE-9026, unpublished) behind a small interface.
14
+ * The CLI refuses, before anything is issued, lines that are estimates or that carry
15
+ * identifying fields, and re-checks the returned card for the same.
16
+ */
17
+ import { createInterface } from "node:readline/promises";
18
+ import { getWebsiteUrl } from "../lib/config.js";
19
+ import { fmt } from "../lib/format.js";
20
+ export const AGENT_SHARE_ENV = "MNEMOM_AGENT_SHARE";
21
+ export function agentShareEnabled(env = process.env) {
22
+ return env[AGENT_SHARE_ENV] === "1";
23
+ }
24
+ const HEX64 = /^[0-9a-f]{64}$/;
25
+ const OPAQUE_ID = /^[a-z0-9_-]{1,64}$/;
26
+ /** Same charset the verifier enforces on labels. */
27
+ const LABEL = /^[A-Za-z0-9 .,()+-]{1,40}$/;
28
+ /** The only top-level fields an AAP-ShareCard/v1 may carry. */
29
+ const CARD_KEYS = [
30
+ "typ",
31
+ "card_id",
32
+ "period",
33
+ "issued_at",
34
+ "signing_key_id",
35
+ "statements",
36
+ "lines",
37
+ "signature",
38
+ ];
39
+ const LINE_KEYS = ["id", "kind", "label", "value", "statement_hash"];
40
+ /** Words that mark a label as naming a repo, org, person, tool, code or goal text. */
41
+ const IDENTIFYING_LABEL = /\b(repo|repository|org|organization|organisation|team|person|user|author|tool|goal|code|branch|commit|pr)\b/i;
42
+ /** Null when the line may be shared; otherwise why it is refused. Fails closed. */
43
+ export function refuseReason(line) {
44
+ const id = typeof line.id === "string" ? line.id : "(unknown)";
45
+ const no = (reason, detail) => ({ id, reason, detail });
46
+ if (line.estimated === true || line.estimate === true || line.provenance === "estimate") {
47
+ return no("estimate", "it is an estimate, not a count from a signed statement");
48
+ }
49
+ const extras = Object.keys(line).filter((k) => !LINE_KEYS.includes(k));
50
+ if (extras.length > 0) {
51
+ return no("identifying", `it carries extra field(s): ${extras.join(", ")}`);
52
+ }
53
+ if (typeof line.id !== "string" || !OPAQUE_ID.test(line.id))
54
+ return no("malformed", "bad line id");
55
+ if (line.kind !== "count" && line.kind !== "revert")
56
+ return no("malformed", "unsupported line kind");
57
+ if (typeof line.value !== "number" || !Number.isSafeInteger(line.value) || line.value < 0) {
58
+ return no("estimate", "its value is not an exact non-negative integer");
59
+ }
60
+ if (typeof line.statement_hash !== "string" || !HEX64.test(line.statement_hash)) {
61
+ return no("estimate", "it is not tied to a signed statement");
62
+ }
63
+ if (typeof line.label !== "string" ||
64
+ !LABEL.test(line.label) ||
65
+ /[/#]|:\/\/|www\./i.test(line.label)) {
66
+ return no("identifying", "its label is not short plain text");
67
+ }
68
+ if (IDENTIFYING_LABEL.test(line.label)) {
69
+ return no("identifying", "its label names a repo, org, person, tool, code or goal");
70
+ }
71
+ return null;
72
+ }
73
+ export function toSharedLine(line) {
74
+ return {
75
+ id: line.id,
76
+ kind: line.kind,
77
+ label: line.label,
78
+ value: line.value,
79
+ statement_hash: line.statement_hash,
80
+ };
81
+ }
82
+ /** Card → `<base>/share#card=<base64url(UTF-8 JSON)>` (the format the share page reads). */
83
+ export function buildShareLink(card, websiteBase) {
84
+ const bytes = new TextEncoder().encode(JSON.stringify(card));
85
+ const b64 = Buffer.from(bytes).toString("base64url");
86
+ return `${websiteBase.replace(/\/+$/, "")}/share#card=${b64}`;
87
+ }
88
+ /** Inverse of buildShareLink's fragment, for tests and `--json` consumers. */
89
+ export function decodeShareFragment(link) {
90
+ const hash = link.slice(link.indexOf("#") + 1);
91
+ const encoded = new URLSearchParams(hash).get("card") ?? "";
92
+ try {
93
+ return JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
94
+ }
95
+ catch {
96
+ return null;
97
+ }
98
+ }
99
+ /** The issued card must contain exactly the chosen lines, unaltered. */
100
+ function checkIssuedCard(card, chosen) {
101
+ if (!card || typeof card !== "object" || !Array.isArray(card.lines))
102
+ return "the card has no lines";
103
+ const extra = Object.keys(card).filter((k) => !CARD_KEYS.includes(k));
104
+ if (extra.length > 0)
105
+ return `the card carries unexpected field(s): ${extra.join(", ")}`;
106
+ if (card.lines.length !== chosen.length)
107
+ return "the card's lines differ from the ones you picked";
108
+ for (const want of chosen) {
109
+ const got = card.lines.find((l) => l.id === want.id);
110
+ if (!got ||
111
+ LINE_KEYS.some((k) => got[k] !== want[k]) ||
112
+ Object.keys(got).length !== LINE_KEYS.length) {
113
+ return `the card's line "${want.id}" differs from the one you picked`;
114
+ }
115
+ }
116
+ return null;
117
+ }
118
+ export async function agentShareCommand(flags, deps, env = process.env) {
119
+ if (!agentShareEnabled(env)) {
120
+ deps.err(fmt.error("unknown command 'share' for 'mnemom agent'"));
121
+ return 1;
122
+ }
123
+ let candidates;
124
+ try {
125
+ candidates = await deps.source.listLines();
126
+ }
127
+ catch (error) {
128
+ deps.err(fmt.error(`Could not read your signed statements: ${error.message}`));
129
+ return 1;
130
+ }
131
+ const offered = [];
132
+ const refused = [];
133
+ for (const c of candidates) {
134
+ const r = refuseReason(c);
135
+ if (r)
136
+ refused.push(r);
137
+ else
138
+ offered.push(toSharedLine(c));
139
+ }
140
+ if (flags.list) {
141
+ if (flags.json) {
142
+ deps.out(JSON.stringify({ lines: offered, refused }, null, 2));
143
+ }
144
+ else {
145
+ if (offered.length === 0)
146
+ deps.out("No lines available to share.");
147
+ for (const l of offered)
148
+ deps.out(`${l.id} ${l.label}: ${l.value}`);
149
+ for (const r of refused)
150
+ deps.out(`(not shareable) ${r.id}: ${r.detail}`);
151
+ }
152
+ return 0;
153
+ }
154
+ let ids;
155
+ if (flags.lines !== undefined) {
156
+ ids = [
157
+ ...new Set(flags.lines
158
+ .split(",")
159
+ .map((s) => s.trim())
160
+ .filter(Boolean)),
161
+ ];
162
+ }
163
+ else if (deps.isTTY()) {
164
+ if (offered.length === 0) {
165
+ deps.err(fmt.error("No lines available to share."));
166
+ return 1;
167
+ }
168
+ ids = await deps.pick(offered);
169
+ }
170
+ else {
171
+ deps.err(fmt.error("No terminal to pick lines in. Pass --lines <id,id> (see --list)."));
172
+ return 1;
173
+ }
174
+ if (ids.length === 0) {
175
+ deps.err(fmt.error("No lines picked."));
176
+ return 1;
177
+ }
178
+ const chosen = [];
179
+ for (const id of ids) {
180
+ const r = refused.find((x) => x.id === id);
181
+ if (r) {
182
+ deps.err(fmt.error(`Refusing line "${id}": ${r.detail}.`));
183
+ return 1;
184
+ }
185
+ const line = offered.find((l) => l.id === id);
186
+ if (!line) {
187
+ deps.err(fmt.error(`Unknown line "${id}". Run with --list to see what is available.`));
188
+ return 1;
189
+ }
190
+ chosen.push(line);
191
+ }
192
+ let card;
193
+ try {
194
+ card = await deps.issuer.issue(chosen);
195
+ }
196
+ catch (error) {
197
+ deps.err(fmt.error(`Could not build the card: ${error.message}`));
198
+ return 1;
199
+ }
200
+ const problem = checkIssuedCard(card, chosen);
201
+ if (problem) {
202
+ deps.err(fmt.error(`Refusing to share: ${problem}.`));
203
+ return 1;
204
+ }
205
+ const link = buildShareLink(card, deps.websiteBase());
206
+ if (flags.json) {
207
+ deps.out(JSON.stringify({ link, lines: chosen }, null, 2));
208
+ return 0;
209
+ }
210
+ deps.out("Your card:");
211
+ for (const r of deps.renderer.renderLines(card)) {
212
+ deps.out(` ${r.text}${r.coverage ? ` (${r.coverage})` : ""}`);
213
+ }
214
+ deps.out("");
215
+ deps.out(link);
216
+ deps.out("");
217
+ deps.out("Nothing was posted or sent. The card is in the link's fragment, which a browser never sends to a server; share the link yourself if you want to.");
218
+ return 0;
219
+ }
220
+ /** Not wired to a backend yet: the signed-statement source and card issuer land with MNE-9026. */
221
+ const notConnected = () => {
222
+ throw new Error("statement source not connected yet (waiting on the card renderer, MNE-9026)");
223
+ };
224
+ export const liveAgentShareDeps = {
225
+ source: { listLines: async () => notConnected() },
226
+ issuer: { issue: async () => notConnected() },
227
+ renderer: {
228
+ renderLines: (card) => (card.lines ?? []).map((l) => ({
229
+ text: `${l.label}: ${l.value}`,
230
+ kind: l.kind,
231
+ })),
232
+ },
233
+ pick: async (lines) => {
234
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
235
+ try {
236
+ lines.forEach((l, i) => process.stderr.write(`${i + 1}. ${l.label}: ${l.value}\n`));
237
+ const answer = await rl.question("Pick lines (numbers, comma-separated): ");
238
+ return answer
239
+ .split(",")
240
+ .map((s) => lines[Number(s.trim()) - 1]?.id)
241
+ .filter((x) => typeof x === "string");
242
+ }
243
+ finally {
244
+ rl.close();
245
+ }
246
+ },
247
+ isTTY: () => Boolean(process.stdin.isTTY),
248
+ websiteBase: getWebsiteUrl,
249
+ out: (line) => console.log(line),
250
+ err: (line) => console.error(line),
251
+ };
@@ -28,8 +28,9 @@
28
28
  import { type MnemomKeyDeps } from "../lib/subscription-login.js";
29
29
  import { type SessionRecord } from "../lib/agent-ledger.js";
30
30
  import { type ExperimentalId } from "../lib/experimental.js";
31
+ import { conductReceiptFor, type ReceiptLaunch } from "../lib/conduct-receipt.js";
31
32
  import { type GovernanceOptIn } from "./agent-setup.js";
32
- import { type ContextProfile, type LaunchShape, type SealedContractEnvelope } from "../lib/agent.js";
33
+ import { type CcSettingsView, type ContextProfile, type LaunchShape, type SealedContractEnvelope } from "../lib/agent.js";
33
34
  export interface AgentOptions {
34
35
  goal?: string;
35
36
  requirement?: string[];
@@ -310,11 +311,79 @@ export interface RcCapability {
310
311
  * and by the auto-default selection (a bare launch prefers RC when capable).
311
312
  */
312
313
  export declare function checkRcCapability(cli: ResolvedCli): RcCapability;
314
+ /**
315
+ * The extra `--settings` the launch merges (beside the autocompact guard): the status line's,
316
+ * and with commit reporting on (W1.8) the `Mnemom-Conversation` commit trailer, which keeps the
317
+ * user's own commit attribution in front of it. Never when the user passed their own --settings.
318
+ */
319
+ export declare function launchSettingsExtra(o: {
320
+ statusline: Record<string, unknown> | null;
321
+ commitReport: boolean;
322
+ ownSettings: boolean;
323
+ conversationId: string;
324
+ /** Claude Code's settings files, highest precedence first (readCcSettings). */
325
+ settings: CcSettingsView[];
326
+ }): Record<string, unknown> | null;
327
+ /**
328
+ * Whether the local base-URL proxy runs: implicit goals (turn header), the conduct receipt and
329
+ * commit reporting (W1.8) all need it; commit reporting reads the lineage key it records.
330
+ */
331
+ export declare function proxyWanted(o: {
332
+ turnHeader: boolean;
333
+ receipt: boolean;
334
+ commitReport: boolean;
335
+ }): boolean;
336
+ /**
337
+ * Whether the proxy captures the gateway's record state: the conduct receipt reads it at exit,
338
+ * and the fed status line reads its lineage key while the session runs.
339
+ */
340
+ export declare function wantsRecordState(receipt: boolean, fed: boolean): boolean;
341
+ /**
342
+ * Point Claude Code at a ready proxy and, on a fed launch, start the status-line feed on the
343
+ * proxy's record state (no state file, no feed: the line stays "mnemom").
344
+ */
345
+ export declare function onBaseUrlProxyReady(baseUrl: string | null, recordStateFile: string | undefined, childEnv: NodeJS.ProcessEnv, feed: {
346
+ feedFile: string;
347
+ orgId: string;
348
+ } | null, start?: (o: {
349
+ recordStateFile: string;
350
+ feedFile: string;
351
+ orgId: string;
352
+ }) => unknown): void;
353
+ /**
354
+ * The status-line feed file for a fed launch (and FEED_ENV set on the child), or null: the
355
+ * segment off, no billing org, or {@link fedStatuslineWanted} false.
356
+ */
357
+ export declare function prepareStatuslineFeed(o: {
358
+ statusline: boolean;
359
+ orgId?: string;
360
+ version: string;
361
+ goalMode: boolean;
362
+ env: NodeJS.ProcessEnv;
363
+ childEnv: NodeJS.ProcessEnv;
364
+ files?: () => {
365
+ feedFile: string;
366
+ };
367
+ }): {
368
+ feedFile: string;
369
+ } | null;
370
+ /**
371
+ * Whether this launch feeds the status line measured model cost and μ (W1.4): prerelease
372
+ * builds only (stable stays "mnemom", decision 14), and only when the turn proxy runs, since
373
+ * the proxy is what learns the gateway's lineage key for the session.
374
+ */
375
+ export declare function fedStatuslineWanted(version: string, goalMode: boolean, env: NodeJS.ProcessEnv): boolean;
313
376
  /**
314
377
  * Add 127.0.0.1 and localhost to BOTH NO_PROXY and no_proxy (proxy clients disagree on which one
315
378
  * wins, and some read only the lowercase one), keeping whatever each already listed.
316
379
  */
317
380
  export declare function exemptLoopbackFromProxy(env: NodeJS.ProcessEnv): void;
381
+ /**
382
+ * Print the conduct receipt (experimental `record`) — the exit call site here, and the one a
383
+ * durable park calls. Never throws: a failure is one warning line, and the caller's exit code
384
+ * is untouched.
385
+ */
386
+ export declare function sayConductReceipt(launch: ReceiptLaunch, trigger?: "exit" | "park", receipt?: typeof conductReceiptFor): Promise<void>;
318
387
  /** Whether a terminal implicit launch runs the turn-origin proxy (MNEMOM_AGENT_TURN_HEADER=0 opts out). */
319
388
  export declare function turnProxyWanted(env: NodeJS.ProcessEnv): boolean;
320
389
  /** Test seams for startBaseUrlProxy (every real caller omits them). */
@@ -325,6 +394,10 @@ export interface BaseUrlProxyOptions {
325
394
  node?: string;
326
395
  /** How long to wait for READY before launching straight to the door (default 5 s). */
327
396
  readyTimeoutMs?: number;
397
+ /** Send x-mnemom-turn (default true); false sets MNEMOM_AGENT_TURN_HEADER=0 for the proxy. */
398
+ turnHeader?: boolean;
399
+ /** Capture the conduct receipt's record state; its path is `done`'s second argument. */
400
+ recordState?: boolean;
328
401
  }
329
402
  /**
330
403
  * Start base-url-proxy.mjs in front of `door`; calls `done` exactly once with its
@@ -333,7 +406,7 @@ export interface BaseUrlProxyOptions {
333
406
  * script is the same case (node exits 1 before READY). The proxy holds no secret (auth headers
334
407
  * pass through it), exits with this process, and is killed on exit.
335
408
  */
336
- export declare function startBaseUrlProxy(door: string, done: (baseUrl: string | null) => void, opts?: BaseUrlProxyOptions): void;
409
+ export declare function startBaseUrlProxy(door: string, done: (baseUrl: string | null, recordStatePath?: string) => void, opts?: BaseUrlProxyOptions): void;
337
410
  /**
338
411
  * The three secret lines the Remote Control proxy reads on stdin (anthropic, mnemom, openai). The
339
412
  * Mnemom key rides line 2 on BOTH auth modes when the launch has one — the proxy forwards it as