@kawacode/mcp 6.9.1 → 6.9.4
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/README.md +17 -1
- package/build/index.js +22 -2
- package/build/index.js.map +1 -1
- package/build/kawacode-on-pre-edit.d.ts +6 -1
- package/build/kawacode-on-pre-edit.js +27 -17
- package/build/kawacode-on-pre-edit.js.map +1 -1
- package/build/pre-edit/acked-decisions.d.ts +30 -0
- package/build/pre-edit/acked-decisions.js +65 -0
- package/build/pre-edit/acked-decisions.js.map +1 -0
- package/build/pre-edit/acked-decisions.test.d.ts +1 -0
- package/build/pre-edit/acked-decisions.test.js +59 -0
- package/build/pre-edit/acked-decisions.test.js.map +1 -0
- package/build/tools/_salvage-mangled-args.d.ts +38 -0
- package/build/tools/_salvage-mangled-args.js +87 -0
- package/build/tools/_salvage-mangled-args.js.map +1 -0
- package/build/tools/_salvage-mangled-args.test.d.ts +1 -0
- package/build/tools/_salvage-mangled-args.test.js +73 -0
- package/build/tools/_salvage-mangled-args.test.js.map +1 -0
- package/build/tools/complete-intent.d.ts +4 -4
- package/build/tools/complete-intent.js +14 -4
- package/build/tools/complete-intent.js.map +1 -1
- package/build/tools/edit-session-decision.d.ts +13 -12
- package/build/tools/edit-session-decision.js +14 -1
- package/build/tools/edit-session-decision.js.map +1 -1
- package/build/tools/index.d.ts +130 -89
- package/build/tools/index.js +3 -1
- package/build/tools/index.js.map +1 -1
- package/build/tools/list-team-intents.d.ts +4 -4
- package/build/tools/log-work.js +13 -2
- package/build/tools/log-work.js.map +1 -1
- package/build/tools/pre-edit-acknowledge.d.ts +11 -12
- package/build/tools/pre-edit-acknowledge.js +18 -25
- package/build/tools/pre-edit-acknowledge.js.map +1 -1
- package/build/tools/record-decision.d.ts +12 -12
- package/build/tools/resume-intent.d.ts +112 -0
- package/build/tools/resume-intent.js +87 -0
- package/build/tools/resume-intent.js.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -89,7 +89,8 @@ Override paths when blocked:
|
|
|
89
89
|
```
|
|
90
90
|
record_decision(type: "fork", supersedes: ["<surfaced-decision-id>"], rationale: "...")
|
|
91
91
|
```
|
|
92
|
-
- **
|
|
92
|
+
- **For this session:** call `pre_edit_acknowledge(decisionIds: ["<surfaced-decision-id>"])`, then retry the Edit — that decision won't block again for the rest of the session. The acknowledgment is read back from your session transcript, so it needs no session token and is unaffected by daemon or session restarts within the same conversation.
|
|
93
|
+
- **One-off:** add `force: true` to the Edit tool args to proceed a single time.
|
|
93
94
|
|
|
94
95
|
Disable the hook for a session with `KAWA_PRE_EDIT_CHECK=off`.
|
|
95
96
|
|
|
@@ -111,6 +112,21 @@ Disable telemetry with `KAWA_PRE_EDIT_TELEMETRY=off`.
|
|
|
111
112
|
- **Zero-knowledge encryption** — code blocks encrypted client-side before sync. The Kawa cloud cannot decrypt your team's code.
|
|
112
113
|
- **Cross-platform** — works with Claude Code, Cursor, and any MCP-compatible AI assistant.
|
|
113
114
|
|
|
115
|
+
## Handing off work to a teammate (no session export)
|
|
116
|
+
|
|
117
|
+
Because the reasoning behind your work — your intents and recorded decisions — lives in Kawa Code rather than in the chat log, a teammate can pick up where you left off from a single prompt. No transcript sharing, no session restore.
|
|
118
|
+
|
|
119
|
+
1. **Commit or push your code first.** A handoff prompt carries your *reasoning*, not your uncommitted working tree — so land the code (or publish the pre-commit diff) before you hand off, otherwise your teammate inherits the decisions without the diff that goes with them.
|
|
120
|
+
2. **Grab the intent id.** The id of the intent you were working under — your agent can read it back with `check_active_intent`, or you can find it in the Kawa Code app.
|
|
121
|
+
3. **Hand over a one-line prompt,** e.g. `Follow up on intent <intent-id>: <what's left to do>`.
|
|
122
|
+
4. **Your teammate pastes it into a fresh session.** Their agent calls `resume_intent(<id>)` — one call that adopts the intent as their current focus *and* loads its recorded decisions — resuming the thread with full context, even though it never saw your chat.
|
|
123
|
+
|
|
124
|
+
**What transfers:** the intent, its decisions, and (once committed) its code. **What doesn't:** your chat transcript and any session-local state. An acknowledgment you made to a pre-edit block is *your* judgment in *your* session, so your teammate re-evaluates it rather than inheriting it — which is what you want.
|
|
125
|
+
|
|
126
|
+
**Teams:** to make the handoff seamless, add one line to your shared `CLAUDE.md` so the agent always treats a follow-up prompt as *resuming* the named intent instead of opening a new one:
|
|
127
|
+
|
|
128
|
+
> When a prompt says "follow up on intent `<id>`" (or similar), call `resume_intent(<id>)` to adopt that intent and load its decisions — do not create a new intent for it.
|
|
129
|
+
|
|
114
130
|
## Migrating or rewriting a codebase? Transplant its decisions
|
|
115
131
|
|
|
116
132
|
When you port a codebase to a new language or rebuild it in a fresh repository, the code moves — but the *reasoning* usually doesn't. The source repo's decision history knows why retired approaches were retired, which constraints are load-bearing, and where the security landmines are. With Kawa Code, that history becomes a first-class migration input.
|
package/build/index.js
CHANGED
|
@@ -3,7 +3,8 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
5
5
|
import { connectToMuninn, ensureRepo } from './services/muninn-ipc.js';
|
|
6
|
-
import {
|
|
6
|
+
import { salvageMangledArgs } from './tools/_salvage-mangled-args.js';
|
|
7
|
+
import { allTools, getRelevantContext, checkActiveIntent, createAndActivateIntent, activateIntent, resumeIntent, getIntentsForFile, getIntentsForLines, listTeamIntents, getIntentChanges, completeIntent, updateIntent, logWork, recordDecision, getSessionDecisions, getProjectDecisions, getDecisionDetail, editSessionDecision, detectIntentConflicts, inferHistory, evolveDecisions, updateFeatures, preEditDecisionCheck, preEditAcknowledge, getResolutionContext, arbiterResolve, arbiterApply } from './tools/index.js';
|
|
7
8
|
import { prompts } from './prompts/intent-first-workflow.js';
|
|
8
9
|
import { resources, readActiveIntentResource } from './resources/active-intent.js';
|
|
9
10
|
// Create MCP server
|
|
@@ -41,7 +42,23 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
41
42
|
});
|
|
42
43
|
// Handle tool calls
|
|
43
44
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
44
|
-
const { name, arguments:
|
|
45
|
+
const { name, arguments: rawArgs } = request.params;
|
|
46
|
+
// A malformed tool-call block (a parameter closed with `</fieldName>` instead
|
|
47
|
+
// of the required closing tag, followed by one opened without the required
|
|
48
|
+
// prefix) makes the harness fold the next field's name AND value into the
|
|
49
|
+
// preceding string. Recover them before dispatch — one seam covers every
|
|
50
|
+
// tool. Guarded and logged, never silent: see _salvage-mangled-args.ts.
|
|
51
|
+
let args = rawArgs;
|
|
52
|
+
const schema = allTools.find(t => t.name === name)?.inputSchema;
|
|
53
|
+
if (args && typeof args === 'object' && schema) {
|
|
54
|
+
const { args: repaired, salvaged } = salvageMangledArgs(args, Object.keys(schema.shape));
|
|
55
|
+
if (salvaged.length > 0) {
|
|
56
|
+
args = repaired;
|
|
57
|
+
// stderr only — stdout is the MCP transport.
|
|
58
|
+
console.error(`[MuninnIPC] Recovered ${salvaged.length} malformed argument(s) for ${name}: ` +
|
|
59
|
+
salvaged.map(s => `${s.field} (absorbed by ${s.fromField})`).join(', '));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
45
62
|
try {
|
|
46
63
|
const repoPath = args?.repoPath;
|
|
47
64
|
if (repoPath) {
|
|
@@ -61,6 +78,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
61
78
|
case 'activate_intent':
|
|
62
79
|
result = await activateIntent(args);
|
|
63
80
|
break;
|
|
81
|
+
case 'resume_intent':
|
|
82
|
+
result = await resumeIntent(args);
|
|
83
|
+
break;
|
|
64
84
|
case 'get_intents_for_file':
|
|
65
85
|
result = await getIntentsForFile(args);
|
|
66
86
|
break;
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,EAC1B,yBAAyB,EACzB,SAAS,EACT,QAAQ,EACT,MAAM,oCAAoC,CAAA;AAE3C,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,10 +14,15 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Maps recommendation to Claude Code's hook protocol:
|
|
16
16
|
* - "investigate-upstream" → exit 2 + stderr message (block)
|
|
17
|
-
* UNLESS tool_input.force === true →
|
|
17
|
+
* UNLESS tool_input.force === true → exit 0 (one-shot proceed)
|
|
18
18
|
* - "review" → exit 0 + JSON on stdout (advisory)
|
|
19
19
|
* - "proceed" / silent → exit 0, no output
|
|
20
20
|
*
|
|
21
|
+
* Decisions the agent has acknowledged via pre_edit_acknowledge are read from
|
|
22
|
+
* this session's transcript and passed as `forceOverrides`, so Muninn filters
|
|
23
|
+
* them before computing the recommendation (replaces the token-keyed
|
|
24
|
+
* pre-edit-cache; kawa.mcp decision 59ad77ed).
|
|
25
|
+
*
|
|
21
26
|
* This hook carries only the SEMANTIC (decision-tier) signal. The live-HAI
|
|
22
27
|
* code-collision signal was relocated off this per-edit path to a once-per-turn
|
|
23
28
|
* check in kawacode-on-stop (COLLISION_SURFACE_RELOCATION.md) — pre-edit no
|
|
@@ -14,10 +14,15 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Maps recommendation to Claude Code's hook protocol:
|
|
16
16
|
* - "investigate-upstream" → exit 2 + stderr message (block)
|
|
17
|
-
* UNLESS tool_input.force === true →
|
|
17
|
+
* UNLESS tool_input.force === true → exit 0 (one-shot proceed)
|
|
18
18
|
* - "review" → exit 0 + JSON on stdout (advisory)
|
|
19
19
|
* - "proceed" / silent → exit 0, no output
|
|
20
20
|
*
|
|
21
|
+
* Decisions the agent has acknowledged via pre_edit_acknowledge are read from
|
|
22
|
+
* this session's transcript and passed as `forceOverrides`, so Muninn filters
|
|
23
|
+
* them before computing the recommendation (replaces the token-keyed
|
|
24
|
+
* pre-edit-cache; kawa.mcp decision 59ad77ed).
|
|
25
|
+
*
|
|
21
26
|
* This hook carries only the SEMANTIC (decision-tier) signal. The live-HAI
|
|
22
27
|
* code-collision signal was relocated off this per-edit path to a once-per-turn
|
|
23
28
|
* check in kawacode-on-stop (COLLISION_SURFACE_RELOCATION.md) — pre-edit no
|
|
@@ -33,6 +38,7 @@ import { readFileSync } from 'node:fs';
|
|
|
33
38
|
import { connectToMuninn, request, disconnect, setQuiet } from './services/muninn-ipc.js';
|
|
34
39
|
import { resolveOrigin } from './tools/resolve-origin.js';
|
|
35
40
|
import { resolvePaths } from './pre-edit/path-resolve.js';
|
|
41
|
+
import { ackedDecisionIds } from './pre-edit/acked-decisions.js';
|
|
36
42
|
import { emitInjection, emitActedOn, estimateTokens } from './telemetry.js';
|
|
37
43
|
// Hook stderr is surfaced to the agent/user on exit-2 blocks — suppress the
|
|
38
44
|
// IPC lifecycle chatter so only the decision-check message comes through.
|
|
@@ -171,7 +177,21 @@ async function main() {
|
|
|
171
177
|
const target = resolveTarget(payload);
|
|
172
178
|
if (!target)
|
|
173
179
|
process.exit(0);
|
|
174
|
-
|
|
180
|
+
// Transcript-based acknowledgment (replaces the token-keyed pre-edit cache):
|
|
181
|
+
// the decision ids the agent already acknowledged via pre_edit_acknowledge,
|
|
182
|
+
// read from THIS session's own transcript. Same namespace as the hook
|
|
183
|
+
// (payload.transcript_path is in payload.session_id's space), so no
|
|
184
|
+
// cross-process session-id rendezvous — see kawa.mcp decision 59ad77ed.
|
|
185
|
+
// Fail-soft: an unreadable transcript just means no overrides (surface all).
|
|
186
|
+
let forceOverrides = [];
|
|
187
|
+
if (payload.transcript_path) {
|
|
188
|
+
try {
|
|
189
|
+
forceOverrides = [...ackedDecisionIds(readFileSync(payload.transcript_path, 'utf8'))];
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
/* no transcript → behave as before (nothing suppressed) */
|
|
193
|
+
}
|
|
194
|
+
}
|
|
175
195
|
// Resolve origin from the repo path. Falls back silently to exit 0
|
|
176
196
|
// when git isn't available or the path isn't a repo.
|
|
177
197
|
let repoOrigin;
|
|
@@ -195,7 +215,7 @@ async function main() {
|
|
|
195
215
|
filePath: target.filePath,
|
|
196
216
|
startLine: target.startLine,
|
|
197
217
|
endLine: target.endLine,
|
|
198
|
-
|
|
218
|
+
forceOverrides,
|
|
199
219
|
});
|
|
200
220
|
}
|
|
201
221
|
catch {
|
|
@@ -206,21 +226,11 @@ async function main() {
|
|
|
206
226
|
const force = payload.tool_input?.force === true;
|
|
207
227
|
const ids = (res.decisions ?? []).map(d => d.decisionId).filter(Boolean);
|
|
208
228
|
if (force) {
|
|
209
|
-
//
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
await request('pre-edit-cache', 'add', {
|
|
213
|
-
sessionToken,
|
|
214
|
-
decisionIds: ids,
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
catch {
|
|
218
|
-
/* best-effort */
|
|
219
|
-
}
|
|
220
|
-
}
|
|
229
|
+
// force:true is a one-shot "proceed now". The DURABLE acknowledgment is a
|
|
230
|
+
// pre_edit_acknowledge call, whose presence in the transcript suppresses
|
|
231
|
+
// future blocks (ackedDecisionIds above) — so there's no cache to write.
|
|
221
232
|
// Track-B acted-on (VALUE_METRICS Phase 2): the agent consciously
|
|
222
|
-
// overrode the surfaced reasoning —
|
|
223
|
-
// One signal per surfaced decision id (refId), origin in hand here.
|
|
233
|
+
// overrode the surfaced reasoning — one signal per surfaced decision id.
|
|
224
234
|
try {
|
|
225
235
|
await Promise.allSettled(ids.map(id => emitActedOn({ type: 'pre_edit', refId: id, repoOrigin })));
|
|
226
236
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kawacode-on-pre-edit.js","sourceRoot":"","sources":["../src/kawacode-on-pre-edit.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"kawacode-on-pre-edit.js","sourceRoot":"","sources":["../src/kawacode-on-pre-edit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure transcript detection for the pre-edit acknowledgment gate.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the token-keyed Muninn `pre-edit-cache`. That cache was a
|
|
5
|
+
* cross-process rendezvous: the hook read it under `payload.session_id` while
|
|
6
|
+
* the acknowledge wrote it under the MCP process's `CLAUDE_CODE_SESSION_ID` —
|
|
7
|
+
* two id sources that diverge after a session restart (the MCP env is a frozen
|
|
8
|
+
* spawn-time snapshot), so the override silently landed in a bucket the check
|
|
9
|
+
* never read. See kawa.mcp decision 59ad77ed.
|
|
10
|
+
*
|
|
11
|
+
* The transcript sidesteps that entirely: the hook reads its OWN transcript
|
|
12
|
+
* (via `payload.transcript_path`, which lives in `payload.session_id`'s
|
|
13
|
+
* namespace), so no cross-process id has to agree. A `pre_edit_acknowledge`
|
|
14
|
+
* tool call's presence in the transcript IS the acknowledgment record.
|
|
15
|
+
*
|
|
16
|
+
* Kept separate from the bin entry so it's unit-testable without triggering the
|
|
17
|
+
* hook's main() — mirrors src/stop/uncompleted-commit.ts.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Every decision id the agent has acknowledged this session, read from
|
|
21
|
+
* `pre_edit_acknowledge` tool_use entries in the Claude Code transcript.
|
|
22
|
+
*
|
|
23
|
+
* Position-independent: an acknowledgment anywhere in the session counts (the
|
|
24
|
+
* override is "for the rest of THIS session"). Robust by construction — a
|
|
25
|
+
* malformed line, a missing/!array `decisionIds`, or a non-array `content` is
|
|
26
|
+
* skipped, never thrown. An empty set is the safe default (nothing suppressed).
|
|
27
|
+
*
|
|
28
|
+
* @param transcriptText raw JSONL contents of the transcript file
|
|
29
|
+
*/
|
|
30
|
+
export declare function ackedDecisionIds(transcriptText: string): Set<string>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure transcript detection for the pre-edit acknowledgment gate.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the token-keyed Muninn `pre-edit-cache`. That cache was a
|
|
5
|
+
* cross-process rendezvous: the hook read it under `payload.session_id` while
|
|
6
|
+
* the acknowledge wrote it under the MCP process's `CLAUDE_CODE_SESSION_ID` —
|
|
7
|
+
* two id sources that diverge after a session restart (the MCP env is a frozen
|
|
8
|
+
* spawn-time snapshot), so the override silently landed in a bucket the check
|
|
9
|
+
* never read. See kawa.mcp decision 59ad77ed.
|
|
10
|
+
*
|
|
11
|
+
* The transcript sidesteps that entirely: the hook reads its OWN transcript
|
|
12
|
+
* (via `payload.transcript_path`, which lives in `payload.session_id`'s
|
|
13
|
+
* namespace), so no cross-process id has to agree. A `pre_edit_acknowledge`
|
|
14
|
+
* tool call's presence in the transcript IS the acknowledgment record.
|
|
15
|
+
*
|
|
16
|
+
* Kept separate from the bin entry so it's unit-testable without triggering the
|
|
17
|
+
* hook's main() — mirrors src/stop/uncompleted-commit.ts.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Every decision id the agent has acknowledged this session, read from
|
|
21
|
+
* `pre_edit_acknowledge` tool_use entries in the Claude Code transcript.
|
|
22
|
+
*
|
|
23
|
+
* Position-independent: an acknowledgment anywhere in the session counts (the
|
|
24
|
+
* override is "for the rest of THIS session"). Robust by construction — a
|
|
25
|
+
* malformed line, a missing/!array `decisionIds`, or a non-array `content` is
|
|
26
|
+
* skipped, never thrown. An empty set is the safe default (nothing suppressed).
|
|
27
|
+
*
|
|
28
|
+
* @param transcriptText raw JSONL contents of the transcript file
|
|
29
|
+
*/
|
|
30
|
+
export function ackedDecisionIds(transcriptText) {
|
|
31
|
+
const acked = new Set();
|
|
32
|
+
if (!transcriptText)
|
|
33
|
+
return acked;
|
|
34
|
+
for (const line of transcriptText.split('\n')) {
|
|
35
|
+
const ln = line.trim();
|
|
36
|
+
if (!ln)
|
|
37
|
+
continue;
|
|
38
|
+
let obj;
|
|
39
|
+
try {
|
|
40
|
+
obj = JSON.parse(ln);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const content = obj?.message?.content;
|
|
46
|
+
if (!Array.isArray(content))
|
|
47
|
+
continue;
|
|
48
|
+
for (const item of content) {
|
|
49
|
+
if (item?.type !== 'tool_use')
|
|
50
|
+
continue;
|
|
51
|
+
// Tolerate the MCP prefix (e.g. `mcp__kawa-intents__pre_edit_acknowledge`).
|
|
52
|
+
if (!/pre_edit_acknowledge/.test(item.name || ''))
|
|
53
|
+
continue;
|
|
54
|
+
const ids = item?.input?.decisionIds;
|
|
55
|
+
if (!Array.isArray(ids))
|
|
56
|
+
continue;
|
|
57
|
+
for (const id of ids) {
|
|
58
|
+
if (typeof id === 'string' && id)
|
|
59
|
+
acked.add(id);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return acked;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=acked-decisions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acked-decisions.js","sourceRoot":"","sources":["../../src/pre-edit/acked-decisions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAAC,cAAsB;IACrD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAA;IAC/B,IAAI,CAAC,cAAc;QAAE,OAAO,KAAK,CAAA;IAEjC,KAAK,MAAM,IAAI,IAAI,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9C,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QACtB,IAAI,CAAC,EAAE;YAAE,SAAQ;QAEjB,IAAI,GAAQ,CAAA;QACZ,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QACtB,CAAC;QAAC,MAAM,CAAC;YACP,SAAQ;QACV,CAAC;QAED,MAAM,OAAO,GAAG,GAAG,EAAE,OAAO,EAAE,OAAO,CAAA;QACrC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;YAAE,SAAQ;QAErC,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YAC3B,IAAI,IAAI,EAAE,IAAI,KAAK,UAAU;gBAAE,SAAQ;YACvC,4EAA4E;YAC5E,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;gBAAE,SAAQ;YAE3D,MAAM,GAAG,GAAG,IAAI,EAAE,KAAK,EAAE,WAAW,CAAA;YACpC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;gBAAE,SAAQ;YACjC,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;gBACrB,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE;oBAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACjD,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { ackedDecisionIds } from './acked-decisions.js';
|
|
4
|
+
// Build one JSONL transcript line carrying an assistant message with the given
|
|
5
|
+
// content items — mirrors the shape scanTranscript consumes in the stop-gate.
|
|
6
|
+
function msgLine(content) {
|
|
7
|
+
return JSON.stringify({ type: 'assistant', message: { role: 'assistant', content } });
|
|
8
|
+
}
|
|
9
|
+
function ackToolUse(name, decisionIds) {
|
|
10
|
+
return { type: 'tool_use', id: 'toolu_x', name, input: { decisionIds } };
|
|
11
|
+
}
|
|
12
|
+
test('collects decisionIds from a pre_edit_acknowledge tool_use', () => {
|
|
13
|
+
const t = msgLine([ackToolUse('pre_edit_acknowledge', ['d1', 'd2'])]);
|
|
14
|
+
assert.deepEqual([...ackedDecisionIds(t)].sort(), ['d1', 'd2']);
|
|
15
|
+
});
|
|
16
|
+
test('tolerates the mcp__ tool-name prefix', () => {
|
|
17
|
+
const t = msgLine([ackToolUse('mcp__kawa-intents__pre_edit_acknowledge', ['d9'])]);
|
|
18
|
+
assert.deepEqual([...ackedDecisionIds(t)], ['d9']);
|
|
19
|
+
});
|
|
20
|
+
test('unions ids across multiple acknowledge calls, deduped', () => {
|
|
21
|
+
const t = [
|
|
22
|
+
msgLine([ackToolUse('pre_edit_acknowledge', ['a', 'b'])]),
|
|
23
|
+
msgLine([{ type: 'text', text: 'some prose' }]),
|
|
24
|
+
msgLine([ackToolUse('pre_edit_acknowledge', ['b', 'c'])]),
|
|
25
|
+
].join('\n');
|
|
26
|
+
assert.deepEqual([...ackedDecisionIds(t)].sort(), ['a', 'b', 'c']);
|
|
27
|
+
});
|
|
28
|
+
test('ignores other tool calls', () => {
|
|
29
|
+
const t = msgLine([
|
|
30
|
+
{ type: 'tool_use', id: 't1', name: 'record_decision', input: { decisionIds: ['nope'] } },
|
|
31
|
+
{ type: 'tool_use', id: 't2', name: 'Edit', input: { file_path: 'x' } },
|
|
32
|
+
]);
|
|
33
|
+
assert.equal(ackedDecisionIds(t).size, 0);
|
|
34
|
+
});
|
|
35
|
+
test('skips malformed JSON lines without throwing', () => {
|
|
36
|
+
const t = ['{ not valid json', msgLine([ackToolUse('pre_edit_acknowledge', ['ok'])]), 'also broken'].join('\n');
|
|
37
|
+
assert.deepEqual([...ackedDecisionIds(t)], ['ok']);
|
|
38
|
+
});
|
|
39
|
+
test('skips a tool_use with missing or non-array decisionIds', () => {
|
|
40
|
+
const t = [
|
|
41
|
+
msgLine([ackToolUse('pre_edit_acknowledge', undefined)]),
|
|
42
|
+
msgLine([ackToolUse('pre_edit_acknowledge', 'd1')]),
|
|
43
|
+
msgLine([ackToolUse('pre_edit_acknowledge', ['real'])]),
|
|
44
|
+
].join('\n');
|
|
45
|
+
assert.deepEqual([...ackedDecisionIds(t)], ['real']);
|
|
46
|
+
});
|
|
47
|
+
test('drops non-string / empty ids inside the array', () => {
|
|
48
|
+
const t = msgLine([ackToolUse('pre_edit_acknowledge', ['keep', '', 42, null, { x: 1 }])]);
|
|
49
|
+
assert.deepEqual([...ackedDecisionIds(t)], ['keep']);
|
|
50
|
+
});
|
|
51
|
+
test('non-array message content is skipped', () => {
|
|
52
|
+
const t = JSON.stringify({ message: { content: 'a string, not an array' } });
|
|
53
|
+
assert.equal(ackedDecisionIds(t).size, 0);
|
|
54
|
+
});
|
|
55
|
+
test('empty / whitespace transcript yields an empty set', () => {
|
|
56
|
+
assert.equal(ackedDecisionIds('').size, 0);
|
|
57
|
+
assert.equal(ackedDecisionIds('\n \n').size, 0);
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=acked-decisions.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acked-decisions.test.js","sourceRoot":"","sources":["../../src/pre-edit/acked-decisions.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recovery for tool-call args mangled by a mis-closed parameter tag.
|
|
3
|
+
*
|
|
4
|
+
* When an LLM emits a parameter block that is opened correctly but closed with
|
|
5
|
+
* `</fieldName>` instead of the required closing tag — and the NEXT parameter is
|
|
6
|
+
* opened without the required prefix — the harness's tool-call parser never sees
|
|
7
|
+
* a parameter boundary. Everything up to the next well-formed close is folded
|
|
8
|
+
* into the preceding field's string value. What reaches this process is
|
|
9
|
+
* perfectly valid JSON in which one field carries a self-describing tail:
|
|
10
|
+
*
|
|
11
|
+
* { consequences: 'real text</consequences>\n<parameter name="symptom">real symptom' }
|
|
12
|
+
*
|
|
13
|
+
* There is no second wire format to parse — the tail is simply data. But it is
|
|
14
|
+
* losslessly recoverable, because it names the field it was meant for and
|
|
15
|
+
* carries that field's value verbatim.
|
|
16
|
+
*
|
|
17
|
+
* This is deliberately a compensating path (the project otherwise forbids
|
|
18
|
+
* fallbacks): left alone, the corruption reaches the store, syncs, and becomes
|
|
19
|
+
* immutable, which has already cost several supersede cycles. Recovery is
|
|
20
|
+
* strictly guarded and always logged, never silent.
|
|
21
|
+
*/
|
|
22
|
+
export interface SalvageEntry {
|
|
23
|
+
/** The field whose value was recovered. */
|
|
24
|
+
field: string;
|
|
25
|
+
/** The field that had absorbed it. */
|
|
26
|
+
fromField: string;
|
|
27
|
+
}
|
|
28
|
+
export interface SalvageResult {
|
|
29
|
+
args: Record<string, unknown>;
|
|
30
|
+
salvaged: SalvageEntry[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Peel absorbed fields out of `args`. Returns a new object; `args` is untouched.
|
|
34
|
+
*
|
|
35
|
+
* `knownFields` are the schema field names of the tool being called — recovery
|
|
36
|
+
* is refused for anything not in that set.
|
|
37
|
+
*/
|
|
38
|
+
export declare function salvageMangledArgs(args: Record<string, unknown>, knownFields: readonly string[]): SalvageResult;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recovery for tool-call args mangled by a mis-closed parameter tag.
|
|
3
|
+
*
|
|
4
|
+
* When an LLM emits a parameter block that is opened correctly but closed with
|
|
5
|
+
* `</fieldName>` instead of the required closing tag — and the NEXT parameter is
|
|
6
|
+
* opened without the required prefix — the harness's tool-call parser never sees
|
|
7
|
+
* a parameter boundary. Everything up to the next well-formed close is folded
|
|
8
|
+
* into the preceding field's string value. What reaches this process is
|
|
9
|
+
* perfectly valid JSON in which one field carries a self-describing tail:
|
|
10
|
+
*
|
|
11
|
+
* { consequences: 'real text</consequences>\n<parameter name="symptom">real symptom' }
|
|
12
|
+
*
|
|
13
|
+
* There is no second wire format to parse — the tail is simply data. But it is
|
|
14
|
+
* losslessly recoverable, because it names the field it was meant for and
|
|
15
|
+
* carries that field's value verbatim.
|
|
16
|
+
*
|
|
17
|
+
* This is deliberately a compensating path (the project otherwise forbids
|
|
18
|
+
* fallbacks): left alone, the corruption reaches the store, syncs, and becomes
|
|
19
|
+
* immutable, which has already cost several supersede cycles. Recovery is
|
|
20
|
+
* strictly guarded and always logged, never silent.
|
|
21
|
+
*/
|
|
22
|
+
/** `</host>` + optional whitespace + a parameter opening + everything after it. */
|
|
23
|
+
const MANGLED_TAIL = /<\/([A-Za-z_][\w-]*)>\s*<parameter name="([A-Za-z_]\w*)">([\s\S]*)$/;
|
|
24
|
+
/**
|
|
25
|
+
* Arrays and objects arrive as JSON text; anything else is a plain string.
|
|
26
|
+
* Only attempt a parse when the value actually looks structured, so a string
|
|
27
|
+
* like "123" or "true" is never silently coerced to another type.
|
|
28
|
+
*/
|
|
29
|
+
function parseSalvagedValue(raw) {
|
|
30
|
+
const trimmed = raw.trim();
|
|
31
|
+
if (trimmed.startsWith('[') || trimmed.startsWith('{')) {
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(trimmed);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Not valid JSON after all — keep the text rather than dropping it.
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return trimmed;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Peel absorbed fields out of `args`. Returns a new object; `args` is untouched.
|
|
43
|
+
*
|
|
44
|
+
* `knownFields` are the schema field names of the tool being called — recovery
|
|
45
|
+
* is refused for anything not in that set.
|
|
46
|
+
*/
|
|
47
|
+
export function salvageMangledArgs(args, knownFields) {
|
|
48
|
+
const known = new Set(knownFields);
|
|
49
|
+
const out = { ...args };
|
|
50
|
+
const salvaged = [];
|
|
51
|
+
// A single malformed emission can chain: field A absorbs B, whose value in
|
|
52
|
+
// turn absorbs C. Keep peeling until a pass finds nothing new.
|
|
53
|
+
let progressed = true;
|
|
54
|
+
while (progressed) {
|
|
55
|
+
progressed = false;
|
|
56
|
+
for (const [hostField, hostValue] of Object.entries(out)) {
|
|
57
|
+
if (typeof hostValue !== 'string')
|
|
58
|
+
continue;
|
|
59
|
+
const match = hostValue.match(MANGLED_TAIL);
|
|
60
|
+
if (!match || match.index === undefined)
|
|
61
|
+
continue;
|
|
62
|
+
const [, closingTag, targetField, rawValue] = match;
|
|
63
|
+
// Three guards, all required. Any miss means this is probably genuine
|
|
64
|
+
// prose (a decision *documenting* this very bug legitimately contains
|
|
65
|
+
// these markers), so leave it alone and let normal validation decide.
|
|
66
|
+
//
|
|
67
|
+
// 1. the bogus closing tag names the host field — evidence of a
|
|
68
|
+
// mis-closed host parameter rather than incidental mention
|
|
69
|
+
if (closingTag !== hostField)
|
|
70
|
+
continue;
|
|
71
|
+
// 2. the absorbed field is real for this tool
|
|
72
|
+
if (!known.has(targetField))
|
|
73
|
+
continue;
|
|
74
|
+
// 3. it was not otherwise supplied — never overwrite genuine input
|
|
75
|
+
const existing = out[targetField];
|
|
76
|
+
if (existing !== undefined && existing !== null && existing !== '')
|
|
77
|
+
continue;
|
|
78
|
+
out[hostField] = hostValue.slice(0, match.index).trimEnd();
|
|
79
|
+
out[targetField] = parseSalvagedValue(rawValue);
|
|
80
|
+
salvaged.push({ field: targetField, fromField: hostField });
|
|
81
|
+
progressed = true;
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { args: out, salvaged };
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=_salvage-mangled-args.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_salvage-mangled-args.js","sourceRoot":"","sources":["../../src/tools/_salvage-mangled-args.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,mFAAmF;AACnF,MAAM,YAAY,GAAG,qEAAqE,CAAA;AAc1F;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,GAAW;IACrC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;IAC1B,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,oEAAoE;QACtE,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAA6B,EAC7B,WAA8B;IAE9B,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAA;IAClC,MAAM,GAAG,GAA4B,EAAE,GAAG,IAAI,EAAE,CAAA;IAChD,MAAM,QAAQ,GAAmB,EAAE,CAAA;IAEnC,2EAA2E;IAC3E,+DAA+D;IAC/D,IAAI,UAAU,GAAG,IAAI,CAAA;IACrB,OAAO,UAAU,EAAE,CAAC;QAClB,UAAU,GAAG,KAAK,CAAA;QAElB,KAAK,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACzD,IAAI,OAAO,SAAS,KAAK,QAAQ;gBAAE,SAAQ;YAE3C,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;YAC3C,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;gBAAE,SAAQ;YAEjD,MAAM,CAAC,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAA;YAEnD,sEAAsE;YACtE,sEAAsE;YACtE,sEAAsE;YACtE,EAAE;YACF,iEAAiE;YACjE,+DAA+D;YAC/D,IAAI,UAAU,KAAK,SAAS;gBAAE,SAAQ;YACtC,+CAA+C;YAC/C,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC;gBAAE,SAAQ;YACrC,oEAAoE;YACpE,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,CAAC,CAAA;YACjC,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,EAAE;gBAAE,SAAQ;YAE5E,GAAG,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAA;YAC1D,GAAG,CAAC,WAAW,CAAC,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAA;YAC/C,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAA;YAC3D,UAAU,GAAG,IAAI,CAAA;YACjB,MAAK;QACP,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAA;AAChC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|