@abelxiaoxing/cadence 1.3.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +172 -13
  2. package/agents/design-explorer.md +17 -11
  3. package/agents/diagnosis-worker.md +9 -1
  4. package/agents/implementation-worker.md +3 -1
  5. package/config/plan-draft.example.json +122 -0
  6. package/config/plan-draft.multiple-tasks.example.json +345 -0
  7. package/package.json +9 -5
  8. package/prompts/abel-design.md +141 -38
  9. package/prompts/abel-diagnose.md +10 -1
  10. package/prompts/abel-implement.md +118 -20
  11. package/prompts/abel-init.md +9 -2
  12. package/src/activity-contracts.ts +82 -0
  13. package/src/apply-transaction.ts +16 -14
  14. package/src/artifact-store.ts +17 -0
  15. package/src/candidate-artifact.ts +143 -0
  16. package/src/candidate-patch.ts +33 -12
  17. package/src/candidate-workspace.ts +74 -0
  18. package/src/canonical.ts +16 -0
  19. package/src/change-contract-schema.ts +191 -0
  20. package/src/change-contract.ts +273 -0
  21. package/src/change-verification.ts +896 -0
  22. package/src/child-model.ts +129 -0
  23. package/src/child-session.ts +296 -328
  24. package/src/contracts.ts +325 -109
  25. package/src/control-contracts.ts +51 -1
  26. package/src/delivery-compiler.ts +241 -141
  27. package/src/delivery-revision.ts +42 -0
  28. package/src/design-control.ts +182 -15
  29. package/src/design-diagnostics.ts +150 -0
  30. package/src/design-journal.ts +216 -72
  31. package/src/durable-contracts.ts +210 -0
  32. package/src/durable-workflow.ts +963 -0
  33. package/src/evidence-draft.ts +122 -0
  34. package/src/implement-graph.ts +14 -16
  35. package/src/implement-plan.ts +70 -0
  36. package/src/index.ts +341 -1405
  37. package/src/isolation-backend.ts +84 -23
  38. package/src/model-source.ts +22 -0
  39. package/src/package-candidate.ts +224 -0
  40. package/src/package-delivery.ts +486 -0
  41. package/src/package-verification.ts +509 -0
  42. package/src/package-workflow.ts +253 -0
  43. package/src/packet-runtime.ts +27 -48
  44. package/src/parent-provider.ts +86 -198
  45. package/src/phase-execution.ts +2248 -0
  46. package/src/pi-adapter.ts +19 -0
  47. package/src/plan-draft-summary.ts +146 -0
  48. package/src/plan-draft.ts +493 -0
  49. package/src/route-policy.ts +1 -11
  50. package/src/run-state.ts +4 -16
  51. package/src/run-store.ts +5 -144
  52. package/src/scoped-grep-worker.mjs +23 -0
  53. package/src/scoped-tools.ts +312 -116
  54. package/src/sqlite-schema.ts +171 -0
  55. package/src/storage-schema.ts +352 -0
  56. package/src/subagent-activity.ts +46 -67
  57. package/src/submit-tool.ts +40 -61
  58. package/src/task-ledger.ts +14 -55
  59. package/src/transport-budget.ts +32 -0
  60. package/src/verification-capability.ts +298 -102
  61. package/src/verification-environment.ts +213 -0
  62. package/src/verification-identity.ts +77 -0
  63. package/src/worker-broker.ts +134 -91
  64. package/src/workflow-engine.ts +28 -8285
  65. package/src/workflow-policy.ts +1388 -0
  66. package/src/workflow-recovery-policy.ts +95 -0
  67. package/src/workflow-scheduling.ts +105 -0
  68. package/src/workflow-state-machine.ts +4086 -0
  69. package/src/workflow-status.ts +384 -0
  70. package/src/workspace-io-entry.mjs +58 -0
  71. package/src/workspace-io-worker.mjs +991 -0
  72. package/src/workspace-io.ts +84 -0
  73. package/src/workspace-store.ts +125 -19
  74. package/skills/abel-workflow/SKILL.md +0 -205
  75. package/src/empty-resource-loader.ts +0 -38
  76. package/src/parent-payload-bridge.ts +0 -523
