@caupulican/pi-adaptative 0.93.3 → 0.93.4

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 (165) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/core/agent-session.d.ts.map +1 -1
  3. package/dist/core/agent-session.js +15 -9
  4. package/dist/core/agent-session.js.map +1 -1
  5. package/dist/core/autonomy/approval-gate.d.ts +1 -0
  6. package/dist/core/autonomy/approval-gate.d.ts.map +1 -1
  7. package/dist/core/autonomy/approval-gate.js +4 -1
  8. package/dist/core/autonomy/approval-gate.js.map +1 -1
  9. package/dist/core/autonomy/composite-tool-gate.d.ts +13 -0
  10. package/dist/core/autonomy/composite-tool-gate.d.ts.map +1 -0
  11. package/dist/core/autonomy/composite-tool-gate.js +44 -0
  12. package/dist/core/autonomy/composite-tool-gate.js.map +1 -0
  13. package/dist/core/autonomy/envelope-enforcement.d.ts +2 -0
  14. package/dist/core/autonomy/envelope-enforcement.d.ts.map +1 -1
  15. package/dist/core/autonomy/envelope-enforcement.js +48 -7
  16. package/dist/core/autonomy/envelope-enforcement.js.map +1 -1
  17. package/dist/core/autonomy/gates.d.ts.map +1 -1
  18. package/dist/core/autonomy/gates.js +48 -76
  19. package/dist/core/autonomy/gates.js.map +1 -1
  20. package/dist/core/autonomy/path-scope.d.ts.map +1 -1
  21. package/dist/core/autonomy/path-scope.js +7 -7
  22. package/dist/core/autonomy/path-scope.js.map +1 -1
  23. package/dist/core/background-tool-task-controller.d.ts +2 -0
  24. package/dist/core/background-tool-task-controller.d.ts.map +1 -1
  25. package/dist/core/background-tool-task-controller.js +13 -5
  26. package/dist/core/background-tool-task-controller.js.map +1 -1
  27. package/dist/core/compaction-controller.d.ts +3 -0
  28. package/dist/core/compaction-controller.d.ts.map +1 -1
  29. package/dist/core/compaction-controller.js +132 -97
  30. package/dist/core/compaction-controller.js.map +1 -1
  31. package/dist/core/delegation/session-root-mailbox.d.ts.map +1 -1
  32. package/dist/core/delegation/session-root-mailbox.js +14 -1
  33. package/dist/core/delegation/session-root-mailbox.js.map +1 -1
  34. package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -1
  35. package/dist/core/delegation/worker-agent-control-coordinator.js +6 -9
  36. package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -1
  37. package/dist/core/delegation/worker-authority-resolver.d.ts.map +1 -1
  38. package/dist/core/delegation/worker-authority-resolver.js +3 -2
  39. package/dist/core/delegation/worker-authority-resolver.js.map +1 -1
  40. package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -1
  41. package/dist/core/delegation/worker-execution-policy.js +10 -8
  42. package/dist/core/delegation/worker-execution-policy.js.map +1 -1
  43. package/dist/core/foreground-recovery-controller.d.ts +2 -0
  44. package/dist/core/foreground-recovery-controller.d.ts.map +1 -1
  45. package/dist/core/foreground-recovery-controller.js +18 -9
  46. package/dist/core/foreground-recovery-controller.js.map +1 -1
  47. package/dist/core/goal-loop-controller.d.ts.map +1 -1
  48. package/dist/core/goal-loop-controller.js +3 -5
  49. package/dist/core/goal-loop-controller.js.map +1 -1
  50. package/dist/core/goals/goal-auto-continue-controller.d.ts.map +1 -1
  51. package/dist/core/goals/goal-auto-continue-controller.js +6 -0
  52. package/dist/core/goals/goal-auto-continue-controller.js.map +1 -1
  53. package/dist/core/goals/goal-state.d.ts.map +1 -1
  54. package/dist/core/goals/goal-state.js +1 -0
  55. package/dist/core/goals/goal-state.js.map +1 -1
  56. package/dist/core/goals/goal-tool-core.d.ts +2 -0
  57. package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
  58. package/dist/core/goals/goal-tool-core.js +1 -1
  59. package/dist/core/goals/goal-tool-core.js.map +1 -1
  60. package/dist/core/orchestration/capability-gateway.d.ts.map +1 -1
  61. package/dist/core/orchestration/capability-gateway.js +32 -9
  62. package/dist/core/orchestration/capability-gateway.js.map +1 -1
  63. package/dist/core/orchestration/lane-tool-manifests.js +5 -5
  64. package/dist/core/orchestration/lane-tool-manifests.js.map +1 -1
  65. package/dist/core/orchestration/policy-compiler.d.ts.map +1 -1
  66. package/dist/core/orchestration/policy-compiler.js +7 -0
  67. package/dist/core/orchestration/policy-compiler.js.map +1 -1
  68. package/dist/core/orchestration/profile-registry.js +2 -2
  69. package/dist/core/orchestration/profile-registry.js.map +1 -1
  70. package/dist/core/pipelines/active-context.d.ts +16 -0
  71. package/dist/core/pipelines/active-context.d.ts.map +1 -0
  72. package/dist/core/pipelines/active-context.js +29 -0
  73. package/dist/core/pipelines/active-context.js.map +1 -0
  74. package/dist/core/pipelines/context.d.ts +1 -1
  75. package/dist/core/pipelines/context.d.ts.map +1 -1
  76. package/dist/core/pipelines/context.js +40 -31
  77. package/dist/core/pipelines/context.js.map +1 -1
  78. package/dist/core/pipelines/discover.d.ts +5 -1
  79. package/dist/core/pipelines/discover.d.ts.map +1 -1
  80. package/dist/core/pipelines/discover.js +91 -33
  81. package/dist/core/pipelines/discover.js.map +1 -1
  82. package/dist/core/pipelines/increment.d.ts +0 -2
  83. package/dist/core/pipelines/increment.d.ts.map +1 -1
  84. package/dist/core/pipelines/increment.js +35 -13
  85. package/dist/core/pipelines/increment.js.map +1 -1
  86. package/dist/core/pipelines/index.d.ts +4 -3
  87. package/dist/core/pipelines/index.d.ts.map +1 -1
  88. package/dist/core/pipelines/index.js +4 -3
  89. package/dist/core/pipelines/index.js.map +1 -1
  90. package/dist/core/pipelines/run-state.d.ts +8 -0
  91. package/dist/core/pipelines/run-state.d.ts.map +1 -1
  92. package/dist/core/pipelines/run-state.js +172 -38
  93. package/dist/core/pipelines/run-state.js.map +1 -1
  94. package/dist/core/pipelines/types.d.ts +2 -0
  95. package/dist/core/pipelines/types.d.ts.map +1 -1
  96. package/dist/core/pipelines/types.js +21 -4
  97. package/dist/core/pipelines/types.js.map +1 -1
  98. package/dist/core/runtime-builder.d.ts +2 -1
  99. package/dist/core/runtime-builder.d.ts.map +1 -1
  100. package/dist/core/runtime-builder.js +46 -10
  101. package/dist/core/runtime-builder.js.map +1 -1
  102. package/dist/core/system-prompt-builder.d.ts +1 -0
  103. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  104. package/dist/core/system-prompt-builder.js +25 -1
  105. package/dist/core/system-prompt-builder.js.map +1 -1
  106. package/dist/core/system-prompt.d.ts.map +1 -1
  107. package/dist/core/system-prompt.js +6 -1
  108. package/dist/core/system-prompt.js.map +1 -1
  109. package/dist/core/tasks/task-projection.d.ts +2 -0
  110. package/dist/core/tasks/task-projection.d.ts.map +1 -1
  111. package/dist/core/tasks/task-projection.js +2 -0
  112. package/dist/core/tasks/task-projection.js.map +1 -1
  113. package/dist/core/tasks/task-state.d.ts +10 -0
  114. package/dist/core/tasks/task-state.d.ts.map +1 -1
  115. package/dist/core/tasks/task-state.js +43 -2
  116. package/dist/core/tasks/task-state.js.map +1 -1
  117. package/dist/core/tool-capability-policy.d.ts +13 -3
  118. package/dist/core/tool-capability-policy.d.ts.map +1 -1
  119. package/dist/core/tool-capability-policy.js +136 -28
  120. package/dist/core/tool-capability-policy.js.map +1 -1
  121. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  122. package/dist/core/tools/edit-diff.js +45 -7
  123. package/dist/core/tools/edit-diff.js.map +1 -1
  124. package/dist/core/tools/edit.d.ts.map +1 -1
  125. package/dist/core/tools/edit.js +4 -0
  126. package/dist/core/tools/edit.js.map +1 -1
  127. package/dist/core/tools/goal.d.ts.map +1 -1
  128. package/dist/core/tools/goal.js +17 -1
  129. package/dist/core/tools/goal.js.map +1 -1
  130. package/dist/core/tools/orchestration-panel.d.ts.map +1 -1
  131. package/dist/core/tools/orchestration-panel.js +1 -0
  132. package/dist/core/tools/orchestration-panel.js.map +1 -1
  133. package/dist/core/tools/persistent-process-coordinator.d.ts.map +1 -1
  134. package/dist/core/tools/persistent-process-coordinator.js +12 -7
  135. package/dist/core/tools/persistent-process-coordinator.js.map +1 -1
  136. package/dist/core/tools/pipeline.d.ts +1 -0
  137. package/dist/core/tools/pipeline.d.ts.map +1 -1
  138. package/dist/core/tools/pipeline.js +95 -44
  139. package/dist/core/tools/pipeline.js.map +1 -1
  140. package/dist/core/tools/secret-store.d.ts.map +1 -1
  141. package/dist/core/tools/secret-store.js +2 -1
  142. package/dist/core/tools/secret-store.js.map +1 -1
  143. package/dist/core/tools/shell-session.d.ts.map +1 -1
  144. package/dist/core/tools/shell-session.js +6 -1
  145. package/dist/core/tools/shell-session.js.map +1 -1
  146. package/dist/core/tools/task-steps.d.ts +10 -0
  147. package/dist/core/tools/task-steps.d.ts.map +1 -1
  148. package/dist/core/tools/task-steps.js +70 -29
  149. package/dist/core/tools/task-steps.js.map +1 -1
  150. package/dist/core/tools/windows-shell-engine.d.ts.map +1 -1
  151. package/dist/core/tools/windows-shell-engine.js +6 -1
  152. package/dist/core/tools/windows-shell-engine.js.map +1 -1
  153. package/dist/core/util/bounded-file.d.ts +8 -0
  154. package/dist/core/util/bounded-file.d.ts.map +1 -1
  155. package/dist/core/util/bounded-file.js +81 -16
  156. package/dist/core/util/bounded-file.js.map +1 -1
  157. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  158. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  159. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  160. package/examples/extensions/sandbox/package-lock.json +2 -2
  161. package/examples/extensions/sandbox/package.json +1 -1
  162. package/examples/extensions/with-deps/package-lock.json +2 -2
  163. package/examples/extensions/with-deps/package.json +1 -1
  164. package/npm-shrinkwrap.json +12 -12
  165. package/package.json +4 -4
