akm-cli 0.9.5 → 0.9.6

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 (105) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/dist/commands/env/env-cli.js +1 -2
  3. package/dist/commands/env/secret-cli.js +1 -5
  4. package/dist/commands/feedback-cli.js +0 -4
  5. package/dist/commands/health/checks.js +0 -32
  6. package/dist/commands/health/surfaces.js +2 -2
  7. package/dist/commands/health.js +6 -15
  8. package/dist/commands/improve/autonomy-gate.js +1 -1
  9. package/dist/commands/improve/consolidate.js +25 -26
  10. package/dist/commands/improve/distill.js +2 -2
  11. package/dist/commands/improve/extract.js +8 -1
  12. package/dist/commands/improve/improve.js +3 -5
  13. package/dist/commands/improve/locks.js +13 -5
  14. package/dist/commands/improve/loop-stages.js +8 -9
  15. package/dist/commands/improve/memory/memory-contradiction-detect.js +1 -19
  16. package/dist/commands/improve/outcome-loop.js +0 -6
  17. package/dist/commands/improve/reflect.js +20 -9
  18. package/dist/commands/lint/index.js +4 -6
  19. package/dist/commands/proposal/validators/proposal-quality-validators.js +0 -7
  20. package/dist/commands/sources/bundle-config-ops.js +9 -4
  21. package/dist/commands/sources/info.js +14 -10
  22. package/dist/commands/tasks/tasks.js +12 -16
  23. package/dist/commands/workflow-cli.js +2 -2
  24. package/dist/core/common.js +61 -24
  25. package/dist/core/config/config-io.js +2 -2
  26. package/dist/core/config/config-sources.js +32 -2
  27. package/dist/core/config/config.js +1 -1
  28. package/dist/core/config/schema/engines.js +9 -7
  29. package/dist/core/config/schema/primitives.js +0 -5
  30. package/dist/core/config/schema/search.js +2 -1
  31. package/dist/core/file-lock.js +2 -1
  32. package/dist/core/maintenance-barrier.js +2 -14
  33. package/dist/core/paths.js +0 -3
  34. package/dist/core/redaction.js +2 -2
  35. package/dist/core/spawn-env.js +8 -12
  36. package/dist/core/state/migrations.js +1 -12
  37. package/dist/core/state-db.js +9 -27
  38. package/dist/core/write-source.js +12 -19
  39. package/dist/execution/directory-identity.js +36 -10
  40. package/dist/indexer/graph/graph-boost.js +0 -4
  41. package/dist/indexer/index-writer-lock.js +43 -24
  42. package/dist/indexer/index-written-assets.js +5 -6
  43. package/dist/indexer/indexer.js +2 -39
  44. package/dist/indexer/materialize-embeddings.js +85 -41
  45. package/dist/indexer/search/db-search.js +15 -48
  46. package/dist/indexer/search/ranking-contributors.js +0 -25
  47. package/dist/indexer/search/ranking.js +3 -13
  48. package/dist/integrations/agent/builder-shared.js +0 -25
  49. package/dist/integrations/agent/model-map.js +2 -60
  50. package/dist/integrations/harnesses/aider/agent-builder.js +1 -3
  51. package/dist/integrations/harnesses/amazonq/agent-builder.js +2 -7
  52. package/dist/integrations/harnesses/claude/agent-builder.js +1 -4
  53. package/dist/integrations/harnesses/codex/agent-builder.js +1 -4
  54. package/dist/integrations/harnesses/copilot/agent-builder.js +2 -6
  55. package/dist/integrations/harnesses/gemini/agent-builder.js +2 -7
  56. package/dist/integrations/harnesses/opencode/agent-builder.js +1 -4
  57. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +2 -16
  58. package/dist/integrations/harnesses/openhands/agent-builder.js +1 -3
  59. package/dist/integrations/harnesses/pi/agent-builder.js +2 -4
  60. package/dist/integrations/session-logs/index.js +0 -9
  61. package/dist/llm/client.js +75 -42
  62. package/dist/llm/embedder.js +7 -3
  63. package/dist/llm/embedders/remote.js +141 -42
  64. package/dist/registry/network.js +5 -37
  65. package/dist/runtime.js +2 -10
  66. package/dist/scripts/akm-migrate-node.js +60 -126
  67. package/dist/scripts/akm-migrate.js +60 -126
  68. package/dist/setup/engine-config.js +2 -5
  69. package/dist/setup/registry-stash-loader.js +0 -8
  70. package/dist/setup/setup.js +9 -46
  71. package/dist/setup/steps/connection-shared.js +10 -13
  72. package/dist/sources/providers/git-install.js +1 -1
  73. package/dist/storage/engines/sqlite-migrations.js +20 -1
  74. package/dist/storage/repositories/index-entries-repository.js +0 -15
  75. package/dist/tasks/backends/launchd.js +15 -20
  76. package/dist/tasks/backends/schtasks.js +18 -8
  77. package/dist/tasks/run/run-native-task.js +8 -6
  78. package/dist/tasks/source/bounded-document.js +2 -4
  79. package/dist/tasks/source/task-source-v3-frozen.js +5 -7
  80. package/dist/tasks/source/task-source-v4.js +5 -10
  81. package/dist/tasks/source/task-to-v3.js +5 -10
  82. package/dist/tasks/source/task-to-v4.js +1 -4
  83. package/dist/tasks/source-v3.js +6 -6
  84. package/dist/workflows/exec/native-executor.js +21 -31
  85. package/dist/workflows/exec/run-workflow.js +5 -6
  86. package/dist/workflows/exec/scheduler.js +3 -19
  87. package/dist/workflows/exec/step-work.js +1 -4
  88. package/dist/workflows/exec/unit-dispatch.js +2 -2
  89. package/dist/workflows/exec/worktree.js +1 -13
  90. package/dist/workflows/freeze/targets/child-workflow.js +2 -10
  91. package/dist/workflows/ir/plan-hash.js +4 -6
  92. package/dist/workflows/ir/schema-v4.js +0 -12
  93. package/dist/workflows/ir/schema.js +20 -31
  94. package/dist/workflows/parser.js +11 -52
  95. package/dist/workflows/renderer.js +2 -3
  96. package/dist/workflows/resource-limits.js +11 -41
  97. package/dist/workflows/runtime/runs.js +3 -4
  98. package/dist/workflows/source-ir/schema.js +14 -30
  99. package/dist/workflows/validator.js +1 -7
  100. package/docs/reference/configuration.md +1 -1
  101. package/package.json +1 -1
  102. package/schemas/akm-config.json +4 -23
  103. package/schemas/akm-task.json +1 -2
  104. package/schemas/akm-workflow.json +1 -13
  105. package/dist/indexer/search/semantic-status.js +0 -142
