agentplane 0.2.25 → 0.2.26

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 (166) hide show
  1. package/bin/agentplane.js +91 -54
  2. package/dist/.build-manifest.json +11 -0
  3. package/dist/backends/task-backend/local-backend.d.ts +2 -0
  4. package/dist/backends/task-backend/local-backend.d.ts.map +1 -1
  5. package/dist/backends/task-backend/local-backend.js +12 -1
  6. package/dist/backends/task-backend/redmine/mapping.d.ts.map +1 -1
  7. package/dist/backends/task-backend/redmine/mapping.js +26 -1
  8. package/dist/backends/task-backend/redmine-backend.d.ts +4 -0
  9. package/dist/backends/task-backend/redmine-backend.d.ts.map +1 -1
  10. package/dist/backends/task-backend/redmine-backend.js +92 -9
  11. package/dist/backends/task-backend/shared/types.d.ts +1 -0
  12. package/dist/backends/task-backend/shared/types.d.ts.map +1 -1
  13. package/dist/backends/task-index.d.ts.map +1 -1
  14. package/dist/backends/task-index.js +8 -1
  15. package/dist/cli/command-guide.d.ts.map +1 -1
  16. package/dist/cli/command-guide.js +21 -8
  17. package/dist/cli/command-snippets.d.ts +24 -0
  18. package/dist/cli/command-snippets.d.ts.map +1 -0
  19. package/dist/cli/command-snippets.js +23 -0
  20. package/dist/cli/reason-codes.d.ts +9 -0
  21. package/dist/cli/reason-codes.d.ts.map +1 -0
  22. package/dist/cli/reason-codes.js +79 -0
  23. package/dist/cli/recipes-bundled.d.ts +1 -0
  24. package/dist/cli/recipes-bundled.d.ts.map +1 -1
  25. package/dist/cli/recipes-bundled.js +4 -1
  26. package/dist/cli/run-cli/command-catalog.d.ts +1 -1
  27. package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
  28. package/dist/cli/run-cli/command-catalog.js +40 -1
  29. package/dist/cli/run-cli/commands/config.d.ts +5 -0
  30. package/dist/cli/run-cli/commands/config.d.ts.map +1 -1
  31. package/dist/cli/run-cli/commands/config.js +86 -1
  32. package/dist/cli/run-cli/commands/core.d.ts.map +1 -1
  33. package/dist/cli/run-cli/commands/core.js +55 -0
  34. package/dist/cli/run-cli/commands/init/recipes.d.ts +5 -1
  35. package/dist/cli/run-cli/commands/init/recipes.d.ts.map +1 -1
  36. package/dist/cli/run-cli/commands/init/recipes.js +24 -4
  37. package/dist/cli/run-cli/commands/init/write-workflow.d.ts +7 -0
  38. package/dist/cli/run-cli/commands/init/write-workflow.d.ts.map +1 -0
  39. package/dist/cli/run-cli/commands/init/write-workflow.js +52 -0
  40. package/dist/cli/run-cli/commands/init.d.ts +2 -1
  41. package/dist/cli/run-cli/commands/init.d.ts.map +1 -1
  42. package/dist/cli/run-cli/commands/init.js +104 -54
  43. package/dist/cli/run-cli.d.ts.map +1 -1
  44. package/dist/cli/run-cli.js +70 -1
  45. package/dist/commands/backend/sync.command.d.ts.map +1 -1
  46. package/dist/commands/backend/sync.command.js +7 -6
  47. package/dist/commands/backend.d.ts.map +1 -1
  48. package/dist/commands/backend.js +2 -0
  49. package/dist/commands/doctor.run.d.ts.map +1 -1
  50. package/dist/commands/doctor.run.js +96 -10
  51. package/dist/commands/guard/impl/commands.d.ts.map +1 -1
  52. package/dist/commands/guard/impl/commands.js +12 -6
  53. package/dist/commands/recipes/impl/commands/install.d.ts.map +1 -1
  54. package/dist/commands/recipes/impl/commands/install.js +36 -13
  55. package/dist/commands/recipes/impl/scenario.d.ts.map +1 -1
  56. package/dist/commands/recipes/impl/scenario.js +25 -0
  57. package/dist/commands/recipes/impl/types.d.ts +4 -0
  58. package/dist/commands/recipes/impl/types.d.ts.map +1 -1
  59. package/dist/commands/scenario/impl/commands.d.ts.map +1 -1
  60. package/dist/commands/scenario/impl/commands.js +74 -3
  61. package/dist/commands/scenario/impl/report.d.ts +8 -0
  62. package/dist/commands/scenario/impl/report.d.ts.map +1 -1
  63. package/dist/commands/scenario/impl/report.js +1 -0
  64. package/dist/commands/shared/reconcile-check.d.ts +7 -0
  65. package/dist/commands/shared/reconcile-check.d.ts.map +1 -0
  66. package/dist/commands/shared/reconcile-check.js +60 -0
  67. package/dist/commands/sync.command.d.ts.map +1 -1
  68. package/dist/commands/sync.command.js +9 -2
  69. package/dist/commands/task/finish.d.ts.map +1 -1
  70. package/dist/commands/task/finish.js +2 -0
  71. package/dist/commands/task/list.d.ts.map +1 -1
  72. package/dist/commands/task/list.js +2 -1
  73. package/dist/commands/task/list.spec.d.ts.map +1 -1
  74. package/dist/commands/task/list.spec.js +7 -0
  75. package/dist/commands/task/next.d.ts.map +1 -1
  76. package/dist/commands/task/next.js +2 -1
  77. package/dist/commands/task/next.spec.d.ts.map +1 -1
  78. package/dist/commands/task/next.spec.js +7 -0
  79. package/dist/commands/task/search.d.ts.map +1 -1
  80. package/dist/commands/task/search.js +2 -1
  81. package/dist/commands/task/search.spec.d.ts.map +1 -1
  82. package/dist/commands/task/search.spec.js +7 -0
  83. package/dist/commands/task/shared.d.ts +7 -0
  84. package/dist/commands/task/shared.d.ts.map +1 -1
  85. package/dist/commands/task/shared.js +21 -1
  86. package/dist/commands/task/verify-record.d.ts.map +1 -1
  87. package/dist/commands/task/verify-record.js +2 -0
  88. package/dist/commands/workflow-build.command.d.ts +8 -0
  89. package/dist/commands/workflow-build.command.d.ts.map +1 -0
  90. package/dist/commands/workflow-build.command.js +96 -0
  91. package/dist/commands/workflow-playbook.command.d.ts +10 -0
  92. package/dist/commands/workflow-playbook.command.d.ts.map +1 -0
  93. package/dist/commands/workflow-playbook.command.js +174 -0
  94. package/dist/commands/workflow-restore.command.d.ts +5 -0
  95. package/dist/commands/workflow-restore.command.d.ts.map +1 -0
  96. package/dist/commands/workflow-restore.command.js +30 -0
  97. package/dist/commands/workflow.command.d.ts +6 -0
  98. package/dist/commands/workflow.command.d.ts.map +1 -0
  99. package/dist/commands/workflow.command.js +36 -0
  100. package/dist/harness/dynamic-tool-contract.d.ts +29 -0
  101. package/dist/harness/dynamic-tool-contract.d.ts.map +1 -0
  102. package/dist/harness/dynamic-tool-contract.js +86 -0
  103. package/dist/harness/hooks-lifecycle.d.ts +27 -0
  104. package/dist/harness/hooks-lifecycle.d.ts.map +1 -0
  105. package/dist/harness/hooks-lifecycle.js +67 -0
  106. package/dist/harness/index.d.ts +9 -0
  107. package/dist/harness/index.d.ts.map +1 -0
  108. package/dist/harness/index.js +8 -0
  109. package/dist/harness/reconcile.d.ts +37 -0
  110. package/dist/harness/reconcile.d.ts.map +1 -0
  111. package/dist/harness/reconcile.js +42 -0
  112. package/dist/harness/retry-policy.d.ts +31 -0
  113. package/dist/harness/retry-policy.d.ts.map +1 -0
  114. package/dist/harness/retry-policy.js +33 -0
  115. package/dist/harness/scheduler.d.ts +18 -0
  116. package/dist/harness/scheduler.d.ts.map +1 -0
  117. package/dist/harness/scheduler.js +55 -0
  118. package/dist/harness/state-machine.d.ts +17 -0
  119. package/dist/harness/state-machine.d.ts.map +1 -0
  120. package/dist/harness/state-machine.js +70 -0
  121. package/dist/harness/token-accounting.d.ts +19 -0
  122. package/dist/harness/token-accounting.d.ts.map +1 -0
  123. package/dist/harness/token-accounting.js +77 -0
  124. package/dist/harness/workspace-safety.d.ts +14 -0
  125. package/dist/harness/workspace-safety.d.ts.map +1 -0
  126. package/dist/harness/workspace-safety.js +62 -0
  127. package/dist/recipes/bundled-recipes.d.ts +4 -0
  128. package/dist/recipes/bundled-recipes.d.ts.map +1 -1
  129. package/dist/recipes/bundled-recipes.js +11 -0
  130. package/dist/shared/errors.d.ts +6 -0
  131. package/dist/shared/errors.d.ts.map +1 -1
  132. package/dist/shared/errors.js +1 -0
  133. package/dist/workflow-runtime/build.d.ts +4 -0
  134. package/dist/workflow-runtime/build.d.ts.map +1 -0
  135. package/dist/workflow-runtime/build.js +114 -0
  136. package/dist/workflow-runtime/enforcement.d.ts +3 -0
  137. package/dist/workflow-runtime/enforcement.d.ts.map +1 -0
  138. package/dist/workflow-runtime/enforcement.js +10 -0
  139. package/dist/workflow-runtime/file-ops.d.ts +11 -0
  140. package/dist/workflow-runtime/file-ops.d.ts.map +1 -0
  141. package/dist/workflow-runtime/file-ops.js +248 -0
  142. package/dist/workflow-runtime/fix.d.ts +9 -0
  143. package/dist/workflow-runtime/fix.d.ts.map +1 -0
  144. package/dist/workflow-runtime/fix.js +107 -0
  145. package/dist/workflow-runtime/index.d.ts +11 -0
  146. package/dist/workflow-runtime/index.d.ts.map +1 -0
  147. package/dist/workflow-runtime/index.js +10 -0
  148. package/dist/workflow-runtime/markdown.d.ts +10 -0
  149. package/dist/workflow-runtime/markdown.d.ts.map +1 -0
  150. package/dist/workflow-runtime/markdown.js +147 -0
  151. package/dist/workflow-runtime/observability.d.ts +12 -0
  152. package/dist/workflow-runtime/observability.d.ts.map +1 -0
  153. package/dist/workflow-runtime/observability.js +14 -0
  154. package/dist/workflow-runtime/paths.d.ts +3 -0
  155. package/dist/workflow-runtime/paths.d.ts.map +1 -0
  156. package/dist/workflow-runtime/paths.js +11 -0
  157. package/dist/workflow-runtime/template.d.ts +7 -0
  158. package/dist/workflow-runtime/template.d.ts.map +1 -0
  159. package/dist/workflow-runtime/template.js +94 -0
  160. package/dist/workflow-runtime/types.d.ts +68 -0
  161. package/dist/workflow-runtime/types.d.ts.map +1 -0
  162. package/dist/workflow-runtime/types.js +1 -0
  163. package/dist/workflow-runtime/validate.d.ts +8 -0
  164. package/dist/workflow-runtime/validate.d.ts.map +1 -0
  165. package/dist/workflow-runtime/validate.js +331 -0
  166. package/package.json +3 -3
