flavor-code 1.3.4 → 1.3.6

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.
@@ -5006,7 +5006,7 @@ async function ensureOnce(options) {
5006
5006
 
5007
5007
  // src/production.ts
5008
5008
  import { readFile as readFile22 } from "fs/promises";
5009
- import { createHash as createHash17, randomUUID as randomUUID18 } from "crypto";
5009
+ import { createHash as createHash17, randomUUID as randomUUID19 } from "crypto";
5010
5010
  import { homedir as homedir2, release as osRelease, tmpdir as tmpdir3, version as osVersion } from "os";
5011
5011
  import { basename as basename6, join as join24, relative as relative20, resolve as resolve27, sep as sep20 } from "path";
5012
5012
 
@@ -7484,11 +7484,18 @@ var DEFAULT_TOOL_OUTPUT_LIMITS = Object.freeze({
7484
7484
  perTurnChars: 2e5
7485
7485
  });
7486
7486
  var PreToolUsePayload = z9.object({
7487
+ // Optional for backwards-compatible hook updatedInput payloads; runtime
7488
+ // emissions always populate it.
7489
+ toolCallId: z9.string().optional(),
7487
7490
  tool: z9.string(),
7488
7491
  input: z9.unknown(),
7489
7492
  agent: z9.enum(["main", "subagent"])
7490
7493
  });
7491
- var PermissionRequestPayload = PreToolUsePayload.extend({ reason: z9.string().optional() });
7494
+ var PermissionRequestPayload = PreToolUsePayload.extend({
7495
+ reason: z9.string().optional(),
7496
+ toolCategory: z9.string(),
7497
+ allowAlways: z9.boolean()
7498
+ });
7492
7499
  var PostToolUsePayload = PreToolUsePayload.extend({ output: z9.unknown() });
7493
7500
  var PostToolUseFailurePayload = PreToolUsePayload.extend({
7494
7501
  error: z9.object({ code: z9.string(), message: z9.string() })
@@ -7605,30 +7612,31 @@ var ToolRuntime = class {
7605
7612
  const tool = this.#tools.get(call.name);
7606
7613
  if (tool === void 0) return { ok: false, error: { code: "unknown_tool", message: `Unknown tool: ${call.name}` } };
7607
7614
  const signal = context.signal ?? new AbortController().signal;
7615
+ const toolCallId = call.id ?? randomUUID7();
7608
7616
  const validation = this.validate(call);
7609
7617
  if (!validation.ok) {
7610
- return this.#fail(call.name, call.input, context.agent, validation.error.code, validation.error.message);
7618
+ return this.#fail(toolCallId, call.name, call.input, context.agent, validation.error.code, validation.error.message);
7611
7619
  }
7612
7620
  let input = validation.input;
7613
7621
  try {
7614
7622
  const pre = await this.#hooks.emit({
7615
7623
  version: 1,
7616
7624
  type: "PreToolUse",
7617
- payload: { tool: tool.name, input, agent: context.agent }
7625
+ payload: { toolCallId, tool: tool.name, input, agent: context.agent }
7618
7626
  });
7619
7627
  if (pre.updatedInput !== void 0) {
7620
7628
  const payload = PreToolUsePayload.parse(pre.updatedInput);
7621
7629
  if (payload.tool !== tool.name || payload.agent !== context.agent) {
7622
- return this.#fail(tool.name, input, context.agent, "invalid_input", "PreToolUse cannot change the tool or agent");
7630
+ return this.#fail(toolCallId, tool.name, input, context.agent, "invalid_input", "PreToolUse cannot change the tool or agent");
7623
7631
  }
7624
7632
  try {
7625
7633
  input = tool.inputSchema.parse(payload.input);
7626
7634
  } catch (error) {
7627
- return this.#fail(tool.name, payload.input, context.agent, "invalid_input", message(error));
7635
+ return this.#fail(toolCallId, tool.name, payload.input, context.agent, "invalid_input", message(error));
7628
7636
  }
7629
7637
  }
7630
7638
  if (pre.decision === "deny") {
7631
- return this.#fail(tool.name, input, context.agent, "hook_denied", pre.reason ?? "Tool use denied by hook");
7639
+ return this.#fail(toolCallId, tool.name, input, context.agent, "hook_denied", pre.reason ?? "Tool use denied by hook");
7632
7640
  }
7633
7641
  const request = {
7634
7642
  agent: context.agent,
@@ -7639,13 +7647,13 @@ var ToolRuntime = class {
7639
7647
  let permission = this.#permissions.decide(request);
7640
7648
  const nonCacheableApproval = request.allowAlways === false || permission.allowAlways === false;
7641
7649
  if (permission.decision === "deny") {
7642
- return this.#fail(tool.name, input, context.agent, "permission_denied", permission.reason ?? "Tool use denied");
7650
+ return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", permission.reason ?? "Tool use denied");
7643
7651
  }
7644
7652
  if (permission.decision === "ask" && context.agent === "main" && !nonCacheableApproval && this.#permissions.mode === "auto" && this.#permissions.profile === "standard" && this.#classify !== void 0) {
7645
7653
  try {
7646
7654
  const classified = await this.#classify(request, signal);
7647
7655
  if (classified.decision === "deny") {
7648
- return this.#fail(tool.name, input, context.agent, "permission_denied", classified.reason ?? "Auto classifier denied tool use");
7656
+ return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", classified.reason ?? "Auto classifier denied tool use");
7649
7657
  }
7650
7658
  if (classified.decision === "allow") permission = classified;
7651
7659
  else {
@@ -7662,13 +7670,21 @@ var ToolRuntime = class {
7662
7670
  const requestDecision = await this.#hooks.emit({
7663
7671
  version: 1,
7664
7672
  type: "PermissionRequest",
7665
- payload: { tool: tool.name, input: tool.permissionInput?.(input) ?? input, agent: context.agent, reason }
7673
+ payload: {
7674
+ toolCallId,
7675
+ tool: tool.name,
7676
+ input: tool.permissionInput?.(input) ?? input,
7677
+ agent: context.agent,
7678
+ reason,
7679
+ toolCategory: getToolCategory(tool.name),
7680
+ allowAlways
7681
+ }
7666
7682
  });
7667
7683
  if (requestDecision.decision === "deny") {
7668
- return this.#fail(tool.name, input, context.agent, "permission_denied", requestDecision.reason ?? "Permission request denied");
7684
+ return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", requestDecision.reason ?? "Permission request denied");
7669
7685
  }
7670
7686
  if (requestDecision.updatedInput !== void 0) {
7671
- return this.#fail(tool.name, input, context.agent, "invalid_input", "PermissionRequest cannot modify an already-authorized tool call");
7687
+ return this.#fail(toolCallId, tool.name, input, context.agent, "invalid_input", "PermissionRequest cannot modify an already-authorized tool call");
7672
7688
  }
7673
7689
  if (this.#hooks.hasListeners("PermissionRequest") && requestDecision.decision === "allow") {
7674
7690
  const ctx = requestDecision.additionalContext ?? "";
@@ -7677,18 +7693,18 @@ var ToolRuntime = class {
7677
7693
  if (allowAlways && category !== "destructive") this.#alwaysAllowed.add(category);
7678
7694
  }
7679
7695
  } else if (context.agent !== "main" && this.#permissions.mode !== "bubble") {
7680
- return this.#fail(tool.name, input, context.agent, "approval_required", reason);
7696
+ return this.#fail(toolCallId, tool.name, input, context.agent, "approval_required", reason);
7681
7697
  } else if (signal.aborted) {
7682
7698
  throw signal.reason;
7683
7699
  } else {
7684
7700
  const category = getToolCategory(tool.name);
7685
7701
  if (allowAlways && this.#alwaysAllowed.has(category)) {
7686
7702
  } else if (this.#approve === void 0) {
7687
- return this.#fail(tool.name, input, context.agent, "permission_denied", reason);
7703
+ return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", reason);
7688
7704
  } else {
7689
7705
  const decision = await this.#approve({ ...request, reason, ...allowAlways ? {} : { allowAlways: false } }, signal);
7690
7706
  if (decision === "deny") {
7691
- return this.#fail(tool.name, input, context.agent, "user_denied", reason);
7707
+ return this.#fail(toolCallId, tool.name, input, context.agent, "user_denied", reason);
7692
7708
  }
7693
7709
  if (decision === "always" && allowAlways && category !== "destructive") {
7694
7710
  this.#alwaysAllowed.add(category);
@@ -7702,7 +7718,7 @@ var ToolRuntime = class {
7702
7718
  try {
7703
7719
  rawOutput = tool.outputSchema === void 0 ? executed : tool.outputSchema.parse(executed);
7704
7720
  } catch (error) {
7705
- return this.#fail(tool.name, input, context.agent, "invalid_output", message(error));
7721
+ return this.#fail(toolCallId, tool.name, input, context.agent, "invalid_output", message(error));
7706
7722
  }
7707
7723
  const declaredPresentation = tool.presentResult?.(rawOutput, input);
7708
7724
  const presentation = declaredPresentation ?? getToolPresentation(rawOutput);
@@ -7713,7 +7729,7 @@ var ToolRuntime = class {
7713
7729
  await this.#hooks.emit({
7714
7730
  version: 1,
7715
7731
  type: "PostToolUse",
7716
- payload: { tool: tool.name, input, agent: context.agent, output }
7732
+ payload: { toolCallId, tool: tool.name, input, agent: context.agent, output }
7717
7733
  });
7718
7734
  return {
7719
7735
  ok: true,
@@ -7723,7 +7739,7 @@ var ToolRuntime = class {
7723
7739
  ...additionalContext === void 0 || additionalContext.length === 0 ? {} : { additionalContext }
7724
7740
  };
7725
7741
  } catch (error) {
7726
- return this.#fail(tool.name, input, context.agent, "tool_error", message(error));
7742
+ return this.#fail(toolCallId, tool.name, input, context.agent, "tool_error", message(error));
7727
7743
  }
7728
7744
  }
7729
7745
  async #limitOutput(tool, output) {
@@ -7771,13 +7787,13 @@ var ToolRuntime = class {
7771
7787
  const limited = await this.#limitOutput(tool, content);
7772
7788
  return typeof limited === "string" ? limited : serializeToolOutput(limited);
7773
7789
  }
7774
- async #fail(tool, input, agent, code, errorMessage2) {
7790
+ async #fail(toolCallId, tool, input, agent, code, errorMessage2) {
7775
7791
  const result = { ok: false, error: { code, message: errorMessage2 } };
7776
7792
  try {
7777
7793
  await this.#hooks.emit({
7778
7794
  version: 1,
7779
7795
  type: "PostToolUseFailure",
7780
- payload: { tool, input, agent, error: result.error }
7796
+ payload: { toolCallId, tool, input, agent, error: result.error }
7781
7797
  });
7782
7798
  } catch {
7783
7799
  }
@@ -8398,6 +8414,7 @@ function isWithin2(root, target) {
8398
8414
 
8399
8415
  // src/hooks/bus.ts
8400
8416
  import { spawn as spawn4 } from "child_process";
8417
+ import { randomUUID as randomUUID9 } from "crypto";
8401
8418
 
8402
8419
  // src/hooks/types.ts
8403
8420
  import { z as z11 } from "zod";
@@ -8464,6 +8481,11 @@ var DEFAULT_MAX_OUTPUT_BYTES = 1048576;
8464
8481
  var HookBus = class {
8465
8482
  #handlers = /* @__PURE__ */ new Map();
8466
8483
  #payloadSchemas = /* @__PURE__ */ new Map();
8484
+ #options;
8485
+ #sequence = 0;
8486
+ constructor(options = {}) {
8487
+ this.#options = options;
8488
+ }
8467
8489
  registerPayloadSchema(type, schema) {
8468
8490
  const registrations = this.#payloadSchemas.get(type) ?? [];
8469
8491
  const registration = { schema };
@@ -8500,7 +8522,9 @@ var HookBus = class {
8500
8522
  async emit(rawEvent, externalSignal) {
8501
8523
  externalSignal?.throwIfAborted();
8502
8524
  let event = HookEventSchema.parse(rawEvent);
8503
- event = { ...event, payload: this.#validatePayload(event.type, event.payload) };
8525
+ const validatedPayload = this.#validatePayload(event.type, event.payload);
8526
+ const envelope = this.#eventEnvelope();
8527
+ event = { ...event, payload: decoratePayload(validatedPayload, envelope) };
8504
8528
  let aggregate2 = { decision: "allow" };
8505
8529
  for (const registered of this.#handlers.get(event.type) ?? []) {
8506
8530
  externalSignal?.throwIfAborted();
@@ -8515,8 +8539,8 @@ var HookBus = class {
8515
8539
  externalSignal?.throwIfAborted();
8516
8540
  if (decision.updatedInput !== void 0) {
8517
8541
  const payload = this.#validatePayload(event.type, decision.updatedInput);
8518
- event = HookEventSchema.parse({ ...event, payload });
8519
- aggregate2 = { ...aggregate2, updatedInput: event.payload };
8542
+ event = HookEventSchema.parse({ ...event, payload: decoratePayload(payload, envelope) });
8543
+ aggregate2 = { ...aggregate2, updatedInput: payload };
8520
8544
  }
8521
8545
  if (decision.additionalContext !== void 0) {
8522
8546
  aggregate2 = {
@@ -8533,6 +8557,15 @@ var HookBus = class {
8533
8557
  const schema = this.#payloadSchemas.get(type)?.at(-1)?.schema;
8534
8558
  return schema ? schema.parse(payload) : zodRecord(payload);
8535
8559
  }
8560
+ #eventEnvelope() {
8561
+ if (this.#options.eventContext === void 0) return void 0;
8562
+ return {
8563
+ ...this.#options.eventContext(),
8564
+ eventId: (this.#options.eventId ?? randomUUID9)(),
8565
+ sequence: ++this.#sequence,
8566
+ timestamp: (this.#options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()))()
8567
+ };
8568
+ }
8536
8569
  async #invoke(registered, event, externalSignal) {
8537
8570
  const timeoutSignal = AbortSignal.timeout(registered.timeoutMs);
8538
8571
  const signal = externalSignal === void 0 ? timeoutSignal : AbortSignal.any([timeoutSignal, externalSignal]);
@@ -8544,6 +8577,9 @@ var HookBus = class {
8544
8577
  return HookDecisionSchema.parse(await Promise.race([invocation, timeout]));
8545
8578
  }
8546
8579
  };
8580
+ function decoratePayload(payload, envelope) {
8581
+ return envelope === void 0 ? payload : { ...payload, ...envelope };
8582
+ }
8547
8583
  function runShellHandler(descriptor, event, signal) {
8548
8584
  return new Promise((resolve28, reject) => {
8549
8585
  const child = spawn4(descriptor.command, [...descriptor.args ?? []], {
@@ -11421,7 +11457,7 @@ function formatPriorGaps(gaps) {
11421
11457
 
11422
11458
  // src/goal/store.ts
11423
11459
  import { constants as constants2 } from "fs";
11424
- import { randomUUID as randomUUID9 } from "crypto";
11460
+ import { randomUUID as randomUUID10 } from "crypto";
11425
11461
  import { lstat as lstat6, mkdir as mkdir11, open as open6, rename as rename4, rm as rm4 } from "fs/promises";
11426
11462
  import { dirname as dirname12, join as join14, relative as relative9, resolve as resolve11, sep as sep9 } from "path";
11427
11463
  var DEFAULT_MAX_BYTES = 1024 * 1024;
@@ -11445,7 +11481,7 @@ var GoalStore = class {
11445
11481
  if (Buffer.byteLength(body) > this.#maxBytes) {
11446
11482
  throw new Error(`Goal state exceeds maximum size of ${this.#maxBytes} bytes`);
11447
11483
  }
11448
- const temporary = join14(this.#root, `.${state.id}.${process.pid}.${randomUUID9()}.tmp`);
11484
+ const temporary = join14(this.#root, `.${state.id}.${process.pid}.${randomUUID10()}.tmp`);
11449
11485
  let handle;
11450
11486
  try {
11451
11487
  handle = await open6(temporary, constants2.O_CREAT | constants2.O_EXCL | constants2.O_WRONLY, 384);
@@ -11558,7 +11594,7 @@ async function ensureWorktreeDirectoryIgnored(root) {
11558
11594
  import { constants as constants3 } from "fs";
11559
11595
  import { lstat as lstat7, mkdir as mkdir13, open as open7, readFile as readFile11, realpath as realpath5, rename as rename5, rm as rm5, stat as stat5 } from "fs/promises";
11560
11596
  import { dirname as dirname13, join as join16, relative as relative10, resolve as resolve13, sep as sep10 } from "path";
11561
- import { randomUUID as randomUUID10 } from "crypto";
11597
+ import { randomUUID as randomUUID11 } from "crypto";
11562
11598
  var LOOP_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
11563
11599
  var DEFAULT_MAX_BYTES2 = 5 * 1024 * 1024;
11564
11600
  var LoopStore = class {
@@ -11589,7 +11625,7 @@ var LoopStore = class {
11589
11625
  const body = `${JSON.stringify(state, null, 2)}
11590
11626
  `;
11591
11627
  if (Buffer.byteLength(body) > this.#maxBytes) throw new Error(`Loop state exceeds maximum size of ${this.#maxBytes} bytes`);
11592
- const temporary = join16(directory, `.state.${process.pid}.${randomUUID10()}.tmp`);
11628
+ const temporary = join16(directory, `.state.${process.pid}.${randomUUID11()}.tmp`);
11593
11629
  let handle;
11594
11630
  try {
11595
11631
  handle = await open7(temporary, constants3.O_CREAT | constants3.O_EXCL | constants3.O_WRONLY, 384);
@@ -11615,7 +11651,7 @@ var LoopStore = class {
11615
11651
  return state;
11616
11652
  } catch (error) {
11617
11653
  if (isCode4(error, "ENOENT")) throw new Error(`Loop "${loopId}" was not found`);
11618
- const quarantine = `${target}.corrupt-${Date.now()}-${randomUUID10().slice(0, 8)}`;
11654
+ const quarantine = `${target}.corrupt-${Date.now()}-${randomUUID11().slice(0, 8)}`;
11619
11655
  await rename5(target, quarantine).catch(() => void 0);
11620
11656
  throw new Error(`Loop "${loopId}" is corrupt and was quarantined: ${message(error)}`);
11621
11657
  }
@@ -14383,7 +14419,7 @@ function createSkillResourceTool(registry) {
14383
14419
  import { constants as constants5 } from "fs";
14384
14420
  import { lstat as lstat9, mkdir as mkdir14, open as open8, readFile as readFile16, readdir as readdir6, realpath as realpath7, rename as rename6, rm as rm6, stat as stat6 } from "fs/promises";
14385
14421
  import { basename as basename3, dirname as dirname15, join as join19, relative as relative13, resolve as resolve18, sep as sep13 } from "path";
14386
- import { randomUUID as randomUUID11 } from "crypto";
14422
+ import { randomUUID as randomUUID12 } from "crypto";
14387
14423
  import { z as z22 } from "zod";
14388
14424
  var SESSION_VERSION = 4;
14389
14425
  var DEFAULT_MAX_SESSION_BYTES = 5 * 1024 * 1024;
@@ -14541,7 +14577,7 @@ var SessionStore = class {
14541
14577
  const body = `${lines.join("\n")}
14542
14578
  `;
14543
14579
  if (Buffer.byteLength(body) > this.#maxBytes) throw new Error(`Session exceeds maximum size of ${this.#maxBytes} bytes`);
14544
- const temporary = join19(this.#sessions, `.${document.sessionId}.${process.pid}.${randomUUID11()}.tmp`);
14580
+ const temporary = join19(this.#sessions, `.${document.sessionId}.${process.pid}.${randomUUID12()}.tmp`);
14545
14581
  let handle;
14546
14582
  try {
14547
14583
  handle = await open8(temporary, constants5.O_CREAT | constants5.O_EXCL | constants5.O_WRONLY, 384);
@@ -14697,7 +14733,7 @@ var SessionStore = class {
14697
14733
  if (!isWithin5(canonicalRoot, canonicalSessions)) throw new Error("Session directory escapes the workspace");
14698
14734
  }
14699
14735
  async #quarantine(path) {
14700
- const target = `${path}.corrupt-${Date.now()}-${randomUUID11().slice(0, 8)}`;
14736
+ const target = `${path}.corrupt-${Date.now()}-${randomUUID12().slice(0, 8)}`;
14701
14737
  await rename6(path, target);
14702
14738
  return target;
14703
14739
  }
@@ -14917,7 +14953,7 @@ function sanitize2(input, seen = /* @__PURE__ */ new WeakSet()) {
14917
14953
 
14918
14954
  // src/sleep/organizer.ts
14919
14955
  import { constants as constants6 } from "fs";
14920
- import { randomUUID as randomUUID12 } from "crypto";
14956
+ import { randomUUID as randomUUID13 } from "crypto";
14921
14957
  import { lstat as lstat10, mkdir as mkdir15, open as open9, readdir as readdir7, realpath as realpath8, rename as rename7, rm as rm7 } from "fs/promises";
14922
14958
  import { isAbsolute as isAbsolute8, join as join20, relative as relative14, resolve as resolve19, sep as sep14 } from "path";
14923
14959
  import { z as z23 } from "zod";
@@ -15074,7 +15110,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
15074
15110
  return (await readdir7(this.#sleepDirectory, { withFileTypes: true })).some((entry2) => entry2.isFile() && entry2.name.startsWith(prefix) && entry2.name.endsWith(".md"));
15075
15111
  }
15076
15112
  async #atomicWrite(path, content) {
15077
- const temporary = join20(this.#sleepDirectory, `.${randomUUID12()}.tmp`);
15113
+ const temporary = join20(this.#sleepDirectory, `.${randomUUID13()}.tmp`);
15078
15114
  let handle;
15079
15115
  try {
15080
15116
  handle = await open9(temporary, constants6.O_CREAT | constants6.O_EXCL | constants6.O_WRONLY, 384);
@@ -15450,7 +15486,7 @@ function computeSessionStats(documents) {
15450
15486
  import { constants as constants7 } from "fs";
15451
15487
  import { mkdir as mkdir16, open as open10, readFile as readFile17, realpath as realpath9, rename as rename8, rm as rm8, stat as stat7 } from "fs/promises";
15452
15488
  import { dirname as dirname16, isAbsolute as isAbsolute9, relative as relative15, resolve as resolve20, sep as sep15 } from "path";
15453
- import { randomUUID as randomUUID13 } from "crypto";
15489
+ import { randomUUID as randomUUID14 } from "crypto";
15454
15490
  import { z as z24 } from "zod";
15455
15491
 
15456
15492
  // src/tools/file-diff.ts
@@ -15803,7 +15839,7 @@ function createPathGuard(workspace) {
15803
15839
  async function atomicWrite(path, content, signal, expectedVersion) {
15804
15840
  abortIfNeeded(signal);
15805
15841
  await mkdir16(dirname16(path), { recursive: true });
15806
- const temporary = `${path}.${randomUUID13()}.tmp`;
15842
+ const temporary = `${path}.${randomUUID14()}.tmp`;
15807
15843
  try {
15808
15844
  const handle = await open10(temporary, constants7.O_CREAT | constants7.O_EXCL | constants7.O_WRONLY, 384);
15809
15845
  try {
@@ -17200,18 +17236,40 @@ function hookAnswersFromUpdatedInput(updatedInput, questions) {
17200
17236
  var QuestionBridge = class {
17201
17237
  #pending;
17202
17238
  #onChange;
17203
- constructor(onChange) {
17239
+ #relay;
17240
+ #active = false;
17241
+ constructor(onChange, relay) {
17204
17242
  this.#onChange = onChange;
17243
+ this.#relay = relay;
17205
17244
  }
17206
17245
  get pending() {
17207
17246
  return this.#pending?.questions;
17208
17247
  }
17209
17248
  ask(questions, signal) {
17210
- if (this.#pending !== void 0) return Promise.reject(new Error("A question is already pending"));
17249
+ if (this.#active) return Promise.reject(new Error("A question is already pending"));
17211
17250
  if (signal.aborted) return Promise.reject(signal.reason);
17251
+ this.#active = true;
17252
+ if (this.#relay !== void 0) return this.#askRelayed(questions, signal);
17253
+ return this.#askLocally(questions, signal);
17254
+ }
17255
+ async #askRelayed(questions, signal) {
17256
+ try {
17257
+ const answers = await this.#relay(questions, signal);
17258
+ if (answers !== void 0) {
17259
+ this.#active = false;
17260
+ return answers;
17261
+ }
17262
+ return await this.#askLocally(questions, signal);
17263
+ } catch (error) {
17264
+ this.#active = false;
17265
+ throw error;
17266
+ }
17267
+ }
17268
+ #askLocally(questions, signal) {
17212
17269
  return new Promise((resolve28, reject) => {
17213
17270
  const onAbort = () => {
17214
17271
  this.#pending = void 0;
17272
+ this.#active = false;
17215
17273
  reject(signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason)));
17216
17274
  };
17217
17275
  signal.addEventListener("abort", onAbort, { once: true });
@@ -17220,12 +17278,14 @@ var QuestionBridge = class {
17220
17278
  resolve: (answers) => {
17221
17279
  signal.removeEventListener("abort", onAbort);
17222
17280
  this.#pending = void 0;
17281
+ this.#active = false;
17223
17282
  this.#onChange?.();
17224
17283
  resolve28(answers);
17225
17284
  },
17226
17285
  reject: (error) => {
17227
17286
  signal.removeEventListener("abort", onAbort);
17228
17287
  this.#pending = void 0;
17288
+ this.#active = false;
17229
17289
  this.#onChange?.();
17230
17290
  reject(error);
17231
17291
  }
@@ -17311,7 +17371,7 @@ function createTodoWriteTool() {
17311
17371
  }
17312
17372
 
17313
17373
  // src/tools/managed.ts
17314
- import { randomUUID as randomUUID14 } from "crypto";
17374
+ import { randomUUID as randomUUID15 } from "crypto";
17315
17375
  import { link as link2, lstat as lstat11, mkdir as mkdir17, readFile as readFile19, readdir as readdir8, unlink as unlink4, writeFile as writeFile10 } from "fs/promises";
17316
17376
  import { dirname as dirname18, join as join21, resolve as resolve22 } from "path";
17317
17377
  import { z as z31 } from "zod";
@@ -17598,7 +17658,7 @@ ${sourceUrl}`
17598
17658
  }
17599
17659
  async function writeExclusive(path, content) {
17600
17660
  await mkdir17(dirname18(path), { recursive: true });
17601
- const temporary = `${path}.${randomUUID14()}.tmp`;
17661
+ const temporary = `${path}.${randomUUID15()}.tmp`;
17602
17662
  try {
17603
17663
  await writeFile10(temporary, content, { encoding: "utf8", flag: "wx", mode: 384 });
17604
17664
  await link2(temporary, path);
@@ -19385,7 +19445,7 @@ function relativeDisplay(workspace, filePath) {
19385
19445
  }
19386
19446
 
19387
19447
  // src/jobs/registry.ts
19388
- import { randomUUID as randomUUID15 } from "crypto";
19448
+ import { randomUUID as randomUUID16 } from "crypto";
19389
19449
  var JobRegistry = class {
19390
19450
  #jobs = /* @__PURE__ */ new Map();
19391
19451
  #maxOutputChars;
@@ -19399,7 +19459,7 @@ var JobRegistry = class {
19399
19459
  }
19400
19460
  create(input) {
19401
19461
  this.#pruneCompleted();
19402
- const id = `job-${randomUUID15().slice(0, 12)}`;
19462
+ const id = `job-${randomUUID16().slice(0, 12)}`;
19403
19463
  const now = (/* @__PURE__ */ new Date()).toISOString();
19404
19464
  let settle;
19405
19465
  const done = new Promise((resolve28) => {
@@ -19525,7 +19585,7 @@ function snapshot2(job) {
19525
19585
  }
19526
19586
 
19527
19587
  // src/terminal/service.ts
19528
- import { randomUUID as randomUUID16 } from "crypto";
19588
+ import { randomUUID as randomUUID17 } from "crypto";
19529
19589
  import { createRequire as nodeCreateRequire } from "module";
19530
19590
  import { isAbsolute as isAbsolute11, relative as relative19, resolve as resolve25, sep as sep19 } from "path";
19531
19591
  var TerminalService = class {
@@ -19543,7 +19603,7 @@ var TerminalService = class {
19543
19603
  open(input) {
19544
19604
  const cwd = workspacePath(this.#root, input.cwd ?? ".");
19545
19605
  const shell = input.shell ?? defaultShell();
19546
- const id = `term-${randomUUID16().slice(0, 12)}`;
19606
+ const id = `term-${randomUUID17().slice(0, 12)}`;
19547
19607
  const pty = this.#factory(shell, input.args ?? [], {
19548
19608
  cwd,
19549
19609
  cols: input.columns ?? 100,
@@ -19769,7 +19829,7 @@ function palSocketAddress(options) {
19769
19829
  }
19770
19830
 
19771
19831
  // src/pals/client.ts
19772
- import { randomUUID as randomUUID17 } from "crypto";
19832
+ import { randomUUID as randomUUID18 } from "crypto";
19773
19833
  import { Socket as Socket3 } from "net";
19774
19834
  var DEFAULT_REQUEST_TIMEOUT_MS = 5e3;
19775
19835
  var DEFAULT_HEARTBEAT_INTERVAL_MS = 1e4;
@@ -19845,7 +19905,7 @@ var PalClient = class {
19845
19905
  return this.starting;
19846
19906
  }
19847
19907
  async list() {
19848
- const data2 = await this.request({ version: 1, type: "list", requestId: randomUUID17() });
19908
+ const data2 = await this.request({ version: 1, type: "list", requestId: randomUUID18() });
19849
19909
  return PalPresenceListSchema.parse(data2);
19850
19910
  }
19851
19911
  async rename(alias) {
@@ -19858,38 +19918,38 @@ var PalClient = class {
19858
19918
  throw error;
19859
19919
  }
19860
19920
  }
19861
- async sendTask(target, message2, messageId = randomUUID17()) {
19862
- const data2 = await this.request({ version: 1, type: "task", requestId: randomUUID17(), messageId, target, goal: message2 });
19921
+ async sendTask(target, message2, messageId = randomUUID18()) {
19922
+ const data2 = await this.request({ version: 1, type: "task", requestId: randomUUID18(), messageId, target, goal: message2 });
19863
19923
  return DeliveryReceiptSchema.parse(data2);
19864
19924
  }
19865
- async sendChat(target, message2, messageId = randomUUID17()) {
19866
- const data2 = await this.request({ version: 1, type: "chat", requestId: randomUUID17(), messageId, target, message: message2 });
19925
+ async sendChat(target, message2, messageId = randomUUID18()) {
19926
+ const data2 = await this.request({ version: 1, type: "chat", requestId: randomUUID18(), messageId, target, message: message2 });
19867
19927
  return DeliveryReceiptSchema.parse(data2);
19868
19928
  }
19869
19929
  async startCoWork(input) {
19870
19930
  const data2 = await this.request({
19871
19931
  version: 1,
19872
19932
  type: "cowork-propose",
19873
- requestId: randomUUID17(),
19874
- coWorkId: input.coWorkId ?? randomUUID17(),
19933
+ requestId: randomUUID18(),
19934
+ coWorkId: input.coWorkId ?? randomUUID18(),
19875
19935
  goal: input.goal,
19876
19936
  participants: input.participants
19877
19937
  });
19878
19938
  return CoWorkSnapshotSchema.parse(data2);
19879
19939
  }
19880
19940
  async coWorkAction(action) {
19881
- const request = { version: 1, requestId: randomUUID17(), ...action };
19941
+ const request = { version: 1, requestId: randomUUID18(), ...action };
19882
19942
  return CoWorkSnapshotSchema.parse(await this.request(request));
19883
19943
  }
19884
19944
  async coWorkStatus(coWorkId) {
19885
- const data2 = await this.request({ version: 1, type: "cowork-get", requestId: randomUUID17(), coWorkId });
19945
+ const data2 = await this.request({ version: 1, type: "cowork-get", requestId: randomUUID18(), coWorkId });
19886
19946
  return CoWorkSnapshotSchema.parse(data2);
19887
19947
  }
19888
19948
  async integrateCoWork(input) {
19889
19949
  return this.coWorkAction({ type: "cowork-integration", ...input });
19890
19950
  }
19891
19951
  async cancelCoWork(coWorkId, reason) {
19892
- const data2 = await this.request({ version: 1, type: "cowork-cancel", requestId: randomUUID17(), coWorkId, reason });
19952
+ const data2 = await this.request({ version: 1, type: "cowork-cancel", requestId: randomUUID18(), coWorkId, reason });
19893
19953
  return CoWorkSnapshotSchema.parse(data2);
19894
19954
  }
19895
19955
  subscribe(listener) {
@@ -19901,7 +19961,7 @@ var PalClient = class {
19901
19961
  if (this.registered && this.socket !== void 0) {
19902
19962
  let disconnectTimer;
19903
19963
  await Promise.race([
19904
- this.request({ version: 1, type: "disconnect", requestId: randomUUID17() }).catch(() => void 0),
19964
+ this.request({ version: 1, type: "disconnect", requestId: randomUUID18() }).catch(() => void 0),
19905
19965
  new Promise((resolve28) => {
19906
19966
  disconnectTimer = setTimeout(resolve28, Math.min(this.requestTimeoutMs, 250));
19907
19967
  })
@@ -19992,7 +20052,7 @@ var PalClient = class {
19992
20052
  const data2 = await this.request({
19993
20053
  version: 1,
19994
20054
  type: "register",
19995
- requestId: randomUUID17(),
20055
+ requestId: randomUUID18(),
19996
20056
  authToken: this.authToken,
19997
20057
  presence: { version: 1, ...this.registration, connectedAt: now, lastSeenAt: now }
19998
20058
  });
@@ -20104,7 +20164,7 @@ var PalClient = class {
20104
20164
  if (!this.registered) return;
20105
20165
  if (this.heartbeatPending) return;
20106
20166
  this.heartbeatPending = true;
20107
- void this.request({ version: 1, type: "heartbeat", requestId: randomUUID17() }).catch(() => void 0).finally(() => {
20167
+ void this.request({ version: 1, type: "heartbeat", requestId: randomUUID18() }).catch(() => void 0).finally(() => {
20108
20168
  this.heartbeatPending = false;
20109
20169
  });
20110
20170
  }, this.heartbeatIntervalMs);
@@ -20391,7 +20451,7 @@ var ApprovalBridge = class {
20391
20451
  request(request, signal = new AbortController().signal) {
20392
20452
  if (this.#settle !== void 0) return Promise.resolve("deny");
20393
20453
  if (signal.aborted) return Promise.resolve("deny");
20394
- this.#pending = { id: randomUUID18(), ...request };
20454
+ this.#pending = { id: randomUUID19(), ...request };
20395
20455
  this.#onChange?.();
20396
20456
  return new Promise((resolvePromise) => {
20397
20457
  this.#settle = resolvePromise;
@@ -20510,7 +20570,14 @@ async function createProductionRuntime(options) {
20510
20570
  environment.OPENAI_API_KEY,
20511
20571
  environment.ANTHROPIC_API_KEY
20512
20572
  ].filter((value) => typeof value === "string" && value.length > 0);
20513
- const hooks = new HookBus();
20573
+ let hookSessionId;
20574
+ const hooks = new HookBus({
20575
+ eventContext: () => ({
20576
+ protocolVersion: 2,
20577
+ workspace,
20578
+ ...hookSessionId === void 0 ? {} : { sessionId: hookSessionId }
20579
+ })
20580
+ });
20514
20581
  hooks.on(
20515
20582
  "PostToolUseFailure",
20516
20583
  createIncidentReporter({
@@ -20530,8 +20597,7 @@ async function createProductionRuntime(options) {
20530
20597
  diagnostics.push(...permissionPolicy.diagnostics);
20531
20598
  const approvals = new ApprovalBridge(options.onApprovalChange);
20532
20599
  const resolveToolApproval = options.approvalPolicy === "deny" && options.rpcToolApprovals !== true ? () => "deny" : (request, signal) => approvals.request(request, signal);
20533
- const questions = new QuestionBridge(options.onApprovalChange);
20534
- const askUserQuestionHandler = async (qs, signal) => {
20600
+ const relayQuestions = async (qs, signal) => {
20535
20601
  if (options.approvalPolicy === "deny") throw new Error("AskUserQuestion is not available in non-interactive mode");
20536
20602
  if (hooks.hasListeners("PermissionRequest")) {
20537
20603
  let decision;
@@ -20549,8 +20615,10 @@ async function createProductionRuntime(options) {
20549
20615
  const hookAnswers = hookAnswersFromUpdatedInput(decision.updatedInput, qs);
20550
20616
  if (hookAnswers !== void 0) return hookAnswers;
20551
20617
  }
20552
- return questions.ask(qs, signal);
20618
+ return void 0;
20553
20619
  };
20620
+ const questions = new QuestionBridge(options.onApprovalChange, relayQuestions);
20621
+ const askUserQuestionHandler = (qs, signal) => questions.ask(qs, signal);
20554
20622
  const executionEnvironment = createExecutionEnvironment(workspace, config.execution);
20555
20623
  const jobs = new JobRegistry();
20556
20624
  const terminals = new TerminalService(workspace, { jobs });
@@ -20874,7 +20942,8 @@ async function createProductionRuntime(options) {
20874
20942
  let taskResults = { ...recovered?.tasks.results ?? {} };
20875
20943
  const subagentStartedAt = {};
20876
20944
  const subagentElapsedMs = {};
20877
- let sessionId = recovered?.sessionId ?? `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID18().slice(0, 8)}`;
20945
+ let sessionId = recovered?.sessionId ?? `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID19().slice(0, 8)}`;
20946
+ hookSessionId = sessionId;
20878
20947
  let harnessJournal = new HarnessJournal({ workspace, sessionId });
20879
20948
  const harnessRecovery = harnessJournal.recover();
20880
20949
  if (harnessRecovery.queue.length > 0 || harnessRecovery.incompleteTools.length > 0 || harnessRecovery.incompleteModelIds.length > 0 || harnessRecovery.interruptedTurnIds.length > 0) {
@@ -20958,7 +21027,13 @@ async function createProductionRuntime(options) {
20958
21027
  const publishTaskState = async () => {
20959
21028
  harness.main.context.updateTaskState(serializedTaskState());
20960
21029
  await persist();
20961
- emitOutput({ type: "tasks", snapshot: taskSnapshot() });
21030
+ const snapshot4 = taskSnapshot();
21031
+ emitOutput({ type: "tasks", snapshot: snapshot4 });
21032
+ await hooks.emit({
21033
+ version: 1,
21034
+ type: "Notification",
21035
+ payload: { kind: "task_snapshot", taskSnapshot: snapshot4 }
21036
+ }).catch(() => void 0);
20962
21037
  };
20963
21038
  for (const tool of createTaskPlanTools({
20964
21039
  getPlan: () => taskPlan,
@@ -21516,7 +21591,7 @@ async function createProductionRuntime(options) {
21516
21591
  return answers[0] === "Continue" ? "approved" : "rejected";
21517
21592
  },
21518
21593
  fingerprint: workspaceFingerprint,
21519
- idFactory: () => `loop-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID18().slice(0, 8)}`
21594
+ idFactory: () => `loop-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID19().slice(0, 8)}`
21520
21595
  });
21521
21596
  const goalStore = new GoalStore({ workspace });
21522
21597
  const goalOrchestrator = new GoalOrchestrator({
@@ -21535,7 +21610,7 @@ async function createProductionRuntime(options) {
21535
21610
  executionEnvironment
21536
21611
  ),
21537
21612
  now: () => (/* @__PURE__ */ new Date()).toISOString(),
21538
- idFactory: () => `goal-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID18().slice(0, 8)}`,
21613
+ idFactory: () => `goal-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID19().slice(0, 8)}`,
21539
21614
  runWorker: ({ workspace: goalWorkspace, prompt, signal }) => runLoopWorker({ workspace: goalWorkspace, prompt, signal })
21540
21615
  });
21541
21616
  let sessionHistory = await SessionHistory.open({
@@ -21648,7 +21723,7 @@ async function createProductionRuntime(options) {
21648
21723
  runExplicitSkill(harness, skills, skill, prompt, signal, selectedModels.mainError),
21649
21724
  persist
21650
21725
  ),
21651
- runLoop: (goal, signal) => runLoopSession(loopOrchestrator, goal, signal),
21726
+ runLoop: (goal, signal) => runLoopSession(loopOrchestrator, hooks, goal, signal),
21652
21727
  runGoal: (goal, signal) => persistEach(runGoalSession(goalOrchestrator, goal, signal), persist),
21653
21728
  mcp: async (command, signal) => {
21654
21729
  signal.throwIfAborted();
@@ -21806,7 +21881,8 @@ ${summary2}`;
21806
21881
  for (const key of Object.keys(subagentStartedAt)) delete subagentStartedAt[key];
21807
21882
  for (const key of Object.keys(subagentElapsedMs)) delete subagentElapsedMs[key];
21808
21883
  const previousIdeSessionId = ideSessionId;
21809
- sessionId = `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID18().slice(0, 8)}`;
21884
+ sessionId = `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID19().slice(0, 8)}`;
21885
+ hookSessionId = sessionId;
21810
21886
  harnessJournal = new HarnessJournal({ workspace, sessionId });
21811
21887
  setUsageSession(sessionId);
21812
21888
  ideSessionId = sessionId;
@@ -22105,9 +22181,10 @@ function formatMcpReconnect(server) {
22105
22181
  if (server.status === "disabled") return `MCP server "${server.name}" is disabled. Enable it before reconnecting.`;
22106
22182
  return `Failed to reconnect MCP server "${server.name}": ${server.error ?? "unknown error"}`;
22107
22183
  }
22108
- async function* runLoopSession(orchestrator, goal, signal) {
22184
+ async function* runLoopSession(orchestrator, hooks, goal, signal) {
22109
22185
  let totalInputTokens = 0;
22110
22186
  let totalOutputTokens = 0;
22187
+ let latestVerification;
22111
22188
  for await (const event of orchestrator.run({ goal, signal })) {
22112
22189
  if (event.type === "worker-event") {
22113
22190
  if (event.event.type === "usage") {
@@ -22121,8 +22198,19 @@ async function* runLoopSession(orchestrator, goal, signal) {
22121
22198
  } else if (event.event.type !== "done") yield event.event;
22122
22199
  continue;
22123
22200
  }
22201
+ if (event.type === "loop-verification") latestVerification = event.evidence;
22124
22202
  yield loopProgressEvent(event);
22125
22203
  if (event.type === "loop-terminal") {
22204
+ await hooks.emit({
22205
+ version: 1,
22206
+ type: "LoopEnd",
22207
+ payload: {
22208
+ loopId: event.loopId,
22209
+ outcome: event.status,
22210
+ reason: event.reason,
22211
+ verification: latestVerification ?? null
22212
+ }
22213
+ }).catch(() => void 0);
22126
22214
  yield { type: "done", usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens } };
22127
22215
  }
22128
22216
  }
@@ -22836,7 +22924,7 @@ function memoryTranscriptHash(messages) {
22836
22924
  return createHash17("sha256").update(visible, "utf8").digest("hex");
22837
22925
  }
22838
22926
  function createMemoryTaskId() {
22839
- return `memory-${randomUUID18()}`;
22927
+ return `memory-${randomUUID19()}`;
22840
22928
  }
22841
22929
 
22842
22930
  export {