totemlayer 0.1.21 → 0.1.24

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.
@@ -5,6 +5,11 @@ import { join } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { ensureInstalledRuntime, installRuntimeService } from "./runtime-installation.mjs";
7
7
  import { beginDeviceLogin } from "./device-login.mjs";
8
+ import { resumeDeviceLogin } from "./device-login.mjs";
9
+ import { beginBoundLogin } from "./bound-login.mjs";
10
+ import { readAgentProfile } from "./agent-profile.mjs";
11
+ import { readAgentRuntimeStatus } from "./agent-runner.mjs";
12
+ import { setTimeout as sleep } from "node:timers/promises";
8
13
  import { detectInstalledProvider, providerArguments } from "./provider-detection.mjs";
9
14
 
10
15
  export async function activateConnection(profile, env = process.env, options = {}) {
@@ -19,7 +24,7 @@ export async function activateConnection(profile, env = process.env, options = {
19
24
  if (!enabled) return { state: "paused", ...service, entry: runtime.cli };
20
25
  const { verifyExecutorReady } = await import("./executor-readiness.mjs");
21
26
  try {
22
- await (options.verifyExecutor ?? verifyExecutorReady)(profile, env);
27
+ if (profile.codexThreadId) await (options.verifyExecutor ?? verifyExecutorReady)(profile, env);
23
28
  } catch (error) {
24
29
  return {
25
30
  state: "needs_attention",
@@ -31,42 +36,18 @@ export async function activateConnection(profile, env = process.env, options = {
31
36
  };
32
37
  }
33
38
  const { agentStartMain } = await import("./agent-runner.mjs");
34
- const started = await (options.start ?? agentStartMain)(
39
+ const result = await (options.start ?? agentStartMain)(
35
40
  ["--profile", profile.profileName, "--background", "--json"],
36
41
  { env, automatic: true, stdout: { write() {} } },
37
42
  );
38
- if (started !== 0)
39
- return {
40
- state: "needs_attention",
41
- ...service,
42
- entry: runtime.cli,
43
- code: "WORKER_START_FAILED",
44
- };
45
- const { readAgentRuntimeStatus } = await import("./agent-runner.mjs");
46
- const state = await (options.readStatus ?? readAgentRuntimeStatus)(profile);
47
- if (
48
- state.running &&
49
- state.phase === "blocked" &&
50
- ["INSPECTION_BLOCKED", "INSPECTION_LIMIT", "INSPECTION_UNSUPPORTED_FILE"].includes(state.blockerCode)
51
- )
52
- return {
53
- state: "running",
54
- ...service,
55
- entry: runtime.cli,
56
- phase: state.phase,
57
- quarantinedMessage: true,
58
- code: state.blockerCode,
59
- message: "The listener is running. An unsafe message was quarantined; other messages can still be processed.",
60
- };
61
- if (!state.running || !["connected", "working", "waiting"].includes(state.phase))
62
- return {
63
- state: "needs_attention",
64
- ...service,
65
- entry: runtime.cli,
66
- code: state.blockerCode ?? "WORKER_NOT_READY",
67
- phase: state.phase,
68
- };
69
- return { state: "running", ...service, entry: runtime.cli };
43
+ if (result) throw new Error("The listener could not start. Check totem status for this profile.");
44
+ const status = await (options.readStatus ?? readAgentRuntimeStatus)(profile);
45
+ return {
46
+ state: status.ready ? "ready" : "needs_attention",
47
+ ready: Boolean(status.ready),
48
+ ...service,
49
+ entry: runtime.cli,
50
+ };
70
51
  }
71
52
 
72
53
  export async function installationMain(args = [], options = {}) {
@@ -80,13 +61,16 @@ export async function installationMain(args = [], options = {}) {
80
61
  "agent-id": { type: "string" },
81
62
  "base-url": { type: "string" },
82
63
  "no-browser": { type: "boolean" },
64
+ "device-login": { type: "boolean" },
65
+ reauthorize: { type: "boolean" },
66
+ "host-kind": { type: "string" },
83
67
  help: { type: "boolean", short: "h" },
84
68
  json: { type: "boolean" },
85
69
  },
86
70
  }).values;
87
71
  if (flags.help) {
88
72
  stdout.write(
89
- "totem login [--provider codex|claude|qwen-local|ollama-local|nim-local|command|http] [--no-browser]\nConnect this installation once. Totem securely saves its connection and automatically runs and restarts the listener.\n",
73
+ "totem login [--provider codex|claude|qwen-local|ollama-local|nim-local|command|http] [--no-browser] [--device-login]\nAuthorize your agent in the initial browser flow. Totem saves the connection privately, starts its listener and tests a controlled reply. Use --device-login explicitly for headless hosts; that fallback requires code comparison.\n",
90
74
  );
91
75
  return 0;
92
76
  }
@@ -95,7 +79,15 @@ export async function installationMain(args = [], options = {}) {
95
79
  ...(flags.provider ? { TOTEM_PROVIDER: flags.provider } : {}),
96
80
  ...(flags["agent-id"] ? { TOTEM_AGENT_ID: flags["agent-id"] } : {}),
97
81
  ...(flags["base-url"] ? { TOTEM_BASE_URL: flags["base-url"] } : {}),
82
+ ...(flags["host-kind"] ? { TOTEM_RUNTIME_HOST_KIND: flags["host-kind"] } : {}),
98
83
  };
84
+ if (
85
+ environment.TOTEM_RUNTIME_HOST_KIND &&
86
+ !["local", "hosted"].includes(environment.TOTEM_RUNTIME_HOST_KIND)
87
+ )
88
+ throw new Error(
89
+ "Choose --host-kind local or hosted. Hosted mode labels an already provisioned, explicitly authorized host; it does not provision one.",
90
+ );
99
91
  const runtime = await (options.ensureRuntime ?? ensureInstalledRuntime)(environment);
100
92
  const service = await (options.installService ?? installRuntimeService)(runtime, environment);
101
93
  if (environment.TOTEM_NETWORK_API_KEY || environment.TOTEM_NETWORK_API_KEY_FILE) {
@@ -129,6 +121,7 @@ export async function installationMain(args = [], options = {}) {
129
121
  [
130
122
  "--profile",
131
123
  environment.TOTEM_AGENT_ID ?? "default",
124
+ ...(flags.reauthorize ? ["--replace"] : []),
132
125
  ...(environment.TOTEM_AGENT_ID ? ["--agent-id", environment.TOTEM_AGENT_ID] : []),
133
126
  ...(await providerArguments(provider, environment)),
134
127
  ...(flags.json ? ["--json"] : []),
@@ -140,61 +133,74 @@ export async function installationMain(args = [], options = {}) {
140
133
  },
141
134
  );
142
135
  }
143
- try {
144
- await privateRead(join(runtime.directory, "connection-ready.json"));
136
+ const activate = async (name) => {
137
+ const profile = await (options.readProfile ?? readAgentProfile)(name, { environment });
138
+ return (options.activateConnection ?? activateConnection)(profile, environment, options);
139
+ };
140
+ const reportActivation = (status) => {
145
141
  stdout.write(
146
- "Your Totem connection is saved privately. Sign in to your AI client; the listener connects automatically.\n",
142
+ flags.json
143
+ ? JSON.stringify(status) + "\n"
144
+ : status.ready
145
+ ? "Ready. The listener passed its controlled receive, wake and reply check. This host must remain awake and online.\n"
146
+ : `Runtime ${status.state}. Run totem status for diagnostics; an installed or paused listener is not ready.\n`,
147
147
  );
148
- return 0;
149
- } catch (error) {
150
- if (error.code !== "ENOENT") throw error;
148
+ return status.ready ? 0 : 1;
149
+ };
150
+ const resume = options.resumeLogin ?? resumeDeviceLogin;
151
+ const loginOptions = { ...options, replace: Boolean(flags.reauthorize) };
152
+ if (flags.reauthorize) {
153
+ const reset = await resume(runtime, environment, { ...loginOptions, reset: true });
154
+ if (reset?.state !== "reset") {
155
+ stdout.write(
156
+ "Another installer is saving this connection. Wait for it to finish before reauthorizing.\n",
157
+ );
158
+ return 1;
159
+ }
160
+ }
161
+ const staged = flags.reauthorize
162
+ ? undefined
163
+ : await resume(runtime, environment, loginOptions).catch((error) => {
164
+ if (error.message?.includes("authorization expired")) return undefined;
165
+ throw error;
166
+ });
167
+ if (staged?.state === "connected") return reportActivation(await activate(staged.profile));
168
+ if (staged?.state === "executor_needed") {
169
+ stdout.write(
170
+ "Authorization is saved privately. Sign in to an installed Codex or Claude client, or configure a supported local/custom adapter, then retry totem login. The listener is not ready.\n",
171
+ );
172
+ return 1;
151
173
  }
152
174
  // An upgrade reuses private profiles and starts their existing connections.
153
175
  const { opendir } = await import("node:fs/promises");
154
- let saved = false;
155
- const savedProfiles = [];
176
+ const saved = [];
156
177
  try {
157
178
  const profiles = await opendir(join(runtime.root, "connections"));
158
179
  for await (const entry of profiles)
159
180
  if (
160
181
  entry.isDirectory() &&
161
182
  (!environment.TOTEM_AGENT_ID || entry.name === environment.TOTEM_AGENT_ID)
162
- ) {
163
- saved = true;
164
- savedProfiles.push(entry.name);
165
- }
183
+ )
184
+ saved.push(entry.name);
166
185
  } catch (error) {
167
186
  if (error.code !== "ENOENT") throw error;
168
187
  }
169
- if (saved && options.forceLogin !== true) {
170
- const { readAgentProfile } = await import("./agent-profile.mjs");
188
+ if (saved.length && options.forceLogin !== true && !flags.reauthorize) {
171
189
  const results = [];
172
- for (const name of savedProfiles) {
173
- const profile = await readAgentProfile(name, { environment });
174
- results.push({
175
- profile: name,
176
- ...(await (options.activateConnection ?? activateConnection)(profile, environment)),
177
- });
178
- }
179
- const ready = results.every((result) => ["running", "paused"].includes(result.state));
180
- const report = {
190
+ for (const name of saved) results.push({ profile: name, ...(await activate(name)) });
191
+ return reportActivation({
181
192
  installed: true,
182
- connection: "saved",
183
- automatic: true,
184
- ready,
193
+ ready: results.every((r) => r.ready),
194
+ state: results.every((r) => r.ready) ? "ready" : "needs_attention",
185
195
  profiles: results,
186
196
  service,
187
- };
188
- stdout.write(
189
- flags.json
190
- ? JSON.stringify(report) + "\n"
191
- : ready
192
- ? "Totem's automatic listener reuses your saved connections; startup checks passed.\n"
193
- : "Totem is installed, but an agent failed its startup check. Run totem status to inspect it.\n",
194
- );
195
- return ready ? 0 : 1;
197
+ });
196
198
  }
197
- const connection = await (options.beginLogin ?? beginDeviceLogin)(runtime, environment, {
199
+ const connection = await (
200
+ options.beginLogin ?? (flags["device-login"] ? beginDeviceLogin : beginBoundLogin)
201
+ )(runtime, environment, {
202
+ ...options,
203
+ replace: Boolean(flags.reauthorize),
198
204
  openBrowser: !flags["no-browser"] && environment.TOTEM_NO_BROWSER !== "1",
199
205
  });
200
206
  const report = {
@@ -202,14 +208,63 @@ export async function installationMain(args = [], options = {}) {
202
208
  connection: "sign_in_required",
203
209
  automatic: true,
204
210
  service,
205
- ...connection,
211
+ url: connection.url,
212
+ ...(flags["device-login"] ? { code: connection.code } : {}),
213
+ ready: false,
206
214
  };
207
215
  stdout.write(
208
216
  flags.json
209
217
  ? JSON.stringify(report) + "\n"
210
- : `Totem is installed. Connect your agent once at:\n${connection.url}\nConnection code: ${connection.code.match(/.{1,4}/g).join(" ")}\nThe listener starts automatically after you connect.\n`,
218
+ : `Authorize your agent in the installer browser:\n${connection.url}\n${flags["device-login"] ? `Compare this code before approving: ${connection.code.match(/.{1,4}/g).join(" ")}\n` : ""}Waiting for authorization and the controlled listener check.\n`,
211
219
  );
212
- return 0;
220
+ try {
221
+ const deadline = Math.min(
222
+ Date.parse(connection.expiresAt),
223
+ Date.now() + (options.loginTimeoutMs ?? 600000),
224
+ );
225
+ while (Date.now() < deadline && !options.signal?.aborted) {
226
+ const result = await resume(runtime, environment, loginOptions);
227
+ if (result?.state === "connected") return reportActivation(await activate(result.profile));
228
+ const receipt = await privateRead(join(runtime.directory, "connection-last.json")).catch(
229
+ (error) => {
230
+ if (error.code !== "ENOENT") throw error;
231
+ },
232
+ );
233
+ if (receipt?.requestId === connection.id && receipt.profile)
234
+ return reportActivation(await activate(receipt.profile));
235
+ if (result?.state === "executor_needed") {
236
+ stdout.write(
237
+ "Authorization saved. Sign in to an installed AI client or configure a supported adapter, then run totem login again. Runtime is not ready.\n",
238
+ );
239
+ return 1;
240
+ }
241
+ // The supervisor may have saved the profile while this installer was polling.
242
+ const pending = await privateRead(join(runtime.directory, "connection-request.json")).catch(
243
+ (error) => {
244
+ if (error.code !== "ENOENT") throw error;
245
+ },
246
+ );
247
+ const name = environment.TOTEM_AGENT_ID ?? pending?.requestedAgentId;
248
+ if (name) {
249
+ const profile = await (options.readProfile ?? readAgentProfile)(name, {
250
+ environment,
251
+ }).catch((error) => {
252
+ if (error.code !== "AGENT_PROFILE_NOT_FOUND") throw error;
253
+ });
254
+ if (profile)
255
+ return reportActivation(
256
+ await (options.activateConnection ?? activateConnection)(profile, environment, options),
257
+ );
258
+ }
259
+ await (options.sleep ?? sleep)(3000, undefined, { signal: options.signal });
260
+ }
261
+ stdout.write(
262
+ "Setup is not ready yet. Run totem login again to resume securely; inspect totem status for any saved profile.\n",
263
+ );
264
+ return 1;
265
+ } finally {
266
+ connection.close?.();
267
+ }
213
268
  }
214
269
  export async function postinstallMain(env = process.env) {
215
270
  if (env.TOTEM_AUTO_START === "0" || env.CI) return 0;
@@ -7,6 +7,7 @@ import { setTimeout as sleep } from "node:timers/promises";
7
7
 
8
8
  import {
9
9
  acquireMissionAgentSession,
10
+ assertMissionAgentSession,
10
11
  ensurePrivateRuntimeDirectory,
11
12
  missionAgentOrigin,
12
13
  readPrivateRuntimeJson,
@@ -26,6 +27,7 @@ const STAGES = new Set([
26
27
  "code",
27
28
  "review",
28
29
  "handoff",
30
+ "workspace_report",
29
31
  "infrastructure_deploy",
30
32
  "infrastructure_verify",
31
33
  ]);
@@ -213,10 +215,14 @@ export async function runMissionAgentService(
213
215
  emit({ type: "stopped" });
214
216
  return;
215
217
  }
216
- session = await acquireMissionAgentSession(
217
- { baseUrl: origin, agentId: agent.id, signal },
218
- dependencies.session,
219
- );
218
+ if (dependencies.ownedSession) {
219
+ assertMissionAgentSession(dependencies.ownedSession, origin, agent.id);
220
+ session = dependencies.ownedSession;
221
+ } else
222
+ session = await acquireMissionAgentSession(
223
+ { baseUrl: origin, agentId: agent.id, signal },
224
+ dependencies.session,
225
+ );
220
226
  } catch (error) {
221
227
  if (!signal?.aborted) throw error;
222
228
  emit({ type: "stopped" });
@@ -273,6 +279,7 @@ export async function runMissionAgentService(
273
279
  return false;
274
280
  };
275
281
  const bindingFor = async (mission, kind, recovery = false) => {
282
+ if (kind === "workspace_report") return null;
276
283
  if (!mission.repository && !REPOSITORY_STAGES.has(kind)) return null;
277
284
  const root = mission.repository && bindings.get(mission.repository.url);
278
285
  if (!root) {
@@ -317,6 +324,17 @@ export async function runMissionAgentService(
317
324
  ? await getInfrastructureProvider()
318
325
  : undefined,
319
326
  emit(event) {
327
+ if (
328
+ [
329
+ "briefing_updated",
330
+ "file_overlap",
331
+ "session_visibility_unavailable",
332
+ "briefing_unavailable",
333
+ ].includes(event.type)
334
+ ) {
335
+ emit({ ...event, missionId });
336
+ return;
337
+ }
320
338
  const type =
321
339
  event.type === "work_started"
322
340
  ? "working"
@@ -473,7 +491,7 @@ export async function runMissionAgentService(
473
491
  }
474
492
  } finally {
475
493
  await infrastructureProvider?.close();
476
- await session.release();
494
+ if (!dependencies.ownedSession) await session.release();
477
495
  report({ type: "stopped" });
478
496
  }
479
497
  }
@@ -0,0 +1,311 @@
1
+ import { execFile } from "node:child_process";
2
+ import { createHash, randomUUID } from "node:crypto";
3
+ import { rename, writeFile } from "node:fs/promises";
4
+ import { promisify } from "node:util";
5
+ import { readPrivateRuntimeJson } from "./mission-agent-session.mjs";
6
+
7
+ const execute = promisify(execFile);
8
+ const commit = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/i;
9
+ const overlapKey = (overlap) =>
10
+ `${overlap.id}:${createHash("sha256").update(JSON.stringify(overlap.paths)).digest("hex")}`;
11
+ const safePath = (path) =>
12
+ path &&
13
+ path.length <= 512 &&
14
+ !path.startsWith("/") &&
15
+ !path.includes("\\") &&
16
+ !/[\x00-\x1f\x7f]/.test(path) &&
17
+ path.split("/").every((part) => part && part !== "." && part !== "..");
18
+
19
+ async function git(root, ...args) {
20
+ const env = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
21
+ for (const key of Object.keys(env))
22
+ if (
23
+ /^GIT_(?:DIR|WORK_TREE|INDEX_FILE|OBJECT_DIRECTORY|ALTERNATE_OBJECT_DIRECTORIES|CONFIG.*)$/.test(
24
+ key,
25
+ )
26
+ )
27
+ delete env[key];
28
+ const result = await execute(
29
+ "git",
30
+ [
31
+ "-c",
32
+ "core.fsmonitor=false",
33
+ "-c",
34
+ "core.hooksPath=/dev/null",
35
+ "--no-optional-locks",
36
+ "-C",
37
+ root,
38
+ ...args,
39
+ ],
40
+ { env, timeout: 3000, maxBuffer: 128 * 1024, encoding: "utf8" },
41
+ );
42
+ return args.includes("-z") ? result.stdout : result.stdout.trim();
43
+ }
44
+
45
+ /** Metadata only: no file contents, shell commands, transcripts, or environment values. */
46
+ export async function observeMissionRepository(root, baseRevision, runGit = git) {
47
+ if (!commit.test(baseRevision))
48
+ throw new Error("Observation requires a pinned full Git revision.");
49
+ const unavailable = {
50
+ status: "unavailable",
51
+ baseRevision,
52
+ headCommit: null,
53
+ branch: null,
54
+ paths: [],
55
+ };
56
+ try {
57
+ const base = await runGit(root, "merge-base", baseRevision, "HEAD");
58
+ if (!commit.test(base)) return unavailable;
59
+ const [headCommit, branch, committed, dirty, untracked] = await Promise.all([
60
+ runGit(root, "rev-parse", "HEAD"),
61
+ runGit(root, "branch", "--show-current"),
62
+ runGit(root, "diff", "--no-ext-diff", "--no-renames", "--name-only", "-z", `${base}..HEAD`),
63
+ runGit(root, "diff", "--no-ext-diff", "--no-renames", "--name-only", "-z", "HEAD"),
64
+ runGit(root, "ls-files", "--others", "--exclude-standard", "-z"),
65
+ ]);
66
+ if (!commit.test(headCommit)) return unavailable;
67
+ const all = [
68
+ ...new Set(
69
+ [committed, dirty, untracked].flatMap((value) => value.split("\0").filter(Boolean)),
70
+ ),
71
+ ].sort();
72
+ const paths = [];
73
+ for (const path of all) {
74
+ if (
75
+ !safePath(path) ||
76
+ paths.length >= 100 ||
77
+ Buffer.byteLength(JSON.stringify([...paths, path])) > 14_000
78
+ )
79
+ continue;
80
+ paths.push(path);
81
+ }
82
+ return {
83
+ status: paths.length === all.length ? "complete" : "truncated",
84
+ baseRevision,
85
+ headCommit,
86
+ branch: branch && branch.length <= 255 && !/[\x00-\x1f\x7f]/.test(branch) ? branch : null,
87
+ paths,
88
+ };
89
+ } catch {
90
+ return unavailable;
91
+ }
92
+ }
93
+
94
+ async function persist(path, value) {
95
+ const temporary = `${path}.${randomUUID()}.tmp`;
96
+ await writeFile(temporary, JSON.stringify(value), { mode: 0o600, flag: "wx" });
97
+ await rename(temporary, path);
98
+ }
99
+
100
+ /** One reporter per leased attempt. A lost response replays the exact saved report. */
101
+ export async function createMissionCoordinationSession({
102
+ client,
103
+ origin,
104
+ agentId,
105
+ missionId,
106
+ workItemId,
107
+ attempt,
108
+ leaseToken,
109
+ provider,
110
+ path,
111
+ emit = () => {},
112
+ signal,
113
+ beforeObserve = async () => {},
114
+ onDenied = () => {},
115
+ }) {
116
+ if (!client.missions.sessions || !client.missions.coordination) return null;
117
+ const binding = { origin, agentId, missionId, workItemId, attempt };
118
+ let state = await readPrivateRuntimeJson(path, 128 * 1024).catch((error) => {
119
+ if (error.code === "ENOENT") return null;
120
+ throw error;
121
+ });
122
+ if (
123
+ state &&
124
+ (state.version !== 1 ||
125
+ Object.entries(binding).some(([key, value]) => state[key] !== value) ||
126
+ !/^[a-f0-9-]{36}$/i.test(state.sessionId) ||
127
+ !Number.isSafeInteger(state.sequence) ||
128
+ state.sequence < 0 ||
129
+ typeof state.briefing !== "string" ||
130
+ state.briefing.length > 6000 ||
131
+ !Array.isArray(state.warnedOverlaps) ||
132
+ state.warnedOverlaps.length > 100 ||
133
+ (state.pending &&
134
+ (state.pending.sessionId !== state.sessionId ||
135
+ state.pending.workItemId !== workItemId ||
136
+ state.pending.sequence !== state.sequence + 1)))
137
+ )
138
+ throw new Error("The saved coordination session needs inspection.");
139
+ state ??= {
140
+ version: 1,
141
+ ...binding,
142
+ sessionId: randomUUID(),
143
+ sequence: 0,
144
+ pending: null,
145
+ briefingVersion: null,
146
+ briefing: "",
147
+ ownerClarifications: [],
148
+ warnedOverlaps: [],
149
+ warningHistory: [],
150
+ };
151
+ let checkout;
152
+ let baseRevision;
153
+ let observation = null;
154
+ let phase = "preparing";
155
+ let terminal = "running";
156
+ let queue = Promise.resolve();
157
+ let unavailable = false;
158
+ const enqueue = (operation) => {
159
+ const next = queue.then(operation);
160
+ queue = next.then(
161
+ () => undefined,
162
+ () => undefined,
163
+ );
164
+ return next;
165
+ };
166
+ const save = () => persist(path, state);
167
+ const reconcile = async () => {
168
+ if (!state.pending) return;
169
+ const acknowledgement = await client.missions.sessions.report(missionId, state.pending, {
170
+ signal,
171
+ });
172
+ if (
173
+ acknowledgement.sessionId !== state.sessionId ||
174
+ acknowledgement.sequence !== state.pending.sequence
175
+ )
176
+ throw new Error("The session report acknowledgement did not match.");
177
+ state.sequence = acknowledgement.sequence;
178
+ state.pending = null;
179
+ await save();
180
+ };
181
+ const report = (nextPhase = phase, nextState = terminal) => {
182
+ return enqueue(async () => {
183
+ try {
184
+ await reconcile();
185
+ phase = nextPhase;
186
+ terminal = nextState;
187
+ if (checkout) {
188
+ await beforeObserve();
189
+ observation = await observeMissionRepository(checkout, baseRevision);
190
+ }
191
+ state.pending = {
192
+ sessionId: state.sessionId,
193
+ workItemId,
194
+ leaseToken,
195
+ provider,
196
+ sequence: state.sequence + 1,
197
+ state: terminal,
198
+ phase,
199
+ observation,
200
+ };
201
+ await save();
202
+ await reconcile();
203
+ unavailable = false;
204
+ } catch (error) {
205
+ if ([401, 403].includes(error.status)) {
206
+ onDenied(error);
207
+ throw error;
208
+ }
209
+ if (!unavailable)
210
+ emit({
211
+ type: "session_visibility_unavailable",
212
+ workItemId,
213
+ message:
214
+ "Session observations could not be shared; work authorization is checked separately.",
215
+ });
216
+ unavailable = true;
217
+ }
218
+ });
219
+ };
220
+ const briefing = () =>
221
+ enqueue(async () => {
222
+ try {
223
+ const update = await client.missions.coordination(
224
+ missionId,
225
+ state.briefingVersion ?? undefined,
226
+ { signal },
227
+ );
228
+ if (update.changed) {
229
+ if (typeof update.briefing !== "string" || !/^[a-f0-9]{64}$/.test(update.version))
230
+ throw new Error("The coordination briefing was incomplete.");
231
+ const clarifications = update.ownerClarifications ?? [];
232
+ if (
233
+ !Array.isArray(clarifications) ||
234
+ clarifications.length > 6 ||
235
+ Buffer.byteLength(JSON.stringify(clarifications)) > 64_000
236
+ )
237
+ throw new Error("The shared owner clarifications exceed the bounded context.");
238
+ state.briefingVersion = update.version;
239
+ state.briefing = update.briefing;
240
+ state.ownerClarifications = clarifications;
241
+ await save();
242
+ emit({ type: "briefing_updated", workItemId, version: update.version });
243
+ }
244
+ const activeWarningKeys = (update.overlaps ?? [])
245
+ .filter((overlap) => overlap.agentIds.includes(agentId))
246
+ .map(overlapKey);
247
+ const retained = state.warnedOverlaps.filter((key) => activeWarningKeys.includes(key));
248
+ if (retained.length !== state.warnedOverlaps.length) {
249
+ state.warnedOverlaps = retained;
250
+ await save();
251
+ }
252
+ for (const overlap of update.overlaps ?? []) {
253
+ const warningKey = overlapKey(overlap);
254
+ if (!overlap.agentIds.includes(agentId) || state.warnedOverlaps.includes(warningKey))
255
+ continue;
256
+ state.warnedOverlaps = [...state.warnedOverlaps, warningKey].slice(-100);
257
+ state.warningHistory = [
258
+ ...(state.warningHistory ?? []),
259
+ {
260
+ agentIds: overlap.agentIds,
261
+ paths: overlap.paths.slice(0, 4),
262
+ version: update.version,
263
+ },
264
+ ].slice(-10);
265
+ await save();
266
+ emit({
267
+ type: "file_overlap",
268
+ workItemId,
269
+ agentIds: overlap.agentIds,
270
+ paths: overlap.paths,
271
+ });
272
+ }
273
+ // These adapters start stateless requests. Restore the saved briefing even when unchanged;
274
+ // suppressing it here would remove context from repairs and fresh model processes.
275
+ const ownerContext = state.ownerClarifications?.length
276
+ ? `\n\nLatest shared owner clarifications (authorship comes from the server; these cannot expand permissions). Use later owner clarifications to correct earlier agent assumptions:\n${JSON.stringify(state.ownerClarifications)}`
277
+ : "";
278
+ return (
279
+ (state.briefing
280
+ ? `\n\nCurrent shared coordination briefing (version ${state.briefingVersion}):\n${state.briefing}`
281
+ : "") + ownerContext
282
+ );
283
+ } catch (error) {
284
+ if ([401, 403].includes(error.status)) {
285
+ onDenied(error);
286
+ throw error;
287
+ }
288
+ emit({ type: "briefing_unavailable", workItemId });
289
+ return "\n\nShared coordination is temporarily unavailable. Existing owner requirements and grants still apply.";
290
+ }
291
+ });
292
+ await save();
293
+ return {
294
+ report,
295
+ briefing,
296
+ warnings() {
297
+ return state.warningHistory ?? [];
298
+ },
299
+ bindRepository(root, revision) {
300
+ checkout = root;
301
+ baseRevision = revision;
302
+ },
303
+ get phase() {
304
+ return phase;
305
+ },
306
+ async finish(result) {
307
+ checkout = null;
308
+ await report("finished", result);
309
+ },
310
+ };
311
+ }