@adhdev/daemon-core 0.9.82-rc.437 → 0.9.82-rc.439

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.
package/dist/index.mjs CHANGED
@@ -404,10 +404,10 @@ function readInjected(value) {
404
404
  }
405
405
  function getDaemonBuildInfo() {
406
406
  if (cached) return cached;
407
- const commit = readInjected(true ? "07c5209009300611151cd8363b63b4b066b6ecec" : void 0) ?? "unknown";
408
- const commitShort = readInjected(true ? "07c52090" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
409
- const version = readInjected(true ? "0.9.82-rc.437" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
410
- const builtAt = readInjected(true ? "2026-07-01T00:49:44.850Z" : void 0);
407
+ const commit = readInjected(true ? "065287032ac520cf8950f90025e1758b873723b1" : void 0) ?? "unknown";
408
+ const commitShort = readInjected(true ? "06528703" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
409
+ const version = readInjected(true ? "0.9.82-rc.439" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
410
+ const builtAt = readInjected(true ? "2026-07-01T04:55:17.522Z" : void 0);
411
411
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
412
412
  return cached;
413
413
  }
@@ -2857,16 +2857,21 @@ __export(mesh_config_exports, {
2857
2857
  createMesh: () => createMesh,
2858
2858
  createMeshHostPairingToken: () => createMeshHostPairingToken,
2859
2859
  deleteMesh: () => deleteMesh,
2860
+ getMagiKindPanel: () => getMagiKindPanel,
2860
2861
  getMagiPanel: () => getMagiPanel,
2861
2862
  getMesh: () => getMesh,
2862
2863
  getMeshByRepo: () => getMeshByRepo,
2864
+ listMagiKindPanels: () => listMagiKindPanels,
2863
2865
  listMagiPanels: () => listMagiPanels,
2864
2866
  listMeshes: () => listMeshes,
2865
2867
  markMeshHostPairingJoined: () => markMeshHostPairingJoined,
2866
2868
  normalizeMagiPanel: () => normalizeMagiPanel,
2869
+ normalizeMagiSlots: () => normalizeMagiSlots,
2867
2870
  normalizeRepoIdentity: () => normalizeRepoIdentity,
2871
+ removeMagiKindPanel: () => removeMagiKindPanel,
2868
2872
  removeMagiPanel: () => removeMagiPanel,
2869
2873
  removeNode: () => removeNode,
2874
+ setMagiKindPanel: () => setMagiKindPanel,
2870
2875
  tokenIdForManualPairing: () => tokenIdForManualPairing,
2871
2876
  updateMesh: () => updateMesh,
2872
2877
  updateNode: () => updateNode,
@@ -3285,11 +3290,13 @@ function normalizeMagiPanel(config) {
3285
3290
  throw new Error(`invalid_magi_panel: member[${idx}].provider is required`);
3286
3291
  }
3287
3292
  const nodeId = typeof m.nodeId === "string" && m.nodeId.trim() ? m.nodeId.trim() : void 0;
3293
+ const model = typeof m.model === "string" && m.model.trim() ? m.model.trim() : void 0;
3288
3294
  const capabilityTags = normalizeCapabilityTags(m.capabilityTags);
3289
3295
  const n = normalizeReplicaCount(m.n);
3290
3296
  return {
3291
3297
  provider,
3292
3298
  ...nodeId ? { nodeId } : {},
3299
+ ...model ? { model } : {},
3293
3300
  ...capabilityTags ? { capabilityTags } : {},
3294
3301
  ...n !== void 0 ? { n } : {}
3295
3302
  };
@@ -3342,7 +3349,78 @@ function removeMagiPanel(name) {
3342
3349
  saveMeshConfig(stored);
3343
3350
  return true;
3344
3351
  }
3345
- var mergeMeshPolicy, MAX_MAGI_PANEL_MEMBERS;
3352
+ function normalizeMagiTaskKindKey(raw) {
3353
+ const s2 = typeof raw === "string" ? raw.trim().toLowerCase() : "";
3354
+ if (!MAGI_KIND_PANEL_KINDS.includes(s2)) {
3355
+ throw new Error(`invalid_magi_kind_panel: task_kind must be one of ${MAGI_KIND_PANEL_KINDS.join(" / ")} (got '${s2 || "(empty)"}')`);
3356
+ }
3357
+ return s2;
3358
+ }
3359
+ function normalizeMagiSlots(slots) {
3360
+ if (!Array.isArray(slots) || slots.length === 0) {
3361
+ throw new Error("invalid_magi_kind_panel: slots must be a non-empty array");
3362
+ }
3363
+ if (slots.length > MAX_MAGI_KIND_SLOTS) {
3364
+ throw new Error(`invalid_magi_kind_panel: too many slots (max ${MAX_MAGI_KIND_SLOTS})`);
3365
+ }
3366
+ return slots.map((entry, idx) => {
3367
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
3368
+ throw new Error(`invalid_magi_kind_panel: slot[${idx}] must be an object`);
3369
+ }
3370
+ const s2 = entry;
3371
+ const provider = typeof s2.provider === "string" ? s2.provider.trim() : "";
3372
+ if (!provider) {
3373
+ throw new Error(`invalid_magi_kind_panel: slot[${idx}].provider is required`);
3374
+ }
3375
+ const nodeId = typeof s2.nodeId === "string" && s2.nodeId.trim() ? s2.nodeId.trim() : void 0;
3376
+ const model = typeof s2.model === "string" && s2.model.trim() ? s2.model.trim() : void 0;
3377
+ const capabilityTags = normalizeCapabilityTags(s2.capabilityTags);
3378
+ const n = normalizeReplicaCount(s2.n);
3379
+ return {
3380
+ provider,
3381
+ ...nodeId ? { nodeId } : {},
3382
+ ...model ? { model } : {},
3383
+ ...capabilityTags ? { capabilityTags } : {},
3384
+ ...n !== void 0 ? { n } : {}
3385
+ };
3386
+ });
3387
+ }
3388
+ function listMagiKindPanels() {
3389
+ return loadMeshConfig().magiKindPanels ?? {};
3390
+ }
3391
+ function getMagiKindPanel(kind) {
3392
+ let key2;
3393
+ try {
3394
+ key2 = normalizeMagiTaskKindKey(kind);
3395
+ } catch {
3396
+ return void 0;
3397
+ }
3398
+ return loadMeshConfig().magiKindPanels?.[key2];
3399
+ }
3400
+ function setMagiKindPanel(kind, slots) {
3401
+ const key2 = normalizeMagiTaskKindKey(kind);
3402
+ const normalized = normalizeMagiSlots(slots);
3403
+ const stored = loadMeshConfig();
3404
+ const map = stored.magiKindPanels ?? {};
3405
+ map[key2] = normalized;
3406
+ stored.magiKindPanels = map;
3407
+ saveMeshConfig(stored);
3408
+ return normalized;
3409
+ }
3410
+ function removeMagiKindPanel(kind) {
3411
+ let key2;
3412
+ try {
3413
+ key2 = normalizeMagiTaskKindKey(kind);
3414
+ } catch {
3415
+ return false;
3416
+ }
3417
+ const stored = loadMeshConfig();
3418
+ if (!stored.magiKindPanels || !stored.magiKindPanels[key2]) return false;
3419
+ delete stored.magiKindPanels[key2];
3420
+ saveMeshConfig(stored);
3421
+ return true;
3422
+ }
3423
+ var mergeMeshPolicy, MAX_MAGI_PANEL_MEMBERS, MAGI_KIND_PANEL_KINDS, MAX_MAGI_KIND_SLOTS;
3346
3424
  var init_mesh_config = __esm({
3347
3425
  "src/config/mesh-config.ts"() {
3348
3426
  "use strict";
@@ -3352,6 +3430,8 @@ var init_mesh_config = __esm({
3352
3430
  init_mesh_host_ownership();
3353
3431
  mergeMeshPolicy = mergeAndNormalizePolicy;
3354
3432
  MAX_MAGI_PANEL_MEMBERS = 24;
3433
+ MAGI_KIND_PANEL_KINDS = ["claim_audit", "rca", "design", "freeform"];
3434
+ MAX_MAGI_KIND_SLOTS = 24;
3355
3435
  }
3356
3436
  });
3357
3437
 
@@ -3418,6 +3498,7 @@ Default branch: \`${mesh.defaultBranch}\`` : ""}`);
3418
3498
  sections.push(TOOLS_SECTION);
3419
3499
  sections.push(TOOL_EXPOSURE_PREFLIGHT_SECTION);
3420
3500
  sections.push(WORKFLOW_SECTION);
3501
+ sections.push(ONBOARDING_SECTION);
3421
3502
  sections.push(buildRulesSection(coordinatorCliType));
3422
3503
  return sections.join("\n\n");
3423
3504
  }
@@ -3450,6 +3531,7 @@ function expandPromptPlaceholders(template, ctx) {
3450
3531
  policy: buildPolicySection(mergeAndNormalizePolicy(void 0, mesh.policy)),
3451
3532
  tools: TOOLS_SECTION,
3452
3533
  workflow: WORKFLOW_SECTION,
3534
+ onboarding: ONBOARDING_SECTION,
3453
3535
  rules: buildRulesSection(coordinatorCliType),
3454
3536
  toolExposurePreflight: TOOL_EXPOSURE_PREFLIGHT_SECTION
3455
3537
  };
@@ -3608,7 +3690,7 @@ function buildRulesSection(coordinatorCliType) {
3608
3690
  - **Never fabricate tool results.** Always call the actual tool.
3609
3691
  - **Keep the user informed.** One or two sentences after each delegation round.${coordinatorNote}`;
3610
3692
  }
3611
- var TOOLS_SECTION, TOOL_EXPOSURE_PREFLIGHT_SECTION, WORKFLOW_SECTION;
3693
+ var TOOLS_SECTION, TOOL_EXPOSURE_PREFLIGHT_SECTION, WORKFLOW_SECTION, ONBOARDING_SECTION;
3612
3694
  var init_coordinator_prompt = __esm({
3613
3695
  "src/mesh/coordinator-prompt.ts"() {
3614
3696
  "use strict";
@@ -3637,7 +3719,12 @@ var init_coordinator_prompt = __esm({
3637
3719
  | \`mesh_clone_node\` | Create a worktree node for isolated parallel branch work |
3638
3720
  | \`mesh_refine_node\` | Validate and merge a completed worktree node back into its base branch |
3639
3721
  | \`mesh_remove_node\` | Remove a node (cleans up worktree if applicable) |
3640
- | \`mesh_cleanup_sessions\` | Manually clean up delegated session records for a node |`;
3722
+ | \`mesh_cleanup_sessions\` | Manually clean up delegated session records for a node |
3723
+ | \`mesh_init\` | Guided onboarding for a fresh repo: dry-run scan \u2192 suggest \`.adhdev/*\` configs (refine/bootstrap/change-impact) + providerPriority + current-config echo; gated write on approval |
3724
+ | \`mesh_reinit\` | Re-onboard an already-configured repo: re-suggest with overwrite semantics + current-vs-suggested diff; dry-run preview first, per-section approval before write |
3725
+ | \`mesh_write_mesh_json_config\` | Gated write of \`.adhdev/mesh.json\` (repo coordinator-prompt config) from the mesh entry \u2014 dry-run/overwrite like mesh_init |
3726
+ | \`mesh_magi_kind_panel_set\` | Bind a task_kind \u2192 MAGI kind-panel slots (machine-local, wholesale replacement \u2014 approve current-vs-new first) |
3727
+ | \`mesh_magi_kind_panel_list\` | List configured task_kind \u2192 MAGI kind-panel slot bindings (machine-local, read-only) |`;
3641
3728
  TOOL_EXPOSURE_PREFLIGHT_SECTION = `## Tool Exposure Preflight
3642
3729
 
3643
3730
  Before doing any coordinator work, confirm that the actual callable tool list includes \`mesh_status\` and the other \`mesh_*\` tools from the table above. If this Repo Mesh coordinator prompt is present but the callable \`mesh_*\` tools are missing, the MCP server/tool manifest is stale or not injected yet. Do not substitute terminal/file/git tools, do not inspect or edit the repository directly, and do not continue as a non-mesh local coding agent. Stop immediately and tell the user to run \`/reload-mcp\` or start a fresh coordinator session so ADHDev can reconnect \`adhdev-mesh\`.`;
@@ -3670,6 +3757,28 @@ Follow these recovery rules:
3670
3757
  2. **If "Max retries exceeded"**: Do NOT retry on the same node. Either reassign the task to a different node, or inform the user that the task requires manual intervention.
3671
3758
  3. **If no recovery context**: The stop may be intentional (normal completion). Use \`mesh_read_chat\` once to verify, then move on.
3672
3759
  4. **Always record what happened**: After handling a failure, briefly note the outcome in your report to the user.`;
3760
+ ONBOARDING_SECTION = `## Onboarding / Reinit
3761
+
3762
+ When the user asks to **set up / configure / onboard** this repo for Repo Mesh (or to **re-init / reconfigure** an already-onboarded repo), run ONE guided, approval-gated conversation. You draft, the user approves, the daemon writes. Never auto-write a heuristic suggestion without an explicit user approval turn.
3763
+
3764
+ **Save scopes \u2014 label every draft with its scope before asking for approval:**
3765
+ - **repo-file (commit target)** \u2014 \`.adhdev/refine.json\`, \`.adhdev/worktree_bootstrap.json\`, \`.adhdev/change-impact.json\`, \`.adhdev/mesh.json\`. These are committed to the repository and shared with every machine/contributor.
3766
+ - **machine-local** \u2014 MAGI kind\u2192panel bindings and named MAGI panels, node providerPriority (\`~/.adhdev/meshes.json\`). These stay on this machine and are NOT committed.
3767
+
3768
+ **Guided sequence:**
3769
+ 1. **Scan (dry-run)** \u2014 Call \`mesh_init\` (write=false, the default). It returns per-domain suggested configs for refine / worktree_bootstrap / change-impact, a recommended providerPriority, AND \`currentConfig\` \u2014 the currently-saved config per domain (repo files + machine-local \`magiKindPanels\`). Nothing is written.
3770
+ 2. **Present drafts** \u2014 For each domain, show the user the suggested config with its **save scope label** (repo-file vs machine-local). When \`currentConfig\` already has a saved value for a domain (init on a partially-onboarded repo, or any reinit), present a **current-vs-suggested diff**, not just the suggestion.
3771
+ 3. **Approve \u2192 gated write** \u2014 Only after the user approves, call the matching gated-write tool:
3772
+ - repo \`.adhdev/*\` config files \u2192 \`mesh_init\` with \`write=true\` (and \`overwrite=true\` ONLY for domains the user approved replacing).
3773
+ - \`.adhdev/mesh.json\` (coordinator prompt / operating notes) \u2192 \`mesh_write_mesh_json_config\` (write=true, overwrite only if approved).
3774
+ - machine-local MAGI kind\u2192panel slots \u2192 \`mesh_magi_kind_panel_set\` (write=true). NOTE: a kind binding is a **wholesale replacement** of that kind's slot list \u2014 present the current-vs-new slots first.
3775
+ - machine-local named MAGI panels \u2192 \`mesh_magi_panel_set\`. providerPriority \u2192 apply via node policy update.
3776
+
3777
+ **init vs reinit:**
3778
+ - **\`mesh_init\`** \u2014 for a fresh, never-onboarded repo. Existing config files are kept (existing-wins) unless the user explicitly approves overwrite. Use for first-time setup.
3779
+ - **\`mesh_reinit\`** \u2014 for a repo that is already onboarded and needs its config refreshed. It re-suggests with OVERWRITE semantics and returns the current-vs-suggested \`currentConfig\` echo. Its first call is a DRY-RUN preview: you MUST present the per-section current-vs-suggested diff and get EXPLICIT per-section approval before re-invoking with write=true. Overwrite is a wholesale replacement, so it silently drops operator hand-edits if you skip the diff \u2014 never do that.
3780
+
3781
+ `;
3673
3782
  }
3674
3783
  });
3675
3784
 
@@ -5272,6 +5381,7 @@ function enqueueTask(meshId, message, opts) {
5272
5381
  ...dependsOn.length > 0 ? { dependsOn } : {},
5273
5382
  ...typeof opts?.missionId === "string" && opts.missionId.trim() ? { missionId: opts.missionId.trim() } : {},
5274
5383
  ...typeof opts?.consensusGroupId === "string" && opts.consensusGroupId.trim() ? { consensusGroupId: opts.consensusGroupId.trim() } : {},
5384
+ ...typeof opts?.model === "string" && opts.model.trim() ? { model: opts.model.trim() } : {},
5275
5385
  ...typeof opts?.sourceCoordinatorSessionId === "string" && opts.sourceCoordinatorSessionId.trim() ? { sourceCoordinatorSessionId: opts.sourceCoordinatorSessionId.trim() } : {},
5276
5386
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
5277
5387
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -12973,7 +13083,10 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
12973
13083
  launchResult2 = await components.dispatchMeshCommand(launchTarget.daemonId, "launch_cli", {
12974
13084
  cliType: resolved.providerType,
12975
13085
  dir: node.workspace,
12976
- settings: remoteSettings
13086
+ settings: remoteSettings,
13087
+ // MAGI-KIND-PANEL model axis: forward the task's model override so the
13088
+ // remote worker session launches with it (initialModel). Best-effort.
13089
+ ...typeof task.model === "string" && task.model.trim() ? { initialModel: task.model.trim() } : {}
12977
13090
  });
12978
13091
  } catch (e) {
12979
13092
  markAutoLaunch(meshId, task.id, { status: "failed", reason: `remote_launch_dispatch_failed: ${e?.message || String(e)}`, nodeId, providerType: resolved.providerType });
@@ -12999,7 +13112,10 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
12999
13112
  const launchResult = await components.cliManager.handleCliCommand("launch_cli", {
13000
13113
  cliType: resolved.providerType,
13001
13114
  dir: node.workspace,
13002
- settings: launchSettings
13115
+ settings: launchSettings,
13116
+ // MAGI-KIND-PANEL model axis: local launch forwards the task's model
13117
+ // override as initialModel (CLI → modelLaunchArgs; ACP → setConfigOption).
13118
+ ...typeof task.model === "string" && task.model.trim() ? { initialModel: task.model.trim() } : {}
13003
13119
  });
13004
13120
  if (!launchResult?.success) {
13005
13121
  const reason = launchResult?.error || "launch_cli_failed";
@@ -18256,6 +18372,11 @@ var init_provider_schema = __esm({
18256
18372
  minimum: 0,
18257
18373
  description: "Delay between pasting prompt text and pressing Enter."
18258
18374
  },
18375
+ modelLaunchArgs: {
18376
+ type: "array",
18377
+ items: { type: "string" },
18378
+ description: "Template for expanding an initialModel selection into launch args. '{{model}}' is substituted with the model string (e.g. ['--model', '{{model}}'] \u2192 --model opus). Applied at launch when a model is requested for this CLI provider (MAGI kind-panel model axis). Absent \u2192 no launch-time model selection."
18379
+ },
18259
18380
  scriptCallBudgetMs: {
18260
18381
  type: "integer",
18261
18382
  minimum: 1,
@@ -28142,6 +28263,16 @@ function callProviderNativeHistoryRead(agentType, canonicalHistory, scripts, his
28142
28263
  const result = fn({
28143
28264
  agentType,
28144
28265
  sessionId: normalizedSessionId,
28266
+ // Arm the native-history executor's session pin guard. When the
28267
+ // instance is already bound to a provider session, pass that id as
28268
+ // `providerSessionId` so the executor rejects any *other* newest
28269
+ // session it would otherwise pick (hermes ≥0.14 creates a fresh
28270
+ // `sessions` row per internal sub-session, so an unpinned
28271
+ // newest-wins query drifts to a different id on every read →
28272
+ // re-bind churn + unbounded history re-hydration). When there is no
28273
+ // bound id yet (first-bind / workspace-only discovery) this is '',
28274
+ // which leaves the guard disarmed so discovery still works.
28275
+ providerSessionId: normalizedSessionId,
28145
28276
  historySessionId: normalizedSessionId,
28146
28277
  workspace,
28147
28278
  format: canonicalHistory?.format,
@@ -38557,24 +38688,28 @@ function executeSqlite(src, input) {
38557
38688
  return null;
38558
38689
  }
38559
38690
  try {
38560
- let sessionRow;
38561
- try {
38562
- const sessionFloorSeconds = typeof input.sessionStartedAtMs === "number" ? Math.floor(input.sessionStartedAtMs / 1e3) : 0;
38563
- const stmt = db.prepare(src.session_query);
38691
+ const requested = input.providerSessionId || "";
38692
+ let sessionId;
38693
+ if (requested) {
38694
+ sessionId = requested;
38695
+ } else {
38696
+ let sessionRow;
38564
38697
  try {
38565
- sessionRow = stmt.get(sessionFloorSeconds);
38698
+ const sessionFloorSeconds = typeof input.sessionStartedAtMs === "number" ? Math.floor(input.sessionStartedAtMs / 1e3) : 0;
38699
+ const stmt = db.prepare(src.session_query);
38700
+ try {
38701
+ sessionRow = stmt.get(sessionFloorSeconds);
38702
+ } catch {
38703
+ sessionRow = stmt.get();
38704
+ }
38566
38705
  } catch {
38567
- sessionRow = stmt.get();
38706
+ return null;
38568
38707
  }
38569
- } catch {
38570
- return null;
38708
+ if (!sessionRow) return null;
38709
+ const sessionIdRaw = Object.values(sessionRow)[0];
38710
+ sessionId = sessionIdRaw == null ? "" : String(sessionIdRaw);
38571
38711
  }
38572
- if (!sessionRow) return null;
38573
- const sessionIdRaw = Object.values(sessionRow)[0];
38574
- const sessionId = sessionIdRaw == null ? "" : String(sessionIdRaw);
38575
38712
  if (!sessionId) return null;
38576
- const requested = input.providerSessionId || "";
38577
- if (requested && sessionId !== requested) return null;
38578
38713
  const messageRows = db.prepare(src.message_query).all(sessionId);
38579
38714
  if (!messageRows || messageRows.length === 0) return null;
38580
38715
  const mtime = safeMtimeMs(resolved);
@@ -42016,7 +42151,9 @@ var CliProviderInstance = class _CliProviderInstance {
42016
42151
  typeof data.providerSessionId === "string" ? data.providerSessionId : ""
42017
42152
  );
42018
42153
  if (patchedProviderSessionId) {
42019
- this.promoteProviderSessionId(patchedProviderSessionId);
42154
+ this.promoteProviderSessionId(patchedProviderSessionId, {
42155
+ authoritative: data.sessionEvent === "new_session"
42156
+ });
42020
42157
  }
42021
42158
  if (data.sessionEvent === "new_session") {
42022
42159
  this.runtimeMessages = [];
@@ -42311,9 +42448,13 @@ ${effect.notification.body || ""}`.trim();
42311
42448
  }
42312
42449
  return lines.join("\n");
42313
42450
  }
42314
- promoteProviderSessionId(sessionId) {
42451
+ promoteProviderSessionId(sessionId, opts = {}) {
42315
42452
  const nextSessionId = String(sessionId || "").trim();
42316
42453
  if (!nextSessionId || nextSessionId === this.providerSessionId) return;
42454
+ if (this.providerSessionId && !opts.authoritative) {
42455
+ LOG.debug("CLI", `[${this.type}] ignoring non-authoritative session id ${nextSessionId} (bound to ${this.providerSessionId})`);
42456
+ return;
42457
+ }
42317
42458
  const previousHistorySessionId = this.providerSessionId || this.instanceId;
42318
42459
  const previousProviderSessionId = this.providerSessionId;
42319
42460
  this.providerSessionId = nextSessionId;
@@ -43953,6 +44094,11 @@ function expandResumeArgs(template, sessionId) {
43953
44094
  if (!Array.isArray(template) || template.length === 0) return void 0;
43954
44095
  return template.map((part) => part === "{{id}}" ? sessionId : part);
43955
44096
  }
44097
+ function expandModelLaunchArgs(template, model) {
44098
+ const m = typeof model === "string" ? model.trim() : "";
44099
+ if (!m || !Array.isArray(template) || template.length === 0) return void 0;
44100
+ return template.map((part) => part === "{{model}}" ? m : part);
44101
+ }
43956
44102
  function readSubcommandSessionId(args, subcommands) {
43957
44103
  const resumeIndex = args.findIndex((arg) => subcommands.includes(arg));
43958
44104
  if (resumeIndex < 0) return void 0;
@@ -44345,7 +44491,12 @@ Run 'adhdev doctor' for detailed diagnostics.`
44345
44491
  if (provider) {
44346
44492
  console.log(colorize("cyan", ` \u{1F4E6} Using provider: ${provider.name} (${provider.type})`));
44347
44493
  }
44348
- const sessionBinding = resolveCliSessionBinding(provider, normalizedType, cliArgs, options?.resumeSessionId);
44494
+ const modelLaunchArgs = expandModelLaunchArgs(provider?.modelLaunchArgs, initialModel);
44495
+ const cliArgsWithModel = modelLaunchArgs ? [...modelLaunchArgs, ...cliArgs || []] : cliArgs;
44496
+ if (initialModel && !modelLaunchArgs) {
44497
+ LOG.warn("CLI", `[${normalizedType}] initialModel='${initialModel}' requested but provider declares no modelLaunchArgs template \u2014 launching without model selection.`);
44498
+ }
44499
+ const sessionBinding = resolveCliSessionBinding(provider, normalizedType, cliArgsWithModel, options?.resumeSessionId);
44349
44500
  const resolvedCliArgs = sessionBinding.cliArgs;
44350
44501
  const instanceManager = this.deps.getInstanceManager();
44351
44502
  if (provider && instanceManager) {
@@ -46224,23 +46375,30 @@ function loadMessagesForSession(db, sessionId) {
46224
46375
  }
46225
46376
  return out;
46226
46377
  }
46227
- function readSession4(sessionPath) {
46378
+ function readSession4(sessionPath, requestedSessionId) {
46228
46379
  if (!sessionPath) return null;
46229
46380
  if (sessionPath === HERMES_STATE_DB) {
46230
46381
  const db = openDb();
46231
46382
  if (!db) return null;
46232
46383
  try {
46233
- const row = db.prepare(
46234
- `SELECT id, started_at FROM sessions
46235
- WHERE source = 'cli' AND message_count > 0
46236
- ORDER BY started_at DESC LIMIT 1`
46237
- ).get();
46238
- if (!row) return null;
46239
- const messages2 = loadMessagesForSession(db, row.id);
46384
+ const pinned = String(requestedSessionId || "").trim();
46385
+ let sessionId2;
46386
+ if (pinned) {
46387
+ sessionId2 = pinned;
46388
+ } else {
46389
+ const row = db.prepare(
46390
+ `SELECT id, started_at FROM sessions
46391
+ WHERE source = 'cli' AND message_count > 0
46392
+ ORDER BY started_at DESC LIMIT 1`
46393
+ ).get();
46394
+ if (!row) return null;
46395
+ sessionId2 = String(row.id);
46396
+ }
46397
+ const messages2 = loadMessagesForSession(db, sessionId2);
46240
46398
  if (messages2.length === 0) return null;
46241
46399
  return {
46242
46400
  messages: messages2,
46243
- providerSessionId: String(row.id),
46401
+ providerSessionId: sessionId2,
46244
46402
  source: "provider-native",
46245
46403
  sourcePath: sessionPath,
46246
46404
  sourceMtimeMs: statMtimeMs4(sessionPath),
@@ -46312,7 +46470,7 @@ function createNativeHistoryDispatcher(reader) {
46312
46470
  } catch {
46313
46471
  }
46314
46472
  }
46315
- const session = readByReader(reader, sourcePath, sessionId, workspace);
46473
+ const session = readByReader(reader, sourcePath, sessionId, workspace, requestedProviderSid);
46316
46474
  if (!session) return null;
46317
46475
  if (requestedProviderSid && session.providerSessionId && session.providerSessionId !== requestedProviderSid) {
46318
46476
  return null;
@@ -46475,7 +46633,7 @@ function resolveHermesPath(workspace, sessionId) {
46475
46633
  if (!fs24.existsSync(dir)) return null;
46476
46634
  return newestRecentFile2(dir, /^session_.*\.json$/);
46477
46635
  }
46478
- function readByReader(reader, sourcePath, sessionId, workspace) {
46636
+ function readByReader(reader, sourcePath, sessionId, workspace, requestedProviderSid) {
46479
46637
  switch (reader) {
46480
46638
  case "claude-cli":
46481
46639
  return readSession(sourcePath);
@@ -46483,8 +46641,13 @@ function readByReader(reader, sourcePath, sessionId, workspace) {
46483
46641
  return readSession2(sourcePath);
46484
46642
  case "antigravity-cli":
46485
46643
  return readSession3(sourcePath, sessionId || void 0, workspace || void 0);
46644
+ // hermes reads a *shared* state.db and would otherwise pick the newest
46645
+ // source='cli' session, which drifts every read (hermes ≥0.14 writes a
46646
+ // fresh row per internal sub-session). Pass the bound id so it reads
46647
+ // THAT session directly instead of newest-wins. claude/codex resolve a
46648
+ // per-session file upstream, so they need no equivalent pin here.
46486
46649
  case "hermes-cli":
46487
- return readSession4(sourcePath);
46650
+ return readSession4(sourcePath, requestedProviderSid || void 0);
46488
46651
  }
46489
46652
  }
46490
46653
  function cwdAsDashes(cwd) {
@@ -49171,6 +49334,88 @@ var meshCrudHandlers = {
49171
49334
  return { success: false, error: e.message };
49172
49335
  }
49173
49336
  },
49337
+ // Gated WRITE path for `.adhdev/mesh.json` — the sibling of export_mesh_json_config
49338
+ // (which only DRAFTS). Same write/overwrite/dry-run contract as mesh_init's config
49339
+ // writer: defaults to dry-run (no write), never clobbers an existing repo mesh.json
49340
+ // unless overwrite=true, and validates the scaffold before persisting. The scaffold
49341
+ // is built from the machine-local mesh entry (coordinator prompt override/append);
49342
+ // policy/operating-notes are intentionally NOT exported (see buildMeshJsonConfigScaffold).
49343
+ write_mesh_json_config: async (_ctx, args) => {
49344
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
49345
+ if (!meshId) return { success: false, error: "meshId required" };
49346
+ const workspace = typeof args?.workspace === "string" && args.workspace.trim() ? args.workspace.trim() : process.cwd();
49347
+ const write = args?.write === true;
49348
+ const overwrite = args?.overwrite === true;
49349
+ try {
49350
+ const { getMesh: getMesh2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
49351
+ const mesh = getMesh2(meshId);
49352
+ if (!mesh) return { success: false, error: "Mesh not found" };
49353
+ const {
49354
+ buildMeshJsonConfigScaffold: buildMeshJsonConfigScaffold2,
49355
+ serializeMeshJsonConfigScaffold: serializeMeshJsonConfigScaffold2,
49356
+ loadRepoMeshJsonConfig: loadRepoMeshJsonConfig2,
49357
+ normalizeRepoMeshDeclarativeConfig: normalizeRepoMeshDeclarativeConfig2,
49358
+ MESH_JSON_CONFIG_LOCATIONS: MESH_JSON_CONFIG_LOCATIONS2
49359
+ } = await Promise.resolve().then(() => (init_mesh_json_config(), mesh_json_config_exports));
49360
+ const { mkdirSync: mkdirSync21, writeFileSync: writeFileSync24 } = await import("fs");
49361
+ const { dirname: dirname17, join: join49 } = await import("path");
49362
+ const scaffold = buildMeshJsonConfigScaffold2(mesh);
49363
+ const scaffoldJson = serializeMeshJsonConfigScaffold2(scaffold);
49364
+ const relativePath = MESH_JSON_CONFIG_LOCATIONS2[0];
49365
+ const absolutePath = join49(workspace, relativePath);
49366
+ const validation = normalizeRepoMeshDeclarativeConfig2(scaffold);
49367
+ if (!validation.valid) {
49368
+ return { success: false, meshId, error: `invalid mesh.json scaffold: ${validation.errors.join("; ")}` };
49369
+ }
49370
+ const existing = loadRepoMeshJsonConfig2(workspace);
49371
+ const existingPresent = existing.sourceType === "repo_file" || existing.sourceType === "invalid";
49372
+ if (existingPresent && !overwrite) {
49373
+ return {
49374
+ success: true,
49375
+ meshId,
49376
+ written: false,
49377
+ dryRun: !write,
49378
+ skippedReason: "already_exists",
49379
+ path: absolutePath,
49380
+ relativePath,
49381
+ existing: existing.config,
49382
+ existingSourceType: existing.sourceType,
49383
+ scaffold,
49384
+ scaffoldJson,
49385
+ note: "A repo mesh.json already exists \u2014 kept as-is. Re-run with overwrite=true to replace it (this silently drops operator hand-edits, so present a current-vs-suggested diff first)."
49386
+ };
49387
+ }
49388
+ if (!write) {
49389
+ return {
49390
+ success: true,
49391
+ meshId,
49392
+ written: false,
49393
+ dryRun: true,
49394
+ path: absolutePath,
49395
+ relativePath,
49396
+ scaffold,
49397
+ scaffoldJson,
49398
+ note: "Dry-run: nothing written. Re-run with write=true to persist to the repo (commit target). meshes.json is untouched."
49399
+ };
49400
+ }
49401
+ mkdirSync21(dirname17(absolutePath), { recursive: true });
49402
+ writeFileSync24(absolutePath, `${scaffoldJson}
49403
+ `, "utf-8");
49404
+ return {
49405
+ success: true,
49406
+ meshId,
49407
+ written: true,
49408
+ dryRun: false,
49409
+ path: absolutePath,
49410
+ relativePath,
49411
+ scaffold,
49412
+ scaffoldJson,
49413
+ note: "Wrote .adhdev/mesh.json (repo commit target). Commit it to the repo; meshes.json (machine-local) is unchanged."
49414
+ };
49415
+ } catch (e) {
49416
+ return { success: false, error: e.message };
49417
+ }
49418
+ },
49174
49419
  delete_mesh: async (_ctx, args) => {
49175
49420
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
49176
49421
  if (!meshId) return { success: false, error: "meshId required" };
@@ -49232,6 +49477,42 @@ var meshCrudHandlers = {
49232
49477
  return { success: false, error: e.message };
49233
49478
  }
49234
49479
  },
49480
+ // ─── MAGI kind → panel bindings (MAGI-KIND-PANEL, machine-local config) ───
49481
+ // Per-task_kind slot lists in ~/.adhdev/meshes.json `magiKindPanels`. Same
49482
+ // owner-only gating and structured-error precedent as the magi_panel_* handlers
49483
+ // above (not listed in canPeerUsePrivilegedShareCommand → owner-only). set/remove
49484
+ // are WRITE commands; list is read-only. normalizeMagiSlots (inside setMagiKindPanel)
49485
+ // surfaces invalid_magi_kind_panel: … messages verbatim for the editor.
49486
+ magi_kind_panel_list: async (_ctx, _args) => {
49487
+ try {
49488
+ const { listMagiKindPanels: listMagiKindPanels2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
49489
+ return { success: true, kindPanels: listMagiKindPanels2() };
49490
+ } catch (e) {
49491
+ return { success: false, error: e.message };
49492
+ }
49493
+ },
49494
+ magi_kind_panel_set: async (_ctx, args) => {
49495
+ const kind = typeof args?.kind === "string" ? args.kind.trim() : "";
49496
+ if (!kind) return { success: false, error: "invalid_magi_kind_panel: task_kind is required" };
49497
+ try {
49498
+ const { setMagiKindPanel: setMagiKindPanel2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
49499
+ const slots = setMagiKindPanel2(kind, args?.slots);
49500
+ return { success: true, kind, slots };
49501
+ } catch (e) {
49502
+ return { success: false, error: e.message };
49503
+ }
49504
+ },
49505
+ magi_kind_panel_remove: async (_ctx, args) => {
49506
+ const kind = typeof args?.kind === "string" ? args.kind.trim() : "";
49507
+ if (!kind) return { success: false, error: "invalid_magi_kind_panel: task_kind is required" };
49508
+ try {
49509
+ const { removeMagiKindPanel: removeMagiKindPanel2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
49510
+ const removed = removeMagiKindPanel2(kind);
49511
+ return { success: true, removed };
49512
+ } catch (e) {
49513
+ return { success: false, error: e.message };
49514
+ }
49515
+ },
49235
49516
  add_mesh_node: async (ctx, args) => {
49236
49517
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
49237
49518
  const workspace = typeof args?.workspace === "string" ? args.workspace.trim() : "";
@@ -50160,10 +50441,13 @@ init_mesh_fast_forward();
50160
50441
  // src/mesh/mesh-init.ts
50161
50442
  init_refine_config();
50162
50443
  init_worktree_bootstrap_config();
50444
+ init_change_impact_config();
50445
+ init_mesh_config();
50163
50446
  import { existsSync as existsSync38, mkdirSync as mkdirSync15, writeFileSync as writeFileSync18 } from "fs";
50164
50447
  import { dirname as dirname11, join as join41 } from "path";
50165
50448
  var MESH_INIT_REFINE_CONFIG_PATH = MESH_REFINE_CONFIG_LOCATIONS[0];
50166
50449
  var MESH_INIT_WORKTREE_BOOTSTRAP_CONFIG_PATH = MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS[0];
50450
+ var MESH_INIT_CHANGE_IMPACT_CONFIG_PATH = CHANGE_IMPACT_CONFIG_LOCATIONS[0];
50167
50451
  var CANDIDATE_STALE_INPUTS = [
50168
50452
  "package-lock.json",
50169
50453
  "pnpm-lock.yaml",
@@ -50245,15 +50529,46 @@ function runMeshInit(mesh, workspace, detected, options = {}) {
50245
50529
  write,
50246
50530
  overwrite
50247
50531
  });
50532
+ const changeImpactLoaded = loadChangeImpactConfig(workspace);
50533
+ const changeImpact = applyConfigSuggestion({
50534
+ workspace,
50535
+ relativePath: MESH_INIT_CHANGE_IMPACT_CONFIG_PATH,
50536
+ existing: changeImpactLoaded.sourceType === "repo_file" ? changeImpactLoaded.config : void 0,
50537
+ suggestedConfig: suggestChangeImpactConfig(workspace).suggestedConfig,
50538
+ validate: (config) => validateChangeImpactConfig(config, MESH_INIT_CHANGE_IMPACT_CONFIG_PATH).valid,
50539
+ write,
50540
+ overwrite
50541
+ });
50248
50542
  const providers = suggestNodeProviderPriority(detected);
50543
+ const refineLoaded = loadMeshRefineConfig(mesh, workspace);
50544
+ const bootstrapLoaded = loadMeshWorktreeBootstrapConfig(mesh, workspace);
50545
+ let magiKindPanels = {};
50546
+ try {
50547
+ magiKindPanels = listMagiKindPanels();
50548
+ } catch {
50549
+ magiKindPanels = {};
50550
+ }
50551
+ const currentConfig2 = {
50552
+ refine: refineLoaded.config,
50553
+ worktreeBootstrap: bootstrapLoaded.config,
50554
+ changeImpact: changeImpactLoaded.sourceType === "repo_file" ? changeImpactLoaded.config : void 0,
50555
+ sourceTypes: {
50556
+ refine: refineLoaded.sourceType,
50557
+ worktreeBootstrap: bootstrapLoaded.sourceType,
50558
+ changeImpact: changeImpactLoaded.sourceType
50559
+ },
50560
+ magiKindPanels
50561
+ };
50249
50562
  return {
50250
50563
  success: true,
50251
50564
  workspace,
50252
50565
  dryRun: !write,
50253
50566
  refine,
50254
50567
  worktreeBootstrap,
50568
+ changeImpact,
50255
50569
  providers,
50256
- note: write ? "Configs written to disk are the execution source of truth; suggestions are scaffold and only take effect once saved. providerPriority is a recommendation \u2014 apply it to node policy via clone/policy update." : "Dry-run: no files written. Re-run with write=true to persist the suggested configs. Heuristic suggestions never execute until saved as repo config."
50570
+ currentConfig: currentConfig2,
50571
+ note: write ? "Configs written to disk are the execution source of truth; suggestions are scaffold and only take effect once saved. providerPriority is a recommendation \u2014 apply it to node policy via clone/policy update. currentConfig echoes what was on disk before this run." : "Dry-run: no files written. Re-run with write=true to persist the suggested configs. Heuristic suggestions never execute until saved as repo config. Use currentConfig to diff current-vs-suggested before overwriting."
50257
50572
  };
50258
50573
  }
50259
50574
  function applyConfigSuggestion(input) {
@@ -65274,6 +65589,7 @@ export {
65274
65589
  getLedgerDir,
65275
65590
  getLedgerSummary,
65276
65591
  getLogLevel,
65592
+ getMagiKindPanel,
65277
65593
  getMagiPanel,
65278
65594
  getMesh,
65279
65595
  getMeshByRepo,
@@ -65330,6 +65646,7 @@ export {
65330
65646
  launchWithCdp,
65331
65647
  listCoordinatorsForWorkspace,
65332
65648
  listHostedCliRuntimes,
65649
+ listMagiKindPanels,
65333
65650
  listMagiPanels,
65334
65651
  listMeshMissionSummaries,
65335
65652
  listMeshes,
@@ -65365,6 +65682,7 @@ export {
65365
65682
  normalizeInteractivePrompt,
65366
65683
  normalizeInteractivePromptResponse,
65367
65684
  normalizeMagiPanel,
65685
+ normalizeMagiSlots,
65368
65686
  normalizeManagedStatus,
65369
65687
  normalizeMeshCapabilityTags,
65370
65688
  normalizeMeshDaemonRole,
@@ -65402,6 +65720,7 @@ export {
65402
65720
  recordMeshToolCall,
65403
65721
  registerExtensionProviders,
65404
65722
  registerMeshCoordinator,
65723
+ removeMagiKindPanel,
65405
65724
  removeMagiPanel,
65406
65725
  removeNode,
65407
65726
  removeWorktree,
@@ -65436,6 +65755,7 @@ export {
65436
65755
  saveState,
65437
65756
  setDebugRuntimeConfig,
65438
65757
  setLogLevel,
65758
+ setMagiKindPanel,
65439
65759
  setupIdeInstance,
65440
65760
  shouldAutoRestoreHostedSessionsOnStartup,
65441
65761
  shouldCollectTraceCategory,