@@ -0,0 +1,19 @@
1
+ export type TokenTotals = {
2
+ inputTokens: number;
3
+ outputTokens: number;
4
+ totalTokens: number;
5
+ };
6
+ export type TokenUsageEvent = {
7
+ threadId: string;
8
+ payload: Record<string, unknown>;
9
+ };
10
+ export type TokenAccumulator = {
11
+ byThread: Record<string, TokenTotals>;
12
+ global: TokenTotals;
13
+ };
14
+ export declare function createTokenAccumulator(): TokenAccumulator;
15
+ export declare function applyTokenUsageEvent(state: TokenAccumulator, event: TokenUsageEvent): {
16
+ state: TokenAccumulator;
17
+ accepted: boolean;
18
+ };
19
+ //# sourceMappingURL=token-accounting.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-accounting.d.ts","sourceRoot":"","sources":["../../src/harness/token-accounting.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACtC,MAAM,EAAE,WAAW,CAAC;CACrB,CAAC;AAwDF,wBAAgB,sBAAsB,IAAI,gBAAgB,CAKzD;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,gBAAgB,EACvB,KAAK,EAAE,eAAe,GACrB;IAAE,KAAK,EAAE,gBAAgB,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAuBhD"}
@@ -0,0 +1,77 @@
1
+ const ZERO_TOTALS = {
2
+ inputTokens: 0,
3
+ outputTokens: 0,
4
+ totalTokens: 0,
5
+ };
6
+ function safeInt(value) {
7
+ if (typeof value === "number" && Number.isFinite(value))
8
+ return Math.trunc(value);
9
+ if (typeof value === "string" && /^\d+$/.test(value))
10
+ return Number.parseInt(value, 10);
11
+ return null;
12
+ }
13
+ function extractAbsoluteTotals(payload) {
14
+ const tokenUsage = payload.tokenUsage;
15
+ if (tokenUsage && typeof tokenUsage === "object" && !Array.isArray(tokenUsage)) {
16
+ const total = tokenUsage.total;
17
+ if (total && typeof total === "object" && !Array.isArray(total)) {
18
+ const totalObj = total;
19
+ const input = safeInt(totalObj.inputTokens);
20
+ const output = safeInt(totalObj.outputTokens);
21
+ const all = safeInt(totalObj.totalTokens);
22
+ if (input !== null && output !== null && all !== null) {
23
+ return { inputTokens: input, outputTokens: output, totalTokens: all };
24
+ }
25
+ }
26
+ }
27
+ const info = payload.info;
28
+ if (info && typeof info === "object" && !Array.isArray(info)) {
29
+ const totalUsage = info.total_token_usage;
30
+ if (totalUsage && typeof totalUsage === "object" && !Array.isArray(totalUsage)) {
31
+ const usage = totalUsage;
32
+ const input = safeInt(usage.input_tokens);
33
+ const output = safeInt(usage.output_tokens);
34
+ const all = safeInt(usage.total_tokens);
35
+ if (input !== null && output !== null && all !== null) {
36
+ return { inputTokens: input, outputTokens: output, totalTokens: all };
37
+ }
38
+ }
39
+ }
40
+ return null;
41
+ }
42
+ function mergeGlobal(byThread) {
43
+ const global = { ...ZERO_TOTALS };
44
+ for (const totals of Object.values(byThread)) {
45
+ global.inputTokens += totals.inputTokens;
46
+ global.outputTokens += totals.outputTokens;
47
+ global.totalTokens += totals.totalTokens;
48
+ }
49
+ return global;
50
+ }
51
+ export function createTokenAccumulator() {
52
+ return {
53
+ byThread: {},
54
+ global: { ...ZERO_TOTALS },
55
+ };
56
+ }
57
+ export function applyTokenUsageEvent(state, event) {
58
+ const absolute = extractAbsoluteTotals(event.payload);
59
+ if (!absolute) {
60
+ return { state, accepted: false };
61
+ }
62
+ const prev = state.byThread[event.threadId] ?? { ...ZERO_TOTALS };
63
+ if (absolute.totalTokens < prev.totalTokens) {
64
+ return { state, accepted: false };
65
+ }
66
+ const nextByThread = {
67
+ ...state.byThread,
68
+ [event.threadId]: absolute,
69
+ };
70
+ return {
71
+ accepted: true,
72
+ state: {
73
+ byThread: nextByThread,
74
+ global: mergeGlobal(nextByThread),
75
+ },
76
+ };
77
+ }
@@ -0,0 +1,14 @@
1
+ export declare function sanitizeWorkspaceKey(raw: string): string;
2
+ export declare function resolveWorkspacePath(root: string, key: string): string;
3
+ export declare function validateWorkspacePathInvariants(opts: {
4
+ root: string;
5
+ workspacePath: string;
6
+ }): Promise<{
7
+ ok: true;
8
+ } | {
9
+ ok: false;
10
+ code: "WORKSPACE_EQUALS_ROOT" | "WORKSPACE_OUTSIDE_ROOT" | "WORKSPACE_SYMLINK_ESCAPE" | "WORKSPACE_PATH_UNREADABLE";
11
+ path: string;
12
+ message: string;
13
+ }>;
14
+ //# sourceMappingURL=workspace-safety.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-safety.d.ts","sourceRoot":"","sources":["../../src/harness/workspace-safety.ts"],"names":[],"mappings":"AAGA,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIxD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,wBAAsB,+BAA+B,CAAC,IAAI,EAAE;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CACP;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GACZ;IACE,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EACA,uBAAuB,GACvB,wBAAwB,GACxB,0BAA0B,GAC1B,2BAA2B,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CACJ,CAqDA"}
@@ -0,0 +1,62 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ export function sanitizeWorkspaceKey(raw) {
4
+ const normalized = raw.trim();
5
+ if (normalized.length === 0)
6
+ return "issue";
7
+ return normalized.replaceAll(/[^a-zA-Z0-9._-]/g, "_");
8
+ }
9
+ export function resolveWorkspacePath(root, key) {
10
+ return path.join(path.resolve(root), sanitizeWorkspaceKey(key));
11
+ }
12
+ export async function validateWorkspacePathInvariants(opts) {
13
+ const root = path.resolve(opts.root);
14
+ const workspace = path.resolve(opts.workspacePath);
15
+ const rootPrefix = `${root}${path.sep}`;
16
+ if (workspace === root) {
17
+ return {
18
+ ok: false,
19
+ code: "WORKSPACE_EQUALS_ROOT",
20
+ path: workspace,
21
+ message: "Workspace path must not equal workspace root.",
22
+ };
23
+ }
24
+ if (!(workspace === root || workspace.startsWith(rootPrefix))) {
25
+ return {
26
+ ok: false,
27
+ code: "WORKSPACE_OUTSIDE_ROOT",
28
+ path: workspace,
29
+ message: "Workspace path escapes configured workspace root.",
30
+ };
31
+ }
32
+ const relative = path.relative(root, workspace);
33
+ const segments = relative.split(path.sep).filter((s) => s.length > 0);
34
+ let cursor = root;
35
+ for (const segment of segments) {
36
+ cursor = path.join(cursor, segment);
37
+ try {
38
+ const stat = await fs.lstat(cursor);
39
+ if (stat.isSymbolicLink()) {
40
+ return {
41
+ ok: false,
42
+ code: "WORKSPACE_SYMLINK_ESCAPE",
43
+ path: cursor,
44
+ message: "Workspace path contains a symlink component.",
45
+ };
46
+ }
47
+ }
48
+ catch (error) {
49
+ const err = error;
50
+ if (err.code === "ENOENT") {
51
+ return { ok: true };
52
+ }
53
+ return {
54
+ ok: false,
55
+ code: "WORKSPACE_PATH_UNREADABLE",
56
+ path: cursor,
57
+ message: `Workspace path cannot be validated: ${err.message}`,
58
+ };
59
+ }
60
+ }
61
+ return { ok: true };
62
+ }
@@ -4,10 +4,14 @@ export type BundledRecipesCatalog = {
4
4
  id: string;
5
5
  summary: string;
6
6
  description?: string;
7
+ source_path?: string;
7
8
  versions: {
8
9
  version: string;
9
10
  }[];
10
11
  }[];
