@diffexai/diffex 0.2.4 → 0.2.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 (81) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/dist/AGENTS.md +0 -11
  4. package/dist/core/agent-session.d.ts +0 -1
  5. package/dist/core/agent-session.js +3 -10
  6. package/dist/core/sdk.js +1 -1
  7. package/dist/core/system-prompt-production.d.ts +7 -0
  8. package/dist/core/system-prompt-production.js +102 -0
  9. package/dist/core/system-prompt.d.ts +2 -2
  10. package/dist/core/system-prompt.js +34 -35
  11. package/dist/core/tools/subagents.js +22 -9
  12. package/dist/modes/print-mode.js +12 -14
  13. package/dist/node_modules/@diffexai/diffex-agent-core/distribution-components.json +4 -4
  14. package/dist/node_modules/@diffexai/diffex-agent-core/distribution-files.json +1 -1
  15. package/dist/node_modules/@diffexai/diffex-agent-core/package.json +1 -1
  16. package/dist/node_modules/@diffexai/diffex-ai/dist/providers/data/.manifest.json +1 -1
  17. package/dist/node_modules/@diffexai/diffex-ai/dist/providers/data/amazon-bedrock.json +1 -1
  18. package/dist/node_modules/@diffexai/diffex-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  19. package/dist/node_modules/@diffexai/diffex-ai/dist/providers/data/fireworks.json +1 -1
  20. package/dist/node_modules/@diffexai/diffex-ai/dist/providers/data/mistral.json +1 -1
  21. package/dist/node_modules/@diffexai/diffex-ai/dist/providers/data/nvidia.json +1 -1
  22. package/dist/node_modules/@diffexai/diffex-ai/dist/providers/data/openrouter.json +1 -1
  23. package/dist/node_modules/@diffexai/diffex-ai/dist/providers/data/qwen-token-plan-cn.json +1 -1
  24. package/dist/node_modules/@diffexai/diffex-ai/dist/providers/data/qwen-token-plan.json +1 -1
  25. package/dist/node_modules/@diffexai/diffex-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  26. package/dist/node_modules/@diffexai/diffex-ai/distribution-components.json +3 -3
  27. package/dist/node_modules/@diffexai/diffex-ai/distribution-files.json +11 -11
  28. package/dist/node_modules/@diffexai/diffex-ai/package.json +1 -1
  29. package/dist/node_modules/@diffexai/diffex-client/distribution-components.json +3 -3
  30. package/dist/node_modules/@diffexai/diffex-client/distribution-files.json +1 -1
  31. package/dist/node_modules/@diffexai/diffex-client/package.json +1 -1
  32. package/dist/node_modules/@diffexai/diffex-harness-state/distribution-components.json +2 -2
  33. package/dist/node_modules/@diffexai/diffex-harness-state/distribution-files.json +1 -1
  34. package/dist/node_modules/@diffexai/diffex-harness-state/package.json +1 -1
  35. package/dist/node_modules/@diffexai/diffex-protocol/distribution-components.json +2 -2
  36. package/dist/node_modules/@diffexai/diffex-protocol/distribution-files.json +1 -1
  37. package/dist/node_modules/@diffexai/diffex-protocol/package.json +1 -1
  38. package/dist/node_modules/@diffexai/diffex-telemetry/distribution-components.json +2 -2
  39. package/dist/node_modules/@diffexai/diffex-telemetry/distribution-files.json +1 -1
  40. package/dist/node_modules/@diffexai/diffex-telemetry/package.json +1 -1
  41. package/dist/node_modules/@diffexai/diffex-tui/distribution-components.json +2 -2
  42. package/dist/node_modules/@diffexai/diffex-tui/distribution-files.json +1 -1
  43. package/dist/node_modules/@diffexai/diffex-tui/package.json +1 -1
  44. package/dist/server/create-harness.js +1 -1
  45. package/distribution-components.json +11 -11
  46. package/distribution-files.json +49 -41
  47. package/npm-shrinkwrap.json +2 -2
  48. package/package.json +1 -31
  49. package/release/distribution-manifest.json +4 -4
  50. package/release/install-package-lock.json +5 -5
  51. package/release/install-package.json +2 -2
  52. package/docs/compaction.md +0 -401
  53. package/docs/containerization.md +0 -84
  54. package/docs/custom-provider.md +0 -774
  55. package/docs/environment-variables.md +0 -88
  56. package/docs/evolution.md +0 -90
  57. package/docs/extensions.md +0 -2982
  58. package/docs/images/interactive-mode.png +0 -0
  59. package/docs/images/tree-view.png +0 -0
  60. package/docs/installation.md +0 -118
  61. package/docs/json.md +0 -91
  62. package/docs/keybindings.md +0 -241
  63. package/docs/llama-cpp.md +0 -99
  64. package/docs/models.md +0 -565
  65. package/docs/packages.md +0 -232
  66. package/docs/prompt-templates.md +0 -96
  67. package/docs/providers.md +0 -317
  68. package/docs/quickstart.md +0 -161
  69. package/docs/rpc.md +0 -1647
  70. package/docs/sdk.md +0 -1332
  71. package/docs/security.md +0 -66
  72. package/docs/session-format.md +0 -438
  73. package/docs/sessions.md +0 -162
  74. package/docs/settings.md +0 -341
  75. package/docs/shell-aliases.md +0 -13
  76. package/docs/skills.md +0 -227
  77. package/docs/terminal-setup.md +0 -152
  78. package/docs/themes.md +0 -326
  79. package/docs/tmux.md +0 -63
  80. package/docs/tui.md +0 -940
  81. package/docs/usage.md +0 -434
