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

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 (37) 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 +401 -33
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +400 -33
  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/driver.d.ts +6 -1
  15. package/dist/providers/spec/schema.gen.d.ts +22 -0
  16. package/dist/providers/spec/types.d.ts +10 -0
  17. package/dist/repo-mesh-types.d.ts +6 -0
  18. package/package.json +1 -1
  19. package/src/boot/daemon-lifecycle.ts +2 -0
  20. package/src/commands/chat-commands.ts +26 -0
  21. package/src/commands/cli-manager.ts +52 -14
  22. package/src/commands/router.ts +35 -4
  23. package/src/git/git-commands.ts +20 -2
  24. package/src/git/git-status.ts +35 -6
  25. package/src/git/git-types.ts +2 -0
  26. package/src/index.ts +1 -1
  27. package/src/providers/cli-provider-instance.ts +110 -9
  28. package/src/providers/contracts.d.ts +55 -0
  29. package/src/providers/contracts.ts +35 -0
  30. package/src/providers/provider-schema.ts +56 -1
  31. package/src/providers/sdk/v1/schemas/cli/provider.schema.json +46 -0
  32. package/src/providers/sdk/v1/types/common/index.ts +19 -0
  33. package/src/providers/spec/driver.ts +68 -1
  34. package/src/providers/spec/schema.gen.ts +12 -1
  35. package/src/providers/spec/schema.json +21 -1
  36. package/src/providers/spec/types.ts +10 -0
  37. package/src/repo-mesh-types.ts +6 -0
package/dist/index.mjs CHANGED
@@ -270,6 +270,8 @@ async function getGitRepoStatus(workspace, options = {}) {
270
270
  if (includeSubmodules) {
271
271
  submodules = await getSubmoduleStatuses(repo, options);
272
272
  }
273
+ const submoduleDirty = (submodules || []).some((submodule) => submodule.dirty || submodule.outOfSync || !!submodule.error);
274
+ const dirty = parsed.staged + parsed.modified + parsed.untracked + parsed.deleted + parsed.renamed > 0 || parsed.conflictFiles.length > 0 || stashCount > 0 || submoduleDirty;
273
275
  return {
274
276
  workspace: repo.workspace,
275
277
  repoRoot: repo.repoRoot,
@@ -288,6 +290,7 @@ async function getGitRepoStatus(workspace, options = {}) {
288
290
  untracked: parsed.untracked,
289
291
  deleted: parsed.deleted,
290
292
  renamed: parsed.renamed,
293
+ dirty,
291
294
  hasConflicts: parsed.conflictFiles.length > 0,
292
295
  conflictFiles: parsed.conflictFiles,
293
296
  stashCount,
@@ -461,6 +464,7 @@ function emptyStatus(workspace, lastCheckedAt, error) {
461
464
  untracked: 0,
462
465
  deleted: 0,
463
466
  renamed: 0,
467
+ dirty: false,
464
468
  hasConflicts: false,
465
469
  conflictFiles: [],
466
470
  stashCount: 0,
@@ -473,17 +477,33 @@ async function getSubmoduleStatuses(repo, options) {
473
477
  if (!repo.repoRoot) return [];
474
478
  try {
475
479
  const result = await runGit(repo, ["submodule", "status", "--recursive"], options);
476
- return parseSubmoduleStatusOutput(result.stdout, repo.repoRoot, options.submoduleIgnorePaths);
480
+ const submodules = parseSubmoduleStatusOutput(result.stdout, repo.repoRoot, options.submoduleIgnorePaths);
481
+ await Promise.all(submodules.map((submodule) => enrichSubmoduleWorktreeStatus(repo, submodule, options)));
482
+ return submodules;
477
483
  } catch {
478
484
  return [];
479
485
  }
480
486
  }
487
+ async function enrichSubmoduleWorktreeStatus(repo, submodule, options) {
488
+ try {
489
+ const result = await runGit(repo, ["status", "--porcelain=v2", "--branch"], {
490
+ ...options,
491
+ cwd: submodule.repoPath
492
+ });
493
+ const parsed = parsePorcelainV2Status(result.stdout);
494
+ const dirty = parsed.staged + parsed.modified + parsed.untracked + parsed.deleted + parsed.renamed > 0 || parsed.conflictFiles.length > 0;
495
+ submodule.dirty = submodule.dirty || dirty;
496
+ } catch (error) {
497
+ submodule.dirty = true;
498
+ submodule.error = formatGitError(error);
499
+ }
500
+ }
481
501
  function parseSubmoduleStatusOutput(output, repoRoot, ignorePaths) {
482
502
  const submodules = [];
483
503
  const ignoreSet = new Set(ignorePaths || []);
484
504
  for (const line of output.split("\n")) {
485
505
  if (!line.trim()) continue;
486
- const match = line.match(/^([\-+\s])([0-9a-f]{40})\s+(\S+)(?:\s+\(([^)]+)\))?/);
506
+ const match = line.match(/^([\-+U\s])([0-9a-f]{40})\s+(\S+)(?:\s+\(([^)]+)\))?/);
487
507
  if (!match) continue;
488
508
  const prefix = match[1];
489
509
  const commit = match[2];
@@ -493,8 +513,8 @@ function parseSubmoduleStatusOutput(output, repoRoot, ignorePaths) {
493
513
  path: path40,
494
514
  commit,
495
515
  repoPath: repoRoot + "/" + path40,
496
- dirty: prefix === "+",
497
- outOfSync: prefix === "-",
516
+ dirty: prefix === "U",
517
+ outOfSync: prefix === "-" || prefix === "+",
498
518
  lastCheckedAt: Date.now()
499
519
  });
500
520
  }
@@ -6150,6 +6170,52 @@ var init_provider_schema = __esm({
6150
6170
  properties: { mode: { const: "env_var" }, name: { type: "string" } }
6151
6171
  }
6152
6172
  ]
6173
+ },
6174
+ delegatedWorkerIsolation: {
6175
+ description: "Provider-declared launch isolation for coordinator-spawned worker sessions. Keeps worker-only sessions from inheriting coordinator MCP/tools/config.",
6176
+ type: "object",
6177
+ additionalProperties: false,
6178
+ properties: {
6179
+ env: {
6180
+ type: "object",
6181
+ additionalProperties: false,
6182
+ properties: {
6183
+ unset: {
6184
+ type: "array",
6185
+ items: { type: "string", minLength: 1 }
6186
+ }
6187
+ }
6188
+ },
6189
+ args: {
6190
+ type: "array",
6191
+ items: {
6192
+ oneOf: [
6193
+ {
6194
+ type: "object",
6195
+ additionalProperties: false,
6196
+ required: ["mode", "flag"],
6197
+ properties: {
6198
+ mode: { const: "empty_mcp_config" },
6199
+ flag: { type: "string", minLength: 1 },
6200
+ strictFlag: { type: "string", minLength: 1 }
6201
+ }
6202
+ },
6203
+ {
6204
+ type: "object",
6205
+ additionalProperties: false,
6206
+ required: ["mode", "flag", "key", "value"],
6207
+ properties: {
6208
+ mode: { const: "config_override" },
6209
+ flag: { type: "string", minLength: 1 },
6210
+ key: { type: "string", minLength: 1 },
6211
+ value: { type: "string", minLength: 1 },
6212
+ dedupeKey: { type: "string", minLength: 1 }
6213
+ }
6214
+ }
6215
+ ]
6216
+ }
6217
+ }
6218
+ }
6153
6219
  }
6154
6220
  }
