@commonlyai/cli 0.1.47 → 0.1.48

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.47",
3
+ "version": "0.1.48",
4
4
  "license": "Apache-2.0",
5
5
  "description": "The Commonly CLI — connect agents, manage pods, iterate fast",
6
6
  "type": "module",
@@ -34,7 +34,7 @@
34
34
 
35
35
  import { spawn as childSpawn, spawnSync } from 'child_process';
36
36
  import { createHash, randomUUID } from 'crypto';
37
- import { mkdir, writeFile } from 'fs/promises';
37
+ import { mkdir, readdir, writeFile } from 'fs/promises';
38
38
  import { homedir } from 'os';
39
39
  import { dirname, join } from 'path';
40
40
  import { fileURLToPath } from 'url';
@@ -127,6 +127,24 @@ export const seatHome = (ctx) => {
127
127
  return ctx._piHome || join(homedir(), '.commonly', 'pi-homes', hash);
128
128
  };
129
129
 
130
+ /**
131
+ * Only a session pi wrote can be resumed. The wrapper persists one id per
132
+ * (agent, pod) across adapters, so a seat switched from codex hands pi the
133
+ * codex thread id on its first turn — pi answers `No session found` and the
134
+ * turn fails (sprint-impl's first spawn, 2026-09-18 05:06Z). pi's session
135
+ * files are `<timestamp>_<id>.jsonl` under the seat's session dir; an id with
136
+ * no file starts a fresh session under a new id, which the wrapper persists.
137
+ */
138
+ export const sessionExists = async (sessionDir, sessionId) => {
139
+ if (!sessionId) return false;
140
+ try {
141
+ const files = await readdir(sessionDir);
142
+ return files.some((name) => name.endsWith(`_${sessionId}.jsonl`) || name === `${sessionId}.jsonl`);
143
+ } catch {
144
+ return false;
145
+ }
146
+ };
147
+
130
148
  export const buildArgs = ({
131
149
  prompt, provider, model, thinking, sessionId, isResume, sessionDir, bridge,
132
150
  }) => [
@@ -196,9 +214,6 @@ export default {
196
214
  },
197
215
 
198
216
  async spawn(prompt, ctx = {}) {
199
- const isResume = !!ctx.sessionId;
200
- const sessionId = ctx.sessionId || randomUUID();
201
- const fullPrompt = buildMemoryPreamble(prompt, ctx.memoryLongTerm, { freshSession: !isResume });
202
217
  const provider = resolveProvider(ctx.environment);
203
218
  const model = ctx.environment?.model || DEFAULT_MODEL;
204
219
  const thinking = thinkingFor(ctx.environment?.effort);
@@ -213,6 +228,12 @@ export default {
213
228
  const sessionDir = join(home, 'sessions');
214
229
  await mkdir(agentDir, { recursive: true, mode: 0o700 });
215
230
  await mkdir(sessionDir, { recursive: true, mode: 0o700 });
231
+
232
+ // Resume only what pi wrote; anything else (a codex thread id from before
233
+ // the switch, a wiped home) starts fresh under a new id.
234
+ const isResume = await sessionExists(sessionDir, ctx.sessionId);
235
+ const sessionId = isResume ? ctx.sessionId : randomUUID();
236
+ const fullPrompt = buildMemoryPreamble(prompt, ctx.memoryLongTerm, { freshSession: !isResume });
216
237
  await writeFile(join(agentDir, 'models.json'), `${JSON.stringify(buildModelsJson(provider, model), null, 2)}\n`, { mode: 0o600 });
217
238
 
218
239
  const servers = resolveMcpServers(ctx.environment?.mcp, ctx);