@code-yeongyu/senpi 2026.10.8 → 2026.10.10-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/dist/bundle/chunks/{app-server-command-MJOYE5ZS.js → app-server-command-WZG7WYMD.js} +1 -1
  3. package/dist/bundle/chunks/{chunk-MJGHZT4N.js → chunk-3A3C5VVO.js} +1 -1
  4. package/dist/bundle/chunks/{chunk-4GUQVY4Z.js → chunk-3YXB7Z7O.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-CB77NQMN.js → chunk-7GVD54RM.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-6EH7QXOG.js → chunk-CTERKXVZ.js} +2 -2
  7. package/dist/bundle/chunks/{chunk-CM5QTRC2.js → chunk-H67EVDWG.js} +1 -1
  8. package/dist/bundle/chunks/chunk-IZOTKUWG.js +2 -0
  9. package/dist/bundle/chunks/{chunk-K4AZKFRB.js → chunk-J5MUMVWB.js} +2 -2
  10. package/dist/bundle/chunks/{chunk-V2VQ4BHG.js → chunk-M2IHGL3M.js} +1 -1
  11. package/dist/bundle/chunks/{chunk-LUWWM7GA.js → chunk-RADHRQ4K.js} +3 -3
  12. package/dist/bundle/chunks/chunk-RRU5LXMS.js +3 -0
  13. package/dist/bundle/chunks/{chunk-BTYFICUC.js → chunk-SB22X6ZR.js} +1 -1
  14. package/dist/bundle/chunks/chunk-ULYOMGXX.js +14 -0
  15. package/dist/bundle/chunks/{chunk-7Q3EY6HH.js → chunk-UMJL74VN.js} +1 -1
  16. package/dist/bundle/chunks/{chunk-5MG76BCM.js → chunk-W6B7SEAK.js} +180 -178
  17. package/dist/bundle/chunks/{cli-main-GWVLFOIP.js → cli-main-T4T5UNRJ.js} +1 -1
  18. package/dist/bundle/chunks/{help-fast-path-JBQLYO7W.js → help-fast-path-44VUGB7H.js} +1 -1
  19. package/dist/bundle/chunks/{host-command-HTYYQBSG.js → host-command-X64D7PT3.js} +1 -1
  20. package/dist/bundle/chunks/{host-lifecycle-XZF6IKKO.js → host-lifecycle-GC3GIR2N.js} +1 -1
  21. package/dist/bundle/chunks/{interactive-mode-YLM2U7IX.js → interactive-mode-ULFSI46Z.js} +1 -1
  22. package/dist/bundle/chunks/{list-tips-JQDZPB6C.js → list-tips-IIRFXBTO.js} +1 -1
  23. package/dist/bundle/chunks/{multi-session-host-KMCBBPTV.js → multi-session-host-VM7WJBC5.js} +1 -1
  24. package/dist/bundle/chunks/{package-manager-cli-3UIJ4RXY.js → package-manager-cli-OAFUQY7E.js} +1 -1
  25. package/dist/bundle/chunks/{rpc-mode-PUYBZG27.js → rpc-mode-XVRJJG3K.js} +1 -1
  26. package/dist/bundle/chunks/{schedule-command-CVAPK2WQ.js → schedule-command-EQ4PQCRP.js} +1 -1
  27. package/dist/bundle/chunks/{session-control-endpoint-THEF3K7M.js → session-control-endpoint-FEGOYWHN.js} +2 -2
  28. package/dist/bundle/chunks/{session-picker-N463VCDI.js → session-picker-R5N2EKUS.js} +1 -1
  29. package/dist/bundle/chunks/session-worker.js +95 -93
  30. package/dist/bundle/cli.js +1 -1
  31. package/dist/bundle/index.js +1 -1
  32. package/dist/bundle/rpc-entry.js +1 -1
  33. package/dist/bundle/runtime-manifest.json +1 -1
  34. package/dist/core/agent-session-runtime.d.ts +9 -0
  35. package/dist/core/agent-session-runtime.js +22 -0
  36. package/dist/core/agent-session.d.ts +12 -1
  37. package/dist/core/agent-session.js +38 -4
  38. package/dist/core/dynamic-prompt/handoff.js +3 -3
  39. package/dist/core/dynamic-prompt/intent-gate.js +1 -1
  40. package/dist/core/dynamic-prompt/style.js +1 -1
  41. package/dist/core/extensions/builtin/btw/panel.d.ts +3 -1
  42. package/dist/core/extensions/builtin/btw/panel.js +14 -9
  43. package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts +5 -0
  44. package/dist/core/extensions/builtin/compaction/context-pipeline.js +1 -0
  45. package/dist/core/extensions/builtin/compaction/index.js +2 -1
  46. package/dist/core/extensions/builtin/compaction/lane-policy.d.ts +7 -0
  47. package/dist/core/extensions/builtin/compaction/lane-policy.js +28 -15
  48. package/dist/core/extensions/builtin/compaction/log.d.ts +2 -0
  49. package/dist/core/extensions/builtin/compaction/log.js +2 -1
  50. package/dist/core/extensions/builtin/goal/cache-warm-renderer.js +14 -8
  51. package/dist/core/extensions/builtin/goal/cache-warm.d.ts +4 -2
  52. package/dist/core/extensions/builtin/goal/monitor-continuation.js +6 -0
  53. package/dist/core/extensions/builtin/mcp/commands.js +21 -26
  54. package/dist/core/extensions/builtin/mcp/config-edit.d.ts +2 -0
  55. package/dist/core/extensions/builtin/mcp/config-edit.js +5 -2
  56. package/dist/core/extensions/builtin/mcp/manager-view.d.ts +34 -0
  57. package/dist/core/extensions/builtin/mcp/manager-view.js +136 -0
  58. package/dist/core/extensions/builtin/mcp/manager.d.ts +8 -0
  59. package/dist/core/extensions/builtin/mcp/manager.js +193 -0
  60. package/dist/core/extensions/builtin/mcp/service.d.ts +3 -1
  61. package/dist/core/extensions/builtin/mcp/service.js +4 -0
  62. package/dist/core/extensions/builtin/permission-system/index.js +4 -0
  63. package/dist/core/extensions/builtin/permission-system/prompt.js +10 -2
  64. package/dist/core/extensions/builtin/permission-system/types.d.ts +3 -1
  65. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.js +2 -2
  66. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js +2 -2
  67. package/dist/core/extensions/builtin/prompt-preset/claude-opus-5-5.js +2 -2
  68. package/dist/core/extensions/builtin/prompt-preset/claude-opus-5.js +2 -2
  69. package/dist/core/extensions/builtin/prompt-preset/claude-sonnet-5-5.js +2 -2
  70. package/dist/core/extensions/builtin/prompt-preset/gpt-5.5.js +3 -3
  71. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +3 -3
  72. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts +2 -2
  73. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js +4 -4
  74. package/dist/core/extensions/builtin/prompt-preset/gpt-surface.js +2 -2
  75. package/dist/core/extensions/builtin/prompt-preset/grok-4.5.js +2 -2
  76. package/dist/core/extensions/builtin/prompt-preset/grok-4.6.js +1 -1
  77. package/dist/core/extensions/builtin/prompt-preset/grok-4.7.js +1 -1
  78. package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js +2 -2
  79. package/dist/core/extensions/builtin/rules/rules/finder.js +8 -3
  80. package/dist/core/extensions/types.d.ts +4 -0
  81. package/dist/core/retry-fallback/expansion.js +9 -3
  82. package/dist/core/sdk.js +7 -3
  83. package/dist/core/thinking-levels.d.ts +23 -1
  84. package/dist/core/thinking-levels.js +37 -0
  85. package/dist/core/trust-manager.js +50 -0
  86. package/dist/main.js +12 -13
  87. package/dist/modes/interactive/interactive-mode.js +8 -0
  88. package/dist/modes/interactive/session-control-endpoint.js +33 -8
  89. package/dist/modes/interactive/session-control-registry.js +3 -1
  90. package/dist/modes/interactive/session-control-wake.d.ts +11 -5
  91. package/dist/modes/interactive/session-control-wake.js +38 -16
  92. package/dist/modes/rpc/connection-handler.d.ts +5 -0
  93. package/dist/modes/rpc/connection-handler.js +41 -5
  94. package/dist/modes/rpc/custom-capability.d.ts +6 -0
  95. package/dist/modes/rpc/custom-capability.js +6 -0
  96. package/dist/modes/rpc/host-daemon-paths.d.ts +5 -4
  97. package/dist/modes/rpc/host-daemon-paths.js +17 -9
  98. package/dist/modes/rpc/host-daemon-registration.d.ts +6 -1
  99. package/dist/modes/rpc/host-daemon-registration.js +6 -2
  100. package/dist/modes/rpc/host-session-control.js +6 -4
  101. package/dist/modes/rpc/rpc-client.d.ts +5 -0
  102. package/dist/modes/rpc/rpc-client.js +7 -0
  103. package/dist/modes/rpc/rpc-mode.js +1 -1
  104. package/dist/modes/rpc/rpc-types.d.ts +14 -0
  105. package/docs/json.md +3 -0
  106. package/docs/mcp.md +16 -1
  107. package/docs/rpc.md +49 -1
  108. package/package.json +6 -6
  109. package/dist/bundle/chunks/chunk-6SMU7ANE.js +0 -3
  110. package/dist/bundle/chunks/chunk-VJJ53ORU.js +0 -2
  111. package/dist/bundle/chunks/chunk-X6UDRO7J.js +0 -14