11
12
  };
13
+ export type BundledRecipeEntry = BundledRecipesCatalog["recipes"][number];
12
14
  export declare const BUNDLED_RECIPES_CATALOG: BundledRecipesCatalog;
15
+ export declare function resolveBundledRecipeSourcePath(recipeId: string): string | null;
16
+ export declare function getBundledRecipeEntry(recipeId: string): BundledRecipeEntry | null;
13
17
  //# sourceMappingURL=bundled-recipes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bundled-recipes.d.ts","sourceRoot":"","sources":["../../src/recipes/bundled-recipes.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG;IAClC,cAAc,EAAE,CAAC,CAAC;IAClB,OAAO,EAAE;QACP,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;KACjC,EAAE,CAAC;CACL,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,qBAGrC,CAAC"}
1
+ {"version":3,"file":"bundled-recipes.d.ts","sourceRoot":"","sources":["../../src/recipes/bundled-recipes.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,qBAAqB,GAAG;IAClC,cAAc,EAAE,CAAC,CAAC;IAClB,OAAO,EAAE;QACP,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;KACjC,EAAE,CAAC;CACL,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1E,eAAO,MAAM,uBAAuB,EAAE,qBAGrC,CAAC;AAEF,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK9E;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAEjF"}
@@ -1,4 +1,15 @@
1
+ import { fileURLToPath } from "node:url";
1
2
  export const BUNDLED_RECIPES_CATALOG = {
2
3
  schema_version: 1,
3
4
  recipes: [],
4
5
  };
6
+ export function resolveBundledRecipeSourcePath(recipeId) {
7
+ const entry = getBundledRecipeEntry(recipeId);
8
+ const sourcePath = entry?.source_path?.trim();
9
+ if (!sourcePath)
10
+ return null;
11
+ return fileURLToPath(new URL(`../../assets/${sourcePath.replace(/^\/+/, "")}`, import.meta.url));
12
+ }
13
+ export function getBundledRecipeEntry(recipeId) {
14
+ return BUNDLED_RECIPES_CATALOG.recipes.find((recipe) => recipe.id === recipeId) ?? null;
15
+ }
@@ -17,6 +17,12 @@ export type JsonErrorGuidance = {
17
17
  reason: string;
18
18
  reasonCode?: string;
19
19
  };
20
+ reasonDecode?: {
21
+ code: string;
22
+ category: string;
23
+ summary: string;
24
+ action: string;
25
+ };
20
26
  };