@@ -2,6 +2,7 @@
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  import { isIP } from "node:net";
5
+ import { backoffDelay, computeRetryDelay, abortableDelay as sharedAbortableDelay, shouldRetry } from "../core/common.js";
5
6
  import { bareHostname, classifyNetworkAddress, classifyNetworkHostname, resolveHostnameAddresses, } from "../core/network-policy.js";
6
7
  import { assertRegistryPinnedTransportAvailable, requestRegistryAddressPinned, } from "./pinned-transport.js";
7
8
  export { requestRegistryAddressPinned } from "./pinned-transport.js";
@@ -77,7 +78,7 @@ async function requestRegistryHop(url, init, options) {
77
78
  }
78
79
  const response = await transport(url, address, init, remainingMs);
79
80
  if (attempt < maxRetries && shouldRetry(response.status)) {
80
- const delay = retryDelay(response, attempt);
81
+ const delay = computeRetryDelay(response, attempt, { maxDelayMs: MAX_REGISTRY_RETRY_DELAY_MS });
81
82
  await cancelRegistryResponse(response);
82
83
  await abortableDelay(delay, init?.signal);
83
84
  continue;
@@ -87,7 +88,7 @@ async function requestRegistryHop(url, init, options) {
87
88
  catch (error) {
88
89
  if (attempt >= maxRetries || init?.signal?.aborted)
89
90
  throw error;
90
- await abortableDelay(backoffDelay(attempt), init?.signal);
91
+ await abortableDelay(backoffDelay(attempt, undefined, MAX_REGISTRY_RETRY_DELAY_MS), init?.signal);
91
92
  }
92
93
  }
93
94
  throw new Error("Registry retry loop is unreachable");
@@ -217,45 +218,12 @@ function isRedirect(status) {
217
218
  export async function cancelRegistryResponse(response) {
218
219
  await response.body?.cancel().catch(() => undefined);
219
220
  }
220
- function shouldRetry(status) {
221
- return status === 429 || status >= 500;
222
- }
223
- function retryDelay(response, attempt) {
224
- const retryAfter = response.headers.get("retry-after");
225
- if (retryAfter) {
226
- const seconds = Number(retryAfter);
227
- if (Number.isFinite(seconds)) {
228
- return seconds >= 0 ? Math.min(MAX_REGISTRY_RETRY_DELAY_MS, seconds * 1_000) : backoffDelay(attempt);
229
- }
230
- const date = Date.parse(retryAfter);
231
- if (Number.isFinite(date))
232
- return Math.min(MAX_REGISTRY_RETRY_DELAY_MS, Math.max(0, date - Date.now()));
233
- }
234
- return backoffDelay(attempt);
235
- }
236
- function backoffDelay(attempt) {
237
- return Math.min(MAX_REGISTRY_RETRY_DELAY_MS, 500 * 2 ** attempt * (0.5 + Math.random() * 0.5));
238
- }
239
221
  /** Test-only visibility for the server-controlled Retry-After clamp. */
240
222
  export function _registryRetryDelayForTests(response, attempt) {
241
- return retryDelay(response, attempt);
223
+ return computeRetryDelay(response, attempt, { maxDelayMs: MAX_REGISTRY_RETRY_DELAY_MS });
242
224
  }
243
225
  function abortableDelay(ms, signal) {
244
- if (!signal)
245
- return new Promise((resolve) => setTimeout(resolve, ms));
246
- if (signal.aborted)
247
- return Promise.reject(signal.reason ?? new Error("Registry request aborted"));
248
- return new Promise((resolve, reject) => {
249
- const timer = setTimeout(() => {
250
- signal.removeEventListener("abort", onAbort);
251
- resolve();
252
- }, ms);
253
- const onAbort = () => {
254
- clearTimeout(timer);
255
- reject(signal.reason ?? new Error("Registry request aborted"));
256
- };
257
- signal.addEventListener("abort", onAbort, { once: true });
258
- });
226
+ return sharedAbortableDelay(ms, signal, "Registry request aborted");
259
227
  }
260
228
  /** Existing local HTTP fixtures remain available only inside the test process. */
261
229
  export function allowPrivateRegistryFixtureForTests(rawUrl) {
package/dist/runtime.js CHANGED
@@ -135,18 +135,10 @@ function stdioFor(mode) {
135
135
  return mode ?? "pipe";
136
136
  }
137
137
  // ── Stdin ───────────────────────────────────────────────────────────────────
138
- /**
139
- * Read all of stdin as a single `Buffer`, enforcing a byte limit. When more
140
- * than `limitBytes` is read, `onLimitExceeded()` is invoked and its return (an
141
- * Error) is thrown — callers supply their own message so behaviour is exact.
142
- */
143
- export async function readStdin(limitBytes, onLimitExceeded) {
138
+ /** Read all of stdin as a single `Buffer`. */
139
+ export async function readStdin() {
144
140
  const chunks = [];
145
- let total = 0;
146
141
  for await (const chunk of stdinIterator()) {
147
- total += chunk.byteLength;
148
- if (total > limitBytes)
149
- throw onLimitExceeded();
150
142
  chunks.push(chunk);
151
143
  }
152
144
  return Buffer.concat(chunks);
@@ -9380,8 +9380,6 @@ function getRegistryIndexCacheDir() {
9380
9380
  }
9381
9381
 
9382
9382
  // src/core/common.ts
9383
- var MAX_CONFIG_FILE_BYTES = 1024 * 1024;
9384
- var MAX_LOCAL_METADATA_BYTES = 1024 * 1024;
9385
9383
  var MAX_LOCK_METADATA_BYTES = 64 * 1024;
9386
9384
  function readTextFileDescriptorWithLimit(fd, maxBytes, label = "File", displayPath = "(open file)") {
9387
9385
  const stat = fs.fstatSync(fd);
@@ -9403,10 +9401,13 @@ function readTextFileDescriptorWithLimit(fd, maxBytes, label = "File", displayPa
9403
9401
  }
9404
9402
  return buffer.subarray(0, total).toString("utf8");
9405
9403
  }
9406
- function readTextFileWithLimit(filePath, maxBytes, label = "File") {
9404
+ function readTextFile(filePath, label = "File") {
9407
9405
  const fd = fs.openSync(filePath, "r");
9408
9406
  try {
9409
- return readTextFileDescriptorWithLimit(fd, maxBytes, label, filePath);
9407
+ const stat = fs.fstatSync(fd);
9408
+ if (!stat.isFile())
9409
+ throw new ConfigError(`${label} is not a regular file: ${filePath}.`, "INVALID_CONFIG_FILE");
9410
+ return fs.readFileSync(fd, "utf8");
9410
9411
  } finally {
9411
9412
  fs.closeSync(fd);
9412
9413
  }
@@ -9506,6 +9507,8 @@ function asNonEmptyString(value) {
9506
9507
  return trimmed.length > 0 ? trimmed : undefined;
9507
9508
  }
9508
9509
  function isProcessAlive(pid) {
9510
+ if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0)
9511
+ return false;
9509
9512
  try {
9510
9513
  process.kill(pid, 0);
9511
9514
  return true;
@@ -14957,25 +14960,13 @@ var EXECUTION_MAX_TIMEOUT_MS = 2 ** 31 - 1;
14957
14960
  // src/workflows/resource-limits.ts
14958
14961
  var WORKFLOW_MAX_PLAN_BYTES = 2 * 1024 * 1024;
14959
14962
  var WORKFLOW_MAX_SOURCE_BYTES = 1024 * 1024;
14960
- var WORKFLOW_MAX_STEPS = 256;
14961
- var WORKFLOW_MAX_PARAMS = 128;
14962
- var WORKFLOW_MAX_ROUTE_BRANCHES = 256;
14963
14963
  var WORKFLOW_MAX_INSTRUCTION_BYTES = 256 * 1024;
14964
14964
  var WORKFLOW_MAX_SCHEMA_BYTES = 256 * 1024;
14965
14965
  var WORKFLOW_MAX_EXTRA_PARAMS_BYTES = 64 * 1024;
14966
- var WORKFLOW_MAX_MAP_EXPANSION = 1e4;
14967
- var WORKFLOW_MAX_INPUTS = 64;
14968
- var WORKFLOW_MAX_OUTPUTS = 64;
14969
14966
  var WORKFLOW_MAX_CONCURRENCY = 64;
14970
- var WORKFLOW_MAX_GATE_LOOPS = 100;
14971
- var WORKFLOW_MAX_RETRIES = 100;
14972
14967
  var WORKFLOW_MAX_TIMEOUT_MS = EXECUTION_MAX_TIMEOUT_MS;
14973
14968
  var WORKFLOW_ENGINE_NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
14974
14969
  var WORKFLOW_MAX_ENGINE_NAME_LENGTH = 63;
14975
- var WORKFLOW_MAX_EXEC_ARGV = 64;
14976
- var WORKFLOW_MAX_EXEC_ARG_BYTES = 4096;
14977
- var WORKFLOW_MAX_EXEC_CWD_LENGTH = 1024;
14978
- var WORKFLOW_MAX_EXEC_PASS_ENV = 32;
14979
14970
  var WORKFLOW_ENV_VAR_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
14980
14971
  var WORKFLOW_MAX_EXEC_OUTPUT_BYTES = 8 * 1024 * 1024;
14981
14972
  var EXEC_CONTEXT_LIMITS_POSIX = {
@@ -14993,13 +14984,13 @@ function jsonBytes(value) {
14993
14984
  var WORKFLOW_MAX_EMBEDDED_CHILD_PLAN_BYTES = 1024 * 1024;
14994
14985
 
14995
14986
  // src/tasks/source/bounded-document.ts
14996
- var TASK_V3_MAX_SOURCE_BYTES = 1024 * 1024;
14997
14987
  var TASK_V3_MAX_JSON_DEPTH = 64;
14998
14988
  var TASK_V3_MAX_JSON_NODES = 1e4;
14999
14989
  var TASK_V3_MAX_COLLECTION_ITEMS = 1024;
15000
14990
  var TASK_V3_MAX_OBJECT_KEYS = 256;
15001
14991
  var TASK_V3_MAX_STRING_BYTES = 256 * 1024;
15002
14992
  var TASK_V3_MAX_SCHEDULES = 64;
14993
+ var TASK_V3_MAX_REDACT_NAMES = 32;
15003
14994
  function own2(value, key) {
15004
14995
  return Object.hasOwn(value, key);
15005
14996
  }
@@ -15288,9 +15279,6 @@ function readBoundedTaskSourceYaml(input, options) {
15288
15279
  if (typeof input.yaml !== "string") {
15289
15280
  throw new UsageError(`Invalid ${sourceLabel} at ${input.filePath}: source must be a string.`, "TASK_SOURCE_INVALID");
15290
15281
  }
15291
- if (utf8Bytes2(input.yaml) > TASK_V3_MAX_SOURCE_BYTES) {
15292
- throw new UsageError(`Invalid ${sourceLabel} at ${input.filePath}: source exceeds the 1 MiB (${TASK_V3_MAX_SOURCE_BYTES}-byte) resource limit.`, "TASK_SOURCE_INVALID");
15293
- }
15294
15282
  const lineCounter2 = new $LineCounter;
15295
15283
  let document;
15296
15284
  try {
@@ -16050,7 +16038,7 @@ function parseExecutionControls(input, ctx) {
16050
16038
  out.timeout = parseTimeoutTopLevel(input.timeout, ctx);
16051
16039
  if (own2(input, "redact")) {
16052
16040
  const names = parseStringArray(input.redact, ctx, ["redact"], {
16053
- max: WORKFLOW_MAX_EXEC_PASS_ENV,
16041
+ max: TASK_V3_MAX_REDACT_NAMES,
16054
16042
  pattern: WORKFLOW_ENV_VAR_NAME_PATTERN
16055
16043
  });
16056
16044
  if (new Set(names).size !== names.length)
@@ -16064,8 +16052,8 @@ function parseExecutionControls(input, ctx) {
16064
16052
  out.maxSteps = input.maxSteps;
16065
16053
  }
16066
16054
  if (own2(input, "maxRetries")) {
16067
- if (!Number.isSafeInteger(input.maxRetries) || input.maxRetries < 0 || input.maxRetries > WORKFLOW_MAX_RETRIES) {
16068
- sourceError(ctx, ["maxRetries"], `must be an integer from 0 through ${WORKFLOW_MAX_RETRIES}.`);
16055
+ if (!Number.isSafeInteger(input.maxRetries) || input.maxRetries < 0) {
16056
+ sourceError(ctx, ["maxRetries"], "must be a non-negative safe integer.");
16069
16057
  }
16070
16058
  out.maxRetries = input.maxRetries;
16071
16059
  }
@@ -16125,9 +16113,6 @@ function parseInputDeclaration(name, raw, ctx) {
16125
16113
  function parseInputDeclarations(value, ctx) {
16126
16114
  const input = asRecord(value, ctx, ["inputs"]);
16127
16115
  const names = Object.keys(input);
16128
- if (names.length > WORKFLOW_MAX_PARAMS) {
16129
- sourceError(ctx, ["inputs"], `accepts at most ${WORKFLOW_MAX_PARAMS} declared inputs.`);
16130
- }
16131
16116
  const result = {};
16132
16117
  for (const name of names) {
16133
16118
  if (!INPUT_NAME_PATTERN.test(name)) {
@@ -16560,7 +16545,7 @@ function parseAkm(value, ctx) {
16560
16545
  out.timeout = parseTimeout(input.timeout, ctx);
16561
16546
  if (own2(input, "redact")) {
16562
16547
  const names = parseStringArray(input.redact, ctx, ["akm", "redact"], {
16563
- max: WORKFLOW_MAX_EXEC_PASS_ENV,
16548
+ max: TASK_V3_MAX_REDACT_NAMES,
16564
16549
  pattern: WORKFLOW_ENV_VAR_NAME_PATTERN
16565
16550
  });
16566
16551
  if (new Set(names).size !== names.length)
@@ -16574,8 +16559,8 @@ function parseAkm(value, ctx) {
16574
16559
  out.maxSteps = input.maxSteps;
16575
16560
  }
16576
16561
  if (own2(input, "maxRetries")) {
16577
- if (!Number.isSafeInteger(input.maxRetries) || input.maxRetries < 0 || input.maxRetries > WORKFLOW_MAX_RETRIES) {
16578
- sourceError(ctx, ["akm", "maxRetries"], `must be an integer from 0 through ${WORKFLOW_MAX_RETRIES}.`);
16562
+ if (!Number.isSafeInteger(input.maxRetries) || input.maxRetries < 0) {
16563
+ sourceError(ctx, ["akm", "maxRetries"], "must be a non-negative safe integer.");
16579
16564
  }
16580
16565
  out.maxRetries = input.maxRetries;
16581
16566
  }
@@ -16892,9 +16877,6 @@ function optionalString(value, label) {
16892
16877
  return exactString(value, label);
16893
16878
  }
16894
16879
  function parseLegacyTaskYaml(input) {
16895
- if (input.bytes.byteLength > TASK_V3_MAX_SOURCE_BYTES) {
16896
- throw new Error(`task YAML exceeds the 1 MiB (${TASK_V3_MAX_SOURCE_BYTES}-byte) source resource limit`);
16897
- }
16898
16880
  let source;
16899
16881
  try {
16900
16882
  source = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(input.bytes);
@@ -16942,7 +16924,7 @@ function validateCommonV2(data) {
16942
16924
  }
16943
16925
  }
16944
16926
  if (data.redact !== undefined && data.redact !== null) {
16945
- if (!Array.isArray(data.redact) || data.redact.length > WORKFLOW_MAX_EXEC_PASS_ENV || data.redact.some((entry) => typeof entry !== "string" || !WORKFLOW_ENV_VAR_NAME_PATTERN.test(entry))) {
16927
+ if (!Array.isArray(data.redact) || data.redact.length > TASK_V3_MAX_REDACT_NAMES || data.redact.some((entry) => typeof entry !== "string" || !WORKFLOW_ENV_VAR_NAME_PATTERN.test(entry))) {
16946
16928
  throw new Error("redact must contain only bounded environment variable names");
16947
16929
  }
16948
16930
  }
@@ -17068,8 +17050,8 @@ function migratedObject(data) {
17068
17050
  akm.maxSteps = data.maxSteps;
17069
17051
  }
17070
17052
  if (data.maxRetries !== undefined && data.maxRetries !== null) {
17071
- if (!Number.isSafeInteger(data.maxRetries) || data.maxRetries < 0 || data.maxRetries > WORKFLOW_MAX_RETRIES) {
17072
- throw new Error(`maxRetries must be from 0 through ${WORKFLOW_MAX_RETRIES}`);
17053
+ if (!Number.isSafeInteger(data.maxRetries) || data.maxRetries < 0) {
17054
+ throw new Error("maxRetries must be a non-negative integer");
17073
17055
  }
17074
17056
  akm.maxRetries = data.maxRetries;
17075
17057
  }
@@ -17328,9 +17310,6 @@ function exactString2(value, label, nonempty = false) {
17328
17310
  return value;
17329
17311
  }
17330
17312
  function parseV3RawYaml(input) {
17331
- if (input.bytes.byteLength > TASK_V3_MAX_SOURCE_BYTES) {
17332
- throw new Error(`task YAML exceeds the 1 MiB (${TASK_V3_MAX_SOURCE_BYTES}-byte) source resource limit`);
17333
- }
17334
17313
  let source;
17335
17314
  try {
17336
17315
  source = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(input.bytes);
@@ -18531,7 +18510,7 @@ function validateStep(value, jobId, index, stepIds, options) {
18531
18510
  }
18532
18511
  validateMap(step.map, `step ${id} map`);
18533
18512
  validateRoute(step.route, `step ${id} route`);
18534
- optionalStringList(step.inputs, `step ${id} inputs`, WORKFLOW_MAX_INPUTS);
18513
+ optionalStringList(step.inputs, `step ${id} inputs`, Infinity);
18535
18514
  if (Array.isArray(step.inputs)) {
18536
18515
  for (const [inputIndex, input] of step.inputs.entries())
18537
18516
  validateReference(input, `step ${id} inputs[${inputIndex}]`);
@@ -18553,12 +18532,7 @@ function validateExec(value, location, options) {
18553
18532
  return;
18554
18533
  const exec = record(value, location);
18555
18534
  keys(exec, ["command", "cwd", "passEnv"], location);
18556
- stringList(exec.command, `${location}.command`, WORKFLOW_MAX_EXEC_ARGV, false);
18557
- for (const [index, argument] of exec.command.entries()) {
18558
- if (utf8Bytes(argument) > WORKFLOW_MAX_EXEC_ARG_BYTES) {
18559
- fail2(`${location}.command[${index}] exceeds ${WORKFLOW_MAX_EXEC_ARG_BYTES} bytes`);
18560
- }
18561
- }
18535
+ stringList(exec.command, `${location}.command`, Infinity, false);
18562
18536
  try {
18563
18537
  rejectNulInArgv(exec.command);
18564
18538
  } catch (cause) {
@@ -18566,9 +18540,6 @@ function validateExec(value, location, options) {
18566
18540
  }
18567
18541
  optionalString2(exec.cwd, `${location}.cwd`);
18568
18542
  if (exec.cwd !== undefined) {
18569
- if (exec.cwd.length > WORKFLOW_MAX_EXEC_CWD_LENGTH) {
18570
- fail2(`${location}.cwd exceeds ${WORKFLOW_MAX_EXEC_CWD_LENGTH} characters`);
18571
- }
18572
18543
  try {
18573
18544
  exec.cwd = canonicalizeWorkflowWorkingDirectory(exec.cwd, options.workspaceRoot);
18574
18545
  } catch (cause) {
@@ -18576,7 +18547,7 @@ function validateExec(value, location, options) {
18576
18547
  }
18577
18548
  }
18578
18549
  if (exec.passEnv !== undefined) {
18579
- stringList(exec.passEnv, `${location}.passEnv`, WORKFLOW_MAX_EXEC_PASS_ENV, false);
18550
+ stringList(exec.passEnv, `${location}.passEnv`, Infinity, false);
18580
18551
  }
18581
18552
  if (Array.isArray(exec.passEnv)) {
18582
18553
  for (const name of exec.passEnv) {
@@ -18627,8 +18598,8 @@ function validateRetry(value, location) {
18627
18598
  return;
18628
18599
  const retry = record(value, location);
18629
18600
  keys(retry, ["max", "on"], location);
18630
- if (!Number.isSafeInteger(retry.max) || retry.max < 0 || retry.max > WORKFLOW_MAX_RETRIES) {
18631
- fail2(`${location}.max must be an integer from 0 through ${WORKFLOW_MAX_RETRIES}`);
18601
+ if (!Number.isSafeInteger(retry.max) || retry.max < 0) {
18602
+ fail2(`${location}.max must be a non-negative integer`);
18632
18603
  }
18633
18604
  stringList(retry.on, `${location}.on`, PROGRAM_RETRY_REASONS.length, false);
18634
18605
  for (const reason of retry.on) {
@@ -18658,8 +18629,8 @@ function validateRoute(value, location) {
18658
18629
  keys(route, ["input", "branches", "defaultStepId"], location);
18659
18630
  nonEmptyString2(route.input, `${location}.input`);
18660
18631
  validateReference(route.input, `${location}.input`);
18661
- if (!Array.isArray(route.branches) || route.branches.length === 0 || route.branches.length > WORKFLOW_MAX_ROUTE_BRANCHES) {
18662
- fail2(`${location}.branches must contain 1 through ${WORKFLOW_MAX_ROUTE_BRANCHES} entries`);
18632
+ if (!Array.isArray(route.branches) || route.branches.length === 0) {
18633
+ fail2(`${location}.branches must be a non-empty array`);
18663
18634
  }
18664
18635
  const matches = new Set;
18665
18636
  for (const [index, value2] of route.branches.entries()) {
@@ -18679,8 +18650,8 @@ function validateGate(value, location) {
18679
18650
  return;
18680
18651
  const gate = record(value, location);
18681
18652
  keys(gate, ["maxLoops", "rubric"], location);
18682
- if (gate.maxLoops !== undefined && (!Number.isSafeInteger(gate.maxLoops) || gate.maxLoops < 1 || gate.maxLoops > WORKFLOW_MAX_GATE_LOOPS)) {
18683
- fail2(`${location}.maxLoops must be an integer from 1 through ${WORKFLOW_MAX_GATE_LOOPS}`);
18653
+ if (gate.maxLoops !== undefined && (!Number.isSafeInteger(gate.maxLoops) || gate.maxLoops < 1)) {
18654
+ fail2(`${location}.maxLoops must be an integer of at least 1`);
18684
18655
  }
18685
18656
  optionalString2(gate.rubric, `${location}.rubric`);
18686
18657
  }
@@ -18713,8 +18684,8 @@ function validateParams(value) {
18713
18684
  if (value === undefined)
18714
18685
  return;
18715
18686
  const params = record(value, "params");
18716
- if (Object.keys(params).length === 0 || Object.keys(params).length > WORKFLOW_MAX_PARAMS) {
18717
- fail2(`params must contain 1 through ${WORKFLOW_MAX_PARAMS} entries`);
18687
+ if (Object.keys(params).length === 0) {
18688
+ fail2(`params must contain at least one entry`);
18718
18689
  }
18719
18690
  for (const [name, schema] of Object.entries(params)) {
18720
18691
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
@@ -18726,8 +18697,8 @@ function validateOutputs(value) {
18726
18697
  if (value === undefined)
18727
18698
  return;
18728
18699
  const outputs = record(value, "outputs");
18729
- if (Object.keys(outputs).length === 0 || Object.keys(outputs).length > WORKFLOW_MAX_OUTPUTS) {
18730
- fail2(`outputs must contain 1 through ${WORKFLOW_MAX_OUTPUTS} entries`);
18700
+ if (Object.keys(outputs).length === 0) {
18701
+ fail2(`outputs must contain at least one entry`);
18731
18702
  }
18732
18703
  for (const [name, declaration] of Object.entries(outputs)) {
18733
18704
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
@@ -18750,9 +18721,6 @@ function validateBudget(value) {
18750
18721
  fail2(`budget.${key} must be a positive integer`);
18751
18722
  }
18752
18723
  }
18753
- if (typeof budget.maxUnits === "number" && budget.maxUnits > WORKFLOW_MAX_MAP_EXPANSION) {
18754
- fail2(`budget.maxUnits must be at most ${WORKFLOW_MAX_MAP_EXPANSION}`);
18755
- }
18756
18724
  }
18757
18725
  function validateLlm(value, location) {
18758
18726
  if (value === undefined)
@@ -19336,12 +19304,6 @@ function checkXrefs(value, line, errors2) {
19336
19304
  }
19337
19305
  }
19338
19306
  function checkResourceLimits(draft, errors2) {
19339
- if (draft.steps.length > WORKFLOW_MAX_STEPS) {
19340
- errors2.push({ line: 1, message: `Workflow must contain at most ${WORKFLOW_MAX_STEPS} steps.` });
19341
- }
19342
- if (Object.keys(draft.params ?? {}).length > WORKFLOW_MAX_PARAMS) {
19343
- errors2.push({ line: 1, message: `Workflow must contain at most ${WORKFLOW_MAX_PARAMS} parameters.` });
19344
- }
19345
19307
  for (const step of draft.steps) {
19346
19308
  if (step.instructions && utf8Bytes(step.instructions.text) > WORKFLOW_MAX_INSTRUCTION_BYTES) {
19347
19309
  errors2.push({
@@ -19703,9 +19665,6 @@ function parseParams(ctx, raw) {
19703
19665
  ctx.err(["params"], `"params" must be a mapping of param name to a JSON Schema object (e.g. changed_files: { type: array }).`);
19704
19666
  return;
19705
19667
  }
19706
- if (Object.keys(raw).length > WORKFLOW_MAX_PARAMS) {
19707
- ctx.err(["params"], `"params" must contain at most ${WORKFLOW_MAX_PARAMS} entries.`);
19708
- }
19709
19668
  const params = {};
19710
19669
  for (const [paramName, value] of Object.entries(raw)) {
19711
19670
  if (!INPUT_NAME_PATTERN.test(paramName)) {
@@ -19731,9 +19690,6 @@ function parseOutputs(ctx, raw) {
19731
19690
  ctx.err(["outputs"], `"outputs" must be a mapping of output name to { from, schema? } (e.g. report: { from: steps.summarize.output }).`);
19732
19691
  return;
19733
19692
  }
19734
- if (Object.keys(raw).length > WORKFLOW_MAX_OUTPUTS) {
19735
- ctx.err(["outputs"], `"outputs" must contain at most ${WORKFLOW_MAX_OUTPUTS} entries.`);
19736
- }
19737
19693
  const outputs = {};
19738
19694
  for (const [outputName, value] of Object.entries(raw)) {
19739
19695
  const path13 = ["outputs", outputName];
@@ -19825,10 +19781,10 @@ function parseBudget(ctx, raw) {
19825
19781
  }
19826
19782
  }
19827
19783
  if (raw.max_units !== undefined) {
19828
- if (typeof raw.max_units === "number" && Number.isInteger(raw.max_units) && raw.max_units >= 1 && raw.max_units <= WORKFLOW_MAX_MAP_EXPANSION) {
19784
+ if (typeof raw.max_units === "number" && Number.isInteger(raw.max_units) && raw.max_units >= 1) {
19829
19785
  budget.maxUnits = raw.max_units;
19830
19786
  } else {
19831
- ctx.err([...path13, "max_units"], `"budget.max_units" must be an integer from 1 through ${WORKFLOW_MAX_MAP_EXPANSION}.`);
19787
+ ctx.err([...path13, "max_units"], `"budget.max_units" must be an integer >= 1.`);
19832
19788
  }
19833
19789
  }
19834
19790
  return Object.keys(budget).length > 0 ? budget : undefined;
@@ -19838,9 +19794,6 @@ function parseSteps(ctx, raw) {
19838
19794
  ctx.err(["steps"], `"steps" is required and must be a list with at least one step.`);
19839
19795
  return [];
19840
19796
  }
19841
- if (raw.length > WORKFLOW_MAX_STEPS) {
19842
- ctx.err(["steps"], `"steps" must contain at most ${WORKFLOW_MAX_STEPS} entries.`);
19843
- }
19844
19797
  const idIndex = new Map;
19845
19798
  raw.forEach((rawStep, index) => {
19846
19799
  if (isPlainRecord(rawStep) && typeof rawStep.id === "string" && !idIndex.has(rawStep.id)) {
@@ -20011,10 +19964,6 @@ function parseExecPassEnv(ctx, raw, path13, stepLabel) {
20011
19964
  ctx.err(path13, `${stepLabel} "exec.pass_env" must be a non-empty list of environment variable NAMES to copy through from ` + `akm's own environment, e.g. pass_env: [CARGO_HOME]. Values never appear here — use "env:" bindings for those.`);
20012
19965
  return;
20013
19966
  }
20014
- if (raw.length > WORKFLOW_MAX_EXEC_PASS_ENV) {
20015
- ctx.err(path13, `${stepLabel} "exec.pass_env" must have at most ${WORKFLOW_MAX_EXEC_PASS_ENV} entries. A command needing ` + `more than that should use explicit named "env:" bindings instead.`);
20016
- return;
20017
- }
20018
19967
  const names = [];
20019
19968
  for (const [index, entry] of raw.entries()) {
20020
19969
  if (typeof entry !== "string" || !WORKFLOW_ENV_VAR_NAME_PATTERN.test(entry)) {
@@ -20034,10 +19983,6 @@ function parseExecCommand(ctx, raw, path13, stepLabel) {
20034
19983
  ctx.err(path13, `${stepLabel} "exec" requires "command": a non-empty argv list, e.g. command: ["bun", "run", "test:unit"]. ` + `A single shell string is not accepted — the command is spawned directly, never through a shell.`);
20035
19984
  return;
20036
19985
  }
20037
- if (raw.length > WORKFLOW_MAX_EXEC_ARGV) {
20038
- ctx.err(path13, `${stepLabel} "exec.command" must have at most ${WORKFLOW_MAX_EXEC_ARGV} entries.`);
20039
- return;
20040
- }
20041
19986
  const argv = [];
20042
19987
  for (const [index, entry] of raw.entries()) {
20043
19988
  if (typeof entry !== "string" || entry === "") {
@@ -20048,10 +19993,6 @@ function parseExecCommand(ctx, raw, path13, stepLabel) {
20048
19993
  ctx.err([...path13, index], `${stepLabel} "exec.command[${index}]" may not contain NUL bytes.`);
20049
19994
  return;
20050
19995
  }
20051
- if (utf8Bytes(entry) > WORKFLOW_MAX_EXEC_ARG_BYTES) {
20052
- ctx.err(path13, `${stepLabel} "exec.command[${index}]" exceeds ${WORKFLOW_MAX_EXEC_ARG_BYTES} bytes.`);
20053
- return;
20054
- }
20055
19996
  argv.push(entry);
20056
19997
  }
20057
19998
  return argv;
@@ -20064,10 +20005,6 @@ function parseExecCwd(ctx, raw, path13, stepLabel) {
20064
20005
  return;
20065
20006
  }
20066
20007
  const value = raw.trim();
20067
- if (value.length > WORKFLOW_MAX_EXEC_CWD_LENGTH) {
20068
- ctx.err(path13, `${stepLabel} "exec.cwd" exceeds ${WORKFLOW_MAX_EXEC_CWD_LENGTH} characters.`);
20069
- return;
20070
- }
20071
20008
  if (!isContainedRelativePath(value)) {
20072
20009
  ctx.err(path13, `${stepLabel} "exec.cwd" (${JSON.stringify(value)}) must be a RELATIVE path inside the unit's working ` + `directory — absolute paths, Windows drive letters, "~", and ".." segments are rejected.`);
20073
20010
  return;
@@ -20101,10 +20038,10 @@ function parseMap(ctx, raw, path13, stepLabel) {
20101
20038
  }
20102
20039
  let concurrency;
20103
20040
  if (raw.concurrency !== undefined) {
20104
- if (typeof raw.concurrency === "number" && Number.isInteger(raw.concurrency) && raw.concurrency > 0 && raw.concurrency <= WORKFLOW_MAX_CONCURRENCY) {
20105
- concurrency = raw.concurrency;
20041
+ if (typeof raw.concurrency === "number" && Number.isInteger(raw.concurrency) && raw.concurrency > 0) {
20042
+ concurrency = Math.min(raw.concurrency, WORKFLOW_MAX_CONCURRENCY);
20106
20043
  } else {
20107
- ctx.err([...path13, "concurrency"], `${stepLabel} "concurrency" must be an integer from 1 through ${WORKFLOW_MAX_CONCURRENCY}.`);
20044
+ ctx.err([...path13, "concurrency"], `${stepLabel} "concurrency" must be a positive integer.`);
20108
20045
  }
20109
20046
  }
20110
20047
  const reducer = parseEnumField(ctx, raw.reducer, [...path13, "reducer"], `${stepLabel} "reducer"`, PROGRAM_REDUCERS);
@@ -20136,9 +20073,6 @@ function parseRoute(ctx, raw, path13, stepLabel, stepIndex, routeChecks) {
20136
20073
  if (!Array.isArray(raw.when) || raw.when.length === 0) {
20137
20074
  ctx.err(whenPath, `${stepLabel} "route" requires "when": a non-empty list of { match, step } branches (e.g. when: [{ match: pass, step: ship }]).`);
20138
20075
  } else {
20139
- if (raw.when.length > WORKFLOW_MAX_ROUTE_BRANCHES) {
20140
- ctx.err(whenPath, `${stepLabel} "when" must contain at most ${WORKFLOW_MAX_ROUTE_BRANCHES} branches.`);
20141
- }
20142
20076
  const seenMatches = new Map;
20143
20077
  raw.when.forEach((branch, i) => {
20144
20078
  const branchPath = [...whenPath, i];
@@ -20194,9 +20128,6 @@ function parseInputs(ctx, raw, path13, stepLabel) {
20194
20128
  ctx.err(path13, `${stepLabel} "inputs" must be a non-empty list of reference strings.`);
20195
20129
  return;
20196
20130
  }
20197
- if (raw.length > WORKFLOW_MAX_INPUTS) {
20198
- ctx.err(path13, `${stepLabel} "inputs" must contain at most ${WORKFLOW_MAX_INPUTS} entries.`);
20199
- }
20200
20131
  const out = [];
20201
20132
  const seen = new Set;
20202
20133
  raw.forEach((entry, i) => {
@@ -20223,10 +20154,10 @@ function parseGate(ctx, raw, path13, stepLabel) {
20223
20154
  checkUnknownKeys(ctx, raw, path13, GATE_KEYS, `${stepLabel} "gate"`);
20224
20155
  const gate = {};
20225
20156
  if (raw.max_loops !== undefined) {
20226
- if (typeof raw.max_loops === "number" && Number.isInteger(raw.max_loops) && raw.max_loops >= 1 && raw.max_loops <= WORKFLOW_MAX_GATE_LOOPS) {
20157
+ if (typeof raw.max_loops === "number" && Number.isInteger(raw.max_loops) && raw.max_loops >= 1) {
20227
20158
  gate.maxLoops = raw.max_loops;
20228
20159
  } else {
20229
- ctx.err([...path13, "max_loops"], `${stepLabel} "gate.max_loops" must be an integer from 1 through ${WORKFLOW_MAX_GATE_LOOPS}.`);
20160
+ ctx.err([...path13, "max_loops"], `${stepLabel} "gate.max_loops" must be an integer of at least 1.`);
20230
20161
  }
20231
20162
  }
20232
20163
  return gate;
@@ -20252,8 +20183,8 @@ function parseRetry(ctx, raw, path13, stepLabel) {
20252
20183
  }
20253
20184
  checkUnknownKeys(ctx, raw, path13, RETRY_KEYS, `${stepLabel} "retry"`);
20254
20185
  let ok = true;
20255
- if (!(typeof raw.max === "number" && Number.isInteger(raw.max) && raw.max >= 0 && raw.max <= WORKFLOW_MAX_RETRIES)) {
20256
- ctx.err([...path13, "max"], `${stepLabel} "retry.max" is required and must be an integer from 0 through ${WORKFLOW_MAX_RETRIES}.`);
20186
+ if (!(typeof raw.max === "number" && Number.isInteger(raw.max) && raw.max >= 0)) {
20187
+ ctx.err([...path13, "max"], `${stepLabel} "retry.max" is required and must be a non-negative integer.`);
20257
20188
  ok = false;
20258
20189
  }
20259
20190
  const on = [];
@@ -23841,7 +23772,7 @@ function withLockOperationMutex(lockPath, run) {
23841
23772
  db.exec("BEGIN IMMEDIATE");
23842
23773
  began = db.inTransaction;
23843
23774
  if (!began)
23844
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 2 ** attempt);
23775
+ sleepSync(2 ** attempt);
23845
23776
  }
23846
23777
  if (!began)
23847
23778
  throw new Error(`Could not acquire lock operation mutex for ${lockPath}.`);
@@ -24004,7 +23935,7 @@ function extractHolderPid(content) {
24004
23935
  // src/core/config/config-io.ts
24005
23936
  function readConfigText(configPath) {
24006
23937
  try {
24007
- return readTextFileWithLimit(configPath, MAX_CONFIG_FILE_BYTES, "Config file");
23938
+ return readTextFile(configPath, "Config file");
24008
23939
  } catch (err) {
24009
23940
  if (err.code === "ENOENT")
24010
23941
  return;
@@ -28111,9 +28042,6 @@ var ExtraParamsSchema = exports_external.record(exports_external.unknown()).supe
28111
28042
  ctx.addIssue({ code: exports_external.ZodIssueCode.custom, path: issue.path, message: issue.message });
28112
28043
  }
28113
28044
  });
28114
- var LlmCapabilitiesSchema = exports_external.object({
28115
- structuredOutput: exports_external.boolean().optional()
28116
- }).passthrough();
28117
28045
  var LlmInvocationOverridesSchema = exports_external.object({
28118
28046
  temperature: exports_external.number().finite().optional(),
28119
28047
  maxTokens: positiveInt.optional(),
@@ -28169,15 +28097,13 @@ var LlmConnectionConfigSchema = exports_external.object({
28169
28097
  maxTokens: positiveInt.optional(),
28170
28098
  timeoutMs: timeoutMsField,
28171
28099
  concurrency: positiveInt.optional(),
28172
- capabilities: LlmCapabilitiesSchema.optional(),
28100
+ supportsJsonSchema: exports_external.boolean().optional(),
28173
28101
  extraParams: ExtraParamsSchema.optional(),
28174
28102
  contextLength: positiveInt.optional(),
28175
28103
  enableThinking: exports_external.boolean().optional(),
28176
28104
  reasoningEffort: nonEmptyString3.optional()
28177
28105
  }).passthrough();
28178
- var LlmProfileConfigSchema = LlmConnectionConfigSchema.extend({
28179
- supportsJsonSchema: exports_external.boolean().optional()
28180
- }).passthrough();
28106
+ var LlmProfileConfigSchema = LlmConnectionConfigSchema.passthrough();
28181
28107
  var AgentPlatformSchema = exports_external.enum(VALID_HARNESS_IDS);
28182
28108
  var LlmEngineSchema = exports_external.object({
28183
28109
  kind: exports_external.literal("llm"),
@@ -28189,7 +28115,6 @@ var LlmEngineSchema = exports_external.object({
28189
28115
  maxTokens: positiveInt.optional(),
28190
28116
  timeoutMs: timeoutMsField,
28191
28117
  concurrency: positiveInt.optional(),
28192
- supportsJsonSchema: exports_external.boolean().optional(),
28193
28118
  extraParams: ExtraParamsSchema.optional(),
28194
28119
  contextLength: positiveInt.optional(),
28195
28120
  enableThinking: exports_external.boolean().optional(),
@@ -28219,7 +28144,6 @@ var AgentEngineSchema = exports_external.object({
28219
28144
  "temperature",
28220
28145
  "maxTokens",
28221
28146
  "concurrency",
28222
- "supportsJsonSchema",
28223
28147
  "extraParams",
28224
28148
  "contextLength",
28225
28149
  "enableThinking",
@@ -28585,7 +28509,7 @@ var SearchGraphBoostSchema = exports_external.object({
28585
28509
  hopBoostPerEntity: nonNegativeNumber.optional(),
28586
28510
  hopBoostCap: nonNegativeNumber.optional(),
28587
28511
  maxHops: positiveInt.max(3).optional(),
28588
- confidenceMode: exports_external.enum(["off", "blend", "multiply"]).default("blend").optional(),
28512
+ confidenceMode: exports_external.enum(["blend"]).default("blend").optional(),
28589
28513
  confidenceWeight: exports_external.number().finite().min(0).max(1).default(0.2).optional()
28590
28514
  }).passthrough();
28591
28515
  var SearchConfigSchema = exports_external.object({
@@ -29127,8 +29051,7 @@ function withMaintenanceStartBarrier(run) {
29127
29051
 
29128
29052
  // src/indexer/index-writer-lock.ts
29129
29053
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
29130
- var INDEX_WRITER_LOCK_STALE_AFTER_MS = 12 * 60 * 60 * 1000;
29131
- var DEFAULT_INDEX_WRITER_MAX_WAIT_MS = 10 * 60 * 1000;
29054
+ var DEFAULT_ASSET_MUTATION_MAX_WAIT_MS = 10 * 60 * 1000;
29132
29055
  var leaseContext = new AsyncLocalStorage2;
29133
29056
 
29134
29057
  // src/integrations/lockfile.ts
@@ -29926,9 +29849,14 @@ function existingDirectory(target) {
29926
29849
  throw cause;
29927
29850
  }
29928
29851
  }
29852
+ function reconcileDuplicateRoot(existing, candidate, sharedPath) {
29853
+ if (existing.writable === candidate.writable && existing.layout === candidate.layout)
29854
+ return;
29855
+ throw new ConfigError(`Bundles "${existing.bundleId}" and "${candidate.bundleId}" both resolve to the same directory ` + `(${sharedPath}) but disagree on writable/adapter settings. Two bundle ids configured for one ` + "directory must have matching settings, or one of them removed — run `akm bundle list` to see both.", "INVALID_CONFIG_FILE");
29856
+ }
29929
29857
  function taskRoots(config, resolutionBase = process.cwd()) {
29930
29858
  const sources = new Map((bundlesToSourceEntries(config) ?? []).map((source) => [source.name, source]));
29931
- const roots = [];
29859
+ const rootsByPath = new Map;
29932
29860
  for (const [bundleId, bundle] of Object.entries(config.bundles ?? {})) {
29933
29861
  if (bundle.enabled === false)
29934
29862
  continue;
@@ -29956,15 +29884,21 @@ function taskRoots(config, resolutionBase = process.cwd()) {
29956
29884
  }
29957
29885
  if (adapter !== "akm" && adapter !== "akm-task")
29958
29886
  continue;
29959
- roots.push({
29887
+ const candidate = {
29960
29888
  bundleId,
29961
29889
  root: componentRoot,
29962
29890
  bundleRoot,
29963
29891
  writable: component?.writable ?? resolveWritable(source),
29964
29892
  layout: adapter === "akm-task" ? "akm-task" : "akm-stash"
29965
- });
29893
+ };
29894
+ const existing = rootsByPath.get(componentRoot);
29895
+ if (existing) {
29896
+ reconcileDuplicateRoot(existing, candidate, componentRoot);
29897
+ continue;
29898
+ }
29899
+ rootsByPath.set(componentRoot, candidate);
29966
29900
  }
29967
- return roots;
29901
+ return [...rootsByPath.values()];
29968
29902
  }
29969
29903
  function summarize(plan) {
29970
29904
  const files = plan.files.map((file) => ({