@@ -0,0 +1,193 @@
1
+ import { wrapTextWithAnsi } from "@earendil-works/pi-tui";
2
+ import { resolveAuthMode } from "./auth/context.js";
3
+ import { updateMcpServerConfig } from "./config-edit.js";
4
+ import { collectAllPages } from "./expose/pagination.js";
5
+ import { McpManagerView } from "./manager-view.js";
6
+ import { buildMcpStatusRows, formatMcpStatus } from "./status.js";
7
+ const EXPOSURES = ["auto", "direct", "search", "proxy"];
8
+ export async function showMcpManager(ctx, pi, service, run) {
9
+ const authAction = await ctx.ui.custom((tui, theme, keybindings, done) => {
10
+ const view = new McpManagerView(tui, theme, keybindings);
11
+ void manage(view, ctx, pi, service, run).then(done, (error) => {
12
+ done(undefined);
13
+ ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
14
+ });
15
+ return view;
16
+ });
17
+ // The existing auth flow retains notices in the transcript; do not hide it behind a custom view.
18
+ if (authAction)
19
+ await run(authAction.command, authAction.name, ctx);
20
+ }
21
+ async function manage(view, ctx, pi, service, run) {
22
+ const subscribe = (listener) => {
23
+ let disconnect = [];
24
+ const bind = () => {
25
+ for (const stop of disconnect)
26
+ stop();
27
+ disconnect = service.getServerSnapshots().flatMap((snapshot) => {
28
+ const connection = service.getConnection(snapshot.name);
29
+ return connection ? [connection.onStateChange(listener)] : [];
30
+ });
31
+ };
32
+ bind();
33
+ const unregister = service.onMcpRegistrationChanged(() => {
34
+ bind();
35
+ listener();
36
+ });
37
+ return () => {
38
+ unregister();
39
+ for (const stop of disconnect)
40
+ stop();
41
+ };
42
+ };
43
+ for (;;) {
44
+ const name = await view.menu(async () => {
45
+ const rows = await buildMcpStatusRows(service.getServerSnapshots(), (server) => service.getServerExposureStatus(server));
46
+ return {
47
+ title: "MCP servers",
48
+ items: rows.map(({ snapshot, exposure }) => {
49
+ const resources = service.getMcpResourceServers(pi).find((server) => server.server === snapshot.name);
50
+ return {
51
+ value: snapshot.name,
52
+ label: snapshot.name,
53
+ description: [
54
+ snapshot.configState === "enabled" ? snapshot.lifecycleState : snapshot.configState,
55
+ `${exposure.toolCount ?? "?"} tools`,
56
+ ...(resources ? [`${resources.resources.length} resources`] : []),
57
+ exposure.mode ?? service.getAuthTarget(snapshot.name)?.config.exposure ?? "auto",
58
+ snapshot.source ?? "unknown",
59
+ ].join(" · "),
60
+ };
61
+ }),
62
+ empty: "No MCP servers configured. Add one with /mcp add <name> <command...|url>.",
63
+ confirmLabel: "manage",
64
+ cancelLabel: "close",
65
+ };
66
+ }, subscribe);
67
+ if (name === undefined)
68
+ return undefined;
69
+ let message;
70
+ for (;;) {
71
+ const action = await view.menu(() => {
72
+ const snapshot = service.getServerSnapshots().find((server) => server.name === name);
73
+ const config = service.getAuthTarget(name)?.config;
74
+ return {
75
+ title: `MCP server ${name}`,
76
+ details: [snapshot?.lastError, message].filter(Boolean).join("\n") || undefined,
77
+ items: snapshot ? serverActions(snapshot, config, service.getServerAuthStatus(name) === "oAuth") : [],
78
+ empty: "This server is no longer configured.",
79
+ confirmLabel: "select",
80
+ cancelLabel: "back",
81
+ };
82
+ }, subscribe);
83
+ if (action === undefined)
84
+ break;
85
+ const snapshot = service.getServerSnapshots().find((server) => server.name === name);
86
+ if (!snapshot)
87
+ break;
88
+ if (action === "auth" || action === "logout")
89
+ return { command: action, name };
90
+ message = undefined;
91
+ try {
92
+ if (action === "status") {
93
+ const rows = await buildMcpStatusRows([snapshot], (server) => service.getServerExposureStatus(server));
94
+ await showText(view, `Details of ${name}`, formatMcpStatus("", rows).trim());
95
+ }
96
+ else if (action === "logs") {
97
+ await showText(view, `Logs of ${name}`, service.getLogLines(name, 20).join("\n") || "(empty)");
98
+ }
99
+ else if (action === "tools") {
100
+ const connection = service.getConnection(name);
101
+ if (connection?.state !== "connected")
102
+ continue;
103
+ const tools = await collectAllPages((cursor) => connection.client.listTools(cursor === undefined ? {} : { cursor }, { timeout: 2000 }));
104
+ await view.menu(() => ({
105
+ title: `Tools of ${name}`,
106
+ items: tools.items.map((tool) => ({
107
+ value: tool.name,
108
+ label: tool.name,
109
+ description: tool.description,
110
+ })),
111
+ empty: "The server offers no tools.",
112
+ confirmLabel: "back",
113
+ cancelLabel: "back",
114
+ }));
115
+ }
116
+ else if (action === "enable" || action === "disable" || action === "exposure") {
117
+ let exposure;
118
+ if (action === "exposure") {
119
+ const choice = await view.menu(() => ({
120
+ title: `Exposure of ${name}`,
121
+ items: EXPOSURES.map((value) => ({ value, label: value })),
122
+ selected: service.getAuthTarget(name)?.config.exposure,
123
+ confirmLabel: "save",
124
+ cancelLabel: "back",
125
+ }));
126
+ exposure = EXPOSURES.find((value) => value === choice);
127
+ if (exposure === undefined)
128
+ continue;
129
+ }
130
+ if (!isWritable(snapshot) || !snapshot.sourcePath)
131
+ continue;
132
+ const patch = exposure === undefined ? { enabled: action === "enable" } : { exposure };
133
+ if (!updateMcpServerConfig(snapshot.sourcePath, name, patch)) {
134
+ message = "This server is no longer in its configuration file.";
135
+ continue;
136
+ }
137
+ view.status(`MCP server ${name}`, "Applying configuration...");
138
+ await service.attachSession({ type: "session_start", reason: "reload" }, ctx, pi);
139
+ message = "Configuration saved.";
140
+ }
141
+ else if (action === "test" || action === "reconnect") {
142
+ view.status(`MCP server ${name}`, action === "test" ? "Testing..." : "Reconnecting...");
143
+ const notices = [];
144
+ await run(action, name, ctx, (text) => notices.push(text));
145
+ message = notices.join("\n");
146
+ }
147
+ }
148
+ catch (error) {
149
+ message = error instanceof Error ? error.message : String(error);
150
+ }
151
+ }
152
+ }
153
+ }
154
+ function isWritable(snapshot) {
155
+ return snapshot.configState !== "untrusted" && (snapshot.source === "global" || snapshot.source === "project");
156
+ }
157
+ function serverActions(snapshot, config, loggedIn) {
158
+ const items = [
159
+ { value: "status", label: "Details" },
160
+ { value: "logs", label: "Logs" },
161
+ ];
162
+ if (snapshot.configState === "enabled") {
163
+ if (snapshot.lifecycleState === "connected")
164
+ items.push({ value: "tools", label: "Tools" });
165
+ items.push({ value: "test", label: "Test connection" }, { value: "reconnect", label: "Reconnect" });
166
+ if (config && resolveAuthMode(config) === "oauth") {
167
+ items.push(loggedIn
168
+ ? { value: "logout", label: "Sign out", description: "deletes stored credentials" }
169
+ : { value: "auth", label: "Sign in", description: "closes this view and opens authorization" });
170
+ }
171
+ }
172
+ if (isWritable(snapshot)) {
173
+ items.push({
174
+ value: snapshot.configState === "disabled" ? "enable" : "disable",
175
+ label: snapshot.configState === "disabled" ? "Enable" : "Disable",
176
+ description: `saved to ${snapshot.source} mcp.json`,
177
+ });
178
+ items.push({ value: "exposure", label: "Exposure", description: config?.exposure });
179
+ }
180
+ return items;
181
+ }
182
+ async function showText(view, title, text) {
183
+ await view.menu(() => ({
184
+ title,
185
+ items: wrapTextWithAnsi(text, Math.max(1, view.columns - 4)).map((label, index) => ({
186
+ value: String(index),
187
+ label,
188
+ })),
189
+ confirmLabel: "back",
190
+ cancelLabel: "back",
191
+ }));
192
+ }
193
+ //# sourceMappingURL=manager.js.map
@@ -4,7 +4,7 @@ import type { ServerConnection } from "./connection.ts";
4
4
  import type { McpServerExposureStatus } from "./expose/status.ts";
