@bastani/atomic 0.8.27 → 0.8.28-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +2 -2
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +14 -0
- package/dist/builtin/workflows/README.md +11 -9
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/authoring.d.ts +5 -2
- package/dist/builtin/workflows/src/extension/background-ui-adapter.ts +3 -1
- package/dist/builtin/workflows/src/extension/hil-answer-notifications.ts +17 -25
- package/dist/builtin/workflows/src/extension/index.ts +133 -18
- package/dist/builtin/workflows/src/extension/render-result.ts +22 -2
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +3 -3
- package/dist/builtin/workflows/src/runs/foreground/executor.ts +210 -16
- package/dist/builtin/workflows/src/sdk-surface.ts +1 -1
- package/dist/builtin/workflows/src/shared/authoring-contract.d.ts +42 -5
- package/dist/builtin/workflows/src/shared/store-types.ts +8 -2
- package/dist/builtin/workflows/src/shared/store.ts +51 -0
- package/dist/builtin/workflows/src/shared/types.ts +14 -4
- package/dist/builtin/workflows/src/tui/graph-view.ts +4 -1
- package/dist/builtin/workflows/src/tui/prompt-card.ts +6 -0
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +11 -1
- package/dist/core/agent-session.d.ts +4 -4
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +147 -31
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-guidance.d.ts +10 -1
- package/dist/core/auth-guidance.d.ts.map +1 -1
- package/dist/core/auth-guidance.js +26 -1
- package/dist/core/auth-guidance.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +2 -2
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +7 -7
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +4 -84
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +3 -479
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/context-compaction.d.ts.map +1 -1
- package/dist/core/compaction/context-compaction.js +39 -82
- package/dist/core/compaction/context-compaction.js.map +1 -1
- package/dist/core/compaction/index.d.ts +1 -1
- package/dist/core/compaction/index.d.ts.map +1 -1
- package/dist/core/compaction/index.js +1 -1
- package/dist/core/compaction/index.js.map +1 -1
- package/dist/core/extensions/types.d.ts +10 -8
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/messages.d.ts +1 -11
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +10 -25
- package/dist/core/messages.js.map +1 -1
- package/dist/core/session-manager.d.ts +5 -8
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +12 -76
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +0 -3
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +0 -4
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.d.ts +1 -5
- package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.js +5 -9
- package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.js +0 -3
- package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +0 -1
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +0 -1
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +4 -27
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +1 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +2 -2
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +1 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +0 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/compaction.md +210 -181
- package/docs/extensions.md +31 -20
- package/docs/json.md +3 -4
- package/docs/session-format.md +12 -21
- package/docs/sessions.md +3 -1
- package/docs/settings.md +2 -5
- package/docs/workflows.md +11 -9
- package/examples/extensions/README.md +1 -1
- package/examples/extensions/custom-compaction.ts +43 -106
- package/examples/extensions/handoff.ts +6 -44
- package/examples/extensions/trigger-compact.ts +5 -4
- package/package.json +5 -5
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +0 -16
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +0 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +0 -43
- package/dist/modes/interactive/components/compaction-summary-message.js.map +0 -1
package/docs/json.md
CHANGED
|
@@ -53,10 +53,9 @@ Base messages come from `@earendil-works/pi-ai` (installed as an Atomic dependen
|
|
|
53
53
|
- `ToolResultMessage`
|
|
54
54
|
|
|
55
55
|
Extended messages from [`packages/coding-agent/src/core/messages.ts`](https://github.com/bastani-inc/atomic/blob/main/packages/coding-agent/src/core/messages.ts#L29):
|
|
56
|
-
- `BashExecutionMessage`
|
|
57
|
-
- `CustomMessage`
|
|
58
|
-
- `BranchSummaryMessage`
|
|
59
|
-
- `CompactionSummaryMessage` (line 62)
|
|
56
|
+
- `BashExecutionMessage`
|
|
57
|
+
- `CustomMessage`
|
|
58
|
+
- `BranchSummaryMessage`
|
|
60
59
|
|
|
61
60
|
## Output Format
|
|
62
61
|
|
package/docs/session-format.md
CHANGED
|
@@ -144,15 +144,10 @@ interface BranchSummaryMessage {
|
|
|
144
144
|
fromId: string; // Entry we branched from
|
|
145
145
|
timestamp: number;
|
|
146
146
|
}
|
|
147
|
-
|
|
148
|
-
interface CompactionSummaryMessage {
|
|
149
|
-
role: "compactionSummary";
|
|
150
|
-
summary: string;
|
|
151
|
-
tokensBefore: number;
|
|
152
|
-
timestamp: number;
|
|
153
|
-
}
|
|
154
147
|
```
|
|
155
148
|
|
|
149
|
+
Historical sessions may contain retired `compactionSummary` role messages from the old summary-compaction implementation. Atomic no longer produces them, they are not part of the active `AgentMessage` union, and they are not injected when active LLM context is rebuilt.
|
|
150
|
+
|
|
156
151
|
### AgentMessage Union
|
|
157
152
|
|
|
158
153
|
```typescript
|
|
@@ -162,8 +157,8 @@ type AgentMessage =
|
|
|
162
157
|
| ToolResultMessage
|
|
163
158
|
| BashExecutionMessage
|
|
164
159
|
| CustomMessage
|
|
165
|
-
| BranchSummaryMessage
|
|
166
|
-
|
|
160
|
+
| BranchSummaryMessage;
|
|
161
|
+
// CompactionSummaryMessage was removed; it is no longer part of the active union.
|
|
167
162
|
```
|
|
168
163
|
|
|
169
164
|
## Entry Base
|
|
@@ -223,14 +218,14 @@ Emitted when the user changes the thinking/reasoning level.
|
|
|
223
218
|
|
|
224
219
|
### CompactionEntry
|
|
225
220
|
|
|
226
|
-
|
|
221
|
+
Retired summary-compaction entry. Atomic no longer produces this entry type, does not treat it as an active compaction boundary, and does not inject its generated summary into active LLM context. Historical JSONL files may still contain these lines for audit/export compatibility.
|
|
227
222
|
|
|
228
223
|
```json
|
|
229
224
|
{"type":"compaction","id":"f6g7h8i9","parentId":"e5f6g7h8","timestamp":"2024-12-03T14:10:00.000Z","summary":"User discussed X, Y, Z...","firstKeptEntryId":"c3d4e5f6","tokensBefore":50000}
|
|
230
225
|
```
|
|
231
226
|
|
|
232
|
-
Optional fields:
|
|
233
|
-
- `details`:
|
|
227
|
+
Optional historical fields:
|
|
228
|
+
- `details`: Legacy implementation-specific data
|
|
234
229
|
- `fromHook`: `true` if generated by an extension, `false`/`undefined` if Atomic-generated (legacy field name)
|
|
235
230
|
|
|
236
231
|
### ContextCompactionEntry
|
|
@@ -316,14 +311,11 @@ Entries form a tree:
|
|
|
316
311
|
|
|
317
312
|
`buildSessionContext()` walks from the current leaf to the root, producing the message list for the LLM:
|
|
318
313
|
|
|
319
|
-
1. Collects all entries on the path
|
|
314
|
+
1. Collects all entries on the active branch path
|
|
320
315
|
2. Extracts current model and thinking level settings
|
|
321
|
-
3.
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
- Then messages after compaction
|
|
325
|
-
4. Applies `ContextCompactionEntry` logical deletions recorded after the latest summary compaction
|
|
326
|
-
5. Converts `BranchSummaryEntry` and `CustomMessageEntry` to appropriate message formats
|
|
316
|
+
3. Applies every `ContextCompactionEntry` logical deletion on that path, filtering targeted entries/content blocks from active context while leaving retained content unchanged
|
|
317
|
+
4. Converts `BranchSummaryEntry` and `CustomMessageEntry` to appropriate message formats
|
|
318
|
+
5. Ignores retired `CompactionEntry` lines for active LLM context; they remain archival JSONL data only
|
|
327
319
|
|
|
328
320
|
## Parsing Example
|
|
329
321
|
|
|
@@ -343,7 +335,7 @@ for (const line of lines) {
|
|
|
343
335
|
console.log(`[${entry.id}] ${entry.message.role}: ${JSON.stringify(entry.message.content)}`);
|
|
344
336
|
break;
|
|
345
337
|
case "compaction":
|
|
346
|
-
console.log(`[${entry.id}]
|
|
338
|
+
console.log(`[${entry.id}] Retired summary-compaction record: ${entry.tokensBefore} tokens summarized historically`);
|
|
347
339
|
break;
|
|
348
340
|
case "context_compaction":
|
|
349
341
|
console.log(`[${entry.id}] Context compaction: ${entry.stats.objectsDeleted} objects deleted`);
|
|
@@ -394,7 +386,6 @@ Key methods for working with sessions programmatically.
|
|
|
394
386
|
- `appendMessage(message)` - Add message
|
|
395
387
|
- `appendThinkingLevelChange(level)` - Record thinking change
|
|
396
388
|
- `appendModelChange(provider, modelId)` - Record model change
|
|
397
|
-
- `appendCompaction(summary, firstKeptEntryId, tokensBefore, details?, fromHook?)` - Add summary compaction
|
|
398
389
|
- `appendContextCompaction(deletedTargets, protectedEntryIds, stats, backupPath?)` - Add logical deletion compaction
|
|
399
390
|
- `appendCustomEntry(customType, data?)` - Extension state (not in context)
|
|
400
391
|
- `appendSessionInfo(name)` - Set session display name
|
package/docs/sessions.md
CHANGED
|
@@ -128,10 +128,12 @@ When prompted, choose one of:
|
|
|
128
128
|
2. summarize with the default prompt
|
|
129
129
|
3. summarize with custom focus instructions
|
|
130
130
|
|
|
131
|
+
Branch summaries are separate from `/compact`: branch navigation can generate summary prose (optionally with focus instructions), while Verbatim Compaction records validated deletion targets and does not accept summary instructions.
|
|
132
|
+
|
|
131
133
|
See [Compaction](/compaction) for Verbatim Compaction, branch summarization internals, and extension hooks.
|
|
132
134
|
|
|
133
135
|
## Session Format
|
|
134
136
|
|
|
135
|
-
Session files are JSONL and contain message entries, model changes, thinking-level changes, labels,
|
|
137
|
+
Session files are JSONL and contain message entries, model changes, thinking-level changes, labels, context compactions, branch summaries, extension entries, and retired legacy `type:"compaction"` records from older sessions.
|
|
136
138
|
|
|
137
139
|
For parsers, extensions, SDK usage, and the full SessionManager API, see [Session Format](/session-format).
|
package/docs/settings.md
CHANGED
|
@@ -92,14 +92,12 @@ Set `ATOMIC_SKIP_VERSION_CHECK=1` to disable the Atomic version update check. Us
|
|
|
92
92
|
|---------|------|---------|-------------|
|
|
93
93
|
| `compaction.enabled` | boolean | `true` | Enable automatic Verbatim Compaction |
|
|
94
94
|
| `compaction.reserveTokens` | number | `16384` | Tokens reserved for LLM response |
|
|
95
|
-
| `compaction.keepRecentTokens` | number | `20000` | Legacy summary-compaction retained-token budget; default Verbatim Compaction protects recent entries structurally |
|
|
96
95
|
|
|
97
96
|
```json
|
|
98
97
|
{
|
|
99
98
|
"compaction": {
|
|
100
99
|
"enabled": true,
|
|
101
|
-
"reserveTokens": 16384
|
|
102
|
-
"keepRecentTokens": 20000
|
|
100
|
+
"reserveTokens": 16384
|
|
103
101
|
}
|
|
104
102
|
}
|
|
105
103
|
```
|
|
@@ -285,8 +283,7 @@ See [Atomic packages](/packages) for package management details.
|
|
|
285
283
|
"theme": "dark",
|
|
286
284
|
"compaction": {
|
|
287
285
|
"enabled": true,
|
|
288
|
-
"reserveTokens": 16384
|
|
289
|
-
"keepRecentTokens": 20000
|
|
286
|
+
"reserveTokens": 16384
|
|
290
287
|
},
|
|
291
288
|
"retry": {
|
|
292
289
|
"enabled": true,
|
package/docs/workflows.md
CHANGED
|
@@ -10,7 +10,7 @@ Use a workflow when a task should be repeatable, inspectable, resumable, or spli
|
|
|
10
10
|
- **Tracked stages** - Name each step and inspect it in workflow status and graph views
|
|
11
11
|
- **Parallel branches** - Run independent research, review, or implementation branches concurrently
|
|
12
12
|
- **Context handoffs** - Pass summaries, artifacts, files, and structured outputs between stages
|
|
13
|
-
- **Human input** - Pause for `ctx.ui.input`, `confirm`, `select`,
|
|
13
|
+
- **Human input** - Pause for `ctx.ui.input`, `confirm`, `select`, `editor`, or custom TUI widget decisions during a run
|
|
14
14
|
- **Resumable control** - Interrupt, pause, resume, attach to, or kill workflow runs
|
|
15
15
|
- **Artifacts** - Save large outputs to files instead of pushing everything through model context
|
|
16
16
|
- **Model fallback chains** - Retry important stages on fallback models when providers fail
|
|
@@ -357,9 +357,11 @@ Named runs go to the background. Common controls:
|
|
|
357
357
|
/workflow kill <run-id> # abort and retain for inspection
|
|
358
358
|
```
|
|
359
359
|
|
|
360
|
-
Human-in-the-loop prompts from `ctx.ui.input`, `ctx.ui.confirm`, `ctx.ui.select`, and `ctx.ui.
|
|
360
|
+
Human-in-the-loop prompts from `ctx.ui.input`, `ctx.ui.confirm`, `ctx.ui.select`, `ctx.ui.editor`, and `ctx.ui.custom<T>` appear as awaiting-input nodes in the workflow graph viewer, not as chat modals — use `/workflow connect <run-id>` (or F2), focus the node, and press Enter to answer them locally.
|
|
361
361
|
|
|
362
|
-
|
|
362
|
+
`ctx.ui.custom<T>(factory, options?)` reuses Atomic's TUI component path: the factory receives the same real `(tui, theme, keybindings, done)` types as extension `ctx.ui.custom`, and the workflow resumes with the value passed to `done(value)`. Use `options.label` for a safe display-only graph/status label and `options.replayIdentity` when widget semantics can change without the callsite changing. Do not put secrets in labels or replay identities; only a hash of the identity is stored, and label text is not part of replay identity. Inline connected rendering is supported; `overlay: true` is rejected clearly because nested workflow graph overlays are not safely supported yet.
|
|
363
|
+
|
|
364
|
+
Prompt answers are replayable only while the source run remains in the live in-memory store. `StageSnapshot.promptAnswerState` is snapshot-safe metadata for continuation: `available` means a matching live answer can be replayed, `unavailable` means the matching prompt node exists but its private answer was purged, and `ambiguous` means multiple matching prompt nodes exist so Atomic asks again. The raw answer lives in a private `PromptAnswerRecord` ledger, is never written to snapshots or persistence, and remains resident in memory until the answer is cleared, the run is removed, or the store is cleared. Prompt replay keys include the prompt kind, message text, select choices, input/editor initial value, custom prompt identity hash, and hashed author callsite, so changing any of those inputs may intentionally re-ask on continuation. An empty `ctx.ui.select(..., [])` has no answerable choices and throws before creating a prompt node. Arbitrary custom-widget answers cannot be supplied through `workflow send`; focus the `custom` awaiting-input node in the interactive graph instead.
|
|
363
365
|
|
|
364
366
|
## When to Use Workflows
|
|
365
367
|
|
|
@@ -763,7 +765,7 @@ Input overrides are bare `key=value` tokens. Values are JSON-parsed when possibl
|
|
|
763
765
|
|
|
764
766
|
In the TUI, `/workflow <name>` opens an input picker when the workflow declares inputs and either no arguments were supplied or required inputs are missing. Supplied values seed the picker. Pass `--no-picker` to skip that interactive flow.
|
|
765
767
|
|
|
766
|
-
In non-interactive (`-p`, `--print`, or `--mode json`) sessions, named workflow dispatch waits for the terminal run snapshot and skips pickers. Because human input is runtime-only and workflows no longer carry a declaration-time HIL marker, headless dispatch does not reject a workflow just because its source contains `ctx.ui.*`. If you copy a HIL workflow example into a headless session, it can pass dispatch and then fail when execution reaches the prompt with an error such as `atomic-workflows: HIL ctx.ui.confirm is unavailable because Atomic runtime did not provide a UI adapter` (the primitive name varies). Run those workflows interactively, or guard/remove runtime `ctx.ui.*` calls before using headless mode.
|
|
768
|
+
In non-interactive (`-p`, `--print`, or `--mode json`) sessions, named workflow dispatch waits for the terminal run snapshot and skips pickers. Because human input is runtime-only and workflows no longer carry a declaration-time HIL marker, headless dispatch does not reject a workflow just because its source contains `ctx.ui.*`. If you copy a HIL workflow example into a headless session, it can pass dispatch and then fail when execution reaches the prompt with an error such as `atomic-workflows: HIL ctx.ui.confirm is unavailable because Atomic runtime did not provide a UI adapter` (the primitive name varies, including `ctx.ui.custom`). Run those workflows interactively, or guard/remove runtime `ctx.ui.*` calls before using headless mode.
|
|
767
769
|
|
|
768
770
|
<p align="center"><img src="images/workflow-input-picker.png" alt="Workflow Input Picker" width="600" /></p>
|
|
769
771
|
|
|
@@ -789,7 +791,7 @@ Use `connect` for the workflow graph. Use `attach` when you want a chat pane for
|
|
|
789
791
|
|
|
790
792
|
<p align="center"><img src="images/workflow-graph.png" alt="Workflow Graph Viewer" width="600" /></p>
|
|
791
793
|
|
|
792
|
-
Human-in-the-loop prompts from `ctx.ui.input`, `ctx.ui.confirm`, `ctx.ui.select`, and `ctx.ui.
|
|
794
|
+
Human-in-the-loop prompts from `ctx.ui.input`, `ctx.ui.confirm`, `ctx.ui.select`, `ctx.ui.editor`, and `ctx.ui.custom<T>` appear as awaiting-input nodes in the workflow UI/graph viewer, not as ordinary chat modals. Workflows do not declare HIL up front; prompt nodes are created when the runtime `ctx.ui.*` call executes. If the prompt lives inside an imported child workflow, it still appears in the same expanded parent graph so the user can focus and answer it without switching to a separate child status entry. Custom widget prompts mount inside the attached stage chat and must be completed interactively with the widget's `done(value)` callback.
|
|
793
795
|
|
|
794
796
|
## Monitor and Control Runs
|
|
795
797
|
|
|
@@ -832,7 +834,7 @@ Control behavior:
|
|
|
832
834
|
- `stages` lists stage summaries, including flattened stages from nested `ctx.workflow(...)` imports and `sessionFile`/`transcriptPath` when a stage has a persisted session. Use `statusFilter: "all"` to include completed, failed, skipped, and pending stages.
|
|
833
835
|
- `stage` returns details for one stage by stage id, unique prefix, or stage name, including nested child stages shown in the expanded graph and the persisted `sessionFile` when available.
|
|
834
836
|
- `transcript` is reference-first with a small preview by default: it returns metadata, transcript paths, and up to 5 recent entries. For targeted lookup, quote the exact `sessionFile`/`transcriptPath` value without changing platform separators (preserve Windows backslashes), search it with `rg` or `grep`, then read only small surrounding ranges. Text results include JSON-escaped `sessionFileJson`/`transcriptPathJson` lines for copy-safe path literals. Pass explicit `tail` or `limit` to override the 5-entry preview; `tail` overrides `limit`; `includeToolOutput` includes captured snapshot tool output in snapshot transcript results.
|
|
835
|
-
- `send` delivery modes are `auto`, `answer`, `prompt`, `steer`, `followUp`, and `resume`. Prompt answers can include `promptId` and can carry answer content in `response`, `text`, or `message`; structured UI prompts usually prefer `response`.
|
|
837
|
+
- `send` delivery modes are `auto`, `answer`, `prompt`, `steer`, `followUp`, and `resume`. Prompt answers can include `promptId` and can carry answer content in `response`, `text`, or `message`; structured UI prompts usually prefer `response`. Arbitrary `ctx.ui.custom<T>` widget prompts require the interactive workflow graph and return a clear unsupported message when targeted through `send`.
|
|
836
838
|
- `delivery: "auto"` first answers a pending prompt, then resumes paused work, then steers a streaming stage, then queues a follow-up.
|
|
837
839
|
- `pause`, `interrupt`, and `kill` can target one top-level run or `all: true`; `stageId` cannot be combined with `all: true`. Stage-scoped controls can target a visible nested child stage from the expanded graph; Atomic routes the operation to the owning nested run internally.
|
|
838
840
|
- `interrupt` is resumable: it pauses live work when pausable stages exist and keeps the run in live history/status.
|
|
@@ -847,7 +849,7 @@ Use slash commands for graph connect and stage attach because those are interact
|
|
|
847
849
|
|
|
848
850
|
Atomic emits deduplicated main-chat notices when top-level workflow runs complete or fail. Nested child workflow completion/failure is reflected inside the expanded parent graph instead of producing separate top-level completion cards. These terminal notices are queued into the active main chat as steering/context messages (`triggerTurn: true`, `deliverAs: "steer"`) so the model can react without the user manually polling status. Awaiting-input workflow states are tracked for dedupe/restore, but they do not enqueue main-chat connect cards or wake the model; prompt state remains visible through workflow status/connect surfaces. Configure lifecycle behavior with `workflowNotifications.enabled` (default `true`) and `workflowNotifications.notifyOn` (default `["completed", "failed", "awaiting_input"]`).
|
|
849
851
|
|
|
850
|
-
Human input is runtime-only: call `ctx.ui.input`, `ctx.ui.confirm`, `ctx.ui.select`, or `ctx.ui.
|
|
852
|
+
Human input is runtime-only: call `ctx.ui.input`, `ctx.ui.confirm`, `ctx.ui.select`, `ctx.ui.editor`, or `ctx.ui.custom<T>` at the point where the workflow actually needs a decision. No builder-level declaration is required or supported.
|
|
851
853
|
|
|
852
854
|
When a workflow needs human input, answer in the graph viewer or attached stage chat when possible:
|
|
853
855
|
|
|
@@ -856,7 +858,7 @@ When a workflow needs human input, answer in the graph viewer or attached stage
|
|
|
856
858
|
/workflow attach <run-id> <stage-id-or-name>
|
|
857
859
|
```
|
|
858
860
|
|
|
859
|
-
Agents can answer pending prompts programmatically with `workflow({ action: "send", delivery: "answer", ... })`; use `promptId` when it is present in the stage details, and provide answer content with `response`, `text`, or `message`.
|
|
861
|
+
Agents can answer primitive and structured pending prompts programmatically with `workflow({ action: "send", delivery: "answer", ... })`; use `promptId` when it is present in the stage details, and provide answer content with `response`, `text`, or `message`. Arbitrary custom TUI widget prompts intentionally refuse this path in iteration 1 because a generic `T` cannot be reconstructed safely from a non-TUI payload.
|
|
860
862
|
|
|
861
863
|
If the user answers a human-in-the-loop prompt in the workflow UI or stage UI broker, the stage receives the answer directly and the active main chat receives a display-only notice (`triggerTurn: false`, `excludeFromContext: true`) containing a concise answer summary. The notice is rendered for the user and persisted for audit, but it does not wake the model, enter LLM context, or authorize answering any other workflow prompt. Prompt answers sent by the main-chat `workflow` tool are suppressed from this notice because the tool result already informs the current turn.
|
|
862
864
|
|
|
@@ -1347,7 +1349,7 @@ Prefer high-level primitives because they create tracked graph nodes, provide co
|
|
|
1347
1349
|
| Dependent sequential tasks | `ctx.chain(steps, options?)` |
|
|
1348
1350
|
| Independent concurrent branches | `ctx.parallel(steps, options?)` |
|
|
1349
1351
|
| Reusable child workflow | Call `ctx.workflow(workflowDefinition, options?)` |
|
|
1350
|
-
| Human input during a workflow run | `ctx.ui.input/confirm/select/editor` |
|
|
1352
|
+
| Human input during a workflow run | `ctx.ui.input/confirm/select/editor/custom` |
|
|
1351
1353
|
| Pure deterministic computation, parsing, or file I/O | Plain TypeScript in `.run()` or helpers |
|
|
1352
1354
|
| Fine-grained session control | `ctx.stage(name, options?)` |
|
|
1353
1355
|
|
|
@@ -89,7 +89,7 @@ cp permission-gate.ts ~/.pi/agent/extensions/
|
|
|
89
89
|
|-----------|-------------|
|
|
90
90
|
| `pirate.ts` | Demonstrates `systemPromptAppend` to dynamically modify system prompt |
|
|
91
91
|
| `claude-rules.ts` | Scans `.claude/rules/` folder and lists rules in system prompt |
|
|
92
|
-
| `custom-compaction.ts` | Custom compaction that
|
|
92
|
+
| `custom-compaction.ts` | Custom compaction policy that provides exact deletion targets |
|
|
93
93
|
| `trigger-compact.ts` | Triggers compaction when context usage exceeds 100k tokens and adds `/trigger-compact` command |
|
|
94
94
|
|
|
95
95
|
### System Integration
|
|
@@ -1,127 +1,64 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Custom Compaction Extension
|
|
2
|
+
* Custom Compaction Deletion Policy Extension
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Verbatim Compaction is deletion-only: extensions cannot replace history with a
|
|
5
|
+
* generated summary. This example shows how to provide an exact deletion request
|
|
6
|
+
* before Atomic's internal planner runs. Atomic still validates the requested
|
|
7
|
+
* entry IDs locally before appending a context_compaction entry.
|
|
8
8
|
*
|
|
9
|
-
* This
|
|
10
|
-
*
|
|
9
|
+
* This policy deletes older, unprotected, successful bash execution entries once
|
|
10
|
+
* they are large enough to matter. If no safe candidates are present, it returns
|
|
11
|
+
* nothing and Atomic uses its default deletion planner.
|
|
11
12
|
*
|
|
12
13
|
* Usage:
|
|
13
|
-
*
|
|
14
|
+
* atomic --extension examples/extensions/custom-compaction.ts
|
|
14
15
|
*/
|
|
15
16
|
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
import type { ContextDeletionRequest, ExtensionAPI } from "@bastani/atomic";
|
|
18
|
+
|
|
19
|
+
const MIN_BASH_OUTPUT_TOKENS = 250;
|
|
20
|
+
const MAX_DELETIONS_PER_RUN = 12;
|
|
19
21
|
|
|
20
22
|
export default function (pi: ExtensionAPI) {
|
|
21
23
|
pi.on("session_before_compact", async (event, ctx) => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (
|
|
30
|
-
ctx.
|
|
24
|
+
const { preparation, reason, mode } = event;
|
|
25
|
+
const candidates = preparation.transcript.entries
|
|
26
|
+
.filter((entry) => !entry.protected)
|
|
27
|
+
.filter((entry) => entry.message.role === "bashExecution" && entry.message.exitCode === 0)
|
|
28
|
+
.filter((entry) => entry.tokenEstimate >= MIN_BASH_OUTPUT_TOKENS)
|
|
29
|
+
.slice(0, MAX_DELETIONS_PER_RUN);
|
|
30
|
+
|
|
31
|
+
if (candidates.length === 0) {
|
|
32
|
+
if (ctx.hasUI) {
|
|
33
|
+
ctx.ui.notify("Custom compaction policy found no safe bash output to delete; using default planner", "info");
|
|
34
|
+
}
|
|
31
35
|
return;
|
|
32
36
|
}
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
const deletions: ContextDeletionRequest["deletions"] = candidates.map((entry) => ({
|
|
39
|
+
kind: "entry",
|
|
40
|
+
entryId: entry.entryId,
|
|
41
|
+
rationale: "Large successful bash output selected by custom compaction policy",
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
if (ctx.hasUI) {
|
|
45
|
+
const tokenEstimate = candidates.reduce((sum, entry) => sum + entry.tokenEstimate, 0);
|
|
46
|
+
ctx.ui.notify(
|
|
47
|
+
`Custom compaction (${reason ?? "manual"}/${mode}): requesting ${deletions.length} deletion(s), about ${tokenEstimate.toLocaleString()} tokens`,
|
|
48
|
+
"info",
|
|
49
|
+
);
|
|
43
50
|
}
|
|
44
51
|
|
|
45
|
-
|
|
46
|
-
|
|
52
|
+
return {
|
|
53
|
+
deletionRequest: { deletions },
|
|
54
|
+
};
|
|
55
|
+
});
|
|
47
56
|
|
|
57
|
+
pi.on("session_compact", async (event, ctx) => {
|
|
58
|
+
if (!ctx.hasUI || !event.fromExtension) return;
|
|
48
59
|
ctx.ui.notify(
|
|
49
|
-
`Custom compaction
|
|
60
|
+
`Custom compaction policy deleted ${event.result.stats.objectsDeleted} object(s)`,
|
|
50
61
|
"info",
|
|
51
62
|
);
|
|
52
|
-
|
|
53
|
-
// Convert messages to readable text format
|
|
54
|
-
const conversationText = serializeConversation(convertToLlm(allMessages));
|
|
55
|
-
|
|
56
|
-
// Include previous summary context if available
|
|
57
|
-
const previousContext = previousSummary ? `\n\nPrevious session summary for context:\n${previousSummary}` : "";
|
|
58
|
-
|
|
59
|
-
// Build messages that ask for a comprehensive summary
|
|
60
|
-
const summaryMessages = [
|
|
61
|
-
{
|
|
62
|
-
role: "user" as const,
|
|
63
|
-
content: [
|
|
64
|
-
{
|
|
65
|
-
type: "text" as const,
|
|
66
|
-
text: `You are a conversation summarizer. Create a comprehensive summary of this conversation that captures:${previousContext}
|
|
67
|
-
|
|
68
|
-
1. The main goals and objectives discussed
|
|
69
|
-
2. Key decisions made and their rationale
|
|
70
|
-
3. Important code changes, file modifications, or technical details
|
|
71
|
-
4. Current state of any ongoing work
|
|
72
|
-
5. Any blockers, issues, or open questions
|
|
73
|
-
6. Next steps that were planned or suggested
|
|
74
|
-
|
|
75
|
-
Be thorough but concise. The summary will replace the ENTIRE conversation history, so include all information needed to continue the work effectively.
|
|
76
|
-
|
|
77
|
-
Format the summary as structured markdown with clear sections.
|
|
78
|
-
|
|
79
|
-
<conversation>
|
|
80
|
-
${conversationText}
|
|
81
|
-
</conversation>`,
|
|
82
|
-
},
|
|
83
|
-
],
|
|
84
|
-
timestamp: Date.now(),
|
|
85
|
-
},
|
|
86
|
-
];
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
// Pass signal to honor abort requests (e.g., user cancels compaction)
|
|
90
|
-
const response = await complete(
|
|
91
|
-
model,
|
|
92
|
-
{ messages: summaryMessages },
|
|
93
|
-
{
|
|
94
|
-
apiKey: auth.apiKey,
|
|
95
|
-
headers: auth.headers,
|
|
96
|
-
maxTokens: 8192,
|
|
97
|
-
signal,
|
|
98
|
-
},
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
const summary = response.content
|
|
102
|
-
.filter((c): c is { type: "text"; text: string } => c.type === "text")
|
|
103
|
-
.map((c) => c.text)
|
|
104
|
-
.join("\n");
|
|
105
|
-
|
|
106
|
-
if (!summary.trim()) {
|
|
107
|
-
if (!signal.aborted) ctx.ui.notify("Compaction summary was empty, using default compaction", "warning");
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Return compaction content - SessionManager adds id/parentId
|
|
112
|
-
// Use firstKeptEntryId from preparation to keep recent messages
|
|
113
|
-
return {
|
|
114
|
-
compaction: {
|
|
115
|
-
summary,
|
|
116
|
-
firstKeptEntryId,
|
|
117
|
-
tokensBefore,
|
|
118
|
-
},
|
|
119
|
-
};
|
|
120
|
-
} catch (error) {
|
|
121
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
122
|
-
ctx.ui.notify(`Compaction failed: ${message}`, "error");
|
|
123
|
-
// Fall back to default compaction on error
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
63
|
});
|
|
127
64
|
}
|
|
@@ -12,10 +12,9 @@
|
|
|
12
12
|
* The generated prompt appears as a draft in the editor for review/editing.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
16
15
|
import { complete, type Message } from "@earendil-works/pi-ai";
|
|
17
|
-
import type { ExtensionAPI
|
|
18
|
-
import { BorderedLoader, convertToLlm, serializeConversation } from "@bastani/atomic";
|
|
16
|
+
import type { ExtensionAPI } from "@bastani/atomic";
|
|
17
|
+
import { BorderedLoader, buildSessionContext, convertToLlm, serializeConversation } from "@bastani/atomic";
|
|
19
18
|
|
|
20
19
|
const SYSTEM_PROMPT = `You are a context transfer assistant. Given a conversation history and the user's goal for a new thread, generate a focused prompt that:
|
|
21
20
|
|
|
@@ -39,44 +38,6 @@ Files involved:
|
|
|
39
38
|
## Task
|
|
40
39
|
[Clear description of what to do next based on user's goal]`;
|
|
41
40
|
|
|
42
|
-
function entryToMessage(entry: SessionEntry): AgentMessage | undefined {
|
|
43
|
-
if (entry.type === "message") {
|
|
44
|
-
return entry.message;
|
|
45
|
-
}
|
|
46
|
-
if (entry.type === "compaction") {
|
|
47
|
-
return {
|
|
48
|
-
role: "compactionSummary",
|
|
49
|
-
summary: entry.summary,
|
|
50
|
-
tokensBefore: entry.tokensBefore,
|
|
51
|
-
timestamp: new Date(entry.timestamp).getTime(),
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function getHandoffMessages(branch: SessionEntry[]): AgentMessage[] {
|
|
58
|
-
let compactionIndex = -1;
|
|
59
|
-
for (let i = branch.length - 1; i >= 0; i--) {
|
|
60
|
-
if (branch[i].type === "compaction") {
|
|
61
|
-
compactionIndex = i;
|
|
62
|
-
break;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
if (compactionIndex < 0) {
|
|
66
|
-
return branch.map(entryToMessage).filter((message) => message !== undefined);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const compaction = branch[compactionIndex];
|
|
70
|
-
const firstKeptIndex =
|
|
71
|
-
compaction.type === "compaction" ? branch.findIndex((entry) => entry.id === compaction.firstKeptEntryId) : -1;
|
|
72
|
-
const compactedBranch = [
|
|
73
|
-
compaction,
|
|
74
|
-
...(firstKeptIndex >= 0 ? branch.slice(firstKeptIndex, compactionIndex) : []),
|
|
75
|
-
...branch.slice(compactionIndex + 1),
|
|
76
|
-
];
|
|
77
|
-
return compactedBranch.map(entryToMessage).filter((message) => message !== undefined);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
41
|
export default function (pi: ExtensionAPI) {
|
|
81
42
|
pi.registerCommand("handoff", {
|
|
82
43
|
description: "Transfer context to a new focused session",
|
|
@@ -97,9 +58,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
97
58
|
return;
|
|
98
59
|
}
|
|
99
60
|
|
|
100
|
-
// Gather
|
|
101
|
-
//
|
|
102
|
-
|
|
61
|
+
// Gather the same active context Atomic would send to the model. This applies
|
|
62
|
+
// context_compaction deletion filters and treats retired summary-compaction
|
|
63
|
+
// records as archival metadata, not replacement conversation context.
|
|
64
|
+
const messages = buildSessionContext(ctx.sessionManager.getEntries(), ctx.sessionManager.getLeafId()).messages;
|
|
103
65
|
|
|
104
66
|
if (messages.length === 0) {
|
|
105
67
|
ctx.ui.notify("No conversation to hand off", "error");
|
|
@@ -5,12 +5,11 @@ const COMPACT_THRESHOLD_TOKENS = 100_000;
|
|
|
5
5
|
export default function (pi: ExtensionAPI) {
|
|
6
6
|
let previousTokens: number | null | undefined;
|
|
7
7
|
|
|
8
|
-
const triggerCompaction = (ctx: ExtensionContext
|
|
8
|
+
const triggerCompaction = (ctx: ExtensionContext) => {
|
|
9
9
|
if (ctx.hasUI) {
|
|
10
10
|
ctx.ui.notify("Compaction started", "info");
|
|
11
11
|
}
|
|
12
12
|
ctx.compact({
|
|
13
|
-
customInstructions,
|
|
14
13
|
onComplete: () => {
|
|
15
14
|
if (ctx.hasUI) {
|
|
16
15
|
ctx.ui.notify("Compaction completed", "info");
|
|
@@ -43,8 +42,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
43
42
|
pi.registerCommand("trigger-compact", {
|
|
44
43
|
description: "Trigger compaction immediately",
|
|
45
44
|
handler: async (args, ctx) => {
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
if (args.trim() && ctx.hasUI) {
|
|
46
|
+
ctx.ui.notify("/trigger-compact ignores arguments; Verbatim Compaction uses a fixed deletion planner", "warning");
|
|
47
|
+
}
|
|
48
|
+
triggerCompaction(ctx);
|
|
48
49
|
},
|
|
49
50
|
});
|
|
50
51
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/atomic",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.28-alpha.1",
|
|
4
4
|
"description": "Atomic coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"atomicConfig": {
|
|
@@ -76,15 +76,15 @@
|
|
|
76
76
|
"@silvia-odwyer/photon-node": "^0.3.4",
|
|
77
77
|
"chalk": "^5.5.0",
|
|
78
78
|
"cross-spawn": "7.0.6",
|
|
79
|
-
"diff": "^
|
|
79
|
+
"diff": "^9.0.0",
|
|
80
80
|
"glob": "^13.0.1",
|
|
81
81
|
"highlight.js": "^11.11.1",
|
|
82
|
-
"hosted-git-info": "^
|
|
82
|
+
"hosted-git-info": "^10.1.1",
|
|
83
83
|
"ignore": "^7.0.5",
|
|
84
84
|
"jiti": "^2.7.0",
|
|
85
85
|
"linkedom": "^0.18.12",
|
|
86
86
|
"minimatch": "^10.2.3",
|
|
87
|
-
"open": "^
|
|
87
|
+
"open": "^11.0.0",
|
|
88
88
|
"p-limit": "^6.1.0",
|
|
89
89
|
"proper-lockfile": "^4.1.2",
|
|
90
90
|
"turndown": "^7.2.0",
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
"@types/diff": "^7.0.2",
|
|
109
109
|
"@types/hosted-git-info": "^3.0.5",
|
|
110
110
|
"@types/ms": "^2.1.0",
|
|
111
|
-
"@types/node": "^
|
|
111
|
+
"@types/node": "^25.9.1",
|
|
112
112
|
"@types/proper-lockfile": "^4.1.4",
|
|
113
113
|
"@typescript/native-preview": "7.0.0-dev.20260511.1",
|
|
114
114
|
"shx": "^0.4.0",
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Box, type MarkdownTheme } from "@earendil-works/pi-tui";
|
|
2
|
-
import type { CompactionSummaryMessage } from "../../../core/messages.ts";
|
|
3
|
-
/**
|
|
4
|
-
* Component that renders a compaction message with collapsed/expanded state.
|
|
5
|
-
* Uses same background color as custom messages for visual consistency.
|
|
6
|
-
*/
|
|
7
|
-
export declare class CompactionSummaryMessageComponent extends Box {
|
|
8
|
-
private expanded;
|
|
9
|
-
private message;
|
|
10
|
-
private markdownTheme;
|
|
11
|
-
constructor(message: CompactionSummaryMessage, markdownTheme?: MarkdownTheme);
|
|
12
|
-
setExpanded(expanded: boolean): void;
|
|
13
|
-
invalidate(): void;
|
|
14
|
-
private updateDisplay;
|
|
15
|
-
}
|
|
16
|
-
//# sourceMappingURL=compaction-summary-message.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"compaction-summary-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/compaction-summary-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAY,KAAK,aAAa,EAAgB,MAAM,wBAAwB,CAAC;AACzF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAI1E;;;GAGG;AACH,qBAAa,iCAAkC,SAAQ,GAAG;IACzD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,aAAa,CAAgB;IAErC,YAAY,OAAO,EAAE,wBAAwB,EAAE,aAAa,GAAE,aAAkC,EAK/F;IAED,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAGnC;IAEQ,UAAU,IAAI,IAAI,CAG1B;IAED,OAAO,CAAC,aAAa;CA2BrB","sourcesContent":["import { Box, Markdown, type MarkdownTheme, Spacer, Text } from \"@earendil-works/pi-tui\";\nimport type { CompactionSummaryMessage } from \"../../../core/messages.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\n\n/**\n * Component that renders a compaction message with collapsed/expanded state.\n * Uses same background color as custom messages for visual consistency.\n */\nexport class CompactionSummaryMessageComponent extends Box {\n\tprivate expanded = false;\n\tprivate message: CompactionSummaryMessage;\n\tprivate markdownTheme: MarkdownTheme;\n\n\tconstructor(message: CompactionSummaryMessage, markdownTheme: MarkdownTheme = getMarkdownTheme()) {\n\t\tsuper(1, 1, (t) => theme.bg(\"customMessageBg\", t));\n\t\tthis.message = message;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate updateDisplay(): void {\n\t\tthis.clear();\n\n\t\tconst tokenStr = this.message.tokensBefore.toLocaleString();\n\t\tconst label = theme.fg(\"customMessageLabel\", `\\x1b[1m[compaction]\\x1b[22m`);\n\t\tthis.addChild(new Text(label, 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (this.expanded) {\n\t\t\tconst header = `**Compacted from ${tokenStr} tokens**\\n\\n`;\n\t\t\tthis.addChild(\n\t\t\t\tnew Markdown(header + this.message.summary, 0, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\"customMessageText\", `Compacted from ${tokenStr} tokens (`) +\n\t\t\t\t\t\ttheme.fg(\"dim\", keyText(\"app.tools.expand\")) +\n\t\t\t\t\t\ttheme.fg(\"customMessageText\", \" Expand)\"),\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { Box, Markdown, Spacer, Text } from "@earendil-works/pi-tui";
|
|
2
|
-
import { getMarkdownTheme, theme } from "../theme/theme.js";
|
|
3
|
-
import { keyText } from "./keybinding-hints.js";
|
|
4
|
-
/**
|
|
5
|
-
* Component that renders a compaction message with collapsed/expanded state.
|
|
6
|
-
* Uses same background color as custom messages for visual consistency.
|
|
7
|
-
*/
|
|
8
|
-
export class CompactionSummaryMessageComponent extends Box {
|
|
9
|
-
constructor(message, markdownTheme = getMarkdownTheme()) {
|
|
10
|
-
super(1, 1, (t) => theme.bg("customMessageBg", t));
|
|
11
|
-
this.expanded = false;
|
|
12
|
-
this.message = message;
|
|
13
|
-
this.markdownTheme = markdownTheme;
|
|
14
|
-
this.updateDisplay();
|
|
15
|
-
}
|
|
16
|
-
setExpanded(expanded) {
|
|
17
|
-
this.expanded = expanded;
|
|
18
|
-
this.updateDisplay();
|
|
19
|
-
}
|
|
20
|
-
invalidate() {
|
|
21
|
-
super.invalidate();
|
|
22
|
-
this.updateDisplay();
|
|
23
|
-
}
|
|
24
|
-
updateDisplay() {
|
|
25
|
-
this.clear();
|
|
26
|
-
const tokenStr = this.message.tokensBefore.toLocaleString();
|
|
27
|
-
const label = theme.fg("customMessageLabel", `\x1b[1m[compaction]\x1b[22m`);
|
|
28
|
-
this.addChild(new Text(label, 0, 0));
|
|
29
|
-
this.addChild(new Spacer(1));
|
|
30
|
-
if (this.expanded) {
|
|
31
|
-
const header = `**Compacted from ${tokenStr} tokens**\n\n`;
|
|
32
|
-
this.addChild(new Markdown(header + this.message.summary, 0, 0, this.markdownTheme, {
|
|
33
|
-
color: (text) => theme.fg("customMessageText", text),
|
|
34
|
-
}));
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
this.addChild(new Text(theme.fg("customMessageText", `Compacted from ${tokenStr} tokens (`) +
|
|
38
|
-
theme.fg("dim", keyText("app.tools.expand")) +
|
|
39
|
-
theme.fg("customMessageText", " Expand)"), 0, 0));
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
//# sourceMappingURL=compaction-summary-message.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"compaction-summary-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/compaction-summary-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAsB,MAAM,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAEzF,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD;;;GAGG;AACH,MAAM,OAAO,iCAAkC,SAAQ,GAAG;IAKzD,YAAY,OAAiC,EAAE,aAAa,GAAkB,gBAAgB,EAAE;QAC/F,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,CAAC;QAL5C,aAAQ,GAAG,KAAK,CAAC;QAMxB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,aAAa,EAAE,CAAC;IACtB,CAAC;IAED,WAAW,CAAC,QAAiB;QAC5B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,aAAa,EAAE,CAAC;IACtB,CAAC;IAEQ,UAAU;QAClB,KAAK,CAAC,UAAU,EAAE,CAAC;QACnB,IAAI,CAAC,aAAa,EAAE,CAAC;IACtB,CAAC;IAEO,aAAa;QACpB,IAAI,CAAC,KAAK,EAAE,CAAC;QAEb,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,cAAc,EAAE,CAAC;QAC5D,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,oBAAoB,EAAE,6BAA6B,CAAC,CAAC;QAC5E,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7B,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,MAAM,MAAM,GAAG,oBAAoB,QAAQ,eAAe,CAAC;YAC3D,IAAI,CAAC,QAAQ,CACZ,IAAI,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE;gBACrE,KAAK,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,mBAAmB,EAAE,IAAI,CAAC;aAC5D,CAAC,CACF,CAAC;QACH,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,QAAQ,CACZ,IAAI,IAAI,CACP,KAAK,CAAC,EAAE,CAAC,mBAAmB,EAAE,kBAAkB,QAAQ,WAAW,CAAC;gBACnE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;gBAC5C,KAAK,CAAC,EAAE,CAAC,mBAAmB,EAAE,UAAU,CAAC,EAC1C,CAAC,EACD,CAAC,CACD,CACD,CAAC;QACH,CAAC;IACF,CAAC;CACD","sourcesContent":["import { Box, Markdown, type MarkdownTheme, Spacer, Text } from \"@earendil-works/pi-tui\";\nimport type { CompactionSummaryMessage } from \"../../../core/messages.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\n\n/**\n * Component that renders a compaction message with collapsed/expanded state.\n * Uses same background color as custom messages for visual consistency.\n */\nexport class CompactionSummaryMessageComponent extends Box {\n\tprivate expanded = false;\n\tprivate message: CompactionSummaryMessage;\n\tprivate markdownTheme: MarkdownTheme;\n\n\tconstructor(message: CompactionSummaryMessage, markdownTheme: MarkdownTheme = getMarkdownTheme()) {\n\t\tsuper(1, 1, (t) => theme.bg(\"customMessageBg\", t));\n\t\tthis.message = message;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate updateDisplay(): void {\n\t\tthis.clear();\n\n\t\tconst tokenStr = this.message.tokensBefore.toLocaleString();\n\t\tconst label = theme.fg(\"customMessageLabel\", `\\x1b[1m[compaction]\\x1b[22m`);\n\t\tthis.addChild(new Text(label, 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (this.expanded) {\n\t\t\tconst header = `**Compacted from ${tokenStr} tokens**\\n\\n`;\n\t\t\tthis.addChild(\n\t\t\t\tnew Markdown(header + this.message.summary, 0, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\"customMessageText\", `Compacted from ${tokenStr} tokens (`) +\n\t\t\t\t\t\ttheme.fg(\"dim\", keyText(\"app.tools.expand\")) +\n\t\t\t\t\t\ttheme.fg(\"customMessageText\", \" Expand)\"),\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
|