niuma-codex 0.0.1-beta.27 → 0.0.1-beta.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/managed.js CHANGED
@@ -8485,9 +8485,42 @@ function parseAttachmentTaskV2StatusQuery(value) {
8485
8485
  return { ...parseAttachmentTaskStatusQuery(normalize(value, true)), version: 2 };
8486
8486
  }
8487
8487
 
8488
+ // packages/protocol/src/thread-load-trace.ts
8489
+ var CLIENT_LOAD_PHASES = ["selected", "restored", "request_started", "snapshot_received", "history_ready", "content_presented"];
8490
+ function object8(value, required, optional = []) {
8491
+ if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) throw new TypeError("invalid_load_report");
8492
+ const descriptors = Object.getOwnPropertyDescriptors(value);
8493
+ if (Reflect.ownKeys(value).some((key) => typeof key !== "string" || ![...required, ...optional].includes(key)) || required.some((key) => !Object.hasOwn(descriptors, key)) || Object.values(descriptors).some((d) => !d.enumerable || !("value" in d))) throw new TypeError("invalid_load_report");
8494
+ return value;
8495
+ }
8496
+ function timing(value) {
8497
+ if (!Number.isInteger(value.elapsed_ms) || value.elapsed_ms < 0 || value.elapsed_ms > 9e5 || value.occurred_at_ms !== void 0 && (!Number.isInteger(value.occurred_at_ms) || value.occurred_at_ms < 1 || value.occurred_at_ms > 864e13)) throw new TypeError("invalid_load_timing");
8498
+ }
8499
+ function parseThreadLoadReportV2(value) {
8500
+ const v = object8(value, ["version", "thread_id", "load_id", "client_class", "samples"], ["terminal"]);
8501
+ if (v.version !== 2 || typeof v.thread_id !== "string" || !v.thread_id.length || v.thread_id.length > 256 || typeof v.load_id !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(v.load_id) || !["web", "ios", "other"].includes(v.client_class) || !Array.isArray(v.samples) || v.samples.length > 6 || !v.samples.length && v.terminal === void 0) throw new TypeError("invalid_load_report");
8502
+ for (const sample of v.samples) {
8503
+ const s = object8(sample, ["phase", "elapsed_ms"], ["occurred_at_ms"]);
8504
+ timing(s);
8505
+ if (!CLIENT_LOAD_PHASES.includes(s.phase)) throw new TypeError("invalid_load_phase");
8506
+ }
8507
+ if (v.terminal !== void 0) {
8508
+ const t = object8(v.terminal, ["kind", "phase", "reason", "elapsed_ms"], ["occurred_at_ms"]);
8509
+ timing(t);
8510
+ const reasons = { failed: ["open_rejected", "history_failed"], cancelled: ["left_view", "user_cancelled"], superseded: ["stream_replaced"] };
8511
+ if (!["request_started", "snapshot_received", "history_ready"].includes(t.phase) || !Object.hasOwn(reasons, t.kind) || !reasons[t.kind]?.includes(t.reason) || v.samples.some((s) => s.phase === "content_presented")) throw new TypeError("invalid_load_terminal");
8512
+ }
8513
+ return v;
8514
+ }
8515
+ function reduceThreadLoadOutcome(current2, event) {
8516
+ if (event === "closed" || event === "expired") return current2.status === "loading" ? { ...current2, status: "incomplete" } : current2;
8517
+ if (current2.status === "loading" || current2.status === "incomplete" && ["cancelled", "superseded"].includes(event)) return { ...current2, status: event };
8518
+ return current2.status === event ? current2 : { ...current2, conflict: true };
8519
+ }
8520
+
8488
8521
  // apps/desktop/src/release-version.ts
8489
8522
  function desktopReleaseVersion() {
8490
- return false ? null : "0.0.1-beta.27";
8523
+ return false ? null : "0.0.1-beta.29";
8491
8524
  }
