@adhdev/daemon-core 0.9.82-rc.187 → 0.9.82-rc.189

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 (42) hide show
  1. package/dist/boot/daemon-lifecycle.d.ts +1 -0
  2. package/dist/commands/cli-manager.d.ts +2 -1
  3. package/dist/commands/router.d.ts +5 -1
  4. package/dist/git/git-commands.d.ts +2 -0
  5. package/dist/git/git-types.d.ts +2 -0
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.js +459 -38
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +458 -38
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/providers/cli-provider-instance.d.ts +4 -0
  12. package/dist/providers/contracts.d.ts +31 -0
  13. package/dist/providers/sdk/v1/types/common/index.d.ts +35 -1
  14. package/dist/providers/spec/adapter.d.ts +4 -0
  15. package/dist/providers/spec/driver.d.ts +10 -1
  16. package/dist/providers/spec/evaluator.d.ts +9 -1
  17. package/dist/providers/spec/schema.gen.d.ts +38 -0
  18. package/dist/providers/spec/types.d.ts +25 -0
  19. package/dist/repo-mesh-types.d.ts +6 -0
  20. package/package.json +1 -1
  21. package/src/boot/daemon-lifecycle.ts +2 -0
  22. package/src/commands/chat-commands.ts +26 -0
  23. package/src/commands/cli-manager.ts +52 -14
  24. package/src/commands/router.ts +35 -4
  25. package/src/git/git-commands.ts +20 -2
  26. package/src/git/git-status.ts +35 -6
  27. package/src/git/git-types.ts +2 -0
  28. package/src/index.ts +1 -1
  29. package/src/mesh/mesh-events.ts +7 -0
  30. package/src/providers/cli-provider-instance.ts +110 -9
  31. package/src/providers/contracts.d.ts +55 -0
  32. package/src/providers/contracts.ts +35 -0
  33. package/src/providers/provider-schema.ts +56 -1
  34. package/src/providers/sdk/v1/schemas/cli/provider.schema.json +46 -0
  35. package/src/providers/sdk/v1/types/common/index.ts +19 -0
  36. package/src/providers/spec/adapter.ts +8 -0
  37. package/src/providers/spec/driver.ts +74 -2
  38. package/src/providers/spec/evaluator.ts +39 -3
  39. package/src/providers/spec/schema.gen.ts +28 -1
  40. package/src/providers/spec/schema.json +26 -2
  41. package/src/providers/spec/types.ts +25 -0
  42. package/src/repo-mesh-types.ts +6 -0
