@gethmy/agent 1.46.0 → 1.47.1

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/README.md CHANGED
@@ -50,7 +50,7 @@ npx @gethmy/mcp connect
50
50
  "agent": {
51
51
  "poolSize": 3,
52
52
  "pickupColumns": ["To Do"],
53
- "claude": { "model": "claude-opus-5", "escalateModel": "claude-fable-5-1", "reviewModel": "sonnet", "deepReviewModel": "sonnet" },
53
+ "claude": { "model": "claude-opus-5-5", "escalateModel": "claude-fable-5-1", "reviewModel": "sonnet", "deepReviewModel": "sonnet" },
54
54
  "review": {
55
55
  "enabled": true,
56
56
  "poolSize": 2,
package/dist/cli.js CHANGED
@@ -2572,6 +2572,9 @@ function untrustedDataBlock(text, options) {
2572
2572
  `);
2573
2573
  }
2574
2574
 
2575
+ // ../harmony-shared/src/xPostLink.ts
2576
+ var init_xPostLink = () => {};
2577
+
2575
2578
  // ../harmony-shared/src/index.ts
2576
2579
  var init_src = __esm(() => {
2577
2580
  init_agentPauseAccounting();
@@ -2602,6 +2605,7 @@ var init_src = __esm(() => {
2602
2605
  init_runRedaction();
2603
2606
  init_stageHandoff();
2604
2607
  init_sweepPresence();
2608
+ init_xPostLink();
2605
2609
  });
2606
2610
 
2607
2611
  // src/config-merge.ts
@@ -3566,12 +3570,12 @@ var init_types = __esm(() => {
3566
3570
  postSummary: true
3567
3571
  },
3568
3572
  claude: {
3569
- model: "claude-opus-5",
3573
+ model: "claude-opus-5-5",
3570
3574
  escalateModel: "claude-fable-5-1",
3571
3575
  escalateAfterAttempts: 2,
3572
3576
  tiers: {
3573
3577
  simple: "claude-sonnet-5",
3574
- advanced: "claude-opus-5",
3578
+ advanced: "claude-opus-5-5",
3575
3579
  research: "claude-fable-5-1"
3576
3580
  },
3577
3581
  scoreBands: DEFAULT_SCORE_BANDS,
@@ -7296,7 +7300,7 @@ class ProgressTracker {
7296
7300
  return PHASES[this.phase].label;
7297
7301
  }
7298
7302
  describeToolAction(name, input) {
7299
- const fileTool = FILE_TOOL_VERBS[name];
7303
+ const fileTool = FILE_TOOL_VERBS.get(name);
7300
7304
  if (fileTool) {
7301
7305
  const [verb, key] = fileTool;
7302
7306
  const fp = this.extractString(input, key);
@@ -7434,13 +7438,13 @@ var init_progress_tracker = __esm(() => {
7434
7438
  finishing: 4
7435
7439
  };
7436
7440
  EDIT_TOOLS = new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
7437
- FILE_TOOL_VERBS = {
7438
- Read: ["Reading", "file_path"],
7439
- Edit: ["Editing", "file_path"],
7440
- MultiEdit: ["Editing", "file_path"],
7441
- Write: ["Writing", "file_path"],
7442
- NotebookEdit: ["Editing notebook", "notebook_path"]
7443
- };
7441
+ FILE_TOOL_VERBS = new Map([
7442
+ ["Read", ["Reading", "file_path"]],
7443
+ ["Edit", ["Editing", "file_path"]],
7444
+ ["MultiEdit", ["Editing", "file_path"]],
7445
+ ["Write", ["Writing", "file_path"]],
7446
+ ["NotebookEdit", ["Editing notebook", "notebook_path"]]
7447
+ ]);
7444
7448
  });
7445
7449
 
7446
7450
  // src/prompt.ts
@@ -11595,6 +11599,15 @@ function resolveEffectiveAgentConfig(cfg, ws) {
11595
11599
  const withWorkspace = ws ? mergeAgentConfig(DEFAULT_AGENT_CONFIG, workspaceConfigToPartial(ws)) : DEFAULT_AGENT_CONFIG;
11596
11600
  return mergeAgentConfig(mergeAgentConfig(withWorkspace, cfg.agentOverrides), cfg.repoAgentOverrides);
11597
11601
  }
11602
+ function withdrawnMapFrom(models) {
11603
+ const withdrawn = {};
11604
+ for (const m of models) {
11605
+ if (m.status === "withdrawn" && m.replaced_by) {
11606
+ withdrawn[m.id] = m.replaced_by;
11607
+ }
11608
+ }
11609
+ return withdrawn;
11610
+ }
11598
11611
  var init_workspace_model_config = __esm(() => {
11599
11612
  init_config_merge();
11600
11613
  init_types();
@@ -14085,16 +14098,7 @@ class Pool {
14085
14098
  const { config: wsConfig, platformDefaults } = await client.getWorkspaceModelConfig(workspaceId);
14086
14099
  return layerPlatformDefaults(platformDefaults, wsConfig);
14087
14100
  }) : undefined;
14088
- const modelCatalog = daemonConfig ? new WorkspaceModelCache(async () => {
14089
- const { models } = await client.getModelCatalog();
14090
- const withdrawn = {};
14091
- for (const m of models) {
14092
- if (m.status === "withdrawn" && m.replaced_by) {
14093
- withdrawn[m.id] = m.replaced_by;
14094
- }
14095
- }
14096
- return withdrawn;
14097
- }) : undefined;
14101
+ const modelCatalog = daemonConfig ? new WorkspaceModelCache(async () => withdrawnMapFrom((await client.getModelCatalog()).models)) : undefined;
14098
14102
  const reportedConfig = daemonConfig ? new ReportedConfigCache(client, effectiveModelReport(config)) : undefined;
14099
14103
  for (let i = 0;i < config.poolSize; i++) {
14100
14104
  this.implWorkers.push(new Worker(i, config, client, this.identity, () => {
@@ -16230,6 +16234,105 @@ var init_reconcile = __esm(() => {
16230
16234
  init_types();
16231
16235
  });
16232
16236
 
16237
+ // src/startup-model.ts
16238
+ function describeStartupModel(config, layers, withdrawn) {
16239
+ const workspace = layers?.workspace ?? null;
16240
+ const platform = layers?.platform ?? null;
16241
+ const database = layerPlatformDefaults(platform, workspace);
16242
+ const claude = resolveEffectiveAgentConfig(config, database).claude;
16243
+ const pinned = ownObject(config.agentOverrides, "claude");
16244
+ const pinnedTiers = ownObject(pinned, "tiers");
16245
+ const pinnedModel = ownString(pinned, "model");
16246
+ let source = "built-in default";
16247
+ if (pinnedModel)
16248
+ source = "config.json";
16249
+ else if (workspace?.implement_model)
16250
+ source = "workspace";
16251
+ else if (platform?.implement_model)
16252
+ source = "platform";
16253
+ const warnings = [];
16254
+ const pins = [
16255
+ ["claude.model", pinnedModel, database?.implement_model],
16256
+ [
16257
+ "claude.escalateModel",
16258
+ ownString(pinned, "escalateModel"),
16259
+ database?.escalate_model
16260
+ ],
16261
+ ...TIERS.map((tier) => [
16262
+ `claude.tiers.${tier}`,
16263
+ ownString(pinnedTiers, tier),
16264
+ database?.tiers?.[tier]
16265
+ ])
16266
+ ];
16267
+ for (const [key, local, db] of pins) {
16268
+ if (local && db && local !== db) {
16269
+ warnings.push(`config.json pins ${key} to "${local}", which overrides the database default "${db}" — remove it from config.json to follow the app's model settings`);
16270
+ }
16271
+ }
16272
+ const effective = [
16273
+ ["claude.model", claude.model],
16274
+ ["claude.escalateModel", claude.escalateModel],
16275
+ ...TIERS.map((tier) => [`claude.tiers.${tier}`, claude.tiers[tier]])
16276
+ ];
16277
+ for (const [key, id] of effective) {
16278
+ const replacement = withdrawn?.[id];
16279
+ if (replacement) {
16280
+ warnings.push(`${key} resolves to "${id}", which is withdrawn in the model catalog — runs use its replacement "${replacement}"; update the setting to match`);
16281
+ }
16282
+ }
16283
+ return {
16284
+ model: claude.model,
16285
+ escalateModel: claude.escalateModel,
16286
+ reviewModel: config.agent.claude.reviewModel,
16287
+ sizingModel: claude.sizingModel,
16288
+ tiers: {
16289
+ simple: claude.tiers.simple,
16290
+ advanced: claude.tiers.advanced,
16291
+ research: claude.tiers.research
16292
+ },
16293
+ source,
16294
+ resolved: layers !== null && withdrawn !== null,
16295
+ warnings
16296
+ };
16297
+ }
16298
+ async function resolveStartupModel(config, client) {
16299
+ const [layers, withdrawn] = await Promise.all([
16300
+ client.getWorkspaceModelConfig(config.workspaceId).then(({ config: workspace, platformDefaults }) => ({
16301
+ workspace,
16302
+ platform: platformDefaults ?? null
16303
+ })).catch(() => null),
16304
+ client.getModelCatalog().then(({ models }) => withdrawnMapFrom(models)).catch(() => null)
16305
+ ]);
16306
+ return describeStartupModel(config, layers, withdrawn);
16307
+ }
16308
+ function ownObject(source, key) {
16309
+ if (typeof source !== "object" || source === null)
16310
+ return {};
16311
+ const value = own(source, key);
16312
+ return typeof value === "object" && value !== null ? value : {};
16313
+ }
16314
+ function ownString(source, key) {
16315
+ const value = own(source, key);
16316
+ return typeof value === "string" && value !== "" ? value : undefined;
16317
+ }
16318
+ function startupModelLine(m) {
16319
+ const origin = m.resolved ? `from ${m.source}` : `from ${m.source}; database model settings unreachable, a pickup may resolve differently`;
16320
+ return `${implementModelsLine(m)} (${origin})`;
16321
+ }
16322
+ function implementModelsLine(m) {
16323
+ if (m.sizingModel) {
16324
+ const ladder = TIERS.map((tier) => `${tier} → ${m.tiers[tier]}`).join(" · ");
16325
+ return `sized per pickup by ${m.sizingModel}: ${ladder} (${m.model} only when sizing fails) · review ${m.reviewModel}`;
16326
+ }
16327
+ return m.model === m.escalateModel ? m.model : `${m.model} → ${m.escalateModel} on retry/high · review ${m.reviewModel}`;
16328
+ }
16329
+ var TIERS;
16330
+ var init_startup_model = __esm(() => {
16331
+ init_src();
16332
+ init_workspace_model_config();
16333
+ TIERS = ["simple", "advanced", "research"];
16334
+ });
16335
+
16233
16336
  // src/startup-banner.ts
