nterminal 1.3.4 → 1.3.6

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.
Files changed (126) hide show
  1. package/README.md +68 -37
  2. package/assets/screenshot/accounts.png +0 -0
  3. package/assets/screenshot/history.png +0 -0
  4. package/assets/screenshot/mobile-panes.png +0 -0
  5. package/assets/screenshot/mobile.png +0 -0
  6. package/assets/screenshot/scr.png +0 -0
  7. package/assets/screenshot/subagents.png +0 -0
  8. package/bin/nterminal.js +10 -3
  9. package/dist/client/assets/MarkdownPreview-B-LrTjTM.js +1 -0
  10. package/dist/client/assets/{TranscriptMarkdownBody-H8ZsISc6.js → TranscriptMarkdownBody-3aYVmqQz.js} +1 -1
  11. package/dist/client/assets/index-D3LN5iAH.js +58 -0
  12. package/dist/client/assets/{index-DxfBq-4P.js → index-Dd_o8KCL.js} +1 -1
  13. package/dist/client/assets/index-W-zGfY1a.css +1 -0
  14. package/dist/client/index.html +2 -2
  15. package/dist/scripts/hooks.js +124 -0
  16. package/dist/scripts/hooks.js.map +1 -0
  17. package/dist/server/accounts/accountService.d.ts +23 -1
  18. package/dist/server/accounts/accountService.js +235 -103
  19. package/dist/server/accounts/accountService.js.map +1 -1
  20. package/dist/server/accounts/providerRequests.d.ts +12 -0
  21. package/dist/server/accounts/providerRequests.js +159 -0
  22. package/dist/server/accounts/providerRequests.js.map +1 -0
  23. package/dist/server/agent/agentRoutes.js +11 -0
  24. package/dist/server/agent/agentRoutes.js.map +1 -1
  25. package/dist/server/hooks/codexAppServer.d.ts +30 -0
  26. package/dist/server/hooks/codexAppServer.js +170 -0
  27. package/dist/server/hooks/codexAppServer.js.map +1 -0
  28. package/dist/server/hooks/codexTrust.d.ts +72 -0
  29. package/dist/server/hooks/codexTrust.js +116 -0
  30. package/dist/server/hooks/codexTrust.js.map +1 -0
  31. package/dist/server/hooks/helperJournal.d.ts +16 -0
  32. package/dist/server/hooks/helperJournal.js +74 -0
  33. package/dist/server/hooks/helperJournal.js.map +1 -0
  34. package/dist/server/hooks/hookBinding.d.ts +39 -0
  35. package/dist/server/hooks/hookBinding.js +91 -0
  36. package/dist/server/hooks/hookBinding.js.map +1 -0
  37. package/dist/server/hooks/hookFiles.d.ts +24 -0
  38. package/dist/server/hooks/hookFiles.js +160 -0
  39. package/dist/server/hooks/hookFiles.js.map +1 -0
  40. package/dist/server/hooks/hookInstallation.d.ts +108 -0
  41. package/dist/server/hooks/hookInstallation.js +1074 -0
  42. package/dist/server/hooks/hookInstallation.js.map +1 -0
  43. package/dist/server/hooks/hookProcess.d.ts +5 -0
  44. package/dist/server/hooks/hookProcess.js +85 -0
  45. package/dist/server/hooks/hookProcess.js.map +1 -0
  46. package/dist/server/hooks/hookReceiver.d.ts +66 -0
  47. package/dist/server/hooks/hookReceiver.js +549 -0
  48. package/dist/server/hooks/hookReceiver.js.map +1 -0
  49. package/dist/server/hooks/lifecycle.d.ts +40 -0
  50. package/dist/server/hooks/lifecycle.js +200 -0
  51. package/dist/server/hooks/lifecycle.js.map +1 -0
  52. package/dist/server/hooks/publicationLock.d.ts +3 -0
  53. package/dist/server/hooks/publicationLock.js +137 -0
  54. package/dist/server/hooks/publicationLock.js.map +1 -0
  55. package/dist/server/hooks/verifiedLock.d.ts +24 -0
  56. package/dist/server/hooks/verifiedLock.js +207 -0
  57. package/dist/server/hooks/verifiedLock.js.map +1 -0
  58. package/dist/server/http.d.ts +4 -0
  59. package/dist/server/http.js +6 -0
  60. package/dist/server/http.js.map +1 -1
  61. package/dist/server/index.js +71 -2
  62. package/dist/server/index.js.map +1 -1
  63. package/dist/server/notifications/agentCompletionMonitor.d.ts +16 -1
  64. package/dist/server/notifications/agentCompletionMonitor.js +261 -47
  65. package/dist/server/notifications/agentCompletionMonitor.js.map +1 -1
  66. package/dist/server/notifications/completionNotificationRouter.d.ts +2 -0
  67. package/dist/server/notifications/completionNotificationRouter.js +9 -0
  68. package/dist/server/notifications/completionNotificationRouter.js.map +1 -1
  69. package/dist/server/routes/accountRoutes.js +11 -0
  70. package/dist/server/routes/accountRoutes.js.map +1 -1
  71. package/dist/server/routes/activityRoutes.d.ts +13 -0
  72. package/dist/server/routes/activityRoutes.js +165 -0
  73. package/dist/server/routes/activityRoutes.js.map +1 -0
  74. package/dist/server/routes/agentManagementRoutes.js +8 -1
  75. package/dist/server/routes/agentManagementRoutes.js.map +1 -1
  76. package/dist/server/routes/hookRoutes.d.ts +16 -0
  77. package/dist/server/routes/hookRoutes.js +109 -0
  78. package/dist/server/routes/hookRoutes.js.map +1 -0
  79. package/dist/server/routes/terminalRoutes.js +4 -0
  80. package/dist/server/routes/terminalRoutes.js.map +1 -1
  81. package/dist/server/routes/transcriptQuery.d.ts +2 -1
  82. package/dist/server/routes/transcriptQuery.js +3 -1
  83. package/dist/server/routes/transcriptQuery.js.map +1 -1
  84. package/dist/server/terminal/PtyAdapter.d.ts +19 -0
  85. package/dist/server/terminal/PtyAdapter.js.map +1 -1
  86. package/dist/server/terminal/TerminalManager.d.ts +61 -0
  87. package/dist/server/terminal/TerminalManager.js +672 -12
  88. package/dist/server/terminal/TerminalManager.js.map +1 -1
  89. package/dist/server/terminal/TmuxPtyAdapter.d.ts +14 -2
  90. package/dist/server/terminal/TmuxPtyAdapter.js +101 -15
  91. package/dist/server/terminal/TmuxPtyAdapter.js.map +1 -1
  92. package/dist/server/terminal/activityChannel.d.ts +57 -0
  93. package/dist/server/terminal/activityChannel.js +166 -0
  94. package/dist/server/terminal/activityChannel.js.map +1 -0
  95. package/dist/server/terminal/agentTranscriptStatus.d.ts +12 -0
  96. package/dist/server/terminal/agentTranscriptStatus.js +78 -7
  97. package/dist/server/terminal/agentTranscriptStatus.js.map +1 -1
  98. package/dist/server/terminal/codexTranscriptSource.d.ts +3 -0
  99. package/dist/server/terminal/codexTranscriptSource.js +99 -39
  100. package/dist/server/terminal/codexTranscriptSource.js.map +1 -1
  101. package/dist/server/terminal/subagentSources.js +2 -1
  102. package/dist/server/terminal/subagentSources.js.map +1 -1
  103. package/dist/server/terminal/transcriptHeader.d.ts +85 -0
  104. package/dist/server/terminal/transcriptHeader.js +376 -0
  105. package/dist/server/terminal/transcriptHeader.js.map +1 -0
  106. package/dist/server/terminal/transcriptHistory.d.ts +16 -2
  107. package/dist/server/terminal/transcriptHistory.js +120 -21
  108. package/dist/server/terminal/transcriptHistory.js.map +1 -1
  109. package/dist/server/terminal/transcriptStore.d.ts +6 -0
  110. package/dist/server/terminal/transcriptStore.js +222 -26
  111. package/dist/server/terminal/transcriptStore.js.map +1 -1
  112. package/dist/shared/activityProbe.d.ts +10 -0
  113. package/dist/shared/activityProbe.js +24 -0
  114. package/dist/shared/activityProbe.js.map +1 -0
  115. package/dist/shared/protocol.d.ts +45 -0
  116. package/dist/shared/protocol.js.map +1 -1
  117. package/dist/shared/types.d.ts +9 -1
  118. package/docs/features.md +21 -1
  119. package/docs/provider-hooks.md +340 -0
  120. package/docs/terminal-history.md +10 -0
  121. package/package.json +2 -1
  122. package/scripts/nterminal-hook.cjs +171 -0
  123. package/scripts/nterminalctl +26 -2
  124. package/dist/client/assets/MarkdownPreview-DjDO4lEl.js +0 -1
  125. package/dist/client/assets/index-CIYazAGv.css +0 -1
  126. package/dist/client/assets/index-DwvLN_WJ.js +0 -56