package/docs/security.md DELETED
@@ -1,66 +0,0 @@
1
- # Security
2
-
3
- Diffex is a local coding agent. It runs with the permissions of the user account that starts it, and it treats files writable by that user as inside the same local trust boundary.
4
-
5
- ## Project Trust
6
-
7
- Project trust controls whether Diffex loads project-local settings, resources, packages, and extensions. It is not a sandbox and it does not restrict what the model can ask tools to do after you start working in a directory.
8
-
9
- Diffex considers a project to have resources that require trust when it finds any of these from the current working directory:
10
-
11
- - `.diffex/settings.json`
12
- - `.diffex/extensions`, `.diffex/skills`, `.diffex/prompts`, or `.diffex/themes`
13
- - `.diffex/SYSTEM.md` or `.diffex/APPEND_SYSTEM.md`
14
- - project `.agents/skills` in the current directory or an ancestor directory
15
-
16
- A bare `.diffex` directory does not count as a project resource that requires trust.
17
-
18
- When an interactive session starts in a project with resources that require trust and no saved decision for the current directory or a parent directory, Diffex follows `defaultProjectTrust` from global settings. The default value is `"ask"`, which asks whether to trust the project when UI is available. Saved decisions are stored by canonical directory in `~/.diffex/agent/trust.json`, and the closest saved decision on the current or parent path applies before the global default.
19
-
20
- Trusting a project allows Diffex to load project resources that require trust, including:
21
-
22
- - `.diffex/settings.json`
23
- - `.diffex` resources such as extensions, skills, prompt templates, themes, and system prompt files
24
- - missing project packages configured through project settings
25
- - project-local extensions and project package-managed extensions
26
-
27
- Declining trust skips protected resources. Context files such as `AGENTS.override.md`, `AGENTS.md`, and `CLAUDE.md` are loaded regardless of project trust unless context loading is disabled. Before trust is resolved, Diffex only loads context files, user/global extensions, and CLI `-e` extensions. User/global and CLI extensions can handle the `project_trust` event; the first extension that returns a yes/no decision owns the decision.
28
-
29
- Non-interactive modes (`-p`, `--mode json`, and `--mode rpc`) do not show a trust prompt. Without an applicable saved trust decision, `defaultProjectTrust: "ask"` and `"never"` ignore such resources, while `"always"` trusts them. Use `--approve`/`-a` or `--no-approve`/`-na` to override project trust for one run.
30
-
31
- ## No Built-in Sandbox
32
-
33
- Diffex does not include a built-in sandbox. Built-in tools can read files, write files, edit files, and run shell commands with the permissions of the Diffex process. Extensions are TypeScript modules that run with the same permissions. Package installs, shell commands, language servers, test commands, and other developer tools behave as ordinary local processes.
34
-
35
- This is intentional. Diffex is designed to operate on local source trees, invoke project toolchains, and integrate with the user's existing development environment. A partial in-process sandbox would be easy to misunderstand as a security boundary while still depending on the host shell, filesystem, package managers, credentials, and extension code. Real isolation needs to come from the operating system or a virtualization/container boundary.
36
-
37
- Project trust is only an input-loading guard. It prevents a repository from silently changing Diffex's settings or extensions before you approve it. It does not make untrusted code, untrusted prompts, or untrusted model output safe. Prompt injection from repository files, comments, documentation, context files, or build output is expected local-agent risk and cannot be reliably prevented by Diffex.
38
-
39
- ## Running Untrusted or Unmonitored Work
40
-
41
- For untrusted repositories, generated code you do not intend to monitor closely, or unattended automation, run Diffex in a contained environment. Use a container, VM, micro-VM, remote sandbox, or policy-controlled sandbox with only the files and credentials required for the task.
42
-
43
- Common patterns are documented in [Containerization](containerization.md):
44
-
45
- - run the whole Diffex process inside a container/sandbox
46
- - mount only the workspace paths the agent should access
47
- - avoid mounting host `~/.diffex/agent` unless the container should access host sessions, settings, and credentials
48
- - pass the minimum required API keys or use short-lived credentials
49
- - restrict network access when the task does not need it
50
- - review diffs and outputs before copying results back to trusted systems
51
-
52
- If you bind-mount a host workspace read/write, writes from inside the container or VM can still modify host files. Use read-only mounts or copy files into and out of the sandbox when you need stronger protection from unintended writes.
53
-
54
- ## Evolution Data and Generated Skills
55
-
56
- Invocation traces, evolution artifacts and failures, candidate history, lifecycle observations, writer receipts, and harness revisions persist beneath the effective agent directory. They can contain session evidence, generated instructions, skill content, paths, and runtime metadata. Evolution model calls may consume provider quota.
57
-
58
- Evolved skills are generated instructions and can direct the model to use tools or referenced assets. Structural, provenance, scope, and security validation reduce risk but do not prove semantic safety and are not a sandbox. Retrieval and behavioral evaluation is temporarily skipped when no evaluator is configured, so those candidates have not been checked for retrieval quality, behavior improvement, misapplication, or regressions. Project trust does not sandbox an already selected harness revision.
59
-
60
- Review `/evolve` provenance, draft diffs, validation, whether evaluation ran or was skipped, and the complete generated package before activation. Use `/version` and rollback controls to select immutable revisions, and use OS or container isolation for unattended work. See [Harness Evolution](evolution.md).
61
-
62
- ## Reporting Security Issues
63
-
64
- To report a security issue, follow the repository [Security Policy](https://github.com/diffexai/diffex/blob/main/SECURITY.md). Do not open a public issue for security-sensitive reports.
65
-
66
- Expected local-agent behavior, lack of a built-in sandbox, prompt injection from untrusted content, and behavior of user-installed extensions or skills are generally outside the security boundary unless the report demonstrates a real privilege-boundary bypass or shows how Diffex grants access that the local user did not already have.
@@ -1,438 +0,0 @@
1
- # Session File Format
2
-
3
- Sessions are stored as JSONL (JSON Lines) files. Each line is a JSON object with a `type` field. Session entries form a tree structure via `id`/`parentId` fields, enabling in-place branching without creating new files.
4
-
5
- ## File Location
6
-
7
- ```
8
- ~/.diffex/agent/sessions/--<path>--/<timestamp>_<uuid>.jsonl
9
- ```
10
-
11
- Where `<path>` is the working directory with `/` replaced by `-`.
12
-
13
- ## Deleting Sessions
14
-
15
- Sessions can be removed by deleting their `.jsonl` files under `~/.diffex/agent/sessions/`.
16
-
17
- Diffex also supports deleting sessions interactively from `/resume` (select a session and press `Ctrl+D`, then confirm). When available, Diffex uses the `trash` CLI to avoid permanent deletion.
18
-
19
- ## Session Version
20
-
21
- Sessions have a version field in the header:
22
-
23
- - **Version 1**: Linear entry sequence (legacy, auto-migrated on load)
24
- - **Version 2**: Tree structure with `id`/`parentId` linking
25
- - **Version 3**: Renamed `hookMessage` role to `custom` (extensions unification)
26
-
27
- Existing sessions are automatically migrated to the current version (v3) when loaded.
28
-
29
- ## Source Files
30
-
31
- Source on GitHub ([diffex](https://github.com/diffexai/diffex)):
32
- - [`packages/coding-agent/src/core/session-manager.ts`](https://github.com/diffexai/diffex/blob/main/packages/coding-agent/src/core/session-manager.ts) - Session entry types and SessionManager
33
- - [`packages/coding-agent/src/core/messages.ts`](https://github.com/diffexai/diffex/blob/main/packages/coding-agent/src/core/messages.ts) - Extended message types (BashExecutionMessage, CustomMessage, etc.)
34
- - [`packages/ai/src/types.ts`](https://github.com/diffexai/diffex/blob/main/packages/ai/src/types.ts) - Base message types (UserMessage, AssistantMessage, ToolResultMessage)
35
- - [`packages/agent/src/types.ts`](https://github.com/diffexai/diffex/blob/main/packages/agent/src/types.ts) - AgentMessage union type
36
-
37
- For TypeScript definitions in your project, inspect `node_modules/@diffexai/diffex/dist/` and `node_modules/@diffexai/diffex-ai/dist/`.
38
-
39
- ## Message Types
40
-
41
- Session entries contain `AgentMessage` objects. Understanding these types is essential for parsing sessions and writing extensions.
42
-
43
- ### Content Blocks
44
-
45
- Messages contain arrays of typed content blocks:
46
-
47
- ```typescript
48
- interface TextContent {
49
- type: "text";
50
- text: string;
51
- }
52
-
53
- interface ImageContent {
54
- type: "image";
55
- data: string; // base64 encoded
56
- mimeType: string; // e.g., "image/jpeg", "image/png"
57
- }
58
-
59
- interface ThinkingContent {
60
- type: "thinking";
61
- thinking: string;
62
- }
63
-
64
- interface ToolCall {
65
- type: "toolCall";
66
- id: string;
67
- name: string;
68
- arguments: Record<string, any>;
69
- }
70
- ```
71
-
72
- ### Base Message Types (from diffex-ai)
73
-
74
- ```typescript
75
- interface UserMessage {
76
- role: "user";
77
- content: string | (TextContent | ImageContent)[];
78
- timestamp: number; // Unix ms
79
- }
80
-
81
- interface AssistantMessage {
82
- role: "assistant";
83
- content: (TextContent | ThinkingContent | ToolCall)[];
84
- api: string;
85
- provider: string;
86
- model: string;
87
- usage: Usage;
88
- stopReason: "stop" | "length" | "toolUse" | "error" | "aborted";
89
- errorMessage?: string;
90
- timestamp: number;
91
- }
92
-
93
- interface ToolResultMessage {
94
- role: "toolResult";
95
- toolCallId: string;
96
- toolName: string;
97
- content: (TextContent | ImageContent)[];
98
- details?: any; // Tool-specific metadata
99
- usage?: Usage; // Nested LLM work performed by the tool
100
- isError: boolean;
101
- timestamp: number;
102
- }
103
-
104
- interface Usage {
105
- input: number;
106
- output: number;
107
- cacheRead: number;
108
- cacheWrite: number;
109
- totalTokens: number;
110
- cost: {
111
- input: number;
112
- output: number;
113
- cacheRead: number;
114
- cacheWrite: number;
115
- total: number;
116
- };
117
- }
118
- ```
119
-
120
- The exported diffex-ai `StopReason` type also includes `"pending"`, but that value is reserved for partial messages in streaming events. Terminal `done`/`error` messages replace it with a completion reason before Diffex persists the assistant message, so `"pending"` should never appear in session JSONL.
121
-
122
- ### Extended Message Types (from diffex-coding-agent)
123
-
124
- ```typescript
125
- interface BashExecutionMessage {
126
- role: "bashExecution";
127
- command: string;
128
- output: string;
129
- exitCode: number | undefined;
130
- cancelled: boolean;
131
- truncated: boolean;
132
- fullOutputPath?: string;
133
- excludeFromContext?: boolean; // true for !! prefix commands
134
- timestamp: number;
135
- }
136
-
137
- interface CustomMessage {
138
- role: "custom";
139
- customType: string; // Extension identifier
140
- content: string | (TextContent | ImageContent)[];
141
- display: boolean; // Show in TUI
142
- details?: any; // Extension-specific metadata
143
- timestamp: number;
144
- }
145
-
146
- interface BranchSummaryMessage {
147
- role: "branchSummary";
148
- summary: string;
149
- fromId: string; // Entry we branched from
150
- timestamp: number;
151
- }
152
-
153
- interface CompactionSummaryMessage {
154
- role: "compactionSummary";
155
- summary: string;
156
- tokensBefore: number;
157
- timestamp: number;
158
- }
159
- ```
160
-
161
- ### AgentMessage Union
162
-
163
- ```typescript
164
- type AgentMessage =
165
- | UserMessage
166
- | AssistantMessage
167
- | ToolResultMessage
168
- | BashExecutionMessage
169
- | CustomMessage
170
- | BranchSummaryMessage
171
- | CompactionSummaryMessage;
172
- ```
173
-
174
- ## Entry Base
175
-
176
- All entries (except `SessionHeader`) extend `SessionEntryBase`:
177
-
178
- ```typescript
179
- interface SessionEntryBase {
180
- type: string;
181
- id: string; // 8-char hex ID
182
- parentId: string | null; // Parent entry ID (null for first entry)
183
- timestamp: string; // ISO timestamp
184
- }
185
- ```
186
-
187
- ## Entry Types
188
-
189
- ### SessionHeader
190
-
191
- First line of the file. Metadata only, not part of the tree (no `id`/`parentId`).
192
-
193
- ```json
194
- {"type":"session","version":3,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project"}
195
- ```
196
-
197
- For sessions with a parent (created via `/fork`, `/clone`, or `newSession({ parentSession })`):
198
-
199
- ```json
200
- {"type":"session","version":3,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project","parentSession":"/path/to/original/session.jsonl"}
201
- ```
202
-
203
- ### SessionMessageEntry
204
-
205
- A message in the conversation. The `message` field contains an `AgentMessage`.
206
-
207
- ```json
208
- {"type":"message","id":"a1b2c3d4","parentId":"prev1234","timestamp":"2024-12-03T14:00:01.000Z","message":{"role":"user","content":"Hello"}}
209
- {"type":"message","id":"b2c3d4e5","parentId":"a1b2c3d4","timestamp":"2024-12-03T14:00:02.000Z","message":{"role":"assistant","content":[{"type":"text","text":"Hi!"}],"provider":"anthropic","model":"claude-sonnet-4-5","usage":{...},"stopReason":"stop"}}
210
- {"type":"message","id":"c3d4e5f6","parentId":"b2c3d4e5","timestamp":"2024-12-03T14:00:03.000Z","message":{"role":"toolResult","toolCallId":"call_123","toolName":"bash","content":[{"type":"text","text":"output"}],"isError":false}}
211
- ```
212
-
213
- ### ModelChangeEntry
214
-
215
- Emitted when the user switches models mid-session.
216
-
217
- ```json
218
- {"type":"model_change","id":"d4e5f6g7","parentId":"c3d4e5f6","timestamp":"2024-12-03T14:05:00.000Z","provider":"openai","modelId":"gpt-4o"}
219
- ```
220
-
221
- ### ThinkingLevelChangeEntry
222
-
223
- Emitted when the user changes the thinking/reasoning level.
224
-
225
- ```json
226
- {"type":"thinking_level_change","id":"e5f6g7h8","parentId":"d4e5f6g7","timestamp":"2024-12-03T14:06:00.000Z","thinkingLevel":"high"}
227
- ```
228
-
229
- ### CompactionEntry
230
-
231
- Created when context is compacted. Stores a summary of earlier messages.
232
-
233
- ```json
234
- {"type":"compaction","id":"f6g7h8i9","parentId":"e5f6g7h8","timestamp":"2024-12-03T14:10:00.000Z","summary":"User discussed X, Y, Z...","firstKeptEntryId":"c3d4e5f6","tokensBefore":50000}
235
- ```
236
-
237
- Newer harness-generated compactions embed the retained post-compaction context directly on the entry, instead of `firstKeptEntryId`:
238
-
239
- ```json
240
- {"type":"compaction","id":"f6g7h8i9","parentId":"e5f6g7h8","timestamp":"2024-12-03T14:10:00.000Z","summary":"User discussed X, Y, Z...","tokensBefore":50000,"retainedTail":[{"role":"user","content":"latest request"},{"role":"assistant","content":[{"type":"text","text":"latest reply"}],"provider":"anthropic","model":"claude-sonnet-4-5","usage":{...},"stopReason":"stop"}]}
241
- ```
242
-
243
- Optional fields:
244
- - `usage`: LLM usage from generating the summary; included in session token and cost totals
245
- - `retainedTail`: Materialized `AgentMessage[]` kept after compaction. This is optional only for backward compatibility with older sessions. Newer harness-generated compactions include it so we can rebuild context from this checkpoint without walking older entries before the compaction entry.
246
- - `details`: Implementation-specific data (e.g., `{ readFiles: string[], modifiedFiles: string[] }` for default, or custom data for extensions)
247
- - `fromHook`: `true` if generated by an extension, `false`/`undefined` if Diffex-generated (legacy field name)
248
- - `firstKeptEntryId`: for compatibility with old entry format.
249
-
250
- ### BranchSummaryEntry
251
-
252
- Created when switching branches via `/tree` with an LLM generated summary of the left branch up to the common ancestor. Captures context from the abandoned path.
253
-
254
- ```json
255
- {"type":"branch_summary","id":"g7h8i9j0","parentId":"a1b2c3d4","timestamp":"2024-12-03T14:15:00.000Z","fromId":"f6g7h8i9","summary":"Branch explored approach A..."}
256
- ```
257
-
258
- Optional fields:
259
- - `usage`: LLM usage from generating the summary; included in session token and cost totals
260
- - `details`: File tracking data (`{ readFiles: string[], modifiedFiles: string[] }`) for default, or custom data for extensions
261
- - `fromHook`: `true` if generated by an extension, `false`/`undefined` if Diffex-generated (legacy field name)
262
-
263
- ### CustomEntry
264
-
265
- Extension state persistence. Does NOT participate in LLM context.
266
-
267
- ```json
268
- {"type":"custom","id":"h8i9j0k1","parentId":"g7h8i9j0","timestamp":"2024-12-03T14:20:00.000Z","customType":"my-extension","data":{"count":42}}
269
- ```
270
-
271
- Use `customType` to identify your extension's entries on reload. Interactive mode can render custom entries via `diffex.registerEntryRenderer(customType, renderer)`, but they still do not participate in LLM context.
272
-
273
- ### CustomMessageEntry
274
-
275
- Extension-injected messages that DO participate in LLM context.
276
-
277
- ```json
278
- {"type":"custom_message","id":"i9j0k1l2","parentId":"h8i9j0k1","timestamp":"2024-12-03T14:25:00.000Z","customType":"my-extension","content":"Injected context...","display":true}
279
- ```
280
-
281
- Fields:
282
- - `content`: String or `(TextContent | ImageContent)[]` (same as UserMessage)
283
- - `display`: `true` = show in TUI with distinct styling, `false` = hidden
284
- - `details`: Optional extension-specific metadata (not sent to LLM)
285
-
286
- ### LabelEntry
287
-
288
- User-defined bookmark/marker on an entry.
289
-
290
- ```json
291
- {"type":"label","id":"j0k1l2m3","parentId":"i9j0k1l2","timestamp":"2024-12-03T14:30:00.000Z","targetId":"a1b2c3d4","label":"checkpoint-1"}
292
- ```
293
-
294
- Set `label` to `undefined` to clear a label.
295
-
296
- ### SessionInfoEntry
297
-
298
- Session metadata (e.g., user-defined display name). Set via `/name`, `--name` / `-n`, or `diffex.setSessionName()` in extensions.
299
-
300
- ```json
301
- {"type":"session_info","id":"k1l2m3n4","parentId":"j0k1l2m3","timestamp":"2024-12-03T14:35:00.000Z","name":"Refactor auth module"}
302
- ```
303
-
304
- The session name is displayed in the session selector (`/resume`) instead of the first message when set.
305
-
306
- ## Tree Structure
307
-
308
- Entries form a tree:
309
- - First entry has `parentId: null`
310
- - Each subsequent entry points to its parent via `parentId`
311
- - Branching creates new children from an earlier entry
312
- - The "leaf" is the current position in the tree
313
-
314
- ```
315
- [user msg] ─── [assistant] ─── [user msg] ─── [assistant] ─┬─ [user msg] ← current leaf
316
- │
317
- └─ [branch_summary] ─── [user msg] ← alternate branch
318
- ```
319
-
320
- ## Context Building
321
-
322
- `buildContextEntries()` walks from the current leaf to the root, producing the active entry list while honoring compaction:
323
-
324
- 1. Collects all entries on the path
325
- 2. If a `CompactionEntry` is on the path:
326
- - Includes the compaction entry first
327
- - If `retainedTail` is present, it acts as a self-contained checkpoint and entries after the compaction are included
328
- - Otherwise entries from `firstKeptEntryId` to the compaction are included
329
- - Then entries after compaction are included
330
- 3. Preserves non-message entries in the selected range so interactive mode can render them
331
-
332
- `buildSessionContext()` builds on that entry list to produce the message list for the LLM:
333
-
334
- 1. Extracts current model and thinking level settings from the full path
335
- 2. Converts selected entries to messages:
336
- - `message` -> stored `AgentMessage`
337
- - `compaction` -> `compactionSummary` plus `retainedTail` when present
338
- - `branch_summary` -> `branchSummary`
339
- - `custom_message` -> `CustomMessage`
340
- - `custom` -> no context message
341
-
342
- This makes newer compactions act like self-contained checkpoints. `retainedTail` is optional only so older sessions that only store `firstKeptEntryId` continue to load correctly.
343
-
344
- ## Parsing Example
345
-
346
- ```typescript
347
- import { readFileSync } from "fs";
348
-
349
- const lines = readFileSync("session.jsonl", "utf8").trim().split("\n");
350
-
351
- for (const line of lines) {
352
- const entry = JSON.parse(line);
353
-
354
- switch (entry.type) {
355
- case "session":
356
- console.log(`Session v${entry.version ?? 1}: ${entry.id}`);
357
- break;
358
- case "message":
359
- console.log(`[${entry.id}] ${entry.message.role}: ${JSON.stringify(entry.message.content)}`);
360
- break;
361
- case "compaction":
362
- console.log(`[${entry.id}] Compaction: ${entry.tokensBefore} tokens summarized`);
363
- break;
364
- case "branch_summary":
365
- console.log(`[${entry.id}] Branch from ${entry.fromId}`);
366
- break;
367
- case "custom":
368
- console.log(`[${entry.id}] Custom (${entry.customType}): ${JSON.stringify(entry.data)}`);
369
- break;
370
- case "custom_message":
371
- console.log(`[${entry.id}] Extension message (${entry.customType}): ${entry.content}`);
372
- break;
373
- case "label":
374
- console.log(`[${entry.id}] Label "${entry.label}" on ${entry.targetId}`);
375
- break;
376
- case "model_change":
377
- console.log(`[${entry.id}] Model: ${entry.provider}/${entry.modelId}`);
378
- break;
379
- case "thinking_level_change":
380
- console.log(`[${entry.id}] Thinking: ${entry.thinkingLevel}`);
381
- break;
382
- }
383
- }
384
- ```
385
-
386
- ## SessionManager API
387
-
388
- Key methods for working with sessions programmatically.
389
-
390
- ### Static Creation Methods
391
- - `SessionManager.create(cwd, sessionDir?)` - New session
392
- - `SessionManager.open(path, sessionDir?)` - Open existing session file
393
- - `SessionManager.continueRecent(cwd, sessionDir?)` - Continue most recent or create new
394
- - `SessionManager.inMemory(cwd?)` - No file persistence
395
- - `SessionManager.forkFrom(sourcePath, targetCwd, sessionDir?)` - Fork session from another project
396
-
397
- ### Static Listing Methods
398
- - `SessionManager.list(cwd, sessionDir?, onProgress?)` - List sessions for a directory
399
- - `SessionManager.listAll(onProgress?)` - List all sessions across all projects
400
-
401
- ### Instance Methods - Session Management
402
- - `newSession(options?)` - Start a new session (options: `{ parentSession?: string }`)
403
- - `setSessionFile(path)` - Switch to a different session file
404
- - `createBranchedSession(leafId)` - Extract branch to new session file
405
-
406
- ### Instance Methods - Appending (all return entry ID)
407
- - `appendMessage(message)` - Add message
408
- - `appendThinkingLevelChange(level)` - Record thinking change
409
- - `appendModelChange(provider, modelId)` - Record model change
410
- - `appendCompaction(summary, firstKeptEntryId, tokensBefore, details?, fromHook?)` - Add compaction
411
- - `appendCustomEntry(customType, data?)` - Extension state (not in context)
412
- - `appendSessionInfo(name)` - Set session display name
413
- - `appendCustomMessageEntry(customType, content, display, details?)` - Extension message (in context)
414
- - `appendLabelChange(targetId, label)` - Set/clear label
415
-
416
- ### Instance Methods - Tree Navigation
417
- - `getLeafId()` - Current position
418
- - `getLeafEntry()` - Get current leaf entry
419
- - `getEntry(id)` - Get entry by ID
420
- - `getBranch(fromId?)` - Walk from entry to root
421
- - `getTree()` - Get full tree structure
422
- - `getChildren(parentId)` - Get direct children
423
- - `getLabel(id)` - Get label for entry
424
- - `branch(entryId)` - Move leaf to earlier entry
425
- - `resetLeaf()` - Reset leaf to null (before any entries)
426
- - `branchWithSummary(entryId, summary, details?, fromHook?)` - Branch with context summary
427
-
428
- ### Instance Methods - Context & Info
429
- - `buildContextEntries()` - Get active branch entries with compaction applied
430
- - `buildSessionContext()` - Get messages, thinkingLevel, and model for LLM
431
- - `getEntries()` - All entries (excluding header)
432
- - `getHeader()` - Session header metadata
433
- - `getSessionName()` - Get display name from latest session_info entry
434
- - `getCwd()` - Working directory
435
- - `getSessionDir()` - Session storage directory
436
- - `getSessionId()` - Session UUID
437
- - `getSessionFile()` - Session file path (undefined for in-memory)
438
- - `isPersisted()` - Whether session is saved to disk
package/docs/sessions.md DELETED
@@ -1,162 +0,0 @@
1
- # Sessions
2
-
3
- Diffex saves conversations as sessions so you can continue work, branch from earlier turns, and revisit previous paths.
4
-
5
- ## Session Storage
6
-
7
- Sessions auto-save to `~/.diffex/agent/sessions/`, organized by working directory. Each session is a JSONL file with a tree structure.
8
-
9
- ```bash
10
- diffex -c # Continue most recent session
11
- diffex -r # Browse and select from past sessions
12
- diffex --no-session # Ephemeral mode; do not save
13
- diffex --name "my task" # Set session display name at startup
14
- diffex --session <path|id> # Use a specific session file or partial session ID
15
- diffex --fork <path|id> # Fork a session file or partial session ID into a new session
16
- ```
17
-
18
- Use `/session` in interactive mode to see the current session file, session ID, message count, tokens, and cost.
19
-
20
- For the JSONL file format and SessionManager API, see [Session Format](session-format.md).
21
-
22
- ## Session Commands
23
-
24
- | Command | Description |
25
- |---------|-------------|
26
- | `/resume` | Browse and select previous sessions |
27
- | `/new` | Start a new session |
28
- | `/name <name>` | Set the current session display name |
29
- | `/session` | Show session info |
30
- | `/subagents` | Show sub-agents in the current session |
31
- | `/tree` | Navigate the current session tree |
32
- | `/fork` | Create a new session from a previous user message |
33
- | `/clone` | Duplicate the current active branch into a new session |
34
- | `/compact [prompt]` | Summarize older context; see [Compaction](compaction.md) |
35
- | `/export [file]` | Export session to HTML |
36
- | `/share` | Upload as a secret GitHub Gist and return its direct URL |
37
-
38
- ## Sub-agents in Saved Sessions
39
-
40
- Saved parent sessions automatically preserve their branch-scoped sub-agent roster and usable child histories. Reopening a parent restores terminal records as recorded and changes children that were queued or running to interrupted. Reopening does not resume provider work, replay the original task, or redeliver a completed result.
41
-
42
- An explicit wake resumes a child from its saved history when that history is usable. If the history is missing, unsafe, or invalid, Diffex creates an isolated replacement and runs only the wake message. A wake uses the child's complete captured model, thinking level, tools, and harness when all remain usable; otherwise it uses the complete current parent configuration and reports a warning rather than mixing configurations.
43
- Restored and replacement children receive the child-only `escalate_to_parent` tool only after an explicit wake starts a live run.
44
-
45
- Escalation reports delivered to the root are hidden from the transcript but are not secret or redacted.
46
- Accepted idle reports, delivered active reports, and the child's tool-call arguments remain normal session history.
47
- Both `subagent_results` and `subagent_escalations` are untrusted delegated-agent reports rather than user or system instructions or authority. The root is instructed to verify material claims against the parent task and available evidence, and to follow embedded instructions only when they agree with the parent task, repository policy, and current evidence.
48
- An idle report in a saved session is appended to the currently selected parent branch without starting a provider call and is supplied to the parent on its next explicit run; an in-memory parent keeps the report only in the live session.
49
- Branch navigation retains only the selected branch's pending saved reports for capacity accounting.
50
- Deleting a parent session continues to delete its parent history and owned child-session namespace through the normal parent-owned deletion behavior.
51
-
52
- Branch navigation and session-context resets reconcile the roster only after the transition is safe. In-memory parents and `--no-session` sessions keep sub-agents ephemeral. Setting `subagents: false` disables the sub-agent tools without erasing a saved roster.
53
-
54
- ## Resuming and Deleting Sessions
55
-
56
- `/resume` opens an interactive session picker for the current project. `diffex -r` opens the same picker at startup.
57
-
58
- In the picker you can:
59
-
60
- - search by typing
61
- - toggle path display with Ctrl+P
62
- - toggle sort mode with Ctrl+S
63
- - filter to named sessions with Ctrl+N
64
- - rename with Ctrl+R
65
- - delete with Ctrl+D, then confirm
66
-
67
- When available, Diffex uses the `trash` CLI for deletion instead of permanently removing files.
68
-
69
- ## Naming Sessions
70
-
71
- Use `/name <name>` to set a human-readable session name:
72
-
73
- ```text
74
- /name Refactor auth module
75
- ```
76
-
77
- Set the name at startup with `--name` or `-n`:
78
-
79
- ```bash
80
- diffex --name "Refactor auth module"
81
- diffex --name "CI audit" -p "Review this build failure"
82
- ```
83
-
84
- Named sessions are easier to find in `/resume` and `diffex -r`.
85
-
86
- ## Branching with `/tree`
87
-
88
- Sessions are stored as trees. Every entry has an `id` and `parentId`, and the current position is the active leaf. `/tree` lets you jump to any previous point and continue from there without creating a new file.
89
-
90
- <p align="center"><img src="images/tree-view.png" alt="Tree View" width="600"></p>
91
-
92
- Example shape:
93
-
94
- ```text
95
- ├─ user: "Hello, can you help..."
96
- │ └─ assistant: "Of course! I can..."
97
- │ ├─ user: "Let's try approach A..."
98
- │ │ └─ assistant: "For approach A..."
99
- │ │ └─ user: "That worked..." ← active
100
- │ └─ user: "Actually, approach B..."
101
- │ └─ assistant: "For approach B..."
102
- ```
103
-
104
- ### Tree Controls
105
-
106
- | Key | Action |
107
- |-----|--------|
108
- | ↑/↓ | Navigate visible entries |
109
- | ←/→ | Page up/down |
110
- | Ctrl+←/Ctrl+→ or Alt+←/Alt+→ | Fold/unfold or jump between branch segments |
111
- | Shift+L | Set or clear a label on the selected entry |
112
- | Shift+T | Toggle label timestamps |
113
- | Enter | Select entry |
114
- | Escape/Ctrl+C | Cancel |
115
- | Ctrl+O | Cycle filter mode |
116
-
117
- Filter modes are: default, no-tools, user-only, labeled-only, and all. Configure the default with `treeFilterMode` in [Settings](settings.md).
118
-
119
- ### Selection Behavior
120
-
121
- Selecting a user or custom message:
122
-
123
- 1. Moves the leaf to the selected message's parent.
124
- 2. Places the selected message text in the editor.
125
- 3. Lets you edit and resubmit, creating a new branch.
126
-
127
- Selecting an assistant, tool, compaction, or other non-user entry:
128
-
129
- 1. Moves the leaf to that entry.
130
- 2. Leaves the editor empty.
131
- 3. Lets you continue from that point.
132
-
133
- Selecting the root user message resets the leaf to an empty conversation and places the original prompt in the editor.
134
-
135
- ## `/tree`, `/fork`, and `/clone`
136
-
137
- | Feature | `/tree` | `/fork` | `/clone` |
138
- |---------|---------|---------|----------|
139
- | Output | Same session file | New session file | New session file |
140
- | View | Full tree | User-message selector | Current active branch |
141
- | Typical use | Explore alternatives in place | Start a new session from an earlier prompt | Duplicate current work before continuing |
142
- | Summary | Optional branch summary | None | None |
143
-
144
- Use `/tree` when you want to keep alternatives together. Use `/fork` or `/clone` when you want a separate session file.
145
-
146
- ## Branch Summaries
147
-
148
- When `/tree` switches away from one branch to another, Diffex can summarize the abandoned branch and attach that summary at the new position. This preserves important context from the path you left without replaying the whole branch.
149
-
150
- When prompted, choose one of:
151
-
152
- 1. no summary
153
- 2. summarize with the default prompt
154
- 3. summarize with custom focus instructions
155
-
156
- See [Compaction](compaction.md) for branch summarization internals and extension hooks.
157
-
158
- ## Session Format
159
-
160
- Session files are JSONL and contain message entries, model changes, thinking-level changes, labels, compactions, branch summaries, and extension entries.
161
-
162
- For parsers, extensions, SDK usage, and the full SessionManager API, see [Session Format](session-format.md).