@@ -0,0 +1,29 @@
1
+ import { createCustomMessage } from "@caupulican/pi-agent-core";
2
+ import { formatActivePipelineContext } from "./context.js";
3
+ import { resolvePipelineDefinitionForRun } from "./discover.js";
4
+ import { resolveCurrentProjectPipelineRun } from "./run-state.js";
5
+ /** Resolve durable run identity, definition ownership, and bounded prompt text through one path. */
6
+ export function resolveActivePipelineContext(options, snapshot) {
7
+ const run = resolveCurrentProjectPipelineRun(options.cwd, snapshot);
8
+ if (!run)
9
+ return undefined;
10
+ const definition = resolvePipelineDefinitionForRun(options, run);
11
+ if (!definition)
12
+ return undefined;
13
+ const text = formatActivePipelineContext(definition, run);
14
+ return text ? { run, text } : undefined;
15
+ }
16
+ /** Build the optional per-turn pipeline message without leaking discovery failures into prompt admission. */
17
+ export function createActivePipelineContextMessage(args) {
18
+ try {
19
+ const context = resolveActivePipelineContext(args.options, args.snapshot);
20
+ return context
21
+ ? createCustomMessage("pipeline_context", context.text, false, { revision: context.run.revision }, new Date().toISOString())
22
+ : undefined;
23
+ }
24
+ catch (error) {
25
+ args.onError(`Pipeline context unavailable: ${error instanceof Error ? error.message : String(error)}`);
26
+ return undefined;
27
+ }
28
+ }
29
+ //# sourceMappingURL=active-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"active-context.js","sourceRoot":"","sources":["../../../src/core/pipelines/active-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAgC,+BAA+B,EAAE,MAAM,eAAe,CAAC;AAC9F,OAAO,EAAE,gCAAgC,EAAE,MAAM,gBAAgB,CAAC;AAQlE,oGAAoG;AACpG,MAAM,UAAU,4BAA4B,CAC3C,OAAgC,EAChC,QAAsB;IAEtB,MAAM,GAAG,GAAG,gCAAgC,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACpE,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,MAAM,UAAU,GAAG,+BAA+B,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACjE,IAAI,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IAClC,MAAM,IAAI,GAAG,2BAA2B,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IAC1D,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACzC,CAAC;AAED,6GAA6G;AAC7G,MAAM,UAAU,kCAAkC,CAAC,IAIlD;IACA,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,4BAA4B,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1E,OAAO,OAAO;YACb,CAAC,CAAC,mBAAmB,CACnB,kBAAkB,EAClB,OAAO,CAAC,IAAI,EACZ,KAAK,EACL,EAAE,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,EAClC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CACxB;YACF,CAAC,CAAC,SAAS,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,CAAC,OAAO,CAAC,iCAAiC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACxG,OAAO,SAAS,CAAC;IAClB,CAAC;AACF,CAAC","sourcesContent":["import { createCustomMessage } from \"@caupulican/pi-agent-core\";\nimport { formatActivePipelineContext } from \"./context.ts\";\nimport { type DiscoverPipelineOptions, resolvePipelineDefinitionForRun } from \"./discover.ts\";\nimport { resolveCurrentProjectPipelineRun } from \"./run-state.ts\";\nimport type { PipelineRun } from \"./types.ts\";\n\nexport interface ActivePipelineContext {\n\trun: PipelineRun;\n\ttext: string;\n}\n\n/** Resolve durable run identity, definition ownership, and bounded prompt text through one path. */\nexport function resolveActivePipelineContext(\n\toptions: DiscoverPipelineOptions,\n\tsnapshot?: PipelineRun,\n): ActivePipelineContext | undefined {\n\tconst run = resolveCurrentProjectPipelineRun(options.cwd, snapshot);\n\tif (!run) return undefined;\n\tconst definition = resolvePipelineDefinitionForRun(options, run);\n\tif (!definition) return undefined;\n\tconst text = formatActivePipelineContext(definition, run);\n\treturn text ? { run, text } : undefined;\n}\n\n/** Build the optional per-turn pipeline message without leaking discovery failures into prompt admission. */\nexport function createActivePipelineContextMessage(args: {\n\toptions: DiscoverPipelineOptions;\n\tsnapshot?: PipelineRun;\n\tonError(message: string): void;\n}): ReturnType<typeof createCustomMessage> | undefined {\n\ttry {\n\t\tconst context = resolveActivePipelineContext(args.options, args.snapshot);\n\t\treturn context\n\t\t\t? createCustomMessage(\n\t\t\t\t\t\"pipeline_context\",\n\t\t\t\t\tcontext.text,\n\t\t\t\t\tfalse,\n\t\t\t\t\t{ revision: context.run.revision },\n\t\t\t\t\tnew Date().toISOString(),\n\t\t\t\t)\n\t\t\t: undefined;\n\t} catch (error) {\n\t\targs.onError(`Pipeline context unavailable: ${error instanceof Error ? error.message : String(error)}`);\n\t\treturn undefined;\n\t}\n}\n"]}
@@ -2,5 +2,5 @@ import { type AssembledStageContext, type PipelineDefinition, type PipelineRun,
2
2
  export declare function currentPipelineStage(definition: PipelineDefinition, run: PipelineRun): PipelineStage | undefined;
3
3
  export declare function assembleStageContext(definition: PipelineDefinition, run: PipelineRun, stage?: PipelineStage): AssembledStageContext;
4
4
  export declare function formatPipelineContext(definition: PipelineDefinition, run: PipelineRun, assembled: AssembledStageContext): string;
5
- export declare function formatActivePipelineContext(run: PipelineRun): string | undefined;
5
+ export declare function formatActivePipelineContext(definition: PipelineDefinition, run: PipelineRun): string | undefined;
6
6
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/core/pipelines/context.ts"],"names":[],"mappings":"AAGA,OAAO,EAEN,KAAK,qBAAqB,EAK1B,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,MAAM,YAAY,CAAC;AA+CpB,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,kBAAkB,EAAE,GAAG,EAAE,WAAW,GAAG,aAAa,GAAG,SAAS,CAEhH;AAED,wBAAgB,oBAAoB,CACnC,UAAU,EAAE,kBAAkB,EAC9B,GAAG,EAAE,WAAW,EAChB,KAAK,GAAE,aAA8E,GACnF,qBAAqB,CAiCvB;AAED,wBAAgB,qBAAqB,CACpC,UAAU,EAAE,kBAAkB,EAC9B,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,qBAAqB,GAC9B,MAAM,CAWR;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAKhF"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/core/pipelines/context.ts"],"names":[],"mappings":"AAGA,OAAO,EAEN,KAAK,qBAAqB,EAK1B,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,MAAM,YAAY,CAAC;AAqDpB,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,kBAAkB,EAAE,GAAG,EAAE,WAAW,GAAG,aAAa,GAAG,SAAS,CAEhH;AAED,wBAAgB,oBAAoB,CACnC,UAAU,EAAE,kBAAkB,EAC9B,GAAG,EAAE,WAAW,EAChB,KAAK,CAAC,EAAE,aAAa,GACnB,qBAAqB,CAuCvB;AAED,wBAAgB,qBAAqB,CACpC,UAAU,EAAE,kBAAkB,EAC9B,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,qBAAqB,GAC9B,MAAM,CAWR;AAED,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,kBAAkB,EAAE,GAAG,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAGhH"}
@@ -1,70 +1,82 @@
1
- import { existsSync, readFileSync, statSync } from "node:fs";
2
- import { isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
3
- import { loadPipelineDefinition } from "./discover.js";
1
+ import { isAbsolute, join, normalize, resolve } from "node:path";
2
+ import { isPathWithinScope, safeRealpathSync } from "../autonomy/path-scope.js";
3
+ import { readFilePrefixSync } from "../util/bounded-file.js";
4
4
  import { isPipelineRunActive, MAX_LAYER_FILE_CHARS, MAX_STAGE_CONTEXT_CHARS, } from "./types.js";
5
5
  const CHARS_PER_TOKEN = 4;
6
- function isInsideRoot(root, candidate) {
7
- const rel = relative(resolve(root), resolve(candidate));
8
- return rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel));
9
- }
10
6
  function resolveUnder(baseDir, sandboxRoot, rawPath) {
11
7
  const trimmed = rawPath.trim();
12
8
  if (!trimmed)
13
9
  return undefined;
14
- const resolved = resolve(isAbsolute(trimmed) ? trimmed : join(baseDir, trimmed));
15
- if (!isInsideRoot(sandboxRoot, resolved))
10
+ try {
11
+ const lexicalPath = resolve(isAbsolute(trimmed) ? trimmed : join(baseDir, trimmed));
12
+ if (!isPathWithinScope(lexicalPath, resolve(sandboxRoot)))
13
+ return undefined;
14
+ const resolved = safeRealpathSync(lexicalPath);
15
+ if (!isPathWithinScope(resolved, safeRealpathSync(sandboxRoot)))
16
+ return undefined;
17
+ return normalize(resolved);
18
+ }
19
+ catch {
16
20
  return undefined;
17
- return normalize(resolved);
21
+ }
18
22
  }
