@odla-ai/harness 0.10.4 → 0.11.0
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.
- package/README.md +24 -0
- package/dist/{chunk-IV3VMKDV.js → chunk-IXYKIPRA.js} +133 -25
- package/dist/chunk-IXYKIPRA.js.map +1 -0
- package/dist/code-runtime-cli.cjs +114 -14
- package/dist/code-runtime-cli.cjs.map +1 -1
- package/dist/code-runtime-cli.js +1 -1
- package/dist/node.cjs +133 -21
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +70 -1
- package/dist/node.d.ts +70 -1
- package/dist/node.js +13 -1
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-IV3VMKDV.js.map +0 -1
package/dist/code-runtime-cli.js
CHANGED
package/dist/node.cjs
CHANGED
|
@@ -27,6 +27,7 @@ __export(node_exports, {
|
|
|
27
27
|
DecompositionError: () => DecompositionError,
|
|
28
28
|
MAX_MEMORY_BODY: () => MAX_MEMORY_BODY,
|
|
29
29
|
MEASURED_PREMIUM: () => MEASURED_PREMIUM,
|
|
30
|
+
REPOSITORY_RECIPES_FILE: () => REPOSITORY_RECIPES_FILE,
|
|
30
31
|
SYSTEM_PROMPT_FOR: () => SYSTEM_PROMPT_FOR,
|
|
31
32
|
TheseusRuntimeEngine: () => TheseusRuntimeEngine,
|
|
32
33
|
V1_SYSTEM_PROMPT: () => V1_SYSTEM_PROMPT,
|
|
@@ -48,6 +49,7 @@ __export(node_exports, {
|
|
|
48
49
|
createCodeToolBroker: () => createCodeToolBroker,
|
|
49
50
|
createCodeWorkspaceCheckpoint: () => createCodeWorkspaceCheckpoint,
|
|
50
51
|
createContainerRecipeExecutor: () => createContainerRecipeExecutor,
|
|
52
|
+
describeCodeRecipes: () => describeCodeRecipes,
|
|
51
53
|
describePatchFailure: () => describePatchFailure,
|
|
52
54
|
digestStagedWorkspace: () => digestStagedWorkspace,
|
|
53
55
|
feedbackIsActionable: () => feedbackIsActionable,
|
|
@@ -62,16 +64,19 @@ __export(node_exports, {
|
|
|
62
64
|
materializeGitTree: () => materializeGitTree,
|
|
63
65
|
outcomeCloses: () => outcomeCloses,
|
|
64
66
|
outcomeMemory: () => outcomeMemory,
|
|
67
|
+
parseRepositoryRecipes: () => parseRepositoryRecipes,
|
|
65
68
|
patchPaths: () => patchPaths,
|
|
66
69
|
planReachCollisions: () => planReachCollisions,
|
|
67
70
|
prepareRuntimeCheckpoint: () => prepareRuntimeCheckpoint,
|
|
68
71
|
racedAttempt: () => racedAttempt,
|
|
69
72
|
readOnlyNotice: () => readOnlyNotice,
|
|
73
|
+
readRepositoryRecipes: () => readRepositoryRecipes,
|
|
70
74
|
recallAbout: () => recallAbout,
|
|
71
75
|
registeredFiles: () => registeredFiles,
|
|
72
76
|
renderMemories: () => renderMemories,
|
|
73
77
|
renderOutcome: () => renderOutcome,
|
|
74
78
|
resolveCodePath: () => resolveCodePath,
|
|
79
|
+
resolveCodeRecipes: () => resolveCodeRecipes,
|
|
75
80
|
restoreCodeWorkspaceCheckpoint: () => restoreCodeWorkspaceCheckpoint,
|
|
76
81
|
runCodeAgent: () => runCodeAgent,
|
|
77
82
|
runCodeAgentAttempt: () => runCodeAgentAttempt,
|
|
@@ -84,6 +89,7 @@ __export(node_exports, {
|
|
|
84
89
|
safeWorkspaceLabel: () => safeWorkspaceLabel,
|
|
85
90
|
selectContainerEngine: () => selectContainerEngine,
|
|
86
91
|
selectWinner: () => selectWinner,
|
|
92
|
+
sessionRecipesFor: () => sessionRecipesFor,
|
|
87
93
|
sessionSkillsFor: () => sessionSkillsFor,
|
|
88
94
|
stageWorkspace: () => stageWorkspace,
|
|
89
95
|
stageWorkspacePair: () => stageWorkspacePair,
|
|
@@ -2000,8 +2006,8 @@ var CodeRuntimeCheckpointManager = class {
|
|
|
2000
2006
|
trustedBaseDigest: active.trustedBaseDigest,
|
|
2001
2007
|
planningInputDigest: active.planningInputDigest,
|
|
2002
2008
|
conversationRefs: active.conversationRefs,
|
|
2003
|
-
fallbackPolicyDigest: this.options.fallbackPolicyDigest,
|
|
2004
|
-
recipes: this.options.recipes,
|
|
2009
|
+
fallbackPolicyDigest: active.buildPolicyDigest ?? this.options.fallbackPolicyDigest,
|
|
2010
|
+
recipes: active.recipes ?? this.options.recipes,
|
|
2005
2011
|
recipeExecutor: this.options.recipeExecutor,
|
|
2006
2012
|
review: (patch2, verification) => this.options.control.review(command.sessionId, { patch: patch2, verification })
|
|
2007
2013
|
});
|
|
@@ -2942,13 +2948,19 @@ function createCodeRuntimeSessionSkillLoader(control) {
|
|
|
2942
2948
|
...tool.acceptsTaint === void 0 ? {} : { acceptsTaint: tool.acceptsTaint },
|
|
2943
2949
|
handler: async (input, context) => {
|
|
2944
2950
|
if (!context.toolCallId) throw new TypeError("collaboration tool call identity is required");
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2951
|
+
try {
|
|
2952
|
+
return await execute2(command.sessionId, {
|
|
2953
|
+
commandId: command.commandId,
|
|
2954
|
+
toolCallId: context.toolCallId,
|
|
2955
|
+
skill: manifest.name,
|
|
2956
|
+
tool: tool.name,
|
|
2957
|
+
input
|
|
2958
|
+
}, context.signal);
|
|
2959
|
+
} catch (cause) {
|
|
2960
|
+
if (context.signal?.aborted) throw cause;
|
|
2961
|
+
const detail = (cause instanceof Error ? cause.message : String(cause)).slice(0, 500);
|
|
2962
|
+
return { content: `Tool "${tool.name}" failed: ${detail}`, isError: true };
|
|
2963
|
+
}
|
|
2952
2964
|
}
|
|
2953
2965
|
}))
|
|
2954
2966
|
}));
|
|
@@ -4417,6 +4429,96 @@ function observeCodeRuntimeSessionSkills(command, skills, emit) {
|
|
|
4417
4429
|
}));
|
|
4418
4430
|
}
|
|
4419
4431
|
|
|
4432
|
+
// src/code-repository-recipes.ts
|
|
4433
|
+
var import_promises15 = require("fs/promises");
|
|
4434
|
+
var import_node_path13 = require("path");
|
|
4435
|
+
var REPOSITORY_RECIPES_FILE = "odla.recipes.json";
|
|
4436
|
+
var MAX_RECIPES = 16;
|
|
4437
|
+
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
4438
|
+
var MAX_TIMEOUT_MS = 15 * 6e4;
|
|
4439
|
+
var ID2 = /^[A-Za-z0-9._:-]{1,120}$/;
|
|
4440
|
+
var RECIPE_FIELDS = /* @__PURE__ */ new Set(["id", "command", "timeoutMs"]);
|
|
4441
|
+
var fault = (detail) => new TypeError(`${REPOSITORY_RECIPES_FILE} is malformed: ${detail}`);
|
|
4442
|
+
function parseRepositoryRecipes(text2, envelope) {
|
|
4443
|
+
let parsed;
|
|
4444
|
+
try {
|
|
4445
|
+
parsed = JSON.parse(text2);
|
|
4446
|
+
} catch {
|
|
4447
|
+
throw fault("not valid JSON");
|
|
4448
|
+
}
|
|
4449
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw fault("the document must be an object");
|
|
4450
|
+
const document = parsed;
|
|
4451
|
+
if (document.version !== 1) throw fault("version must be 1");
|
|
4452
|
+
const entries = document.recipes;
|
|
4453
|
+
if (!Array.isArray(entries) || entries.length < 1) throw fault("recipes must be a non-empty array");
|
|
4454
|
+
if (entries.length > MAX_RECIPES) throw fault(`at most ${MAX_RECIPES} recipes may be declared`);
|
|
4455
|
+
const recipes = [];
|
|
4456
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4457
|
+
entries.forEach((entry, index) => {
|
|
4458
|
+
const label = `recipe ${index + 1}`;
|
|
4459
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) throw fault(`${label} must be an object`);
|
|
4460
|
+
const row = entry;
|
|
4461
|
+
const unknown = Object.keys(row).filter((key) => !RECIPE_FIELDS.has(key));
|
|
4462
|
+
if (unknown.length) throw fault(`${label} has unsupported field ${unknown[0]}; the host owns image and resource limits`);
|
|
4463
|
+
if (typeof row.id !== "string" || !ID2.test(row.id)) throw fault(`${label} needs an id of 1 to 120 letters, digits, . _ : or -`);
|
|
4464
|
+
if (seen.has(row.id)) throw fault(`${label} repeats id ${row.id}`);
|
|
4465
|
+
seen.add(row.id);
|
|
4466
|
+
if (!Array.isArray(row.command) || row.command.length < 1 || row.command.some((part) => typeof part !== "string" || !part)) {
|
|
4467
|
+
throw fault(`${label} (${row.id}) needs command as a non-empty array of non-empty strings`);
|
|
4468
|
+
}
|
|
4469
|
+
const timeoutMs = row.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
4470
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > MAX_TIMEOUT_MS) {
|
|
4471
|
+
throw fault(`${label} (${row.id}) timeoutMs must be an integer from 1 to ${MAX_TIMEOUT_MS}`);
|
|
4472
|
+
}
|
|
4473
|
+
const recipe2 = {
|
|
4474
|
+
id: row.id,
|
|
4475
|
+
command: [...row.command],
|
|
4476
|
+
timeoutMs,
|
|
4477
|
+
image: envelope.image,
|
|
4478
|
+
maxOutputBytes: envelope.maxOutputBytes,
|
|
4479
|
+
cpus: envelope.cpus,
|
|
4480
|
+
memory: envelope.memory,
|
|
4481
|
+
pids: envelope.pids
|
|
4482
|
+
};
|
|
4483
|
+
try {
|
|
4484
|
+
assertCodeBuildRecipe(recipe2);
|
|
4485
|
+
} catch (cause) {
|
|
4486
|
+
throw fault(`${label} (${row.id}) is not a runnable recipe: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
4487
|
+
}
|
|
4488
|
+
recipes.push(recipe2);
|
|
4489
|
+
});
|
|
4490
|
+
return recipes;
|
|
4491
|
+
}
|
|
4492
|
+
async function readRepositoryRecipes(baselineDir, envelope) {
|
|
4493
|
+
let text2;
|
|
4494
|
+
try {
|
|
4495
|
+
text2 = await (0, import_promises15.readFile)((0, import_node_path13.join)(baselineDir, REPOSITORY_RECIPES_FILE), "utf8");
|
|
4496
|
+
} catch (cause) {
|
|
4497
|
+
if (cause.code === "ENOENT") return null;
|
|
4498
|
+
throw cause;
|
|
4499
|
+
}
|
|
4500
|
+
return parseRepositoryRecipes(text2, envelope);
|
|
4501
|
+
}
|
|
4502
|
+
async function resolveCodeRecipes(baselineDir, release, envelope) {
|
|
4503
|
+
const declared = envelope ? await readRepositoryRecipes(baselineDir, envelope) : null;
|
|
4504
|
+
return declared ? { recipes: declared, source: "repository" } : { recipes: release, source: "release" };
|
|
4505
|
+
}
|
|
4506
|
+
function describeCodeRecipes(resolved) {
|
|
4507
|
+
const ids = resolved.recipes.map((recipe2) => recipe2.id).join(", ");
|
|
4508
|
+
return resolved.source === "repository" ? `Verification recipes: declared by the repository in ${REPOSITORY_RECIPES_FILE} \xB7 ${ids}` : `Verification recipes: release-owned (the repository declares none in ${REPOSITORY_RECIPES_FILE}) \xB7 ${ids}`;
|
|
4509
|
+
}
|
|
4510
|
+
async function sessionRecipesFor(workspace, options, resume) {
|
|
4511
|
+
const resolved = await resolveCodeRecipes(workspace.baselineDir, options.recipes, options.repositoryRecipes ?? null).catch(async (cause) => {
|
|
4512
|
+
await workspace.cleanup();
|
|
4513
|
+
throw cause;
|
|
4514
|
+
});
|
|
4515
|
+
return {
|
|
4516
|
+
...resolved,
|
|
4517
|
+
buildPolicyDigest: digestRuntimeValue(JSON.stringify(resolved.recipes)),
|
|
4518
|
+
note: !resume && resolved.source === "repository" ? describeCodeRecipes(resolved) : null
|
|
4519
|
+
};
|
|
4520
|
+
}
|
|
4521
|
+
|
|
4420
4522
|
// src/code-runtime-engine.ts
|
|
4421
4523
|
var TheseusRuntimeEngine = class {
|
|
4422
4524
|
constructor(options) {
|
|
@@ -4468,6 +4570,7 @@ var TheseusRuntimeEngine = class {
|
|
|
4468
4570
|
control: this.options.control,
|
|
4469
4571
|
...this.options.localSource ? { localSource: this.options.localSource } : {}
|
|
4470
4572
|
});
|
|
4573
|
+
const resolvedRecipes = await sessionRecipesFor(workspace, this.options, resume);
|
|
4471
4574
|
const abort = new AbortController(), startGate = codeRuntimeAcknowledgementGate(abort.signal);
|
|
4472
4575
|
const conversationRefs = [];
|
|
4473
4576
|
const active = {
|
|
@@ -4476,6 +4579,8 @@ var TheseusRuntimeEngine = class {
|
|
|
4476
4579
|
conversationRefs,
|
|
4477
4580
|
acknowledged: false,
|
|
4478
4581
|
startGate,
|
|
4582
|
+
recipes: resolvedRecipes.recipes,
|
|
4583
|
+
buildPolicyDigest: resolvedRecipes.buildPolicyDigest,
|
|
4479
4584
|
role: metadata.role,
|
|
4480
4585
|
readOnly: metadata.readOnly,
|
|
4481
4586
|
title: metadata.title,
|
|
@@ -4493,6 +4598,7 @@ var TheseusRuntimeEngine = class {
|
|
|
4493
4598
|
done: Promise.resolve(null)
|
|
4494
4599
|
};
|
|
4495
4600
|
this.#active.set(command.sessionId, active);
|
|
4601
|
+
if (resolvedRecipes.note) await this.#event(command, { type: "message", actor: "system", body: resolvedRecipes.note }, conversationRefs);
|
|
4496
4602
|
if (requestedLocal) {
|
|
4497
4603
|
await this.#event(command, {
|
|
4498
4604
|
type: "message",
|
|
@@ -4528,7 +4634,7 @@ var TheseusRuntimeEngine = class {
|
|
|
4528
4634
|
const active = await this.#takeOver(command, "pursue requires an active Code session");
|
|
4529
4635
|
active.done = startGoalPursuit({
|
|
4530
4636
|
spec,
|
|
4531
|
-
recipes:
|
|
4637
|
+
recipes: active.recipes,
|
|
4532
4638
|
recipeExecutor: this.options.recipeExecutor ?? createContainerRecipeExecutor(this.options.engine),
|
|
4533
4639
|
workspace: active.workspace,
|
|
4534
4640
|
baseCommitSha: active.baseCommitSha,
|
|
@@ -4607,7 +4713,7 @@ var TheseusRuntimeEngine = class {
|
|
|
4607
4713
|
async #runAttempt(command, metadata, active) {
|
|
4608
4714
|
const lease = fakeCodeLease(command, metadata);
|
|
4609
4715
|
const broker = this.#observed(command, active, createCodeRuntimeToolBroker({
|
|
4610
|
-
recipes:
|
|
4716
|
+
recipes: active.recipes,
|
|
4611
4717
|
engine: this.options.engine,
|
|
4612
4718
|
recipeAuthorization: this.options.recipeAuthorization
|
|
4613
4719
|
}, lease, metadata.role));
|
|
@@ -4633,7 +4739,7 @@ var TheseusRuntimeEngine = class {
|
|
|
4633
4739
|
// A review-role session is read-only and still runs the proof; a planner
|
|
4634
4740
|
// marked read-only by its payload runs nothing.
|
|
4635
4741
|
recipes: metadata.role === "review" || !metadata.readOnly,
|
|
4636
|
-
recipeIds:
|
|
4742
|
+
recipeIds: active.recipes.map((recipe2) => recipe2.id),
|
|
4637
4743
|
...extraSkills.length ? { extraSkills } : {}
|
|
4638
4744
|
});
|
|
4639
4745
|
const closing = result.finalText.trim();
|
|
@@ -4964,12 +5070,12 @@ function chooseStrategy(signals = {}) {
|
|
|
4964
5070
|
var feedbackIsActionable = actionable;
|
|
4965
5071
|
|
|
4966
5072
|
// src/code-recipe-dependencies.ts
|
|
4967
|
-
var
|
|
4968
|
-
var
|
|
5073
|
+
var import_promises16 = require("fs/promises");
|
|
5074
|
+
var import_node_path14 = require("path");
|
|
4969
5075
|
var RESERVED_MOUNTS = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage"]);
|
|
4970
5076
|
function withRecipeDependencies(executor, dependencies) {
|
|
4971
5077
|
const mountAs = dependencies.mountAs ?? "node_modules";
|
|
4972
|
-
if (!(0,
|
|
5078
|
+
if (!(0, import_node_path14.isAbsolute)(dependencies.source)) {
|
|
4973
5079
|
throw new TypeError("recipe dependency source must be an absolute path");
|
|
4974
5080
|
}
|
|
4975
5081
|
if (!RESERVED_MOUNTS.has(mountAs)) {
|
|
@@ -4977,24 +5083,24 @@ function withRecipeDependencies(executor, dependencies) {
|
|
|
4977
5083
|
}
|
|
4978
5084
|
return {
|
|
4979
5085
|
run: async (input) => {
|
|
4980
|
-
const target = (0,
|
|
5086
|
+
const target = (0, import_node_path14.join)(input.workspaceDir, mountAs);
|
|
4981
5087
|
let linked = false;
|
|
4982
5088
|
try {
|
|
4983
|
-
const existing = await (0,
|
|
5089
|
+
const existing = await (0, import_promises16.lstat)(target).catch(() => null);
|
|
4984
5090
|
if (!existing) {
|
|
4985
|
-
await (0,
|
|
5091
|
+
await (0, import_promises16.symlink)(dependencies.source, target, "dir");
|
|
4986
5092
|
linked = true;
|
|
4987
5093
|
}
|
|
4988
5094
|
return await executor.run(input);
|
|
4989
5095
|
} finally {
|
|
4990
|
-
if (linked) await (0,
|
|
5096
|
+
if (linked) await (0, import_promises16.rm)(target, { force: true, recursive: false }).catch(() => void 0);
|
|
4991
5097
|
}
|
|
4992
5098
|
}
|
|
4993
5099
|
};
|
|
4994
5100
|
}
|
|
4995
5101
|
async function installedDependencies(repoRoot) {
|
|
4996
|
-
const source = (0,
|
|
4997
|
-
const info = await (0,
|
|
5102
|
+
const source = (0, import_node_path14.join)(repoRoot, "node_modules");
|
|
5103
|
+
const info = await (0, import_promises16.lstat)(source).catch(() => null);
|
|
4998
5104
|
return info?.isDirectory() ? { source } : null;
|
|
4999
5105
|
}
|
|
5000
5106
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -5006,6 +5112,7 @@ async function installedDependencies(repoRoot) {
|
|
|
5006
5112
|
DecompositionError,
|
|
5007
5113
|
MAX_MEMORY_BODY,
|
|
5008
5114
|
MEASURED_PREMIUM,
|
|
5115
|
+
REPOSITORY_RECIPES_FILE,
|
|
5009
5116
|
SYSTEM_PROMPT_FOR,
|
|
5010
5117
|
TheseusRuntimeEngine,
|
|
5011
5118
|
V1_SYSTEM_PROMPT,
|
|
@@ -5027,6 +5134,7 @@ async function installedDependencies(repoRoot) {
|
|
|
5027
5134
|
createCodeToolBroker,
|
|
5028
5135
|
createCodeWorkspaceCheckpoint,
|
|
5029
5136
|
createContainerRecipeExecutor,
|
|
5137
|
+
describeCodeRecipes,
|
|
5030
5138
|
describePatchFailure,
|
|
5031
5139
|
digestStagedWorkspace,
|
|
5032
5140
|
feedbackIsActionable,
|
|
@@ -5041,16 +5149,19 @@ async function installedDependencies(repoRoot) {
|
|
|
5041
5149
|
materializeGitTree,
|
|
5042
5150
|
outcomeCloses,
|
|
5043
5151
|
outcomeMemory,
|
|
5152
|
+
parseRepositoryRecipes,
|
|
5044
5153
|
patchPaths,
|
|
5045
5154
|
planReachCollisions,
|
|
5046
5155
|
prepareRuntimeCheckpoint,
|
|
5047
5156
|
racedAttempt,
|
|
5048
5157
|
readOnlyNotice,
|
|
5158
|
+
readRepositoryRecipes,
|
|
5049
5159
|
recallAbout,
|
|
5050
5160
|
registeredFiles,
|
|
5051
5161
|
renderMemories,
|
|
5052
5162
|
renderOutcome,
|
|
5053
5163
|
resolveCodePath,
|
|
5164
|
+
resolveCodeRecipes,
|
|
5054
5165
|
restoreCodeWorkspaceCheckpoint,
|
|
5055
5166
|
runCodeAgent,
|
|
5056
5167
|
runCodeAgentAttempt,
|
|
@@ -5063,6 +5174,7 @@ async function installedDependencies(repoRoot) {
|
|
|
5063
5174
|
safeWorkspaceLabel,
|
|
5064
5175
|
selectContainerEngine,
|
|
5065
5176
|
selectWinner,
|
|
5177
|
+
sessionRecipesFor,
|
|
5066
5178
|
sessionSkillsFor,
|
|
5067
5179
|
stageWorkspace,
|
|
5068
5180
|
stageWorkspacePair,
|