6155
6221
  },
@@ -12025,7 +12091,14 @@ async function handleGitCommand(command, args, services = defaultGitCommandServi
12025
12091
  switch (command) {
12026
12092
  case "git_status": {
12027
12093
  if (!services.getStatus) return serviceNotImplemented(command);
12028
- const status = await runService(() => services.getStatus({ workspace, refreshUpstream: optionalBoolean(args?.refreshUpstream) }));
12094
+ const submoduleIgnorePaths = Array.isArray(args?.submoduleIgnorePaths) ? args.submoduleIgnorePaths.filter((value) => typeof value === "string" && value.trim().length > 0) : void 0;
12095
+ const statusParams = { workspace };
12096
+ const refreshUpstream = optionalBoolean(args?.refreshUpstream);
12097
+ const includeSubmodules = optionalBoolean(args?.includeSubmodules);
12098
+ if (refreshUpstream !== void 0) statusParams.refreshUpstream = refreshUpstream;
12099
+ if (includeSubmodules !== void 0) statusParams.includeSubmodules = includeSubmodules;
12100
+ if (submoduleIgnorePaths && submoduleIgnorePaths.length > 0) statusParams.submoduleIgnorePaths = submoduleIgnorePaths;
12101
+ const status = await runService(() => services.getStatus(statusParams));
12029
12102
  return "success" in status ? status : { success: true, status };
12030
12103
  }
12031
12104
  case "git_diff_summary": {
@@ -12158,6 +12231,14 @@ async function gitCheckpoint(workspace, message, includeUntracked) {
12158
12231
  if (statusResult.hasConflicts) {
12159
12232
  throw new GitCommandError("conflict", "Repository has conflicts \u2014 resolve before checkpointing");
12160
12233
  }
12234
+ const dirtySubmodules = (statusResult.submodules || []).filter((submodule) => submodule.dirty);
12235
+ if (dirtySubmodules.length > 0) {
12236
+ const paths = dirtySubmodules.map((submodule) => submodule.path).join(", ");
12237
+ throw new GitCommandError(
12238
+ "dirty_index_required",
12239
+ `Repository has dirty submodules that must be checkpointed first: ${paths}. Checkpoint or commit each dirty submodule, then checkpoint this repository to record gitlink changes.`
12240
+ );
12241
+ }
12161
12242
  const addArgs = includeUntracked ? ["-A"] : ["-u"];
12162
12243
  await runGit(repo, ["add", ...addArgs], { cwd: repoRoot });
12163
12244
  const fullMsg = `adhdev: checkpoint ${message}`;
@@ -21479,6 +21560,14 @@ function hasVisibleAssistantMessage(messages) {
21479
21560
  return String(message.content || "").trim().length > 0;
21480
21561
  });
21481
21562
  }
21563
+ function hasFinalVisibleAssistantMessage(messages) {
21564
+ if (!Array.isArray(messages)) return false;
21565
+ const visible = filterUserFacingChatMessages(messages);
21566
+ const last = visible[visible.length - 1];
21567
+ const role = typeof last?.role === "string" ? last.role.trim().toLowerCase() : "";
21568
+ const content = last ? flattenContent(last.content).trim() : "";
21569
+ return (role === "assistant" || role === "model") && content.length > 0;
21570
+ }
21482
21571
  function shouldTrustCliAdapterTerminalStatus(parsedStatus, activeModal, adapter, adapterStatus) {
21483
21572
  if (!isGeneratingLikeStatus(parsedStatus)) return false;
21484
21573
  if (hasNonEmptyModalButtons(activeModal)) return false;
@@ -22333,6 +22422,18 @@ async function handleReadChat(h, args) {
22333
22422
  });
22334
22423
  }
22335
22424
  }
