@commonlyai/cli 0.1.29 → 0.1.30

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.29",
3
+ "version": "0.1.30",
4
4
  "license": "Apache-2.0",
5
5
  "description": "The Commonly CLI \u2014 connect agents, manage pods, iterate fast",
6
6
  "type": "module",
@@ -3,8 +3,9 @@
3
3
  *
4
4
  * Contract: ADR-005 §Adapter pattern.
5
5
  *
6
- * Memory preamble: if ctx.memoryLongTerm is non-empty, the adapter prepends
7
- * it to the prompt as a system-context preamble (§Memory bridge).
6
+ * Memory preamble: the adapter prepends the kernel's long-term memory context
7
+ * on every turn. A fresh underlying session additionally receives the
8
+ * read-first and durable-state-at-boundary cues (§Memory bridge).
8
9
  *
9
10
  * Environment (ADR-008 Phase 1): if ctx.environment is present, the adapter
10
11
  * symlinks declared Claude skills into `<cwd>/.claude/skills/`, writes an MCP
@@ -476,7 +477,7 @@ export default {
476
477
  // the only two paths that build a real prompt. buildPrompt handles
477
478
  // undefined and '' as absence itself; it does not need a guard, it needs
478
479
  // the value.
479
- const fullPrompt = buildPrompt(prompt, ctx.memoryLongTerm);
480
+ const fullPrompt = buildPrompt(prompt, ctx.memoryLongTerm, { freshSession: !isResume });
480
481
  const sessionFlag = isResume ? '--resume' : '--session-id';
481
482
  // Model pin from the ADR-008 environment spec. Absent it, claude picks its
482
483
  // own default — which is how a fleet of ten agents ended up running three
@@ -552,7 +553,11 @@ export default {
552
553
  // session id poisons every subsequent event re-delivery.
553
554
  if (isResume && /already in use|no conversation|no session/i.test(String(err.message))) {
554
555
  const freshId = randomUUID();
555
- const retryBase = ['-p', fullPrompt, '--output-format', 'text', '--session-id', freshId, ...modelArgs];
556
+ // The retry creates a new underlying CLI session. Rebuild its prompt
557
+ // as fresh too: otherwise a session-recovery path is the one fresh
558
+ // session that misses the durable-state cue.
559
+ const freshPrompt = buildPrompt(prompt, ctx.memoryLongTerm, { freshSession: true });
560
+ const retryBase = ['-p', freshPrompt, '--output-format', 'text', '--session-id', freshId, ...modelArgs];
556
561
  const retry = await prepareArgv(retryBase, {
557
562
  ...ctx,
558
563
  mcpConfigPath: mcpConfig?.file || null,
@@ -20,10 +20,10 @@
20
20
  * `--output-last-message` short alias) — cleaner than parsing every
21
21
  * event-type variant the model can emit.
22
22
  *
23
- * Memory preamble: if ctx.memoryLongTerm is non-empty, the adapter prepends
24
- * it to the prompt as a system-context preamble (§Memory bridge), matching
25
- * the claude adapter's shape so the run loop's per-event memory plumbing
26
- * works identically across drivers.
23
+ * Memory preamble: the adapter prepends the kernel's long-term memory context
24
+ * on every turn. A fresh underlying session additionally receives the
25
+ * read-first and durable-state-at-boundary cues, matching the Claude adapter
26
+ * so the run loop's memory plumbing works identically across drivers.
27
27
  *
28
28
  * Purity (§Load-bearing invariants #1): input = argv + env + prompt;
29
29
  * output = text + session id. No direct network, no direct CAP calls.
@@ -421,7 +421,9 @@ export default {
421
421
  // the only two paths that build a real prompt. buildPrompt handles
422
422
  // undefined and '' as absence itself; it does not need a guard, it needs
423
423
  // the value.
424
- const fullPrompt = buildPrompt(prompt, ctx.memoryLongTerm);
424
+ const fullPrompt = buildPrompt(prompt, ctx.memoryLongTerm, {
425
+ freshSession: !ctx.sessionId,
426
+ });
425
427
 
426
428
  // Per-spawn temp dir for --output-last-message. Cleaned up in `finally`
427
429
  // so a crash in the middle of the spawn doesn't leak files in $TMPDIR.
@@ -39,29 +39,54 @@ export const SOURCE_RUNTIME = 'local-cli';
39
39
  * and is never seen again; the write and the silence are indistinguishable from
40
40
  * a correct round trip. Naming the section is the whole point of the cue.
41
41
  */
42
- export const buildMemoryPreamble = (prompt, memoryLongTerm) => {
42
+ export const buildMemoryPreamble = (prompt, memoryLongTerm, { freshSession = false } = {}) => {
43
43
  // `null` is the UNREADABLE signal, and it is deliberately not the same value
44
44
  // as `''`. Telling a seat whose token was revoked that "nothing has ever been
45
45
  // saved here" is a false claim about its own history, and it is the same
46
46
  // defect this cue exists to fix — one state over. When we could not read, say
47
47
  // that, and say nothing about what is stored.
48
+ let context;
48
49
  if (memoryLongTerm === null) {
49
- return `=== Context (your persistent memory) ===\n`
50
+ context = `=== Context (your persistent memory) ===\n`
50
51
  + `(unreadable this turn — the memory read failed, so this says NOTHING `
51
52
  + `about what you have saved. Do not treat it as empty and do not re-save `
52
- + `state you may already hold.)\n`
53
- + `=== Current turn ===\n${prompt}`;
53
+ + `state you may already hold.)`;
54
+ } else if (memoryLongTerm) {
55
+ context = `=== Context (your persistent memory) ===\n${memoryLongTerm}`;
56
+ } else {
57
+ context = `=== Context (your persistent memory) ===\n`
58
+ + `(empty — nothing has ever been saved here)\n`
59
+ + `Only the \`long_term\` section is read back into this prompt. To make `
60
+ + `something survive your next session, call commonly_save_my_memory({ `
61
+ + `section: 'long_term', content: '...' }). A write to any other section `
62
+ + `succeeds and is never shown to you again.`;
54
63
  }
55
- if (memoryLongTerm) {
56
- return `=== Context (your persistent memory) ===\n${memoryLongTerm}\n=== Current turn ===\n${prompt}`;
64
+
65
+ if (!freshSession) return `${context}\n=== Current turn ===\n${prompt}`;
66
+
67
+ // This belongs on a fresh underlying CLI session, rather than every turn:
68
+ // a resumed session already carries the earlier instruction in its own
69
+ // transcript. Repeating it on each wake spends prompt budget while making
70
+ // the cue easier to ignore. The wrapper cannot know the final event of a
71
+ // session in advance, so it gives the end-of-session reminder while the
72
+ // agent can still act on it.
73
+ const freshReadCue = '=== Fresh session ===\n'
74
+ + 'This is a fresh session. Read the persistent memory context above before acting; '
75
+ + 'it carries durable state from prior sessions.\n';
76
+ let sessionEndCue = '=== Before this session ends ===\n';
77
+ if (memoryLongTerm === null) {
78
+ sessionEndCue += 'Memory was unreadable on this fresh session. Do not treat it as empty or '
79
+ + 'write a replacement based on this cue.';
80
+ } else if (memoryLongTerm) {
81
+ sessionEndCue += 'At a natural end to meaningful work, save durable working state — '
82
+ + 'gates held, decisions pending, and task context, not a transcript — with '
83
+ + "commonly_save_my_memory({ section: 'long_term', content: '...' }).";
84
+ } else {
85
+ sessionEndCue += 'At a natural end to meaningful work, save durable working state — '
86
+ + 'gates held, decisions pending, and task context, not a transcript — using '
87
+ + 'the long_term write above.';
57
88
  }
58
- return `=== Context (your persistent memory) ===\n`
59
- + `(empty — nothing has ever been saved here)\n`
60
- + `Only the \`long_term\` section is read back into this prompt. To make `
61
- + `something survive your next session, call commonly_save_my_memory({ `
62
- + `section: 'long_term', content: '...' }). A write to any other section `
63
- + `succeeds and is never shown to you again.\n`
64
- + `=== Current turn ===\n${prompt}`;
89
+ return `${context}\n${freshReadCue}\n=== Current turn ===\n${prompt}\n${sessionEndCue}`;
65
90
  };
66
91
 
67
92
  export const readLongTerm = async (client, { onError } = {}) => {