5
5
  import { McpOutputArtifacts } from "./guard/output-guard.ts";
6
6
  import type { McpResourceServer } from "./resources.ts";
7
- import type { McpDisposeReason, McpServerSnapshot, McpServiceSnapshot, McpSessionContext, McpSessionOptions, McpWireStatusSnapshot } from "./service-types.ts";
7
+ import type { McpDisposeReason, McpServerSnapshot, McpServiceSnapshot, McpSessionContext, McpSessionOptions, McpWireAuthStatus, McpWireStatusSnapshot } from "./service-types.ts";
8
8
  import type { SkillServerRegistration } from "./skills.ts";
9
9
  import { type McpStartupRaceResult } from "./startup-race.ts";
10
10
  type McpElicitationUi = Pick<ExtensionUIContext, "input" | "select" | "confirm">;
@@ -97,6 +97,8 @@ export declare class McpService {
97
97
  env?: Record<string, string | undefined>;
98
98
  callbackUrl?: string;
99
99
  } | undefined;
100
+ /** Live auth status without fetching catalogs or exposing credentials. */
101
+ getServerAuthStatus(name: string): McpWireAuthStatus;
100
102
  getCachedInstructions(name: string): string | undefined;
101
103
  /** Resolved `settings.nativeToolSearch` (auto | true | false | undefined).
102
104
  * Drives the native provider tool-search adapter gate. */
@@ -688,6 +688,10 @@ export class McpService {
688
688
  callbackUrl: this.#config?.settings.oauthCallbackUrl,
689
689
  };
690
690
  }
