@mnemom/mnemom 0.17.34-next.1 → 0.17.34-next.3

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 (236) hide show
  1. package/dist/base-url-proxy.mjs +18 -4
  2. package/dist/commands/agent-config.js +5 -1
  3. package/dist/commands/agent-doctor.d.ts +9 -0
  4. package/dist/commands/agent-doctor.js +4 -0
  5. package/dist/commands/agent-run.d.ts +14 -6
  6. package/dist/commands/agent-run.js +44 -27
  7. package/dist/commands/agent-session.d.ts +1 -2
  8. package/dist/commands/agent-session.js +1 -2
  9. package/dist/commands/agent-setup.d.ts +6 -0
  10. package/dist/commands/agent-setup.js +12 -1
  11. package/dist/commands/agent-share-test-issuer.d.ts +67 -0
  12. package/dist/commands/agent-share-test-issuer.js +139 -0
  13. package/dist/commands/agent-share.d.ts +7 -1
  14. package/dist/commands/agent-share.js +14 -4
  15. package/dist/commands/agent.d.ts +18 -5
  16. package/dist/commands/agent.js +186 -59
  17. package/dist/commands/durable-doctor.d.ts +50 -0
  18. package/dist/commands/durable-doctor.js +186 -0
  19. package/dist/commands/durable-lock.d.ts +261 -0
  20. package/dist/commands/durable-lock.js +600 -0
  21. package/dist/commands/durable-sessions.d.ts +87 -0
  22. package/dist/commands/durable-sessions.js +303 -0
  23. package/dist/commands/durable-verbs.d.ts +116 -0
  24. package/dist/commands/durable-verbs.js +393 -0
  25. package/dist/commands/scan.d.ts +12 -1
  26. package/dist/commands/scan.js +22 -3
  27. package/dist/commands/wrap.js +1 -1
  28. package/dist/durable/adopt-live.js +5 -8
  29. package/dist/durable/adopt.d.ts +8 -0
  30. package/dist/durable/adopt.js +27 -6
  31. package/dist/durable/cwd-rule.d.ts +9 -1
  32. package/dist/durable/cwd-rule.js +18 -0
  33. package/dist/durable/flock.d.ts +26 -0
  34. package/dist/durable/flock.js +80 -0
  35. package/dist/durable/launch-spec.d.ts +9 -1
  36. package/dist/durable/launch-spec.js +11 -4
  37. package/dist/durable/seat-proxy-entry.d.ts +11 -0
  38. package/dist/durable/seat-proxy-entry.js +25 -0
  39. package/dist/durable/seat-proxy.d.ts +107 -0
  40. package/dist/durable/seat-proxy.js +599 -0
  41. package/dist/durable/seat-socket.d.ts +46 -0
  42. package/dist/durable/seat-socket.js +179 -0
  43. package/dist/durable/seat-spec-validator.d.ts +28 -0
  44. package/dist/durable/seat-spec-validator.js +59 -0
  45. package/dist/durable/test-mode.mjs +105 -0
  46. package/dist/durable/turn-chain.d.ts +55 -0
  47. package/dist/durable/turn-chain.js +178 -0
  48. package/dist/host/auto-continue.d.ts +67 -0
  49. package/dist/host/auto-continue.js +118 -0
  50. package/dist/host/bootid.d.ts +13 -0
  51. package/dist/host/bootid.js +41 -0
  52. package/dist/host/client.d.ts +125 -0
  53. package/dist/host/client.js +171 -0
  54. package/dist/host/commands.d.ts +2 -0
  55. package/dist/host/commands.js +122 -0
  56. package/dist/host/control.d.ts +33 -0
  57. package/dist/host/control.js +144 -0
  58. package/dist/host/daemon.d.ts +221 -0
  59. package/dist/host/daemon.js +616 -0
  60. package/dist/host/gate.d.ts +46 -0
  61. package/dist/host/gate.js +82 -0
  62. package/dist/host/grant.d.ts +24 -0
  63. package/dist/host/grant.js +24 -0
  64. package/dist/host/instance.d.ts +35 -0
  65. package/dist/host/instance.js +62 -0
  66. package/dist/host/integrity.d.ts +35 -0
  67. package/dist/host/integrity.js +115 -0
  68. package/dist/host/launchd.d.ts +16 -0
  69. package/dist/host/launchd.js +45 -0
  70. package/dist/host/lock.d.ts +20 -0
  71. package/dist/host/lock.js +63 -0
  72. package/dist/host/owner.d.ts +58 -0
  73. package/dist/host/owner.js +123 -0
  74. package/dist/host/plist.d.ts +16 -0
  75. package/dist/host/plist.js +85 -0
  76. package/dist/host/procs.d.ts +27 -0
  77. package/dist/host/procs.js +105 -0
  78. package/dist/host/run.d.ts +65 -0
  79. package/dist/host/run.js +299 -0
  80. package/dist/host/runtime.d.ts +8 -0
  81. package/dist/host/runtime.js +22 -0
  82. package/dist/host/s1-names.d.ts +23 -0
  83. package/dist/host/s1-names.js +22 -0
  84. package/dist/host/seat-credential.d.ts +27 -0
  85. package/dist/host/seat-credential.js +41 -0
  86. package/dist/host/seat-launch-main.d.ts +1 -0
  87. package/dist/host/seat-launch-main.js +59 -0
  88. package/dist/host/seat-launch.d.ts +116 -0
  89. package/dist/host/seat-launch.js +302 -0
  90. package/dist/host/seat.d.ts +48 -0
  91. package/dist/host/seat.js +149 -0
  92. package/dist/host/state.d.ts +33 -0
  93. package/dist/host/state.js +117 -0
  94. package/dist/host/status.d.ts +101 -0
  95. package/dist/host/status.js +307 -0
  96. package/dist/host/tmux.d.ts +56 -0
  97. package/dist/host/tmux.js +182 -0
  98. package/dist/host/transcript.d.ts +30 -0
  99. package/dist/host/transcript.js +130 -0
  100. package/dist/host/verbs.d.ts +52 -0
  101. package/dist/host/verbs.js +282 -0
  102. package/dist/index.d.ts +8 -6
  103. package/dist/index.js +209 -43
  104. package/dist/lib/agent.d.ts +13 -29
  105. package/dist/lib/agent.js +17 -29
  106. package/dist/lib/claude-bin.d.ts +6 -0
  107. package/dist/lib/claude-bin.js +26 -0
  108. package/dist/lib/claude-plugin.d.ts +11 -2
  109. package/dist/lib/claude-plugin.js +10 -1
  110. package/dist/lib/commit-report.d.ts +5 -4
  111. package/dist/lib/commit-report.js +6 -9
  112. package/dist/lib/config.d.ts +8 -1
  113. package/dist/lib/config.js +28 -0
  114. package/dist/lib/durable/api.d.ts +136 -0
  115. package/dist/lib/durable/api.js +220 -0
  116. package/dist/lib/durable/codes.d.ts +8 -0
  117. package/dist/lib/durable/codes.js +2 -0
  118. package/dist/lib/durable/gate.d.ts +48 -0
  119. package/dist/lib/durable/gate.js +122 -0
  120. package/dist/lib/durable/handover.d.ts +101 -0
  121. package/dist/lib/durable/handover.js +175 -0
  122. package/dist/lib/durable/launch.d.ts +199 -0
  123. package/dist/lib/durable/launch.js +461 -0
  124. package/dist/lib/durable/launching.d.ts +15 -0
  125. package/dist/lib/durable/launching.js +70 -0
  126. package/dist/lib/durable/live.d.ts +190 -0
  127. package/dist/lib/durable/live.js +633 -0
  128. package/dist/lib/durable/refusal.d.ts +57 -0
  129. package/dist/lib/durable/refusal.js +78 -0
  130. package/dist/lib/durable/view.d.ts +83 -0
  131. package/dist/lib/durable/view.js +244 -0
  132. package/dist/lib/experimental.d.ts +0 -9
  133. package/dist/lib/experimental.js +18 -17
  134. package/dist/lib/launcher-config.d.ts +13 -0
  135. package/dist/lib/launcher-config.js +32 -0
  136. package/dist/lib/runner/exec-append.d.ts +15 -0
  137. package/dist/lib/runner/exec-append.js +2 -0
  138. package/dist/lib/runner/executor-client.d.ts +2 -1
  139. package/dist/lib/runner/executor-state.d.ts +10 -0
  140. package/dist/lib/runner/executor-state.js +24 -0
  141. package/dist/lib/runner/github-base.d.ts +24 -0
  142. package/dist/lib/runner/github-base.js +27 -0
  143. package/dist/lib/runner/job.d.ts +9 -4
  144. package/dist/lib/runner/job.js +43 -15
  145. package/dist/lib/runner/oidc.d.ts +3 -2
  146. package/dist/lib/runner/oidc.js +7 -4
  147. package/dist/lib/runner/options.d.ts +6 -0
  148. package/dist/lib/runner/options.js +19 -1
  149. package/dist/lib/runner/phases.d.ts +7 -4
  150. package/dist/lib/runner/phases.js +8 -4
  151. package/dist/lib/runner/proxy.d.ts +5 -1
  152. package/dist/lib/runner/proxy.js +7 -1
  153. package/dist/lib/scan/cost-per-pr.d.ts +93 -0
  154. package/dist/lib/scan/cost-per-pr.js +101 -0
  155. package/dist/lib/scan/record.d.ts +27 -1
  156. package/dist/lib/scan/record.js +114 -16
  157. package/dist/lib/scan/report-email.d.ts +26 -6
  158. package/dist/lib/scan/report-email.js +32 -3
  159. package/dist/lib/scan/scan.d.ts +3 -0
  160. package/dist/lib/scan/scan.js +13 -1
  161. package/dist/lib/subscription-login.d.ts +15 -0
  162. package/dist/lib/subscription-login.js +28 -1
  163. package/dist/vendor/durable-contract/index.js +240 -18
  164. package/dist/vendor/shared/durable-contract/admission.d.ts +240 -0
  165. package/dist/vendor/shared/durable-contract/admission.js +533 -0
  166. package/dist/vendor/shared/durable-contract/arm.d.ts +51 -0
  167. package/dist/vendor/shared/durable-contract/arm.js +133 -0
  168. package/dist/vendor/shared/durable-contract/canonical.d.ts +29 -0
  169. package/dist/vendor/shared/durable-contract/canonical.js +98 -0
  170. package/dist/vendor/shared/durable-contract/cell-id.d.ts +22 -0
  171. package/dist/vendor/shared/durable-contract/cell-id.js +38 -0
  172. package/dist/vendor/shared/durable-contract/cli-output.d.ts +342 -0
  173. package/dist/vendor/shared/durable-contract/cli-output.js +201 -0
  174. package/dist/vendor/shared/durable-contract/cli-surface.d.ts +119 -0
  175. package/dist/vendor/shared/durable-contract/cli-surface.js +270 -0
  176. package/dist/vendor/shared/durable-contract/codes.d.ts +537 -0
  177. package/dist/vendor/shared/durable-contract/codes.js +475 -0
  178. package/dist/vendor/shared/durable-contract/constants.d.ts +153 -0
  179. package/dist/vendor/shared/durable-contract/constants.js +179 -0
  180. package/dist/vendor/shared/durable-contract/doors.d.ts +119 -0
  181. package/dist/vendor/shared/durable-contract/doors.js +249 -0
  182. package/dist/vendor/shared/durable-contract/envelope.d.ts +103 -0
  183. package/dist/vendor/shared/durable-contract/envelope.js +317 -0
  184. package/dist/vendor/shared/durable-contract/events.d.ts +147 -0
  185. package/dist/vendor/shared/durable-contract/events.js +630 -0
  186. package/dist/vendor/shared/durable-contract/executor-state.d.ts +41 -0
  187. package/dist/vendor/shared/durable-contract/executor-state.js +32 -0
  188. package/dist/vendor/shared/durable-contract/fixlines.d.ts +318 -0
  189. package/dist/vendor/shared/durable-contract/fixlines.js +483 -0
  190. package/dist/vendor/shared/durable-contract/freeze-record.d.ts +787 -0
  191. package/dist/vendor/shared/durable-contract/freeze-record.js +361 -0
  192. package/dist/vendor/shared/durable-contract/index.d.ts +36 -0
  193. package/dist/vendor/shared/durable-contract/index.js +37 -0
  194. package/dist/vendor/shared/durable-contract/journal-approvals.d.ts +29 -0
  195. package/dist/vendor/shared/durable-contract/journal-approvals.js +60 -0
  196. package/dist/vendor/shared/durable-contract/keys.d.ts +24 -0
  197. package/dist/vendor/shared/durable-contract/keys.js +66 -0
  198. package/dist/vendor/shared/durable-contract/lock.d.ts +128 -0
  199. package/dist/vendor/shared/durable-contract/lock.js +151 -0
  200. package/dist/vendor/shared/durable-contract/names.d.ts +25 -0
  201. package/dist/vendor/shared/durable-contract/names.js +78 -0
  202. package/dist/vendor/shared/durable-contract/owner-verbs.d.ts +175 -0
  203. package/dist/vendor/shared/durable-contract/owner-verbs.js +403 -0
  204. package/dist/vendor/shared/durable-contract/paths.d.ts +23 -0
  205. package/dist/vendor/shared/durable-contract/paths.js +35 -0
  206. package/dist/vendor/shared/durable-contract/pure-fn.d.ts +48 -0
  207. package/dist/vendor/shared/durable-contract/pure-fn.js +89 -0
  208. package/dist/vendor/shared/durable-contract/routes.d.ts +205 -0
  209. package/dist/vendor/shared/durable-contract/routes.js +315 -0
  210. package/dist/vendor/shared/durable-contract/schema.d.ts +126 -0
  211. package/dist/vendor/shared/durable-contract/schema.js +441 -0
  212. package/dist/vendor/shared/durable-contract/seal.d.ts +54 -0
  213. package/dist/vendor/shared/durable-contract/seal.js +99 -0
  214. package/dist/vendor/shared/durable-contract/secret-scope.d.ts +28 -0
  215. package/dist/vendor/shared/durable-contract/secret-scope.js +76 -0
  216. package/dist/vendor/shared/durable-contract/session-id.d.ts +21 -0
  217. package/dist/vendor/shared/durable-contract/session-id.js +58 -0
  218. package/dist/vendor/shared/durable-contract/spec.d.ts +162 -0
  219. package/dist/vendor/shared/durable-contract/spec.js +286 -0
  220. package/dist/vendor/shared/durable-contract/states.d.ts +96 -0
  221. package/dist/vendor/shared/durable-contract/states.js +218 -0
  222. package/dist/vendor/shared/durable-contract/telemetry.d.ts +94 -0
  223. package/dist/vendor/shared/durable-contract/telemetry.js +180 -0
  224. package/dist/vendor/shared/durable-contract/test-mode.d.ts +62 -0
  225. package/dist/vendor/shared/durable-contract/test-mode.js +140 -0
  226. package/dist/vendor/shared/durable-contract/turn.d.ts +57 -0
  227. package/dist/vendor/shared/durable-contract/turn.js +81 -0
  228. package/dist/vendor/shared/durable-contract/vectors.d.ts +191 -0
  229. package/dist/vendor/shared/durable-contract/vectors.js +208 -0
  230. package/package.json +3 -2
  231. package/plugins/integrity-manifest.json +35 -0
  232. package/plugins/mnemom/hooks/hooks.json +15 -1
  233. package/plugins/mnemom/hooks/register.tsx +60 -2
  234. package/plugins/mnemom/hooks/seat-hello.mjs +116 -0
  235. package/dist/commands/agent-implicit-notice.d.ts +0 -31
  236. package/dist/commands/agent-implicit-notice.js +0 -73