16234
16337
  var exports_startup_banner = {};
16235
16338
  __export(exports_startup_banner, {
@@ -16245,6 +16348,7 @@ function prettyBanner(config, version) {
16245
16348
  let projectName;
16246
16349
  let gitProvider;
16247
16350
  let httpPort;
16351
+ let model;
16248
16352
  let failed = false;
16249
16353
  let rendered = false;
16250
16354
  return {
@@ -16257,6 +16361,9 @@ function prettyBanner(config, version) {
16257
16361
  setHttpPort(port) {
16258
16362
  httpPort = port;
16259
16363
  },
16364
+ setModel(resolved) {
16365
+ model = resolved;
16366
+ },
16260
16367
  check(message) {
16261
16368
  checks.push({ kind: "ok", message });
16262
16369
  },
@@ -16278,6 +16385,7 @@ function prettyBanner(config, version) {
16278
16385
  projectName,
16279
16386
  gitProvider,
16280
16387
  httpPort,
16388
+ model,
16281
16389
  checks,
16282
16390
  readyMessage: message
16283
16391
  });
@@ -16287,12 +16395,13 @@ function prettyBanner(config, version) {
16287
16395
  }
16288
16396
  function jsonBanner(config, version) {
16289
16397
  log45.info(TAG41, `Harmony Agent Daemon v${version} starting...`);
16290
- log45.info(TAG41, `Project: ${config.projectId} | Pool: ${config.agent.poolSize} | Model: ${config.agent.claude.model} | Runner: ${config.agent.runner} | Pickup: ${config.agent.pickupColumns.join(", ")}`);
16398
+ log45.info(TAG41, `Project: ${config.projectId} | Pool: ${config.agent.poolSize} | Runner: ${config.agent.runner} | Pickup: ${config.agent.pickupColumns.join(", ")}`);
16291
16399
  log45.info(TAG41, `Config: ${contextSourceLine(config)}`);
16292
16400
  if (config.agent.review.enabled) {
16293
16401
  log45.info(TAG41, `Review: enabled | Columns: ${config.agent.review.pickupColumns.join(", ")} | → ${config.agent.review.moveToColumn} / ${config.agent.review.failColumn}`);
16294
16402
  }
16295
16403
  let failed = false;
16404
+ let modelLogged = false;
16296
16405
  return {
16297
16406
  setProjectName(_name) {},
16298
16407
  setGitProvider(provider) {
@@ -16301,6 +16410,10 @@ function jsonBanner(config, version) {
16301
16410
  setHttpPort(port) {
16302
16411
  log45.info(TAG41, `HTTP server on port ${port}`);
16303
16412
  },
16413
+ setModel(resolved) {
16414
+ modelLogged = true;
16415
+ log45.info(TAG41, `Model: ${startupModelLine(resolved)}`);
16416
+ },
16304
16417
  check(message) {
16305
16418
  log45.info(TAG41, message);
16306
16419
  },
@@ -16313,6 +16426,8 @@ function jsonBanner(config, version) {
16313
16426
  async ready(message) {
16314
16427
  if (failed)
16315
16428
  return;
16429
+ if (!modelLogged)
16430
+ log45.info(TAG41, `Model: ${localModelLine(config)}`);
16316
16431
  log45.info(TAG41, message);
16317
16432
  }
16318
16433
  };
@@ -16324,6 +16439,7 @@ function renderPretty(input) {
16324
16439
  projectName,
16325
16440
  gitProvider,
16326
16441
  httpPort,
16442
+ model,
16327
16443
  checks,
16328
16444
  readyMessage
16329
16445
  } = input;
@@ -16331,7 +16447,7 @@ function renderPretty(input) {
16331
16447
  lines.push("");
16332
16448
  lines.push(titleRule(`Harmony Agent Daemon v${version}`));
16333
16449
  lines.push("");
16334
- for (const row of configRows(config, projectName, gitProvider, httpPort)) {
16450
+ for (const row of configRows(config, projectName, gitProvider, httpPort, model)) {
16335
16451
  lines.push(` ${dim(row.label.padEnd(9))} ${row.value}`);
16336
16452
  }
16337
16453
  lines.push("");
@@ -16345,7 +16461,7 @@ function renderPretty(input) {
16345
16461
  return lines.join(`
16346
16462
  `);
16347
16463
  }
16348
- function configRows(config, projectName, gitProvider, httpPort) {
16464
+ function configRows(config, projectName, gitProvider, httpPort, model) {
16349
16465
  const rows = [];
16350
16466
  const projectLabel = projectName ? `${projectName} (${shortenId(config.projectId)})` : shortenId(config.projectId);
16351
16467
  rows.push({ label: "Project", value: projectLabel });
@@ -16354,8 +16470,7 @@ function configRows(config, projectName, gitProvider, httpPort) {
16354
16470
  const reviewEnabled = config.agent.review.enabled;
16355
16471
  const poolDesc = reviewEnabled ? `Pool ${config.agent.poolSize} impl + ${config.agent.review.poolSize} review` : `Pool ${config.agent.poolSize} impl`;
16356
16472
  const flowDesc = reviewEnabled ? `Pickup ${config.agent.pickupColumns[0]} → ${config.agent.completion.moveToColumn} → ${config.agent.review.moveToColumn}` : `Pickup ${config.agent.pickupColumns.join(", ")}`;
16357
- const { model, escalateModel, reviewModel } = config.agent.claude;
16358
- const modelDesc = model === escalateModel ? model : `${model} → ${escalateModel} on retry/high · review ${reviewModel}`;
16473
+ const modelDesc = model ? startupModelLine(model) : localModelLine(config);
16359
16474
  rows.push({
16360
16475
  label: "Model",
16361
16476
  value: `${modelDesc} · ${poolDesc} · ${flowDesc}`
@@ -16389,6 +16504,9 @@ function titleRule(title) {
16389
16504
  const suffix = "─".repeat(Math.max(3, RULE_WIDTH - prefix.length - title.length - surround.length));
16390
16505
  return dim(`${prefix}${title}${surround}${suffix}`);
16391
16506
  }
16507
+ function localModelLine(config) {
16508
+ return `${implementModelsLine(config.agent.claude)} (local config only; database model settings not read)`;
16509
+ }
16392
16510
  function runnerDesc(runner) {
16393
16511
  return runner === "sdk" ? "sdk (Agent SDK)" : "cli (Claude CLI)";
16394
16512
  }
@@ -16409,6 +16527,7 @@ function yellow(s) {
16409
16527
  var TAG41 = "daemon", RULE_WIDTH = 70, ANSI;
16410
16528
  var init_startup_banner = __esm(() => {
16411
16529
  init_port_registry();
16530
+ init_startup_model();
16412
16531
  ANSI = {
16413
16532
  reset: "\x1B[0m",
16414
16533
  dim: "\x1B[2m",
@@ -17766,6 +17885,11 @@ async function main() {
17766
17885
  } catch (err) {
17767
17886
  log50.warn(TAG46, `Could not report model config: ${err instanceof Error ? err.message : String(err)}`);
17768
17887
  }
17888
+ const startupModel = await resolveStartupModel(config, client);
17889
+ banner.setModel(startupModel);
17890
+ for (const warning of startupModel.warnings) {
17891
+ banner.warn(warning);
17892
+ }
17769
17893
  migrateConfigDir();
17770
17894
  try {
17771
17895
  migrateLegacyStateFile({
@@ -18088,6 +18212,7 @@ var init_src2 = __esm(() => {
18088
18212
  init_recovery();
18089
18213
  init_review_worktree();
18090
18214
  init_startup_banner();
18215
+ init_startup_model();
18091
18216
  init_state_migration();
18092
18217
  init_state_store();
18093
18218
  init_stream_parser_selftest();
package/dist/index.js CHANGED
@@ -2571,6 +2571,9 @@ function untrustedDataBlock(text, options) {
2571
2571
  `);
2572
2572
  }
2573
2573
 
2574
+ // ../harmony-shared/src/xPostLink.ts
2575
+ var init_xPostLink = () => {};
2576
+
2574
2577
  // ../harmony-shared/src/index.ts
2575
2578
  var init_src = __esm(() => {
2576
2579
  init_agentPauseAccounting();
@@ -2601,6 +2604,7 @@ var init_src = __esm(() => {
2601
2604
  init_runRedaction();
2602
2605
  init_stageHandoff();
2603
2606
  init_sweepPresence();
2607
+ init_xPostLink();
2604
2608
  });
2605
2609
 
2606
2610
  // src/config-merge.ts
@@ -3565,12 +3569,12 @@ var init_types = __esm(() => {
3565
3569
  postSummary: true
3566
3570
  },
3567
3571
  claude: {
3568
- model: "claude-opus-5",
3572
+ model: "claude-opus-5-5",
3569
3573
  escalateModel: "claude-fable-5-1",
3570
3574
  escalateAfterAttempts: 2,
3571
3575
  tiers: {
3572
3576
  simple: "claude-sonnet-5",
3573
- advanced: "claude-opus-5",
3577
+ advanced: "claude-opus-5-5",
3574
3578
  research: "claude-fable-5-1"
3575
3579
  },
3576
3580
  scoreBands: DEFAULT_SCORE_BANDS,
@@ -7295,7 +7299,7 @@ class ProgressTracker {
7295
7299
  return PHASES[this.phase].label;
7296
7300
  }
7297
7301
  describeToolAction(name, input) {
7298
- const fileTool = FILE_TOOL_VERBS[name];
7302
+ const fileTool = FILE_TOOL_VERBS.get(name);
7299
7303
  if (fileTool) {
7300
7304
  const [verb, key] = fileTool;
7301
7305
  const fp = this.extractString(input, key);
@@ -7433,13 +7437,13 @@ var init_progress_tracker = __esm(() => {
7433
7437
  finishing: 4
7434
7438
  };
7435
7439
  EDIT_TOOLS = new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
7436
- FILE_TOOL_VERBS = {
7437
- Read: ["Reading", "file_path"],
7438
- Edit: ["Editing", "file_path"],
7439
- MultiEdit: ["Editing", "file_path"],
7440
- Write: ["Writing", "file_path"],
7441
- NotebookEdit: ["Editing notebook", "notebook_path"]
7442
- };
7440
+ FILE_TOOL_VERBS = new Map([
7441
+ ["Read", ["Reading", "file_path"]],
7442
+ ["Edit", ["Editing", "file_path"]],
7443
+ ["MultiEdit", ["Editing", "file_path"]],
7444
+ ["Write", ["Writing", "file_path"]],
7445
+ ["NotebookEdit", ["Editing notebook", "notebook_path"]]
7446
+ ]);
7443
7447
  });
7444
7448
 
7445
7449
  // src/prompt.ts
@@ -11594,6 +11598,15 @@ function resolveEffectiveAgentConfig(cfg, ws) {
11594
11598
  const withWorkspace = ws ? mergeAgentConfig(DEFAULT_AGENT_CONFIG, workspaceConfigToPartial(ws)) : DEFAULT_AGENT_CONFIG;
11595
11599
  return mergeAgentConfig(mergeAgentConfig(withWorkspace, cfg.agentOverrides), cfg.repoAgentOverrides);
11596
11600
  }
11601
+ function withdrawnMapFrom(models) {
11602
+ const withdrawn = {};
11603
+ for (const m of models) {
11604
+ if (m.status === "withdrawn" && m.replaced_by) {
11605
+ withdrawn[m.id] = m.replaced_by;
11606
+ }
11607
+ }
11608
+ return withdrawn;
11609
+ }
11597
11610
  var init_workspace_model_config = __esm(() => {
11598
11611
  init_config_merge();
11599
11612
  init_types();
@@ -14084,16 +14097,7 @@ class Pool {
14084
14097
  const { config: wsConfig, platformDefaults } = await client.getWorkspaceModelConfig(workspaceId);
14085
14098
  return layerPlatformDefaults(platformDefaults, wsConfig);
14086
14099
  }) : undefined;
14087
- const modelCatalog = daemonConfig ? new WorkspaceModelCache(async () => {
14088
- const { models } = await client.getModelCatalog();
14089
- const withdrawn = {};
14090
- for (const m of models) {
14091
- if (m.status === "withdrawn" && m.replaced_by) {
14092
- withdrawn[m.id] = m.replaced_by;
14093
- }
14094
- }
14095
- return withdrawn;
14096
- }) : undefined;
14100
+ const modelCatalog = daemonConfig ? new WorkspaceModelCache(async () => withdrawnMapFrom((await client.getModelCatalog()).models)) : undefined;
14097
14101
  const reportedConfig = daemonConfig ? new ReportedConfigCache(client, effectiveModelReport(config)) : undefined;
14098
14102
  for (let i = 0;i < config.poolSize; i++) {
14099
14103
  this.implWorkers.push(new Worker(i, config, client, this.identity, () => {
@@ -16229,6 +16233,105 @@ var init_reconcile = __esm(() => {
16229
16233
  init_types();
16230
16234
  });
16231
16235
 
16236
+ // src/startup-model.ts
16237
+ function describeStartupModel(config, layers, withdrawn) {
16238
+ const workspace = layers?.workspace ?? null;
16239
+ const platform = layers?.platform ?? null;
16240
+ const database = layerPlatformDefaults(platform, workspace);
16241
+ const claude = resolveEffectiveAgentConfig(config, database).claude;
16242
+ const pinned = ownObject(config.agentOverrides, "claude");
16243
+ const pinnedTiers = ownObject(pinned, "tiers");
16244
+ const pinnedModel = ownString(pinned, "model");
16245
+ let source = "built-in default";
16246
+ if (pinnedModel)
16247
+ source = "config.json";
16248
+ else if (workspace?.implement_model)
16249
+ source = "workspace";
16250
+ else if (platform?.implement_model)
16251
+ source = "platform";
16252
+ const warnings = [];
16253
+ const pins = [
16254
+ ["claude.model", pinnedModel, database?.implement_model],
16255
+ [
16256
+ "claude.escalateModel",
16257
+ ownString(pinned, "escalateModel"),
16258
+ database?.escalate_model
16259
+ ],
16260
+ ...TIERS.map((tier) => [
16261
+ `claude.tiers.${tier}`,
16262
+ ownString(pinnedTiers, tier),
16263
+ database?.tiers?.[tier]
16264
+ ])
16265
+ ];
16266
+ for (const [key, local, db] of pins) {
16267
+ if (local && db && local !== db) {
16268
+ warnings.push(`config.json pins ${key} to "${local}", which overrides the database default "${db}" — remove it from config.json to follow the app's model settings`);
16269
+ }
16270
+ }
16271
+ const effective = [
16272
+ ["claude.model", claude.model],
16273
+ ["claude.escalateModel", claude.escalateModel],
16274
+ ...TIERS.map((tier) => [`claude.tiers.${tier}`, claude.tiers[tier]])
16275
+ ];
16276
+ for (const [key, id] of effective) {
16277
+ const replacement = withdrawn?.[id];
16278
+ if (replacement) {
16279
+ warnings.push(`${key} resolves to "${id}", which is withdrawn in the model catalog — runs use its replacement "${replacement}"; update the setting to match`);
16280
+ }
16281
+ }
16282
+ return {
16283
+ model: claude.model,
16284
+ escalateModel: claude.escalateModel,
16285
+ reviewModel: config.agent.claude.reviewModel,
16286
+ sizingModel: claude.sizingModel,
16287
+ tiers: {
16288
+ simple: claude.tiers.simple,
16289
+ advanced: claude.tiers.advanced,
16290
+ research: claude.tiers.research
16291
+ },
16292
+ source,
16293
+ resolved: layers !== null && withdrawn !== null,
16294
+ warnings
16295
+ };
16296
+ }
16297
+ async function resolveStartupModel(config, client) {
16298
+ const [layers, withdrawn] = await Promise.all([
16299
+ client.getWorkspaceModelConfig(config.workspaceId).then(({ config: workspace, platformDefaults }) => ({
16300
+ workspace,
16301
+ platform: platformDefaults ?? null
16302
+ })).catch(() => null),
16303
+ client.getModelCatalog().then(({ models }) => withdrawnMapFrom(models)).catch(() => null)
16304
+ ]);
16305
+ return describeStartupModel(config, layers, withdrawn);
16306
+ }
16307
+ function ownObject(source, key) {
16308
+ if (typeof source !== "object" || source === null)
16309
+ return {};
16310
+ const value = own(source, key);
16311
+ return typeof value === "object" && value !== null ? value : {};
16312
+ }
16313
+ function ownString(source, key) {
16314
+ const value = own(source, key);
16315
+ return typeof value === "string" && value !== "" ? value : undefined;
16316
+ }
16317
+ function startupModelLine(m) {
16318
+ const origin = m.resolved ? `from ${m.source}` : `from ${m.source}; database model settings unreachable, a pickup may resolve differently`;
16319
+ return `${implementModelsLine(m)} (${origin})`;
16320
+ }
16321
+ function implementModelsLine(m) {
16322
+ if (m.sizingModel) {
16323
+ const ladder = TIERS.map((tier) => `${tier} → ${m.tiers[tier]}`).join(" · ");
16324
+ return `sized per pickup by ${m.sizingModel}: ${ladder} (${m.model} only when sizing fails) · review ${m.reviewModel}`;
16325
+ }
16326
+ return m.model === m.escalateModel ? m.model : `${m.model} → ${m.escalateModel} on retry/high · review ${m.reviewModel}`;
16327
+ }
16328
+ var TIERS;
16329
+ var init_startup_model = __esm(() => {
16330
+ init_src();
16331
+ init_workspace_model_config();
16332
+ TIERS = ["simple", "advanced", "research"];
16333
+ });
16334
+
16232
16335
  // src/startup-banner.ts
16233
16336
  var exports_startup_banner = {};
16234
16337
  __export(exports_startup_banner, {
@@ -16244,6 +16347,7 @@ function prettyBanner(config, version) {
16244
16347
  let projectName;
16245
16348
  let gitProvider;
16246
16349
  let httpPort;
16350
+ let model;
16247
16351
  let failed = false;
16248
16352
  let rendered = false;
16249
16353
  return {
@@ -16256,6 +16360,9 @@ function prettyBanner(config, version) {
16256
16360
  setHttpPort(port) {
16257
16361
  httpPort = port;
16258
16362
  },
16363
+ setModel(resolved) {
16364
+ model = resolved;
16365
+ },
16259
16366
  check(message) {
16260
16367
  checks.push({ kind: "ok", message });
16261
16368
  },
@@ -16277,6 +16384,7 @@ function prettyBanner(config, version) {
16277
16384
  projectName,
16278
16385
  gitProvider,
16279
16386
  httpPort,
16387
+ model,
16280
16388
  checks,
16281
16389
  readyMessage: message
16282
16390
  });
@@ -16286,12 +16394,13 @@ function prettyBanner(config, version) {
16286
16394
  }
16287
16395
  function jsonBanner(config, version) {
16288
16396
  log45.info(TAG41, `Harmony Agent Daemon v${version} starting...`);
16289
- log45.info(TAG41, `Project: ${config.projectId} | Pool: ${config.agent.poolSize} | Model: ${config.agent.claude.model} | Runner: ${config.agent.runner} | Pickup: ${config.agent.pickupColumns.join(", ")}`);
16397
+ log45.info(TAG41, `Project: ${config.projectId} | Pool: ${config.agent.poolSize} | Runner: ${config.agent.runner} | Pickup: ${config.agent.pickupColumns.join(", ")}`);
16290
16398
  log45.info(TAG41, `Config: ${contextSourceLine(config)}`);
16291
16399
  if (config.agent.review.enabled) {
16292
16400
  log45.info(TAG41, `Review: enabled | Columns: ${config.agent.review.pickupColumns.join(", ")} | → ${config.agent.review.moveToColumn} / ${config.agent.review.failColumn}`);
16293
16401
  }
16294
16402
  let failed = false;
16403
+ let modelLogged = false;
16295
16404
  return {
16296
16405
  setProjectName(_name) {},
16297
16406
  setGitProvider(provider) {
@@ -16300,6 +16409,10 @@ function jsonBanner(config, version) {
16300
16409
  setHttpPort(port) {
16301
16410
  log45.info(TAG41, `HTTP server on port ${port}`);
16302
16411
  },
16412
+ setModel(resolved) {
16413
+ modelLogged = true;
16414
+ log45.info(TAG41, `Model: ${startupModelLine(resolved)}`);
16415
+ },
16303
16416
  check(message) {
16304
16417
  log45.info(TAG41, message);
16305
16418
  },
@@ -16312,6 +16425,8 @@ function jsonBanner(config, version) {
16312
16425
  async ready(message) {
16313
16426
  if (failed)
16314
16427
  return;
16428
+ if (!modelLogged)
16429
+ log45.info(TAG41, `Model: ${localModelLine(config)}`);
16315
16430
  log45.info(TAG41, message);
16316
16431
  }
16317
16432
  };
@@ -16323,6 +16438,7 @@ function renderPretty(input) {
16323
16438
  projectName,
16324
16439
  gitProvider,
16325
16440
  httpPort,
16441
+ model,
16326
16442
  checks,
16327
16443
  readyMessage
16328
16444
  } = input;
@@ -16330,7 +16446,7 @@ function renderPretty(input) {
16330
16446
  lines.push("");
16331
16447
  lines.push(titleRule(`Harmony Agent Daemon v${version}`));
16332
16448
  lines.push("");
16333
- for (const row of configRows(config, projectName, gitProvider, httpPort)) {
16449
+ for (const row of configRows(config, projectName, gitProvider, httpPort, model)) {
16334
16450
  lines.push(` ${dim(row.label.padEnd(9))} ${row.value}`);
16335
16451
  }
16336
16452
  lines.push("");
@@ -16344,7 +16460,7 @@ function renderPretty(input) {
16344
16460
  return lines.join(`
16345
16461
  `);
16346
16462
  }
16347
- function configRows(config, projectName, gitProvider, httpPort) {
16463
+ function configRows(config, projectName, gitProvider, httpPort, model) {
16348
16464
  const rows = [];
16349
16465
  const projectLabel = projectName ? `${projectName} (${shortenId(config.projectId)})` : shortenId(config.projectId);
16350
16466
  rows.push({ label: "Project", value: projectLabel });
@@ -16353,8 +16469,7 @@ function configRows(config, projectName, gitProvider, httpPort) {
16353
16469
  const reviewEnabled = config.agent.review.enabled;
16354
16470
  const poolDesc = reviewEnabled ? `Pool ${config.agent.poolSize} impl + ${config.agent.review.poolSize} review` : `Pool ${config.agent.poolSize} impl`;
16355
16471
  const flowDesc = reviewEnabled ? `Pickup ${config.agent.pickupColumns[0]} → ${config.agent.completion.moveToColumn} → ${config.agent.review.moveToColumn}` : `Pickup ${config.agent.pickupColumns.join(", ")}`;
16356
- const { model, escalateModel, reviewModel } = config.agent.claude;
16357
- const modelDesc = model === escalateModel ? model : `${model} → ${escalateModel} on retry/high · review ${reviewModel}`;
16472
+ const modelDesc = model ? startupModelLine(model) : localModelLine(config);
16358
16473
  rows.push({
16359
16474
  label: "Model",
16360
16475
  value: `${modelDesc} · ${poolDesc} · ${flowDesc}`
@@ -16388,6 +16503,9 @@ function titleRule(title) {
16388
16503
  const suffix = "─".repeat(Math.max(3, RULE_WIDTH - prefix.length - title.length - surround.length));
16389
16504
  return dim(`${prefix}${title}${surround}${suffix}`);
16390
16505
  }
16506
+ function localModelLine(config) {
16507
+ return `${implementModelsLine(config.agent.claude)} (local config only; database model settings not read)`;
16508
+ }
16391
16509
  function runnerDesc(runner) {
16392
16510
  return runner === "sdk" ? "sdk (Agent SDK)" : "cli (Claude CLI)";
16393
16511
  }
@@ -16408,6 +16526,7 @@ function yellow(s) {
16408
16526
  var TAG41 = "daemon", RULE_WIDTH = 70, ANSI;
16409
16527
  var init_startup_banner = __esm(() => {
16410
16528
  init_port_registry();
16529
+ init_startup_model();
16411
16530
  ANSI = {
16412
16531
  reset: "\x1B[0m",
16413
16532
  dim: "\x1B[2m",
@@ -17765,6 +17884,11 @@ async function main() {
17765
17884
  } catch (err) {
17766
17885
  log50.warn(TAG46, `Could not report model config: ${err instanceof Error ? err.message : String(err)}`);
17767
17886
  }
17887
+ const startupModel = await resolveStartupModel(config, client);
17888
+ banner.setModel(startupModel);
17889
+ for (const warning of startupModel.warnings) {
17890
+ banner.warn(warning);
17891
+ }
17768
17892
  migrateConfigDir();
17769
17893
  try {
17770
17894
  migrateLegacyStateFile({
@@ -18087,6 +18211,7 @@ var init_src2 = __esm(() => {
18087
18211
  init_recovery();
18088
18212
  init_review_worktree();
18089
18213
  init_startup_banner();
18214
+ init_startup_model();
18090
18215
  init_state_migration();
18091
18216
  init_state_store();
18092
18217
  init_stream_parser_selftest();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gethmy/agent",
3
- "version": "1.46.0",
3
+ "version": "1.47.1",
4
4
  "description": "Push-based agent daemon for Harmony — picks up assigned cards, builds them in isolated git worktrees, and opens PRs. It runs where you put it.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -45,8 +45,8 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@anthropic-ai/claude-agent-sdk": "^0.3.178",
48
- "@gethmy/harness": "1.19.1",
49
- "@gethmy/mcp": "3.16.0",
48
+ "@gethmy/harness": "1.19.2",
49
+ "@gethmy/mcp": "3.17.0",
50
50
  "@supabase/supabase-js": "2.95.3"
51
51
  },
52
52
  "devDependencies": {