oasis_test_v2 2.2.105 → 2.2.106

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.
Files changed (2) hide show
  1. package/dist/index.js +1673 -369
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -388,9 +388,14 @@ var init_org_model = __esm({
388
388
  });
389
389
 
390
390
  // ../contract/src/company.ts
391
+ function profileNameLength(value2) {
392
+ return Array.from(value2.trim()).length;
393
+ }
394
+ var PROFILE_NAME_MAX;
391
395
  var init_company = __esm({
392
396
  "../contract/src/company.ts"() {
393
397
  "use strict";
398
+ PROFILE_NAME_MAX = 30;
394
399
  }
395
400
  });
396
401
 
@@ -492,6 +497,7 @@ function inboxItemTargetPath(item) {
492
497
  return null;
493
498
  }
494
499
  function notificationTargetPath(r) {
500
+ if (r.category === "todo") return r.chatSessionId ? chatPath(r.chatSessionId) : null;
495
501
  if (r.category !== "action") return r.workorderId ? taskPath(r.workorderId) : null;
496
502
  const viaItem = r.item ? inboxItemTargetPath(r.item) : null;
497
503
  const wid = r.workorderId ?? r.item?.workspace;
@@ -1350,6 +1356,137 @@ var init_variable_name = __esm({
1350
1356
  }
1351
1357
  });
1352
1358
 
1359
+ // ../contract/src/time-zone.ts
1360
+ function canonicalTimeZone(name) {
1361
+ if (typeof name !== "string") return null;
1362
+ const trimmed = name.trim();
1363
+ if (!trimmed || /^[+-]/.test(trimmed)) return null;
1364
+ try {
1365
+ return new Intl.DateTimeFormat("en-US", { timeZone: trimmed }).resolvedOptions().timeZone;
1366
+ } catch {
1367
+ return null;
1368
+ }
1369
+ }
1370
+ function isValidTimeZone(name) {
1371
+ return canonicalTimeZone(name) !== null;
1372
+ }
1373
+ function effectiveTimeZone(company, deploymentDefault) {
1374
+ return company?.timeZone || deploymentDefault;
1375
+ }
1376
+ function zoneOffsetMs(ms, timeZone) {
1377
+ const parts = new Intl.DateTimeFormat("en-US", {
1378
+ timeZone,
1379
+ hour12: false,
1380
+ year: "numeric",
1381
+ month: "2-digit",
1382
+ day: "2-digit",
1383
+ hour: "2-digit",
1384
+ minute: "2-digit",
1385
+ second: "2-digit"
1386
+ }).formatToParts(new Date(ms));
1387
+ const get = (type) => Number(parts.find((p2) => p2.type === type)?.value ?? "0");
1388
+ const asIfUtc = Date.UTC(get("year"), get("month") - 1, get("day"), get("hour") % 24, get("minute"), get("second"));
1389
+ return asIfUtc - (ms - (ms % 1e3 + 1e3) % 1e3);
1390
+ }
1391
+ function zonedDateOf(ms, timeZone) {
1392
+ const d = new Date(ms + zoneOffsetMs(ms, timeZone));
1393
+ return {
1394
+ date: `${d.getUTCFullYear()}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())}`,
1395
+ weekday: d.getUTCDay()
1396
+ };
1397
+ }
1398
+ function zonedToday(timeZone, nowMs = Date.now()) {
1399
+ return zonedDateOf(nowMs, timeZone);
1400
+ }
1401
+ function isValidDay(day) {
1402
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(day)) return false;
1403
+ const t = Date.parse(`${day}T00:00:00Z`);
1404
+ return !Number.isNaN(t) && new Date(t).toISOString().slice(0, 10) === day;
1405
+ }
1406
+ function zonedDateTimeToUtcMs(day, time3, timeZone) {
1407
+ if (!isValidDay(day)) throw new RangeError(`\u4E0D\u662F\u6709\u6548\u65E5\u671F\uFF08\u987B\u4E3A YYYY-MM-DD\uFF09\uFF1A${day}`);
1408
+ const m2 = /^(\d{2}):(\d{2})$/.exec(time3);
1409
+ if (!m2 || Number(m2[1]) > 23 || Number(m2[2]) > 59) throw new RangeError(`\u4E0D\u662F\u6709\u6548\u65F6\u523B\uFF08\u987B\u4E3A HH:mm\uFF09\uFF1A${time3}`);
1410
+ const local = Date.parse(`${day}T${time3}:00Z`);
1411
+ const before = local - zoneOffsetMs(local - DAY_MS, timeZone);
1412
+ const after = local - zoneOffsetMs(local + DAY_MS, timeZone);
1413
+ const fits = (t) => t + zoneOffsetMs(t, timeZone) === local;
1414
+ const okBefore = fits(before);
1415
+ const okAfter = fits(after);
1416
+ if (okBefore && okAfter) return Math.min(before, after);
1417
+ if (okBefore) return before;
1418
+ if (okAfter) return after;
1419
+ return before;
1420
+ }
1421
+ function zonedStartOfDayMs(day, timeZone) {
1422
+ return zonedDateTimeToUtcMs(day, "00:00", timeZone);
1423
+ }
1424
+ var DEFAULT_TIME_ZONE, DAY_MS, pad2;
1425
+ var init_time_zone = __esm({
1426
+ "../contract/src/time-zone.ts"() {
1427
+ "use strict";
1428
+ DEFAULT_TIME_ZONE = "Asia/Shanghai";
1429
+ DAY_MS = 24 * 60 * 60 * 1e3;
1430
+ pad2 = (n) => String(n).padStart(2, "0");
1431
+ }
1432
+ });
1433
+
1434
+ // ../contract/src/todos.ts
1435
+ function todoAssignees(todo) {
1436
+ return [todo.assigneeActorId];
1437
+ }
1438
+ function isTodoParty(todo, actorId) {
1439
+ return todo.creatorActorId === actorId || todoAssignees(todo).includes(actorId);
1440
+ }
1441
+ function isValidTodoTime(time3) {
1442
+ const m2 = /^(\d{2}):(\d{2})$/.exec(time3);
1443
+ return !!m2 && Number(m2[1]) <= 23 && Number(m2[2]) <= 59;
1444
+ }
1445
+ function weekdayOfDay(day) {
1446
+ return (/* @__PURE__ */ new Date(`${day}T00:00:00Z`)).getUTCDay();
1447
+ }
1448
+ function isTodoStartBeforeDue(t) {
1449
+ if (!t.startDate) return true;
1450
+ if (t.startDate !== t.dueDate) return t.startDate < t.dueDate;
1451
+ return !(t.startTime && t.dueTime) || t.startTime <= t.dueTime;
1452
+ }
1453
+ function formatTodoDue(day, time3) {
1454
+ const [, m2, d] = day.split("-");
1455
+ return `${Number(m2)} \u6708 ${Number(d)} \u65E5\uFF08${WEEKDAY_LABEL[weekdayOfDay(day)]}\uFF09${time3 ? ` ${time3}` : ""}`;
1456
+ }
1457
+ function formatTodoSpan(t) {
1458
+ const due = formatTodoDue(t.dueDate, t.dueTime);
1459
+ if (!t.startDate) return `\u622A\u6B62 ${due}`;
1460
+ const join48 = (text7, time3) => `${text7}${time3 ? " " : ""}`;
1461
+ return `${join48(formatTodoDue(t.startDate, t.startTime), t.startTime)}\u5F00\u59CB \u2192 ${join48(due, t.dueTime)}\u622A\u6B62`;
1462
+ }
1463
+ function todoToday(timeZone, nowMs = Date.now()) {
1464
+ return { ...zonedToday(timeZone, nowMs), timeZone };
1465
+ }
1466
+ function formatTodayLine(today) {
1467
+ return `\u4ECA\u5929\uFF1A${today.date} ${WEEKDAY_LABEL[today.weekday]}\uFF08${today.timeZone}\uFF09`;
1468
+ }
1469
+ function shiftDay(day, n) {
1470
+ const t = Date.parse(`${day}T00:00:00Z`) + n * 864e5;
1471
+ return new Date(t).toISOString().slice(0, 10);
1472
+ }
1473
+ function compareTodos(a, b2) {
1474
+ if (a.dueDate !== b2.dueDate) return a.dueDate < b2.dueDate ? -1 : 1;
1475
+ const ta = a.dueTime ?? "99:99";
1476
+ const tb = b2.dueTime ?? "99:99";
1477
+ if (ta !== tb) return ta < tb ? -1 : 1;
1478
+ if (a.createdAt !== b2.createdAt) return a.createdAt < b2.createdAt ? -1 : 1;
1479
+ return a.id < b2.id ? -1 : a.id > b2.id ? 1 : 0;
1480
+ }
1481
+ var WEEKDAY_LABEL;
1482
+ var init_todos = __esm({
1483
+ "../contract/src/todos.ts"() {
1484
+ "use strict";
1485
+ init_time_zone();
1486
+ WEEKDAY_LABEL = ["\u5468\u65E5", "\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D"];
1487
+ }
1488
+ });
1489
+
1353
1490
  // ../contract/src/credential-gap.ts