package/dist/index.js CHANGED
@@ -275,6 +275,8 @@ async function getGitRepoStatus(workspace, options = {}) {
275
275
  if (includeSubmodules) {
276
276
  submodules = await getSubmoduleStatuses(repo, options);
277
277
  }
278
+ const submoduleDirty = (submodules || []).some((submodule) => submodule.dirty || submodule.outOfSync || !!submodule.error);
279
+ const dirty = parsed.staged + parsed.modified + parsed.untracked + parsed.deleted + parsed.renamed > 0 || parsed.conflictFiles.length > 0 || stashCount > 0 || submoduleDirty;
278
280
  return {
279
281
  workspace: repo.workspace,
280
282
  repoRoot: repo.repoRoot,
@@ -293,6 +295,7 @@ async function getGitRepoStatus(workspace, options = {}) {
293
295
  untracked: parsed.untracked,
294
296
  deleted: parsed.deleted,
295
297
  renamed: parsed.renamed,
298
+ dirty,
296
299
  hasConflicts: parsed.conflictFiles.length > 0,
297
300
  conflictFiles: parsed.conflictFiles,
298
301
  stashCount,
@@ -466,6 +469,7 @@ function emptyStatus(workspace, lastCheckedAt, error) {
466
469
  untracked: 0,
467
470
  deleted: 0,
468
471
  renamed: 0,
472
+ dirty: false,
469
473
  hasConflicts: false,
470
474
  conflictFiles: [],
471
475
  stashCount: 0,
@@ -478,17 +482,33 @@ async function getSubmoduleStatuses(repo, options) {
478
482
  if (!repo.repoRoot) return [];
479
483
  try {
480
484
  const result = await runGit(repo, ["submodule", "status", "--recursive"], options);
481
- return parseSubmoduleStatusOutput(result.stdout, repo.repoRoot, options.submoduleIgnorePaths);
485
+ const submodules = parseSubmoduleStatusOutput(result.stdout, repo.repoRoot, options.submoduleIgnorePaths);
486
+ await Promise.all(submodules.map((submodule) => enrichSubmoduleWorktreeStatus(repo, submodule, options)));
487
+ return submodules;
482
488
  } catch {
483
489
  return [];
484
490
  }
485
491
  }
492
+ async function enrichSubmoduleWorktreeStatus(repo, submodule, options) {
493
+ try {
494
+ const result = await runGit(repo, ["status", "--porcelain=v2", "--branch"], {
495
+ ...options,
496
+ cwd: submodule.repoPath
497
+ });
498
+ const parsed = parsePorcelainV2Status(result.stdout);
499
+ const dirty = parsed.staged + parsed.modified + parsed.untracked + parsed.deleted + parsed.renamed > 0 || parsed.conflictFiles.length > 0;
500
+ submodule.dirty = submodule.dirty || dirty;
501
+ } catch (error) {
502
+ submodule.dirty = true;
503
+ submodule.error = formatGitError(error);
504
+ }
505
+ }
486
506
  function parseSubmoduleStatusOutput(output, repoRoot, ignorePaths) {
487
507
  const submodules = [];
488
508
  const ignoreSet = new Set(ignorePaths || []);
489
509
  for (const line of output.split("\n")) {
490
510
  if (!line.trim()) continue;
491
- const match = line.match(/^([\-+\s])([0-9a-f]{40})\s+(\S+)(?:\s+\(([^)]+)\))?/);
511
+ const match = line.match(/^([\-+U\s])([0-9a-f]{40})\s+(\S+)(?:\s+\(([^)]+)\))?/);
492
512
  if (!match) continue;
493
513
  const prefix = match[1];
494
514
  const commit = match[2];
@@ -498,8 +518,8 @@ function parseSubmoduleStatusOutput(output, repoRoot, ignorePaths) {
498
518
  path: path40,
499
519
  commit,
500
520
  repoPath: repoRoot + "/" + path40,
501
- dirty: prefix === "+",
502
- outOfSync: prefix === "-",
521
+ dirty: prefix === "U",
522
+ outOfSync: prefix === "-" || prefix === "+",
503
523
  lastCheckedAt: Date.now()
504
524
  });
505
525
  }
@@ -5589,6 +5609,8 @@ function handleMeshForwardEvent(components, payload) {
5589
5609
  const nodeId = readNonEmptyString2(payload.nodeId);
5590
5610
  const workspace = readNonEmptyString2(payload.workspace);
5591
5611
  const nodeLabel = nodeId ? `Node '${nodeId}'` : workspace ? `Agent at ${workspace}` : "Remote agent";
5612
+ const relayModalMessage = readNonEmptyString2(payload.modalMessage);
5613
+ const relayModalButtons = Array.isArray(payload.modalButtons) ? payload.modalButtons.filter((b) => typeof b === "string" && b.trim().length > 0) : null;
5592
5614
  return injectMeshSystemMessage(components, {
5593
5615
  meshId,
5594
5616
  nodeId,
@@ -5606,6 +5628,8 @@ function handleMeshForwardEvent(components, payload) {
5606
5628
  startedAt: readNonEmptyString2(payload.startedAt),
5607
5629
  completedAt: readNonEmptyString2(payload.completedAt),
5608
5630
  retryOfJobId: readNonEmptyString2(payload.retryOfJobId),
5631
+ ...relayModalMessage ? { modalMessage: relayModalMessage } : {},
5632
+ ...relayModalButtons && relayModalButtons.length > 0 ? { modalButtons: relayModalButtons } : {},
5609
5633
  ...payload.result && typeof payload.result === "object" && !Array.isArray(payload.result) ? { result: payload.result } : {},
5610
5634
  ...payload.completionDiagnostic && typeof payload.completionDiagnostic === "object" && !Array.isArray(payload.completionDiagnostic) ? { completionDiagnostic: payload.completionDiagnostic } : {},
5611
5635
  ...payload.workerResult && typeof payload.workerResult === "object" && !Array.isArray(payload.workerResult) ? { workerResult: payload.workerResult } : {},
@@ -6157,6 +6181,52 @@ var init_provider_schema = __esm({
6157
6181
  properties: { mode: { const: "env_var" }, name: { type: "string" } }
6158
6182
  }
6159
6183
  ]
6184
+ },
6185
+ delegatedWorkerIsolation: {
6186
+ description: "Provider-declared launch isolation for coordinator-spawned worker sessions. Keeps worker-only sessions from inheriting coordinator MCP/tools/config.",
6187
+ type: "object",
6188
+ additionalProperties: false,
6189
+ properties: {
6190
+ env: {
6191
+ type: "object",
6192
+ additionalProperties: false,
6193
+ properties: {
6194
+ unset: {
6195
+ type: "array",
6196
+ items: { type: "string", minLength: 1 }
6197
+ }
6198
+ }
6199
+ },
6200
+ args: {
6201
+ type: "array",
6202
+ items: {
6203
+ oneOf: [
6204
+ {
6205
+ type: "object",
6206
+ additionalProperties: false,
6207
+ required: ["mode", "flag"],
6208
+ properties: {
6209
+ mode: { const: "empty_mcp_config" },
6210
+ flag: { type: "string", minLength: 1 },
6211
+ strictFlag: { type: "string", minLength: 1 }
6212
+ }
6213
+ },
6214
+ {
6215
+ type: "object",
6216
+ additionalProperties: false,
6217
+ required: ["mode", "flag", "key", "value"],
6218
+ properties: {
6219
+ mode: { const: "config_override" },
6220
+ flag: { type: "string", minLength: 1 },
6221
+ key: { type: "string", minLength: 1 },
6222
+ value: { type: "string", minLength: 1 },
6223
+ dedupeKey: { type: "string", minLength: 1 }
6224
+ }
6225
+ }
6226
+ ]
6227
+ }
6228
+ }
6229
+ }
6160
6230
  }
6161
6231
  }
6162
6232
  },
@@ -11290,6 +11360,7 @@ __export(index_exports, {
11290
11360
  probeCdpPort: () => probeCdpPort,
11291
11361
  queuePendingMeshCoordinatorEvent: () => queuePendingMeshCoordinatorEvent,
11292
11362
  readAntigravityCliSession: () => readSession3,
11363
+ readCachedInlineMeshActiveSessionDetails: () => readCachedInlineMeshActiveSessionDetails,
11293
11364
  readChatHistory: () => readChatHistory,
11294
11365
  readClaudeCliSession: () => readSession,
11295
11366
  readCodexCliSession: () => readSession2,
@@ -12334,7 +12405,14 @@ async function handleGitCommand(command, args, services = defaultGitCommandServi
12334
12405
  switch (command) {
12335
12406
  case "git_status": {
12336
12407
  if (!services.getStatus) return serviceNotImplemented(command);
12337
- const status = await runService(() => services.getStatus({ workspace, refreshUpstream: optionalBoolean(args?.refreshUpstream) }));
12408
+ const submoduleIgnorePaths = Array.isArray(args?.submoduleIgnorePaths) ? args.submoduleIgnorePaths.filter((value) => typeof value === "string" && value.trim().length > 0) : void 0;
12409
+ const statusParams = { workspace };
12410
+ const refreshUpstream = optionalBoolean(args?.refreshUpstream);
12411
+ const includeSubmodules = optionalBoolean(args?.includeSubmodules);
12412
+ if (refreshUpstream !== void 0) statusParams.refreshUpstream = refreshUpstream;
12413
+ if (includeSubmodules !== void 0) statusParams.includeSubmodules = includeSubmodules;
12414
+ if (submoduleIgnorePaths && submoduleIgnorePaths.length > 0) statusParams.submoduleIgnorePaths = submoduleIgnorePaths;
12415
+ const status = await runService(() => services.getStatus(statusParams));
12338
12416
  return "success" in status ? status : { success: true, status };
12339
12417
  }
12340
12418
  case "git_diff_summary": {
@@ -12467,6 +12545,14 @@ async function gitCheckpoint(workspace, message, includeUntracked) {
12467
12545
  if (statusResult.hasConflicts) {
12468
12546
  throw new GitCommandError("conflict", "Repository has conflicts \u2014 resolve before checkpointing");
12469
12547
  }
12548
+ const dirtySubmodules = (statusResult.submodules || []).filter((submodule) => submodule.dirty);
12549
+ if (dirtySubmodules.length > 0) {
12550
+ const paths = dirtySubmodules.map((submodule) => submodule.path).join(", ");
12551
+ throw new GitCommandError(
12552
+ "dirty_index_required",
12553
+ `Repository has dirty submodules that must be checkpointed first: ${paths}. Checkpoint or commit each dirty submodule, then checkpoint this repository to record gitlink changes.`
12554
+ );
12555
+ }
12470
12556
  const addArgs = includeUntracked ? ["-A"] : ["-u"];
12471
12557
  await runGit(repo, ["add", ...addArgs], { cwd: repoRoot });
12472
12558
  const fullMsg = `adhdev: checkpoint ${message}`;
@@ -21788,6 +21874,14 @@ function hasVisibleAssistantMessage(messages) {
21788
21874
  return String(message.content || "").trim().length > 0;
21789
21875
  });
21790
21876
  }
21877
+ function hasFinalVisibleAssistantMessage(messages) {
21878
+ if (!Array.isArray(messages)) return false;
21879
+ const visible = filterUserFacingChatMessages(messages);
21880
+ const last = visible[visible.length - 1];
21881
+ const role = typeof last?.role === "string" ? last.role.trim().toLowerCase() : "";
21882
+ const content = last ? flattenContent(last.content).trim() : "";
21883
+ return (role === "assistant" || role === "model") && content.length > 0;
21884
+ }
21791
21885
  function shouldTrustCliAdapterTerminalStatus(parsedStatus, activeModal, adapter, adapterStatus) {
21792
21886
  if (!isGeneratingLikeStatus(parsedStatus)) return false;
21793
21887
  if (hasNonEmptyModalButtons(activeModal)) return false;
@@ -22642,6 +22736,18 @@ async function handleReadChat(h, args) {
22642
22736
  });
22643
22737
  }
22644
22738
  }
22739
+ if (isGeneratingLikeStatus(selectedStatus) && selectedTranscriptAuthority === "provider" && !hasNonEmptyModalButtons(activeModal) && hasFinalVisibleAssistantMessage(selectedMessages)) {
22740
+ selectedStatus = "idle";
22741
+ selectedMessages = finalizeStreamingMessagesWhenIdle(selectedMessages, selectedStatus);
22742
+ messageSource = {
22743
+ ...messageSource,
22744
+ statusReconciled: {
22745
+ from: returnedStatus,
22746
+ to: "idle",
22747
+ reason: "provider_native_final_assistant"
22748
+ }
22749
+ };
22750
+ }
22645
22751
  LOG.debug("Command", `[read_chat] cli-like parsed provider=${adapter.cliType} target=${String(args?.targetSessionId || "")} adapterStatus=${String(adapterStatus.status || "")} parsedStatus=${String(parsedRecord.status || "")} parsedMsgCount=${parsedRecord.messages.length} returnedMsgCount=${returnedMessages.length}`);
22646
22752
  return buildReadChatCommandResult({
22647
22753
  messages: selectedMessages,
@@ -25858,6 +25964,13 @@ var TerminalAdapter = class {
25858
25964
  snapshot() {
25859
25965
  return this.lastScreen || this.computeScreen();
25860
25966
  }
25967
+ getCursorPosition() {
25968
+ const buf = this.term.buffer.active;
25969
+ return {
25970
+ row: Math.max(0, buf.cursorY ?? 0),
25971
+ col: Math.max(0, buf.cursorX ?? 0)
25972
+ };
25973
+ }
25861
25974
  kill() {
25862
25975
  this.stopTimers();
25863
25976
  try {
@@ -25962,14 +26075,33 @@ function compileLinePattern(ref) {
25962
26075
  const flags = (ref.flags ?? "m").replace(/g/g, "");
25963
26076
  return new RegExp(ref.pattern, flags);
25964
26077
  }
25965
- function matchState(state, sections, fullScreen, trace) {
26078
+ function matchState(state, sections, fullScreen, trace, cursor) {
25966
26079
  const haystack = sectionText(sections, state.when.section, fullScreen);
25967
26080
  const re = compileRegex(state.when);
25968
26081
  if (!re.test(haystack)) {
25969
26082
  trace.push({ kind: "state_skip", text: `state[${state.id}] when ${state.when.section ?? "*"}~/${state.when.regex}/ no match` });
25970
26083
  return { matched: false, title: null };
25971
26084
  }
25972
- trace.push({ kind: "state_match", text: `state[${state.id}] matched via ${state.when.section ?? "*"}~/${state.when.regex}/` });
26085
+ if (cursor !== void 0) {
26086
+ const w = state.when;
26087
+ if (w.cursor_row_min !== void 0 && cursor.row < w.cursor_row_min) {
26088
+ trace.push({ kind: "state_skip", text: `state[${state.id}] cursor row ${cursor.row} < cursor_row_min ${w.cursor_row_min}` });
26089
+ return { matched: false, title: null };
26090
+ }
26091
+ if (w.cursor_row_max !== void 0 && cursor.row > w.cursor_row_max) {
26092
+ trace.push({ kind: "state_skip", text: `state[${state.id}] cursor row ${cursor.row} > cursor_row_max ${w.cursor_row_max}` });
26093
+ return { matched: false, title: null };
26094
+ }
26095
+ if (w.cursor_col_min !== void 0 && cursor.col < w.cursor_col_min) {
26096
+ trace.push({ kind: "state_skip", text: `state[${state.id}] cursor col ${cursor.col} < cursor_col_min ${w.cursor_col_min}` });
26097
+ return { matched: false, title: null };
26098
+ }
26099
+ if (w.cursor_col_max !== void 0 && cursor.col > w.cursor_col_max) {
26100
+ trace.push({ kind: "state_skip", text: `state[${state.id}] cursor col ${cursor.col} > cursor_col_max ${w.cursor_col_max}` });
26101
+ return { matched: false, title: null };
26102
+ }
26103
+ }
26104
+ trace.push({ kind: "state_match", text: `state[${state.id}] matched via ${state.when.section ?? "*"}~/${state.when.regex}/${cursor !== void 0 ? ` cursor=(${cursor.row},${cursor.col})` : ""}` });
25973
26105
  let title = null;
25974
26106
  if (state.extract_title) {
25975
26107
  const titleHay = sectionText(sections, state.extract_title.section, fullScreen);
@@ -26027,17 +26159,20 @@ function extractModal(state, sections, fullScreen, title, trace) {
26027
26159
  trace.push({ kind: "modal", text: `modal_buttons matched ${buttons.length} choices` });
26028
26160
  return { title, buttons };
26029
26161
  }
26030
- function evaluate(spec, screenText) {
26162
+ function evaluate(spec, screenText, cursor) {
26031
26163
  const trace = [];
26032
26164
  const lines = screenText.split("\n");
26033
26165
  const sections = resolveSections(spec, lines);
26034
26166
  for (const s of sections) {
26035
26167
  trace.push({ kind: "section", text: `section[${s.id}] lines [${s.fromLine}, ${s.toLine}) (${s.toLine - s.fromLine} lines)` });
26036
26168
  }
26169
+ if (cursor !== void 0) {
26170
+ trace.push({ kind: "section", text: `cursor (${cursor.row}, ${cursor.col})` });
26171
+ }
26037
26172
  let activeState = null;
26038
26173
  let modal = null;
26039
26174
  for (const st of spec.states) {
26040
- const { matched, title } = matchState(st, sections, screenText, trace);
26175
+ const { matched, title } = matchState(st, sections, screenText, trace, cursor);
26041
26176
  if (!matched) continue;
26042
26177
  const extractedModal = extractModal(st, sections, screenText, title, trace);
26043
26178
  if (st.modal_buttons && !extractedModal) {
@@ -26228,7 +26363,18 @@ var SCHEMA = {
26228
26363
  "additionalProperties": false,
26229
26364
  "properties": {
26230
26365
  "busy_hold_ms": { "type": "integer", "minimum": 0 },
26231
- "startup_grace_ms": { "type": "integer", "minimum": 0 }
26366
+ "startup_grace_ms": { "type": "integer", "minimum": 0 },
26367
+ "completion_idle_after": {
26368
+ "type": "object",
26369
+ "additionalProperties": false,
26370
+ "required": ["regex", "hold_ms"],
26371
+ "properties": {
26372
+ "section": { "type": "string", "minLength": 1 },
26373
+ "regex": { "type": "string", "minLength": 1 },
26374
+ "flags": { "type": "string" },
26375
+ "hold_ms": { "type": "integer", "minimum": 0 }
26376
+ }
26377
+ }
26232
26378
  }
26233
26379
  }
26234
26380
  },
@@ -26294,6 +26440,22 @@ var SCHEMA = {
26294
26440
  "flags": {
26295
26441
  "type": "string",
26296
26442
  "default": "i"
26443
+ },
26444
+ "cursor_row_min": {
26445
+ "type": "integer",
26446
+ "minimum": 0
26447
+ },
26448
+ "cursor_row_max": {
26449
+ "type": "integer",
26450
+ "minimum": 0
26451
+ },
26452
+ "cursor_col_min": {
26453
+ "type": "integer",
26454
+ "minimum": 0
26455
+ },
26456
+ "cursor_col_max": {
26457
+ "type": "integer",
26458
+ "minimum": 0
26297
26459
  }
26298
26460
  }
26299
26461
  },
@@ -26708,6 +26870,30 @@ function resolveSubmitDelayMs(specBeforeSubmit, text) {
26708
26870
  const spec = typeof specBeforeSubmit === "number" && specBeforeSubmit > 0 ? specBeforeSubmit : 0;
26709
26871
  return Math.max(spec, SUBMIT_DELAY_FLOOR_MS + linesBonus);
26710
26872
  }
26873
+ function matchesCompletionIdleRule(spec, ev, screen) {
26874
+ const rule = spec.debounce?.completion_idle_after;
26875
+ if (!rule?.regex) return null;
26876
+ const haystack = rule.section ? ev.sections.find((section) => section.id === rule.section)?.text ?? "" : screen;
26877
+ if (!haystack) return null;
26878
+ try {
26879
+ const regex = new RegExp(rule.regex, rule.flags || "");
26880
+ const match = haystack.match(regex);
26881
+ return match?.[0] || null;
26882
+ } catch {
26883
+ return null;
26884
+ }
26885
+ }
26886
+ function matchesCompletionIdleTargetState(spec, ev, screen) {
26887
+ const target = spec.states.find((state) => state.id === spec.default_state) ?? spec.states.find((state) => state.id === "idle");
26888
+ if (!target?.when?.regex) return false;
26889
+ const haystack = target.when.section ? ev.sections.find((section) => section.id === target.when.section)?.text ?? "" : screen;
26890
+ if (!haystack) return false;
26891
+ try {
26892
+ return new RegExp(target.when.regex, target.when.flags || "i").test(haystack);
26893
+ } catch {
26894
+ return false;
26895
+ }
26896
+ }
26711
26897
  var SpecDriver = class {
26712
26898
  constructor(opts) {
26713
26899
  this.opts = opts;
@@ -26747,6 +26933,8 @@ var SpecDriver = class {
26747
26933
  * because the evaluator already moved past busy by the time the hold
26748
26934
  * kicks in. */
26749
26935
  lastBusyState = null;
26936
+ completionIdleFirstSeenAt = 0;
26937
+ completionIdleKey = "";
26750
26938
  /** Timer that re-runs evaluate() once the hold window expires. Needed
26751
26939
  * because the PTY stops emitting once the agent finishes; without an
26752
26940
  * explicit wake-up there's nothing to trigger the busy → idle
@@ -26797,6 +26985,9 @@ var SpecDriver = class {
26797
26985
  snapshot() {
26798
26986
  return this.adapter.snapshot();
26799
26987
  }
26988
+ getCursorPosition() {
26989
+ return this.adapter.getCursorPosition();
26990
+ }
26800
26991
  shutdown() {
26801
26992
  for (const t of this.delegateTimers.values()) clearTimeout(t);
26802
26993
  this.delegateTimers.clear();
@@ -26861,7 +27052,8 @@ var SpecDriver = class {
26861
27052
  }
26862
27053
  reevaluate(forceEmit = false) {
26863
27054
  const screen = this.adapter.snapshot();
26864
- const ev = evaluate(this.spec, screen);
27055
+ const cursor = this.adapter.getCursorPosition();
27056
+ const ev = evaluate(this.spec, screen, cursor);
26865
27057
  let evState = ev.state;
26866
27058
  const busyHoldMs = this.spec.debounce?.busy_hold_ms ?? BUSY_HOLD_MS;
26867
27059
  if (this.currentStateId === "busy" && evState.id === "idle") {
@@ -26870,10 +27062,40 @@ var SpecDriver = class {
26870
27062
  evState = this.lastBusyState ?? evState;
26871
27063
  }
26872
27064
  }
27065
+ const completionIdleRule = this.spec.debounce?.completion_idle_after;
27066
+ let busyWakeMs = busyHoldMs;
27067
+ if (evState.id === "busy" && completionIdleRule) {
27068
+ const completionKey = matchesCompletionIdleRule(this.spec, ev, screen);
27069
+ if (completionKey) {
27070
+ const now = Date.now();
27071
+ if (completionKey !== this.completionIdleKey) {
27072
+ this.completionIdleKey = completionKey;
27073
+ this.completionIdleFirstSeenAt = now;
27074
+ }
27075
+ const holdMs = Math.max(0, completionIdleRule.hold_ms || 0);
27076
+ const ageMs = now - this.completionIdleFirstSeenAt;
27077
+ if (ageMs >= holdMs) {
27078
+ if (matchesCompletionIdleTargetState(this.spec, ev, screen)) {
27079
+ const idle = this.spec.states.find((state) => state.id === this.spec.default_state) ?? this.spec.states.find((state) => state.id === "idle");
27080
+ evState = idle ? { id: idle.id, label: idle.label, title: null } : { id: "idle", label: "Ready", title: null };
27081
+ } else {
27082
+ busyWakeMs = Math.min(busyWakeMs, 1e3);
27083
+ }
27084
+ } else {
27085
+ busyWakeMs = Math.min(busyWakeMs, Math.max(holdMs - ageMs, 0));
27086
+ }
27087
+ } else {
27088
+ this.completionIdleKey = "";
27089
+ this.completionIdleFirstSeenAt = 0;
27090
+ }
27091
+ } else if (evState.id !== "busy") {
27092
+ this.completionIdleKey = "";
27093
+ this.completionIdleFirstSeenAt = 0;
27094
+ }
26873
27095
  if (evState.id === "busy") {
26874
27096
  this.lastBusyAt = Date.now();
26875
27097
  this.lastBusyState = evState;
26876
- this.scheduleBusyExpiry(busyHoldMs);
27098
+ this.scheduleBusyExpiry(busyWakeMs);
26877
27099
  }
26878
27100
  const changed = forceEmit || evState.id !== this.currentStateId || !shallowSameModal(ev, this.currentEval) || !shallowSameControls(ev, this.currentEval);
26879
27101
  if (this.pickerInProgress) this.tryAdvancePicker(screen);
@@ -27776,6 +27998,8 @@ var CliProviderInstance = class {
27776
27998
  historyWriter;
27777
27999
  runtimeMessages = [];
27778
28000
  lastPersistedHistoryMessages = [];
28001
+ lastAcknowledgedUserInputAt = 0;
28002
+ externalBusyIdleFingerprint = "";
27779
28003
  lastNativeSourceCanonicalCheckAt = 0;
27780
28004
  lastNativeSourceCanonicalCacheKey = void 0;
27781
28005
  cachedSqliteDb = null;
@@ -27904,7 +28128,11 @@ var CliProviderInstance = class {
27904
28128
  typeof adapterStatus?.providerSessionId === "string" ? adapterStatus.providerSessionId : ""
27905
28129
  );
27906
28130
  const autoApproveActive = this.maybeAutoApproveStatus(adapterStatus, Date.now());
27907
- const visibleStatus = parseErrorMessage || parsedStatus?.status === "error" ? "error" : autoApproveActive ? "generating" : adapterStatus.status;
28131
+ let visibleStatus = parseErrorMessage || parsedStatus?.status === "error" ? "error" : autoApproveActive ? "generating" : adapterStatus.status;
28132
+ const externalNativeFinal = this.getExternalNativeFinalReconciliation(parsedStatus?.messages, adapterStatus);
28133
+ if (externalNativeFinal && isCliGeneratingLikeStatus(visibleStatus)) {
28134
+ visibleStatus = "idle";
28135
+ }
27908
28136
  const runtime = this.adapter.getRuntimeMetadata();
27909
28137
  this.maybeAppendRuntimeRecoveryMessage(runtime);
27910
28138
  let parsedMessages = Array.isArray(parsedStatus?.messages) ? parsedStatus.messages : [];
@@ -28066,7 +28294,22 @@ var CliProviderInstance = class {
28066
28294
  };
28067
28295
  }
28068
28296
  updateSettings(newSettings) {
28069
- this.settings = { ...newSettings };
28297
+ const runtimeMeshSettings = {};
28298
+ for (const key of [
28299
+ "meshNodeFor",
28300
+ "meshNodeId",
28301
+ "meshActiveTaskId",
28302
+ "meshCoordinatorFor",
28303
+ "meshCoordinatorDaemonId",
28304
+ "meshCoordinatorNodeId",
28305
+ "spawnedSessionVisibility",
28306
+ "launchedByCoordinator"
28307
+ ]) {
28308
+ if (this.settings[key] !== void 0 && newSettings[key] === void 0) {
28309
+ runtimeMeshSettings[key] = this.settings[key];
28310
+ }
28311
+ }
28312
+ this.settings = { ...newSettings, ...runtimeMeshSettings };
28070
28313
  this.adapter.updateRuntimeSettings?.(this.settings);
28071
28314
  this.monitor.updateConfig({
28072
28315
  approvalAlert: this.settings.approvalAlert !== false,
@@ -28157,6 +28400,8 @@ var CliProviderInstance = class {
28157
28400
  const content = typeof input === "string" ? input.trim() : buildCliStructuredInputPrompt(input).trim();
28158
28401
  if (!content) return;
28159
28402
  const receivedAt = Date.now();
28403
+ this.lastAcknowledgedUserInputAt = receivedAt;
28404
+ this.externalBusyIdleFingerprint = "";
28160
28405
  const dedupKey = `user_input_ack:${crypto4.createHash("sha256").update(`${this.instanceId}:${content}:${receivedAt}`).digest("hex").slice(0, 24)}`;
28161
28406
  this.appendRuntimeMessage(buildChatMessage({
28162
28407
  role: "user",
@@ -28305,6 +28550,50 @@ var CliProviderInstance = class {
28305
28550
  const evidence = this.completionFinalAssistantEvidence(parsedMessages);
28306
28551
  return extractFinalSummaryFromMessages(evidence.messages);
28307
28552
  }
28553
+ externalNativeFinalFingerprint(evidence) {
28554
+ const messages = Array.isArray(evidence.messages) ? evidence.messages : [];
28555
+ const visibleMessages = messages.filter((message) => isUserFacingChatMessage(message));
28556
+ const lastVisible = visibleMessages[visibleMessages.length - 1];
28557
+ const content = lastVisible ? flattenContent(lastVisible.content).trim() : "";
28558
+ const receivedAt = lastVisible ? getMessageTime(lastVisible) : 0;
28559
+ const probe = this.lastExternalCompletionProbe;
28560
+ return crypto4.createHash("sha256").update([
28561
+ this.type,
28562
+ this.providerSessionId || "",
28563
+ probe?.sourcePath || "",
28564
+ String(probe?.sourceMtimeMs || 0),
28565
+ String(receivedAt || 0),
28566
+ content.slice(-500)
28567
+ ].join("\0")).digest("hex").slice(0, 24);
28568
+ }
28569
+ getExternalNativeFinalReconciliation(parsedMessages, adapterStatus) {
28570
+ const rawStatus = typeof adapterStatus?.status === "string" ? adapterStatus.status.trim() : "";
28571
+ if (!isCliGeneratingLikeStatus(rawStatus)) return null;
28572
+ if (hasNonEmptyCliModalButtons(adapterStatus?.activeModal ?? adapterStatus?.modal)) return null;
28573
+ const evidence = this.completionFinalAssistantEvidence(parsedMessages);
28574
+ if (evidence.source !== "external-native" || !evidence.present) return null;
28575
+ const messages = Array.isArray(evidence.messages) ? evidence.messages : [];
28576
+ const visibleMessages = messages.filter((message) => isUserFacingChatMessage(message));
28577
+ const lastVisible = visibleMessages[visibleMessages.length - 1];
28578
+ const lastMessageAt = lastVisible ? getMessageTime(lastVisible) : 0;
28579
+ const sourceMtimeMs = Number(this.lastExternalCompletionProbe?.sourceMtimeMs || 0);
28580
+ const minEvidenceAt = Math.max(
28581
+ this.startedAt > 0 ? this.startedAt - 5e3 : 0,
28582
+ this.generatingStartedAt > 0 ? this.generatingStartedAt - 5e3 : 0,
28583
+ this.lastAcknowledgedUserInputAt > 0 ? this.lastAcknowledgedUserInputAt - 1e3 : 0
28584
+ );
28585
+ if (minEvidenceAt > 0 && lastMessageAt > 0 && lastMessageAt < minEvidenceAt && sourceMtimeMs < minEvidenceAt) {
28586
+ return null;
28587
+ }
28588
+ const finalSummary = extractFinalSummaryFromMessages(evidence.messages);
28589
+ if (!finalSummary) return null;
28590
+ const fingerprint = this.externalNativeFinalFingerprint(evidence);
28591
+ if (fingerprint === this.externalBusyIdleFingerprint) {
28592
+ return { fingerprint, finalSummary, evidence };
28593
+ }
28594
+ this.externalBusyIdleFingerprint = fingerprint;
28595
+ return { fingerprint, finalSummary, evidence };
28596
+ }
28308
28597
  buildCompletedFinalizationDiagnostic(args) {
28309
28598
  let parsed = null;
28310
28599
  let parseError;
@@ -28375,17 +28664,18 @@ var CliProviderInstance = class {
28375
28664
  if (typeof adapterAny?.responseBuffer === "string" && adapterAny.responseBuffer.trim()) return false;
28376
28665
  return true;
28377
28666
  }
28378
- getCompletedFinalizationBlock(latestVisibleStatus, pending) {
28667
+ getCompletedFinalizationBlock(latestVisibleStatus, pending, opts) {
28379
28668
  if (latestVisibleStatus !== "idle") return { reason: `status:${latestVisibleStatus}`, terminal: true };
28380
28669
  const adapterAny = this.adapter;
28381
28670
  const approvalResolvedIdle = pending.previousStatus === "waiting_approval";
28382
- if (!approvalResolvedIdle) {
28671
+ const externalNativeFinal = opts?.externalNativeFinal || null;
28672
+ if (!approvalResolvedIdle && !externalNativeFinal) {
28383
28673
  if (adapterAny?.isWaitingForResponse === true) return { reason: "adapter_waiting_for_response", terminal: true };
28384
28674
  if (adapterAny?.currentTurnScope) return { reason: "adapter_turn_scope_active", terminal: true };
28385
28675
  if (this.hasAdapterPendingResponse()) return { reason: "adapter_pending_response", terminal: true };
28386
28676
  }
28387
28677
  const partial = typeof this.adapter.getPartialResponse === "function" ? this.adapter.getPartialResponse() : "";
28388
- if (typeof partial === "string" && partial.trim()) return { reason: "partial_response_pending", terminal: true };
28678
+ if (!externalNativeFinal && typeof partial === "string" && partial.trim()) return { reason: "partial_response_pending", terminal: true };
28389
28679
  let parsed;
28390
28680
  try {
28391
28681
  parsed = this.adapter.getScriptParsedStatus();
@@ -28395,6 +28685,7 @@ var CliProviderInstance = class {
28395
28685
  const parsedStatus = typeof parsed?.status === "string" ? parsed.status : "unknown";
28396
28686
  if (parsedStatus !== "idle") {
28397
28687
  const adapterStatus = this.adapter.getStatus({ allowParse: false });
28688
+ if (externalNativeFinal && isCliGeneratingLikeStatus(parsedStatus)) return null;
28398
28689
  if (this.shouldSuppressStaleParsedBusyStatus(parsed, adapterStatus)) return null;
28399
28690
  return { reason: `parsed_status:${parsedStatus}`, terminal: isCliGeneratingLikeStatus(parsedStatus) };
28400
28691
  }
@@ -28447,14 +28738,15 @@ var CliProviderInstance = class {
28447
28738
  }
28448
28739
  const latestStatus = this.adapter.getStatus({ allowParse: false });
28449
28740
  const latestAutoApproveActive = latestStatus.status === "waiting_approval" && this.shouldAutoApprove();
28450
- const latestVisibleStatus = latestAutoApproveActive ? "generating" : latestStatus.status;
28741
+ const externalNativeFinal = this.getExternalNativeFinalReconciliation(void 0, latestStatus);
28742
+ const latestVisibleStatus = externalNativeFinal && isCliGeneratingLikeStatus(latestStatus.status) ? "idle" : latestAutoApproveActive ? "generating" : latestStatus.status;
28451
28743
  if (latestVisibleStatus !== "idle") {
28452
28744
  LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed ${latestVisibleStatus})`);
28453
28745
  this.completedDebouncePending = null;
28454
28746
  this.completedDebounceTimer = null;
28455
28747
  return;
28456
28748
  }
28457
- const block2 = this.getCompletedFinalizationBlock(latestVisibleStatus, pending);
28749
+ const block2 = this.getCompletedFinalizationBlock(latestVisibleStatus, pending, { externalNativeFinal });
28458
28750
  if (block2) {
28459
28751
  const blockReason = block2.reason;
28460
28752
  const waitedMs = Date.now() - pending.firstObservedAt;
@@ -28495,7 +28787,18 @@ var CliProviderInstance = class {
28495
28787
  chatTitle: pending.chatTitle,
28496
28788
  duration: pending.duration,
28497
28789
  timestamp: pending.timestamp,
28498
- finalSummary: this.completionFinalSummary(this.adapter?.getScriptParsedStatus()?.messages)
28790
+ finalSummary: externalNativeFinal?.finalSummary || this.completionFinalSummary(this.adapter?.getScriptParsedStatus()?.messages),
28791
+ ...externalNativeFinal ? {
28792
+ completionDiagnostic: {
28793
+ providerType: this.type,
28794
+ sessionId: this.instanceId,
28795
+ providerSessionId: this.providerSessionId || null,
28796
+ reconciliationReason: "external_native_final_assistant_while_adapter_busy",
28797
+ finalAssistantPresent: true,
28798
+ finalAssistantEvidenceSource: externalNativeFinal.evidence.source,
28799
+ externalFinalFingerprint: externalNativeFinal.fingerprint
28800
+ }
28801
+ } : {}
28499
28802
  });
28500
28803
  this.completedDebouncePending = null;
28501
28804
  this.completedDebounceTimer = null;
@@ -28551,7 +28854,8 @@ var CliProviderInstance = class {
28551
28854
  const parsedStatus = null;
28552
28855
  const rawStatus = adapterStatus.status;
28553
28856
  const autoApproveActive = this.maybeAutoApproveStatus(adapterStatus, now);
28554
- const newStatus = autoApproveActive ? "generating" : rawStatus;
28857
+ const externalNativeFinal = this.getExternalNativeFinalReconciliation(void 0, adapterStatus);
28858
+ const newStatus = externalNativeFinal && isCliGeneratingLikeStatus(rawStatus) ? "idle" : autoApproveActive ? "generating" : rawStatus;
28555
28859
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
28556
28860
  const chatTitle = `${this.provider.name} \xB7 ${dirName}`;
28557
28861
  const partial = this.adapter.getPartialResponse();
@@ -30552,15 +30856,29 @@ function colorize(color, text) {
30552
30856
  const fn = chalkApi?.[color];
30553
30857
  return typeof fn === "function" ? fn(text) : text;
30554
30858
  }
30555
- var COORDINATOR_DELEGATED_ENV_UNSETS = {
30556
- ADHDEV_INLINE_MESH: "",
30557
- ADHDEV_MCP_TRANSPORT: "",
30558
- ADHDEV_MESH_ID: "",
30559
- HERMES_EPHEMERAL_SYSTEM_PROMPT: ""
30560
- };
30859
+ var DEFAULT_COORDINATOR_DELEGATED_ENV_UNSETS = [
30860
+ "ADHDEV_INLINE_MESH",
30861
+ "ADHDEV_MCP_TRANSPORT",
30862
+ "ADHDEV_MESH_ID",
30863
+ "HERMES_EPHEMERAL_SYSTEM_PROMPT"
30864
+ ];
30561
30865
  function hasCliArg(args, flag) {
30562
30866
  return args.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
30563
30867
  }
30868
+ function hasConfigOverride(args, key) {
30869
+ for (let index = 0; index < args.length; index += 1) {
30870
+ const arg = args[index];
30871
+ const next = args[index + 1];
30872
+ if ((arg === "-c" || arg === "--config") && typeof next === "string") {
30873
+ if (next === key || next.startsWith(`${key}=`) || next.startsWith(`${key}.`)) return true;
30874
+ }
30875
+ if (arg.startsWith("--config=")) {
30876
+ const value = arg.slice("--config=".length);
30877
+ if (value === key || value.startsWith(`${key}=`) || value.startsWith(`${key}.`)) return true;
30878
+ }
30879
+ }
30880
+ return false;
30881
+ }
30564
30882
  function ensureEmptyDelegatedMcpConfig(workspace) {
30565
30883
  const baseDir = path23.join(os17.tmpdir(), "adhdev-delegated-agent-empty-mcp");
30566
30884
  (0, import_fs12.mkdirSync)(baseDir, { recursive: true });
@@ -30570,11 +30888,30 @@ function ensureEmptyDelegatedMcpConfig(workspace) {
30570
30888
  return filePath;
30571
30889
  }
30572
30890
  function buildCoordinatorDelegatedCliLaunchOptions(input) {
30573
- const cliType = String(input.cliType || "").trim();
30574
30891
  const cliArgs = Array.isArray(input.cliArgs) ? [...input.cliArgs] : [];
30575
- const env = { ...input.env || {}, ...COORDINATOR_DELEGATED_ENV_UNSETS };
30576
- if (cliType === "claude-cli" && !hasCliArg(cliArgs, "--mcp-config")) {
30577
- cliArgs.unshift("--mcp-config", ensureEmptyDelegatedMcpConfig(input.workspace));
30892
+ const env = { ...input.env || {} };
30893
+ const envUnsets = new Set(DEFAULT_COORDINATOR_DELEGATED_ENV_UNSETS);
30894
+ for (const key of input.isolation?.env?.unset || []) {
30895
+ if (typeof key === "string" && key.trim()) envUnsets.add(key.trim());
30896
+ }
30897
+ for (const key of envUnsets) env[key] = "";
30898
+ for (const rule of input.isolation?.args || []) {
30899
+ if (!rule || typeof rule !== "object") continue;
30900
+ if (rule.mode === "empty_mcp_config") {
30901
+ if (rule.flag && !hasCliArg(cliArgs, rule.flag)) {
30902
+ cliArgs.unshift(rule.flag, ensureEmptyDelegatedMcpConfig(input.workspace));
30903
+ }
30904
+ if (rule.strictFlag && !hasCliArg(cliArgs, rule.strictFlag)) {
30905
+ cliArgs.unshift(rule.strictFlag);
30906
+ }
30907
+ continue;
30908
+ }
30909
+ if (rule.mode === "config_override") {
30910
+ const key = String(rule.dedupeKey || rule.key || "").trim();
30911
+ const flag = String(rule.flag || "").trim();
30912
+ if (!key || !flag || hasConfigOverride(cliArgs, key)) continue;
30913
+ cliArgs.unshift(flag, `${rule.key}=${rule.value}`);
30914
+ }
30578
30915
  }
30579
30916
  return { cliArgs, env };
30580
30917
  }
@@ -31237,22 +31574,25 @@ Run 'adhdev doctor' for detailed diagnostics.`
31237
31574
  const dir = resolved.path;
31238
31575
  const launchSource = resolved.source;
31239
31576
  if (!cliType) throw new Error("cliType required");
31577
+ const providerType = this.providerLoader.resolveAlias(cliType);
31578
+ const provLookup = this.providerLoader.getMeta(providerType);
31240
31579
  const settingsOverride = args?.settings && typeof args.settings === "object" ? args.settings : void 0;
31241
31580
  const delegatedLaunch = settingsOverride?.launchedByCoordinator === true ? buildCoordinatorDelegatedCliLaunchOptions({
31242
31581
  cliType,
31243
31582
  workspace: dir,
31244
31583
  cliArgs: args?.cliArgs,
31245
- env: args?.env
31584
+ env: args?.env,
31585
+ isolation: provLookup?.meshCoordinator?.delegatedWorkerIsolation
31246
31586
  }) : null;
31247
- const provLookup = this.providerLoader.getMeta(this.providerLoader.resolveAlias(cliType));
31248
- const provTrust = provLookup?._sourceTrust;
31587
+ const provMeta = provLookup;
31588
+ const provTrust = provMeta?._sourceTrust;
31249
31589
  if (provTrust === "external-untrusted" && args?.confirmExternalUntrusted !== true) {
31250
31590
  return {
31251
31591
  success: false,
31252
31592
  error: "untrusted_external_provider",
31253
31593
  provider: {
31254
31594
  type: provLookup?.type ?? cliType,
31255
- sourceName: provLookup?._sourceName ?? null,
31595
+ sourceName: provMeta?._sourceName ?? null,
31256
31596
  trust: provTrust
31257
31597
  },
31258
31598
  hint: "Resend launch_cli with confirmExternalUntrusted=true after the user explicitly approves running JavaScript from this 3rd-party source."
@@ -31724,7 +32064,10 @@ function validateMeshCoordinator(raw, errors) {
31724
32064
  if (meshCoordinator.reason !== void 0 && (typeof meshCoordinator.reason !== "string" || !meshCoordinator.reason.trim())) {
31725
32065
  errors.push("meshCoordinator.reason must be a non-empty string when provided");
31726
32066
  }
31727
- const mcpConfig = meshCoordinator.mcpConfig;
32067
+ validateMeshCoordinatorMcpConfig(meshCoordinator.mcpConfig, errors);
32068
+ validateMeshCoordinatorDelegatedWorkerIsolation(meshCoordinator.delegatedWorkerIsolation, errors);
32069
+ }
32070
+ function validateMeshCoordinatorMcpConfig(mcpConfig, errors) {
31728
32071
  if (mcpConfig === void 0) return;
31729
32072
  if (!mcpConfig || typeof mcpConfig !== "object" || Array.isArray(mcpConfig)) {
31730
32073
  errors.push("meshCoordinator.mcpConfig must be an object");
@@ -31765,6 +32108,56 @@ function validateMeshCoordinator(raw, errors) {
31765
32108
  }
31766
32109
  }
31767
32110
  }
32111
+ function validateMeshCoordinatorDelegatedWorkerIsolation(raw, errors) {
32112
+ if (raw === void 0) return;
32113
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
32114
+ errors.push("meshCoordinator.delegatedWorkerIsolation must be an object");
32115
+ return;
32116
+ }
32117
+ const isolation = raw;
32118
+ const env = isolation.env;
32119
+ if (env !== void 0) {
32120
+ if (!env || typeof env !== "object" || Array.isArray(env)) {
32121
+ errors.push("meshCoordinator.delegatedWorkerIsolation.env must be an object");
32122
+ } else {
32123
+ const unset = env.unset;
32124
+ if (unset !== void 0 && (!Array.isArray(unset) || unset.some((key) => typeof key !== "string" || !key.trim()))) {
32125
+ errors.push("meshCoordinator.delegatedWorkerIsolation.env.unset must be an array of non-empty strings");
32126
+ }
32127
+ }
32128
+ }
32129
+ const args = isolation.args;
32130
+ if (args === void 0) return;
32131
+ if (!Array.isArray(args)) {
32132
+ errors.push("meshCoordinator.delegatedWorkerIsolation.args must be an array");
32133
+ return;
32134
+ }
32135
+ for (const [index, rule] of args.entries()) {
32136
+ const prefix = `meshCoordinator.delegatedWorkerIsolation.args[${index}]`;
32137
+ if (!rule || typeof rule !== "object" || Array.isArray(rule)) {
32138
+ errors.push(`${prefix} must be an object`);
32139
+ continue;
32140
+ }
32141
+ const item = rule;
32142
+ const mode = item.mode;
32143
+ if (mode !== "empty_mcp_config" && mode !== "config_override") {
32144
+ errors.push(`${prefix}.mode must be one of: empty_mcp_config, config_override`);
32145
+ continue;
32146
+ }
32147
+ for (const key of mode === "empty_mcp_config" ? ["flag"] : ["flag", "key", "value"]) {
32148
+ const value = item[key];
32149
+ if (typeof value !== "string" || !value.trim()) {
32150
+ errors.push(`${prefix}.${key} must be a non-empty string`);
32151
+ }
32152
+ }
32153
+ for (const key of ["strictFlag", "dedupeKey"]) {
32154
+ const value = item[key];
32155
+ if (value !== void 0 && (typeof value !== "string" || !value.trim())) {
32156
+ errors.push(`${prefix}.${key} must be a non-empty string when provided`);
32157
+ }
32158
+ }
32159
+ }
32160
+ }
31768
32161
  function validateControl(control, errors) {
31769
32162
  if (!control || typeof control !== "object") {
31770
32163
  errors.push("controls: each control must be an object");
@@ -37426,9 +37819,14 @@ function readCachedInlineMeshActiveSessionDetails(node) {
37426
37819
  node?.provider_type
37427
37820
  ),
37428
37821
  state: readStringValue(fallbackSession.status, fallbackSession.state, fallbackSession.lifecycle),
37822
+ chatStatus: readStringValue(fallbackSession.chatStatus, fallbackSession.chat_status),
37429
37823
  lifecycle: readStringValue(fallbackSession.lifecycle),
37430
37824
  title: readStringValue(fallbackSession.title, fallbackSession.displayName, fallbackSession.display_name) ?? null,
37431
37825
  workspace: readStringValue(fallbackSession.workspace, node?.workspace) ?? null,
37826
+ role: readStringValue(fallbackSession.role) ?? null,
37827
+ isSelfCoordinator: fallbackSession.isSelfCoordinator === true || fallbackSession.is_self_coordinator === true,
37828
+ createdAt: readStringValue(fallbackSession.createdAt, fallbackSession.created_at) ?? null,
37829
+ startedAt: readStringValue(fallbackSession.startedAt, fallbackSession.started_at) ?? null,
37432
37830
  lastActivityAt: readStringValue(fallbackSession.lastActivityAt, fallbackSession.last_activity_at) ?? null,
37433
37831
  recoveryState: readStringValue(fallbackSession.recoveryState, fallbackSession.recovery_state) ?? null,
37434
37832
  isCached: true
@@ -37569,15 +37967,26 @@ async function hydrateInlineMeshDirectTruth(args) {
37569
37967
  };
37570
37968
  }
37571
37969
  function summarizeMeshSessionRecord(record) {
37970
+ const meta = readObjectRecord(record?.meta);
37971
+ const isSelfCoordinator = Boolean(readStringValue(meta.meshCoordinatorFor));
37972
+ const chatStatus = readStringValue(record?.chatStatus, record?.activeChat?.status, meta.chatStatus, meta.sessionStatus);
37973
+ const state = readLiveMeshSessionState(record);
37974
+ const statusNote = isSelfCoordinator && (!chatStatus || chatStatus === "idle" || state === "idle") ? "Coordinator self status is sampled from the session host and may read idle while the coordinator is generating this response." : null;
37572
37975
  return {
37573
37976
  sessionId: readStringValue(record?.sessionId) || "unknown",
37574
37977
  providerType: readStringValue(record?.providerType),
37575
- state: readLiveMeshSessionState(record),
37978
+ state,
37979
+ chatStatus,
37576
37980
  lifecycle: readStringValue(record?.lifecycle),
37577
37981
  surfaceKind: getSessionHostSurfaceKind(record),
37578
- recoveryState: readStringValue(record?.meta?.runtimeRecoveryState) ?? null,
37982
+ recoveryState: readStringValue(meta.runtimeRecoveryState) ?? null,
37579
37983
  workspace: readStringValue(record?.workspace) ?? null,
37580
37984
  title: readStringValue(record?.displayName, record?.workspaceLabel) ?? null,
37985
+ role: isSelfCoordinator ? "coordinator" : readStringValue(meta.meshRole, meta.role) ?? null,
37986
+ isSelfCoordinator,
37987
+ statusNote,
37988
+ createdAt: toIsoTimestamp(record?.createdAt ?? record?.created_at),
37989
+ startedAt: toIsoTimestamp(record?.startedAt ?? record?.started_at ?? record?.spawnedAtMs ?? record?.spawned_at_ms),
37581
37990
  lastActivityAt: toIsoTimestamp(record?.updatedAt ?? record?.lastActivityAt ?? record?.last_activity_at),
37582
37991
  isCached: false
37583
37992
  };
@@ -38601,6 +39010,15 @@ var DaemonCommandRouter = class {
38601
39010
  this.aggregateMeshStatusCache.set(meshId, { builtAt, snapshot: this.cloneJsonValue(next), queueRevision: getMeshQueueRevision(meshId) });
38602
39011
  return next;
38603
39012
  }
39013
+ getCachedInlineMeshNodes() {
39014
+ const nodes = [];
39015
+ for (const mesh of this.inlineMeshCache.values()) {
39016
+ if (Array.isArray(mesh?.nodes)) {
39017
+ nodes.push(...mesh.nodes);
39018
+ }
39019
+ }
39020
+ return nodes;
39021
+ }
38604
39022
  getCachedInlineMesh(meshId, inlineMesh) {
38605
39023
  if (inlineMesh && typeof inlineMesh === "object") {
38606
39024
  return this.warmInlineMeshCache(meshId, inlineMesh);
@@ -38649,6 +39067,7 @@ var DaemonCommandRouter = class {
38649
39067
  }
38650
39068
  invalidateAggregateMeshStatus(meshId) {
38651
39069
  this.aggregateMeshStatusCache.delete(meshId);
39070
+ this.deps.onMeshStateChange?.(meshId);
38652
39071
  }
38653
39072
  async requireMeshHostMutationOwner(meshId, inlineMesh, operation) {
38654
39073
  const meshRecord = await this.getMeshForCommand(meshId, inlineMesh, { preferInline: true });
@@ -50134,6 +50553,7 @@ async function initDaemonComponents(config) {
50134
50553
  },
50135
50554
  onIdeConnected: () => poller?.start(),
50136
50555
  onStatusChange: config.onStatusChange,
50556
+ onMeshStateChange: config.onMeshStateChange,
50137
50557
  onPostChatCommand: config.onPostChatCommand,
50138
50558
  sessionHostControl: config.sessionHostControl,
50139
50559
  statusInstanceId: config.statusInstanceId,
@@ -50727,6 +51147,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
50727
51147
  probeCdpPort,
50728
51148
  queuePendingMeshCoordinatorEvent,
50729
51149
  readAntigravityCliSession,
51150
+ readCachedInlineMeshActiveSessionDetails,
50730
51151
  readChatHistory,
50731
51152
  readClaudeCliSession,
50732
51153
  readCodexCliSession,