@@ -0,0 +1,24 @@
1
+ // Transport controls are deliberately not activity journal or monitor events.
2
+ export const activityProbeCapability = 'nonce-v1';
3
+ export const activityProbeMaxBytes = 512;
4
+ export const activityProbeIntervalMs = 30_000;
5
+ export const activityProbeDeadlineMs = 10_000;
6
+ export function decodeActivityProbe(text) {
7
+ if (text.length > activityProbeMaxBytes || new TextEncoder().encode(text).byteLength > activityProbeMaxBytes)
8
+ return null;
9
+ try {
10
+ const value = JSON.parse(text);
11
+ if (!value || typeof value !== 'object' || Array.isArray(value))
12
+ return null;
13
+ const probe = value;
14
+ if (Object.keys(probe).length !== 3 || !Object.hasOwn(probe, 'type') || !Object.hasOwn(probe, 'version')
15
+ || !Object.hasOwn(probe, 'nonce') || !['activity.ping', 'activity.pong'].includes(probe.type)
16
+ || probe.version !== 1 || typeof probe.nonce !== 'string' || !/^[A-Za-z0-9_-]{1,64}$/.test(probe.nonce))
17
+ return null;
18
+ return probe;
19
+ }
20
+ catch {
21
+ return null;
22
+ }
23
+ }
24
+ //# sourceMappingURL=activityProbe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activityProbe.js","sourceRoot":"","sources":["../../src/shared/activityProbe.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,CAAC,MAAM,uBAAuB,GAAG,UAAU,CAAC;AAClD,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AACzC,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC;AAC9C,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC;AAQ9C,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IAAI,IAAI,CAAC,MAAM,GAAG,qBAAqB,IAAI,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,GAAG,qBAAqB;QAAE,OAAO,IAAI,CAAC;IAC1H,IAAI,CAAC;QACH,MAAM,KAAK,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAC7E,MAAM,KAAK,GAAG,KAAsB,CAAC;QACrC,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC;eACnG,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;eAC1F,KAAK,CAAC,OAAO,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACvH,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;AAC1B,CAAC"}
@@ -225,6 +225,31 @@ export interface CodeServerStopRequest {
225
225
  instanceId?: string;
226
226
  }
227
227
  export type AccountProvider = 'codex' | 'claude';