22425
+ if (isGeneratingLikeStatus(selectedStatus) && selectedTranscriptAuthority === "provider" && !hasNonEmptyModalButtons(activeModal) && hasFinalVisibleAssistantMessage(selectedMessages)) {
22426
+ selectedStatus = "idle";
22427
+ selectedMessages = finalizeStreamingMessagesWhenIdle(selectedMessages, selectedStatus);
22428
+ messageSource = {
22429
+ ...messageSource,
22430
+ statusReconciled: {
22431
+ from: returnedStatus,
22432
+ to: "idle",
22433
+ reason: "provider_native_final_assistant"
22434
+ }
22435
+ };
22436
+ }
22336
22437
  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}`);
22337
22438
  return buildReadChatCommandResult({
22338
22439
  messages: selectedMessages,
@@ -25919,7 +26020,18 @@ var SCHEMA = {
25919
26020
  "additionalProperties": false,
25920
26021
  "properties": {
25921
26022
  "busy_hold_ms": { "type": "integer", "minimum": 0 },
25922
- "startup_grace_ms": { "type": "integer", "minimum": 0 }
26023
+ "startup_grace_ms": { "type": "integer", "minimum": 0 },
26024
+ "completion_idle_after": {
26025
+ "type": "object",
26026
+ "additionalProperties": false,
26027
+ "required": ["regex", "hold_ms"],
26028
+ "properties": {
26029
+ "section": { "type": "string", "minLength": 1 },
26030
+ "regex": { "type": "string", "minLength": 1 },
26031
+ "flags": { "type": "string" },
26032
+ "hold_ms": { "type": "integer", "minimum": 0 }
26033
+ }
26034
+ }
25923
26035
  }
25924
26036
  }
25925
26037
  },
@@ -26399,6 +26511,30 @@ function resolveSubmitDelayMs(specBeforeSubmit, text) {
26399
26511
  const spec = typeof specBeforeSubmit === "number" && specBeforeSubmit > 0 ? specBeforeSubmit : 0;
26400
26512
  return Math.max(spec, SUBMIT_DELAY_FLOOR_MS + linesBonus);
26401
26513
  }
26514
+ function matchesCompletionIdleRule(spec, ev, screen) {
26515
+ const rule = spec.debounce?.completion_idle_after;
26516
+ if (!rule?.regex) return null;
26517
+ const haystack = rule.section ? ev.sections.find((section) => section.id === rule.section)?.text ?? "" : screen;
26518
+ if (!haystack) return null;
26519
+ try {
26520
+ const regex = new RegExp(rule.regex, rule.flags || "");
26521
+ const match = haystack.match(regex);
26522
+ return match?.[0] || null;
26523
+ } catch {
26524
+ return null;
26525
+ }
26526
+ }
26527
+ function matchesCompletionIdleTargetState(spec, ev, screen) {
26528
+ const target = spec.states.find((state) => state.id === spec.default_state) ?? spec.states.find((state) => state.id === "idle");
26529
+ if (!target?.when?.regex) return false;
26530
+ const haystack = target.when.section ? ev.sections.find((section) => section.id === target.when.section)?.text ?? "" : screen;
26531
+ if (!haystack) return false;
26532
+ try {
26533
+ return new RegExp(target.when.regex, target.when.flags || "i").test(haystack);
26534
+ } catch {
26535
+ return false;
26536
+ }
26537
+ }
26402
26538
  var SpecDriver = class {
26403
26539
  constructor(opts) {
26404
26540
  this.opts = opts;
@@ -26438,6 +26574,8 @@ var SpecDriver = class {
26438
26574
  * because the evaluator already moved past busy by the time the hold
26439
26575
  * kicks in. */
26440
26576
  lastBusyState = null;
26577
+ completionIdleFirstSeenAt = 0;
26578
+ completionIdleKey = "";
26441
26579
  /** Timer that re-runs evaluate() once the hold window expires. Needed
26442
26580
  * because the PTY stops emitting once the agent finishes; without an
26443
26581
  * explicit wake-up there's nothing to trigger the busy → idle
@@ -26561,10 +26699,40 @@ var SpecDriver = class {
26561
26699
  evState = this.lastBusyState ?? evState;
26562
26700
  }
26563
26701
  }
26702
+ const completionIdleRule = this.spec.debounce?.completion_idle_after;
26703
+ let busyWakeMs = busyHoldMs;
26704
+ if (evState.id === "busy" && completionIdleRule) {
26705
+ const completionKey = matchesCompletionIdleRule(this.spec, ev, screen);
26706
+ if (completionKey) {
26707
+ const now = Date.now();
26708
+ if (completionKey !== this.completionIdleKey) {
26709
+ this.completionIdleKey = completionKey;
26710
+ this.completionIdleFirstSeenAt = now;
26711
+ }
26712
+ const holdMs = Math.max(0, completionIdleRule.hold_ms || 0);
26713
+ const ageMs = now - this.completionIdleFirstSeenAt;
26714
+ if (ageMs >= holdMs) {
26715
+ if (matchesCompletionIdleTargetState(this.spec, ev, screen)) {
26716
+ const idle = this.spec.states.find((state) => state.id === this.spec.default_state) ?? this.spec.states.find((state) => state.id === "idle");
26717
+ evState = idle ? { id: idle.id, label: idle.label, title: null } : { id: "idle", label: "Ready", title: null };
26718
+ } else {
26719
+ busyWakeMs = Math.min(busyWakeMs, 1e3);
26720
+ }
26721
+ } else {
26722
+ busyWakeMs = Math.min(busyWakeMs, Math.max(holdMs - ageMs, 0));
26723
+ }
26724
+ } else {
26725
+ this.completionIdleKey = "";
26726
+ this.completionIdleFirstSeenAt = 0;
26727
+ }
26728
+ } else if (evState.id !== "busy") {
26729
+ this.completionIdleKey = "";
26730
+ this.completionIdleFirstSeenAt = 0;
26731
+ }
26564
26732
  if (evState.id === "busy") {
26565
26733
  this.lastBusyAt = Date.now();
26566
26734
  this.lastBusyState = evState;
26567
- this.scheduleBusyExpiry(busyHoldMs);
26735
+ this.scheduleBusyExpiry(busyWakeMs);
26568
26736
  }
26569
26737
  const changed = forceEmit || evState.id !== this.currentStateId || !shallowSameModal(ev, this.currentEval) || !shallowSameControls(ev, this.currentEval);
26570
26738
  if (this.pickerInProgress) this.tryAdvancePicker(screen);
@@ -27467,6 +27635,8 @@ var CliProviderInstance = class {
27467
27635
  historyWriter;
27468
27636
  runtimeMessages = [];
27469
27637
  lastPersistedHistoryMessages = [];
27638
+ lastAcknowledgedUserInputAt = 0;
27639
+ externalBusyIdleFingerprint = "";
27470
27640
  lastNativeSourceCanonicalCheckAt = 0;
27471
27641
  lastNativeSourceCanonicalCacheKey = void 0;
27472
27642
  cachedSqliteDb = null;
@@ -27595,7 +27765,11 @@ var CliProviderInstance = class {
27595
27765
  typeof adapterStatus?.providerSessionId === "string" ? adapterStatus.providerSessionId : ""
27596
27766
  );
27597
27767
  const autoApproveActive = this.maybeAutoApproveStatus(adapterStatus, Date.now());
27598
- const visibleStatus = parseErrorMessage || parsedStatus?.status === "error" ? "error" : autoApproveActive ? "generating" : adapterStatus.status;
27768
+ let visibleStatus = parseErrorMessage || parsedStatus?.status === "error" ? "error" : autoApproveActive ? "generating" : adapterStatus.status;
27769
+ const externalNativeFinal = this.getExternalNativeFinalReconciliation(parsedStatus?.messages, adapterStatus);
27770
+ if (externalNativeFinal && isCliGeneratingLikeStatus(visibleStatus)) {
27771
+ visibleStatus = "idle";
27772
+ }
27599
27773
  const runtime = this.adapter.getRuntimeMetadata();
27600
27774
  this.maybeAppendRuntimeRecoveryMessage(runtime);
27601
27775
  let parsedMessages = Array.isArray(parsedStatus?.messages) ? parsedStatus.messages : [];
@@ -27757,7 +27931,22 @@ var CliProviderInstance = class {
27757
27931
  };
27758
27932
  }
27759
27933
  updateSettings(newSettings) {
27760
- this.settings = { ...newSettings };
27934
+ const runtimeMeshSettings = {};
27935
+ for (const key of [
27936
+ "meshNodeFor",
27937
+ "meshNodeId",
27938
+ "meshActiveTaskId",
27939
+ "meshCoordinatorFor",
27940
+ "meshCoordinatorDaemonId",
27941
+ "meshCoordinatorNodeId",
27942
+ "spawnedSessionVisibility",
27943
+ "launchedByCoordinator"
27944
+ ]) {
27945
+ if (this.settings[key] !== void 0 && newSettings[key] === void 0) {
27946
+ runtimeMeshSettings[key] = this.settings[key];
27947
+ }
27948
+ }
27949
+ this.settings = { ...newSettings, ...runtimeMeshSettings };
27761
27950
  this.adapter.updateRuntimeSettings?.(this.settings);
27762
27951
  this.monitor.updateConfig({
27763
27952
  approvalAlert: this.settings.approvalAlert !== false,
@@ -27848,6 +28037,8 @@ var CliProviderInstance = class {
27848
28037
  const content = typeof input === "string" ? input.trim() : buildCliStructuredInputPrompt(input).trim();
27849
28038
  if (!content) return;
27850
28039
  const receivedAt = Date.now();
28040
+ this.lastAcknowledgedUserInputAt = receivedAt;
28041
+ this.externalBusyIdleFingerprint = "";
27851
28042
  const dedupKey = `user_input_ack:${crypto4.createHash("sha256").update(`${this.instanceId}:${content}:${receivedAt}`).digest("hex").slice(0, 24)}`;
27852
28043
  this.appendRuntimeMessage(buildChatMessage({
27853
28044
  role: "user",
@@ -27996,6 +28187,50 @@ var CliProviderInstance = class {
27996
28187
  const evidence = this.completionFinalAssistantEvidence(parsedMessages);
27997
28188
  return extractFinalSummaryFromMessages(evidence.messages);
27998
28189
  }
28190
+ externalNativeFinalFingerprint(evidence) {
28191
+ const messages = Array.isArray(evidence.messages) ? evidence.messages : [];
28192
+ const visibleMessages = messages.filter((message) => isUserFacingChatMessage(message));
28193
+ const lastVisible = visibleMessages[visibleMessages.length - 1];
28194
+ const content = lastVisible ? flattenContent(lastVisible.content).trim() : "";
28195
+ const receivedAt = lastVisible ? getMessageTime(lastVisible) : 0;
28196
+ const probe = this.lastExternalCompletionProbe;
28197
+ return crypto4.createHash("sha256").update([
28198
+ this.type,
28199
+ this.providerSessionId || "",
28200
+ probe?.sourcePath || "",
28201
+ String(probe?.sourceMtimeMs || 0),
28202
+ String(receivedAt || 0),
28203
+ content.slice(-500)
28204
+ ].join("\0")).digest("hex").slice(0, 24);
28205
+ }
28206
+ getExternalNativeFinalReconciliation(parsedMessages, adapterStatus) {
28207
+ const rawStatus = typeof adapterStatus?.status === "string" ? adapterStatus.status.trim() : "";
28208
+ if (!isCliGeneratingLikeStatus(rawStatus)) return null;
28209
+ if (hasNonEmptyCliModalButtons(adapterStatus?.activeModal ?? adapterStatus?.modal)) return null;
28210
+ const evidence = this.completionFinalAssistantEvidence(parsedMessages);
28211
+ if (evidence.source !== "external-native" || !evidence.present) return null;
28212
+ const messages = Array.isArray(evidence.messages) ? evidence.messages : [];
28213
+ const visibleMessages = messages.filter((message) => isUserFacingChatMessage(message));
28214
+ const lastVisible = visibleMessages[visibleMessages.length - 1];
28215
+ const lastMessageAt = lastVisible ? getMessageTime(lastVisible) : 0;
28216
+ const sourceMtimeMs = Number(this.lastExternalCompletionProbe?.sourceMtimeMs || 0);
28217
+ const minEvidenceAt = Math.max(
28218
+ this.startedAt > 0 ? this.startedAt - 5e3 : 0,
28219
+ this.generatingStartedAt > 0 ? this.generatingStartedAt - 5e3 : 0,
28220
+ this.lastAcknowledgedUserInputAt > 0 ? this.lastAcknowledgedUserInputAt - 1e3 : 0
28221
+ );
28222
+ if (minEvidenceAt > 0 && lastMessageAt > 0 && lastMessageAt < minEvidenceAt && sourceMtimeMs < minEvidenceAt) {
28223
+ return null;
28224
+ }
28225
+ const finalSummary = extractFinalSummaryFromMessages(evidence.messages);
28226
+ if (!finalSummary) return null;
28227
+ const fingerprint = this.externalNativeFinalFingerprint(evidence);
28228
+ if (fingerprint === this.externalBusyIdleFingerprint) {
28229
+ return { fingerprint, finalSummary, evidence };
28230
+ }
28231
+ this.externalBusyIdleFingerprint = fingerprint;
28232
+ return { fingerprint, finalSummary, evidence };
28233
+ }
27999
28234
  buildCompletedFinalizationDiagnostic(args) {
28000
28235
  let parsed = null;
28001
28236
  let parseError;
@@ -28066,17 +28301,18 @@ var CliProviderInstance = class {
28066
28301
  if (typeof adapterAny?.responseBuffer === "string" && adapterAny.responseBuffer.trim()) return false;
28067
28302
  return true;
28068
28303
  }
28069
- getCompletedFinalizationBlock(latestVisibleStatus, pending) {
28304
+ getCompletedFinalizationBlock(latestVisibleStatus, pending, opts) {
28070
28305
  if (latestVisibleStatus !== "idle") return { reason: `status:${latestVisibleStatus}`, terminal: true };
28071
28306
  const adapterAny = this.adapter;
28072
28307
  const approvalResolvedIdle = pending.previousStatus === "waiting_approval";
28073
- if (!approvalResolvedIdle) {
28308
+ const externalNativeFinal = opts?.externalNativeFinal || null;
28309
+ if (!approvalResolvedIdle && !externalNativeFinal) {
28074
28310
  if (adapterAny?.isWaitingForResponse === true) return { reason: "adapter_waiting_for_response", terminal: true };
28075
28311
  if (adapterAny?.currentTurnScope) return { reason: "adapter_turn_scope_active", terminal: true };
28076
28312
  if (this.hasAdapterPendingResponse()) return { reason: "adapter_pending_response", terminal: true };
28077
28313
  }
28078
28314
  const partial = typeof this.adapter.getPartialResponse === "function" ? this.adapter.getPartialResponse() : "";
28079
- if (typeof partial === "string" && partial.trim()) return { reason: "partial_response_pending", terminal: true };
28315
+ if (!externalNativeFinal && typeof partial === "string" && partial.trim()) return { reason: "partial_response_pending", terminal: true };
28080
28316
  let parsed;
28081
28317
  try {
28082
28318
  parsed = this.adapter.getScriptParsedStatus();
@@ -28086,6 +28322,7 @@ var CliProviderInstance = class {
28086
28322
  const parsedStatus = typeof parsed?.status === "string" ? parsed.status : "unknown";
28087
28323
  if (parsedStatus !== "idle") {
28088
28324
  const adapterStatus = this.adapter.getStatus({ allowParse: false });
28325
+ if (externalNativeFinal && isCliGeneratingLikeStatus(parsedStatus)) return null;
28089
28326
  if (this.shouldSuppressStaleParsedBusyStatus(parsed, adapterStatus)) return null;
28090
28327
  return { reason: `parsed_status:${parsedStatus}`, terminal: isCliGeneratingLikeStatus(parsedStatus) };
28091
28328
  }
@@ -28138,14 +28375,15 @@ var CliProviderInstance = class {
28138
28375
  }
28139
28376
  const latestStatus = this.adapter.getStatus({ allowParse: false });
28140
28377
  const latestAutoApproveActive = latestStatus.status === "waiting_approval" && this.shouldAutoApprove();
28141
- const latestVisibleStatus = latestAutoApproveActive ? "generating" : latestStatus.status;
28378
+ const externalNativeFinal = this.getExternalNativeFinalReconciliation(void 0, latestStatus);
28379
+ const latestVisibleStatus = externalNativeFinal && isCliGeneratingLikeStatus(latestStatus.status) ? "idle" : latestAutoApproveActive ? "generating" : latestStatus.status;
28142
28380
  if (latestVisibleStatus !== "idle") {
28143
28381
  LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed ${latestVisibleStatus})`);
