granttap-mcp 0.8.78 → 0.8.79

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.
@@ -12,5 +12,11 @@ as a person's message. A real request next to that context is retained. Ordinary
12
12
  JSON requests remain visible. Native rollouts are read without modifying the
13
13
  provider's conversation files.
14
14
 
15
+ Forked child rollouts retain their first native identity. Copied parent metadata
16
+ and entries older than the child's creation are excluded from the child's
17
+ catalog, activity and usage; they must not replace the root Task or double count
18
+ its history. `tests/forked-agents.test.ts` covers cached rescans and all three
19
+ nested conversations through the public catalog and activity readers.
20
+
15
21
  Tests live in [`tests/`](tests/). The selected-Page regression covers the public
16
22
  catalog, cached rescans, live activity, and paginated transcript history.
@@ -227,7 +227,7 @@ export function codexActivity(session: SessionInfo): ActivityEntry[] {
227
227
  const out: ActivityEntry[] = [];
228
228
  const seen = new Set<string>();
229
229
  for (const source of sources) {
230
- const head = codexHeadRequest(source.path);
230
+ const head = source.child ? undefined : codexHeadRequest(source.path);
231
231
  if (head) {
232
232
  const createdAt = head.createdAt || session.startedAt;
233
233
  const childFields = source.child ? childEntryFields(source.child) : {};
@@ -0,0 +1,18 @@
1
+ import { safeParse, ts } from '../../../support/common';
2
+
3
+ /** Forked rollouts begin with their own metadata, then a copied parent log. */
4
+ export function ownCodexLines(lines: string[]): string[] {
5
+ const first = lines.find(line => safeParse(line)?.type === 'session_meta');
6
+ if (!first) return lines;
7
+ const native = safeParse(first);
8
+ const meta = native?.payload;
9
+ if (!meta?.source?.subagent?.thread_spawn?.parent_thread_id) return lines;
10
+ const startedAt = ts(native.timestamp ?? meta.timestamp);
11
+ return lines.filter(line => {
12
+ const row = safeParse(line);
13
+ if (!row) return false;
14
+ if (row.type === 'session_meta') return row.payload?.id === meta.id;
15
+ const at = ts(row.timestamp);
16
+ return !startedAt || !at || at >= startedAt;
17
+ });
18
+ }
@@ -166,7 +166,10 @@ export function parseCodexFile(file: string): CodexCandidate | undefined {
166
166
  const d = safeParse(line);
167
167
  if (d) applyCodexLine(d, state);
168
168
  }
169
- if (!state.title) adoptRequestTitle(state, codexHeadRequest(file, true)?.text);
169
+ if (!state.title && !state.child) adoptRequestTitle(state, codexHeadRequest(file, true)?.text);
170
+ if (!state.title && state.child?.agentPath) {
171
+ state.title = childTitle(state.child.agentPath.split('/').filter(Boolean).at(-1)?.replace(/_/g, ' '));
172
+ }
170
173
  state.title ??= state.provisionalTitle;
171
174
  state.lastActivityAt = Math.max(state.lastActivityAt, fileStat.mtimeMs);
172
175
  state.startedAt ||= fileStat.birthtimeMs || state.lastActivityAt;
@@ -2,6 +2,7 @@ import { toolDescription } from "../../support/activity-helpers";
2
2
  import { diffPreviewFromInput, sensitivePath, statsFromInput } from "../../support/edit-stats";
3
3
  import { redactSecrets } from "../../telemetry/command-preview";
4
4
  import { recordObservedWrite, writtenPaths } from "../../../mesh/observed/writes";
5
+ import { ownCodexLines } from './lineage';
5
6
  /**
6
7
  * Codex session logs:
7
8
  * ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl
@@ -135,7 +136,7 @@ export function readCodexLogWindow(
135
136
  tailBytes = CODEX_SUMMARY_TAIL_BYTES,
136
137
  ): string[] {
137
138
  const size = statSync(path).size;
138
- if (size <= headBytes + tailBytes) return readFileSync(path, "utf8").split("\n");
139
+ if (size <= headBytes + tailBytes) return ownCodexLines(readFileSync(path, "utf8").split("\n"));
139
140
  const fd = openSync(path, "r");
140
141
  try {
141
142
  const head = Buffer.allocUnsafe(headBytes);
@@ -153,7 +154,7 @@ export function readCodexLogWindow(
153
154
  const tailBreak = tailText.indexOf("\n");
154
155
  tailText = tailBreak >= 0 ? tailText.slice(tailBreak + 1) : "";
155
156
  }
156
- return `${headText}${tailText}`.split("\n");
157
+ return ownCodexLines(`${headText}${tailText}`.split("\n"));
157
158
  } finally {
158
159
  closeSync(fd);
159
160
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "granttap",
3
3
  "displayName": "GrantTap",
4
- "version": "0.1.18",
4
+ "version": "0.1.19",
5
5
  "description": "GrantTap pairs this computer with iPhone. The connection card shows a one-time QR when Cursor supports interactive MCP Apps; an interactive terminal command is available if it does not. The website approves the coding app.",
6
6
  "author": {
7
7
  "name": "GrantTap",
@@ -10,7 +10,7 @@ keeps each phone response tied to the exact originating chat and prompt.
10
10
  Install the supported CLI on this computer, then run:
11
11
 
12
12
  ```bash
13
- npm install -g granttap-mcp@0.8.78
13
+ npm install -g granttap-mcp@0.8.79
14
14
  granttap setup
15
15
  granttap status
16
16
  ```
@@ -4,7 +4,7 @@
4
4
  "command": "node",
5
5
  "args": [
6
6
  "-e",
7
- "\"use strict\";\n\n// Cursor may start this MCP from any cwd. Execute JavaScript entry points with\n// Node itself: Windows batch shims require shell quoting that differs by host.\nconst { spawn } = require(\"node:child_process\");\nconst { existsSync } = require(\"node:fs\");\nconst { delimiter, dirname, join } = require(\"node:path\");\n\nconst PACKAGE = \"granttap-mcp@0.8.78\";\nconst dryRun = process.env.GRANTTAP_BOOTSTRAP_DRY_RUN === \"1\";\nconst platform = dryRun && process.env.GRANTTAP_BOOTSTRAP_PLATFORM\n ? process.env.GRANTTAP_BOOTSTRAP_PLATFORM : process.platform;\nconst nodeRoot = dryRun && process.env.GRANTTAP_BOOTSTRAP_NODE_ROOT\n ? process.env.GRANTTAP_BOOTSTRAP_NODE_ROOT : dirname(process.execPath);\nconst pathRoots = (process.env.PATH || \"\").split(delimiter).filter(Boolean);\nconst appData = process.env.APPDATA;\nconst roots = [nodeRoot, ...pathRoots, ...(appData ? [join(appData, \"npm\")] : [])];\nconst first = (paths) => paths.find((path) => existsSync(path));\n\nfunction launch() {\n if (platform !== \"win32\") {\n const installed = first(roots.map((root) => join(root, \"granttap-mcp\")));\n if (installed) return { command: installed, args: [] };\n const npx = first(roots.map((root) => join(root, \"npx\"))) || \"npx\";\n return { command: npx, args: [\"-y\", PACKAGE] };\n }\n const installed = first(roots.map((root) =>\n join(root, \"node_modules\", \"granttap-mcp\", \"bin\", \"granttap-mcp.mjs\")));\n if (installed) return { command: process.execPath, args: [installed] };\n const npx = first(roots.map((root) =>\n join(root, \"node_modules\", \"npm\", \"bin\", \"npx-cli.js\")));\n if (npx) return { command: process.execPath, args: [npx, \"-y\", PACKAGE] };\n throw new Error(\"Node.js/npm 20+ is required on this Windows device. Install npm and retry GrantTap.\");\n}\n\nlet target;\ntry { target = launch(); }\ncatch (error) {\n process.stderr.write(`[granttap] ${error.message}\\n`);\n process.exit(1);\n}\n\nif (dryRun) {\n process.stdout.write(JSON.stringify(target));\n process.exit(0);\n}\n\nconst child = spawn(target.command, target.args, { stdio: \"inherit\", windowsHide: true });\nchild.on(\"error\", (error) => {\n process.stderr.write(`[granttap] ${error.message}\\n`);\n process.exit(1);\n});\nchild.on(\"exit\", (code, signal) => {\n if (signal && platform !== \"win32\") {\n try { process.kill(process.pid, signal); } catch { process.exit(code ?? 1); }\n return;\n }\n process.exit(code ?? 1);\n});\n"
7
+ "\"use strict\";\n\n// Cursor may start this MCP from any cwd. Execute JavaScript entry points with\n// Node itself: Windows batch shims require shell quoting that differs by host.\nconst { spawn } = require(\"node:child_process\");\nconst { existsSync } = require(\"node:fs\");\nconst { delimiter, dirname, join } = require(\"node:path\");\n\nconst PACKAGE = \"granttap-mcp@0.8.79\";\nconst dryRun = process.env.GRANTTAP_BOOTSTRAP_DRY_RUN === \"1\";\nconst platform = dryRun && process.env.GRANTTAP_BOOTSTRAP_PLATFORM\n ? process.env.GRANTTAP_BOOTSTRAP_PLATFORM : process.platform;\nconst nodeRoot = dryRun && process.env.GRANTTAP_BOOTSTRAP_NODE_ROOT\n ? process.env.GRANTTAP_BOOTSTRAP_NODE_ROOT : dirname(process.execPath);\nconst pathRoots = (process.env.PATH || \"\").split(delimiter).filter(Boolean);\nconst appData = process.env.APPDATA;\nconst roots = [nodeRoot, ...pathRoots, ...(appData ? [join(appData, \"npm\")] : [])];\nconst first = (paths) => paths.find((path) => existsSync(path));\n\nfunction launch() {\n if (platform !== \"win32\") {\n const installed = first(roots.map((root) => join(root, \"granttap-mcp\")));\n if (installed) return { command: installed, args: [] };\n const npx = first(roots.map((root) => join(root, \"npx\"))) || \"npx\";\n return { command: npx, args: [\"-y\", PACKAGE] };\n }\n const installed = first(roots.map((root) =>\n join(root, \"node_modules\", \"granttap-mcp\", \"bin\", \"granttap-mcp.mjs\")));\n if (installed) return { command: process.execPath, args: [installed] };\n const npx = first(roots.map((root) =>\n join(root, \"node_modules\", \"npm\", \"bin\", \"npx-cli.js\")));\n if (npx) return { command: process.execPath, args: [npx, \"-y\", PACKAGE] };\n throw new Error(\"Node.js/npm 20+ is required on this Windows device. Install npm and retry GrantTap.\");\n}\n\nlet target;\ntry { target = launch(); }\ncatch (error) {\n process.stderr.write(`[granttap] ${error.message}\\n`);\n process.exit(1);\n}\n\nif (dryRun) {\n process.stdout.write(JSON.stringify(target));\n process.exit(0);\n}\n\nconst child = spawn(target.command, target.args, { stdio: \"inherit\", windowsHide: true });\nchild.on(\"error\", (error) => {\n process.stderr.write(`[granttap] ${error.message}\\n`);\n process.exit(1);\n});\nchild.on(\"exit\", (code, signal) => {\n if (signal && platform !== \"win32\") {\n try { process.kill(process.pid, signal); } catch { process.exit(code ?? 1); }\n return;\n }\n process.exit(code ?? 1);\n});\n"
8
8
  ]
9
9
  }
10
10
  }
@@ -7,7 +7,7 @@ const { spawn } = require("node:child_process");
7
7
  const { existsSync } = require("node:fs");
8
8
  const { delimiter, dirname, join } = require("node:path");
9
9
 
10
- const PACKAGE = "granttap-mcp@0.8.78";
10
+ const PACKAGE = "granttap-mcp@0.8.79";
11
11
  const dryRun = process.env.GRANTTAP_BOOTSTRAP_DRY_RUN === "1";
12
12
  const platform = dryRun && process.env.GRANTTAP_BOOTSTRAP_PLATFORM
13
13
  ? process.env.GRANTTAP_BOOTSTRAP_PLATFORM : process.platform;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "granttap-mcp",
3
- "version": "0.8.78",
3
+ "version": "0.8.79",
4
4
  "description": "Commercial computer runtime connecting local coding agents to GrantTap on Mac, iPhone, iPad and Apple Watch.",
5
5
  "type": "module",
6
6
  "bin": {