zidane 6.6.3 → 7.0.0
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/{acp-BQqhozmt.js → acp-Wr8l4IDI.js} +7 -180
- package/dist/acp-Wr8l4IDI.js.map +1 -0
- package/dist/acp-cli.js +6 -6
- package/dist/acp.d.ts +1 -1
- package/dist/acp.d.ts.map +1 -1
- package/dist/acp.js +2 -1
- package/dist/{agent-DTxH7RHW.js → agent-C-camKQW.js} +162 -12
- package/dist/agent-C-camKQW.js.map +1 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +1 -1
- package/dist/{anthropic-DRNRnd6W.js → anthropic-BDOGzhtj.js} +3 -3
- package/dist/{anthropic-DRNRnd6W.js.map → anthropic-BDOGzhtj.js.map} +1 -1
- package/dist/{transcript-anchors-BjH-68k2.js → approvals-CqqxE5Vh.js} +4924 -275
- package/dist/approvals-CqqxE5Vh.js.map +1 -0
- package/dist/{auth-7rv6ZfWk.js → auth-BdwDROYg.js} +12 -8
- package/dist/{auth-7rv6ZfWk.js.map → auth-BdwDROYg.js.map} +1 -1
- package/dist/chat/pure.d.ts +221 -4
- package/dist/chat/pure.d.ts.map +1 -0
- package/dist/chat/pure.js +4 -2
- package/dist/chat.d.ts +6 -2911
- package/dist/chat.js +8 -261
- package/dist/{context-transport-D8yeGdpd.js → context-transport-BqYwzJYE.js} +2 -2
- package/dist/{context-transport-D8yeGdpd.js.map → context-transport-BqYwzJYE.js.map} +1 -1
- package/dist/contexts/daytona.d.ts +2 -1
- package/dist/contexts/daytona.d.ts.map +1 -1
- package/dist/contexts/daytona.js +14 -3
- package/dist/contexts/daytona.js.map +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/contexts/e2b.js +1 -1
- package/dist/daemon-cli.d.ts +1 -0
- package/dist/daemon-cli.js +90 -0
- package/dist/daemon-cli.js.map +1 -0
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +1 -1
- package/dist/extensions.d.ts +2 -2
- package/dist/extensions.js +2 -2
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +4 -4
- package/dist/{index-D0O-QO7Z.d.ts → index-B-FSaM3_.d.ts} +2863 -2375
- package/dist/index-B-FSaM3_.d.ts.map +1 -0
- package/dist/index-DgdzXRPH.d.ts +3590 -0
- package/dist/index-DgdzXRPH.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +14 -1179
- package/dist/ipc-CIv87xuH.js +331 -0
- package/dist/ipc-CIv87xuH.js.map +1 -0
- package/dist/ipc.d.ts +849 -0
- package/dist/ipc.d.ts.map +1 -0
- package/dist/ipc.js +4 -0
- package/dist/json-rpc-rSWMUh9b.js +192 -0
- package/dist/json-rpc-rSWMUh9b.js.map +1 -0
- package/dist/{logger-DBX9uYOw.js → logger--C1VQjwJ.js} +3 -13
- package/dist/logger--C1VQjwJ.js.map +1 -0
- package/dist/logs-view-CWG8aUAG.js +44 -0
- package/dist/logs-view-CWG8aUAG.js.map +1 -0
- package/dist/{mcp-CVtSQTWg.js → mcp-BIWa57eY.js} +3 -3
- package/dist/{mcp-CVtSQTWg.js.map → mcp-BIWa57eY.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-CZMUuKzo.js → messages-21XfHxyF.js} +3 -3
- package/dist/{messages-CZMUuKzo.js.map → messages-21XfHxyF.js.map} +1 -1
- package/dist/{openai-compat-BoTi-9fd.js → openai-compat-DY-AKxCg.js} +2 -2
- package/dist/{openai-compat-BoTi-9fd.js.map → openai-compat-DY-AKxCg.js.map} +1 -1
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/{plugins-onDvTHEm.js → plugins-B531Tz5b.js} +2 -2
- package/dist/{plugins-onDvTHEm.js.map → plugins-B531Tz5b.js.map} +1 -1
- package/dist/plugins.d.ts +1 -1
- package/dist/plugins.js +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/anthropic.js +1 -1
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/arcee.js +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/baseten.js +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/cerebras.js +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/local.js +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai-compat.js +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.js +3 -3
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/xai.js +1 -1
- package/dist/{providers-CmhdtD7w.js → providers-DX91jwir.js} +2 -2
- package/dist/{providers-CmhdtD7w.js.map → providers-DX91jwir.js.map} +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +3 -3
- package/dist/restate.d.ts +1 -1
- package/dist/restate.js +8 -8
- package/dist/restate.js.map +1 -1
- package/dist/server-DJqp9dIR.js +1317 -0
- package/dist/server-DJqp9dIR.js.map +1 -0
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session/sqlite.d.ts.map +1 -1
- package/dist/session/sqlite.js +42 -4
- package/dist/session/sqlite.js.map +1 -1
- package/dist/{session-CyRLSsVa.js → session-K3icbPH9.js} +8 -2
- package/dist/session-K3icbPH9.js.map +1 -0
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/{skills-BMGH_KK1.js → skills-PJyiSRCp.js} +3 -21
- package/dist/skills-PJyiSRCp.js.map +1 -0
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +1 -1
- package/dist/src-DzzhE2YO.js +2461 -0
- package/dist/src-DzzhE2YO.js.map +1 -0
- package/dist/todo-edits-Dw2oO2ON.js +378 -0
- package/dist/todo-edits-Dw2oO2ON.js.map +1 -0
- package/dist/{tool-formatters-NrzD6Fkk.d.ts → tool-formatters-DO56YBBw.d.ts} +104 -4
- package/dist/tool-formatters-DO56YBBw.d.ts.map +1 -0
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-UhC58Tmj.d.ts → transcript-anchors-DXqWWGbq.d.ts} +53 -4
- package/dist/transcript-anchors-DXqWWGbq.d.ts.map +1 -0
- package/dist/tui.d.ts +94 -13
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +2241 -1009
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-mMxDRgLY.js → turn-operations-CZLsNgw-.js} +896 -11
- package/dist/turn-operations-CZLsNgw-.js.map +1 -0
- package/dist/{turn-operations-Cuc9pwiB.d.ts → turn-operations-DKFYWP3O.d.ts} +501 -4
- package/dist/turn-operations-DKFYWP3O.d.ts.map +1 -0
- package/dist/{types-BIarq1qE.js → types-CWgENVLY.js} +24 -2
- package/dist/types-CWgENVLY.js.map +1 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/xml-D5HBhJle.js +22 -0
- package/dist/xml-D5HBhJle.js.map +1 -0
- package/docs/CHAT.md +39 -0
- package/docs/IPC.md +420 -0
- package/docs/TUI.md +62 -0
- package/package.json +8 -2
- package/dist/acp-BQqhozmt.js.map +0 -1
- package/dist/agent-DTxH7RHW.js.map +0 -1
- package/dist/chat.d.ts.map +0 -1
- package/dist/chat.js.map +0 -1
- package/dist/index-D0O-QO7Z.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/inline-autocomplete-D0MPvBaS.js +0 -596
- package/dist/inline-autocomplete-D0MPvBaS.js.map +0 -1
- package/dist/logger-DBX9uYOw.js.map +0 -1
- package/dist/login-CB23LyI1.js +0 -707
- package/dist/login-CB23LyI1.js.map +0 -1
- package/dist/session-CyRLSsVa.js.map +0 -1
- package/dist/skills-BMGH_KK1.js.map +0 -1
- package/dist/tool-formatters-NrzD6Fkk.d.ts.map +0 -1
- package/dist/transcript-anchors-BjH-68k2.js.map +0 -1
- package/dist/transcript-anchors-UhC58Tmj.d.ts.map +0 -1
- package/dist/turn-operations-Cuc9pwiB.d.ts.map +0 -1
- package/dist/turn-operations-mMxDRgLY.js.map +0 -1
- package/dist/types-BIarq1qE.js.map +0 -1
|
@@ -1,596 +0,0 @@
|
|
|
1
|
-
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
2
|
-
import { readdirSync } from "node:fs";
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
|
-
//#region src/chat/commands.ts
|
|
5
|
-
/**
|
|
6
|
-
* Persisted form of output blocks inside a display-only `command` turn:
|
|
7
|
-
* plain blocks → `text` content; `detail` blocks → `tool_result` content
|
|
8
|
-
* with the label/syntax carried in namespaced metadata (rebuilt into the
|
|
9
|
-
* collapsed rendering on reload). Shared by the command emit flush and
|
|
10
|
-
* `ctx.appendNote` so both persist identically.
|
|
11
|
-
*/
|
|
12
|
-
function commandBlocksToContent(blocks) {
|
|
13
|
-
return blocks.map((block) => {
|
|
14
|
-
if (!block.detail && !block.custom) return {
|
|
15
|
-
type: "text",
|
|
16
|
-
text: block.text
|
|
17
|
-
};
|
|
18
|
-
const commandMeta = block.custom ? { custom: {
|
|
19
|
-
renderer: block.custom.renderer,
|
|
20
|
-
...block.custom.data !== void 0 ? { data: block.custom.data } : {}
|
|
21
|
-
} } : {
|
|
22
|
-
...block.detail?.label ? { label: block.detail.label } : {},
|
|
23
|
-
...block.detail?.syntax ? { syntax: block.detail.syntax } : {}
|
|
24
|
-
};
|
|
25
|
-
return {
|
|
26
|
-
type: "tool_result",
|
|
27
|
-
callId: `command_detail_${crypto.randomUUID()}`,
|
|
28
|
-
output: block.text,
|
|
29
|
-
...Object.keys(commandMeta).length > 0 ? { metadata: { command: commandMeta } } : {}
|
|
30
|
-
};
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Map ONE command-output block to its LIVE transcript event — the
|
|
35
|
-
* in-memory mirror of {@link commandBlocksToContent}'s persisted shape
|
|
36
|
-
* (whose replay side lives in `eventsFromTurns`). One definition shared
|
|
37
|
-
* by the command emit flush and the `ctx.appendNote` host impl so live
|
|
38
|
-
* rendering can never drift from reloaded rendering — the exact drift
|
|
39
|
-
* that shipped `custom` blocks rendering only after a reload.
|
|
40
|
-
*
|
|
41
|
-
* Precedence matches the persisted side: `custom` > `detail` > markdown.
|
|
42
|
-
*/
|
|
43
|
-
function commandBlockToEvent(block) {
|
|
44
|
-
if (block.custom) return {
|
|
45
|
-
kind: "custom",
|
|
46
|
-
text: block.text,
|
|
47
|
-
custom: {
|
|
48
|
-
renderer: block.custom.renderer,
|
|
49
|
-
...block.custom.data !== void 0 ? { data: block.custom.data } : {}
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
if (block.detail) return {
|
|
53
|
-
kind: "command-detail",
|
|
54
|
-
text: block.text,
|
|
55
|
-
...block.detail.label ? { label: block.detail.label } : {},
|
|
56
|
-
...block.detail.syntax ? { syntax: block.detail.syntax } : {}
|
|
57
|
-
};
|
|
58
|
-
return {
|
|
59
|
-
kind: "markdown",
|
|
60
|
-
text: block.text,
|
|
61
|
-
streaming: false
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
/** Normalize a command to its ordered step list (`steps` wins over `run`). */
|
|
65
|
-
function commandSteps(command) {
|
|
66
|
-
if (command.steps && command.steps.length > 0) return command.steps;
|
|
67
|
-
if (command.run) return [{ run: command.run }];
|
|
68
|
-
return [];
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Parse a prompt as a command invocation, but only when the command token is
|
|
72
|
-
* the LEADING token of the whole prompt and names a known command. This is
|
|
73
|
-
* what keeps mid-prompt `/skill` mentions routed to the skills provider:
|
|
74
|
-
* `"summarize /research"` is not a command, `"/research foo"` is.
|
|
75
|
-
*
|
|
76
|
-
* `known` is matched case-insensitively; entries may be bare names or
|
|
77
|
-
* `{ name, aliases }` records — an alias hit canonicalizes to the entry's
|
|
78
|
-
* `name` (registered casing), so dispatch-by-name downstream just works.
|
|
79
|
-
*/
|
|
80
|
-
function parseCommandInvocation(prompt, known) {
|
|
81
|
-
const body = prompt.trimStart();
|
|
82
|
-
if (body[0] !== "/") return null;
|
|
83
|
-
const afterSlash = body.slice(1);
|
|
84
|
-
const wsIdx = afterSlash.search(/\s/);
|
|
85
|
-
const typed = (wsIdx === -1 ? afterSlash : afterSlash.slice(0, wsIdx)).toLowerCase();
|
|
86
|
-
if (typed.length === 0) return null;
|
|
87
|
-
let canonical;
|
|
88
|
-
for (const entry of known) {
|
|
89
|
-
const name = typeof entry === "string" ? entry : entry.name;
|
|
90
|
-
if (name.toLowerCase() === typed) {
|
|
91
|
-
canonical = name;
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
if (typeof entry !== "string" && entry.aliases?.some((a) => a.toLowerCase() === typed)) {
|
|
95
|
-
canonical = name;
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
if (canonical === void 0) return null;
|
|
100
|
-
const args = (wsIdx === -1 ? "" : afterSlash.slice(wsIdx + 1)).trim();
|
|
101
|
-
return {
|
|
102
|
-
name: canonical,
|
|
103
|
-
invokedAs: typed,
|
|
104
|
-
args,
|
|
105
|
-
argv: args.length > 0 ? args.split(/\s+/) : []
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Run a command's steps in order, threading `ctx.data`. Stops at the first
|
|
110
|
-
* step that throws (error isolated + returned, not rethrown) or when the
|
|
111
|
-
* signal aborts. Pure orchestration — all I/O lives in the host-supplied
|
|
112
|
-
* `ctx` capabilities.
|
|
113
|
-
*/
|
|
114
|
-
async function executeCommand(command, ctx) {
|
|
115
|
-
const steps = commandSteps(command);
|
|
116
|
-
let ranSteps = 0;
|
|
117
|
-
for (const step of steps) {
|
|
118
|
-
if (ctx.signal.aborted) return {
|
|
119
|
-
ok: false,
|
|
120
|
-
ranSteps,
|
|
121
|
-
aborted: true
|
|
122
|
-
};
|
|
123
|
-
ranSteps++;
|
|
124
|
-
try {
|
|
125
|
-
const result = await step.run(ctx);
|
|
126
|
-
if (result && result.data) Object.assign(ctx.data, result.data);
|
|
127
|
-
} catch (error) {
|
|
128
|
-
if (ctx.signal.aborted) return {
|
|
129
|
-
ok: false,
|
|
130
|
-
ranSteps,
|
|
131
|
-
aborted: true
|
|
132
|
-
};
|
|
133
|
-
return {
|
|
134
|
-
ok: false,
|
|
135
|
-
ranSteps,
|
|
136
|
-
error
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return {
|
|
141
|
-
ok: true,
|
|
142
|
-
ranSteps
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
//#endregion
|
|
146
|
-
//#region src/zod.ts
|
|
147
|
-
function isStandardSchema(value) {
|
|
148
|
-
if (typeof value !== "object" || value === null) return false;
|
|
149
|
-
const marker = value["~standard"];
|
|
150
|
-
return typeof marker === "object" && marker !== null && typeof marker.vendor === "string";
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Resolve a {@link SchemaInput} to the plain JSON Schema object providers
|
|
154
|
-
* expect. Zod schemas convert via the lazily-imported `z.toJSONSchema`
|
|
155
|
-
* (v4 native); other Standard Schema vendors are rejected with a clear
|
|
156
|
-
* error — conversion is vendor-specific and only zod is wired today.
|
|
157
|
-
*/
|
|
158
|
-
async function resolveSchemaInput(schema) {
|
|
159
|
-
if (!isStandardSchema(schema)) return schema;
|
|
160
|
-
const vendor = schema["~standard"].vendor;
|
|
161
|
-
if (vendor !== "zod") throw new Error(`unsupported schema vendor "${vendor}" — pass a zod v4 schema or a plain JSON Schema object`);
|
|
162
|
-
let toJSONSchema;
|
|
163
|
-
try {
|
|
164
|
-
toJSONSchema = (await import("zod")).z.toJSONSchema;
|
|
165
|
-
} catch {
|
|
166
|
-
throw new Error("a zod schema was passed but the \"zod\" package could not be imported — install zod >= 4");
|
|
167
|
-
}
|
|
168
|
-
return zodToJsonSchema(toJSONSchema(schema));
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Normalize a JSON Schema (e.g. from zod v4's z.toJSONSchema()) for use
|
|
172
|
-
* as a ToolSpec.inputSchema.
|
|
173
|
-
*
|
|
174
|
-
* Strips the $schema key that some providers reject.
|
|
175
|
-
*/
|
|
176
|
-
function zodToJsonSchema(jsonSchema) {
|
|
177
|
-
const { $schema, ...rest } = jsonSchema;
|
|
178
|
-
return rest;
|
|
179
|
-
}
|
|
180
|
-
//#endregion
|
|
181
|
-
//#region src/chat/generate-title.ts
|
|
182
|
-
/** Hard cap on the result length. Anything longer is truncated client-side. */
|
|
183
|
-
const TITLE_MAX_CHARS = 60;
|
|
184
|
-
/** Default turn count fed to the model. 10 covers most exchanges without bloat. */
|
|
185
|
-
const DEFAULT_MAX_TURNS = 10;
|
|
186
|
-
/** Tokens budgeted for the model's reply. 64 fits any sane title with headroom. */
|
|
187
|
-
const TITLE_RESPONSE_MAX_TOKENS = 64;
|
|
188
|
-
/** Per-turn body cap when assembling the prompt — keeps the request payload tight. */
|
|
189
|
-
const PROMPT_TURN_CHAR_MAX = 2e3;
|
|
190
|
-
/**
|
|
191
|
-
* System prompt for the titling call.
|
|
192
|
-
*
|
|
193
|
-
* Written as a LABELING task, not a chat turn: handed a bare "generate a
|
|
194
|
-
* title for this conversation" instruction with the transcript inline, a
|
|
195
|
-
* cheap model reads the trailing assistant message and just keeps
|
|
196
|
-
* talking — observed in the wild as a session titled `I don't have a
|
|
197
|
-
* "plan tool" available in this conversation…`, i.e. the model answering
|
|
198
|
-
* the last user turn instead of naming it. The rules below say what the
|
|
199
|
-
* model IS (an annotator, not a participant), and the examples show the
|
|
200
|
-
* shape — including the exact failure case (a transcript whose last turn
|
|
201
|
-
* is a refusal) so the answer-shaped completion has a labeled
|
|
202
|
-
* counter-example to copy instead.
|
|
203
|
-
*/
|
|
204
|
-
const TITLE_SYSTEM_PROMPT = [
|
|
205
|
-
"You label conversations. Each input is a transcript of a session between a user and a coding agent.",
|
|
206
|
-
"Name what the session is ABOUT. You are an annotator, never a participant in the conversation.",
|
|
207
|
-
"",
|
|
208
|
-
"Rules:",
|
|
209
|
-
"- Reply with ONLY the label: no quotes, no markdown, no trailing punctuation, no preamble.",
|
|
210
|
-
`- 3 to 7 words, under ${TITLE_MAX_CHARS} characters, written as a noun phrase or an imperative.`,
|
|
211
|
-
"- Name the user's topic or task using concrete nouns from the transcript: files, tools, features, errors.",
|
|
212
|
-
"- NEVER answer, continue, or comment on the conversation — not even when the last message is a question, a refusal, or an error.",
|
|
213
|
-
"- No first person (\"I\", \"we\"), no \"the user\", and no filler words like conversation, session, chat, request, discussion, help.",
|
|
214
|
-
"",
|
|
215
|
-
"Examples of a transcript and its label:",
|
|
216
|
-
"- user asks why `bun test` hangs after an upgrade, agent finds a stuck worker → Bun test hangs on stuck worker",
|
|
217
|
-
"- user asks for a --json flag on the CLI → Add JSON output flag to the CLI",
|
|
218
|
-
"- user asks about a plan tool, agent replies that no such tool exists → Missing plan tool in the agent toolset"
|
|
219
|
-
].join("\n");
|
|
220
|
-
/**
|
|
221
|
-
* Openers that mark a model reply rather than a label — the model
|
|
222
|
-
* answering the transcript instead of naming it. Treated as a failed
|
|
223
|
-
* generation (see {@link cleanTitle}) so the auto-titler retries on the
|
|
224
|
-
* next run rather than persisting a sentence as the session name.
|
|
225
|
-
*
|
|
226
|
-
* Deliberately conservative: only phrasings that a real title would
|
|
227
|
-
* never start with. Excluded on purpose because they head perfectly good
|
|
228
|
-
* titles: `no` / `there is` ("No JSON output in the CLI"), `ok` / `yes` /
|
|
229
|
-
* `great` ("OK button never fires", "Yes/no confirm regression").
|
|
230
|
-
*
|
|
231
|
-
* The pronoun cases require FOLLOWING WHITESPACE rather than a word
|
|
232
|
-
* boundary — `\b` after `i` also matches punctuation, which rejected
|
|
233
|
-
* "I/O timeout in the docker context". Contractions are matched
|
|
234
|
-
* explicitly instead, with both the straight and typographic apostrophe
|
|
235
|
-
* (models emit U+2019 freely, and `here's` / `let's` have no bare-word
|
|
236
|
-
* alternative to fall back on).
|
|
237
|
-
*/
|
|
238
|
-
const ANSWER_SHAPED_OPENER = /^(?:i\s|i['’](?:m|ve|ll|d)\b|we\s|we['’](?:re|ve|ll)\b|let['’]s\b|let\s+(?:me|us)\b|sure\b|certainly\b|okay\b|sorry\b|thanks\b|unfortunately\b|here['’]s\b|here\s+is\b|as\s+an\b|you\s+(?:can|should|need)\b|it\s+(?:looks|seems|appears)\b)/i;
|
|
239
|
-
/**
|
|
240
|
-
* Drive the provider's `stream()` once and return a concise title for
|
|
241
|
-
* the conversation represented by `turns`. Throws when:
|
|
242
|
-
* - `turns` contains no text-bearing content (nothing to summarize),
|
|
243
|
-
* - the provider stream completes with no output text,
|
|
244
|
-
* - the model answered the transcript instead of labeling it (see
|
|
245
|
-
* {@link cleanTitle}) — a failed generation the caller retries,
|
|
246
|
-
* - `signal` is aborted (rethrown verbatim).
|
|
247
|
-
*
|
|
248
|
-
* Output is trimmed, single-line, with surrounding quotes / trailing
|
|
249
|
-
* punctuation stripped, and truncated to `TITLE_MAX_CHARS`.
|
|
250
|
-
*/
|
|
251
|
-
async function generateSessionTitle({ provider, model, turns, maxTurns = DEFAULT_MAX_TURNS, signal }) {
|
|
252
|
-
const transcript = renderTitleTranscript(turns, maxTurns);
|
|
253
|
-
if (!transcript) throw new Error("No text content in the recent turns to summarize.");
|
|
254
|
-
const userPrompt = [
|
|
255
|
-
"<transcript>",
|
|
256
|
-
transcript,
|
|
257
|
-
"</transcript>",
|
|
258
|
-
"",
|
|
259
|
-
"Write the label for the session in the transcript above. Label only."
|
|
260
|
-
].join("\n");
|
|
261
|
-
let text = "";
|
|
262
|
-
const result = await provider.stream({
|
|
263
|
-
model,
|
|
264
|
-
system: TITLE_SYSTEM_PROMPT,
|
|
265
|
-
tools: [],
|
|
266
|
-
messages: [provider.userMessage(userPrompt)],
|
|
267
|
-
maxTokens: TITLE_RESPONSE_MAX_TOKENS,
|
|
268
|
-
...signal ? { signal } : {}
|
|
269
|
-
}, { onText: (delta) => {
|
|
270
|
-
text += delta;
|
|
271
|
-
} });
|
|
272
|
-
return cleanTitle(text.trim().length > 0 ? text : result.text);
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Decide whether a session is ready for one-shot automatic titling.
|
|
276
|
-
*
|
|
277
|
-
* Returns `true` iff the conversation has had its first exchange — at
|
|
278
|
-
* least two turns, i.e. the opening user message plus the assistant's
|
|
279
|
-
* reply (this is exactly the "N turns" count the sessions list shows) —
|
|
280
|
-
* AND no explicit title has been set yet. An existing `metadata.title`
|
|
281
|
-
* (a previous auto-generation, or a manual title from the session-details
|
|
282
|
-
* "generate title" action) is never clobbered.
|
|
283
|
-
*
|
|
284
|
-
* Pure + total. The runtime layers its own "is one already in flight?"
|
|
285
|
-
* and "is a provider picked?" guards on top of this; the predicate only
|
|
286
|
-
* encodes the content-level rule so it can be pinned in tests without a
|
|
287
|
-
* mock provider — same rationale as the exported {@link cleanTitle}.
|
|
288
|
-
*/
|
|
289
|
-
function shouldAutoGenerateTitle(turns, metadata) {
|
|
290
|
-
if ((typeof metadata?.title === "string" ? metadata.title.trim() : "").length > 0) return false;
|
|
291
|
-
return turns.length >= 2;
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* Assemble the transcript fed to the titling model: the session's
|
|
295
|
-
* OPENING user turn (when it falls outside the window) followed by the
|
|
296
|
-
* last `maxTurns` turns, separated by an `…` gap marker.
|
|
297
|
-
*
|
|
298
|
-
* The tail alone is not enough. A session's topic is set by the first
|
|
299
|
-
* thing the user asked; after a dozen turns that message is off the
|
|
300
|
-
* window and the model ends up naming whatever tangent the conversation
|
|
301
|
-
* was on when titling fired — or, worse, the assistant's last reply.
|
|
302
|
-
* Anchoring on the opening intent keeps the label about the session.
|
|
303
|
-
*
|
|
304
|
-
* `maxTurns` keeps its meaning (a cap on TRAILING turns); the anchor is
|
|
305
|
-
* one extra entry on top, and only exists when the window actually cut
|
|
306
|
-
* it off. Returns `''` when nothing in the selection carries text.
|
|
307
|
-
*/
|
|
308
|
-
function renderTitleTranscript(turns, maxTurns) {
|
|
309
|
-
const cap = Math.max(1, maxTurns);
|
|
310
|
-
const tail = renderTurnsForPrompt(turns.slice(-cap));
|
|
311
|
-
const opening = turns.slice(0, Math.max(0, turns.length - cap)).find((turn) => turn.role === "user" && textBodyOf(turn).length > 0);
|
|
312
|
-
const anchor = opening ? renderTurnsForPrompt([opening]) : "";
|
|
313
|
-
if (anchor && tail) return `${anchor}\n\n…\n\n${tail}`;
|
|
314
|
-
return anchor || tail;
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* Compact a turn list into a transcript-style prompt:
|
|
318
|
-
*
|
|
319
|
-
* user: …
|
|
320
|
-
* assistant: …
|
|
321
|
-
* user: …
|
|
322
|
-
*
|
|
323
|
-
* Tool calls and tool results are dropped entirely — a cheap title model
|
|
324
|
-
* happily echoes plumbing like `[tool: list_files]` back as "the title"
|
|
325
|
-
* when a marker is the only assistant content it sees. The topical text
|
|
326
|
-
* is what a title needs. Per-turn text is clipped at `PROMPT_TURN_CHAR_MAX`
|
|
327
|
-
* so a single huge code dump doesn't blow the budget.
|
|
328
|
-
*
|
|
329
|
-
* Returns an empty string when the slice has no text-bearing content
|
|
330
|
-
* (e.g. a turn list that's only tool plumbing).
|
|
331
|
-
*
|
|
332
|
-
* Exported for reuse by other silent one-shot helpers (`generate-data`'s
|
|
333
|
-
* optional conversation context) so every side-channel inference compacts
|
|
334
|
-
* history the same way.
|
|
335
|
-
*/
|
|
336
|
-
function renderTurnsForPrompt(turns) {
|
|
337
|
-
const lines = [];
|
|
338
|
-
for (const turn of turns) {
|
|
339
|
-
const body = textBodyOf(turn);
|
|
340
|
-
if (!body) continue;
|
|
341
|
-
const role = turn.role === "assistant" ? "assistant" : turn.role === "user" ? "user" : "system";
|
|
342
|
-
lines.push(`${role}: ${clip(body, PROMPT_TURN_CHAR_MAX)}`);
|
|
343
|
-
}
|
|
344
|
-
return lines.join("\n\n");
|
|
345
|
-
}
|
|
346
|
-
function textBodyOf(turn) {
|
|
347
|
-
const parts = [];
|
|
348
|
-
for (const block of turn.content) if (block.type === "text" && block.text.trim()) parts.push(block.text.trim());
|
|
349
|
-
return parts.join(" ");
|
|
350
|
-
}
|
|
351
|
-
/** Strip one layer of matched surrounding quotes (`"foo"`, `'foo'`, `` `foo` ``, `“foo”`). */
|
|
352
|
-
function unwrapQuotes(s) {
|
|
353
|
-
if (s.length < 2) return s;
|
|
354
|
-
const first = s.charAt(0);
|
|
355
|
-
const last = s.charAt(s.length - 1);
|
|
356
|
-
return first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`" || first === "“" && last === "”" ? s.slice(1, -1).trim() : s;
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* Normalize a model-generated title into the shape we want to persist:
|
|
360
|
-
*
|
|
361
|
-
* - Collapse internal whitespace + take the first line only (some
|
|
362
|
-
* models emit "Title: foo\n\nReason: …" despite instructions).
|
|
363
|
-
* - Strip surrounding quotes, a leading `Title:` / `title -` preamble,
|
|
364
|
-
* and a `user:` / `assistant:` role prefix echoed from the transcript
|
|
365
|
-
* — repeatedly, in any nesting order.
|
|
366
|
-
* - Reject placeholders and answer-shaped replies (see
|
|
367
|
-
* {@link ANSWER_SHAPED_OPENER}) by throwing.
|
|
368
|
-
* - Strip trailing punctuation (`.`, `!`, `?`) — titles read cleaner
|
|
369
|
-
* without it.
|
|
370
|
-
* - Truncate to `TITLE_MAX_CHARS` with a trailing `…` when over.
|
|
371
|
-
*
|
|
372
|
-
* Exported as `cleanTitle` so tests can pin the normalization rules
|
|
373
|
-
* without going through a mock provider.
|
|
374
|
-
*/
|
|
375
|
-
function cleanTitle(raw) {
|
|
376
|
-
let s = raw.split("\n")[0]?.trim() ?? "";
|
|
377
|
-
for (let pass = 0; pass < 4; pass++) {
|
|
378
|
-
const before = s;
|
|
379
|
-
s = unwrapQuotes(s);
|
|
380
|
-
s = s.replace(/^\s*title\s*[:\-–—]\s*/i, "");
|
|
381
|
-
s = s.replace(/^\s*(?:user|assistant|system)\s*:\s*/i, "");
|
|
382
|
-
if (s === before) break;
|
|
383
|
-
}
|
|
384
|
-
s = s.replace(/[.!?]+$/, "").trim();
|
|
385
|
-
if (s.length === 0) throw new Error("Model returned an empty title.");
|
|
386
|
-
if (/^\[.*\]$/.test(s)) throw new Error("Model returned a placeholder instead of a title.");
|
|
387
|
-
if (ANSWER_SHAPED_OPENER.test(s)) throw new Error("Model answered the conversation instead of titling it.");
|
|
388
|
-
return s.length > TITLE_MAX_CHARS ? `${s.slice(0, TITLE_MAX_CHARS - 1)}…` : s;
|
|
389
|
-
}
|
|
390
|
-
function clip(text, max) {
|
|
391
|
-
return text.length > max ? `${text.slice(0, max)}…` : text;
|
|
392
|
-
}
|
|
393
|
-
//#endregion
|
|
394
|
-
//#region src/chat/generate-data.ts
|
|
395
|
-
/** Synthetic forced-choice tool name (never persisted — the call is side-channel only). */
|
|
396
|
-
const DATA_TOOL_NAME = "__data__";
|
|
397
|
-
/** Default cap on conversation turns folded into the context block. */
|
|
398
|
-
const DEFAULT_CONTEXT_TURNS = 12;
|
|
399
|
-
async function generateStructuredData(options) {
|
|
400
|
-
const { provider, model, prompt, signal } = options;
|
|
401
|
-
const spec = {
|
|
402
|
-
name: DATA_TOOL_NAME,
|
|
403
|
-
description: "Return the result. The input of this call IS the answer — it must conform to the schema.",
|
|
404
|
-
inputSchema: await resolveSchemaInput(options.schema)
|
|
405
|
-
};
|
|
406
|
-
const contextTurns = options.turns && options.turns.length > 0 ? options.turns.slice(-(options.maxContextTurns ?? DEFAULT_CONTEXT_TURNS)) : [];
|
|
407
|
-
const transcript = contextTurns.length > 0 ? renderTurnsForPrompt(contextTurns) : "";
|
|
408
|
-
const userPrompt = transcript ? `<conversation>\n${transcript}\n</conversation>\n\n${prompt}` : prompt;
|
|
409
|
-
const payload = (await provider.stream({
|
|
410
|
-
model,
|
|
411
|
-
system: options.system ?? `You produce structured data. Call the ${DATA_TOOL_NAME} tool exactly once with the requested value. No prose.`,
|
|
412
|
-
tools: provider.formatTools([spec]),
|
|
413
|
-
messages: [provider.userMessage(userPrompt)],
|
|
414
|
-
toolChoice: {
|
|
415
|
-
type: "tool",
|
|
416
|
-
name: DATA_TOOL_NAME
|
|
417
|
-
},
|
|
418
|
-
...signal ? { signal } : {}
|
|
419
|
-
}, { onText: () => {} })).toolCalls.find((tc) => tc.name === DATA_TOOL_NAME)?.input;
|
|
420
|
-
if (payload === void 0) throw new Error("generateStructuredData: the model returned no structured payload");
|
|
421
|
-
return payload;
|
|
422
|
-
}
|
|
423
|
-
//#endregion
|
|
424
|
-
//#region src/chat/inline-autocomplete.ts
|
|
425
|
-
/**
|
|
426
|
-
* Inline (ghost-text) autocomplete for the prompt.
|
|
427
|
-
*
|
|
428
|
-
* Distinct from the trigger-based completion popover (`@` files, `/`
|
|
429
|
-
* commands): providers here suggest plain text SUFFIX candidates to
|
|
430
|
-
* append at the cursor — the first renders as dim ghost text, up/down
|
|
431
|
-
* rotates through the rest, Tab accepts. No popover.
|
|
432
|
-
*
|
|
433
|
-
* Providers are queried in registration order with a shared AbortSignal;
|
|
434
|
-
* the engine returns the first provider's non-empty candidate list.
|
|
435
|
-
* Extensions add providers via `ctx.registerInlineAutocompleteProvider`
|
|
436
|
-
* — the built-in path provider ({@link createPathAutocompleteProvider})
|
|
437
|
-
* is always first.
|
|
438
|
-
*/
|
|
439
|
-
/** Default per-provider deadline — a hung provider is treated as "no suggestion". */
|
|
440
|
-
const PROVIDER_DEADLINE_MS = 150;
|
|
441
|
-
/** Hard cap on a provider's `deadlineMs` — past this, waiting for a ghost reads as broken. */
|
|
442
|
-
const MAX_PROVIDER_DEADLINE_MS = 2e3;
|
|
443
|
-
/**
|
|
444
|
-
* Query `providers` in order; the first provider yielding a non-empty
|
|
445
|
-
* candidate list wins (a bare string normalizes to a 1-element list).
|
|
446
|
-
* Provider errors are swallowed (a broken extension provider must not
|
|
447
|
-
* break typing), and each provider races its deadline —
|
|
448
|
-
* {@link PROVIDER_DEADLINE_MS} by default, a provider's own `deadlineMs`
|
|
449
|
-
* (capped at {@link MAX_PROVIDER_DEADLINE_MS}) when declared — so one
|
|
450
|
-
* hung provider can't permanently suppress the ghosts of providers
|
|
451
|
-
* behind it. Returns null when aborted or nothing matched.
|
|
452
|
-
*/
|
|
453
|
-
async function resolveInlineSuggestion(providers, ctx, deadlineMs = PROVIDER_DEADLINE_MS, maxDeadlineMs = MAX_PROVIDER_DEADLINE_MS) {
|
|
454
|
-
const TIMEOUT = Symbol("timeout");
|
|
455
|
-
for (const provider of providers) {
|
|
456
|
-
if (ctx.signal.aborted) return null;
|
|
457
|
-
try {
|
|
458
|
-
let timer;
|
|
459
|
-
const budget = provider.deadlineMs !== void 0 ? Math.min(Math.max(provider.deadlineMs, 1), maxDeadlineMs) : deadlineMs;
|
|
460
|
-
const result = await Promise.race([Promise.resolve(provider.suggest(ctx)), new Promise((r) => {
|
|
461
|
-
timer = setTimeout(r, budget, TIMEOUT);
|
|
462
|
-
})]);
|
|
463
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
464
|
-
if (result === TIMEOUT) continue;
|
|
465
|
-
if (ctx.signal.aborted) return null;
|
|
466
|
-
const suffixes = (typeof result === "string" ? [result] : result ?? []).filter((s) => typeof s === "string" && s.length > 0);
|
|
467
|
-
if (suffixes.length > 0) return {
|
|
468
|
-
suffixes,
|
|
469
|
-
providerId: provider.id
|
|
470
|
-
};
|
|
471
|
-
} catch {}
|
|
472
|
-
}
|
|
473
|
-
return null;
|
|
474
|
-
}
|
|
475
|
-
/**
|
|
476
|
-
* Extract the whitespace-delimited token that ends at `cursor`. Returns
|
|
477
|
-
* null when the cursor sits right after whitespace (nothing to complete).
|
|
478
|
-
*/
|
|
479
|
-
function tokenEndingAtCursor(text, cursor) {
|
|
480
|
-
const match = text.slice(0, cursor).match(/\S+$/);
|
|
481
|
-
return match ? match[0] : null;
|
|
482
|
-
}
|
|
483
|
-
/**
|
|
484
|
-
* Does this token carry an explicit path shape (`/` separator or a `~`
|
|
485
|
-
* prefix)? Path-shaped tokens complete from the first character after
|
|
486
|
-
* the separator; bare tokens (`sr` → `c/`) still complete against the
|
|
487
|
-
* cwd listing but need ≥2 chars first — see the provider.
|
|
488
|
-
*/
|
|
489
|
-
function looksLikePath(token) {
|
|
490
|
-
return token.includes("/") || token.startsWith("~");
|
|
491
|
-
}
|
|
492
|
-
/**
|
|
493
|
-
* Cap on candidates returned for an ambiguous fragment — rotation past
|
|
494
|
-
* a dozen-odd entries stops being faster than typing another character.
|
|
495
|
-
*/
|
|
496
|
-
const MAX_PATH_CANDIDATES = 16;
|
|
497
|
-
function defaultReaddir(dir) {
|
|
498
|
-
return readdirSync(dir, { withFileTypes: true }).map((e) => ({
|
|
499
|
-
name: e.name,
|
|
500
|
-
isDirectory: e.isDirectory()
|
|
501
|
-
}));
|
|
502
|
-
}
|
|
503
|
-
/**
|
|
504
|
-
* Built-in provider: complete filesystem paths under `cwd` (or absolute /
|
|
505
|
-
* `~` paths). Given the token `src/chat/comp`, lists `src/chat/` and
|
|
506
|
-
* returns every matching continuation of `comp` as a candidate list —
|
|
507
|
-
* directories first, alphabetical, `/`-terminated for directories so
|
|
508
|
-
* typing can continue. The host ghosts the first candidate and rotates
|
|
509
|
-
* through the rest on up/down.
|
|
510
|
-
*
|
|
511
|
-
* Bare tokens (no `/` yet) are matched against the cwd listing too, so
|
|
512
|
-
* a directory ghost appears as soon as the first characters of `src`
|
|
513
|
-
* are typed — but only from the second character on, so 1-letter prose
|
|
514
|
-
* words don't ghost-spam.
|
|
515
|
-
*
|
|
516
|
-
* Dotfiles are only offered when the typed fragment itself starts with
|
|
517
|
-
* `.` — the usual completion convention.
|
|
518
|
-
*/
|
|
519
|
-
function createPathAutocompleteProvider(opts = {}) {
|
|
520
|
-
const readdir = opts.io?.readdir ?? defaultReaddir;
|
|
521
|
-
const fallbackCwd = opts.cwd ?? (() => process.cwd());
|
|
522
|
-
return {
|
|
523
|
-
id: "paths",
|
|
524
|
-
suggest(ctx) {
|
|
525
|
-
const token = tokenEndingAtCursor(ctx.text, ctx.cursor);
|
|
526
|
-
if (!token) return null;
|
|
527
|
-
if (!looksLikePath(token) && token.length < 2) return null;
|
|
528
|
-
const expanded = token.startsWith("~") ? join(homedir(), token.slice(1)) : token;
|
|
529
|
-
const endsWithSep = expanded.endsWith("/");
|
|
530
|
-
const dirPart = endsWithSep ? expanded : dirname(expanded);
|
|
531
|
-
const fragment = endsWithSep ? "" : basename(expanded);
|
|
532
|
-
const baseCwd = ctx.cwd || fallbackCwd();
|
|
533
|
-
const dir = isAbsolute(dirPart) ? dirPart : resolve(baseCwd, dirPart);
|
|
534
|
-
let entries;
|
|
535
|
-
try {
|
|
536
|
-
entries = readdir(dir);
|
|
537
|
-
} catch {
|
|
538
|
-
return null;
|
|
539
|
-
}
|
|
540
|
-
const matches = entries.filter((e) => e.name.startsWith(fragment) && (fragment.startsWith(".") || !e.name.startsWith(".")));
|
|
541
|
-
matches.sort((a, b) => a.isDirectory !== b.isDirectory ? a.isDirectory ? -1 : 1 : a.name.localeCompare(b.name));
|
|
542
|
-
const suffixes = matches.map((e) => e.name.slice(fragment.length) + (e.isDirectory ? "/" : "")).filter((s) => s.length > 0).slice(0, MAX_PATH_CANDIDATES);
|
|
543
|
-
return suffixes.length > 0 ? suffixes : null;
|
|
544
|
-
}
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
/**
|
|
548
|
-
* Optimistically transform the current ghost across a buffer edit so it
|
|
549
|
-
* stays visible while the debounced provider re-query is in flight —
|
|
550
|
-
* without this, every keystroke inside a completable token blinks the
|
|
551
|
-
* ghost (hide → 120 ms → reshow).
|
|
552
|
-
*
|
|
553
|
-
* Two edits are reconcilable (everything else returns null and waits for
|
|
554
|
-
* the authoritative result):
|
|
555
|
-
* - typing FORWARD along the suggestion (`bi` → `bin` with ghost
|
|
556
|
-
* `n/`): candidates are filtered to those starting with the typed
|
|
557
|
-
* chars and trimmed;
|
|
558
|
-
* - deleting BACKWARD within the token (`bin` → `bi` with ghost `/`):
|
|
559
|
-
* the deleted chars are re-prepended to every candidate. Guarded to
|
|
560
|
-
* non-whitespace so crossing a token boundary drops the ghost.
|
|
561
|
-
*
|
|
562
|
-
* The patched ghost is provisional — the provider's answer replaces it
|
|
563
|
-
* (and may add candidates the patch couldn't know about).
|
|
564
|
-
*/
|
|
565
|
-
function patchInlineGhost(prev, text, cursor) {
|
|
566
|
-
if (!prev) return null;
|
|
567
|
-
if (text === prev.text && cursor === prev.cursor) return prev;
|
|
568
|
-
if (text.slice(cursor) !== prev.text.slice(prev.cursor)) return null;
|
|
569
|
-
if (cursor > prev.cursor) {
|
|
570
|
-
if (text.slice(0, prev.cursor) !== prev.text.slice(0, prev.cursor)) return null;
|
|
571
|
-
const typed = text.slice(prev.cursor, cursor);
|
|
572
|
-
const shown = prev.suffixes[prev.index];
|
|
573
|
-
const suffixes = prev.suffixes.filter((s) => s.startsWith(typed) && s.length > typed.length).map((s) => s.slice(typed.length));
|
|
574
|
-
if (suffixes.length === 0) return null;
|
|
575
|
-
const shownTrimmed = shown.startsWith(typed) ? shown.slice(typed.length) : null;
|
|
576
|
-
return {
|
|
577
|
-
suffixes,
|
|
578
|
-
index: shownTrimmed !== null ? Math.max(0, suffixes.indexOf(shownTrimmed)) : 0,
|
|
579
|
-
text,
|
|
580
|
-
cursor
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
if (prev.text.slice(0, cursor) !== text.slice(0, cursor)) return null;
|
|
584
|
-
const deleted = prev.text.slice(cursor, prev.cursor);
|
|
585
|
-
if (deleted.length === 0 || /\s/.test(deleted)) return null;
|
|
586
|
-
return {
|
|
587
|
-
suffixes: prev.suffixes.map((s) => deleted + s),
|
|
588
|
-
index: prev.index,
|
|
589
|
-
text,
|
|
590
|
-
cursor
|
|
591
|
-
};
|
|
592
|
-
}
|
|
593
|
-
//#endregion
|
|
594
|
-
export { generateStructuredData as a, shouldAutoGenerateTitle as c, zodToJsonSchema as d, commandBlockToEvent as f, parseCommandInvocation as g, executeCommand as h, tokenEndingAtCursor as i, isStandardSchema as l, commandSteps as m, patchInlineGhost as n, cleanTitle as o, commandBlocksToContent as p, resolveInlineSuggestion as r, generateSessionTitle as s, createPathAutocompleteProvider as t, resolveSchemaInput as u };
|
|
595
|
-
|
|
596
|
-
//# sourceMappingURL=inline-autocomplete-D0MPvBaS.js.map
|