feat: compressed prompts slightly

This commit is contained in:
can1357
2026-04-30 02:39:47 +02:00
parent ae3417fc3b
commit c6cab807d5
7 changed files with 80 additions and 255 deletions
+2 -2
View File
@@ -2,7 +2,7 @@
//!
//! Two encodings are exposed:
//!
//! - `O200kBase` — GPT-4o / o1 / GPT-5 (the modern OpenAI default).
//! - `O200kBase` — GPT-4o / o1 / GPT-5 (the modern `OpenAI` default).
//! - `Cl100kBase` — GPT-3.5 / GPT-4 / older models.
//!
//! `o200k_base` is the default. Anthropic doesn't publish their tokenizer, so
@@ -51,7 +51,7 @@ fn encoder(encoding: Option<Encoding>) -> &'static CoreBPE {
///
/// Uses ordinary encoding (no special-token handling), which is the right
/// choice for measuring user/model content rather than wire-protocol tokens.
/// Defaults to `o200k_base`; pass `Cl100kBase` for older OpenAI models.
/// Defaults to `o200k_base`; pass `Cl100kBase` for older `OpenAI` models.
#[napi]
pub fn count_tokens(input: Either<String, Vec<String>>, encoding: Option<Encoding>) -> u32 {
let bpe = encoder(encoding);
@@ -28,9 +28,10 @@ Lid= blank the anchored line's content but KEEP the line (results in an em
- Cursor-only ops (`^`, `$`, `@Lid`, `^Lid`) reposition without modifying. To insert anything you **MUST** follow them with `+TEXT` (or `+` for a blank).
- TEXT in `+TEXT`, `Lid=TEXT`, and `\TEXT` is literal line content, INCLUDING leading whitespace. You **MUST NOT** trim or re-indent it.
- Consecutive `+TEXT` ops produce consecutive lines in the order written. You **MUST NOT** separate them with a stray `+` unless you intend to insert a blank line.
- `Lid=TEXT` rewrites ONE line. To rewrite K adjacent lines, you **MUST** use `LidA..LidB=FIRST_LINE` followed immediately by `\NEXT_LINE` continuation lines. You **MUST** use bare `\` for blank replacement lines.
- `Lid=TEXT` rewrites ONE line. To rewrite K adjacent lines, you **MUST** use `LidA..LidB=FIRST_LINE` followed immediately by `\NEXT_LINE` continuation lines (canonical form for any block replacement). You **MUST** use bare `\` for blank replacement lines.
- You **MUST** prefix every replacement continuation line with `\`, especially when the replacement line starts with edit syntax characters such as `#`, `+`, `-`, `@`, `$`, `^`, `!`, or a Lid-shaped token.
- `\TEXT` **MUST** appear only immediately after an active `Lid=…` or `LidA..LidB=…` replacement. It **MUST NOT** be used as a general insert operator.
- A `\TEXT` line **MUST** be the immediate continuation of a `Lid=…` or `LidA..LidB=…` op on the line above (or another `\` line rooted in one). If the line above is `+TEXT`, a bare Lid, a cursor op, or whitespace, the `\` is invalid and the tool will not interpret it as part of a replacement.
- The legacy `-LidA..LidB` + `+TEXT…` block-rewrite form also works.
- To insert ABOVE a line, you **MUST** use `^Lid` then `+TEXT`. To insert above line 1, you **MUST** use `^` (BOF) then `+TEXT`. To insert below a line, you **MUST** use `@Lid` then `+TEXT`.
- Multiple `---PATH` sections **MAY** appear in one input; each section is applied in order.
@@ -82,6 +83,16 @@ Lid= blank the anchored line's content but KEEP the line (results in an em
\ return (name || DEF).trim().toUpperCase();
\}
# Replace a block with a longer multi-line block, including blank lines (canonical form for refactors)
---a.ts
{{hrefr 3}}..{{hrefr 6}}=/** Format a display label, falling back to DEF when empty. */
\export function label(name: string): string {
\ const clean = (name || DEF).trim();
\
\ if (clean.length === 0) return DEF;
\ return clean.toUpperCase();
\}
# Insert ABOVE a line
---a.ts
^{{hrefr 5}}
@@ -128,6 +139,8 @@ $
- Current/added preview lines include fresh `LINE+hash|content` anchors. Removed preview lines show deleted content and **MUST NOT** be reused as anchors.
- You **MUST** emit only lines that change. You **MUST NOT** echo unchanged context; the anchor implies position.
- You **MUST NOT** write `Lid=<sameTextThatIsAlreadyOnThatLine>`; the tool reports a no-op (no change applied). Emit `Lid=TEXT` only when TEXT differs.
- A line of the form `Lid|content` (a Lid, then `|`, then text, with NO leading `+`/`-`/`^`/`@`/`\`/`=`/`..`) is **FORBIDDEN**. That shape only appears in `read`/`grep` output as an anchor for *you*; it is never an edit op. If you copy a `Lid|content` line verbatim from a read into a patch, you have made an error — every edit op must start with `+`, `-`, `^`, `@`, `\`, `$`, `!`, or a Lid immediately followed by `=` or `..`.
- To replace a contiguous block with new content, the canonical form is `LidA..LidB=FIRST_LINE` + `\NEXT_LINE…`. You **MUST NOT** write the old block and then the new block — that is unified-diff thinking and the tool does not understand it. If you find yourself emitting pre-image lines (with or without operators) before your new content, STOP and rewrite the section as a single range-replace.
- TEXT after `=`, `+`, or `\` includes leading whitespace verbatim. You **MUST NOT** trim or re-indent it.
- This is NOT unified diff. You **MUST NOT** write `@@` headers, `-OLD`/`+NEW` pairs, context lines, or `+Lid|…` (bad: `+5th|new text`; good: `5th=new text`).
- You **MUST NOT** split `Lid=TEXT` across two physical lines.
@@ -1,40 +1,6 @@
Signals plan completion, requests user approval, and provides the final plan title for handoff.
Submits a finalized implementation plan for user approval.
<conditions>
Use when:
- Plan written to `local://PLAN.md`
- No unresolved questions about requirements or approach
- Ready for user review and approval
</conditions>
<instruction>
- You **MUST** write plan to plan file BEFORE calling this tool
- Tool reads plan from file—does not take plan content as parameter
- You **MUST** provide a `title` argument for the final plan artifact (example: `WP_MIGRATION_PLAN`)
- `.md` is optional in `title`; it is appended automatically when omitted
- User sees plan contents when reviewing
</instruction>
<output>
Presents plan to user for approval. If approved, plan mode exits with full tool access restored and the plan is renamed to `local://<title>.md`.
</output>
<examples>
# Ready
Plan complete at local://PLAN.md, no open questions.
→ Call `exit_plan_mode` with `{ "title": "WP_MIGRATION_PLAN" }`
# Unclear
Unsure about auth method (OAuth vs JWT).
→ Use `ask` first to clarify, then call `exit_plan_mode`
</examples>
<avoid>
- **MUST NOT** call before plan is written to file
- **MUST NOT** omit `title`
- **MUST NOT** use `ask` to request plan approval (this tool does that)
- **MUST NOT** call after pure research tasks (no implementation planned)
</avoid>
<critical>
You **MUST** only use when planning implementation steps. Research tasks (searching, reading, understanding) do not need this tool.
</critical>
Write the plan to `local://PLAN.md` first, then call this with `title` (e.g. `WP_MIGRATION_PLAN`); on approval the file is renamed to `local://<title>.md` and full tool access is restored.
- Use only after planning implementation steps; not for pure research.
- **MUST NOT** call before the plan file exists.
- **MUST NOT** use `ask` to request plan approval — this tool does that.
+35 -148
View File
@@ -1,167 +1,54 @@
Launches subagents to parallelize workflows.
{{#if asyncEnabled}}
- Use `read jobs://` to inspect state; `read jobs://<job_id>` for detail.
- Use the `job` tool (with `poll`) to wait until completion. You **MUST NOT** poll `read jobs://` in a loop.
- `read jobs://` for state, `read jobs://<id>` for detail.
- Use `job` (with `poll`) to wait. **MUST NOT** poll `read jobs://` in a loop.
{{/if}}
{{#if defaultMode}}
Current input mode: `default`. Shared `context` and custom task-call `schema` are available.
{{/if}}
{{#if schemaFreeMode}}
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.
{{/if}}
{{#if independentMode}}
Current input mode: `independent`. Shared `context` and custom `schema` are both disabled. Every assignment must stand on its own.
{{/if}}
{{#if contextEnabled}}
Subagents lack your conversation history. Every decision, file content, and user requirement they need **MUST** be explicit in `context` or `assignment`.
{{else}}
Subagents lack your conversation history. Every decision, file content, and user requirement they need **MUST** be explicit in each task `assignment`.
{{/if}}
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}}.
<parameters>
- `agent`: Agent type for all tasks.
- `.id`: CamelCase, max 32 chars
- `.description`: UI display only — subagent never sees it
- `.assignment`: Complete self-contained instructions. One-liners PROHIBITED; missing acceptance criteria = too vague.
{{#if contextEnabled}}
- `context`: Shared background prepended to every assignment. Session-specific info only.
{{/if}}
{{#if customSchemaEnabled}}
- `schema`: JSON-encoded JTD schema for expected output. Format lives here — **MUST NOT** be duplicated in assignments.
{{/if}}
- `tasks`: Tasks to execute in parallel.
{{#if isolationEnabled}}
- `isolated`: Run in isolated environment; returns patches. Use when tasks edit overlapping files.
{{/if}}
- `agent`: agent type for all tasks
- `tasks`: tasks to execute in parallel
- `.id`: CamelCase, ≤32 chars
- `.description`: UI label only — subagent never sees it
- `.assignment`: complete self-contained instructions; one-liners and missing acceptance criteria are PROHIBITED
{{#if contextEnabled}}- `context`: shared background prepended to every assignment; session-specific only{{/if}}
{{#if customSchemaEnabled}}- `schema`: JTD schema for expected structured output (do not put format rules in assignments){{/if}}
{{#if isolationEnabled}}- `isolated`: run in isolated env; use when tasks edit overlapping files{{/if}}
</parameters>
<critical>
{{#if contextEnabled}}
- **MUST NOT** duplicate shared constraints across assignments — put them in `context` once.
{{else}}
- Every `assignment` must repeat any constraints, reference paths, and acceptance criteria it needs — there is no shared `context` field.
{{/if}}
- **MUST NOT** tell tasks to run project-wide build/test/lint. Parallel agents share the working tree; each task edits, stops. Caller verifies after all complete.
- For large payloads (traces, JSON blobs), write to `local://<path>` and pass the path in {{#if contextEnabled}}`context`{{else}}the relevant `assignment`{{/if}}.
- Prefer `task` agents that investigate **and** edit in one pass. Launch a dedicated read-only discovery step only when affected files are genuinely unknown.
</critical>
<scope>
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.
</scope>
<rules>
- **MUST NOT** assign tasks to run project-wide build/test/lint. Caller verifies after the batch.
- Each task: ≤3–5 explicit files. No globs, no "update all", no package-wide scope. Fan out to a cluster instead.
- Pass large payloads via `local://<path>` URIs, not inline.
{{#if contextEnabled}}- Put shared constraints in `context` once; do not duplicate across assignments.{{/if}}
- Prefer agents that investigate **and** edit in one pass; only spin a read-only discovery step when affected files are genuinely unknown.
</rules>
<parallelization>
**Test:** Can task B produce correct output without seeing A's result? Yes → parallel. No → sequential.
|Sequential first|Then|Reason|
|---|---|---|
|Types/interfaces|Consumers|Need contract|
|API exports|Callers|Need signatures|
|Core module|Dependents|Import dependency|
|Schema/migration|App logic|Schema dependency|
**Safe to parallelize:** independent modules, isolated file-scoped refactors, tests for existing code.
Test: can task B run correctly without seeing A's output? If no, sequence A → B.
Sequential when one task produces a contract (types, API, schema, core module) the other consumes.
Parallel when tasks touch disjoint files or are independent refactors/tests.
</parallelization>
<templates>
{{#if contextEnabled}}
**context:**
```
## Goal ← one sentence: what the batch accomplishes
## Non-goals ← what tasks must not touch
## Constraints ← MUST/MUST NOT rules and session decisions
## API Contract ← exact types/signatures if tasks share an interface (omit if N/A)
## Acceptance ← definition of done; build/lint runs AFTER all tasks complete
```
{{else}}
No shared `context` field exists in this mode. Fold goal, non-goals, constraints, and acceptance criteria into each `assignment`.
{{/if}}
**assignment:**
```
## Target ← exact file paths; named symbols; explicit non-goals
## Change ← step-by-step what to add/remove/rename; patterns/APIs to use
## Edge Cases ← tricky inputs; existing behavior that must survive
## Acceptance ← observable result proving the task is done; no project-wide commands
```
</templates>
<checklist>
Before invoking:
{{#if contextEnabled}}
- `context` contains only session-specific info
{{else}}
- Every `assignment` includes its own goal, constraints, and acceptance criteria (no shared context)
{{/if}}
- Every `assignment` follows the template; no one-liners; edge cases covered
- Tasks are truly parallel — you can articulate why none depends on another's output
- File paths are explicit; no globs
{{#if customSchemaEnabled}}
- `schema` is set if you expect structured output
{{else}}
- Do not pass a custom task-call `schema` in this mode
{{/if}}
</checklist>
{{#if contextEnabled}}
<example label="Rename exported symbol + update all call sites">
Two tasks with non-overlapping file sets — demonstrates scope partitioning.
<context>
## Goal
Rename `parseConfig` → `loadConfig` in `src/config/parser.ts` and all callers.
## Non-goals
No behavior or signature changes; rename only.
## Acceptance (global)
Caller runs `bun check:ts` after both tasks complete. Tasks must NOT run it.
</context>
<tasks>
<task name="RenameExport">
<assignment>
## Target
- `src/config/parser.ts`: function `parseConfig`
- If `src/config/index.ts` re-exports it, update the re-export
- Non-goals: do not touch callers or tests
## Change
- Rename `parseConfig` → `loadConfig` (declaration + any JSDoc references)
## Edge Cases
- Rename all overload signatures if overloaded
- Internal helpers like `_parseConfigValue` are different symbols — leave untouched
- Do not add a backwards-compat alias
## Acceptance
- `parseConfig` no longer appears as a top-level export in `parser.ts`
</assignment>
</task>
<task name="UpdateCallers">
<assignment>
## Target
- `src/cli/init.ts`, `src/server/bootstrap.ts`, `src/worker/index.ts`
- Non-goals: do not touch `src/config/parser.ts` or `src/config/index.ts`
## Change
- Replace `import { parseConfig }` → `import { loadConfig }`
- Replace every call site `parseConfig(` → `loadConfig(`
- For `import * as cfg` users, update `cfg.parseConfig` property access
## Edge Cases
- String literals containing "parseConfig" (logs, comments) are documentation — leave them
- 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
## Acceptance
- No bare `parseConfig` identifier remains in the three target files
</assignment>
</task>
</tasks>
</example>
<context-fmt>
# Goal ← one sentence: what the batch accomplishes
# Constraints ← **MUST**/**MUST NOT** rules and session decisions
# Contract ← exact types/signatures if tasks share an interface
</context-fmt>
{{/if}}
<assignment-fmt>
# Target ← exact files and symbols; explicit non-goals
# Change ← step-by-step add/remove/rename; APIs and patterns
# Acceptance ← observable result; no project-wide commands
</assignment-fmt>
<agents>
{{#list agents join="\n"}}
### Agent: {{name}}
**Tools:** {{default (join tools ", ") "All"}}
# {{name}}
{{description}}
{{/list}}
</agents>
@@ -1,69 +1,33 @@
Manages a phased task list through an `ops` array of flat operations.
The next pending task is auto-promoted to `in_progress` after completing the current one.
Manages a phased task list. Pass `ops`: a flat array of operations.
The next pending task is auto-promoted to `in_progress` after each completion.
<protocol>
## Shape
## Operations
Pass an object with an `ops` array:
```ts
{
ops: [
{ op: "replace", phases: [...] },
{ op: "start", task: "task-3" },
{ op: "done", phase: "Implementation" },
{ op: "rm" },
{ op: "drop", task: "task-9" },
{ op: "append", phase: "Implementation", items: [{ id: "task-10", label: "Run tests" }] },
],
}
```
## Operation fields
|Field|Type|When to use|
|`op`|Required fields|Effect|
|---|---|---|
|`op`|string|Required. One of `replace`, `start`, `done`, `rm`, `drop`, `append`, `note`|
|`task`|string|Task id for `start`, or a task target for `done` / `rm` / `drop`|
|`phase`|string|Phase target for `done` / `rm` / `drop`, or append destination for `append`|
|`items`|{id, label}[]|Required for `append`. If the phase does not exist, it is created at the end|
|`phases`|Phase[]|Only for `replace`. Keeps initial phased setup available for harness bootstrap and full restructures|
|`text`|string|Required for `note`. The note text appended to `task.notes` (which is a list, joined with newlines on render)|
|`replace`|`phases`|Replace the full list (initial setup, full restructure)|
|`start`|`task`|Set task to `in_progress`|
|`done`|`task` or `phase` (or neither = all)|Mark completed|
|`drop`|`task` or `phase` (or neither = all)|Mark abandoned|
|`rm`|`task` or `phase` (or neither = all)|Remove|
|`append`|`phase`, `items: {id, label}[]`|Append tasks; creates phase if missing|
|`note`|`task`, `text`|Append a note to `task.notes`. Only use to leave reminders for future-you.|
## Semantics
- `start`: requires `task`; sets that task to `in_progress`
- `done`: marks one task, one phase, or all tasks completed
- `rm`: removes one task, one phase's tasks, or all tasks
- `drop`: marks one task, one phase, or all tasks abandoned
- `append`: appends `items` to `phase`; creates the phase if missing
- `replace`: replaces the full todo list
- `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.
If `done`, `rm`, or `drop` omits both `task` and `phase`, it applies to all tasks.
## Task Anatomy
- `label`: Short label (5-10 words). What is being done, not how.
- `replace` task `content` should stay short and specific.
## Phase Anatomy
- `name`: Short, human-readable noun phrase (1-3 words). Capitalize naturally.
- 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.
- You **MUST NOT** use snake_case, `Phase1_*`, arabic numerals (`1.`), or letter prefixes (`A.`) — they render as ugly identifiers.
## Anatomy
- **Task `label`**: 5–10 words, what is being done, not how.
- **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.
## Rules
- Mark tasks done immediately after finishing — never defer.
- Complete phases in order — do not skip ahead while earlier ones are pending.
- On blockers, append a new task to the active phase.
- Complete phases in order.
- On blockers, `append` a new task to the active phase.
- Keep ids stable once introduced.
</protocol>
<conditions>
Create a todo list when:
1. Task requires 3+ distinct steps
2. User explicitly requests one
3. User provides a set of tasks to complete
4. New instructions arrive mid-task — capture before proceeding
</conditions>
## When to create a list
- Task requires 3+ distinct steps
- User explicitly requests one
- User provides a set of tasks to complete
- New instructions arrive mid-task — capture before proceeding
<examples>
# Initial setup (multi-phase)
@@ -81,9 +45,3 @@ Create a todo list when:
# Append tasks to a phase
`{"ops":[{"op":"append","phase":"II. Auth","items":[{"id":"task-8","label":"Handle retries"},{"id":"task-9","label":"Run tests"}]}]}`
</examples>
<avoid>
- Single-step tasks — act directly
- Conversational or informational requests
- Tasks completable in under 3 trivial steps
</avoid>
+3 -1
View File
@@ -93,7 +93,9 @@ const debugSchema = Type.Object({
condition: Type.Optional(Type.String({ description: "breakpoint condition" })),
hit_condition: Type.Optional(Type.String()),
expression: Type.Optional(Type.String({ description: "expression to evaluate" })),
context: Type.Optional(Type.String({ description: "evaluate context: watch | repl | hover | variables | clipboard" })),
context: Type.Optional(
Type.String({ description: "evaluate context: watch | repl | hover | variables | clipboard" }),
),
frame_id: Type.Optional(Type.Number()),
scope_id: Type.Optional(Type.Number({ description: "scope variables reference" })),
variable_ref: Type.Optional(Type.Number({ description: "variable reference" })),
@@ -46,7 +46,6 @@ interface PromptTaskModel {
const PROMPT_TASK_LIMIT = 20;
interface PromptRunnerModel {
id: string;
label: string;