28144
28382
  this.completedDebouncePending = null;
28145
28383
  this.completedDebounceTimer = null;
28146
28384
  return;
28147
28385
  }
28148
- const block2 = this.getCompletedFinalizationBlock(latestVisibleStatus, pending);
28386
+ const block2 = this.getCompletedFinalizationBlock(latestVisibleStatus, pending, { externalNativeFinal });
28149
28387
  if (block2) {
28150
28388
  const blockReason = block2.reason;
28151
28389
  const waitedMs = Date.now() - pending.firstObservedAt;
@@ -28186,7 +28424,18 @@ var CliProviderInstance = class {
28186
28424
  chatTitle: pending.chatTitle,
28187
28425
  duration: pending.duration,
28188
28426
  timestamp: pending.timestamp,
28189
- finalSummary: this.completionFinalSummary(this.adapter?.getScriptParsedStatus()?.messages)
28427
+ finalSummary: externalNativeFinal?.finalSummary || this.completionFinalSummary(this.adapter?.getScriptParsedStatus()?.messages),
28428
+ ...externalNativeFinal ? {
28429
+ completionDiagnostic: {
28430
+ providerType: this.type,
28431
+ sessionId: this.instanceId,
28432
+ providerSessionId: this.providerSessionId || null,
28433
+ reconciliationReason: "external_native_final_assistant_while_adapter_busy",
28434
+ finalAssistantPresent: true,
28435
+ finalAssistantEvidenceSource: externalNativeFinal.evidence.source,
28436
+ externalFinalFingerprint: externalNativeFinal.fingerprint
28437
+ }
28438
+ } : {}
28190
28439
  });
