@commonlyai/cli 0.1.171 → 0.1.172

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.171",
3
+ "version": "0.1.172",
4
4
  "license": "Apache-2.0",
5
5
  "description": "The Commonly CLI — connect agents, manage pods, iterate fast",
6
6
  "type": "module",
@@ -81,7 +81,7 @@ import { deliverSeatCredential, withholdRuntimeCredential } from '../mcp-credent
81
81
  import {
82
82
  prepareMcpSpawn, PUBLIC_MCP_UNAVAILABLE_CODE, publicMcpUnavailableError, selectedMcpVersionDir,
83
83
  } from '../mcp-home.js';
84
- import { buildMemoryPreamble } from '../memory-bridge.js';
84
+ import { buildMemoryPreamble, ROW_SLICING_CUE } from '../memory-bridge.js';
85
85
  import { claudeConfigDirFor, claudeContextTokens } from '../session-recycle.js';
86
86
  import { skillsAsText } from '../skill-prompt.js';
87
87
  import { adapterFailure, spawnCredentials } from '../upstream-refusal.js';
@@ -98,6 +98,16 @@ const DEFAULT_TIMEOUT_MS = (() => {
98
98
  })();
99
99
 
100
100
  const buildPrompt = buildMemoryPreamble;
101
+
102
+ // The working habits a private claude seat is taught on a fresh session (D48,
103
+ // TASK-545). Parallel reads live here because only this CLI has a subagent
104
+ // tool; codex is passed row slicing alone. A public seat is passed neither: its
105
+ // argv allows Read and the MCP under dontAsk, so it has no subagent to spawn,
106
+ // and it answers strangers rather than working board rows.
107
+ const PARALLEL_READS_CUE = 'Parallel reads: when a row needs several files or sources read, '
108
+ + 'use the Agent tool. Spawn read-only subagents in parallel, brief each one with the files '
109
+ + 'to read and the question to answer, and keep only their conclusions.';
110
+ const WORK_CUES = [PARALLEL_READS_CUE, ROW_SLICING_CUE];
101
111
  const RUNTIME_CREDENTIAL_PLACEHOLDER = '${COMMONLY_AGENT_TOKEN}';
102
112
 
103
113
  const QUOTE_SHELL_ARGUMENT = (value) => `'${String(value).replaceAll("'", "'\\''")}'`;
