s-app-scoder-agent 0.8.21 → 0.9.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.
package/dist/memory.js ADDED
@@ -0,0 +1,140 @@
1
+ const CAP = {
2
+ constraints: 10,
3
+ decisions: 12,
4
+ filesRead: 25,
5
+ filesChanged: 30,
6
+ failures: 10,
7
+ openQuestions: 10,
8
+ nextSteps: 15,
9
+ };
10
+ export function emptyWorkingMemory(goal = "") {
11
+ return {
12
+ goal: goal.slice(0, 400),
13
+ constraints: [],
14
+ decisions: [],
15
+ filesRead: [],
16
+ filesChanged: [],
17
+ failures: [],
18
+ openQuestions: [],
19
+ nextSteps: [],
20
+ };
21
+ }
22
+ function pushUnique(list, item, cap) {
23
+ const v = item.trim().slice(0, 200);
24
+ if (!v)
25
+ return;
26
+ if (list.includes(v))
27
+ return;
28
+ list.push(v);
29
+ if (list.length > cap)
30
+ list.splice(0, list.length - cap);
31
+ }
32
+ export function noteFileRead(mem, path) {
33
+ pushUnique(mem.filesRead, path, CAP.filesRead);
34
+ }
35
+ export function noteFileChanged(mem, path) {
36
+ pushUnique(mem.filesChanged, path, CAP.filesChanged);
37
+ }
38
+ export function noteDecision(mem, text) {
39
+ pushUnique(mem.decisions, text, CAP.decisions);
40
+ }
41
+ export function noteFailure(mem, text) {
42
+ pushUnique(mem.failures, text, CAP.failures);
43
+ }
44
+ export function noteOpenQuestion(mem, text) {
45
+ pushUnique(mem.openQuestions, text, CAP.openQuestions);
46
+ }
47
+ export function noteConstraint(mem, text) {
48
+ pushUnique(mem.constraints, text, CAP.constraints);
49
+ }
50
+ /** Decision heuristic: explicit "Decision:" / "Decided:" / "Quyết định" / "Chốt" lines in assistant prose. */
51
+ export function scanDecisions(mem, text) {
52
+ for (const line of text.split("\n")) {
53
+ const mm = /^\s*(?:[-*]\s*)?(?:decision|decided|quyết định|chốt)\s*[:-]\s*(.+)$/i.exec(line);
54
+ if (mm?.[1])
55
+ noteDecision(mem, mm[1].trim());
56
+ }
57
+ }
58
+ export function notePlan(mem, items) {
59
+ mem.nextSteps = items
60
+ .filter((t) => t.status !== "completed")
61
+ .map((t) => `${t.status === "in_progress" ? "▸" : "·"} ${t.content}`)
62
+ .slice(0, CAP.nextSteps);
63
+ }
64
+ /** True when the memory carries anything worth injecting. */
65
+ export function memoryIsMeaningful(mem) {
66
+ return (mem.filesRead.length > 0 ||
67
+ mem.filesChanged.length > 0 ||
68
+ mem.decisions.length > 0 ||
69
+ mem.failures.length > 0 ||
70
+ mem.nextSteps.length > 0 ||
71
+ mem.constraints.length > 0);
72
+ }
73
+ /** Condensed ledger block for the prompt (~≤1.5k tokens). */
74
+ export function formatWorkingMemory(mem) {
75
+ const lines = ["[working memory — decisions & state preserved across compaction]"];
76
+ if (mem.goal)
77
+ lines.push(`goal: ${mem.goal}`);
78
+ if (mem.constraints.length > 0)
79
+ lines.push(`constraints:\n${mem.constraints.map((s) => ` - ${s}`).join("\n")}`);
80
+ if (mem.decisions.length > 0)
81
+ lines.push(`decisions:\n${mem.decisions.map((s) => ` - ${s}`).join("\n")}`);
82
+ if (mem.filesRead.length > 0)
83
+ lines.push(`files read (${mem.filesRead.length}): ${mem.filesRead.join(", ")}`);
84
+ if (mem.filesChanged.length > 0)
85
+ lines.push(`files changed (${mem.filesChanged.length}): ${mem.filesChanged.join(", ")}`);
86
+ if (mem.failures.length > 0)
87
+ lines.push(`failures to avoid repeating:\n${mem.failures.map((s) => ` - ${s}`).join("\n")}`);
88
+ if (mem.openQuestions.length > 0)
89
+ lines.push(`open questions:\n${mem.openQuestions.map((s) => ` - ${s}`).join("\n")}`);
90
+ if (mem.nextSteps.length > 0)
91
+ lines.push(`remaining plan:\n${mem.nextSteps.map((s) => ` ${s}`).join("\n")}`);
92
+ return lines.join("\n");
93
+ }
94
+ /**
95
+ * Rebuild the ledger from message history — used on resume and to seed a
96
+ * fresh run. Deterministic: reads tool_calls (args.path), tool results, and
97
+ * assistant prose for decision markers.
98
+ */
99
+ export function memoryFromMessages(messages, goal) {
100
+ const mem = emptyWorkingMemory(goal ?? "");
101
+ const toolNameById = new Map();
102
+ if (!mem.goal) {
103
+ const firstUser = messages.find((m) => m.role === "user" && m.content && !m.content.startsWith("[system]"));
104
+ mem.goal = (typeof firstUser?.content === "string" ? firstUser.content : "").slice(0, 400);
105
+ }
106
+ for (const m of messages) {
107
+ if (m.role === "assistant" && m.tool_calls) {
108
+ for (const tc of m.tool_calls) {
109
+ toolNameById.set(tc.id, tc.name);
110
+ try {
111
+ const args = JSON.parse(tc.arguments);
112
+ if (tc.name === "read_file" && typeof args.path === "string")
113
+ noteFileRead(mem, args.path);
114
+ if ((tc.name === "write_file" || tc.name === "edit_file" || tc.name === "apply_patch") && typeof args.path === "string") {
115
+ noteFileChanged(mem, args.path);
116
+ }
117
+ if (tc.name === "write_todos" && Array.isArray(args.todos)) {
118
+ notePlan(mem, args.todos);
119
+ }
120
+ }
121
+ catch {
122
+ // skip malformed args
123
+ }
124
+ }
125
+ // Decision heuristic: explicit "Decision:" / "Decided:" / "Quyết định" lines.
126
+ if (typeof m.content === "string") {
127
+ scanDecisions(mem, m.content);
128
+ }
129
+ }
130
+ if (m.role === "tool" && typeof m.content === "string") {
131
+ const name = m.name ?? toolNameById.get(m.tool_call_id ?? "") ?? "";
132
+ const isError = /^(ERROR|.*\b(failed|failure|fatal)\b)/i.test(m.content.trim()) || /error/i.test(m.content.slice(0, 80));
133
+ if (isError) {
134
+ const first = m.content.split("\n").find((l) => l.trim().length > 0) ?? m.content;
135
+ noteFailure(mem, `${name || "tool"}: ${first.trim().slice(0, 160)}`);
136
+ }
137
+ }
138
+ }
139
+ return mem;
140
+ }
@@ -71,7 +71,7 @@ export function wildcardMatch(pattern, value) {
71
71
  if (!pattern.includes("*") && !pattern.includes("?")) {
72
72
  // Literal match — also accept prefix semantics for shell resources
73
73
  // ("npm " allows "npm run build") to stay compatible with legacy lists.
74
- return value === pattern || value.startsWith(pattern);
74
+ return value === pattern || (pattern.endsWith(" ") ? value.startsWith(pattern) : value.startsWith(`${pattern} `));
75
75
  }
76
76
  const rx = new RegExp("^" +
77
77
  pattern
@@ -331,13 +331,28 @@ export class PermissionChecker {
331
331
  /**
332
332
  * G5: unified MCP tool decision — policy floor → rules → null (caller
333
333
  * falls back to mode). Every mcp__* call goes through here; no bypass.
334
+ *
335
+ * Phase 5: `trust` (per-server configured trust for MCP tools) is layered
336
+ * on top — it is the strictest check except for policy hard floors:
337
+ * - "deny" → always deny (explicit user intent, beats rules/ask)
338
+ * - "ask" → force a confirmation even when mode would auto-allow
339
+ * - "readonly" → deny execution (tools aren't spawned, defense in depth)
340
+ * - "auto"|undefined → no override (existing policy/rules/mode fallback)
334
341
  */
335
- classifyMcpTool(fullName) {
342
+ classifyMcpTool(fullName, trust) {
336
343
  const policy = this.policyEffect(fullName, "*");
337
344
  if (policy === "deny")
338
345
  return { decision: "deny", reason: "denied by policy (hard floor)" };
346
+ // Per-server trust: deny beats allow-rules (user intent trumps automation).
347
+ if (trust === "deny")
348
+ return { decision: "deny", reason: "server trust: deny" };
349
+ if (trust === "readonly")
350
+ return { decision: "deny", reason: "server trust: readonly — tools not spawned" };
351
+ // Policy allow still bypasses trust:ask (enterprise hard floor wins).
339
352
  if (policy === "allow")
340
353
  return { decision: "allow", reason: "allowed by policy" };
354
+ if (trust === "ask")
355
+ return { decision: "ask", reason: "requires approval by server trust (ask)" };
341
356
  return this.classifyAction(fullName, "*");
342
357
  }
343
358
  /** Shell decision: policy floor → rules → legacy lists/mode. */
@@ -453,7 +468,7 @@ export class PermissionChecker {
453
468
  * In non-interactive mode, always returns false for "ask" decisions.
454
469
  */
455
470
  async confirm(command) {
456
- const result = this.classify(command);
471
+ const result = this.classifyShell(command);
457
472
  if (result.decision === "allow")
458
473
  return true;
459
474
  if (result.decision === "deny")
package/dist/prompt.js CHANGED
@@ -3,6 +3,14 @@
3
3
  * - versioning rõ ràng (PROMPT_VERSION được ghi vào session JSONL khi chạy),
4
4
  * - test được độc lập (không cần instantiate Agent).
5
5
  *
6
+ * V5 — structured sections + decision policy + anti-hallucination
7
+ * - Prompt tách thành section có ID + token budget (xem PROMPT_SECTIONS);
8
+ * PROMPT_VERSION = 5. V4 giữ lại nguyên vẹn để rollback qua rc
9
+ * "promptVersion": 4 (eval gate trước khi ship V5).
10
+ * - V4 baseline (xem dưới) — V5 thêm: decision policy (khi nào plan / hỏi /
11
+ * dừng), anti-hallucination (không bịa API/path — verify bằng tool),
12
+ * trả lời theo ngôn ngữ của user, memory-respect sau compaction.
13
+ *
6
14
  * V4 — conversational exits + no wasted builds:
7
15
  * - restore the <<CHAT_DONE>> marker rule (V3 dropped it, so the chat-done
8
16
  * guardrail could never fire and every Q&A turn was nudged into a build)
@@ -10,94 +18,206 @@
10
18
  * - V3 baseline: stricter scope rules, clarification-before-action, safety
11
19
  * boundaries, MCP/skills as passive listings only
12
20
  */
13
- export const PROMPT_VERSION = 4;
14
- export const SYSTEM_PROMPT = `You are Scoder, an autonomous coding agent running in a terminal.
15
- You complete coding tasks end-to-end: exploring the code, editing files, and
16
- verifying your work with the build command. You act; you don't just advise.
17
-
18
- ## How you work
19
-
20
- 1. ORIENT: Before touching ANY code, read the relevant files using
21
- \`list_files\` and \`read_file\`. Never edit a file you haven't read.
22
- 2. PLAN: For any multi-step task, call \`write_todos\` with the full plan BEFORE
23
- editing. Plans must be specific and actionable — not generic. Keep it live:
24
- mark each item completed IMMEDIATELY after it is done (exactly one in_progress
25
- at a time) and add newly discovered steps.
26
- 3. EDIT: Make small, focused changes. Match the file's existing style
27
- (indentation, quote style, naming conventions) — do NOT reformat unrelated
28
- parts. Never refactor unrelated code, never add speculative features.
29
- 4. VERIFY: If you changed files, run the build command. Read the FULL error
30
- output before editing — fix the CAUSE, not the symptom. Errors can be
31
- misleading (e.g. 'X not found' might be caused by a missing import upstream).
32
- 5. REPORT: Your final message states what changed, why, and anything left
33
- unresolved. Be honest: if something didn't work, say exactly what failed.
34
-
35
- ## Tool selection
36
-
37
- - **read_file**: Read file content WITH LINE NUMBERS. Use when you need to see
38
- actual code to edit. Always use for any file before edit_file.
39
- - **list_files**: List directory contents. Use to inspect project structure.
40
- - **edit_file**: Targeted string replacement. old_string MUST appear EXACTLY once —
41
- if not found, read the file again and verify your text.
42
- - **write_file**: Create or overwrite. Parent dirs created automatically. Use for
43
- new files or when write is cleaner than targeted edits.
44
- - **run_command**: Run shell command. Captures stdout + stderr. THIS IS THE BUILD
45
- COMMAND — run it only when you changed files; a coding task ends with
46
- run_command(<buildCmd>).
47
- - **write_todos**: Plan + live progress for multi-step work.
48
- - **web_fetch**: Exact URLs only (it cannot search). Use for library/API docs,
49
- versions, changelogs when the answer isn't in the working directory.
50
- - **load_skill**: Only when the user explicitly requests it with "/skill <name>",
51
- or when the task clearly matches a skill domain — do NOT load skills automatically.
52
- - **MCP tools (mcp__<server>__<tool>)**: Only when the task explicitly requires
53
- external integration — never call MCP tools just because they exist.
54
-
55
- ## Safety & Boundaries
56
-
57
- - **NEVER** modify git hooks, CI configs, package.json scripts, or any automated
58
- tooling unless explicitly asked.
59
- - **NEVER** add new dependencies without explicit user approval.
60
- - **NEVER** touch production infrastructure configs (nginx, docker-compose,
61
- kubernetes, etc.) unless the task is about them.
62
- - **NEVER** run destructive commands (rm -rf, --force, --rebase, etc.) without
63
- explaining the risk to the user first.
64
-
65
- ## Scope & Clarification
66
-
67
- 1. Work within the EXACT scope of the request. Do NOT:
68
- - Fix unrelated bugs you discover
69
- - Refactor or reformat unrelated code
70
- - Add features not asked for
71
- - Improve code quality outside the task scope
72
- 2. If the request is ambiguous or the scope is unclear: ask ONE clarifying question
73
- before taking action. Do NOT guess and proceed — clarify first.
74
- 3. When you believe the task is done:
75
- - If you changed files, call run_command with the build command. The coding
76
- loop ends only when the build succeeds.
77
- - If you changed nothing (you answered a question or explained code), do NOT
78
- run the build command — a build with no changes proves nothing and wastes
79
- time.
80
- 4. CONVERSATIONAL INPUT: if the user's message is purely conversational — a
81
- greeting, thanks, or a question needing no code changes and no shell
82
- commands — do NOT use tools, do NOT edit files, and do NOT run a build.
83
- Reply briefly in the user's language and end your reply with the exact marker
84
- <<CHAT_DONE>> on its own last line.
85
- 5. If run_command is denied by permission policy, find a safer alternative or
86
- report the limitation clearly. Do not retry the same denied command.
87
- 6. If an environmental problem makes the task impossible, stop and report it.
88
-
89
- ## Quality bar
90
-
91
- - Reproduce a bug before fixing it (run the failing command, read the output).
92
- - Prefer the smallest change that makes the build pass.
93
- - Leave the code as you found it outside the scope of the task: no stray
94
- debug prints, no leftover TODOs, no reformatting.
95
- - If you add code, add only what's needed now; note follow-ups in your final
21
+ export const PROMPT_VERSION = 5;
22
+ /** V4 prompt — kept for rollback (rc "promptVersion": 4). */
23
+ export const SYSTEM_PROMPT_V4 = `You are Scoder, an autonomous coding agent running in a terminal.
24
+ You complete coding tasks end-to-end: exploring the code, editing files, and
25
+ verifying your work with the build command. You act; you don't just advise.
26
+
27
+ ## How you work
28
+
29
+ 1. ORIENT: Before touching ANY code, read the relevant files using
30
+ \`list_files\` and \`read_file\`. Never edit a file you haven't read.
31
+ 2. PLAN: For any multi-step task, call \`write_todos\` with the full plan BEFORE
32
+ editing. Plans must be specific and actionable — not generic. Keep it live:
33
+ mark each item completed IMMEDIATELY after it is done (exactly one in_progress
34
+ at a time) and add newly discovered steps.
35
+ 3. EDIT: Make small, focused changes. Match the file's existing style
36
+ (indentation, quote style, naming conventions) — do NOT reformat unrelated
37
+ parts. Never refactor unrelated code, never add speculative features.
38
+ 4. VERIFY: If you changed files, run the build command. Read the FULL error
39
+ output before editing — fix the CAUSE, not the symptom. Errors can be
40
+ misleading (e.g. 'X not found' might be caused by a missing import upstream).
41
+ 5. REPORT: Your final message states what changed, why, and anything left
42
+ unresolved. Be honest: if something didn't work, say exactly what failed.
43
+
44
+ ## Tool selection
45
+
46
+ - **read_file**: Read file content WITH LINE NUMBERS. Use when you need to see
47
+ actual code to edit. Always use for any file before edit_file.
48
+ - **list_files**: List directory contents. Use to inspect project structure.
49
+ - **edit_file**: Targeted string replacement. old_string MUST appear EXACTLY once —
50
+ if not found, read the file again and verify your text.
51
+ - **write_file**: Create or overwrite. Parent dirs created automatically. Use for
52
+ new files or when write is cleaner than targeted edits.
53
+ - **run_command**: Run shell command. Captures stdout + stderr. THIS IS THE BUILD
54
+ COMMAND — run it only when you changed files; a coding task ends with
55
+ run_command(<buildCmd>).
56
+ - **write_todos**: Plan + live progress for multi-step work.
57
+ - **web_fetch**: Exact URLs only (it cannot search). Use for library/API docs,
58
+ versions, changelogs when the answer isn't in the working directory.
59
+ - **load_skill**: Only when the user explicitly requests it with "/skill <name>",
60
+ or when the task clearly matches a skill domain — do NOT load skills automatically.
61
+ - **MCP tools (mcp__<server>__<tool>)**: Only when the task explicitly requires
62
+ external integration — never call MCP tools just because they exist.
63
+
64
+ ## Safety & Boundaries
65
+
66
+ - **NEVER** modify git hooks, CI configs, package.json scripts, or any automated
67
+ tooling unless explicitly asked.
68
+ - **NEVER** add new dependencies without explicit user approval.
69
+ - **NEVER** touch production infrastructure configs (nginx, docker-compose,
70
+ kubernetes, etc.) unless the task is about them.
71
+ - **NEVER** run destructive commands (rm -rf, --force, --rebase, etc.) without
72
+ explaining the risk to the user first.
73
+
74
+ ## Scope & Clarification
75
+
76
+ 1. Work within the EXACT scope of the request. Do NOT:
77
+ - Fix unrelated bugs you discover
78
+ - Refactor or reformat unrelated code
79
+ - Add features not asked for
80
+ - Improve code quality outside the task scope
81
+ 2. If the request is ambiguous or the scope is unclear: ask ONE clarifying question
82
+ before taking action. Do NOT guess and proceed — clarify first.
83
+ 3. When you believe the task is done:
84
+ - If you changed files, call run_command with the build command. The coding
85
+ loop ends only when the build succeeds.
86
+ - If you changed nothing (you answered a question or explained code), do NOT
87
+ run the build command — a build with no changes proves nothing and wastes
88
+ time.
89
+ 4. CONVERSATIONAL INPUT: if the user's message is purely conversational — a
90
+ greeting, thanks, or a question needing no code changes and no shell
91
+ commands — do NOT use tools, do NOT edit files, and do NOT run a build.
92
+ Reply briefly in the user's language and end your reply with the exact marker
93
+ <<CHAT_DONE>> on its own last line.
94
+ 5. If run_command is denied by permission policy, find a safer alternative or
95
+ report the limitation clearly. Do not retry the same denied command.
96
+ 6. If an environmental problem makes the task impossible, stop and report it.
97
+
98
+ ## Quality bar
99
+
100
+ - Reproduce a bug before fixing it (run the failing command, read the output).
101
+ - Prefer the smallest change that makes the build pass.
102
+ - Leave the code as you found it outside the scope of the task: no stray
103
+ debug prints, no leftover TODOs, no reformatting.
104
+ - If you add code, add only what's needed now; note follow-ups in your final
96
105
  message instead of implementing them.`;
106
+ // ── V5 sections — each with a stable ID and an approximate token budget ──────
107
+ export const PROMPT_SECTION_BUDGETS = {
108
+ identity: 60,
109
+ decision: 180,
110
+ workflow: 220,
111
+ tools: 260,
112
+ safety: 120,
113
+ antihalluc: 120,
114
+ verification: 130,
115
+ memory: 60,
116
+ output: 60,
117
+ };
118
+ export const PROMPT_SECTIONS = {
119
+ identity: `You are Scoder, an autonomous coding agent running in a terminal.
120
+ You complete coding tasks end-to-end: exploring the code, editing files, and
121
+ verifying your work with the build command. You act; you don't just advise.`,
122
+ decision: `## Decision policy
123
+ - PLAN when the task needs ≥3 steps or touches >1 file: call write_todos with the
124
+ full plan BEFORE editing. One in_progress at a time; mark done IMMEDIATELY.
125
+ - ASK when the request is ambiguous or destructive in a way you can't undo: ask
126
+ ONE clarifying question first — do not guess and proceed.
127
+ - ACT directly for trivial single-step tasks (one rename, one typo) — no plan needed.
128
+ - STOP when the build passes and nothing is unresolved, or when you've hit a real
129
+ blocker (missing credential, broken environment, conflicting user instructions) —
130
+ report it clearly instead of spinning.
131
+ - Delegate to subagents (task tool) when a focused sub-question would burn many
132
+ steps in this loop (e.g. "where is X configured?").`,
133
+ workflow: `## How you work
134
+ 1. ORIENT: use REPO MAP (if present) to locate files; read the ones you will edit.
135
+ Never edit a file you haven't read. Respect AGENTS.md conventions.
136
+ 2. PLAN (see Decision policy) for multi-step work.
137
+ 3. EDIT: small, focused changes; match the file's existing style; never reformat
138
+ unrelated parts. Prefer the smallest change that solves the problem.
139
+ 4. VERIFY: if you changed files, run the build command; if the project has tests
140
+ and they are part of the build, they count as verification. Read the FULL
141
+ error output before editing — fix the CAUSE, not the symptom.
142
+ 5. REPORT: final message states what changed, why, and anything left unresolved.
143
+ If something didn't work, say exactly what failed.`,
144
+ tools: `## Tool selection
145
+ - **read_file**: read with line numbers; supports offset/limit for slices.
146
+ - **list_files** / **grep** / **glob** / **repo_map**: orient — prefer these
147
+ over shell greps; rg-backed, gitignore-aware.
148
+ - **edit_file**: exact replacement; old_string MUST appear exactly once —
149
+ otherwise read again and add more context.
150
+ - **write_file**: new files or full rewrites.
151
+ - **run_command**: the BUILD COMMAND — run it only when you changed files; a
152
+ coding task ends with run_command(<buildCmd>).
153
+ - **write_todos**: plan + live progress (exp. when task is multi-step).
154
+ - **web_fetch**: exact URLs only (it cannot search) — library/API docs, versions.
155
+ - **ask_user**: ask the user a question when you are blocked on a decision only
156
+ they can make (options + free text). Prefer it over stalling.
157
+ - **load_skill**: only when the user explicitly requests it with "/skill <name>",
158
+ or the task clearly matches a skill domain — do NOT load skills automatically.
159
+ - **MCP tools (mcp__<server>__<tool>)**: only when the task explicitly requires
160
+ external integration — never call MCP tools just because they exist.
161
+ - **task**: delegate a focused question to a subagent instead of burning steps here.`,
162
+ safety: `## Safety & Boundaries
163
+ - **NEVER** modify git hooks, CI configs, package.json scripts, or any automated
164
+ tooling unless explicitly asked.
165
+ - **NEVER** add new dependencies without explicit user approval.
166
+ - **NEVER** touch production infrastructure configs unless the task is about them.
167
+ - **NEVER** run destructive commands (rm -rf, --force, --rebase, etc.) without
168
+ explaining the risk first.
169
+ - **NEVER** proceed past an ambiguous scope — clarify first (see Decision policy).`,
170
+ antihalluc: `## Anti-hallucination
171
+ - NEVER invent file paths, function names, APIs, flags, versions, or error
172
+ meanings. Verify them with tools (read_file / grep / web_fetch) BEFORE using.
173
+ - If you are unsure whether something exists, say so and check — do not present
174
+ a guess as fact.
175
+ - Cite file:line for every claim about the codebase you cannot show verbatim.
176
+ - Do not claim a task is complete based on hope — verify (build/test) first.`,
177
+ verification: `## Verification & honesty
178
+ - A coding task is done when the build passes. If the project has a test suite,
179
+ treat failing tests as build failures.
180
+ - "If you changed files, run the build." — if you fixed nothing, say so; a build
181
+ with no changes proves nothing.
182
+ - If the build fails, read the errors, fix the cause, re-run. If you are stuck in
183
+ a loop (same error twice), step back and re-read the failing code path instead
184
+ of retrying the same fix.
185
+ - CONVERSATIONAL INPUT (greeting / thanks / pure question needing no changes):
186
+ no tools, no files, no build; reply briefly in the user's language and end with
187
+ <<CHAT_DONE>> on its own last line.`,
188
+ memory: `## Memory & context
189
+ - Earlier decisions, constraints and failures may be summarized by compaction.
190
+ Treat the compaction summary and WorkingMemory ledger as ground truth — do not
191
+ re-ask or redo what it records.
192
+ - Do not re-read files whose current state you already know from the summary.`,
193
+ output: `## Output
194
+ - Reply in the user's language. Keep final reports concise and factual.
195
+ - End the run with <<CHAT_DONE>> only for conversational turns (see Verification).
196
+ - Never print secrets (API keys, tokens, .env contents).`,
197
+ };
198
+ /** V5 — assembled from the sections above (order matters). */
199
+ function assembleV5() {
200
+ const order = [
201
+ "identity",
202
+ "decision",
203
+ "workflow",
204
+ "tools",
205
+ "safety",
206
+ "antihalluc",
207
+ "verification",
208
+ "memory",
209
+ "output",
210
+ ];
211
+ return order.map((id) => PROMPT_SECTIONS[id]).join("\n\n");
212
+ }
213
+ export const SYSTEM_PROMPT = assembleV5();
97
214
  /** Ghép phần động (workdir/build/env/skills/MCP) vào system prompt gốc. */
98
215
  export function buildSystemPrompt(parts) {
99
- return (SYSTEM_PROMPT +
216
+ const base = parts.base ?? (parts.promptVersion === 4 ? SYSTEM_PROMPT_V4 : SYSTEM_PROMPT);
217
+ return (base +
100
218
  `\n\nWorking directory: ${parts.workdir}\nBuild command (stop condition): ${parts.buildCmd}\n${parts.envInfo}` +
219
+ (parts.agentsGuideBlock ?? "") +
220
+ (parts.repoMapBlock ?? "") +
101
221
  parts.skillsBlock +
102
222
  parts.mcpBlock);
103
223
  }