228
+ export interface AccountRefreshStatus {
229
+ state: 'success' | 'retrying' | 'reauth_required' | 'deferred';
230
+ lastAttemptAt: string | null;
231
+ lastSuccessAt: string | null;
232
+ nextRetryAt: string | null;
233
+ code: string | null;
234
+ httpStatus: number | null;
235
+ }
236
+ export interface AccountUsage {
237
+ status: 'available' | 'unavailable' | 'reauth_required' | 'unsupported';
238
+ checkedAt: string;
239
+ code: string | null;
240
+ windows: Array<{
241
+ kind: 'weekly' | 'session';
242
+ usedPercent: number;
243
+ resetsAt: string | null;
244
+ }>;
245
+ }
246
+ export interface AccountUsageResponse {
247
+ provider: AccountProvider;
248
+ profiles: Array<{
249
+ profileId: string;
250
+ usage: AccountUsage;
251
+ }>;
252
+ }
228
253
  export interface AccountProfile {
229
254
  id: string;
230
255
  provider: AccountProvider;
@@ -233,6 +258,7 @@ export interface AccountProfile {
233
258
  updatedAt: string;
234
259
  expiresAt: string | null;
235
260
  refreshTokenExpiresAt: string | null;
261
+ refresh?: AccountRefreshStatus;
236
262
  }
237
263
  export interface AccountRunningPane {
238
264
  terminalId: TerminalId;
@@ -312,8 +338,27 @@ export interface TerminalTranscriptHistoryResponse {
312
338
  cursor: string | null;
313
339
  hasMore: boolean;
314
340
  output: string[];
341
+ page?: {
342
+ identity: string;
343
+ snapshot: string;
344
+ /** Settled readable end, excluding any incomplete trailing JSON. */
345
+ snapshotSize: number;
346
+ snapshotMtime: number;
347
+ startOffset: number;
348
+ endOffset: number;
349
+ stableBefore: number;
350
+ /** Display-capped blocks in ascending source order, without archive trailer. */
351
+ records: Array<{
352
+ id: string;
353
+ startOffset: number;
354
+ endOffset: number;
355
+ block: string;
356
+ }>;
357
+ unchanged?: boolean;
358
+ };
315
359
  }
316
360
  export interface TerminalTranscriptHistoryRequest {
361
+ ifSnapshot?: string;
317
362
  cursor?: string;
318
363
  limit?: number;
319
364
  maxLines?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/shared/protocol.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/shared/protocol.ts"],"names":[],"mappings":"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"}
@@ -16,7 +16,15 @@ export interface TerminalSummary {
16
16
  agentStatus?: {
17
17
  provider: 'codex' | 'claude';
18
18
  sessionId?: string;
19
- state: 'working' | 'done' | 'stopped' | 'idle' | 'unknown';
19
+ state: 'working' | 'finishing' | 'waiting' | 'done' | 'stopped' | 'idle' | 'unknown';
20
+ source?: 'hook' | 'transcript';
21
+ revision?: number;
22
+ epoch?: string;
23
+ completion?: {
24
+ id: string;
25
+ outcome: TerminalCompletionOutcome;
26
+ occurredAt: string;
27
+ };
20
28
  };
21
29
  agentCache?: {
22
30
  source: {
package/docs/features.md CHANGED
@@ -3,13 +3,17 @@
3
3
  ## Terminal Workspace
4
4
 
5
5
  - Tabs and split panes for real host PTYs.
6
+ - Named workspaces per server, each with a default folder and its own tabs and panes. Workspaces can be renamed, reordered, and deleted (deleting closes their sessions).
6
7
  - Per-server workspace state.
7
8
  - Refresh and temporary WebSocket reconnects reattach to active PTYs.
8
9
  - Soft close detaches terminals into restorable pills.
9
10
  - Tab rename, drag reorder, and drag-to-split controls.
10
11
  - Activity dots and completion badges show whether a tab is running, idle, exited, or finished in the background.
12
+ - Working indicators are green and unread completion indicators are blue across themes, including mobile and favorites.
11
13
  - Detached terminal destroy uses a themed confirmation modal.
12
14
  - Tab titles sync with tmux window names when tmux persistence is active.
15
+ - Favorite tabs: star a tab, then open the favorites dialog (`Ctrl+Alt+F` or the ★ button) in grid or list view. Favorites are stored on the main server, work across servers, can be reordered, and a favorite whose tab is gone is recreated in its saved folder.
16
+ - The pane header can open the pane's folder in code-server (browser VS Code). NTerminal downloads and verifies the code-server release into `~/.nterminal/addons/code-server` on first use (Linux x64/arm64, needs `curl` and `tar`) and stops idle instances.
13
17
 
14
18
  ## Server Management
15
19
 
@@ -20,6 +24,7 @@
20
24
  - CPU, memory, disk, and swap percentages above 90% / 95% are highlighted with warning / danger tones.
21
25
  - Settings includes a Servers section for checking npm package updates on each server.
22
26
  - Update all runs secondary server updates in parallel first, then updates the main server last.
27
+ - The Server information button on each server card shows the server role, external IPv4, that server's favorite tabs, and its processes grouped by project folder with CPU and memory totals.
23
28
  - Background update checks run hourly and badge Settings/Updates when an update is available.
24
29
 
25
30
  ## Security And Access
@@ -31,9 +36,18 @@
31
36
  - Trusted networks can be added by exact IP or CIDR to skip the re-auth prompt.
32
37
  - Security settings can revoke trusted devices, remove trusted networks, and disable TOTP.
33
38
 
39
+ ## Provider Accounts
40
+
41
+ - Each server keeps its own encrypted Codex and Claude credential vault. Active credentials remain in the CLI's native file, not a second vault copy.
42
+ - Account dialogs show access-token expiry, weekly usage remaining, reset time, and the last usage check. Quota checks are read-only, cached briefly, and never swap the active credential file.
43
+ - Saved accounts renew independently, including while another account is running or a new login is unfinished. Refresh outcomes include the last attempt, success, retry time, and a redacted provider error code.
44
+ - NTerminal defers direct refresh of an active credential while a native CLI owns it. An idle conversation does not mean authentication activity has stopped; native-owner refresh delegation is not implemented yet.
45
+ - Expired access tokens and revoked refresh tokens are different states. A provider-revoked refresh token requires a new login, not repeated automatic renewal attempts.
46
+ - Optional [provider lifecycle hooks](provider-hooks.md) can be installed from each server's account dialog or `nterminal hooks`. They provide verified session/activity signals without replacing native CLI authentication.
47
+
34
48
  ## Terminal Appearance And Settings
35
49
 
36
- - Settings is organized into Terminal, Shortcuts, Notifications, Security, and Servers sections.
50
+ - Settings is organized into Appearance, Shortcuts, Notifications, Security, and Servers sections.
37
51
  - The settings sidebar highlights the visible section and jumps directly to that section.
38
52
  - Terminal font size is adjustable with a slider, stepper, numeric input, and reset button.
39
53
  - Terminal font family can switch between MesloLGS NF, JetBrains Mono, and system monospace stacks.
@@ -48,6 +62,8 @@
48
62
  - Supported Codex/Claude panes show a `View history` header button and can continue into archived transcript history.
49
63
  - JSONL transcript history is loaded by cursor only when NTerminal can determine the session source unambiguously.
50
64
  - The history viewer opens near fullscreen, renders the transcript as terminal-like raw output, starts at the newest line, and loads older entries as you scroll upward.
65
+ - Server-side search scans from newest to oldest, including records not yet loaded; selecting a result loads the surrounding history. See [Terminal History](terminal-history.md#search).
66
+ - `View agents` (`Alt+A`) lists the subagents a Codex or Claude session launched, with their state, and opens each subagent's conversation. Nested subagents fold under their parent.
51
67
 
52
68
  ## Files
53
69
 
@@ -129,6 +145,10 @@ NTerminal uses Control-based workspace shortcuts so browser-reserved Command sho
129
145
  | Select pane by screen order | `Ctrl+Alt+Shift+1` through `Ctrl+Alt+Shift+9` |
130
146
  | Split right | `Ctrl+Alt+Shift+Right` |
131
147
  | Split down | `Ctrl+Alt+Shift+Down` |
148
+ | View transcript history | `Alt+H` |
149
+ | View subagents | `Alt+A` |
150
+ | Toggle tmux scrollback | `Alt+S` |
151
+ | Open favorite tabs | `Ctrl+Alt+F` |
132
152
 
133
153
  Shortcuts can be customized in Settings. If the browser, operating system, or focused editable control claims a key combination first, that handler wins before NTerminal can receive it.
134
154
 
@@ -0,0 +1,340 @@
1
+ # Provider Lifecycle Hooks
2
+
3
+ Lifecycle hooks are optional, per runtime user and per server. They improve
4
+ Codex/Claude session identification and activity notifications without forwarding
5
+ terminal output or changing the conversation. Existing transcript/process
6
+ detection remains available when hooks are absent or a binding is ambiguous.
7
+
8
+ ## Enable
9
+
10
+ Open the server's Codex or Claude account dialog, then choose **Install hooks**
11
+ and confirm. Main and secondary servers use the same control, including outbound
12
+ secondaries. Preview cannot change provider configuration.
13
+
14
+ The equivalent commands, run as the NTerminal runtime user, are:
15
+
16
+ ```sh
17
+ nterminal hooks status --provider codex
18
+ nterminal hooks install --provider codex
19
+ nterminal hooks install --provider claude
20
+ nterminal hooks remove --provider codex
21
+ nterminal hooks remove --provider claude
22
+ nterminal hooks trust --provider codex # asks for confirmation; --yes skips it
23
+ ```
24
+
25
+ Codex runs a hook from `hooks.json` only after you trust it. You can do that in
26
+ Codex (open `/hooks` and trust each NTerminal hook), or let NTerminal ask Codex
27
+ to do the same thing in one step:
28
+
29
+ - In the Codex account dialog, choose **Trust NTerminal hooks in Codex** and
30
+ confirm, or run `nterminal hooks trust --provider codex`.
31
+ - NTerminal starts a private, short-lived `codex app-server` over stdio, with
32
+ the same `CODEX_HOME` the hooks were installed into. It does not connect to or
33
+ start the shared Codex daemon, and remote control is off for that process.
34
+ The `codex` it runs is the first one on the runtime's `PATH` (then
35
+ `~/.npm-global/bin`, `~/.local/bin`, `/usr/local/bin`, `/opt/homebrew/bin`).
36
+ - Codex lists its hooks (`hooks/list`). NTerminal selects only the hooks that
37
+ come from its own `hooks.json`, are not managed by policy, and whose event,
38
+ command, timeout, and `async` flag exactly match what NTerminal installed and
39
+ recorded. If any NTerminal hook is missing, edited, or duplicated, nothing is
40
+ trusted.
41
+ - For those hooks only, Codex writes the trust itself (`config/batchWrite`),
42
+ exactly as pressing Trust in `/hooks` does: one
43
+ `[hooks.state."<hooks.json>:<event>:<group>:<handler>"]` table with
44
+ `trusted_hash` per hook in your `config.toml`. Codex computes the hash and
45
+ keeps the rest of the file. Other hooks in the same file stay untrusted.
46
+ - NTerminal then lists the hooks again and reports how many were trusted,
47
+ already trusted, or left unchanged, and fails with `verification_failed` if
48
+ Codex does not report every NTerminal hook as trusted.
49
+ - The whole exchange is limited to 10 seconds, and the Codex process (with any
50
+ helper it started) is always stopped afterwards. Preview refuses, and the
51
+ operation waits for no other hook installation: it uses the same lock.
52
+
53
+ Trusted hooks run outside the Codex sandbox; the command being trusted is the
54
+ NTerminal hook helper shown below. New Codex sessions (and resumed ones) read
55
+ the trust when they start, including sessions served by the shared daemon.
56
+ Sessions already running keep their hooks until they are reopened.
57
+
58
+ The dialog also asks Codex, read-only, whether the hooks are trusted. That
59
+ check runs at most once while `hooks.json`, `config.toml`, and NTerminal's
60
+ manifest are unchanged, and the result is reused by the status.
61
+
62
+ Errors: `codex_unavailable` (no `codex` found), `codex_timeout`,
63
+ `codex_rpc_failed` (Codex refused the request or its output was malformed),
64
+ `hooks_not_installed`, `hooks_not_listed` (Codex did not list every NTerminal
65
+ hook exactly as installed), `hooks_disabled`, `installation_busy`, and
66
+ `verification_failed`. In each case `/hooks` in Codex still works.
67
+
68
+ NTerminal never computes a trust hash itself or bypasses provider policy.
69
+ An unchanged hook definition retains its native trust;
70
+ an edited definition may require review again. Reinstalling or upgrading updates
71
+ NTerminal's hook groups in place, so their position (part of Codex's trust key)
72
+ and the position of your own hooks do not change. A new command (for example
73
+ after Node.js moved) still needs one review.
74
+
75
+ Configuration defaults are `~/.codex/hooks.json` and `~/.claude/settings.json`.
76
+ Runtime environment variables `CODEX_HOME` and `CLAUDE_CONFIG_DIR` override the
77
+ respective homes. Provider homes and settings files may be symlinks you own (for
78
+ example a dotfiles checkout); NTerminal edits the resolved file and keeps the
79
+ link. A symlink into a directory owned by another user or writable by others is
80
+ refused, and the error names that path. The state directory (`~/.nterminal`) may
81
+ also be a symlink. Provider policy can disable hooks even when the entries exist.
82
+ A configured hook is not necessarily active; the account dialog separately
83
+ reports whether verified events have been observed. The CLI reads the same
84
+ runtime `.env` as the launcher, including `export` assignments. Inherited
85
+ environment values take precedence; relative paths resolve from the application
86
+ directory, not the caller's working directory.
87
+
88
+ NTerminal never restarts a CLI to activate hooks. A CLI that does not reload its
89
+ configuration must be reopened by the operator. Use current provider versions
90
+ that support the installed events (Codex 0.158 and Claude Code 2.1.284 were
91
+ checked).
92
+
93
+ ### What Gets Installed
94
+
95
+ One hook group per event, each running the same command:
96
+
97
+ ```sh
98
+ [ -x '<node>' ] && [ -f '<state>/hooks/forward.cjs' ] && exec '<node>' '<state>/hooks/forward.cjs' '--provider' '<provider>' '--endpoint' '<state>/hooks/endpoint.json' || exit 0
99
+ ```
100
+
101
+ `<state>` is the resolved (realpath) state directory. The command uses only
102
+ POSIX `[`, `exec`, and `exit`, which `sh`, `bash`, `zsh`, and `fish` accept
103
+ (Codex runs hooks with the user's shell, Claude with bash or `$SHELL`). If
104
+ Node.js moved (for example an `nvm` upgrade) or the state directory was deleted,
105
+ the hook exits 0 without output instead of showing a hook error. Reinstall to
106
+ point the hooks at the current Node.js.
107
+
108
+ - Codex: `SessionStart`, `UserPromptSubmit`, `PreToolUse`, `PostToolUse`,
109
+ `PermissionRequest`, `Stop`, `Interrupt`, `SessionEnd`.
110
+ - Claude: `SessionStart`, `UserPromptSubmit`, `PreToolUse`, `PostToolUse`,
111
+ `PostToolUseFailure`, `PermissionRequest`, `Notification`, `Stop`,
112
+ `StopFailure`, `SessionEnd`.
113
+
114
+ NTerminal never uses a hook's response, so tool, permission, notification, and
115
+ interrupt hooks are `async: true`: the CLI does not wait for them. Only the turn
116
+ and session boundaries (`SessionStart`, `UserPromptSubmit`, `Stop`,
117
+ `StopFailure`, `SessionEnd`) stay synchronous, bounded by a 1-second timeout.
118
+ Codex does not allow asynchronous `SessionEnd`, and a CLI may exit right after a
119
+ boundary event. Events carry the helper's start time, so an asynchronous tool
120
+ event that finishes late is still ordered before a later `Stop`. `Subagent*`
121
+ events are not registered because child lifecycle events do not change a pane's
122
+ status.
123
+
124
+ Codex `notify` is not used. `hooks.json` provides `Stop`, and the session's
125
+ transcript `task_complete` record confirms the turn within the regular status
126
+ poll. `notify` is a single slot that may belong to you, and Codex passes its JSON
127
+ as one command-line argument, which fails for payloads over 128 KiB. An existing
128
+ `notify` (root or `[profiles.*]`) is left untouched and reported as
129
+ `notify_foreign`. Earlier NTerminal versions installed a `# BEGIN/END NTerminal
130
+ notify` block in `config.toml`; reinstalling or removing deletes that block (it
131
+ is recognised even after Codex rewrote other parts of the file) and nothing else.
132
+
133
+ ## Remove And Uninstall
134
+
135
+ `nterminal hooks remove --provider codex|claude` deletes only the hook groups
136
+ recorded in NTerminal's ownership manifest. Your own hooks and settings stay as
137
+ they were, including key order, indentation, and line endings. NTerminal also
138
+ deletes the Codex trust records (`[hooks.state."<hooks.json>:<event>:<group>:0"]`)
139
+ of the groups it removed, and nothing else in `config.toml`. A `hooks.json` or
140
+ `settings.json` that NTerminal created is deleted when it becomes empty. When
141
+ NTerminal's group is removed, your hooks listed after it in the same event move
142
+ up one position; Codex may then ask you to review them again.
143
+
144
+ `nterminal clean --force` and `nterminal uninstall --force` first remove
145
+ NTerminal's hooks from both providers. This is best effort: a failure prints a
146
+ warning and the command continues. Run `nterminal hooks status --provider
147
+ <provider>` afterwards if a warning appears. Uninstall NTerminal's hooks before
148
+ `npm uninstall -g nterminal`, because the hooks command is part of the package.
149
+
150
+ ## Troubleshooting
151
+
152
+ `nterminal hooks status --provider <provider>` prints `reason` and `warnings`:
153
+
154
+ - `node_unavailable`: the recorded Node.js no longer exists. The hooks exit 0
155
+ silently; reinstall to use the current Node.js.
156
+ - `helper_missing`: `<state>/hooks/forward.cjs` is gone. Reinstall.
157
+ - `unsafe_path` with `unsafePath`: that path is a symlink to a location NTerminal
158
+ will not edit, or a symlink inside the state directory.
159
+ - `hooks_disabled`: hooks are off in `config.toml` (`[features] hooks = false`,
160
+ also in the active `profile`), in `/etc/codex/requirements.toml` or
161
+ `/etc/codex/managed_config.toml` (`allow_managed_hooks_only`, `features`), in
162
+ `settings.json` (`disableAllHooks`, `allowManagedHooksOnly`), or in Claude's
163
+ managed `/etc/claude-code/managed-settings.json` (and
164
+ `managed-settings.d/*.json`).
165
+ - `ownership_conflict`: an NTerminal hook group or legacy notify command was
166
+ edited. NTerminal does not adopt edited entries; restore or delete them by
167
+ hand.
168
+ - Warnings: `legacy_definitions` (installed by an earlier version; reinstall to
169
+ get asynchronous tool hooks and the guarded command), `notify_foreign`,
170
+ `notify_profile_override` (a profile's `notify` overrides NTerminal's legacy
171
+ notify), `profile_hooks_disabled` (a non-default Codex profile disables hooks),
172
+ and `managed_policy_unreadable`.
173
+
174
+ Not checked: Claude project settings (`.claude/settings.json` and
175
+ `.claude/settings.local.json` in a repository) can disable hooks for that
176
+ project. Codex hook trust is reported only after a trust check or trust
177
+ operation (see [Enable](#enable)); `approval_required` then means Codex lists
178
+ NTerminal hooks that still need approval, and `approval_unknown` that no report
179
+ is available.
180
+
181
+ ## State And Notifications
182
+
183
+ - Idle retains the theme's existing idle color.
184
+ - Working is green; completion awaiting confirmation also stays active.
185
+ - Unread completion is blue in tabs, workspaces, and favorites. Displaying the
186
+ completed pane clears its unread mark. On mobile, other split panes remain
187
+ unread until displayed; focusing an editor does not mark a hidden terminal
188
+ read. A background server's last selected tab is not considered visible.
189
+ - Permission/input waits are not successful completion.
190
+ - A Codex `Stop` is only a completion candidate. The rollout's matching
191
+ `task_complete` marker confirms the turn on the next status poll (about 2 s).
192
+ Installs from earlier NTerminal versions may still send the legacy `notify`
193
+ `agent-turn-complete`, which is honored as confirmation too; current installs
194
+ no longer add a `notify` command.
195
+ - A Claude `Stop` is also provisional because another hook can continue the
196
+ turn. A matching post-turn `turn_duration` record confirms it when no pending
197
+ background agents/workflows remain. The record shape was checked against
198
+ Claude Code 2.1.283. A subsequent native idle notification can also confirm a
199
+ pending Stop. Missing/unknown evidence is not turned into success by a timer.
200
+ - A lost prompt hook can recover from a later Stop and fresh post-turn evidence.
201
+ A competing still-active turn is not replaced by anonymous evidence; it stays
202
+ pending until its identity can be verified. A failure recorded for that
203
+ candidate remains a stopped outcome when the identity is later confirmed.
204
+ - Errors and interruption produce a stopped outcome, not successful completion.
205
+ - Child/background hooks do not finish the parent session. A completed foreground
206
+ turn does not imply that a long-running goal or every detached task is finished.
207
+
208
+ Completion IDs are shared by live status, unread badges, animation notifications,
209
+ and push routing. The activity stream works independently of an open pane socket.
210
+ Reconnects replay a bounded recent metadata journal. A journal gap or server
211
+ restart sends a non-notifying snapshot instead of replaying historical alarms.
212
+ Missing entries in an activity snapshot never imply a terminal exit: remote
213
+ snapshots can be retained during an outage. Explicit terminal lifecycle events
214
+ and terminal discovery remain responsible for that state.
215
+ Initial REST loading preserves newer live state, local layout edits, and explicit
216
+ terminal deletion. It does not generate historical completion notifications.
217
+
218
+ The browser checks the separate activity connection while visible and on resume.
219
+ An unresponsive connection is replaced without reconnecting terminal output,
220
+ replaying input, resizing panes, or changing keyboard focus. These checks cannot
221
+ run while the browser suspends JavaScript; server-side push remains independent.
222
+
223
+ ## Ownership And Resource Limits
224
+
225
+ The installed helper is kept under the persistent NTerminal state directory,
226
+ outside the npm package. Updates refresh only a verified, previously opted-in
227
+ helper; provider configuration and approval definitions are not silently changed.
228
+ Removal deletes only entries whose ownership still matches the installation
229
+ manifest. Concurrent edits and unknown existing commands fail closed.
230
+ Interrupted installations retain an ownership journal. Installer locks are
231
+ reclaimed only after verifying a dead owner; PID reuse, boot identity, and foreign
232
+ hosts are checked. Without a stable machine ID, recovery is limited to the same
233
+ boot. Unverifiable old locks are left intact, not removed based on age.
234
+
235
+ Helper upgrades and rollback use one durable ownership journal shared by both
236
+ providers. A failed configuration rollback retains any helper that a remaining
237
+ hook may reference. User-modified helper files are never silently adopted.
238
+
239
+ The receiver also recovers publication locks whose recorded owner is provably
240
+ dead. An old, empty `hooks/.endpoint-lock` directory has no ownership record and
241
+ cannot be recovered automatically. If that specific legacy marker blocks hook
242
+ activation, stop every runtime sharing the state directory, verify the marker
243
+ is an empty directory, and remove only that empty directory with `rmdir` before
244
+ starting again. Do not delete ownership records or a lock held by a running
245
+ runtime. Ordinary transcript/process detection remains available when hook
246
+ activation is unavailable.
247
+
248
+ Events are sent through a private Unix socket, authenticated with a runtime
249
+ capability. The receiver checks the live helper/provider process ancestry and
250
+ process start identities. A session is assigned to a pane using verified process
251
+ ownership or an exact native binding, never just the working directory. Shared
252
+ daemon ambiguity falls back to native discovery/manual history selection rather
253
+ than assigning another pane's session. Claude hooks run as children of the
254
+ pane's Claude process, so their binding is exact. Codex needs the extra rules in
255
+ [Codex Shared Background Server](#codex-shared-background-server).
256
+ A transient discovery gap does not retire an existing hook session. Old resume
257
+ arguments cannot replace it, and process ownership is rechecked after asynchronous
258
+ validation before publishing status or completion.
259
+ Nested provider processes do not take over the parent pane's binding, including
260
+ children sharing its foreground process group. Normal Codex npm launchers and
261
+ provider commands that replace the pane shell remain supported.
262
+
263
+ Transcript identity checks use 64 KiB chunks with a 512 KiB header limit, not a
264
+ whole-history read. A bounded cache retains identity proofs rather than message
265
+ text; changed files require prefix revalidation. Oversized headers need an exact
266
+ live writable-file association. Incomplete, conflicting, or unprovable fork
267
+ metadata stays unconfirmed instead of selecting a different conversation.
268
+
269
+ Only lifecycle metadata is forwarded. Prompts, tool arguments, assistant bodies,
270
+ credentials, and tokens are not included in event payloads or diagnostic logs.
271
+ Queues, messages, sockets, journal size, and helper duration are bounded. Hook
272
+ failures return neutral output so NTerminal cannot block or steer the native CLI.
273
+ Under load, per-tool events (`PreToolUse`/`PostToolUse`, synchronous or
274
+ asynchronous) are coalesced per session and dropped first. Start, stop, and
275
+ completion events use a reserved part of the queue and evict waiting tool
276
+ events, so a `Stop` is not refused while tool events are queued. Tool events for
277
+ a session no pane is bound to never trigger a Codex daemon lookup. A failed
278
+ lookup for a session is reused for 5 seconds unless a `SessionStart` or
279
+ `UserPromptSubmit` for it arrives.
280
+ JSONL remains the source for history, token usage, and completion reconciliation;
281
+ hook-backed terminals use a slower usage refresh and a small cached tail check
282
+ while confirming completion.
283
+
284
+ ## Codex Shared Background Server
285
+
286
+ Codex 0.158 hands every interactive session to one shared background server
287
+ (`codex app-server --managed-daemon`). Its hooks run as children of that
288
+ server, not of the pane, so process ancestry cannot identify the pane. NTerminal
289
+ instead follows each pane's Codex connection to the server and matches the
290
+ server's open sessions by working directory. That match alone is not enough to
291
+ bind a session to a pane, because another Codex client, such as another
292
+ terminal or VS Code, can have a session open in the same directory.
293
+
294
+ A new Codex binding therefore also needs evidence tied to that pane's Codex
295
+ process:
296
+
297
+ - the first binding after Codex starts: `codex resume <id>` names the session,
298
+ or the session was created while that Codex process was starting (Codex
299
+ session IDs record their creation time);
300
+ - a later switch in the same process (`/new`, `/resume`): the session's
301
+ `SessionStart` arrived after the pane's last bound event;
302
+ - or, at any time, the pane's Codex is the only client of the shared server in
303
+ that directory: every other connected client is an identified Codex process
304
+ working in a different directory. This covers sessions opened from the
305
+ `codex resume` picker (no ID in the command line) and switches whose
306
+ `SessionStart` was missed. A connected client that cannot be identified as
307
+ Codex, or whose directory cannot be read, disables this rule.
308
+
309
+ A session already bound to one pane is never moved to another pane. If several
310
+ panes qualify, none is bound. A binding created by a completion event does not
311
+ notify for that turn, because NTerminal did not see the turn start.
312
+
313
+ Limits:
314
+
315
+ - **One Codex per directory.** Hook binding needs one Codex session per working
316
+ directory on the shared server. With two sessions in the same directory, both
317
+ panes stay unbound. The account dialog lists these panes as not linked to hook
318
+ events, and they keep transcript-based status. To run several Codex sessions
319
+ in one directory with hooks, start them with `codex --no-daemon` (also
320
+ `codex resume --no-daemon <id>`). That runs the session inside the pane's own
321
+ Codex process, where binding is exact.
322
+ - **About 60 seconds after `/new` or `/resume`.** The server keeps the previous
323
+ session loaded for about a minute. Until it unloads, the pane has two
324
+ sessions in the same directory and stays on its previous binding. Hook status
325
+ resumes with the next prompt or stop event after that. Codex also creates a
326
+ new session's file only when the session is first saved, so a new session has
327
+ nothing to match until its first prompt.
328
+ - **After an NTerminal restart.** A Codex pane that switched sessions with
329
+ `/new` or `/resume` before the restart binds again once it is the only
330
+ client in its directory; otherwise it keeps transcript-based status until its
331
+ next `/new` or `/resume`.
332
+ - **Clients outside NTerminal.** Other connected Codex clients, inside or
333
+ outside NTerminal, are ruled out by directory. A client that has already
334
+ disconnected while its turn keeps running on the server, in the same
335
+ directory as a pane whose own session is not saved yet, cannot be told
336
+ apart. One Codex per directory, or `--no-daemon`, avoids this.
337
+
338
+ The local receiver currently requires Linux procfs and Unix sockets. Unsupported
339
+ environments retain transcript/process detection. This integration does not
340
+ change credential-switch or credential-refresh safety rules.
@@ -45,10 +45,20 @@ JSONL records are loaded by cursor and rendered into readable user, assistant, a
45
45
 
46
46
  Loading older pages keeps previously loaded pages in the dialog, so ordinary scrolling can return to recent conversations without fetching them again.
47
47
 
48
+ Closing History retains its conversation pages and reading position in a browser-memory cache. Reopening the same server/terminal/session restores them immediately while checking the latest source snapshot. Unchanged sources need no conversation payload. If output was appended, bounded backward pages bridge the previous stable prefix before replacing the mutable tail; disconnected ranges are never joined as if contiguous. Source replacement or truncation resets the snapshot. Text selection defers applying refreshed content. Search-result excerpts are kept separate from the browsing snapshot.
49
+
50
+ The browser cache is cleared on logout or authentication loss, and late requests cannot refill it. Its 64 MiB approximate text budget and 30-minute idle expiry reclaim closed readers on cache activity, never truncate an active reader. Nothing is written to localStorage or IndexedDB. A full page reload therefore still fetches History from the server's disk cache.
51
+
48
52
  ### Derived Conversation Cache
49
53
 
50
54
  History and search share private derived files below the state directory's `transcript-cache` directory. Recent records are returned first. After a user opens or searches a session, a background worker fills the remaining archive, one approximately 1 MiB source window at a time with a pause between windows. Foreground requests take priority over subsequent background work. Completed archives are checked for changes every 30 seconds; appended JSONL output updates the tail while keeping valid older ranges. Source replacement, truncation, or a parser version change invalidates affected cache data. Previously visited sources are remembered in private cache metadata so prebuilding resumes after a server restart.
51
55
 
56
+ Source extraction windows are independent of storage size. Background compaction merges adjacent stable segments toward 1 MiB of derived data, retaining original offsets and empty coverage. Mutable tail segments are excluded. Merges require balanced input sizes so small appends cannot repeatedly rewrite a large segment. Each pass handles at most 16 files and yields at file boundaries when foreground work arrives; deferred merges remain pending rather than being marked complete. New files are published through an atomic manifest replacement before old files are removed; existing cache data is reused without reparsing the JSONL. Read requests reuse parsed segments, avoid permission writes when modes are already private, and do not rewrite unchanged manifests. Access timestamps are persisted at most once per minute. Fragmented caches limit a page to 32 windows and retain a continuation cursor rather than performing unbounded small-file I/O.
57
+
58
+ Files retired by invalidation or compaction are reclaimed by the bounded background maintenance scan, not by the request that replaces the manifest. They remain included in disk usage until cleanup accounts for their deletion. A parser upgrade therefore does not make the first History request wait for thousands of old cache files to be deleted.
59
+
60
+ Parser v3 bounds Codex duplicate evidence to the same 64 KiB context used for stable-tail invalidation. Forward distances use the ends of the JSON payloads, excluding trailing whitespace and line delimiters, so a bounded read or a later newline cannot change the result. Duplicate evidence outside that range remains visible instead of changing old content depending on page size or future appends. Existing v2 Codex cache data is regenerated once for this semantic correction; v2 Claude records are reused without re-extraction.
61
+
52
62
  The cache survives an NTerminal restart and uses private directory/file permissions. There is no session-count or per-session size quota. Maintenance expires caches seven days after the last user History or search request, and evicts least recently used caches at an approximately 5 GiB total safety limit. Background activity does not renew retention. New writes pause when less than 512 MiB of disk space is available; cached reads and original JSONL reads remain available. Maintenance normally runs every five minutes, and also when the total limit is reached. It contains conversation text and source paths, so treat the state directory as sensitive. Source JSONL files remain authoritative and are never changed. Missing or invalid cache files are rebuilt on demand.
53
63
 
54
64
  Cache storage failures fall back to reading the source JSONL. Search continuation does not depend on the cache remaining available. Search text is extracted before display truncation; a result deep inside a long message opens an excerpt around the match.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nterminal",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "license": "GPL-3.0-only",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.7",
@@ -22,6 +22,7 @@
22
22
  "docs/",
23
23
  "public/",
24
24
  "scripts/nterminalctl",
25
+ "scripts/nterminal-hook.cjs",
25
26
  "scripts/nterminal-tmux.conf"
26
27
  ],
27
28
  "scripts": {