@odla-ai/cli 0.40.1 → 0.41.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/dist/bin.cjs CHANGED
@@ -7915,7 +7915,7 @@ var init_code2 = __esm({
7915
7915
  }
7916
7916
  });
7917
7917
 
7918
- // ../harness/dist/chunk-L2T3LPEF.js
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: options.surface ?? "v2",
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: run.finalText,
9047
+ status: run.stoppedReason === "refusal" || missingClosing ? "failed" : "completed",
9048
+ finalText,
9034
9049
  stoppedReason: run.stoppedReason,
9035
- ...run.stoppedReason === "refusal" ? { error: run.finalText || "the agent refused the task" } : {}
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, metadata: metadata2, request: request3, state: state2 } = input;
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 init_chunk_L2T3LPEF = __esm({
9919
- "../harness/dist/chunk-L2T3LPEF.js"() {
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, noticeEmitted: false, costUsd: 0, costKnown: true };
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 body = result.finalText.trim() || (result.status === "completed" ? "The agent finished without a closing message." : result.error ?? "The agent failed.");
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: result.status === "completed" ? "agent" : "system",
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: result.status === "completed" ? "idle" : "failed",
10359
+ status: completed ? "idle" : "failed",
10371
10360
  durationMs: Date.now() - startedAt
10372
10361
  }, active.conversationRefs).catch(() => void 0);
10373
- if (result.status === "failed") {
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
- init_chunk_L2T3LPEF();
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
- ctx.out.log(`@[${item.label}](${item.kind}/${item.id}) ${item.kind} ${item.hint ?? ""}`);
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, import_ai5.putSecret)({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
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, import_ai5, import_node_process17;
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
- import_ai5 = require("@odla-ai/ai");
14795
+ import_ai6 = require("@odla-ai/ai");
14798
14796
  import_node_process17 = __toESM(require("process"), 1);
14799
14797
  init_config();
14800
14798
  init_calendar();