28191
28440
  this.completedDebouncePending = null;
28192
28441
  this.completedDebounceTimer = null;
@@ -28242,7 +28491,8 @@ var CliProviderInstance = class {
28242
28491
  const parsedStatus = null;
28243
28492
  const rawStatus = adapterStatus.status;
28244
28493
  const autoApproveActive = this.maybeAutoApproveStatus(adapterStatus, now);
28245
- const newStatus = autoApproveActive ? "generating" : rawStatus;
28494
+ const externalNativeFinal = this.getExternalNativeFinalReconciliation(void 0, adapterStatus);
28495
+ const newStatus = externalNativeFinal && isCliGeneratingLikeStatus(rawStatus) ? "idle" : autoApproveActive ? "generating" : rawStatus;
28246
28496
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
28247
28497
  const chatTitle = `${this.provider.name} \xB7 ${dirName}`;
28248
28498
  const partial = this.adapter.getPartialResponse();
@@ -30248,15 +30498,29 @@ function colorize(color, text) {
30248
30498
  const fn = chalkApi?.[color];
30249
30499
  return typeof fn === "function" ? fn(text) : text;
30250
30500
  }
30251
- var COORDINATOR_DELEGATED_ENV_UNSETS = {
30252
- ADHDEV_INLINE_MESH: "",
30253
- ADHDEV_MCP_TRANSPORT: "",
30254
- ADHDEV_MESH_ID: "",
30255
- HERMES_EPHEMERAL_SYSTEM_PROMPT: ""
30256
- };
30501
+ var DEFAULT_COORDINATOR_DELEGATED_ENV_UNSETS = [
30502
+ "ADHDEV_INLINE_MESH",
30503
+ "ADHDEV_MCP_TRANSPORT",
30504
+ "ADHDEV_MESH_ID",
30505
+ "HERMES_EPHEMERAL_SYSTEM_PROMPT"
30506
+ ];
30257
30507
  function hasCliArg(args, flag) {
30258
30508
  return args.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
30259
30509
  }
30510
+ function hasConfigOverride(args, key) {
30511
+ for (let index = 0; index < args.length; index += 1) {
30512
+ const arg = args[index];
30513
+ const next = args[index + 1];
30514
+ if ((arg === "-c" || arg === "--config") && typeof next === "string") {
30515
+ if (next === key || next.startsWith(`${key}=`) || next.startsWith(`${key}.`)) return true;
30516
+ }
30517
+ if (arg.startsWith("--config=")) {
30518
+ const value = arg.slice("--config=".length);
30519
+ if (value === key || value.startsWith(`${key}=`) || value.startsWith(`${key}.`)) return true;
30520
+ }
30521
+ }
30522
+ return false;
30523
+ }
30260
30524
  function ensureEmptyDelegatedMcpConfig(workspace) {
30261
30525
  const baseDir = path23.join(os17.tmpdir(), "adhdev-delegated-agent-empty-mcp");
30262
30526
  mkdirSync11(baseDir, { recursive: true });
@@ -30266,11 +30530,30 @@ function ensureEmptyDelegatedMcpConfig(workspace) {
30266
30530
  return filePath;
30267
30531
  }
30268
30532
  function buildCoordinatorDelegatedCliLaunchOptions(input) {
30269
- const cliType = String(input.cliType || "").trim();
30270
30533
  const cliArgs = Array.isArray(input.cliArgs) ? [...input.cliArgs] : [];
30271
- const env = { ...input.env || {}, ...COORDINATOR_DELEGATED_ENV_UNSETS };
30272
- if (cliType === "claude-cli" && !hasCliArg(cliArgs, "--mcp-config")) {
30273
- cliArgs.unshift("--mcp-config", ensureEmptyDelegatedMcpConfig(input.workspace));
30534
+ const env = { ...input.env || {} };
30535
+ const envUnsets = new Set(DEFAULT_COORDINATOR_DELEGATED_ENV_UNSETS);
30536
+ for (const key of input.isolation?.env?.unset || []) {
30537
+ if (typeof key === "string" && key.trim()) envUnsets.add(key.trim());
30538
+ }
30539
+ for (const key of envUnsets) env[key] = "";
30540
+ for (const rule of input.isolation?.args || []) {
30541
+ if (!rule || typeof rule !== "object") continue;
30542
+ if (rule.mode === "empty_mcp_config") {
30543
+ if (rule.flag && !hasCliArg(cliArgs, rule.flag)) {
30544
+ cliArgs.unshift(rule.flag, ensureEmptyDelegatedMcpConfig(input.workspace));
30545
+ }
30546
+ if (rule.strictFlag && !hasCliArg(cliArgs, rule.strictFlag)) {
30547
+ cliArgs.unshift(rule.strictFlag);
30548
+ }
30549
+ continue;
30550
+ }
30551
+ if (rule.mode === "config_override") {
30552
+ const key = String(rule.dedupeKey || rule.key || "").trim();
30553
+ const flag = String(rule.flag || "").trim();
30554
+ if (!key || !flag || hasConfigOverride(cliArgs, key)) continue;
30555
+ cliArgs.unshift(flag, `${rule.key}=${rule.value}`);
30556
+ }
30274
30557
  }
30275
30558
  return { cliArgs, env };
30276
30559
  }
@@ -30933,22 +31216,25 @@ Run 'adhdev doctor' for detailed diagnostics.`
30933
31216
  const dir = resolved.path;
30934
31217
  const launchSource = resolved.source;
30935
31218
  if (!cliType) throw new Error("cliType required");
31219
+ const providerType = this.providerLoader.resolveAlias(cliType);
31220
+ const provLookup = this.providerLoader.getMeta(providerType);
30936
31221
  const settingsOverride = args?.settings && typeof args.settings === "object" ? args.settings : void 0;
30937
31222
  const delegatedLaunch = settingsOverride?.launchedByCoordinator === true ? buildCoordinatorDelegatedCliLaunchOptions({
30938
31223
  cliType,
30939
31224
  workspace: dir,
30940
31225
  cliArgs: args?.cliArgs,
30941
- env: args?.env
31226
+ env: args?.env,
31227
+ isolation: provLookup?.meshCoordinator?.delegatedWorkerIsolation
30942
31228
  }) : null;
30943
- const provLookup = this.providerLoader.getMeta(this.providerLoader.resolveAlias(cliType));
30944
- const provTrust = provLookup?._sourceTrust;
31229
+ const provMeta = provLookup;
31230
+ const provTrust = provMeta?._sourceTrust;
30945
31231
  if (provTrust === "external-untrusted" && args?.confirmExternalUntrusted !== true) {
30946
31232
  return {
30947
31233
  success: false,
30948
31234
  error: "untrusted_external_provider",
30949
31235
  provider: {
30950
31236
  type: provLookup?.type ?? cliType,
30951
- sourceName: provLookup?._sourceName ?? null,
31237
+ sourceName: provMeta?._sourceName ?? null,
30952
31238
  trust: provTrust
30953
31239
  },
30954
31240
  hint: "Resend launch_cli with confirmExternalUntrusted=true after the user explicitly approves running JavaScript from this 3rd-party source."
@@ -31420,7 +31706,10 @@ function validateMeshCoordinator(raw, errors) {
31420
31706
  if (meshCoordinator.reason !== void 0 && (typeof meshCoordinator.reason !== "string" || !meshCoordinator.reason.trim())) {
31421
31707
  errors.push("meshCoordinator.reason must be a non-empty string when provided");
31422
31708
  }
31423
- const mcpConfig = meshCoordinator.mcpConfig;
31709
+ validateMeshCoordinatorMcpConfig(meshCoordinator.mcpConfig, errors);
31710
+ validateMeshCoordinatorDelegatedWorkerIsolation(meshCoordinator.delegatedWorkerIsolation, errors);
31711
+ }
31712
+ function validateMeshCoordinatorMcpConfig(mcpConfig, errors) {
31424
31713
  if (mcpConfig === void 0) return;
31425
31714
  if (!mcpConfig || typeof mcpConfig !== "object" || Array.isArray(mcpConfig)) {
31426
31715
  errors.push("meshCoordinator.mcpConfig must be an object");
@@ -31461,6 +31750,56 @@ function validateMeshCoordinator(raw, errors) {
31461
31750
  }
31462
31751
  }
31463
31752
  }
31753
+ function validateMeshCoordinatorDelegatedWorkerIsolation(raw, errors) {
31754
+ if (raw === void 0) return;
31755
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
31756
+ errors.push("meshCoordinator.delegatedWorkerIsolation must be an object");
31757
+ return;
31758
+ }
31759
+ const isolation = raw;
31760
+ const env = isolation.env;
31761
+ if (env !== void 0) {
31762
+ if (!env || typeof env !== "object" || Array.isArray(env)) {
31763
+ errors.push("meshCoordinator.delegatedWorkerIsolation.env must be an object");
31764
+ } else {
31765
+ const unset = env.unset;
31766
+ if (unset !== void 0 && (!Array.isArray(unset) || unset.some((key) => typeof key !== "string" || !key.trim()))) {
31767
+ errors.push("meshCoordinator.delegatedWorkerIsolation.env.unset must be an array of non-empty strings");
31768
+ }
31769
+ }
31770
+ }
31771
+ const args = isolation.args;
31772
+ if (args === void 0) return;
31773
+ if (!Array.isArray(args)) {
31774
+ errors.push("meshCoordinator.delegatedWorkerIsolation.args must be an array");
31775
+ return;
31776
+ }
31777
+ for (const [index, rule] of args.entries()) {
31778
+ const prefix = `meshCoordinator.delegatedWorkerIsolation.args[${index}]`;
31779
+ if (!rule || typeof rule !== "object" || Array.isArray(rule)) {
31780
+ errors.push(`${prefix} must be an object`);
31781
+ continue;
31782
+ }
31783
+ const item = rule;
31784
+ const mode = item.mode;
31785
+ if (mode !== "empty_mcp_config" && mode !== "config_override") {
31786
+ errors.push(`${prefix}.mode must be one of: empty_mcp_config, config_override`);
31787
+ continue;
31788
+ }
31789
+ for (const key of mode === "empty_mcp_config" ? ["flag"] : ["flag", "key", "value"]) {
31790
+ const value = item[key];
31791
+ if (typeof value !== "string" || !value.trim()) {
31792
+ errors.push(`${prefix}.${key} must be a non-empty string`);
31793
+ }
31794
+ }
31795
+ for (const key of ["strictFlag", "dedupeKey"]) {
31796
+ const value = item[key];
31797
+ if (value !== void 0 && (typeof value !== "string" || !value.trim())) {
31798
+ errors.push(`${prefix}.${key} must be a non-empty string when provided`);
31799
+ }
31800
+ }
31801
+ }
31802
+ }
31464
31803
  function validateControl(control, errors) {
31465
31804
  if (!control || typeof control !== "object") {
31466
31805
  errors.push("controls: each control must be an object");
@@ -37122,9 +37461,14 @@ function readCachedInlineMeshActiveSessionDetails(node) {
37122
37461
  node?.provider_type
37123
37462
  ),
37124
37463
  state: readStringValue(fallbackSession.status, fallbackSession.state, fallbackSession.lifecycle),
37464
+ chatStatus: readStringValue(fallbackSession.chatStatus, fallbackSession.chat_status),
37125
37465
  lifecycle: readStringValue(fallbackSession.lifecycle),
37126
37466
  title: readStringValue(fallbackSession.title, fallbackSession.displayName, fallbackSession.display_name) ?? null,
37127
37467
  workspace: readStringValue(fallbackSession.workspace, node?.workspace) ?? null,
37468
+ role: readStringValue(fallbackSession.role) ?? null,
37469
+ isSelfCoordinator: fallbackSession.isSelfCoordinator === true || fallbackSession.is_self_coordinator === true,
37470
+ createdAt: readStringValue(fallbackSession.createdAt, fallbackSession.created_at) ?? null,
37471
+ startedAt: readStringValue(fallbackSession.startedAt, fallbackSession.started_at) ?? null,
37128
37472
  lastActivityAt: readStringValue(fallbackSession.lastActivityAt, fallbackSession.last_activity_at) ?? null,
37129
37473
  recoveryState: readStringValue(fallbackSession.recoveryState, fallbackSession.recovery_state) ?? null,
37130
37474
  isCached: true
@@ -37265,15 +37609,26 @@ async function hydrateInlineMeshDirectTruth(args) {
37265
37609
  };
37266
37610
  }
37267
37611
  function summarizeMeshSessionRecord(record) {
37612
+ const meta = readObjectRecord(record?.meta);
37613
+ const isSelfCoordinator = Boolean(readStringValue(meta.meshCoordinatorFor));
37614
+ const chatStatus = readStringValue(record?.chatStatus, record?.activeChat?.status, meta.chatStatus, meta.sessionStatus);
37615
+ const state = readLiveMeshSessionState(record);
37616
+ 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;
37268
37617
  return {
37269
37618
  sessionId: readStringValue(record?.sessionId) || "unknown",
37270
37619
  providerType: readStringValue(record?.providerType),
37271
- state: readLiveMeshSessionState(record),
37620
+ state,
37621
+ chatStatus,
37272
37622
  lifecycle: readStringValue(record?.lifecycle),
37273
37623
  surfaceKind: getSessionHostSurfaceKind(record),
37274
- recoveryState: readStringValue(record?.meta?.runtimeRecoveryState) ?? null,
37624
+ recoveryState: readStringValue(meta.runtimeRecoveryState) ?? null,
37275
37625
  workspace: readStringValue(record?.workspace) ?? null,
37276
37626
  title: readStringValue(record?.displayName, record?.workspaceLabel) ?? null,
37627
+ role: isSelfCoordinator ? "coordinator" : readStringValue(meta.meshRole, meta.role) ?? null,
37628
+ isSelfCoordinator,
37629
+ statusNote,
37630
+ createdAt: toIsoTimestamp(record?.createdAt ?? record?.created_at),
37631
+ startedAt: toIsoTimestamp(record?.startedAt ?? record?.started_at ?? record?.spawnedAtMs ?? record?.spawned_at_ms),
37277
37632
  lastActivityAt: toIsoTimestamp(record?.updatedAt ?? record?.lastActivityAt ?? record?.last_activity_at),
37278
37633
  isCached: false
37279
37634
  };
@@ -38297,6 +38652,15 @@ var DaemonCommandRouter = class {
38297
38652
  this.aggregateMeshStatusCache.set(meshId, { builtAt, snapshot: this.cloneJsonValue(next), queueRevision: getMeshQueueRevision(meshId) });
38298
38653
  return next;
38299
38654
  }
38655
+ getCachedInlineMeshNodes() {
38656
+ const nodes = [];
38657
+ for (const mesh of this.inlineMeshCache.values()) {
38658
+ if (Array.isArray(mesh?.nodes)) {
38659
+ nodes.push(...mesh.nodes);
38660
+ }
38661
+ }
38662
+ return nodes;
38663
+ }
38300
38664
  getCachedInlineMesh(meshId, inlineMesh) {
38301
38665
  if (inlineMesh && typeof inlineMesh === "object") {
38302
38666
  return this.warmInlineMeshCache(meshId, inlineMesh);
@@ -38345,6 +38709,7 @@ var DaemonCommandRouter = class {
38345
38709
  }
38346
38710
  invalidateAggregateMeshStatus(meshId) {
38347
38711
  this.aggregateMeshStatusCache.delete(meshId);
38712
+ this.deps.onMeshStateChange?.(meshId);
38348
38713
  }
38349
38714
  async requireMeshHostMutationOwner(meshId, inlineMesh, operation) {
38350
38715
  const meshRecord = await this.getMeshForCommand(meshId, inlineMesh, { preferInline: true });
@@ -49837,6 +50202,7 @@ async function initDaemonComponents(config) {
49837
50202
  },
49838
50203
  onIdeConnected: () => poller?.start(),
49839
50204
  onStatusChange: config.onStatusChange,
50205
+ onMeshStateChange: config.onMeshStateChange,
49840
50206
  onPostChatCommand: config.onPostChatCommand,
49841
50207
  sessionHostControl: config.sessionHostControl,
49842
50208
  statusInstanceId: config.statusInstanceId,
@@ -50429,6 +50795,7 @@ export {
50429
50795
  probeCdpPort,
50430
50796
  queuePendingMeshCoordinatorEvent,
50431
50797
  readSession3 as readAntigravityCliSession,
50798
+ readCachedInlineMeshActiveSessionDetails,
50432
50799
  readChatHistory,
50433
50800
  readSession as readClaudeCliSession,
50434
50801
  readSession2 as readCodexCliSession,