@@ -51,6 +51,24 @@ export function classifyCwd(cwd, deps, ccSessionId = null) {
51
51
  return { ok: false, cwd_kind: "symlink_transcript", params: { realpath: real } };
52
52
  return { ok: false, cwd_kind: null, unadoptable: "no_transcript" };
53
53
  }
54
+ /**
55
+ * The daemon's spawn-time cwd check (attestation check `cwd`, C14-R04; mnemom-test #1002): the
56
+ * committed cwd's realpath must equal `git rev-parse --show-toplevel` there. Null when it holds;
57
+ * otherwise the S1 CWD_CHECK_CAUSES value: `missing` when there is no committed cwd or it does not
58
+ * resolve, `not_toplevel` when it resolves but is not a work tree's top level (a subdirectory, or
59
+ * not in a git work tree at all).
60
+ */
61
+ export function spawnCwdCause(cwd, deps) {
62
+ if (cwd === null)
63
+ return "missing";
64
+ const real = deps.realpath(cwd);
65
+ if (real === null)
66
+ return "missing";
67
+ const top = deps.gitToplevel(real);
68
+ if (top === null)
69
+ return "not_toplevel";
70
+ return (deps.realpath(top) ?? top) === real ? null : "not_toplevel";
71
+ }
54
72
  const CWD_FIX = {
55
73
  subdirectory: "cwd_subdirectory",
56
74
  not_git: "cwd_not_git",
@@ -0,0 +1,26 @@
1
+ export type LockResult = {
2
+ ok: true;
3
+ fd: number;
4
+ } | {
5
+ ok: false;
6
+ reason: "held" | "error";
7
+ detail: string;
8
+ };
9
+ /** The helper commands, tried in order. Each locks fd 3 and exits 0 (taken), 3 (held), else error. */
10
+ export declare const FLOCK_HELPERS: readonly (readonly string[])[];
11
+ export interface FlockDeps {
12
+ openSync: (p: string, flags: string, mode: number) => number;
13
+ closeSync: (fd: number) => void;
14
+ mkdirSync: (p: string, opts: {
15
+ recursive: true;
16
+ mode: number;
17
+ }) => unknown;
18
+ run: (argv: readonly string[], fd: number) => number | null;
19
+ exists: (p: string) => boolean;
20
+ }
21
+ export declare const defaultFlockDeps: FlockDeps;
22
+ /**
23
+ * Take an exclusive non-blocking flock on `lockPath` (created 0600 in a 0700 directory). On success
24
+ * the returned fd must stay open for the life of the lock; closing it (or exiting) releases it.
25
+ */
26
+ export declare function tryLockFile(lockPath: string, deps?: FlockDeps): LockResult;
@@ -0,0 +1,80 @@
1
+ // flock(2) LOCK_EX|LOCK_NB for the durable seat proxy's per-conversation lock (S1 proxyLockPath,
2
+ // BACKBONE amendment 51, C13-R4).
3
+ //
4
+ // Node has no flock binding, so the lock is taken by a short-lived helper on an fd this process
5
+ // opened and passes down: flock locks belong to the open file description, so the lock stays held
6
+ // by this process's fd after the helper exits, is visible to `lsof -p <this pid>`, and is released
7
+ // when this process exits for any reason (kill -9 included). The helper is the system perl
8
+ // (Fcntl), present on every macOS; python3 is the fallback.
9
+ import fs from "node:fs";
10
+ import path from "node:path";
11
+ import { spawnSync } from "node:child_process";
12
+ /** The helper commands, tried in order. Each locks fd 3 and exits 0 (taken), 3 (held), else error. */
13
+ export const FLOCK_HELPERS = [
14
+ [
15
+ "/usr/bin/perl",
16
+ "-MFcntl=:flock",
17
+ "-e",
18
+ 'open(my $fh, "+<&=", 3) or exit 9; flock($fh, LOCK_EX|LOCK_NB) ? exit 0 : exit 3',
19
+ ],
20
+ [
21
+ "/usr/bin/python3",
22
+ "-I",
23
+ "-c",
24
+ "import fcntl,sys\ntry:\n fcntl.flock(3, fcntl.LOCK_EX|fcntl.LOCK_NB)\nexcept BlockingIOError:\n sys.exit(3)\nexcept Exception:\n sys.exit(9)",
25
+ ],
26
+ ];
27
+ export const defaultFlockDeps = {
28
+ openSync: (p, flags, mode) => fs.openSync(p, flags, mode),
29
+ closeSync: (fd) => fs.closeSync(fd),
30
+ mkdirSync: (p, opts) => fs.mkdirSync(p, opts),
31
+ run: (argv, fd) => spawnSync(argv[0], argv.slice(1), {
32
+ stdio: ["ignore", "ignore", "ignore", fd],
33
+ timeout: 10_000,
34
+ }).status,
35
+ exists: (p) => fs.existsSync(p),
36
+ };
37
+ /**
38
+ * Take an exclusive non-blocking flock on `lockPath` (created 0600 in a 0700 directory). On success
39
+ * the returned fd must stay open for the life of the lock; closing it (or exiting) releases it.
40
+ */
41
+ export function tryLockFile(lockPath, deps = defaultFlockDeps) {
42
+ let fd;
43
+ try {
44
+ deps.mkdirSync(path.dirname(lockPath), { recursive: true, mode: 0o700 });
45
+ fd = deps.openSync(lockPath, "a", 0o600);
46
+ }
47
+ catch (e) {
48
+ return { ok: false, reason: "error", detail: e.message };
49
+ }
50
+ // The fd is handed back only when the lock is taken; every other way out (held, no helper, a
51
+ // helper or the existence probe throwing) closes it here.
52
+ let taken = false;
53
+ try {
54
+ for (const helper of FLOCK_HELPERS) {
55
+ if (!deps.exists(helper[0]))
56
+ continue;
57
+ const status = deps.run(helper, fd);
58
+ if (status === 0) {
59
+ taken = true;
60
+ return { ok: true, fd };
61
+ }
62
+ if (status === 3)
63
+ return { ok: false, reason: "held", detail: "lock held" };
64
+ }
65
+ return { ok: false, reason: "error", detail: "no flock helper could take the lock" };
66
+ }
67
+ catch (e) {
68
+ return { ok: false, reason: "error", detail: e.message };
69
+ }
70
+ finally {
71
+ if (!taken) {
72
+ try {
73
+ deps.closeSync(fd);
74
+ }
75
+ catch {
76
+ /* already closed */
77
+ }
78
+ }
79
+ }
80
+ }
@@ -83,6 +83,11 @@ export interface LauncherArgvSplit {
83
83
  launcherArgv: string[];
84
84
  passthrough: string[];
85
85
  yoloInjected: boolean;
86
+ /**
87
+ * The launcher's interactive Remote Control prefix (`--remote-control <name> [--name <name>]`,
88
+ * agent.ts launch): named as a dropped --remote-control, its session name never a positional.
89
+ */
90
+ rcInjected: boolean;
86
91
  /** The --session-id the launcher pinned, if any. */
87
92
  sessionId: string | null;
88
93
  /**
@@ -100,7 +105,8 @@ export interface LauncherArgvSplit {
100
105
  }
101
106
  /**
102
107
  * Split an original claude argv (after argv[0]) the way `mnemom agent` built it (buildCliArgs):
103
- * [--model v] [--effort v] [yolo] [--session-id id] [--plugin-dir dir] <passthrough>
108
+ * [--remote-control n [--name n]] [--model v] [--effort v] [yolo] [--session-id id]
109
+ * [--plugin-dir dir] <passthrough>
104
110
  * [--strict-mcp-config] [--mcp-config <v>]. `passthroughHint` (the tokens after the parent
105
111
  * launcher's `--`, read from its own argv) pins the split exactly when it occurs where the
106
112
  * layout allows; an empty hint (the parent was read and had no `--`) makes the shape read exact;
@@ -132,6 +138,8 @@ export declare function launchSpecChangeFixLines(ctx: {
132
138
  flags: string;
133
139
  launcherKey: LauncherKey;
134
140
  soleLockedSession: boolean;
141
+ /** Whether the name holds a registry row; absent: it does (every launch path). */
142
+ nameHeld?: boolean;
135
143
  }): [string, ...string[]];
