akm-cli 0.9.2-alpha.4 → 0.9.2

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 (143) hide show
  1. package/CHANGELOG.md +493 -0
  2. package/STABILITY.md +23 -5
  3. package/dist/assets/hints/cli-hints-full.md +12 -7
  4. package/dist/assets/tasks/core/extract.yml +3 -5
  5. package/dist/assets/tasks/core/improve.yml +3 -5
  6. package/dist/assets/tasks/core/index-refresh.yml +3 -5
  7. package/dist/assets/tasks/core/sync.yml +3 -5
  8. package/dist/assets/tasks/core/version-check.yml +3 -5
  9. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +3 -5
  10. package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -6
  11. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +3 -5
  12. package/dist/assets/tasks/improve/akm-improve-frequent.yml +3 -5
  13. package/dist/assets/tasks/improve/akm-improve-nightly.yml +3 -5
  14. package/dist/cli/unknown-flags.js +12 -1
  15. package/dist/cli.js +8 -1
  16. package/dist/commands/command/command-execution.js +23 -2
  17. package/dist/commands/health/improve-metrics.js +38 -0
  18. package/dist/commands/health/windows.js +8 -4
  19. package/dist/commands/health.js +8 -4
  20. package/dist/commands/lint/index.js +1 -1
  21. package/dist/commands/migrate-cli.js +130 -24
  22. package/dist/commands/proposal/validators/proposal-validators.js +7 -2
  23. package/dist/commands/tasks/explain.js +304 -0
  24. package/dist/commands/tasks/tasks-cli.js +185 -3
  25. package/dist/commands/tasks/tasks.js +265 -52
  26. package/dist/commands/workflow/plan.js +159 -0
  27. package/dist/commands/workflow-cli.js +94 -2
  28. package/dist/core/activation-policy.js +2 -12
  29. package/dist/core/adapter/adapters/akm-lint.js +7 -4
  30. package/dist/core/adapter/adapters/akm-metadata.js +26 -14
  31. package/dist/core/adapter/adapters/akm-task-adapter.js +13 -10
  32. package/dist/core/errors.js +45 -0
  33. package/dist/core/json-schema.js +15 -5
  34. package/dist/core/state/migrations.js +57 -0
  35. package/dist/core/state-db.js +16 -14
  36. package/dist/core/subprocess.js +47 -13
  37. package/dist/execution/guarded-source.js +44 -0
  38. package/dist/execution/input-contract.js +250 -0
  39. package/dist/execution/target-ref.js +63 -0
  40. package/dist/indexer/usage/usage-events.js +14 -3
  41. package/dist/integrations/agent/execution-lowering.js +12 -1
  42. package/dist/output/shapes/passthrough.js +2 -0
  43. package/dist/output/text/helpers.js +1 -1
  44. package/dist/output/text/migrate.js +12 -3
  45. package/dist/output/text/workflow-format.js +192 -10
  46. package/dist/output/text/workflow.js +2 -1
  47. package/dist/runtime.js +1 -0
  48. package/dist/scripts/akm-migrate-node.js +11838 -10118
  49. package/dist/scripts/akm-migrate.js +11828 -10117
  50. package/dist/setup/steps/tasks.js +34 -17
  51. package/dist/storage/repositories/task-history-repository.js +5 -1
  52. package/dist/storage/repositories/workflow-runs-repository.js +144 -6
  53. package/dist/tasks/backends/launchd.js +31 -84
  54. package/dist/tasks/embedded.js +13 -7
  55. package/dist/tasks/model/invocation.js +4 -0
  56. package/dist/tasks/prepare/prepare-script-target.js +9 -0
  57. package/dist/tasks/prepare/prepare-support.js +154 -0
  58. package/dist/tasks/prepare/prepare.js +117 -0
  59. package/dist/tasks/prepare/prepared-execution.js +4 -0
  60. package/dist/tasks/prepare/script-capture.js +80 -0
  61. package/dist/tasks/run/attempt-lifecycle.js +165 -0
  62. package/dist/tasks/run/load-task.js +117 -0
  63. package/dist/tasks/run/provenance.js +20 -0
  64. package/dist/tasks/run/run-command-task.js +92 -0
  65. package/dist/tasks/run/run-native-task.js +222 -0
  66. package/dist/tasks/run/run-task.js +99 -0
  67. package/dist/tasks/run/run-workflow-task.js +222 -0
  68. package/dist/tasks/run/task-history.js +134 -0
  69. package/dist/tasks/run/task-log.js +179 -0
  70. package/dist/tasks/run/task-result.js +19 -0
  71. package/dist/tasks/scheduler-binding.js +66 -2
  72. package/dist/tasks/scheduler-invocation.js +63 -3
  73. package/dist/tasks/scheduler-sync.js +77 -14
  74. package/dist/tasks/source/bounded-document.js +455 -0
  75. package/dist/tasks/source/parse-task-source.js +59 -0
  76. package/dist/tasks/source/project-v4.js +62 -0
  77. package/dist/tasks/source/task-input-diagnostics.js +36 -0
  78. package/dist/tasks/source/task-source-v4.js +626 -0
  79. package/dist/tasks/source-v3.js +10 -733
  80. package/dist/tasks/task-run-reserved-flags.js +79 -0
  81. package/dist/workflows/authoring/authoring.js +17 -8
  82. package/dist/workflows/exec/child-invocation.js +34 -0
  83. package/dist/workflows/exec/child-workflow.js +370 -0
  84. package/dist/workflows/exec/exec-unit.js +50 -170
  85. package/dist/workflows/exec/frozen-judge.js +19 -2
  86. package/dist/workflows/exec/native-executor.js +49 -27
  87. package/dist/workflows/exec/param-secrets.js +12 -0
  88. package/dist/workflows/exec/run-workflow.js +48 -59
  89. package/dist/workflows/exec/step-work.js +222 -80
  90. package/dist/workflows/exec/unit-dispatch.js +72 -0
  91. package/dist/workflows/freeze/child-output-references.js +94 -0
  92. package/dist/workflows/freeze/environment.js +174 -0
  93. package/dist/workflows/freeze/identity.js +22 -0
  94. package/dist/workflows/freeze/resolve-steps.js +78 -0
  95. package/dist/workflows/freeze/source-freeze.js +57 -0
  96. package/dist/workflows/freeze/step-values.js +68 -0
  97. package/dist/workflows/freeze/targets/child-workflow.js +206 -0
  98. package/dist/workflows/freeze/targets/command.js +81 -0
  99. package/dist/workflows/freeze/targets/script.js +57 -0
  100. package/dist/workflows/freeze/targets/shell.js +31 -0
  101. package/dist/workflows/freeze/targets/task.js +179 -0
  102. package/dist/workflows/freeze/task-bindings.js +180 -0
  103. package/dist/workflows/ir/compile.js +59 -11
  104. package/dist/workflows/ir/environment-v4.js +3 -3
  105. package/dist/workflows/ir/freeze-v4.js +41 -7
  106. package/dist/workflows/ir/params.js +58 -131
  107. package/dist/workflows/ir/plan-hash.js +3 -3
  108. package/dist/workflows/ir/schema-v4.js +246 -17
  109. package/dist/workflows/parser.js +74 -2
  110. package/dist/workflows/program/schema.js +5 -2
  111. package/dist/workflows/resource-limits.js +20 -0
  112. package/dist/workflows/runtime/plan-classifier.js +24 -7
  113. package/dist/workflows/runtime/run-outputs.js +103 -0
  114. package/dist/workflows/runtime/runs.js +114 -9
  115. package/dist/workflows/runtime/workflow-asset-loader.js +14 -6
  116. package/dist/workflows/source-files.js +5 -5
  117. package/dist/workflows/source-ir/compare.js +17 -0
  118. package/dist/workflows/source-ir/compile.js +7 -3
  119. package/dist/workflows/source-ir/github-yaml.js +64 -17
  120. package/dist/workflows/source-ir/schema.js +69 -21
  121. package/dist/workflows/source-ir/semantics.js +7 -25
  122. package/dist/workflows/source-ir/triggers.js +79 -0
  123. package/dist/workflows/source-ir/uses.js +33 -7
  124. package/docs/migration/README.md +1 -1
  125. package/docs/migration/release-notes/0.9.2.md +87 -11
  126. package/docs/migration/release-notes/README.md +3 -2
  127. package/docs/migration/v0.8-to-v0.9.md +13 -11
  128. package/docs/migration/v0.9.0-troubleshooting.md +20 -13
  129. package/docs/migration/v0.9.1-to-v0.9.2.md +598 -49
  130. package/docs/reference/README.md +1 -1
  131. package/docs/reference/cli.md +140 -46
  132. package/docs/reference/configuration.md +6 -5
  133. package/docs/reference/supported-formats.md +9 -5
  134. package/docs/reference/tasks.md +338 -75
  135. package/docs/reference/workflow-schema.md +290 -16
  136. package/docs/reference/workflows.md +57 -7
  137. package/package.json +1 -1
  138. package/schemas/akm-task.json +173 -118
  139. package/schemas/akm-workflow.json +28 -0
  140. package/dist/tasks/runner.js +0 -941
  141. package/dist/tasks/runtime-v3.js +0 -281
  142. package/dist/workflows/ir/source-freeze-v4.js +0 -506
  143. package/dist/workflows/source-ir/ordering.js +0 -38
