autopilot-ai 2.1.0 → 2.2.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.
@@ -1213,6 +1213,21 @@ declare function createDefaultPermissionHandler(options: {
1213
1213
 
1214
1214
  declare function startRpcServer(input?: Readable, output?: Writable, createSession?: typeof createAgentSession): Promise<void>;
1215
1215
 
1216
+ /**
1217
+ * The start of a user prompt, the same in every host (TUI, print, emit,
1218
+ * serve, SDK/RPC): SessionStart (once per session), then the veto-able
1219
+ * UserPromptSubmit hook. Observers such as Agent Beacon see the order
1220
+ * SessionStart → UserPromptSubmit → tool events → Stop, always with the
1221
+ * session id, so hosts must have the id before they call this.
1222
+ */
1223
+
1224
+ /** Thrown by non-interactive hosts (SDK, RPC) when a UserPromptSubmit hook blocks the prompt. */
1225
+ declare class PromptBlockedError extends Error {
1226
+ readonly reason: string;
1227
+ readonly code = "prompt_blocked";
1228
+ constructor(reason: string);
1229
+ }
1230
+
1216
1231
  declare function startAsterCellBridge(port?: number): Promise<ReturnType<typeof createServer>>;
1217
1232
  interface AsterCellControlRequest {
1218
1233
  method: "GET" | "POST";
@@ -1468,4 +1483,4 @@ declare class RunWakeScheduler {
1468
1483
 
1469
1484
  declare function resolveSdkConfig(opts: CreateSessionOptions): Promise<KimiConfig>;
1470
1485
 
1471
- export { type AsterCellControlRequest, type ChatMessage, type CreateSessionOptions, DEFAULT_MODEL, DEFAULT_REASONING_EFFORT, type KimiConfig, type KimiFlareSession, type PermissionDecision, type PermissionHandler, type PermissionRequest, type PromptOptions, type RunEvent, type RunEventType, type RunRecord, type RunStatus, RunStore, type RunTimer, type RunWakeEvent, RunWakeScheduler, type RunWakeSchedulerOptions, type SessionEvent, type SessionStatus, type SessionUsage, type Task, type TimerCondition, type TimerStatus, type ToolSpec, createAgentSession, createDefaultPermissionHandler, executeAsterCellControl, loadConfig, resolveSdkConfig, saveConfig, startAsterCellBridge, startRpcServer };
1486
+ export { type AsterCellControlRequest, type ChatMessage, type CreateSessionOptions, DEFAULT_MODEL, DEFAULT_REASONING_EFFORT, type KimiConfig, type KimiFlareSession, type PermissionDecision, type PermissionHandler, type PermissionRequest, PromptBlockedError, type PromptOptions, type RunEvent, type RunEventType, type RunRecord, type RunStatus, RunStore, type RunTimer, type RunWakeEvent, RunWakeScheduler, type RunWakeSchedulerOptions, type SessionEvent, type SessionStatus, type SessionUsage, type Task, type TimerCondition, type TimerStatus, type ToolSpec, createAgentSession, createDefaultPermissionHandler, executeAsterCellControl, loadConfig, resolveSdkConfig, saveConfig, startAsterCellBridge, startRpcServer };
package/dist/sdk/index.js CHANGED
@@ -3175,6 +3175,146 @@ var init_redact = __esm({
3175
3175
  }
3176
3176
  });
3177
3177
 
3178
+ // src/util/line-diff.ts
3179
+ function splitLines(text3) {
3180
+ if (text3 === "") return [];
3181
+ const lines = text3.split("\n");
3182
+ if (lines[lines.length - 1] === "") lines.pop();
3183
+ return lines;
3184
+ }
3185
+ function myers(a, b) {
3186
+ const n = a.length;
3187
+ const m = b.length;
3188
+ const max = n + m;
3189
+ if (max === 0) return [];
3190
+ const offset = max;
3191
+ let v = new Int32Array(2 * max + 2);
3192
+ const trace = [];
3193
+ for (let d = 0; d <= max; d++) {
3194
+ if ((n + m) * d > MAX_MYERS_COST) return null;
3195
+ trace.push(v.slice());
3196
+ const next = v;
3197
+ for (let k = -d; k <= d; k += 2) {
3198
+ let x;
3199
+ if (k === -d || k !== d && next[offset + k - 1] < next[offset + k + 1]) x = next[offset + k + 1];
3200
+ else x = next[offset + k - 1] + 1;
3201
+ let y = x - k;
3202
+ while (x < n && y < m && a[x] === b[y]) {
3203
+ x++;
3204
+ y++;
3205
+ }
3206
+ next[offset + k] = x;
3207
+ if (x >= n && y >= m) {
3208
+ trace.push(next.slice());
3209
+ return backtrack(trace, a, b, offset);
3210
+ }
3211
+ }
3212
+ v = next;
3213
+ }
3214
+ return null;
3215
+ }
3216
+ function backtrack(trace, a, b, offset) {
3217
+ const ops = [];
3218
+ let x = a.length;
3219
+ let y = b.length;
3220
+ for (let d = trace.length - 2; d >= 0; d--) {
3221
+ const v = trace[d];
3222
+ const k = x - y;
3223
+ let prevK;
3224
+ if (k === -d || k !== d && v[offset + k - 1] < v[offset + k + 1]) prevK = k + 1;
3225
+ else prevK = k - 1;
3226
+ const prevX = d === 0 ? 0 : v[offset + prevK];
3227
+ const prevY = prevX - prevK;
3228
+ while (x > prevX && y > prevY) {
3229
+ ops.push({ kind: " ", text: a[x - 1] });
3230
+ x--;
3231
+ y--;
3232
+ }
3233
+ if (d === 0) break;
3234
+ if (x === prevX) ops.push({ kind: "+", text: b[y - 1] });
3235
+ else ops.push({ kind: "-", text: a[x - 1] });
3236
+ x = prevX;
3237
+ y = prevY;
3238
+ }
3239
+ while (x > 0 && y > 0) {
3240
+ ops.push({ kind: " ", text: a[x - 1] });
3241
+ x--;
3242
+ y--;
3243
+ }
3244
+ return ops.reverse();
3245
+ }
3246
+ function editScript(a, b) {
3247
+ let pre = 0;
3248
+ while (pre < a.length && pre < b.length && a[pre] === b[pre]) pre++;
3249
+ let suf = 0;
3250
+ while (suf < a.length - pre && suf < b.length - pre && a[a.length - 1 - suf] === b[b.length - 1 - suf]) suf++;
3251
+ const midA = a.slice(pre, a.length - suf);
3252
+ const midB = b.slice(pre, b.length - suf);
3253
+ const mid = myers(midA, midB) ?? [
3254
+ ...midA.map((text3) => ({ kind: "-", text: text3 })),
3255
+ ...midB.map((text3) => ({ kind: "+", text: text3 }))
3256
+ ];
3257
+ return [
3258
+ ...a.slice(0, pre).map((text3) => ({ kind: " ", text: text3 })),
3259
+ ...mid,
3260
+ ...a.slice(a.length - suf).map((text3) => ({ kind: " ", text: text3 }))
3261
+ ];
3262
+ }
3263
+ function structuredPatch(oldText, newText, context = DEFAULT_CONTEXT) {
3264
+ const ops = editScript(splitLines(oldText), splitLines(newText));
3265
+ const hunks = [];
3266
+ let oldNo = 1;
3267
+ let newNo = 1;
3268
+ const pos = ops.map((op) => {
3269
+ const at = { old: oldNo, new: newNo };
3270
+ if (op.kind !== "+") oldNo++;
3271
+ if (op.kind !== "-") newNo++;
3272
+ return at;
3273
+ });
3274
+ let i = 0;
3275
+ while (i < ops.length) {
3276
+ if (ops[i].kind === " ") {
3277
+ i++;
3278
+ continue;
3279
+ }
3280
+ const start = Math.max(0, i - context);
3281
+ let end = i;
3282
+ for (; ; ) {
3283
+ while (end < ops.length && ops[end].kind !== " ") end++;
3284
+ let gap = end;
3285
+ while (gap < ops.length && ops[gap].kind === " ") gap++;
3286
+ if (gap < ops.length && gap - end <= 2 * context) {
3287
+ end = gap;
3288
+ continue;
3289
+ }
3290
+ end = Math.min(ops.length, end + context);
3291
+ break;
3292
+ }
3293
+ const slice = ops.slice(start, end);
3294
+ const oldLines = slice.filter((o) => o.kind !== "+").length;
3295
+ const newLines = slice.filter((o) => o.kind !== "-").length;
3296
+ const first = pos[start];
3297
+ hunks.push({
3298
+ // jsdiff's convention: a hunk with no old (or new) lines starts one line earlier.
3299
+ oldStart: oldLines === 0 ? first.old - 1 : first.old,
3300
+ oldLines,
3301
+ newStart: newLines === 0 ? first.new - 1 : first.new,
3302
+ newLines,
3303
+ lines: slice.map((o) => o.kind + o.text)
3304
+ });
3305
+ i = end;
3306
+ }
3307
+ return hunks;
3308
+ }
3309
+ var DEFAULT_CONTEXT, MAX_MYERS_COST;
3310
+ var init_line_diff = __esm({
3311
+ "src/util/line-diff.ts"() {
3312
+ "use strict";
3313
+ DEFAULT_CONTEXT = 3;
3314
+ MAX_MYERS_COST = 4e6;
3315
+ }
3316
+ });
3317
+
3178
3318
  // src/hooks/claude-compat.ts
3179
3319
  function claudeToolName(tool, args) {
3180
3320
  if (tool === "edit" && Array.isArray(args.edits)) return "MultiEdit";
@@ -3259,9 +3399,35 @@ function claudeToolResponse(tool, args, cwd, result2) {
3259
3399
  }
3260
3400
  if ((tool === "write" || tool === "edit") && isStr(args.path) && !failed) {
3261
3401
  response.filePath = resolvePath(cwd, args.path);
3402
+ if (result2.fileChange) Object.assign(response, claudeFileChangeFields(tool, args, result2.fileChange));
3262
3403
  }
3263
3404
  return response;
3264
3405
  }
3406
+ function claudeFileChangeFields(tool, args, change) {
3407
+ const fields = {};
3408
+ if (tool === "write") {
3409
+ fields.type = change.existed ? "update" : "create";
3410
+ if (isStr(args.content)) fields.content = args.content;
3411
+ } else if (Array.isArray(args.edits)) {
3412
+ fields.edits = editPairs(args);
3413
+ } else {
3414
+ fields.oldString = args.old_string;
3415
+ fields.newString = args.new_string;
3416
+ fields.replaceAll = args.replace_all === true;
3417
+ }
3418
+ if (!change.existed) {
3419
+ fields.originalFile = null;
3420
+ fields.structuredPatch = [];
3421
+ } else {
3422
+ if (change.original !== null && Buffer.byteLength(change.original, "utf8") <= ORIGINAL_FILE_CAP_BYTES) {
3423
+ fields.originalFile = change.original;
3424
+ }
3425
+ if (change.original !== null && change.updated !== null) {
3426
+ fields.structuredPatch = structuredPatch(change.original, change.updated);
3427
+ }
3428
+ }
3429
+ return fields;
3430
+ }
3265
3431
  function claudeError(result2) {
3266
3432
  if (isNum(result2.exitCode) && result2.exitCode !== 0) {
3267
3433
  const output = [result2.stdout, result2.stderr].filter((s) => s && s.trim()).map((s) => s.trimEnd()).join("\n");
@@ -3294,12 +3460,13 @@ function claudeToolFields(payload) {
3294
3460
  }
3295
3461
  return { tool_name: toolName, ...fields };
3296
3462
  }
3297
- var CLAUDE_TOOL_NAMES, FILE_EDIT_NAMES, isStr, isNum;
3463
+ var CLAUDE_TOOL_NAMES, FILE_EDIT_NAMES, isStr, isNum, ORIGINAL_FILE_CAP_BYTES;
3298
3464
  var init_claude_compat = __esm({
3299
3465
  "src/hooks/claude-compat.ts"() {
3300
3466
  "use strict";
3301
3467
  init_edit();
3302
3468
  init_paths();
3469
+ init_line_diff();
3303
3470
  CLAUDE_TOOL_NAMES = {
3304
3471
  bash: "Bash",
3305
3472
  edit: "Edit",
@@ -3314,6 +3481,7 @@ var init_claude_compat = __esm({
3314
3481
  FILE_EDIT_NAMES = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit"]);
3315
3482
  isStr = (v) => typeof v === "string";
3316
3483
  isNum = (v) => typeof v === "number";
3484
+ ORIGINAL_FILE_CAP_BYTES = 256 * 1024;
3317
3485
  }
3318
3486
  });
3319
3487
 
@@ -3920,6 +4088,10 @@ function hookWirePayload(payload) {
3920
4088
  if (payload.session_id) Object.assign(wire, hookTranscriptFields(payload.session_id));
3921
4089
  if (isToolEventPayload(payload)) {
3922
4090
  Object.assign(wire, claudeToolFields(payload));
4091
+ if ((payload.event === "PostToolUse" || payload.event === "PostToolUseFailure") && payload.result.fileChange) {
4092
+ const { fileChange: _fileChange, ...result2 } = payload.result;
4093
+ wire.result = result2;
4094
+ }
3923
4095
  if (payload.tool_call_id) wire.tool_use_id = payload.tool_call_id;
3924
4096
  if (payload.event === "PermissionRequest") wire.permission_mode = claudePermissionMode(payload.mode);
3925
4097
  }
@@ -4742,7 +4914,7 @@ __export(openrouter_catalog_exports, {
4742
4914
  loadOpenRouterCatalog: () => loadOpenRouterCatalog,
4743
4915
  mapOpenRouterModel: () => mapOpenRouterModel
4744
4916
  });
4745
- import { readFile as readFile16, writeFile as writeFile9, mkdir as mkdir10 } from "fs/promises";
4917
+ import { readFile as readFile17, writeFile as writeFile9, mkdir as mkdir10 } from "fs/promises";
4746
4918
  import { homedir as homedir17 } from "os";
4747
4919
  import { join as join29, dirname as dirname14 } from "path";
4748
4920
  function catalogCachePath() {
@@ -4798,7 +4970,7 @@ async function fetchOpenRouterCatalog(fetchImpl2 = fetch) {
4798
4970
  }
4799
4971
  async function readCache2() {
4800
4972
  try {
4801
- const raw = await readFile16(catalogCachePath(), "utf8");
4973
+ const raw = await readFile17(catalogCachePath(), "utf8");
4802
4974
  const parsed = JSON.parse(raw);
4803
4975
  return parsed.version === CACHE_VERSION ? parsed : null;
4804
4976
  } catch {
@@ -4857,7 +5029,7 @@ __export(requesty_catalog_exports, {
4857
5029
  loadRequestyCatalog: () => loadRequestyCatalog,
4858
5030
  mapRequestyModel: () => mapRequestyModel
4859
5031
  });
4860
- import { readFile as readFile17, writeFile as writeFile10, mkdir as mkdir11 } from "fs/promises";
5032
+ import { readFile as readFile18, writeFile as writeFile10, mkdir as mkdir11 } from "fs/promises";
4861
5033
  import { homedir as homedir18 } from "os";
4862
5034
  import { join as join30, dirname as dirname15 } from "path";
4863
5035
  function catalogCachePath2() {
@@ -4918,7 +5090,7 @@ async function fetchRequestyCatalog(fetchImpl2 = fetch) {
4918
5090
  }
4919
5091
  async function readCache3() {
4920
5092
  try {
4921
- const raw = await readFile17(catalogCachePath2(), "utf8");
5093
+ const raw = await readFile18(catalogCachePath2(), "utf8");
4922
5094
  const parsed = JSON.parse(raw);
4923
5095
  if (parsed.version !== CACHE_VERSION2) return null;
4924
5096
  return {
@@ -4996,7 +5168,7 @@ __export(provider_catalog_exports, {
4996
5168
  mapModelsDevModel: () => mapModelsDevModel,
4997
5169
  mapModelsDevProvider: () => mapModelsDevProvider
4998
5170
  });
4999
- import { readFile as readFile18, writeFile as writeFile11, mkdir as mkdir12 } from "fs/promises";
5171
+ import { readFile as readFile19, writeFile as writeFile11, mkdir as mkdir12 } from "fs/promises";
5000
5172
  import { homedir as homedir19 } from "os";
5001
5173
  import { join as join31, dirname as dirname16 } from "path";
5002
5174
  function mapModelsDevModel(m) {
@@ -5036,7 +5208,7 @@ function cachePath() {
5036
5208
  }
5037
5209
  async function readCache4() {
5038
5210
  try {
5039
- const parsed = JSON.parse(await readFile18(cachePath(), "utf8"));
5211
+ const parsed = JSON.parse(await readFile19(cachePath(), "utf8"));
5040
5212
  return parsed.version === CACHE_VERSION3 ? parsed : null;
5041
5213
  } catch {
5042
5214
  return null;
@@ -5412,6 +5584,89 @@ async function endSessions(reason = "exit", timeoutMs = 3e3) {
5412
5584
  await Promise.all([...[...started.keys()].map((id) => endSession(id, reason, timeoutMs)), ...ending]);
5413
5585
  }
5414
5586
 
5587
+ // src/intent/classify.ts
5588
+ var INTENT_PATTERNS = {
5589
+ qa: /\b(what|how|why|explain|describe|what's|what is)\b/i,
5590
+ diagnose: /\b(broken|failing|error|bug|crash|why.*fail|not working)\b/i,
5591
+ verify: /\b(correct|right|verify|review|check|is this|does this)\b/i,
5592
+ polish: /\b(rename|refactor|extract|move|clean|lint|format)\b/i,
5593
+ small_edit: /\b(add|change|update|fix|remove|delete)\b.+\b(line|here|this|variable|function)\b/i,
5594
+ feature_bounded: /\b(add|implement|create|support)\b.+\b(flag|option|param|arg|field)\b/i,
5595
+ feature_exploratory: /\b(add|implement|migrate|integrate|build)\b.+\b(module|system|auth|oauth|framework|service)\b/i,
5596
+ explore: /\b(how.*work|architecture|structure|where.*used|find.*all|understand)\b/i,
5597
+ meta: /\b(plan|design|strategy|ontology|roadmap|approach)\b/i
5598
+ };
5599
+ var HEAVY_SIGNAL = /\b(research|investigat\w*|audit|deep[\s-]?(dive|thinking)|explorat\w*|comprehensive|end[\s-]?to[\s-]?end|entire\s+(code\s?base|repo\w*|project|app)|whole\s+(code\s?base|repo\w*|project|app)|across\s+the\s+(code\s?base|repo\w*|project)|security|vulnerab\w*|loophole|exploit\w*|threat\s?model|attack\s+surface|penetration)\b/i;
5600
+ function classifyIntent(prompt) {
5601
+ let intentScore = 0;
5602
+ let matchedIntent = "other";
5603
+ for (const [intent, pattern] of Object.entries(INTENT_PATTERNS)) {
5604
+ const matches = (prompt.match(pattern) || []).length;
5605
+ if (matches > intentScore) {
5606
+ intentScore = matches;
5607
+ matchedIntent = intent;
5608
+ }
5609
+ }
5610
+ const hasFileMentions = (prompt.match(/@\w+|\b[\w/-]+\.(ts|tsx|js|jsx|py|go|rs)\b/g) || []).length;
5611
+ const hasMutatingVerb = /\b(add|create|write|edit|delete|remove|rename|migrate|implement)\b/i.test(prompt);
5612
+ const isQuestion = prompt.trim().endsWith("?") || /\b(what|how|why|is|does|can)\b/i.test(prompt.split(" ")[0] || "");
5613
+ const hasHeavySignal = HEAVY_SIGNAL.test(prompt);
5614
+ const rawScore = Math.min(
5615
+ 1,
5616
+ intentScore * 0.25 + (hasFileMentions > 2 ? 0.3 : hasFileMentions * 0.1) + (hasMutatingVerb ? 0.25 : 0) + (isQuestion ? 0 : 0.1) + (hasHeavySignal ? 0.6 : 0)
5617
+ );
5618
+ const tier = rawScore < 0.3 ? "light" : rawScore < 0.65 ? "medium" : "heavy";
5619
+ return {
5620
+ intent: hasHeavySignal && matchedIntent === "other" ? "explore" : matchedIntent,
5621
+ rawScore,
5622
+ tier,
5623
+ confidence: 0.5 + (intentScore > 0 || hasHeavySignal ? 0.3 : 0) + (hasFileMentions > 0 ? 0.1 : 0)
5624
+ };
5625
+ }
5626
+
5627
+ // src/hooks/prompt-submit.ts
5628
+ init_llm_auth();
5629
+ var PROMPT_BLOCKED_DEFAULT_REASON = "UserPromptSubmit hook blocked the prompt";
5630
+ var PromptBlockedError = class extends Error {
5631
+ reason;
5632
+ code = "prompt_blocked";
5633
+ constructor(reason) {
5634
+ super(`prompt blocked by a UserPromptSubmit hook: ${reason}`);
5635
+ this.name = "PromptBlockedError";
5636
+ this.reason = reason;
5637
+ }
5638
+ };
5639
+ async function submitUserPrompt(info) {
5640
+ let provider;
5641
+ try {
5642
+ provider = info.auth ? providerPresetOf(info.auth).id : void 0;
5643
+ } catch {
5644
+ provider = void 0;
5645
+ }
5646
+ await noteSessionStart({
5647
+ ...info.hooks ? { hooks: info.hooks } : {},
5648
+ sessionId: info.sessionId,
5649
+ cwd: info.cwd,
5650
+ resumed: info.resumed,
5651
+ model: info.model,
5652
+ ...provider ? { provider } : {}
5653
+ });
5654
+ if (!info.hooks?.hasEnabledHooks("UserPromptSubmit")) return { blocked: false };
5655
+ const outcome = await info.hooks.fire(
5656
+ "UserPromptSubmit",
5657
+ {
5658
+ event: "UserPromptSubmit",
5659
+ session_id: info.sessionId,
5660
+ cwd: info.cwd,
5661
+ prompt: info.prompt,
5662
+ tier: info.tier ?? classifyIntent(info.prompt).tier
5663
+ },
5664
+ null
5665
+ );
5666
+ if (!outcome.vetoed) return { blocked: false };
5667
+ return { blocked: true, reason: outcome.vetoReason || PROMPT_BLOCKED_DEFAULT_REASON };
5668
+ }
5669
+
5415
5670
  // src/sdk/process-exit.ts
5416
5671
  init_otel_sink();
5417
5672
  init_logger();
@@ -9185,15 +9440,15 @@ async function gitNumstat(cwd, opts = {}) {
9185
9440
  if (opts.includeUntracked) {
9186
9441
  try {
9187
9442
  const { stdout } = await execFileAsync("git", ["ls-files", "--others", "--exclude-standard"], { cwd, timeout: 5e3 });
9188
- const { readFile: readFile20, stat: stat8 } = await import("fs/promises");
9443
+ const { readFile: readFile21, stat: stat9 } = await import("fs/promises");
9189
9444
  const { join: join36 } = await import("path");
9190
9445
  for (const file of stdout.split("\n").filter(Boolean)) {
9191
9446
  let lines = 0;
9192
9447
  try {
9193
9448
  const path = join36(cwd, file);
9194
- const size = (await stat8(path)).size;
9449
+ const size = (await stat9(path)).size;
9195
9450
  if (size > 0 && size <= 1024 * 1024) {
9196
- const text3 = await readFile20(path, "utf8");
9451
+ const text3 = await readFile21(path, "utf8");
9197
9452
  lines = text3.split("\n").length - (text3.endsWith("\n") ? 1 : 0);
9198
9453
  }
9199
9454
  } catch {
@@ -14612,8 +14867,8 @@ function waitForProcessStartTime(pid, jobId, exitPath) {
14612
14867
  function readProcessStartTime(pid) {
14613
14868
  if (process.platform === "linux") {
14614
14869
  try {
14615
- const stat8 = readFileSync6(`/proc/${pid}/stat`, "utf8");
14616
- const fieldsAfterCommand = stat8.slice(stat8.lastIndexOf(")") + 1).trim().split(/\s+/);
14870
+ const stat9 = readFileSync6(`/proc/${pid}/stat`, "utf8");
14871
+ const fieldsAfterCommand = stat9.slice(stat9.lastIndexOf(")") + 1).trim().split(/\s+/);
14617
14872
  return fieldsAfterCommand[19] ? `linux:${fieldsAfterCommand[19]}` : null;
14618
14873
  } catch {
14619
14874
  return null;
@@ -15484,7 +15739,8 @@ var waitForTool = {
15484
15739
  };
15485
15740
 
15486
15741
  // src/tools/executor.ts
15487
- import { realpath as realpath4 } from "fs/promises";
15742
+ init_paths();
15743
+ import { readFile as readFile15, realpath as realpath4, stat as stat6 } from "fs/promises";
15488
15744
  import { isAbsolute as isAbsolute3, relative as relative4, resolve as resolve5, sep as sep3 } from "path";
15489
15745
  var ALL_TOOLS = [
15490
15746
  { ...readTool, isReadOnly: true },
@@ -15537,6 +15793,17 @@ function toPermissionResult(d) {
15537
15793
  return d;
15538
15794
  }
15539
15795
  var HOOK_RESULT_CONTENT_CAP_BYTES = 4 * 1024;
15796
+ var HOOK_FILE_SNAPSHOT_MAX_BYTES = 1024 * 1024;
15797
+ async function snapshotForHook(abs) {
15798
+ try {
15799
+ const st = await stat6(abs);
15800
+ if (!st.isFile()) return { existed: true, content: null };
15801
+ if (st.size > HOOK_FILE_SNAPSHOT_MAX_BYTES) return { existed: true, content: null };
15802
+ return { existed: true, content: await readFile15(abs, "utf8") };
15803
+ } catch {
15804
+ return { existed: false, content: null };
15805
+ }
15806
+ }
15540
15807
  function shellResultForHook(meta) {
15541
15808
  if (!meta || meta.exitCode === void 0) return {};
15542
15809
  return {
@@ -15555,6 +15822,15 @@ function capContentForHook(s) {
15555
15822
  return `${cut}
15556
15823
  [\u2026truncated for hook payload]`;
15557
15824
  }
15825
+ async function fileChangeAfter(tool, args, cwd, before) {
15826
+ let updated = null;
15827
+ if (tool === "write" && typeof args.content === "string") {
15828
+ updated = Buffer.byteLength(args.content, "utf8") <= HOOK_FILE_SNAPSHOT_MAX_BYTES ? args.content : null;
15829
+ } else {
15830
+ updated = (await snapshotForHook(resolvePath(cwd, args.path))).content;
15831
+ }
15832
+ return { existed: before.existed, original: before.content, updated };
15833
+ }
15558
15834
  var ToolExecutor = class {
15559
15835
  sessionAllowed = /* @__PURE__ */ new Set();
15560
15836
  tools;
@@ -15732,8 +16008,10 @@ var ToolExecutor = class {
15732
16008
  runStore.recordToolIntent(ctx.runId, call.id, call.name, args);
15733
16009
  intentRecorded = true;
15734
16010
  }
16011
+ const before = this.wantsFileChange(call.name, args) ? await snapshotForHook(resolvePath(ctx.cwd, args.path)) : void 0;
15735
16012
  const result2 = await tool.run(args, ctx);
15736
16013
  toolCompleted = true;
16014
+ const fileChange = before ? await fileChangeAfter(call.name, args, ctx.cwd, before) : void 0;
15737
16015
  const normalized = normalizeToolOutput(result2);
15738
16016
  tr?.raw(normalized);
15739
16017
  if (runStore && ctx.runId) {
@@ -15785,7 +16063,7 @@ var ToolExecutor = class {
15785
16063
  artifactId: reduced.artifactId,
15786
16064
  ...normalized.waitRequest ? { waitRequest: normalized.waitRequest } : {}
15787
16065
  };
15788
- this.firePostToolUse(call, args, success, ctx, normalized.meta);
16066
+ this.firePostToolUse(call, args, success, ctx, normalized.meta, fileChange);
15789
16067
  return success;
15790
16068
  } catch (e) {
15791
16069
  if (e instanceof RunToolBudgetExceededError) ctx.onRunBudgetExceeded?.(e.reason);
@@ -15856,7 +16134,7 @@ var ToolExecutor = class {
15856
16134
  * `content` field is capped via `capContentForHook` to keep large tool
15857
16135
  * outputs from blowing the OS env-var limits on `KIMIFLARE_HOOK_PAYLOAD`.
15858
16136
  */
15859
- firePostToolUse(call, args, result2, ctx, meta) {
16137
+ firePostToolUse(call, args, result2, ctx, meta, fileChange) {
15860
16138
  const hooks = this.hooks;
15861
16139
  if (!hooks) return;
15862
16140
  const post = hooks.hasEnabledHooks("PostToolUse");
@@ -15866,7 +16144,8 @@ var ToolExecutor = class {
15866
16144
  ok: result2.ok,
15867
16145
  content: capContentForHook(result2.content),
15868
16146
  errorCode: result2.errorCode,
15869
- ...shellResultForHook(meta)
16147
+ ...shellResultForHook(meta),
16148
+ ...fileChange ? { fileChange } : {}
15870
16149
  };
15871
16150
  const base = {
15872
16151
  session_id: ctx.sessionId ?? null,
@@ -15884,6 +16163,11 @@ var ToolExecutor = class {
15884
16163
  void hooks.fire("PostToolUseFailure", { event: "PostToolUseFailure", ...base, error: claudeError(resultFields) }, call.name, ctx.signal).catch(swallow);
15885
16164
  }
15886
16165
  }
16166
+ /** True when a PostToolUse hook would get this write / edit call's diff. */
16167
+ wantsFileChange(tool, args) {
16168
+ if (tool !== "write" && tool !== "edit" || typeof args.path !== "string" || !args.path) return false;
16169
+ return !!this.hooks && (this.hooks.hasEnabledHooks("PostToolUse") || this.hooks.hasEnabledHooks("PostToolUseFailure"));
16170
+ }
15887
16171
  permissionKey(tool, args) {
15888
16172
  if (tool.name === "bash" && typeof args.command === "string") {
15889
16173
  const firstToken = args.command.trim().split(/\s+/)[0] ?? "";
@@ -17602,7 +17886,7 @@ function makeLspTools(manager) {
17602
17886
  init_sessions();
17603
17887
 
17604
17888
  // src/usage-tracker.ts
17605
- import { readFile as readFile15, rename as rename3 } from "fs/promises";
17889
+ import { readFile as readFile16, rename as rename3 } from "fs/promises";
17606
17890
  import { homedir as homedir16 } from "os";
17607
17891
  import { join as join28 } from "path";
17608
17892
  import { EventEmitter as EventEmitter2 } from "events";
@@ -17634,7 +17918,7 @@ init_openrouter();
17634
17918
  init_storage_limits();
17635
17919
 
17636
17920
  // src/util/atomic-file.ts
17637
- import { mkdir as mkdir9, rename as rename2, rm as rm3, stat as stat6, writeFile as writeFile8 } from "fs/promises";
17921
+ import { mkdir as mkdir9, rename as rename2, rm as rm3, stat as stat7, writeFile as writeFile8 } from "fs/promises";
17638
17922
  import { dirname as dirname13 } from "path";
17639
17923
  import { randomUUID as randomUUID6 } from "crypto";
17640
17924
  async function writeFileAtomic(path, data) {
@@ -17661,7 +17945,7 @@ async function withFileLock2(lockPath, fn) {
17661
17945
  held = true;
17662
17946
  } catch (err) {
17663
17947
  if (err.code !== "EEXIST") break;
17664
- const age = await stat6(lockPath).then((s) => Date.now() - s.mtimeMs, () => 0);
17948
+ const age = await stat7(lockPath).then((s) => Date.now() - s.mtimeMs, () => 0);
17665
17949
  if (age > LOCK_STALE_MS) {
17666
17950
  await rm3(lockPath, { recursive: true, force: true }).catch(() => void 0);
17667
17951
  continue;
@@ -17711,7 +17995,7 @@ function cutoffDate(daysBack) {
17711
17995
  async function loadLog() {
17712
17996
  let raw;
17713
17997
  try {
17714
- raw = await readFile15(usagePath(), "utf8");
17998
+ raw = await readFile16(usagePath(), "utf8");
17715
17999
  } catch {
17716
18000
  return { version: LOG_VERSION2, days: [], sessions: [] };
17717
18001
  }
@@ -17737,7 +18021,7 @@ function withLock(fn) {
17737
18021
  }
17738
18022
  async function loadHistory() {
17739
18023
  try {
17740
- const raw = await readFile15(historyPath(), "utf8");
18024
+ const raw = await readFile16(historyPath(), "utf8");
17741
18025
  const lines = raw.split("\n").filter((l) => l.trim());
17742
18026
  const entries = [];
17743
18027
  for (const line of lines) {
@@ -17926,48 +18210,6 @@ init_llm_auth();
17926
18210
 
17927
18211
  // src/agent/session-core.ts
17928
18212
  init_llm_auth();
17929
-
17930
- // src/intent/classify.ts
17931
- var INTENT_PATTERNS = {
17932
- qa: /\b(what|how|why|explain|describe|what's|what is)\b/i,
17933
- diagnose: /\b(broken|failing|error|bug|crash|why.*fail|not working)\b/i,
17934
- verify: /\b(correct|right|verify|review|check|is this|does this)\b/i,
17935
- polish: /\b(rename|refactor|extract|move|clean|lint|format)\b/i,
17936
- small_edit: /\b(add|change|update|fix|remove|delete)\b.+\b(line|here|this|variable|function)\b/i,
17937
- feature_bounded: /\b(add|implement|create|support)\b.+\b(flag|option|param|arg|field)\b/i,
17938
- feature_exploratory: /\b(add|implement|migrate|integrate|build)\b.+\b(module|system|auth|oauth|framework|service)\b/i,
17939
- explore: /\b(how.*work|architecture|structure|where.*used|find.*all|understand)\b/i,
17940
- meta: /\b(plan|design|strategy|ontology|roadmap|approach)\b/i
17941
- };
17942
- var HEAVY_SIGNAL = /\b(research|investigat\w*|audit|deep[\s-]?(dive|thinking)|explorat\w*|comprehensive|end[\s-]?to[\s-]?end|entire\s+(code\s?base|repo\w*|project|app)|whole\s+(code\s?base|repo\w*|project|app)|across\s+the\s+(code\s?base|repo\w*|project)|security|vulnerab\w*|loophole|exploit\w*|threat\s?model|attack\s+surface|penetration)\b/i;
17943
- function classifyIntent(prompt) {
17944
- let intentScore = 0;
17945
- let matchedIntent = "other";
17946
- for (const [intent, pattern] of Object.entries(INTENT_PATTERNS)) {
17947
- const matches = (prompt.match(pattern) || []).length;
17948
- if (matches > intentScore) {
17949
- intentScore = matches;
17950
- matchedIntent = intent;
17951
- }
17952
- }
17953
- const hasFileMentions = (prompt.match(/@\w+|\b[\w/-]+\.(ts|tsx|js|jsx|py|go|rs)\b/g) || []).length;
17954
- const hasMutatingVerb = /\b(add|create|write|edit|delete|remove|rename|migrate|implement)\b/i.test(prompt);
17955
- const isQuestion = prompt.trim().endsWith("?") || /\b(what|how|why|is|does|can)\b/i.test(prompt.split(" ")[0] || "");
17956
- const hasHeavySignal = HEAVY_SIGNAL.test(prompt);
17957
- const rawScore = Math.min(
17958
- 1,
17959
- intentScore * 0.25 + (hasFileMentions > 2 ? 0.3 : hasFileMentions * 0.1) + (hasMutatingVerb ? 0.25 : 0) + (isQuestion ? 0 : 0.1) + (hasHeavySignal ? 0.6 : 0)
17960
- );
17961
- const tier = rawScore < 0.3 ? "light" : rawScore < 0.65 ? "medium" : "heavy";
17962
- return {
17963
- intent: hasHeavySignal && matchedIntent === "other" ? "explore" : matchedIntent,
17964
- rawScore,
17965
- tier,
17966
- confidence: 0.5 + (intentScore > 0 || hasHeavySignal ? 0.3 : 0) + (hasFileMentions > 0 ? 0.1 : 0)
17967
- };
17968
- }
17969
-
17970
- // src/agent/session-core.ts
17971
18213
  function lastUserText(messages) {
17972
18214
  for (let i = messages.length - 1; i >= 0; i--) {
17973
18215
  const m = messages[i];
@@ -18096,7 +18338,7 @@ init_redact();
18096
18338
 
18097
18339
  // src/trace/retention.ts
18098
18340
  init_storage_limits();
18099
- import { readdir as readdir5, rm as rm4, stat as stat7 } from "fs/promises";
18341
+ import { readdir as readdir5, rm as rm4, stat as stat8 } from "fs/promises";
18100
18342
  import { join as join32 } from "path";
18101
18343
 
18102
18344
  // src/trace/session-start.ts
@@ -18411,6 +18653,16 @@ var InternalSession = class {
18411
18653
  return;
18412
18654
  }
18413
18655
  const mode = options?.mode ?? this.currentMode;
18656
+ const gate = await submitUserPrompt({
18657
+ ...this.hooks ? { hooks: this.hooks } : {},
18658
+ sessionId: this.sessionId,
18659
+ cwd: this.cwd,
18660
+ prompt: text3,
18661
+ resumed: this.messages.some((m) => m.role === "assistant"),
18662
+ model: this.model,
18663
+ auth: llmAuthFromConfig(this.config)
18664
+ });
18665
+ if (gate.blocked) throw new PromptBlockedError(gate.reason);
18414
18666
  const messageId = `msg_${this.nextMessageId++}`;
18415
18667
  let userContent2 = text3;
18416
18668
  if (options?.images && options.images.length > 0) {
@@ -18756,7 +19008,7 @@ ${notes.join("\n")}` };
18756
19008
  this.lspManager?.notifyChange(path, content);
18757
19009
  } else {
18758
19010
  void import("fs/promises").then(
18759
- ({ readFile: readFile20 }) => readFile20(path, "utf8").then((c) => this.lspManager?.notifyChange(path, c)).catch(() => {
19011
+ ({ readFile: readFile21 }) => readFile21(path, "utf8").then((c) => this.lspManager?.notifyChange(path, c)).catch(() => {
18760
19012
  })
18761
19013
  ).catch(() => {
18762
19014
  });
@@ -18963,7 +19215,7 @@ async function startRpcServer(input = process.stdin, output = process.stdout, cr
18963
19215
  }
18964
19216
 
18965
19217
  // src/sdk/aster-cell.ts
18966
- import { appendFile as appendFile3, mkdir as mkdir13, readFile as readFile19, unlink as unlink3, writeFile as writeFile12 } from "fs/promises";
19218
+ import { appendFile as appendFile3, mkdir as mkdir13, readFile as readFile20, unlink as unlink3, writeFile as writeFile12 } from "fs/promises";
18967
19219
  import { createServer as createServer2 } from "http";
18968
19220
  import { PassThrough, Writable } from "stream";
18969
19221
  import { join as join35, resolve as resolvePath2 } from "path";
@@ -19162,13 +19414,13 @@ async function readCommand(req) {
19162
19414
  async function readState() {
19163
19415
  let state;
19164
19416
  try {
19165
- state = JSON.parse(await readFile19(TURN_STATE, "utf8"));
19417
+ state = JSON.parse(await readFile20(TURN_STATE, "utf8"));
19166
19418
  } catch {
19167
19419
  state = { sessionId: null, activeRunId: null, initialized: false, cursor: 0 };
19168
19420
  }
19169
19421
  let last;
19170
19422
  try {
19171
- const lines = (await readFile19(EVENT_LOG, "utf8")).trim().split("\n").filter(Boolean);
19423
+ const lines = (await readFile20(EVENT_LOG, "utf8")).trim().split("\n").filter(Boolean);
19172
19424
  last = lines.length ? JSON.parse(lines.at(-1)) : void 0;
19173
19425
  } catch {
19174
19426
  }
@@ -19196,7 +19448,7 @@ async function persistState(state) {
19196
19448
  async function readEvents(after) {
19197
19449
  let contents;
19198
19450
  try {
19199
- contents = await readFile19(EVENT_LOG, "utf8");
19451
+ contents = await readFile20(EVENT_LOG, "utf8");
19200
19452
  } catch {
19201
19453
  return [];
19202
19454
  }
@@ -19217,7 +19469,7 @@ async function executeAsterCellControl(requestId) {
19217
19469
  if (!CONTROL_UUID_RE.test(requestId)) throw new Error("invalid_control_request");
19218
19470
  const requestPath = resolvePath2(REQUEST_DIR, requestId + ".json");
19219
19471
  if (!requestPath.startsWith(REQUEST_DIR + "/")) throw new Error("invalid_control_request");
19220
- const request = JSON.parse(await readFile19(requestPath, "utf8"));
19472
+ const request = JSON.parse(await readFile20(requestPath, "utf8"));
19221
19473
  await unlink3(requestPath).catch(() => {
19222
19474
  });
19223
19475
  if (request.method === "GET" && request.path.startsWith("/events?after=")) {
@@ -19362,6 +19614,7 @@ var RunWakeScheduler = class {
19362
19614
  export {
19363
19615
  DEFAULT_MODEL,
19364
19616
  DEFAULT_REASONING_EFFORT,
19617
+ PromptBlockedError,
19365
19618
  RunStore,
19366
19619
  RunWakeScheduler,
19367
19620
  createAgentSession,