19
- function readBounded(path, maxChars) {
23
+ function readBounded(path, scopeRoot, maxChars) {
20
24
  try {
21
- if (!existsSync(path) || !statSync(path).isFile()) {
25
+ const resolvedPath = safeRealpathSync(path);
26
+ if (!isPathWithinScope(resolvedPath, safeRealpathSync(scopeRoot))) {
22
27
  return { text: `(missing: ${path})`, truncated: false };
23
28
  }
24
- const raw = readFileSync(path, "utf8");
25
- if (raw.length <= maxChars)
29
+ const maxBytes = Math.max(1, maxChars * 4);
30
+ const prefix = readFilePrefixSync(resolvedPath, maxBytes, "Pipeline context file");
31
+ const raw = prefix.content.toString("utf8");
32
+ const truncated = prefix.truncated || raw.length > maxChars;
33
+ if (!truncated)
26
34
  return { text: raw, truncated: false };
27
- return { text: `${raw.slice(0, maxChars)}\n…[truncated ${raw.length - maxChars} chars]`, truncated: true };
35
+ return { text: `${raw.slice(0, maxChars)}\n…[truncated]`, truncated: true };
28
36
  }
29
37
  catch (error) {
30
38
  return { text: `(unreadable: ${error instanceof Error ? error.message : String(error)})`, truncated: false };
31
39
  }
32
40
  }
33
- function pushLayer(layers, layer, path, label, remaining) {
41
+ function pushLayer(layers, layer, path, scopeRoot, label, remaining) {
34
42
  if (remaining <= 0) {
35
43
  layers.push({ layer, path, label, text: "(omitted: stage context budget)", truncated: true });
36
44
  return 0;
37
45
  }
38
46
  const budget = Math.min(MAX_LAYER_FILE_CHARS, remaining);
39
- const read = readBounded(path, budget);
47
+ const read = readBounded(path, scopeRoot, budget);
40
48
  layers.push({ layer, path, label, text: read.text, truncated: read.truncated });
41
49
  return remaining - read.text.length;
42
50
  }
43
51
  export function currentPipelineStage(definition, run) {
44
52
  return definition.stages.find((stage) => stage.id === run.currentStageId);
45
53
  }
46
- export function assembleStageContext(definition, run, stage = currentPipelineStage(definition, run) ?? definition.stages[0]) {
54
+ export function assembleStageContext(definition, run, stage) {
55
+ const currentStage = stage ?? currentPipelineStage(definition, run);
56
+ if (!currentStage) {
57
+ throw new Error(`Current pipeline stage '${run.currentStageId}' is missing from definition '${definition.name}'.`);
58
+ }
47
59
  const layers = [];
48
60
  let remaining = MAX_STAGE_CONTEXT_CHARS;
49
- remaining = pushLayer(layers, 0, definition.entryFile, "L0 entry", remaining);
61
+ remaining = pushLayer(layers, 0, definition.entryFile, definition.rootDir, "L0 entry", remaining);
50
62
  if (definition.routingPath !== definition.entryFile) {
51
- remaining = pushLayer(layers, 1, definition.routingPath, "L1 routing", remaining);
63
+ remaining = pushLayer(layers, 1, definition.routingPath, definition.rootDir, "L1 routing", remaining);
52
64
  }
53
- remaining = pushLayer(layers, 2, stage.contractPath, "L2 stage contract", remaining);
54
- const definitionStageDir = join(definition.stagesDir, stage.folderName);
55
- const runStageDir = join(run.runRoot, stage.folderName);
56
- for (const input of stage.contract.inputs) {
65
+ remaining = pushLayer(layers, 2, currentStage.contractPath, definition.rootDir, "L2 stage contract", remaining);
66
+ const definitionStageDir = join(definition.stagesDir, currentStage.folderName);
67
+ const runStageDir = join(run.runRoot, currentStage.folderName);
68
+ for (const input of currentStage.contract.inputs) {
57
69
  if (input.kind === "reference") {
58
70
  const path = resolveUnder(definitionStageDir, definition.rootDir, input.path);
59
71
  if (!path)
60
72
  continue;
61
- remaining = pushLayer(layers, 3, path, `L3 ${input.path}`, remaining);
73
+ remaining = pushLayer(layers, 3, path, definition.rootDir, `L3 ${input.path}`, remaining);
62
74
  }
63
75
  else {
64
76
  const path = resolveUnder(runStageDir, run.runRoot, input.path);
65
77
  if (!path)
66
78
  continue;
67
- remaining = pushLayer(layers, 4, path, `L4 ${input.path}`, remaining);
79
+ remaining = pushLayer(layers, 4, path, run.runRoot, `L4 ${input.path}`, remaining);
68
80
  }
69
81
  }
70
82
  const text = layers
@@ -72,7 +84,7 @@ export function assembleStageContext(definition, run, stage = currentPipelineSta
72
84
  .join("\n\n")
73
85
  .slice(0, MAX_STAGE_CONTEXT_CHARS);
74
86
  return {
75
- stageId: stage.id,
87
+ stageId: currentStage.id,
76
88
  layers,
77
89
  text,
78
90
  tokenEstimate: Math.ceil(text.length / CHARS_PER_TOKEN),
@@ -90,12 +102,9 @@ export function formatPipelineContext(definition, run, assembled) {
90
102
  ].filter((line) => line.length > 0);
91
103
  return `${PIPELINE_CONTEXT_OPEN} revision=${run.revision}>\n${lines.join("\n")}\n</pipeline_context>`;
92
104
  }
93
- export function formatActivePipelineContext(run) {
105
+ export function formatActivePipelineContext(definition, run) {
94
106
  if (!isPipelineRunActive(run))
95
107
  return undefined;
96
- const definition = loadPipelineDefinition(run.definitionPath);
97
- if (!definition)
98
- return undefined;
99
108
  return formatPipelineContext(definition, run, assembleStageContext(definition, run));
100
109
  }
101
110
  const PIPELINE_CONTEXT_OPEN = "<pipeline_context";
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/core/pipelines/context.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { isAbsolute, join, normalize, relative, resolve, sep } from \"node:path\";\nimport { loadPipelineDefinition } from \"./discover.ts\";\nimport {\n\ttype AssembledLayer,\n\ttype AssembledStageContext,\n\ttype ContextLayer,\n\tisPipelineRunActive,\n\tMAX_LAYER_FILE_CHARS,\n\tMAX_STAGE_CONTEXT_CHARS,\n\ttype PipelineDefinition,\n\ttype PipelineRun,\n\ttype PipelineStage,\n} from \"./types.ts\";\n\nconst CHARS_PER_TOKEN = 4;\n\nfunction isInsideRoot(root: string, candidate: string): boolean {\n\tconst rel = relative(resolve(root), resolve(candidate));\n\treturn rel === \"\" || (!rel.startsWith(`..${sep}`) && rel !== \"..\" && !isAbsolute(rel));\n}\n\nfunction resolveUnder(baseDir: string, sandboxRoot: string, rawPath: string): string | undefined {\n\tconst trimmed = rawPath.trim();\n\tif (!trimmed) return undefined;\n\tconst resolved = resolve(isAbsolute(trimmed) ? trimmed : join(baseDir, trimmed));\n\tif (!isInsideRoot(sandboxRoot, resolved)) return undefined;\n\treturn normalize(resolved);\n}\n\nfunction readBounded(path: string, maxChars: number): { text: string; truncated: boolean } {\n\ttry {\n\t\tif (!existsSync(path) || !statSync(path).isFile()) {\n\t\t\treturn { text: `(missing: ${path})`, truncated: false };\n\t\t}\n\t\tconst raw = readFileSync(path, \"utf8\");\n\t\tif (raw.length <= maxChars) return { text: raw, truncated: false };\n\t\treturn { text: `${raw.slice(0, maxChars)}\\n…[truncated ${raw.length - maxChars} chars]`, truncated: true };\n\t} catch (error) {\n\t\treturn { text: `(unreadable: ${error instanceof Error ? error.message : String(error)})`, truncated: false };\n\t}\n}\n\nfunction pushLayer(\n\tlayers: AssembledLayer[],\n\tlayer: ContextLayer,\n\tpath: string,\n\tlabel: string,\n\tremaining: number,\n): number {\n\tif (remaining <= 0) {\n\t\tlayers.push({ layer, path, label, text: \"(omitted: stage context budget)\", truncated: true });\n\t\treturn 0;\n\t}\n\tconst budget = Math.min(MAX_LAYER_FILE_CHARS, remaining);\n\tconst read = readBounded(path, budget);\n\tlayers.push({ layer, path, label, text: read.text, truncated: read.truncated });\n\treturn remaining - read.text.length;\n}\n\nexport function currentPipelineStage(definition: PipelineDefinition, run: PipelineRun): PipelineStage | undefined {\n\treturn definition.stages.find((stage) => stage.id === run.currentStageId);\n}\n\nexport function assembleStageContext(\n\tdefinition: PipelineDefinition,\n\trun: PipelineRun,\n\tstage: PipelineStage = currentPipelineStage(definition, run) ?? definition.stages[0]!,\n): AssembledStageContext {\n\tconst layers: AssembledLayer[] = [];\n\tlet remaining = MAX_STAGE_CONTEXT_CHARS;\n\tremaining = pushLayer(layers, 0, definition.entryFile, \"L0 entry\", remaining);\n\tif (definition.routingPath !== definition.entryFile) {\n\t\tremaining = pushLayer(layers, 1, definition.routingPath, \"L1 routing\", remaining);\n\t}\n\tremaining = pushLayer(layers, 2, stage.contractPath, \"L2 stage contract\", remaining);\n\n\tconst definitionStageDir = join(definition.stagesDir, stage.folderName);\n\tconst runStageDir = join(run.runRoot, stage.folderName);\n\tfor (const input of stage.contract.inputs) {\n\t\tif (input.kind === \"reference\") {\n\t\t\tconst path = resolveUnder(definitionStageDir, definition.rootDir, input.path);\n\t\t\tif (!path) continue;\n\t\t\tremaining = pushLayer(layers, 3, path, `L3 ${input.path}`, remaining);\n\t\t} else {\n\t\t\tconst path = resolveUnder(runStageDir, run.runRoot, input.path);\n\t\t\tif (!path) continue;\n\t\t\tremaining = pushLayer(layers, 4, path, `L4 ${input.path}`, remaining);\n\t\t}\n\t}\n\n\tconst text = layers\n\t\t.map((item) => `### ${item.label}\\npath: ${item.path}\\n${item.text}`)\n\t\t.join(\"\\n\\n\")\n\t\t.slice(0, MAX_STAGE_CONTEXT_CHARS);\n\treturn {\n\t\tstageId: stage.id,\n\t\tlayers,\n\t\ttext,\n\t\ttokenEstimate: Math.ceil(text.length / CHARS_PER_TOKEN),\n\t};\n}\n\nexport function formatPipelineContext(\n\tdefinition: PipelineDefinition,\n\trun: PipelineRun,\n\tassembled: AssembledStageContext,\n): string {\n\tconst stage = currentPipelineStage(definition, run);\n\tconst humanCheck = stage?.contract.humanCheck.trim();\n\tconst lines = [\n\t\t`PIPELINE ${definition.name} run=${run.runId} status=${run.status} stage=${run.currentStageId}`,\n\t\tstage ? `ONE JOB: ${stage.contract.oneJob || stage.contract.title || stage.id}` : \"\",\n\t\thumanCheck ? `HUMAN CHECK: ${humanCheck}` : \"\",\n\t\t`Increment completes this stage after output/ has files, then starts the next. Status is the files on disk.`,\n\t\tassembled.text,\n\t].filter((line) => line.length > 0);\n\treturn `${PIPELINE_CONTEXT_OPEN} revision=${run.revision}>\\n${lines.join(\"\\n\")}\\n</pipeline_context>`;\n}\n\nexport function formatActivePipelineContext(run: PipelineRun): string | undefined {\n\tif (!isPipelineRunActive(run)) return undefined;\n\tconst definition = loadPipelineDefinition(run.definitionPath);\n\tif (!definition) return undefined;\n\treturn formatPipelineContext(definition, run, assembleStageContext(definition, run));\n}\n\nconst PIPELINE_CONTEXT_OPEN = \"<pipeline_context\";\n"]}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/core/pipelines/context.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAbsolute, join, normalize, resolve } from \"node:path\";\nimport { isPathWithinScope, safeRealpathSync } from \"../autonomy/path-scope.ts\";\nimport { readFilePrefixSync } from \"../util/bounded-file.ts\";\nimport {\n\ttype AssembledLayer,\n\ttype AssembledStageContext,\n\ttype ContextLayer,\n\tisPipelineRunActive,\n\tMAX_LAYER_FILE_CHARS,\n\tMAX_STAGE_CONTEXT_CHARS,\n\ttype PipelineDefinition,\n\ttype PipelineRun,\n\ttype PipelineStage,\n} from \"./types.ts\";\n\nconst CHARS_PER_TOKEN = 4;\n\nfunction resolveUnder(baseDir: string, sandboxRoot: string, rawPath: string): string | undefined {\n\tconst trimmed = rawPath.trim();\n\tif (!trimmed) return undefined;\n\ttry {\n\t\tconst lexicalPath = resolve(isAbsolute(trimmed) ? trimmed : join(baseDir, trimmed));\n\t\tif (!isPathWithinScope(lexicalPath, resolve(sandboxRoot))) return undefined;\n\t\tconst resolved = safeRealpathSync(lexicalPath);\n\t\tif (!isPathWithinScope(resolved, safeRealpathSync(sandboxRoot))) return undefined;\n\t\treturn normalize(resolved);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction readBounded(path: string, scopeRoot: string, maxChars: number): { text: string; truncated: boolean } {\n\ttry {\n\t\tconst resolvedPath = safeRealpathSync(path);\n\t\tif (!isPathWithinScope(resolvedPath, safeRealpathSync(scopeRoot))) {\n\t\t\treturn { text: `(missing: ${path})`, truncated: false };\n\t\t}\n\t\tconst maxBytes = Math.max(1, maxChars * 4);\n\t\tconst prefix = readFilePrefixSync(resolvedPath, maxBytes, \"Pipeline context file\");\n\t\tconst raw = prefix.content.toString(\"utf8\");\n\t\tconst truncated = prefix.truncated || raw.length > maxChars;\n\t\tif (!truncated) return { text: raw, truncated: false };\n\t\treturn { text: `${raw.slice(0, maxChars)}\\n…[truncated]`, truncated: true };\n\t} catch (error) {\n\t\treturn { text: `(unreadable: ${error instanceof Error ? error.message : String(error)})`, truncated: false };\n\t}\n}\n\nfunction pushLayer(\n\tlayers: AssembledLayer[],\n\tlayer: ContextLayer,\n\tpath: string,\n\tscopeRoot: string,\n\tlabel: string,\n\tremaining: number,\n): number {\n\tif (remaining <= 0) {\n\t\tlayers.push({ layer, path, label, text: \"(omitted: stage context budget)\", truncated: true });\n\t\treturn 0;\n\t}\n\tconst budget = Math.min(MAX_LAYER_FILE_CHARS, remaining);\n\tconst read = readBounded(path, scopeRoot, budget);\n\tlayers.push({ layer, path, label, text: read.text, truncated: read.truncated });\n\treturn remaining - read.text.length;\n}\n\nexport function currentPipelineStage(definition: PipelineDefinition, run: PipelineRun): PipelineStage | undefined {\n\treturn definition.stages.find((stage) => stage.id === run.currentStageId);\n}\n\nexport function assembleStageContext(\n\tdefinition: PipelineDefinition,\n\trun: PipelineRun,\n\tstage?: PipelineStage,\n): AssembledStageContext {\n\tconst currentStage = stage ?? currentPipelineStage(definition, run);\n\tif (!currentStage) {\n\t\tthrow new Error(\n\t\t\t`Current pipeline stage '${run.currentStageId}' is missing from definition '${definition.name}'.`,\n\t\t);\n\t}\n\tconst layers: AssembledLayer[] = [];\n\tlet remaining = MAX_STAGE_CONTEXT_CHARS;\n\tremaining = pushLayer(layers, 0, definition.entryFile, definition.rootDir, \"L0 entry\", remaining);\n\tif (definition.routingPath !== definition.entryFile) {\n\t\tremaining = pushLayer(layers, 1, definition.routingPath, definition.rootDir, \"L1 routing\", remaining);\n\t}\n\tremaining = pushLayer(layers, 2, currentStage.contractPath, definition.rootDir, \"L2 stage contract\", remaining);\n\n\tconst definitionStageDir = join(definition.stagesDir, currentStage.folderName);\n\tconst runStageDir = join(run.runRoot, currentStage.folderName);\n\tfor (const input of currentStage.contract.inputs) {\n\t\tif (input.kind === \"reference\") {\n\t\t\tconst path = resolveUnder(definitionStageDir, definition.rootDir, input.path);\n\t\t\tif (!path) continue;\n\t\t\tremaining = pushLayer(layers, 3, path, definition.rootDir, `L3 ${input.path}`, remaining);\n\t\t} else {\n\t\t\tconst path = resolveUnder(runStageDir, run.runRoot, input.path);\n\t\t\tif (!path) continue;\n\t\t\tremaining = pushLayer(layers, 4, path, run.runRoot, `L4 ${input.path}`, remaining);\n\t\t}\n\t}\n\n\tconst text = layers\n\t\t.map((item) => `### ${item.label}\\npath: ${item.path}\\n${item.text}`)\n\t\t.join(\"\\n\\n\")\n\t\t.slice(0, MAX_STAGE_CONTEXT_CHARS);\n\treturn {\n\t\tstageId: currentStage.id,\n\t\tlayers,\n\t\ttext,\n\t\ttokenEstimate: Math.ceil(text.length / CHARS_PER_TOKEN),\n\t};\n}\n\nexport function formatPipelineContext(\n\tdefinition: PipelineDefinition,\n\trun: PipelineRun,\n\tassembled: AssembledStageContext,\n): string {\n\tconst stage = currentPipelineStage(definition, run);\n\tconst humanCheck = stage?.contract.humanCheck.trim();\n\tconst lines = [\n\t\t`PIPELINE ${definition.name} run=${run.runId} status=${run.status} stage=${run.currentStageId}`,\n\t\tstage ? `ONE JOB: ${stage.contract.oneJob || stage.contract.title || stage.id}` : \"\",\n\t\thumanCheck ? `HUMAN CHECK: ${humanCheck}` : \"\",\n\t\t`Increment completes this stage after output/ has files, then starts the next. Status is the files on disk.`,\n\t\tassembled.text,\n\t].filter((line) => line.length > 0);\n\treturn `${PIPELINE_CONTEXT_OPEN} revision=${run.revision}>\\n${lines.join(\"\\n\")}\\n</pipeline_context>`;\n}\n\nexport function formatActivePipelineContext(definition: PipelineDefinition, run: PipelineRun): string | undefined {\n\tif (!isPipelineRunActive(run)) return undefined;\n\treturn formatPipelineContext(definition, run, assembleStageContext(definition, run));\n}\n\nconst PIPELINE_CONTEXT_OPEN = \"<pipeline_context\";\n"]}
@@ -1,6 +1,8 @@
1
- import { type PipelineDefinition } from "./types.ts";
1
+ import { type PipelineDefinition, type PipelineRun } from "./types.ts";
2
2
  export declare function projectPipelinesDir(cwd: string): string;
3
3
  export declare function projectPipelineRunsDir(cwd: string): string;
4
+ /** Validate one project-owned pipeline storage root without following a managed symlink first. */
5
+ export declare function assertProjectPipelineDirectory(cwd: string, name: "pipelines" | "pipeline-runs"): string;
4
6
  export interface DiscoverPipelineOptions {
5
7
  agentPipelinesDir: string;
6
8
  cwd: string;
@@ -8,4 +10,6 @@ export interface DiscoverPipelineOptions {
8
10
  export declare function loadPipelineDefinition(rootDir: string): PipelineDefinition | undefined;
9
11
  export declare function discoverPipelineDefinitions(options: DiscoverPipelineOptions): PipelineDefinition[];
10
12
  export declare function findPipelineDefinition(options: DiscoverPipelineOptions, name: string): PipelineDefinition | undefined;
13
+ /** Resolve a run's immutable definition path, with the normal discovery roots as a move-safe fallback. */
14
+ export declare function resolvePipelineDefinitionForRun(options: DiscoverPipelineOptions, run: Pick<PipelineRun, "definitionPath" | "pipelineName">): PipelineDefinition | undefined;
11
15
  //# sourceMappingURL=discover.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../../../src/core/pipelines/discover.ts"],"names":[],"mappings":"AAGA,OAAO,EAIN,KAAK,kBAAkB,EAEvB,MAAM,YAAY,CAAC;AAOpB,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,MAAM,WAAW,uBAAuB;IACvC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;CACZ;AAoFD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAqBtF;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,uBAAuB,GAAG,kBAAkB,EAAE,CAelG;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,EAAE,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAIrH"}
1
+ {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../../../src/core/pipelines/discover.ts"],"names":[],"mappings":"AAKA,OAAO,EAIN,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAEhB,MAAM,YAAY,CAAC;AAUpB,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,kGAAkG;AAClG,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,eAAe,GAAG,MAAM,CAsBvG;AAED,MAAM,WAAW,uBAAuB;IACvC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;CACZ;AAyFD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAyBtF;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,uBAAuB,GAAG,kBAAkB,EAAE,CAsBlG;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,EAAE,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAIrH;AAED,0GAA0G;AAC1G,wBAAgB,+BAA+B,CAC9C,OAAO,EAAE,uBAAuB,EAChC,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,gBAAgB,GAAG,cAAc,CAAC,GACvD,kBAAkB,GAAG,SAAS,CAOhC"}
@@ -1,20 +1,57 @@
1
- import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
1
+ import { lstatSync } from "node:fs";
2
2
  import { basename, join } from "node:path";
3
+ import { canonicalPathScopeIdentity, isPathWithinScope, safeRealpathSync } from "../autonomy/path-scope.js";
4
+ import { readBoundedDirectoryNamesSync, readBoundedTextFileSync } from "../util/bounded-file.js";
3
5
  import { parseStageContract, parseWorkspaceFrontmatter } from "./parse-contract.js";
4
6
  import { MAX_PIPELINE_NAME_LENGTH, MAX_PIPELINE_STAGES, PIPELINE_STAGE_FOLDER_RE, } from "./types.js";
5
7
  const ENTRY_FILES = ["AGENTS.md", "CLAUDE.md"];
6
8
  const ROUTING_FILE = "CONTEXT.md";
7
9
  const CONTRACT_FILE = "CONTEXT.md";
8
10
  const FACTORY_DIRS = ["_shared", "references"];
11
+ const MAX_PIPELINE_DEFINITION_FILE_BYTES = 64 * 1024;
12
+ const MAX_PIPELINE_DIRECTORY_ENTRIES = 1_024;
13
+ const MAX_PIPELINE_DEFINITIONS = 64;
9
14
  export function projectPipelinesDir(cwd) {
10
15
  return join(cwd, ".pi", "pipelines");
11
16
  }
12
17
  export function projectPipelineRunsDir(cwd) {
13
18
  return join(cwd, ".pi", "pipeline-runs");
14
19
  }
15
- function readText(path) {
20
+ /** Validate one project-owned pipeline storage root without following a managed symlink first. */
21
+ export function assertProjectPipelineDirectory(cwd, name) {
22
+ const piRoot = join(cwd, ".pi");
23
+ const directory = join(piRoot, name);
24
+ for (const [path, label] of [
25
+ [piRoot, "Project .pi path"],
26
+ [directory, `Project ${name} path`],
27
+ ]) {
28
+ let stats;
29
+ try {
30
+ stats = lstatSync(path);
31
+ }
32
+ catch (error) {
33
+ if (error.code === "ENOENT")
34
+ continue;
35
+ throw error;
36
+ }
37
+ if (!stats.isDirectory() || stats.isSymbolicLink()) {
38
+ throw new Error(`${label} is not a real directory: ${path}`);
39
+ }
40
+ }
41
+ if (!isPathWithinScope(safeRealpathSync(directory), safeRealpathSync(cwd))) {
42
+ throw new Error(`Project ${name} directory escapes the working directory.`);
43
+ }
44
+ return directory;
45
+ }
46
+ function readText(path, rootDir) {
16
47
  try {
17
- return readFileSync(path, "utf8");
48
+ const stats = lstatSync(path);
49
+ if (!stats.isFile() || stats.isSymbolicLink() || stats.size > MAX_PIPELINE_DEFINITION_FILE_BYTES) {
50
+ return undefined;
51
+ }
52
+ if (!isPathWithinScope(safeRealpathSync(path), safeRealpathSync(rootDir)))
53
+ return undefined;
54
+ return readBoundedTextFileSync(path, MAX_PIPELINE_DEFINITION_FILE_BYTES, "Pipeline definition file");
18
55
  }
19
56
  catch {
20
57
  return undefined;
@@ -22,7 +59,8 @@ function readText(path) {
22
59
  }
23
60
  function isDir(path) {
24
61
  try {
25
- return statSync(path).isDirectory();
62
+ const stats = lstatSync(path);
63
+ return stats.isDirectory() && !stats.isSymbolicLink();
26
64
  }
27
65
  catch {
28
66
  return false;
@@ -32,9 +70,8 @@ function listDirs(path) {
32
70
  if (!isDir(path))
33
71
  return [];
34
72
  try {
35
- return readdirSync(path, { withFileTypes: true })
36
- .filter((entry) => entry.isDirectory() && !entry.name.startsWith("."))
37
- .map((entry) => entry.name)
73
+ return readBoundedDirectoryNamesSync(path, MAX_PIPELINE_DIRECTORY_ENTRIES, "Pipeline directory")
74
+ .filter((name) => !name.startsWith(".") && isDir(join(path, name)))
38
75
  .sort();
39
76
  }
40
77
  catch {
@@ -44,7 +81,7 @@ function listDirs(path) {
44
81
  function pickEntryFile(rootDir) {
45
82
  for (const name of ENTRY_FILES) {
46
83
  const path = join(rootDir, name);
47
- if (existsSync(path))
84
+ if (readText(path, rootDir) !== undefined)
48
85
  return path;
49
86
  }
50
87
  return undefined;
@@ -63,12 +100,12 @@ function parseStageFolder(name) {
63
100
  return undefined;
64
101
  return { ordinal: Number(match[1]), slug: match[2] };
65
102
  }
66
- function loadStage(stagesDir, folderName) {
103
+ function loadStage(rootDir, stagesDir, folderName) {
67
104
  const parsed = parseStageFolder(folderName);
68
105
  if (!parsed)
69
106
  return undefined;
70
107
  const contractPath = join(stagesDir, folderName, CONTRACT_FILE);
71
- const content = readText(contractPath);
108
+ const content = readText(contractPath, rootDir);
72
109
  if (content === undefined)
73
110
  return undefined;
74
111
  const contract = parseStageContract(content);
@@ -87,7 +124,7 @@ function collectStages(rootDir) {
87
124
  const stagesDir = isDir(nested) && listDirs(nested).some((name) => PIPELINE_STAGE_FOLDER_RE.test(name)) ? nested : rootDir;
88
125
  const stages = [];
89
126
  for (const name of listDirs(stagesDir)) {
90
- const stage = loadStage(stagesDir, name);
127
+ const stage = loadStage(rootDir, stagesDir, name);
91
128
  if (stage)
92
129
  stages.push(stage);
93
130
  if (stages.length >= MAX_PIPELINE_STAGES)
@@ -97,35 +134,49 @@ function collectStages(rootDir) {
97
134
  return { stagesDir, stages };
98
135
  }
99
136
  export function loadPipelineDefinition(rootDir) {
100
- if (!isDir(rootDir))
101
- return undefined;
102
- const { stagesDir, stages } = collectStages(rootDir);
103
- if (stages.length === 0)
137
+ try {
138
+ if (!isDir(rootDir))
139
+ return undefined;
140
+ const { stagesDir, stages } = collectStages(rootDir);
141
+ if (stages.length === 0)
142
+ return undefined;
143
+ const routingPath = join(rootDir, ROUTING_FILE);
144
+ const routing = readText(routingPath, rootDir);
145
+ const parsed = routing !== undefined ? parseWorkspaceFrontmatter(routing) : undefined;
146
+ const folderName = basename(rootDir).slice(0, MAX_PIPELINE_NAME_LENGTH);
147
+ const name = (parsed?.name || folderName).slice(0, MAX_PIPELINE_NAME_LENGTH);
148
+ const entryFile = pickEntryFile(rootDir) ?? routingPath;
149
+ return {
150
+ name,
151
+ description: parsed?.description ?? "",
152
+ form: parsed?.form ?? "pipeline",
153
+ rootDir,
154
+ entryFile,
155
+ routingPath: routing !== undefined ? routingPath : entryFile,
156
+ factoryDir: pickFactoryDir(rootDir),
157
+ stagesDir,
158
+ stages,
159
+ };
160
+ }
161
+ catch {
104
162
  return undefined;
105
- const routingPath = join(rootDir, ROUTING_FILE);
106
- const routing = readText(routingPath);
107
- const parsed = routing !== undefined ? parseWorkspaceFrontmatter(routing) : undefined;
108
- const folderName = basename(rootDir).slice(0, MAX_PIPELINE_NAME_LENGTH);
109
- const name = (parsed?.name || folderName).slice(0, MAX_PIPELINE_NAME_LENGTH);
110
- const entryFile = pickEntryFile(rootDir) ?? routingPath;
111
- return {
112
- name,
113
- description: parsed?.description ?? "",
114
- form: parsed?.form ?? "pipeline",
115
- rootDir,
116
- entryFile,
117
- routingPath: existsSync(routingPath) ? routingPath : entryFile,
118
- factoryDir: pickFactoryDir(rootDir),
119
- stagesDir,
120
- stages,
121
- };
163
+ }
122
164
  }
123
165
  export function discoverPipelineDefinitions(options) {
124
- const roots = [options.agentPipelinesDir, projectPipelinesDir(options.cwd)];
166
+ let projectRoot;
167
+ try {
168
+ projectRoot = assertProjectPipelineDirectory(options.cwd, "pipelines");
169
+ }
170
+ catch {
171
+ projectRoot = undefined;
172
+ }
173
+ const roots = [options.agentPipelinesDir, ...(projectRoot ? [projectRoot] : [])];
125
174
  const seen = new Set();
126
175
  const definitions = [];
127
176
  for (const root of roots) {
128
177
  for (const name of listDirs(root)) {
178
+ if (definitions.length >= MAX_PIPELINE_DEFINITIONS)
179
+ return definitions;
129
180
  const path = join(root, name);
130
181
  if (seen.has(path))
131
182
  continue;
@@ -144,4 +195,11 @@ export function findPipelineDefinition(options, name) {
144
195
  return undefined;
145
196
  return discoverPipelineDefinitions(options).find((definition) => definition.name.toLocaleLowerCase() === wanted);
146
197
  }
198
+ /** Resolve a run's immutable definition path, with the normal discovery roots as a move-safe fallback. */
199
+ export function resolvePipelineDefinitionForRun(options, run) {
200
+ const definitions = discoverPipelineDefinitions(options);
201
+ const storedPath = canonicalPathScopeIdentity(run.definitionPath);
202
+ return (definitions.find((definition) => canonicalPathScopeIdentity(definition.rootDir) === storedPath) ??
203
+ definitions.find((definition) => definition.name.toLocaleLowerCase() === run.pipelineName.toLocaleLowerCase()));
204
+ }
147
205
  //# sourceMappingURL=discover.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../../src/core/pipelines/discover.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport { parseStageContract, parseWorkspaceFrontmatter } from \"./parse-contract.ts\";\nimport {\n\tMAX_PIPELINE_NAME_LENGTH,\n\tMAX_PIPELINE_STAGES,\n\tPIPELINE_STAGE_FOLDER_RE,\n\ttype PipelineDefinition,\n\ttype PipelineStage,\n} from \"./types.ts\";\n\nconst ENTRY_FILES = [\"AGENTS.md\", \"CLAUDE.md\"] as const;\nconst ROUTING_FILE = \"CONTEXT.md\";\nconst CONTRACT_FILE = \"CONTEXT.md\";\nconst FACTORY_DIRS = [\"_shared\", \"references\"] as const;\n\nexport function projectPipelinesDir(cwd: string): string {\n\treturn join(cwd, \".pi\", \"pipelines\");\n}\n\nexport function projectPipelineRunsDir(cwd: string): string {\n\treturn join(cwd, \".pi\", \"pipeline-runs\");\n}\n\nexport interface DiscoverPipelineOptions {\n\tagentPipelinesDir: string;\n\tcwd: string;\n}\n\nfunction readText(path: string): string | undefined {\n\ttry {\n\t\treturn readFileSync(path, \"utf8\");\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction isDir(path: string): boolean {\n\ttry {\n\t\treturn statSync(path).isDirectory();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction listDirs(path: string): string[] {\n\tif (!isDir(path)) return [];\n\ttry {\n\t\treturn readdirSync(path, { withFileTypes: true })\n\t\t\t.filter((entry) => entry.isDirectory() && !entry.name.startsWith(\".\"))\n\t\t\t.map((entry) => entry.name)\n\t\t\t.sort();\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction pickEntryFile(rootDir: string): string | undefined {\n\tfor (const name of ENTRY_FILES) {\n\t\tconst path = join(rootDir, name);\n\t\tif (existsSync(path)) return path;\n\t}\n\treturn undefined;\n}\n\nfunction pickFactoryDir(rootDir: string): string {\n\tfor (const name of FACTORY_DIRS) {\n\t\tconst path = join(rootDir, name);\n\t\tif (isDir(path)) return path;\n\t}\n\treturn join(rootDir, \"_shared\");\n}\n\nfunction parseStageFolder(name: string): { ordinal: number; slug: string } | undefined {\n\tconst match = name.match(PIPELINE_STAGE_FOLDER_RE);\n\tif (!match) return undefined;\n\treturn { ordinal: Number(match[1]), slug: match[2] };\n}\n\nfunction loadStage(stagesDir: string, folderName: string): PipelineStage | undefined {\n\tconst parsed = parseStageFolder(folderName);\n\tif (!parsed) return undefined;\n\tconst contractPath = join(stagesDir, folderName, CONTRACT_FILE);\n\tconst content = readText(contractPath);\n\tif (content === undefined) return undefined;\n\tconst contract = parseStageContract(content);\n\treturn {\n\t\tid: `${String(parsed.ordinal).padStart(2, \"0\")}_${parsed.slug}`,\n\t\tordinal: parsed.ordinal,\n\t\tslug: parsed.slug,\n\t\tfolderName,\n\t\tcontractPath,\n\t\toutputDir: join(stagesDir, folderName, \"output\"),\n\t\tcontract,\n\t};\n}\n\nfunction collectStages(rootDir: string): { stagesDir: string; stages: PipelineStage[] } {\n\tconst nested = join(rootDir, \"stages\");\n\tconst stagesDir =\n\t\tisDir(nested) && listDirs(nested).some((name) => PIPELINE_STAGE_FOLDER_RE.test(name)) ? nested : rootDir;\n\tconst stages: PipelineStage[] = [];\n\tfor (const name of listDirs(stagesDir)) {\n\t\tconst stage = loadStage(stagesDir, name);\n\t\tif (stage) stages.push(stage);\n\t\tif (stages.length >= MAX_PIPELINE_STAGES) break;\n\t}\n\tstages.sort((left, right) => left.ordinal - right.ordinal || left.slug.localeCompare(right.slug));\n\treturn { stagesDir, stages };\n}\n\nexport function loadPipelineDefinition(rootDir: string): PipelineDefinition | undefined {\n\tif (!isDir(rootDir)) return undefined;\n\tconst { stagesDir, stages } = collectStages(rootDir);\n\tif (stages.length === 0) return undefined;\n\tconst routingPath = join(rootDir, ROUTING_FILE);\n\tconst routing = readText(routingPath);\n\tconst parsed = routing !== undefined ? parseWorkspaceFrontmatter(routing) : undefined;\n\tconst folderName = basename(rootDir).slice(0, MAX_PIPELINE_NAME_LENGTH);\n\tconst name = (parsed?.name || folderName).slice(0, MAX_PIPELINE_NAME_LENGTH);\n\tconst entryFile = pickEntryFile(rootDir) ?? routingPath;\n\treturn {\n\t\tname,\n\t\tdescription: parsed?.description ?? \"\",\n\t\tform: parsed?.form ?? \"pipeline\",\n\t\trootDir,\n\t\tentryFile,\n\t\troutingPath: existsSync(routingPath) ? routingPath : entryFile,\n\t\tfactoryDir: pickFactoryDir(rootDir),\n\t\tstagesDir,\n\t\tstages,\n\t};\n}\n\nexport function discoverPipelineDefinitions(options: DiscoverPipelineOptions): PipelineDefinition[] {\n\tconst roots = [options.agentPipelinesDir, projectPipelinesDir(options.cwd)];\n\tconst seen = new Set<string>();\n\tconst definitions: PipelineDefinition[] = [];\n\tfor (const root of roots) {\n\t\tfor (const name of listDirs(root)) {\n\t\t\tconst path = join(root, name);\n\t\t\tif (seen.has(path)) continue;\n\t\t\tconst definition = loadPipelineDefinition(path);\n\t\t\tif (!definition) continue;\n\t\t\tseen.add(path);\n\t\t\tdefinitions.push(definition);\n\t\t}\n\t}\n\treturn definitions;\n}\n\nexport function findPipelineDefinition(options: DiscoverPipelineOptions, name: string): PipelineDefinition | undefined {\n\tconst wanted = name.trim().toLocaleLowerCase();\n\tif (!wanted) return undefined;\n\treturn discoverPipelineDefinitions(options).find((definition) => definition.name.toLocaleLowerCase() === wanted);\n}\n"]}
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../../src/core/pipelines/discover.ts"],"names":[],"mappings":"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","sourcesContent":["import { lstatSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport { canonicalPathScopeIdentity, isPathWithinScope, safeRealpathSync } from \"../autonomy/path-scope.ts\";\nimport { readBoundedDirectoryNamesSync, readBoundedTextFileSync } from \"../util/bounded-file.ts\";\nimport { parseStageContract, parseWorkspaceFrontmatter } from \"./parse-contract.ts\";\nimport {\n\tMAX_PIPELINE_NAME_LENGTH,\n\tMAX_PIPELINE_STAGES,\n\tPIPELINE_STAGE_FOLDER_RE,\n\ttype PipelineDefinition,\n\ttype PipelineRun,\n\ttype PipelineStage,\n} from \"./types.ts\";\n\nconst ENTRY_FILES = [\"AGENTS.md\", \"CLAUDE.md\"] as const;\nconst ROUTING_FILE = \"CONTEXT.md\";\nconst CONTRACT_FILE = \"CONTEXT.md\";\nconst FACTORY_DIRS = [\"_shared\", \"references\"] as const;\nconst MAX_PIPELINE_DEFINITION_FILE_BYTES = 64 * 1024;\nconst MAX_PIPELINE_DIRECTORY_ENTRIES = 1_024;\nconst MAX_PIPELINE_DEFINITIONS = 64;\n\nexport function projectPipelinesDir(cwd: string): string {\n\treturn join(cwd, \".pi\", \"pipelines\");\n}\n\nexport function projectPipelineRunsDir(cwd: string): string {\n\treturn join(cwd, \".pi\", \"pipeline-runs\");\n}\n\n/** Validate one project-owned pipeline storage root without following a managed symlink first. */\nexport function assertProjectPipelineDirectory(cwd: string, name: \"pipelines\" | \"pipeline-runs\"): string {\n\tconst piRoot = join(cwd, \".pi\");\n\tconst directory = join(piRoot, name);\n\tfor (const [path, label] of [\n\t\t[piRoot, \"Project .pi path\"],\n\t\t[directory, `Project ${name} path`],\n\t] as const) {\n\t\tlet stats: ReturnType<typeof lstatSync>;\n\t\ttry {\n\t\t\tstats = lstatSync(path);\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") continue;\n\t\t\tthrow error;\n\t\t}\n\t\tif (!stats.isDirectory() || stats.isSymbolicLink()) {\n\t\t\tthrow new Error(`${label} is not a real directory: ${path}`);\n\t\t}\n\t}\n\tif (!isPathWithinScope(safeRealpathSync(directory), safeRealpathSync(cwd))) {\n\t\tthrow new Error(`Project ${name} directory escapes the working directory.`);\n\t}\n\treturn directory;\n}\n\nexport interface DiscoverPipelineOptions {\n\tagentPipelinesDir: string;\n\tcwd: string;\n}\n\nfunction readText(path: string, rootDir: string): string | undefined {\n\ttry {\n\t\tconst stats = lstatSync(path);\n\t\tif (!stats.isFile() || stats.isSymbolicLink() || stats.size > MAX_PIPELINE_DEFINITION_FILE_BYTES) {\n\t\t\treturn undefined;\n\t\t}\n\t\tif (!isPathWithinScope(safeRealpathSync(path), safeRealpathSync(rootDir))) return undefined;\n\t\treturn readBoundedTextFileSync(path, MAX_PIPELINE_DEFINITION_FILE_BYTES, \"Pipeline definition file\");\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction isDir(path: string): boolean {\n\ttry {\n\t\tconst stats = lstatSync(path);\n\t\treturn stats.isDirectory() && !stats.isSymbolicLink();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction listDirs(path: string): string[] {\n\tif (!isDir(path)) return [];\n\ttry {\n\t\treturn readBoundedDirectoryNamesSync(path, MAX_PIPELINE_DIRECTORY_ENTRIES, \"Pipeline directory\")\n\t\t\t.filter((name) => !name.startsWith(\".\") && isDir(join(path, name)))\n\t\t\t.sort();\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction pickEntryFile(rootDir: string): string | undefined {\n\tfor (const name of ENTRY_FILES) {\n\t\tconst path = join(rootDir, name);\n\t\tif (readText(path, rootDir) !== undefined) return path;\n\t}\n\treturn undefined;\n}\n\nfunction pickFactoryDir(rootDir: string): string {\n\tfor (const name of FACTORY_DIRS) {\n\t\tconst path = join(rootDir, name);\n\t\tif (isDir(path)) return path;\n\t}\n\treturn join(rootDir, \"_shared\");\n}\n\nfunction parseStageFolder(name: string): { ordinal: number; slug: string } | undefined {\n\tconst match = name.match(PIPELINE_STAGE_FOLDER_RE);\n\tif (!match) return undefined;\n\treturn { ordinal: Number(match[1]), slug: match[2] };\n}\n\nfunction loadStage(rootDir: string, stagesDir: string, folderName: string): PipelineStage | undefined {\n\tconst parsed = parseStageFolder(folderName);\n\tif (!parsed) return undefined;\n\tconst contractPath = join(stagesDir, folderName, CONTRACT_FILE);\n\tconst content = readText(contractPath, rootDir);\n\tif (content === undefined) return undefined;\n\tconst contract = parseStageContract(content);\n\treturn {\n\t\tid: `${String(parsed.ordinal).padStart(2, \"0\")}_${parsed.slug}`,\n\t\tordinal: parsed.ordinal,\n\t\tslug: parsed.slug,\n\t\tfolderName,\n\t\tcontractPath,\n\t\toutputDir: join(stagesDir, folderName, \"output\"),\n\t\tcontract,\n\t};\n}\n\nfunction collectStages(rootDir: string): { stagesDir: string; stages: PipelineStage[] } {\n\tconst nested = join(rootDir, \"stages\");\n\tconst stagesDir =\n\t\tisDir(nested) && listDirs(nested).some((name) => PIPELINE_STAGE_FOLDER_RE.test(name)) ? nested : rootDir;\n\tconst stages: PipelineStage[] = [];\n\tfor (const name of listDirs(stagesDir)) {\n\t\tconst stage = loadStage(rootDir, stagesDir, name);\n\t\tif (stage) stages.push(stage);\n\t\tif (stages.length >= MAX_PIPELINE_STAGES) break;\n\t}\n\tstages.sort((left, right) => left.ordinal - right.ordinal || left.slug.localeCompare(right.slug));\n\treturn { stagesDir, stages };\n}\n\nexport function loadPipelineDefinition(rootDir: string): PipelineDefinition | undefined {\n\ttry {\n\t\tif (!isDir(rootDir)) return undefined;\n\t\tconst { stagesDir, stages } = collectStages(rootDir);\n\t\tif (stages.length === 0) return undefined;\n\t\tconst routingPath = join(rootDir, ROUTING_FILE);\n\t\tconst routing = readText(routingPath, rootDir);\n\t\tconst parsed = routing !== undefined ? parseWorkspaceFrontmatter(routing) : undefined;\n\t\tconst folderName = basename(rootDir).slice(0, MAX_PIPELINE_NAME_LENGTH);\n\t\tconst name = (parsed?.name || folderName).slice(0, MAX_PIPELINE_NAME_LENGTH);\n\t\tconst entryFile = pickEntryFile(rootDir) ?? routingPath;\n\t\treturn {\n\t\t\tname,\n\t\t\tdescription: parsed?.description ?? \"\",\n\t\t\tform: parsed?.form ?? \"pipeline\",\n\t\t\trootDir,\n\t\t\tentryFile,\n\t\t\troutingPath: routing !== undefined ? routingPath : entryFile,\n\t\t\tfactoryDir: pickFactoryDir(rootDir),\n\t\t\tstagesDir,\n\t\t\tstages,\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function discoverPipelineDefinitions(options: DiscoverPipelineOptions): PipelineDefinition[] {\n\tlet projectRoot: string | undefined;\n\ttry {\n\t\tprojectRoot = assertProjectPipelineDirectory(options.cwd, \"pipelines\");\n\t} catch {\n\t\tprojectRoot = undefined;\n\t}\n\tconst roots = [options.agentPipelinesDir, ...(projectRoot ? [projectRoot] : [])];\n\tconst seen = new Set<string>();\n\tconst definitions: PipelineDefinition[] = [];\n\tfor (const root of roots) {\n\t\tfor (const name of listDirs(root)) {\n\t\t\tif (definitions.length >= MAX_PIPELINE_DEFINITIONS) return definitions;\n\t\t\tconst path = join(root, name);\n\t\t\tif (seen.has(path)) continue;\n\t\t\tconst definition = loadPipelineDefinition(path);\n\t\t\tif (!definition) continue;\n\t\t\tseen.add(path);\n\t\t\tdefinitions.push(definition);\n\t\t}\n\t}\n\treturn definitions;\n}\n\nexport function findPipelineDefinition(options: DiscoverPipelineOptions, name: string): PipelineDefinition | undefined {\n\tconst wanted = name.trim().toLocaleLowerCase();\n\tif (!wanted) return undefined;\n\treturn discoverPipelineDefinitions(options).find((definition) => definition.name.toLocaleLowerCase() === wanted);\n}\n\n/** Resolve a run's immutable definition path, with the normal discovery roots as a move-safe fallback. */\nexport function resolvePipelineDefinitionForRun(\n\toptions: DiscoverPipelineOptions,\n\trun: Pick<PipelineRun, \"definitionPath\" | \"pipelineName\">,\n): PipelineDefinition | undefined {\n\tconst definitions = discoverPipelineDefinitions(options);\n\tconst storedPath = canonicalPathScopeIdentity(run.definitionPath);\n\treturn (\n\t\tdefinitions.find((definition) => canonicalPathScopeIdentity(definition.rootDir) === storedPath) ??\n\t\tdefinitions.find((definition) => definition.name.toLocaleLowerCase() === run.pipelineName.toLocaleLowerCase())\n\t);\n}\n"]}
@@ -1,10 +1,8 @@
1
1
  import type { BackgroundToolTaskRef } from "../background-tool-task-controller.ts";
2
- import type { GoalState } from "../goals/goal-state.ts";
3
2
  import type { OpenTaskStepRef } from "../goals/goal-tool-core.ts";
4
3
  import { type TaskStep, type TaskStepsState } from "../tasks/task-state.ts";
5
4
  import { type IncrementResult, type PipelineDefinition, type PipelineRun } from "./types.ts";
6
5
  export interface PipelineIncrementJoin {
7
- goal?: GoalState;
8
6
  openTaskSteps?: readonly OpenTaskStepRef[];
9
7
  backgroundToolTasks?: readonly BackgroundToolTaskRef[];
10
8
  }
@@ -1 +1 @@
1
- {"version":3,"file":"increment.d.ts","sourceRoot":"","sources":["../../../src/core/pipelines/increment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AACnF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAElE,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,cAAc,EAAkB,MAAM,wBAAwB,CAAC;AAG5F,OAAO,EAAE,KAAK,eAAe,EAAuB,KAAK,kBAAkB,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAElH,MAAM,WAAW,qBAAqB;IACrC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,aAAa,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAC3C,mBAAmB,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;CACvD;AAED,qBAAa,sBAAuB,SAAQ,KAAK;IAChD,YAAY,OAAO,EAAE,MAAM,EAG1B;CACD;AAeD,wBAAgB,oBAAoB,CACnC,UAAU,EAAE,kBAAkB,EAC9B,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,qBAA0B,GAC9B;IAAE,GAAG,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,CAmD/C;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CAG7E;AAED,wBAAgB,gBAAgB,CAC/B,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,MAAM,GACT;IACF,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,eAAe,CAAC;CACxB,CA2CA;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,QAAQ,GAAG,SAAS,CAEpF"}
1
+ {"version":3,"file":"increment.d.ts","sourceRoot":"","sources":["../../../src/core/pipelines/increment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AACnF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,cAAc,EAAkB,MAAM,wBAAwB,CAAC;AAG5F,OAAO,EACN,KAAK,eAAe,EAEpB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAEhB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,qBAAqB;IACrC,aAAa,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAC3C,mBAAmB,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;CACvD;AAED,qBAAa,sBAAuB,SAAQ,KAAK;IAChD,YAAY,OAAO,EAAE,MAAM,EAG1B;CACD;AAgBD,wBAAgB,oBAAoB,CACnC,UAAU,EAAE,kBAAkB,EAC9B,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,qBAA0B,GAC9B;IAAE,GAAG,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,CAmD/C;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CAG7E;AAED,wBAAgB,gBAAgB,CAC/B,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,MAAM,GACT;IACF,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,eAAe,CAAC;CACxB,CAgEA;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,QAAQ,GAAG,SAAS,CAEpF"}