@@ -1,506 +0,0 @@
1
- // This Source Code Form is subject to the terms of the Mozilla Public
2
- // License, v. 2.0. If a copy of the MPL was not distributed with this
3
- // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- import { spawnSync } from "node:child_process";
5
- import { createHash } from "node:crypto";
6
- import fs from "node:fs";
7
- import path from "node:path";
8
- import { prepareCommandInvocation } from "../../commands/command/command-execution.js";
9
- import { loadAdapterExecutionSource } from "../../commands/command/execution-source-loader.js";
10
- import { makeBundleRef, parseBundleRef } from "../../core/asset/asset-ref.js";
11
- import { parseEnvRef } from "../../core/env-secret-ref.js";
12
- import { ConfigError, UsageError } from "../../core/errors.js";
13
- import { captureFrozenDirectoryIdentity } from "../../execution/directory-identity.js";
14
- import { freezeExecutableIdentity } from "../../execution/executable-identity.js";
15
- import { canonicalResolvedExecutionRequest, decodeResolvedExecutionRequest, } from "../../execution/resolved-request.js";
16
- import { deriveInstallations } from "../../indexer/installations.js";
17
- import { resolveSourceEntries } from "../../indexer/search/search-source.js";
18
- import { fallbackAnnouncement } from "../../integrations/agent/engine-fallback.js";
19
- import { requireAuthorizedExecutionPlan } from "../../integrations/agent/execution-cascade.js";
20
- import { lowerResolvedExecutionRequest } from "../../integrations/agent/execution-lowering.js";
21
- import { prepareInlineExecution } from "../../integrations/agent/inline-execution.js";
22
- import { resolveAssetPath } from "../../sources/resolve.js";
23
- import { prepareTaskV3Execution, } from "../../tasks/runtime-v3.js";
24
- import { parseTaskV3Yaml } from "../../tasks/source-v3.js";
25
- import { defaultLlmEngineConcurrency } from "../concurrency-policy.js";
26
- import { DEFAULT_EXEC_TIMEOUT_MS } from "../resource-limits.js";
27
- import { compileWorkflowSource } from "../source-ir/compile.js";
28
- import { sourceStepProgramUnit, sourceStepRef, workflowShellCommand } from "../source-ir/program.js";
29
- import { classifyWorkflowStepUses } from "../source-ir/semantics.js";
30
- import { freezeWorkflowEnvironment } from "./environment-v4.js";
31
- /** Resolve every authored target through the shared command/task authorities before v4 publication. */
32
- export async function resolveWorkflowSourceV4(asset, workflowSource, config, collector) {
33
- const compiled = compileWorkflowSource(workflowSource.content, { path: asset.path, workspaceRoot: asset.sourcePath });
34
- if (!compiled.ok) {
35
- throw new UsageError(`Workflow source cannot be frozen: ${compiled.errors.map((error) => error.message).join("; ")}`, "INVALID_FLAG_VALUE");
36
- }
37
- if (compiled.ir.jobs.length !== 1) {
38
- throw new UsageError("Multi-job workflow cannot execute until job boundaries and needs have a durable runtime representation.", "INVALID_FLAG_VALUE");
39
- }
40
- const context = { asset, config, collector, sourceIr: compiled.ir };
41
- const units = new Map();
42
- const judges = new Map();
43
- let engineAnnouncement;
44
- const sourceSteps = compiled.ir.jobs[0]?.steps ?? [];
45
- for (const sourceStep of sourceSteps) {
46
- if (!sourceStep.route) {
47
- const resolved = await resolveStep(sourceStep, context);
48
- units.set(sourceStep.id, Object.freeze(resolved));
49
- engineAnnouncement ??= resolved.engineAnnouncement;
50
- }
51
- if (sourceStep.gate?.rubric?.trim()) {
52
- const judge = resolveJudge(sourceStep, context);
53
- if (judge.target.kind !== "command")
54
- throw new Error(`workflow judge ${sourceStep.id} did not resolve to a command target`);
55
- judges.set(sourceStep.id, judge.target);
56
- engineAnnouncement ??= judge.engineAnnouncement;
57
- }
58
- }
59
- return Object.freeze({
60
- sourceIr: compiled.ir,
61
- units,
62
- judges,
63
- ...(engineAnnouncement ? { engineAnnouncement } : {}),
64
- });
65
- }
66
- async function resolveStep(source, context) {
67
- const baseUnit = sourceStepProgramUnit(source);
68
- if (source.exec || source.run !== undefined)
69
- return directShell(source, baseUnit, context);
70
- if (!source.uses)
71
- return inlineDispatch(source, baseUnit, context);
72
- const target = classifyWorkflowStepUses(source.uses);
73
- if (target.kind === "task")
74
- return taskDispatch(source, baseUnit, target.ref, context);
75
- if (target.kind === "script")
76
- return directScript(source, baseUnit, target.ref, context);
77
- if (target.kind === "command" || target.kind === "builtin-command") {
78
- const action = target.kind === "builtin-command"
79
- ? source.with
80
- : { ref: qualifyRef(target.ref, "commands", context.asset, context.config) };
81
- return commandDispatch(source, baseUnit, action, context);
82
- }
83
- throw new UsageError(`Workflow target ${source.uses} is not executable in 0.9.2.`, "INVALID_FLAG_VALUE");
84
- }
85
- async function commandDispatch(source, baseUnit, action, context) {
86
- const prepared = await prepareCommandInvocation({
87
- action,
88
- config: context.config,
89
- invocationKind: "workflow",
90
- ...(context.sourceIr.defaults
91
- ? { invocationDefaults: executionUnitValues(context.sourceIr.defaults, context.asset.sourcePath) }
92
- : {}),
93
- ...(source.commandMode === "literal" ? { inlineContentMode: "literal" } : {}),
94
- current: executionValues(source, context.asset.sourcePath),
95
- sourceLoader: (ref, kind) => guardedExecutionSource(ref, kind, context),
96
- });
97
- return commandResult(source, baseUnit, prepared, context);
98
- }
99
- function inlineDispatch(source, baseUnit, context) {
100
- const content = source.instructions ?? `Execute workflow step ${source.id}.`;
101
- const prepared = prepareInlineExecution({
102
- content,
103
- config: context.config,
104
- invocationKind: "workflow",
105
- ...(context.sourceIr.defaults
106
- ? { invocationDefaults: executionUnitValues(context.sourceIr.defaults, context.asset.sourcePath) }
107
- : {}),
108
- current: executionValues(source, context.asset.sourcePath),
109
- });
110
- return commandResult(source, baseUnit, prepared, context);
111
- }
112
- function resolveJudge(source, context) {
113
- const engine = context.config.workflow?.judgeEngine;
114
- if (!engine) {
115
- throw new ConfigError("This workflow declares completion criteria but no verification engine is configured. Set workflow.judgeEngine to a named LLM or agent engine.", "INVALID_CONFIG_FILE");
116
- }
117
- const content = source.gate?.rubric?.trim() ?? "Judge workflow completion.";
118
- const prepared = prepareInlineExecution({
119
- content,
120
- config: context.config,
121
- invocationKind: "workflow",
122
- current: { engine },
123
- });
124
- return commandResult(source, { onError: "fail", source: sourceStepRef(source) }, prepared, context);
125
- }
126
- async function taskDispatch(source, baseUnit, refInput, context) {
127
- const owned = await resolveOwnedAsset(refInput, "task", context);
128
- const retained = captureOwned(owned, context.collector);
129
- const task = parseTaskV3Yaml({ yaml: retained.content, filePath: owned.file, workspaceRoot: owned.root });
130
- if (task.target.kind === "uses" && task.target.uses.kind === "workflow") {
131
- throw new UsageError("A workflow task step cannot compose a nested workflow target.", "INVALID_FLAG_VALUE");
132
- }
133
- const prepared = await prepareTaskV3Execution(task, {
134
- taskId: parseBundleRef(owned.ref).conceptId.slice("tasks/".length),
135
- taskRef: owned.ref,
136
- bundleName: owned.bundle,
137
- bundleRoot: owned.root,
138
- config: context.config,
139
- commandSourceLoader: (ref, kind) => guardedExecutionSource(ref, kind, context),
140
- resolveAsset: async ({ ref, type }) => {
141
- const target = await resolveOwnedAsset(ref, type, context);
142
- captureOwned(target, context.collector);
143
- return { file: target.file, bundleRoot: target.root };
144
- },
145
- readFile: (file, root = owned.root) => context.collector.readBytes(file, root),
146
- });
147
- if (prepared.kind === "workflow") {
148
- throw new UsageError("A workflow task step cannot compose a nested workflow target.", "INVALID_FLAG_VALUE");
149
- }
150
- const taskLiterals = Object.entries(prepared.environment).map(([name, value]) => Object.freeze({ kind: "literal", name, value }));
151
- if (prepared.kind === "command") {
152
- return commandResult(source, baseUnit, prepared.invocation, context, taskLiterals);
153
- }
154
- if (prepared.kind === "shell") {
155
- const authoredExec = {
156
- command: workflowShellCommand(prepared.shell, prepared.command),
157
- ...(prepared.cwdIdentity.realCwd !== prepared.cwdIdentity.realRoot
158
- ? { cwd: path.relative(prepared.cwdIdentity.realRoot, prepared.cwdIdentity.realCwd) }
159
- : {}),
160
- };
161
- const exec = freezeExecSpec(source, authoredExec, context);
162
- const environment = Object.freeze([...taskLiterals, ...freezeEnvironment(source, authoredExec, context)]);
163
- const executable = freezeExecutableIdentity(exec.command[0], { cwd: prepared.cwdIdentity.realCwd });
164
- const target = Object.freeze({
165
- kind: "shell",
166
- contentHash: "",
167
- exec,
168
- cwdIdentity: prepared.cwdIdentity,
169
- executable,
170
- ...gitIdentity(baseUnit, prepared.cwdIdentity.realRoot),
171
- });
172
- return {
173
- target,
174
- environment,
175
- unit: { ...baseUnit, exec: authoredExec },
176
- instructions: source.instructions ?? `Run task ${owned.ref}.`,
177
- };
178
- }
179
- return scriptResult(source, baseUnit, prepared, context, taskLiterals);
180
- }
181
- async function directScript(source, baseUnit, refInput, context) {
182
- const owned = await resolveOwnedAsset(refInput, "script", context);
183
- captureOwned(owned, context.collector);
184
- const synthetic = parseTaskV3Yaml({
185
- yaml: `version: 3\nuses: ${owned.ref}\nakm:\n schedule: "@daily"\n`,
186
- filePath: `${context.asset.path}#${source.id}`,
187
- workspaceRoot: context.asset.sourcePath,
188
- });
189
- const prepared = await prepareTaskV3Execution(synthetic, {
190
- taskId: source.id,
191
- taskRef: `${context.asset.ref}#${source.id}`,
192
- bundleName: parseBundleRef(context.asset.ref).bundle ?? owned.bundle,
193
- bundleRoot: context.asset.sourcePath,
194
- config: context.config,
195
- resolveAsset: async () => ({ file: owned.file, bundleRoot: owned.root }),
196
- readFile: () => context.collector.readBytes(owned.file, owned.root),
197
- });
198
- if (prepared.kind !== "script")
199
- throw new Error("direct script did not project as a script");
200
- return scriptResult(source, baseUnit, prepared, context, []);
201
- }
202
- function scriptResult(source, baseUnit, prepared, context, literals) {
203
- const requestedExecutable = scriptExecutable(prepared.interpreter);
204
- const executable = freezeExecutableIdentity(requestedExecutable, { cwd: prepared.cwdIdentity.realCwd });
205
- const authoredExec = { command: [executable.absolutePath, "<frozen-script>"] };
206
- const exec = freezeExecSpec(source, authoredExec, context);
207
- const environment = Object.freeze([...literals, ...freezeEnvironment(source, authoredExec, context)]);
208
- const target = Object.freeze({
209
- kind: "script",
210
- ref: prepared.sourceRef,
211
- contentHash: prepared.sha256,
212
- exec,
213
- interpreter: prepared.interpreter,
214
- extension: prepared.extension,
215
- bytesBase64: prepared.bytesBase64,
216
- byteLength: prepared.byteLength,
217
- cwdIdentity: prepared.cwdIdentity,
218
- materialization: "ephemeral-0700-delete",
219
- executable,
220
- ...gitIdentity(baseUnit, prepared.cwdIdentity.realRoot),
221
- });
222
- return {
223
- target,
224
- environment,
225
- unit: { ...baseUnit, exec: authoredExec },
226
- instructions: source.instructions ?? `Run script ${prepared.sourceRef}.`,
227
- };
228
- }
229
- function directShell(source, baseUnit, context) {
230
- const authoredExec = baseUnit.exec;
231
- if (!authoredExec)
232
- throw new Error(`workflow shell step ${source.id} lost its source-IR execution spec`);
233
- const exec = freezeExecSpec(source, authoredExec, context);
234
- const cwdIdentity = captureFrozenDirectoryIdentity(context.asset.sourcePath, authoredExec.cwd);
235
- const executable = freezeExecutableIdentity(authoredExec.command[0], { cwd: cwdIdentity.realCwd });
236
- const environment = Object.freeze(freezeEnvironment(source, authoredExec, context));
237
- const target = Object.freeze({
238
- kind: "shell",
239
- contentHash: "",
240
- exec,
241
- cwdIdentity,
242
- executable,
243
- ...gitIdentity(baseUnit, cwdIdentity.realRoot),
244
- });
245
- return {
246
- target,
247
- environment,
248
- unit: { ...baseUnit, exec },
249
- instructions: source.instructions ?? `Run ${source.run ?? authoredExec.command.join(" ")}.`,
250
- };
251
- }
252
- function commandResult(source, baseUnit, prepared, context, literals = []) {
253
- const request = durableRequest(requireAuthorizedExecutionPlan(prepared.plan));
254
- const lowered = lowerResolvedExecutionRequest(request, prepared.config);
255
- const cwdIdentity = captureFrozenDirectoryIdentity(context.asset.sourcePath);
256
- let runner = lowered.runner;
257
- let executable;
258
- if (runner.kind === "agent") {
259
- executable = freezeExecutableIdentity(runner.profile.bin, { cwd: cwdIdentity.realCwd });
260
- runner = Object.freeze({ ...runner, profile: Object.freeze({ ...runner.profile, bin: executable.absolutePath }) });
261
- }
262
- const unit = {
263
- ...baseUnit,
264
- engine: request.engine.name,
265
- ...(request.model ? { model: request.model.resolved } : {}),
266
- ...(Object.hasOwn(request.runtime, "timeoutMs") ? { timeoutMs: request.runtime.timeoutMs } : {}),
267
- ...(request.inference ? { llm: request.inference } : {}),
268
- ...(request.outputSchema ? { output: request.outputSchema } : {}),
269
- };
270
- const environment = Object.freeze([...literals, ...freezeEnvironment(source, undefined, context)]);
271
- const target = Object.freeze({
272
- kind: "command",
273
- ref: request.command.source?.ref ?? null,
274
- contentHash: createHash("sha256").update(request.command.content).digest("hex"),
275
- request: JSON.parse(canonicalResolvedExecutionRequest(request)),
276
- runner,
277
- ...(targetConcurrency(runner, context.config) ? { concurrency: targetConcurrency(runner, context.config) } : {}),
278
- cwdIdentity,
279
- ...(executable ? { executable } : {}),
280
- ...gitIdentity(baseUnit, cwdIdentity.realRoot),
281
- });
282
- const engineAnnouncement = fallbackAnnouncement(prepared.fallbackEngineName, request.engine.name);
283
- return {
284
- target,
285
- environment,
286
- unit,
287
- instructions: request.command.content,
288
- ...(engineAnnouncement ? { engineAnnouncement } : {}),
289
- };
290
- }
291
- function freezeExecSpec(source, exec, context) {
292
- const declared = Object.hasOwn(source.unit ?? {}, "timeoutMs")
293
- ? source.unit?.timeoutMs
294
- : context.sourceIr.defaults && Object.hasOwn(context.sourceIr.defaults, "timeoutMs")
295
- ? context.sourceIr.defaults.timeoutMs
296
- : undefined;
297
- return {
298
- ...exec,
299
- command: exec.command,
300
- timeoutMs: declared === undefined ? DEFAULT_EXEC_TIMEOUT_MS : declared,
301
- };
302
- }
303
- function targetConcurrency(runner, config) {
304
- if (runner.kind === "llm") {
305
- const configured = typeof runner.engine === "string" ? config.engines?.[runner.engine] : undefined;
306
- return defaultLlmEngineConcurrency(runner.connection.endpoint, configured?.kind === "llm" ? configured.concurrency : undefined);
307
- }
308
- if (runner.kind !== "sdk" || !runner.fallbackConnection)
309
- return undefined;
310
- const selected = typeof runner.engine === "string" ? config.engines?.[runner.engine] : undefined;
311
- const fallbackName = selected?.kind === "agent" ? (selected.llmEngine ?? config.defaults?.llmEngine) : undefined;
312
- const fallback = fallbackName ? config.engines?.[fallbackName] : undefined;
313
- return defaultLlmEngineConcurrency(runner.fallbackConnection.endpoint, fallback?.kind === "llm" ? fallback.concurrency : undefined);
314
- }
315
- function durableRequest(request) {
316
- const wire = JSON.parse(canonicalResolvedExecutionRequest(request));
317
- const runtime = { ...wire.runtime };
318
- delete runtime.environment;
319
- wire.runtime = runtime;
320
- return decodeResolvedExecutionRequest(wire);
321
- }
322
- function executionValues(source, workspace) {
323
- return executionUnitValues(source.unit, workspace);
324
- }
325
- function executionUnitValues(unit, workspace) {
326
- return Object.freeze({
327
- ...(unit && Object.hasOwn(unit, "engine") ? { engine: unit.engine } : {}),
328
- ...(unit && Object.hasOwn(unit, "model") ? { model: unit.model } : {}),
329
- ...(unit && Object.hasOwn(unit, "llm") ? { inference: unit.llm } : {}),
330
- ...(unit && Object.hasOwn(unit, "timeoutMs") ? { timeout: unit.timeoutMs } : {}),
331
- ...(unit && "output" in unit && Object.hasOwn(unit, "output") ? { outputSchema: unit.output } : {}),
332
- workspace,
333
- });
334
- }
335
- function freezeEnvironment(source, exec, context) {
336
- const literals = Object.entries(source.env ?? {}).map(([name, value]) => Object.freeze({ kind: "literal", name, value: String(value) }));
337
- const passThrough = (exec?.passEnv ?? []).map((name) => Object.freeze({ kind: "pass-through", name }));
338
- const refs = source.unit?.env ?? [];
339
- const envRefs = freezeWorkflowEnvironment(refs, {
340
- collector: context.collector,
341
- resolveRef: (ref) => {
342
- const parsedEnv = parseEnvRef(ref);
343
- if (parsedEnv.type !== "env")
344
- throw new UsageError(`Expected an env ref; got ${ref}.`, "INVALID_FLAG_VALUE");
345
- const owned = resolveOwnedAssetSync(ref, "env", context);
346
- return { ref: owned.ref, bundle: owned.bundle, adapter: owned.adapter, root: owned.root, path: owned.file };
347
- },
348
- });
349
- return [...literals, ...passThrough, ...envRefs];
350
- }
351
- async function guardedExecutionSource(ref, kind, context) {
352
- const owned = await resolveOwnedAsset(ref, kind === "command" ? "command" : "agent", context);
353
- captureOwned(owned, context.collector);
354
- const options = {
355
- config: context.config,
356
- fileContext: () => context.collector.fileContext(owned.root, owned.file),
357
- };
358
- const rendered = kind === "command"
359
- ? await loadAdapterExecutionSource(owned.ref, "command", options)
360
- : await loadAdapterExecutionSource(owned.ref, "persona", options);
361
- context.collector.bindIdentity(owned.file, owned.root, rendered.identity);
362
- return rendered;
363
- }
364
- async function resolveOwnedAsset(ref, type, context) {
365
- return resolveOwnedAssetCore(ref, type, context, false);
366
- }
367
- function resolveOwnedAssetSync(ref, type, context) {
368
- return resolveOwnedAssetCore(ref, type, context, true);
369
- }
370
- function resolveOwnedAssetCore(refInput, type, context, sync) {
371
- const parsed = parseBundleRef(refInput);
372
- const plural = type === "env" ? "env" : `${type}s`;
373
- const conceptId = parsed.conceptId.startsWith(`${plural}/`) ? parsed.conceptId : `${plural}/${parsed.conceptId}`;
374
- const name = conceptId.slice(plural.length + 1);
375
- const direct = parsed.bundle ? configuredOwner(parsed.bundle, context.config) : undefined;
376
- const sources = resolveSourceEntries(undefined, context.config);
377
- const installations = deriveInstallations(sources);
378
- const candidates = direct
379
- ? [direct]
380
- : sources.flatMap((source, index) => {
381
- const installation = installations[index];
382
- if (!installation || (parsed.bundle && installation.id !== parsed.bundle))
383
- return [];
384
- return [
385
- {
386
- bundle: installation.id,
387
- root: source.path,
388
- adapter: source.adapterId ?? installation.components[0]?.adapter ?? "akm",
389
- },
390
- ];
391
- });
392
- const findSync = () => {
393
- for (const candidate of candidates) {
394
- const directory = path.join(candidate.root, plural);
395
- for (const extension of assetExtensions(type)) {
396
- const file = path.resolve(directory, `${name}${extension}`);
397
- if (fs.existsSync(file) && fs.statSync(file).isFile()) {
398
- return { ...candidate, ref: makeBundleRef(candidate.bundle, conceptId), file };
399
- }
400
- }
401
- }
402
- throw new UsageError(`Workflow source target ${refInput} was not found.`, "INVALID_FLAG_VALUE");
403
- };
404
- if (sync)
405
- return findSync();
406
- return (async () => {
407
- for (const candidate of candidates) {
408
- try {
409
- const file = await resolveAssetPath(candidate.root, type, name);
410
- return { ...candidate, ref: makeBundleRef(candidate.bundle, conceptId), file };
411
- }
412
- catch {
413
- // Continue in installation priority order.
414
- }
415
- }
416
- return findSync();
417
- })();
418
- }
419
- function configuredOwner(bundle, config) {
420
- const entry = config.bundles?.[bundle];
421
- if (!entry || typeof entry.path !== "string")
422
- return undefined;
423
- const components = entry.components ? Object.values(entry.components) : [];
424
- const component = components[0];
425
- return {
426
- bundle,
427
- root: path.resolve(entry.path, component?.root ?? "."),
428
- adapter: component?.adapter ?? "akm",
429
- };
430
- }
431
- function assetExtensions(type) {
432
- if (type === "script")
433
- return [
434
- "",
435
- ".sh",
436
- ".ts",
437
- ".js",
438
- ".py",
439
- ".rb",
440
- ".go",
441
- ".pl",
442
- ".php",
443
- ".lua",
444
- ".r",
445
- ".swift",
446
- ".kt",
447
- ".kts",
448
- ".ps1",
449
- ".cmd",
450
- ".bat",
451
- ];
452
- if (type === "env")
453
- return ["", ".env"];
454
- return ["", ".md", ".yml"];
455
- }
456
- function captureOwned(owned, collector) {
457
- trackAncestry(collector, owned.root, owned.file);
458
- const retained = collector.capture(owned.file, owned.root, { authored: true });
459
- return collector.bindIdentity(owned.file, owned.root, {
460
- ref: owned.ref,
461
- bundle: owned.bundle,
462
- adapter: owned.adapter,
463
- file: retained.relativePath,
464
- hash: retained.sha256,
465
- });
466
- }
467
- function trackAncestry(collector, rootInput, file) {
468
- const root = path.resolve(rootInput);
469
- collector.trackDirectory(root, root);
470
- const relative = path.relative(root, path.dirname(file));
471
- if (relative.startsWith("..") || path.isAbsolute(relative)) {
472
- throw new UsageError(`${file} resolves outside its owning root.`, "PATH_ESCAPE_VIOLATION");
473
- }
474
- let current = root;
475
- for (const segment of relative === "" ? [] : relative.split(path.sep)) {
476
- current = path.join(current, segment);
477
- collector.trackDirectory(current, root);
478
- }
479
- }
480
- function qualifyRef(ref, plural, asset, config) {
481
- const parsed = parseBundleRef(ref);
482
- if (parsed.bundle)
483
- return ref;
484
- const bundle = parseBundleRef(asset.ref).bundle ?? config.defaultBundle;
485
- if (!bundle)
486
- throw new UsageError(`Workflow ref ${ref} has no owning bundle.`, "INVALID_FLAG_VALUE");
487
- const concept = parsed.conceptId.startsWith(`${plural}/`) ? parsed.conceptId : `${plural}/${parsed.conceptId}`;
488
- return makeBundleRef(bundle, concept);
489
- }
490
- function scriptExecutable(interpreter) {
491
- if (interpreter === "bun" || interpreter === "bun-standalone")
492
- return process.execPath;
493
- if (interpreter === "kotlin")
494
- return "kotlin";
495
- return interpreter;
496
- }
497
- function gitIdentity(unit, root) {
498
- if (unit.isolation !== "worktree")
499
- return {};
500
- const result = spawnSync("git", ["-C", root, "rev-parse", "HEAD"], { encoding: "utf8" });
501
- const oid = result.status === 0 ? result.stdout.trim() : "";
502
- if (!/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(oid)) {
503
- throw new UsageError(`Worktree-isolated workflow root ${root} has no immutable Git HEAD OID.`, "INVALID_FLAG_VALUE");
504
- }
505
- return { gitCommitOid: oid };
506
- }
@@ -1,38 +0,0 @@
1
- // This Source Code Form is subject to the terms of the Mozilla Public
2
- // License, v. 2.0. If a copy of the MPL was not distributed with this
3
- // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- /** The one canonical job ordering: emit one lexical ready job, then recompute readiness. */
5
- export function canonicalTopologicalJobs(jobs) {
6
- const byId = new Map(jobs.map((job) => [job.id, job]));
7
- for (const job of jobs) {
8
- for (const dependency of job.needs) {
9
- if (!byId.has(dependency)) {
10
- return { ok: false, kind: "missing", job, dependency };
11
- }
12
- }
13
- }
14
- const ordered = [];
15
- const emitted = new Set();
16
- while (ordered.length < jobs.length) {
17
- const ready = jobs
18
- .filter((job) => !emitted.has(job.id) && job.needs.every((need) => emitted.has(need)))
19
- .sort((left, right) => compareCodePoints(left.id, right.id))[0];
20
- if (!ready) {
21
- const cyclic = jobs
22
- .filter((job) => !emitted.has(job.id))
23
- .sort((left, right) => compareCodePoints(left.id, right.id))[0];
24
- if (!cyclic)
25
- throw new Error("Dependency ordering stalled without a remaining job.");
26
- return { ok: false, kind: "cycle", job: cyclic };
27
- }
28
- ordered.push(ready);
29
- emitted.add(ready.id);
30
- }
31
- return { ok: true, jobs: ordered };
32
- }
33
- export function compareWorkflowSourceCodePoints(left, right) {
34
- return left < right ? -1 : left > right ? 1 : 0;
35
- }
36
- function compareCodePoints(left, right) {
37
- return compareWorkflowSourceCodePoints(left, right);
38
- }