@oh-my-pi/pi-coding-agent 14.5.7 → 14.5.9
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 +43 -0
- package/package.json +7 -7
- package/src/config/model-registry.ts +23 -1
- package/src/config/settings-schema.ts +23 -0
- package/src/edit/modes/atom.lark +7 -5
- package/src/edit/modes/atom.ts +462 -56
- package/src/edit/modes/hashline.ts +21 -1
- package/src/lsp/index.ts +2 -4
- package/src/lsp/render.ts +0 -3
- package/src/lsp/types.ts +1 -4
- package/src/lsp/utils.ts +18 -14
- package/src/modes/components/settings-defs.ts +10 -0
- package/src/modes/controllers/command-controller.ts +17 -0
- package/src/modes/controllers/event-controller.ts +14 -9
- package/src/modes/controllers/input-controller.ts +13 -1
- package/src/modes/interactive-mode.ts +44 -23
- package/src/modes/types.ts +5 -2
- package/src/modes/utils/context-usage.ts +294 -0
- package/src/prompts/tools/atom.md +99 -44
- package/src/prompts/tools/exit-plan-mode.md +5 -39
- package/src/prompts/tools/lsp.md +2 -3
- package/src/prompts/tools/recipe.md +16 -0
- package/src/prompts/tools/task.md +34 -147
- package/src/prompts/tools/todo-write.md +22 -64
- package/src/session/compaction/compaction.ts +35 -22
- package/src/session/session-dump-format.ts +1 -0
- package/src/slash-commands/builtin-registry.ts +12 -5
- package/src/tools/bash.ts +149 -115
- package/src/tools/debug.ts +57 -70
- package/src/tools/index.ts +11 -0
- package/src/tools/recipe/index.ts +80 -0
- package/src/tools/recipe/render.ts +19 -0
- package/src/tools/recipe/runner.ts +219 -0
- package/src/tools/recipe/runners/cargo.ts +131 -0
- package/src/tools/recipe/runners/index.ts +8 -0
- package/src/tools/recipe/runners/just.ts +73 -0
- package/src/tools/recipe/runners/make.ts +101 -0
- package/src/tools/recipe/runners/pkg.ts +165 -0
- package/src/tools/recipe/runners/task.ts +72 -0
- package/src/tools/renderers.ts +2 -0
|
@@ -1,167 +1,54 @@
|
|
|
1
1
|
Launches subagents to parallelize workflows.
|
|
2
2
|
|
|
3
3
|
{{#if asyncEnabled}}
|
|
4
|
-
-
|
|
5
|
-
- Use
|
|
4
|
+
- `read jobs://` for state, `read jobs://<id>` for detail.
|
|
5
|
+
- Use `job` (with `poll`) to wait. **MUST NOT** poll `read jobs://` in a loop.
|
|
6
6
|
{{/if}}
|
|
7
7
|
|
|
8
|
-
{{#if
|
|
9
|
-
Current input mode: `default`. Shared `context` and custom task-call `schema` are available.
|
|
10
|
-
{{/if}}
|
|
11
|
-
{{#if schemaFreeMode}}
|
|
12
|
-
Current input mode: `schema-free`. Shared `context` is available; custom task-call `schema` is disabled. For structured output, rely on the agent definition or inherited session schema.
|
|
13
|
-
{{/if}}
|
|
14
|
-
{{#if independentMode}}
|
|
15
|
-
Current input mode: `independent`. Shared `context` and custom `schema` are both disabled. Every assignment must stand on its own.
|
|
16
|
-
{{/if}}
|
|
17
|
-
|
|
18
|
-
{{#if contextEnabled}}
|
|
19
|
-
Subagents lack your conversation history. Every decision, file content, and user requirement they need **MUST** be explicit in `context` or `assignment`.
|
|
20
|
-
{{else}}
|
|
21
|
-
Subagents lack your conversation history. Every decision, file content, and user requirement they need **MUST** be explicit in each task `assignment`.
|
|
22
|
-
{{/if}}
|
|
8
|
+
Subagents have no access to your conversation history. Every fact, file path, and decision they need **MUST** be explicit in {{#if contextEnabled}}`context` or `assignment`{{else}}each `assignment`{{/if}}.
|
|
23
9
|
|
|
24
10
|
<parameters>
|
|
25
|
-
- `agent`:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- `context`:
|
|
31
|
-
{{/if}}
|
|
32
|
-
{{#if
|
|
33
|
-
- `schema`: JSON-encoded JTD schema for expected output. Format lives here — **MUST NOT** be duplicated in assignments.
|
|
34
|
-
{{/if}}
|
|
35
|
-
- `tasks`: Tasks to execute in parallel.
|
|
36
|
-
{{#if isolationEnabled}}
|
|
37
|
-
- `isolated`: Run in isolated environment; returns patches. Use when tasks edit overlapping files.
|
|
38
|
-
{{/if}}
|
|
11
|
+
- `agent`: agent type for all tasks
|
|
12
|
+
- `tasks`: tasks to execute in parallel
|
|
13
|
+
- `.id`: CamelCase, ≤32 chars
|
|
14
|
+
- `.description`: UI label only — subagent never sees it
|
|
15
|
+
- `.assignment`: complete self-contained instructions; one-liners and missing acceptance criteria are PROHIBITED
|
|
16
|
+
{{#if contextEnabled}}- `context`: shared background prepended to every assignment; session-specific only{{/if}}
|
|
17
|
+
{{#if customSchemaEnabled}}- `schema`: JTD schema for expected structured output (do not put format rules in assignments){{/if}}
|
|
18
|
+
{{#if isolationEnabled}}- `isolated`: run in isolated env; use when tasks edit overlapping files{{/if}}
|
|
39
19
|
</parameters>
|
|
40
20
|
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
- For large payloads (traces, JSON blobs), write to `local://<path>` and pass the path in {{#if contextEnabled}}`context`{{else}}the relevant `assignment`{{/if}}.
|
|
49
|
-
- Prefer `task` agents that investigate **and** edit in one pass. Launch a dedicated read-only discovery step only when affected files are genuinely unknown.
|
|
50
|
-
</critical>
|
|
51
|
-
|
|
52
|
-
<scope>
|
|
53
|
-
Each task: **at most 3–5 files**. Globs, "update all", or package-wide scope = too broad. Enumerate files explicitly and fan out to a cluster of agents.
|
|
54
|
-
</scope>
|
|
21
|
+
<rules>
|
|
22
|
+
- **MUST NOT** assign tasks to run project-wide build/test/lint. Caller verifies after the batch.
|
|
23
|
+
- Each task: ≤3–5 explicit files. No globs, no "update all", no package-wide scope. Fan out to a cluster instead.
|
|
24
|
+
- Pass large payloads via `local://<path>` URIs, not inline.
|
|
25
|
+
{{#if contextEnabled}}- Put shared constraints in `context` once; do not duplicate across assignments.{{/if}}
|
|
26
|
+
- Prefer agents that investigate **and** edit in one pass; only spin a read-only discovery step when affected files are genuinely unknown.
|
|
27
|
+
</rules>
|
|
55
28
|
|
|
56
29
|
<parallelization>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|---|---|---|
|
|
61
|
-
|Types/interfaces|Consumers|Need contract|
|
|
62
|
-
|API exports|Callers|Need signatures|
|
|
63
|
-
|Core module|Dependents|Import dependency|
|
|
64
|
-
|Schema/migration|App logic|Schema dependency|
|
|
65
|
-
|
|
66
|
-
**Safe to parallelize:** independent modules, isolated file-scoped refactors, tests for existing code.
|
|
30
|
+
Test: can task B run correctly without seeing A's output? If no, sequence A → B.
|
|
31
|
+
Sequential when one task produces a contract (types, API, schema, core module) the other consumes.
|
|
32
|
+
Parallel when tasks touch disjoint files or are independent refactors/tests.
|
|
67
33
|
</parallelization>
|
|
68
34
|
|
|
69
|
-
<templates>
|
|
70
|
-
{{#if contextEnabled}}
|
|
71
|
-
**context:**
|
|
72
|
-
```
|
|
73
|
-
## Goal ← one sentence: what the batch accomplishes
|
|
74
|
-
## Non-goals ← what tasks must not touch
|
|
75
|
-
## Constraints ← MUST/MUST NOT rules and session decisions
|
|
76
|
-
## API Contract ← exact types/signatures if tasks share an interface (omit if N/A)
|
|
77
|
-
## Acceptance ← definition of done; build/lint runs AFTER all tasks complete
|
|
78
|
-
```
|
|
79
|
-
{{else}}
|
|
80
|
-
No shared `context` field exists in this mode. Fold goal, non-goals, constraints, and acceptance criteria into each `assignment`.
|
|
81
|
-
{{/if}}
|
|
82
|
-
**assignment:**
|
|
83
|
-
```
|
|
84
|
-
## Target ← exact file paths; named symbols; explicit non-goals
|
|
85
|
-
## Change ← step-by-step what to add/remove/rename; patterns/APIs to use
|
|
86
|
-
## Edge Cases ← tricky inputs; existing behavior that must survive
|
|
87
|
-
## Acceptance ← observable result proving the task is done; no project-wide commands
|
|
88
|
-
```
|
|
89
|
-
</templates>
|
|
90
|
-
|
|
91
|
-
<checklist>
|
|
92
|
-
Before invoking:
|
|
93
35
|
{{#if contextEnabled}}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
36
|
+
<context-fmt>
|
|
37
|
+
# Goal ← one sentence: what the batch accomplishes
|
|
38
|
+
# Constraints ← **MUST**/**MUST NOT** rules and session decisions
|
|
39
|
+
# Contract ← exact types/signatures if tasks share an interface
|
|
40
|
+
</context-fmt>
|
|
97
41
|
{{/if}}
|
|
98
|
-
- Every `assignment` follows the template; no one-liners; edge cases covered
|
|
99
|
-
- Tasks are truly parallel — you can articulate why none depends on another's output
|
|
100
|
-
- File paths are explicit; no globs
|
|
101
|
-
{{#if customSchemaEnabled}}
|
|
102
|
-
- `schema` is set if you expect structured output
|
|
103
|
-
{{else}}
|
|
104
|
-
- Do not pass a custom task-call `schema` in this mode
|
|
105
|
-
{{/if}}
|
|
106
|
-
</checklist>
|
|
107
|
-
|
|
108
|
-
{{#if contextEnabled}}
|
|
109
|
-
<example label="Rename exported symbol + update all call sites">
|
|
110
|
-
Two tasks with non-overlapping file sets — demonstrates scope partitioning.
|
|
111
42
|
|
|
112
|
-
<
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
## Acceptance (global)
|
|
118
|
-
Caller runs `bun check:ts` after both tasks complete. Tasks must NOT run it.
|
|
119
|
-
</context>
|
|
120
|
-
<tasks>
|
|
121
|
-
<task name="RenameExport">
|
|
122
|
-
<assignment>
|
|
123
|
-
## Target
|
|
124
|
-
- `src/config/parser.ts`: function `parseConfig`
|
|
125
|
-
- If `src/config/index.ts` re-exports it, update the re-export
|
|
126
|
-
- Non-goals: do not touch callers or tests
|
|
127
|
-
|
|
128
|
-
## Change
|
|
129
|
-
- Rename `parseConfig` → `loadConfig` (declaration + any JSDoc references)
|
|
130
|
-
|
|
131
|
-
## Edge Cases
|
|
132
|
-
- Rename all overload signatures if overloaded
|
|
133
|
-
- Internal helpers like `_parseConfigValue` are different symbols — leave untouched
|
|
134
|
-
- Do not add a backwards-compat alias
|
|
135
|
-
|
|
136
|
-
## Acceptance
|
|
137
|
-
- `parseConfig` no longer appears as a top-level export in `parser.ts`
|
|
138
|
-
</assignment>
|
|
139
|
-
</task>
|
|
140
|
-
<task name="UpdateCallers">
|
|
141
|
-
<assignment>
|
|
142
|
-
## Target
|
|
143
|
-
- `src/cli/init.ts`, `src/server/bootstrap.ts`, `src/worker/index.ts`
|
|
144
|
-
- Non-goals: do not touch `src/config/parser.ts` or `src/config/index.ts`
|
|
145
|
-
|
|
146
|
-
## Change
|
|
147
|
-
- Replace `import { parseConfig }` → `import { loadConfig }`
|
|
148
|
-
- Replace every call site `parseConfig(` → `loadConfig(`
|
|
149
|
-
- For `import * as cfg` users, update `cfg.parseConfig` property access
|
|
150
|
-
|
|
151
|
-
## Edge Cases
|
|
152
|
-
- String literals containing "parseConfig" (logs, comments) are documentation — leave them
|
|
153
|
-
- If a file re-exports to an external package boundary, keep the old name via `export { loadConfig as parseConfig }` with a `// TODO: remove after next major` comment
|
|
154
|
-
|
|
155
|
-
## Acceptance
|
|
156
|
-
- No bare `parseConfig` identifier remains in the three target files
|
|
157
|
-
</assignment>
|
|
158
|
-
</task>
|
|
159
|
-
</tasks>
|
|
160
|
-
</example>
|
|
161
|
-
{{/if}}
|
|
43
|
+
<assignment-fmt>
|
|
44
|
+
# Target ← exact files and symbols; explicit non-goals
|
|
45
|
+
# Change ← step-by-step add/remove/rename; APIs and patterns
|
|
46
|
+
# Acceptance ← observable result; no project-wide commands
|
|
47
|
+
</assignment-fmt>
|
|
162
48
|
|
|
49
|
+
<agents>
|
|
163
50
|
{{#list agents join="\n"}}
|
|
164
|
-
|
|
165
|
-
**Tools:** {{default (join tools ", ") "All"}}
|
|
51
|
+
# {{name}}
|
|
166
52
|
{{description}}
|
|
167
53
|
{{/list}}
|
|
54
|
+
</agents>
|
|
@@ -1,69 +1,33 @@
|
|
|
1
|
-
Manages a phased task list
|
|
2
|
-
The next pending task is auto-promoted to `in_progress` after
|
|
1
|
+
Manages a phased task list. Pass `ops`: a flat array of operations.
|
|
2
|
+
The next pending task is auto-promoted to `in_progress` after each completion.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
## Shape
|
|
4
|
+
## Operations
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
{
|
|
11
|
-
ops: [
|
|
12
|
-
{ op: "replace", phases: [...] },
|
|
13
|
-
{ op: "start", task: "task-3" },
|
|
14
|
-
{ op: "done", phase: "Implementation" },
|
|
15
|
-
{ op: "rm" },
|
|
16
|
-
{ op: "drop", task: "task-9" },
|
|
17
|
-
{ op: "append", phase: "Implementation", items: [{ id: "task-10", label: "Run tests" }] },
|
|
18
|
-
],
|
|
19
|
-
}
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Operation fields
|
|
23
|
-
|
|
24
|
-
|Field|Type|When to use|
|
|
6
|
+
|`op`|Required fields|Effect|
|
|
25
7
|
|---|---|---|
|
|
26
|
-
|`
|
|
27
|
-
|`task`|
|
|
28
|
-
|`
|
|
29
|
-
|`
|
|
30
|
-
|`
|
|
31
|
-
|`
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
- `
|
|
36
|
-
- `
|
|
37
|
-
- `drop`: marks one task, one phase, or all tasks abandoned
|
|
38
|
-
- `append`: appends `items` to `phase`; creates the phase if missing
|
|
39
|
-
- `replace`: replaces the full todo list
|
|
40
|
-
- `note`: append `text` as a new note attached to `task`. Notes are append-only context the user added; they only render to you when the task is `in_progress`. Other tasks display only a `+N` marker. Use this when you want to leave a follow-up reminder for yourself when you reach a later task.
|
|
41
|
-
|
|
42
|
-
If `done`, `rm`, or `drop` omits both `task` and `phase`, it applies to all tasks.
|
|
43
|
-
|
|
44
|
-
## Task Anatomy
|
|
45
|
-
- `label`: Short label (5-10 words). What is being done, not how.
|
|
46
|
-
- `replace` task `content` should stay short and specific.
|
|
47
|
-
|
|
48
|
-
## Phase Anatomy
|
|
49
|
-
- `name`: Short, human-readable noun phrase (1-3 words). Capitalize naturally.
|
|
50
|
-
- Always prefix with a roman-numeral ordinal (`I.`, `II.`, `III.`, `IV.`, …) to convey ordering — e.g. `I. Foundation`, `II. Auth`, `III. Routing`. Single-phase plans use `I.` too.
|
|
51
|
-
- You **MUST NOT** use snake_case, `Phase1_*`, arabic numerals (`1.`), or letter prefixes (`A.`) — they render as ugly identifiers.
|
|
8
|
+
|`replace`|`phases`|Replace the full list (initial setup, full restructure)|
|
|
9
|
+
|`start`|`task`|Set task to `in_progress`|
|
|
10
|
+
|`done`|`task` or `phase` (or neither = all)|Mark completed|
|
|
11
|
+
|`drop`|`task` or `phase` (or neither = all)|Mark abandoned|
|
|
12
|
+
|`rm`|`task` or `phase` (or neither = all)|Remove|
|
|
13
|
+
|`append`|`phase`, `items: {id, label}[]`|Append tasks; creates phase if missing|
|
|
14
|
+
|`note`|`task`, `text`|Append a note to `task.notes`. Only use to leave reminders for future-you.|
|
|
15
|
+
|
|
16
|
+
## Anatomy
|
|
17
|
+
- **Task `label`**: 5–10 words, what is being done, not how.
|
|
18
|
+
- **Phase `name`**: short noun phrase prefixed with a roman numeral — `I. Foundation`, `II. Auth`, `III. Verification`. Single-phase plans still use `I.`. Never use snake_case, arabic numerals, or letter prefixes.
|
|
52
19
|
|
|
53
20
|
## Rules
|
|
54
21
|
- Mark tasks done immediately after finishing — never defer.
|
|
55
|
-
- Complete phases in order
|
|
56
|
-
- On blockers, append a new task to the active phase.
|
|
22
|
+
- Complete phases in order.
|
|
23
|
+
- On blockers, `append` a new task to the active phase.
|
|
57
24
|
- Keep ids stable once introduced.
|
|
58
|
-
</protocol>
|
|
59
25
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
4. New instructions arrive mid-task — capture before proceeding
|
|
66
|
-
</conditions>
|
|
26
|
+
## When to create a list
|
|
27
|
+
- Task requires 3+ distinct steps
|
|
28
|
+
- User explicitly requests one
|
|
29
|
+
- User provides a set of tasks to complete
|
|
30
|
+
- New instructions arrive mid-task — capture before proceeding
|
|
67
31
|
|
|
68
32
|
<examples>
|
|
69
33
|
# Initial setup (multi-phase)
|
|
@@ -81,9 +45,3 @@ Create a todo list when:
|
|
|
81
45
|
# Append tasks to a phase
|
|
82
46
|
`{"ops":[{"op":"append","phase":"II. Auth","items":[{"id":"task-8","label":"Handle retries"},{"id":"task-9","label":"Run tests"}]}]}`
|
|
83
47
|
</examples>
|
|
84
|
-
|
|
85
|
-
<avoid>
|
|
86
|
-
- Single-step tasks — act directly
|
|
87
|
-
- Conversational or informational requests
|
|
88
|
-
- Tasks completable in under 3 trivial steps
|
|
89
|
-
</avoid>
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
getOpenAIResponsesHistoryPayload,
|
|
27
27
|
normalizeResponsesToolCallId,
|
|
28
28
|
} from "@oh-my-pi/pi-ai/utils";
|
|
29
|
+
import { countTokens } from "@oh-my-pi/pi-natives";
|
|
29
30
|
import { logger, prompt } from "@oh-my-pi/pi-utils";
|
|
30
31
|
import compactionShortSummaryPrompt from "../../prompts/compaction/compaction-short-summary.md" with { type: "text" };
|
|
31
32
|
import compactionSummaryPrompt from "../../prompts/compaction/compaction-summary.md" with { type: "text" };
|
|
@@ -218,7 +219,7 @@ export function shouldCompact(contextTokens: number, contextWindow: number, sett
|
|
|
218
219
|
return contextTokens > thresholdTokens;
|
|
219
220
|
}
|
|
220
221
|
|
|
221
|
-
function resolveThresholdTokens(contextWindow: number, settings: CompactionSettings): number {
|
|
222
|
+
export function resolveThresholdTokens(contextWindow: number, settings: CompactionSettings): number {
|
|
222
223
|
// Fixed token limit takes priority over percentage
|
|
223
224
|
const thresholdTokens = settings.thresholdTokens;
|
|
224
225
|
if (typeof thresholdTokens === "number" && Number.isFinite(thresholdTokens) && thresholdTokens > 0) {
|
|
@@ -240,67 +241,79 @@ function resolveThresholdTokens(contextWindow: number, settings: CompactionSetti
|
|
|
240
241
|
// ============================================================================
|
|
241
242
|
|
|
242
243
|
/**
|
|
243
|
-
*
|
|
244
|
-
*
|
|
244
|
+
* Image content has no tokenizer representation; charge a fixed estimate
|
|
245
|
+
* matching what providers typically bill for inline images.
|
|
246
|
+
*/
|
|
247
|
+
const IMAGE_TOKEN_ESTIMATE = 1200;
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Estimate token count for a message using cl100k_base via the native
|
|
251
|
+
* tokenizer. This is not Claude's first-party tokenizer (Anthropic doesn't
|
|
252
|
+
* publish one) but is within ~5–10% across English/code text.
|
|
245
253
|
*/
|
|
246
254
|
export function estimateTokens(message: AgentMessage): number {
|
|
247
|
-
|
|
255
|
+
const fragments: string[] = [];
|
|
256
|
+
let extra = 0;
|
|
248
257
|
|
|
249
258
|
switch (message.role) {
|
|
250
259
|
case "user": {
|
|
251
260
|
const content = (message as { content: string | Array<{ type: string; text?: string }> }).content;
|
|
252
261
|
if (typeof content === "string") {
|
|
253
|
-
|
|
262
|
+
fragments.push(content);
|
|
254
263
|
} else if (Array.isArray(content)) {
|
|
255
264
|
for (const block of content) {
|
|
256
265
|
if (block.type === "text" && block.text) {
|
|
257
|
-
|
|
266
|
+
fragments.push(block.text);
|
|
258
267
|
}
|
|
259
268
|
}
|
|
260
269
|
}
|
|
261
|
-
|
|
270
|
+
break;
|
|
262
271
|
}
|
|
263
272
|
case "assistant": {
|
|
264
273
|
const assistant = message as AssistantMessage;
|
|
265
274
|
for (const block of assistant.content) {
|
|
266
275
|
if (block.type === "text") {
|
|
267
|
-
|
|
276
|
+
fragments.push(block.text);
|
|
268
277
|
} else if (block.type === "thinking") {
|
|
269
|
-
|
|
278
|
+
fragments.push(block.thinking);
|
|
270
279
|
} else if (block.type === "toolCall") {
|
|
271
|
-
|
|
280
|
+
fragments.push(block.name);
|
|
281
|
+
fragments.push(JSON.stringify(block.arguments));
|
|
272
282
|
}
|
|
273
283
|
}
|
|
274
|
-
|
|
284
|
+
break;
|
|
275
285
|
}
|
|
276
286
|
case "hookMessage":
|
|
277
287
|
case "toolResult": {
|
|
278
288
|
if (typeof message.content === "string") {
|
|
279
|
-
|
|
289
|
+
fragments.push(message.content);
|
|
280
290
|
} else {
|
|
281
291
|
for (const block of message.content) {
|
|
282
292
|
if (block.type === "text" && block.text) {
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
chars += 4800; // Estimate images as 4000 chars, or 1200 tokens
|
|
293
|
+
fragments.push(block.text);
|
|
294
|
+
} else if (block.type === "image") {
|
|
295
|
+
extra += IMAGE_TOKEN_ESTIMATE;
|
|
287
296
|
}
|
|
288
297
|
}
|
|
289
298
|
}
|
|
290
|
-
|
|
299
|
+
break;
|
|
291
300
|
}
|
|
292
301
|
case "bashExecution": {
|
|
293
|
-
|
|
294
|
-
|
|
302
|
+
fragments.push(message.command);
|
|
303
|
+
fragments.push(message.output);
|
|
304
|
+
break;
|
|
295
305
|
}
|
|
296
306
|
case "branchSummary":
|
|
297
307
|
case "compactionSummary": {
|
|
298
|
-
|
|
299
|
-
|
|
308
|
+
fragments.push(message.summary);
|
|
309
|
+
break;
|
|
300
310
|
}
|
|
311
|
+
default:
|
|
312
|
+
return 0;
|
|
301
313
|
}
|
|
302
314
|
|
|
303
|
-
return
|
|
315
|
+
if (fragments.length === 0) return extra;
|
|
316
|
+
return extra + countTokens(fragments);
|
|
304
317
|
}
|
|
305
318
|
|
|
306
319
|
function estimateEntriesTokens(entries: SessionEntry[], startIndex: number, endIndex: number): number {
|
|
@@ -114,6 +114,7 @@ export function formatSessionDumpText(options: FormatSessionDumpTextOptions): st
|
|
|
114
114
|
if (c.type === "text") {
|
|
115
115
|
lines.push(c.text);
|
|
116
116
|
} else if (c.type === "thinking") {
|
|
117
|
+
if (c.thinking.trim().length === 0) continue;
|
|
117
118
|
lines.push("<thinking>");
|
|
118
119
|
lines.push(c.thinking);
|
|
119
120
|
lines.push("</thinking>\n");
|
|
@@ -123,11 +123,10 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
name: "loop",
|
|
126
|
-
description:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
await runtime.ctx.handleLoopCommand(command.args || undefined);
|
|
126
|
+
description:
|
|
127
|
+
"Toggle loop mode. While enabled, the next prompt you send re-submits after every yield. Esc cancels the current iteration; /loop again to disable.",
|
|
128
|
+
handle: async (_command, runtime) => {
|
|
129
|
+
await runtime.ctx.handleLoopCommand();
|
|
131
130
|
runtime.ctx.editor.setText("");
|
|
132
131
|
},
|
|
133
132
|
},
|
|
@@ -356,6 +355,14 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
356
355
|
runtime.ctx.editor.setText("");
|
|
357
356
|
},
|
|
358
357
|
},
|
|
358
|
+
{
|
|
359
|
+
name: "context",
|
|
360
|
+
description: "Show estimated context usage breakdown",
|
|
361
|
+
handle: (_command, runtime) => {
|
|
362
|
+
runtime.ctx.handleContextCommand();
|
|
363
|
+
runtime.ctx.editor.setText("");
|
|
364
|
+
},
|
|
365
|
+
},
|
|
359
366
|
{
|
|
360
367
|
name: "extensions",
|
|
361
368
|
aliases: ["status"],
|