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.
@@ -1,6 +1,6 @@
1
1
  // src/desktop/main.ts
2
2
  import { appendFileSync } from "fs";
3
- import { createHash as createHash27, randomUUID as randomUUID27 } from "crypto";
3
+ import { createHash as createHash27, randomUUID as randomUUID28 } from "crypto";
4
4
  import { readFile as readFile39, stat as stat18, writeFile as writeFile19 } from "fs/promises";
5
5
  import { basename as basename11, dirname as dirname24, join as join46, resolve as resolve40 } from "path";
6
6
  import { fileURLToPath as fileURLToPath4 } from "url";
@@ -4218,7 +4218,7 @@ ${summarizeReport(report2)}`).join("\n\n")
4218
4218
 
4219
4219
  // src/production.ts
4220
4220
  import { readFile as readFile27 } from "fs/promises";
4221
- import { createHash as createHash20, randomUUID as randomUUID23 } from "crypto";
4221
+ import { createHash as createHash20, randomUUID as randomUUID24 } from "crypto";
4222
4222
  import { homedir as homedir2, release as osRelease, tmpdir as tmpdir3, version as osVersion } from "os";
4223
4223
  import { basename as basename8, join as join31, relative as relative23, resolve as resolve32, sep as sep23 } from "path";
4224
4224
 
@@ -7524,11 +7524,18 @@ var DEFAULT_TOOL_OUTPUT_LIMITS = Object.freeze({
7524
7524
  perTurnChars: 2e5
7525
7525
  });
7526
7526
  var PreToolUsePayload = z14.object({
7527
+ // Optional for backwards-compatible hook updatedInput payloads; runtime
7528
+ // emissions always populate it.
7529
+ toolCallId: z14.string().optional(),
7527
7530
  tool: z14.string(),
7528
7531
  input: z14.unknown(),
7529
7532
  agent: z14.enum(["main", "subagent"])
7530
7533
  });
7531
- var PermissionRequestPayload = PreToolUsePayload.extend({ reason: z14.string().optional() });
7534
+ var PermissionRequestPayload = PreToolUsePayload.extend({
7535
+ reason: z14.string().optional(),
7536
+ toolCategory: z14.string(),
7537
+ allowAlways: z14.boolean()
7538
+ });
7532
7539
  var PostToolUsePayload = PreToolUsePayload.extend({ output: z14.unknown() });
7533
7540
  var PostToolUseFailurePayload = PreToolUsePayload.extend({
7534
7541
  error: z14.object({ code: z14.string(), message: z14.string() })
@@ -7645,30 +7652,31 @@ var ToolRuntime = class {
7645
7652
  const tool = this.#tools.get(call.name);
7646
7653
  if (tool === void 0) return { ok: false, error: { code: "unknown_tool", message: `Unknown tool: ${call.name}` } };
7647
7654
  const signal = context.signal ?? new AbortController().signal;
7655
+ const toolCallId = call.id ?? randomUUID7();
7648
7656
  const validation = this.validate(call);
7649
7657
  if (!validation.ok) {
7650
- return this.#fail(call.name, call.input, context.agent, validation.error.code, validation.error.message);
7658
+ return this.#fail(toolCallId, call.name, call.input, context.agent, validation.error.code, validation.error.message);
7651
7659
  }
7652
7660
  let input = validation.input;
7653
7661
  try {
7654
7662
  const pre = await this.#hooks.emit({
7655
7663
  version: 1,
7656
7664
  type: "PreToolUse",
7657
- payload: { tool: tool.name, input, agent: context.agent }
7665
+ payload: { toolCallId, tool: tool.name, input, agent: context.agent }
7658
7666
  });
7659
7667
  if (pre.updatedInput !== void 0) {
7660
7668
  const payload = PreToolUsePayload.parse(pre.updatedInput);
7661
7669
  if (payload.tool !== tool.name || payload.agent !== context.agent) {
7662
- return this.#fail(tool.name, input, context.agent, "invalid_input", "PreToolUse cannot change the tool or agent");
7670
+ return this.#fail(toolCallId, tool.name, input, context.agent, "invalid_input", "PreToolUse cannot change the tool or agent");
7663
7671
  }
7664
7672
  try {
7665
7673
  input = tool.inputSchema.parse(payload.input);
7666
7674
  } catch (error) {
7667
- return this.#fail(tool.name, payload.input, context.agent, "invalid_input", message(error));
7675
+ return this.#fail(toolCallId, tool.name, payload.input, context.agent, "invalid_input", message(error));
7668
7676
  }
7669
7677
  }
7670
7678
  if (pre.decision === "deny") {
7671
- return this.#fail(tool.name, input, context.agent, "hook_denied", pre.reason ?? "Tool use denied by hook");
7679
+ return this.#fail(toolCallId, tool.name, input, context.agent, "hook_denied", pre.reason ?? "Tool use denied by hook");
7672
7680
  }
7673
7681
  const request = {
7674
7682
  agent: context.agent,
@@ -7679,13 +7687,13 @@ var ToolRuntime = class {
7679
7687
  let permission = this.#permissions.decide(request);
7680
7688
  const nonCacheableApproval = request.allowAlways === false || permission.allowAlways === false;
7681
7689
  if (permission.decision === "deny") {
7682
- return this.#fail(tool.name, input, context.agent, "permission_denied", permission.reason ?? "Tool use denied");
7690
+ return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", permission.reason ?? "Tool use denied");
7683
7691
  }
7684
7692
  if (permission.decision === "ask" && context.agent === "main" && !nonCacheableApproval && this.#permissions.mode === "auto" && this.#permissions.profile === "standard" && this.#classify !== void 0) {
7685
7693
  try {
7686
7694
  const classified = await this.#classify(request, signal);
7687
7695
  if (classified.decision === "deny") {
7688
- return this.#fail(tool.name, input, context.agent, "permission_denied", classified.reason ?? "Auto classifier denied tool use");
7696
+ return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", classified.reason ?? "Auto classifier denied tool use");
7689
7697
  }
7690
7698
  if (classified.decision === "allow") permission = classified;
7691
7699
  else {
@@ -7702,13 +7710,21 @@ var ToolRuntime = class {
7702
7710
  const requestDecision = await this.#hooks.emit({
7703
7711
  version: 1,
7704
7712
  type: "PermissionRequest",
7705
- payload: { tool: tool.name, input: tool.permissionInput?.(input) ?? input, agent: context.agent, reason }
7713
+ payload: {
7714
+ toolCallId,
7715
+ tool: tool.name,
7716
+ input: tool.permissionInput?.(input) ?? input,
7717
+ agent: context.agent,
7718
+ reason,
7719
+ toolCategory: getToolCategory(tool.name),
7720
+ allowAlways
7721
+ }
7706
7722
  });
7707
7723
  if (requestDecision.decision === "deny") {
7708
- return this.#fail(tool.name, input, context.agent, "permission_denied", requestDecision.reason ?? "Permission request denied");
7724
+ return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", requestDecision.reason ?? "Permission request denied");
7709
7725
  }
7710
7726
  if (requestDecision.updatedInput !== void 0) {
7711
- return this.#fail(tool.name, input, context.agent, "invalid_input", "PermissionRequest cannot modify an already-authorized tool call");
7727
+ return this.#fail(toolCallId, tool.name, input, context.agent, "invalid_input", "PermissionRequest cannot modify an already-authorized tool call");
7712
7728
  }
7713
7729
  if (this.#hooks.hasListeners("PermissionRequest") && requestDecision.decision === "allow") {
7714
7730
  const ctx = requestDecision.additionalContext ?? "";
@@ -7717,18 +7733,18 @@ var ToolRuntime = class {
7717
7733
  if (allowAlways && category !== "destructive") this.#alwaysAllowed.add(category);
7718
7734
  }
7719
7735
  } else if (context.agent !== "main" && this.#permissions.mode !== "bubble") {
7720
- return this.#fail(tool.name, input, context.agent, "approval_required", reason);
7736
+ return this.#fail(toolCallId, tool.name, input, context.agent, "approval_required", reason);
7721
7737
  } else if (signal.aborted) {
7722
7738
  throw signal.reason;
7723
7739
  } else {
7724
7740
  const category = getToolCategory(tool.name);
7725
7741
  if (allowAlways && this.#alwaysAllowed.has(category)) {
7726
7742
  } else if (this.#approve === void 0) {
7727
- return this.#fail(tool.name, input, context.agent, "permission_denied", reason);
7743
+ return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", reason);
7728
7744
  } else {
7729
7745
  const decision = await this.#approve({ ...request, reason, ...allowAlways ? {} : { allowAlways: false } }, signal);
7730
7746
  if (decision === "deny") {
7731
- return this.#fail(tool.name, input, context.agent, "user_denied", reason);
7747
+ return this.#fail(toolCallId, tool.name, input, context.agent, "user_denied", reason);
7732
7748
  }
7733
7749
  if (decision === "always" && allowAlways && category !== "destructive") {
7734
7750
  this.#alwaysAllowed.add(category);
@@ -7742,7 +7758,7 @@ var ToolRuntime = class {
7742
7758
  try {
7743
7759
  rawOutput = tool.outputSchema === void 0 ? executed : tool.outputSchema.parse(executed);
7744
7760
  } catch (error) {
7745
- return this.#fail(tool.name, input, context.agent, "invalid_output", message(error));
7761
+ return this.#fail(toolCallId, tool.name, input, context.agent, "invalid_output", message(error));
7746
7762
  }
7747
7763
  const declaredPresentation = tool.presentResult?.(rawOutput, input);
7748
7764
  const presentation = declaredPresentation ?? getToolPresentation(rawOutput);
@@ -7753,7 +7769,7 @@ var ToolRuntime = class {
7753
7769
  await this.#hooks.emit({
7754
7770
  version: 1,
7755
7771
  type: "PostToolUse",
7756
- payload: { tool: tool.name, input, agent: context.agent, output }
7772
+ payload: { toolCallId, tool: tool.name, input, agent: context.agent, output }
7757
7773
  });
7758
7774
  return {
7759
7775
  ok: true,
@@ -7763,7 +7779,7 @@ var ToolRuntime = class {
7763
7779
  ...additionalContext === void 0 || additionalContext.length === 0 ? {} : { additionalContext }
7764
7780
  };
7765
7781
  } catch (error) {
7766
- return this.#fail(tool.name, input, context.agent, "tool_error", message(error));
7782
+ return this.#fail(toolCallId, tool.name, input, context.agent, "tool_error", message(error));
7767
7783
  }
7768
7784
  }
7769
7785
  async #limitOutput(tool, output) {
@@ -7811,13 +7827,13 @@ var ToolRuntime = class {
7811
7827
  const limited = await this.#limitOutput(tool, content);
7812
7828
  return typeof limited === "string" ? limited : serializeToolOutput(limited);
7813
7829
  }
7814
- async #fail(tool, input, agent, code, errorMessage3) {
7830
+ async #fail(toolCallId, tool, input, agent, code, errorMessage3) {
7815
7831
  const result = { ok: false, error: { code, message: errorMessage3 } };
7816
7832
  try {
7817
7833
  await this.#hooks.emit({
7818
7834
  version: 1,
7819
7835
  type: "PostToolUseFailure",
7820
- payload: { tool, input, agent, error: result.error }
7836
+ payload: { toolCallId, tool, input, agent, error: result.error }
7821
7837
  });
7822
7838
  } catch {
7823
7839
  }
@@ -8438,6 +8454,7 @@ function isWithin2(root, target) {
8438
8454
 
8439
8455
  // src/hooks/bus.ts
8440
8456
  import { spawn } from "child_process";
8457
+ import { randomUUID as randomUUID9 } from "crypto";
8441
8458
 
8442
8459
  // src/hooks/types.ts
8443
8460
  import { z as z16 } from "zod";
@@ -8504,6 +8521,11 @@ var DEFAULT_MAX_OUTPUT_BYTES = 1048576;
8504
8521
  var HookBus = class {
8505
8522
  #handlers = /* @__PURE__ */ new Map();
8506
8523
  #payloadSchemas = /* @__PURE__ */ new Map();
8524
+ #options;
8525
+ #sequence = 0;
8526
+ constructor(options = {}) {
8527
+ this.#options = options;
8528
+ }
8507
8529
  registerPayloadSchema(type, schema) {
8508
8530
  const registrations = this.#payloadSchemas.get(type) ?? [];
8509
8531
  const registration = { schema };
@@ -8540,7 +8562,9 @@ var HookBus = class {
8540
8562
  async emit(rawEvent, externalSignal) {
8541
8563
  externalSignal?.throwIfAborted();
8542
8564
  let event = HookEventSchema.parse(rawEvent);
8543
- event = { ...event, payload: this.#validatePayload(event.type, event.payload) };
8565
+ const validatedPayload = this.#validatePayload(event.type, event.payload);
8566
+ const envelope = this.#eventEnvelope();
8567
+ event = { ...event, payload: decoratePayload(validatedPayload, envelope) };
8544
8568
  let aggregate2 = { decision: "allow" };
8545
8569
  for (const registered of this.#handlers.get(event.type) ?? []) {
8546
8570
  externalSignal?.throwIfAborted();
@@ -8555,8 +8579,8 @@ var HookBus = class {
8555
8579
  externalSignal?.throwIfAborted();
8556
8580
  if (decision.updatedInput !== void 0) {
8557
8581
  const payload = this.#validatePayload(event.type, decision.updatedInput);
8558
- event = HookEventSchema.parse({ ...event, payload });
8559
- aggregate2 = { ...aggregate2, updatedInput: event.payload };
8582
+ event = HookEventSchema.parse({ ...event, payload: decoratePayload(payload, envelope) });
8583
+ aggregate2 = { ...aggregate2, updatedInput: payload };
8560
8584
  }
8561
8585
  if (decision.additionalContext !== void 0) {
8562
8586
  aggregate2 = {
@@ -8573,6 +8597,15 @@ var HookBus = class {
8573
8597
  const schema = this.#payloadSchemas.get(type)?.at(-1)?.schema;
8574
8598
  return schema ? schema.parse(payload) : zodRecord(payload);
8575
8599
  }
8600
+ #eventEnvelope() {
8601
+ if (this.#options.eventContext === void 0) return void 0;
8602
+ return {
8603
+ ...this.#options.eventContext(),
8604
+ eventId: (this.#options.eventId ?? randomUUID9)(),
8605
+ sequence: ++this.#sequence,
8606
+ timestamp: (this.#options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()))()
8607
+ };
8608
+ }
8576
8609
  async #invoke(registered, event, externalSignal) {
8577
8610
  const timeoutSignal = AbortSignal.timeout(registered.timeoutMs);
8578
8611
  const signal = externalSignal === void 0 ? timeoutSignal : AbortSignal.any([timeoutSignal, externalSignal]);
@@ -8584,6 +8617,9 @@ var HookBus = class {
8584
8617
  return HookDecisionSchema.parse(await Promise.race([invocation, timeout]));
8585
8618
  }
8586
8619
  };
8620
+ function decoratePayload(payload, envelope) {
8621
+ return envelope === void 0 ? payload : { ...payload, ...envelope };
8622
+ }
8587
8623
  function runShellHandler(descriptor, event, signal) {
8588
8624
  return new Promise((resolve41, reject) => {
8589
8625
  const child = spawn(descriptor.command, [...descriptor.args ?? []], {
@@ -11980,7 +12016,7 @@ function formatPriorGaps(gaps) {
11980
12016
 
11981
12017
  // src/goal/store.ts
11982
12018
  import { constants as constants3 } from "fs";
11983
- import { randomUUID as randomUUID9 } from "crypto";
12019
+ import { randomUUID as randomUUID10 } from "crypto";
11984
12020
  import { lstat as lstat5, mkdir as mkdir11, open as open5, rename as rename4, rm as rm6 } from "fs/promises";
11985
12021
  import { dirname as dirname10, join as join15, relative as relative9, resolve as resolve11, sep as sep9 } from "path";
11986
12022
  var DEFAULT_MAX_BYTES = 1024 * 1024;
@@ -12004,7 +12040,7 @@ var GoalStore = class {
12004
12040
  if (Buffer.byteLength(body) > this.#maxBytes) {
12005
12041
  throw new Error(`Goal state exceeds maximum size of ${this.#maxBytes} bytes`);
12006
12042
  }
