1
>
/*---------------------------------------------------------------------------------------------
editChunkExtractor.ts
2
>
* Copyright (c) Microsoft Corporation. All rights reserved.
3
>
* Licensed under the MIT License. See License.txt in the project root for license information.
4
>
*--------------------------------------------------------------------------------------------*/
5
>
6
>
/*
7
>
* Extracts the explicit AI-written text chunks from a file-edit tool's
8
>
* input payload. Both Claude (via @anthropic-ai/claude-agent-sdk) and
9
>
* Copilot CLI (via @github/copilot-sdk) accept canonical tool schemas
10
>
* whose shapes we can read structurally — Claude uses PascalCase names
11
>
* (`Write`, `Edit`, `MultiEdit`) with `_string` fields, Copilot uses
12
>
* snake_case (`create`, `edit`, `str_replace`, `insert`,
13
>
* `str_replace_editor` command-dispatched, `apply_patch` /
14
>
* `git_apply_patch` V4A patch body) with `_str` / `file_text` fields.
15
>
*
16
>
* Returning an empty array means "we couldn't read this — fall back to
17
>
* whole-file scoring." Defensive against malformed SDK input: every
18
>
* branch checks the value shape before reading.
19
>
*
20
>
* Coverage invariant: every tool the agent host currently treats as a
21
>
* file-edit tool (`isClaudeFileEditTool`, `isEditTool` in Copilot) has
22
>
* a matching case below — so in practice every edit gets chunked
23
>
* scoring. The whole-file fallback is a safety net for SDK shape drift
24
>
* (a tool input changes shape) and for newly added tools (a new edit
25
>
* tool added to one of those gates without a matching case here). If
26
>
* you add a new file-edit tool to either gate, add a case here too so
27
>
* the survival reporter keeps producing chunked scores.
28
>
*/
29
>
30
>
/**
31
>
* Returns the AI-written text chunks for a known file-edit tool, or
32
>
* `[]` if the tool / input shape is not recognised. Callers should
33
>
* treat `[]` as "fall back to whole-file scoring."
34
>
*
35
>
* Supported Claude SDK tools (one file per call):
36
>
* - `Write { content }` → `[content]`
37
>
* - `Edit { new_string }` → `[new_string]`
38
>
* - `MultiEdit { edits: [{ new_string }] }` → one chunk per edit
39
>
*
40
>
* Supported Copilot CLI tools (one file per call unless noted):
41
>
* - `create { file_text }` → `[file_text]`
42
>
* - `edit`, `str_replace { new_str }` → `[new_str]`
43
>
* - `insert { new_str }` → `[new_str]`
44
>
* - `str_replace_editor { command, ... }` → dispatch on command
45
>
* - `apply_patch`, `git_apply_patch` → `+` lines from the
46
>
* V4A patch body, scoped to {@link forFilePath} when supplied
47
>
* (the patch may touch multiple files; we only want chunks for
48
>
* the file we're sampling).
49
>
*
50
>
* `NotebookEdit` is not handled here: the reporter currently skips
51
>
* `.ipynb` files at launch time, so notebook tool inputs never reach
52
>
* the survival math. Add a branch here if we extend tracking to
53
>
* notebooks.
54
>
*
55
>
* @param toolName Tool identifier (Claude PascalCase or Copilot
56
>
* snake_case; tools we don't recognise just return `[]`).
57
>
* @param input The tool input. May be `unknown`, a JSON object,
58
>
* or — for `apply_patch` — a bare V4A patch string. Defensive
59
>
* against all three.
60
>
* @param forFilePath Optional. When supplied and the tool is a
61
>
* multi-file patch (`apply_patch` / `git_apply_patch`), only the
62
>
* `+` lines under that file's header contribute. Single-file tools
63
>
* ignore this argument.
64
>
*/
65
>
export function extractAiChunks(toolName: string, input: unknown, forFilePath?: string): string[] {
66
switch (toolName) {
67
// ---- Claude SDK -------------------------------------------------