@awak-app/simy-cli 0.6.27 → 0.6.28

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": "@awak-app/simy-cli",
3
- "version": "0.6.27",
3
+ "version": "0.6.28",
4
4
  "description": "Local SIMY executor for non-coding Local Tasks and Agentic Loop runs.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,7 +13,7 @@ import {
13
13
  } from "./durable-local-task-steps-contract.js";
14
14
  import { buildExecutionCapabilityContract } from "./execution-capability-contract.js";
15
15
 
16
- export const CLI_VERSION = "0.6.27";
16
+ export const CLI_VERSION = "0.6.28";
17
17
  export const CLI_API_CONTRACT_VERSION = 10;
18
18
 
19
19
  export function narrativeImageCapabilityMetadata() {
@@ -1511,7 +1511,7 @@ async function providerProcess(provider) {
1511
1511
  }
1512
1512
  return null;
1513
1513
  }
1514
- export async function handleHook({ input, provider, root = rootDirectory(), now = Date.now(), observedProcess = undefined, inspectProcess = processIdentity }) {
1514
+ export async function handleHook({ input, provider, root = rootDirectory(), now = Date.now(), observedProcess = undefined, inspectProcess = processIdentity, env = process.env }) {
1515
1515
  const link = await providerLinkState(root, provider);
1516
1516
  if (!link.connected) return {};
1517
1517
  if (!EVENTS.has(input?.hook_event_name) || !text(input.session_id) || !text(input.cwd)) return {};
@@ -1524,6 +1524,16 @@ export async function handleHook({ input, provider, root = rootDirectory(), now
1524
1524
  if (!currentLink.connected || currentLink.epoch !== link.epoch) return old;
1525
1525
  if (old && old.cwd !== cwd) return old;
1526
1526
  const record = old || { schemaVersion: 1, provider, session: input.session_id, cwd, run: null };
1527
+ // Inherited provider identity links an AI-launched conversation to its
1528
+ // origin. Never infer parentage from a shared workspace or nearby time.
1529
+ if (event === 'SessionStart' && !record.parentSession) {
1530
+ const parentProvider = env.SIMY_PARENT_PROVIDER || (provider === 'claude' && env.CODEX_THREAD_ID ? 'codex' : null);
1531
+ const parentId = env.SIMY_PARENT_SESSION || (parentProvider === 'codex' ? env.CODEX_THREAD_ID : null);
1532
+ if (['codex', 'claude'].includes(parentProvider) && /^[0-9a-f-]{36}$/i.test(parentId || '') &&
1533
+ !(parentProvider === provider && parentId === input.session_id)) {
1534
+ record.parentSession = { provider: parentProvider, session: parentId };
1535
+ }
1536
+ }
1527
1537
  const owner = record.run?.managed?.liveHookOwner;
1528
1538
  if (owner && ['SessionStart', 'UserPromptSubmit', 'Stop', 'StopFailure', 'SessionEnd', 'Interrupt'].includes(event) &&
1529
1539
  (!identity || identity.pid !== owner.pid || identity.identity !== owner.identity)) {
@@ -22,7 +22,7 @@ function rememberMetadata(path, value) {
22
22
  }
23
23
  function candidateMetadata(id, metadata, lastActivityAt, observedAt) {
24
24
  return {
25
- provider: 'claude', session: id, cwd: metadata.cwd, title: metadata.title,
25
+ provider: 'claude', session: id, ...(metadata.parentSessionId ? { parentSession: { provider: 'claude', session: metadata.parentSessionId } } : {}), cwd: metadata.cwd, title: metadata.title,
26
26
  source: 'claude_cli', lastActivityAt,
27
27
  observation: { source: 'claude_local_index', observedAt },
28
28
  availability: 'unsupported', model: null,
@@ -60,13 +60,14 @@ function metadataFromPrefix(bytes, id) {
60
60
  const text = bytes.toString('utf8');
61
61
  const complete = text.slice(0, text.lastIndexOf('\n') + 1);
62
62
  if (!complete) return null;
63
- let cwd = '', title = '', customTitle = '', found = false;
63
+ let cwd = '', title = '', customTitle = '', parentSessionId = null, found = false;
64
64
  for (const line of complete.split('\n').slice(0, MAX_LINES)) {
65
65
  if (!line) continue;
66
66
  let item;
67
67
  try { item = JSON.parse(line); } catch { return null; }
68
68
  if (unscopedFileHistory(item)) continue;
69
69
  if (item.sessionId !== id) return null;
70
+ if (UUID.test(item.parentSessionId || '') && item.parentSessionId !== id) parentSessionId = item.parentSessionId;
70
71
  if (item.type === 'custom-title' && item.isSidechain !== true) { customTitle = sessionTitle(item.customTitle); continue; }
71
72
  if (item.type !== 'user' && item.type !== 'assistant') continue;
72
73
  if (item.isSidechain === true) return null;
@@ -77,7 +78,7 @@ function metadataFromPrefix(bytes, id) {
77
78
  if (!title) title = sessionTitle(item.slug);
78
79
  found = true;
79
80
  }
80
- return found ? { cwd, title: customTitle || title } : null;
81
+ return found ? { cwd, title: customTitle || title, parentSessionId } : null;
81
82
  }
82
83
 
83
84
  // /rename appends a custom-title envelope, often beyond the bounded prefix.
@@ -195,6 +195,8 @@ export function codexThreadMetadata(thread) {
195
195
  if (thread.parentThreadId) return null;
196
196
  return {
197
197
  provider: 'codex', session: thread.id, cwd: thread.cwd,
198
+ ...(typeof thread.forkedFromId === 'string' && /^[0-9a-f-]{36}$/i.test(thread.forkedFromId) && thread.forkedFromId !== thread.id
199
+ ? { parentSession: { provider: 'codex', session: thread.forkedFromId } } : {}),
198
200
  title: typeof thread.name === 'string' && thread.name.trim() ? thread.name.slice(0, 200) : '',
199
201
  source: typeof thread.source === 'string' ? thread.source : thread.source?.type || 'unknown',
200
202
  lastActivityAt: Number.isFinite(thread.updatedAt) ? thread.updatedAt * 1000 : null,
@@ -231,6 +231,7 @@ export async function discoverSessions({ root, openClient = openCodexAppServer,
231
231
  } catch { /* Unverified process leaves runtime status unknown. */ }
232
232
  }
233
233
  return { provider: item.provider, session: item.session, cwd: item.cwd,
234
+ ...((item.parentSession || record?.parentSession) ? { parentSession: item.parentSession || record.parentSession } : {}),
234
235
  title: sessionTitle(item.title) || (item.provider === 'codex' ? codexTitles.get(item.session) || '' : ''), source: item.source,
235
236
  ...(typeof isRunning === 'boolean' ? { isRunning } : {}),
236
237
  ...(item.latestTurnId ? { latestTurnId: item.latestTurnId } : {}),