8492
8525
  var sourcePresenceVersion = (() => {
8493
8526
  if (true) return null;
@@ -11986,6 +12019,59 @@ function discoverDirectGatewayOrigins(host, port, interfaces = networkInterfaces
11986
12019
  // apps/desktop/src/daemon/launcher.ts
11987
12020
  var BACKGROUND_DAEMON_STARTUP_TIMEOUT_MS = Math.max(6e4, desktopPlatform().daemon.startupTimeoutMs);
11988
12021
 
12022
+ // apps/desktop/src/daemon/runtime/ipc-thread-discovery.ts
12023
+ var RuntimeIpcThreadDiscovery = class {
12024
+ constructor(ports) {
12025
+ this.ports = ports;
12026
+ }
12027
+ ports;
12028
+ pending = /* @__PURE__ */ new Map();
12029
+ discover(id5) {
12030
+ const started2 = Date.now();
12031
+ const report = (phase) => {
12032
+ try {
12033
+ this.ports.diagnostic(id5, phase, Date.now() - started2);
12034
+ } catch {
12035
+ }
12036
+ };
12037
+ if (!this.ports.eligible(id5)) {
12038
+ report("ignored");
12039
+ return;
12040
+ }
12041
+ if (this.pending.has(id5)) {
12042
+ report("deduplicated");
12043
+ return;
12044
+ }
12045
+ if (this.pending.size >= 66) {
12046
+ report("capacity");
12047
+ return;
12048
+ }
12049
+ const current2 = this.ports.capture();
12050
+ report("received");
12051
+ const task = this.run(id5, current2, report).catch(() => report("failed")).finally(() => {
12052
+ if (this.pending.get(id5) === task) this.pending.delete(id5);
12053
+ });
12054
+ this.pending.set(id5, task);
12055
+ }
12056
+ async run(id5, current2, report) {
12057
+ const result = await this.ports.read(id5);
12058
+ report("read_completed");
12059
+ const valid = () => current2() && this.ports.eligible(id5) && result.kind === "ordinary" && result.summary?.id === id5 && !!result.token && this.ports.current(result.token);
12060
+ if (!valid()) {
12061
+ report("rejected");
12062
+ return;
12063
+ }
12064
+ await this.ports.enqueue(() => {
12065
+ if (!valid()) {
12066
+ report("stale");
12067
+ return;
12068
+ }
12069
+ this.ports.commit(result.summary);
12070
+ report("published");
12071
+ });
12072
+ }
12073
+ };
12074
+
11989
12075
  // apps/desktop/src/daemon/async-question-response.ts
11990
12076
  import { createHash } from "node:crypto";
11991
12077
  function submissionId(input) {
@@ -14763,11 +14849,11 @@ var RuntimeActivityPublication = class {
14763
14849
  if (!thread4) {
14764
14850
  return null;
14765
14851
  }
14766
- const timing = this.ports.turnTiming(signal.threadId, signal.turnId);
14767
- const timingFields = timing ? {
14768
- turnStartedAt: timing.startedAt,
14769
- ...timing.endedAt !== void 0 ? { turnEndedAt: timing.endedAt } : {},
14770
- ...timing.outcome !== void 0 ? { turnOutcome: timing.outcome } : {}
14852
+ const timing2 = this.ports.turnTiming(signal.threadId, signal.turnId);
14853
+ const timingFields = timing2 ? {
14854
+ turnStartedAt: timing2.startedAt,
14855
+ ...timing2.endedAt !== void 0 ? { turnEndedAt: timing2.endedAt } : {},
14856
+ ...timing2.outcome !== void 0 ? { turnOutcome: timing2.outcome } : {}
14771
14857
  } : {};
14772
14858
  if (signal.type === "turn_aborted") {
14773
14859
  return {
@@ -20297,7 +20383,7 @@ function fail14(reason) {
20297
20383
  var clone = (value) => structuredClone(value);
20298
20384
  var diskEntry = (entry) => ({ ...entry, nativeToken: entry.nativeToken !== null });
20299
20385
  var byteSize = (value) => Buffer.byteLength(JSON.stringify(value));
20300
- function object8(value, keys3, optional = []) {
20386
+ function object9(value, keys3, optional = []) {
20301
20387
  if (!value || typeof value !== "object" || Array.isArray(value)) return fail14("invalid");
20302
20388
  const record25 = value;
20303
20389
  if (Object.keys(record25).some((key) => !keys3.includes(key) && !optional.includes(key)) || !keys3.every((key) => Object.hasOwn(record25, key))) return fail14("invalid");
@@ -20331,7 +20417,7 @@ function parseReceipt(value, clientMessageId) {
20331
20417
  return receipt;
20332
20418
  }
20333
20419
  function parsePost(value, clientMessageId) {
20334
- const record25 = object8(value, ["httpStatus", "code", "message", "data"]);
20420
+ const record25 = object9(value, ["httpStatus", "code", "message", "data"]);
20335
20421
  if (![200, 400, 404, 500].includes(record25.httpStatus) || !Number.isInteger(record25.code) || record25.code !== 0 && (record25.code < 1e5 || record25.code > 999999) || typeof record25.message !== "string" || record25.message.length === 0 || Buffer.byteLength(record25.message) > MAX_MESSAGE_BYTES || byteSize(record25.message) > MAX_MESSAGE_BYTES + 2) fail14("invalid");
20336
20422
  if (record25.code === 0) {
20337
20423
  if (record25.httpStatus !== 200) fail14("invalid");
@@ -20344,10 +20430,10 @@ function parsePost(value, clientMessageId) {
20344
20430
  } else if (record25.data !== null) {
20345
20431
  const data = record25.data;
20346
20432
  if (data.state === "unknown") {
20347
- object8(data, ["client_message_id", "state"]);
20433
+ object9(data, ["client_message_id", "state"]);
20348
20434
  if (data.client_message_id !== clientMessageId) fail14("invalid");
20349
20435
  } else {
20350
- object8(data, ["dispatch_state"]);
20436
+ object9(data, ["dispatch_state"]);
20351
20437
  if (data.dispatch_state !== "not_dispatched") fail14("invalid");
20352
20438
  }
20353
20439
  }
@@ -20380,13 +20466,13 @@ function parseDocument(raw, maxEntries) {
20380
20466
  } catch {
20381
20467
  return fail14("invalid");
20382
20468
  }
20383
- const document = object8(value, ["version", "entries"]);
20469
+ const document = object9(value, ["version", "entries"]);
20384
20470
  if (document.version !== 1 || !Array.isArray(document.entries)) fail14("invalid");
20385
20471
  if (document.entries.length > maxEntries) fail14("capacity");
20386
20472
  const entries = /* @__PURE__ */ new Map();
20387
20473
  const nativeItems = /* @__PURE__ */ new Set();
20388
20474
  for (const value2 of document.entries) {
20389
- const record25 = object8(value2, ["principal", "threadId", "clientMessageId", "fingerprint", "nativeToken", "receipt", "firstResult", "createdAt", "updatedAt"], ["questionId"]);
20475
+ const record25 = object9(value2, ["principal", "threadId", "clientMessageId", "fingerprint", "nativeToken", "receipt", "firstResult", "createdAt", "updatedAt"], ["questionId"]);
20390
20476
  if (typeof record25.nativeToken !== "boolean" || !Number.isSafeInteger(record25.createdAt) || record25.createdAt < 0 || !Number.isSafeInteger(record25.updatedAt) || record25.updatedAt < 0) fail14("invalid");
20391
20477
  const entry = { ...record25, nativeToken: record25.nativeToken ? record25.clientMessageId : null };
20392
20478
  validateReservation(entry);
@@ -20478,7 +20564,7 @@ async function openMessageSubmissionStore(options) {
20478
20564
  reserve(input) {
20479
20565
  const captured = clone(input);
20480
20566
  return enqueue(async () => {
20481
- object8(captured, ["principal", "threadId", "clientMessageId", "fingerprint", "nativeToken"], ["questionId"]);
20567
+ object9(captured, ["principal", "threadId", "clientMessageId", "fingerprint", "nativeToken"], ["questionId"]);
20482
20568
  validateReservation(captured);
20483
20569
  const existing = entries.get(captured.clientMessageId);
20484
20570
  if (existing) {
@@ -23728,8 +23814,8 @@ var AppServerTransport = class {
23728
23814
  /** 慢响应只保留每分钟首条及抑制数;超时和协议失败仍逐次保留。 */
23729
23815
  auditSlowResponse(id5, pending, bytes3, failed) {
23730
23816
  if (!Number.isFinite(pending.startedAtMs)) return;
23731
- const timing = this.requestTiming(pending), now = performance.now();
23732
- if (timing.elapsedMs < 5e3) return;
23817
+ const timing2 = this.requestTiming(pending), now = performance.now();
23818
+ if (timing2.elapsedMs < 5e3) return;
23733
23819
  if (now - this.lastSlowAuditAtMs < 6e4) {
23734
23820
  this.suppressedSlowAudits++;
23735
23821
  return;
@@ -23740,7 +23826,7 @@ var AppServerTransport = class {
23740
23826
  ...typeof id5 === "number" ? { requestId: id5 } : {},
23741
23827
  ...isSafeErrorAuditOperation(pending.method) ? { method: pending.method } : {},
23742
23828
  bytes: bytes3,
23743
- requestTiming: timing,
23829
+ requestTiming: timing2,
23744
23830
  responseDisposition: failed ? "error" : "success",
23745
23831
  suppressedCount: this.suppressedSlowAudits
23746
23832
  });
@@ -28051,7 +28137,7 @@ import { randomBytes as randomBytes3 } from "node:crypto";
28051
28137
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
28052
28138
  function logicalDescriptor(descriptor) {
28053
28139
  if (descriptor.pathname !== "/api/v1/thread/open") return descriptor;
28054
- const { load_id: _loadId, range_resume_token: _rangeToken, ...query2 } = descriptor.query;
28140
+ const { load_id: _loadId, load_trace: _loadTrace, range_resume_token: _rangeToken, ...query2 } = descriptor.query;
28055
28141
  return { pathname: descriptor.pathname, query: query2 };
28056
28142
  }
28057
28143
  function fail15(code, message) {
@@ -29320,6 +29406,11 @@ var AutostartError = class extends Error {
29320
29406
  };
29321
29407
 
29322
29408
  // apps/desktop/src/pairing-web/settings.ts
29409
+ function dispatchSettingsVersion(request2) {
29410
+ if (request2.url !== "/api/v1/settings/version" || request2.method !== "GET") throw new HttpApiError(404, 900005, "\u63A5\u53E3\u4E0D\u5B58\u5728");
29411
+ if (request2.headers["transfer-encoding"] || Number(request2.headers["content-length"] ?? 0) > 0) throw new HttpApiError(400, 100004, "\u67E5\u8BE2\u4E0D\u80FD\u643A\u5E26\u8BF7\u6C42\u4F53");
29412
+ return { daemon_version: desktopPresenceVersion() };
29413
+ }
29323
29414
  function projection(s) {
29324
29415
  return {
29325
29416
  supported: s.supported,
@@ -30253,8 +30344,7 @@ function invalidPushServerOrigin() {
30253
30344
  function resolvePushServerOrigin(environment) {
30254
30345
  const value = environment.NIUMA_PUSH_SERVER_ORIGIN;
30255
30346
  if (value === void 0 || isBlankConfigValue(value)) {
30256
- const release2 = true;
30257
- return release2 ? DEFAULT_PUSH_PUBLIC_URL : resolveLocalServerOrigin(environment);
30347
+ return DEFAULT_PUSH_PUBLIC_URL;
30258
30348
  }
30259
30349
  if (URL_CONTROL_OR_SPACE_PATTERN.test(value)) {
30260
30350
  throw invalidPushServerOrigin();
@@ -30357,6 +30447,20 @@ var ThreadLoadReportSchema = z8.object({
30357
30447
  load_id: z8.string().uuid(),
30358
30448
  samples: z8.array(LoadSampleSchema).min(1).max(6)
30359
30449
  }).strict();
30450
+ var ThreadLoadReportV2Schema = z8.object({
30451
+ version: z8.literal(2),
30452
+ thread_id: z8.string().min(1).max(256),
30453
+ load_id: z8.string().uuid(),
30454
+ client_class: z8.enum(["web", "ios", "other"]),
30455
+ samples: z8.array(LoadSampleSchema).max(6),
30456
+ terminal: z8.object({ kind: z8.enum(["failed", "cancelled", "superseded"]), phase: z8.enum(["request_started", "snapshot_received", "history_ready"]), reason: z8.enum(["open_rejected", "history_failed", "left_view", "user_cancelled", "stream_replaced"]), elapsed_ms: z8.number().int().min(0).max(9e5), occurred_at_ms: occurredAtSchema }).strict().optional()
30457
+ }).strict().superRefine((value, context) => {
30458
+ try {
30459
+ parseThreadLoadReportV2(value);
30460
+ } catch {
30461
+ context.addIssue({ code: "custom", message: "\u52A0\u8F7D\u91C7\u6837\u7EC4\u5408\u65E0\u6548" });
30462
+ }
30463
+ });
30360
30464
  var LoadingTraceSchema = z8.object({
30361
30465
  load_id: z8.string().uuid(),
30362
30466
  lease_id: z8.string().regex(/^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u),
@@ -30366,27 +30470,128 @@ var LoadingTraceSchema = z8.object({
30366
30470
  occurred_at_ms: occurredAtSchema
30367
30471
  }).strict().refine((value) => value.clock === "browser" ? ClientLoadPhaseSchema.safeParse(value.phase).success : ["open_received", "identity_ready", "lease_acquired", "shell_sent", "history_sent", "stream_closed"].includes(value.phase));
30368
30472
  var ThreadLoadReports = class {
30369
- constructor(now = Date.now) {
30473
+ constructor(now = Date.now, audit = () => void 0) {
30370
30474
  this.now = now;
30475
+ this.audit = audit;
30371
30476
  }
30372
30477
  now;
30478
+ audit;
30373
30479
  entries = /* @__PURE__ */ new Map();
30374
- register(loadId, threadId2, owner2, record25) {
30480
+ used = /* @__PURE__ */ new Set();
30481
+ clear() {
30482
+ this.entries.clear();
30483
+ }
30484
+ begin(input) {
30375
30485
  for (const [key, value] of this.entries) if (value.expires < this.now()) this.entries.delete(key);
30376
- if (!z8.string().uuid().safeParse(loadId).success || this.entries.size >= 256 || this.entries.has(loadId)) return () => {
30486
+ if (!z8.string().uuid().safeParse(input.loadId).success || this.entries.size >= 256 || this.used.size >= 1e4 || this.used.has(input.loadId)) return null;
30487
+ const id5 = input.loadId;
30488
+ const writer = this.audit();
30489
+ if (writer?.reserveLoadId && !writer.reserveLoadId(id5)) {
30490
+ this.used.add(id5);
30491
+ return null;
30492
+ }
30493
+ this.used.add(id5);
30494
+ const epoch = writer?.loadGeneration?.();
30495
+ let outcome = { status: "loading", conflict: false };
30496
+ let lease, trace;
30497
+ const entry = { threadId: input.threadId, owner: input.owner, expires: input.nowMs + 9e5, seen: /* @__PURE__ */ new Set(), record: () => {
30498
+ }, valid: () => this.entries.get(id5) === entry && writer?.loadGeneration?.() === epoch && this.now() <= entry.expires };
30499
+ const write = (record25) => {
30500
+ if (!entry.valid?.()) return;
30501
+ try {
30502
+ writer?.recordLoad?.({ ...record25, load_id: id5, thread_id: input.threadId, occurred_at_ms: this.now(), ...lease ? { lease_id: lease } : {}, ...trace ? { trace_id: trace } : {} });
30503
+ } catch {
30504
+ }
30377
30505
  };
30378
- const entry = { threadId: threadId2, owner: owner2, expires: this.now() + 9e5, record: record25, seen: /* @__PURE__ */ new Set() };
30506
+ const end = (event) => {
30507
+ outcome = reduceThreadLoadOutcome(outcome, event);
30508
+ entry.expires = Math.min(entry.expires, this.now() + 3e4);
30509
+ };
30510
+ const elapsed = () => Math.max(0, Math.min(9e5, this.now() - input.nowMs));
30511
+ const handle = {
30512
+ markDaemon: (phase, elapsedMs, occurredAtMs = this.now()) => {
30513
+ if (!entry.valid?.() || entry.seen.has("daemon:" + phase)) return;
30514
+ entry.seen.add("daemon:" + phase);
30515
+ write({ record_type: "load_sample", clock: "daemon", phase, elapsed_ms: elapsedMs, event_at_ms: occurredAtMs });
30516
+ },
30517
+ attachLease: (leaseId, traceId) => {
30518
+ lease = leaseId;
30519
+ trace = traceId;
30520
+ },
30521
+ // 每种确定终态只记一次;保留不同证据供 reducer 标示冲突,不延长授权期限。
30522
+ finish: (kind, evidence = {}) => {
30523
+ if (!entry.valid?.() || entry.seen.has("terminal:" + kind)) return;
30524
+ entry.seen.add("terminal:" + kind);
30525
+ write({ record_type: "load_terminal", clock: "daemon", phase: kind, elapsed_ms: elapsed(), ...evidence });
30526
+ end(kind);
30527
+ },
30528
+ close: () => {
30529
+ if (!entry.valid?.() || entry.seen.has("closed")) return;
30530
+ entry.seen.add("closed");
30531
+ write({ record_type: "load_close", clock: "daemon", phase: "closed", elapsed_ms: elapsed() });
30532
+ end("closed");
30533
+ },
30534
+ outcome: () => outcome
30535
+ };
30536
+ entry.handle = handle;
30537
+ entry.accept = (samples, client, terminal) => {
30538
+ for (const sample of samples) if (!entry.seen.has("client:" + sample.phase)) {
30539
+ entry.seen.add("client:" + sample.phase);
30540
+ write({ record_type: "load_sample", clock: "client", phase: sample.phase, elapsed_ms: sample.elapsed_ms, ...sample.occurred_at_ms ? { event_at_ms: sample.occurred_at_ms } : {}, ...client ? { client_class: client } : {} });
30541
+ if (sample.phase === "content_presented") end("presented");
30542
+ try {
30543
+ entry.record(sample);
30544
+ } catch {
30545
+ }
30546
+ }
30547
+ if (terminal && !entry.seen.has("terminal")) {
30548
+ entry.seen.add("terminal");
30549
+ write({ record_type: "load_terminal", clock: "client", phase: terminal.kind, terminal_phase: terminal.phase, reason: terminal.reason, elapsed_ms: terminal.elapsed_ms, ...terminal.occurred_at_ms ? { event_at_ms: terminal.occurred_at_ms } : {}, ...client ? { client_class: client } : {} });
30550
+ end(terminal.kind);
30551
+ }
30552
+ };
30553
+ this.entries.set(id5, entry);
30554
+ write({ record_type: "load_start", clock: "daemon", phase: "open_received", elapsed_ms: 0, source_transport: input.transport });
30555
+ return handle;
30556
+ }
30557
+ register(loadId, threadId2, owner2, record25, attempt) {
30558
+ for (const [key, value] of this.entries) if (value.expires < this.now()) this.entries.delete(key);
30559
+ const existing = this.entries.get(loadId);
30560
+ if (existing?.handle && existing.handle === attempt && existing.threadId === threadId2 && existing.owner === owner2) {
30561
+ existing.record = record25;
30562
+ return () => {
30563
+ };
30564
+ }
30565
+ if (!z8.string().uuid().safeParse(loadId).success || this.entries.size >= 256 || this.used.size >= 1e4 || this.entries.has(loadId) || this.used.has(loadId)) return () => {
30566
+ };
30567
+ const writer = this.audit(), epoch = writer?.loadGeneration?.();
30568
+ if (writer?.reserveLoadId && !writer.reserveLoadId(loadId)) {
30569
+ this.used.add(loadId);
30570
+ return () => {
30571
+ };
30572
+ }
30573
+ this.used.add(loadId);
30574
+ const entry = { threadId: threadId2, owner: owner2, expires: this.now() + 9e5, record: record25, seen: /* @__PURE__ */ new Set(), valid: () => this.entries.get(loadId) === entry && writer?.loadGeneration?.() === epoch && this.now() <= entry.expires };
30379
30575
  this.entries.set(loadId, entry);
30380
30576
  return () => {
30381
30577
  if (this.entries.get(loadId) === entry) this.entries.delete(loadId);
30382
30578
  };
30383
30579
  }
30384
30580
  report(input, owner2) {
30385
- const parsed = ThreadLoadReportSchema.safeParse(input);
30581
+ let parsed;
30582
+ try {
30583
+ parsed = input?.version === 2 ? { success: true, data: parseThreadLoadReportV2(input) } : ThreadLoadReportSchema.safeParse(input);
30584
+ } catch {
30585
+ parsed = { success: false };
30586
+ }
30386
30587
  if (!parsed.success) return { accepted: false };
30387
30588
  const { load_id, thread_id, samples } = parsed.data;
30388
30589
  const entry = this.entries.get(load_id);
30389
- if (!entry || entry.threadId !== thread_id || entry.owner !== owner2 || entry.expires < this.now()) return { accepted: false };
30590
+ if (!entry || entry.threadId !== thread_id || entry.owner !== owner2 || entry.expires < this.now() || entry.valid && !entry.valid()) return { accepted: false };
30591
+ if (entry.accept) {
30592
+ entry.accept(samples, parsed.data.client_class, parsed.data.terminal);
30593
+ return { accepted: true };
30594
+ }
30390
30595
  for (const sample of samples) if (!entry.seen.has(sample.phase)) {
30391
30596
  entry.seen.add(sample.phase);
30392
30597
  try {
@@ -30613,6 +30818,8 @@ var ThreadOpenQuerySchema = z9.object({
30613
30818
  range_resume_version: z9.literal("1").transform(() => 1).optional(),
30614
30819
  range_resume_token: z9.string().regex(/^[A-Za-z0-9_-]{43}$/).optional(),
30615
30820
  load_id: z9.string().uuid().optional(),
30821
+ // 恢复关联仍保留,只有首屏采样可显式关闭。
30822
+ load_trace: z9.literal("false").transform(() => false).optional(),
30616
30823
  include_execution: z9.literal("true").transform(() => true).optional(),
30617
30824
  include_message_times: z9.literal("true").transform(() => true).optional(),
30618
30825
  include_async_questions: z9.literal("true").transform(() => true).optional(),
@@ -31733,6 +31940,7 @@ function createThreadOpenSseProjection(rawQuery, options = {}) {
31733
31940
  ...query2.range_resume_version ? { rangeResumeVersion: 1 } : {},
31734
31941
  ...query2.range_resume_token ? { rangeResumeToken: query2.range_resume_token } : {},
31735
31942
  ...query2.load_id ? { loadId: query2.load_id } : {},
31943
+ ...query2.load_trace === false ? { loadTrace: false } : {},
31736
31944
  viewVersion: THREAD_VIEW_VERSION,
31737
31945
  ...query2.include_async_questions ? { includeAsyncQuestions: true } : {},
31738
31946
  ...query2.include_read_receipts ? { includeReadReceipts: true } : {},
@@ -32767,7 +32975,7 @@ async function dispatchRestRoute(request2, dependencies) {
32767
32975
  }
32768
32976
  }
32769
32977
  if (route === "POST /api/v1/thread/load/report") {
32770
- const body = ThreadLoadReportSchema.parse(request2.body);
32978
+ const body = request2.body?.version === 2 ? ThreadLoadReportV2Schema.parse(request2.body) : ThreadLoadReportSchema.parse(request2.body);
32771
32979
  const result = await daemonClient.request("thread/load/report", body, {
32772
32980
  ...daemonRequestOptions(5e3, request2.signal),
32773
32981
  ...request2.authenticatedMessageSubmissionScope !== void 0 ? { authenticatedMessageSubmissionScope: request2.authenticatedMessageSubmissionScope } : {}
@@ -42937,7 +43145,7 @@ var PairingWebServer = class {
42937
43145
  const api = raw.startsWith("/api/");
42938
43146
  authorizePairingRequest(request2, request2.socket.localPort, api);
42939
43147
  if (api) {
42940
- sendJson(response, 200, successResponse(await (raw.startsWith("/api/v1/settings/") && this.settings ? dispatchAutostartSettings(request2, this.settings) : dispatchPairingRoute(request2, this.service))));
43148
+ sendJson(response, 200, successResponse(await (raw === "/api/v1/settings/version" ? dispatchSettingsVersion(request2) : raw.startsWith("/api/v1/settings/") && this.settings ? dispatchAutostartSettings(request2, this.settings) : dispatchPairingRoute(request2, this.service))));
42941
43149
  return;
42942
43150
  }
42943
43151
  const asset = ASSETS[raw];
@@ -43063,8 +43271,8 @@ function createRelayAttachmentDataLink(options) {
43063
43271
  const child = { permit, lease: null, ws: null, receiver: null, closed: false, paired: false, ready: false, opening: null, cleanup: null, unregister: null };
43064
43272
  children.set(permit.transfer_id, child);
43065
43273
  const registrationStarted = Date.now();
43066
- const timing = () => ({ attach_remaining_ms: permit.attach_expires_at - Date.now(), transfer_remaining_ms: permit.expires_at - Date.now(), registration_elapsed_ms: Date.now() - registrationStarted });
43067
- recordAttachmentDiagnostic(options.diagnostic, { phase: "file.register", reason: "started", ...timing() });
43274
+ const timing2 = () => ({ attach_remaining_ms: permit.attach_expires_at - Date.now(), transfer_remaining_ms: permit.expires_at - Date.now(), registration_elapsed_ms: Date.now() - registrationStarted });
43275
+ recordAttachmentDiagnostic(options.diagnostic, { phase: "file.register", reason: "started", ...timing2() });
43068
43276
  try {
43069
43277
  const reply = await options.control.requestFileControl("file.register", {
43070
43278
  version: 1,
@@ -43076,7 +43284,7 @@ function createRelayAttachmentDataLink(options) {
43076
43284
  max_wire_bytes: attachmentTransferWireBudget(permit.context.byte_size)
43077
43285
  });
43078
43286
  if (reply.code !== 0) {
43079
- recordAttachmentDiagnostic(options.diagnostic, { phase: "file.register", reason: "failed", code: reply.code, ...timing() });
43287
+ recordAttachmentDiagnostic(options.diagnostic, { phase: "file.register", reason: "failed", code: reply.code, ...timing2() });
43080
43288
  const error2 = unavailable7();
43081
43289
  error2.cause = { stage: "file.register", relayCode: reply.code };
43082
43290
  throw error2;
@@ -46641,7 +46849,7 @@ var RuntimeDesktopIngress = class {
46641
46849
  resetMethodValidity() {
46642
46850
  this.ipcMethodInvalidatedThreads.clear();
46643
46851
  }
46644
- async process(frame, ownClientId = this.ports.ipc()?.getClientId?.() ?? null, receivedIpcGeneration = this.ports.ipcConnectionGeneration(), timing, readDiagnostic) {
46852
+ async process(frame, ownClientId = this.ports.ipc()?.getClientId?.() ?? null, receivedIpcGeneration = this.ports.ipcConnectionGeneration(), timing2, readDiagnostic) {
46645
46853
  this.ports.writeDebug({
46646
46854
  component: "daemon",
46647
46855
  channel: "ipc",
@@ -46651,7 +46859,7 @@ var RuntimeDesktopIngress = class {
46651
46859
  attributes: {
46652
46860
  type: frame.type,
46653
46861
  ...typeof frame.method === "string" ? { method: frame.method } : {},
46654
- ...timing
46862
+ ...timing2
46655
46863
  }
46656
46864
  });
46657
46865
  this.ports.writeRawDebug({
@@ -46683,6 +46891,9 @@ var RuntimeDesktopIngress = class {
46683
46891
  }
46684
46892
  const status2 = objectRecord(frame.params);
46685
46893
  const methodThreadId = extractThreadIdFromIpcFrame(frame);
46894
+ if (!this.ports.stopping() && receivedIpcGeneration === this.ports.ipcConnectionGeneration() && this.ports.desktopIpcState() === "ready" && ownClientId && frame.type === "broadcast" && frame.sourceClientId && frame.sourceClientId !== ownClientId && status2.hostId === "local" && methodThreadId && (frame.method === "thread-stream-following-status-requested" || frame.method === "thread-stream-following-changed" && status2.following === true)) {
46895
+ this.ports.discoverThread(methodThreadId);
46896
+ }
46686
46897
  if (frame.method === "thread-stream-state-changed" && methodThreadId && this.ipcMethodInvalidatedThreads.has(methodThreadId) && objectRecord(status2.change).type !== "snapshot") {
46687
46898
  if (readDiagnostic) readDiagnostic.reason = "baseline_required";
46688
46899
  return;
@@ -47338,19 +47549,19 @@ var DesktopIpcClient = class {
47338
47549
  this.events.on("requestDiagnostic", listener);
47339
47550
  return () => this.events.off("requestDiagnostic", listener);
47340
47551
  }
47341
- emitRequestDiagnostic(timing, phase, outcome) {
47552
+ emitRequestDiagnostic(timing2, phase, outcome) {
47342
47553
  this.events.emit("requestDiagnostic", {
47343
- requestId: timing.requestId,
47344
- method: timing.method,
47345
- threadId: timing.threadId,
47554
+ requestId: timing2.requestId,
47555
+ method: timing2.method,
47556
+ threadId: timing2.threadId,
47346
47557
  phase,
47347
47558
  outcome,
47348
- durationMs: Math.max(0, Math.floor(performance.now() - timing.startedAt))
47559
+ durationMs: Math.max(0, Math.floor(performance.now() - timing2.startedAt))
47349
47560
  });
47350
47561
  }
47351
47562
  pruneTimedOutRequests() {
47352
- for (const [id5, timing] of this.timedOutRequests) {
47353
- if (performance.now() - timing.startedAt > 12e4) this.timedOutRequests.delete(id5);
47563
+ for (const [id5, timing2] of this.timedOutRequests) {
47564
+ if (performance.now() - timing2.startedAt > 12e4) this.timedOutRequests.delete(id5);
47354
47565
  }
47355
47566
  while (this.timedOutRequests.size > 128) this.timedOutRequests.delete(this.timedOutRequests.keys().next().value);
47356
47567
  }
@@ -47487,20 +47698,20 @@ var DesktopIpcClient = class {
47487
47698
  this.pruneTimedOutRequests();
47488
47699
  const localParams = params && typeof params === "object" ? params : {};
47489
47700
  const threadId2 = typeof localParams.threadId === "string" ? localParams.threadId : typeof localParams.conversationId === "string" ? localParams.conversationId : void 0;
47490
- const timing = { requestId, method: method2, threadId: threadId2, startedAt: performance.now() };
47701
+ const timing2 = { requestId, method: method2, threadId: threadId2, startedAt: performance.now() };
47491
47702
  const responsePromise = new Promise((resolve5, reject3) => {
47492
47703
  const timer = setTimeout(() => {
47493
47704
  this.pending.delete(requestId);
47494
- this.timedOutRequests.set(requestId, timing);
47705
+ this.timedOutRequests.set(requestId, timing2);
47495
47706
  this.pruneTimedOutRequests();
47496
- this.emitRequestDiagnostic(timing, "timeout", "unknown");
47707
+ this.emitRequestDiagnostic(timing2, "timeout", "unknown");
47497
47708
  reject3(new NiumaError("UNKNOWN_RESULT", `IPC \u8BF7\u6C42\u8D85\u65F6: ${method2}`, { method: method2 }));
47498
47709
  }, options.timeoutMs ?? this.requestTimeoutMs);
47499
- this.pending.set(requestId, { timer, resolve: resolve5, reject: reject3, timing });
47710
+ this.pending.set(requestId, { timer, resolve: resolve5, reject: reject3, timing: timing2 });
47500
47711
  });
47501
47712
  try {
47502
47713
  await this.write(frame);
47503
- this.emitRequestDiagnostic(timing, "sent", "pending");
47714
+ this.emitRequestDiagnostic(timing2, "sent", "pending");
47504
47715
  } catch (error2) {
47505
47716
  this.clearPending(requestId);
47506
47717
  throw error2 instanceof Error ? new NiumaError("IPC_NOT_INITIALIZED", error2.message) : new NiumaError("IPC_NOT_INITIALIZED", String(error2));
@@ -47599,10 +47810,10 @@ var DesktopIpcClient = class {
47599
47810
  this.pruneTimedOutRequests();
47600
47811
  const outcome = frame.resultType === "error" ? "error" : "success";
47601
47812
  if (!pending) {
47602
- const timing = this.timedOutRequests.get(requestId);
47603
- if (timing) {
47813
+ const timing2 = this.timedOutRequests.get(requestId);
47814
+ if (timing2) {
47604
47815
  this.timedOutRequests.delete(requestId);
47605
- this.emitRequestDiagnostic(timing, "late_response", outcome);
47816
+ this.emitRequestDiagnostic(timing2, "late_response", outcome);
47606
47817
  }
47607
47818
  return;
47608
47819
  }
@@ -56315,15 +56526,46 @@ function createHistoryHandlers(ports) {
56315
56526
  if (negotiated && (!handoffScope || !scope || scope.bindingId !== handoffScope.bindingId)) {
56316
56527
  throw new HttpApiError(200, 200004, "\u6B63\u6587\u4EA4\u63A5\u9700\u8981\u8BA4\u8BC1\u4F1A\u8BDD");
56317
56528
  }
56529
+ const loadStartedAt = performance.now();
56530
+ const loadAttempt = params.loadTrace === false ? null : ports.threadLoadReports().begin?.({ loadId: params.loadId, threadId: threadId2, owner: scope ? `binding:${scope.bindingId.toLowerCase()}` : "local", transport: scope?.transport ?? "local", nowMs: Date.now() });
56531
+ const loadElapsed = () => Math.max(0, Math.min(9e5, Math.round(performance.now() - loadStartedAt)));
56532
+ let loadFailurePhase = "open_received";
56533
+ const failOpen = (error2) => {
56534
+ if (!options.signal?.aborted && normalizeHttpError(error2).httpStatus === 200) loadAttempt?.finish("failed", { terminal_phase: loadFailurePhase, reason: "open_rejected" });
56535
+ };
56318
56536
  if (negotiated && typeof params.handoffTicket === "string") {
56319
- if (options.signal?.aborted || socket.destroyed || socket.writableEnded) return STREAMING_RESPONSE;
56320
- const consumer = ports.connectionHandoff().claim(
56321
- handoffScope,
56322
- params.handoffDescriptor,
56323
- params.handoffTicket,
56324
- { emit: (frame) => socket.write(`${JSON.stringify(successEnvelope(frame))}
56325
- `), onClose: () => socket.end() }
56326
- );
56537
+ if (options.signal?.aborted || socket.destroyed || socket.writableEnded) {
56538
+ loadAttempt?.close();
56539
+ return STREAMING_RESPONSE;
56540
+ }
56541
+ let consumer;
56542
+ try {
56543
+ consumer = ports.connectionHandoff().claim(
56544
+ handoffScope,
56545
+ params.handoffDescriptor,
56546
+ params.handoffTicket,
56547
+ { emit: (frame) => {
56548
+ socket.write(`${JSON.stringify(successEnvelope(frame))}
56549
+ `);
56550
+ if (frame.type === "thread_view_snapshot") {
56551
+ loadFailurePhase = "shell_sent";
56552
+ loadAttempt?.markDaemon("shell_sent", loadElapsed());
56553
+ }
56554
+ const view = frame.data;
56555
+ if (view?.availability?.state === "ready") {
56556
+ loadFailurePhase = "history_sent";
56557
+ loadAttempt?.markDaemon("history_sent", loadElapsed());
56558
+ }
56559
+ }, onClose: () => {
56560
+ loadAttempt?.close();
56561
+ socket.end();
56562
+ } }
56563
+ );
56564
+ } catch (error2) {
56565
+ failOpen(error2);
56566
+ loadAttempt?.close();
56567
+ throw error2;
56568
+ }
56327
56569
  let recoveryClosed = false;
56328
56570
  let unregisterRecovery2 = () => {
56329
56571
  };
@@ -56336,12 +56578,14 @@ function createHistoryHandlers(ports) {
56336
56578
  }) : () => {
56337
56579
  };
56338
56580
  } catch (error2) {
56581
+ loadAttempt?.close();
56339
56582
  consumer.close();
56340
56583
  throw error2;
56341
56584
  }
56342
56585
  let unregisterArchive2 = () => {
56343
56586
  };
56344
56587
  const close2 = () => {
56588
+ loadAttempt?.close();
56345
56589
  unregisterArchive2();
56346
56590
  recoveryClosed = true;
56347
56591
  unregisterRecovery2();
@@ -56368,7 +56612,6 @@ function createHistoryHandlers(ports) {
56368
56612
  `);
56369
56613
  return STREAMING_RESPONSE;
56370
56614
  }
56371
- const loadStartedAt = performance.now();
56372
56615
  let loadTrace = null;
56373
56616
  let loadLeaseId = null;
56374
56617
  let unregisterLoad = () => {
@@ -56378,7 +56621,9 @@ function createHistoryHandlers(ports) {
56378
56621
  const pendingLoad = [];
56379
56622
  const loadSeen = /* @__PURE__ */ new Set();
56380
56623
  const recordLoad = (phase, clock = "daemon", elapsed = Math.round(performance.now() - loadStartedAt), occurredAt4 = clock === "daemon" ? Date.now() : void 0) => {
56381
- if (typeof params.loadId !== "string" || elapsed < 0 || elapsed > 9e5) return;
56624
+ if (clock === "daemon" && ["open_received", "identity_ready", "lease_acquired", "shell_sent", "history_sent"].includes(phase)) loadFailurePhase = phase;
56625
+ if (params.loadTrace === false || typeof params.loadId !== "string" || elapsed < 0 || elapsed > 9e5) return;
56626
+ if (clock === "daemon") loadAttempt?.markDaemon(phase, elapsed, occurredAt4);
56382
56627
  if (!loadLeaseId) {
56383
56628
  if (pendingLoad.length < 8) pendingLoad.push({ phase, elapsed, occurredAt: occurredAt4 });
56384
56629
  return;
@@ -56467,6 +56712,7 @@ function createHistoryHandlers(ports) {
56467
56712
  readReceiptTimer = null;
56468
56713
  }
56469
56714
  recordLoad("stream_closed");
56715
+ loadAttempt?.close();
56470
56716
  unregisterLoad();
56471
56717
  unregisterRecovery();
56472
56718
  closed = true;
@@ -56503,6 +56749,7 @@ function createHistoryHandlers(ports) {
56503
56749
  const close = () => {
56504
56750
  if (physicalClosed) return;
56505
56751
  physicalClosed = true;
56752
+ loadAttempt?.close();
56506
56753
  unregisterRecovery();
56507
56754
  options.signal?.removeEventListener("abort", close);
56508
56755
  publisher?.close();
@@ -56519,7 +56766,14 @@ function createHistoryHandlers(ports) {
56519
56766
  return STREAMING_RESPONSE;
56520
56767
  }
56521
56768
  if (!ports.store().getThread(threadId2, { tailCount: 0 })) {
56522
- await ports.verifyThreadForOpen(threadId2);
56769
+ try {
56770
+ await ports.verifyThreadForOpen(threadId2);
56771
+ } catch (error2) {
56772
+ failOpen(error2);
56773
+ loadAttempt?.close();
56774
+ detachClose();
56775
+ throw error2;
56776
+ }
56523
56777
  }
56524
56778
  if (closed || socketIsClosed()) {
56525
56779
  closed = true;
@@ -56568,13 +56822,15 @@ function createHistoryHandlers(ports) {
56568
56822
  }
56569
56823
  for (const sample of pendingLoad) recordLoad(sample.phase, "daemon", sample.elapsed, sample.occurredAt);
56570
56824
  pendingLoad.length = 0;
56825
+ if (loadLeaseId && loadTrace) loadAttempt?.attachLease(loadLeaseId, loadTrace.traceId);
56571
56826
  recordLoad("lease_acquired");
56572
56827
  try {
56573
- if (loadLeaseId && loadTrace) unregisterLoad = ports.threadLoadReports().register(
56828
+ if (loadAttempt && loadLeaseId && loadTrace) unregisterLoad = ports.threadLoadReports().register(
56574
56829
  params.loadId,
56575
56830
  threadId2,
56576
56831
  scope ? `binding:${scope.bindingId.toLowerCase()}` : "local",
56577
- (sample) => recordLoad(sample.phase, "browser", sample.elapsed_ms, sample.occurred_at_ms)
56832
+ (sample) => recordLoad(sample.phase, "browser", sample.elapsed_ms, sample.occurred_at_ms),
56833
+ loadAttempt
56578
56834
  );
56579
56835
  } catch {
56580
56836
  }
@@ -56688,6 +56944,7 @@ function createHistoryHandlers(ports) {
56688
56944
  ports.requestThreadAuthorityRecovery(threadId2, true);
56689
56945
  ports.history().onHistoryLeaseStateChanged(threadId2, ports.activeThreadFollowerCoordinator()?.leaseKindsFor(threadId2) ?? []);
56690
56946
  unsubscribeHistoryWindow = historyWindow.subscribe(() => {
56947
+ if (historyWindow?.initialWindowState === "failed") loadAttempt?.finish("failed", { terminal_phase: "shell_sent", reason: "history_failed" });
56691
56948
  if (rangeClaim && historyWindow?.initialWindowState === "failed") {
56692
56949
  socket.write(`${JSON.stringify(errorEnvelope(new NiumaError("HISTORY_SNAPSHOT_EXPIRED", "\u5386\u53F2\u6062\u590D\u8303\u56F4\u4E0D\u53EF\u7528\u6216\u5DF2\u8FC7\u671F")))}
56693
56950
  `);
@@ -56762,6 +57019,7 @@ function createHistoryHandlers(ports) {
56762
57019
  `);
56763
57020
  }
56764
57021
  } catch (error2) {
57022
+ failOpen(error2);
56765
57023
  try {
56766
57024
  const sites = [
56767
57025
  ["historySourceAssertion", "Object.sourceCurrent"],
@@ -56833,20 +57091,20 @@ function text10(value) {
56833
57091
  return false;
56834
57092
  }
56835
57093
  }
56836
- function object9(value, keys3) {
57094
+ function object10(value, keys3) {
56837
57095
  if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) return fail16("invalid");
56838
57096
  const own = Reflect.ownKeys(value);
56839
57097
  if (own.length !== keys3.length || !own.every((key) => typeof key === "string" && keys3.includes(key) && "value" in Object.getOwnPropertyDescriptor(value, key))) return fail16("invalid");
56840
57098
  return value;
56841
57099
  }
56842
57100
  function parseAsyncQuestionBatch(value) {
56843
- const raw = object9(value, ["thread_id", "group_id", "revision", "idempotency_key", "question_ids", "answers"]);
57101
+ const raw = object10(value, ["thread_id", "group_id", "revision", "idempotency_key", "question_ids", "answers"]);
56844
57102
  if (!id3(raw.thread_id) || !id3(raw.group_id) || !id3(raw.idempotency_key) || !Number.isSafeInteger(raw.revision) || raw.revision < 0 || !Array.isArray(raw.question_ids) || !raw.question_ids.length || raw.question_ids.length > 256 || !Array.isArray(raw.answers) || raw.answers.length > raw.question_ids.length) return fail16("invalid");
56845
57103
  const questionIds = Array.from(raw.question_ids);
56846
57104
  if (!questionIds.every(id3) || new Set(questionIds).size !== questionIds.length) return fail16("invalid");
56847
57105
  const seen = /* @__PURE__ */ new Set();
56848
57106
  const answers = Array.from(raw.answers, (value2) => {
56849
- const answer = object9(value2, ["question_id", "title", "answer"]);
57107
+ const answer = object10(value2, ["question_id", "title", "answer"]);
56850
57108
  if (!id3(answer.question_id) || !questionIds.includes(answer.question_id) || seen.has(answer.question_id) || !text10(answer.title) || !text10(answer.answer)) return fail16("invalid");
56851
57109
  seen.add(answer.question_id);
56852
57110
  return { question_id: answer.question_id, title: answer.title, answer: answer.answer };
@@ -59635,10 +59893,10 @@ var ThreadHistoryWindowLoader = class {
59635
59893
  sourceRevision: 0
59636
59894
  };
59637
59895
  for (let attempt = 0; attempt < 2; attempt++) {
59638
- const timing = await this.timestampVersion(turn, signal);
59896
+ const timing2 = await this.timestampVersion(turn, signal);
59639
59897
  this.assertSource(epoch);
59640
59898
  signal.throwIfAborted();
59641
- const versionKey = JSON.stringify([epoch, description.contentVersion, timing]);
59899
+ const versionKey = JSON.stringify([epoch, description.contentVersion, timing2]);
59642
59900
  let cached = this.turns.get(identity6);
59643
59901
  if (cached?.current?.version === versionKey) {
59644
59902
  if (description.sourceRevision >= cached.sourceRevision && cached.nativeOrder !== description.nativeOrder) {
@@ -59655,7 +59913,7 @@ var ThreadHistoryWindowLoader = class {
59655
59913
  this.assertSource(epoch);
59656
59914
  signal.throwIfAborted();
59657
59915
  if (enriched.turnId !== turn.id) throw new RangeError("\u7A97\u53E3\u65F6\u95F4\u8865\u9F50 Turn \u8EAB\u4EFD\u4E0D\u5339\u914D");
59658
- if (await this.timestampVersion(turn, signal) !== timing) continue;
59916
+ if (await this.timestampVersion(turn, signal) !== timing2) continue;
59659
59917
  this.assertSource(epoch);
59660
59918
  signal.throwIfAborted();
59661
59919
  const freeze = (value) => {
@@ -59713,11 +59971,11 @@ var ThreadHistoryWindowLoader = class {
59713
59971
  const epoch = this.sourceEpoch;
59714
59972
  this.currentProtection.addTurnIds([turn.id]);
59715
59973
  const description = this.reader.describeTurn?.(turn) ?? { contentVersion: createHash26("sha256").update(JSON.stringify(turn)).digest("hex") };
59716
- const timing = await this.timestampVersion(turn, signal);
59974
+ const timing2 = await this.timestampVersion(turn, signal);
59717
59975
  this.assertSource(epoch);
59718
59976
  signal.throwIfAborted();
59719
59977
  const cached = this.turns.get(JSON.stringify([this.currentThreadId, turn.id]));
59720
- if (cached?.current?.version === JSON.stringify([epoch, description.contentVersion, timing])) {
59978
+ if (cached?.current?.version === JSON.stringify([epoch, description.contentVersion, timing2])) {
59721
59979
  this.holdVersion(this.currentProtection, cached.current);
59722
59980
  return cached.current.projection.displayItemCount;
59723
59981
  }
@@ -60937,17 +61195,17 @@ var AppServerThreadViewAdapter = class {
60937
61195
  lastError: null
60938
61196
  }).status;
60939
61197
  const terminalStatus = failed || aborted || status2 === "completed" ? lifecycleStatus : "completed";
60940
- const timing = context.getTurnLifecycle?.(turnId);
61198
+ const timing2 = context.getTurnLifecycle?.(turnId);
60941
61199
  return [{
60942
61200
  type: "ThreadStatusChanged",
60943
61201
  thread_id: context.threadId,
60944
61202
  status: terminalStatus,
60945
61203
  turn_id: turnId,
60946
61204
  terminal_item_state: terminalStatus === "error" ? "failed" : "completed",
60947
- ...timing ? { process_lifecycle: {
60948
- startedAt: lifecycleTime(timing.startedAt),
60949
- endedAt: lifecycleTime(timing.endedAt),
60950
- outcome: timing.outcome
61205
+ ...timing2 ? { process_lifecycle: {
61206
+ startedAt: lifecycleTime(timing2.startedAt),
61207
+ endedAt: lifecycleTime(timing2.endedAt),
61208
+ outcome: timing2.outcome
60951
61209
  } } : {},
60952
61210
  last_error: terminalStatus === "error" ? {
60953
61211
  code: 900001,
@@ -61202,10 +61460,10 @@ var ThreadHistoryWindowProjection = class {
61202
61460
  const enriched = this.options.enrichTimeline ? await this.options.enrichTimeline(sources.map((source2) => source2.timeline), startedAt) : this.timestamps.enrichTimelineFromReplay(sources.map((source2) => source2.timeline), startedAt, replay);
61203
61461
  const timingById = new Map(sources.map((source2, index) => [source2.id, enriched[index]]));
61204
61462
  const items = projected.items.map((item) => {
61205
- const timing = timingById.get(item.id);
61463
+ const timing2 = timingById.get(item.id);
61206
61464
  const fact = item.process_fact;
61207
- if (!timing || !fact) return item;
61208
- const merged = mergeThreadHistoryTimelineLifecycle(timing, {
61465
+ if (!timing2 || !fact) return item;
61466
+ const merged = mergeThreadHistoryTimelineLifecycle(timing2, {
61209
61467
  ...fact.startedAt !== null ? { turnStartedAt: fact.startedAt } : {},
61210
61468
  ...fact.endedAt !== null ? { turnEndedAt: fact.endedAt } : {},
61211
61469
  ...fact.durationMs != null ? { turnDurationMs: fact.durationMs } : {},
@@ -62054,7 +62312,7 @@ function readStateDiagnostic(before, after, candidate = after.confirmed) {
62054
62312
  sameCycle: candidate ? candidate.cycle === after.cycle : null
62055
62313
  };
62056
62314
  }
62057
- function object10(value) {
62315
+ function object11(value) {
62058
62316
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
62059
62317
  }
62060
62318
  function field(value, present, count) {
@@ -62064,15 +62322,15 @@ function field(value, present, count) {
62064
62322
  }
62065
62323
  function unreadFrameDiagnostic(frame) {
62066
62324
  if (frame.method !== "thread-stream-state-changed") return null;
62067
- const params = object10(frame.params), change = object10(params.change);
62325
+ const params = object11(frame.params), change = object11(params.change);
62068
62326
  const kind = change.type === "snapshot" ? "snapshot" : change.type === "patches" ? "patch" : "invalid";
62069
- const values = kind === "snapshot" ? object10(change.conversationState) : {};
62327
+ const values = kind === "snapshot" ? object11(change.conversationState) : {};
62070
62328
  let unread = field(values.hasUnreadTurn, Object.hasOwn(values, "hasUnreadTurn"), false);
62071
62329
  let count = field(values.unreadMessageCount, Object.hasOwn(values, "unreadMessageCount"), true);
62072
62330
  let countValue = values.unreadMessageCount;
62073
62331
  if (kind === "patch") {
62074
62332
  for (const value of Array.isArray(change.patches) ? change.patches : []) {
62075
- const patch = object10(value), path54 = Array.isArray(patch.path) ? patch.path : [];
62333
+ const patch = object11(value), path54 = Array.isArray(patch.path) ? patch.path : [];
62076
62334
  const name2 = path54.length === 1 ? path54[0] : path54.length === 2 && path54[0] === "conversationState" ? path54[1] : null;
62077
62335
  if (name2 !== "hasUnreadTurn" && name2 !== "unreadMessageCount") continue;
62078
62336
  const state = patch.op === "remove" ? "removed" : ["add", "replace"].includes(String(patch.op)) ? field(patch.value, Object.hasOwn(patch, "value"), name2 === "unreadMessageCount") : "invalid";
@@ -62114,10 +62372,10 @@ var RuntimeIngressDispatch = class {
62114
62372
  }
62115
62373
  ports;
62116
62374
  /** 单帧入站与最终结果成对记录;原始字段与业务结果分开,拒绝帧同样留证。 */
62117
- async handleIpcFrame(frame, ownClientId = this.ports.desktopSession().ipcValue?.getClientId?.() ?? null, receivedIpcGeneration = this.ports.desktopSession().ipcConnectionGenerationValue, timing) {
62375
+ async handleIpcFrame(frame, ownClientId = this.ports.desktopSession().ipcValue?.getClientId?.() ?? null, receivedIpcGeneration = this.ports.desktopSession().ipcConnectionGenerationValue, timing2) {
62118
62376
  const attributes = this.ports.options().debugLogger ? unreadFrameDiagnostic(frame) : null;
62119
62377
  const threadId2 = attributes ? extractThreadIdFromIpcFrame(frame) : null;
62120
- if (!attributes || !threadId2) return this.ports.desktopIngress().process(frame, ownClientId, receivedIpcGeneration, timing);
62378
+ if (!attributes || !threadId2) return this.ports.desktopIngress().process(frame, ownClientId, receivedIpcGeneration, timing2);
62121
62379
  const before = this.ports.catalog().storeValue.getThread(threadId2, { tailCount: 0 })?.unread;
62122
62380
  const diagnostic = { reason: "processed" };
62123
62381
  const metadata = { ...attributes, sequence: this.ports.diagnostics().threadReadDiagnosticSequenceValue++, generation: receivedIpcGeneration, currentGeneration: this.ports.desktopSession().ipcConnectionGenerationValue };
@@ -62130,7 +62388,7 @@ var RuntimeIngressDispatch = class {
62130
62388
  }
62131
62389
  });
62132
62390
  try {
62133
- await this.ports.desktopIngress().process(frame, ownClientId, receivedIpcGeneration, timing, diagnostic);
62391
+ await this.ports.desktopIngress().process(frame, ownClientId, receivedIpcGeneration, timing2, diagnostic);
62134
62392
  } catch (error2) {
62135
62393
  diagnostic.reason = "processing_failed";
62136
62394
  throw error2;
@@ -63858,11 +64116,11 @@ var RuntimeProjectionContext = class {
63858
64116
  } : null),
63859
64117
  enrichTimelineItemTiming: (item) => this.ports.timelinePublication().enrichTimelineItemTiming(item, timelineItems),
63860
64118
  getTurnLifecycle: (turnId) => {
63861
- const timing = this.ports.timelinePublication().turnTiming(threadId2, turnId);
63862
- return timing ? {
63863
- startedAt: timing.startedAt,
63864
- endedAt: timing.endedAt ?? null,
63865
- outcome: timing.outcome ?? null
64119
+ const timing2 = this.ports.timelinePublication().turnTiming(threadId2, turnId);
64120
+ return timing2 ? {
64121
+ startedAt: timing2.startedAt,
64122
+ endedAt: timing2.endedAt ?? null,
64123
+ outcome: timing2.outcome ?? null
63866
64124
  } : null;
63867
64125
  },
63868
64126
  resolveKnownLocalImage: (sourcePath, itemId, partIndex) => this.ports.resolveThreadViewImage(threadId2, sourcePath, () => this.ports.isCurrentAppServerAsyncFence(threadId2, fence), itemId, partIndex)
@@ -64391,7 +64649,7 @@ var DesktopThreadSettingsTracker = class {
64391
64649
 
64392
64650
  // apps/desktop/src/daemon/active-thread-follower-coordinator.ts
64393
64651
  init_session_history_file();
64394
- var ACTIVE_THREAD_SCAN_INTERVAL_MS = 1e4;
64652
+ var ACTIVE_THREAD_SCAN_INTERVAL_MS = 6e4;
64395
64653
  var ACTIVE_THREAD_READ_CONCURRENCY = 4;
64396
64654
  var ACTIVE_FOLLOWER_SNAPSHOT_TIMEOUT_MS = 12e3;
64397
64655
  var ACTIVE_FOLLOWER_RETRY_DELAYS_MS = [1e3, 2e3, 5e3, 1e4, 3e4];
@@ -71373,7 +71631,7 @@ var RuntimeThreadStateCoordination = class {
71373
71631
  return token3;
71374
71632
  }
71375
71633
  /** 仅供已核验的同轮终态使用;同时收敛列表与详情,不伪造新的完成通知。 */
71376
- applyRecoveredTerminal(threadId2, turnId, outcome, occurredAt4, message, timing) {
71634
+ applyRecoveredTerminal(threadId2, turnId, outcome, occurredAt4, message, timing2) {
71377
71635
  const previous = this.ports.catalog().storeValue.getThread(threadId2, { tailCount: 0 });
71378
71636
  const mutation = this.ports.catalog().storeValue.applyActivitySignal({
71379
71637
  type: "snapshot",
@@ -71393,7 +71651,7 @@ var RuntimeThreadStateCoordination = class {
71393
71651
  turn_id: turnId,
71394
71652
  status: outcome === "aborted" ? "idle" : outcome === "failed" ? "error" : "completed",
71395
71653
  terminal_item_state: outcome === "failed" ? "failed" : "completed",
71396
- process_lifecycle: { startedAt: timing?.startedAt ?? null, endedAt: timing?.endedAt ?? null, outcome },
71654
+ process_lifecycle: { startedAt: timing2?.startedAt ?? null, endedAt: timing2?.endedAt ?? null, outcome },
71397
71655
  last_error: outcome === "failed" && message ? { code: 900001, message, occurred_at: occurredAt4, turn_id: turnId } : null
71398
71656
  },
71399
71657
  { type: "ThreadActivityChanged", thread_id: threadId2, turn_id: turnId, activity: null },
@@ -71632,15 +71890,15 @@ var RuntimeTimelinePublication = class {
71632
71890
  if (!item.turnId) {
71633
71891
  return item;
71634
71892
  }
71635
- const timing = this.liveTurnTimings.get(this.turnTimingKey(item.threadId, item.turnId));
71636
- if (!timing) {
71893
+ const timing2 = this.liveTurnTimings.get(this.turnTimingKey(item.threadId, item.turnId));
71894
+ if (!timing2) {
71637
71895
  return item;
71638
71896
  }
71639
71897
  return {
71640
71898
  ...item,
71641
- ...item.turnStartedAt === void 0 ? { turnStartedAt: timing.startedAt } : {},
71642
- ...item.turnEndedAt === void 0 && timing.endedAt !== void 0 ? { turnEndedAt: timing.endedAt } : {},
71643
- ...item.turnOutcome === void 0 && timing.outcome !== void 0 ? { turnOutcome: timing.outcome } : {}
71899
+ ...item.turnStartedAt === void 0 ? { turnStartedAt: timing2.startedAt } : {},
71900
+ ...item.turnEndedAt === void 0 && timing2.endedAt !== void 0 ? { turnEndedAt: timing2.endedAt } : {},
71901
+ ...item.turnOutcome === void 0 && timing2.outcome !== void 0 ? { turnOutcome: timing2.outcome } : {}
71644
71902
  };
71645
71903
  }
71646
71904
  enrichTimelineItemTiming(item, historyItems = []) {
@@ -71660,9 +71918,9 @@ var RuntimeTimelinePublication = class {
71660
71918
  turnTimingKey(threadId2, turnId) {
71661
71919
  return `${threadId2}:${turnId}`;
71662
71920
  }
71663
- setLiveTurnTiming(key, timing) {
71921
+ setLiveTurnTiming(key, timing2) {
71664
71922
  this.liveTurnTimings.delete(key);
71665
- this.liveTurnTimings.set(key, timing);
71923
+ this.liveTurnTimings.set(key, timing2);
71666
71924
  while (this.liveTurnTimings.size > LIVE_TURN_TIMING_LIMIT) {
71667
71925
  const oldestKey = this.liveTurnTimings.keys().next().value;
71668
71926
  if (typeof oldestKey !== "string") break;
@@ -79785,8 +80043,7 @@ var RuntimeNativeAdmission = class {
79785
80043
  else if (kind === "ordinary") this.hidden.delete(thread4.id);
79786
80044
  const old = this.entries.get(thread4.id);
79787
80045
  this.failures.delete(thread4.id);
79788
- if (old && old.kind === kind) return old.token;
79789
- const token3 = { threadId: thread4.id, generation: ++this.sequence, executionGeneration };
80046
+ const token3 = old && old.kind === kind ? old.token : { threadId: thread4.id, generation: ++this.sequence, executionGeneration };
79790
80047
  this.entries.set(thread4.id, { token: token3, kind });
79791
80048
  try {
79792
80049
  if (kind !== previous) this.ports.onChange?.(thread4.id, previous, kind);
@@ -79796,7 +80053,7 @@ var RuntimeNativeAdmission = class {
79796
80053
  }
79797
80054
  const pending = this.jobs.get(thread4.id);
79798
80055
  if (pending) {
79799
- this.finishWait(pending, kind);
80056
+ this.finishWait(pending, kind, thread4, token3);
79800
80057
  if (!pending.started) {
79801
80058
  this.removeQueued(pending);
79802
80059
  this.removeJob(pending);
@@ -79804,13 +80061,20 @@ var RuntimeNativeAdmission = class {
79804
80061
  }
79805
80062
  return token3;
79806
80063
  }
79807
- ensure(id5, executionGeneration) {
79808
- if (executionGeneration !== this.execution) return Promise.resolve("unknown");
80064
+ async ensure(id5, executionGeneration) {
80065
+ return (await this.query(id5, executionGeneration, false)).kind;
80066
+ }
80067
+ /** 目录缺项时请求摘要,与未知准入共用单飞、退避和物理槽。 */
80068
+ ensureSummary(id5, executionGeneration) {
80069
+ return this.query(id5, executionGeneration, true);
80070
+ }
80071
+ query(id5, executionGeneration, needSummary) {
80072
+ if (executionGeneration !== this.execution) return Promise.resolve({ kind: "unknown" });
79809
80073
  const known = this.kind(id5);
79810
- if (known !== "unknown") return Promise.resolve(known);
80074
+ if (known !== "unknown" && (!needSummary || known === "subagent")) return Promise.resolve({ kind: known });
79811
80075
  const pending = this.jobs.get(id5);
79812
- if (pending) return pending.execution === this.execution ? pending.promise : Promise.resolve("unknown");
79813
- if ((this.failures.get(id5) ?? 0) > this.now() || this.queue.length >= 64) return Promise.resolve("unknown");
80076
+ if (pending) return pending.execution === this.execution ? pending.promise : Promise.resolve({ kind: "unknown" });
80077
+ if ((this.failures.get(id5) ?? 0) > this.now() || this.queue.length >= 64) return Promise.resolve({ kind: "unknown" });
79814
80078
  let resolve5;
79815
80079
  const promise = new Promise((done) => {
79816
80080
  resolve5 = done;
@@ -79867,11 +80131,11 @@ var RuntimeNativeAdmission = class {
79867
80131
  removeJob(job) {
79868
80132
  if (this.jobs.get(job.id) === job) this.jobs.delete(job.id);
79869
80133
  }
79870
- finishWait(job, kind) {
80134
+ finishWait(job, kind, summary, token3) {
79871
80135
  if (job.settled) return;
79872
80136
  job.settled = true;
79873
80137
  job.cancel();
79874
- job.resolve(kind);
80138
+ job.resolve({ kind, ...summary ? { summary, token: token3 } : {} });
79875
80139
  }
79876
80140
  fail(job) {
79877
80141
  if (job.execution === this.execution) {
@@ -79905,7 +80169,7 @@ var RuntimeNativeAdmission = class {
79905
80169
  this.observe(result.thread, job.execution);
79906
80170
  const kind = this.kind(job.id);
79907
80171
  if (kind === "unknown") this.fail(job);
79908
- this.finishWait(job, kind);
80172
+ this.finishWait(job, kind, result.thread, this.token(job.id));
79909
80173
  }).catch(() => {
79910
80174
  if (!job.settled) {
79911
80175
  this.fail(job);
@@ -80268,6 +80532,7 @@ var DEBUG_EVENTS = [
80268
80532
  "ipc_recovery",
80269
80533
  "ipc_forwarding_flow",
80270
80534
  "notification_delivery",
80535
+ "ipc_thread_discovery",
80271
80536
  "startup_timing",
80272
80537
  "thread_settings_cache",
80273
80538
  "thread_list_cache",
@@ -80501,6 +80766,7 @@ var SAFE_ATTRIBUTE_KEYS = {
80501
80766
  "inFlight"
80502
80767
  ],
80503
80768
  // 启动性能仅保留固定阶段耗时和汇总计数,禁止记录路径、会话或异常原文。
80769
+ ipc_thread_discovery: ["durationMs"],
80504
80770
  startup_timing: [
80505
80771
  "durationMs",
80506
80772
  "threadCount",
@@ -81050,6 +81316,7 @@ var SAFE_PHASES = {
81050
81316
  "dropped_busy",
81051
81317
  "deadline_expired"
81052
81318
  ],
81319
+ ipc_thread_discovery: ["received", "ignored", "deduplicated", "capacity", "read_completed", "rejected", "stale", "published", "failed"],
81053
81320
  startup_timing: [
81054
81321
  "app_server_initialized",
81055
81322
  "thread_baseline_scanned",
@@ -81743,19 +82010,19 @@ function withoutPersistentImageBytes(value, key = "", seen = /* @__PURE__ */ new
81743
82010
  if (seen.has(value)) return "[circular]";
81744
82011
  seen.add(value);
81745
82012
  try {
81746
- const object11 = value;
81747
- if (object11.type === "Buffer" && Array.isArray(object11.data)) {
81748
- return { type: "binary_omitted", byteSize: object11.data.length };
82013
+ const object12 = value;
82014
+ if (object12.type === "Buffer" && Array.isArray(object12.data)) {
82015
+ return { type: "binary_omitted", byteSize: object12.data.length };
81749
82016
  }
81750
- if (object11.event === "app_server_stdout") {
81751
- const payload = object11.rawPayload;
81752
- const metadata = Object.fromEntries(Object.entries(object11).filter(([name2]) => name2 !== "rawPayload").map(([name2, entry]) => [name2, withoutPersistentImageBytes(entry, name2, seen)]));
82017
+ if (object12.event === "app_server_stdout") {
82018
+ const payload = object12.rawPayload;
82019
+ const metadata = Object.fromEntries(Object.entries(object12).filter(([name2]) => name2 !== "rawPayload").map(([name2, entry]) => [name2, withoutPersistentImageBytes(entry, name2, seen)]));
81753
82020
  return { ...metadata, rawPayload: { type: "stdout_fragment", bytes: typeof payload?.bytes === "number" ? payload.bytes : typeof payload?.fragment === "string" ? Buffer.byteLength(payload.fragment) : 0 } };
81754
82021
  }
81755
82022
  if (Array.isArray(value)) return value.map((entry) => withoutPersistentImageBytes(entry, key, seen));
81756
- return Object.fromEntries(Object.entries(object11).map(([name2, entry]) => [
82023
+ return Object.fromEntries(Object.entries(object12).map(([name2, entry]) => [
81757
82024
  name2,
81758
- withoutPersistentImageBytes(entry, object11.type === "image" && name2 === "data" ? "base64" : name2, seen)
82025
+ withoutPersistentImageBytes(entry, object12.type === "image" && name2 === "data" ? "base64" : name2, seen)
81759
82026
  ]));
81760
82027
  } finally {
81761
82028
  seen.delete(value);
@@ -82849,6 +83116,7 @@ var NiumaDaemonRuntime = class {
82849
83116
  }
82850
83117
  desktopTrackers = new RuntimeDesktopIngressTrackers();
82851
83118
  desktopIngress = new RuntimeDesktopIngress({
83119
+ discoverThread: (id5) => this.ipcThreadDiscovery.discover(id5),
82852
83120
  nativeAdmission: () => this.nativeAdmission,
82853
83121
  ensureNativeAdmission: (id5) => this.ensureNativeAdmission(id5),
82854
83122
  requestReadFileRefresh: () => this.followerCoordination.desktopReadStateFileValue?.requestRefresh(),
@@ -83384,6 +83652,33 @@ var NiumaDaemonRuntime = class {
83384
83652
  }
83385
83653
  }
83386
83654
  });
83655
+ ipcThreadDiscovery = new RuntimeIpcThreadDiscovery({
83656
+ eligible: (id5) => !this.lifecycle.stoppingValue && this.desktopSession.desktopIpcStateValue === "ready" && this.executionHost.appServerStateValue === "ready" && !this.archiveState.isConfirmed(id5) && !this.catalog.hasActiveTombstone(id5) && this.nativeAdmission.isVisible(id5) && !this.catalog.storeValue.getPublicThreadSummary(id5),
83657
+ capture: () => {
83658
+ const runtime = this.lifecycle.runtimeGenerationValue, ipc = this.desktopSession.ipcConnectionGenerationValue;
83659
+ const client = this.executionHost.appServerValue, generation = this.appServerLoads.connectionGeneration;
83660
+ return () => !this.lifecycle.stoppingValue && runtime === this.lifecycle.runtimeGenerationValue && ipc === this.desktopSession.ipcConnectionGenerationValue && client === this.executionHost.appServerValue && generation === this.appServerLoads.connectionGeneration;
83661
+ },
83662
+ read: (id5) => this.nativeAdmission.ensureSummary(id5, this.appServerLoads.connectionGeneration),
83663
+ current: (token3) => this.nativeAdmission.isCurrent(token3),
83664
+ enqueue: (run) => this.eventSequencer.enqueue(async () => {
83665
+ run();
83666
+ }),
83667
+ commit: (thread4) => {
83668
+ this.directoryAuthority.begin(false);
83669
+ this.catalog.upsertAuthoritativeThreadSummary(thread4);
83670
+ this.emitEvent(this.publication.threadUpdatedEvent(thread4.id, "app_server", nowSeconds()));
83671
+ this.activityPublication.publishGlobalStatusIfChanged();
83672
+ },
83673
+ diagnostic: (threadId2, phase, elapsedMs) => this.diagnostics.writeDebug({
83674
+ component: "daemon",
83675
+ channel: "ipc",
83676
+ event: "ipc_thread_discovery",
83677
+ threadId: threadId2,
83678
+ phase,
83679
+ attributes: { durationMs: elapsedMs }
83680
+ })
83681
+ });
83387
83682
  async ensureNativeAdmission(id5) {
83388
83683
  return !this.lifecycle.stoppingValue && !this.archiveState.isConfirmed(id5) && await this.nativeAdmission.ensure(id5, this.appServerLoads.connectionGeneration) === "ordinary";
83389
83684
  }
@@ -84070,7 +84365,7 @@ var NiumaDaemonRuntime = class {
84070
84365
  isBlocked: isStartBlockedByLifecycle
84071
84366
  });
84072
84367
  // 所有 runtime 与 coordinator 共用同一审计实例,确保 thread-scope 事件可关联当前 trace。
84073
- threadLoadReports = new ThreadLoadReports();
84368
+ threadLoadReports = new ThreadLoadReports(Date.now, () => this.followerOpenTraceAudit);
84074
84369
  followerOpenTraceAudit;
84075
84370
  // 异步选择只等待目录;端口在实际调用时读取,不能提前冻结Owner或缓存。
84076
84371
  settingsSelection = new RuntimeSettingsSelection({
@@ -84408,6 +84703,34 @@ var NiumaDaemonRuntime = class {
84408
84703
  });
84409
84704
  };
84410
84705
 
84706
+ // apps/desktop/src/daemon/thread-load-record.ts
84707
+ import { z as z17 } from "zod";
84708
+ var LoadRecordSchema = z17.object({
84709
+ schema_version: z17.literal(3),
84710
+ record_type: z17.enum(["load_start", "load_sample", "load_terminal", "load_close"]),
84711
+ load_id: z17.string().uuid(),
84712
+ thread_id: z17.string().min(1).max(256),
84713
+ sequence: z17.number().int().nonnegative(),
84714
+ occurred_at_ms: z17.number().int().nonnegative().max(864e13),
84715
+ clock: z17.enum(["client", "daemon"]),
84716
+ phase: z17.enum([...CLIENT_LOAD_PHASES, "open_received", "identity_ready", "lease_acquired", "shell_sent", "history_sent", "stream_closed", "failed", "cancelled", "superseded", "closed", "expired"]),
84717
+ elapsed_ms: z17.number().int().min(0).max(9e5),
84718
+ event_at_ms: z17.number().int().min(1).max(864e13).optional(),
84719
+ source_transport: z17.enum(["local", "direct", "relay"]).optional(),
84720
+ client_class: z17.enum(["web", "ios", "other"]).optional(),
84721
+ title: z17.string().max(256).optional(),
84722
+ lease_id: z17.string().regex(/^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u).optional(),
84723
+ trace_id: z17.string().regex(/^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u).optional(),
84724
+ terminal_phase: z17.enum(["request_started", "snapshot_received", "history_ready", "open_received", "identity_ready", "lease_acquired", "shell_sent", "history_sent"]).optional(),
84725
+ reason: z17.enum(["open_rejected", "history_failed", "left_view", "user_cancelled", "stream_replaced"]).optional()
84726
+ }).strict().superRefine((v, ctx) => {
84727
+ const fail18 = () => ctx.addIssue({ code: "custom", message: "invalid_load_record" });
84728
+ if (v.record_type === "load_start" && (v.phase !== "open_received" || v.clock !== "daemon" || !v.source_transport)) fail18();
84729
+ if (v.record_type === "load_terminal" && !["failed", "cancelled", "superseded", "expired"].includes(v.phase)) fail18();
84730
+ if (v.record_type === "load_close" && (v.phase !== "closed" || v.clock !== "daemon")) fail18();
84731
+ if (v.record_type === "load_sample" && (v.clock === "client" ? !CLIENT_LOAD_PHASES.includes(v.phase) : !["open_received", "identity_ready", "lease_acquired", "shell_sent", "history_sent", "stream_closed"].includes(v.phase))) fail18();
84732
+ });
84733
+
84411
84734
  // apps/desktop/src/daemon/follower-open-trace.ts
84412
84735
  import { randomUUID as randomUUID52 } from "node:crypto";
84413
84736
  import path51 from "node:path";
@@ -84416,6 +84739,7 @@ var MAX_FILE_BYTES2 = 5 * 1024 * 1024;
84416
84739
  var MAX_TOTAL_BYTES2 = 20 * 1024 * 1024;
84417
84740
  var MAX_LINE_BYTES2 = 64 * 1024;
84418
84741
  var MAX_DIRECTORY_ENTRIES3 = 256;
84742
+ var MAX_PROCESSED_LINES2 = 1e4;
84419
84743
  var SUBSCRIPTION_TRACE_SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;
84420
84744
  var FOLLOWER_OPEN_TRACE_TITLE_MAX_LENGTH = 256;
84421
84745
  var TRANSPORTS = /* @__PURE__ */ new Set(["local", "direct", "relay"]);
@@ -84577,6 +84901,9 @@ function isOptionalTraceTitle(value) {
84577
84901
  function hasOnlyKeys2(value, allowed) {
84578
84902
  return value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).every((key) => allowed.includes(key));
84579
84903
  }
84904
+ function isRecent(value, nowMs) {
84905
+ return value <= nowMs && nowMs - value <= DAEMON_LOG_RETENTION_MS;
84906
+ }
84580
84907
  function dateFileName2(timestampMs) {
84581
84908
  return `${new Date(timestampMs).toISOString().slice(0, 10)}.ndjson`;
84582
84909
  }
@@ -84683,6 +85010,10 @@ function parseRecord2(line) {
84683
85010
  }
84684
85011
  if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
84685
85012
  const record25 = value;
85013
+ if (record25.schema_version === 3) {
85014
+ const parsed = LoadRecordSchema.safeParse(record25);
85015
+ return parsed.success ? parsed.data : null;
85016
+ }
84686
85017
  if (record25.schema_version === 1) return parseV1Record(record25);
84687
85018
  if (record25.schema_version === 2) return parseV2Record(record25);
84688
85019
  return null;
@@ -84718,6 +85049,46 @@ function readPrivateTraceFile(file, remainingBytes, storage) {
84718
85049
  return { bytesRead: error2 instanceof PrivateLogFileError ? error2.bytesRead : 0, incomplete: true };
84719
85050
  }
84720
85051
  }
85052
+ function readRecords(runtimeDir, nowMs, storage) {
85053
+ if (!Number.isFinite(nowMs)) return { records: [], incomplete: true };
85054
+ const candidates = listPrivateTraceFiles(runtimeDir, storage);
85055
+ const records = [];
85056
+ let incomplete = candidates.incomplete;
85057
+ let totalBytes = 0;
85058
+ let processedLines = 0;
85059
+ for (const filePath of candidates.files) {
85060
+ if (totalBytes >= MAX_TOTAL_BYTES2) {
85061
+ incomplete = true;
85062
+ break;
85063
+ }
85064
+ const file = readPrivateTraceFile(filePath, MAX_TOTAL_BYTES2 - totalBytes, storage);
85065
+ totalBytes += file.bytesRead;
85066
+ if (file.incomplete) incomplete = true;
85067
+ if (file.content === void 0) continue;
85068
+ let end = file.content.endsWith("\n") ? file.content.length - 1 : file.content.length;
85069
+ while (end > 0 && processedLines < MAX_PROCESSED_LINES2) {
85070
+ const newline = file.content.lastIndexOf("\n", end - 1);
85071
+ const start = newline + 1;
85072
+ const length = end - start;
85073
+ processedLines += 1;
85074
+ if (length > 0 && length <= MAX_LINE_BYTES2) {
85075
+ const line = file.content.slice(start, end);
85076
+ if (Buffer.byteLength(line, "utf8") <= MAX_LINE_BYTES2) {
85077
+ const record25 = parseRecord2(line);
85078
+ if (record25 && timestampOf(record25) <= nowMs) records.push(record25);
85079
+ else incomplete = true;
85080
+ } else incomplete = true;
85081
+ } else if (length > 0) incomplete = true;
85082
+ end = Math.max(0, newline);
85083
+ }
85084
+ if (totalBytes >= MAX_TOTAL_BYTES2 || processedLines >= MAX_PROCESSED_LINES2) {
85085
+ if (end > 0 || filePath !== candidates.files.at(-1)) incomplete = true;
85086
+ break;
85087
+ }
85088
+ }
85089
+ records.reverse();
85090
+ return { records, incomplete: incomplete || hasIncompleteSubscriptionHistory(records, nowMs) };
85091
+ }
84721
85092
  function readPersistedSequenceFloor(runtimeDir, storage) {
84722
85093
  let maximum = 0;
84723
85094
  let totalBytes = 0;
@@ -84736,6 +85107,56 @@ function readPersistedSequenceFloor(runtimeDir, storage) {
84736
85107
  }
84737
85108
  return maximum;
84738
85109
  }
85110
+ function subscriptionIdentity(record25) {
85111
+ return `${record25.trace_id}\0${record25.thread_id}\0${record25.lease_epoch}`;
85112
+ }
85113
+ function occursAfterStart(record25, trace) {
85114
+ return record25.occurred_at_ms > trace.opened_at_ms || record25.occurred_at_ms === trace.opened_at_ms && record25.sequence > trace.sequence;
85115
+ }
85116
+ function occursBeforeClose(record25, close) {
85117
+ return record25.occurred_at_ms < close.occurred_at_ms || record25.occurred_at_ms === close.occurred_at_ms && record25.sequence < close.sequence;
85118
+ }
85119
+ function hasIncompleteSubscriptionHistory(records, nowMs) {
85120
+ const starts = /* @__PURE__ */ new Map();
85121
+ const eventsByIdentity = /* @__PURE__ */ new Map();
85122
+ const closesByIdentity = /* @__PURE__ */ new Map();
85123
+ for (const record25 of records) {
85124
+ if (record25.record_type === "subscription_start") {
85125
+ const identity6 = subscriptionIdentity(record25);
85126
+ if (starts.has(identity6)) return true;
85127
+ starts.set(identity6, record25);
85128
+ continue;
85129
+ }
85130
+ if (record25.record_type === "lease_event" || record25.record_type === "subscription_event") {
85131
+ const identity6 = subscriptionIdentity(record25);
85132
+ const events = eventsByIdentity.get(identity6) ?? [];
85133
+ events.push(record25);
85134
+ eventsByIdentity.set(identity6, events);
85135
+ continue;
85136
+ }
85137
+ if (record25.record_type === "subscription_close") {
85138
+ const identity6 = subscriptionIdentity(record25);
85139
+ const closes = closesByIdentity.get(identity6) ?? [];
85140
+ closes.push(record25);
85141
+ closesByIdentity.set(identity6, closes);
85142
+ }
85143
+ }
85144
+ for (const [identity6, events] of eventsByIdentity) {
85145
+ const start = starts.get(identity6);
85146
+ if (!start || events.some((event) => !occursAfterStart(event, start))) return true;
85147
+ }
85148
+ for (const start of starts.values()) {
85149
+ const identity6 = subscriptionIdentity(start);
85150
+ const closes = closesByIdentity.get(identity6) ?? [];
85151
+ if (closes.some((close2) => !occursAfterStart(close2, start))) return true;
85152
+ if (closes.length > 1) return true;
85153
+ const close = closes[0];
85154
+ if (close && (eventsByIdentity.get(identity6) ?? []).some((event) => !occursBeforeClose(event, close))) return true;
85155
+ if (!close && !isRecent(start.opened_at_ms, nowMs)) return true;
85156
+ }
85157
+ if ([...closesByIdentity.keys()].some((identity6) => !starts.has(identity6))) return true;
85158
+ return false;
85159
+ }
84739
85160
  var FollowerOpenTraceAuditWriter = class {
84740
85161
  constructor(runtimeDir, nowMs, files) {
84741
85162
  this.runtimeDir = runtimeDir;
@@ -84749,6 +85170,30 @@ var FollowerOpenTraceAuditWriter = class {
84749
85170
  nowMs;
84750
85171
  files;
84751
85172
  sequence;
85173
+ loadEpoch = 0;
85174
+ loadIds;
85175
+ reserveLoadId(id5) {
85176
+ if (!this.loadIds) {
85177
+ try {
85178
+ this.ensureTraceDirectory(path51.join(this.runtimeDir, TRACE_DIRECTORY_NAME));
85179
+ } catch {
85180
+ return false;
85181
+ }
85182
+ const read = readRecords(this.runtimeDir, this.nowMs(), this.files);
85183
+ if (read.incomplete) return false;
85184
+ this.loadIds = new Set(read.records.filter((r) => r.schema_version === 3).map((r) => r.load_id));
85185
+ }
85186
+ if (this.loadIds.size >= 1e4 || this.loadIds.has(id5)) return false;
85187
+ this.loadIds.add(id5);
85188
+ return true;
85189
+ }
85190
+ loadGeneration() {
85191
+ return this.loadEpoch;
85192
+ }
85193
+ recordLoad(input) {
85194
+ const parsed = LoadRecordSchema.safeParse({ ...input, schema_version: 3, sequence: this.nextSequence() });
85195
+ if (parsed.success) this.write(parsed.data);
85196
+ }
84752
85197
  traceDirectoryInitialized = false;
84753
85198
  /** 只缓存当前追加文件;日期切换或清空后重新发现已有分片。 */
84754
85199
  appendFilePath;
@@ -84882,6 +85327,7 @@ var FollowerOpenTraceAuditWriter = class {
84882
85327
  for (const entry of matching) this.files.remove(entry.file);
84883
85328
  }
84884
85329
  this.appendFilePath = void 0;
85330
+ this.loadEpoch += 1;
84885
85331
  this.activeTraces.clear();
84886
85332
  this.activeSubscriptionTraces.clear();
84887
85333
  this.activeSubscriptionTraceHandles.clear();
@@ -85121,16 +85567,16 @@ import { dirname as dirname5, join as join3 } from "node:path";
85121
85567
 
85122
85568
  // apps/desktop/src/daemon/async-input-notification-store.ts
85123
85569
  import { createHash as createHash41 } from "node:crypto";
85124
- import { z as z17 } from "zod";
85570
+ import { z as z18 } from "zod";
85125
85571
  var ASYNC_INPUT_NOTIFICATION_MAX_BYTES = 2 * 1024 * 1024;
85126
- var identityText = z17.string().min(1).max(8e3);
85127
- var entrySchema2 = z17.object({
85128
- key: z17.string().regex(/^[a-f0-9]{64}$/),
85572
+ var identityText = z18.string().min(1).max(8e3);
85573
+ var entrySchema2 = z18.object({
85574
+ key: z18.string().regex(/^[a-f0-9]{64}$/),
85129
85575
  threadId: identityText,
85130
85576
  turnId: identityText,
85131
- stage: z17.enum(["admitted", "sent"])
85577
+ stage: z18.enum(["admitted", "sent"])
85132
85578
  }).strict();
85133
- var documentSchema2 = z17.object({ version: z17.literal(1), entries: z17.array(entrySchema2).max(4096) }).strict();
85579
+ var documentSchema2 = z18.object({ version: z18.literal(1), entries: z18.array(entrySchema2).max(4096) }).strict();
85134
85580
  var AsyncInputNotificationStore = class _AsyncInputNotificationStore {
85135
85581
  constructor(journal, document) {
85136
85582
  this.journal = journal;