@odla-ai/cli 0.40.2 → 0.41.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.
- package/bin/odla-ai.js +17 -7
- package/bin/stale-build.d.ts +16 -0
- package/bin/stale-build.js +77 -0
- package/bin/workspace-build.d.ts +5 -0
- package/bin/workspace-build.js +16 -4
- package/dist/bin.cjs +52 -54
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-XUTMQMXZ.js → chunk-6RGM4W73.js} +49 -51
- package/dist/chunk-6RGM4W73.js.map +1 -0
- package/dist/{cli-WJSQEBDO.js → cli-N5PASRYU.js} +2 -2
- package/dist/index.cjs +50 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-XUTMQMXZ.js.map +0 -1
- /package/dist/{cli-WJSQEBDO.js.map → cli-N5PASRYU.js.map} +0 -0
package/bin/odla-ai.js
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
import { workspaceBuildOrder } from "./workspace-build.js";
|
|
7
|
+
import { firstStaleWorkspace, staleBuildNotice } from "./stale-build.js";
|
|
7
8
|
|
|
8
9
|
const packageRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
9
10
|
const builtEntry = join(packageRoot, "dist", "bin.js");
|
|
11
|
+
const workspaceRoot = join(packageRoot, "..", "..");
|
|
12
|
+
const rootManifestPath = join(workspaceRoot, "package.json");
|
|
13
|
+
const rootManifest = existsSync(rootManifestPath)
|
|
14
|
+
? JSON.parse(readFileSync(rootManifestPath, "utf8"))
|
|
15
|
+
: null;
|
|
16
|
+
const inWorkspace = rootManifest?.name === "odla-ai" && rootManifest.private === true;
|
|
10
17
|
|
|
11
18
|
if (!existsSync(builtEntry)) {
|
|
12
|
-
|
|
13
|
-
const rootManifestPath = join(workspaceRoot, "package.json");
|
|
14
|
-
const rootManifest = existsSync(rootManifestPath)
|
|
15
|
-
? JSON.parse(readFileSync(rootManifestPath, "utf8"))
|
|
16
|
-
: null;
|
|
17
|
-
if (rootManifest?.name !== "odla-ai" || rootManifest.private !== true) {
|
|
19
|
+
if (!inWorkspace) {
|
|
18
20
|
throw new Error("odla-ai CLI build is missing; reinstall @odla-ai/cli from npm");
|
|
19
21
|
}
|
|
20
22
|
|
|
@@ -28,6 +30,14 @@ if (!existsSync(builtEntry)) {
|
|
|
28
30
|
);
|
|
29
31
|
if (child.error) throw child.error;
|
|
30
32
|
if (child.status !== 0) process.exit(child.status ?? 1);
|
|
33
|
+
} else if (inWorkspace) {
|
|
34
|
+
// stderr, never stdout: every --json command stays parseable.
|
|
35
|
+
const stale = firstStaleWorkspace(
|
|
36
|
+
workspaceRoot,
|
|
37
|
+
workspaceBuildOrder(workspaceRoot),
|
|
38
|
+
statSync(builtEntry).mtimeMs,
|
|
39
|
+
);
|
|
40
|
+
if (stale) console.error(staleBuildNotice(stale));
|
|
31
41
|
}
|
|
32
42
|
|
|
33
43
|
await import(pathToFileURL(builtEntry).href);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** A workspace whose sources are newer than the built entry about to run. */
|
|
2
|
+
export interface StaleWorkspace {
|
|
3
|
+
workspace: string;
|
|
4
|
+
file: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/** The first workspace in `workspaceNames` whose `src` is newer than
|
|
8
|
+
* `builtAtMs`, or null when the build is current or the tree is unreadable. */
|
|
9
|
+
export declare function firstStaleWorkspace(
|
|
10
|
+
root: string,
|
|
11
|
+
workspaceNames: readonly string[],
|
|
12
|
+
builtAtMs: number,
|
|
13
|
+
): StaleWorkspace | null;
|
|
14
|
+
|
|
15
|
+
/** The one-line stderr notice naming the stale workspace and the way out. */
|
|
16
|
+
export declare function staleBuildNotice(stale: StaleWorkspace): string;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// A workspace checkout runs the CLI through a symlink into packages/cli, so
|
|
2
|
+
// `npx odla-ai` answers from whatever was last built — which can be a day older
|
|
3
|
+
// than the source sitting next to it, with nothing saying so. That is not a
|
|
4
|
+
// cosmetic problem: an agent reads the stale output, believes it, and reasons
|
|
5
|
+
// from a surface that no longer exists. The launcher already handles "not built
|
|
6
|
+
// at all"; this handles "built, but behind".
|
|
7
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { workspacePackages } from "./workspace-build.js";
|
|
10
|
+
|
|
11
|
+
/** Directories that never hold sources worth comparing. */
|
|
12
|
+
const SKIP = new Set(["node_modules", "dist", "js", ".turbo", ".wrangler"]);
|
|
13
|
+
|
|
14
|
+
/** First file under `dir` modified after `builtAtMs`, or null. Depth-first and
|
|
15
|
+
* early-exiting: the answer is "is anything newer", not "what is newest". */
|
|
16
|
+
function firstNewerFile(dir, builtAtMs) {
|
|
17
|
+
let entries;
|
|
18
|
+
try {
|
|
19
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
for (const entry of entries) {
|
|
24
|
+
if (entry.name.startsWith(".") || SKIP.has(entry.name)) continue;
|
|
25
|
+
const path = join(dir, entry.name);
|
|
26
|
+
if (entry.isDirectory()) {
|
|
27
|
+
const found = firstNewerFile(path, builtAtMs);
|
|
28
|
+
if (found) return found;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
if (statSync(path).mtimeMs > builtAtMs) return path;
|
|
33
|
+
} catch {
|
|
34
|
+
// A file that vanished mid-walk is not evidence of staleness.
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The first workspace whose sources are newer than the built entry about to be
|
|
42
|
+
* run, or null when the build is current.
|
|
43
|
+
*
|
|
44
|
+
* Checks the CLI's whole internal dependency closure, not just its own `src`:
|
|
45
|
+
* a stale `@odla-ai/db` breaks the CLI just as thoroughly, and reports itself
|
|
46
|
+
* as a missing export rather than as a stale build.
|
|
47
|
+
*
|
|
48
|
+
* Returns null rather than throwing for anything unreadable — a diagnostic
|
|
49
|
+
* must never be the reason a command fails.
|
|
50
|
+
*/
|
|
51
|
+
export function firstStaleWorkspace(root, workspaceNames, builtAtMs) {
|
|
52
|
+
try {
|
|
53
|
+
const packages = workspacePackages(root);
|
|
54
|
+
for (const name of workspaceNames) {
|
|
55
|
+
const entry = packages.get(name);
|
|
56
|
+
if (!entry) continue;
|
|
57
|
+
// A published install ships `dist` and no `src`, so its absence is how we
|
|
58
|
+
// know this is not a source checkout rather than a stale one.
|
|
59
|
+
const source = join(entry.dir, "src");
|
|
60
|
+
if (!existsSync(source)) continue;
|
|
61
|
+
const file = firstNewerFile(source, builtAtMs);
|
|
62
|
+
if (file) return { workspace: name, file };
|
|
63
|
+
}
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The one line a stale workspace build is worth, and what ends it. */
|
|
71
|
+
export function staleBuildNotice(stale) {
|
|
72
|
+
return (
|
|
73
|
+
`odla-ai: running a stale build — ${stale.workspace} source is newer than ` +
|
|
74
|
+
"the built CLI, so this output can describe code that no longer exists. " +
|
|
75
|
+
"Run `npm run build`."
|
|
76
|
+
);
|
|
77
|
+
}
|
package/bin/workspace-build.d.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
/** Return buildable internal dependencies in dependency-first order. */
|
|
2
2
|
export declare function workspaceBuildOrder(root: string): string[];
|
|
3
|
+
|
|
4
|
+
/** Every workspace in the checkout, by package name, with its directory. */
|
|
5
|
+
export declare function workspacePackages(
|
|
6
|
+
root: string,
|
|
7
|
+
): Map<string, { dir: string; manifest: Record<string, unknown> }>;
|
package/bin/workspace-build.js
CHANGED
|
@@ -3,8 +3,10 @@ import { join } from "node:path";
|
|
|
3
3
|
|
|
4
4
|
const manifestAt = (path) => JSON.parse(readFileSync(path, "utf8"));
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
|
|
6
|
+
/** Every workspace in the checkout, by package name, with the directory it
|
|
7
|
+
* lives in. Shared so the launcher's build order and its staleness check agree
|
|
8
|
+
* on what a workspace is and where its sources are. */
|
|
9
|
+
export function workspacePackages(root) {
|
|
8
10
|
const rootManifest = manifestAt(join(root, "package.json"));
|
|
9
11
|
const workspaces = new Map();
|
|
10
12
|
for (const pattern of rootManifest.workspaces ?? []) {
|
|
@@ -13,12 +15,22 @@ export function workspaceBuildOrder(root) {
|
|
|
13
15
|
if (!existsSync(parent)) continue;
|
|
14
16
|
for (const entry of readdirSync(parent, { withFileTypes: true })) {
|
|
15
17
|
if (!entry.isDirectory()) continue;
|
|
16
|
-
const
|
|
18
|
+
const dir = join(parent, entry.name);
|
|
19
|
+
const manifestPath = join(dir, "package.json");
|
|
17
20
|
if (!existsSync(manifestPath)) continue;
|
|
18
21
|
const manifest = manifestAt(manifestPath);
|
|
19
|
-
if (manifest.name) workspaces.set(manifest.name, manifest);
|
|
22
|
+
if (manifest.name) workspaces.set(manifest.name, { dir, manifest });
|
|
20
23
|
}
|
|
21
24
|
}
|
|
25
|
+
return workspaces;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Return buildable internal dependencies in dependency-first order. */
|
|
29
|
+
export function workspaceBuildOrder(root) {
|
|
30
|
+
const packages = workspacePackages(root);
|
|
31
|
+
const workspaces = new Map(
|
|
32
|
+
[...packages].map(([name, entry]) => [name, entry.manifest]),
|
|
33
|
+
);
|
|
22
34
|
|
|
23
35
|
const visited = new Set();
|
|
24
36
|
const order = [];
|
package/dist/bin.cjs
CHANGED
|
@@ -7915,7 +7915,7 @@ var init_code2 = __esm({
|
|
|
7915
7915
|
}
|
|
7916
7916
|
});
|
|
7917
7917
|
|
|
7918
|
-
// ../harness/dist/chunk-
|
|
7918
|
+
// ../harness/dist/chunk-5HX5LWTG.js
|
|
7919
7919
|
async function digestStagedWorkspace(root, limits) {
|
|
7920
7920
|
const files = [];
|
|
7921
7921
|
const walk = async (directory) => {
|
|
@@ -9013,6 +9013,7 @@ async function runCodeAgent(options) {
|
|
|
9013
9013
|
}
|
|
9014
9014
|
async function runCodeAgentAttempt(options) {
|
|
9015
9015
|
try {
|
|
9016
|
+
const surface = options.surface ?? "v2";
|
|
9016
9017
|
const { run } = await runCodeAgent({
|
|
9017
9018
|
inference: options.inference,
|
|
9018
9019
|
broker: options.broker,
|
|
@@ -9022,17 +9023,31 @@ async function runCodeAgentAttempt(options) {
|
|
|
9022
9023
|
// The brokered route resolves the real model from platform policy; this
|
|
9023
9024
|
// id only labels the request the control plane is about to rewrite.
|
|
9024
9025
|
model: "brokered",
|
|
9025
|
-
surface
|
|
9026
|
+
surface,
|
|
9026
9027
|
...options.maxSteps === void 0 ? {} : { maxSteps: options.maxSteps },
|
|
9027
9028
|
...options.budget ? { budget: options.budget } : {},
|
|
9028
9029
|
...options.signal ? { signal: options.signal } : {},
|
|
9029
9030
|
...options.onToolCall ? { onToolCall: options.onToolCall } : {}
|
|
9030
9031
|
});
|
|
9032
|
+
let finalText = run.finalText.trim();
|
|
9033
|
+
if (!finalText && run.stoppedReason !== "refusal") {
|
|
9034
|
+
const closing = await options.inference.chat({
|
|
9035
|
+
model: "brokered",
|
|
9036
|
+
system: `${SYSTEM_PROMPT_FOR[surface]}
|
|
9037
|
+
|
|
9038
|
+
Finish with a concise, non-empty answer to the owner. Do not call tools or promise future work.`,
|
|
9039
|
+
messages: [...run.messages, { role: "user", content: "Give the owner the closing answer now, grounded in the repository evidence and tool results above." }],
|
|
9040
|
+
maxTokens: 16384,
|
|
9041
|
+
...options.signal ? { signal: options.signal } : {}
|
|
9042
|
+
});
|
|
9043
|
+
finalText = (0, import_ai5.extractText)(closing.content).trim();
|
|
9044
|
+
}
|
|
9045
|
+
const missingClosing = !finalText && run.stoppedReason !== "refusal";
|
|
9031
9046
|
return {
|
|
9032
|
-
status: run.stoppedReason === "refusal" ? "failed" : "completed",
|
|
9033
|
-
finalText
|
|
9047
|
+
status: run.stoppedReason === "refusal" || missingClosing ? "failed" : "completed",
|
|
9048
|
+
finalText,
|
|
9034
9049
|
stoppedReason: run.stoppedReason,
|
|
9035
|
-
...run.stoppedReason === "refusal" ? { error:
|
|
9050
|
+
...run.stoppedReason === "refusal" ? { error: finalText || "the agent refused the task" } : missingClosing ? { error: "the Code agent did not produce a closing answer" } : {}
|
|
9036
9051
|
};
|
|
9037
9052
|
} catch (cause) {
|
|
9038
9053
|
const error = (cause instanceof Error ? cause.message : String(cause)).slice(0, 2e3);
|
|
@@ -9040,31 +9055,7 @@ async function runCodeAgentAttempt(options) {
|
|
|
9040
9055
|
}
|
|
9041
9056
|
}
|
|
9042
9057
|
async function handleCodeRuntimeInference(input) {
|
|
9043
|
-
const { command,
|
|
9044
|
-
if (state2.tokens >= metadata2.maxTokensPerInteraction) {
|
|
9045
|
-
if (!state2.noticeEmitted) {
|
|
9046
|
-
state2.noticeEmitted = true;
|
|
9047
|
-
await input.event({
|
|
9048
|
-
type: "message",
|
|
9049
|
-
actor: "system",
|
|
9050
|
-
body: `The agent paused at the ${metadata2.maxTokensPerInteraction.toLocaleString("en-US")}-token per-interaction limit. Send a new instruction to continue.`
|
|
9051
|
-
}).catch(() => void 0);
|
|
9052
|
-
}
|
|
9053
|
-
return {
|
|
9054
|
-
protocolVersion: HARNESS_PROTOCOL_VERSION,
|
|
9055
|
-
type: "inference.response",
|
|
9056
|
-
requestId: request3.requestId,
|
|
9057
|
-
response: {
|
|
9058
|
-
id: `budget:${command.commandId}`,
|
|
9059
|
-
provider: "openai",
|
|
9060
|
-
model: "interaction-budget",
|
|
9061
|
-
role: "assistant",
|
|
9062
|
-
content: [{ type: "text", text: "Pause now. The owner-set token limit for this interaction has been reached." }],
|
|
9063
|
-
stopReason: "end_turn",
|
|
9064
|
-
usage: { inputTokens: 0, outputTokens: 0 }
|
|
9065
|
-
}
|
|
9066
|
-
};
|
|
9067
|
-
}
|
|
9058
|
+
const { command, request: request3, state: state2 } = input;
|
|
9068
9059
|
const startedAt = Date.now();
|
|
9069
9060
|
const response2 = await input.control.infer(command.sessionId, {
|
|
9070
9061
|
requestId: request3.requestId,
|
|
@@ -9084,7 +9075,6 @@ async function handleCodeRuntimeInference(input) {
|
|
|
9084
9075
|
durationMs: Date.now() - startedAt,
|
|
9085
9076
|
interactionId: command.commandId,
|
|
9086
9077
|
interactionTokens: state2.tokens,
|
|
9087
|
-
interactionMaxTokens: metadata2.maxTokensPerInteraction,
|
|
9088
9078
|
...costUsd === void 0 ? {} : { costUsd },
|
|
9089
9079
|
...state2.costKnown ? { interactionCostUsd: state2.costUsd } : {}
|
|
9090
9080
|
}).catch(() => void 0);
|
|
@@ -9914,9 +9904,9 @@ async function appendCodeRuntimeEvent(control, command, event, refs) {
|
|
|
9914
9904
|
const bounded = event.type === "message" ? { ...event, body: event.body.trim().slice(0, 2e4) || `${event.actor} event` } : event;
|
|
9915
9905
|
await control.appendSessionEvent(command.sessionId, eventId, bounded);
|
|
9916
9906
|
}
|
|
9917
|
-
var import_crypto, import_promises5, import_path5, import_child_process4, import_promises6, import_path6, import_child_process5, import_process2, import_crypto2, import_crypto3, import_fs2, import_promises7, import_path7, import_promises8, import_os3, import_path8, import_ai4, import_promises9, import_path9, import_promises10, import_promises11, import_path10, import_crypto4, CODE_RUNTIME_PROTOCOL_VERSION, CodeRuntimeReconciler, CodeRuntimeControlError, record5, invalid, RESERVED, SECRET, PATH, FORBIDDEN, ARTIFACT_PATH, PRIVATE_ARTIFACT_PART, SHA3, DIGEST3, ID3, RULE, DEFAULT_PREFIXES, DEFAULT_SUFFIXES, message, CodeRuntimeCheckpointManager, record22, SOURCE_LIMITS, RESERVED2, SECRET2, SOURCE_MAX_FILES, SOURCE_MAX_BYTES, SOURCE_SET_MAX_BYTES, V1_SYSTEM_PROMPT, V2_SYSTEM_PROMPT, V3_SYSTEM_PROMPT, SYSTEM_PROMPT_FOR, DEFAULT_MAX_FILES, DEFAULT_MAX_RESULTS, DEFAULT_MAX_FILE_BYTES, DESTINATIONS, READ, LIST, SEARCH, GRAPH, PATCH, RECIPE, cache, shortId, GRAPH_TOOLS, MAX_MEMORY_BODY, POSITIVE, digestRuntimeValue, runtimeErrorMessage, CodePiRuntimeEngine;
|
|
9918
|
-
var
|
|
9919
|
-
"../harness/dist/chunk-
|
|
9907
|
+
var import_crypto, import_promises5, import_path5, import_child_process4, import_promises6, import_path6, import_child_process5, import_process2, import_crypto2, import_crypto3, import_fs2, import_promises7, import_path7, import_promises8, import_os3, import_path8, import_ai4, import_ai5, import_promises9, import_path9, import_promises10, import_promises11, import_path10, import_crypto4, CODE_RUNTIME_PROTOCOL_VERSION, CodeRuntimeReconciler, CodeRuntimeControlError, record5, invalid, RESERVED, SECRET, PATH, FORBIDDEN, ARTIFACT_PATH, PRIVATE_ARTIFACT_PART, SHA3, DIGEST3, ID3, RULE, DEFAULT_PREFIXES, DEFAULT_SUFFIXES, message, CodeRuntimeCheckpointManager, record22, SOURCE_LIMITS, RESERVED2, SECRET2, SOURCE_MAX_FILES, SOURCE_MAX_BYTES, SOURCE_SET_MAX_BYTES, V1_SYSTEM_PROMPT, V2_SYSTEM_PROMPT, V3_SYSTEM_PROMPT, SYSTEM_PROMPT_FOR, DEFAULT_MAX_FILES, DEFAULT_MAX_RESULTS, DEFAULT_MAX_FILE_BYTES, DESTINATIONS, READ, LIST, SEARCH, GRAPH, PATCH, RECIPE, cache, shortId, GRAPH_TOOLS, MAX_MEMORY_BODY, POSITIVE, digestRuntimeValue, runtimeErrorMessage, CodePiRuntimeEngine;
|
|
9908
|
+
var init_chunk_5HX5LWTG = __esm({
|
|
9909
|
+
"../harness/dist/chunk-5HX5LWTG.js"() {
|
|
9920
9910
|
"use strict";
|
|
9921
9911
|
init_cjs_shims();
|
|
9922
9912
|
init_chunk_K76I2TCQ();
|
|
@@ -9941,6 +9931,7 @@ var init_chunk_L2T3LPEF = __esm({
|
|
|
9941
9931
|
import_os3 = require("os");
|
|
9942
9932
|
import_path8 = require("path");
|
|
9943
9933
|
import_ai4 = require("@odla-ai/ai");
|
|
9934
|
+
import_ai5 = require("@odla-ai/ai");
|
|
9944
9935
|
import_promises9 = require("fs/promises");
|
|
9945
9936
|
import_path9 = require("path");
|
|
9946
9937
|
init_dist();
|
|
@@ -10337,7 +10328,7 @@ Never claim a build or test passed unless odla_run_recipe returned that result.`
|
|
|
10337
10328
|
recipeAuthorization: this.options.recipeAuthorization
|
|
10338
10329
|
}, lease, metadata2.role));
|
|
10339
10330
|
const startedAt = Date.now();
|
|
10340
|
-
const interaction = { tokens: 0,
|
|
10331
|
+
const interaction = { tokens: 0, costUsd: 0, costKnown: true };
|
|
10341
10332
|
const inference = createCodeRuntimeInference({
|
|
10342
10333
|
command,
|
|
10343
10334
|
metadata: metadata2,
|
|
@@ -10352,31 +10343,30 @@ Never claim a build or test passed unless odla_run_recipe returned that result.`
|
|
|
10352
10343
|
lease,
|
|
10353
10344
|
workspaceDir: active.workspace.workspaceDir,
|
|
10354
10345
|
prompt: metadata2.prompt,
|
|
10355
|
-
signal: active.abort.signal
|
|
10356
|
-
// The owner's per-interaction allowance, enforced by runAgent against
|
|
10357
|
-
// INCREMENTAL usage. The control plane still reserves against the same
|
|
10358
|
-
// ceiling, but this is what stops the loop cleanly at the boundary rather
|
|
10359
|
-
// than letting it discover the limit through a synthesized pause reply.
|
|
10360
|
-
budget: { maxTotalTokens: metadata2.maxTokensPerInteraction }
|
|
10346
|
+
signal: active.abort.signal
|
|
10361
10347
|
});
|
|
10362
|
-
const
|
|
10348
|
+
const closing = result.finalText.trim();
|
|
10349
|
+
const completed = result.status === "completed" && Boolean(closing);
|
|
10350
|
+
const detail = result.error?.trim() || (closing ? "the Code agent failed" : "the Code agent did not produce a closing answer");
|
|
10351
|
+
const body = closing || detail;
|
|
10363
10352
|
await this.#event(command, {
|
|
10364
10353
|
type: "message",
|
|
10365
|
-
actor:
|
|
10354
|
+
actor: completed ? "agent" : "system",
|
|
10366
10355
|
body
|
|
10367
10356
|
}, active.conversationRefs).catch(() => void 0);
|
|
10368
10357
|
await this.#event(command, {
|
|
10369
10358
|
type: "status",
|
|
10370
|
-
status:
|
|
10359
|
+
status: completed ? "idle" : "failed",
|
|
10371
10360
|
durationMs: Date.now() - startedAt
|
|
10372
10361
|
}, active.conversationRefs).catch(() => void 0);
|
|
10373
|
-
if (
|
|
10374
|
-
const detail = (result.error ?? "").trim() || "the Code agent failed";
|
|
10362
|
+
if (!completed) {
|
|
10375
10363
|
await this.#diagnostic(command, active, detail);
|
|
10376
10364
|
await this.#failure(command, active, detail);
|
|
10377
10365
|
}
|
|
10378
10366
|
return {
|
|
10379
10367
|
...result,
|
|
10368
|
+
status: completed ? "completed" : "failed",
|
|
10369
|
+
...!completed ? { error: detail } : {},
|
|
10380
10370
|
tokens: interaction.tokens,
|
|
10381
10371
|
...interaction.costKnown ? { costUsd: interaction.costUsd } : {}
|
|
10382
10372
|
};
|
|
@@ -10438,7 +10428,7 @@ var init_node = __esm({
|
|
|
10438
10428
|
"../harness/dist/node.js"() {
|
|
10439
10429
|
"use strict";
|
|
10440
10430
|
init_cjs_shims();
|
|
10441
|
-
|
|
10431
|
+
init_chunk_5HX5LWTG();
|
|
10442
10432
|
init_chunk_K76I2TCQ();
|
|
10443
10433
|
MEASURED_PREMIUM = Object.freeze({
|
|
10444
10434
|
/** 3 racers vs pure depth at equal budget: 21,044 / 7,936. */
|
|
@@ -11519,12 +11509,12 @@ Usage:
|
|
|
11519
11509
|
odla-ai pm <goal|task|decision|bug> rm <id>
|
|
11520
11510
|
odla-ai pm handoff --app <id> [--project <id>] [--json]
|
|
11521
11511
|
odla-ai discuss groups [--json]
|
|
11522
|
-
odla-ai discuss list [--app <id>] [--q <text>] [--state open|resolved|all] [--json]
|
|
11512
|
+
odla-ai discuss list [--app <id>] [--q <text>] [--state open|resolved|all] [--mentions] [--json]
|
|
11523
11513
|
odla-ai discuss read <topic> [--limit <n> --offset <n>] [--json]
|
|
11524
11514
|
odla-ai discuss post --app <id> --subject "..." --body "..." [--markup "... @[Label](kind/id)"] [--mutation-id <id>]
|
|
11525
11515
|
odla-ai discuss reply <topic> --body "..." [--markup "..."] [--mutation-id <id>]
|
|
11526
11516
|
odla-ai discuss resolve <topic> [--reopen] [--mutation-id <id>]
|
|
11527
|
-
odla-ai discuss who --q <text> [--app <id>] [--kinds user,pm:task] [--json]
|
|
11517
|
+
odla-ai discuss who --q <text> [--app <id>] [--topic <id>] [--kinds user,pm:task] [--json]
|
|
11528
11518
|
odla-ai discuss watch [<topic>] [--cursor <cursor>] [--by <authorId>] [--self <authorId>] [--interval <s>] [--timeout <s>] [--json|--jsonl]
|
|
11529
11519
|
odla-ai agent jobs [--env dev] [--state pending|running|succeeded|dead_letter] [--limit 50] [--token <ODLA_API_KEY>] [--json]
|
|
11530
11520
|
odla-ai agent retry <job-id> [--env dev] [--token <ODLA_API_KEY>] [--json]
|
|
@@ -11992,6 +11982,7 @@ async function discussList(ctx, parsed) {
|
|
|
11992
11982
|
const value2 = stringOpt(parsed.options[flag]);
|
|
11993
11983
|
if (value2) query.set(param, value2);
|
|
11994
11984
|
}
|
|
11985
|
+
if (parsed.options.mentions === true) query.set("mentions", "1");
|
|
11995
11986
|
const qs = query.toString();
|
|
11996
11987
|
const page2 = await request(ctx, "GET", `/topics${qs ? `?${qs}` : ""}`);
|
|
11997
11988
|
emit(ctx, page2, () => {
|
|
@@ -12098,11 +12089,16 @@ async function discussWho(ctx, parsed) {
|
|
|
12098
12089
|
if (app) query.set("app", app);
|
|
12099
12090
|
const kinds = stringOpt(parsed.options.kinds);
|
|
12100
12091
|
if (kinds) query.set("kinds", kinds);
|
|
12092
|
+
const topic = stringOpt(parsed.options.topic);
|
|
12093
|
+
if (topic) query.set("topic", topic);
|
|
12101
12094
|
const found = await request(ctx, "GET", `/mentionables?${query.toString()}`);
|
|
12102
12095
|
emit(ctx, found, () => {
|
|
12103
|
-
ctx.out.log("mention kind hint");
|
|
12096
|
+
ctx.out.log("mention kind use hint");
|
|
12104
12097
|
for (const item of found.items) {
|
|
12105
|
-
|
|
12098
|
+
const use = item.kind === "agent" ? item.meta?.executionAvailability === "runnable" ? "runnable" : "reference" : "";
|
|
12099
|
+
ctx.out.log(
|
|
12100
|
+
`@[${item.label}](${item.kind}/${item.id}) ${item.kind} ${use} ${item.hint ?? ""}`
|
|
12101
|
+
);
|
|
12106
12102
|
}
|
|
12107
12103
|
if (found.failed.length > 0) ctx.out.error(`(no results from: ${found.failed.join(", ")})`);
|
|
12108
12104
|
});
|
|
@@ -12457,6 +12453,8 @@ var init_discuss_command = __esm({
|
|
|
12457
12453
|
"reopen",
|
|
12458
12454
|
"by",
|
|
12459
12455
|
"self",
|
|
12456
|
+
"topic",
|
|
12457
|
+
"mentions",
|
|
12460
12458
|
"interval",
|
|
12461
12459
|
"timeout",
|
|
12462
12460
|
"cursor",
|
|
@@ -14752,7 +14750,7 @@ async function provision(options) {
|
|
|
14752
14750
|
const key = import_node_process17.default.env[cfg.ai.keyEnv];
|
|
14753
14751
|
if (key) {
|
|
14754
14752
|
const secretName = cfg.ai.secretName ?? defaultSecretName(cfg.ai.provider);
|
|
14755
|
-
await (0,
|
|
14753
|
+
await (0, import_ai6.putSecret)({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
|
|
14756
14754
|
out.log(`${env}: ${cfg.ai.provider} key stored in vault (${secretName})`);
|
|
14757
14755
|
} else {
|
|
14758
14756
|
out.log(`${env}: ${cfg.ai.keyEnv} not set; skipped provider key storage`);
|
|
@@ -14788,13 +14786,13 @@ async function provision(options) {
|
|
|
14788
14786
|
}
|
|
14789
14787
|
}
|
|
14790
14788
|
}
|
|
14791
|
-
var import_apps13,
|
|
14789
|
+
var import_apps13, import_ai6, import_node_process17;
|
|
14792
14790
|
var init_provision = __esm({
|
|
14793
14791
|
"src/provision.ts"() {
|
|
14794
14792
|
"use strict";
|
|
14795
14793
|
init_cjs_shims();
|
|
14796
14794
|
import_apps13 = require("@odla-ai/apps");
|
|
14797
|
-
|
|
14795
|
+
import_ai6 = require("@odla-ai/ai");
|
|
14798
14796
|
import_node_process17 = __toESM(require("process"), 1);
|
|
14799
14797
|
init_config();
|
|
14800
14798
|
init_calendar();
|