@agentclientprotocol/claude-agent-acp 0.85.2-preview.19 → 0.85.2-preview.3
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 +0 -1
- package/dist/acp-agent.d.ts +18 -81
- package/dist/acp-agent.d.ts.map +1 -1
- package/dist/acp-agent.js +170 -435
- package/dist/air-extension.d.ts +0 -2
- package/dist/air-extension.d.ts.map +1 -1
- package/dist/air-extension.js +0 -2
- package/dist/diff.d.ts +8 -45
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +30 -111
- package/dist/permissions/presentation.d.ts +2 -17
- package/dist/permissions/presentation.d.ts.map +1 -1
- package/dist/permissions/presentation.js +0 -4
- package/dist/resumed-session.d.ts +6 -16
- package/dist/resumed-session.d.ts.map +1 -1
- package/dist/resumed-session.js +25 -91
- package/dist/session-mode.d.ts +0 -14
- package/dist/session-mode.d.ts.map +1 -1
- package/dist/session-mode.js +0 -22
- package/dist/tool-calls/client-capabilities.d.ts +2 -20
- package/dist/tool-calls/client-capabilities.d.ts.map +1 -1
- package/dist/tool-calls/client-capabilities.js +4 -14
- package/dist/tool-calls/renderer.d.ts.map +1 -1
- package/dist/tool-calls/renderer.js +2 -3
- package/dist/tool-calls/reporters/file-edit.d.ts +1 -1
- package/dist/tool-calls/reporters/file-edit.d.ts.map +1 -1
- package/dist/tool-calls/reporters/file-edit.js +14 -20
- package/dist/turn-events.d.ts +5 -11
- package/dist/turn-events.d.ts.map +1 -1
- package/dist/usage-markdown.d.ts +0 -2
- package/dist/usage-markdown.d.ts.map +1 -1
- package/dist/usage-markdown.js +1 -2
- package/dist/utils.d.ts +0 -12
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +0 -28
- package/dist/v2/agent.d.ts.map +1 -1
- package/dist/v2/agent.js +17 -121
- package/dist/v2/initialize.d.ts +5 -2
- package/dist/v2/initialize.d.ts.map +1 -1
- package/dist/v2/initialize.js +6 -23
- package/dist/v2/session-update.d.ts +0 -6
- package/dist/v2/session-update.d.ts.map +1 -1
- package/dist/v2/session-update.js +0 -27
- package/dist/v2/session.d.ts +4 -15
- package/dist/v2/session.d.ts.map +1 -1
- package/dist/v2/session.js +6 -20
- package/package.json +2 -2
- package/dist/mcp-command.d.ts +0 -51
- package/dist/mcp-command.d.ts.map +0 -1
- package/dist/mcp-command.js +0 -321
- package/dist/v2/permission.d.ts +0 -25
- package/dist/v2/permission.d.ts.map +0 -1
- package/dist/v2/permission.js +0 -38
- package/dist/v2/prompt.d.ts +0 -47
- package/dist/v2/prompt.d.ts.map +0 -1
- package/dist/v2/prompt.js +0 -153
- package/dist/v2/terminal.d.ts +0 -42
- package/dist/v2/terminal.d.ts.map +0 -1
- package/dist/v2/terminal.js +0 -95
- package/dist/v2/tool-call.d.ts +0 -38
- package/dist/v2/tool-call.d.ts.map +0 -1
- package/dist/v2/tool-call.js +0 -49
package/dist/air-extension.d.ts
CHANGED
|
@@ -17,8 +17,6 @@ export declare const AIR_SKILL_KEY = "skill";
|
|
|
17
17
|
export declare const AIR_CONTEXT_COMPACTION_KEY = "contextCompaction";
|
|
18
18
|
export declare const AIR_GOAL_KEY = "goal";
|
|
19
19
|
export declare const AIR_KIND_KEY = "kind";
|
|
20
|
-
/** The absolute path of the `SKILL.md` of a skill command in `available_commands_update`. */
|
|
21
|
-
export declare const AIR_SKILL_PATH_KEY = "skillPath";
|
|
22
20
|
export declare const AIR_PERMISSION_KEY = "permission";
|
|
23
21
|
export declare const AIR_CUSTOM_ANSWER_KEY = "customAnswer";
|
|
24
22
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"air-extension.d.ts","sourceRoot":"","sources":["../src/air-extension.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uCAAuC,2BAA2B,CAAC;AAChF,eAAO,MAAM,0BAA0B,eAAe,CAAC;AACvD,eAAO,MAAM,8BAA8B,mBAAmB,CAAC;AAC/D,eAAO,MAAM,uCAAuC,qBAAqB,CAAC;AAC1E,eAAO,MAAM,yBAAyB,cAAc,CAAC;AACrD,4EAA4E;AAC5E,eAAO,MAAM,kCAAkC,sBAAsB,CAAC;AACtE;;;GAGG;AACH,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAEnD,8EAA8E;AAC9E,eAAO,MAAM,qBAAqB,iBAAiB,CAAC;AACpD,eAAO,MAAM,gBAAgB,aAAa,CAAC;AAC3C,eAAO,MAAM,aAAa,UAAU,CAAC;AACrC,eAAO,MAAM,0BAA0B,sBAAsB,CAAC;AAC9D,eAAO,MAAM,YAAY,SAAS,CAAC;AACnC,eAAO,MAAM,YAAY,SAAS,CAAC;AACnC,
|
|
1
|
+
{"version":3,"file":"air-extension.d.ts","sourceRoot":"","sources":["../src/air-extension.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uCAAuC,2BAA2B,CAAC;AAChF,eAAO,MAAM,0BAA0B,eAAe,CAAC;AACvD,eAAO,MAAM,8BAA8B,mBAAmB,CAAC;AAC/D,eAAO,MAAM,uCAAuC,qBAAqB,CAAC;AAC1E,eAAO,MAAM,yBAAyB,cAAc,CAAC;AACrD,4EAA4E;AAC5E,eAAO,MAAM,kCAAkC,sBAAsB,CAAC;AACtE;;;GAGG;AACH,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAEnD,8EAA8E;AAC9E,eAAO,MAAM,qBAAqB,iBAAiB,CAAC;AACpD,eAAO,MAAM,gBAAgB,aAAa,CAAC;AAC3C,eAAO,MAAM,aAAa,UAAU,CAAC;AACrC,eAAO,MAAM,0BAA0B,sBAAsB,CAAC;AAC9D,eAAO,MAAM,YAAY,SAAS,CAAC;AACnC,eAAO,MAAM,YAAY,SAAS,CAAC;AACnC,eAAO,MAAM,kBAAkB,eAAe,CAAC;AAC/C,eAAO,MAAM,qBAAqB,iBAAiB,CAAC;AACpD;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,iCAAiC,CAAC;AAQ3E,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,GAAG,YAAY,EAAE,MAAM,EAAE,2BAE1D;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,EAChD,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,GACf,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAczB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,YAAY,EAAE,OAAO,GAAG,OAAO,CAE1D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,OAAO,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GACpC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAErC;AAED,2FAA2F;AAC3F,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAKnF;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAY9F"}
|
package/dist/air-extension.js
CHANGED
|
@@ -17,8 +17,6 @@ export const AIR_SKILL_KEY = "skill";
|
|
|
17
17
|
export const AIR_CONTEXT_COMPACTION_KEY = "contextCompaction";
|
|
18
18
|
export const AIR_GOAL_KEY = "goal";
|
|
19
19
|
export const AIR_KIND_KEY = "kind";
|
|
20
|
-
/** The absolute path of the `SKILL.md` of a skill command in `available_commands_update`. */
|
|
21
|
-
export const AIR_SKILL_PATH_KEY = "skillPath";
|
|
22
20
|
export const AIR_PERMISSION_KEY = "permission";
|
|
23
21
|
export const AIR_CUSTOM_ANSWER_KEY = "customAnswer";
|
|
24
22
|
/**
|
package/dist/diff.d.ts
CHANGED
|
@@ -23,17 +23,6 @@ interface PatchHunk {
|
|
|
23
23
|
}
|
|
24
24
|
/** The kind of file change that a git patch describes. */
|
|
25
25
|
type FileChange = "create" | "update";
|
|
26
|
-
/**
|
|
27
|
-
* The diff that carries an exact patch: AIR's `diffPatch` extension of a v1
|
|
28
|
-
* diff, or an ACP v2 diff.
|
|
29
|
-
*/
|
|
30
|
-
export type PatchForm = "air" | "v2";
|
|
31
|
-
/**
|
|
32
|
-
* How a git patch names its file. `git` is git's own form: the leading slash
|
|
33
|
-
* dropped and the `a/` and `b/` prefixes added. `absolute` is the form of an
|
|
34
|
-
* ACP v2 `git_patch`, whose paths must be absolute: the path itself.
|
|
35
|
-
*/
|
|
36
|
-
type GitPatchPaths = "git" | "absolute";
|
|
37
26
|
/**
|
|
38
27
|
* Builds the exact patch shown before Claude runs an Edit or Write tool.
|
|
39
28
|
*
|
|
@@ -46,14 +35,14 @@ type GitPatchPaths = "git" | "absolute";
|
|
|
46
35
|
* normalizes quotes or fails.
|
|
47
36
|
*
|
|
48
37
|
* A Write always gets content that shows the change: the patch, the standard
|
|
49
|
-
* diff
|
|
50
|
-
*
|
|
38
|
+
* diff of a text that cannot have an exact patch, or a notice that the Write
|
|
39
|
+
* overwrites a file that the adapter cannot show.
|
|
51
40
|
*
|
|
52
41
|
* The preview uses the tool input as it is. Claude can still remove the
|
|
53
42
|
* trailing whitespace of a line before it writes. The PostToolUse hook then
|
|
54
43
|
* sends the patch of the written file.
|
|
55
44
|
*/
|
|
56
|
-
export declare function previewPatchContent(toolName: string, input: Record<string, unknown>, cwd?: string
|
|
45
|
+
export declare function previewPatchContent(toolName: string, input: Record<string, unknown>, cwd?: string): Promise<ToolCallContent[] | undefined>;
|
|
57
46
|
/**
|
|
58
47
|
* Builds the git patch for a finished Edit or Write from the file on disk.
|
|
59
48
|
*
|
|
@@ -65,7 +54,7 @@ export declare function previewPatchContent(toolName: string, input: Record<stri
|
|
|
65
54
|
* A written file that contains a CR or a byte order mark is declined, because
|
|
66
55
|
* Claude removed those bytes from `originalFile`.
|
|
67
56
|
*/
|
|
68
|
-
export declare function patchUpdateFromDiffToolResponse(toolResponse: unknown
|
|
57
|
+
export declare function patchUpdateFromDiffToolResponse(toolResponse: unknown): Promise<{
|
|
69
58
|
content: ToolCallContent[];
|
|
70
59
|
locations: ToolCallLocation[];
|
|
71
60
|
} | undefined>;
|
|
@@ -80,39 +69,13 @@ export declare function toolUpdateFromDiffToolResponse(toolResponse: unknown): {
|
|
|
80
69
|
content?: ToolCallContent[];
|
|
81
70
|
locations?: ToolCallLocation[];
|
|
82
71
|
};
|
|
83
|
-
/**
|
|
84
|
-
* The ACP v2 diff of a finished Edit or Write: the exact patch of
|
|
85
|
-
* {@link patchUpdateFromDiffToolResponse} when it can be built, else the
|
|
86
|
-
* change with a patch from what the toolResponse holds.
|
|
87
|
-
*
|
|
88
|
-
* The fallback patch of an update comes from the `structuredPatch`, which is
|
|
89
|
-
* display data (see {@link patchUpdateFromDiffToolResponse}). That is enough
|
|
90
|
-
* for a v2 patch, which is renderable text, not a patch to apply. An update
|
|
91
|
-
* without hunks, such as one whose previous content was too large to diff,
|
|
92
|
-
* gets the change alone. An Edit with an empty `old_string` reports the same
|
|
93
|
-
* response for a created file and a filled empty one, so it gets no diff.
|
|
94
|
-
*/
|
|
95
|
-
export declare function v2UpdateFromDiffToolResponse(toolResponse: unknown): Promise<{
|
|
96
|
-
content?: ToolCallContent[];
|
|
97
|
-
locations?: ToolCallLocation[];
|
|
98
|
-
}>;
|
|
99
72
|
/**
|
|
100
73
|
* The text of one git patch for `filePath`.
|
|
101
74
|
*
|
|
102
|
-
* The headers follow `git diff`: the path
|
|
103
|
-
* and
|
|
104
|
-
*
|
|
105
|
-
*/
|
|
106
|
-
export declare function gitPatchText(filePath: string, change: FileChange, hunks: PatchHunk[], paths?: GitPatchPaths): string;
|
|
107
|
-
/**
|
|
108
|
-
* The ACP v2 diff of a change to the text file `filePath`: the change and,
|
|
109
|
-
* given hunks, its git patch.
|
|
110
|
-
*
|
|
111
|
-
* The agent builds tool call content in v1 types, and v1 cannot express a v2
|
|
112
|
-
* diff, so the diff travels as v1 content. Nothing in the agent reads inside a
|
|
113
|
-
* diff, and only a v2 client gets one: the v2 surface sends it as it is
|
|
114
|
-
* (`src/v2/tool-call.ts`).
|
|
75
|
+
* The headers follow `git diff`: the path loses its leading slash, gets the
|
|
76
|
+
* `a/` and `b/` prefixes, and is quoted when git would quote it. A created
|
|
77
|
+
* file gets its mode line and a `/dev/null` side.
|
|
115
78
|
*/
|
|
116
|
-
export declare function
|
|
79
|
+
export declare function gitPatchText(filePath: string, change: FileChange, hunks: PatchHunk[]): string;
|
|
117
80
|
export {};
|
|
118
81
|
//# sourceMappingURL=diff.d.ts.map
|
package/dist/diff.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAQ7E;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,QAAc,CAAC;AAkBhD;;;;;GAKG;AACH,UAAU,SAAS;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AA0BD,0DAA0D;AAC1D,KAAK,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEtC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,EAAE,GAAG,SAAS,CAAC,CAkExC;AA0CD;;;;;;;;;;GAUG;AACH,wBAAsB,+BAA+B,CACnD,YAAY,EAAE,OAAO,GACpB,OAAO,CAAC;IAAE,OAAO,EAAE,eAAe,EAAE,CAAC;IAAC,SAAS,EAAE,gBAAgB,EAAE,CAAA;CAAE,GAAG,SAAS,CAAC,CA0CpF;AAED;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAAC,YAAY,EAAE,OAAO,GAAG;IACrE,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAChC,CAoEA;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,CAM7F"}
|
package/dist/diff.js
CHANGED
|
@@ -37,14 +37,14 @@ const NO_NEWLINE_MARKER = "\";
|
|
|
37
37
|
* normalizes quotes or fails.
|
|
38
38
|
*
|
|
39
39
|
* A Write always gets content that shows the change: the patch, the standard
|
|
40
|
-
* diff
|
|
41
|
-
*
|
|
40
|
+
* diff of a text that cannot have an exact patch, or a notice that the Write
|
|
41
|
+
* overwrites a file that the adapter cannot show.
|
|
42
42
|
*
|
|
43
43
|
* The preview uses the tool input as it is. Claude can still remove the
|
|
44
44
|
* trailing whitespace of a line before it writes. The PostToolUse hook then
|
|
45
45
|
* sends the patch of the written file.
|
|
46
46
|
*/
|
|
47
|
-
export async function previewPatchContent(toolName, input, cwd
|
|
47
|
+
export async function previewPatchContent(toolName, input, cwd) {
|
|
48
48
|
if (toolName === "Edit") {
|
|
49
49
|
const edit = input;
|
|
50
50
|
if (typeof edit.file_path !== "string" ||
|
|
@@ -67,7 +67,7 @@ export async function previewPatchContent(toolName, input, cwd, form = "air") {
|
|
|
67
67
|
if (oldText !== null && (oldText.trim() !== "" || oldText.includes("\uFEFF"))) {
|
|
68
68
|
return undefined;
|
|
69
69
|
}
|
|
70
|
-
return
|
|
70
|
+
return optionalContent(filePatch(filePath, oldText, newString)?.content);
|
|
71
71
|
}
|
|
72
72
|
if (oldText === null)
|
|
73
73
|
return undefined;
|
|
@@ -78,7 +78,7 @@ export async function previewPatchContent(toolName, input, cwd, form = "air") {
|
|
|
78
78
|
return undefined;
|
|
79
79
|
}
|
|
80
80
|
const newText = replacedText(oldText, oldString, newString, edit.replace_all === true);
|
|
81
|
-
return
|
|
81
|
+
return optionalContent(filePatch(filePath, oldText, newText)?.content);
|
|
82
82
|
}
|
|
83
83
|
if (toolName === "Write") {
|
|
84
84
|
const write = normalizeWriteInput(input);
|
|
@@ -104,14 +104,12 @@ export async function previewPatchContent(toolName, input, cwd, form = "air") {
|
|
|
104
104
|
if (oldText === content)
|
|
105
105
|
return undefined;
|
|
106
106
|
const standard = [
|
|
107
|
-
|
|
108
|
-
? v2DiffContent(filePath, oldText === null ? "create" : "update")
|
|
109
|
-
: { type: "diff", path: write.file_path, oldText, newText: content },
|
|
107
|
+
{ type: "diff", path: write.file_path, oldText, newText: content },
|
|
110
108
|
];
|
|
111
109
|
if (!isPatchableText(content))
|
|
112
110
|
return standard;
|
|
113
111
|
// A dense change can exceed the diff budget. The approval then shows the standard diff.
|
|
114
|
-
return
|
|
112
|
+
return optionalContent(filePatch(filePath, oldText, content)?.content) ?? standard;
|
|
115
113
|
}
|
|
116
114
|
return undefined;
|
|
117
115
|
}
|
|
@@ -153,7 +151,7 @@ function replacedText(fileText, oldString, newString, replaceAll) {
|
|
|
153
151
|
* A written file that contains a CR or a byte order mark is declined, because
|
|
154
152
|
* Claude removed those bytes from `originalFile`.
|
|
155
153
|
*/
|
|
156
|
-
export async function patchUpdateFromDiffToolResponse(toolResponse
|
|
154
|
+
export async function patchUpdateFromDiffToolResponse(toolResponse) {
|
|
157
155
|
if (!toolResponse || typeof toolResponse !== "object")
|
|
158
156
|
return undefined;
|
|
159
157
|
const response = toolResponse;
|
|
@@ -187,11 +185,11 @@ export async function patchUpdateFromDiffToolResponse(toolResponse, form = "air"
|
|
|
187
185
|
return undefined;
|
|
188
186
|
if (response.type === "create" && newText !== response.content)
|
|
189
187
|
return undefined;
|
|
190
|
-
const patch = filePatch(oldText, newText);
|
|
188
|
+
const patch = filePatch(response.filePath, oldText, newText);
|
|
191
189
|
if (!patch)
|
|
192
190
|
return undefined;
|
|
193
191
|
return {
|
|
194
|
-
content: [
|
|
192
|
+
content: [patch.content],
|
|
195
193
|
// A created file keeps the location of its Write tool call.
|
|
196
194
|
locations: oldText === null
|
|
197
195
|
? [{ path: response.filePath }]
|
|
@@ -276,75 +274,26 @@ export function toolUpdateFromDiffToolResponse(toolResponse) {
|
|
|
276
274
|
result.locations = locations;
|
|
277
275
|
return result;
|
|
278
276
|
}
|
|
279
|
-
/**
|
|
280
|
-
* The ACP v2 diff of a finished Edit or Write: the exact patch of
|
|
281
|
-
* {@link patchUpdateFromDiffToolResponse} when it can be built, else the
|
|
282
|
-
* change with a patch from what the toolResponse holds.
|
|
283
|
-
*
|
|
284
|
-
* The fallback patch of an update comes from the `structuredPatch`, which is
|
|
285
|
-
* display data (see {@link patchUpdateFromDiffToolResponse}). That is enough
|
|
286
|
-
* for a v2 patch, which is renderable text, not a patch to apply. An update
|
|
287
|
-
* without hunks, such as one whose previous content was too large to diff,
|
|
288
|
-
* gets the change alone. An Edit with an empty `old_string` reports the same
|
|
289
|
-
* response for a created file and a filled empty one, so it gets no diff.
|
|
290
|
-
*/
|
|
291
|
-
export async function v2UpdateFromDiffToolResponse(toolResponse) {
|
|
292
|
-
const exact = await patchUpdateFromDiffToolResponse(toolResponse, "v2");
|
|
293
|
-
if (exact)
|
|
294
|
-
return exact;
|
|
295
|
-
if (!toolResponse || typeof toolResponse !== "object")
|
|
296
|
-
return {};
|
|
297
|
-
const response = toolResponse;
|
|
298
|
-
const filePath = response.filePath;
|
|
299
|
-
if (typeof filePath !== "string")
|
|
300
|
-
return {};
|
|
301
|
-
if (response.type === "create") {
|
|
302
|
-
const hunk = typeof response.content === "string" && isPatchableText(response.content)
|
|
303
|
-
? wholeFileHunk(response.content)
|
|
304
|
-
: undefined;
|
|
305
|
-
return {
|
|
306
|
-
content: [v2DiffContent(filePath, "create", hunk && [hunk])],
|
|
307
|
-
locations: [{ path: filePath }],
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
if (response.type === undefined && response.oldString === "" && response.originalFile === "") {
|
|
311
|
-
return {};
|
|
312
|
-
}
|
|
313
|
-
// A Write whose content is the previous text changed nothing.
|
|
314
|
-
if (response.type === "update" && response.content === response.originalFile)
|
|
315
|
-
return {};
|
|
316
|
-
const hunks = Array.isArray(response.structuredPatch) && response.structuredPatch.length > 0
|
|
317
|
-
? response.structuredPatch
|
|
318
|
-
: undefined;
|
|
319
|
-
return {
|
|
320
|
-
content: [v2DiffContent(filePath, "update", hunks)],
|
|
321
|
-
locations: hunks
|
|
322
|
-
? hunks.map(({ newStart }) => ({ path: filePath, line: newStart }))
|
|
323
|
-
: [{ path: filePath }],
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
277
|
/**
|
|
327
278
|
* The text of one git patch for `filePath`.
|
|
328
279
|
*
|
|
329
|
-
* The headers follow `git diff`: the path
|
|
330
|
-
* and
|
|
331
|
-
*
|
|
280
|
+
* The headers follow `git diff`: the path loses its leading slash, gets the
|
|
281
|
+
* `a/` and `b/` prefixes, and is quoted when git would quote it. A created
|
|
282
|
+
* file gets its mode line and a `/dev/null` side.
|
|
332
283
|
*/
|
|
333
|
-
export function gitPatchText(filePath, change, hunks
|
|
284
|
+
export function gitPatchText(filePath, change, hunks) {
|
|
334
285
|
return [
|
|
335
|
-
...gitPatchHeader(filePath, change
|
|
286
|
+
...gitPatchHeader(filePath, change),
|
|
336
287
|
...hunks.flatMap((hunk) => [hunkHeader(hunk), ...hunk.lines]),
|
|
337
288
|
"",
|
|
338
289
|
].join("\n");
|
|
339
290
|
}
|
|
340
|
-
function gitPatchHeader(filePath, change
|
|
341
|
-
//
|
|
342
|
-
// every platform.
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
const
|
|
346
|
-
const oldName = quoteGitPath(paths === "git" ? "a/" : "", name);
|
|
347
|
-
const newName = quoteGitPath(paths === "git" ? "b/" : "", name);
|
|
291
|
+
function gitPatchHeader(filePath, change) {
|
|
292
|
+
// git drops one leading slash of an absolute path; a Windows path gets
|
|
293
|
+
// forward slashes so the header names the same file on every platform.
|
|
294
|
+
const name = filePath.replaceAll("\\", "/").replace(/^\/+/u, "");
|
|
295
|
+
const oldName = quoteGitPath("a/", name);
|
|
296
|
+
const newName = quoteGitPath("b/", name);
|
|
348
297
|
// git ends a ---/+++ name that contains a space with a tab, for GNU patch.
|
|
349
298
|
const tab = name.includes(" ") ? "\t" : "";
|
|
350
299
|
return [
|
|
@@ -451,15 +400,15 @@ function textLines(text) {
|
|
|
451
400
|
return text.match(/[^\n]*\n|[^\n]+$/g) ?? [];
|
|
452
401
|
}
|
|
453
402
|
/**
|
|
454
|
-
* The patch of a change
|
|
455
|
-
*
|
|
456
|
-
* its time budget.
|
|
403
|
+
* The git patch of a file change and its hunks. Returns undefined when
|
|
404
|
+
* nothing changed or the diff runs out of its time budget.
|
|
457
405
|
*/
|
|
458
|
-
function filePatch(oldText, newText) {
|
|
406
|
+
function filePatch(filePath, oldText, newText) {
|
|
459
407
|
const hunks = diffHunks(oldText, newText);
|
|
460
408
|
if (!hunks || hunks.length === 0)
|
|
461
409
|
return undefined;
|
|
462
|
-
|
|
410
|
+
const change = oldText === null ? "create" : "update";
|
|
411
|
+
return { content: patchContent(filePath, gitPatchText(filePath, change, hunks)), hunks };
|
|
463
412
|
}
|
|
464
413
|
/**
|
|
465
414
|
* Reads a file as patch input.
|
|
@@ -523,16 +472,8 @@ function isPatchableText(text) {
|
|
|
523
472
|
!text.includes("\0") &&
|
|
524
473
|
!text.includes("\r"));
|
|
525
474
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
const patch = filePatch(oldText, newText);
|
|
529
|
-
return patch && [patchContent(filePath, patch, form)];
|
|
530
|
-
}
|
|
531
|
-
/** The content that carries a patch, in the given form. */
|
|
532
|
-
function patchContent(filePath, patch, form) {
|
|
533
|
-
return form === "v2"
|
|
534
|
-
? v2DiffContent(filePath, patch.change, patch.hunks)
|
|
535
|
-
: airPatchContent(filePath, patch);
|
|
475
|
+
function optionalContent(content) {
|
|
476
|
+
return content ? [content] : undefined;
|
|
536
477
|
}
|
|
537
478
|
/**
|
|
538
479
|
* A diff block in the AIR patch form.
|
|
@@ -540,7 +481,7 @@ function patchContent(filePath, patch, form) {
|
|
|
540
481
|
* `oldText: null` and `newText: ""` only satisfy the ACP schema. The adapter
|
|
541
482
|
* sends this form only to a client that advertised `diffPatch`.
|
|
542
483
|
*/
|
|
543
|
-
function
|
|
484
|
+
function patchContent(filePath, text) {
|
|
544
485
|
return {
|
|
545
486
|
type: "diff",
|
|
546
487
|
path: filePath,
|
|
@@ -549,32 +490,10 @@ function airPatchContent(filePath, { change, hunks }) {
|
|
|
549
490
|
_meta: withAirMeta(undefined, AIR_DIFF_PATCH_CAPABILITY, {
|
|
550
491
|
version: 1,
|
|
551
492
|
format: "git_patch",
|
|
552
|
-
text
|
|
493
|
+
text,
|
|
553
494
|
}),
|
|
554
495
|
};
|
|
555
496
|
}
|
|
556
|
-
/**
|
|
557
|
-
* The ACP v2 diff of a change to the text file `filePath`: the change and,
|
|
558
|
-
* given hunks, its git patch.
|
|
559
|
-
*
|
|
560
|
-
* The agent builds tool call content in v1 types, and v1 cannot express a v2
|
|
561
|
-
* diff, so the diff travels as v1 content. Nothing in the agent reads inside a
|
|
562
|
-
* diff, and only a v2 client gets one: the v2 surface sends it as it is
|
|
563
|
-
* (`src/v2/tool-call.ts`).
|
|
564
|
-
*/
|
|
565
|
-
export function v2DiffContent(filePath, change, hunks) {
|
|
566
|
-
const fileChange = change === "create"
|
|
567
|
-
? { operation: "add", path: filePath, fileType: "text" }
|
|
568
|
-
: { operation: "modify", path: filePath, fileType: "text" };
|
|
569
|
-
const diff = {
|
|
570
|
-
type: "diff",
|
|
571
|
-
changes: [fileChange],
|
|
572
|
-
...(hunks?.length
|
|
573
|
-
? { patch: { format: "git_patch", text: gitPatchText(filePath, change, hunks, "absolute") } }
|
|
574
|
-
: {}),
|
|
575
|
-
};
|
|
576
|
-
return diff;
|
|
577
|
-
}
|
|
578
497
|
function resolveToolPath(filePath, cwd) {
|
|
579
498
|
return path.isAbsolute(filePath) ? filePath : path.resolve(cwd ?? process.cwd(), filePath);
|
|
580
499
|
}
|
|
@@ -1,24 +1,12 @@
|
|
|
1
1
|
import type { RequestPermissionRequest, ToolCallContent } from "@agentclientprotocol/sdk";
|
|
2
2
|
import { ClientCapabilities } from "../tool-calls/client-capabilities.js";
|
|
3
|
-
/**
|
|
4
|
-
* A permission request as the agent builds it. For a v2 client it also
|
|
5
|
-
* carries the heading and the explanation of the prompt, which ACP v2 sends as
|
|
6
|
-
* the request's `title` and `description`. v1 has no field for them; AIR gets
|
|
7
|
-
* them in `_meta`.
|
|
8
|
-
*/
|
|
9
|
-
export type AcpPermissionRequest = RequestPermissionRequest & {
|
|
10
|
-
/** The heading of the prompt. It is not the title of the tool call. */
|
|
11
|
-
title?: string;
|
|
12
|
-
/** Why the permission is asked. */
|
|
13
|
-
description?: string;
|
|
14
|
-
};
|
|
15
3
|
export interface ClaudePermissionPresentationInput {
|
|
16
4
|
toolName: string;
|
|
17
5
|
input: Record<string, unknown>;
|
|
18
6
|
toolUseID: string;
|
|
19
7
|
cwd?: string;
|
|
20
8
|
capabilities?: ClientCapabilities;
|
|
21
|
-
/** The exact patch of the change to approve, for a negotiated `diffPatch` client
|
|
9
|
+
/** The exact patch of the change to approve, for a negotiated `diffPatch` client. */
|
|
22
10
|
previewContent?: ToolCallContent[];
|
|
23
11
|
blockedPath?: string;
|
|
24
12
|
title?: string;
|
|
@@ -34,9 +22,6 @@ export interface ClaudePermissionPresentationInput {
|
|
|
34
22
|
* adapter emits the `tool_call` before the request, so the client already
|
|
35
23
|
* holds the rest. The request adds only what it shows new: the exact preview
|
|
36
24
|
* patch, and the blocked path when the tool call has no such location.
|
|
37
|
-
*
|
|
38
|
-
* The heading and the explanation of the prompt go to AIR in `_meta`, and to a
|
|
39
|
-
* v2 client as `title` and `description`.
|
|
40
25
|
*/
|
|
41
|
-
export declare function buildClaudePermissionPresentation(value: ClaudePermissionPresentationInput): Pick<
|
|
26
|
+
export declare function buildClaudePermissionPresentation(value: ClaudePermissionPresentationInput): Pick<RequestPermissionRequest, "toolCall" | "_meta">;
|
|
42
27
|
//# sourceMappingURL=presentation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation.d.ts","sourceRoot":"","sources":["../../src/permissions/presentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE1F,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAG1E
|
|
1
|
+
{"version":3,"file":"presentation.d.ts","sourceRoot":"","sources":["../../src/permissions/presentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE1F,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAG1E,MAAM,WAAW,iCAAiC;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,qFAAqF;IACrF,cAAc,CAAC,EAAE,eAAe,EAAE,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA0BD;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,iCAAiC,GACvC,IAAI,CAAC,wBAAwB,EAAE,UAAU,GAAG,OAAO,CAAC,CAgEtD"}
|
|
@@ -29,9 +29,6 @@ function compactText(value) {
|
|
|
29
29
|
* adapter emits the `tool_call` before the request, so the client already
|
|
30
30
|
* holds the rest. The request adds only what it shows new: the exact preview
|
|
31
31
|
* patch, and the blocked path when the tool call has no such location.
|
|
32
|
-
*
|
|
33
|
-
* The heading and the explanation of the prompt go to AIR in `_meta`, and to a
|
|
34
|
-
* v2 client as `title` and `description`.
|
|
35
32
|
*/
|
|
36
33
|
export function buildClaudePermissionPresentation(value) {
|
|
37
34
|
const capabilities = value.capabilities ?? new ClientCapabilities();
|
|
@@ -92,6 +89,5 @@ export function buildClaudePermissionPresentation(value) {
|
|
|
92
89
|
}),
|
|
93
90
|
}
|
|
94
91
|
: {}),
|
|
95
|
-
...(capabilities.v2 ? { title, ...(description ? { description } : {}) } : {}),
|
|
96
92
|
};
|
|
97
93
|
}
|
|
@@ -15,25 +15,15 @@ export declare function resumedModelFromTranscript(messages: SessionMessage[]):
|
|
|
15
15
|
/** Read the resume model from the local transcript without starting a Claude
|
|
16
16
|
* control request. This is intentionally on the load critical path. */
|
|
17
17
|
export declare function readResumedSession(sessionId: string, logger?: ResumeLogger): Promise<ResumedSessionSnapshot>;
|
|
18
|
-
/** What a resume restores from the end of the local transcript. */
|
|
19
|
-
export type ResumedTail = {
|
|
20
|
-
model?: string;
|
|
21
|
-
/** The last permission mode of the main thread. See {@link TailScan}. */
|
|
22
|
-
permissionMode?: string;
|
|
23
|
-
};
|
|
24
18
|
/**
|
|
25
|
-
* Read the resume model
|
|
26
|
-
* of its local transcript.
|
|
27
|
-
*
|
|
28
|
-
* A resume needs only these two values, not the messages. The transcript file
|
|
29
|
-
* is read backwards until both are known, so the cost does not grow with the
|
|
30
|
-
* length of the session. A session without a local transcript file falls back
|
|
31
|
-
* to {@link readResumedSession} for the model.
|
|
19
|
+
* Read the resume model of a session from the end of its local transcript.
|
|
32
20
|
*
|
|
33
|
-
* A
|
|
34
|
-
*
|
|
21
|
+
* A resume needs only the model, not the messages. The transcript file is
|
|
22
|
+
* read backwards until the last real assistant record of the main thread, so
|
|
23
|
+
* the cost does not grow with the length of the session. A session without a
|
|
24
|
+
* local transcript file falls back to {@link readResumedSession}.
|
|
35
25
|
*/
|
|
36
|
-
export declare function
|
|
26
|
+
export declare function readResumedModel(sessionId: string, logger?: ResumeLogger): Promise<string | undefined>;
|
|
37
27
|
/** The local transcript of a session in any project directory, as the SDK looks it up. */
|
|
38
28
|
export declare function findTranscript(sessionId: string): Promise<string | undefined>;
|
|
39
29
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resumed-session.d.ts","sourceRoot":"","sources":["../src/resumed-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,gCAAgC,CAAC;AASzF,KAAK,YAAY,GAAG;IAClB,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAClC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;iDAGiD;AACjD,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,MAAM,GAAG,SAAS,CAgBzF;AASD;uEACuE;AACvE,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,YAAY,GACpB,OAAO,CAAC,sBAAsB,CAAC,CAejC;AAED
|
|
1
|
+
{"version":3,"file":"resumed-session.d.ts","sourceRoot":"","sources":["../src/resumed-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,gCAAgC,CAAC;AASzF,KAAK,YAAY,GAAG;IAClB,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAClC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;iDAGiD;AACjD,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,MAAM,GAAG,SAAS,CAgBzF;AASD;uEACuE;AACvE,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,YAAY,GACpB,OAAO,CAAC,sBAAsB,CAAC,CAejC;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAc7B;AAED,0FAA0F;AAC1F,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAkBnF"}
|
package/dist/resumed-session.js
CHANGED
|
@@ -52,32 +52,28 @@ export async function readResumedSession(sessionId, logger) {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
|
-
* Read the resume model
|
|
56
|
-
* of its local transcript.
|
|
55
|
+
* Read the resume model of a session from the end of its local transcript.
|
|
57
56
|
*
|
|
58
|
-
* A resume needs only
|
|
59
|
-
*
|
|
60
|
-
* length of the session. A session without a
|
|
61
|
-
* to {@link readResumedSession}
|
|
62
|
-
*
|
|
63
|
-
* A caller that already has the model passes `withModel: false`. The read then
|
|
64
|
-
* looks for the permission mode only and has no fallback.
|
|
57
|
+
* A resume needs only the model, not the messages. The transcript file is
|
|
58
|
+
* read backwards until the last real assistant record of the main thread, so
|
|
59
|
+
* the cost does not grow with the length of the session. A session without a
|
|
60
|
+
* local transcript file falls back to {@link readResumedSession}.
|
|
65
61
|
*/
|
|
66
|
-
export async function
|
|
62
|
+
export async function readResumedModel(sessionId, logger) {
|
|
67
63
|
const timing = new SessionTiming(logger, "models", sessionId);
|
|
68
64
|
try {
|
|
69
65
|
const filePath = await findTranscript(sessionId);
|
|
70
66
|
if (filePath) {
|
|
71
|
-
const
|
|
72
|
-
timing.phase("read-transcript-tail", ` model=${
|
|
73
|
-
return
|
|
67
|
+
const model = await lastAssistantModel(filePath);
|
|
68
|
+
timing.phase("read-transcript-tail", ` model=${model ?? "unknown"}`);
|
|
69
|
+
return model;
|
|
74
70
|
}
|
|
75
71
|
}
|
|
76
72
|
catch (error) {
|
|
77
73
|
timing.phase("read-transcript-tail", " outcome=error");
|
|
78
74
|
logger?.error(`Failed to read the transcript tail of resumed session ${sessionId}:`, error);
|
|
79
75
|
}
|
|
80
|
-
return
|
|
76
|
+
return (await readResumedSession(sessionId, logger)).model;
|
|
81
77
|
}
|
|
82
78
|
/** The local transcript of a session in any project directory, as the SDK looks it up. */
|
|
83
79
|
export async function findTranscript(sessionId) {
|
|
@@ -101,11 +97,9 @@ export async function findTranscript(sessionId) {
|
|
|
101
97
|
}
|
|
102
98
|
return undefined;
|
|
103
99
|
}
|
|
104
|
-
/** The model of the last real assistant record
|
|
105
|
-
|
|
106
|
-
async function lastTailRecords(filePath, withModel) {
|
|
100
|
+
/** The model of the last real assistant record of the main thread in a JSONL transcript. */
|
|
101
|
+
async function lastAssistantModel(filePath) {
|
|
107
102
|
const handle = await open(filePath, "r");
|
|
108
|
-
const scan = new TailScan(withModel);
|
|
109
103
|
try {
|
|
110
104
|
let end = (await handle.stat()).size;
|
|
111
105
|
// The bytes of the line whose start is not read yet, in file order.
|
|
@@ -115,96 +109,36 @@ async function lastTailRecords(filePath, withModel) {
|
|
|
115
109
|
let chunk = Buffer.alloc(end - start);
|
|
116
110
|
await handle.read(chunk, 0, chunk.length, start);
|
|
117
111
|
for (let at = chunk.lastIndexOf(0x0a); at >= 0; at = chunk.lastIndexOf(0x0a)) {
|
|
118
|
-
|
|
119
|
-
|
|
112
|
+
const model = assistantModelOfLine(Buffer.concat([chunk.subarray(at + 1), ...pending]));
|
|
113
|
+
if (model)
|
|
114
|
+
return model;
|
|
120
115
|
pending = [];
|
|
121
116
|
chunk = chunk.subarray(0, at);
|
|
122
117
|
}
|
|
123
118
|
pending.unshift(chunk);
|
|
124
119
|
end = start;
|
|
125
120
|
}
|
|
126
|
-
|
|
127
|
-
return scan.tail;
|
|
121
|
+
return assistantModelOfLine(Buffer.concat(pending));
|
|
128
122
|
}
|
|
129
123
|
finally {
|
|
130
124
|
await handle.close();
|
|
131
125
|
}
|
|
132
126
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
* The `permission-mode` records of the CLI metadata block are not used: they
|
|
138
|
-
* can disagree with the prompts, and the SDK does not write them.
|
|
139
|
-
* The SDK writes no record when it leaves plan mode through ExitPlanMode.
|
|
140
|
-
* It writes a `plan_mode_exit` attachment only, without the new mode. Thus a
|
|
141
|
-
* `plan` mode before a later plan exit is not the current mode, and the scan
|
|
142
|
-
* gives no mode.
|
|
143
|
-
*
|
|
144
|
-
* The mode search stops at the first mode record or at the last human prompt.
|
|
145
|
-
* Each human prompt of a current Claude Code has the mode, so a prompt without
|
|
146
|
-
* it means that the transcript does not record modes. A compaction does not
|
|
147
|
-
* stop the search: the mode of the prompt before it is still current.
|
|
148
|
-
*/
|
|
149
|
-
class TailScan {
|
|
150
|
-
withModel;
|
|
151
|
-
tail = {};
|
|
152
|
-
modeDone = false;
|
|
153
|
-
planExited = false;
|
|
154
|
-
constructor(withModel) {
|
|
155
|
-
this.withModel = withModel;
|
|
156
|
-
}
|
|
157
|
-
/** Visit the previous line. Return true when the scan has all it needs. */
|
|
158
|
-
visit(line) {
|
|
159
|
-
let parsed;
|
|
160
|
-
// Most lines have no marker of interest. The check skips their JSON parse.
|
|
161
|
-
const record = (marker) => {
|
|
162
|
-
if (line.indexOf(marker) < 0)
|
|
163
|
-
return undefined;
|
|
164
|
-
if (parsed === undefined)
|
|
165
|
-
parsed = parseMainThreadRecord(line);
|
|
166
|
-
return parsed ?? undefined;
|
|
167
|
-
};
|
|
168
|
-
if (this.withModel && this.tail.model === undefined) {
|
|
169
|
-
const entry = record('"assistant"');
|
|
170
|
-
if (entry?.type === "assistant")
|
|
171
|
-
this.tail.model = concreteModel(entry.message?.model);
|
|
172
|
-
}
|
|
173
|
-
if (!this.modeDone)
|
|
174
|
-
this.visitForMode(record);
|
|
175
|
-
return (!this.withModel || this.tail.model !== undefined) && this.modeDone;
|
|
176
|
-
}
|
|
177
|
-
visitForMode(record) {
|
|
178
|
-
const modeEntry = record('"permissionMode"');
|
|
179
|
-
if (modeEntry?.type === "user" &&
|
|
180
|
-
typeof modeEntry.permissionMode === "string" &&
|
|
181
|
-
modeEntry.permissionMode.trim() !== "") {
|
|
182
|
-
const mode = modeEntry.permissionMode.trim();
|
|
183
|
-
this.modeDone = true;
|
|
184
|
-
if (!(this.planExited && mode === "plan"))
|
|
185
|
-
this.tail.permissionMode = mode;
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
if (record('"plan_mode_exit"')?.attachment?.type === "plan_mode_exit") {
|
|
189
|
-
this.planExited = true;
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
const human = record('"human"');
|
|
193
|
-
if (human?.type === "user" && human.origin?.kind === "human")
|
|
194
|
-
this.modeDone = true;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
/** The parsed record of one line, or null for a subagent record or a bad line. */
|
|
198
|
-
function parseMainThreadRecord(line) {
|
|
127
|
+
function assistantModelOfLine(line) {
|
|
128
|
+
// Most lines are not assistant records. The check skips their JSON parse.
|
|
129
|
+
if (line.indexOf('"assistant"') < 0)
|
|
130
|
+
return undefined;
|
|
199
131
|
let entry;
|
|
200
132
|
try {
|
|
201
133
|
entry = JSON.parse(line.toString("utf8"));
|
|
202
134
|
}
|
|
203
135
|
catch {
|
|
204
|
-
return
|
|
136
|
+
return undefined;
|
|
205
137
|
}
|
|
206
138
|
if (!entry || typeof entry !== "object")
|
|
207
|
-
return
|
|
139
|
+
return undefined;
|
|
208
140
|
const record = entry;
|
|
209
|
-
|
|
141
|
+
if (record.type !== "assistant" || record.isSidechain === true)
|
|
142
|
+
return undefined;
|
|
143
|
+
return concreteModel(record.message?.model);
|
|
210
144
|
}
|