Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/coding-agent/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
- `--system-prompt` and `--append-system-prompt` work again on the CLI path, and now compose with per-model prompt presets instead of being clobbered by them. A custom system prompt replaces the generated base and makes the preset step aside (the startup "Optimized system prompt applied" header also stands down); append texts are reattached after a preset replaces the base, so they survive on preset-matching models and across model switches. The CLI flags had been parsed but disconnected since 2026-07-19 because presets overwrote user overrides. Extensions can now read the user overrides via `ctx.getSystemPromptOptions()`, which moved from the command context to the base `ExtensionContext` ([#903](https://github.com/code-yeongyu/senpi/pull/903)).
- Settings files now support dependency-free JSONC comments and trailing commas. When both `settings.jsonc` and `settings.json` exist in one config directory, JSONC wins; writes remain on the loaded file, config reload watches both formats, RPC emits `settings_source_selected` with the selected path/format/reason, and the interactive TUI shows the choice at startup or reload ([#902](https://github.com/code-yeongyu/senpi/pull/902)).
- GLM 5.3 prompt preset: a new `glm-5.3` system-prompt preset cloned from `glm-5.2` (thin `tuningSection` wrapper over the shared dynamic core, `workstationDialect: "claude"`). The `hasGlm53Signal`/`isGlm53Model` matcher is checked before the 5.2 matcher, `"glm-5.3"` joins `PromptPresetName`/`VALID_PRESETS`, and the settings.md value list is updated. Models selecting GLM 5.3 now get the tuned system prompt instead of the untuned fallback ([#895](https://github.com/code-yeongyu/senpi/pull/895)).
- Gemini and Muse Spark prompt presets: new `gemini` and `muse-spark` system-prompt presets as thin `tuningSection` wrappers over the shared dynamic core (`workstationDialect: "default"`), each carrying its vendor-guidance behaviors as typed rule data (`GEMINI_RULES` / `MUSE_SPARK_RULES`) and ending with a machine-consumed `model-family: <name>` token. The `hasGeminiSignal` matcher covers the catalog Gemini 3.x Flash ids (`google/gemini-3.6-flash`, `google/gemini-3.1-flash-lite`, `google/gemini-3.5-flash`, `google/gemini-3.5-flash-lite`, `google/gemini-3.7-flash`, plus the unprefixed, `:batch`-tagged, and `-preview` shapes; `-image` variants stay out) and `hasMuseSparkSignal` covers `meta/muse-spark-1.1`, `meta/muse-spark-1.2`, and `meta/muse-spark-1.2-contributor`; truncated ids match neither. Both names join `PromptPresetName`/`VALID_PRESETS` and the settings.md value list. Models selecting these families now get tuned system prompts instead of the untuned fallback ([#899](https://github.com/code-yeongyu/senpi/pull/899)).

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion packages/coding-agent/docs/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Permission rules are a confirmation policy, not a sandbox. Senpi, extensions, pa
| `modelThinkingLevels` | object | - | Per-model reasoning effort memory (`"provider/id": "level"`) |
| `modelLastOnThinkingLevels` | object | - | Per-model last non-off reasoning level, used by `/reasoning on` to restore the previous effort |
| `modelServiceTiers` | object | - | Per-model service tier memory (`"provider/id": "auto" \| "priority"`) |
| `promptPreset` | string | `"auto"` | Force a system prompt preset: `"auto"`, `"kimi-k2-6"`, `"kimi-k2-7"`, `"kimi-k3"`, `"glm-5.2"`, `"glm-5.3"`, `"grok-4.5"`, `"grok-4.6"`, `"claude-fable-5"`, `"claude-opus-5"`, `"claude-opus-4-5"`, `"claude-opus-4-6"`, `"claude-opus-4-7"`, `"claude-opus-4-8"`, `"deepseek-v4-flash"`, `"deepseek-v4-flash-0731"`, `"deepseek-v4-pro"`, `"gpt-5"`, `"gpt-5.2"`, `"gpt-5.3-codex"`, `"gpt-5.4"`, `"gpt-5.5"`, or `"gpt-5.6"` |
| `promptPreset` | string | `"auto"` | Force a system prompt preset: `"auto"`, `"kimi-k2-6"`, `"kimi-k2-7"`, `"kimi-k3"`, `"glm-5.2"`, `"glm-5.3"`, `"grok-4.5"`, `"grok-4.6"`, `"claude-fable-5"`, `"claude-opus-5"`, `"claude-opus-4-5"`, `"claude-opus-4-6"`, `"claude-opus-4-7"`, `"claude-opus-4-8"`, `"deepseek-v4-flash"`, `"deepseek-v4-flash-0731"`, `"deepseek-v4-pro"`, `"gemini"`, `"muse-spark"`, `"gpt-5"`, `"gpt-5.2"`, `"gpt-5.3-codex"`, `"gpt-5.4"`, `"gpt-5.5"`, or `"gpt-5.6"` |
| `hideThinkingBlock` | boolean | `false` | Hide thinking blocks in output |
| `showCacheMissNotices` | boolean | `false` | Show transcript notices for significant prompt-cache misses |
| `thinkingBudgets` | object | - | Custom token budgets per thinking level |
Expand Down
2 changes: 1 addition & 1 deletion packages/coding-agent/src/core/dynamic-prompt/style.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export function buildStyleSection(): string {
return `## Style

Be concise and concrete. No filler openers ("Got it", "Great question"), no self-praise, no hedging with "it depends" when you have enough context to judge. Use bullets only for genuinely list-shaped content. Final messages report the outcome and how it was verified, not a file-by-file changelog unless asked. Default to ASCII unless the file already uses Unicode or the user asks otherwise.
Be concise and concrete. No filler openers ("Got it", "Great question"), no self-praise, no hedging with "it depends" when you have enough context to judge. Use bullets only for genuinely list-shaped content. Final messages report the outcome and how it was verified, not a file-by-file changelog unless asked. Match the user's language: when the user's messages, project context, or ~/.omo/AGENTS.md indicates a language (e.g. Korean), respond in that language; default to the user's language rather than ASCII.

Smallest correct change wins. Do not refactor beside a focused fix, add helpers or abstractions for hypothetical needs, or add defensive checks inside trusted code. Trust framework guarantees; validate only at system boundaries.

Expand Down
4 changes: 2 additions & 2 deletions packages/coding-agent/src/core/extensions/builtin/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
| 1 | `hooks` | `hooks/` | Settings-configured lifecycle command hooks (PreToolUse/PostToolUse-style) with trust hashing + live status |
| 2 | `permission-system` | `permission-system/` | Full opencode-style permission port: rules, JSONL storage, prompts |
| 3 | `gpt-apply-patch` | `gpt-apply-patch/` | Codex-style `apply_patch` tool with rich render + freeform grammar |
| 4 | `prompt-preset` | `prompt-preset/` | Per-model system prompts (gpt-5.x, claude-fable-5, claude-opus-5, claude-opus-4-{5,6,7,8}, glm-5.2, glm-5.3, deepseek-v4-{flash,flash-0731,pro}, kimi-k2-{6,7}, kimi-k3) |
| 4 | `prompt-preset` | `prompt-preset/` | Per-model system prompts (gpt-5.x, claude-fable-5, claude-opus-5, claude-opus-4-{5,6,7,8}, glm-5.2, glm-5.3, deepseek-v4-{flash,flash-0731,pro}, gemini, muse-spark, kimi-k2-{6,7}, kimi-k3) |
| 5 | `todowrite` | `todotools/` | Op-based oh-my-pi todo port + `/todo` command; fully diverged from `../pi-extensions/pi-todotools` |
| 6 | `redraws` | `redraws.ts` | Force-redraw event hooks for stable streaming visuals |
| 7 | `anthropic-web-search` | `anthropic-web-search/` | Anthropic-native web search tool |
Expand Down Expand Up @@ -58,7 +58,7 @@ Shared non-factory modules in this directory:

- **Subdirectory extensions** ship multi-file: `index.ts` + supporting `.ts` (`registry.ts`, `types.ts`, `parsers.ts`, etc.).
- **Single-file extensions** are kept flat (`diff.ts`, `files.ts`, `redraws.ts`, `service-tier.ts`, `tps.ts`, `prompt-url-widget.ts`).
- **`prompt-preset/`** has per-model files (`gpt-5.5.ts`, `claude-opus-4-7.ts`, …) and a shared `file-operations.ts` tuning block. New model = new preset file + entry in `presets.ts`. Models covered: gpt-5.x, claude-fable-5, claude-opus-5, claude-opus-4-{5,6,7,8}, glm-5.2, glm-5.3, deepseek-v4-{flash,flash-0731,pro}, kimi-k2-{6,7}, kimi-k3.
- **`prompt-preset/`** has per-model files (`gpt-5.5.ts`, `claude-opus-4-7.ts`, …) and a shared `file-operations.ts` tuning block. New model = new preset file + entry in `presets.ts`. Models covered: gpt-5.x, claude-fable-5, claude-opus-5, claude-opus-4-{5,6,7,8}, glm-5.2, glm-5.3, deepseek-v4-{flash,flash-0731,pro}, gemini, muse-spark, kimi-k2-{6,7}, kimi-k3.
- **`permission-system/` is a full port** of opencode's permission flow.
- **`compaction/`** is policy-rich (`policy.ts`, `speculative.ts`, `restoration-tracker.ts`, `circuit-breaker.ts`, `degradation-monitor.ts`, `per-turn-cap.ts`, `tool-truncation.ts`, `checkpoint-state.ts`, `context-reduction.ts`, `openai-remote.ts`, `repair-tool-pairs.ts`, `state.ts`, `todo-bridge.ts`, `prompts.ts`). Touch only with policy tests in lock-step.
- **External versions**: `external-versions.json` pins versions of sibling `../pi-extensions` packages used as vendored builtins; refresh with `packages/coding-agent/scripts/sync-builtin-extensions.mjs`.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# builtin/prompt-preset

Builtin extension #3. On `before_agent_start` and `model_select`, picks a system prompt preset by **model family** (gpt-5.x through gpt-5.6, claude-fable-5, claude-opus-5, claude-opus-4-{5,6,7,8}, glm-5.2, glm-5.3, deepseek-v4-{flash,flash-0731,pro}, kimi-k2-{6,7}, kimi-k3) and falls back to the senpi dynamic prompt when nothing matches. Renders the active preset name in the startup header. After 2026-04-30, presets are thin wrappers around `buildDynamicSystemPrompt()` carrying only model-specific tuning.
Builtin extension #3. On `before_agent_start` and `model_select`, picks a system prompt preset by **model family** (gpt-5.x through gpt-5.6, claude-fable-5, claude-opus-5, claude-opus-4-{5,6,7,8}, glm-5.2, glm-5.3, deepseek-v4-{flash,flash-0731,pro}, gemini, muse-spark, kimi-k2-{6,7}, kimi-k3) and falls back to the senpi dynamic prompt when nothing matches. Renders the active preset name in the startup header. After 2026-04-30, presets are thin wrappers around `buildDynamicSystemPrompt()` carrying only model-specific tuning.

## FILES

Expand All @@ -25,6 +25,8 @@ prompt-preset/
├── claude-opus-4-8.ts # Claude Opus 4.8 preset
├── glm-5-2.ts # GLM 5.2 preset
├── glm-5-3.ts # GLM 5.3 preset
├── gemini.ts # Gemini 3.x Flash preset — typed rule data (`GEMINI_RULES`) over a thin tuningSection wrapper
├── muse-spark.ts # Muse Spark preset — typed rule data (`MUSE_SPARK_RULES`) over a thin tuningSection wrapper
├── deepseek-v4.ts # Shared DeepSeek V4 rule data (`DEEPSEEK_V4_RULES`) + tuning builders (directive authority, todo discipline, missing-info, settled-reading, reasoning-aim)
├── deepseek-v4-flash.ts # DeepSeek V4 Flash preset (thin tuningSection over the shared core)
├── deepseek-v4-flash-0731.ts # DeepSeek V4 Flash 0731 snapshot preset — dated snapshot resolves before the generic flash alias
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,27 @@
### Expected merge conflict zones on next upstream sync

- LOW: `index.ts` handler bodies; keep the customPrompt yield and append reattachment when upstream reshapes handlers.
## Gemini + Muse Spark presets (2026-08-16)

### What changed

- `gemini.ts` / `muse-spark.ts`: new presets for the Gemini 3.x Flash and Muse Spark families — thin `tuningSection` wrappers over the shared dynamic core with `workstationDialect: "default"`, each carrying its vendor-guidance behaviors as typed rule data (`GEMINI_RULES` / `MUSE_SPARK_RULES`, deepseek-v4.ts precedent) and ending the tuning block with a machine-consumed `model-family: <name>` token line. Gemini rules: direct-instructions, lean-output, long-context-anchoring, behavior-requirements-binding, action-budget. Muse Spark rules: exposed-tools-only, no-hidden-control, one-goal-per-turn, evidence-before-success, observe-first, observation-summary, chain-checkpoints.
- `presets.ts`: `hasGeminiSignal` / `hasMuseSparkSignal` matchers on normalized id OR display name with `[/@:._-]` boundaries, verified against the installed pi-ai provider catalogs (2026-08-16). Gemini matches the exact flash ids `google/gemini-3.6-flash`, `google/gemini-3.1-flash-lite`, `google/gemini-3.5-flash`, `google/gemini-3.5-flash-lite`, `google/gemini-3.7-flash` plus their unprefixed (google, google-vertex, github-copilot, opencode), `:batch`-tagged, and `-preview` shapes; `-image` variants (Nano Banana image models) are excluded as a different modality, and truncated ids (`google/gemini-3.6`) do not match. Muse Spark matches `meta/muse-spark-1.1`, `meta/muse-spark-1.2`, `meta/muse-spark-1.2-contributor`; truncated `meta/muse-spark` and bare `spark` substrings do not.
- `settings.ts`: `"gemini"` and `"muse-spark"` join `PromptPresetName` and `VALID_PRESETS`.
- `docs/settings.md`, `AGENTS.md`, `builtin/AGENTS.md`: preset lists updated.
- `test/suite/prompt-presets-gemini-muse.test.ts` (new): routing decisions and `model-family` tokens only — exact-id tables for both families, truncated-id and loose-substring negatives, image-variant exclusion, a deepseek-v4-pro negative control, settings force + `parsePromptPreset` wiring, a catalog sweep asserting every built-in Gemini/Muse catalog model resolves, and per-prompt token stamping with cross-token non-leakage.

### Why

- Gemini 3.x Flash and Muse Spark models ship in the provider catalogs without a preset, so they fell back to the untuned dynamic prompt. Google's Gemini 3 guidance reads as calibration for a native reasoner (direct instructions, lean output, instruction-at-the-end anchoring, binding behavior requirements, binding action budgets); Meta's Muse Spark guidance centers on capability honesty and evidence-anchored tool loops. Both fit the thin-wrapper architecture with no shared-core changes.

### Why extension system couldn't handle this differently

- Content-only addition inside this builtin; follows the thin-wrapper preset architecture (`tuningSection` only).

### Expected merge conflict zones on next upstream sync

- LOW: `gemini.ts`, `muse-spark.ts`, and the test file are new files; `presets.ts`/`settings.ts` touch shared lists — trivial adjacent-line conflicts if upstream adds presets.

## GLM 5.3 preset (2026-08-16)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Gemini 3.x Flash family tuning.
//
// Gemini 3 reasons natively, so the vendor prompting guidance (Google's
// Gemini 3 API docs, 2026-08) reads as calibration rather than scaffolding:
// direct instructions over chain-of-thought prompt engineering, lean output
// by default, instruction-at-the-end for long-context tasks, behavior
// requirements treated as binding system-instruction-style rules, and stated
// action budgets honored when tool calls would otherwise over-trigger. The
// preset stays a thin tuningSection wrapper over the shared dynamic core and
// carries those behaviors as typed rule data (gpt-5.6.ts / deepseek-v4.ts
// precedent) so tests assert parsed rules and routing tokens instead of
// pinned sentences.

import { type BuildDynamicSystemPromptOptions, buildDynamicSystemPrompt } from "../../../dynamic-prompt/build.ts";

export type GeminiRuleId =
| "direct-instructions"
| "lean-output"
| "long-context-anchoring"
| "behavior-requirements-binding"
| "action-budget";

export type GeminiConcern = "style" | "grounding" | "harness-contract" | "tool-orchestration";

export interface GeminiRule {
readonly id: GeminiRuleId;
readonly concern: GeminiConcern;
readonly directive: string;
}

export const GEMINI_RULES: readonly GeminiRule[] = [
{
id: "direct-instructions",
concern: "style",
directive:
"Be concise and direct: the instructions in this prompt are plain directives to execute as written, not material to elaborate on. Verbose chain-of-thought prompt engineering - restating the task, narrating a plan before acting, explaining instructions back - adds no signal here; do the reasoning internally and let the answer or the tool call carry the result.",
},
{
id: "lean-output",
concern: "style",
directive:
"Default output is lean: short answers, minimal preamble, no summaries of what you are about to do. Steer toward verbosity - longer explanations, walkthroughs, extra detail - only when the user explicitly asks for it.",
},
{
id: "long-context-anchoring",
concern: "grounding",
directive:
'When long data precedes the task - pasted files, logs, or documents - treat the specific instruction at the end as the operative request and anchor the answer on the provided data, grounding claims in it explicitly ("Based on the information above...") instead of answering from prior knowledge.',
},
{
id: "behavior-requirements-binding",
concern: "harness-contract",
directive:
'Behavior requirements in this prompt and in injected directives are binding system-instruction-style rules, applied literally and at full weight every turn - not style suggestions to weigh against the task. When a requirement states a scope ("every", "all", "never"), that scope is exact.',
},
{
id: "action-budget",
concern: "tool-orchestration",
directive:
"When a stated action budget or step limit applies, treat it as binding: plan tool calls to fit inside it instead of over-triggering, and when one more call would break the budget, consolidate the remaining work into fewer, denser calls or stop and report.",
},
];

const GEMINI_INTRO =
"You are running on Gemini 3.x, a reasoning model. Reasoning is already on, so instructions are read as direct, literal input - the rules below calibrate style, grounding, and tool use rather than scaffolding thought.";

function buildGeminiTuning(): string {
return [GEMINI_INTRO, ...GEMINI_RULES.map((rule) => rule.directive), "model-family: gemini"].join("\n\n");
}

export function buildGeminiPrompt(options: BuildDynamicSystemPromptOptions): string {
return buildDynamicSystemPrompt({ ...options, tuningSection: buildGeminiTuning(), workstationDialect: "default" });
}
Loading