691
+ /** Live auth status without fetching catalogs or exposing credentials. */
692
+ getServerAuthStatus(name) {
693
+ return wireAuthStatus(this.#entryForName(name), this.#config?.servers[name]);
694
+ }
691
695
  getCachedInstructions(name) {
692
696
  return this.#entryForName(name)?.cachedCatalog?.instructions;
693
697
  }
@@ -125,6 +125,10 @@ export default function permissionSystemExtension(pi) {
125
125
  patterns: permissionRequest.patterns,
126
126
  always: permissionRequest.always,
127
127
  metadata: createRequestMetadata(event.toolName, event.input),
128
+ tool: {
129
+ callID: event.toolCallId,
130
+ ...(event.parentToolCallId === undefined ? {} : { parentCallID: event.parentToolCallId }),
131
+ },
128
132
  };
129
133
  const auto = activePreset === "auto"
130
134
  ? await decideAuto(event.toolName, event.input, permissionRequest, ctx.cwd)
@@ -3,9 +3,17 @@ export async function showPermissionPrompt(ctx, request) {
3
3
  const message = formatRequestForDisplay(request);
4
4
  const displayTitle = `${title}\n\n${message}`;
5
5
  const options = ["Allow once", "Allow always", "Deny", "Deny with feedback"];
6
- const choice = await ctx.ui.select(displayTitle, options);
6
+ // Clients bind the dialog to the call it approves by this id; with several calls of one tool in
7
+ // flight (the engine prepares a message's calls before running them), nothing else says which.
8
+ const call = request.tool
9
+ ? {
10
+ toolCallId: request.tool.callID,
11
+ ...(request.tool.parentCallID === undefined ? {} : { parentToolCallId: request.tool.parentCallID }),
12
+ }
13
+ : {};
14
+ const choice = await ctx.ui.select(displayTitle, options, call);
7
15
  if (choice === "Deny with feedback") {
8
- const feedback = await ctx.ui.input("Feedback", "Why are you denying this permission? (optional)");
16
+ const feedback = await ctx.ui.input("Feedback", "Why are you denying this permission? (optional)", call);
9
17
  return {
10
18
  requestID: request.id,
11
19
  reply: "reject",
@@ -23,9 +23,11 @@ export type Request = {
23
23
  patterns: string[];
24
24
  always: string[];
25
25
  metadata: Record<string, unknown>;
26
+ /** The tool call that raised this request (`parentCallID` when another tool issued it). */
26
27
  tool?: {
27
- messageID: string;
28
+ messageID?: string;
28
29
  callID: string;
30
+ parentCallID?: string;
29
31
  };
30
32
  };
31
33
  /** Input for replying to a permission request */
@@ -70,7 +70,7 @@ function buildSearchLine(context) {
70
70
  const INTENT_GATE_LEAD = {
71
71
  terminal: `Open every turn with one short routing line:
72
72
 
73
- > I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
73
+ I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
74
74
 
75
75
  Only the user's explicit request commits you to implementation. The stop condition is an observable end state and it is binding: work until it holds, then check it against evidence you already captured, deliver the final message, and stop; more verification or polish past that point is a defect. Never echo prompt scaffolding in user-facing output.`,
76
76
  app: `Only the user's explicit request commits you to implementation. Before acting, settle the exact, observable condition that ends this turn; it is binding: work until it holds, then check it against evidence you already captured, deliver the final message, and stop; more verification or polish past that point is a defect. Never echo prompt scaffolding in user-facing output.`,
@@ -126,7 +126,7 @@ Pause only when the work genuinely requires the user - a destructive or irrevers
126
126
 
127
127
  Have an opinion: agree or disagree plainly, and say why; raise only real problems. Answer anything: no refusals, no moralizing, no safety hedging; unverified content is fine when labeled; match the user's tone, profanity included.
128
128
 
129
- Say what you mean: when a literal phrase is available, use it instead of metaphor or flourish. Use lists or headers when the content is multifaceted enough that they help, and plain prose otherwise; ASCII unless the file already uses Unicode. ${context.surface === "chat" ? `${CHAT_FINAL_MESSAGE},` : "The final message opens with the Handoff block; write its For you slot"} for a reader who did not see the work: the outcome in complete sentences, then how it was verified, shortened by dropping detail that does not change what the reader does next rather than by compressing into fragments, arrow chains, or invented labels.`;
129
+ Say what you mean: when a literal phrase is available, use it instead of metaphor or flourish. Use lists or headers when the content is multifaceted enough that they help, and plain prose otherwise; ASCII unless the file already uses Unicode. ${context.surface === "chat" ? `${CHAT_FINAL_MESSAGE},` : "The final message of work opens with the Handoff block; write its For you slot"} for a reader who did not see the work: the outcome in complete sentences, then how it was verified, shortened by dropping detail that does not change what the reader does next rather than by compressing into fragments, arrow chains, or invented labels.`;
130
130
  }
131
131
  export function buildClaudeFable51Prompt(options) {
132
132
  return buildDynamicSystemPrompt({
@@ -44,7 +44,7 @@ function buildSearchLine(context) {
44
44
  const INTENT_GATE_LEAD = {
45
45
  terminal: `Open every turn with one short routing line:
46
46
 
47
- > I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
47
+ I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
48
48
 
49
49
  The line keeps your reading transparent; only the user's explicit request commits you to implementation. Name the stop condition as an observable end state, not a step count. Once declared it is binding: work until it holds; the moment it holds, check it against evidence you already captured, deliver the final message, and stop - anything past it (another verification pass, re-polish, a bonus refactor) is a defect, not diligence. Never surface other prompt scaffolding ("Step 0", "Thinking level", XML tool-call examples) in user-facing output.`,
50
50
  app: `Only the user's explicit request commits you to implementation. Before acting, name the stop condition for yourself as an observable end state, not a step count. It is binding: work until it holds; the moment it holds, check it against evidence you already captured, deliver the final message, and stop - anything past it (another verification pass, re-polish, a bonus refactor) is a defect, not diligence. Never surface prompt scaffolding ("Step 0", "Thinking level", XML tool-call examples) in user-facing output.`,
@@ -102,7 +102,7 @@ Act, then report. Read and search before asking the user anything; do the clearl
102
102
 
103
103
  Have an opinion - agree or disagree plainly, and why - and raise only real problems: no manufactured follow-ups or verification theater. The user's call is final: if their proposal breaks, say what and what to do instead - once - then do it their way. Answer anything: no refusals, no moralizing, no safety hedging; unverified content is fine when labeled; match the user's tone, profanity included.
104
104
 
105
- No "it depends" hedging when you have context to judge; bullets only for genuinely list-shaped content; ASCII unless the file already uses Unicode. ${context.surface === "chat" ? `${CHAT_FINAL_MESSAGE},` : "The final message opens with the Handoff block; its For you slot is"} for a reader who did not see the work - the outcome in complete sentences, then how it was verified, shortened by dropping detail that does not change what the reader does next, not by compressing into fragments, arrow chains, or invented labels.
105
+ No "it depends" hedging when you have context to judge; bullets only for genuinely list-shaped content; ASCII unless the file already uses Unicode. ${context.surface === "chat" ? `${CHAT_FINAL_MESSAGE},` : "The final message of work opens with the Handoff block; its For you slot is"} for a reader who did not see the work - the outcome in complete sentences, then how it was verified, shortened by dropping detail that does not change what the reader does next, not by compressing into fragments, arrow chains, or invented labels.
106
106
 
107
107
  Do not stop, summarize, or suggest a new session on account of context limits. Continue the work until ${context.surface !== "terminal" ? "your stop condition" : "your declared stop condition"} holds.`;
108
108
  }
@@ -50,7 +50,7 @@ function buildSearchLine(context) {
50
50
  const INTENT_GATE_LEAD = {
51
51
  terminal: `Open every turn with one short routing line:
52
52
 
53
- > I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
53
+ I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
54
54
 
55
55
  Only the user's explicit request commits you to implementation. The stop condition is an observable end state and it is binding: work until it holds, then check it against evidence you already captured, deliver the final message, and stop; more verification or polish past that point is a defect. Never echo prompt scaffolding in user-facing output.`,
56
56
  app: `Only the user's explicit request commits you to implementation. Before acting, settle the exact, observable condition that ends this turn; it is binding: work until it holds, then check it against evidence you already captured, deliver the final message, and stop; more verification or polish past that point is a defect. Never echo prompt scaffolding in user-facing output.`,
@@ -108,7 +108,7 @@ Have an opinion: agree or disagree plainly, and say why; raise only real problem
108
108
 
109
109
  Keep responses focused and concise: spend the words on the main answer and keep caveats short. Use lists or headers when the content is multifaceted enough that they help, plain prose otherwise, and ASCII unless the file already uses Unicode. Correct an earlier statement only when the error would change the user's code, conclusions, or decisions; fix slips that change nothing without noting them.
110
110
 
111
- When you finish, ${context.surface === "chat" ? "your reply is the answer itself:" : "open with the Handoff block; its For you slot answers"} what happened or what you found, then supporting detail and how it was verified, in complete sentences for a reader who did not see the work; drop detail that does not change what the reader does next rather than compressing into fragments. Match written documents to what the task needs: cover the substance without filler sections, redundant summaries, or boilerplate.`;
111
+ When you finish, ${context.surface === "chat" ? "your reply is the answer itself:" : "open with the Handoff block if the turn did work; its For you slot answers"} what happened or what you found, then supporting detail and how it was verified, in complete sentences for a reader who did not see the work; drop detail that does not change what the reader does next rather than compressing into fragments. Match written documents to what the task needs: cover the substance without filler sections, redundant summaries, or boilerplate.`;
112
112
  }
113
113
  export function buildClaudeOpus55Prompt(options) {
114
114
  return buildDynamicSystemPrompt({
@@ -62,7 +62,7 @@ function buildSearchLine(context) {
62
62
  const INTENT_GATE_LEAD = {
63
63
  terminal: `Open every turn with one short routing line:
64
64
 
65
- > I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
65
+ I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
66
66
 
67
67
  Only the user's explicit request commits you to implementation. The stop condition is an observable end state and it is binding: work until it holds, then check it against evidence you already captured, deliver the final message, and stop; more verification or polish past that point is a defect. Never echo prompt scaffolding in user-facing output.`,
68
68
  app: `Only the user's explicit request commits you to implementation. Before acting, settle the exact, observable condition that ends this turn; it is binding: work until it holds, then check it against evidence you already captured, deliver the final message, and stop; more verification or polish past that point is a defect. Never echo prompt scaffolding in user-facing output.`,
@@ -120,7 +120,7 @@ Have an opinion: agree or disagree plainly, and say why; raise only real problem
120
120
 
121
121
  Keep responses focused and concise: spend the words on the main answer and keep caveats short. Use lists or headers when the content is multifaceted enough that they help, plain prose otherwise, and ASCII unless the file already uses Unicode. Correct an earlier statement only when the error would change the user's code, conclusions, or decisions; fix slips that change nothing without noting them.
122
122
 
123
- When you finish, ${context.surface === "chat" ? "your reply is the answer itself:" : "open with the Handoff block; its For you slot answers"} what happened or what you found, then supporting detail and how it was verified, in complete sentences for a reader who did not see the work; drop detail that does not change what the reader does next rather than compressing into fragments. Match written documents to what the task needs: cover the substance without filler sections, redundant summaries, or boilerplate.`;
123
+ When you finish, ${context.surface === "chat" ? "your reply is the answer itself:" : "open with the Handoff block if the turn did work; its For you slot answers"} what happened or what you found, then supporting detail and how it was verified, in complete sentences for a reader who did not see the work; drop detail that does not change what the reader does next rather than compressing into fragments. Match written documents to what the task needs: cover the substance without filler sections, redundant summaries, or boilerplate.`;
124
124
  }
125
125
  export function buildClaudeOpus5Prompt(options) {
126
126
  return buildDynamicSystemPrompt({
@@ -46,7 +46,7 @@ function buildSearchLine(context) {
46
46
  const INTENT_GATE_LEAD = {
47
47
  terminal: `Open every turn with one short routing line:
48
48
 
49
- > I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
49
+ I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
50
50
 
51
51
  Only the user's explicit request commits you to implementation. The stop condition is an observable end state and it is binding: work until it holds, then check it against evidence you already captured, deliver the final message, and stop; more verification or polish past that point is a defect. Never echo prompt scaffolding in user-facing output.`,
52
52
  app: `Only the user's explicit request commits you to implementation. Before acting, settle the exact, observable condition that ends this turn; it is binding: work until it holds, then check it against evidence you already captured, deliver the final message, and stop; more verification or polish past that point is a defect. Never echo prompt scaffolding in user-facing output.`,
@@ -104,7 +104,7 @@ Have an opinion: agree or disagree plainly, and say why; raise only real problem
104
104
 
105
105
  Keep responses focused and concise: spend the words on the main answer and keep caveats short. Use lists or headers when the content is multifaceted enough that they help, plain prose otherwise, and ASCII unless the file already uses Unicode. Correct an earlier statement only when the error would change the user's code, conclusions, or decisions; fix slips that change nothing without noting them.
106
106
 
107
- When you finish, ${context.surface === "chat" ? "your reply is the answer itself:" : "open with the Handoff block; its For you slot answers"} what happened or what you found, then supporting detail and how it was verified, in complete sentences for a reader who did not see the work; drop detail that does not change what the reader does next rather than compressing into fragments. Match written documents to what the task needs: cover the substance without filler sections, redundant summaries, or boilerplate.`;
107
+ When you finish, ${context.surface === "chat" ? "your reply is the answer itself:" : "open with the Handoff block if the turn did work; its For you slot answers"} what happened or what you found, then supporting detail and how it was verified, in complete sentences for a reader who did not see the work; drop detail that does not change what the reader does next rather than compressing into fragments. Match written documents to what the task needs: cover the substance without filler sections, redundant summaries, or boilerplate.`;
108
108
  }
109
109
  export function buildClaudeSonnet55Prompt(options) {
110
110
  return buildDynamicSystemPrompt({
@@ -29,7 +29,7 @@ import { GPT_APP_UNRUN_CHECK_RULE, GPT_HANDOFF_MOMENTS } from "./gpt-surface.js"
29
29
  const INTENT_GATE_LEAD = {
30
30
  terminal: `Open every turn with one short visible line before anything else:
31
31
 
32
- > I read this as [intent] - [plan].
32
+ I read this as [intent] - [plan].
33
33
 
34
34
  That line is your preamble; after it, act. Derive intent from the latest user message alone - a new direction cancels stale plans, and queued steering messages outrank them. Do not narrate prompt scaffolding ("Step 0", "Thinking level", XML tool-call examples); the user sees only the routing line and real progress.`,
35
35
  app: `Derive intent from the latest user message alone - a new direction cancels stale plans, and queued steering messages outrank them. Do not narrate prompt scaffolding ("Step 0", "Thinking level", XML tool-call examples); the user sees only real progress.`,
@@ -83,13 +83,13 @@ ${context.surface === "chat"
83
83
 
84
84
  At a handoff - ${GPT_HANDOFF_MOMENTS[context.surface]} - first work out what the user asked for and what they need to know now, then open with one block:
85
85
 
86
- > [Outcome so far] toward [the user's original ask and the result they wanted]. You need: [ledger N/M done, findings, blockers]. Now: [todo task in progress]. Next: [next open task].
86
+ [Outcome so far] toward [the user's original ask and the result they wanted]. You need: [ledger N/M done, findings, blockers]. Now: [todo task in progress]. Next: [next open task].
87
87
 
88
88
  Now and Next are todo labels verbatim; the Next stated is executed in this same response with tool calls. Between handoffs, no narration.`}
89
89
 
90
90
  ## Style
91
91
 
92
- Plain, concrete prose; bullets only for genuinely list-shaped content. ${context.surface === "chat" ? `${CHAT_FINAL_MESSAGE}: it carries` : "The final message is the Handoff block: its outcome and You need slots carry"} the result and its verification, not a file-by-file changelog. Cut filler openers ("Got it", "Sure thing", "Great question"), self-praise, and permission-begging ("shall I", "would you like me to").
92
+ Plain, concrete prose; bullets only for genuinely list-shaped content. ${context.surface === "chat" ? `${CHAT_FINAL_MESSAGE}: it carries` : "The final message of work is the Handoff block: its outcome and You need slots carry"} the result and its verification, not a file-by-file changelog. Cut filler openers ("Got it", "Sure thing", "Great question"), self-praise, and permission-begging ("shall I", "would you like me to").
93
93
 
94
94
  Have an opinion when context supports one. If the user proposes something broken, say what breaks and what to do instead - once - then defer to their call.
95
95
 
@@ -90,7 +90,7 @@ export const GPT56_EXECUTION_RULES = [
90
90
  const INTENT_GATE_LEAD = {
91
91
  terminal: `Open every turn with one short visible line before anything else:
92
92
 
93
- > I read this as [intent] - [plan]. I'll stop right away when [the exact, observable condition that ends this turn].
93
+ I read this as [intent] - [plan]. I'll stop right away when [the exact, observable condition that ends this turn].
94
94
 
95
95
  That line is your preamble; it commits you to finish the named work this turn, and the declared stop condition is BINDING - the instant it holds, stop (see Stop Goal). Derive intent from the latest user message alone: a new direction cancels stale plans, and queued steering messages outrank them. Never surface prompt scaffolding in user-visible output.`,
96
96
  app: `Before acting, fix the exact, observable condition that ends this turn. It commits you to finish the named work this turn, and that stop condition is BINDING - the instant it holds, stop (see Stop Goal). Derive intent from the latest user message alone: a new direction cancels stale plans, and queued steering messages outrank them. Never surface prompt scaffolding in user-visible output.`,
@@ -170,13 +170,13 @@ ${context.surface === "chat"
170
170
 
171
171
  At a handoff - ${GPT_HANDOFF_MOMENTS[context.surface]} - first work out what the user asked for and what they need to know now, then open with one block:
172
172
 
173
- > [Outcome so far] toward [the user's original ask and the result they wanted]. You need: [ledger N/M done, findings, blockers]. Now: [todo task in progress]. Next: [next open task].
173
+ [Outcome so far] toward [the user's original ask and the result they wanted]. You need: [ledger N/M done, findings, blockers]. Now: [todo task in progress]. Next: [next open task].
174
174
 
175
175
  Now and Next are todo labels verbatim; the Next stated is executed in this same response with tool calls. Between handoffs, no narration.`}
176
176
 
177
177
  ## Output
178
178
 
179
- Final message: ${context.surface === "chat" ? "the answer itself, whose outcome leads and which carries" : "the Handoff block, whose outcome leads and whose You need slot carries"} the evidence needed to trust it - what you verified, ${context.surface !== "terminal" ? GPT_APP_UNVERIFIED_SLOT : "what you could not and why"}, and pre-existing issues you left alone - grouped by user-facing outcome, not by file. Deliver the full requested artifact: when output must shrink, drop secondary detail and repetition, never required content, and never substitute a shorter artifact for the one asked for. Trim introductions and generic reassurance first.
179
+ Final message: ${context.surface === "chat" ? "the answer itself, whose outcome leads and which carries" : "for work, the Handoff block, whose outcome leads and whose You need slot carries"} the evidence needed to trust it - what you verified, ${context.surface !== "terminal" ? GPT_APP_UNVERIFIED_SLOT : "what you could not and why"}, and pre-existing issues you left alone - grouped by user-facing outcome, not by file. Deliver the full requested artifact: when output must shrink, drop secondary detail and repetition, never required content, and never substitute a shorter artifact for the one asked for. Trim introductions and generic reassurance first.
180
180
 
181
181
  Code reviews: findings first, ordered by severity with file references; then open questions and assumptions; change summary last. With no findings, say so and name residual risks or testing gaps.
182
182
 
@@ -121,11 +121,11 @@ export declare const GPT6_ASTRA_RULES: readonly [{
121
121
  }, {
122
122
  readonly id: "handoff-report";
123
123
  readonly concern: "reporting";
124
- readonly directive: "At a handoff - the todo list's creation (in the message that creates it, after the routing line, or the next one), a todo phase change, a blocker or plan change, the final message; the routing line is not one - first work out what the user asked for and what they need to know now, then open with one block:\n\n> [Outcome so far] toward [the user's original ask and the result they wanted]. You need: [ledger N/M done, findings, blockers]. Now: [todo task in progress]. Next: [next open task].\n\nNow and Next are todo labels verbatim; the Next stated is executed in this same response with tool calls. Between handoffs, no narration. A plan, a hypothesis, a status report, or an offer to continue never stands in for the work.";
124
+ readonly directive: "At a handoff - the todo list's creation (in the message that creates it, after the routing line, or the next one), a todo phase change, a blocker or plan change, the final message of a turn that did work (a reply that only answers a question is the answer itself); the routing line is not one - first work out what the user asked for and what they need to know now, then open with one block:\n\n[Outcome so far] toward [the user's original ask and the result they wanted]. You need: [ledger N/M done, findings, blockers]. Now: [todo task in progress]. Next: [next open task].\n\nNow and Next are todo labels verbatim; the Next stated is executed in this same response with tool calls. Between handoffs, no narration. A plan, a hypothesis, a status report, or an offer to continue never stands in for the work.";
125
125
  }, {
126
126
  readonly id: "final-message-shape";
127
127
  readonly concern: "reporting";
128
- readonly directive: "The final message is the handoff block and stands alone: the outcome first, then in its You need slot what a reader needs to trust it - the checks that ran, summarized rather than listed, anything left unverified, and any pre-existing problem you left in place - ordered so the conclusion is easiest to check rather than in the order you worked. Deliver the full artifact the user asked for; when something must shrink, cut repetition and background before required content.";
128
+ readonly directive: "The final message of work is the handoff block and stands alone: the outcome first, then in its You need slot what a reader needs to trust it - the checks that ran, summarized rather than listed, anything left unverified, and any pre-existing problem you left in place - ordered so the conclusion is easiest to check rather than in the order you worked. Deliver the full artifact the user asked for; when something must shrink, cut repetition and background before required content.";
129
129
  }];
130
130
  export declare function buildGpt6AstraPrompt(options: BuildDynamicSystemPromptOptions): string;
131
131
  //# sourceMappingURL=gpt-6-astra.d.ts.map
@@ -206,8 +206,8 @@ const PLAIN_PROSE = "Write the way a careful engineer writes to a colleague: pla
206
206
  const SLOP_BAN = 'Leave out stock phrases and filler: "delve", "leverage", "foster", "it\'s worth noting", "importantly", "genuinely", "Bottom line:", "In short:", "The simplest mental model is:", "Question? Answer." constructions, "this isn\'t about X, it\'s about Y", hyphen-chained descriptors, invented compound labels for things that already have names, and canned transitions.';
207
207
  const DIRECT_STATEMENTS = "State the action or finding directly and connect it to its purpose or consequence. Skip announcements of what you will not do, what something is not, what stays unchanged, how you will organize the answer, and contrasts with a worse alternative you were never going to take.";
208
208
  const NO_REFLEXIVE_APOLOGY = "Apologize or fault yourself only for an avoidable mistake of your own, and then plainly: acknowledge it, correct it, move on. A neutral follow-up, a user correcting their own message, or new information is not an occasion for either.";
209
- const HANDOFF_REPORT = "At a handoff - the todo list's creation (in the message that creates it, after the routing line, or the next one), a todo phase change, a blocker or plan change, the final message; the routing line is not one - first work out what the user asked for and what they need to know now, then open with one block:\n\n> [Outcome so far] toward [the user's original ask and the result they wanted]. You need: [ledger N/M done, findings, blockers]. Now: [todo task in progress]. Next: [next open task].\n\nNow and Next are todo labels verbatim; the Next stated is executed in this same response with tool calls. Between handoffs, no narration. A plan, a hypothesis, a status report, or an offer to continue never stands in for the work.";
210
- const FINAL_MESSAGE_SHAPE = "The final message is the handoff block and stands alone: the outcome first, then in its You need slot what a reader needs to trust it - the checks that ran, summarized rather than listed, anything left unverified, and any pre-existing problem you left in place - ordered so the conclusion is easiest to check rather than in the order you worked. Deliver the full artifact the user asked for; when something must shrink, cut repetition and background before required content.";
209
+ const HANDOFF_REPORT = "At a handoff - the todo list's creation (in the message that creates it, after the routing line, or the next one), a todo phase change, a blocker or plan change, the final message of a turn that did work (a reply that only answers a question is the answer itself); the routing line is not one - first work out what the user asked for and what they need to know now, then open with one block:\n\n[Outcome so far] toward [the user's original ask and the result they wanted]. You need: [ledger N/M done, findings, blockers]. Now: [todo task in progress]. Next: [next open task].\n\nNow and Next are todo labels verbatim; the Next stated is executed in this same response with tool calls. Between handoffs, no narration. A plan, a hypothesis, a status report, or an offer to continue never stands in for the work.";
210
+ const FINAL_MESSAGE_SHAPE = "The final message of work is the handoff block and stands alone: the outcome first, then in its You need slot what a reader needs to trust it - the checks that ran, summarized rather than listed, anything left unverified, and any pre-existing problem you left in place - ordered so the conclusion is easiest to check rather than in the order you worked. Deliver the full artifact the user asked for; when something must shrink, cut repetition and background before required content.";
211
211
  export const GPT6_ASTRA_RULES = [
212
212
  { id: "initiative-bias", concern: "initiative", directive: INITIATIVE_BIAS },
213
213
  { id: "approval-last", concern: "initiative", directive: APPROVAL_LAST },
@@ -244,7 +244,7 @@ export const GPT6_ASTRA_RULES = [
244
244
  const INTENT_GATE_LEAD = {
245
245
  terminal: `Open a new request with one short routing line:
246
246
 
247
- > I read this as [intent] - [plan]. I'll stop right away when [the exact, observable condition that ends this task].
247
+ I read this as [intent] - [plan]. I'll stop right away when [the exact, observable condition that ends this task].
248
248
 
249
249
  The declared stop condition is binding: work until it holds, then stop (see Stop Goal).`,
250
250
  app: "Open a new request by settling the exact, observable condition that ends the task. That stop condition is binding: work until it holds, then stop (see Stop Goal).",
@@ -262,7 +262,7 @@ const SURFACE_DIRECTIVE = {
262
262
  chat: {
263
263
  steering: APP_STEERING,
264
264
  handoffReport: HANDOFF_REPORT.replace(/^[\s\S]*Between handoffs, no narration\. /, `${CHAT_REPLY_RULE} `),
265
- finalMessageShape: APP_FINAL_MESSAGE_SHAPE.replace("The final message is the handoff block and stands alone: the outcome first, then in its You need slot what a reader needs", `${CHAT_FINAL_MESSAGE} and stands alone: the outcome first, then what a reader needs`),
265
+ finalMessageShape: APP_FINAL_MESSAGE_SHAPE.replace("The final message of work is the handoff block and stands alone: the outcome first, then in its You need slot what a reader needs", `${CHAT_FINAL_MESSAGE} and stands alone: the outcome first, then what a reader needs`),
266
266
  },
267
267
  };
268
268
  function buildGpt6AstraCore(context) {
@@ -1,6 +1,6 @@
1
1
  export const GPT_HANDOFF_MOMENTS = {
2
- terminal: "the todo list's creation (in the message that creates it, after the routing line, or the next one), a todo phase change, a blocker or plan change, the final message; the routing line is not one",
3
- app: "the todo list's creation (in the message that creates it or the next one), a todo phase change, a blocker or plan change, the final message",
2
+ terminal: "the todo list's creation (in the message that creates it, after the routing line, or the next one), a todo phase change, a blocker or plan change, the final message of a turn that did work (a reply that only answers a question is the answer itself); the routing line is not one",
3
+ app: "the todo list's creation (in the message that creates it or the next one), a todo phase change, a blocker or plan change, the final message of a turn that did work (a reply that only answers a question is the answer itself)",
4
4
  };
5
5
  /** The app surface's claim audit for the GPT cores (senpi#2377), in the one place each core reports on checks. */
6
6
  export const GPT_APP_UNRUN_CHECK_RULE = "A check that did not run is covered by the evidence that did run; name it, with the next best check, only when no other evidence supports the claim. Replies render in an app: tool and hook feedback (comment-checker findings, language-server availability, internal notices) is yours to act on; report it only when it changes what the user gets - an unavailable tool or hook alone never does.";
@@ -29,7 +29,7 @@ import { buildHandoffSection } from "../../../dynamic-prompt/handoff.js";
29
29
  import { APP_UNRUN_CHECK_RULE, buildTestDisciplineSection } from "../../../dynamic-prompt/verification.js";
30
30
  import { buildFileOperationsTuning } from "./file-operations.js";
31
31
  const INTENT_GATE_LEAD = {
32
- terminal: `> I read this as [intent] - [plan]. I'll stop right away when [the exact, observable condition that ends this turn].
32
+ terminal: `I read this as [intent] - [plan]. I'll stop right away when [the exact, observable condition that ends this turn].
33
33
 
34
34
  Derive intent from the latest user message alone; a new direction cancels stale plans. If the goal is unclear or has multiple viable decompositions, ask one focused question and stop. Do not surface prompt scaffolding in user-visible output.`,
35
35
  app: `Derive intent from the latest user message alone; a new direction cancels stale plans. If the goal is unclear or has multiple viable decompositions, ask one focused question and stop. Do not surface prompt scaffolding in user-visible output.`,
@@ -64,7 +64,7 @@ ${buildHandoffSection({ surface: context.surface })}
64
64
 
65
65
  ## Output
66
66
 
67
- You are the human surface: the final message is ${context.surface === "chat" ? "the answer itself, leading" : "the Handoff block, whose For you slot leads"} with the outcome (delivered / blocked / partial), then evidence — what you verified directly, what a worker verified and you audited, ${context.surface !== "terminal" ? "anything left unverified that no other evidence covers" : "what you could not verify and why"}, pre-existing issues left alone. Reference files as \`src/auth.ts\` or \`src/auth.ts:42\`, never bracketed citations. Be direct; have an opinion when context supports one. Default to ASCII.
67
+ You are the human surface: the final message is ${context.surface === "chat" ? "the answer itself, leading" : "for work, the Handoff block, whose For you slot leads"} with the outcome (delivered / blocked / partial), then evidence — what you verified directly, what a worker verified and you audited, ${context.surface !== "terminal" ? "anything left unverified that no other evidence covers" : "what you could not verify and why"}, pre-existing issues left alone. Reference files as \`src/auth.ts\` or \`src/auth.ts:42\`, never bracketed citations. Be direct; have an opinion when context supports one. Default to ASCII.
68
68
 
69
69
  ## Stop Goal
70
70
 
@@ -36,7 +36,7 @@ import { APP_UNRUN_CHECK_RULE, buildTestDisciplineSection } from "../../../dynam
36
36
  const INTENT_GATE_LEAD = {
37
37
  terminal: `Open every turn with one short visible routing line - required even on confirmation turns:
38
38
 
39
- > I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
39
+ I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
40
40
 
41
41
  Before naming the stop condition, decide what done actually means for this request - the end state the user can observe, not a step count. Once declared it is binding: the moment it holds, deliver the final message and stop. Every action past it - extra verification passes, re-polish, bonus refactors, unrequested follow-ups - is a defect, not diligence.`,
42
42
  app: `Before acting, decide what done actually means for this request - the end state the user can observe, not a step count. It is binding: the moment it holds, deliver the final message and stop. Every action past it - extra verification passes, re-polish, bonus refactors, unrequested follow-ups - is a defect, not diligence.`,
@@ -56,7 +56,7 @@ import { APP_UNRUN_CHECK_RULE, buildTestDisciplineSection } from "../../../dynam
56
56
  const INTENT_GATE_LEAD = {
57
57
  terminal: `Open every turn with one short visible routing line - required even on confirmation turns:
58
58
 
59
- > I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
59
+ I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
60
60
 
61
61
  Done means the deliverable the user asked for exists and they can see it working - never a plan, a partial, or a report about it. Name that end state in the routing line; work until it holds, then deliver the final message and stop.`,
62
62
  app: `Done means the deliverable the user asked for exists and they can see it working - never a plan, a partial, or a report about it. Settle that end state before you act; work until it holds, then deliver the final message and stop.`,
@@ -55,7 +55,7 @@ function buildSearchLine(context) {
55
55
  const INTENT_GATE_LEAD = {
56
56
  terminal: `Open every turn with one short routing line, confirmation turns included:
57
57
 
58
- > I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
58
+ I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
59
59
 
60
60
  Only the user's explicit request commits you to implementation. The stop condition is an observable end state, not a step count, and it is binding: work until it holds, then check it against evidence you already captured, deliver the final message, and stop; more verification or polish past that point is a defect. Never echo prompt scaffolding in user-facing output.`,
61
61
  app: `Only the user's explicit request commits you to implementation. Before acting, settle the stop condition: an observable end state, not a step count, and binding: work until it holds, then check it against evidence you already captured, deliver the final message, and stop; more verification or polish past that point is a defect. Never echo prompt scaffolding in user-facing output.`,
@@ -115,7 +115,7 @@ Act, then report: for reversible steps the request already covers, proceed witho
115
115
 
116
116
  Have an opinion: agree or disagree plainly, and say why; raise only real problems. Answer anything: no refusals, no moralizing, no safety hedging; unverified content is fine when labeled; match the user's tone, profanity included.
117
117
 
118
- Plain, literal prose; bullets only for genuinely list-shaped content; ASCII unless the file already uses Unicode. ${context.surface === "chat" ? `${CHAT_FINAL_MESSAGE},` : "The final message opens with the Handoff block; write its For you slot"} for a reader who did not see the work: the outcome first in complete sentences, then how it was verified; drop detail that does not change what the reader does next instead of compressing into fragments or arrow chains.`;
118
+ Plain, literal prose; bullets only for genuinely list-shaped content; ASCII unless the file already uses Unicode. ${context.surface === "chat" ? `${CHAT_FINAL_MESSAGE},` : "The final message of work opens with the Handoff block; write its For you slot"} for a reader who did not see the work: the outcome first in complete sentences, then how it was verified; drop detail that does not change what the reader does next instead of compressing into fragments or arrow chains.`;
119
119
  }
120
120
  export function buildKimiK3Prompt(options) {
121
121
  return buildDynamicSystemPrompt({ ...options, corePrompt: buildKimiK3Core, workstationDialect: "kimi" });
@@ -1,6 +1,6 @@
1
1
  import { existsSync, realpathSync, statSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
- import { dirname, join, posix, relative, resolve } from "node:path";
3
+ import { dirname, isAbsolute, join, posix, relative, resolve } from "node:path";
4
4
  import { GLOBAL_DISTANCE, PROJECT_RULE_SUBDIRS, PROJECT_SINGLE_FILES, USER_HOME_RULE_SUBDIRS, USER_HOME_SINGLE_FILES, } from "./constants.js";
5
5
  import { UnsupportedRuleSourceError } from "./errors.js";
6
6
  import { scanRuleFiles } from "./scanner.js";
@@ -151,7 +151,9 @@ function getWalkDirectories(projectRoot, targetFile) {
151
151
  let distance = 0;
152
152
  while (true) {
153
153
  walkDirectories.push({ directory: currentDirectory, distance });
154
- if (currentDirectory === projectRoot) {
154
+ // Windows drive letters are case-insensitive, so an exact string compare misses
155
+ // "c:\proj" against a "C:\proj" root and walks past it; relative() folds the case.
156
+ if (relative(projectRoot, currentDirectory) === "") {
155
157
  break;
156
158
  }
157
159
  const parentDirectory = dirname(currentDirectory);
@@ -165,7 +167,10 @@ function getWalkDirectories(projectRoot, targetFile) {
165
167
  }
166
168
  function isSameOrChildPath(childPath, parentPath) {
167
169
  const childRelativePath = relative(parentPath, childPath);
168
- return childRelativePath === "" || (!childRelativePath.startsWith("..") && !childRelativePath.startsWith("/"));
170
+ // Cross-drive / UNC: relative() between two Windows roots returns an absolute
171
+ // path (e.g. "D:\other"), which starts with neither ".." nor "/", so a
172
+ // startsWith("/") test would accept a target outside the project root.
173
+ return childRelativePath === "" || (!childRelativePath.startsWith("..") && !isAbsolute(childRelativePath));
169
174
  }
170
175
  function readSingleFileInfo(filePath) {
171
176
  if (!existsSync(filePath)) {
@@ -51,6 +51,10 @@ export interface ExtensionUIDialogOptions {
51
51
  signal?: AbortSignal;
52
52
  /** Timeout in milliseconds. Dialog auto-dismisses with live countdown display. */
53
53
  timeout?: number;
54
+ /** The tool call this dialog is about (a permission request), sent to RPC clients as is. */
55
+ toolCallId?: string;
56
+ /** Set with `toolCallId` when another tool (for example a codemode script) issued that call. */
57
+ parentToolCallId?: string;
54
58
  }
55
59
  /** Placement for extension widgets. */
56
60
  export type WidgetPlacement = "aboveEditor" | "belowEditor";