136
144
  /** The adopt Note for an adopted row (C14-R12, R21): S1 grammar, unchanged builder names. */
137
145
  export declare function adoptNote(dropped: readonly string[]): string;
@@ -159,15 +159,21 @@ function isSubsequenceAt(hay, needle, at) {
159
159
  }
160
160
  /**
161
161
  * Split an original claude argv (after argv[0]) the way `mnemom agent` built it (buildCliArgs):
162
- * [--model v] [--effort v] [yolo] [--session-id id] [--plugin-dir dir] <passthrough>
162
+ * [--remote-control n [--name n]] [--model v] [--effort v] [yolo] [--session-id id]
163
+ * [--plugin-dir dir] <passthrough>
163
164
  * [--strict-mcp-config] [--mcp-config <v>]. `passthroughHint` (the tokens after the parent
164
165
  * launcher's `--`, read from its own argv) pins the split exactly when it occurs where the
165
166
  * layout allows; an empty hint (the parent was read and had no `--`) makes the shape read exact;
166
167
  * without a hint (null or absent) the prefix and suffix are read by shape and reported ambiguous.
167
168
  */
168
169
  export function splitLauncherArgv(argv, passthroughHint) {
169
- // Prefix: launcher-injected flags in their fixed order.
170
+ // The RC launch puts its own `--remote-control <name> --name <name>` first.
170
171
  let i = 0;
172
+ const rcName = argv[1];
173
+ const rcInjected = argv[0] === REMOTE_CONTROL_FLAG && rcName !== undefined && readToken(rcName).flag === null;
174
+ if (rcInjected)
175
+ i = argv[2] === "--name" && argv[3] === rcName ? 4 : 2;
176
+ // Prefix: launcher-injected flags in their fixed order.
171
177
  const prefix = {};
172
178
  for (const [flag, arity] of LAUNCHER_PREFIX) {
173
179
  if (argv[i] !== flag)
@@ -230,6 +236,7 @@ export function splitLauncherArgv(argv, passthroughHint) {
230
236
  launcherArgv,
231
237
  passthrough,
232
238
  yoloInjected: prefix[YOLO_FLAG] === true,
239
+ rcInjected,
233
240
  sessionId: typeof prefix["--session-id"] === "string" ? prefix["--session-id"] : null,
234
241
  exact,
235
242
  ambiguous,
@@ -245,7 +252,7 @@ export function runningSourceInput(originalArgv, cwd, opts = {}) {
245
252
  userArgv: [...split.ambiguous, ...split.passthrough],
246
253
  yoloInjected: split.yoloInjected,
247
254
  // Names are deduplicated, so a --remote-control the user also passed is named once.
248
- remoteControl: opts.remoteControl === true,
255
+ remoteControl: opts.remoteControl === true || split.rcInjected,
249
256
  cwd,
250
257
  };
251
258
  }
@@ -263,7 +270,7 @@ export async function compareWithCommitted(built, committed) {
263
270
  export function launchSpecChangeFixLines(ctx) {
264
271
  const render = {
265
272
  launcherKey: ctx.launcherKey,
266
- nameHeld: true,
273
+ nameHeld: ctx.nameHeld ?? true,
267
274
  params: { name: ctx.name, flags: ctx.flags },
268
275
  };
269
276
  const lines = [renderFixLine("launch_spec_change", render)];
@@ -0,0 +1,11 @@
1
+ import { type ExperimentalPrefs } from "../lib/experimental.js";
2
+ /** True when argv (after the node + script pair) names the seat proxy. */
3
+ export declare function isSeatProxyArgv(argv: readonly string[]): boolean;
4
+ export interface SeatProxyEntryDeps {
5
+ stored: () => ExperimentalPrefs | undefined;
6
+ stderr: (line: string) => void;
7
+ exit: (code: number) => void;
8
+ run: (argv: readonly string[]) => Promise<unknown>;
9
+ }
10
+ /** Gate, then run the proxy with the arguments after `seat-proxy`. */
11
+ export declare function runSeatProxyEntry(argv: readonly string[], deps: SeatProxyEntryDeps): void;
@@ -0,0 +1,25 @@
1
+ // Entry dispatch for the hidden `mnemom agent host seat-proxy` (C3). It bypasses commander (the
2
+ // `agent` launcher's options would reject the proxy's flags, as with `agent slack`), the update
3
+ // hook and the experimental migration, so the proxy's stdout carries only its READY line.
4
+ //
5
+ // Gate (BACKBONE Flags/CLI): the durable experimental feature, read from config.json only, like the
6
+ // host daemon that spawns the proxy (resolveExperimental with an empty env; MNEMOM_EXPERIMENTAL is
7
+ // never read here). Off: exit 2 with the gate message, no HTTP, no file under HOME, no process.
8
+ import { gatedMessage, resolveExperimental } from "../lib/experimental.js";
9
+ /** True when argv (after the node + script pair) names the seat proxy. */
10
+ export function isSeatProxyArgv(argv) {
11
+ return argv[0] === "agent" && argv[1] === "host" && argv[2] === "seat-proxy";
12
+ }
13
+ /** Gate, then run the proxy with the arguments after `seat-proxy`. */
14
+ export function runSeatProxyEntry(argv, deps) {
15
+ const on = resolveExperimental({ stored: deps.stored(), env: {} }).enabled.includes("durable");
16
+ if (!on) {
17
+ deps.stderr(`${gatedMessage("`mnemom agent host`", "durable")}\n`);
18
+ deps.exit(2);
19
+ return;
20
+ }
21
+ deps.run(argv.slice(3)).catch(() => {
22
+ deps.stderr("mnemom agent host seat-proxy: internal error\n");
23
+ deps.exit(1);
24
+ });
25
+ }
@@ -0,0 +1,107 @@
1
+ import http from "node:http";
2
+ import type { Duplex } from "node:stream";
3
+ import { TurnOrigins } from "../turn-origin.mjs";
4
+ import { type LockResult } from "./flock.js";
5
+ import { type SeatIdentity, type HelloRelay } from "./seat-socket.js";
6
+ export declare const EXIT_CONFIG = 2;
7
+ export declare const EXIT_LOCK_HELD = 3;
8
+ export declare const EXIT_KEY_DEAD = 4;
9
+ /**
10
+ * The drop reasons and exit reasons this proxy logs (the C13 test hooks name them). These are local
11
+ * literals, not S1 values: S1 freezes no drop-reason or exit-reason enum (gap
12
+ * c3-seat-proxy-enums-not-in-s1), so other pieces cannot import them yet.
13
+ */
14
+ export type DropReason = "arm_missing" | "arm_unparseable" | "unattended_hold_off" | "unattended_fence_off" | "turn_wait_timeout" | "pre_first_prompt" | "no_session";
15
+ export type ExitReason = "fenced_exit" | "revoked_exit" | "ended_exit";
16
+ /** The most a dead-key-status (401/409/410) body is buffered to; a longer one is dropped, never truncated. */
17
+ export declare const DEAD_BODY_CAP: number;
18
+ /** The largest request body forwarded (the Messages API request limit); a larger one gets a local 413. */
19
+ export declare const REQUEST_BODY_CAP: number;
20
+ /**
21
+ * Client headers that never pass: hop-by-hop, proxy-authorization and every x-mnemom-* (the proxy
22
+ * sets the Mnemom ones from the lease). The provider credential (authorization, x-api-key) passes:
23
+ * the seat carries it exactly as `mnemom agent` does (dogfood B6 ruling, superseding C13-R2/R8).
24
+ */
25
+ export declare function isStrippedClientHeader(name: string): boolean;
26
+ export interface SeatProxyArgs {
27
+ conversation: string;
28
+ agentId: string;
29
+ door: URL;
30
+ cwd: string;
31
+ seat: SeatIdentity | null;
32
+ helloRelay: string | null;
33
+ }
34
+ /**
35
+ * Parse and validate the argv after `seat-proxy`. Errors name the option, never echo a value.
36
+ * `isDir` and `env` are injected so the check is pure.
37
+ */
38
+ export declare function parseSeatProxyArgs(argv: readonly string[], env: Record<string, string | undefined>, isDir: (p: string) => boolean): {
39
+ ok: true;
40
+ args: SeatProxyArgs;
41
+ } | {
42
+ ok: false;
43
+ error: string;
44
+ };
45
+ /** The first stdin line (CR stripped), or "" on EOF / timeout. Only the first line is ever read. */
46
+ export declare function readKeyLine(stream: NodeJS.ReadableStream, timeoutMs?: number): Promise<string>;
47
+ /** The arm verdict for one door response: forward, or the drop reason. */
48
+ export declare function armVerdict(pathname: string, rawHeaders: readonly string[]): {
49
+ forward: true;
50
+ } | {
51
+ forward: false;
52
+ reason: DropReason;
53
+ };
54
+ /** The dead-key signal in a door response (C13-R7), read from the JSON body's error.type. */
55
+ export declare function deadKeyReason(status: number, body: string): ExitReason | null;
56
+ /** Whether a request path is origin-form and free of dot segments (raw or percent-encoded). */
57
+ export declare function isCleanOriginForm(url: string): boolean;
58
+ export type Route = "messages" | "count_tokens" | "hello" | "telemetry" | "refused";
59
+ /** Where a request goes: the two model POSTs are forwarded; startup paths are answered locally. */
60
+ export declare function routeOf(method: string, pathname: string): Route;
61
+ export interface SeatProxyDeps {
62
+ log: (line: string) => void;
63
+ exit: (code: number) => void;
64
+ turns: TurnOrigins;
65
+ requestId?: () => string;
66
+ /** Overrides REQUEST_BODY_CAP (tests). */
67
+ requestBodyCap?: number;
68
+ /** Overrides DEAD_BODY_CAP (tests). */
69
+ deadBodyCap?: number;
70
+ }
71
+ export interface SeatProxyState {
72
+ dead: ExitReason | null;
73
+ }
74
+ /** The request handler of a seat proxy for `cfg` with lease key `key`. */
75
+ export declare function createSeatProxyHandler(cfg: SeatProxyArgs, key: string, deps: SeatProxyDeps, state?: SeatProxyState): (req: http.IncomingMessage, res: http.ServerResponse) => void;
76
+ export interface SeatProxyMainDeps {
77
+ env: Record<string, string | undefined>;
78
+ stdin: NodeJS.ReadableStream;
79
+ stdout: (line: string) => void;
80
+ stderr: (line: string) => void;
81
+ exit: (code: number) => void;
82
+ home: string;
83
+ isDir: (p: string) => boolean;
84
+ lock: (lockPath: string) => LockResult;
85
+ relayFor: (socketPath: string) => HelloRelay;
86
+ keyTimeoutMs?: number;
87
+ /** Loopback listen addresses (tests override them to exercise a failed bind). */
88
+ hostV4?: string;
89
+ hostV6?: string;
90
+ }
91
+ export declare function defaultMainDeps(): SeatProxyMainDeps;
92
+ /**
93
+ * The 'connect' listener for a seat proxy's loopback servers: a CONNECT (a tunnel request, never
94
+ * origin-form) is refused with a 400 and the socket closed. Without a listener Node would drop the
95
+ * socket silently; this makes the refusal explicit and logged.
96
+ */
97
+ export declare function refuseConnect(log: (line: string) => void): (req: http.IncomingMessage, socket: Duplex) => void;
98
+ export interface RunningSeatProxy {
99
+ port: number;
100
+ socketPath: string;
101
+ close: () => Promise<void>;
102
+ }
103
+ /**
104
+ * `mnemom agent host seat-proxy …`: validate, read the key, take the conversation lock, listen,
105
+ * print READY. Resolves with the running proxy, or with an exit code it already passed to `exit`.
106
+ */
107
+ export declare function seatProxyMain(argv: readonly string[], deps?: SeatProxyMainDeps): Promise<RunningSeatProxy | number>;