@@ -799,7 +809,12 @@ export default {
799
809
  // the only two paths that build a real prompt. buildPrompt handles
800
810
  // undefined and '' as absence itself; it does not need a guard, it needs
801
811
  // the value.
802
- const fullPrompt = buildPrompt(prompt, ctx.memoryLongTerm, { freshSession: !isResume });
812
+ const publicTrust = ctx.environment?.sandbox?.trust === 'public';
813
+ const workCues = publicTrust ? [] : WORK_CUES;
814
+ const fullPrompt = buildPrompt(prompt, ctx.memoryLongTerm, {
815
+ freshSession: !isResume,
816
+ workCues,
817
+ });
803
818
  const sessionFlag = isResume ? '--resume' : '--session-id';
804
819
  // Model pin from the ADR-008 environment spec. Absent it, claude picks its
805
820
  // own default — which is how a fleet of ten agents ended up running three
@@ -839,7 +854,6 @@ export default {
839
854
  let mcpConfig = null;
840
855
  let publicClaudeState = null;
841
856
  let publicSkillArgs = [];
842
- const publicTrust = ctx.environment?.sandbox?.trust === 'public';
843
857
  const publicNativeSandbox = publicTrust
844
858
  && PUBLIC_SANDBOX_MODES.has(resolvePublicSandboxMode(ctx.environment?.sandbox));
845
859
  const publicBwrapSandbox = publicTrust
@@ -927,7 +941,10 @@ export default {
927
941
  // The retry creates a new underlying CLI session. Rebuild its prompt
928
942
  // as fresh too: otherwise a session-recovery path is the one fresh
929
943
  // session that misses the durable-state cue.
930
- const freshPrompt = buildPrompt(prompt, ctx.memoryLongTerm, { freshSession: true });
944
+ const freshPrompt = buildPrompt(prompt, ctx.memoryLongTerm, {
945
+ freshSession: true,
946
+ workCues,
947
+ });
931
948
  const retryBase = ['-p', freshPrompt,
932
949
  '--output-format', 'text', '--session-id', freshId, ...modelArgs];
933
950
  const retryCtx = {
@@ -66,7 +66,7 @@ import { CREDENTIAL_KEY, writeCredentialFile } from '../credential-file.js';
66
66
  import { supportedGitHooks } from '../commit-attribution.js';
67
67
  import { deliverSeatCredential, withholdRuntimeCredential } from '../mcp-credential-delivery.js';
68
68
  import { prepareMcpSpawn, publicMcpUnavailableError, selectedMcpVersionDir } from '../mcp-home.js';
69
- import { buildMemoryPreamble } from '../memory-bridge.js';
69
+ import { buildMemoryPreamble, ROW_SLICING_CUE } from '../memory-bridge.js';
70
70
  import { resolveSkillSource, skillsAsText } from '../skill-prompt.js';
71
71
  import { isLegacySandboxTrust, normalizeSandboxTrust } from '../environment.js';
72
72
  import { adapterFailure, spawnCredentials } from '../upstream-refusal.js';
@@ -650,14 +650,20 @@ export default {
650
650
 
651
651
  async spawn(prompt, ctx = {}) {
652
652
  const seatModel = requireSeatModel(ctx.environment, 'codex');
653
+ const sandbox = normalizeSandboxTrust(ctx.environment?.sandbox);
653
654
  // Passed through UNCOALESCED. `ctx.memoryLongTerm || ''` was here, and
654
655
  // `null || ''` is `''` — which routed the unreadable case straight into
655
656
  // the empty-memory branch and made the whole distinction unreachable from
656
657
  // the only two paths that build a real prompt. buildPrompt handles
657
658
  // undefined and '' as absence itself; it does not need a guard, it needs
658
659
  // the value.
660
+ //
661
+ // Codex has no subagent tool, so of D48's two habits (TASK-545) it is
662
+ // taught row slicing alone; claude.js adds parallel reads. A public seat
663
+ // answers strangers rather than working board rows, so it is taught neither.
659
664
  const fullPrompt = buildPrompt(prompt, ctx.memoryLongTerm, {
660
665
  freshSession: !ctx.sessionId,
666
+ workCues: sandbox?.trust === 'public' ? [] : [ROW_SLICING_CUE],
661
667
  });
662
668
 
663
669
  // Per-spawn temp dir for --output-last-message. Cleaned up in `finally`
@@ -674,7 +680,6 @@ export default {
674
680
  });
675
681
 
676
682
  try {
677
- const sandbox = normalizeSandboxTrust(ctx.environment?.sandbox);
678
683
  const mcp = buildMcpOverrideArgs(ctx.environment?.mcp, {
679
684
  runtimeToken: ctx.runtimeToken,
680
685
  instanceUrl: ctx.instanceUrl,
@@ -22,6 +22,18 @@
22
22
 
23
23
  export const SOURCE_RUNTIME = 'local-cli';
24
24
 
25
+ /**
26
+ * Row slicing, the runtime-agnostic half of D48 (TASK-545). A row too large
27
+ * for one turn drifts when one seat carries it whole, so the seat splits it on
28
+ * the board before any code. It names the exact call and field for the reason
29
+ * the memory cue below names `long_term`: a seat adopts a habit through a
30
+ * surface it can see. Each adapter decides whether to pass it.
31
+ */
32
+ export const ROW_SLICING_CUE = 'Slicing: when a row is too large for one turn, split it before '
33
+ + 'writing any code. Post each slice as a child row (commonly_create_task with parentTask set '
34
+ + "to the row's taskId) with one assignee, a written file or area boundary, and its own "
35
+ + 'acceptance line.';
36
+
25
37
  /**
26
38
  * The turn preamble, shared by every adapter so the two cannot drift.
27
39
  *
@@ -39,8 +51,17 @@ export const SOURCE_RUNTIME = 'local-cli';
39
51
  * the section whose name most invites exactly this use — succeeds, returns 200,
40
52
  * and is never seen again; the write and the silence are indistinguishable from
41
53
  * a correct round trip. Naming the section is the whole point of the cue.
54
+ *
55
+ * `workCues` are working habits an adapter teaches on a fresh session, beside
56
+ * the memory cues and under the same once-per-session rule. An adapter passes
57
+ * only the habits its CLI can act on; passing none leaves the preamble as it
58
+ * was.
42
59
  */
43
- export const buildMemoryPreamble = (prompt, memoryLongTerm, { freshSession = false } = {}) => {
60
+ export const buildMemoryPreamble = (
61
+ prompt,
62
+ memoryLongTerm,
63
+ { freshSession = false, workCues = [] } = {},
64
+ ) => {
44
65
  // `null` is the UNREADABLE signal, and it is deliberately not the same value
45
66
  // as `''`. Telling a seat whose token was revoked that "nothing has ever been
46
67
  // saved here" is a false claim about its own history, and it is the same
@@ -87,7 +108,10 @@ export const buildMemoryPreamble = (prompt, memoryLongTerm, { freshSession = fal
87
108
  + 'gates held, decisions pending, and task context, not a transcript — using '
88
109
  + 'the long_term write above.';
89
110
  }
90
- return `${context}\n${freshReadCue}\n=== Current turn ===\n${prompt}\n${sessionEndCue}`;
111
+ const workCue = workCues.length
112
+ ? `\n=== Working a row ===\n${workCues.map((cue) => `- ${cue}`).join('\n')}\n`
113
+ : '';
114
+ return `${context}\n${freshReadCue}${workCue}\n=== Current turn ===\n${prompt}\n${sessionEndCue}`;
91
115
  };
92
116
 
93
117
  export const readLongTerm = async (client, { onError } = {}) => {