12007
- const temporary = join15(this.#root, `.${state.id}.${process.pid}.${randomUUID9()}.tmp`);
12043
+ const temporary = join15(this.#root, `.${state.id}.${process.pid}.${randomUUID10()}.tmp`);
12008
12044
  let handle;
12009
12045
  try {
12010
12046
  handle = await open5(temporary, constants3.O_CREAT | constants3.O_EXCL | constants3.O_WRONLY, 384);
@@ -12117,7 +12153,7 @@ async function ensureWorktreeDirectoryIgnored(root) {
12117
12153
  import { constants as constants4 } from "fs";
12118
12154
  import { lstat as lstat6, mkdir as mkdir13, open as open6, readFile as readFile12, realpath as realpath8, rename as rename5, rm as rm7, stat as stat6 } from "fs/promises";
12119
12155
  import { dirname as dirname11, join as join17, relative as relative10, resolve as resolve13, sep as sep10 } from "path";
12120
- import { randomUUID as randomUUID10 } from "crypto";
12156
+ import { randomUUID as randomUUID11 } from "crypto";
12121
12157
  var LOOP_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
12122
12158
  var DEFAULT_MAX_BYTES2 = 5 * 1024 * 1024;
12123
12159
  var LoopStore = class {
@@ -12148,7 +12184,7 @@ var LoopStore = class {
12148
12184
  const body = `${JSON.stringify(state, null, 2)}
12149
12185
  `;
12150
12186
  if (Buffer.byteLength(body) > this.#maxBytes) throw new Error(`Loop state exceeds maximum size of ${this.#maxBytes} bytes`);
12151
- const temporary = join17(directory, `.state.${process.pid}.${randomUUID10()}.tmp`);
12187
+ const temporary = join17(directory, `.state.${process.pid}.${randomUUID11()}.tmp`);
12152
12188
  let handle;
12153
12189
  try {
12154
12190
  handle = await open6(temporary, constants4.O_CREAT | constants4.O_EXCL | constants4.O_WRONLY, 384);
@@ -12174,7 +12210,7 @@ var LoopStore = class {
12174
12210
  return state;
12175
12211
  } catch (error) {
12176
12212
  if (isCode5(error, "ENOENT")) throw new Error(`Loop "${loopId}" was not found`);
12177
- const quarantine = `${target}.corrupt-${Date.now()}-${randomUUID10().slice(0, 8)}`;
12213
+ const quarantine = `${target}.corrupt-${Date.now()}-${randomUUID11().slice(0, 8)}`;
12178
12214
  await rename5(target, quarantine).catch(() => void 0);
12179
12215
  throw new Error(`Loop "${loopId}" is corrupt and was quarantined: ${message(error)}`);
12180
12216
  }
@@ -15033,7 +15069,7 @@ function createSkillResourceTool(registry) {
15033
15069
  import { constants as constants6 } from "fs";
15034
15070
  import { lstat as lstat8, mkdir as mkdir15, open as open7, readFile as readFile17, readdir as readdir6, realpath as realpath10, rename as rename6, rm as rm8, stat as stat7 } from "fs/promises";
15035
15071
  import { basename as basename5, dirname as dirname15, join as join22, relative as relative13, resolve as resolve19, sep as sep13 } from "path";
15036
- import { randomUUID as randomUUID11 } from "crypto";
15072
+ import { randomUUID as randomUUID12 } from "crypto";
15037
15073
  import { z as z27 } from "zod";
15038
15074
 
15039
15075
  // src/ui/transcript.ts
@@ -15721,7 +15757,7 @@ var SessionStore = class {
15721
15757
  const body = `${lines.join("\n")}
15722
15758
  `;
15723
15759
  if (Buffer.byteLength(body) > this.#maxBytes) throw new Error(`Session exceeds maximum size of ${this.#maxBytes} bytes`);
15724
- const temporary = join22(this.#sessions, `.${document.sessionId}.${process.pid}.${randomUUID11()}.tmp`);
15760
+ const temporary = join22(this.#sessions, `.${document.sessionId}.${process.pid}.${randomUUID12()}.tmp`);
15725
15761
  let handle;
15726
15762
  try {
15727
15763
  handle = await open7(temporary, constants6.O_CREAT | constants6.O_EXCL | constants6.O_WRONLY, 384);
@@ -15877,7 +15913,7 @@ var SessionStore = class {
15877
15913
  if (!isWithin5(canonicalRoot, canonicalSessions)) throw new Error("Session directory escapes the workspace");
15878
15914
  }
15879
15915
  async #quarantine(path) {
15880
- const target = `${path}.corrupt-${Date.now()}-${randomUUID11().slice(0, 8)}`;
15916
+ const target = `${path}.corrupt-${Date.now()}-${randomUUID12().slice(0, 8)}`;
15881
15917
  await rename6(path, target);
15882
15918
  return target;
15883
15919
  }
@@ -16096,12 +16132,12 @@ function sanitize2(input, seen = /* @__PURE__ */ new WeakSet()) {
16096
16132
  }
16097
16133
 
16098
16134
  // src/session/tree.ts
16099
- import { randomUUID as randomUUID13 } from "crypto";
16135
+ import { randomUUID as randomUUID14 } from "crypto";
16100
16136
  import { mkdir as mkdir17, readFile as readFile19, rename as rename8, writeFile as writeFile11 } from "fs/promises";
16101
16137
  import { dirname as dirname17, join as join24, resolve as resolve21 } from "path";
16102
16138
 
16103
16139
  // src/session/checkpoint.ts
16104
- import { createHash as createHash14, randomUUID as randomUUID12 } from "crypto";
16140
+ import { createHash as createHash14, randomUUID as randomUUID13 } from "crypto";
16105
16141
  import { execFile as execFile2 } from "child_process";
16106
16142
  import { lstat as lstat9, mkdir as mkdir16, open as open8, readFile as readFile18, readdir as readdir7, rename as rename7, rm as rm9, writeFile as writeFile10 } from "fs/promises";
16107
16143
  import { dirname as dirname16, join as join23, relative as relative14, resolve as resolve20, sep as sep14 } from "path";
@@ -16154,7 +16190,7 @@ var WorkspaceCheckpointStore = class {
16154
16190
  }
16155
16191
  const checkpoint = {
16156
16192
  version: 1,
16157
- id: `checkpoint-${Date.now()}-${randomUUID12().slice(0, 8)}`,
16193
+ id: `checkpoint-${Date.now()}-${randomUUID13().slice(0, 8)}`,
16158
16194
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
16159
16195
  ...label === void 0 || label.trim().length === 0 ? {} : { label: label.trim() },
16160
16196
  files
@@ -16191,7 +16227,7 @@ var WorkspaceCheckpointStore = class {
16191
16227
  const target = contained(this.#workspace, file.path);
16192
16228
  const object = objects.get(file.digest);
16193
16229
  await mkdir16(dirname16(target), { recursive: true });
16194
- const temporary = `${target}.${process.pid}.${randomUUID12()}.tmp`;
16230
+ const temporary = `${target}.${process.pid}.${randomUUID13()}.tmp`;
16195
16231
  await writeFile10(temporary, object, { mode: file.mode });
16196
16232
  await rename7(temporary, target);
16197
16233
  }
@@ -16263,7 +16299,7 @@ function contained(workspace, path) {
16263
16299
  }
16264
16300
  async function atomicJson(path, value) {
16265
16301
  await mkdir16(dirname16(path), { recursive: true });
16266
- const temporary = `${path}.${process.pid}.${randomUUID12()}.tmp`;
16302
+ const temporary = `${path}.${process.pid}.${randomUUID13()}.tmp`;
16267
16303
  await writeFile10(temporary, `${JSON.stringify(value)}
16268
16304
  `, { encoding: "utf8", mode: 384 });
16269
16305
  await rename7(temporary, path);
@@ -16308,7 +16344,7 @@ var SessionHistory = class _SessionHistory {
16308
16344
  async append(input) {
16309
16345
  const checkpoint = await this.#checkpoints.create(input.label);
16310
16346
  const node = {
16311
- id: `turn-${Date.now()}-${randomUUID13().slice(0, 8)}`,
16347
+ id: `turn-${Date.now()}-${randomUUID14().slice(0, 8)}`,
16312
16348
  parentId: this.#leafId,
16313
16349
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
16314
16350
  prompt: input.prompt,
@@ -16366,7 +16402,7 @@ var SessionHistory = class _SessionHistory {
16366
16402
  nodes: this.#nodes
16367
16403
  };
16368
16404
  await mkdir17(dirname17(this.#path), { recursive: true });
16369
- const temporary = `${this.#path}.${process.pid}.${randomUUID13()}.tmp`;
16405
+ const temporary = `${this.#path}.${process.pid}.${randomUUID14()}.tmp`;
16370
16406
  await writeFile11(temporary, `${JSON.stringify(document)}
16371
16407
  `, { encoding: "utf8", mode: 384 });
16372
16408
  await rename8(temporary, this.#path);
@@ -16392,7 +16428,7 @@ function isCode10(error, code) {
16392
16428
 
16393
16429
  // src/sleep/organizer.ts
16394
16430
  import { constants as constants7 } from "fs";
16395
- import { randomUUID as randomUUID14 } from "crypto";
16431
+ import { randomUUID as randomUUID15 } from "crypto";
16396
16432
  import { lstat as lstat10, mkdir as mkdir18, open as open9, readdir as readdir8, realpath as realpath11, rename as rename9, rm as rm10 } from "fs/promises";
16397
16433
  import { isAbsolute as isAbsolute10, join as join25, relative as relative15, resolve as resolve22, sep as sep15 } from "path";
16398
16434
  import { z as z28 } from "zod";
@@ -16549,7 +16585,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
16549
16585
  return (await readdir8(this.#sleepDirectory, { withFileTypes: true })).some((entry2) => entry2.isFile() && entry2.name.startsWith(prefix) && entry2.name.endsWith(".md"));
16550
16586
  }
16551
16587
  async #atomicWrite(path, content) {
16552
- const temporary = join25(this.#sleepDirectory, `.${randomUUID14()}.tmp`);
16588
+ const temporary = join25(this.#sleepDirectory, `.${randomUUID15()}.tmp`);
16553
16589
  let handle;
16554
16590
  try {
16555
16591
  handle = await open9(temporary, constants7.O_CREAT | constants7.O_EXCL | constants7.O_WRONLY, 384);
@@ -16925,7 +16961,7 @@ function computeSessionStats(documents) {
16925
16961
  import { constants as constants8 } from "fs";
16926
16962
  import { mkdir as mkdir19, open as open10, readFile as readFile20, realpath as realpath12, rename as rename10, rm as rm11, stat as stat8 } from "fs/promises";
16927
16963
  import { dirname as dirname18, isAbsolute as isAbsolute11, relative as relative16, resolve as resolve23, sep as sep16 } from "path";
16928
- import { randomUUID as randomUUID15 } from "crypto";
16964
+ import { randomUUID as randomUUID16 } from "crypto";
16929
16965
  import { z as z29 } from "zod";
16930
16966
 
16931
16967
  // src/tools/file-diff.ts
@@ -17278,7 +17314,7 @@ function createPathGuard(workspace) {
17278
17314
  async function atomicWrite(path, content, signal, expectedVersion) {
17279
17315
  abortIfNeeded(signal);
17280
17316
  await mkdir19(dirname18(path), { recursive: true });
17281
- const temporary = `${path}.${randomUUID15()}.tmp`;
17317
+ const temporary = `${path}.${randomUUID16()}.tmp`;
17282
17318
  try {
17283
17319
  const handle = await open10(temporary, constants8.O_CREAT | constants8.O_EXCL | constants8.O_WRONLY, 384);
17284
17320
  try {
@@ -19340,18 +19376,40 @@ function hookAnswersFromUpdatedInput(updatedInput, questions) {
19340
19376
  var QuestionBridge = class {
19341
19377
  #pending;
19342
19378
  #onChange;
19343
- constructor(onChange) {
19379
+ #relay;
19380
+ #active = false;
19381
+ constructor(onChange, relay) {
19344
19382
  this.#onChange = onChange;
19383
+ this.#relay = relay;
19345
19384
  }
19346
19385
  get pending() {
19347
19386
  return this.#pending?.questions;
19348
19387
  }
19349
19388
  ask(questions, signal) {
19350
- if (this.#pending !== void 0) return Promise.reject(new Error("A question is already pending"));
19389
+ if (this.#active) return Promise.reject(new Error("A question is already pending"));
19351
19390
  if (signal.aborted) return Promise.reject(signal.reason);
19391
+ this.#active = true;
19392
+ if (this.#relay !== void 0) return this.#askRelayed(questions, signal);
19393
+ return this.#askLocally(questions, signal);
19394
+ }
19395
+ async #askRelayed(questions, signal) {
19396
+ try {
19397
+ const answers = await this.#relay(questions, signal);
19398
+ if (answers !== void 0) {
19399
+ this.#active = false;
19400
+ return answers;
19401
+ }
19402
+ return await this.#askLocally(questions, signal);
19403
+ } catch (error) {
19404
+ this.#active = false;
19405
+ throw error;
19406
+ }
19407
+ }
19408
+ #askLocally(questions, signal) {
19352
19409
  return new Promise((resolve41, reject) => {
19353
19410
  const onAbort = () => {
19354
19411
  this.#pending = void 0;
19412
+ this.#active = false;
19355
19413
  reject(signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason)));
19356
19414
  };
19357
19415
  signal.addEventListener("abort", onAbort, { once: true });
@@ -19360,12 +19418,14 @@ var QuestionBridge = class {
19360
19418
  resolve: (answers) => {
19361
19419
  signal.removeEventListener("abort", onAbort);
19362
19420
  this.#pending = void 0;
19421
+ this.#active = false;
19363
19422
  this.#onChange?.();
19364
19423
  resolve41(answers);
19365
19424
  },
19366
19425
  reject: (error) => {
19367
19426
  signal.removeEventListener("abort", onAbort);
19368
19427
  this.#pending = void 0;
19428
+ this.#active = false;
19369
19429
  this.#onChange?.();
19370
19430
  reject(error);
19371
19431
  }
@@ -19451,7 +19511,7 @@ function createTodoWriteTool() {
19451
19511
  }
19452
19512
 
19453
19513
  // src/tools/managed.ts
19454
- import { randomUUID as randomUUID16 } from "crypto";
19514
+ import { randomUUID as randomUUID17 } from "crypto";
19455
19515
  import { link, lstat as lstat11, mkdir as mkdir20, readFile as readFile22, readdir as readdir9, unlink as unlink2, writeFile as writeFile12 } from "fs/promises";
19456
19516
  import { dirname as dirname21, join as join26, resolve as resolve26 } from "path";
19457
19517
  import { z as z37 } from "zod";
@@ -19738,7 +19798,7 @@ ${sourceUrl}`
19738
19798
  }
19739
19799
  async function writeExclusive(path, content) {
19740
19800
  await mkdir20(dirname21(path), { recursive: true });
19741
- const temporary = `${path}.${randomUUID16()}.tmp`;
19801
+ const temporary = `${path}.${randomUUID17()}.tmp`;
19742
19802
  try {
19743
19803
  await writeFile12(temporary, content, { encoding: "utf8", flag: "wx", mode: 384 });
19744
19804
  await link(temporary, path);
@@ -22966,7 +23026,7 @@ function relativeDisplay(workspace, filePath) {
22966
23026
  }
22967
23027
 
22968
23028
  // src/jobs/registry.ts
22969
- import { randomUUID as randomUUID17 } from "crypto";
23029
+ import { randomUUID as randomUUID18 } from "crypto";
22970
23030
  var JobRegistry = class {
22971
23031
  #jobs = /* @__PURE__ */ new Map();
22972
23032
  #maxOutputChars;
@@ -22980,7 +23040,7 @@ var JobRegistry = class {
22980
23040
  }
22981
23041
  create(input) {
22982
23042
  this.#pruneCompleted();
22983
- const id = `job-${randomUUID17().slice(0, 12)}`;
23043
+ const id = `job-${randomUUID18().slice(0, 12)}`;
22984
23044
  const now = (/* @__PURE__ */ new Date()).toISOString();
22985
23045
  let settle;
22986
23046
  const done = new Promise((resolve41) => {
@@ -23106,7 +23166,7 @@ function snapshot2(job) {
23106
23166
  }
23107
23167
 
23108
23168
  // src/terminal/service.ts
23109
- import { randomUUID as randomUUID18 } from "crypto";
23169
+ import { randomUUID as randomUUID19 } from "crypto";
23110
23170
  import { createRequire as nodeCreateRequire } from "module";
23111
23171
  import { isAbsolute as isAbsolute14, relative as relative22, resolve as resolve30, sep as sep22 } from "path";
23112
23172
  var TerminalService = class {
@@ -23124,7 +23184,7 @@ var TerminalService = class {
23124
23184
  open(input) {
23125
23185
  const cwd = workspacePath(this.#root, input.cwd ?? ".");
23126
23186
  const shell2 = input.shell ?? defaultShell();
23127
- const id = `term-${randomUUID18().slice(0, 12)}`;
23187
+ const id = `term-${randomUUID19().slice(0, 12)}`;
23128
23188
  const pty = this.#factory(shell2, input.args ?? [], {
23129
23189
  cwd,
23130
23190
  cols: input.columns ?? 100,
@@ -23357,13 +23417,13 @@ import { join as join30 } from "path";
23357
23417
  import { spawn as spawn7 } from "child_process";
23358
23418
 
23359
23419
  // src/pals/broker.ts
23360
- import { createHash as createHash18, randomUUID as randomUUID21, timingSafeEqual } from "crypto";
23420
+ import { createHash as createHash18, randomUUID as randomUUID22, timingSafeEqual } from "crypto";
23361
23421
  import { chmod as chmod2, lstat as lstat12, mkdir as mkdir23, unlink as unlink5 } from "fs/promises";
23362
23422
  import { createServer as createServer3, Socket } from "net";
23363
23423
  import { dirname as dirname23 } from "path";
23364
23424
 
23365
23425
  // src/pals/auth.ts
23366
- import { randomBytes as randomBytes3, randomUUID as randomUUID19 } from "crypto";
23426
+ import { randomBytes as randomBytes3, randomUUID as randomUUID20 } from "crypto";
23367
23427
  import { chmod, link as link2, mkdir as mkdir21, open as open12, readFile as readFile25, unlink as unlink3 } from "fs/promises";
23368
23428
  import { homedir } from "os";
23369
23429
  import { join as join29 } from "path";
@@ -23375,7 +23435,7 @@ async function loadOrCreatePalAuthToken(options = {}) {
23375
23435
  await mkdir21(directory, { recursive: true, mode: 448 });
23376
23436
  await chmod(directory, 448);
23377
23437
  const token = (options.random ?? randomBytes3)(PAL_AUTH_TOKEN_BYTES).toString("hex");
23378
- const temporaryPath = join29(directory, `.auth-token-${process.pid}-${randomUUID19()}.tmp`);
23438
+ const temporaryPath = join29(directory, `.auth-token-${process.pid}-${randomUUID20()}.tmp`);
23379
23439
  let handle;
23380
23440
  try {
23381
23441
  handle = await open12(temporaryPath, "wx", 384);
@@ -23409,7 +23469,7 @@ function validatePalAuthToken(token) {
23409
23469
  }
23410
23470
 
23411
23471
  // src/pals/lifecycle.ts
23412
- import { randomUUID as randomUUID20 } from "crypto";
23472
+ import { randomUUID as randomUUID21 } from "crypto";
23413
23473
  import { mkdir as mkdir22, open as open13, readFile as readFile26, rename as rename11, stat as stat11, unlink as unlink4 } from "fs/promises";
23414
23474
  import { dirname as dirname22 } from "path";
23415
23475
  function defaultProcessAlive(pid) {
@@ -23435,7 +23495,7 @@ async function acquirePalFileLock(options) {
23435
23495
  await mkdir22(dirname22(options.path), { recursive: true, mode: 448 });
23436
23496
  const pid = options.pid ?? process.pid;
23437
23497
  const now = options.now ?? Date.now;
23438
- const nonce = randomUUID20();
23498
+ const nonce = randomUUID21();
23439
23499
  const metadata = { pid, createdAt: now(), nonce };
23440
23500
  let handle;
23441
23501
  try {
@@ -23554,7 +23614,7 @@ async function ensureOnce(options) {
23554
23614
  }
23555
23615
 
23556
23616
  // src/pals/client.ts
23557
- import { randomUUID as randomUUID22 } from "crypto";
23617
+ import { randomUUID as randomUUID23 } from "crypto";
23558
23618
  import { Socket as Socket3 } from "net";
23559
23619
  var DEFAULT_REQUEST_TIMEOUT_MS = 5e3;
23560
23620
  var DEFAULT_HEARTBEAT_INTERVAL_MS = 1e4;
@@ -23630,7 +23690,7 @@ var PalClient = class {
23630
23690
  return this.starting;
23631
23691
  }
23632
23692
  async list() {
23633
- const data2 = await this.request({ version: 1, type: "list", requestId: randomUUID22() });
23693
+ const data2 = await this.request({ version: 1, type: "list", requestId: randomUUID23() });
23634
23694
  return PalPresenceListSchema.parse(data2);
23635
23695
  }
23636
23696
  async rename(alias) {
@@ -23643,38 +23703,38 @@ var PalClient = class {
23643
23703
  throw error;
23644
23704
  }
23645
23705
  }
23646
- async sendTask(target, message2, messageId = randomUUID22()) {
23647
- const data2 = await this.request({ version: 1, type: "task", requestId: randomUUID22(), messageId, target, goal: message2 });
23706
+ async sendTask(target, message2, messageId = randomUUID23()) {
23707
+ const data2 = await this.request({ version: 1, type: "task", requestId: randomUUID23(), messageId, target, goal: message2 });
23648
23708
  return DeliveryReceiptSchema.parse(data2);
23649
23709
  }
23650
- async sendChat(target, message2, messageId = randomUUID22()) {
23651
- const data2 = await this.request({ version: 1, type: "chat", requestId: randomUUID22(), messageId, target, message: message2 });
23710
+ async sendChat(target, message2, messageId = randomUUID23()) {
23711
+ const data2 = await this.request({ version: 1, type: "chat", requestId: randomUUID23(), messageId, target, message: message2 });
23652
23712
  return DeliveryReceiptSchema.parse(data2);
23653
23713
  }
23654
23714
  async startCoWork(input) {
23655
23715
  const data2 = await this.request({
23656
23716
  version: 1,
23657
23717
  type: "cowork-propose",
23658
- requestId: randomUUID22(),
23659
- coWorkId: input.coWorkId ?? randomUUID22(),
23718
+ requestId: randomUUID23(),
23719
+ coWorkId: input.coWorkId ?? randomUUID23(),
23660
23720
  goal: input.goal,
23661
23721
  participants: input.participants
23662
23722
  });
23663
23723
  return CoWorkSnapshotSchema.parse(data2);
23664
23724
  }
23665
23725
  async coWorkAction(action) {
23666
- const request = { version: 1, requestId: randomUUID22(), ...action };
23726
+ const request = { version: 1, requestId: randomUUID23(), ...action };
23667
23727
  return CoWorkSnapshotSchema.parse(await this.request(request));
23668
23728
  }
23669
23729
  async coWorkStatus(coWorkId) {
23670
- const data2 = await this.request({ version: 1, type: "cowork-get", requestId: randomUUID22(), coWorkId });
23730
+ const data2 = await this.request({ version: 1, type: "cowork-get", requestId: randomUUID23(), coWorkId });
23671
23731
  return CoWorkSnapshotSchema.parse(data2);
23672
23732
  }
23673
23733
  async integrateCoWork(input) {
23674
23734
  return this.coWorkAction({ type: "cowork-integration", ...input });
23675
23735
  }
23676
23736
  async cancelCoWork(coWorkId, reason) {
23677
- const data2 = await this.request({ version: 1, type: "cowork-cancel", requestId: randomUUID22(), coWorkId, reason });
23737
+ const data2 = await this.request({ version: 1, type: "cowork-cancel", requestId: randomUUID23(), coWorkId, reason });
23678
23738
  return CoWorkSnapshotSchema.parse(data2);
23679
23739
  }
23680
23740
  subscribe(listener) {
@@ -23686,7 +23746,7 @@ var PalClient = class {
23686
23746
  if (this.registered && this.socket !== void 0) {
23687
23747
  let disconnectTimer;
23688
23748
  await Promise.race([
23689
- this.request({ version: 1, type: "disconnect", requestId: randomUUID22() }).catch(() => void 0),
23749
+ this.request({ version: 1, type: "disconnect", requestId: randomUUID23() }).catch(() => void 0),
23690
23750
  new Promise((resolve41) => {
23691
23751
  disconnectTimer = setTimeout(resolve41, Math.min(this.requestTimeoutMs, 250));
23692
23752
  })
@@ -23777,7 +23837,7 @@ var PalClient = class {
23777
23837
  const data2 = await this.request({
23778
23838
  version: 1,
23779
23839
  type: "register",
23780
- requestId: randomUUID22(),
23840
+ requestId: randomUUID23(),
23781
23841
  authToken: this.authToken,
23782
23842
  presence: { version: 1, ...this.registration, connectedAt: now, lastSeenAt: now }
23783
23843
  });
@@ -23889,7 +23949,7 @@ var PalClient = class {
23889
23949
  if (!this.registered) return;
23890
23950
  if (this.heartbeatPending) return;
23891
23951
  this.heartbeatPending = true;
23892
- void this.request({ version: 1, type: "heartbeat", requestId: randomUUID22() }).catch(() => void 0).finally(() => {
23952
+ void this.request({ version: 1, type: "heartbeat", requestId: randomUUID23() }).catch(() => void 0).finally(() => {
23893
23953
  this.heartbeatPending = false;
23894
23954
  });
23895
23955
  }, this.heartbeatIntervalMs);
@@ -24176,7 +24236,7 @@ var ApprovalBridge = class {
24176
24236
  request(request, signal = new AbortController().signal) {
24177
24237
  if (this.#settle !== void 0) return Promise.resolve("deny");
24178
24238
  if (signal.aborted) return Promise.resolve("deny");
24179
- this.#pending = { id: randomUUID23(), ...request };
24239
+ this.#pending = { id: randomUUID24(), ...request };
24180
24240
  this.#onChange?.();
24181
24241
  return new Promise((resolvePromise) => {
24182
24242
  this.#settle = resolvePromise;
@@ -24295,7 +24355,14 @@ async function createProductionRuntime(options) {
24295
24355
  environment.OPENAI_API_KEY,
24296
24356
  environment.ANTHROPIC_API_KEY
24297
24357
  ].filter((value) => typeof value === "string" && value.length > 0);
24298
- const hooks = new HookBus();
24358
+ let hookSessionId;
24359
+ const hooks = new HookBus({
24360
+ eventContext: () => ({
24361
+ protocolVersion: 2,
24362
+ workspace,
24363
+ ...hookSessionId === void 0 ? {} : { sessionId: hookSessionId }
24364
+ })
24365
+ });
24299
24366
  hooks.on(
24300
24367
  "PostToolUseFailure",
24301
24368
  createIncidentReporter({
@@ -24315,8 +24382,7 @@ async function createProductionRuntime(options) {
24315
24382
  diagnostics.push(...permissionPolicy.diagnostics);
24316
24383
  const approvals = new ApprovalBridge(options.onApprovalChange);
24317
24384
  const resolveToolApproval = options.approvalPolicy === "deny" && options.rpcToolApprovals !== true ? () => "deny" : (request, signal) => approvals.request(request, signal);
24318
- const questions = new QuestionBridge(options.onApprovalChange);
24319
- const askUserQuestionHandler = async (qs, signal) => {
24385
+ const relayQuestions = async (qs, signal) => {
24320
24386
  if (options.approvalPolicy === "deny") throw new Error("AskUserQuestion is not available in non-interactive mode");
24321
24387
  if (hooks.hasListeners("PermissionRequest")) {
24322
24388
  let decision;
@@ -24334,8 +24400,10 @@ async function createProductionRuntime(options) {
24334
24400
  const hookAnswers = hookAnswersFromUpdatedInput(decision.updatedInput, qs);
24335
24401
  if (hookAnswers !== void 0) return hookAnswers;
24336
24402
  }
24337
- return questions.ask(qs, signal);
24403
+ return void 0;
24338
24404
  };
24405
+ const questions = new QuestionBridge(options.onApprovalChange, relayQuestions);
24406
+ const askUserQuestionHandler = (qs, signal) => questions.ask(qs, signal);
24339
24407
  const executionEnvironment = createExecutionEnvironment(workspace, config.execution);
24340
24408
  const jobs = new JobRegistry();
24341
24409
  const terminals = new TerminalService(workspace, { jobs });
@@ -24659,7 +24727,8 @@ async function createProductionRuntime(options) {
24659
24727
  let taskResults = { ...recovered?.tasks.results ?? {} };
24660
24728
  const subagentStartedAt = {};
24661
24729
  const subagentElapsedMs = {};
24662
- let sessionId = recovered?.sessionId ?? `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID23().slice(0, 8)}`;
24730
+ let sessionId = recovered?.sessionId ?? `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID24().slice(0, 8)}`;
24731
+ hookSessionId = sessionId;
24663
24732
  let harnessJournal = new HarnessJournal({ workspace, sessionId });
24664
24733
  const harnessRecovery = harnessJournal.recover();
24665
24734
  if (harnessRecovery.queue.length > 0 || harnessRecovery.incompleteTools.length > 0 || harnessRecovery.incompleteModelIds.length > 0 || harnessRecovery.interruptedTurnIds.length > 0) {
@@ -24743,7 +24812,13 @@ async function createProductionRuntime(options) {
24743
24812
  const publishTaskState = async () => {
24744
24813
  harness.main.context.updateTaskState(serializedTaskState());
24745
24814
  await persist();
24746
- emitOutput({ type: "tasks", snapshot: taskSnapshot() });
24815
+ const snapshot4 = taskSnapshot();
24816
+ emitOutput({ type: "tasks", snapshot: snapshot4 });
24817
+ await hooks.emit({
24818
+ version: 1,
24819
+ type: "Notification",
24820
+ payload: { kind: "task_snapshot", taskSnapshot: snapshot4 }
24821
+ }).catch(() => void 0);
24747
24822
  };
24748
24823
  for (const tool of createTaskPlanTools({
24749
24824
  getPlan: () => taskPlan,
@@ -25301,7 +25376,7 @@ async function createProductionRuntime(options) {
25301
25376
  return answers[0] === "Continue" ? "approved" : "rejected";
25302
25377
  },
25303
25378
  fingerprint: workspaceFingerprint,
25304
- idFactory: () => `loop-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID23().slice(0, 8)}`
25379
+ idFactory: () => `loop-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID24().slice(0, 8)}`
25305
25380
  });
25306
25381
  const goalStore = new GoalStore({ workspace });
25307
25382
  const goalOrchestrator = new GoalOrchestrator({
@@ -25320,7 +25395,7 @@ async function createProductionRuntime(options) {
25320
25395
  executionEnvironment
25321
25396
  ),
25322
25397
  now: () => (/* @__PURE__ */ new Date()).toISOString(),
25323
- idFactory: () => `goal-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID23().slice(0, 8)}`,
25398
+ idFactory: () => `goal-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID24().slice(0, 8)}`,
25324
25399
  runWorker: ({ workspace: goalWorkspace, prompt, signal }) => runLoopWorker({ workspace: goalWorkspace, prompt, signal })
25325
25400
  });
25326
25401
  let sessionHistory = await SessionHistory.open({
@@ -25433,7 +25508,7 @@ async function createProductionRuntime(options) {
25433
25508
  runExplicitSkill(harness, skills, skill, prompt, signal, selectedModels.mainError),
25434
25509
  persist
25435
25510
  ),
25436
- runLoop: (goal, signal) => runLoopSession(loopOrchestrator, goal, signal),
25511
+ runLoop: (goal, signal) => runLoopSession(loopOrchestrator, hooks, goal, signal),
25437
25512
  runGoal: (goal, signal) => persistEach(runGoalSession(goalOrchestrator, goal, signal), persist),
25438
25513
  mcp: async (command, signal) => {
25439
25514
  signal.throwIfAborted();
@@ -25591,7 +25666,8 @@ ${summary2}`;
25591
25666
  for (const key of Object.keys(subagentStartedAt)) delete subagentStartedAt[key];
25592
25667
  for (const key of Object.keys(subagentElapsedMs)) delete subagentElapsedMs[key];
25593
25668
  const previousIdeSessionId = ideSessionId;
25594
- sessionId = `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID23().slice(0, 8)}`;
25669
+ sessionId = `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID24().slice(0, 8)}`;
25670
+ hookSessionId = sessionId;
25595
25671
  harnessJournal = new HarnessJournal({ workspace, sessionId });
25596
25672
  setUsageSession(sessionId);
25597
25673
  ideSessionId = sessionId;
@@ -25890,9 +25966,10 @@ function formatMcpReconnect(server) {
25890
25966
  if (server.status === "disabled") return `MCP server "${server.name}" is disabled. Enable it before reconnecting.`;
25891
25967
  return `Failed to reconnect MCP server "${server.name}": ${server.error ?? "unknown error"}`;
25892
25968
  }
25893
- async function* runLoopSession(orchestrator, goal, signal) {
25969
+ async function* runLoopSession(orchestrator, hooks, goal, signal) {
25894
25970
  let totalInputTokens = 0;
25895
25971
  let totalOutputTokens = 0;
25972
+ let latestVerification;
25896
25973
  for await (const event of orchestrator.run({ goal, signal })) {
25897
25974
  if (event.type === "worker-event") {
25898
25975
  if (event.event.type === "usage") {
@@ -25906,8 +25983,19 @@ async function* runLoopSession(orchestrator, goal, signal) {
25906
25983
  } else if (event.event.type !== "done") yield event.event;
25907
25984
  continue;
25908
25985
  }
25986
+ if (event.type === "loop-verification") latestVerification = event.evidence;
25909
25987
  yield loopProgressEvent(event);
25910
25988
  if (event.type === "loop-terminal") {
25989
+ await hooks.emit({
25990
+ version: 1,
25991
+ type: "LoopEnd",
25992
+ payload: {
25993
+ loopId: event.loopId,
25994
+ outcome: event.status,
25995
+ reason: event.reason,
25996
+ verification: latestVerification ?? null
25997
+ }
25998
+ }).catch(() => void 0);
25911
25999
  yield { type: "done", usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens } };
25912
26000
  }
25913
26001
  }
@@ -26621,7 +26709,7 @@ function memoryTranscriptHash(messages) {
26621
26709
  return createHash20("sha256").update(visible, "utf8").digest("hex");
26622
26710
  }
26623
26711
  function createMemoryTaskId() {
26624
- return `memory-${randomUUID23()}`;
26712
+ return `memory-${randomUUID24()}`;
26625
26713
  }
26626
26714
 
26627
26715
  // src/desktop/runtime-controller.ts
@@ -27605,13 +27693,13 @@ ${managed.output().slice(-4e3)}`);
27605
27693
  }
27606
27694
 
27607
27695
  // src/d2c/product.ts
27608
- import { createHash as createHash23, randomUUID as randomUUID25 } from "crypto";
27696
+ import { createHash as createHash23, randomUUID as randomUUID26 } from "crypto";
27609
27697
  import { copyFile as copyFile2, mkdir as mkdir25, readFile as readFile32, rename as rename13, stat as stat12, unlink as unlink6, writeFile as writeFile15 } from "fs/promises";
27610
27698
  import { join as join36 } from "path";
27611
27699
  import { z as z44 } from "zod";
27612
27700
 
27613
27701
  // src/e2e/prd-governance.ts
27614
- import { createHash as createHash22, randomUUID as randomUUID24 } from "crypto";
27702
+ import { createHash as createHash22, randomUUID as randomUUID25 } from "crypto";
27615
27703
  import { readFile as readFile31, rename as rename12, rm as rm13, writeFile as writeFile14 } from "fs/promises";
27616
27704
  function hashPrd(markdown) {
27617
27705
  return createHash22("sha256").update(markdown, "utf8").digest("hex");
@@ -27711,7 +27799,7 @@ ${body}
27711
27799
 
27712
27800
  `;
27713
27801
  const markdown = `${current.slice(0, section.contentStart)}${replacement}${current.slice(section.contentEnd)}`;
27714
- const temporary = `${path}.${process.pid}.${randomUUID24()}.tmp`;
27802
+ const temporary = `${path}.${process.pid}.${randomUUID25()}.tmp`;
27715
27803
  try {
27716
27804
  await writeFile14(temporary, markdown, { encoding: "utf8", flag: "wx", mode: 384 });
27717
27805
  await rename12(temporary, path);
@@ -27911,7 +27999,7 @@ async function writeD2cProductPlan(workspace, plan, operations = defaultPlanFile
27911
27999
  const target = planPath(workspace, parsed.task);
27912
28000
  await serializePlanWrite(target, async () => {
27913
28001
  await mkdir25(taskDir(workspace, parsed.task), { recursive: true });
27914
- const temporary = `${target}.${process.pid}.${randomUUID25()}.tmp`;
28002
+ const temporary = `${target}.${process.pid}.${randomUUID26()}.tmp`;
27915
28003
  await writeFile15(temporary, `${JSON.stringify(parsed, null, 2)}
27916
28004
  `, { encoding: "utf8", mode: 384 });
27917
28005
  await replaceD2cProductPlanFile(temporary, target, operations);
@@ -30364,7 +30452,7 @@ function safeGitTarget(value) {
30364
30452
  }
30365
30453
 
30366
30454
  // src/desktop/worktree-manager.ts
30367
- import { randomUUID as randomUUID26 } from "crypto";
30455
+ import { randomUUID as randomUUID27 } from "crypto";
30368
30456
  import { mkdir as mkdir28, readFile as readFile37, readdir as readdir13, rm as rm17, stat as stat15, writeFile as writeFile18 } from "fs/promises";
30369
30457
  import { basename as basename10, isAbsolute as isAbsolute19, join as join43, relative as relative27, resolve as resolve37, sep as sep27 } from "path";
30370
30458
  var DesktopWorktreeManager = class {
@@ -30377,7 +30465,7 @@ var DesktopWorktreeManager = class {
30377
30465
  async create(label) {
30378
30466
  if (!await isGitRepository(this.#repository)) throw new Error("Task worktrees require a Git repository");
30379
30467
  const safe = label.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 36) || "task";
30380
- const suffix = randomUUID26().slice(0, 8);
30468
+ const suffix = randomUUID27().slice(0, 8);
30381
30469
  const id = `worktree-${safe}-${suffix}`;
30382
30470
  const branch = `flavor/desktop-${safe}-${suffix}`;
30383
30471
  const path = this.#safeStoragePath(id);
@@ -30841,7 +30929,7 @@ function emitManagedDesktopEvent(workspace, owner2, event) {
30841
30929
  }
30842
30930
  }
30843
30931
  function createDesktopController(workspace, owner2) {
30844
- const collaborationId = randomUUID27();
30932
+ const collaborationId = randomUUID28();
30845
30933
  return new DesktopRuntimeController({
30846
30934
  emit: (event) => emitManagedDesktopEvent(workspace, owner2, event),
30847
30935
  runD2cInteractionTests: (manifest, baseUrl, mockUrl) => d2cEmbedded.run(manifest, baseUrl, mockUrl),