@@ -0,0 +1,213 @@
1
+ import {
2
+ lstatSync,
3
+ mkdirSync,
4
+ mkdtempSync,
5
+ readdirSync,
6
+ readlinkSync,
7
+ realpathSync,
8
+ rmSync,
9
+ symlinkSync,
10
+ writeFileSync,
11
+ } from "node:fs";
12
+ import path from "node:path";
13
+ import {
14
+ type StructuredVerificationContract,
15
+ VERIFICATION_CONFIGURATION_PATHS,
16
+ } from "./contracts.ts";
17
+ import type { IsolationMount } from "./isolation-backend.ts";
18
+ import {
19
+ resolveVerificationRunner,
20
+ type VerificationRunnerBinding,
21
+ validateVerificationAdapterCapability,
22
+ verificationRunnerFiles,
23
+ } from "./verification-capability.ts";
24
+ import { runWorkspaceIo } from "./workspace-io.ts";
25
+
26
+ export async function captureVerificationEnvironmentIdentity(
27
+ dependencyOwner: string,
28
+ verifications: readonly StructuredVerificationContract[],
29
+ signal?: AbortSignal,
30
+ ): Promise<string> {
31
+ const roots = [path.join(dependencyOwner, "node_modules")];
32
+ const node = resolveVerificationRunner("node");
33
+ if (node) roots.push(realpathSync(node.executablePath));
34
+ for (const verification of verifications) {
35
+ // Identity describes installed tools; admission/execution separately owns config authorization.
36
+ const capability = validateVerificationAdapterCapability(
37
+ dependencyOwner,
38
+ verification,
39
+ { executionWritePaths: VERIFICATION_CONFIGURATION_PATHS },
40
+ );
41
+ if (!capability.ok) throw new Error("verification-environment-unavailable");
42
+ roots.push(
43
+ ...verificationRunnerFiles(
44
+ dependencyOwner,
45
+ verification,
46
+ capability.runnerBindings,
47
+ ),
48
+ );
49
+ for (const binding of capability.runnerBindings)
50
+ if (binding.mountSource) roots.push(binding.mountSource);
51
+ }
52
+ return runWorkspaceIo<string>({
53
+ root: dependencyOwner,
54
+ artifactRoot: dependencyOwner,
55
+ operation: "verificationIdentity",
56
+ args: [roots],
57
+ signal,
58
+ });
59
+ }
60
+
61
+ const CACHES = [".vite", ".vite-temp"];
62
+ function within(root: string, candidate: string): boolean {
63
+ const relative = path.relative(root, candidate);
64
+ return (
65
+ relative !== ".." &&
66
+ !relative.startsWith(`..${path.sep}`) &&
67
+ !path.isAbsolute(relative)
68
+ );
69
+ }
70
+
71
+ /** A disposable dependency view: immutable packages and independently writable caches. */
72
+ export function prepareVerificationEnvironment(
73
+ root: string,
74
+ dependencyOwner: string,
75
+ runnerBindings: readonly VerificationRunnerBinding[],
76
+ ) {
77
+ const privateRoot = mkdtempSync(
78
+ path.join(path.dirname(root), ".cadence-verification-"),
79
+ );
80
+ const cleanup = () => rmSync(privateRoot, { recursive: true, force: true });
81
+ try {
82
+ const mounts: IsolationMount[] = [
83
+ { source: privateRoot, target: "/cadence", writable: true },
84
+ ];
85
+ for (const directory of [
86
+ "home",
87
+ "cache",
88
+ "tmp",
89
+ "reports",
90
+ "dependencies",
91
+ ]) {
92
+ mkdirSync(path.join(privateRoot, directory), { mode: 0o700 });
93
+ }
94
+ writeFileSync(path.join(privateRoot, "home/user.npmrc"), "");
95
+ writeFileSync(path.join(privateRoot, "home/global.npmrc"), "");
96
+ const dependencies = path.join(dependencyOwner, "node_modules");
97
+ const stat = lstatSync(dependencies, { throwIfNoEntry: false });
98
+ if (stat) {
99
+ const target = path.join(root, "node_modules");
100
+ const targetStat = lstatSync(target, { throwIfNoEntry: false });
101
+ if (
102
+ !stat.isDirectory() ||
103
+ stat.isSymbolicLink() ||
104
+ (targetStat &&
105
+ (!targetStat.isDirectory() || targetStat.isSymbolicLink()))
106
+ ) {
107
+ throw new Error("dependency-path-unsafe");
108
+ }
109
+ if (!targetStat) mkdirSync(target, { mode: 0o700 });
110
+ const view = path.join(privateRoot, "dependencies");
111
+ mounts.push({ source: view, target: "/workspace/node_modules" });
112
+ const entries = readdirSync(dependencies);
113
+ if (entries.length > 20_000) throw new Error("dependency-view-too-large");
114
+ const canonical = realpathSync(dependencies);
115
+ for (const name of entries) {
116
+ if (CACHES.includes(name)) continue;
117
+ const source = path.join(dependencies, name);
118
+ const placeholder = path.join(view, name);
119
+ const entry = lstatSync(source);
120
+ if (entry.isSymbolicLink()) {
121
+ const link = readlinkSync(source);
122
+ if (
123
+ path.isAbsolute(link) ||
124
+ !within(canonical, realpathSync(source))
125
+ ) {
126
+ throw new Error("dependency-path-unsafe");
127
+ }
128
+ symlinkSync(link, placeholder);
129
+ } else {
130
+ if (entry.isDirectory()) mkdirSync(placeholder);
131
+ else if (entry.isFile()) writeFileSync(placeholder, "");
132
+ else throw new Error("dependency-path-unsafe");
133
+ mounts.push({ source, target: `/workspace/node_modules/${name}` });
134
+ }
135
+ }
136
+ for (const cache of CACHES) {
137
+ mkdirSync(path.join(view, cache));
138
+ const source = path.join(privateRoot, "cache", cache);
139
+ mkdirSync(source);
140
+ mounts.push({
141
+ source,
142
+ target: `/workspace/node_modules/${cache}`,
143
+ writable: true,
144
+ });
145
+ }
146
+ }
147
+ const sources = new Map<string, string>();
148
+ const bindings = runnerBindings.map((binding) => {
149
+ if (!binding.mountSource) return { ...binding };
150
+ const source = path.resolve(binding.mountSource);
151
+ const stat = lstatSync(source);
152
+ const executable = realpathSync(binding.executablePath);
153
+ if (
154
+ !stat.isDirectory() ||
155
+ stat.isSymbolicLink() ||
156
+ !within(source, executable)
157
+ ) {
158
+ throw new Error("runner-mount-unavailable");
159
+ }
160
+ let target = sources.get(source);
161
+ if (!target) {
162
+ target = `/cadence-runners/${sources.size}`;
163
+ sources.set(source, target);
164
+ mounts.push({ source, target });
165
+ }
166
+ const translate = (file: string) =>
167
+ path.posix.join(target, ...path.relative(source, file).split(path.sep));
168
+ return {
169
+ ...binding,
170
+ executablePath: translate(executable),
171
+ ...(binding.fixedArgs
172
+ ? {
173
+ fixedArgs: binding.fixedArgs.map((arg) =>
174
+ path.isAbsolute(arg) && within(source, arg)
175
+ ? translate(arg)
176
+ : arg,
177
+ ),
178
+ }
179
+ : {}),
180
+ };
181
+ });
182
+ return {
183
+ privateRoot,
184
+ mounts,
185
+ bindings,
186
+ cleanup,
187
+ environment: {
188
+ CI: "1",
189
+ HOME: "/cadence/home",
190
+ XDG_CACHE_HOME: "/cadence/cache",
191
+ TMPDIR: "/cadence/tmp",
192
+ npm_config_cache: "/cadence/cache/npm",
193
+ npm_config_userconfig: "/cadence/home/user.npmrc",
194
+ npm_config_globalconfig: "/cadence/home/global.npmrc",
195
+ PATH: [
196
+ ...new Set(
197
+ // Honor explicitly resolved private runtimes before system fallbacks.
198
+ [
199
+ ...bindings.filter((binding) => binding.mountSource),
200
+ ...bindings.filter((binding) => !binding.mountSource),
201
+ ].map((binding) => path.posix.dirname(binding.executablePath)),
202
+ ),
203
+ "/usr/local/bin",
204
+ "/usr/bin",
205
+ "/bin",
206
+ ].join(":"),
207
+ },
208
+ };
209
+ } catch (error) {
210
+ cleanup();
211
+ throw error;
212
+ }
213
+ }
@@ -0,0 +1,77 @@
1
+ import { createHash } from "node:crypto";
2
+ import {
3
+ closeSync,
4
+ constants,
5
+ fstatSync,
6
+ lstatSync,
7
+ openSync,
8
+ readdirSync,
9
+ readlinkSync,
10
+ readSync,
11
+ } from "node:fs";
12
+ import path from "node:path";
13
+
14
+ /** Content identity, including executable modes and links. Never follows dependency links. */
15
+ export function verificationEnvironmentDigest(
16
+ roots: readonly string[],
17
+ checkCancelled: () => void = () => {},
18
+ ) {
19
+ const digest = createHash("sha256");
20
+ let entries = 0;
21
+ let bytes = 0;
22
+ const started = Date.now();
23
+ const buffer = Buffer.alloc(256 * 1024);
24
+ const visit = (file: string, relative: string, dependencyRoot: boolean) => {
25
+ checkCancelled();
26
+ if (++entries > 500_000 || Date.now() - started > 60_000)
27
+ throw new Error("verification-environment-limit");
28
+ const stat = lstatSync(file, { throwIfNoEntry: false });
29
+ digest.update(JSON.stringify([relative, stat ? stat.mode & 0o777 : null]));
30
+ if (!stat) {
31
+ digest.update("absent");
32
+ return;
33
+ }
34
+ if (stat.isSymbolicLink()) {
35
+ digest.update(JSON.stringify(["link", readlinkSync(file)]));
36
+ return;
37
+ }
38
+ if (stat.isDirectory()) {
39
+ digest.update("directory");
40
+ for (const name of readdirSync(file).sort()) {
41
+ if (dependencyRoot && [".vite", ".vite-temp"].includes(name)) continue;
42
+ visit(path.join(file, name), `${relative}/${name}`, false);
43
+ }
44
+ return;
45
+ }
46
+ if (!stat.isFile()) throw new Error("verification-environment-unsafe");
47
+ const fd = openSync(file, constants.O_RDONLY | constants.O_NOFOLLOW);
48
+ try {
49
+ const before = fstatSync(fd);
50
+ if (before.ino !== stat.ino || before.dev !== stat.dev)
51
+ throw new Error("verification-environment-changed");
52
+ const content = createHash("sha256");
53
+ for (;;) {
54
+ const count = readSync(fd, buffer, 0, buffer.length, null);
55
+ if (count === 0) break;
56
+ checkCancelled();
57
+ bytes += count;
58
+ if (bytes > 16 * 1024 ** 3 || Date.now() - started > 60_000)
59
+ throw new Error("verification-environment-limit");
60
+ content.update(buffer.subarray(0, count));
61
+ }
62
+ const after = fstatSync(fd);
63
+ if (
64
+ before.size !== after.size ||
65
+ before.mtimeMs !== after.mtimeMs ||
66
+ before.ctimeMs !== after.ctimeMs
67
+ )
68
+ throw new Error("verification-environment-changed");
69
+ digest.update(JSON.stringify(["file", content.digest("hex")]));
70
+ } finally {
71
+ closeSync(fd);
72
+ }
73
+ };
74
+ for (const root of [...new Set(roots)].sort())
75
+ visit(root, root, path.basename(root) === "node_modules");
76
+ return digest.digest("hex");
77
+ }