@commonlyai/cli 0.1.27 → 0.1.29

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commonlyai/cli",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
4
4
  "license": "Apache-2.0",
5
5
  "description": "The Commonly CLI \u2014 connect agents, manage pods, iterate fast",
6
6
  "type": "module",
@@ -1001,8 +1001,9 @@ export const performRun = ({
1001
1001
  }
1002
1002
  }
1003
1003
 
1004
+ let turnResult;
1004
1005
  try {
1005
- return await runTurn({
1006
+ turnResult = await runTurn({
1006
1007
  event,
1007
1008
  eventPodId,
1008
1009
  prompt: peerFrame ? `${peerFrame}\n\n${prompt}` : prompt,
@@ -1011,8 +1012,20 @@ export const performRun = ({
1011
1012
  claimKeeper,
1012
1013
  trigger,
1013
1014
  });
1015
+ return turnResult;
1014
1016
  } finally {
1015
- await claimKeeper?.release();
1017
+ // A silent, normally completed human wake is an explicit decline, not
1018
+ // a successful answer. Tell the kernel so it can hand the message to
1019
+ // exactly one remaining original listener. Any posted reply, refusal
1020
+ // with a reason, or thrown spawn retains completion/legacy semantics:
1021
+ // re-offering those would duplicate a visible response or defeat normal
1022
+ // at-least-once redelivery after an infrastructure failure.
1023
+ const claimOutcome = event.type === 'message.posted'
1024
+ && event.payload?.senderIsHuman === true
1025
+ && turnResult?.outcome === 'no_action' && !turnResult?.reason
1026
+ ? 'declined'
1027
+ : (turnResult ? 'completed' : undefined);
1028
+ await claimKeeper?.release(claimOutcome);
1016
1029
  }
1017
1030
  };
1018
1031
 
@@ -488,7 +488,15 @@ export default {
488
488
  // present in one but not the other means a retry silently runs a different
489
489
  // model than the turn it is replacing — the same drifting-copy shape that
490
490
  // has bitten this codebase repeatedly.
491
- const modelArgs = ctx.environment?.model ? ['--model', String(ctx.environment.model)] : [];
491
+ // `effort` rides in the same array for the same reason: Sam's 2026-09-01
492
+ // order ("flip all fable agents into fable 5.1 with high or above
493
+ // effort") needs both facts to reach every spawn, including the
494
+ // session-recovery retry, or a retry runs at a different effort than the
495
+ // turn it replaces.
496
+ const modelArgs = [
497
+ ...(ctx.environment?.model ? ['--model', String(ctx.environment.model)] : []),
498
+ ...(ctx.environment?.effort ? ['--effort', String(ctx.environment.effort)] : []),
499
+ ];
492
500
  const baseArgs = ['-p', fullPrompt, '--output-format', 'text', sessionFlag, sessionId, ...modelArgs];
493
501
 
494
502
  if (ctx.environment && ctx.cwd) {
package/src/lib/api.js CHANGED
@@ -63,9 +63,10 @@ export const createClient = ({ instance = null, token = undefined } = {}) => {
63
63
  body: JSON.stringify(body),
64
64
  }).then((res) => handleResponse(res, session));
65
65
 
66
- const del = (path) => fetch(`${baseUrl}${path}`, {
66
+ const del = (path, body) => fetch(`${baseUrl}${path}`, {
67
67
  method: 'DELETE',
68
68
  headers: headers(authToken),
69
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
69
70
  }).then((res) => handleResponse(res, session));
70
71
 
71
72
  // Multipart upload via native FormData/Blob (Node 18+) — no runtime deps.
@@ -420,11 +420,15 @@ export const createClaimKeeper = (client, {
420
420
  if (timer && typeof timer.unref === 'function') timer.unref();
421
421
  },
422
422
 
423
- async release() {
423
+ async release(outcome) {
424
424
  stopRenewal();
425
425
  if (!acquired || lost) return;
426
426
  try {
427
- await client.del(path);
427
+ // Preserve the one-argument legacy call when there is no explicit
428
+ // outcome. Besides keeping old clients' DELETE shape intact, callers
429
+ // that assert their transport arguments must not see a synthetic
430
+ // `undefined` body. An explicit outcome is the new D6.1 contract.
431
+ await (outcome ? client.del(path, { outcome }) : client.del(path));
428
432
  } catch {
429
433
  // Best-effort: a miss just means the lease already expired.
430
434
  }
@@ -41,7 +41,7 @@ import { homedir } from 'os';
41
41
  // "persona and runtime are chosen separately" requires to mean anything for a
42
42
  // BYO seat, and what lets an identity card answer "what is this running".
43
43
  const ALLOWED_TOP_KEYS = new Set([
44
- 'version', 'workspace', 'sandbox', 'skills', 'mcp', 'model',
44
+ 'version', 'workspace', 'sandbox', 'skills', 'mcp', 'model', 'effort',
45
45
  ]);
46
46
  const ALLOWED_SANDBOX_MODES = new Set([
47
47
  'none', 'workspace', 'read-only', 'bwrap', 'firejail', 'container', 'managed',
@@ -135,6 +135,16 @@ export const validateEnvironmentSpec = (spec) => {
135
135
  }
136
136
  }
137
137
 
138
+ // `effort` is the reasoning budget the claude adapter passes to `--effort`.
139
+ // Unlike `model` it IS a closed set — the CLI documents exactly these — so a
140
+ // typo fails here at attach time, not silently at the first spawn.
141
+ if (spec.effort !== undefined) {
142
+ const EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max'];
143
+ if (typeof spec.effort !== 'string' || !EFFORTS.includes(spec.effort)) {
144
+ errors.push(`effort must be one of: ${EFFORTS.join(', ')}`);
145
+ }
146
+ }
147
+
138
148
  if (spec.workspace !== undefined) {
139
149
  if (typeof spec.workspace !== 'object' || spec.workspace === null) {
140
150
  errors.push('workspace must be an object');