21
27
  export declare function formatJsonError(err: CliError, guidance?: JsonErrorGuidance): string;
22
28
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/shared/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,cAAc,GACd,MAAM,GACN,OAAO,GACP,WAAW,GACX,WAAW,GACX,YAAY,CAAC;AAEjB,qBAAa,QAAS,SAAQ,KAAK;IACjC,SAAgB,QAAQ,EAAE,MAAM,CAAC;IACjC,SAAgB,IAAI,EAAE,SAAS,CAAC;IAChC,SAAgB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAEtC,IAAI,EAAE;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,SAAS,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACnC;CAMF;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH,CAAC;AAEF,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAcnF"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/shared/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,cAAc,GACd,MAAM,GACN,OAAO,GACP,WAAW,GACX,WAAW,GACX,YAAY,CAAC;AAEjB,qBAAa,QAAS,SAAQ,KAAK;IACjC,SAAgB,QAAQ,EAAE,MAAM,CAAC;IACjC,SAAgB,IAAI,EAAE,SAAS,CAAC;IAChC,SAAgB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAEtC,IAAI,EAAE;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,SAAS,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACnC;CAMF;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,YAAY,CAAC,EAAE;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC;AAEF,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAenF"}
@@ -17,6 +17,7 @@ export function formatJsonError(err, guidance) {
17
17
  context: err.context ?? undefined,
18
18
  hint: guidance?.hint,
19
19
  next_action: guidance?.nextAction,
20
+ reason_decode: guidance?.reasonDecode,
20
21
  },
