@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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
// src/code-runtime-cli.ts
|
|
5
5
|
var import_node_os4 = require("os");
|
|
6
|
-
var
|
|
6
|
+
var import_promises14 = require("fs/promises");
|
|
7
7
|
|
|
8
8
|
// src/code-runtime-client-validation.ts
|
|
9
9
|
var import_code = require("@odla-ai/camel/code");
|
|
@@ -1412,8 +1412,8 @@ var CodeRuntimeCheckpointManager = class {
|
|
|
1412
1412
|
trustedBaseDigest: active.trustedBaseDigest,
|
|
1413
1413
|
planningInputDigest: active.planningInputDigest,
|
|
1414
1414
|
conversationRefs: active.conversationRefs,
|
|
1415
|
-
fallbackPolicyDigest: this.options.fallbackPolicyDigest,
|
|
1416
|
-
recipes: this.options.recipes,
|
|
1415
|
+
fallbackPolicyDigest: active.buildPolicyDigest ?? this.options.fallbackPolicyDigest,
|
|
1416
|
+
recipes: active.recipes ?? this.options.recipes,
|
|
1417
1417
|
recipeExecutor: this.options.recipeExecutor,
|
|
1418
1418
|
review: (patch2, verification) => this.options.control.review(command.sessionId, { patch: patch2, verification })
|
|
1419
1419
|
});
|
|
@@ -2354,13 +2354,19 @@ function createCodeRuntimeSessionSkillLoader(control) {
|
|
|
2354
2354
|
...tool.acceptsTaint === void 0 ? {} : { acceptsTaint: tool.acceptsTaint },
|
|
2355
2355
|
handler: async (input, context) => {
|
|
2356
2356
|
if (!context.toolCallId) throw new TypeError("collaboration tool call identity is required");
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2357
|
+
try {
|
|
2358
|
+
return await execute2(command.sessionId, {
|
|
2359
|
+
commandId: command.commandId,
|
|
2360
|
+
toolCallId: context.toolCallId,
|
|
2361
|
+
skill: manifest.name,
|
|
2362
|
+
tool: tool.name,
|
|
2363
|
+
input
|
|
2364
|
+
}, context.signal);
|
|
2365
|
+
} catch (cause) {
|
|
2366
|
+
if (context.signal?.aborted) throw cause;
|
|
2367
|
+
const detail = (cause instanceof Error ? cause.message : String(cause)).slice(0, 500);
|
|
2368
|
+
return { content: `Tool "${tool.name}" failed: ${detail}`, isError: true };
|
|
2369
|
+
}
|
|
2364
2370
|
}
|
|
2365
2371
|
}))
|
|
2366
2372
|
}));
|
|
@@ -3806,6 +3812,96 @@ function observeCodeRuntimeSessionSkills(command, skills, emit) {
|
|
|
3806
3812
|
}));
|
|
3807
3813
|
}
|
|
3808
3814
|
|
|
3815
|
+
// src/code-repository-recipes.ts
|
|
3816
|
+
var import_promises13 = require("fs/promises");
|
|
3817
|
+
var import_node_path12 = require("path");
|
|
3818
|
+
var REPOSITORY_RECIPES_FILE = "odla.recipes.json";
|
|
3819
|
+
var MAX_RECIPES = 16;
|
|
3820
|
+
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
3821
|
+
var MAX_TIMEOUT_MS = 15 * 6e4;
|
|
3822
|
+
var ID2 = /^[A-Za-z0-9._:-]{1,120}$/;
|
|
3823
|
+
var RECIPE_FIELDS = /* @__PURE__ */ new Set(["id", "command", "timeoutMs"]);
|
|
3824
|
+
var fault = (detail) => new TypeError(`${REPOSITORY_RECIPES_FILE} is malformed: ${detail}`);
|
|
3825
|
+
function parseRepositoryRecipes(text2, envelope) {
|
|
3826
|
+
let parsed;
|
|
3827
|
+
try {
|
|
3828
|
+
parsed = JSON.parse(text2);
|
|
3829
|
+
} catch {
|
|
3830
|
+
throw fault("not valid JSON");
|
|
3831
|
+
}
|
|
3832
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw fault("the document must be an object");
|
|
3833
|
+
const document = parsed;
|
|
3834
|
+
if (document.version !== 1) throw fault("version must be 1");
|
|
3835
|
+
const entries = document.recipes;
|
|
3836
|
+
if (!Array.isArray(entries) || entries.length < 1) throw fault("recipes must be a non-empty array");
|
|
3837
|
+
if (entries.length > MAX_RECIPES) throw fault(`at most ${MAX_RECIPES} recipes may be declared`);
|
|
3838
|
+
const recipes = [];
|
|
3839
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3840
|
+
entries.forEach((entry, index) => {
|
|
3841
|
+
const label = `recipe ${index + 1}`;
|
|
3842
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) throw fault(`${label} must be an object`);
|
|
3843
|
+
const row = entry;
|
|
3844
|
+
const unknown = Object.keys(row).filter((key) => !RECIPE_FIELDS.has(key));
|
|
3845
|
+
if (unknown.length) throw fault(`${label} has unsupported field ${unknown[0]}; the host owns image and resource limits`);
|
|
3846
|
+
if (typeof row.id !== "string" || !ID2.test(row.id)) throw fault(`${label} needs an id of 1 to 120 letters, digits, . _ : or -`);
|
|
3847
|
+
if (seen.has(row.id)) throw fault(`${label} repeats id ${row.id}`);
|
|
3848
|
+
seen.add(row.id);
|
|
3849
|
+
if (!Array.isArray(row.command) || row.command.length < 1 || row.command.some((part) => typeof part !== "string" || !part)) {
|
|
3850
|
+
throw fault(`${label} (${row.id}) needs command as a non-empty array of non-empty strings`);
|
|
3851
|
+
}
|
|
3852
|
+
const timeoutMs = row.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
3853
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > MAX_TIMEOUT_MS) {
|
|
3854
|
+
throw fault(`${label} (${row.id}) timeoutMs must be an integer from 1 to ${MAX_TIMEOUT_MS}`);
|
|
3855
|
+
}
|
|
3856
|
+
const recipe2 = {
|
|
3857
|
+
id: row.id,
|
|
3858
|
+
command: [...row.command],
|
|
3859
|
+
timeoutMs,
|
|
3860
|
+
image: envelope.image,
|
|
3861
|
+
maxOutputBytes: envelope.maxOutputBytes,
|
|
3862
|
+
cpus: envelope.cpus,
|
|
3863
|
+
memory: envelope.memory,
|
|
3864
|
+
pids: envelope.pids
|
|
3865
|
+
};
|
|
3866
|
+
try {
|
|
3867
|
+
assertCodeBuildRecipe(recipe2);
|
|
3868
|
+
} catch (cause) {
|
|
3869
|
+
throw fault(`${label} (${row.id}) is not a runnable recipe: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
3870
|
+
}
|
|
3871
|
+
recipes.push(recipe2);
|
|
3872
|
+
});
|
|
3873
|
+
return recipes;
|
|
3874
|
+
}
|
|
3875
|
+
async function readRepositoryRecipes(baselineDir, envelope) {
|
|
3876
|
+
let text2;
|
|
3877
|
+
try {
|
|
3878
|
+
text2 = await (0, import_promises13.readFile)((0, import_node_path12.join)(baselineDir, REPOSITORY_RECIPES_FILE), "utf8");
|
|
3879
|
+
} catch (cause) {
|
|
3880
|
+
if (cause.code === "ENOENT") return null;
|
|
3881
|
+
throw cause;
|
|
3882
|
+
}
|
|
3883
|
+
return parseRepositoryRecipes(text2, envelope);
|
|
3884
|
+
}
|
|
3885
|
+
async function resolveCodeRecipes(baselineDir, release, envelope) {
|
|
3886
|
+
const declared = envelope ? await readRepositoryRecipes(baselineDir, envelope) : null;
|
|
3887
|
+
return declared ? { recipes: declared, source: "repository" } : { recipes: release, source: "release" };
|
|
3888
|
+
}
|
|
3889
|
+
function describeCodeRecipes(resolved) {
|
|
3890
|
+
const ids = resolved.recipes.map((recipe2) => recipe2.id).join(", ");
|
|
3891
|
+
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}`;
|
|
3892
|
+
}
|
|
3893
|
+
async function sessionRecipesFor(workspace, options, resume) {
|
|
3894
|
+
const resolved = await resolveCodeRecipes(workspace.baselineDir, options.recipes, options.repositoryRecipes ?? null).catch(async (cause) => {
|
|
3895
|
+
await workspace.cleanup();
|
|
3896
|
+
throw cause;
|
|
3897
|
+
});
|
|
3898
|
+
return {
|
|
3899
|
+
...resolved,
|
|
3900
|
+
buildPolicyDigest: digestRuntimeValue(JSON.stringify(resolved.recipes)),
|
|
3901
|
+
note: !resume && resolved.source === "repository" ? describeCodeRecipes(resolved) : null
|
|
3902
|
+
};
|
|
3903
|
+
}
|
|
3904
|
+
|
|
3809
3905
|
// src/code-runtime-engine.ts
|
|
3810
3906
|
var TheseusRuntimeEngine = class {
|
|
3811
3907
|
constructor(options) {
|
|
@@ -3857,6 +3953,7 @@ var TheseusRuntimeEngine = class {
|
|
|
3857
3953
|
control: this.options.control,
|
|
3858
3954
|
...this.options.localSource ? { localSource: this.options.localSource } : {}
|
|
3859
3955
|
});
|
|
3956
|
+
const resolvedRecipes = await sessionRecipesFor(workspace, this.options, resume);
|
|
3860
3957
|
const abort = new AbortController(), startGate = codeRuntimeAcknowledgementGate(abort.signal);
|
|
3861
3958
|
const conversationRefs = [];
|
|
3862
3959
|
const active = {
|
|
@@ -3865,6 +3962,8 @@ var TheseusRuntimeEngine = class {
|
|
|
3865
3962
|
conversationRefs,
|
|
3866
3963
|
acknowledged: false,
|
|
3867
3964
|
startGate,
|
|
3965
|
+
recipes: resolvedRecipes.recipes,
|
|
3966
|
+
buildPolicyDigest: resolvedRecipes.buildPolicyDigest,
|
|
3868
3967
|
role: metadata.role,
|
|
3869
3968
|
readOnly: metadata.readOnly,
|
|
3870
3969
|
title: metadata.title,
|
|
@@ -3882,6 +3981,7 @@ var TheseusRuntimeEngine = class {
|
|
|
3882
3981
|
done: Promise.resolve(null)
|
|
3883
3982
|
};
|
|
3884
3983
|
this.#active.set(command.sessionId, active);
|
|
3984
|
+
if (resolvedRecipes.note) await this.#event(command, { type: "message", actor: "system", body: resolvedRecipes.note }, conversationRefs);
|
|
3885
3985
|
if (requestedLocal) {
|
|
3886
3986
|
await this.#event(command, {
|
|
3887
3987
|
type: "message",
|
|
@@ -3917,7 +4017,7 @@ var TheseusRuntimeEngine = class {
|
|
|
3917
4017
|
const active = await this.#takeOver(command, "pursue requires an active Code session");
|
|
3918
4018
|
active.done = startGoalPursuit({
|
|
3919
4019
|
spec,
|
|
3920
|
-
recipes:
|
|
4020
|
+
recipes: active.recipes,
|
|
3921
4021
|
recipeExecutor: this.options.recipeExecutor ?? createContainerRecipeExecutor(this.options.engine),
|
|
3922
4022
|
workspace: active.workspace,
|
|
3923
4023
|
baseCommitSha: active.baseCommitSha,
|
|
@@ -3996,7 +4096,7 @@ var TheseusRuntimeEngine = class {
|
|
|
3996
4096
|
async #runAttempt(command, metadata, active) {
|
|
3997
4097
|
const lease = fakeCodeLease(command, metadata);
|
|
3998
4098
|
const broker = this.#observed(command, active, createCodeRuntimeToolBroker({
|
|
3999
|
-
recipes:
|
|
4099
|
+
recipes: active.recipes,
|
|
4000
4100
|
engine: this.options.engine,
|
|
4001
4101
|
recipeAuthorization: this.options.recipeAuthorization
|
|
4002
4102
|
}, lease, metadata.role));
|
|
@@ -4022,7 +4122,7 @@ var TheseusRuntimeEngine = class {
|
|
|
4022
4122
|
// A review-role session is read-only and still runs the proof; a planner
|
|
4023
4123
|
// marked read-only by its payload runs nothing.
|
|
4024
4124
|
recipes: metadata.role === "review" || !metadata.readOnly,
|
|
4025
|
-
recipeIds:
|
|
4125
|
+
recipeIds: active.recipes.map((recipe2) => recipe2.id),
|
|
4026
4126
|
...extraSkills.length ? { extraSkills } : {}
|
|
4027
4127
|
});
|
|
4028
4128
|
const closing = result.finalText.trim();
|
|
@@ -4131,7 +4231,7 @@ function parse(argv) {
|
|
|
4131
4231
|
};
|
|
4132
4232
|
}
|
|
4133
4233
|
async function readPolicy(path) {
|
|
4134
|
-
const value = JSON.parse(await (0,
|
|
4234
|
+
const value = JSON.parse(await (0, import_promises14.readFile)(path, "utf8"));
|
|
4135
4235
|
if (!value || Object.keys(value).some((key) => !["recipes", "recipeAuthorization"].includes(key)) || !Array.isArray(value.recipes) || !value.recipes.length || value.recipeAuthorization !== void 0 && value.recipeAuthorization !== "registered_recipe" && value.recipeAuthorization !== "exact_approval") throw new TypeError("invalid Code build policy file");
|
|
4136
4236
|
const recipes = value.recipes;
|
|
4137
4237
|
for (const recipe2 of recipes) assertCodeBuildRecipe(recipe2);
|