1354
1491
  function buildCredentialGapDescription(opts) {
1355
1492
  const { key: key2, scope: scope2, project, desc } = opts;
@@ -2236,9 +2373,9 @@ function firstSentenceSummary(text7, max = CARD_SUMMARY_MAX) {
2236
2373
  const raw = (text7 ?? "").trim();
2237
2374
  if (!raw) return "";
2238
2375
  const cut = raw.split(/[。!?\n]|——/u)[0].trim() || raw.trim();
2239
- const clean2 = stripInternalIds(cut).trim();
2240
- const cps = [...clean2];
2241
- return cps.length > max ? `${cps.slice(0, max).join("")}\u2026` : clean2;
2376
+ const clean3 = stripInternalIds(cut).trim();
2377
+ const cps = [...clean3];
2378
+ return cps.length > max ? `${cps.slice(0, max).join("")}\u2026` : clean3;
2242
2379
  }
2243
2380
  function requoteStepTitle(text7, title) {
2244
2381
  const t = title?.trim();
@@ -2415,6 +2552,8 @@ var init_src = __esm({
2415
2552
  init_skill_market();
2416
2553
  init_credential_ref();
2417
2554
  init_variable_name();
2555
+ init_time_zone();
2556
+ init_todos();
2418
2557
  init_credential_gap();
2419
2558
  init_views();
2420
2559
  init_telemetry();
@@ -20258,10 +20397,10 @@ var init_chat_item_ledger = __esm({
20258
20397
  }
20259
20398
  insert(input) {
20260
20399
  const prepared = this.prepareInsert(input);
20261
- const create = prepared.create;
20262
- if (!create) return prepared.id;
20400
+ const create2 = prepared.create;
20401
+ if (!create2) return prepared.id;
20263
20402
  this.enqueue(async () => {
20264
- prepared.persisted(await this.deps.items.createItem(create));
20403
+ prepared.persisted(await this.deps.items.createItem(create2));
20265
20404
  }, `create:${input.kind}`);
20266
20405
  return prepared.id;
20267
20406
  }
@@ -20303,7 +20442,7 @@ var init_chat_item_ledger = __esm({
20303
20442
  const messageId = row.messageId;
20304
20443
  const payload2 = input.payload ?? this.payloadOf(row);
20305
20444
  const metadata = input.metadata ?? { [CHAT_ITEM_META_LEGACY_CONTENT]: input.inContent };
20306
- const create = {
20445
+ const create2 = {
20307
20446
  id,
20308
20447
  sessionId: this.deps.sessionId,
20309
20448
  messageId,
@@ -20344,7 +20483,7 @@ var init_chat_item_ledger = __esm({
20344
20483
  }
20345
20484
  }
20346
20485
  };
20347
- return { id, create, persisted };
20486
+ return { id, create: create2, persisted };
20348
20487
  }
20349
20488
  /**
20350
20489
  * 追加合并:**排队期间到达的片段合进同一次 `append_text`**,一串片段一次往返。
@@ -31305,10 +31444,10 @@ var require_abort = __commonJS({
31305
31444
  "../../node_modules/.pnpm/asynckit@0.4.0/node_modules/asynckit/lib/abort.js"(exports2, module2) {
31306
31445
  module2.exports = abort;
31307
31446
  function abort(state) {
31308
- Object.keys(state.jobs).forEach(clean2.bind(state));
31447
+ Object.keys(state.jobs).forEach(clean3.bind(state));
31309
31448
  state.jobs = {};
31310
31449
  }
31311
- function clean2(key2) {
31450
+ function clean3(key2) {
31312
31451
  if (typeof this.jobs[key2] == "function") {
31313
31452
  this.jobs[key2]();
31314
31453
  }
@@ -35639,29 +35778,29 @@ var require_axios = __commonJS({
35639
35778
  }
35640
35779
  }
35641
35780
  }
35642
- let pad2 = 0;
35781
+ let pad3 = 0;
35643
35782
  let idx = len - 1;
35644
35783
  const tailIsPct3D = (j) => j >= 2 && body2.charCodeAt(j - 2) === 37 && // '%'
35645
35784
  body2.charCodeAt(j - 1) === 51 && // '3'
35646
35785
  (body2.charCodeAt(j) === 68 || body2.charCodeAt(j) === 100);
35647
35786
  if (idx >= 0) {
35648
35787
  if (body2.charCodeAt(idx) === 61) {
35649
- pad2++;
35788
+ pad3++;
35650
35789
  idx--;
35651
35790
  } else if (tailIsPct3D(idx)) {
35652
- pad2++;
35791
+ pad3++;
35653
35792
  idx -= 3;
35654
35793
  }
35655
35794
  }
35656
- if (pad2 === 1 && idx >= 0) {
35795
+ if (pad3 === 1 && idx >= 0) {
35657
35796
  if (body2.charCodeAt(idx) === 61) {
35658
- pad2++;
35797
+ pad3++;
35659
35798
  } else if (tailIsPct3D(idx)) {
35660
- pad2++;
35799
+ pad3++;
35661
35800
  }
35662
35801
  }
35663
35802
  const groups = Math.floor(effectiveLen / 4);
35664
- const bytes2 = groups * 3 - (pad2 || 0);
35803
+ const bytes2 = groups * 3 - (pad3 || 0);
35665
35804
  return bytes2 > 0 ? bytes2 : 0;
35666
35805
  }
35667
35806
  return Buffer.byteLength(body2, "utf8");
@@ -37520,7 +37659,7 @@ var require_axios = __commonJS({
37520
37659
  const instance = bind(Axios$1.prototype.request, context2);
37521
37660
  utils$1.extend(instance, Axios$1.prototype, context2, { allOwnKeys: true });
37522
37661
  utils$1.extend(instance, context2, null, { allOwnKeys: true });
37523
- instance.create = function create(instanceConfig) {
37662
+ instance.create = function create2(instanceConfig) {
37524
37663
  return createInstance(mergeConfig2(defaultConfig, instanceConfig));
37525
37664
  };
37526
37665
  return instance;
@@ -43137,7 +43276,7 @@ var require_websocket = __commonJS({
43137
43276
  var http2 = require("http");
43138
43277
  var net = require("net");
43139
43278
  var tls = require("tls");
43140
- var { randomBytes: randomBytes16, createHash: createHash46 } = require("crypto");
43279
+ var { randomBytes: randomBytes17, createHash: createHash46 } = require("crypto");
43141
43280
  var { Duplex, Readable } = require("stream");
43142
43281
  var { URL: URL2 } = require("url");
43143
43282
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -43675,7 +43814,7 @@ var require_websocket = __commonJS({
43675
43814
  }
43676
43815
  }
43677
43816
  const defaultPort = isSecure ? 443 : 80;
43678
- const key2 = randomBytes16(16).toString("base64");
43817
+ const key2 = randomBytes17(16).toString("base64");
43679
43818
  const request2 = isSecure ? https.request : http2.request;
43680
43819
  const protocolSet = /* @__PURE__ */ new Set();
43681
43820
  let perMessageDeflate;
@@ -46393,7 +46532,7 @@ var require_writer = __commonJS({
46393
46532
  this.tail = this.head;
46394
46533
  this.states = null;
46395
46534
  }
46396
- var create = function create2() {
46535
+ var create2 = function create3() {
46397
46536
  return util2.Buffer ? function create_buffer_setup() {
46398
46537
  return (Writer.create = function create_buffer() {
46399
46538
  return new BufferWriter();
@@ -46402,7 +46541,7 @@ var require_writer = __commonJS({
46402
46541
  return new Writer();
46403
46542
  };
46404
46543
  };
46405
- Writer.create = create();
46544
+ Writer.create = create2();
46406
46545
  Writer.alloc = function alloc(size) {
46407
46546
  return new util2.Array(size);
46408
46547
  };
@@ -46549,7 +46688,7 @@ var require_writer = __commonJS({
46549
46688
  };
46550
46689
  Writer._configure = function(BufferWriter_) {
46551
46690
  BufferWriter = BufferWriter_;
46552
- Writer.create = create();
46691
+ Writer.create = create2();
46553
46692
  BufferWriter._configure();
46554
46693
  };
46555
46694
  }
@@ -46631,14 +46770,14 @@ var require_reader = __commonJS({
46631
46770
  return new Reader(buffer);
46632
46771
  throw Error("illegal buffer");
46633
46772
  };
46634
- var create = function create2() {
46773
+ var create2 = function create3() {
46635
46774
  return util2.Buffer ? function create_buffer_setup(buffer) {
46636
46775
  return (Reader.create = function create_buffer(buffer2) {
46637
46776
  return util2.Buffer.isBuffer(buffer2) ? new BufferReader(buffer2) : create_array(buffer2);
46638
46777
  })(buffer);
46639
46778
  } : create_array;
46640
46779
  };
46641
- Reader.create = create();
46780
+ Reader.create = create2();
46642
46781
  Reader.prototype._slice = util2.Array.prototype.subarray || /* istanbul ignore next */
46643
46782
  util2.Array.prototype.slice;
46644
46783
  Reader.prototype.uint32 = /* @__PURE__ */ function read_uint32_setup() {
@@ -46806,7 +46945,7 @@ var require_reader = __commonJS({
46806
46945
  };
46807
46946
  Reader._configure = function(BufferReader_) {
46808
46947
  BufferReader = BufferReader_;
46809
- Reader.create = create();
46948
+ Reader.create = create2();
46810
46949
  BufferReader._configure();
46811
46950
  var fn = util2.Long ? "toLong" : (
46812
46951
  /* istanbul ignore next */
@@ -47144,15 +47283,15 @@ var require_lib2 = __commonJS({
47144
47283
  function buildUserAgent(source, opts) {
47145
47284
  let ua = `oapi-node-sdk/${getSdkVersion()}`;
47146
47285
  if (source) {
47147
- const clean2 = sanitizeSource(source);
47148
- if (clean2)
47149
- ua += ` source/${clean2}`;
47286
+ const clean3 = sanitizeSource(source);
47287
+ if (clean3)
47288
+ ua += ` source/${clean3}`;
47150
47289
  }
47151
47290
  if (opts === null || opts === void 0 ? void 0 : opts.extraTags) {
47152
47291
  for (const t of opts.extraTags) {
47153
- const clean2 = sanitizeSource(t);
47154
- if (clean2)
47155
- ua += ` ${clean2}`;
47292
+ const clean3 = sanitizeSource(t);
47293
+ if (clean3)
47294
+ ua += ` ${clean3}`;
47156
47295
  }
47157
47296
  }
47158
47297
  return ua;
@@ -129101,7 +129240,7 @@ var require_lib2 = __commonJS({
129101
129240
  }
129102
129241
  Header.prototype.key = "";
129103
129242
  Header.prototype.value = "";
129104
- Header.create = function create(properties) {
129243
+ Header.create = function create2(properties) {
129105
129244
  return new Header(properties);
129106
129245
  };
129107
129246
  Header.encode = function encode(message, writer) {
@@ -129213,7 +129352,7 @@ var require_lib2 = __commonJS({
129213
129352
  Frame.prototype.payloadType = "";
129214
129353
  Frame.prototype.payload = $util.newBuffer([]);
129215
129354
  Frame.prototype.LogIDNew = "";
129216
- Frame.create = function create(properties) {
129355
+ Frame.create = function create2(properties) {
129217
129356
  return new Frame(properties);
129218
129357
  };
129219
129358
  Frame.encode = function encode(message, writer) {
@@ -137317,7 +137456,7 @@ var require_pg_int8 = __commonJS({
137317
137456
  var carry;
137318
137457
  var t;
137319
137458
  var digits;
137320
- var pad2;
137459
+ var pad3;
137321
137460
  var l;
137322
137461
  var i;
137323
137462
  {
@@ -137329,12 +137468,12 @@ var require_pg_int8 = __commonJS({
137329
137468
  if (low === 0 && high === 0) {
137330
137469
  return sign3 + digits + result;
137331
137470
  }
137332
- pad2 = "";
137471
+ pad3 = "";
137333
137472
  l = 6 - digits.length;
137334
137473
  for (i = 0; i < l; i++) {
137335
- pad2 += "0";
137474
+ pad3 += "0";
137336
137475
  }
137337
- result = pad2 + digits + result;
137476
+ result = pad3 + digits + result;
137338
137477
  }
137339
137478
  {
137340
137479
  carry = high % BASE2;
@@ -137345,12 +137484,12 @@ var require_pg_int8 = __commonJS({
137345
137484
  if (low === 0 && high === 0) {
137346
137485
  return sign3 + digits + result;
137347
137486
  }
137348
- pad2 = "";
137487
+ pad3 = "";
137349
137488
  l = 6 - digits.length;
137350
137489
  for (i = 0; i < l; i++) {
137351
- pad2 += "0";
137490
+ pad3 += "0";
137352
137491
  }
137353
- result = pad2 + digits + result;
137492
+ result = pad3 + digits + result;
137354
137493
  }
137355
137494
  {
137356
137495
  carry = high % BASE2;
@@ -137361,12 +137500,12 @@ var require_pg_int8 = __commonJS({
137361
137500
  if (low === 0 && high === 0) {
137362
137501
  return sign3 + digits + result;
137363
137502
  }
137364
- pad2 = "";
137503
+ pad3 = "";
137365
137504
  l = 6 - digits.length;
137366
137505
  for (i = 0; i < l; i++) {
137367
- pad2 += "0";
137506
+ pad3 += "0";
137368
137507
  }
137369
- result = pad2 + digits + result;
137508
+ result = pad3 + digits + result;
137370
137509
  }
137371
137510
  {
137372
137511
  carry = high % BASE2;
@@ -137909,7 +138048,7 @@ var require_utils3 = __commonJS({
137909
138048
  var nodeCrypto = require("crypto");
137910
138049
  module2.exports = {
137911
138050
  postgresMd5PasswordHash,
137912
- randomBytes: randomBytes16,
138051
+ randomBytes: randomBytes17,
137913
138052
  deriveKey,
137914
138053
  sha256: sha2563,
137915
138054
  hashByName,
@@ -137919,7 +138058,7 @@ var require_utils3 = __commonJS({
137919
138058
  var webCrypto = nodeCrypto.webcrypto || globalThis.crypto;
137920
138059
  var subtleCrypto = webCrypto.subtle;
137921
138060
  var textEncoder = new TextEncoder();
137922
- function randomBytes16(length) {
138061
+ function randomBytes17(length) {
137923
138062
  return webCrypto.getRandomValues(Buffer.alloc(length));
137924
138063
  }
137925
138064
  async function md5(string7) {
@@ -153397,14 +153536,14 @@ var init_kernel_bridge = __esm({
153397
153536
  fresh.add(id);
153398
153537
  }
153399
153538
  }
153400
- const clean2 = this.resyncRequested ? ids2.filter((id) => !fresh.has(id)) : [];
153539
+ const clean3 = this.resyncRequested ? ids2.filter((id) => !fresh.has(id)) : [];
153401
153540
  this.resyncRequested = false;
153402
- if (clean2.length > 0) {
153403
- const slice = Math.min(_EngineKernel.REFRESH_RESYNC_SLICE, clean2.length);
153541
+ if (clean3.length > 0) {
153542
+ const slice = Math.min(_EngineKernel.REFRESH_RESYNC_SLICE, clean3.length);
153404
153543
  for (let i = 0; i < slice; i++) {
153405
- toLoad.push(clean2[(this.resyncCursor + i) % clean2.length]);
153544
+ toLoad.push(clean3[(this.resyncCursor + i) % clean3.length]);
153406
153545
  }
153407
- this.resyncCursor = (this.resyncCursor + slice) % clean2.length;
153546
+ this.resyncCursor = (this.resyncCursor + slice) % clean3.length;
153408
153547
  }
153409
153548
  const claimed = toLoad.filter((id) => this.dirtyWorkorders.delete(id));
153410
153549
  let loaded;
@@ -175535,7 +175674,11 @@ var init_assistants = __esm({
175535
175674
  return p2?.autoAssignAssistant !== false;
175536
175675
  }
175537
175676
  /* ---------- 懒创建(原子 + 幂等 + 探针 + opt-out) ---------- */
175538
- async ensureFor(humanId, by = humanId) {
175677
+ /**
175678
+ * @param preferred 调用方(入职向导「我的助理」)指定的那台运行时。只是**偏好**:在本公司在线运行时里
175679
+ * 找得到就用它建,找不到(下线了 / 不是本公司的 / 乱填)就当没给,照常按负载挑——见 {@link pickAssistantRuntime}。
175680
+ */
175681
+ async ensureFor(humanId, by = humanId, preferred) {
175539
175682
  const pre = await this.opts.bindStore.getByHuman(humanId);
175540
175683
  if (pre) return { agentId: pre.agentActorId, created: false, source: "existing" };
175541
175684
  if (!await this.isAutoAssignOn(humanId)) {
@@ -175563,7 +175706,7 @@ var init_assistants = __esm({
175563
175706
  return { agentId: restored.agentActorId, created: false, source: "existing" };
175564
175707
  }
175565
175708
  }
175566
- const agentId = await this._createLocked(humanId, by, "lazy-create");
175709
+ const agentId = await this._createLocked(humanId, by, "lazy-create", preferred);
175567
175710
  return { agentId, created: true, source: "lazy-created" };
175568
175711
  });
175569
175712
  }
@@ -175817,11 +175960,11 @@ var init_assistants = __esm({
175817
175960
  }
175818
175961
  }
175819
175962
  /* ---------- 私有:创建流水线(不加锁;调用方须已持 withHumanLock) ---------- */
175820
- async _createLocked(humanId, by, origin) {
175963
+ async _createLocked(humanId, by, origin, preferred) {
175821
175964
  const human = await this.opts.registry.getActor(humanId);
175822
175965
  if (!human) throw new AssistantLazyCreateError("human-not-found");
175823
175966
  if (human.kind !== "human" || human.status !== "active") throw new AssistantLazyCreateError("human-inactive");
175824
- const runtime = await this.pickAssistantRuntime();
175967
+ const runtime = await this.pickAssistantRuntime(preferred);
175825
175968
  if (!runtime) throw new AssistantLazyCreateError("no-runtime");
175826
175969
  const agentId = `actor:agent:asst-${slug4(human.name)}-${short6()}`;
175827
175970
  await this.opts.actors.upsertActor({ id: agentId, kind: "agent", name: `${human.name}\u7684\u52A9\u7406`, status: "active", roles: ["assistant"] }, by);
@@ -175860,7 +176003,18 @@ var init_assistants = __esm({
175860
176003
  return companyId ? { companyId } : void 0;
175861
176004
  }
175862
176005
  /* ---------- runtime 挑选(ADR §3.3.a):负载均衡 + 幂等 tie-break ---------- */
175863
- async pickAssistantRuntime() {
176006
+ /**
176007
+ * 挑一台给助理用的运行时。优先级:
176008
+ * ① 调用方指定的那台(入职向导里用户在「我的助理」下拉选的),且它**在本公司在线**;
176009
+ * ② 公司默认 kind(`runtime_configs.default_assistant_runtime_kind`,缺省 claude-code)里按负载挑;
176010
+ * ③ 默认 kind 一台在线的都没有 → 退回本公司任意在线运行时,同样按负载挑。
176011
+ * 本公司一台在线的都没有才返回 null(→ 503 no-runtime)。
176012
+ *
176013
+ * ①③ 是 Bug 0308(2026-10-06)补的:此前只认默认 kind,团队只接了 codex / hermes 等机器时
176014
+ * 懒建助理 100% `no-runtime`——入职向导里用户明明选了 codex,助理照样建不出来,进团队后
176015
+ * 「没有可用的 Agent」。用户选了哪台就该用哪台;没选时有机器也不该因为 kind 不是默认值就一个不给。
176016
+ */
176017
+ async pickAssistantRuntime(preferred) {
175864
176018
  const runtimes = (await this.opts.listRuntimes(void 0, this.scope())).filter((r) => r.status === "online");
175865
176019
  if (runtimes.length === 0) return null;
175866
176020
  let defaultKind = DEFAULT_ASSISTANT_RUNTIME_KIND;
@@ -175871,8 +176025,12 @@ var init_assistants = __esm({
175871
176025
  break;
175872
176026
  }
175873
176027
  }
175874
- const candidates = runtimes.filter((r) => r.kind === defaultKind);
175875
- if (candidates.length === 0) return null;
176028
+ if (preferred) {
176029
+ const hit = runtimes.find((r) => r.nodeId === preferred.nodeId && r.kind === preferred.runtimeKind);
176030
+ if (hit) return hit;
176031
+ }
176032
+ const ofDefaultKind = runtimes.filter((r) => r.kind === defaultKind);
176033
+ const candidates = ofDefaultKind.length > 0 ? ofDefaultKind : runtimes;
175876
176034
  const bindings = (await this.opts.registry.listBindings()).filter((b2) => b2.status === "active");
175877
176035
  const load = (r) => bindings.filter((b2) => b2.nodeId === r.nodeId && b2.runtimeKind === r.kind).length;
175878
176036
  return candidates.slice().sort((a, b2) => {
@@ -177316,7 +177474,7 @@ function buildWorkorderDraftDetail(draft, resolveActor, spawnReviewers, resolveR
177316
177474
  const hasDeliverySummary = spawns.some((op) => op.type === DELIVERY_SUMMARY_ARTIFACT_TYPE);
177317
177475
  const nodes = spawns.map((op) => {
177318
177476
  const rows = spawnReviewers ? draft.plan.ops.filter((o) => o.action === "edit" && o.artifactId === op.id && o.reviewers !== void 0).reduce(
177319
- (current, edit2) => applyReviewerList({ current, next: edit2.reviewers }),
177477
+ (current, edit3) => applyReviewerList({ current, next: edit3.reviewers }),
177320
177478
  [...spawnReviewers(op.type, op.reviewers ?? [], { reviewersFinal: op.reviewersFinal === true })]
177321
177479
  ) : [];
177322
177480
  const pv = rows.length > 0 ? reviewerPreviewOfRows(rows.map((r) => ({ actor: r.reviewerActorId, source: r.source, reviewGroup: r.reviewGroup }))) : null;
@@ -188413,16 +188571,16 @@ function parse2(options) {
188413
188571
  );
188414
188572
  const parser = {
188415
188573
  constructs: constructs2,
188416
- content: create(content),
188574
+ content: create2(content),
188417
188575
  defined: [],
188418
- document: create(document2),
188419
- flow: create(flow),
188576
+ document: create2(document2),
188577
+ flow: create2(flow),
188420
188578
  lazy: {},
188421
- string: create(string3),
188422
- text: create(text)
188579
+ string: create2(string3),
188580
+ text: create2(text)
188423
188581
  };
188424
188582
  return parser;
188425
- function create(initial) {
188583
+ function create2(initial) {
188426
188584
  return creator;
188427
188585
  function creator(from) {
188428
188586
  return createTokenizer(parser, initial, from);
@@ -188866,10 +189024,10 @@ function compiler(options) {
188866
189024
  events[start][1]._spread = listSpread;
188867
189025
  return length;
188868
189026
  }
188869
- function opener(create, and) {
189027
+ function opener(create2, and) {
188870
189028
  return open3;
188871
189029
  function open3(token2) {
188872
- enter.call(this, create(token2), token2);
189030
+ enter.call(this, create2(token2), token2);
188873
189031
  if (and) and.call(this, token2);
188874
189032
  }
188875
189033
  }
@@ -212960,10 +213118,10 @@ async function startOasisServer(opts) {
212960
213118
  ...fp(p2)
212961
213119
  }))
212962
213120
  ] : [];
212963
- const { randomUUID: randomUUID48 } = await import("node:crypto");
213121
+ const { randomUUID: randomUUID50 } = await import("node:crypto");
212964
213122
  try {
212965
213123
  await store.enqueuePendingMessage({
212966
- id: randomUUID48(),
213124
+ id: randomUUID50(),
212967
213125
  chatSessionId: input.chatSessionId,
212968
213126
  text: input.text,
212969
213127
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -212996,11 +213154,11 @@ async function startOasisServer(opts) {
212996
213154
  ...f2?.mtime ? { srcMtime: f2.mtime } : {}
212997
213155
  };
212998
213156
  });
212999
- const { randomUUID: randomUUID48 } = await import("node:crypto");
213157
+ const { randomUUID: randomUUID50 } = await import("node:crypto");
213000
213158
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
213001
213159
  try {
213002
213160
  await store.enqueuePendingMessage({
213003
- id: randomUUID48(),
213161
+ id: randomUUID50(),
213004
213162
  chatSessionId: input.chatSessionId,
213005
213163
  text: "",
213006
213164
  createdAt: nowIso,
@@ -214475,7 +214633,7 @@ async function startOasisServer(opts) {
214475
214633
  const openRoom = async () => {
214476
214634
  let chatSessionId2;
214477
214635
  if (chatStore) {
214478
- const { randomUUID: randomUUID48 } = await import("node:crypto");
214636
+ const { randomUUID: randomUUID50 } = await import("node:crypto");
214479
214637
  const known = discussSessions.get(itemKey);
214480
214638
  const knownSession = known ? await chatStore.getSession(known) : null;
214481
214639
  if (knownSession && knownSession.workspace === ctx.workspace && knownSession.humanActorId === ctx.ownerId && (!body2.escalationId || knownSession.escalationId === body2.escalationId)) chatSessionId2 = known;
@@ -214484,7 +214642,7 @@ async function startOasisServer(opts) {
214484
214642
  chatSessionId2 = existing[0]?.id;
214485
214643
  }
214486
214644
  if (!chatSessionId2) {
214487
- chatSessionId2 = randomUUID48();
214645
+ chatSessionId2 = randomUUID50();
214488
214646
  const now = (/* @__PURE__ */ new Date()).toISOString();
214489
214647
  const title = `\u89E3\u51B3\uFF1A${ctx.seed.slice(ctx.seed.indexOf("\uFF1A") + 1, ctx.seed.indexOf("\uFF1A") + 25) || "\u5DE5\u5355\u5361\u70B9"}`;
214490
214648
  const reg0 = (await resolveEngine(currentCompanyId).catch(() => null))?.registry ?? opts.registry;
@@ -215690,7 +215848,7 @@ ${composed}`;
215690
215848
  if (rejectedLedger.failures === 0 && retriedRow) {
215691
215849
  turnPersisted = true;
215692
215850
  } else if (rejectedLedger.failures === 0) {
215693
- const { randomUUID: randomUUID48 } = await import("node:crypto");
215851
+ const { randomUUID: randomUUID50 } = await import("node:crypto");
215694
215852
  const now = (/* @__PURE__ */ new Date()).toISOString();
215695
215853
  const rejectedAttachments = effectiveAttachments.map((a) => ({
215696
215854
  name: a.name,
@@ -215698,7 +215856,7 @@ ${composed}`;
215698
215856
  ...a.contentType !== void 0 ? { contentType: a.contentType } : {}
215699
215857
  }));
215700
215858
  await chatStore.appendMessage({
215701
- id: randomUUID48(),
215859
+ id: randomUUID50(),
215702
215860
  sessionId: persistTarget.id,
215703
215861
  role: "user",
215704
215862
  content: persistedUserMessage,
@@ -215783,14 +215941,14 @@ ${composed}`;
215783
215941
  runtimeSessionId: session.nativeSessionId ?? session.id
215784
215942
  }).catch(() => void 0);
215785
215943
  }
215786
- const { randomUUID: randomUUID48 } = await import("node:crypto");
215944
+ const { randomUUID: randomUUID50 } = await import("node:crypto");
215787
215945
  const persistAttachments = effectiveAttachments.map((a) => ({
215788
215946
  name: a.name,
215789
215947
  ...a.blobRef !== void 0 ? { blobRef: a.blobRef } : {},
215790
215948
  ...a.contentType !== void 0 ? { contentType: a.contentType } : {}
215791
215949
  }));
215792
215950
  if (!retriedRow) await chatStore.appendMessage({
215793
- id: randomUUID48(),
215951
+ id: randomUUID50(),
215794
215952
  sessionId: persistTarget.id,
215795
215953
  role: "user",
215796
215954
  content: persistedUserMessage,
@@ -216442,8 +216600,8 @@ ${composed}`;
216442
216600
  res.writeHead(400, { "content-type": "application/json" }).end(JSON.stringify({ error: `\u56DE\u8C03\u5730\u5740\u4E0D\u53EF\u7528\uFF0C\u5DF2\u4E2D\u6B62\uFF08\u907F\u514D\u5728 GitHub \u4E0A\u7559\u4E0B\u5B64\u513F App\uFF09\uFF1A${e.message}` }));
216443
216601
  return;
216444
216602
  }
216445
- const { randomUUID: randomUUID48 } = await import("node:crypto");
216446
- const state = randomUUID48();
216603
+ const { randomUUID: randomUUID50 } = await import("node:crypto");
216604
+ const state = randomUUID50();
216447
216605
  const nameSlug = (actorName || actorId?.split(":").pop() || owner?.login || "bot").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-|-$/g, "").slice(0, 24) || "bot";
216448
216606
  const nameSuffix = await chooseAppNameSuffix(
216449
216607
  { employeeSlug: nameSlug, namePrefix: "oasis" },
@@ -220173,6 +220331,10 @@ var init_memory_control_plane_store = __esm({
220173
220331
  }
220174
220332
  if (patch.status !== void 0) c.status = patch.status;
220175
220333
  if (patch.onboardingVariant !== void 0) c.onboardingVariant = patch.onboardingVariant;
220334
+ if (patch.timeZone !== void 0) {
220335
+ if (patch.timeZone === null) delete c.timeZone;
220336
+ else c.timeZone = patch.timeZone;
220337
+ }
220176
220338
  if (patch.dissolvedAt !== void 0) {
220177
220339
  if (patch.dissolvedAt === null) delete c.dissolvedAt;
220178
220340
  else c.dissolvedAt = patch.dissolvedAt;
@@ -220816,12 +220978,12 @@ function isUncategorizedProjectId(projectId2) {
220816
220978
  function latestUncategorizedActivityAt(candidates) {
220817
220979
  let bestIso = UNCATEGORIZED_UNKNOWN_TIME;
220818
220980
  let bestMs = Date.parse(UNCATEGORIZED_UNKNOWN_TIME);
220819
- for (const iso8 of candidates) {
220820
- if (!iso8) continue;
220821
- const ms = Date.parse(iso8);
220981
+ for (const iso9 of candidates) {
220982
+ if (!iso9) continue;
220983
+ const ms = Date.parse(iso9);
220822
220984
  if (Number.isNaN(ms) || ms <= bestMs) continue;
220823
220985
  bestMs = ms;
220824
- bestIso = iso8;
220986
+ bestIso = iso9;
220825
220987
  }
220826
220988
  return bestIso;
220827
220989
  }
@@ -221594,9 +221756,9 @@ function deriveRounds(runs) {
221594
221756
  }
221595
221757
  return rounds.sort((a, b2) => Date.parse(a.startedAt) - Date.parse(b2.startedAt) || a.key.localeCompare(b2.key));
221596
221758
  }
221597
- function inWindow(iso8, fromMs, toMs) {
221598
- if (!iso8) return false;
221599
- const ms = Date.parse(iso8);
221759
+ function inWindow(iso9, fromMs, toMs) {
221760
+ if (!iso9) return false;
221761
+ const ms = Date.parse(iso9);
221600
221762
  return Number.isFinite(ms) && ms >= fromMs && ms <= toMs;
221601
221763
  }
221602
221764
  function computeFacts(input) {
@@ -231728,40 +231890,21 @@ var init_migrate = __esm({
231728
231890
  });
231729
231891
 
231730
231892
  // ../server/src/domains/actors/variable-expiry.ts
231731
- function zoneOffsetMs(ms, timeZone) {
231732
- const parts = new Intl.DateTimeFormat("en-US", {
231733
- timeZone,
231734
- hour12: false,
231735
- year: "numeric",
231736
- month: "2-digit",
231737
- day: "2-digit",
231738
- hour: "2-digit",
231739
- minute: "2-digit",
231740
- second: "2-digit"
231741
- }).formatToParts(new Date(ms));
231742
- const get = (type) => Number(parts.find((p2) => p2.type === type)?.value ?? "0");
231743
- const asIfUtc = Date.UTC(get("year"), get("month") - 1, get("day"), get("hour") % 24, get("minute"), get("second"));
231744
- return asIfUtc - ms;
231745
- }
231746
- function zonedStartOfDayMs(dateStr, timeZone) {
231747
- const utcMidnight = Date.parse(`${dateStr}T00:00:00Z`);
231748
- const firstGuess = utcMidnight - zoneOffsetMs(utcMidnight, timeZone);
231749
- const corrected = utcMidnight - zoneOffsetMs(firstGuess, timeZone);
231750
- return corrected;
231751
- }
231752
231893
  function isVariableExpired(expiresAt2, nowMs, timeZone = DEFAULT_VARIABLE_TIME_ZONE) {
231753
231894
  if (!expiresAt2) return false;
231754
231895
  const t = Date.parse(expiresAt2);
231755
231896
  if (Number.isNaN(t)) return false;
231756
231897
  const d = new Date(t);
231757
231898
  const dateStr = `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}`;
231899
+ if (!isValidDay(dateStr)) return false;
231758
231900
  return nowMs >= zonedStartOfDayMs(dateStr, timeZone);
231759
231901
  }
231760
231902
  var DEFAULT_VARIABLE_TIME_ZONE, pad;
231761
231903
  var init_variable_expiry = __esm({
231762
231904
  "../server/src/domains/actors/variable-expiry.ts"() {
231763
231905
  "use strict";
231764
- DEFAULT_VARIABLE_TIME_ZONE = "Asia/Shanghai";
231906
+ init_src();
231907
+ DEFAULT_VARIABLE_TIME_ZONE = DEFAULT_TIME_ZONE;
231765
231908
  pad = (n) => String(n).padStart(2, "0");
231766
231909
  }
231767
231910
  });
@@ -233300,6 +233443,19 @@ ${input.description}
233300
233443
  if (rows.some((r) => r.key === key2)) return true;
233301
233444
  return this.variableRecordsEnabled() && (await this.opts.store.listVariableRecords()).some((r) => r.key === key2);
233302
233445
  }
233446
+ /**
233447
+ * 变量到期按哪个时区算日界:本组织的生效时区(PRD v1.2 §7「按组织时区,未设置时按默认」)。
233448
+ * 读失败回落 Asia/Shanghai 并告警——宁可按默认日界判,也不让一次控制面抖动把整次派单打挂。
233449
+ */
233450
+ async variableTimeZone() {
233451
+ if (!this.opts.timeZone) return DEFAULT_VARIABLE_TIME_ZONE;
233452
+ try {
233453
+ return await this.opts.timeZone();
233454
+ } catch (err) {
233455
+ console.warn(`[variables] \u8BFB\u7EC4\u7EC7\u65F6\u533A\u5931\u8D25\uFF0C\u6309 ${DEFAULT_VARIABLE_TIME_ZONE} \u5224\u5230\u671F\uFF1A${err.message}`);
233456
+ return DEFAULT_VARIABLE_TIME_ZONE;
233457
+ }
233458
+ }
233303
233459
  async resolveActorEnv(actorId) {
233304
233460
  const cfg = await this.opts.store.latestConfig(actorId);
233305
233461
  const enabledConnectors = await this.effectiveEnabledConnectors(actorId, cfg);
@@ -233319,12 +233475,13 @@ ${input.description}
233319
233475
  const decode2 = (ct) => ct.startsWith("plain:") ? ct.slice(6) : this.decrypt(ct);
233320
233476
  const expiredWinner = /* @__PURE__ */ new Map();
233321
233477
  const nowMs = Date.now();
233478
+ const timeZone = await this.variableTimeZone();
233322
233479
  for (const row of rows) {
233323
233480
  if (row.scope !== "global") continue;
233324
233481
  if (row.actorId) continue;
233325
233482
  if (!row.connectorId || enabledConnectors.has(row.connectorId)) {
233326
233483
  env[row.key] = decode2(row.valueEncrypted);
233327
- expiredWinner.set(row.key, isVariableExpired(row.expiresAt, nowMs));
233484
+ expiredWinner.set(row.key, isVariableExpired(row.expiresAt, nowMs, timeZone));
233328
233485
  }
233329
233486
  }
233330
233487
  for (const row of rows) {
@@ -233332,7 +233489,7 @@ ${input.description}
233332
233489
  if (row.actorId && row.actorId !== actorId) continue;
233333
233490
  if (!row.connectorId || enabledConnectors.has(row.connectorId)) {
233334
233491
  env[row.key] = decode2(row.valueEncrypted);
233335
- expiredWinner.set(row.key, isVariableExpired(row.expiresAt, nowMs));
233492
+ expiredWinner.set(row.key, isVariableExpired(row.expiresAt, nowMs, timeZone));
233336
233493
  }
233337
233494
  }
233338
233495
  for (const [key2, expired] of expiredWinner) if (expired) delete env[key2];
@@ -233376,13 +233533,14 @@ ${input.description}
233376
233533
  if (!prev || rank[row.binding.kind] > rank[prev.binding.kind]) best.set(row.key, row);
233377
233534
  }
233378
233535
  const nowMs = Date.now();
233536
+ const timeZone = await this.variableTimeZone();
233379
233537
  const out = [];
233380
233538
  for (const row of best.values()) {
233381
233539
  if (!isInjectableVariableName(row.key)) {
233382
233540
  skipUninjectable(row.key);
233383
233541
  continue;
233384
233542
  }
233385
- if (isVariableExpired(row.expiresAt, nowMs)) continue;
233543
+ if (isVariableExpired(row.expiresAt, nowMs, timeZone)) continue;
233386
233544
  out.push(row);
233387
233545
  }
233388
233546
  return out;
@@ -233559,6 +233717,7 @@ ${input.description}
233559
233717
  const merged = /* @__PURE__ */ new Map();
233560
233718
  const expiredWinner = /* @__PURE__ */ new Map();
233561
233719
  const nowMs = Date.now();
233720
+ const timeZone = await this.variableTimeZone();
233562
233721
  const gated = (row) => !row.connectorId || enabledConnectors.has(row.connectorId);
233563
233722
  const put3 = (row) => {
233564
233723
  if (!gated(row)) return;
@@ -233572,7 +233731,7 @@ ${input.description}
233572
233731
  deliveryMode: row.deliveryMode ?? "env",
233573
233732
  origin: { ...row.actorId !== void 0 ? { actorId: row.actorId } : {}, ...row.projectId !== void 0 ? { projectId: row.projectId } : {} }
233574
233733
  });
233575
- expiredWinner.set(row.key, isVariableExpired(row.expiresAt, nowMs));
233734
+ expiredWinner.set(row.key, isVariableExpired(row.expiresAt, nowMs, timeZone));
233576
233735
  };
233577
233736
  for (const row of rows) {
233578
233737
  if (row.scope === "global" && !row.actorId) put3(row);
@@ -234511,10 +234670,10 @@ function normalizeExpiresAt(raw) {
234511
234670
  if (Number.isNaN(t)) throw new ApiError(400, "BAD_REQUEST", `expiresAt \u4E0D\u662F\u5408\u6CD5\u65E5\u671F\uFF1A${raw}`);
234512
234671
  return new Date(t).toISOString();
234513
234672
  }
234514
- function assertCreatableVariable(key2, expiresAt2, nowMs = Date.now()) {
234673
+ function assertCreatableVariable(key2, expiresAt2, nowMs = Date.now(), timeZone = DEFAULT_VARIABLE_TIME_ZONE) {
234515
234674
  const problem = variableNameProblem(key2);
234516
234675
  if (problem) throw new ApiError(400, problem.code, problem.message);
234517
- if (expiresAt2 && isVariableExpired(expiresAt2, nowMs)) {
234676
+ if (expiresAt2 && isVariableExpired(expiresAt2, nowMs, timeZone)) {
234518
234677
  throw new ApiError(400, "VARIABLE_EXPIRY_NOT_FUTURE", "\u65B0\u5EFA\u53D8\u91CF\u7684\u5230\u671F\u65F6\u95F4\u987B\u665A\u4E8E\u4ECA\u5929");
234519
234678
  }
234520
234679
  }
@@ -235069,7 +235228,7 @@ function actorsDomain(opts) {
235069
235228
  const { service } = await resolveCtx(req.auth.companyId);
235070
235229
  const b2 = req.body;
235071
235230
  if (typeof b2?.key !== "string" || !b2.key) throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 key");
235072
- const create = parseCreateFlag(b2.create);
235231
+ const create2 = parseCreateFlag(b2.create);
235073
235232
  const legacyOpts = { replace: parseReplaceFlag(b2.replace), displaced: [] };
235074
235233
  const expiresAt2 = normalizeExpiresAt(b2.expiresAt);
235075
235234
  const audienceMeta = parseAudienceMeta(b2);
@@ -235123,9 +235282,9 @@ function actorsDomain(opts) {
235123
235282
  by: req.auth.actor,
235124
235283
  expiresAt: expiresAt2
235125
235284
  };
235126
- if (create) {
235285
+ if (create2) {
235127
235286
  if (b2.value === void 0 || b2.value === "") throw new ApiError(400, "BAD_REQUEST", "\u65B0\u5EFA\u53D8\u91CF\u5FC5\u987B\u63D0\u4F9B\u53D8\u91CF\u503C");
235128
- assertCreatableVariable(b2.key, expiresAt2);
235287
+ assertCreatableVariable(b2.key, expiresAt2, Date.now(), await service.variableTimeZone());
235129
235288
  const outcome = await service.createVariable({ ...input, value: b2.value }, legacyOpts).catch(rethrowBindingConflict);
235130
235289
  if (outcome === "exists") throw await variableExistsError(service, b2.key, selector);
235131
235290
  } else {
@@ -235262,7 +235421,7 @@ function actorsDomain(opts) {
235262
235421
  throw new ApiError(400, "MISSING_DELIVERY_MODE", "\u5FC5\u987B\u663E\u5F0F\u63D0\u4F9B deliveryMode\uFF08env | ref | broker\uFF09\u2014\u2014\u6295\u9012\u6863\u51B3\u5B9A\u660E\u6587\u662F\u5426\u8FDB agent env\uFF0C\u4E0D\u8BBE\u7F3A\u7701");
235263
235422
  }
235264
235423
  const expiresAt2 = normalizeExpiresAt(b2.expiresAt);
235265
- assertCreatableVariable(b2.key, expiresAt2);
235424
+ assertCreatableVariable(b2.key, expiresAt2, Date.now(), await service.variableTimeZone());
235266
235425
  const bindings = parseBindingTargets(b2.bindings, "bindings");
235267
235426
  const replace2 = parseBindingTargets(b2.replace, "replace");
235268
235427
  const connectorId = await connectorOwner(service, b2.key);
@@ -235353,9 +235512,9 @@ function actorsDomain(opts) {
235353
235512
  const actorId = req.params.id;
235354
235513
  const b2 = req.body;
235355
235514
  if (typeof b2?.key !== "string" || !b2.key) throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 key");
235356
- const create = parseCreateFlag(b2.create);
235515
+ const create2 = parseCreateFlag(b2.create);
235357
235516
  const legacyOpts = { replace: parseReplaceFlag(b2.replace), displaced: [] };
235358
- if (create) {
235517
+ if (create2) {
235359
235518
  const owner = await service.getActor(actorId);
235360
235519
  if (!owner) throw new ApiError(404, "NOT_FOUND", `\u6CA1\u6709\u8FD9\u4E2A Agent\uFF1A${actorId}`);
235361
235520
  if (owner.kind !== "agent") throw new ApiError(400, "BAD_REQUEST", "\u4E2A\u4EBA\u53D8\u91CF\u53EA\u80FD\u5EFA\u5728 Agent \u4E0A");
@@ -235371,8 +235530,8 @@ function actorsDomain(opts) {
235371
235530
  "\u5FC5\u987B\u663E\u5F0F\u63D0\u4F9B deliveryMode\uFF08env | ref | broker\uFF09\u2014\u2014\u6295\u9012\u6863\u51B3\u5B9A\u660E\u6587\u662F\u5426\u8FDB agent env\uFF0C\u4E0D\u8BBE\u7F3A\u7701"
235372
235531
  );
235373
235532
  }
235374
- if (create && (b2.value === void 0 || b2.value === "")) throw new ApiError(400, "BAD_REQUEST", "\u65B0\u5EFA\u53D8\u91CF\u5FC5\u987B\u63D0\u4F9B\u53D8\u91CF\u503C");
235375
- if (create) assertCreatableVariable(b2.key, expiresAt2);
235533
+ if (create2 && (b2.value === void 0 || b2.value === "")) throw new ApiError(400, "BAD_REQUEST", "\u65B0\u5EFA\u53D8\u91CF\u5FC5\u987B\u63D0\u4F9B\u53D8\u91CF\u503C");
235534
+ if (create2) assertCreatableVariable(b2.key, expiresAt2, Date.now(), await service.variableTimeZone());
235376
235535
  if (b2.value === void 0 || b2.value === "") return { status: 200, body: { ok: true } };
235377
235536
  assertInjectableVariableName(b2.key);
235378
235537
  if (b2.connectorId === void 0) {
@@ -235400,7 +235559,7 @@ function actorsDomain(opts) {
235400
235559
  by: req.auth.actor,
235401
235560
  expiresAt: expiresAt2
235402
235561
  };
235403
- if (create) {
235562
+ if (create2) {
235404
235563
  const outcome = await service.createVariable(input, legacyOpts).catch(rethrowBindingConflict);
235405
235564
  if (outcome === "exists") throw await variableExistsError(service, b2.key, { actorId });
235406
235565
  } else {
@@ -236431,6 +236590,7 @@ function createActorsDomain(opts) {
236431
236590
  ...opts.listSharedSkillFiles !== void 0 ? { listSharedSkillFiles: opts.listSharedSkillFiles } : {},
236432
236591
  ...opts.isInstalledSkillAvailable !== void 0 ? { isInstalledSkillAvailable: opts.isInstalledSkillAvailable } : {},
236433
236592
  ...opts.listBindingModelIds !== void 0 ? { listBindingModelIds: opts.listBindingModelIds } : {},
236593
+ ...opts.companyTimeZone !== void 0 ? { timeZone: () => opts.companyTimeZone(companyOf()) } : {},
236434
236594
  // 服务内部的变量写入(连接器授权)抢占记录时由服务自己记审计(ADR 0578 评审 N-3),与路由同一个旁账。
236435
236595
  ...opts.credentialAudit !== void 0 ? { credentialAudit: (r) => opts.credentialAudit({ ...r, companyId: companyOf() }) } : {},
236436
236596
  ...kernel !== void 0 ? { onRolesChanged: (a, r) => kernel.setActorRoles(a, r) } : {},
@@ -236555,7 +236715,9 @@ function assistantsDomain(opts) {
236555
236715
  const humanId = req.params.humanId;
236556
236716
  try {
236557
236717
  await service.assertWritePermission(req.auth.actor, humanId);
236558
- const r = await service.ensureFor(humanId, req.auth.actor);
236718
+ const b2 = req.body ?? {};
236719
+ const preferred = typeof b2.nodeId === "string" && b2.nodeId && typeof b2.runtimeKind === "string" && b2.runtimeKind ? { nodeId: b2.nodeId, runtimeKind: b2.runtimeKind } : void 0;
236720
+ const r = await service.ensureFor(humanId, req.auth.actor, preferred);
236559
236721
  return { status: 201, body: { agentId: r.agentId, source: r.source } };
236560
236722
  } catch (e) {
236561
236723
  toApiError2(e);
@@ -241623,7 +241785,8 @@ function fromRow(row) {
241623
241785
  ["result", str2(row["result"])],
241624
241786
  ["invalidReason", str2(row["invalid_reason"])],
241625
241787
  ["readAt", iso2(row["read_at"])],
241626
- ["item", row["item"] ?? void 0]
241788
+ ["item", row["item"] ?? void 0],
241789
+ ["chatSessionId", str2(row["chat_session_id"])]
241627
241790
  ];
241628
241791
  for (const [k2, v2] of opt) if (v2 !== void 0 && v2 !== null) r[k2] = v2;
241629
241792
  return r;
@@ -241789,6 +241952,7 @@ var init_notification_store = __esm({
241789
241952
  read_at timestamptz,
241790
241953
  item jsonb
241791
241954
  )`);
241955
+ await sql.query(`ALTER TABLE "${s2}".notifications ADD COLUMN IF NOT EXISTS chat_session_id text`);
241792
241956
  await sql.query(`CREATE UNIQUE INDEX IF NOT EXISTS notifications_pending_source_uq
241793
241957
  ON "${s2}".notifications (recipient, source_key) WHERE status = 'pending'`);
241794
241958
  await sql.query(`CREATE UNIQUE INDEX IF NOT EXISTS notifications_notice_source_uq
@@ -241801,8 +241965,8 @@ var init_notification_store = __esm({
241801
241965
  const id = `ntf:${(0, import_node_crypto58.randomUUID)()}`;
241802
241966
  const r = await this.sql.query(
241803
241967
  `INSERT INTO ${this.t} (id, recipient, category, source_key, kind, workorder_id, node_id, card_id, actor_id,
241804
- title, workspace_title, entered_at, updated_at, status, read_at, item)
241805
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$12,$13,$14,$15)
241968
+ title, workspace_title, entered_at, updated_at, status, read_at, item, chat_session_id)
241969
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$12,$13,$14,$15,$16)
241806
241970
  ON CONFLICT DO NOTHING
241807
241971
  RETURNING *`,
241808
241972
  [
@@ -241820,7 +241984,8 @@ var init_notification_store = __esm({
241820
241984
  n.enteredAt,
241821
241985
  n.status ?? null,
241822
241986
  n.readAt ?? null,
241823
- n.item ? JSON.stringify(n.item) : null
241987
+ n.item ? JSON.stringify(n.item) : null,
241988
+ n.chatSessionId ?? null
241824
241989
  ]
241825
241990
  );
241826
241991
  if (r.rows[0]) return fromRow(r.rows[0]);
@@ -242472,11 +242637,11 @@ function parseUsageQuery(raw, now) {
242472
242637
  let timeZone = "UTC";
242473
242638
  if (raw.timeZone !== void 0 && raw.timeZone !== null && raw.timeZone !== "") {
242474
242639
  if (typeof raw.timeZone !== "string") throw new UsageQueryError("timeZone \u5FC5\u987B\u662F IANA \u65F6\u533A\u540D");
242475
- timeZone = canonicalTimeZone(raw.timeZone.trim());
242640
+ timeZone = canonicalTimeZone2(raw.timeZone.trim());
242476
242641
  }
242477
242642
  return { range, asOf: new Date(asOfMs).toISOString(), timeZone };
242478
242643
  }
242479
- function canonicalTimeZone(tz) {
242644
+ function canonicalTimeZone2(tz) {
242480
242645
  if (!tz) throw new UsageQueryError("timeZone \u5FC5\u987B\u662F IANA \u65F6\u533A\u540D");
242481
242646
  try {
242482
242647
  return new Intl.DateTimeFormat("en-US", { timeZone: tz }).resolvedOptions().timeZone;
@@ -242995,10 +243160,10 @@ var init_tags = __esm({
242995
243160
  return [...new Set(ids2.map((s2) => String(s2).trim()).filter(Boolean))].filter((id) => known.has(id));
242996
243161
  }
242997
243162
  ensureByName(companyId, name) {
242998
- const clean2 = cleanName(name ?? "");
242999
- if (!clean2) return null;
243000
- const hit = this.entries(companyId).find((t) => nameKey(t.name) === nameKey(clean2));
243001
- return hit ?? this.create(companyId, { name: clean2 });
243163
+ const clean3 = cleanName(name ?? "");
243164
+ if (!clean3) return null;
243165
+ const hit = this.entries(companyId).find((t) => nameKey(t.name) === nameKey(clean3));
243166
+ return hit ?? this.create(companyId, { name: clean3 });
243002
243167
  }
243003
243168
  /** 子类落盘钩子(内存实现为空)。 */
243004
243169
  persist() {
@@ -244383,6 +244548,11 @@ var init_projects = __esm({
244383
244548
  });
244384
244549
 
244385
244550
  // ../server/src/domains/companies/service.ts
244551
+ function assertProfileNameLength(value2, label2) {
244552
+ if (profileNameLength(value2) > PROFILE_NAME_MAX) {
244553
+ throw new CompanyError(400, "NAME_TOO_LONG", `${label2}\u6700\u591A ${PROFILE_NAME_MAX} \u4E2A\u5B57\u7B26`);
244554
+ }
244555
+ }
244386
244556
  function normalizeEmail(raw) {
244387
244557
  const v2 = raw?.trim().toLowerCase() ?? "";
244388
244558
  return v2.length > 0 ? v2 : void 0;
@@ -244396,6 +244566,7 @@ var init_service6 = __esm({
244396
244566
  "../server/src/domains/companies/service.ts"() {
244397
244567
  "use strict";
244398
244568
  import_node_crypto60 = require("node:crypto");
244569
+ init_src();
244399
244570
  init_console_url();
244400
244571
  ROLES = ["owner", "admin", "member"];
244401
244572
  INVITABLE_ROLES = ["admin", "member"];
@@ -244417,8 +244588,11 @@ var init_service6 = __esm({
244417
244588
  now;
244418
244589
  sender;
244419
244590
  consoleUrl;
244591
+ /** 部署默认时区,见 {@link CompaniesServiceOptions.defaultTimeZone}。 */
244592
+ defaultTimeZone;
244420
244593
  constructor(opts) {
244421
244594
  this.store = opts.store;
244595
+ this.defaultTimeZone = opts.defaultTimeZone ?? DEFAULT_TIME_ZONE;
244422
244596
  this.now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
244423
244597
  this.sender = opts.sender;
244424
244598
  this.consoleUrl = opts.consoleUrl;
@@ -244484,12 +244658,47 @@ var init_service6 = __esm({
244484
244658
  await this.store.updateCompany(companyId, { onboardingVariant: variant });
244485
244659
  return { ...company, onboardingVariant: variant };
244486
244660
  }
244661
+ /** 给成员看的公司:存的字段 + 实际生效的时区(含部署默认值回落)+ 部署默认时区。 */
244662
+ view(company) {
244663
+ return { ...company, effectiveTimeZone: effectiveTimeZone(company, this.defaultTimeZone), defaultTimeZone: this.defaultTimeZone };
244664
+ }
244665
+ /**
244666
+ * 这家组织实际生效的时区。组织不存在、或控制面读失败时回落部署默认值并告警——
244667
+ * 调用方是变量到期这类「必须给出一个答案」的判定,读不到组织行不该让派单整个失败。
244668
+ */
244669
+ async effectiveTimeZoneOf(companyId) {
244670
+ if (!companyId) return this.defaultTimeZone;
244671
+ try {
244672
+ return effectiveTimeZone(await this.store.getCompany(companyId), this.defaultTimeZone);
244673
+ } catch (err) {
244674
+ console.warn(`[companies] \u8BFB\u7EC4\u7EC7 ${companyId} \u7684\u65F6\u533A\u5931\u8D25\uFF0C\u6309\u90E8\u7F72\u9ED8\u8BA4 ${this.defaultTimeZone}\uFF1A${err.message}`);
244675
+ return this.defaultTimeZone;
244676
+ }
244677
+ }
244678
+ /**
244679
+ * 设置组织时区(IANA 名,存规范写法);`null` = 清空,回到部署默认值。非法名 400。
244680
+ * 权限(owner/admin)由路由层强制;这里守「组织还可用」(D8.2)。
244681
+ */
244682
+ async setTimeZone(companyId, timeZone) {
244683
+ const canonical2 = timeZone === null ? null : canonicalTimeZone(timeZone);
244684
+ if (timeZone !== null && !canonical2) {
244685
+ throw new CompanyError(400, "INVALID_TIME_ZONE", `timeZone \u4E0D\u662F\u5408\u6CD5\u7684 IANA \u65F6\u533A\u540D\uFF1A${JSON.stringify(timeZone)}`);
244686
+ }
244687
+ const company = await this.requireActiveCompany(companyId);
244688
+ await this.store.updateCompany(companyId, { timeZone: canonical2 });
244689
+ if (canonical2 === null) {
244690
+ const { timeZone: _drop, ...rest } = company;
244691
+ return rest;
244692
+ }
244693
+ return { ...company, timeZone: canonical2 };
244694
+ }
244487
244695
  /**
244488
244696
  * 改团队名称。**只改 name,不动 slug**——slug 是路由键(company:<slug> 与引擎 schema co_<slug>
244489
244697
  * 的一部分,`create` 里注释「建成不可改」),改它会让所有历史链接失效。
244490
244698
  * 校验(非空 / trim)在路由层做完了,这里假设 name 已经是非空 trim 过的字符串。
244491
244699
  */
244492
244700
  async renameCompany(id, name) {
244701
+ assertProfileNameLength(name, "\u56E2\u961F\u7A7A\u95F4\u540D\u79F0");
244493
244702
  const company = await this.requireCompany(id);
244494
244703
  await this.store.updateCompany(id, { name });
244495
244704
  return { ...company, name };
@@ -244608,6 +244817,7 @@ var init_service6 = __esm({
244608
244817
  if (!SLUG_RE.test(slug5)) throw new CompanyError(400, "BAD_SLUG", "slug \u987B\u4E3A URL \u5B89\u5168\u7684\u5C0F\u5199\u4E32\uFF08^[a-z][a-z0-9-]*$\uFF09");
244609
244818
  const name = input.name?.trim() ?? "";
244610
244819
  if (!name) throw new CompanyError(400, "BAD_REQUEST", "\u7F3A\u5C11 name");
244820
+ assertProfileNameLength(name, "\u56E2\u961F\u7A7A\u95F4\u540D\u79F0");
244611
244821
  if (await this.store.getCompanyBySlug(slug5)) throw new CompanyError(409, "SLUG_TAKEN", `slug \u5DF2\u88AB\u5360\u7528\uFF1A${slug5}`);
244612
244822
  const company = {
244613
244823
  id: `company:${slug5}`,
@@ -244800,6 +245010,7 @@ var init_service6 = __esm({
244800
245010
  async setMemberDisplayName(companyId, accountId, displayName3, callerAccountId) {
244801
245011
  const trimmed = displayName3.trim();
244802
245012
  if (!trimmed) throw new CompanyError(400, "BAD_REQUEST", "displayName \u4E0D\u80FD\u4E3A\u7A7A");
245013
+ assertProfileNameLength(trimmed, "\u540D\u5B57");
244803
245014
  await this.requireCompany(companyId);
244804
245015
  const editingSomeoneElse = callerAccountId !== accountId;
244805
245016
  const res = await this.store.updateMemberDisplayName(companyId, accountId, trimmed, {
@@ -245687,7 +245898,7 @@ function isCompanyCrossTenantPath(pathname) {
245687
245898
  return COMPANY_CROSS_TENANT_PREFIXES.some((p2) => pathname === p2 || pathname.startsWith(`${p2}/`));
245688
245899
  }
245689
245900
  function companiesDomain(opts) {
245690
- const { service, currentUser, blobs, resolveActorsCtx, offboarding, agentCredentials } = opts;
245901
+ const { service, currentUser, blobs, resolveActorsCtx, offboarding, agentCredentials, verifyAccountDeletionTicket, assistantOfHuman } = opts;
245691
245902
  const mapErr = (e) => {
245692
245903
  if (e instanceof CompanyError) throw new ApiError(e.status, e.code, e.message, e.details);
245693
245904
  throw e;
@@ -245707,8 +245918,13 @@ function companiesDomain(opts) {
245707
245918
  if (!name) return;
245708
245919
  const { service: actorsSvc } = await resolveActorsCtx(member.companyId);
245709
245920
  const existing = await actorsSvc.getActor(member.actorId);
245710
- if (!existing || existing.name === name) return;
245711
- await actorsSvc.upsertActor({ ...existing, name }, by);
245921
+ if (existing && existing.name !== name) await actorsSvc.upsertActor({ ...existing, name }, by);
245922
+ const bond = assistantOfHuman ? await assistantOfHuman(member.companyId, member.actorId) : null;
245923
+ if (!bond || bond.origin !== "lazy-create") return;
245924
+ const assistant = await actorsSvc.getActor(bond.agentActorId);
245925
+ const assistantName = assistantNameFor(name);
245926
+ if (!assistant || assistant.name === assistantName) return;
245927
+ await actorsSvc.upsertActor({ ...assistant, name: assistantName }, by);
245712
245928
  };
245713
245929
  const requireIfMatch = (req) => {
245714
245930
  const raw = req.raw.headers["if-match"];
@@ -245752,13 +245968,13 @@ function companiesDomain(opts) {
245752
245968
  router.get("/api/companies", async (req) => {
245753
245969
  const me = await whoami(req);
245754
245970
  const items = await service.listForUser(me.accountId);
245755
- return { status: 200, body: { items } };
245971
+ return { status: 200, body: { items: items.map((c) => service.view(c)) } };
245756
245972
  });
245757
245973
  router.get("/api/companies/:id", async (req) => {
245758
245974
  const cid = req.params.id;
245759
245975
  const me = await whoami(req);
245760
245976
  const company = await service.get(cid);
245761
- if (company && await service.getMember(cid, me.accountId)) return { status: 200, body: company };
245977
+ if (company && await service.getMember(cid, me.accountId)) return { status: 200, body: service.view(company) };
245762
245978
  if (company && await service.hasPendingInvitation(cid, me.email)) {
245763
245979
  return { status: 200, body: { id: company.id, name: company.name } };
245764
245980
  }
@@ -245769,8 +245985,9 @@ function companiesDomain(opts) {
245769
245985
  const b2 = req.body ?? {};
245770
245986
  const hasName = b2.name !== void 0;
245771
245987
  const hasVariant = b2.onboardingVariant !== void 0;
245772
- if (!hasName && !hasVariant) {
245773
- throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11\u53EF\u66F4\u65B0\u5B57\u6BB5\uFF08name / onboardingVariant\uFF09");
245988
+ const hasTimeZone = b2.timeZone !== void 0;
245989
+ if (!hasName && !hasVariant && !hasTimeZone) {
245990
+ throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11\u53EF\u66F4\u65B0\u5B57\u6BB5\uFF08name / onboardingVariant / timeZone\uFF09");
245774
245991
  }
245775
245992
  let trimmedName;
245776
245993
  if (hasName) {
@@ -245782,6 +245999,9 @@ function companiesDomain(opts) {
245782
245999
  if (hasVariant && b2.onboardingVariant !== "A" && b2.onboardingVariant !== "B") {
245783
246000
  throw new ApiError(400, "BAD_REQUEST", `onboardingVariant \u987B\u4E3A "A" | "B"`);
245784
246001
  }
246002
+ if (hasTimeZone && b2.timeZone !== null && !isValidTimeZone(b2.timeZone)) {
246003
+ throw new ApiError(400, "INVALID_TIME_ZONE", `timeZone \u987B\u4E3A\u5408\u6CD5\u7684 IANA \u65F6\u533A\u540D\uFF08\u5982 "Asia/Shanghai"\uFF09\uFF0C\u6216 null \u6E05\u7A7A\uFF1A${JSON.stringify(b2.timeZone)}`);
246004
+ }
245785
246005
  await requireOwnerOrAdmin(req, cid);
245786
246006
  let company;
245787
246007
  if (trimmedName !== void 0) {
@@ -245790,7 +246010,10 @@ function companiesDomain(opts) {
245790
246010
  if (hasVariant) {
245791
246011
  company = await service.setOnboardingVariant(cid, b2.onboardingVariant).catch(mapErr);
245792
246012
  }
245793
- return { status: 200, body: company };
246013
+ if (hasTimeZone) {
246014
+ company = await service.setTimeZone(cid, b2.timeZone).catch(mapErr);
246015
+ }
246016
+ return { status: 200, body: service.view(company) };
245794
246017
  });
245795
246018
  router.post("/api/companies", async (req) => {
245796
246019
  const b2 = req.body ?? {};
@@ -245942,6 +246165,16 @@ function companiesDomain(opts) {
245942
246165
  router.delete("/api/accounts/me", async (req) => {
245943
246166
  const off = requireOffboarding();
245944
246167
  const me = await whoami(req);
246168
+ if (!verifyAccountDeletionTicket) {
246169
+ throw new ApiError(501, "NOT_IMPLEMENTED", "\u672C\u90E8\u7F72\u672A\u63A5\u5165\u6CE8\u9500\u90AE\u7BB1\u9A8C\u8BC1\uFF0C\u6682\u4E0D\u80FD\u6CE8\u9500\u8D26\u53F7");
246170
+ }
246171
+ const ticket = req.body?.ticket;
246172
+ if (typeof ticket !== "string" || !ticket) {
246173
+ throw new ApiError(403, "DELETION_VERIFICATION_REQUIRED", "\u6CE8\u9500\u524D\u9700\u8981\u5148\u5B8C\u6210\u90AE\u7BB1\u9A8C\u8BC1\u7801\u9A8C\u8BC1");
246174
+ }
246175
+ if (!await verifyAccountDeletionTicket(me.accountId, ticket)) {
246176
+ throw new ApiError(403, "DELETION_VERIFICATION_INVALID", "\u90AE\u7BB1\u9A8C\u8BC1\u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u83B7\u53D6\u9A8C\u8BC1\u7801");
246177
+ }
245945
246178
  const { soloOwned, memberships } = await service.peekAccountDeletion(me.accountId).catch(mapErr);
245946
246179
  for (const c of soloOwned) {
245947
246180
  await service.dissolveSoloCompanyOnAccountDeletion(c.id, me.accountId).catch(mapErr);
@@ -246140,14 +246373,16 @@ function companiesDomain(opts) {
246140
246373
  });
246141
246374
  };
246142
246375
  }
246143
- var COMPANY_CROSS_TENANT_PREFIXES;
246376
+ var COMPANY_CROSS_TENANT_PREFIXES, assistantNameFor;
246144
246377
  var init_routes6 = __esm({
246145
246378
  "../server/src/domains/companies/routes.ts"() {
246146
246379
  "use strict";
246380
+ init_src();
246147
246381
  init_router();
246148
246382
  init_service6();
246149
246383
  init_image_upload();
246150
246384
  COMPANY_CROSS_TENANT_PREFIXES = ["/api/companies", "/api/invitations", "/api/invite-links", "/api/accounts"];
246385
+ assistantNameFor = (humanName) => `${humanName}\u7684\u52A9\u7406`;
246151
246386
  }
246152
246387
  });
246153
246388
 
@@ -246273,13 +246508,35 @@ var init_agent_credentials = __esm({
246273
246508
  }
246274
246509
  });
246275
246510
 
246511
+ // ../server/src/domains/companies/time-zone.ts
246512
+ function defaultTimeZoneFromEnv(raw) {
246513
+ const v2 = raw?.trim();
246514
+ if (!v2) return DEFAULT_TIME_ZONE;
246515
+ const canonical2 = canonicalTimeZone(v2);
246516
+ if (!canonical2) {
246517
+ throw new Error(
246518
+ `${DEFAULT_TIME_ZONE_ENV}=${JSON.stringify(raw)} \u4E0D\u662F\u5408\u6CD5\u7684 IANA \u65F6\u533A\u540D\uFF08\u4F8B\uFF1AAsia/Shanghai\u3001America/New_York\u3001UTC\uFF09\uFF1B\u4E0D\u8BBE\u5219\u9ED8\u8BA4 ${DEFAULT_TIME_ZONE}\u3002\u62D2\u7EDD\u5E26\u7740\u9519\u8BEF\u65F6\u533A\u542F\u52A8\u3002`
246519
+ );
246520
+ }
246521
+ return canonical2;
246522
+ }
246523
+ var DEFAULT_TIME_ZONE_ENV;
246524
+ var init_time_zone2 = __esm({
246525
+ "../server/src/domains/companies/time-zone.ts"() {
246526
+ "use strict";
246527
+ init_src();
246528
+ DEFAULT_TIME_ZONE_ENV = "OASIS_DEFAULT_TIMEZONE";
246529
+ }
246530
+ });
246531
+
246276
246532
  // ../server/src/domains/companies/index.ts
246277
246533
  function createCompaniesDomain(opts) {
246278
246534
  const service = new CompaniesService({
246279
246535
  store: opts.store,
246280
246536
  ...opts.now !== void 0 ? { now: opts.now } : {},
246281
246537
  ...opts.sender !== void 0 ? { sender: opts.sender } : {},
246282
- ...opts.consoleUrl !== void 0 ? { consoleUrl: opts.consoleUrl } : {}
246538
+ ...opts.consoleUrl !== void 0 ? { consoleUrl: opts.consoleUrl } : {},
246539
+ ...opts.defaultTimeZone !== void 0 ? { defaultTimeZone: opts.defaultTimeZone } : {}
246283
246540
  });
246284
246541
  const currentUser = opts.currentUser ?? makeCurrentUser(opts.store);
246285
246542
  const offboarding = new MemberOffboardingService({
@@ -246325,8 +246582,10 @@ function createCompaniesDomain(opts) {
246325
246582
  offboarding,
246326
246583
  currentUser,
246327
246584
  ...opts.agentCredentials !== void 0 ? { agentCredentials: opts.agentCredentials } : {},
246585
+ ...opts.verifyAccountDeletionTicket !== void 0 ? { verifyAccountDeletionTicket: opts.verifyAccountDeletionTicket } : {},
246328
246586
  ...opts.blobs !== void 0 ? { blobs: opts.blobs } : {},
246329
- ...opts.resolveActorsCtx !== void 0 ? { resolveActorsCtx: opts.resolveActorsCtx } : {}
246587
+ ...opts.resolveActorsCtx !== void 0 ? { resolveActorsCtx: opts.resolveActorsCtx } : {},
246588
+ ...opts.assistantOfHuman !== void 0 ? { assistantOfHuman: opts.assistantOfHuman } : {}
246330
246589
  }),
246331
246590
  currentUser
246332
246591
  };
@@ -246348,6 +246607,7 @@ var init_companies = __esm({
246348
246607
  init_agent_credentials();
246349
246608
  init_routes6();
246350
246609
  init_migrate();
246610
+ init_time_zone2();
246351
246611
  }
246352
246612
  });
246353
246613
 
@@ -246365,6 +246625,14 @@ function parseCookies(header) {
246365
246625
  function looksLikeEmail(s2) {
246366
246626
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s2);
246367
246627
  }
246628
+ function meetsChangePasswordPolicy(pw) {
246629
+ if (pw.length < 8) return false;
246630
+ let classes = 0;
246631
+ if (/[A-Za-z]/.test(pw)) classes++;
246632
+ if (/\d/.test(pw)) classes++;
246633
+ if (/[^A-Za-z0-9]/.test(pw)) classes++;
246634
+ return classes >= 2;
246635
+ }
246368
246636
  function maskEmail(email2) {
246369
246637
  const at2 = email2.indexOf("@");
246370
246638
  if (at2 <= 0) return "***";
@@ -246378,7 +246646,9 @@ function createAuth(opts) {
246378
246646
  const maxAttempts = opts.maxCodeAttempts ?? 5;
246379
246647
  const resendThrottleMs = opts.resendThrottleMs ?? 60 * 1e3;
246380
246648
  const riskFailureThreshold = opts.riskFailureThreshold ?? 3;
246649
+ const deletionTicketTtlMs = opts.deletionTicketTtlMs ?? 10 * 60 * 1e3;
246381
246650
  const { store, sender, jwtSecret } = opts;
246651
+ const deletionTicketSecret = `${jwtSecret}\0account-deletion-ticket`;
246382
246652
  const passwordFailures = /* @__PURE__ */ new Map();
246383
246653
  const sessionCookie = (accountId, tokenVersion) => {
246384
246654
  const t = now();
@@ -246398,38 +246668,46 @@ function createAuth(opts) {
246398
246668
  const tokenVersion = sec?.tokenVersion ?? 0;
246399
246669
  return { status: 200, body: account, setCookie: sessionCookie(account.id, tokenVersion) };
246400
246670
  };
246401
- const sendCode = async (email2) => {
246402
- const existing = await store.getVerificationCode(email2);
246403
- const fresh = existing && Date.parse(existing.createdAt) > now() - resendThrottleMs;
246404
- if (fresh) return;
246671
+ const codeKey = (email2, purpose = "login") => purpose === "login" ? email2 : `${purpose} ${email2}`;
246672
+ const sendCodeDetailed = async (email2, purpose = "login") => {
246673
+ const key2 = codeKey(email2, purpose);
246674
+ const existing = await store.getVerificationCode(key2);
246675
+ const createdAt = existing ? Date.parse(existing.createdAt) : Number.NaN;
246676
+ if (existing && createdAt > now() - resendThrottleMs) {
246677
+ return { sent: false, retryAfterMs: createdAt + resendThrottleMs - now() };
246678
+ }
246405
246679
  const code2 = generateCode();
246406
246680
  const t = now();
246407
246681
  await store.putVerificationCode({
246408
- email: email2,
246682
+ email: key2,
246409
246683
  codeHash: hashCode(code2, jwtSecret),
246410
246684
  createdAt: new Date(t).toISOString(),
246411
246685
  expiresAt: new Date(t + codeTtlMs).toISOString(),
246412
246686
  attempts: 0
246413
246687
  });
246414
246688
  try {
246415
- await sender.sendCode(email2, code2);
246689
+ if (purpose === "login") await sender.sendCode(email2, code2);
246690
+ else await sender.sendCode(email2, code2, purpose);
246416
246691
  } catch (e) {
246417
246692
  console.error(`[auth] \u9A8C\u8BC1\u7801\u53D1\u9001\u5931\u8D25 ${maskEmail(email2)}: ${e instanceof Error ? e.message : String(e)}`);
246418
246693
  }
246694
+ return { sent: true, retryAfterMs: resendThrottleMs };
246419
246695
  };
246420
- const verifyEmailCode = async (email2, code2) => {
246421
- const rec2 = email2 ? await store.getVerificationCode(email2) : null;
246696
+ const sendCode = async (email2, purpose = "login") => (await sendCodeDetailed(email2, purpose)).sent;
246697
+ const verifyEmailCode = async (email2, code2, purpose = "login") => {
246698
+ const key2 = codeKey(email2, purpose);
246699
+ const rec2 = email2 ? await store.getVerificationCode(key2) : null;
246422
246700
  if (!rec2) return "invalid";
246423
246701
  if (Date.parse(rec2.expiresAt) <= now()) {
246424
- await store.deleteVerificationCode(email2);
246702
+ await store.deleteVerificationCode(key2);
246425
246703
  return "expired";
246426
246704
  }
246427
- if (rec2.attempts >= maxAttempts) return "invalid";
246705
+ if (rec2.attempts >= maxAttempts) return "locked";
246428
246706
  if (!code2 || !safeEqualHash(rec2.codeHash, hashCode(code2, jwtSecret))) {
246429
- await store.incrementCodeAttempts(email2);
246707
+ await store.incrementCodeAttempts(key2);
246430
246708
  return "invalid";
246431
246709
  }
246432
- await store.deleteVerificationCode(email2);
246710
+ await store.deleteVerificationCode(key2);
246433
246711
  return "ok";
246434
246712
  };
246435
246713
  const currentAccount = async (headers) => {
@@ -246447,6 +246725,15 @@ function createAuth(opts) {
246447
246725
  async sessionAccountId(headers) {
246448
246726
  return (await currentAccount(headers))?.id ?? null;
246449
246727
  },
246728
+ async verifyAccountDeletionTicket(accountId, ticket) {
246729
+ if (!ticket) return false;
246730
+ const claims = verifySession(ticket, deletionTicketSecret, now());
246731
+ if (!claims || claims.sub !== accountId) return false;
246732
+ const sec = await store.getAccountSecret(accountId);
246733
+ if (!sec || sec.tokenVersion !== claims.tokenVersion) return false;
246734
+ const account = await store.getAccount(accountId);
246735
+ return Boolean(account && mayHoldSession(account));
246736
+ },
246450
246737
  async handle(method, pathname, rawBody, headers) {
246451
246738
  const route2 = pathname.replace(/^\/api\/auth\//, "");
246452
246739
  if (method === "GET" && route2 === "me") {
@@ -246550,6 +246837,59 @@ function createAuth(opts) {
246550
246837
  await store.setAccountPassword(account.id, hashPassword(password));
246551
246838
  return { status: 200, body: { ok: true } };
246552
246839
  }
246840
+ if (route2 === "change-password/request-code") {
246841
+ const account = await currentAccount(headers);
246842
+ if (!account) return { status: 401, body: { error: { code: "UNAUTHENTICATED", message: "\u672A\u767B\u5F55" } } };
246843
+ const email2 = account.email.trim().toLowerCase();
246844
+ const { sent, retryAfterMs: retryAfterMs2 } = await sendCodeDetailed(email2, "change-password");
246845
+ return {
246846
+ status: 202,
246847
+ body: { ok: true, maskedEmail: maskEmail(email2), resent: sent, retryAfterSeconds: Math.max(0, Math.ceil(retryAfterMs2 / 1e3)) }
246848
+ };
246849
+ }
246850
+ if (route2 === "change-password") {
246851
+ const account = await currentAccount(headers);
246852
+ if (!account) return { status: 401, body: { error: { code: "UNAUTHENTICATED", message: "\u672A\u767B\u5F55" } } };
246853
+ const code2 = typeof parsed.code === "string" ? parsed.code.trim() : "";
246854
+ const password = typeof parsed.password === "string" ? parsed.password : "";
246855
+ if (!meetsChangePasswordPolicy(password)) {
246856
+ return { status: 400, body: { error: { code: "WEAK_PASSWORD", message: "\u81F3\u5C11 8 \u4F4D\uFF0C\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u7B26\u53F7\u4E2D\u7684\u81F3\u5C11\u4E24\u7C7B" } } };
246857
+ }
246858
+ if (!/^\d{6}$/.test(code2)) {
246859
+ return { status: 400, body: { error: { code: "BAD_CODE_FORMAT", message: "\u8BF7\u8F93\u5165 6 \u4F4D\u6570\u5B57\u9A8C\u8BC1\u7801" } } };
246860
+ }
246861
+ const verdict = await verifyEmailCode(account.email.trim().toLowerCase(), code2, "change-password");
246862
+ if (verdict !== "ok") {
246863
+ return { status: 401, body: { error: { code: "INVALID_CODE", message: "\u9A8C\u8BC1\u7801\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u83B7\u53D6", reason: verdict } } };
246864
+ }
246865
+ await store.setAccountPassword(account.id, hashPassword(password));
246866
+ return { status: 200, body: { ok: true } };
246867
+ }
246868
+ if (route2 === "account-deletion/request-code") {
246869
+ const account = await currentAccount(headers);
246870
+ if (!account) return { status: 401, body: { error: { code: "UNAUTHENTICATED", message: "\u672A\u767B\u5F55" } } };
246871
+ const email2 = account.email.trim().toLowerCase();
246872
+ const sent = await sendCode(email2, "account-deletion");
246873
+ return { status: 202, body: { ok: true, maskedEmail: maskEmail(email2), resent: sent } };
246874
+ }
246875
+ if (route2 === "account-deletion/verify-code") {
246876
+ const account = await currentAccount(headers);
246877
+ if (!account) return { status: 401, body: { error: { code: "UNAUTHENTICATED", message: "\u672A\u767B\u5F55" } } };
246878
+ const code2 = typeof parsed.code === "string" ? parsed.code.trim() : "";
246879
+ if (!/^\d{6}$/.test(code2)) {
246880
+ return { status: 400, body: { error: { code: "BAD_CODE_FORMAT", message: "\u8BF7\u8F93\u5165 6 \u4F4D\u6570\u5B57\u9A8C\u8BC1\u7801" } } };
246881
+ }
246882
+ const verdict = await verifyEmailCode(account.email.trim().toLowerCase(), code2, "account-deletion");
246883
+ if (verdict !== "ok") {
246884
+ const message = verdict === "expired" ? "\u9A8C\u8BC1\u7801\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u83B7\u53D6" : verdict === "locked" ? "\u9A8C\u8BC1\u7801\u9519\u8BEF\u6B21\u6570\u8FC7\u591A\uFF0C\u8BF7\u91CD\u65B0\u83B7\u53D6" : "\u9A8C\u8BC1\u7801\u9519\u8BEF\u6216\u5DF2\u5931\u6548\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u8BD5";
246885
+ return { status: 401, body: { error: { code: "INVALID_CODE", message, reason: verdict } } };
246886
+ }
246887
+ const sec = await store.getAccountSecret(account.id);
246888
+ const t = now();
246889
+ const exp = t + deletionTicketTtlMs;
246890
+ const ticket = signSession({ sub: account.id, tokenVersion: sec?.tokenVersion ?? 0, iat: t, exp }, deletionTicketSecret);
246891
+ return { status: 200, body: { ticket, expiresAt: new Date(exp).toISOString() } };
246892
+ }
246553
246893
  if (route2 === "logout") {
246554
246894
  return { status: 200, body: { ok: true }, setCookie: clearCookie() };
246555
246895
  }
@@ -246574,8 +246914,8 @@ var init_auth = __esm({
246574
246914
  // ../server/src/auth/sender.ts
246575
246915
  function consoleSender() {
246576
246916
  return {
246577
- async sendCode(email2, code2) {
246578
- console.log(`[auth] \u9A8C\u8BC1\u7801 \u2192 ${email2}: ${code2}`);
246917
+ async sendCode(email2, code2, purpose) {
246918
+ console.log(`[auth] ${purpose && purpose !== "login" ? CODE_PURPOSE_LABEL[purpose] : "\u9A8C\u8BC1\u7801"} \u2192 ${email2}: ${code2}`);
246579
246919
  },
246580
246920
  async sendInvitation(email2, opts) {
246581
246921
  console.log(
@@ -246587,15 +246927,17 @@ function consoleSender() {
246587
246927
  function resendSender(opts) {
246588
246928
  const doFetch = opts.fetchImpl ?? fetch;
246589
246929
  return {
246590
- async sendCode(email2, code2) {
246930
+ async sendCode(email2, code2, purpose) {
246931
+ const deletion = purpose === "account-deletion";
246932
+ const label2 = CODE_PURPOSE_LABEL[purpose ?? "login"];
246591
246933
  const res = await doFetch("https://api.resend.com/emails", {
246592
246934
  method: "POST",
246593
246935
  headers: { authorization: `Bearer ${opts.apiKey}`, "content-type": "application/json" },
246594
246936
  body: JSON.stringify({
246595
246937
  from: opts.from,
246596
246938
  to: [email2],
246597
- subject: "\u4F60\u7684 Oasis \u767B\u5F55\u9A8C\u8BC1\u7801",
246598
- html: `<div style="font-family:sans-serif;max-width:400px;margin:0 auto"><h2>\u767B\u5F55\u9A8C\u8BC1\u7801</h2><p style="font-size:32px;font-weight:bold;letter-spacing:8px;margin:24px 0">${code2}</p><p>\u9A8C\u8BC1\u7801 10 \u5206\u949F\u5185\u6709\u6548\u3002</p><p style="color:#666;font-size:14px">\u5982\u679C\u4E0D\u662F\u4F60\u672C\u4EBA\u64CD\u4F5C\uFF0C\u5FFD\u7565\u6B64\u90AE\u4EF6\u5373\u53EF\u3002</p></div>`
246939
+ subject: `\u4F60\u7684 Oasis ${label2}`,
246940
+ html: `<div style="font-family:sans-serif;max-width:400px;margin:0 auto"><h2>${label2}</h2><p style="font-size:32px;font-weight:bold;letter-spacing:8px;margin:24px 0">${code2}</p><p>\u9A8C\u8BC1\u7801 10 \u5206\u949F\u5185\u6709\u6548\u3002</p>` + (deletion ? `<p style="color:#b42318;font-size:14px">\u4F60\u6B63\u5728\u6CE8\u9500 Oasis \u8D26\u53F7\uFF0C\u6CE8\u9500\u540E\u4E0D\u53EF\u6062\u590D\u3002\u8BF7\u52FF\u628A\u9A8C\u8BC1\u7801\u544A\u8BC9\u4EFB\u4F55\u4EBA\uFF1B\u5982\u679C\u4E0D\u662F\u4F60\u672C\u4EBA\u64CD\u4F5C\uFF0C\u8BF7\u5FFD\u7565\u6B64\u90AE\u4EF6\u5E76\u5C3D\u5FEB\u4FEE\u6539\u5BC6\u7801\u3002</p></div>` : purpose === "change-password" ? `<p style="color:#b42318;font-size:14px">\u4F60\u6B63\u5728\u4FEE\u6539 Oasis \u767B\u5F55\u5BC6\u7801\u3002\u8BF7\u52FF\u628A\u9A8C\u8BC1\u7801\u544A\u8BC9\u4EFB\u4F55\u4EBA\uFF1B\u5982\u679C\u4E0D\u662F\u4F60\u672C\u4EBA\u64CD\u4F5C\uFF0C\u8BF7\u5FFD\u7565\u6B64\u90AE\u4EF6\u5E76\u5C3D\u5FEB\u68C0\u67E5\u8D26\u53F7\u5B89\u5168\u3002</p></div>` : `<p style="color:#666;font-size:14px">\u5982\u679C\u4E0D\u662F\u4F60\u672C\u4EBA\u64CD\u4F5C\uFF0C\u5FFD\u7565\u6B64\u90AE\u4EF6\u5373\u53EF\u3002</p></div>`)
246599
246941
  })
246600
246942
  });
246601
246943
  if (!res.ok) {
@@ -246622,9 +246964,15 @@ function resendSender(opts) {
246622
246964
  }
246623
246965
  };
246624
246966
  }
246967
+ var CODE_PURPOSE_LABEL;
246625
246968
  var init_sender = __esm({
246626
246969
  "../server/src/auth/sender.ts"() {
246627
246970
  "use strict";
246971
+ CODE_PURPOSE_LABEL = {
246972
+ login: "\u767B\u5F55\u9A8C\u8BC1\u7801",
246973
+ "account-deletion": "\u6CE8\u9500\u8D26\u53F7\u9A8C\u8BC1\u7801",
246974
+ "change-password": "\u4FEE\u6539\u5BC6\u7801\u9A8C\u8BC1\u7801"
246975
+ };
246628
246976
  }
246629
246977
  });
246630
246978
 
@@ -249921,7 +250269,7 @@ var init_notification_reconciler = __esm({
249921
250269
  if (!store) return 0;
249922
250270
  const members = new Set(ctx.humans);
249923
250271
  const titleOf = workspaceTitleMap(model);
249924
- const fresh = (iso8) => nowMs - Date.parse(iso8) <= TASK_NOTICE_FRESH_MS;
250272
+ const fresh = (iso9) => nowMs - Date.parse(iso9) <= TASK_NOTICE_FRESH_MS;
249925
250273
  let sent = 0;
249926
250274
  const emit = async (workorderId, kind, rec2, trigger) => {
249927
250275
  const recipients = taskNoticeRecipients(model, workorderId, await loadSnapshot2(workorderId), members, trigger);
@@ -250143,6 +250491,7 @@ function pushThrottleOptionsFromEnv(env = process.env) {
250143
250491
  };
250144
250492
  }
250145
250493
  function shouldPush(record10) {
250494
+ if (record10.category === "todo") return !record10.readAt;
250146
250495
  if (record10.category !== "action") return false;
250147
250496
  if (record10.readAt) return false;
250148
250497
  return record10.status === "pending";
@@ -250458,7 +250807,7 @@ var init_push_notifier = __esm({
250458
250807
  function makeSeededWorkorderCreator(deps) {
250459
250808
  const locks = createKeyedMutex();
250460
250809
  const genWorkspace = deps.genWorkspace ?? (() => `ws:wo-${(0, import_node_crypto66.randomUUID)().slice(0, 8)}`);
250461
- const create = async (input) => {
250810
+ const create2 = async (input) => {
250462
250811
  const company = input.companyId && deps.resolveCompany ? await deps.resolveCompany(input.companyId) : void 0;
250463
250812
  const kernel = company?.kernel ?? deps.kernel;
250464
250813
  const blobs = company?.blobs ?? deps.blobs;
@@ -250546,7 +250895,7 @@ function makeSeededWorkorderCreator(deps) {
250546
250895
  return async (input) => {
250547
250896
  const release = await locks.acquire(`${input.companyId ?? ""}\0${input.idempotencyKey ?? (0, import_node_crypto66.randomUUID)()}`);
250548
250897
  try {
250549
- return await create(input);
250898
+ return await create2(input);
250550
250899
  } finally {
250551
250900
  release();
250552
250901
  }
@@ -254763,6 +255112,496 @@ var init_agent_default_runtime_store = __esm({
254763
255112
  }
254764
255113
  });
254765
255114
 
255115
+ // ../server/src/domains/todos/flag.ts
255116
+ function personalTodoEnabledFromEnv(env = process.env) {
255117
+ const raw = (env["OASIS_PERSONAL_TODO"] ?? "").trim().toLowerCase();
255118
+ return !(raw === "off" || raw === "0" || raw === "false");
255119
+ }
255120
+ var init_flag = __esm({
255121
+ "../server/src/domains/todos/flag.ts"() {
255122
+ "use strict";
255123
+ }
255124
+ });
255125
+
255126
+ // ../server/src/domains/todos/service.ts
255127
+ function assertFirstVersionAssignee(creator, assignee) {
255128
+ if (creator !== assignee) {
255129
+ throw new ApiError(403, "TODO_ASSIGNEE_NOT_SELF", "\u4E2A\u4EBA\u5F85\u529E\u7B2C\u4E00\u7248\u53EA\u80FD\u8BB0\u7ED9\u81EA\u5DF1\uFF1A\u53D1\u8D77\u4EBA\u548C\u627F\u63A5\u4EBA\u5FC5\u987B\u662F\u540C\u4E00\u4E2A\u4EBA\u3002");
255130
+ }
255131
+ }
255132
+ function canAccessTodo(todo, caller) {
255133
+ return isTodoParty(todo, caller.principal);
255134
+ }
255135
+ function newTodoId() {
255136
+ return `td_${(0, import_node_crypto69.randomBytes)(4).toString("hex")}`;
255137
+ }
255138
+ function assertDay(day, what = "\u622A\u6B62\u65E5") {
255139
+ if (typeof day !== "string" || !isValidDay(day)) {
255140
+ throw new ApiError(400, "TODO_INVALID_DATE", `${what}\u987B\u4E3A\u4E0D\u5E26\u65F6\u533A\u7684\u771F\u5B9E\u65E5\u671F YYYY-MM-DD\uFF08\u4F8B\uFF1A2026-10-09\uFF09\uFF0C\u6536\u5230\uFF1A${String(day)}`);
255141
+ }
255142
+ }
255143
+ function assertTime(time3, what = "\u622A\u6B62\u65F6\u523B") {
255144
+ if (typeof time3 !== "string" || !isValidTodoTime(time3)) {
255145
+ throw new ApiError(400, "TODO_INVALID_TIME", `${what}\u987B\u4E3A 24 \u5C0F\u65F6\u5236 HH:mm\uFF08\u4F8B\uFF1A18:00\uFF09\uFF0C\u6536\u5230\uFF1A${String(time3)}`);
255146
+ }
255147
+ }
255148
+ function assertStartBeforeDue(t) {
255149
+ if (!isTodoStartBeforeDue(t)) {
255150
+ const fmt = (d, h) => `${d}${h ? ` ${h}` : ""}`;
255151
+ throw new ApiError(400, "TODO_START_AFTER_DUE", `\u5F00\u59CB\uFF08${fmt(t.startDate, t.startTime)}\uFF09\u4E0D\u80FD\u665A\u4E8E\u622A\u6B62\uFF08${fmt(t.dueDate, t.dueTime)}\uFF09`);
255152
+ }
255153
+ }
255154
+ var import_node_crypto69, TITLE_MAX, NOTE_MAX, REASON_MAX, clean2, TodoService;
255155
+ var init_service10 = __esm({
255156
+ "../server/src/domains/todos/service.ts"() {
255157
+ "use strict";
255158
+ import_node_crypto69 = require("node:crypto");
255159
+ init_src();
255160
+ init_router();
255161
+ TITLE_MAX = 200;
255162
+ NOTE_MAX = 4e3;
255163
+ REASON_MAX = 500;
255164
+ clean2 = (v2) => {
255165
+ const t = v2?.trim();
255166
+ return t ? t : void 0;
255167
+ };
255168
+ TodoService = class {
255169
+ constructor(deps) {
255170
+ this.deps = deps;
255171
+ }
255172
+ nowMs() {
255173
+ return this.deps.now?.() ?? Date.now();
255174
+ }
255175
+ async today() {
255176
+ return todoToday(await this.deps.timeZone(), this.nowMs());
255177
+ }
255178
+ async respond(todo) {
255179
+ const today = await this.today();
255180
+ return { today, todayLine: formatTodayLine(today), todo };
255181
+ }
255182
+ change(todoId, caller, at2, field, oldValue, newValue, reason) {
255183
+ return {
255184
+ id: `tdc_${(0, import_node_crypto69.randomUUID)()}`,
255185
+ todoId,
255186
+ actorId: caller.principal,
255187
+ at: at2,
255188
+ field,
255189
+ ...caller.hand ? { handActorId: caller.hand } : {},
255190
+ ...oldValue !== void 0 ? { oldValue } : {},
255191
+ ...newValue !== void 0 ? { newValue } : {},
255192
+ ...reason ? { reason } : {}
255193
+ };
255194
+ }
255195
+ async create(caller, input) {
255196
+ const title = clean2(input.title);
255197
+ if (!title) throw new ApiError(400, "TODO_INVALID", "title \u4E0D\u80FD\u4E3A\u7A7A");
255198
+ if (title.length > TITLE_MAX) throw new ApiError(400, "TODO_INVALID", `title \u4E0D\u80FD\u8D85\u8FC7 ${TITLE_MAX} \u5B57`);
255199
+ const note = clean2(input.note);
255200
+ if (note && note.length > NOTE_MAX) throw new ApiError(400, "TODO_INVALID", `note \u4E0D\u80FD\u8D85\u8FC7 ${NOTE_MAX} \u5B57`);
255201
+ assertDay(input.dueDate);
255202
+ if (input.dueTime !== void 0) assertTime(input.dueTime);
255203
+ if (input.startDate !== void 0) assertDay(input.startDate, "\u5F00\u59CB\u65E5");
255204
+ if (input.startTime !== void 0) {
255205
+ if (input.startDate === void 0) throw new ApiError(400, "TODO_INVALID", "\u6709\u5F00\u59CB\u65F6\u523B\u5C31\u8981\u6709\u5F00\u59CB\u65E5\uFF08startTime \u987B\u914D startDate\uFF09");
255206
+ assertTime(input.startTime, "\u5F00\u59CB\u65F6\u523B");
255207
+ }
255208
+ assertStartBeforeDue({ ...input });
255209
+ const creator = caller.principal;
255210
+ const assignee = caller.principal;
255211
+ assertFirstVersionAssignee(creator, assignee);
255212
+ const at2 = new Date(this.nowMs()).toISOString();
255213
+ const todo = {
255214
+ id: newTodoId(),
255215
+ title,
255216
+ ...note ? { note } : {},
255217
+ creatorActorId: creator,
255218
+ assigneeActorId: assignee,
255219
+ recordedByActorId: caller.actor,
255220
+ dueDate: input.dueDate,
255221
+ ...input.dueTime ? { dueTime: input.dueTime } : {},
255222
+ ...input.startDate ? { startDate: input.startDate } : {},
255223
+ ...input.startTime ? { startTime: input.startTime } : {},
255224
+ status: "open",
255225
+ ...clean2(input.sourceChatSessionId) ? { sourceChatSessionId: clean2(input.sourceChatSessionId) } : {},
255226
+ createdAt: at2,
255227
+ updatedAt: at2
255228
+ };
255229
+ const saved = await this.deps.store.create(todo, [this.change(todo.id, caller, at2, "created", void 0, title)]);
255230
+ return this.respond(saved);
255231
+ }
255232
+ async list(caller, opts = {}) {
255233
+ const statuses2 = opts.all ? ["open", "done", "cancelled"] : ["open"];
255234
+ const items = (await this.deps.store.listInvolving(caller.principal, statuses2)).filter((t) => canAccessTodo(t, caller));
255235
+ const today = await this.today();
255236
+ return { today, todayLine: formatTodayLine(today), items };
255237
+ }
255238
+ /** 读一条并校验权限:不存在与无权限分开报(无权限 403——任务书口径「其他人 403」)。 */
255239
+ async get(caller, id) {
255240
+ const todo = await this.deps.store.get(id);
255241
+ if (!todo) throw new ApiError(404, "TODO_NOT_FOUND", `\u5F85\u529E ${id} \u4E0D\u5B58\u5728`);
255242
+ if (!canAccessTodo(todo, caller)) throw new ApiError(403, "TODO_FORBIDDEN", "\u65E0\u6743\u67E5\u770B\u6216\u4FEE\u6539\u8FD9\u6761\u5F85\u529E");
255243
+ return todo;
255244
+ }
255245
+ async save(caller, before, next, changes) {
255246
+ if (changes.length === 0) return this.respond(before);
255247
+ const saved = await this.deps.store.update(next, before.updatedAt, changes);
255248
+ if (!saved) throw new ApiError(409, "TODO_CONFLICT", "\u8FD9\u6761\u5F85\u529E\u521A\u88AB\u522B\u5904\u6539\u8FC7\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\u540E\u518D\u6539");
255249
+ return this.respond(saved);
255250
+ }
255251
+ async edit(caller, id, input) {
255252
+ const before = await this.get(caller, id);
255253
+ const reason = clean2(input.reason);
255254
+ if (reason && reason.length > REASON_MAX) throw new ApiError(400, "TODO_INVALID", `reason \u4E0D\u80FD\u8D85\u8FC7 ${REASON_MAX} \u5B57`);
255255
+ const at2 = new Date(this.nowMs()).toISOString();
255256
+ const next = { ...before };
255257
+ const changes = [];
255258
+ const set = (field, value2) => {
255259
+ const old = before[field];
255260
+ if (old === value2) return;
255261
+ if (value2 === void 0) delete next[field];
255262
+ else next[field] = value2;
255263
+ changes.push(this.change(id, caller, at2, field, old, value2, reason));
255264
+ };
255265
+ if (input.title !== void 0) {
255266
+ const title = clean2(input.title);
255267
+ if (!title) throw new ApiError(400, "TODO_INVALID", "title \u4E0D\u80FD\u6E05\u7A7A");
255268
+ if (title.length > TITLE_MAX) throw new ApiError(400, "TODO_INVALID", `title \u4E0D\u80FD\u8D85\u8FC7 ${TITLE_MAX} \u5B57`);
255269
+ set("title", title);
255270
+ }
255271
+ if (input.note !== void 0) {
255272
+ const note = clean2(input.note);
255273
+ if (note && note.length > NOTE_MAX) throw new ApiError(400, "TODO_INVALID", `note \u4E0D\u80FD\u8D85\u8FC7 ${NOTE_MAX} \u5B57`);
255274
+ set("note", note);
255275
+ }
255276
+ if (input.startDate !== void 0) {
255277
+ if (input.startDate === null) {
255278
+ set("startDate", void 0);
255279
+ if (input.startTime === void 0) set("startTime", void 0);
255280
+ } else {
255281
+ assertDay(input.startDate, "\u5F00\u59CB\u65E5");
255282
+ set("startDate", input.startDate);
255283
+ }
255284
+ }
255285
+ if (input.startTime !== void 0) {
255286
+ if (input.startTime !== null) assertTime(input.startTime, "\u5F00\u59CB\u65F6\u523B");
255287
+ set("startTime", input.startTime ?? void 0);
255288
+ }
255289
+ if (input.dueDate !== void 0) {
255290
+ assertDay(input.dueDate);
255291
+ set("dueDate", input.dueDate);
255292
+ }
255293
+ if (input.dueTime !== void 0) {
255294
+ if (input.dueTime !== null) assertTime(input.dueTime);
255295
+ set("dueTime", input.dueTime ?? void 0);
255296
+ }
255297
+ if (input.linkedWorkorderId !== void 0) set("linkedWorkorderId", clean2(input.linkedWorkorderId));
255298
+ if (next.startTime && !next.startDate) throw new ApiError(400, "TODO_INVALID", "\u6709\u5F00\u59CB\u65F6\u523B\u5C31\u8981\u6709\u5F00\u59CB\u65E5\uFF08\u5148\u8BBE startDate\uFF09");
255299
+ assertStartBeforeDue(next);
255300
+ if (changes.length > 0) {
255301
+ next.updatedAt = at2;
255302
+ next.recordedByActorId = caller.actor;
255303
+ }
255304
+ return this.save(caller, before, next, changes);
255305
+ }
255306
+ async setStatus(caller, id, status, reason) {
255307
+ const before = await this.get(caller, id);
255308
+ const why = clean2(reason);
255309
+ if (why && why.length > REASON_MAX) throw new ApiError(400, "TODO_INVALID", `reason \u4E0D\u80FD\u8D85\u8FC7 ${REASON_MAX} \u5B57`);
255310
+ if (before.status === status) return this.respond(before);
255311
+ const at2 = new Date(this.nowMs()).toISOString();
255312
+ const next = { ...before, status, updatedAt: at2, recordedByActorId: caller.actor };
255313
+ delete next.doneAt;
255314
+ delete next.cancelledAt;
255315
+ if (status === "done") next.doneAt = at2;
255316
+ if (status === "cancelled") next.cancelledAt = at2;
255317
+ return this.save(caller, before, next, [this.change(id, caller, at2, "status", before.status, status, why)]);
255318
+ }
255319
+ history(caller, id) {
255320
+ return this.get(caller, id).then(() => this.deps.store.listChanges(id));
255321
+ }
255322
+ };
255323
+ }
255324
+ });
255325
+
255326
+ // ../server/src/domains/todos/store.ts
255327
+ var import_node_crypto70, import_promises15, import_node_path25, MemoryTodoStore;
255328
+ var init_store3 = __esm({
255329
+ "../server/src/domains/todos/store.ts"() {
255330
+ "use strict";
255331
+ import_node_crypto70 = require("node:crypto");
255332
+ import_promises15 = require("node:fs/promises");
255333
+ import_node_path25 = require("node:path");
255334
+ init_src();
255335
+ MemoryTodoStore = class _MemoryTodoStore {
255336
+ constructor(filename) {
255337
+ this.filename = filename;
255338
+ }
255339
+ rows = { todos: {}, changes: [] };
255340
+ tail = Promise.resolve();
255341
+ static async open(filename) {
255342
+ const store = new _MemoryTodoStore(filename);
255343
+ try {
255344
+ store.rows = JSON.parse(await (0, import_promises15.readFile)(filename, "utf8"));
255345
+ } catch (error2) {
255346
+ if (error2.code !== "ENOENT") throw error2;
255347
+ }
255348
+ return store;
255349
+ }
255350
+ write(run) {
255351
+ const pending = this.tail.then(async () => {
255352
+ const next = structuredClone(this.rows);
255353
+ const result = run(next);
255354
+ if (result === null) return null;
255355
+ if (this.filename) {
255356
+ await (0, import_promises15.mkdir)((0, import_node_path25.dirname)(this.filename), { recursive: true });
255357
+ const temp = `${this.filename}.${(0, import_node_crypto70.randomUUID)()}.tmp`;
255358
+ await (0, import_promises15.writeFile)(temp, JSON.stringify(next), "utf8");
255359
+ await (0, import_promises15.rename)(temp, this.filename);
255360
+ }
255361
+ this.rows = next;
255362
+ return structuredClone(result);
255363
+ });
255364
+ this.tail = pending.catch(() => void 0);
255365
+ return pending;
255366
+ }
255367
+ async create(todo, changes) {
255368
+ const saved = await this.write((next) => {
255369
+ if (next.todos[todo.id]) throw new Error(`todo ${todo.id} already exists`);
255370
+ next.todos[todo.id] = structuredClone(todo);
255371
+ next.changes.push(...structuredClone(changes));
255372
+ return todo;
255373
+ });
255374
+ return saved;
255375
+ }
255376
+ async get(id) {
255377
+ await this.tail;
255378
+ return structuredClone(this.rows.todos[id] ?? null);
255379
+ }
255380
+ update(next, expectedUpdatedAt, changes) {
255381
+ return this.write((rows) => {
255382
+ const current = rows.todos[next.id];
255383
+ if (!current || current.updatedAt !== expectedUpdatedAt) return null;
255384
+ rows.todos[next.id] = structuredClone(next);
255385
+ rows.changes.push(...structuredClone(changes));
255386
+ return next;
255387
+ });
255388
+ }
255389
+ async listInvolving(actorId, statuses2) {
255390
+ await this.tail;
255391
+ return Object.values(this.rows.todos).filter((t) => statuses2.includes(t.status) && (t.creatorActorId === actorId || todoAssignees(t).includes(actorId))).sort(compareTodos).map((t) => structuredClone(t));
255392
+ }
255393
+ async listOpenReminderCandidates(fromDay, toDay) {
255394
+ await this.tail;
255395
+ const within = (day) => day !== void 0 && day >= fromDay && day <= toDay;
255396
+ return Object.values(this.rows.todos).filter((t) => t.status === "open" && (within(t.dueDate) || within(t.startDate))).sort(compareTodos).map((t) => structuredClone(t));
255397
+ }
255398
+ async listChanges(todoId) {
255399
+ await this.tail;
255400
+ return this.rows.changes.filter((c) => c.todoId === todoId).map((c) => structuredClone(c));
255401
+ }
255402
+ };
255403
+ }
255404
+ });
255405
+
255406
+ // ../server/src/domains/todos/routes.ts
255407
+ function todosDomain(options) {
255408
+ const wrap = (fn) => async (req) => {
255409
+ if (!options.enabled()) throw new ApiError(404, "NOT_FOUND", "\u4E2A\u4EBA\u5F85\u529E\u672A\u5F00\u542F");
255410
+ try {
255411
+ const service = await options.serviceFor(req.auth.companyId);
255412
+ const caller = await options.resolveCaller(req.auth.actor, req.auth.companyId);
255413
+ return await fn(req, service, caller);
255414
+ } catch (e) {
255415
+ if (e instanceof ZodError) throw new ApiError(400, "TODO_INVALID", e.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; "));
255416
+ throw e;
255417
+ }
255418
+ };
255419
+ const sourceSession = (req, fromBody) => req.auth.dispatch?.chatSessionId ?? fromBody;
255420
+ return (router) => {
255421
+ router.get("/api/todos", wrap(async (req, service, caller) => {
255422
+ const all2 = ["1", "true"].includes((req.query.get("all") ?? "").toLowerCase());
255423
+ return { status: 200, body: await service.list(caller, { all: all2 }) };
255424
+ }));
255425
+ router.post("/api/todos", wrap(async (req, service, caller) => {
255426
+ const body2 = create.parse(req.body ?? {});
255427
+ const sourceChatSessionId = sourceSession(req, body2.sourceChatSessionId);
255428
+ return { status: 201, body: await service.create(caller, { ...body2, ...sourceChatSessionId ? { sourceChatSessionId } : {} }) };
255429
+ }));
255430
+ router.get("/api/todos/:id", wrap(async (req, service, caller) => {
255431
+ const todo = await service.get(caller, req.params.id);
255432
+ return { status: 200, body: { todo, changes: await service.history(caller, todo.id) } };
255433
+ }));
255434
+ router.patch("/api/todos/:id", wrap(async (req, service, caller) => {
255435
+ const body2 = edit.parse(req.body ?? {});
255436
+ return { status: 200, body: await service.edit(caller, req.params.id, body2) };
255437
+ }));
255438
+ const status = (path47, to) => router.post(`/api/todos/:id/${path47}`, wrap(async (req, service, caller) => {
255439
+ const body2 = statusBody.parse(req.body ?? {});
255440
+ return { status: 200, body: await service.setStatus(caller, req.params.id, to, body2.reason) };
255441
+ }));
255442
+ status("done", "done");
255443
+ status("cancel", "cancelled");
255444
+ status("reopen", "open");
255445
+ };
255446
+ }
255447
+ var create, edit, statusBody;
255448
+ var init_routes11 = __esm({
255449
+ "../server/src/domains/todos/routes.ts"() {
255450
+ "use strict";
255451
+ init_zod();
255452
+ init_router();
255453
+ create = external_exports.object({
255454
+ title: external_exports.string(),
255455
+ note: external_exports.string().optional(),
255456
+ dueDate: external_exports.string(),
255457
+ dueTime: external_exports.string().optional(),
255458
+ startDate: external_exports.string().optional(),
255459
+ startTime: external_exports.string().optional(),
255460
+ sourceChatSessionId: external_exports.string().optional()
255461
+ }).strict();
255462
+ edit = external_exports.object({
255463
+ title: external_exports.string().optional(),
255464
+ note: external_exports.string().nullable().optional(),
255465
+ dueDate: external_exports.string().optional(),
255466
+ dueTime: external_exports.string().nullable().optional(),
255467
+ startDate: external_exports.string().nullable().optional(),
255468
+ startTime: external_exports.string().nullable().optional(),
255469
+ linkedWorkorderId: external_exports.string().nullable().optional(),
255470
+ reason: external_exports.string().optional()
255471
+ }).strict();
255472
+ statusBody = external_exports.object({ reason: external_exports.string().optional() }).strict();
255473
+ }
255474
+ });
255475
+
255476
+ // ../server/src/domains/todos/reminders.ts
255477
+ function todoReminderSourceKey(todo, kind) {
255478
+ if (kind === "todo-start") return `todo:${todo.id}@start:${todo.startDate}`;
255479
+ return `todo:${todo.id}@${kind === "todo-due-1d" ? "due-1d" : "due"}:${todo.dueDate}`;
255480
+ }
255481
+ function dueReminders(todo, timeZone, nowMs) {
255482
+ if (todo.status !== "open") return [];
255483
+ const created = Date.parse(todo.createdAt);
255484
+ const dayBefore = zonedDateTimeToUtcMs(shiftDay(todo.dueDate, -1), TODO_REMINDER_TIME, timeZone);
255485
+ const onDay = zonedDateTimeToUtcMs(todo.dueDate, TODO_REMINDER_TIME, timeZone);
255486
+ const dayEnd = zonedStartOfDayMs(shiftDay(todo.dueDate, 1), timeZone);
255487
+ const out = [];
255488
+ if (nowMs >= dayBefore && nowMs < onDay && created <= dayBefore) {
255489
+ out.push({ kind: "todo-due-1d", sourceKey: todoReminderSourceKey(todo, "todo-due-1d"), fireAt: dayBefore });
255490
+ }
255491
+ if (nowMs >= onDay && nowMs < dayEnd && created <= onDay) {
255492
+ out.push({ kind: "todo-due", sourceKey: todoReminderSourceKey(todo, "todo-due"), fireAt: onDay });
255493
+ }
255494
+ if (todo.startDate && todo.startDate !== todo.dueDate) {
255495
+ const startAt = zonedDateTimeToUtcMs(todo.startDate, TODO_REMINDER_TIME, timeZone);
255496
+ const startEnd = zonedStartOfDayMs(shiftDay(todo.startDate, 1), timeZone);
255497
+ if (nowMs >= startAt && nowMs < startEnd && created <= startAt) {
255498
+ out.push({ kind: "todo-start", sourceKey: todoReminderSourceKey(todo, "todo-start"), fireAt: startAt });
255499
+ }
255500
+ }
255501
+ return out;
255502
+ }
255503
+ var TODO_REMINDER_TIME, TODO_REMINDER_SCAN_MS, TODO_REMINDER_TITLE, TodoReminderScanner;
255504
+ var init_reminders = __esm({
255505
+ "../server/src/domains/todos/reminders.ts"() {
255506
+ "use strict";
255507
+ init_src();
255508
+ TODO_REMINDER_TIME = "09:00";
255509
+ TODO_REMINDER_SCAN_MS = 6e4;
255510
+ TODO_REMINDER_TITLE = {
255511
+ "todo-due-1d": (title) => `\u5F85\u529E\u660E\u5929\u5230\u671F\uFF1A${title}`,
255512
+ "todo-due": (title) => `\u5F85\u529E\u4ECA\u5929\u5230\u671F\uFF1A${title}`,
255513
+ "todo-start": (title) => `\u5F85\u529E\u4ECA\u5929\u5F00\u59CB\uFF1A${title}`
255514
+ };
255515
+ TodoReminderScanner = class {
255516
+ constructor(deps) {
255517
+ this.deps = deps;
255518
+ }
255519
+ timer;
255520
+ first;
255521
+ scanning = false;
255522
+ /** 本进程已经确认在台账里的 (公司, 收件人, 来源键):省掉每拍对同一条的重复 insert。重启后靠台账唯一约束兜底。 */
255523
+ seen = /* @__PURE__ */ new Set();
255524
+ /** 扫一遍所有公司。防重入:上一拍没扫完,这一拍直接跳过。返回本拍新写入的条数。 */
255525
+ async sweep() {
255526
+ if (this.scanning || !this.deps.enabled()) return 0;
255527
+ this.scanning = true;
255528
+ let inserted = 0;
255529
+ try {
255530
+ for (const companyId of await this.deps.companies()) {
255531
+ try {
255532
+ inserted += await this.sweepCompany(companyId);
255533
+ } catch (error2) {
255534
+ this.deps.warn(new Error(`\u5F85\u529E\u63D0\u9192 company=${companyId}: ${String(error2)}`));
255535
+ }
255536
+ }
255537
+ } catch (error2) {
255538
+ this.deps.warn(error2);
255539
+ } finally {
255540
+ this.scanning = false;
255541
+ }
255542
+ return inserted;
255543
+ }
255544
+ async sweepCompany(companyId) {
255545
+ const ledger = await this.deps.notifications(companyId);
255546
+ if (!ledger) return 0;
255547
+ const nowMs = this.deps.now?.() ?? Date.now();
255548
+ const timeZone = await this.deps.timeZone(companyId);
255549
+ const today = zonedToday(timeZone, nowMs).date;
255550
+ const candidates = await (await this.deps.todos(companyId)).listOpenReminderCandidates(today, shiftDay(today, 1));
255551
+ const enteredAt = new Date(nowMs).toISOString();
255552
+ const fresh = [];
255553
+ for (const todo of candidates) {
255554
+ for (const r of dueReminders(todo, timeZone, nowMs)) {
255555
+ for (const recipient of todoAssignees(todo)) {
255556
+ const key2 = JSON.stringify([companyId, recipient, r.sourceKey]);
255557
+ if (this.seen.has(key2)) continue;
255558
+ const row = await ledger.insert({
255559
+ recipient,
255560
+ category: "todo",
255561
+ sourceKey: r.sourceKey,
255562
+ kind: r.kind,
255563
+ title: TODO_REMINDER_TITLE[r.kind](todo.title),
255564
+ enteredAt,
255565
+ ...todo.sourceChatSessionId ? { chatSessionId: todo.sourceChatSessionId } : {}
255566
+ });
255567
+ this.seen.add(key2);
255568
+ if (row.enteredAt === enteredAt) fresh.push(row);
255569
+ }
255570
+ }
255571
+ }
255572
+ if (fresh.length > 0) this.deps.onInserted?.(companyId, fresh);
255573
+ return fresh.length;
255574
+ }
255575
+ /** 首拍延后一点(默认 10 秒,与通知对账器同款):等 serve 把推送、台账都装好再扫。 */
255576
+ start(intervalMs = TODO_REMINDER_SCAN_MS, firstDelayMs = 1e4) {
255577
+ if (this.timer) return;
255578
+ this.timer = setInterval(() => void this.sweep(), intervalMs);
255579
+ this.timer.unref?.();
255580
+ this.first = setTimeout(() => void this.sweep(), firstDelayMs);
255581
+ this.first.unref?.();
255582
+ }
255583
+ stop() {
255584
+ if (this.timer) clearInterval(this.timer);
255585
+ if (this.first) clearTimeout(this.first);
255586
+ this.timer = void 0;
255587
+ this.first = void 0;
255588
+ }
255589
+ };
255590
+ }
255591
+ });
255592
+
255593
+ // ../server/src/domains/todos/index.ts
255594
+ var init_todos2 = __esm({
255595
+ "../server/src/domains/todos/index.ts"() {
255596
+ "use strict";
255597
+ init_flag();
255598
+ init_service10();
255599
+ init_store3();
255600
+ init_routes11();
255601
+ init_reminders();
255602
+ }
255603
+ });
255604
+
254766
255605
  // ../server/src/domains/playbooks/registry.ts
254767
255606
  function deriveGraph(structure, filerReviews) {
254768
255607
  const idOf = (n) => `${n.type}:${n.title}`;
@@ -256128,7 +256967,7 @@ function busyError(chatSessionId, blockedBy) {
256128
256967
  return `\u76EE\u6807\u4F1A\u8BDD ${chatSessionId} \u6B63\u6709\u4E00\u8F6E\u5728\u8DD1${blockedBy ? `\uFF08${blockedBy}\uFF09` : ""}\u2014\u2014\u672C\u6B21\u89E6\u53D1\u5DF2\u4E22\u5F03\uFF0C\u4E0D\u6392\u961F\u3001\u4E0D\u8865\u8DD1`;
256129
256968
  }
256130
256969
  function genWebhookToken() {
256131
- return `owt_${(0, import_node_crypto69.randomBytes)(32).toString("base64url")}`;
256970
+ return `owt_${(0, import_node_crypto71.randomBytes)(32).toString("base64url")}`;
256132
256971
  }
256133
256972
  function validateTitleTemplate(tpl) {
256134
256973
  const residue = tpl.replace(DATE_TOKEN, "");
@@ -256184,11 +257023,11 @@ function appendSourceNote(brief, automation, source, atIso) {
256184
257023
  ---
256185
257024
  \u672C\u5DE5\u5355\u7531\u81EA\u52A8\u5316\u300C${automation.name}\u300D\u4E8E ${atIso} \u89E6\u53D1\uFF08\u6765\u6E90\uFF1A${source}\uFF09\u3002\u5F00\u5DE5\u540E\u8BF7\u628A\u5DE5\u5355\u6807\u9898\u6539\u6210\u51C6\u786E\u53CD\u6620\u5B9E\u9645\u5DE5\u4F5C\u7684\u63CF\u8FF0\u3002`;
256186
257025
  }
256187
- var import_node_crypto69, DATE_TOKEN, AutomationsService, EVENT_RETRY_BASE_MS, EVENT_RETRY_MAX_MS, EVENT_RETRY_MAX_ATTEMPTS, eventAttempts, eventLastAttemptAt, eventLastError, CHAT_REPLY_MAX;
256188
- var init_service10 = __esm({
257026
+ var import_node_crypto71, DATE_TOKEN, AutomationsService, EVENT_RETRY_BASE_MS, EVENT_RETRY_MAX_MS, EVENT_RETRY_MAX_ATTEMPTS, eventAttempts, eventLastAttemptAt, eventLastError, CHAT_REPLY_MAX;
257027
+ var init_service11 = __esm({
256189
257028
  "../server/src/domains/automations/service.ts"() {
256190
257029
  "use strict";
256191
- import_node_crypto69 = require("node:crypto");
257030
+ import_node_crypto71 = require("node:crypto");
256192
257031
  init_src();
256193
257032
  init_registry3();
256194
257033
  init_attribution();
@@ -257002,11 +257841,11 @@ function verifySignature(rawBody, headers, secret) {
257002
257841
  if (!secret) return "not_required";
257003
257842
  const given = header1(headers, "x-hub-signature-256");
257004
257843
  if (!given) return "missing";
257005
- const expected = `sha256=${(0, import_node_crypto70.createHmac)("sha256", secret).update(rawBody).digest("hex")}`;
257844
+ const expected = `sha256=${(0, import_node_crypto72.createHmac)("sha256", secret).update(rawBody).digest("hex")}`;
257006
257845
  const a = Buffer.from(given);
257007
257846
  const b2 = Buffer.from(expected);
257008
257847
  if (a.length !== b2.length) return "invalid";
257009
- return (0, import_node_crypto70.timingSafeEqual)(a, b2) ? "valid" : "invalid";
257848
+ return (0, import_node_crypto72.timingSafeEqual)(a, b2) ? "valid" : "invalid";
257010
257849
  }
257011
257850
  function eventAllowed(filters, envelope) {
257012
257851
  if (!filters || filters.length === 0) return true;
@@ -257036,11 +257875,11 @@ function splitEvent(envelope) {
257036
257875
  }
257037
257876
  return { eventName, actionCandidates: candidates };
257038
257877
  }
257039
- var import_node_crypto70, MAX_BODY_BYTES, AutomationWebhookService, header1, PROVIDER_PREFIXES;
257878
+ var import_node_crypto72, MAX_BODY_BYTES, AutomationWebhookService, header1, PROVIDER_PREFIXES;
257040
257879
  var init_webhook = __esm({
257041
257880
  "../server/src/domains/automations/webhook.ts"() {
257042
257881
  "use strict";
257043
- import_node_crypto70 = require("node:crypto");
257882
+ import_node_crypto72 = require("node:crypto");
257044
257883
  MAX_BODY_BYTES = 256 * 1024;
257045
257884
  AutomationWebhookService = class {
257046
257885
  store;
@@ -257914,13 +258753,13 @@ function automationsDomain(opts) {
257914
258753
  };
257915
258754
  }
257916
258755
  var RECENT_LIMIT2, TARGET_ISSUE_MESSAGE;
257917
- var init_routes11 = __esm({
258756
+ var init_routes12 = __esm({
257918
258757
  "../server/src/domains/automations/routes.ts"() {
257919
258758
  "use strict";
257920
258759
  init_src();
257921
258760
  init_templates();
257922
258761
  init_router();
257923
- init_service10();
258762
+ init_service11();
257924
258763
  init_projection2();
257925
258764
  init_scheduler();
257926
258765
  init_registry3();
@@ -258035,11 +258874,11 @@ function createAutomationsDomain(opts) {
258035
258874
  var init_automations = __esm({
258036
258875
  "../server/src/domains/automations/index.ts"() {
258037
258876
  "use strict";
258038
- init_service10();
258877
+ init_service11();
258039
258878
  init_webhook();
258040
- init_routes11();
258041
- init_service10();
258042
- init_routes11();
258879
+ init_routes12();
258880
+ init_service11();
258881
+ init_routes12();
258043
258882
  init_projection2();
258044
258883
  init_webhook();
258045
258884
  init_attribution();
@@ -258192,7 +259031,7 @@ function playbooksDomain(opts) {
258192
259031
  opts.overrides.set(companyOf(req), ref2, nodeKey, nextOverride);
258193
259032
  try {
258194
259033
  await opts.audit?.({
258195
- id: `reg_${(0, import_node_crypto71.randomUUID)()}`,
259034
+ id: `reg_${(0, import_node_crypto73.randomUUID)()}`,
258196
259035
  actor: req.auth.actor,
258197
259036
  kind: "registry_change",
258198
259037
  target: `playbook:${ref2}#${nodeKey}`,
@@ -258209,11 +259048,11 @@ function playbooksDomain(opts) {
258209
259048
  });
258210
259049
  };
258211
259050
  }
258212
- var import_node_crypto71;
258213
- var init_routes12 = __esm({
259051
+ var import_node_crypto73;
259052
+ var init_routes13 = __esm({
258214
259053
  "../server/src/domains/playbooks/routes.ts"() {
258215
259054
  "use strict";
258216
- import_node_crypto71 = require("node:crypto");
259055
+ import_node_crypto73 = require("node:crypto");
258217
259056
  init_router();
258218
259057
  init_registry3();
258219
259058
  init_planner();
@@ -258232,12 +259071,12 @@ function normalize(patch) {
258232
259071
  ...reviewers !== void 0 ? { reviewers: [...new Set(reviewers.map((r) => r.trim()).filter(Boolean))] } : {}
258233
259072
  };
258234
259073
  }
258235
- var import_node_fs22, import_node_path25, SEP3, keyOf2, prefixOf2, MemoryPlaybookOverrideStore, FilePlaybookOverrideStore;
259074
+ var import_node_fs22, import_node_path26, SEP3, keyOf2, prefixOf2, MemoryPlaybookOverrideStore, FilePlaybookOverrideStore;
258236
259075
  var init_overrides = __esm({
258237
259076
  "../server/src/domains/playbooks/overrides.ts"() {
258238
259077
  "use strict";
258239
259078
  import_node_fs22 = __toESM(require("node:fs"), 1);
258240
- import_node_path25 = __toESM(require("node:path"), 1);
259079
+ import_node_path26 = __toESM(require("node:path"), 1);
258241
259080
  SEP3 = "::";
258242
259081
  keyOf2 = (companyId, ref2, nodeKey) => `${companyId}${SEP3}${ref2}${SEP3}${nodeKey}`;
258243
259082
  prefixOf2 = (companyId, ref2) => `${companyId}${SEP3}${ref2}${SEP3}`;
@@ -258293,7 +259132,7 @@ var init_overrides = __esm({
258293
259132
  }
258294
259133
  persist() {
258295
259134
  try {
258296
- import_node_fs22.default.mkdirSync(import_node_path25.default.dirname(this.file), { recursive: true });
259135
+ import_node_fs22.default.mkdirSync(import_node_path26.default.dirname(this.file), { recursive: true });
258297
259136
  const tmp = `${this.file}.tmp`;
258298
259137
  import_node_fs22.default.writeFileSync(tmp, JSON.stringify(Object.fromEntries(this.map), null, 2));
258299
259138
  import_node_fs22.default.renameSync(tmp, this.file);
@@ -258329,9 +259168,9 @@ function createPlaybooksDomain(opts) {
258329
259168
  var init_playbooks = __esm({
258330
259169
  "../server/src/domains/playbooks/index.ts"() {
258331
259170
  "use strict";
258332
- init_routes12();
259171
+ init_routes13();
258333
259172
  init_registry3();
258334
- init_routes12();
259173
+ init_routes13();
258335
259174
  init_overrides();
258336
259175
  }
258337
259176
  });
@@ -258342,17 +259181,17 @@ function completionFingerprint(workspace, members) {
258342
259181
  workspace,
258343
259182
  members: [...members].sort((a, b2) => a.artifactId < b2.artifactId ? -1 : a.artifactId > b2.artifactId ? 1 : 0).map((m2) => ({ artifactId: m2.artifactId, currentRevisionId: m2.currentRevisionId ?? null }))
258344
259183
  });
258345
- return (0, import_node_crypto72.createHash)("sha256").update(canonical2).digest("hex");
259184
+ return (0, import_node_crypto74.createHash)("sha256").update(canonical2).digest("hex");
258346
259185
  }
258347
259186
  function eventConsumptionKey(args) {
258348
259187
  const raw = `project-event:${EVENT_KEY_VERSION}:${args.triggerId}:${args.event}:${args.workspace}:${args.fingerprint}`;
258349
- return raw.length <= 200 ? raw : `project-event:${EVENT_KEY_VERSION}:sha256:${(0, import_node_crypto72.createHash)("sha256").update(raw).digest("hex")}`;
259188
+ return raw.length <= 200 ? raw : `project-event:${EVENT_KEY_VERSION}:sha256:${(0, import_node_crypto74.createHash)("sha256").update(raw).digest("hex")}`;
258350
259189
  }
258351
- var import_node_crypto72, EVENT_KEY_VERSION, EMPTY_METRICS, AUTOMATION_EVENT_HANDLERS, ProjectEventPoller;
259190
+ var import_node_crypto74, EVENT_KEY_VERSION, EMPTY_METRICS, AUTOMATION_EVENT_HANDLERS, ProjectEventPoller;
258352
259191
  var init_project_event_poller = __esm({
258353
259192
  "../server/src/automations/project-event-poller.ts"() {
258354
259193
  "use strict";
258355
- import_node_crypto72 = require("node:crypto");
259194
+ import_node_crypto74 = require("node:crypto");
258356
259195
  EVENT_KEY_VERSION = "v1";
258357
259196
  EMPTY_METRICS = () => ({
258358
259197
  scanDurationMs: 0,
@@ -258562,7 +259401,7 @@ function queryInputReceipt(hub, nodeId, dispatchId, clientMessageKey, pending, t
258562
259401
  const viaSession = hub.hasSession?.(dispatchId) === true;
258563
259402
  const supported = viaSession ? hub.supportsInputReceiptQuery?.(dispatchId) === true : daemonMetaOf(hub, nodeId)?.supportsInputReceiptQuery === true;
258564
259403
  if (!supported) return Promise.resolve({ status: "unknown", reason: "unsupported" });
258565
- const requestId = (0, import_node_crypto73.randomUUID)();
259404
+ const requestId = (0, import_node_crypto75.randomUUID)();
258566
259405
  return new Promise((resolve11) => {
258567
259406
  const timer = setTimeout(() => {
258568
259407
  pending.delete(requestId);
@@ -258624,7 +259463,7 @@ function sendAppendInput(hub, nodeId, dispatchId, entry, input, timeoutMs, sessi
258624
259463
  if (input.requireConsumptionReceipt && !hub.supportsInputConsumptionReceipts?.(dispatchId)) {
258625
259464
  return Promise.resolve({ accepted: false, reason: "consumption-unsupported" });
258626
259465
  }
258627
- const requestId = (0, import_node_crypto73.randomUUID)();
259466
+ const requestId = (0, import_node_crypto75.randomUUID)();
258628
259467
  const sent = routeToSession(hub, nodeId, dispatchId, { type: "append_input", dispatchId, input, requestId });
258629
259468
  if (!sent) return Promise.resolve({ accepted: false, reason: "session-closing", ...unknownIfInterrupting });
258630
259469
  return new Promise((resolve11) => {
@@ -258637,11 +259476,11 @@ function sendAppendInput(hub, nodeId, dispatchId, entry, input, timeoutMs, sessi
258637
259476
  entry.appendWaiters.push({ requestId, requireConsumptionReceipt: input.requireConsumptionReceipt, resolve: resolve11, timer });
258638
259477
  });
258639
259478
  }
258640
- var import_node_crypto73, STASH_TTL_MS, STASH_MAX_FRAMES_PER_ID, STASH_MAX_TOTAL_FRAMES, STASH_MAX_IDS, STASH_LOSS_MAX_IDS, STASH_LOSS_TTL_MS, DaemonHubAdapter, BindingRouterAdapter, WorkdirBridge;
259479
+ var import_node_crypto75, STASH_TTL_MS, STASH_MAX_FRAMES_PER_ID, STASH_MAX_TOTAL_FRAMES, STASH_MAX_IDS, STASH_LOSS_MAX_IDS, STASH_LOSS_TTL_MS, DaemonHubAdapter, BindingRouterAdapter, WorkdirBridge;
258641
259480
  var init_daemon_adapter = __esm({
258642
259481
  "../server/src/daemon-adapter.ts"() {
258643
259482
  "use strict";
258644
- import_node_crypto73 = require("node:crypto");
259483
+ import_node_crypto75 = require("node:crypto");
258645
259484
  init_src();
258646
259485
  STASH_TTL_MS = 5 * 6e4;
258647
259486
  STASH_MAX_FRAMES_PER_ID = 1e4;
@@ -259100,7 +259939,7 @@ var init_daemon_adapter = __esm({
259100
259939
  async spawn(job) {
259101
259940
  const nodeId = job.binding?.nodeId;
259102
259941
  if (!nodeId) throw new Error("DaemonHubAdapter \u9700\u8981 job.binding.nodeId\uFF08\u8DEF\u7531\u9519\u8BEF\uFF09");
259103
- const dispatchId = job.dispatchId ?? `dispatch:${(0, import_node_crypto73.randomUUID)()}`;
259942
+ const dispatchId = job.dispatchId ?? `dispatch:${(0, import_node_crypto75.randomUUID)()}`;
259104
259943
  if (job.dispatchId && this.pending.has(dispatchId)) {
259105
259944
  return this.recover(dispatchId, nodeId, job.binding?.runtimeKind, job.actor);
259106
259945
  }
@@ -259357,7 +260196,7 @@ var init_daemon_adapter = __esm({
259357
260196
  return daemon?.meta.supportsWorkdirWalk === true;
259358
260197
  }
259359
260198
  request(nodeId, buildFrame) {
259360
- const requestId = (0, import_node_crypto73.randomUUID)();
260199
+ const requestId = (0, import_node_crypto75.randomUUID)();
259361
260200
  const sent = this.hub.dispatch(nodeId, buildFrame(requestId));
259362
260201
  if (!sent) return Promise.resolve({ ok: false, code: "NODE_OFFLINE" });
259363
260202
  return new Promise((resolve11) => {
@@ -259415,12 +260254,12 @@ var init_dispatch_fence = __esm({
259415
260254
  });
259416
260255
 
259417
260256
  // ../server/src/side-map.ts
259418
- var import_node_fs23, import_node_path26, FileSideMap;
260257
+ var import_node_fs23, import_node_path27, FileSideMap;
259419
260258
  var init_side_map = __esm({
259420
260259
  "../server/src/side-map.ts"() {
259421
260260
  "use strict";
259422
260261
  import_node_fs23 = __toESM(require("node:fs"), 1);
259423
- import_node_path26 = __toESM(require("node:path"), 1);
260262
+ import_node_path27 = __toESM(require("node:path"), 1);
259424
260263
  FileSideMap = class {
259425
260264
  constructor(file) {
259426
260265
  this.file = file;
@@ -259447,7 +260286,7 @@ var init_side_map = __esm({
259447
260286
  }
259448
260287
  persist() {
259449
260288
  try {
259450
- import_node_fs23.default.mkdirSync(import_node_path26.default.dirname(this.file), { recursive: true });
260289
+ import_node_fs23.default.mkdirSync(import_node_path27.default.dirname(this.file), { recursive: true });
259451
260290
  const tmp = `${this.file}.tmp`;
259452
260291
  import_node_fs23.default.writeFileSync(tmp, JSON.stringify(Object.fromEntries(this.map), null, 2));
259453
260292
  import_node_fs23.default.renameSync(tmp, this.file);
@@ -259465,13 +260304,13 @@ function classifyPage(page, lastPushedHash, serviceAccount) {
259465
260304
  if (sha(body2) === lastPushedHash) return { kind: "echo" };
259466
260305
  return { kind: "human-edit", content: body2, updatedBy: page.updatedBy };
259467
260306
  }
259468
- var import_node_crypto74, sha, enc2, dec, MirrorEngine, MemoryMirrorStateStore, MapMirrorIdentities;
260307
+ var import_node_crypto76, sha, enc2, dec, MirrorEngine, MemoryMirrorStateStore, MapMirrorIdentities;
259469
260308
  var init_engine = __esm({
259470
260309
  "../server/src/mirror/engine.ts"() {
259471
260310
  "use strict";
259472
- import_node_crypto74 = require("node:crypto");
260311
+ import_node_crypto76 = require("node:crypto");
259473
260312
  init_src();
259474
- sha = (s2) => (0, import_node_crypto74.createHash)("sha256").update(s2, "utf8").digest("hex");
260313
+ sha = (s2) => (0, import_node_crypto76.createHash)("sha256").update(s2, "utf8").digest("hex");
259475
260314
  enc2 = (s2) => new TextEncoder().encode(s2);
259476
260315
  dec = (b2) => new TextDecoder().decode(b2);
259477
260316
  MirrorEngine = class {
@@ -259752,11 +260591,11 @@ function humanExitCause(exit3) {
259752
260591
  const base = (exit3.reason ? label2[exit3.reason] : void 0) ?? `\u4F1A\u8BDD\u5F02\u5E38\u7ED3\u675F\uFF08${exit3.reason ?? "\u65E0\u9000\u51FA\u4FE1\u606F"}\uFF09`;
259753
260592
  return exit3.errorMessage ? `${base}\uFF1A${exit3.errorMessage.slice(0, 200)}` : base;
259754
260593
  }
259755
- var import_node_crypto75, AUTONOMOUS_ETHOS, CONVERSATIONAL_ETHOS, SHARED_GRAPH_BODY, AUTONOMOUS_RECOVERY_TOOLS, HIGH_RISK_COMMANDS, REJECTED_PROPOSALS_IN_TASK, COORDINATOR_SYSTEM_PROMPT, CONVERSATIONAL_RECOVERY_TOOLS, CONVERSATIONAL_ESCALATION_TOOLS, CONVERSATIONAL_MANAGER_BODY, MACHINE_EXIT_CODES, CoordinatorWorker;
260594
+ var import_node_crypto77, AUTONOMOUS_ETHOS, CONVERSATIONAL_ETHOS, SHARED_GRAPH_BODY, AUTONOMOUS_RECOVERY_TOOLS, HIGH_RISK_COMMANDS, REJECTED_PROPOSALS_IN_TASK, COORDINATOR_SYSTEM_PROMPT, CONVERSATIONAL_RECOVERY_TOOLS, CONVERSATIONAL_ESCALATION_TOOLS, CONVERSATIONAL_MANAGER_BODY, MACHINE_EXIT_CODES, CoordinatorWorker;
259756
260595
  var init_worker = __esm({
259757
260596
  "../server/src/coordinator/worker.ts"() {
259758
260597
  "use strict";
259759
- import_node_crypto75 = require("node:crypto");
260598
+ import_node_crypto77 = require("node:crypto");
259760
260599
  init_src7();
259761
260600
  init_identity();
259762
260601
  init_escalation_kind();
@@ -260029,7 +260868,7 @@ ${HIGH_RISK_COMMANDS}`;
260029
260868
  return this.deps.kernel.model.lastSeq.get(artifactId) ?? 0;
260030
260869
  }
260031
260870
  evaluationKey(kind, artifactId, evidence) {
260032
- const fingerprint2 = (0, import_node_crypto75.createHash)("sha256").update(JSON.stringify(evidence)).digest("hex");
260871
+ const fingerprint2 = (0, import_node_crypto77.createHash)("sha256").update(JSON.stringify(evidence)).digest("hex");
260033
260872
  return `${kind}:${artifactId}:${fingerprint2}`;
260034
260873
  }
260035
260874
  artifactEvidence(id) {
@@ -260420,8 +261259,8 @@ ${ctx.nodeFault}
260420
261259
  const taskWithRejections = rejectedSection ? `${task}
260421
261260
 
260422
261261
  ${rejectedSection}` : task;
260423
- const runtimeSessionId = opts?.resumeSessionId ?? (0, import_node_crypto75.randomUUID)();
260424
- const coordinatorRunId = `coordinate:${(0, import_node_crypto75.randomUUID)()}`;
261262
+ const runtimeSessionId = opts?.resumeSessionId ?? (0, import_node_crypto77.randomUUID)();
261263
+ const coordinatorRunId = `coordinate:${(0, import_node_crypto77.randomUUID)()}`;
260425
261264
  const taskWithContext = coordinatorContext ? [
260426
261265
  taskWithRejections,
260427
261266
  ``,
@@ -260465,7 +261304,7 @@ ${rejectedSection}` : task;
260465
261304
  const startedAtMs = Date.now();
260466
261305
  let trajectoryStarted = false;
260467
261306
  if (this.deps.trajectory) {
260468
- const bundleManifest = Object.fromEntries(Object.entries(job.bundle.files).map(([name, content3]) => [name, (0, import_node_crypto75.createHash)("sha256").update(content3).digest("hex")]));
261307
+ const bundleManifest = Object.fromEntries(Object.entries(job.bundle.files).map(([name, content3]) => [name, (0, import_node_crypto77.createHash)("sha256").update(content3).digest("hex")]));
260469
261308
  const session = {
260470
261309
  runId: coordinatorRunId,
260471
261310
  runtimeSessionId,
@@ -260676,11 +261515,11 @@ var init_pg_ident = __esm({
260676
261515
  });
260677
261516
 
260678
261517
  // ../storage/src/postgres.ts
260679
- var import_node_crypto76, ident5, isUniqueViolation, PostgresOplogStore, PostgresBlobStore;
261518
+ var import_node_crypto78, ident5, isUniqueViolation, PostgresOplogStore, PostgresBlobStore;
260680
261519
  var init_postgres = __esm({
260681
261520
  "../storage/src/postgres.ts"() {
260682
261521
  "use strict";
260683
- import_node_crypto76 = require("node:crypto");
261522
+ import_node_crypto78 = require("node:crypto");
260684
261523
  init_pg_ident();
260685
261524
  init_esm();
260686
261525
  init_src();
@@ -260860,7 +261699,7 @@ var init_postgres = __esm({
260860
261699
  return new _PostgresBlobStore(pool, schema);
260861
261700
  }
260862
261701
  async put(bytes2) {
260863
- const hash3 = (0, import_node_crypto76.createHash)("sha256").update(bytes2).digest("hex");
261702
+ const hash3 = (0, import_node_crypto78.createHash)("sha256").update(bytes2).digest("hex");
260864
261703
  await this.pool.query(
260865
261704
  `INSERT INTO ${this.t} (hash, bytes, size, content_type) VALUES ($1, $2, $3, $4) ON CONFLICT (hash) DO NOTHING`,
260866
261705
  [hash3, Buffer.from(bytes2), bytes2.byteLength, sniffContentType(bytes2) ?? null]
@@ -260908,7 +261747,7 @@ function stageAtLeast(stage, min2) {
260908
261747
  return STAGES.indexOf(stage) >= STAGES.indexOf(min2);
260909
261748
  }
260910
261749
  function newVariableId() {
260911
- return `var_${(0, import_node_crypto77.randomUUID)()}`;
261750
+ return `var_${(0, import_node_crypto79.randomUUID)()}`;
260912
261751
  }
260913
261752
  async function declareRecordsAware(db) {
260914
261753
  await db.query(`SELECT set_config('${RECORDS_AWARE_SETTING}', 'on', true)`);
@@ -261264,11 +262103,11 @@ async function listBindingTargetIds(db, s2, kind) {
261264
262103
  [kind]
261265
262104
  )).rows.map((x2) => x2.target_id);
261266
262105
  }
261267
- var import_node_crypto77, STAGES, RECORDS_AWARE_SETTING, markLockName;
262106
+ var import_node_crypto79, STAGES, RECORDS_AWARE_SETTING, markLockName;
261268
262107
  var init_variable_bindings = __esm({
261269
262108
  "../storage/src/variable-bindings.ts"() {
261270
262109
  "use strict";
261271
- import_node_crypto77 = require("node:crypto");
262110
+ import_node_crypto79 = require("node:crypto");
261272
262111
  init_src();
261273
262112
  STAGES = ["off", "dual", "read", "records"];
261274
262113
  RECORDS_AWARE_SETTING = "oasis.variable_records_aware";
@@ -264020,9 +264859,11 @@ var init_postgres_control_plane = __esm({
264020
264859
  avatar text, -- "blob:<hash>"\uFF0C\u7A7A=\u9ED8\u8BA4
264021
264860
  status text NOT NULL, -- active | suspended
264022
264861
  created_at timestamptz NOT NULL,
264023
- onboarding_variant text -- \u767B\u5F55\u540E\u52A9\u7406\u5F15\u5BFC\u53D8\u4F53 A|B\uFF1BNULL=\u7F3A\u7701\u89C6\u4E3A A
264862
+ onboarding_variant text, -- \u767B\u5F55\u540E\u52A9\u7406\u5F15\u5BFC\u53D8\u4F53 A|B\uFF1BNULL=\u7F3A\u7701\u89C6\u4E3A A
264863
+ time_zone text -- \u7EC4\u7EC7\u65F6\u533A IANA \u540D\uFF1BNULL=\u6309\u90E8\u7F72\u9ED8\u8BA4\u503C
264024
264864
  )`);
264025
264865
  await pool.query(`ALTER TABLE "${s2}".companies ADD COLUMN IF NOT EXISTS onboarding_variant text`);
264866
+ await pool.query(`ALTER TABLE "${s2}".companies ADD COLUMN IF NOT EXISTS time_zone text`);
264026
264867
  await pool.query(`ALTER TABLE "${s2}".companies ADD COLUMN IF NOT EXISTS dissolved_at timestamptz`);
264027
264868
  await pool.query(`ALTER TABLE "${s2}".companies ADD COLUMN IF NOT EXISTS dissolved_by text`);
264028
264869
  await pool.query(`
@@ -264231,8 +265072,8 @@ var init_postgres_control_plane = __esm({
264231
265072
  // —— 公司 ——
264232
265073
  async createCompany(c) {
264233
265074
  await this.pool.query(
264234
- `INSERT INTO ${this.s}.companies (id, slug, name, avatar, status, created_at, onboarding_variant) VALUES ($1,$2,$3,$4,$5,$6,$7)`,
264235
- [c.id, c.slug, c.name, c.avatar ?? null, c.status, c.createdAt, c.onboardingVariant ?? null]
265075
+ `INSERT INTO ${this.s}.companies (id, slug, name, avatar, status, created_at, onboarding_variant, time_zone) VALUES ($1,$2,$3,$4,$5,$6,$7,$8)`,
265076
+ [c.id, c.slug, c.name, c.avatar ?? null, c.status, c.createdAt, c.onboardingVariant ?? null, c.timeZone ?? null]
264236
265077
  );
264237
265078
  }
264238
265079
  async getCompany(id) {
@@ -264266,6 +265107,10 @@ var init_postgres_control_plane = __esm({
264266
265107
  args.push(patch.onboardingVariant);
264267
265108
  sets.push(`onboarding_variant = $${args.length}`);
264268
265109
  }
265110
+ if (patch.timeZone !== void 0) {
265111
+ args.push(patch.timeZone);
265112
+ sets.push(`time_zone = $${args.length}`);
265113
+ }
264269
265114
  if (patch.dissolvedAt !== void 0) {
264270
265115
  args.push(patch.dissolvedAt);
264271
265116
  sets.push(`dissolved_at = $${args.length}`);
@@ -265274,6 +266119,7 @@ var init_postgres_control_plane = __esm({
265274
266119
  status: row.status,
265275
266120
  createdAt: new Date(row.created_at).toISOString(),
265276
266121
  ...row.onboarding_variant ? { onboardingVariant: row.onboarding_variant } : {},
266122
+ ...row.time_zone ? { timeZone: row.time_zone } : {},
265277
266123
  // 解散痕迹(组织三层身份 ADR D9.2):两列一起有、一起空。普通运维停用不写它们。
265278
266124
  ...row.dissolved_at != null ? { dissolvedAt: toIso(row.dissolved_at) } : {},
265279
266125
  ...row.dissolved_by != null ? { dissolvedBy: row.dissolved_by } : {}
@@ -271571,11 +272417,11 @@ var init_postgres_delegations = __esm({
271571
272417
  });
271572
272418
 
271573
272419
  // ../storage/src/postgres-nodes.ts
271574
- var import_node_crypto78, ident14, PostgresNodeStore, PostgresNodeTokenStore, rowToNode, rowToFault, rowToQuota, rowToRuntime;
272420
+ var import_node_crypto80, ident14, PostgresNodeStore, PostgresNodeTokenStore, rowToNode, rowToFault, rowToQuota, rowToRuntime;
271575
272421
  var init_postgres_nodes = __esm({
271576
272422
  "../storage/src/postgres-nodes.ts"() {
271577
272423
  "use strict";
271578
- import_node_crypto78 = require("node:crypto");
272424
+ import_node_crypto80 = require("node:crypto");
271579
272425
  init_pg_ident();
271580
272426
  init_esm();
271581
272427
  ident14 = (s2) => {
@@ -271929,7 +272775,7 @@ var init_postgres_nodes = __esm({
271929
272775
  return store;
271930
272776
  }
271931
272777
  issue(nodeId) {
271932
- const token2 = `ont_${(0, import_node_crypto78.randomBytes)(24).toString("base64url")}`;
272778
+ const token2 = `ont_${(0, import_node_crypto80.randomBytes)(24).toString("base64url")}`;
271933
272779
  this.cache.set(token2, nodeId);
271934
272780
  void this.pool.query(`INSERT INTO ${this.s}.node_tokens (token,node_id) VALUES ($1,$2)`, [token2, nodeId]);
271935
272781
  return token2;
@@ -272231,11 +273077,11 @@ var init_postgres_type_registry = __esm({
272231
273077
  });
272232
273078
 
272233
273079
  // ../storage/src/postgres-actor-memory.ts
272234
- var import_node_crypto79, matchClause, ident16, PostgresActorMemoryStore, rowToIndexEntry, rowToRecord;
273080
+ var import_node_crypto81, matchClause, ident16, PostgresActorMemoryStore, rowToIndexEntry, rowToRecord;
272235
273081
  var init_postgres_actor_memory = __esm({
272236
273082
  "../storage/src/postgres-actor-memory.ts"() {
272237
273083
  "use strict";
272238
- import_node_crypto79 = require("node:crypto");
273084
+ import_node_crypto81 = require("node:crypto");
272239
273085
  init_pg_ident();
272240
273086
  init_src();
272241
273087
  matchClause = (q2) => q2.requireMatch && q2.keywords.length > 0 ? " WHERE m > 0" : "";
@@ -272445,7 +273291,7 @@ var init_postgres_actor_memory = __esm({
272445
273291
  }
272446
273292
  async write(input, now) {
272447
273293
  if (input.memId === void 0) {
272448
- const memId = `mem:${(0, import_node_crypto79.randomUUID)()}`;
273294
+ const memId = `mem:${(0, import_node_crypto81.randomUUID)()}`;
272449
273295
  const r2 = await this.pool.query(
272450
273296
  `INSERT INTO ${this.s}.actor_memories
272451
273297
  (mem_id, actor_id, project_id, keywords, content, version, created_at, updated_at, accessed_at, source_artifact_id, source_session_id)
@@ -274016,14 +274862,14 @@ function companySchemaName(companyId) {
274016
274862
  if (typeof companyId !== "string" || !companyId.trim()) {
274017
274863
  throw new Error("company schema mapping \u9700\u8981\u975E\u7A7A companyId\uFF08fail-closed\uFF1A\u4E0D\u56DE\u9000\u9ED8\u8BA4\u516C\u53F8\uFF09");
274018
274864
  }
274019
- const hex = (0, import_node_crypto80.createHash)("sha256").update(companyId, "utf8").digest("hex");
274865
+ const hex = (0, import_node_crypto82.createHash)("sha256").update(companyId, "utf8").digest("hex");
274020
274866
  return `${COMPANY_SCHEMA_PREFIX}${hex.slice(0, DIGEST_HEX_LEN)}`;
274021
274867
  }
274022
- var import_node_crypto80, COMPANY_SCHEMA_PREFIX, DIGEST_HEX_LEN, SchemaMappingConflictError, TenantStorageLocationStore;
274868
+ var import_node_crypto82, COMPANY_SCHEMA_PREFIX, DIGEST_HEX_LEN, SchemaMappingConflictError, TenantStorageLocationStore;
274023
274869
  var init_tenant_schema_mapping = __esm({
274024
274870
  "../storage/src/tenant-schema-mapping.ts"() {
274025
274871
  "use strict";
274026
- import_node_crypto80 = require("node:crypto");
274872
+ import_node_crypto82 = require("node:crypto");
274027
274873
  init_esm();
274028
274874
  init_pg_ident();
274029
274875
  COMPANY_SCHEMA_PREFIX = "c_";
@@ -274097,7 +274943,7 @@ var init_tenant_schema_mapping = __esm({
274097
274943
 
274098
274944
  // ../storage/src/company-data-plane-schema.ts
274099
274945
  function canonicalTypeDefHash(def) {
274100
- return `sha256:${(0, import_node_crypto81.createHash)("sha256").update(JSON.stringify(def), "utf8").digest("hex")}`;
274946
+ return `sha256:${(0, import_node_crypto83.createHash)("sha256").update(JSON.stringify(def), "utf8").digest("hex")}`;
274101
274947
  }
274102
274948
  function defaultArtifactTypeTemplate() {
274103
274949
  return [
@@ -274279,11 +275125,11 @@ async function seedDefaultArtifactTypes(pool, schema, templates = defaultArtifac
274279
275125
  }
274280
275126
  return { inserted };
274281
275127
  }
274282
- var import_node_crypto81, DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION;
275128
+ var import_node_crypto83, DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION;
274283
275129
  var init_company_data_plane_schema = __esm({
274284
275130
  "../storage/src/company-data-plane-schema.ts"() {
274285
275131
  "use strict";
274286
- import_node_crypto81 = require("node:crypto");
275132
+ import_node_crypto83 = require("node:crypto");
274287
275133
  init_esm();
274288
275134
  init_pg_ident();
274289
275135
  DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION = "4c368e5c-types-v1";
@@ -274362,11 +275208,11 @@ function rowToFile2(row) {
274362
275208
  updatedAt: new Date(row.updated_at).toISOString()
274363
275209
  };
274364
275210
  }
274365
- var import_node_crypto82, COMPANY_CONFLICT_MSG, CompanyIdentifierConflictError, CompanyOwnedRegistryStore;
275211
+ var import_node_crypto84, COMPANY_CONFLICT_MSG, CompanyIdentifierConflictError, CompanyOwnedRegistryStore;
274366
275212
  var init_company_owned_registry_store = __esm({
274367
275213
  "../storage/src/company-owned-registry-store.ts"() {
274368
275214
  "use strict";
274369
- import_node_crypto82 = require("node:crypto");
275215
+ import_node_crypto84 = require("node:crypto");
274370
275216
  init_esm();
274371
275217
  init_src();
274372
275218
  init_pg_ident();
@@ -274751,7 +275597,7 @@ var init_company_owned_registry_store = __esm({
274751
275597
  enabled: skill.enabled,
274752
275598
  files: files.map((f2) => ({ path: f2.path, blobHash: f2.blobHash }))
274753
275599
  });
274754
- return (0, import_node_crypto82.createHash)("sha256").update(body2).digest("hex");
275600
+ return (0, import_node_crypto84.createHash)("sha256").update(body2).digest("hex");
274755
275601
  }
274756
275602
  };
274757
275603
  }
@@ -277561,7 +278407,7 @@ function sourceSetConservation(input) {
277561
278407
  return input.sourceFinal === input.copied + input.approvedRuntimeConversion + input.approvedArchive + input.approvedDiscard;
277562
278408
  }
277563
278409
  function payloadChecksum(value2) {
277564
- return (0, import_node_crypto83.createHash)("sha256").update(JSON.stringify(value2)).digest("hex");
278410
+ return (0, import_node_crypto85.createHash)("sha256").update(JSON.stringify(value2)).digest("hex");
277565
278411
  }
277566
278412
  async function copyWorkorderDraftsIdempotent(pool, companyId, companySchema, manifestId, opts) {
277567
278413
  const cs = quoteIdent2(companySchema);
@@ -277953,11 +278799,11 @@ async function copyTraceFamilyIdempotent(pool, companyId, companySchema, manifes
277953
278799
  sequences
277954
278800
  };
277955
278801
  }
277956
- var import_node_crypto83, MissingInstallerActorsError, TypeCatalogNotCoveredError, ORG_REGISTRY_TABLES, TRACE_FAMILY;
278802
+ var import_node_crypto85, MissingInstallerActorsError, TypeCatalogNotCoveredError, ORG_REGISTRY_TABLES, TRACE_FAMILY;
277957
278803
  var init_tenant_migration_operator = __esm({
277958
278804
  "../storage/src/tenant-migration-operator.ts"() {
277959
278805
  "use strict";
277960
- import_node_crypto83 = require("node:crypto");
278806
+ import_node_crypto85 = require("node:crypto");
277961
278807
  init_esm();
277962
278808
  init_pg_ident();
277963
278809
  init_tenancy_attribution();
@@ -278193,8 +279039,8 @@ var init_tenant_new_company_provision = __esm({
278193
279039
  // ../storage/src/knowledge-snapshot-crypto.ts
278194
279040
  function sealSnapshot(snapshot, key2) {
278195
279041
  if (snapshot.content.startsWith(PREFIX3)) return structuredClone(snapshot);
278196
- const iv = (0, import_node_crypto84.randomBytes)(12);
278197
- const cipher = (0, import_node_crypto84.createCipheriv)("aes-256-gcm", bytes(key2), iv);
279042
+ const iv = (0, import_node_crypto86.randomBytes)(12);
279043
+ const cipher = (0, import_node_crypto86.createCipheriv)("aes-256-gcm", bytes(key2), iv);
278198
279044
  cipher.setAAD(aad2(snapshot));
278199
279045
  const encrypted = Buffer.concat([cipher.update(snapshot.content, "utf8"), cipher.final()]);
278200
279046
  return { ...structuredClone(snapshot), content: `${PREFIX3}${iv.toString("base64url")}.${cipher.getAuthTag().toString("base64url")}.${encrypted.toString("base64url")}` };
@@ -278204,7 +279050,7 @@ function openSnapshot(snapshot, key2) {
278204
279050
  const packed = snapshot.content.slice(PREFIX3.length).split(".");
278205
279051
  if (packed.length !== 3 || packed.some((part) => !part)) throw new Error(`knowledge snapshot ${snapshot.ref} ciphertext is malformed`);
278206
279052
  const [iv, tag, data] = packed;
278207
- const decipher = (0, import_node_crypto84.createDecipheriv)("aes-256-gcm", bytes(key2), Buffer.from(iv, "base64url"));
279053
+ const decipher = (0, import_node_crypto86.createDecipheriv)("aes-256-gcm", bytes(key2), Buffer.from(iv, "base64url"));
278208
279054
  decipher.setAAD(aad2(snapshot));
278209
279055
  decipher.setAuthTag(Buffer.from(tag, "base64url"));
278210
279056
  return {
@@ -278212,11 +279058,11 @@ function openSnapshot(snapshot, key2) {
278212
279058
  content: Buffer.concat([decipher.update(Buffer.from(data, "base64url")), decipher.final()]).toString("utf8")
278213
279059
  };
278214
279060
  }
278215
- var import_node_crypto84, PREFIX3, bytes, aad2;
279061
+ var import_node_crypto86, PREFIX3, bytes, aad2;
278216
279062
  var init_knowledge_snapshot_crypto = __esm({
278217
279063
  "../storage/src/knowledge-snapshot-crypto.ts"() {
278218
279064
  "use strict";
278219
- import_node_crypto84 = require("node:crypto");
279065
+ import_node_crypto86 = require("node:crypto");
278220
279066
  PREFIX3 = "oasis-knowledge:v1:";
278221
279067
  bytes = (key2) => {
278222
279068
  const value2 = Buffer.from(key2);
@@ -278965,6 +279811,9 @@ var init_default_company_schema_move = __esm({
278965
279811
  "review_requirements",
278966
279812
  "review_view",
278967
279813
  "reviews",
279814
+ // 个人待办与它的变更记录(2026-10-08 落库,ADR 0635):每行锚在本公司成员与会话上,整表搬(与 discussion_entries 同理)。
279815
+ "todo_changes",
279816
+ "todos",
278968
279817
  "work_artifacts",
278969
279818
  "work_inputs",
278970
279819
  "work_order_files",
@@ -279111,7 +279960,7 @@ function reportedCost(v2) {
279111
279960
  }
279112
279961
  function newUsageBackfillManifestId(now = /* @__PURE__ */ new Date()) {
279113
279962
  const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
279114
- return `ulb-${stamp}-${(0, import_node_crypto85.randomBytes)(3).toString("hex")}`;
279963
+ return `ulb-${stamp}-${(0, import_node_crypto87.randomBytes)(3).toString("hex")}`;
279115
279964
  }
279116
279965
  async function backfillUsageLedger(ledger, opts) {
279117
279966
  if (!opts.dryRun && !opts.manifestId) throw new Error("execute \u9700\u8981 manifestId");
@@ -279660,11 +280509,11 @@ async function audit(c, s2, manifestId, runId, field, before, after, createdAt)
279660
280509
  [manifestId, runId, field, before == null ? null : JSON.stringify(before), after == null ? null : JSON.stringify(after), createdAt]
279661
280510
  );
279662
280511
  }
279663
- var import_node_crypto85, BUCKETS, ENDED_SQL, START_EVIDENCE_SQL, CHAT_RUN_SQL, ALLOWED_MODEL_EVIDENCE_TIERS, emptyStep, MODEL_BACKFILL_SOURCE, fourOf, sameUsage, ChatRunLookup;
280512
+ var import_node_crypto87, BUCKETS, ENDED_SQL, START_EVIDENCE_SQL, CHAT_RUN_SQL, ALLOWED_MODEL_EVIDENCE_TIERS, emptyStep, MODEL_BACKFILL_SOURCE, fourOf, sameUsage, ChatRunLookup;
279664
280513
  var init_usage_ledger_backfill = __esm({
279665
280514
  "../storage/src/usage-ledger-backfill.ts"() {
279666
280515
  "use strict";
279667
- import_node_crypto85 = require("node:crypto");
280516
+ import_node_crypto87 = require("node:crypto");
279668
280517
  init_src();
279669
280518
  init_pg_ident();
279670
280519
  init_postgres_trace();
@@ -280049,26 +280898,26 @@ var init_cutover_aware_workorder_drafts = __esm({
280049
280898
 
280050
280899
  // ../server/src/governance/builtin-skills.ts
280051
280900
  async function collectDir(root, dir, out) {
280052
- const entries = await (0, import_promises15.readdir)(dir, { withFileTypes: true });
280901
+ const entries = await (0, import_promises16.readdir)(dir, { withFileTypes: true });
280053
280902
  for (const e of entries) {
280054
- const abs = (0, import_node_path27.join)(dir, e.name);
280903
+ const abs = (0, import_node_path28.join)(dir, e.name);
280055
280904
  if (e.isDirectory()) {
280056
280905
  await collectDir(root, abs, out);
280057
280906
  } else if (e.isFile()) {
280058
- const info = await (0, import_promises15.stat)(abs);
280907
+ const info = await (0, import_promises16.stat)(abs);
280059
280908
  if (info.size > MAX_FILE_BYTES2) {
280060
280909
  console.warn(`[builtin-skills] skip oversized file (${info.size}B): ${abs}`);
280061
280910
  continue;
280062
280911
  }
280063
- const rel = (0, import_node_path27.relative)(root, abs).split(/[\\/]/).join("/");
280064
- out[rel] = await (0, import_promises15.readFile)(abs, "utf8");
280912
+ const rel = (0, import_node_path28.relative)(root, abs).split(/[\\/]/).join("/");
280913
+ out[rel] = await (0, import_promises16.readFile)(abs, "utf8");
280065
280914
  }
280066
280915
  }
280067
280916
  }
280068
280917
  async function loadBuiltinSkills(skillsDir) {
280069
280918
  let dirents;
280070
280919
  try {
280071
- dirents = await (0, import_promises15.readdir)(skillsDir, { withFileTypes: true });
280920
+ dirents = await (0, import_promises16.readdir)(skillsDir, { withFileTypes: true });
280072
280921
  } catch {
280073
280922
  return [];
280074
280923
  }
@@ -280076,7 +280925,7 @@ async function loadBuiltinSkills(skillsDir) {
280076
280925
  for (const d of dirents) {
280077
280926
  if (!d.isDirectory()) continue;
280078
280927
  const slug5 = d.name;
280079
- const skillDir = (0, import_node_path27.join)(skillsDir, slug5);
280928
+ const skillDir = (0, import_node_path28.join)(skillsDir, slug5);
280080
280929
  const files = {};
280081
280930
  try {
280082
280931
  await collectDir(skillDir, skillDir, files);
@@ -280105,7 +280954,8 @@ function materializeBuiltinSkills(builtins, skillsDirPrefix) {
280105
280954
  return out;
280106
280955
  }
280107
280956
  function applyBuiltinSkillVariants(builtins, opts) {
280108
- return builtins.map((skill) => {
280957
+ const personalTodoEnabled = opts.personalTodoEnabled ?? personalTodoEnabledFromEnv();
280958
+ return builtins.filter((skill) => skill.id !== PERSONAL_TODO_SKILL_ID || personalTodoEnabled).map((skill) => {
280109
280959
  const variant = skill.files[BUILTIN_SKILL_DELEGATION_VARIANT];
280110
280960
  if (variant === void 0) return skill;
280111
280961
  const files = { ...skill.files };
@@ -280116,16 +280966,18 @@ function applyBuiltinSkillVariants(builtins, opts) {
280116
280966
  return { ...skill, name: name || skill.id, description, files };
280117
280967
  });
280118
280968
  }
280119
- var import_promises15, import_node_path27, MAX_FILE_BYTES2, BUILTIN_SKILL_DELEGATION_VARIANT;
280969
+ var import_promises16, import_node_path28, MAX_FILE_BYTES2, BUILTIN_SKILL_DELEGATION_VARIANT, PERSONAL_TODO_SKILL_ID;
280120
280970
  var init_builtin_skills = __esm({
280121
280971
  "../server/src/governance/builtin-skills.ts"() {
280122
280972
  "use strict";
280123
- import_promises15 = require("node:fs/promises");
280124
- import_node_path27 = require("node:path");
280973
+ import_promises16 = require("node:fs/promises");
280974
+ import_node_path28 = require("node:path");
280125
280975
  init_skill_fetcher();
280126
280976
  init_skill_materializer();
280977
+ init_flag();
280127
280978
  MAX_FILE_BYTES2 = 1 << 20;
280128
280979
  BUILTIN_SKILL_DELEGATION_VARIANT = "SKILL.delegation.md";
280980
+ PERSONAL_TODO_SKILL_ID = "personal-todo";
280129
280981
  }
280130
280982
  });
280131
280983
 
@@ -280153,7 +281005,7 @@ var init_chat_builtin_skills = __esm({
280153
281005
 
280154
281006
  // ../server/src/governance/connector-tool-versions.ts
280155
281007
  function connectorToolVersionsFile(dataDir) {
280156
- return (0, import_node_path28.join)(dataDir, "connector-tools", "versions.json");
281008
+ return (0, import_node_path29.join)(dataDir, "connector-tools", "versions.json");
280157
281009
  }
280158
281010
  function registryBase() {
280159
281011
  const raw = process.env["npm_config_registry"] || process.env["NPM_CONFIG_REGISTRY"] || "https://registry.npmjs.org";
@@ -280172,7 +281024,7 @@ async function fetchLatestNpmVersion(pkg) {
280172
281024
  }
280173
281025
  async function readRegisteredToolVersions(dataDir) {
280174
281026
  try {
280175
- const raw = JSON.parse(await (0, import_promises16.readFile)(connectorToolVersionsFile(dataDir), "utf8"));
281027
+ const raw = JSON.parse(await (0, import_promises17.readFile)(connectorToolVersionsFile(dataDir), "utf8"));
280176
281028
  if (!raw || typeof raw !== "object") return {};
280177
281029
  const out = {};
280178
281030
  for (const [k2, v2] of Object.entries(raw)) {
@@ -280185,11 +281037,11 @@ async function readRegisteredToolVersions(dataDir) {
280185
281037
  }
280186
281038
  async function writeRegisteredToolVersions(dataDir, versions) {
280187
281039
  const file = connectorToolVersionsFile(dataDir);
280188
- await (0, import_promises16.mkdir)((0, import_node_path28.join)(dataDir, "connector-tools"), { recursive: true });
281040
+ await (0, import_promises17.mkdir)((0, import_node_path29.join)(dataDir, "connector-tools"), { recursive: true });
280189
281041
  const tmp = `${file}.${process.pid}.tmp`;
280190
- await (0, import_promises16.writeFile)(tmp, `${JSON.stringify(versions, null, 2)}
281042
+ await (0, import_promises17.writeFile)(tmp, `${JSON.stringify(versions, null, 2)}
280191
281043
  `, "utf8");
280192
- await (0, import_promises16.rename)(tmp, file);
281044
+ await (0, import_promises17.rename)(tmp, file);
280193
281045
  }
280194
281046
  async function refreshConnectorToolVersions(opts) {
280195
281047
  const fetchLatest = opts.fetchLatest ?? fetchLatestNpmVersion;
@@ -280232,12 +281084,12 @@ function pickToolVersions(registered, tools) {
280232
281084
  }
280233
281085
  return out;
280234
281086
  }
280235
- var import_promises16, import_node_path28, NPM_PROBE_TIMEOUT_MS;
281087
+ var import_promises17, import_node_path29, NPM_PROBE_TIMEOUT_MS;
280236
281088
  var init_connector_tool_versions = __esm({
280237
281089
  "../server/src/governance/connector-tool-versions.ts"() {
280238
281090
  "use strict";
280239
- import_promises16 = require("node:fs/promises");
280240
- import_node_path28 = require("node:path");
281091
+ import_promises17 = require("node:fs/promises");
281092
+ import_node_path29 = require("node:path");
280241
281093
  init_src8();
280242
281094
  NPM_PROBE_TIMEOUT_MS = 3e3;
280243
281095
  }
@@ -281330,6 +282182,7 @@ __export(src_exports2, {
281330
282182
  DEFAULT_PUSH_THROTTLE_MS: () => DEFAULT_PUSH_THROTTLE_MS,
281331
282183
  DEFAULT_SIMPLE_QUERY_MAX_CHARS: () => DEFAULT_SIMPLE_QUERY_MAX_CHARS,
281332
282184
  DEFAULT_TERMINAL_MAX_AGE_MS: () => DEFAULT_TERMINAL_MAX_AGE_MS,
282185
+ DEFAULT_TIME_ZONE_ENV: () => DEFAULT_TIME_ZONE_ENV,
281333
282186
  DELIVERY_REPAIR_BATCH: () => DELIVERY_REPAIR_BATCH,
281334
282187
  DENY_ALL_TRACE_READ: () => DENY_ALL_TRACE_READ,
281335
282188
  DISPATCH_DROP_BACKOFF_MS: () => DISPATCH_DROP_BACKOFF_MS,
@@ -281413,6 +282266,7 @@ __export(src_exports2, {
281413
282266
  MemoryProjectStateStore: () => MemoryProjectStateStore,
281414
282267
  MemoryPushSubscriptionStore: () => MemoryPushSubscriptionStore,
281415
282268
  MemoryReadMarkerStore: () => MemoryReadMarkerStore,
282269
+ MemoryTodoStore: () => MemoryTodoStore,
281416
282270
  MemoryWorkorderDraftPlannerIssuesStore: () => MemoryWorkorderDraftPlannerIssuesStore,
281417
282271
  MemoryWorkorderTagStore: () => MemoryWorkorderTagStore,
281418
282272
  MemoryWriteConflictError: () => MemoryWriteConflictError,
@@ -281431,6 +282285,7 @@ __export(src_exports2, {
281431
282285
  OWNER_PROTECTED_PROFILE_MESSAGE: () => OWNER_PROTECTED_PROFILE_MESSAGE,
281432
282286
  PARENT_CONVERSATION_PATH: () => PARENT_CONVERSATION_PATH,
281433
282287
  PARENT_CONVERSATION_RECENT_TURNS: () => PARENT_CONVERSATION_RECENT_TURNS,
282288
+ PERSONAL_TODO_SKILL_ID: () => PERSONAL_TODO_SKILL_ID,
281434
282289
  PostgresNotificationStore: () => PostgresNotificationStore,
281435
282290
  PostgresPushSubscriptionStore: () => PostgresPushSubscriptionStore,
281436
282291
  PostgresWorkorderDraftPlannerIssuesStore: () => PostgresWorkorderDraftPlannerIssuesStore,
@@ -281461,7 +282316,12 @@ __export(src_exports2, {
281461
282316
  STATUS_MARKER: () => STATUS_MARKER,
281462
282317
  SessionRuntimeOverrideError: () => SessionRuntimeOverrideError,
281463
282318
  SkillFetchHttpError: () => SkillFetchHttpError,
282319
+ TODO_REMINDER_SCAN_MS: () => TODO_REMINDER_SCAN_MS,
282320
+ TODO_REMINDER_TIME: () => TODO_REMINDER_TIME,
282321
+ TODO_REMINDER_TITLE: () => TODO_REMINDER_TITLE,
281464
282322
  TenancyVisibilityInputError: () => TenancyVisibilityInputError,
282323
+ TodoReminderScanner: () => TodoReminderScanner,
282324
+ TodoService: () => TodoService,
281465
282325
  TraceService: () => TraceService,
281466
282326
  TraceStoreSink: () => TraceStoreSink,
281467
282327
  TypesAdminService: () => TypesAdminService,
@@ -281487,10 +282347,12 @@ __export(src_exports2, {
281487
282347
  assertBindingWritePermission: () => assertBindingWritePermission,
281488
282348
  assertCompanyManager: () => assertCompanyManager,
281489
282349
  assertCreatableVariable: () => assertCreatableVariable,
282350
+ assertFirstVersionAssignee: () => assertFirstVersionAssignee,
281490
282351
  assertMayCreateAgent: () => assertMayCreateAgent,
281491
282352
  assertMayManageAgent: () => assertMayManageAgent,
281492
282353
  assertMaySelfOrManage: () => assertMaySelfOrManage,
281493
282354
  assistantBoundToMember: () => assistantBoundToMember,
282355
+ assistantNameFor: () => assistantNameFor,
281494
282356
  assistantRowRecovery: () => assistantRowRecovery,
281495
282357
  assistantsDomain: () => assistantsDomain,
281496
282358
  atCapacity: () => atCapacity,
@@ -281526,6 +282388,7 @@ __export(src_exports2, {
281526
282388
  buildWorkorderMetrics: () => buildWorkorderMetrics,
281527
282389
  buildWorkorderSummaries: () => buildWorkorderSummaries,
281528
282390
  builtinSkillToFiles: () => builtinSkillToFiles,
282391
+ canAccessTodo: () => canAccessTodo,
281529
282392
  canAnnounceRuntimeSwitch: () => canAnnounceRuntimeSwitch,
281530
282393
  canRetryAllocation: () => canRetryAllocation,
281531
282394
  chatBroadcastEnabledFromEnv: () => chatBroadcastEnabledFromEnv,
@@ -281611,6 +282474,7 @@ __export(src_exports2, {
281611
282474
  decideArrival: () => decideArrival,
281612
282475
  decideFlush: () => decideFlush,
281613
282476
  defaultAvatarRefOf: () => defaultAvatarRefOf,
282477
+ defaultTimeZoneFromEnv: () => defaultTimeZoneFromEnv,
281614
282478
  delegationArtifactsView: () => delegationArtifactsView,
281615
282479
  delegationBundleAdditions: () => delegationBundleAdditions,
281616
282480
  delegationDirName: () => delegationDirName,
@@ -281635,6 +282499,7 @@ __export(src_exports2, {
281635
282499
  dispatchIntentMarker: () => dispatchIntentMarker,
281636
282500
  displayCostUsdMicros: () => displayCostUsdMicros,
281637
282501
  dropUnchangedOutputs: () => dropUnchangedOutputs,
282502
+ dueReminders: () => dueReminders,
281638
282503
  employeeHref: () => employeeHref,
281639
282504
  encryptPushMessage: () => encryptPushMessage,
281640
282505
  ensureCompanyActors: () => ensureCompanyActors,
@@ -281770,6 +282635,7 @@ __export(src_exports2, {
281770
282635
  parseWriteScope: () => parseWriteScope,
281771
282636
  parseZhizengzengMarkdown: () => parseZhizengzengMarkdown,
281772
282637
  participatingTaskCount: () => participatingTaskCount,
282638
+ personalTodoEnabledFromEnv: () => personalTodoEnabledFromEnv,
281773
282639
  pickToolVersions: () => pickToolVersions,
281774
282640
  placeholderRuntimeSessionFlag: () => placeholderRuntimeSessionFlag,
281775
282641
  planChatContinuation: () => planChatContinuation,
@@ -281911,6 +282777,8 @@ __export(src_exports2, {
281911
282777
  toBundleFiles: () => toBundleFiles,
281912
282778
  toChatDispatchRequest: () => toChatDispatchRequest,
281913
282779
  toYamlDoubleQuoted: () => toYamlDoubleQuoted,
282780
+ todoReminderSourceKey: () => todoReminderSourceKey,
282781
+ todosDomain: () => todosDomain,
281914
282782
  touchKey: () => touchKey,
281915
282783
  traceDomain: () => traceDomain,
281916
282784
  traceReadAllows: () => traceReadAllows,
@@ -282039,6 +282907,7 @@ var init_src11 = __esm({
282039
282907
  init_agent_default_runtime_store();
282040
282908
  init_knowledge2();
282041
282909
  init_delegations();
282910
+ init_todos2();
282042
282911
  init_automations();
282043
282912
  init_automations();
282044
282913
  init_playbooks();
@@ -282450,14 +283319,14 @@ init_src();
282450
283319
  var os16 = __toESM(require("node:os"));
282451
283320
  var path46 = __toESM(require("node:path"));
282452
283321
  var import_node_child_process23 = require("node:child_process");
282453
- var import_node_crypto98 = require("node:crypto");
283322
+ var import_node_crypto100 = require("node:crypto");
282454
283323
 
282455
283324
  // ../cli/src/cli.ts
282456
283325
  init_src();
282457
283326
  var fs44 = __toESM(require("node:fs"), 1);
282458
283327
  var os13 = __toESM(require("node:os"), 1);
282459
283328
  var path39 = __toESM(require("node:path"), 1);
282460
- var import_node_crypto96 = require("node:crypto");
283329
+ var import_node_crypto98 = require("node:crypto");
282461
283330
 
282462
283331
  // ../cli/src/serve.ts
282463
283332
  init_src11();
@@ -282470,11 +283339,11 @@ var fs36 = __toESM(require("node:fs"), 1);
282470
283339
  var os9 = __toESM(require("node:os"), 1);
282471
283340
  var path29 = __toESM(require("node:path"), 1);
282472
283341
  var import_node_child_process15 = require("node:child_process");
282473
- var import_node_crypto91 = require("node:crypto");
283342
+ var import_node_crypto93 = require("node:crypto");
282474
283343
  var import_node_url7 = require("node:url");
282475
283344
 
282476
283345
  // ../cli/src/artifact-discovery-source.ts
282477
- var import_node_crypto86 = require("node:crypto");
283346
+ var import_node_crypto88 = require("node:crypto");
282478
283347
  init_src7();
282479
283348
  var ArtifactDiscoverySourceUnreadableError = class extends Error {
282480
283349
  constructor(message) {
@@ -282504,7 +283373,7 @@ async function resolveArtifactDiscoverySourceContent(blobs, input) {
282504
283373
  throw new ArtifactDiscoverySourceUnreadableError(`Artifact Revision ${input.revisionId} \u7684 contentKind \u4E0D\u53D7\u652F\u6301\uFF1A${String(input.contentKind)}`);
282505
283374
  }
282506
283375
  const rootBytes = await blobs.get(input.contentRef);
282507
- const sourceFingerprint = (0, import_node_crypto86.createHash)("sha256").update(rootBytes).digest("hex");
283376
+ const sourceFingerprint = (0, import_node_crypto88.createHash)("sha256").update(rootBytes).digest("hex");
282508
283377
  if (input.contentKind === "inline-blob") {
282509
283378
  const sourceContent = decodeText3(rootBytes);
282510
283379
  if (sourceContent === null) {
@@ -282528,7 +283397,7 @@ ${entry.text}`).join("\n\n"),
282528
283397
  }
282529
283398
 
282530
283399
  // ../server/src/domains/discussions/service.ts
282531
- var import_node_crypto87 = require("node:crypto");
283400
+ var import_node_crypto89 = require("node:crypto");
282532
283401
  init_zod();
282533
283402
  init_discussions();
282534
283403
  init_router();
@@ -282597,7 +283466,7 @@ var decisionOption = (row) => ({
282597
283466
  state: row.value.record.state,
282598
283467
  revision: row.revision
282599
283468
  });
282600
- var hash2 = (s2) => (0, import_node_crypto87.createHash)("sha256").update(s2).digest("hex").slice(0, 24);
283469
+ var hash2 = (s2) => (0, import_node_crypto89.createHash)("sha256").update(s2).digest("hex").slice(0, 24);
282601
283470
  var canonical = (s2) => s2.normalize("NFKC").trim().toLocaleLowerCase();
282602
283471
  function decisionFingerprint(d) {
282603
283472
  return hash2(JSON.stringify([d.statement, d.rationale, d.alternatives]));
@@ -282870,7 +283739,7 @@ var DiscussionService = class {
282870
283739
  throw new ApiError(409, "DISCUSSION_CONFLICT", "\u9879\u76EE\u8BAE\u9898\u6216\u51B3\u7B56\u5DF2\u53D8\u5316\uFF0C\u8BF7\u8BFB\u53D6\u6700\u65B0\u5185\u5BB9\u540E\u4FEE\u6B63");
282871
283740
  }
282872
283741
  }
282873
- const operation = (value2) => tx.put("operation", (0, import_node_crypto87.randomUUID)(), {
283742
+ const operation = (value2) => tx.put("operation", (0, import_node_crypto89.randomUUID)(), {
282874
283743
  ...value2,
282875
283744
  projectId: projectId2,
282876
283745
  actorId: ctx.actorId,
@@ -282887,7 +283756,7 @@ var DiscussionService = class {
282887
283756
  if (id.startsWith("new:")) {
282888
283757
  if (BUG_TOPIC.test(t.title.trim())) invalid("topics.title", `\u300C${t.title}\u300D\u662F\u5355\u4E2A Bug\uFF1A\u4FEE\u590D\u60C5\u51B5\u5199\u8FDB\u8BA8\u8BBA\u8BB0\u5F55\uFF0C\u5B9A\u51FA\u7684\u89C4\u5219\u6302\u5230\u5BF9\u5E94\u529F\u80FD\u8BAE\u9898\u4E0B\uFF0C\u4E0D\u6309 Bug \u53F7\u5EFA\u8BAE\u9898`);
282889
283758
  const existing = [...topicMap.values()].find((x2) => canonical(x2.title) === canonical(t.title));
282890
- id = existing?.topicId ?? `topic_${(0, import_node_crypto87.randomUUID)()}`;
283759
+ id = existing?.topicId ?? `topic_${(0, import_node_crypto89.randomUUID)()}`;
282891
283760
  if (!existing) createdTopics.add(id);
282892
283761
  } else if (!topicMap.has(id)) invalid("topics.topic_id", `\u8BAE\u9898 ${id} \u4E0D\u5B58\u5728\uFF1B\u65B0\u8BAE\u9898\u4F7F\u7528 new: \u524D\u7F00`);
282893
283762
  topicAliases.set(t.topic_id, id);
@@ -282939,7 +283808,7 @@ var DiscussionService = class {
282939
283808
  if (id.startsWith("new:")) {
282940
283809
  const candidateKey = JSON.stringify([topicId, canonical(d.record.statement)]);
282941
283810
  const existing = [...decisionMap.values()].find((x2) => x2.topicId === topicId && canonical(x2.record.statement) === canonical(d.record.statement));
282942
- id = existing?.decisionId ?? candidateIds.get(candidateKey) ?? `decision_${(0, import_node_crypto87.randomUUID)()}`;
283811
+ id = existing?.decisionId ?? candidateIds.get(candidateKey) ?? `decision_${(0, import_node_crypto89.randomUUID)()}`;
282943
283812
  candidateIds.set(candidateKey, id);
282944
283813
  } else if (!decisionMap.has(id)) invalid("decision_id", `\u51B3\u7B56 ${id} \u4E0D\u5B58\u5728\uFF1B\u65B0\u51B3\u7B56\u4F7F\u7528 new: \u524D\u7F00`);
282945
283814
  decisionAliases.set(d.decision_id, id);
@@ -283063,15 +283932,15 @@ var DiscussionService = class {
283063
283932
  });
283064
283933
  }
283065
283934
  async editTopic(projectId2, topicId, revision, raw, actorId) {
283066
- const edit2 = external_exports.object({ title: nonempty, summary: text6.optional() }).strict().parse(raw);
283935
+ const edit3 = external_exports.object({ title: nonempty, summary: text6.optional() }).strict().parse(raw);
283067
283936
  return this.store.transaction(async (tx) => {
283068
283937
  const key2 = entryId(projectId2, topicId);
283069
283938
  const old = await tx.get("topic", key2);
283070
283939
  if (!old) throw new ApiError(404, "NOT_FOUND", "\u8BAE\u9898\u4E0D\u5B58\u5728");
283071
283940
  assertRevision(old.revision, revision);
283072
- const summary = (edit2.summary ?? old.value.summary ?? "").trim();
283073
- if (old.value.title === edit2.title && (old.value.summary ?? "") === summary) return old;
283074
- const result = await tx.put("topic", key2, { ...old.value, title: edit2.title, summary, updated_at: new Date(this.now()).toISOString() });
283941
+ const summary = (edit3.summary ?? old.value.summary ?? "").trim();
283942
+ if (old.value.title === edit3.title && (old.value.summary ?? "") === summary) return old;
283943
+ const result = await tx.put("topic", key2, { ...old.value, title: edit3.title, summary, updated_at: new Date(this.now()).toISOString() });
283075
283944
  await this.operation(tx, actorId, "topic", key2, old.revision, result.revision);
283076
283945
  return result;
283077
283946
  });
@@ -283201,7 +284070,7 @@ var DiscussionService = class {
283201
284070
  ...row.value.confirmation ? { confirmation: { ...row.value.confirmation, fingerprint: decisionFingerprint(record10) } } : {}
283202
284071
  };
283203
284072
  await tx.put("decision", row.id, value2);
283204
- await tx.put("operation", (0, import_node_crypto87.randomUUID)(), {
284073
+ await tx.put("operation", (0, import_node_crypto89.randomUUID)(), {
283205
284074
  action: "edit",
283206
284075
  kind: "decision",
283207
284076
  projectId: projectId2,
@@ -283265,7 +284134,7 @@ var DiscussionService = class {
283265
284134
  if (!current || current.revision !== row.revision) throw new ApiError(409, "DISCUSSION_CONFLICT", `\u8BA8\u8BBA ${row.id} \u5DF2\u88AB\u4FEE\u6539\uFF0C\u8BF7\u8BFB\u53D6\u6700\u65B0\u5185\u5BB9\u540E\u91CD\u8BD5`);
283266
284135
  const summary = given.get(row.id);
283267
284136
  await tx.put("discussion", row.id, { ...current.value, record: { ...current.value.record, summary } });
283268
- await tx.put("operation", (0, import_node_crypto87.randomUUID)(), {
284137
+ await tx.put("operation", (0, import_node_crypto89.randomUUID)(), {
283269
284138
  action: "edit",
283270
284139
  kind: "discussion",
283271
284140
  projectId: current.value.record.project_id,
@@ -283281,7 +284150,7 @@ var DiscussionService = class {
283281
284150
  });
283282
284151
  }
283283
284152
  async operation(tx, actorId, kind, id, from, to) {
283284
- await tx.put("operation", (0, import_node_crypto87.randomUUID)(), { actorId, kind, id, from, to, at: new Date(this.now()).toISOString() });
284153
+ await tx.put("operation", (0, import_node_crypto89.randomUUID)(), { actorId, kind, id, from, to, at: new Date(this.now()).toISOString() });
283285
284154
  }
283286
284155
  };
283287
284156
  function pickDiscussionContent(d) {
@@ -283412,13 +284281,13 @@ function renderProjectContext(input) {
283412
284281
  const dir = PROJECT_DISCUSSIONS_DIR;
283413
284282
  const files = {};
283414
284283
  const width = (n) => Math.max(2, String(n).length);
283415
- const pad2 = (i, n) => String(i + 1).padStart(width(n), "0");
284284
+ const pad3 = (i, n) => String(i + 1).padStart(width(n), "0");
283416
284285
  const mine = new Set(input.discussions.flatMap((row) => row.value.record.topics.map((t) => `${row.value.record.project_id}\0${t.topic_id}`)));
283417
- const discussionPaths = input.discussions.map((row, i) => `${dir}/\u6211\u7684\u8BA8\u8BBA/${pad2(i, input.discussions.length)}-${row.value.discussedAt.slice(0, 10)}-${fileSegment(row.value.record.title, "\u672A\u547D\u540D\u8BA8\u8BBA")}.md`);
284286
+ const discussionPaths = input.discussions.map((row, i) => `${dir}/\u6211\u7684\u8BA8\u8BBA/${pad3(i, input.discussions.length)}-${row.value.discussedAt.slice(0, 10)}-${fileSegment(row.value.record.title, "\u672A\u547D\u540D\u8BA8\u8BBA")}.md`);
283418
284287
  input.discussions.forEach((row, i) => {
283419
284288
  files[discussionPaths[i]] = [`# ${row.value.record.title}`, "", ...discussionBody(input, row)].join("\n");
283420
284289
  });
283421
- const topicPaths = input.topics.map((t, i) => `${dir}/\u8BAE\u9898/${pad2(i, input.topics.length)}-${fileSegment(t.value.title, "\u672A\u547D\u540D\u8BAE\u9898")}.md`);
284290
+ const topicPaths = input.topics.map((t, i) => `${dir}/\u8BAE\u9898/${pad3(i, input.topics.length)}-${fileSegment(t.value.title, "\u672A\u547D\u540D\u8BAE\u9898")}.md`);
283422
284291
  input.topics.forEach((t, i) => {
283423
284292
  files[topicPaths[i]] = [`# \u8BAE\u9898\uFF1A${t.value.title}`, "", decisionLegend, "", ...topicBody(input, t, "##")].join("\n");
283424
284293
  });
@@ -283581,7 +284450,7 @@ var DiscussionContext = class {
283581
284450
  };
283582
284451
 
283583
284452
  // ../server/src/domains/discussions/worker.ts
283584
- var import_node_crypto88 = require("node:crypto");
284453
+ var import_node_crypto90 = require("node:crypto");
283585
284454
  var DISCUSSION_IDLE_MS = 15 * 6e4;
283586
284455
  var DISCUSSION_MAX_CORRECTIONS = 3;
283587
284456
  var DISCUSSION_LEASE_MS = 2 * 6e4;
@@ -283607,7 +284476,7 @@ var DiscussionWorker = class {
283607
284476
  if (await this.isReplying(sessionId)) return false;
283608
284477
  const cutoff = eligibleCutoff(await this.service.chat.listMessages(sessionId), this.service.now());
283609
284478
  if (!cutoff) return false;
283610
- const owner = (0, import_node_crypto88.randomUUID)();
284479
+ const owner = (0, import_node_crypto90.randomUUID)();
283611
284480
  const claimed = await this.service.store.transaction(async (tx) => {
283612
284481
  const old = (await tx.get("job", sessionId))?.value;
283613
284482
  if (old?.status === "running" && (old.leaseUntil ?? 0) > this.service.now()) return null;
@@ -284033,7 +284902,7 @@ init_zod();
284033
284902
  init_discussions();
284034
284903
  init_router();
284035
284904
  init_uncategorized_project();
284036
- var edit = external_exports.object({ revision: external_exports.number().int().positive(), content: external_exports.unknown() }).strict();
284905
+ var edit2 = external_exports.object({ revision: external_exports.number().int().positive(), content: external_exports.unknown() }).strict();
284037
284906
  var removal = external_exports.object({ revision: external_exports.number().int().positive(), reason: external_exports.string() }).strict();
284038
284907
  function paging(query) {
284039
284908
  const parsed = external_exports.object({
@@ -284122,7 +284991,7 @@ function discussionsDomain(options) {
284122
284991
  router.patch("/api/chat-sessions/:id/discussion", wrap(async (req) => {
284123
284992
  await human(req);
284124
284993
  const { service, session } = await sessionAccess(req);
284125
- const body2 = edit.parse(req.body);
284994
+ const body2 = edit2.parse(req.body);
284126
284995
  return { status: 200, body: await service.editDiscussion(session.id, body2.revision, body2.content, req.auth.actor) };
284127
284996
  }));
284128
284997
  router.post("/api/chat-sessions/:id/discussion/retry", wrap(async (req) => {
@@ -284184,7 +285053,7 @@ function discussionsDomain(options) {
284184
285053
  await human(req);
284185
285054
  const service = await options.serviceFor(req.auth.companyId);
284186
285055
  await service.project(req.params.id);
284187
- const body2 = edit.extend({ relatedRevisions: external_exports.record(external_exports.number().int().positive()).optional() }).parse(req.body);
285056
+ const body2 = edit2.extend({ relatedRevisions: external_exports.record(external_exports.number().int().positive()).optional() }).parse(req.body);
284188
285057
  return { status: 200, body: await service.editDecision(req.params.id, req.params.decisionId, body2.revision, body2.content, req.auth.actor, body2.relatedRevisions) };
284189
285058
  }));
284190
285059
  router.get("/api/chat-sessions/:id/project-context", wrap(async (req) => {
@@ -284367,9 +285236,9 @@ function createDiscussionRuntime(options) {
284367
285236
  }
284368
285237
 
284369
285238
  // ../server/src/domains/discussions/store.ts
284370
- var import_node_crypto89 = require("node:crypto");
284371
- var import_promises17 = require("node:fs/promises");
284372
- var import_node_path29 = require("node:path");
285239
+ var import_node_crypto91 = require("node:crypto");
285240
+ var import_promises18 = require("node:fs/promises");
285241
+ var import_node_path30 = require("node:path");
284373
285242
  var key = (kind, id) => JSON.stringify([kind, id]);
284374
285243
  function contains(value2, match) {
284375
285244
  if (match !== null && typeof match === "object" && !Array.isArray(match)) {
@@ -284390,7 +285259,7 @@ var MemoryDiscussionStore = class _MemoryDiscussionStore {
284390
285259
  static async open(filename) {
284391
285260
  const store = new _MemoryDiscussionStore(filename);
284392
285261
  try {
284393
- store.rows = JSON.parse(await (0, import_promises17.readFile)(filename, "utf8"));
285262
+ store.rows = JSON.parse(await (0, import_promises18.readFile)(filename, "utf8"));
284394
285263
  } catch (error2) {
284395
285264
  if (error2.code !== "ENOENT") throw error2;
284396
285265
  }
@@ -284442,10 +285311,10 @@ var MemoryDiscussionStore = class _MemoryDiscussionStore {
284442
285311
  const result = await run(tx);
284443
285312
  if (changed) {
284444
285313
  if (this.filename) {
284445
- await (0, import_promises17.mkdir)((0, import_node_path29.dirname)(this.filename), { recursive: true });
284446
- const temp = `${this.filename}.${(0, import_node_crypto89.randomUUID)()}.tmp`;
284447
- await (0, import_promises17.writeFile)(temp, JSON.stringify(next), "utf8");
284448
- await (0, import_promises17.rename)(temp, this.filename);
285314
+ await (0, import_promises18.mkdir)((0, import_node_path30.dirname)(this.filename), { recursive: true });
285315
+ const temp = `${this.filename}.${(0, import_node_crypto91.randomUUID)()}.tmp`;
285316
+ await (0, import_promises18.writeFile)(temp, JSON.stringify(next), "utf8");
285317
+ await (0, import_promises18.rename)(temp, this.filename);
284449
285318
  }
284450
285319
  this.rows = next;
284451
285320
  }
@@ -284536,6 +285405,225 @@ var PostgresDiscussionStore = class _PostgresDiscussionStore {
284536
285405
  }
284537
285406
  };
284538
285407
 
285408
+ // ../cli/src/todo-wiring.ts
285409
+ init_src();
285410
+ init_router();
285411
+ init_service10();
285412
+ init_routes11();
285413
+ init_reminders();
285414
+ init_flag();
285415
+ function createTodoRuntime(options) {
285416
+ const enabled = options.enabled ?? (() => personalTodoEnabledFromEnv());
285417
+ const cache = /* @__PURE__ */ new Map();
285418
+ const serviceFor = (companyId) => {
285419
+ const id = companyId ?? options.defaultCompanyId();
285420
+ let value2 = cache.get(id);
285421
+ if (!value2) {
285422
+ value2 = options.store(id).then((store) => new TodoService({ store, timeZone: () => options.timeZone(id) }));
285423
+ cache.set(id, value2);
285424
+ void value2.catch(() => cache.delete(id));
285425
+ }
285426
+ return value2;
285427
+ };
285428
+ const resolveCaller = async (actorId, companyId) => {
285429
+ if (isHumanActorId(actorId)) return { principal: actorId, actor: actorId };
285430
+ const human = await options.boundHuman(actorId, companyId ?? options.defaultCompanyId());
285431
+ if (!human) throw new ApiError(403, "TODO_NO_OWNER", "\u4E2A\u4EBA\u5F85\u529E\u53EA\u80FD\u7531\u771F\u4EBA\u6216\u771F\u4EBA\u7684\u52A9\u7406\u8BB0\uFF1A\u8FD9\u4E2A agent \u6CA1\u6709\u7ED1\u5B9A\u4EFB\u4F55\u771F\u4EBA\u3002");
285432
+ return { principal: human, hand: actorId, actor: actorId };
285433
+ };
285434
+ const register = todosDomain({ serviceFor, resolveCaller, enabled });
285435
+ const scanner = new TodoReminderScanner({
285436
+ companies: options.companies,
285437
+ todos: options.store,
285438
+ notifications: options.notifications,
285439
+ timeZone: options.timeZone,
285440
+ enabled,
285441
+ warn: options.warn,
285442
+ ...options.onReminders ? { onInserted: options.onReminders } : {}
285443
+ });
285444
+ return {
285445
+ register,
285446
+ serviceFor,
285447
+ resolveCaller,
285448
+ scanner,
285449
+ start: () => scanner.start(),
285450
+ stop: () => scanner.stop()
285451
+ };
285452
+ }
285453
+
285454
+ // ../cli/src/serve.ts
285455
+ init_store3();
285456
+
285457
+ // ../storage/src/postgres-todos.ts
285458
+ init_pg_ident();
285459
+ init_schema_ddl_lock();
285460
+ var PostgresTodoStore = class _PostgresTodoStore {
285461
+ constructor(pool, schema) {
285462
+ this.pool = pool;
285463
+ this.s = quoteIdent2(schema);
285464
+ }
285465
+ s;
285466
+ static async open(pool, schema) {
285467
+ const store = new _PostgresTodoStore(pool, schema);
285468
+ await withSchemaDdlLock(pool, schema, async () => {
285469
+ await pool.query(`CREATE SCHEMA IF NOT EXISTS ${store.s}`);
285470
+ await pool.query(`CREATE TABLE IF NOT EXISTS ${store.s}.todos (
285471
+ id text PRIMARY KEY,
285472
+ title text NOT NULL,
285473
+ note text,
285474
+ creator_actor_id text NOT NULL,
285475
+ assignee_actor_id text NOT NULL,
285476
+ recorded_by_actor_id text NOT NULL,
285477
+ due_date text NOT NULL,
285478
+ due_time text,
285479
+ start_date text,
285480
+ start_time text,
285481
+ status text NOT NULL CHECK (status IN ('open','done','cancelled')),
285482
+ done_at timestamptz,
285483
+ cancelled_at timestamptz,
285484
+ source_chat_session_id text,
285485
+ linked_workorder_id text,
285486
+ created_at timestamptz NOT NULL,
285487
+ updated_at timestamptz NOT NULL
285488
+ )`);
285489
+ await pool.query(`CREATE INDEX IF NOT EXISTS todos_assignee_status_due_idx ON ${store.s}.todos (assignee_actor_id, status, due_date)`);
285490
+ await pool.query(`CREATE INDEX IF NOT EXISTS todos_creator_status_due_idx ON ${store.s}.todos (creator_actor_id, status, due_date)`);
285491
+ await pool.query(`CREATE INDEX IF NOT EXISTS todos_open_due_idx ON ${store.s}.todos (due_date) WHERE status = 'open'`);
285492
+ await pool.query(`CREATE INDEX IF NOT EXISTS todos_open_start_idx ON ${store.s}.todos (start_date) WHERE status = 'open' AND start_date IS NOT NULL`);
285493
+ await pool.query(`CREATE TABLE IF NOT EXISTS ${store.s}.todo_changes (
285494
+ id text PRIMARY KEY,
285495
+ todo_id text NOT NULL REFERENCES ${store.s}.todos(id) ON DELETE CASCADE,
285496
+ actor_id text NOT NULL,
285497
+ hand_actor_id text,
285498
+ at timestamptz NOT NULL,
285499
+ field text NOT NULL,
285500
+ old_value text,
285501
+ new_value text,
285502
+ reason text
285503
+ )`);
285504
+ await pool.query(`CREATE INDEX IF NOT EXISTS todo_changes_todo_at_idx ON ${store.s}.todo_changes (todo_id, at, id)`);
285505
+ });
285506
+ return store;
285507
+ }
285508
+ async tx(run) {
285509
+ const client = await this.pool.connect();
285510
+ try {
285511
+ await client.query("BEGIN");
285512
+ const result = await run(client);
285513
+ await client.query("COMMIT");
285514
+ return result;
285515
+ } catch (error2) {
285516
+ await client.query("ROLLBACK");
285517
+ throw error2;
285518
+ } finally {
285519
+ client.release();
285520
+ }
285521
+ }
285522
+ async insertChanges(client, changes) {
285523
+ for (const c of changes) {
285524
+ await client.query(
285525
+ `INSERT INTO ${this.s}.todo_changes (id,todo_id,actor_id,hand_actor_id,at,field,old_value,new_value,reason)
285526
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)`,
285527
+ [c.id, c.todoId, c.actorId, c.handActorId ?? null, c.at, c.field, c.oldValue ?? null, c.newValue ?? null, c.reason ?? null]
285528
+ );
285529
+ }
285530
+ }
285531
+ async create(todo, changes) {
285532
+ return this.tx(async (client) => {
285533
+ const r = await client.query(`INSERT INTO ${this.s}.todos (id,title,note,creator_actor_id,assignee_actor_id,recorded_by_actor_id,
285534
+ due_date,due_time,status,done_at,cancelled_at,source_chat_session_id,linked_workorder_id,created_at,updated_at,start_date,start_time)
285535
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17) RETURNING *`, rowValues(todo));
285536
+ await this.insertChanges(client, changes);
285537
+ return fromRow3(r.rows[0]);
285538
+ });
285539
+ }
285540
+ async get(id) {
285541
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.todos WHERE id=$1`, [id]);
285542
+ return r.rows[0] ? fromRow3(r.rows[0]) : null;
285543
+ }
285544
+ async update(next, expectedUpdatedAt, changes) {
285545
+ return this.tx(async (client) => {
285546
+ const r = await client.query(`UPDATE ${this.s}.todos SET title=$2,note=$3,creator_actor_id=$4,assignee_actor_id=$5,
285547
+ recorded_by_actor_id=$6,due_date=$7,due_time=$8,status=$9,done_at=$10,cancelled_at=$11,source_chat_session_id=$12,
285548
+ linked_workorder_id=$13,created_at=$14,updated_at=$15,start_date=$16,start_time=$17
285549
+ WHERE id=$1 AND updated_at=$18::timestamptz RETURNING *`, [...rowValues(next), expectedUpdatedAt]);
285550
+ if (!r.rows[0]) return null;
285551
+ await this.insertChanges(client, changes);
285552
+ return fromRow3(r.rows[0]);
285553
+ });
285554
+ }
285555
+ async listInvolving(actorId, statuses2) {
285556
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.todos
285557
+ WHERE (creator_actor_id=$1 OR assignee_actor_id=$1) AND status = ANY($2::text[])
285558
+ ORDER BY due_date, COALESCE(due_time,'99:99'), created_at, id`, [actorId, [...statuses2]]);
285559
+ return r.rows.map(fromRow3);
285560
+ }
285561
+ async listOpenReminderCandidates(fromDay, toDay) {
285562
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.todos
285563
+ WHERE status='open' AND (due_date BETWEEN $1 AND $2 OR start_date BETWEEN $1 AND $2)
285564
+ ORDER BY due_date, COALESCE(due_time,'99:99'), created_at, id`, [fromDay, toDay]);
285565
+ return r.rows.map(fromRow3);
285566
+ }
285567
+ async listChanges(todoId) {
285568
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.todo_changes WHERE todo_id=$1 ORDER BY at, id`, [todoId]);
285569
+ return r.rows.map((row) => {
285570
+ const c = { id: row.id, todoId: row.todo_id, actorId: row.actor_id, at: iso8(row.at), field: row.field };
285571
+ if (row.hand_actor_id) c.handActorId = row.hand_actor_id;
285572
+ if (row.old_value !== null) c.oldValue = row.old_value;
285573
+ if (row.new_value !== null) c.newValue = row.new_value;
285574
+ if (row.reason !== null) c.reason = row.reason;
285575
+ return c;
285576
+ });
285577
+ }
285578
+ };
285579
+ var iso8 = (v2) => v2 instanceof Date ? v2.toISOString() : typeof v2 === "string" ? new Date(v2).toISOString() : void 0;
285580
+ function rowValues(t) {
285581
+ return [
285582
+ t.id,
285583
+ t.title,
285584
+ t.note ?? null,
285585
+ t.creatorActorId,
285586
+ t.assigneeActorId,
285587
+ t.recordedByActorId,
285588
+ t.dueDate,
285589
+ t.dueTime ?? null,
285590
+ t.status,
285591
+ t.doneAt ?? null,
285592
+ t.cancelledAt ?? null,
285593
+ t.sourceChatSessionId ?? null,
285594
+ t.linkedWorkorderId ?? null,
285595
+ t.createdAt,
285596
+ t.updatedAt,
285597
+ t.startDate ?? null,
285598
+ t.startTime ?? null
285599
+ ];
285600
+ }
285601
+ function fromRow3(row) {
285602
+ const t = {
285603
+ id: row["id"],
285604
+ title: row["title"],
285605
+ creatorActorId: row["creator_actor_id"],
285606
+ assigneeActorId: row["assignee_actor_id"],
285607
+ recordedByActorId: row["recorded_by_actor_id"],
285608
+ dueDate: row["due_date"],
285609
+ status: row["status"],
285610
+ createdAt: iso8(row["created_at"]),
285611
+ updatedAt: iso8(row["updated_at"])
285612
+ };
285613
+ const opt = [
285614
+ ["note", row["note"]],
285615
+ ["dueTime", row["due_time"]],
285616
+ ["startDate", row["start_date"]],
285617
+ ["startTime", row["start_time"]],
285618
+ ["doneAt", iso8(row["done_at"])],
285619
+ ["cancelledAt", iso8(row["cancelled_at"])],
285620
+ ["sourceChatSessionId", row["source_chat_session_id"]],
285621
+ ["linkedWorkorderId", row["linked_workorder_id"]]
285622
+ ];
285623
+ for (const [k2, v2] of opt) if (v2 !== void 0 && v2 !== null) t[k2] = v2;
285624
+ return t;
285625
+ }
285626
+
284539
285627
  // ../cli/src/rebuild-artifact-projections-per-company.ts
284540
285628
  init_src11();
284541
285629
  var DEFAULT_LOGGER = {
@@ -284592,7 +285680,7 @@ init_src4();
284592
285680
  init_chat_exit();
284593
285681
 
284594
285682
  // ../cli/src/chat-task-header.ts
284595
- var import_node_crypto90 = require("node:crypto");
285683
+ var import_node_crypto92 = require("node:crypto");
284596
285684
  init_src11();
284597
285685
  init_src();
284598
285686
  var BRAINSTORM_LEGACY_LINE = "\u8BF7\u76F4\u63A5\u56DE\u7B54\u7528\u6237\u95EE\u9898\u3002\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u5199\u5165\u7CFB\u7EDF\uFF0C\u5426\u5219\u4E0D\u8981\u521B\u5EFA\u6216\u4FEE\u6539 artifact\u3002";
@@ -284653,7 +285741,7 @@ function buildChatTurnPrompts(input) {
284653
285741
  };
284654
285742
  }
284655
285743
  function chatSystemPromptFingerprint(systemPrompt) {
284656
- return (0, import_node_crypto90.createHash)("sha256").update(systemPrompt ?? "").digest("hex").slice(0, 16);
285744
+ return (0, import_node_crypto92.createHash)("sha256").update(systemPrompt ?? "").digest("hex").slice(0, 16);
284657
285745
  }
284658
285746
 
284659
285747
  // ../cli/src/chat-task-md.ts
@@ -285507,7 +286595,7 @@ function oasisWrapperScript() {
285507
286595
  const repoRoot = (0, import_node_url7.fileURLToPath)(new URL("../../../", __esm_import_meta_url));
285508
286596
  const tsx = path29.join(repoRoot, "node_modules/.bin/tsx");
285509
286597
  const main = path29.join(repoRoot, "packages/cli/src/main.ts");
285510
- const key2 = (0, import_node_crypto91.createHash)("sha256").update(repoRoot).digest("hex").slice(0, 12);
286598
+ const key2 = (0, import_node_crypto93.createHash)("sha256").update(repoRoot).digest("hex").slice(0, 12);
285511
286599
  const uid = typeof process.getuid === "function" ? process.getuid() : "na";
285512
286600
  const stable = path29.join(os9.tmpdir(), `oasis-cli-wrapper-${uid}-${key2}`);
285513
286601
  let dir;
@@ -285727,6 +286815,14 @@ function consoleLinksFromEnv(rawUrl, rawRoutes) {
285727
286815
  }
285728
286816
  async function startServe(opts) {
285729
286817
  const serverStartedAt = (/* @__PURE__ */ new Date()).toISOString();
286818
+ let defaultTimeZone;
286819
+ try {
286820
+ defaultTimeZone = defaultTimeZoneFromEnv(process.env[DEFAULT_TIME_ZONE_ENV]);
286821
+ console.log(`[serve] \u90E8\u7F72\u9ED8\u8BA4\u65F6\u533A=${defaultTimeZone}\uFF08${DEFAULT_TIME_ZONE_ENV}\uFF1B\u7EC4\u7EC7\u6CA1\u914D\u65F6\u533A\u65F6\u4F7F\u7528\uFF09`);
286822
+ } catch (err) {
286823
+ console.error(`[serve] ${err.message}`);
286824
+ throw err;
286825
+ }
285730
286826
  fs36.mkdirSync(opts.dir, { recursive: true });
285731
286827
  const serverHeartbeatFile = path29.join(opts.dir, "serve-heartbeat.json");
285732
286828
  let previousServerHeartbeatAt;
@@ -285919,7 +287015,7 @@ async function startServe(opts) {
285919
287015
  if (!hub) return Promise.resolve({ ok: false, reason: "\u672C\u8FDB\u7A0B\u6CA1\u6709\u8282\u70B9\u7F51\u5173" });
285920
287016
  const online = hub.connectedDaemons().some((d) => d.daemonId === nodeId);
285921
287017
  if (!online) return Promise.resolve({ ok: false, reason: "\u8282\u70B9\u5F53\u524D\u4E0D\u5728\u7EBF\uFF0C\u7528\u7684\u662F\u5B83\u4E0A\u6B21\u62A5\u7684\u6E05\u5355" });
285922
- const requestId = (0, import_node_crypto91.randomUUID)();
287018
+ const requestId = (0, import_node_crypto93.randomUUID)();
285923
287019
  const task = new Promise((resolve11) => {
285924
287020
  const timer = setTimeout(() => {
285925
287021
  modelRefreshWaiters.delete(requestId);
@@ -285993,6 +287089,8 @@ async function startServe(opts) {
285993
287089
  // 租户闸(ADR-0164 §5):`POST /api/bindings` 据它拦「把本公司员工绑到别家公司的机器上」。
285994
287090
  // 闭包读当前 nodeStore(下方 PG 分支会重赋值),与 `listBindingModelIds` 同一写法。
285995
287091
  resolveNodeTenancy: async (nodeId) => nodeStore.getNode(nodeId),
287092
+ // 变量到期按组织的生效时区算日界(PRD v1.2 §7)。`companies` 在下方才建好——本箭头只在派单 / 请求时跑,那时已就绪。
287093
+ companyTimeZone: (companyId) => companies.service.effectiveTimeZoneOf(companyId),
285996
287094
  store: registryStore,
285997
287095
  oplog,
285998
287096
  kernel: defaultCompanyKernel,
@@ -286766,7 +287864,7 @@ async function startServe(opts) {
286766
287864
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
286767
287865
  if (buf || session.runId) {
286768
287866
  await turnStore.appendMessage({
286769
- id: (0, import_node_crypto91.randomUUID)(),
287867
+ id: (0, import_node_crypto93.randomUUID)(),
286770
287868
  sessionId: chatSessionId,
286771
287869
  role: "assistant",
286772
287870
  content: buf,
@@ -286812,7 +287910,7 @@ async function startServe(opts) {
286812
287910
  chatTargets: {
286813
287911
  inspect: inspectChatTarget,
286814
287912
  createTargetSession: async ({ companyId, agentId, ownerHumanActorId, title }) => {
286815
- const sessionId = (0, import_node_crypto91.randomUUID)();
287913
+ const sessionId = (0, import_node_crypto93.randomUUID)();
286816
287914
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
286817
287915
  const binding = await registryStore.getBinding(agentId).catch(() => null);
286818
287916
  await (await chatStoreFor(companyId)).createSession({
@@ -286928,7 +288026,26 @@ async function startServe(opts) {
286928
288026
  secret: jwtSecret ?? "oasis-dev-secret",
286929
288027
  onAuthFailure: (reason) => console.warn(`[org-model] agent credential auth failure: ${reason}`)
286930
288028
  });
286931
- const companies = createCompaniesDomain({ store: controlPlaneStore, blobs, sender: mailSender, consoleUrl, resolveActorsCtx: actors.resolveCtx, listInFlightAssignments, agentCredentials });
288029
+ const companies = createCompaniesDomain({
288030
+ store: controlPlaneStore,
288031
+ blobs,
288032
+ sender: mailSender,
288033
+ consoleUrl,
288034
+ resolveActorsCtx: actors.resolveCtx,
288035
+ listInFlightAssignments,
288036
+ agentCredentials,
288037
+ defaultTimeZone,
288038
+ // 注销的邮箱验证门禁(设置页 PRD §4.3):票据由 auth 域签发与校验。auth 要拿 companies.service
288039
+ // 建号,只能建在后面——这里包一层闭包,请求时才解引用,不撞 TDZ。
288040
+ verifyAccountDeletionTicket: (accountId, ticket) => auth.verifyAccountDeletionTicket(accountId, ticket),
288041
+ // 改成员昵称时同步其懒创建助理的名字(设置页 PRD §3.1)。解析器在下方才装好、只在请求时调用;
288042
+ // 还没装好(启动期,没有请求)就当作没有绑定,只改真人档案。
288043
+ assistantOfHuman: async (companyId, humanActorId) => {
288044
+ const ctx = await resolveAssistantsCtxLazy(companyId);
288045
+ const bond = ctx ? await ctx.service.getByHuman(humanActorId) : null;
288046
+ return bond ? { agentActorId: bond.agentActorId, origin: bond.origin } : null;
288047
+ }
288048
+ });
286932
288049
  const auth = createAuth({
286933
288050
  store: controlPlaneStore,
286934
288051
  sender: mailSender,
@@ -287954,7 +289071,7 @@ async function startServe(opts) {
287954
289071
  onQuotaExhausted: (t) => {
287955
289072
  const daemon = hub?.connectedDaemons().find((d) => d.daemonId === t.nodeId);
287956
289073
  if (!daemon?.meta.supportsQuotaRefresh) return;
287957
- hub?.dispatch(t.nodeId, { type: "refresh_quotas", requestId: (0, import_node_crypto91.randomUUID)() });
289074
+ hub?.dispatch(t.nodeId, { type: "refresh_quotas", requestId: (0, import_node_crypto93.randomUUID)() });
287958
289075
  },
287959
289076
  actorName: async (actorId) => (await (await actors.resolveCtx(companyId)).service.getActor(actorId).catch(() => null))?.name ?? null
287960
289077
  });
@@ -288069,6 +289186,30 @@ async function startServe(opts) {
288069
289186
  isDelegationChild: async (sessionId, companyId) => Boolean(await (await delegationStoreFor(companyId)).getByChildSession(sessionId)),
288070
289187
  warn: (error2) => console.warn(`[discussions] ${String(error2)}`)
288071
289188
  });
289189
+ const todoStores = /* @__PURE__ */ new Map();
289190
+ const todoRuntime = createTodoRuntime({
289191
+ companies: async () => (await controlPlaneStore.listCompanies()).filter((c) => c.status === "active").map((c) => c.id),
289192
+ store: (companyId) => {
289193
+ let store = todoStores.get(companyId);
289194
+ if (!store) {
289195
+ store = pgPool ? PostgresTodoStore.open(pgPool, engineSchemaFor(companyId)) : MemoryTodoStore.open(path29.join(opts.dir, "companies", companyEngineDirName(companyId), "todos.json"));
289196
+ todoStores.set(companyId, store);
289197
+ void store.catch(() => todoStores.delete(companyId));
289198
+ }
289199
+ return store;
289200
+ },
289201
+ timeZone: (companyId) => companies.service.effectiveTimeZoneOf(companyId),
289202
+ boundHuman: async (agentId, companyId) => (await (await resolveAssistantsCtx(companyId)).service.getByAgent(agentId))?.humanActorId ?? null,
289203
+ notifications: (companyId) => notificationStoreFor(companyId),
289204
+ onReminders: (companyId, records) => {
289205
+ pushNotifier?.notify(companyId, records);
289206
+ const recipients = [...new Set(records.map((r) => r.recipient))];
289207
+ console.warn(`[todos] ${companyId} \u5230\u671F\u63D0\u9192 ${records.length} \u6761 \u2192 ${recipients.join(",")}`);
289208
+ auditSink.notifyNotificationsChanged(recipients);
289209
+ },
289210
+ defaultCompanyId: () => defaultCompanyId,
289211
+ warn: (error2) => console.warn(`[todos] ${String(error2)}`)
289212
+ });
288072
289213
  let notificationLedgerWorker;
288073
289214
  let pushNotifier;
288074
289215
  let notificationLedgerTimer;
@@ -288279,7 +289420,7 @@ async function startServe(opts) {
288279
289420
  }));
288280
289421
  }
288281
289422
  const limits = { wallClockMs: wallClockForKind("planner") };
288282
- const artifactId = `artifact:planner:${(0, import_node_crypto91.randomUUID)()}`;
289423
+ const artifactId = `artifact:planner:${(0, import_node_crypto93.randomUUID)()}`;
288283
289424
  const handle = await chatRemoteAdapter.spawn({
288284
289425
  actor: planner.id,
288285
289426
  // companyId 同协调者:planner 用的就是默认公司的 actors 服务,漏签会让它一调公司域端点就 404。
@@ -288321,6 +289462,7 @@ async function startServe(opts) {
288321
289462
  companies.register,
288322
289463
  projectsDomain2.register,
288323
289464
  discussionRuntime.register,
289465
+ todoRuntime.register,
288324
289466
  typesDomain({
288325
289467
  admin: typesAdmin,
288326
289468
  resolveAdmin: async (companyId) => {
@@ -289201,10 +290343,10 @@ async function startServe(opts) {
289201
290343
  if (isDeletedActor(dispatchTarget)) {
289202
290344
  throw new ApiError(409, ACTOR_DELETED_ERROR_CODE, ACTOR_DELETED_CHAT_MESSAGE);
289203
290345
  }
289204
- const runtimeSessionId = sessionId ?? (requestDispatchId ? deterministicUuid(retargetClaimedDispatch ? `chat-runtime:${requestDispatchId}:retry:${(0, import_node_crypto91.randomUUID)()}` : `chat-runtime:${requestDispatchId}`) : (0, import_node_crypto91.randomUUID)());
290346
+ const runtimeSessionId = sessionId ?? (requestDispatchId ? deterministicUuid(retargetClaimedDispatch ? `chat-runtime:${requestDispatchId}:retry:${(0, import_node_crypto93.randomUUID)()}` : `chat-runtime:${requestDispatchId}`) : (0, import_node_crypto93.randomUUID)());
289205
290347
  const resumeRuntimeSession = Boolean(sessionId);
289206
- const traceRunId = requestDispatchId ? stableChatTraceRunId(requestDispatchId) : `chat-run:${(0, import_node_crypto91.randomUUID)()}`;
289207
- const artifactId = requestDispatchId ? `artifact:chat:${deterministicUuid(`chat-artifact:${requestDispatchId}`)}` : `artifact:chat:${(0, import_node_crypto91.randomUUID)()}`;
290348
+ const traceRunId = requestDispatchId ? stableChatTraceRunId(requestDispatchId) : `chat-run:${(0, import_node_crypto93.randomUUID)()}`;
290349
+ const artifactId = requestDispatchId ? `artifact:chat:${deterministicUuid(`chat-artifact:${requestDispatchId}`)}` : `artifact:chat:${(0, import_node_crypto93.randomUUID)()}`;
289208
290350
  let stableDispatch = requestDispatchId ? await resolveStableChatDispatch({ store: await traceStoreFor(runCompanyId), runId: traceRunId }) : null;
289209
290351
  const retargetClaim = Boolean(retargetClaimedDispatch && stableDispatch && allocatedTurn);
289210
290352
  if (stableDispatch && !stableDispatch.shouldDispatch && !retargetClaim) {
@@ -289961,6 +291103,7 @@ async function startServe(opts) {
289961
291103
  notifications: auditSink.notifications
289962
291104
  });
289963
291105
  discussionRuntime.start();
291106
+ todoRuntime.start();
289964
291107
  projectsDomain2.service.setProjectCreatedHandler(async (project) => {
289965
291108
  if (!server.knowledgeGovernance) return;
289966
291109
  await server.knowledgeGovernance.initializeProjectSpace({
@@ -290867,7 +292010,7 @@ async function startServe(opts) {
290867
292010
  console.log(`[new-engine] dispatchWork ${workId} \u662F human work\u2014\u2014\u4E0D\u6D3E runtime\uFF0C\u7B49\u5F85\u4EBA\u5DE5\u5904\u7406`);
290868
292011
  return;
290869
292012
  }
290870
- const ledgerId = opts2?.protocolRecovery?.dispatchId ?? `dispatch:${(0, import_node_crypto91.randomUUID)()}`;
292013
+ const ledgerId = opts2?.protocolRecovery?.dispatchId ?? `dispatch:${(0, import_node_crypto93.randomUUID)()}`;
290871
292014
  try {
290872
292015
  const inserted = await (await dispatchLedgerFor(companyId)).insertOpen({
290873
292016
  id: ledgerId,
@@ -290967,7 +292110,7 @@ async function startServe(opts) {
290967
292110
  const kernelModel = newKernel.model;
290968
292111
  const art = kernelModel.artifacts.get(nodeId);
290969
292112
  const revWid = art?.workspace ?? "";
290970
- const ledgerId = `dispatch:${(0, import_node_crypto91.randomUUID)()}`;
292113
+ const ledgerId = `dispatch:${(0, import_node_crypto93.randomUUID)()}`;
290971
292114
  try {
290972
292115
  await (await dispatchLedgerFor(companyId)).insertOpen({
290973
292116
  id: ledgerId,
@@ -291338,7 +292481,7 @@ async function startServe(opts) {
291338
292481
  resolveSupplementalContext: server.knowledgeGovernance ? async ({ actorId, artifactId, workOrderId, action, part, runtimeKind, taskMarkdown }) => {
291339
292482
  try {
291340
292483
  const governance = server.knowledgeGovernance;
291341
- const taskFingerprint = (0, import_node_crypto91.createHash)("sha256").update(JSON.stringify({ artifactId, workOrderId, action, part: part ?? null, taskMarkdown })).digest("hex");
292484
+ const taskFingerprint = (0, import_node_crypto93.createHash)("sha256").update(JSON.stringify({ artifactId, workOrderId, action, part: part ?? null, taskMarkdown })).digest("hex");
291342
292485
  const requestContext = {
291343
292486
  organizationId: companyId,
291344
292487
  actorId,
@@ -291811,8 +292954,8 @@ async function startServe(opts) {
291811
292954
  const quotaStarvationMs = opts.sla?.quotaStarvationMs ?? 6 * 36e5;
291812
292955
  const nodeIdleFallbackMs = opts.sla?.nodeIdleFallbackMs ?? opts.sla?.reviewStallMs ?? (Number(process.env["OASIS_SLA_NODE_IDLE_FALLBACK_MS"]) || 2 * 36e5);
291813
292956
  console.log(`[sla] \u8282\u70B9\u9759\u6B62\u515C\u5E95\u9608\u503C nodeIdleFallbackMs=${nodeIdleFallbackMs}ms\uFF08${Math.round(nodeIdleFallbackMs / 6e4)} \u5206\u949F\uFF09`);
291814
- const fmtSince = (iso8, now) => {
291815
- const min2 = Math.floor(Math.max(0, now - Date.parse(iso8)) / 6e4);
292957
+ const fmtSince = (iso9, now) => {
292958
+ const min2 = Math.floor(Math.max(0, now - Date.parse(iso9)) / 6e4);
291816
292959
  if (!Number.isFinite(min2)) return "\u521A\u624D";
291817
292960
  if (min2 < 1) return "\u4E0D\u5230 1 \u5206\u949F\u524D";
291818
292961
  if (min2 < 60) return `${min2} \u5206\u949F\u524D`;
@@ -292815,6 +293958,7 @@ ${nodeFault}` : "");
292815
293958
  })
292816
293959
  ]);
292817
293960
  await discussionRuntime.stop();
293961
+ todoRuntime.stop();
292818
293962
  for (const timer of dispatchTimers) clearInterval(timer);
292819
293963
  clearInterval(slaTimer);
292820
293964
  escalationDeliveryWorker.stop();
@@ -293841,9 +294985,9 @@ function readCodexQuota(binary, signal, timeoutMs = 15e3) {
293841
294985
 
293842
294986
  // ../cli/src/daemon/quota/claude.ts
293843
294987
  var import_node_child_process18 = require("node:child_process");
293844
- var import_promises18 = require("node:fs/promises");
294988
+ var import_promises19 = require("node:fs/promises");
293845
294989
  var import_node_os12 = require("node:os");
293846
- var import_node_path30 = require("node:path");
294990
+ var import_node_path31 = require("node:path");
293847
294991
  var import_node_util5 = require("node:util");
293848
294992
  var exec = (0, import_node_util5.promisify)(import_node_child_process18.execFile);
293849
294993
  var USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
@@ -293862,9 +295006,9 @@ async function credentials(env, signal) {
293862
295006
  }
293863
295007
  }
293864
295008
  if (!raw) {
293865
- const file = (0, import_node_path30.join)(env.CLAUDE_CONFIG_DIR || (0, import_node_path30.join)((0, import_node_os12.homedir)(), ".claude"), ".credentials.json");
293866
- if ((await (0, import_promises18.stat)(file)).size > 256 * 1024) return void 0;
293867
- raw = await (0, import_promises18.readFile)(file, { encoding: "utf8", ...signal ? { signal } : {} });
295009
+ const file = (0, import_node_path31.join)(env.CLAUDE_CONFIG_DIR || (0, import_node_path31.join)((0, import_node_os12.homedir)(), ".claude"), ".credentials.json");
295010
+ if ((await (0, import_promises19.stat)(file)).size > 256 * 1024) return void 0;
295011
+ raw = await (0, import_promises19.readFile)(file, { encoding: "utf8", ...signal ? { signal } : {} });
293868
295012
  }
293869
295013
  const oauth = object6(object6(JSON.parse(raw)).claudeAiOauth);
293870
295014
  if (typeof oauth.expiresAt === "number" && oauth.expiresAt <= Date.now()) return void 0;
@@ -293933,18 +295077,18 @@ async function readClaudeQuota(binary, signal, deps = {}) {
293933
295077
  }
293934
295078
 
293935
295079
  // ../cli/src/daemon/quota/shared-cache.ts
293936
- var import_node_crypto92 = require("node:crypto");
295080
+ var import_node_crypto94 = require("node:crypto");
293937
295081
  var import_node_fs25 = require("node:fs");
293938
295082
  var import_node_os13 = require("node:os");
293939
- var import_node_path31 = require("node:path");
295083
+ var import_node_path32 = require("node:path");
293940
295084
  init_src();
293941
295085
  function profileOf(kind, env) {
293942
- if (kind === "codex") return `codex:${env.CODEX_HOME || (0, import_node_path31.join)((0, import_node_os13.homedir)(), ".codex")}`;
293943
- if (env.CLAUDE_CODE_OAUTH_TOKEN) return `claude-token:${(0, import_node_crypto92.createHash)("sha256").update(env.CLAUDE_CODE_OAUTH_TOKEN).digest("hex")}`;
293944
- return `claude:${env.CLAUDE_CONFIG_DIR || (0, import_node_path31.join)((0, import_node_os13.homedir)(), ".claude")}`;
295086
+ if (kind === "codex") return `codex:${env.CODEX_HOME || (0, import_node_path32.join)((0, import_node_os13.homedir)(), ".codex")}`;
295087
+ if (env.CLAUDE_CODE_OAUTH_TOKEN) return `claude-token:${(0, import_node_crypto94.createHash)("sha256").update(env.CLAUDE_CODE_OAUTH_TOKEN).digest("hex")}`;
295088
+ return `claude:${env.CLAUDE_CONFIG_DIR || (0, import_node_path32.join)((0, import_node_os13.homedir)(), ".claude")}`;
293945
295089
  }
293946
295090
  function fileQuotaCache(dir, env = process.env) {
293947
- const file = (kind) => (0, import_node_path31.join)(dir, `${kind}-${(0, import_node_crypto92.createHash)("sha256").update(profileOf(kind, env)).digest("hex").slice(0, 16)}.json`);
295091
+ const file = (kind) => (0, import_node_path32.join)(dir, `${kind}-${(0, import_node_crypto94.createHash)("sha256").update(profileOf(kind, env)).digest("hex").slice(0, 16)}.json`);
293948
295092
  return {
293949
295093
  get(kind) {
293950
295094
  try {
@@ -294126,7 +295270,7 @@ var RuntimeQuotaMonitor = class _RuntimeQuotaMonitor {
294126
295270
  init_wrapper();
294127
295271
  init_src();
294128
295272
  var import_node_os14 = require("node:os");
294129
- var import_node_crypto93 = require("node:crypto");
295273
+ var import_node_crypto95 = require("node:crypto");
294130
295274
  init_src8();
294131
295275
  init_src2();
294132
295276
 
@@ -294192,7 +295336,7 @@ function detectRuntimes() {
294192
295336
 
294193
295337
  // ../cli/src/daemon/workdir-handler.ts
294194
295338
  var import_node_fs26 = __toESM(require("node:fs"), 1);
294195
- var import_node_path32 = __toESM(require("node:path"), 1);
295339
+ var import_node_path33 = __toESM(require("node:path"), 1);
294196
295340
  init_src2();
294197
295341
  init_src();
294198
295342
  var WORKDIR_READ_MAX_BYTES = 2 * 1024 * 1024;
@@ -294213,10 +295357,10 @@ function isSensitiveSegment(segment) {
294213
295357
  }
294214
295358
  function relPathIsSensitive(rel) {
294215
295359
  if (!rel) return false;
294216
- return rel.split(import_node_path32.default.sep).some((seg) => seg.length > 0 && isSensitiveSegment(seg));
295360
+ return rel.split(import_node_path33.default.sep).some((seg) => seg.length > 0 && isSensitiveSegment(seg));
294217
295361
  }
294218
295362
  function withinBase(p2, base) {
294219
- return p2 === base || p2.startsWith(base + import_node_path32.default.sep);
295363
+ return p2 === base || p2.startsWith(base + import_node_path33.default.sep);
294220
295364
  }
294221
295365
  function normalizeRel(raw) {
294222
295366
  const trimmed = (raw ?? "").trim();
@@ -294233,7 +295377,7 @@ async function trustedCanonicalContainer(req, logicalBase, dirKind) {
294233
295377
  } catch {
294234
295378
  return null;
294235
295379
  }
294236
- return isLegacy ? import_node_path32.default.join(trustedRootReal, "oasis-chat-sessions") : import_node_path32.default.join(trustedRootReal, "sessions", dirKind);
295380
+ return isLegacy ? import_node_path33.default.join(trustedRootReal, "oasis-chat-sessions") : import_node_path33.default.join(trustedRootReal, "sessions", dirKind);
294237
295381
  }
294238
295382
  async function resolveWithinWorkdir(req) {
294239
295383
  const dirKind = sessionDirKind(req.runtimeKind);
@@ -294251,11 +295395,11 @@ async function resolveWithinWorkdir(req) {
294251
295395
  } catch {
294252
295396
  return { ok: false, code: "NOT_FOUND" };
294253
295397
  }
294254
- if (import_node_path32.default.dirname(base) !== canonicalContainer) return { ok: false, code: "PATH_ESCAPE" };
295398
+ if (import_node_path33.default.dirname(base) !== canonicalContainer) return { ok: false, code: "PATH_ESCAPE" };
294255
295399
  const rel = normalizeRel(req.path);
294256
- const requested = import_node_path32.default.resolve(base, rel);
295400
+ const requested = import_node_path33.default.resolve(base, rel);
294257
295401
  if (!withinBase(requested, base)) return { ok: false, code: "PATH_ESCAPE" };
294258
- const cleanRel = base === requested ? "" : import_node_path32.default.relative(base, requested);
295402
+ const cleanRel = base === requested ? "" : import_node_path33.default.relative(base, requested);
294259
295403
  if (relPathIsSensitive(cleanRel)) return { ok: false, code: "SENSITIVE" };
294260
295404
  let real;
294261
295405
  try {
@@ -294265,7 +295409,7 @@ async function resolveWithinWorkdir(req) {
294265
295409
  return { ok: false, code: "PATH_ESCAPE" };
294266
295410
  }
294267
295411
  if (!withinBase(real, base)) return { ok: false, code: "PATH_ESCAPE" };
294268
- const realRel = base === real ? "" : import_node_path32.default.relative(base, real);
295412
+ const realRel = base === real ? "" : import_node_path33.default.relative(base, real);
294269
295413
  if (relPathIsSensitive(realRel)) return { ok: false, code: "SENSITIVE" };
294270
295414
  return { ok: true, base, real };
294271
295415
  }
@@ -294301,7 +295445,7 @@ async function handleWorkdirList(req) {
294301
295445
  const slice = truncated ? visible.slice(0, WORKDIR_LIST_MAX_ENTRIES) : visible;
294302
295446
  const entries = [];
294303
295447
  for (const d of slice) {
294304
- const abs = import_node_path32.default.join(anchor, d.name);
295448
+ const abs = import_node_path33.default.join(anchor, d.name);
294305
295449
  try {
294306
295450
  const st = await import_node_fs26.default.promises.lstat(abs);
294307
295451
  if (st.isSymbolicLink()) continue;
@@ -294368,7 +295512,7 @@ async function verifyOpenedFd(fh, base, fallback) {
294368
295512
  const fdReal = await fdCanonicalPath(fh);
294369
295513
  if (fdReal === null) return { anchor: fallback };
294370
295514
  if (!withinBase(fdReal, base)) return { error: { ok: false, code: "PATH_ESCAPE" } };
294371
- const fdRel = base === fdReal ? "" : import_node_path32.default.relative(base, fdReal);
295515
+ const fdRel = base === fdReal ? "" : import_node_path33.default.relative(base, fdReal);
294372
295516
  if (relPathIsSensitive(fdRel)) return { error: { ok: false, code: "SENSITIVE" } };
294373
295517
  return { anchor: `/proc/self/fd/${fh.fd}` };
294374
295518
  }
@@ -294462,7 +295606,7 @@ function looksBinary(bytes2) {
294462
295606
  function contentTypeFor(absPath, bytes2) {
294463
295607
  const sniffed = sniffContentType(bytes2);
294464
295608
  if (sniffed) return sniffed;
294465
- const ext = import_node_path32.default.extname(absPath).toLowerCase();
295609
+ const ext = import_node_path33.default.extname(absPath).toLowerCase();
294466
295610
  if (EXT_CONTENT_TYPE[ext]) return EXT_CONTENT_TYPE[ext];
294467
295611
  return looksBinary(bytes2) ? "application/octet-stream" : "text/plain; charset=utf-8";
294468
295612
  }
@@ -295045,7 +296189,7 @@ var DaemonWsClient = class {
295045
296189
  */
295046
296190
  async queryArtifactStates(artifactIds, timeoutMs = 15e3) {
295047
296191
  if (artifactIds.length === 0) return {};
295048
- const requestId = (0, import_node_crypto93.randomUUID)();
296192
+ const requestId = (0, import_node_crypto95.randomUUID)();
295049
296193
  return new Promise((resolve11, reject) => {
295050
296194
  const timer = setTimeout(() => {
295051
296195
  this.gcPending.delete(requestId);
@@ -295225,17 +296369,17 @@ var RuntimeRouterAdapter = class {
295225
296369
  // ../cli/src/daemon/reap-claude-projects.ts
295226
296370
  var import_node_fs27 = __toESM(require("node:fs"), 1);
295227
296371
  var import_node_os15 = __toESM(require("node:os"), 1);
295228
- var import_node_path33 = __toESM(require("node:path"), 1);
296372
+ var import_node_path34 = __toESM(require("node:path"), 1);
295229
296373
  function claudeProjectSlug(cwd) {
295230
296374
  return cwd.replace(/[^a-zA-Z0-9]/g, "-");
295231
296375
  }
295232
296376
  function claudeProjectsRoot() {
295233
- const configDir = process.env["CLAUDE_CONFIG_DIR"] || import_node_path33.default.join(import_node_os15.default.homedir(), ".claude");
295234
- return import_node_path33.default.join(configDir, "projects");
296377
+ const configDir = process.env["CLAUDE_CONFIG_DIR"] || import_node_path34.default.join(import_node_os15.default.homedir(), ".claude");
296378
+ return import_node_path34.default.join(configDir, "projects");
295235
296379
  }
295236
296380
  function reapClaudeProjects(workdir, runtimeKind) {
295237
296381
  if (runtimeKind !== "claude" && runtimeKind !== "claude-code") return;
295238
- const dir = import_node_path33.default.join(claudeProjectsRoot(), claudeProjectSlug(workdir));
296382
+ const dir = import_node_path34.default.join(claudeProjectsRoot(), claudeProjectSlug(workdir));
295239
296383
  if (!import_node_fs27.default.existsSync(dir)) return;
295240
296384
  try {
295241
296385
  import_node_fs27.default.rmSync(dir, { recursive: true, force: true });
@@ -295244,7 +296388,7 @@ function reapClaudeProjects(workdir, runtimeKind) {
295244
296388
  }
295245
296389
  function measureClaudeProjects(workdir, runtimeKind) {
295246
296390
  if (runtimeKind !== "claude" && runtimeKind !== "claude-code") return 0;
295247
- const dir = import_node_path33.default.join(claudeProjectsRoot(), claudeProjectSlug(workdir));
296391
+ const dir = import_node_path34.default.join(claudeProjectsRoot(), claudeProjectSlug(workdir));
295248
296392
  return dirSizeBytes2(dir);
295249
296393
  }
295250
296394
  function dirSizeBytes2(dir) {
@@ -295256,7 +296400,7 @@ function dirSizeBytes2(dir) {
295256
296400
  return 0;
295257
296401
  }
295258
296402
  for (const e of entries) {
295259
- const p2 = import_node_path33.default.join(dir, e.name);
296403
+ const p2 = import_node_path34.default.join(dir, e.name);
295260
296404
  if (e.isSymbolicLink()) continue;
295261
296405
  if (e.isDirectory()) {
295262
296406
  total += dirSizeBytes2(p2);
@@ -295273,7 +296417,7 @@ function dirSizeBytes2(dir) {
295273
296417
  // ../cli/src/node.ts
295274
296418
  init_src2();
295275
296419
  var import_node_os16 = __toESM(require("node:os"), 1);
295276
- var import_node_path34 = __toESM(require("node:path"), 1);
296420
+ var import_node_path35 = __toESM(require("node:path"), 1);
295277
296421
  var idleFor = (runtimeKind) => CODE_DEFAULT_POLICY.resolve({ executor: "agent", scenario: "runtime-idle", runtimeKind }).idleTimeoutMs ?? 0;
295278
296422
  var RESILIENCE = {
295279
296423
  codex: { idleTimeoutMs: idleFor("codex") }
@@ -295345,7 +296489,7 @@ async function startNode(opts) {
295345
296489
  const client = new DaemonWsClient(opts.serverUrl, opts.nodeId, sessions, adapters, opts.token, runtimes, opts.nodeName, opts.reportRuntimes ?? false, true, opts.onUpdate, opts.daemonVersion, {
295346
296490
  ...opts.daemonPkg ? { daemonPkg: opts.daemonPkg } : {},
295347
296491
  // 同一个 OS 用户下的所有实例(~/.oasis/instances/*)共用一份额度采集结果:同账号 5 分钟只问一次上游。
295348
- quotaCacheDir: import_node_path34.default.join(import_node_os16.default.homedir(), ".oasis", "quota-cache"),
296492
+ quotaCacheDir: import_node_path35.default.join(import_node_os16.default.homedir(), ".oasis", "quota-cache"),
295349
296493
  // chat 文件浏览 handler 用它定位会话目录;production 不传 = 与 adapter 同款默认根(~/.oasis/work)。
295350
296494
  ...opts.workRoot !== void 0 ? { workRoot: opts.workRoot } : {},
295351
296495
  ...opts.spawnSessionProcess ? { spawnSessionProcess: opts.spawnSessionProcess } : {},
@@ -295464,7 +296608,7 @@ function forwardSignal(child, sig, killGroup = (pgid, signal) => {
295464
296608
  // ../cli/src/daemon/machine-id.ts
295465
296609
  var import_node_child_process20 = require("node:child_process");
295466
296610
  var import_node_fs28 = require("node:fs");
295467
- var import_node_crypto94 = require("node:crypto");
296611
+ var import_node_crypto96 = require("node:crypto");
295468
296612
  var import_node_os17 = require("node:os");
295469
296613
  function linuxMachineId() {
295470
296614
  for (const p2 of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
@@ -295530,7 +296674,7 @@ var defaultSources = {
295530
296674
  function resolveNodeId(sources = {}) {
295531
296675
  const s2 = { ...defaultSources, ...sources };
295532
296676
  const material = `${s2.machineFingerprint()}:${s2.osUser()}`;
295533
- const digest = (0, import_node_crypto94.createHash)("sha256").update(material).digest("hex").slice(0, 12);
296677
+ const digest = (0, import_node_crypto96.createHash)("sha256").update(material).digest("hex").slice(0, 12);
295534
296678
  return `node-${digest}`;
295535
296679
  }
295536
296680
 
@@ -297211,6 +298355,76 @@ var COMMAND_DECLS = {
297211
298355
  ],
297212
298356
  examples: ["oasis var rm STALE_KEY --scope project --project oasis", "oasis var rm --id var_1234 --yes"]
297213
298357
  },
298358
+ // —— 个人待办(ADR 0635):人对助理说「帮我记一下」→ 助理用这组命令记、改、勾掉。发起人 / 承接人由服务端按调用者
298359
+ // 填(agent 调 = 它绑定的那个真人),命令行不收。开关 OASIS_PERSONAL_TODO=off 时服务端 404。 ——
298360
+ todo: {
298361
+ group: "\u4E2A\u4EBA\u5F85\u529E",
298362
+ usage: "oasis todo <add|list|edit|done|cancel|reopen> ...",
298363
+ description: "\u4E2A\u4EBA\u5F85\u529E\uFF1A\u66FF\u4F60\u7ED1\u5B9A\u7684\u90A3\u4E2A\u771F\u4EBA\u8BB0\u4E0B\u6709\u622A\u6B62\u65E5\u7684\u4E8B\uFF0C\u5230\u671F\u524D\u4E00\u5929 09:00\u3001\u5F53\u5929 09:00 \u7CFB\u7EDF\u4F1A\u63D0\u9192 TA\uFF08\u8BBE\u4E86\u5F00\u59CB\u65E5\u7684\uFF0C\u5F00\u59CB\u5F53\u5929 09:00 \u4E5F\u63D0\u9192\u4E00\u6B21\uFF09\u3002\u6BCF\u6761\u8F93\u51FA\u7B2C\u4E00\u884C\u662F\u300C\u4ECA\u5929\uFF1AYYYY-MM-DD \u5468X\uFF08\u65F6\u533A\uFF09\u300D\u2014\u2014\u6362\u7B97\u300C\u8FD9\u5468\u4E94\u300D\u300C\u6708\u5E95\u300D\u4E00\u5F8B\u4EE5\u5B83\u4E3A\u51C6\u3002**\u6CA1\u6709 `oasis todo` \u547D\u4EE4\u6267\u884C\u6210\u529F\uFF0C\u5C31\u4E0D\u80FD\u8BF4\u8BB0\u4E0B\u4E86\u3002**",
298364
+ subCommands: ["todo add", "todo list", "todo edit", "todo done", "todo cancel", "todo reopen"],
298365
+ examples: [
298366
+ 'oasis todo add --title "\u4EA4 Q3 \u590D\u76D8\u62A5\u544A" --note "\u738B\u603B\u8981\uFF0C\u5468\u4F1A\u524D\u8FC7\u4E00\u904D" --due 2026-10-09',
298367
+ "oasis todo list",
298368
+ 'oasis todo edit td_3f9a1c2b --due 2026-10-12 --reason "\u6392\u671F\u8C03\u6574"',
298369
+ "oasis todo done td_3f9a1c2b"
298370
+ ]
298371
+ },
298372
+ "todo add": {
298373
+ group: "\u4E2A\u4EBA\u5F85\u529E",
298374
+ usage: "oasis todo add --title <\u4E00\u53E5\u8BDD\uFF0C\u52A8\u8BCD\u5F00\u5934> [--note <\u8C01\u8981\u3001\u4E3A\u4EC0\u4E48\u3001\u539F\u8BDD\u8981\u70B9>] [--start YYYY-MM-DD [--start-time HH:mm]] --due YYYY-MM-DD [--time HH:mm]",
298375
+ description: "\u8BB0\u4E00\u6761\u5F85\u529E\u3002--due / --start \u53EA\u6536\u4E0D\u5E26\u65F6\u533A\u7684\u65E5\u671F\uFF08\u7EC4\u7EC7\u65F6\u533A\u91CC\u7684\u90A3\u4E00\u5929\uFF09\uFF0C\u975E\u6CD5\u65E5\u671F\u5F53\u573A\u62A5\u9519\uFF1B\u6CA1\u8BF4\u51E0\u70B9\u5C31\u522B\u7ED9 --time\uFF1B\u7528\u6237\u6CA1\u8BF4\u4EC0\u4E48\u65F6\u5019\u5F00\u59CB\u5C31\u522B\u7ED9 --start\uFF08\u4E0D\u8FFD\u95EE\u3001\u4E0D\u731C\uFF09\uFF0C\u5F00\u59CB\u4E0D\u80FD\u665A\u4E8E\u622A\u6B62\u3002\u6765\u6E90\u5BF9\u8BDD\u81EA\u52A8\u53D6 $OASIS_CHAT_SESSION_ID\uFF08\u63D0\u9192\u70B9\u5F00\u4F1A\u8DF3\u56DE\u8FD9\u91CC\uFF09\u3002\u6210\u529F\u8F93\u51FA\u300C\u8BB0\u4E0B\u4E86\uFF1A<\u6807\u9898>\uFF0C\u622A\u6B62 X \u6708 X \u65E5\uFF08\u5468X\uFF09\u300D\u3002",
298376
+ flags: [
298377
+ { name: "title", desc: "\u6807\u9898\uFF1A\u4E00\u53E5\u8BDD\u3001\u52A8\u8BCD\u5F00\u5934", required: true, own: true },
298378
+ { name: "note", desc: "\u5907\u6CE8\uFF1A\u8C01\u8981\u3001\u4E3A\u4EC0\u4E48\u8981\u3001\u539F\u8BDD\u91CC\u7684\u5173\u952E\u4FE1\u606F", own: true },
298379
+ { name: "due", desc: "\u622A\u6B62\u65E5 YYYY-MM-DD\uFF08\u4E0D\u5E26\u65F6\u533A\uFF09", required: true, own: true },
298380
+ { name: "time", desc: "\u622A\u6B62\u65F6\u523B HH:mm\uFF0824 \u5C0F\u65F6\u5236\uFF1B\u6CA1\u8BF4\u5C31\u4E0D\u7ED9\uFF09", own: true },
298381
+ { name: "start", desc: "\u5F00\u59CB\u65E5 YYYY-MM-DD\uFF08\u7528\u6237\u8BF4\u4E86\u300C\u54EA\u5929\u5F00\u59CB\u5F04\u300D\u624D\u7ED9\uFF09", own: true },
298382
+ { name: "start-time", desc: "\u5F00\u59CB\u65F6\u523B HH:mm\uFF08\u914D --start\uFF09", own: true }
298383
+ ],
298384
+ examples: ['oasis todo add --title "\u4EA4 Q3 \u590D\u76D8\u62A5\u544A" --due 2026-10-09 --time 18:00']
298385
+ },
298386
+ "todo list": {
298387
+ group: "\u4E2A\u4EBA\u5F85\u529E",
298388
+ usage: "oasis todo list [--all]",
298389
+ description: "\u5217\u51FA\u5F85\u529E\uFF0C\u6309\u622A\u6B62\u65E5\u6392\u5E8F\uFF0C\u8FC7\u671F\u7684\u6807\u300C\u5DF2\u8FC7\u671F\u300D\uFF1B\u8BBE\u4E86\u5F00\u59CB\u65E5\u7684\u663E\u793A\u300CX \u6708 X \u65E5\u5F00\u59CB \u2192 X \u6708 X \u65E5\u622A\u6B62\u300D\u3002\u9ED8\u8BA4\u53EA\u5217\u672A\u5B8C\u6210\u7684\uFF1B--all \u8FDE\u5DF2\u5B8C\u6210\u3001\u5DF2\u53D6\u6D88\u7684\u4E00\u8D77\u5217\u3002",
298390
+ flags: [{ name: "all", desc: "\u8FDE\u5DF2\u5B8C\u6210 / \u5DF2\u53D6\u6D88\u7684\u4E00\u8D77\u5217", boolean: true, own: true }]
298391
+ },
298392
+ "todo edit": {
298393
+ group: "\u4E2A\u4EBA\u5F85\u529E",
298394
+ usage: "oasis todo edit <id> [--title \u2026] [--note \u2026] [--start YYYY-MM-DD|none] [--start-time HH:mm|none] [--due YYYY-MM-DD] [--time HH:mm|none] [--workorder <\u5DE5\u5355 id>|none] [--reason \u2026]",
298395
+ description: '\u6539\u4E00\u6761\u5F85\u529E\u7684\u5B57\u6BB5\uFF0C\u7ED9\u4E86\u54EA\u4E2A\u6539\u54EA\u4E2A\uFF1B\u6BCF\u6539\u4E00\u4E2A\u5B57\u6BB5\u90FD\u7559\u4E00\u6761\u8BB0\u5F55\uFF08\u65E7\u503C\u3001\u65B0\u503C\u3001\u539F\u56E0\uFF09\u3002\u6539\u671F\u8981\u5199\u539F\u56E0\uFF1A\u7528\u6237\u6CA1\u8BF4\u5C31\u95EE\u4E00\u53E5\uFF0C\u7528\u6237\u8BF4\u4E0D\u7528\u5C31\u4E0D\u5199\uFF0C\u522B\u7F16\u3002--start none\uFF08\u8FDE\u540C\u5F00\u59CB\u65F6\u523B\uFF09/ --start-time none / --time none / --workorder none \u6E05\u7A7A\uFF0C--note "" \u6E05\u7A7A\u5907\u6CE8\u3002',
298396
+ positional: [{ name: "<id>", required: true, desc: "\u5F85\u529E id\uFF08oasis todo list \u7B2C\u4E00\u5217\uFF09" }],
298397
+ flags: [
298398
+ { name: "title", desc: "\u65B0\u6807\u9898", own: true },
298399
+ { name: "note", desc: "\u65B0\u5907\u6CE8\uFF08\u7A7A\u4E32 = \u6E05\u7A7A\uFF09", own: true },
298400
+ { name: "start", desc: "\u65B0\u5F00\u59CB\u65E5 YYYY-MM-DD\uFF1Bnone = \u6E05\u7A7A\uFF08\u8FDE\u540C\u5F00\u59CB\u65F6\u523B\uFF09", own: true },
298401
+ { name: "start-time", desc: "\u65B0\u5F00\u59CB\u65F6\u523B HH:mm\uFF1Bnone = \u6E05\u7A7A", own: true },
298402
+ { name: "due", desc: "\u65B0\u622A\u6B62\u65E5 YYYY-MM-DD", own: true },
298403
+ { name: "time", desc: "\u65B0\u622A\u6B62\u65F6\u523B HH:mm\uFF1Bnone = \u6E05\u7A7A", own: true },
298404
+ { name: "workorder", desc: "\u5173\u8054\u7684\u5DE5\u5355 id\uFF08\u300C\u8FD9\u4EF6\u4F60\u5E2E\u6211\u505A\u4E86\u5427\u300D\u5EFA\u5355\u540E\u6302\u4E0A\uFF09\uFF1Bnone = \u6E05\u7A7A", own: true },
298405
+ { name: "reason", desc: "\u4E3A\u4EC0\u4E48\u6539\uFF08\u6539\u671F\u65F6\u8981\u5199\uFF1B\u7528\u6237\u8BF4\u4E0D\u7528\u5C31\u4E0D\u5199\uFF09", own: true }
298406
+ ],
298407
+ examples: ['oasis todo edit td_3f9a1c2b --due 2026-10-12 --reason "\u5BA2\u6237\u5EF6\u671F\u4E00\u5468"']
298408
+ },
298409
+ "todo done": {
298410
+ group: "\u4E2A\u4EBA\u5F85\u529E",
298411
+ usage: "oasis todo done <id>",
298412
+ description: "\u6807\u8BB0\u5B8C\u6210\u3002\u5339\u914D\u5230\u591A\u6761\u65F6\u5148\u95EE\u7528\u6237\u662F\u54EA\u6761\uFF0C\u522B\u731C\u3002",
298413
+ positional: [{ name: "<id>", required: true, desc: "\u5F85\u529E id" }]
298414
+ },
298415
+ "todo cancel": {
298416
+ group: "\u4E2A\u4EBA\u5F85\u529E",
298417
+ usage: "oasis todo cancel <id> [--reason \u2026]",
298418
+ description: "\u53D6\u6D88\uFF08\u4E0D\u7528\u505A\u4E86\uFF09\u3002\u53EA\u6539\u72B6\u6001\u3001\u4E0D\u5220\u9664\uFF0C--all \u5217\u8868\u91CC\u8FD8\u770B\u5F97\u5230\u3002",
298419
+ positional: [{ name: "<id>", required: true, desc: "\u5F85\u529E id" }],
298420
+ flags: [{ name: "reason", desc: "\u4E3A\u4EC0\u4E48\u4E0D\u7528\u505A\u4E86", own: true }]
298421
+ },
298422
+ "todo reopen": {
298423
+ group: "\u4E2A\u4EBA\u5F85\u529E",
298424
+ usage: "oasis todo reopen <id>",
298425
+ description: "\u628A\u5DF2\u5B8C\u6210 / \u5DF2\u53D6\u6D88\u7684\u5F85\u529E\u91CD\u65B0\u6253\u5F00\u3002",
298426
+ positional: [{ name: "<id>", required: true, desc: "\u5F85\u529E id" }]
298427
+ },
297214
298428
  // —— 技能:组织技能库的 CLI 面(Web 上是「管理 > 技能」那一页)。
297215
298429
  // agent 把自己写好的一个技能目录沉淀进公司,并决定它对谁生效。 ——
297216
298430
  skill: {
@@ -297300,21 +298514,21 @@ function formatCommandHelp(decl) {
297300
298514
  const posNames = (decl.positional ?? []).map((p2) => p2.name);
297301
298515
  const flagNames = (decl.flags ?? []).map((f2) => `--${f2.name}`);
297302
298516
  const allNames = [...posNames, ...flagNames];
297303
- const pad2 = allNames.length > 0 ? Math.max(...allNames.map((n) => n.length)) + 2 : 0;
298517
+ const pad3 = allNames.length > 0 ? Math.max(...allNames.map((n) => n.length)) + 2 : 0;
297304
298518
  const reqPos = (decl.positional ?? []).filter((p2) => p2.required);
297305
298519
  const reqFlags = (decl.flags ?? []).filter((f2) => f2.required);
297306
298520
  if (reqPos.length > 0 || reqFlags.length > 0) {
297307
298521
  lines.push("\u5FC5\u586B\uFF1A");
297308
- for (const p2 of reqPos) lines.push(` ${p2.name.padEnd(pad2)} ${p2.desc}`);
297309
- for (const f2 of reqFlags) lines.push(` --${f2.name.padEnd(pad2 - 2)} ${f2.desc}`);
298522
+ for (const p2 of reqPos) lines.push(` ${p2.name.padEnd(pad3)} ${p2.desc}`);
298523
+ for (const f2 of reqFlags) lines.push(` --${f2.name.padEnd(pad3 - 2)} ${f2.desc}`);
297310
298524
  lines.push("");
297311
298525
  }
297312
298526
  const optPos = (decl.positional ?? []).filter((p2) => !p2.required);
297313
298527
  const optFlags = (decl.flags ?? []).filter((f2) => !f2.required);
297314
298528
  if (optPos.length > 0 || optFlags.length > 0) {
297315
298529
  lines.push("\u53EF\u9009\uFF1A");
297316
- for (const p2 of optPos) lines.push(` ${p2.name.padEnd(pad2)} ${p2.desc}`);
297317
- for (const f2 of optFlags) lines.push(` --${f2.name.padEnd(pad2 - 2)} ${f2.desc}`);
298530
+ for (const p2 of optPos) lines.push(` ${p2.name.padEnd(pad3)} ${p2.desc}`);
298531
+ for (const f2 of optFlags) lines.push(` --${f2.name.padEnd(pad3 - 2)} ${f2.desc}`);
297318
298532
  lines.push("");
297319
298533
  }
297320
298534
  lines.push(`\u8BF4\u660E\uFF1A${decl.description}`);
@@ -297341,6 +298555,7 @@ var GROUP_ORDER = [
297341
298555
  "\u957F\u671F\u8BB0\u5FC6\uFF08ADR-0084/0102\uFF09",
297342
298556
  "\u77E5\u8BC6\u5E93",
297343
298557
  "\u59D4\u6D3E\u4E13\u5BB6",
298558
+ "\u4E2A\u4EBA\u5F85\u529E",
297344
298559
  "\u751F\u547D\u5468\u671F",
297345
298560
  "\u89D2\u8272 & \u4EBA\u5458",
297346
298561
  "\u8BFB\u4EA7\u7269",
@@ -297405,13 +298620,13 @@ function ownFlagsFromDecl(command2) {
297405
298620
  }
297406
298621
 
297407
298622
  // ../cli/src/connector-effect-runner.ts
297408
- var import_node_crypto95 = require("node:crypto");
298623
+ var import_node_crypto97 = require("node:crypto");
297409
298624
  var import_node_child_process21 = require("node:child_process");
297410
298625
  init_src8();
297411
298626
  function stableKey(effect) {
297412
298627
  const keyFields = Object.fromEntries(Object.entries(effect.keyFields).sort(([a], [b2]) => a.localeCompare(b2)));
297413
298628
  const canonical2 = JSON.stringify({ kind: effect.kind, target: effect.target, keyFields });
297414
- return `sha256:${(0, import_node_crypto95.createHash)("sha256").update(canonical2).digest("hex")}`;
298629
+ return `sha256:${(0, import_node_crypto97.createHash)("sha256").update(canonical2).digest("hex")}`;
297415
298630
  }
297416
298631
  async function runConnectorEffect(connector, toolArgs, deps) {
297417
298632
  const effect = connector.describeExternalEffect?.(toolArgs) ?? null;
@@ -297630,8 +298845,8 @@ function formatSize(size) {
297630
298845
  if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)}KB`;
297631
298846
  return `${(size / 1024 / 1024).toFixed(1)}MB`;
297632
298847
  }
297633
- function formatTime(iso8) {
297634
- return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(iso8) ? `${iso8.slice(5, 10)} ${iso8.slice(11, 16)}` : iso8;
298848
+ function formatTime(iso9) {
298849
+ return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(iso9) ? `${iso9.slice(5, 10)} ${iso9.slice(11, 16)}` : iso9;
297635
298850
  }
297636
298851
  function displayName2(name) {
297637
298852
  const cut = name.lastIndexOf("/");
@@ -297980,7 +299195,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
297980
299195
  const timeoutMs = resolveAuthWaitMs();
297981
299196
  const controller = new AbortController();
297982
299197
  const timer = setTimeout(() => controller.abort(), timeoutMs);
297983
- const waiterId = (0, import_node_crypto96.randomUUID)();
299198
+ const waiterId = (0, import_node_crypto98.randomUUID)();
297984
299199
  process.stderr.write(
297985
299200
  `\u7B49\u5F85\u4EBA\u5DE5\u6388\u6743\u4E2D\u2026\u2026\u6700\u591A ${Math.round(timeoutMs / 1e3)} \u79D2\uFF0C\u8D85\u65F6\u4F1A\u81EA\u52A8\u8F6C\u4E3A\u5F85\u5BA1\uFF1B\u8FD9\u4E0D\u662F\u9519\u8BEF\uFF0C\u8BF7\u52FF\u91CD\u8BD5\u540C\u4E00\u6761\u547D\u4EE4\u3002
297986
299201
  `
@@ -298064,7 +299279,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
298064
299279
  }
298065
299280
  }
298066
299281
  }
298067
- var CLI_VERSION = "2.2.105" ? "2.2.105" : "dev";
299282
+ var CLI_VERSION = "2.2.106" ? "2.2.106" : "dev";
298068
299283
  function apiHeaders(token2, company) {
298069
299284
  const headers = {
298070
299285
  authorization: `Bearer ${token2}`,
@@ -298154,7 +299369,7 @@ function fieldsFromFlags(flags, ownFlags) {
298154
299369
  }
298155
299370
  return Object.keys(fields).length > 0 ? fields : void 0;
298156
299371
  }
298157
- function buildStageOp(cmd, flags, positional, readFile12 = (file) => fs44.readFileSync(file, "utf8")) {
299372
+ function buildStageOp(cmd, flags, positional, readFile13 = (file) => fs44.readFileSync(file, "utf8")) {
298158
299373
  switch (cmd) {
298159
299374
  case "link":
298160
299375
  return {
@@ -298176,7 +299391,7 @@ function buildStageOp(cmd, flags, positional, readFile12 = (file) => fs44.readFi
298176
299391
  ...flags.get("title") !== void 0 ? { title: flags.get("title") } : {},
298177
299392
  ...(flags.get("brief") ?? flags.get("description")) !== void 0 ? { description: flags.get("brief") ?? flags.get("description") } : {},
298178
299393
  ...flags.get("input") !== void 0 ? { inputs: flags.get("input").split(",").map((to) => ({ to: to.trim() })) } : {},
298179
- ...partsFile !== void 0 ? { parts: JSON.parse(readFile12(partsFile)) } : {},
299394
+ ...partsFile !== void 0 ? { parts: JSON.parse(readFile13(partsFile)) } : {},
298180
299395
  ...fields !== void 0 ? { fields } : {}
298181
299396
  };
298182
299397
  }
@@ -298430,7 +299645,7 @@ async function runCli(argv, println = console.log, progressln = console.error) {
298430
299645
  const store = createNodeTokenStore(path39.join(dir, "node-tokens.json"));
298431
299646
  const sub = positional[0];
298432
299647
  if (sub === "issue") {
298433
- const id = flags.get("id") ?? `node-${(0, import_node_crypto96.randomUUID)()}`;
299648
+ const id = flags.get("id") ?? `node-${(0, import_node_crypto98.randomUUID)()}`;
298434
299649
  const token3 = store.issue(id);
298435
299650
  println(token3);
298436
299651
  process.stderr.write(
@@ -299582,7 +300797,7 @@ ${t.ref} ${t.name} [\u5267\u672C=${t.playbookRef}]`);
299582
300797
  if (!name) throw new Error("\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
299583
300798
  const goal = flags.get("goal")?.trim();
299584
300799
  const slug5 = flags.get("slug");
299585
- const id = slug5 !== void 0 ? projectSlug(slug5) : projectIdFromUuid((0, import_node_crypto96.randomUUID)());
300800
+ const id = slug5 !== void 0 ? projectSlug(slug5) : projectIdFromUuid((0, import_node_crypto98.randomUUID)());
299586
300801
  const result = await api.request("POST", "/api/projects", {
299587
300802
  ...id ? { id } : {},
299588
300803
  name,
@@ -300434,6 +301649,95 @@ ${res.warning}`);
300434
301649
  oasis var rm <key> --scope \u2026 | oasis var rm --id <\u8BB0\u5F55 id> [--yes]
300435
301650
  \u540E\u4E09\u4E2A\u662F\u7BA1\u7406\u9762\uFF0C\u4EC5\u9650\u4EBA\u7C7B operator token\u3002`);
300436
301651
  }
301652
+ case "todo": {
301653
+ const sub = positional[0];
301654
+ const due = (t) => formatTodoSpan(t);
301655
+ const call = async (method, url, body2) => {
301656
+ try {
301657
+ return await api.request(method, url, body2);
301658
+ } catch (e) {
301659
+ if (e instanceof ApiRequestError && e.status === 404 && e.body?.error?.code === "NOT_FOUND") {
301660
+ throw new Error("\u4E2A\u4EBA\u5F85\u529E\u6CA1\u6709\u5F00\u542F\uFF08\u670D\u52A1\u7AEF OASIS_PERSONAL_TODO=off\uFF09\uFF0C\u8FD9\u6761\u6CA1\u6709\u8BB0\u4E0B\u3002");
301661
+ }
301662
+ throw e;
301663
+ }
301664
+ };
301665
+ const checkDay = (v2, flag) => {
301666
+ if (!isValidDay(v2)) throw new Error(`--${flag} \u53EA\u6536\u4E0D\u5E26\u65F6\u533A\u7684\u65E5\u671F YYYY-MM-DD\uFF08\u4F8B\uFF1A2026-10-09\uFF09\uFF0C\u6536\u5230\uFF1A${v2}`);
301667
+ return v2;
301668
+ };
301669
+ const checkTime = (v2, flag = "time") => {
301670
+ if (!isValidTodoTime(v2)) throw new Error(`--${flag} \u53EA\u6536 24 \u5C0F\u65F6\u5236 HH:mm\uFF08\u4F8B\uFF1A18:00\uFF09\uFF0C\u6536\u5230\uFF1A${v2}`);
301671
+ return v2;
301672
+ };
301673
+ const idOf = () => needPos(positional, 1, `oasis todo ${sub} <id>`);
301674
+ const todoPath = (id, tail = "") => `/api/todos/${encodeURIComponent(id)}${tail}`;
301675
+ if (sub === "add") {
301676
+ const title = need(flags, "title");
301677
+ const dueDate = checkDay(need(flags, "due"), "due");
301678
+ const time3 = flags.get("time");
301679
+ const note = flags.get("note");
301680
+ const start = flags.get("start");
301681
+ const startTime = flags.get("start-time");
301682
+ if (startTime !== void 0 && start === void 0) throw new Error("--start-time \u8981\u914D --start\uFF08\u5148\u8BF4\u54EA\u5929\u5F00\u59CB\uFF09");
301683
+ if (start !== void 0 && start > dueDate && isValidDay(start)) throw new Error(`--start ${start} \u665A\u4E8E --due ${dueDate}\uFF1A\u5F00\u59CB\u4E0D\u80FD\u665A\u4E8E\u622A\u6B62`);
301684
+ const sourceChatSessionId = process.env["OASIS_CHAT_SESSION_ID"];
301685
+ const r = await call("POST", "/api/todos", {
301686
+ title,
301687
+ dueDate,
301688
+ ...time3 !== void 0 ? { dueTime: checkTime(time3) } : {},
301689
+ ...start !== void 0 ? { startDate: checkDay(start, "start") } : {},
301690
+ ...startTime !== void 0 ? { startTime: checkTime(startTime, "start-time") } : {},
301691
+ ...note ? { note } : {},
301692
+ ...sourceChatSessionId ? { sourceChatSessionId } : {}
301693
+ });
301694
+ println(r.todayLine);
301695
+ println(`\u8BB0\u4E0B\u4E86\uFF1A${r.todo.title}\uFF0C${due(r.todo)}`);
301696
+ println(`id\uFF1A${r.todo.id}`);
301697
+ break;
301698
+ }
301699
+ if (sub === "list") {
301700
+ const all2 = flags.get("all") === "true";
301701
+ const r = await call("GET", `/api/todos${all2 ? "?all=1" : ""}`);
301702
+ println(r.todayLine);
301703
+ if (r.items.length === 0) {
301704
+ println(all2 ? "\u6CA1\u6709\u5F85\u529E\u3002" : "\u6CA1\u6709\u672A\u5B8C\u6210\u7684\u5F85\u529E\u3002");
301705
+ break;
301706
+ }
301707
+ for (const t of r.items) {
301708
+ const tag = t.status === "done" ? " [\u5DF2\u5B8C\u6210]" : t.status === "cancelled" ? " [\u5DF2\u53D6\u6D88]" : t.dueDate < r.today.date ? " [\u5DF2\u8FC7\u671F]" : "";
301709
+ const when = t.startDate ? formatTodoSpan(t) : formatTodoDue(t.dueDate, t.dueTime);
301710
+ println(`${t.id} ${when} ${t.title}${tag}`);
301711
+ }
301712
+ break;
301713
+ }
301714
+ if (sub === "edit") {
301715
+ const id = idOf();
301716
+ const body2 = {};
301717
+ if (flags.has("title")) body2["title"] = flags.get("title");
301718
+ if (flags.has("note")) body2["note"] = flags.get("note") === "" ? null : flags.get("note");
301719
+ if (flags.has("due")) body2["dueDate"] = checkDay(flags.get("due"), "due");
301720
+ if (flags.has("time")) body2["dueTime"] = flags.get("time") === "none" ? null : checkTime(flags.get("time"));
301721
+ if (flags.has("start")) body2["startDate"] = flags.get("start") === "none" ? null : checkDay(flags.get("start"), "start");
301722
+ if (flags.has("start-time")) body2["startTime"] = flags.get("start-time") === "none" ? null : checkTime(flags.get("start-time"), "start-time");
301723
+ if (flags.has("workorder")) body2["linkedWorkorderId"] = flags.get("workorder") === "none" ? null : flags.get("workorder");
301724
+ if (Object.keys(body2).length === 0) throw new Error("\u6CA1\u6709\u8981\u6539\u7684\u5B57\u6BB5\uFF1A\u7ED9 --title / --note / --start / --start-time / --due / --time / --workorder \u91CC\u7684\u81F3\u5C11\u4E00\u4E2A");
301725
+ if (flags.get("reason")) body2["reason"] = flags.get("reason");
301726
+ const r = await call("PATCH", todoPath(id), body2);
301727
+ println(r.todayLine);
301728
+ println(`\u5DF2\u6539\uFF1A${r.todo.title}\uFF0C${due(r.todo)}`);
301729
+ break;
301730
+ }
301731
+ if (sub === "done" || sub === "cancel" || sub === "reopen") {
301732
+ const id = idOf();
301733
+ const reason = sub === "cancel" ? flags.get("reason") : void 0;
301734
+ const r = await call("POST", todoPath(id, `/${sub}`), reason ? { reason } : {});
301735
+ println(r.todayLine);
301736
+ println(`${sub === "done" ? "\u5DF2\u5B8C\u6210" : sub === "cancel" ? "\u5DF2\u53D6\u6D88" : "\u5DF2\u91CD\u65B0\u6253\u5F00"}\uFF1A${r.todo.title}\uFF0C${due(r.todo)}`);
301737
+ break;
301738
+ }
301739
+ throw new Error("\u7528\u6CD5: oasis todo <add|list|edit|done|cancel|reopen> \u2026\uFF08oasis todo --help \u770B\u8BE6\u60C5\uFF09");
301740
+ }
300437
301741
  case "skill": {
300438
301742
  const sub = positional[0];
300439
301743
  const asJson2 = flags.get("json") === "true";
@@ -301577,8 +302881,8 @@ var LOG_RETAIN_DAYS = 30;
301577
302881
  var LOG_RETAIN_MAX_FILES = 10;
301578
302882
  var ROTATED_SUFFIX = /^\d{8}-\d{6}(-\d+)?$/;
301579
302883
  function stampOf(now) {
301580
- const iso8 = now.toISOString();
301581
- return `${iso8.slice(0, 10).replace(/-/g, "")}-${iso8.slice(11, 19).replace(/:/g, "")}`;
302884
+ const iso9 = now.toISOString();
302885
+ return `${iso9.slice(0, 10).replace(/-/g, "")}-${iso9.slice(11, 19).replace(/:/g, "")}`;
301582
302886
  }
301583
302887
  function rotateLogIfNeeded(file, opts = {}) {
301584
302888
  const maxBytes = opts.maxBytes ?? LOG_ROTATE_MAX_BYTES;
@@ -301971,13 +303275,13 @@ async function exchangeEnrollToken(o) {
301971
303275
  }
301972
303276
 
301973
303277
  // src/install.ts
301974
- var import_node_crypto97 = require("node:crypto");
303278
+ var import_node_crypto99 = require("node:crypto");
301975
303279
  var fs50 = __toESM(require("node:fs"));
301976
303280
  var path45 = __toESM(require("node:path"));
301977
303281
  function versionLabel(sourceFile, pkgVersion, npmPrefixDir, pkgName = LEGACY_PKG_NAME) {
301978
303282
  const tag = pkgName === LEGACY_PKG_NAME ? "" : `+pkg.${pkgName.replace(/[^A-Za-z0-9-]/g, "-")}`;
301979
303283
  if (isFromNpmPrefix(sourceFile, npmPrefixDir)) return `${pkgVersion}${tag}`;
301980
- const digest = (0, import_node_crypto97.createHash)("sha256").update(fs50.readFileSync(sourceFile)).digest("hex").slice(0, 8);
303284
+ const digest = (0, import_node_crypto99.createHash)("sha256").update(fs50.readFileSync(sourceFile)).digest("hex").slice(0, 8);
301981
303285
  return tag ? `${pkgVersion}${tag}.local.${digest}` : `${pkgVersion}+local.${digest}`;
301982
303286
  }
301983
303287
  function isFromNpmPrefix(sourceFile, npmPrefixDir) {
@@ -302100,7 +303404,7 @@ function shimScript() {
302100
303404
  }
302101
303405
 
302102
303406
  // src/index.ts
302103
- var PKG_VERSION = true ? "2.2.105" : "dev";
303407
+ var PKG_VERSION = true ? "2.2.106" : "dev";
302104
303408
  var LOCAL_BIN = localBin();
302105
303409
  var NPM_PREFIX = npmPrefix();
302106
303410
  var INSTANCE = DEFAULT_INSTANCE;
@@ -302408,7 +303712,7 @@ function newInstanceName() {
302408
303712
  const existing = new Set(listInstances());
302409
303713
  if (!existing.has(DEFAULT_INSTANCE)) return DEFAULT_INSTANCE;
302410
303714
  for (; ; ) {
302411
- const n = `inst-${(0, import_node_crypto98.randomBytes)(3).toString("hex")}`;
303715
+ const n = `inst-${(0, import_node_crypto100.randomBytes)(3).toString("hex")}`;
302412
303716
  if (!existing.has(n)) return n;
302413
303717
  }
302414
303718
  }