21
22
  }, null, 2);
22
23
  }
@@ -0,0 +1,4 @@
1
+ import type { WorkflowBuildInput, WorkflowBuildOutput } from "./types.js";
2
+ export declare function buildWorkflowFromTemplates(input: WorkflowBuildInput): WorkflowBuildOutput;
3
+ export declare const DEFAULT_WORKFLOW_TEMPLATE = "---\nversion: 1\nmode: direct\nowners:\n orchestrator: ORCHESTRATOR\napprovals:\n require_plan: true\n require_verify: true\n require_network: true\nretry_policy:\n normal_exit_continuation: true\n abnormal_backoff: exponential\n max_attempts: 5\ntimeouts:\n stall_seconds: 900\nin_scope_paths:\n - packages/**\n---\n\n## Prompt Template\nRepository root: {{ runtime.repo_root }}\nWorkflow mode: {{ workflow.mode }}\n\n## Checks\n- preflight\n- verify\n- finish\n\n## Fallback\nlast_known_good: .agentplane/workflows/last-known-good.md\n";
4
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/workflow-runtime/build.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAsB,MAAM,YAAY,CAAC;AAqC9F,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,kBAAkB,GAAG,mBAAmB,CA8DzF;AAED,eAAO,MAAM,yBAAyB,uiBA8BrC,CAAC"}
@@ -0,0 +1,114 @@
1
+ import { parseWorkflowMarkdown, serializeWorkflowMarkdown } from "./markdown.js";
2
+ import { emitWorkflowEvent } from "./observability.js";
3
+ import { renderTemplateStrict, validateTemplateStrict } from "./template.js";
4
+ import { validateWorkflowDocument } from "./validate.js";
5
+ function mergeRecord(baseValue, overrideValue) {
6
+ const out = { ...baseValue };
7
+ for (const [key, value] of Object.entries(overrideValue)) {
8
+ if (value &&
9
+ typeof value === "object" &&
10
+ !Array.isArray(value) &&
11
+ out[key] &&
12
+ typeof out[key] === "object" &&
13
+ !Array.isArray(out[key])) {
14
+ out[key] = mergeRecord(out[key], value);
15
+ continue;
16
+ }
17
+ out[key] = value;
18
+ }
19
+ return out;
20
+ }
21
+ function mergeSections(baseSections, overrideSections) {
22
+ const out = { ...baseSections };
23
+ for (const [key, value] of Object.entries(overrideSections)) {
24
+ if (value.trim().length === 0)
25
+ continue;
26
+ out[key] = value;
27
+ }
28
+ return out;
29
+ }
30
+ export function buildWorkflowFromTemplates(input) {
31
+ emitWorkflowEvent({ event: "workflow_build_started" });
32
+ const diagnostics = [];
33
+ const base = parseWorkflowMarkdown(input.baseTemplate);
34
+ diagnostics.push(...base.diagnostics);
35
+ const override = input.projectOverrideTemplate
36
+ ? parseWorkflowMarkdown(input.projectOverrideTemplate)
37
+ : null;
38
+ if (override)
39
+ diagnostics.push(...override.diagnostics);
40
+ const mergedFrontMatter = mergeRecord(base.document.frontMatterRaw, override?.document.frontMatterRaw ?? {});
41
+ const runtimeWorkflow = input.runtimeContext.workflow;
42
+ if (runtimeWorkflow && typeof runtimeWorkflow === "object" && !Array.isArray(runtimeWorkflow)) {
43
+ const runtimeMode = runtimeWorkflow.mode;
44
+ if (runtimeMode === "direct" || runtimeMode === "branch_pr") {
45
+ mergedFrontMatter.mode = runtimeMode;
46
+ }
47
+ }
48
+ const mergedSections = mergeSections(base.document.sections, override?.document.sections ?? {});
49
+ const promptTemplate = mergedSections["Prompt Template"] ?? "";
50
+ const strict = validateTemplateStrict(promptTemplate, input.runtimeContext, {
51
+ strictVariables: true,
52
+ strictFilters: true,
53
+ });
54
+ diagnostics.push(...strict.diagnostics);
55
+ const renderedPrompt = renderTemplateStrict(promptTemplate, input.runtimeContext, {
56
+ strictVariables: true,
57
+ strictFilters: true,
58
+ });
59
+ diagnostics.push(...renderedPrompt.diagnostics);
60
+ mergedSections["Prompt Template"] = renderedPrompt.text;
61
+ const renderedText = serializeWorkflowMarkdown(mergedFrontMatter, mergedSections);
62
+ const parsedRendered = parseWorkflowMarkdown(renderedText);
63
+ diagnostics.push(...parsedRendered.diagnostics);
64
+ const schema = validateWorkflowDocument(parsedRendered.document);
65
+ diagnostics.push(...schema.diagnostics);
66
+ const hasError = diagnostics.some((d) => d.severity === "ERROR");
67
+ if (hasError) {
68
+ emitWorkflowEvent({
69
+ event: "workflow_build_failed",
70
+ details: { diagnostics: diagnostics.length },
71
+ });
72
+ }
73
+ else {
74
+ emitWorkflowEvent({
75
+ event: "workflow_build_completed",
76
+ details: { diagnostics: diagnostics.length },
77
+ });
78
+ }
79
+ return {
80
+ text: renderedText,
81
+ diagnostics,
82
+ };
83
+ }
84
+ export const DEFAULT_WORKFLOW_TEMPLATE = `---
85
+ version: 1
86
+ mode: direct
87
+ owners:
88
+ orchestrator: ORCHESTRATOR
89
+ approvals:
90
+ require_plan: true
91
+ require_verify: true
92
+ require_network: true
93
+ retry_policy:
94
+ normal_exit_continuation: true
95
+ abnormal_backoff: exponential
96
+ max_attempts: 5
97
+ timeouts:
98
+ stall_seconds: 900
99
+ in_scope_paths:
100
+ - packages/**
101
+ ---
102
+
103
+ ## Prompt Template
104
+ Repository root: {{ runtime.repo_root }}
105
+ Workflow mode: {{ workflow.mode }}
106
+
107
+ ## Checks
108
+ - preflight
109
+ - verify
110
+ - finish
111
+
112
+ ## Fallback
113
+ last_known_good: .agentplane/workflows/last-known-good.md
114
+ `;
@@ -0,0 +1,3 @@
1
+ export declare function isWorkflowEnforcementDisabled(env?: NodeJS.ProcessEnv): boolean;
2
+ export declare function workflowEnforcementEnvHint(): string;
3
+ //# sourceMappingURL=enforcement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enforcement.d.ts","sourceRoot":"","sources":["../../src/workflow-runtime/enforcement.ts"],"names":[],"mappings":"AAEA,wBAAgB,6BAA6B,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAI3F;AAED,wBAAgB,0BAA0B,IAAI,MAAM,CAEnD"}
@@ -0,0 +1,10 @@
1
+ const DISABLED_VALUES = new Set(["0", "false", "off", "disabled"]);
2
+ export function isWorkflowEnforcementDisabled(env = process.env) {
3
+ const raw = env.AGENTPLANE_WORKFLOW_ENFORCEMENT ?? env.AGENTPLANE_WORKFLOW_CONTRACT;
4
+ if (typeof raw !== "string")
5
+ return false;
6
+ return DISABLED_VALUES.has(raw.trim().toLowerCase());
7
+ }
8
+ export function workflowEnforcementEnvHint() {
9
+ return "AGENTPLANE_WORKFLOW_ENFORCEMENT";
10
+ }
@@ -0,0 +1,11 @@
1
+ import type { WorkflowDiagnostic, WorkflowDocument, WorkflowValidationResult } from "./types.js";
2
+ export declare function readWorkflowDocument(repoRoot: string, absPath?: string): Promise<{
3
+ document: WorkflowDocument | null;
4
+ diagnostics: WorkflowDiagnostic[];
5
+ path: string;
6
+ }>;
7
+ export declare function validateWorkflowAtPath(repoRoot: string, absPath?: string): Promise<WorkflowValidationResult>;
8
+ export declare function validateWorkflowText(repoRoot: string, workflowText: string): Promise<WorkflowValidationResult>;
9
+ export declare function publishWorkflowCandidate(repoRoot: string, candidateText: string): Promise<WorkflowValidationResult>;
10
+ export declare function restoreWorkflowFromLastKnownGood(repoRoot: string): Promise<WorkflowValidationResult>;
11
+ //# sourceMappingURL=file-ops.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-ops.d.ts","sourceRoot":"","sources":["../../src/workflow-runtime/file-ops.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAiDjG,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CA0BjG;AAED,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,wBAAwB,CAAC,CAuBnC;AAED,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,wBAAwB,CAAC,CAanC;AAED,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,wBAAwB,CAAC,CA0DnC;AAED,wBAAsB,gCAAgC,CACpD,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,wBAAwB,CAAC,CA6EnC"}
@@ -0,0 +1,248 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { loadConfig } from "@agentplaneorg/core";
4
+ import { parseWorkflowMarkdown } from "./markdown.js";
5
+ import { emitWorkflowEvent } from "./observability.js";
6
+ import { resolveWorkflowPaths } from "./paths.js";
7
+ import { validateWorkflowDocument } from "./validate.js";
8
+ async function pathExists(absPath) {
9
+ try {
10
+ await fs.access(absPath);
11
+ return true;
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ }
17
+ async function resolveWorkflowReadPath(paths) {
18
+ if (await pathExists(paths.workflowPath))
19
+ return paths.workflowPath;
20
+ if (await pathExists(paths.legacyWorkflowPath))
21
+ return paths.legacyWorkflowPath;
22
+ return paths.workflowPath;
23
+ }
24
+ async function removeLegacyWorkflowIfPresent(paths) {
25
+ if (paths.workflowPath === paths.legacyWorkflowPath)
26
+ return;
27
+ try {
28
+ await fs.rm(paths.legacyWorkflowPath, { force: true });
29
+ }
30
+ catch {
31
+ // best effort cleanup
32
+ }
33
+ }
34
+ async function listAgentIds(agentplaneDir) {
35
+ const ids = new Set();
36
+ const dir = path.join(agentplaneDir, "agents");
37
+ try {
38
+ const entries = await fs.readdir(dir, { withFileTypes: true });
39
+ for (const ent of entries) {
40
+ if (ent.isFile() && ent.name.endsWith(".json")) {
41
+ ids.add(ent.name.replace(/\.json$/i, ""));
42
+ }
43
+ }
44
+ }
45
+ catch {
46
+ // best effort
47
+ }
48
+ return ids;
49
+ }
50
+ export async function readWorkflowDocument(repoRoot, absPath) {
51
+ const paths = resolveWorkflowPaths(repoRoot);
52
+ const targetPath = absPath ?? (await resolveWorkflowReadPath(paths));
53
+ try {
54
+ const content = await fs.readFile(targetPath, "utf8");
55
+ const parsed = parseWorkflowMarkdown(content, targetPath);
56
+ return {
57
+ document: parsed.document,
58
+ diagnostics: parsed.diagnostics,
59
+ path: targetPath,
60
+ };
61
+ }
62
+ catch (error) {
63
+ return {
64
+ document: null,
65
+ diagnostics: [
66
+ {
67
+ code: (await pathExists(targetPath)) ? "WF_READ_FAILED" : "WF_MISSING_FILE",
68
+ severity: "ERROR",
69
+ path: "file",
70
+ message: `Cannot read workflow file ${targetPath}: ${error instanceof Error ? error.message : String(error)}`,
71
+ },
72
+ ],
73
+ path: targetPath,
74
+ };
75
+ }
76
+ }
77
+ export async function validateWorkflowAtPath(repoRoot, absPath) {
78
+ const read = await readWorkflowDocument(repoRoot, absPath);
79
+ const diagnostics = [...read.diagnostics];
80
+ if (!read.document) {
81
+ return {
82
+ ok: false,
83
+ diagnostics,
84
+ };
85
+ }
86
+ const loaded = await loadConfig(path.join(repoRoot, ".agentplane"));
87
+ const knownAgentIds = await listAgentIds(path.join(repoRoot, ".agentplane"));
88
+ const validated = validateWorkflowDocument(read.document, {
89
+ repoRoot,
90
+ knownAgentIds,
91
+ config: loaded.config,
92
+ });
93
+ diagnostics.push(...validated.diagnostics);
94
+ return {
95
+ ok: diagnostics.every((d) => d.severity !== "ERROR"),
96
+ diagnostics,
97
+ };
98
+ }
99
+ export async function validateWorkflowText(repoRoot, workflowText) {
100
+ const parsed = parseWorkflowMarkdown(workflowText);
101
+ const loaded = await loadConfig(path.join(repoRoot, ".agentplane"));
102
+ const knownAgentIds = await listAgentIds(path.join(repoRoot, ".agentplane"));
103
+ const validated = validateWorkflowDocument(parsed.document, {
104
+ repoRoot,
105
+ knownAgentIds,
106
+ config: loaded.config,
107
+ });
108
+ return {
109
+ ok: [...parsed.diagnostics, ...validated.diagnostics].every((d) => d.severity !== "ERROR"),
110
+ diagnostics: [...parsed.diagnostics, ...validated.diagnostics],
111
+ };
112
+ }
113
+ export async function publishWorkflowCandidate(repoRoot, candidateText) {
114
+ const paths = resolveWorkflowPaths(repoRoot);
115
+ const tempPath = `${paths.workflowPath}.tmp.${Date.now()}`;
116
+ const parsed = parseWorkflowMarkdown(candidateText, paths.workflowPath);
117
+ const configLoaded = await loadConfig(path.join(repoRoot, ".agentplane"));
118
+ const validation = validateWorkflowDocument(parsed.document, {
119
+ repoRoot,
120
+ knownAgentIds: await listAgentIds(path.join(repoRoot, ".agentplane")),
121
+ config: configLoaded.config,
122
+ });
123
+ const diagnostics = [...parsed.diagnostics, ...validation.diagnostics];
124
+ if (diagnostics.some((d) => d.severity === "ERROR")) {
125
+ emitWorkflowEvent({
126
+ event: "workflow_publish_failed",
127
+ code: "WF_PARSE_ERROR",
128
+ details: { reason: "validation failed before publish", diagnostics: diagnostics.length },
129
+ });
130
+ return { ok: false, diagnostics };
131
+ }
132
+ try {
133
+ await fs.mkdir(path.dirname(paths.workflowPath), { recursive: true });
134
+ await fs.mkdir(paths.workflowDir, { recursive: true });
135
+ await fs.writeFile(tempPath, candidateText, "utf8");
136
+ await fs.rename(tempPath, paths.workflowPath);
137
+ await removeLegacyWorkflowIfPresent(paths);
138
+ await fs.copyFile(paths.workflowPath, paths.lastKnownGoodPath);
139
+ emitWorkflowEvent({
140
+ event: "workflow_publish_completed",
141
+ details: { workflowPath: paths.workflowPath, lastKnownGoodPath: paths.lastKnownGoodPath },
142
+ });
143
+ return { ok: true, diagnostics };
144
+ }
145
+ catch (error) {
146
+ try {
147
+ await fs.rm(tempPath, { force: true });
148
+ }
149
+ catch {
150
+ // best effort cleanup
151
+ }
152
+ emitWorkflowEvent({
153
+ event: "workflow_publish_failed",
154
+ code: "WF_READ_FAILED",
155
+ details: { reason: error instanceof Error ? error.message : String(error) },
156
+ });
157
+ return {
158
+ ok: false,
159
+ diagnostics: [
160
+ ...diagnostics,
161
+ {
162
+ code: "WF_READ_FAILED",
163
+ severity: "ERROR",
164
+ path: "file",
165
+ message: `Failed to atomically publish workflow: ${error instanceof Error ? error.message : String(error)}`,
166
+ },
167
+ ],
168
+ };
169
+ }
170
+ }
171
+ export async function restoreWorkflowFromLastKnownGood(repoRoot) {
172
+ const paths = resolveWorkflowPaths(repoRoot);
173
+ const tempPath = `${paths.workflowPath}.restore.tmp.${Date.now()}`;
174
+ let lkgText = "";
175
+ try {
176
+ lkgText = await fs.readFile(paths.lastKnownGoodPath, "utf8");
177
+ }
178
+ catch (error) {
179
+ emitWorkflowEvent({
180
+ event: "workflow_restore_failed",
181
+ code: "WF_MISSING_FILE",
182
+ details: { reason: error instanceof Error ? error.message : String(error) },
183
+ });
184
+ return {
185
+ ok: false,
186
+ diagnostics: [
187
+ {
188
+ code: "WF_MISSING_FILE",
189
+ severity: "ERROR",
190
+ path: "file",
191
+ message: `Missing last-known-good workflow snapshot at ${paths.lastKnownGoodPath}`,
192
+ },
193
+ ],
194
+ };
195
+ }
196
+ const parsed = parseWorkflowMarkdown(lkgText, paths.lastKnownGoodPath);
197
+ const configLoaded = await loadConfig(path.join(repoRoot, ".agentplane"));
198
+ const validated = validateWorkflowDocument(parsed.document, {
199
+ repoRoot,
200
+ knownAgentIds: await listAgentIds(path.join(repoRoot, ".agentplane")),
201
+ config: configLoaded.config,
202
+ });
203
+ const diagnostics = [...parsed.diagnostics, ...validated.diagnostics];
204
+ if (diagnostics.some((d) => d.severity === "ERROR")) {
205
+ emitWorkflowEvent({
206
+ event: "workflow_restore_failed",
207
+ code: "WF_PARSE_ERROR",
208
+ details: { reason: "last-known-good validation failed" },
209
+ });
210
+ return { ok: false, diagnostics };
211
+ }
212
+ try {
213
+ await fs.mkdir(path.dirname(paths.workflowPath), { recursive: true });
214
+ await fs.writeFile(tempPath, lkgText, "utf8");
215
+ await fs.rename(tempPath, paths.workflowPath);
216
+ await removeLegacyWorkflowIfPresent(paths);
217
+ emitWorkflowEvent({
218
+ event: "workflow_restore_completed",
219
+ details: { workflowPath: paths.workflowPath, from: paths.lastKnownGoodPath },
220
+ });
221
+ return { ok: true, diagnostics };
222
+ }
223
+ catch (error) {
224
+ try {
225
+ await fs.rm(tempPath, { force: true });
226
+ }
227
+ catch {
228
+ // best effort cleanup
229
+ }
230
+ emitWorkflowEvent({
231
+ event: "workflow_restore_failed",
232
+ code: "WF_READ_FAILED",
233
+ details: { reason: error instanceof Error ? error.message : String(error) },
234
+ });
235
+ return {
236
+ ok: false,
237
+ diagnostics: [
238
+ ...diagnostics,
239
+ {
240
+ code: "WF_READ_FAILED",
241
+ severity: "ERROR",
242
+ path: "file",
243
+ message: `Failed to restore workflow from snapshot: ${error instanceof Error ? error.message : String(error)}`,
244
+ },
245
+ ],
246
+ };
247
+ }
248
+ }
@@ -0,0 +1,9 @@
1
+ import type { WorkflowDiagnostic } from "./types.js";
2
+ type WorkflowFixResult = {
3
+ changed: boolean;
4
+ text: string;
5
+ diagnostics: WorkflowDiagnostic[];
6
+ };
7
+ export declare function safeAutofixWorkflowText(text: string): WorkflowFixResult;
8
+ export {};
9
+ //# sourceMappingURL=fix.d.ts.map