toolroll 0.9.41 → 0.9.43

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 (89) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/chat-actions.js +1 -1
  3. package/dist/chat-approval.d.ts +45 -0
  4. package/dist/chat-approval.js +81 -0
  5. package/dist/chat-channel.js +5 -0
  6. package/dist/chat-decide.d.ts +18 -6
  7. package/dist/chat-decide.js +54 -20
  8. package/dist/chat-flow.js +2 -2
  9. package/dist/cli.js +4 -2
  10. package/dist/coding-types.d.ts +2 -24
  11. package/dist/coding-workspace.js +12 -11
  12. package/dist/contracts/cli.d.ts +736 -0
  13. package/dist/contracts/cli.js +223 -0
  14. package/dist/contracts/coding-activity.d.ts +64 -0
  15. package/dist/contracts/coding-activity.js +58 -0
  16. package/dist/contracts/console-api.d.ts +484 -0
  17. package/dist/contracts/console-api.js +274 -0
  18. package/dist/contracts/diff-stat.d.ts +58 -0
  19. package/dist/contracts/diff-stat.js +59 -0
  20. package/dist/contracts/integration-metadata.d.ts +99 -0
  21. package/dist/contracts/integration-metadata.js +101 -0
  22. package/dist/contracts/lead-tools.d.ts +11 -0
  23. package/dist/contracts/lead-tools.js +2 -0
  24. package/dist/contracts/model-catalog.d.ts +21 -0
  25. package/dist/contracts/model-catalog.js +14 -0
  26. package/dist/contracts/native-census.d.ts +42 -0
  27. package/dist/contracts/native-census.js +36 -0
  28. package/dist/contracts/observation-request.d.ts +37 -0
  29. package/dist/contracts/observation-request.js +25 -0
  30. package/dist/contracts/provider.d.ts +30 -0
  31. package/dist/contracts/provider.js +15 -0
  32. package/dist/contracts/recipes.d.ts +166 -0
  33. package/dist/contracts/recipes.js +52 -0
  34. package/dist/contracts/retention.d.ts +42 -0
  35. package/dist/contracts/retention.js +24 -0
  36. package/dist/contracts/storage.d.ts +23 -0
  37. package/dist/contracts/storage.js +10 -0
  38. package/dist/contracts/store-json.d.ts +315 -0
  39. package/dist/contracts/store-json.js +196 -0
  40. package/dist/contracts/trigger-payloads.d.ts +369 -0
  41. package/dist/contracts/trigger-payloads.js +144 -0
  42. package/dist/contracts/update-journal.d.ts +254 -0
  43. package/dist/contracts/update-journal.js +155 -0
  44. package/dist/contracts/verification-view.d.ts +93 -0
  45. package/dist/contracts/verification-view.js +43 -0
  46. package/dist/control-setup.d.ts +1 -1
  47. package/dist/desktop-update.d.ts +7 -40
  48. package/dist/desktop-update.js +36 -8
  49. package/dist/evidence.d.ts +3 -19
  50. package/dist/flow-engine.d.ts +2 -0
  51. package/dist/flow-engine.js +10 -2
  52. package/dist/flow-triggers.js +61 -60
  53. package/dist/flows-cli.d.ts +28 -0
  54. package/dist/flows-cli.js +43 -1
  55. package/dist/guides.js +2 -0
  56. package/dist/mate-contract.js +1 -1
  57. package/dist/mate-tools.js +43 -1
  58. package/dist/mcp-connect.js +37 -22
  59. package/dist/model-catalog.js +8 -6
  60. package/dist/observations.d.ts +2 -6
  61. package/dist/observations.js +13 -9
  62. package/dist/operate.js +55 -4
  63. package/dist/process-recovery-eligibility.d.ts +1 -1
  64. package/dist/process-recovery-native.d.ts +2 -13
  65. package/dist/process-recovery-native.js +8 -5
  66. package/dist/project-tools.d.ts +6 -24
  67. package/dist/project-tools.js +22 -18
  68. package/dist/provider.d.ts +3 -8
  69. package/dist/provider.js +15 -11
  70. package/dist/recipes.d.ts +3 -28
  71. package/dist/recipes.js +79 -52
  72. package/dist/retention.d.ts +4 -5
  73. package/dist/retention.js +5 -4
  74. package/dist/scope.d.ts +4 -2
  75. package/dist/serve.js +193 -30
  76. package/dist/storage.d.ts +2 -1
  77. package/dist/storage.js +12 -9
  78. package/dist/store.d.ts +11 -16
  79. package/dist/store.js +122 -160
  80. package/dist/surface.d.ts +6 -54
  81. package/dist/surface.js +10 -2
  82. package/dist/teammate-work.js +1 -1
  83. package/dist/telegram-flow.d.ts +41 -1
  84. package/dist/telegram-flow.js +96 -12
  85. package/dist/telegram.js +27 -3
  86. package/dist/toolroll-update.d.ts +8 -99
  87. package/dist/toolroll-update.js +28 -18
  88. package/dist/verification-evidence.js +27 -12
  89. package/package.json +1 -1
package/dist/store.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { chatSchema, chatTables } from "./contracts/chat-tables.js";
2
+ import { parseStoreColumn, readStoreColumn, readStoreStringifiedList, readStoreTextList } from "./contracts/store-json.js";
2
3
  import { assessmentFromSavedEvidence, verificationEvidence } from "./verification-evidence.js";
3
4
  import { LEARNING_SCHEMA, queueLearning } from "./project-learning.js";
4
5
  import { SKILLS_SCHEMA } from "./project-skills.js";
@@ -353,6 +354,40 @@ CREATE TABLE IF NOT EXISTS telegram_flow_choice (
353
354
  );
354
355
  CREATE INDEX IF NOT EXISTS telegram_flow_choice_visit ON telegram_flow_choice (card, entry);
355
356
  `;
357
+ /** An owner's lasting choice to approve plans and merges from their paired chat (chat-approval.ts), for all their
358
+ * projects (scope '*') or one (its path), with the limits they agreed to. Off unless a row says on; a project's own row
359
+ * outranks the all-projects one. `generation` is the account's at the change: a reset password ends it. */
360
+ export const CHAT_APPROVAL_SCHEMA = `
361
+ CREATE TABLE IF NOT EXISTS chat_approval_setting (
362
+ approver TEXT NOT NULL,
363
+ scope TEXT NOT NULL,
364
+ enabled INTEGER NOT NULL CHECK (enabled IN (0,1)),
365
+ full_access INTEGER NOT NULL DEFAULT 0 CHECK (full_access IN (0,1)),
366
+ attempt_cap_microusd INTEGER CHECK (attempt_cap_microusd IS NULL OR attempt_cap_microusd >= 0),
367
+ generation INTEGER NOT NULL,
368
+ digest TEXT NOT NULL,
369
+ updated_by TEXT NOT NULL,
370
+ updated_at TEXT NOT NULL,
371
+ PRIMARY KEY (approver, scope)
372
+ );
373
+ `;
374
+ /** The second tap on a flow decision in Telegram (telegram-flow.ts): Approve arms Yes and Cancel, bound to the binding,
375
+ * chat, message and exact visit of the card; Yes decides it once. */
376
+ export const TELEGRAM_FLOW_CONFIRM_SCHEMA = `
377
+ CREATE TABLE IF NOT EXISTS telegram_flow_confirm (
378
+ token TEXT PRIMARY KEY,
379
+ binding INTEGER NOT NULL,
380
+ chat_id TEXT NOT NULL,
381
+ message_id TEXT NOT NULL,
382
+ card INTEGER NOT NULL,
383
+ entry INTEGER NOT NULL,
384
+ phase TEXT NOT NULL CHECK (phase IN ('yes','cancel')),
385
+ created_at TEXT NOT NULL,
386
+ expires_at TEXT NOT NULL,
387
+ consumed_at TEXT
388
+ );
389
+ CREATE INDEX IF NOT EXISTS telegram_flow_confirm_visit ON telegram_flow_confirm (card, entry);
390
+ `;
356
391
  export const CHAT_DECIDE_SCHEMA = `
357
392
  CREATE TABLE IF NOT EXISTS chat_decide_action (
358
393
  token TEXT PRIMARY KEY,
@@ -504,10 +539,10 @@ const isCheckExitCode = (value) => value === null || (Number.isSafeInteger(value
504
539
  /** Read a stored suite list; a malformed optional summary reads as no suite detail. */
505
540
  export function checkSuitesOf(value) {
506
541
  try {
507
- const parsed = JSON.parse(String(value ?? "[]"));
508
- if (!Array.isArray(parsed))
542
+ const read = readStoreColumn("run_check.suites_json", value ?? "[]");
543
+ if (!read.ok)
509
544
  return [];
510
- return parsed.filter((one) => {
545
+ return read.value.filter((one) => {
511
546
  if (one === null || typeof one !== "object")
512
547
  return false;
513
548
  const row = one;
@@ -620,37 +655,22 @@ function readTeammateMemory(row) {
620
655
  createdBy: String(row["created_by"]), createdAt: String(row["created_at"]), updatedBy: String(row["updated_by"]), updatedAt: String(row["updated_at"]), usedAt: row["used_at"] === null ? null : String(row["used_at"]) };
621
656
  }
622
657
  function readTeammateSuggestion(row) {
623
- const parsed = (key, fallback) => { try {
624
- return JSON.parse(String(row[key]));
625
- }
626
- catch {
627
- return fallback;
628
- } };
629
- return { id: Number(row["id"]), teammate: Number(row["teammate"]), tool: String(row["tool"]), action: String(row["action"]), rule: parsed("rule_json", { use: "ask" }), was: parsed("was_json", { use: "ask" }),
630
- evidence: parsed("evidence_json", []), said: String(row["said"]), state: String(row["state"]),
658
+ const parsed = (column, key, fallback) => { const read = readStoreColumn(column, row[key]); return read.ok ? read.value : fallback; };
659
+ return { id: Number(row["id"]), teammate: Number(row["teammate"]), tool: String(row["tool"]), action: String(row["action"]), rule: parsed("teammate_suggestion.rule_json", "rule_json", { use: "ask" }), was: parsed("teammate_suggestion.was_json", "was_json", { use: "ask" }),
660
+ evidence: parsed("teammate_suggestion.evidence_json", "evidence_json", []), said: String(row["said"]), state: String(row["state"]),
631
661
  decidedBy: row["decided_by"] === null ? null : String(row["decided_by"]), decidedAt: row["decided_at"] === null ? null : String(row["decided_at"]), createdAt: String(row["created_at"]) };
632
662
  }
633
663
  function readTeammateCall(row) {
634
664
  const text = (key) => row[key] === null || row[key] === undefined ? null : String(row[key]);
635
- let input = {};
636
- try {
637
- input = JSON.parse(String(row["input_json"]));
638
- }
639
- catch {
640
- input = {};
641
- }
665
+ const read = readStoreColumn("teammate_call.input_json", row["input_json"]);
666
+ const input = read.ok ? read.value : {};
642
667
  return { id: Number(row["id"]), teammate: Number(row["teammate"]), card: Number(row["card"]), entry: Number(row["entry"]), tool: String(row["tool"]), action: String(row["action"]), input,
643
668
  rule: String(row["rule"]), why: String(row["why"]), state: String(row["state"]), result: text("result"), decidedBy: text("decided_by"), decidedAt: text("decided_at"),
644
669
  createdAt: String(row["created_at"]), doneAt: text("done_at"), undoOf: row["undo_of"] === null || row["undo_of"] === undefined ? null : Number(row["undo_of"]), undoneBy: text("undone_by"), undoneAt: text("undone_at") };
645
670
  }
646
671
  function readTeammateGrant(row) {
647
- const parsed = (key, fallback) => { try {
648
- return JSON.parse(String(row[key]));
649
- }
650
- catch {
651
- return fallback;
652
- } };
653
- return { teammate: Number(row["teammate"]), tool: String(row["tool"]), actions: parsed("actions_json", []), rules: parsed("rules_json", {}),
672
+ const parsed = (column, key, fallback) => { const read = readStoreColumn(column, row[key]); return read.ok ? read.value : fallback; };
673
+ return { teammate: Number(row["teammate"]), tool: String(row["tool"]), actions: parsed("teammate_tool.actions_json", "actions_json", []), rules: parsed("teammate_tool.rules_json", "rules_json", {}),
654
674
  listedAt: row["listed_at"] === null ? null : String(row["listed_at"]), updatedBy: String(row["updated_by"]), updatedAt: String(row["updated_at"]) };
655
675
  }
656
676
  function readTeammateRow(row) {
@@ -663,7 +683,7 @@ function readTeammateRow(row) {
663
683
  function readTeammateQuestion(row) {
664
684
  const text = (key) => row[key] === null || row[key] === undefined ? null : String(row[key]);
665
685
  return { id: Number(row["id"]), teammate: Number(row["teammate"]), card: Number(row["card"]), entry: Number(row["entry"]), question: String(row["question"]),
666
- options: JSON.parse(String(row["options_json"])), askedOf: String(row["asked_of"]), state: String(row["state"]),
686
+ options: parseStoreColumn("teammate_question.options_json", row["options_json"]), askedOf: String(row["asked_of"]), state: String(row["state"]),
667
687
  choice: text("choice"), answer: text("answer"), answeredBy: text("answered_by"), answeredVia: text("answered_via"), answeredAt: text("answered_at"), createdAt: String(row["created_at"]),
668
688
  toolCall: row["tool_call"] === null || row["tool_call"] === undefined ? null : Number(row["tool_call"]),
669
689
  suggestion: row["suggestion"] === null || row["suggestion"] === undefined ? null : Number(row["suggestion"]) };
@@ -697,10 +717,11 @@ function readFlowCardRow(row) {
697
717
  function readFlowCardSource(value) {
698
718
  if (typeof value !== "string")
699
719
  return null;
720
+ const read = readStoreColumn("flow_card.source_json", value);
721
+ if (!read.ok)
722
+ return null;
700
723
  try {
701
- const parsed = JSON.parse(value);
702
- if (typeof parsed["label"] !== "string")
703
- return null;
724
+ const parsed = read.value;
704
725
  const mail = parsed["mail"] !== null && typeof parsed["mail"] === "object" ? parsed["mail"] : null;
705
726
  const chat = parsed["chat"] !== null && typeof parsed["chat"] === "object" ? parsed["chat"] : null;
706
727
  return { kind: String(parsed["kind"] ?? ""), label: parsed["label"], url: typeof parsed["url"] === "string" ? parsed["url"] : null,
@@ -3977,6 +3998,9 @@ function initializeStore(db, file) {
3977
3998
  db.exec(MATE_ASK_SCHEMA);
3978
3999
  // Decisions in the chat app (no version bump: additive only): result, plan and merge buttons, and Request changes.
3979
4000
  db.exec(CHAT_DECIDE_SCHEMA);
4001
+ // An owner's lasting chat-approval setting (no version bump: additive only; absent means off).
4002
+ db.exec(CHAT_APPROVAL_SCHEMA);
4003
+ db.exec(TELEGRAM_FLOW_CONFIRM_SCHEMA);
3980
4004
  // A flow's "Send to me" and "Person chooses" zones (no version bump: additive only).
3981
4005
  db.exec(FLOW_SEND_SCHEMA);
3982
4006
  db.exec(SPEND_SCHEMA);
@@ -6684,12 +6708,10 @@ function migrateToV24(db) {
6684
6708
  }
6685
6709
  else {
6686
6710
  let touches = [];
6687
- try {
6688
- touches = JSON.parse(row.touches);
6689
- }
6690
- catch {
6691
- touches = [];
6692
- }
6711
+ const read = readStoreColumn("task_scope.touches", row.touches);
6712
+ // Unlike the display reader, v24 passed every valid JSON value to digestOf, including non-arrays.
6713
+ if (read.ok)
6714
+ touches = read.value;
6693
6715
  const recomputed = digestOf({ goal: row.goal, outOfScope: row.outOfScope, touches, budgetMicrousd: row.budget }, resolved.profile);
6694
6716
  stampUnapproved.run(snapshot, recomputed, provenance, row.taskId);
6695
6717
  }
@@ -8812,7 +8834,7 @@ export class Store {
8812
8834
  // (setup review): the approval is what was stale, and it is new.
8813
8835
  const ref = this.db.prepare("SELECT id FROM task_ref WHERE backend = ? AND external_id = ?").get(BUILT_IN, taskId);
8814
8836
  if (ref !== undefined) {
8815
- if (basis === undefined)
8837
+ if (basis === undefined || basis.kind === "chat")
8816
8838
  this.noteTaskAct(Number(ref["id"]), "approved", now);
8817
8839
  this.db.prepare("DELETE FROM hold WHERE owner_kind = 'backoff' AND owner_id = ?").run(`stale:${Number(ref["id"])}`);
8818
8840
  this.db
@@ -8828,7 +8850,9 @@ export class Store {
8828
8850
  subject: "Scope approved",
8829
8851
  body: basis === undefined
8830
8852
  ? `Approved by ${currentActor()?.lead === true ? "the lead" : lifecycleWords(by, 40)}. A connected worker can take it next.`
8831
- : "Approved automatically under the operating mode. A connected worker can take it next.",
8853
+ : basis.kind === "chat"
8854
+ ? `Approved by ${lifecycleWords(by, 40)} in chat. A connected worker can take it next.`
8855
+ : "Approved automatically under the operating mode. A connected worker can take it next.",
8832
8856
  link: chatControlHref("task", taskId),
8833
8857
  }, now);
8834
8858
  }
@@ -9086,7 +9110,7 @@ export class Store {
9086
9110
  return { ok: true };
9087
9111
  let terms;
9088
9112
  try {
9089
- terms = JSON.parse(mode.termsJson);
9113
+ terms = parseStoreColumn("operating_mode.terms_json", mode.termsJson);
9090
9114
  }
9091
9115
  catch {
9092
9116
  return { ok: true };
@@ -9243,15 +9267,7 @@ export class Store {
9243
9267
  const row = this.db.prepare("SELECT * FROM approval_policy WHERE repo = ?").get(repo);
9244
9268
  if (row === undefined)
9245
9269
  return { ...NO_RULES, updatedBy: null, updatedAt: null };
9246
- let paths = [];
9247
- try {
9248
- const parsed = JSON.parse(String(row["protected_paths"]));
9249
- if (Array.isArray(parsed))
9250
- paths = parsed.filter((one) => typeof one === "string");
9251
- }
9252
- catch {
9253
- paths = [];
9254
- }
9270
+ const paths = readStoreTextList("approval_policy.protected_paths", row["protected_paths"]);
9255
9271
  return { notRequester: Number(row["not_requester"]) === 1, protectProject: Number(row["protect_project"]) === 1, protectedPaths: paths, updatedBy: String(row["updated_by"]), updatedAt: String(row["updated_at"]) };
9256
9272
  }
9257
9273
  /** Change a project's rules; the ledger keeps before → after. */
@@ -9302,15 +9318,7 @@ export class Store {
9302
9318
  return { verdict: "seal", protectedWork: false };
9303
9319
  const repo = String(ref["repo"]);
9304
9320
  const rules = this.approvalRules(repo);
9305
- let touches = [];
9306
- try {
9307
- const parsed = JSON.parse(String(scope?.["touches"] ?? "[]"));
9308
- if (Array.isArray(parsed))
9309
- touches = parsed.map(String);
9310
- }
9311
- catch {
9312
- touches = [];
9313
- }
9321
+ const touches = readStoreStringifiedList("task_scope.touches", scope?.["touches"] ?? "[]");
9314
9322
  const protectedWork = isProtectedWork(rules, touches);
9315
9323
  if (!rules.notRequester && !protectedWork)
9316
9324
  return { verdict: "seal", protectedWork: false };
@@ -12048,7 +12056,7 @@ export class Store {
12048
12056
  let reviewAuto = false;
12049
12057
  if (mode !== null && mode.digest === String(row["mode_digest"] ?? "")) {
12050
12058
  try {
12051
- reviewAuto = JSON.parse(mode.termsJson).reviewAuto === true;
12059
+ reviewAuto = parseStoreColumn("operating_mode.terms_json", mode.termsJson).reviewAuto === true;
12052
12060
  }
12053
12061
  catch {
12054
12062
  reviewAuto = false;
@@ -13458,13 +13466,10 @@ export class Store {
13458
13466
  const row = this.db.prepare("SELECT entries_json FROM fallback_config WHERE scope = ? AND phase = 'build'").get(scope);
13459
13467
  if (row === undefined)
13460
13468
  return { ok: true, entries: [] };
13461
- let parsed;
13462
- try {
13463
- parsed = JSON.parse(String(row["entries_json"]));
13464
- }
13465
- catch {
13469
+ const read = readStoreColumn("fallback_config.entries_json", row["entries_json"]);
13470
+ if (!read.ok && read.malformed)
13466
13471
  return { ok: false, problem: `the fallback configuration for ${scope} is not valid JSON — set it again with \`config set fallback\`, or clear it` };
13467
- }
13472
+ const parsed = read.ok ? read.value : null;
13468
13473
  if (!Array.isArray(parsed))
13469
13474
  return { ok: false, problem: `the fallback configuration for ${scope} is not a list of entries — set it again with \`config set fallback\`, or clear it` };
13470
13475
  // A PRESENT row carries one to three entries (atomic authority
@@ -13834,7 +13839,7 @@ export class Store {
13834
13839
  .filter(row => {
13835
13840
  if (assignedRunner !== null)
13836
13841
  return String(row["runner"]) === assignedRunner;
13837
- const repos = readJsonArray(row["repos"]);
13842
+ const repos = readStoreTextList("runner.repos", row["repos"]);
13838
13843
  return repo === null || repos.includes(repo);
13839
13844
  })
13840
13845
  .sort((a, b) => Number(alive(b)) - Number(alive(a)));
@@ -14004,7 +14009,7 @@ export class Store {
14004
14009
  if (open !== null) {
14005
14010
  let pinned = null;
14006
14011
  try {
14007
- const terms = JSON.parse(open.termsJson);
14012
+ const terms = parseStoreColumn("attended_authorization.terms_json", open.termsJson);
14008
14013
  pinned = profileFromJson(typeof terms.profileJson === "string" ? terms.profileJson : null);
14009
14014
  }
14010
14015
  catch {
@@ -14695,7 +14700,10 @@ export class Store {
14695
14700
  .all(...admittedRepos);
14696
14701
  const optionsOf = (raw) => {
14697
14702
  try {
14698
- const parsed = JSON.parse(String(raw));
14703
+ const read = readStoreColumn("decision.options", raw);
14704
+ if (!read.ok)
14705
+ return [];
14706
+ const parsed = read.value;
14699
14707
  return Array.isArray(parsed)
14700
14708
  ? parsed.slice(0, 6).map(one => ({ id: String(one?.id ?? ""), label: String(one?.label ?? ""), reversible: one?.reversible === true }))
14701
14709
  : [];
@@ -15856,7 +15864,7 @@ export class Store {
15856
15864
  refuse(`the attended authorization ${live.id} was minted for ${live.runner} at generation ${live.runnerGeneration}, not ${named.generation}`);
15857
15865
  let pinned = null;
15858
15866
  try {
15859
- const terms = JSON.parse(live.termsJson);
15867
+ const terms = parseStoreColumn("attended_authorization.terms_json", live.termsJson);
15860
15868
  pinned = profileFromJson(typeof terms.profileJson === "string" ? terms.profileJson : null);
15861
15869
  }
15862
15870
  catch {
@@ -17421,7 +17429,7 @@ export class Store {
17421
17429
  let quickMint = false;
17422
17430
  if (mode !== null && mode.digest === input.basis.digest && mode.signedBy === input.approver) {
17423
17431
  try {
17424
- quickMint = JSON.parse(mode.termsJson).quickMint === true;
17432
+ quickMint = parseStoreColumn("operating_mode.terms_json", mode.termsJson).quickMint === true;
17425
17433
  }
17426
17434
  catch {
17427
17435
  quickMint = false;
@@ -18544,6 +18552,7 @@ export class Store {
18544
18552
  const stamp = now.toISOString();
18545
18553
  this.db.prepare("UPDATE telegram_flow_action SET consumed_at = ? WHERE card = ? AND entry = ? AND consumed_at IS NULL").run(stamp, card, entry);
18546
18554
  this.db.prepare("UPDATE telegram_flow_prompt SET consumed_at = ? WHERE card = ? AND entry = ? AND consumed_at IS NULL").run(stamp, card, entry);
18555
+ this.db.prepare("UPDATE telegram_flow_confirm SET consumed_at = ? WHERE card = ? AND entry = ? AND consumed_at IS NULL").run(stamp, card, entry);
18547
18556
  }
18548
18557
  recordTelegramFlowPrompt(prompt, now, hours = 24) {
18549
18558
  this.db.prepare("INSERT OR REPLACE INTO telegram_flow_prompt (chat_id, message_id, binding, card, entry, mode, created_at, expires_at, consumed_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL)")
@@ -18808,7 +18817,7 @@ export class Store {
18808
18817
  if (mode !== null) {
18809
18818
  let publication;
18810
18819
  try {
18811
- publication = JSON.parse(mode.termsJson).publication;
18820
+ publication = parseStoreColumn("operating_mode.terms_json", mode.termsJson).publication;
18812
18821
  }
18813
18822
  catch {
18814
18823
  publication = undefined;
@@ -18938,7 +18947,7 @@ export class Store {
18938
18947
  if (basis === "mode") {
18939
18948
  let publication;
18940
18949
  try {
18941
- publication = mode === null ? undefined : JSON.parse(mode.termsJson).publication;
18950
+ publication = mode === null ? undefined : parseStoreColumn("operating_mode.terms_json", mode.termsJson).publication;
18942
18951
  }
18943
18952
  catch {
18944
18953
  publication = undefined;
@@ -18950,7 +18959,7 @@ export class Store {
18950
18959
  else if (basis === "grant" && mode !== null) {
18951
18960
  let publication;
18952
18961
  try {
18953
- publication = JSON.parse(mode.termsJson).publication;
18962
+ publication = parseStoreColumn("operating_mode.terms_json", mode.termsJson).publication;
18954
18963
  }
18955
18964
  catch {
18956
18965
  publication = undefined;
@@ -19486,7 +19495,7 @@ export class Store {
19486
19495
  teammateEvents(teammate, limit, since = null) {
19487
19496
  return this.db.prepare(`SELECT * FROM teammate_event WHERE teammate = ? ${since === null ? "" : "AND at >= ?"} ORDER BY id DESC LIMIT ?`).all(...(since === null ? [teammate, limit] : [teammate, since, limit])).map(row => ({
19488
19497
  id: Number(row["id"]), teammate: Number(row["teammate"]), card: row["card"] === null ? null : Number(row["card"]), entry: row["entry"] === null ? null : Number(row["entry"]),
19489
- kind: String(row["kind"]), said: String(row["said"]), detail: row["detail_json"] === null ? null : JSON.parse(String(row["detail_json"])),
19498
+ kind: String(row["kind"]), said: String(row["said"]), detail: row["detail_json"] === null ? null : parseStoreColumn("teammate_event.detail_json", row["detail_json"]),
19490
19499
  by: row["by"] === null ? null : String(row["by"]), at: String(row["at"])
19491
19500
  }));
19492
19501
  }
@@ -19837,15 +19846,7 @@ export class Store {
19837
19846
  /** A card's discussion, oldest first. */
19838
19847
  flowComments(card) {
19839
19848
  return this.db.prepare("SELECT * FROM flow_comment WHERE card = ? ORDER BY id").all(card).map(row => {
19840
- let mentions = [];
19841
- try {
19842
- const parsed = JSON.parse(String(row["mentions_json"]));
19843
- if (Array.isArray(parsed))
19844
- mentions = parsed.filter((one) => typeof one === "string");
19845
- }
19846
- catch {
19847
- mentions = [];
19848
- }
19849
+ const mentions = readStoreTextList("flow_comment.mentions_json", row["mentions_json"]);
19849
19850
  return { id: Number(row["id"]), card: Number(row["card"]), kind: String(row["kind"]), author: String(row["author"]), body: String(row["body"]), mentions, at: String(row["at"]) };
19850
19851
  });
19851
19852
  }
@@ -19947,13 +19948,8 @@ export class Store {
19947
19948
  const row = this.db.prepare("SELECT tools_json FROM tool_seal WHERE task = ? AND approved_digest = ?").get(taskId, approvedDigest);
19948
19949
  if (row === undefined)
19949
19950
  return null;
19950
- try {
19951
- const parsed = JSON.parse(String(row["tools_json"]));
19952
- return Array.isArray(parsed) ? parsed.filter((one) => typeof one?.name === "string" && typeof one?.digest === "string") : [];
19953
- }
19954
- catch {
19955
- return [];
19956
- }
19951
+ const read = readStoreColumn("tool_seal.tools_json", row["tools_json"]);
19952
+ return read.ok ? read.value.filter((one) => typeof one?.name === "string" && typeof one?.digest === "string") : [];
19957
19953
  }
19958
19954
  /** What one attempt launched with (first launch of the run wins its record; a resumed turn updates it). */
19959
19955
  recordRunTools(run, toolsJson, now) {
@@ -19969,19 +19965,15 @@ export class Store {
19969
19965
  const row = this.db.prepare("SELECT json_extract(tools_json, '$.fence') AS fence FROM run_tool WHERE run = ?").get(run);
19970
19966
  if (row === undefined || row["fence"] === null)
19971
19967
  return null;
19972
- try {
19973
- return JSON.parse(String(row["fence"]));
19974
- }
19975
- catch {
19976
- return null;
19977
- }
19968
+ const read = readStoreColumn("run_tool.tools_json.fence", row["fence"]);
19969
+ return read.ok ? read.value : null;
19978
19970
  }
19979
19971
  runTools(run) {
19980
19972
  const row = this.db.prepare("SELECT tools_json FROM run_tool WHERE run = ?").get(run);
19981
19973
  if (row === undefined)
19982
19974
  return null;
19983
19975
  try {
19984
- const parsed = JSON.parse(String(row["tools_json"]));
19976
+ const parsed = parseStoreColumn("run_tool.tools_json", row["tools_json"]);
19985
19977
  return { tools: Array.isArray(parsed.tools) ? parsed.tools : [], skipped: Array.isArray(parsed.skipped) ? parsed.skipped : [] };
19986
19978
  }
19987
19979
  catch {
@@ -20075,7 +20067,7 @@ export class Store {
20075
20067
  const row = this.db.prepare("SELECT a.* FROM mate_ask a JOIN mate_turn t ON t.id = a.turn WHERE a.turn = ? AND t.state = 'answered'").get(turn);
20076
20068
  if (row === undefined)
20077
20069
  return null;
20078
- const options = JSON.parse(String(row["options_json"]));
20070
+ const options = parseStoreColumn("mate_ask.options_json", row["options_json"]);
20079
20071
  return { turn: Number(row["turn"]), thread: Number(row["thread"]), question: String(row["question"]), options: Array.isArray(options) ? options.map(String) : [], createdAt: String(row["created_at"]) };
20080
20072
  }
20081
20073
  /** What a tap on the question finds: open; answered (its owner has written in its thread since the reply that asked
@@ -22721,13 +22713,7 @@ export class Store {
22721
22713
  const row = this.db.prepare("SELECT about_json FROM lead_config WHERE account = ?").get(account);
22722
22714
  if (row === undefined)
22723
22715
  return [];
22724
- try {
22725
- const lines = JSON.parse(String(row["about_json"]));
22726
- return Array.isArray(lines) ? lines.filter((one) => typeof one === "string") : [];
22727
- }
22728
- catch {
22729
- return [];
22730
- }
22716
+ return readStoreTextList("lead_config.about_json", row["about_json"]);
22731
22717
  }
22732
22718
  /** Save what this person's lead knows about them (checked by the caller: lead-about.ts). A person who never named
22733
22719
  * their lead gets an empty name and persona, so they keep following the defaults. */
@@ -23284,7 +23270,7 @@ export class Store {
23284
23270
  mateRequestReceipt(session, request) {
23285
23271
  const row = this.db.prepare("SELECT result FROM mutation WHERE idempotency_key = ? AND operation = 'mate-send'")
23286
23272
  .get(`mate-send:${session}:${request}`);
23287
- return row === undefined ? null : JSON.parse(String(row["result"]));
23273
+ return row === undefined ? null : parseStoreColumn("mutation.result", row["result"]);
23288
23274
  }
23289
23275
  /**
23290
23276
  * Run `body` once per key, ever.
@@ -23315,7 +23301,7 @@ export class Store {
23315
23301
  .prepare("SELECT result FROM mutation WHERE idempotency_key = ?")
23316
23302
  .get(idempotencyKey);
23317
23303
  if (seen !== undefined)
23318
- return JSON.parse(String(seen["result"]));
23304
+ return parseStoreColumn("mutation.result", seen["result"]);
23319
23305
  const result = body();
23320
23306
  if (!worthRecording(result))
23321
23307
  return result;
@@ -23596,7 +23582,7 @@ function readDecision(row) {
23596
23582
  state: String(row["state"]),
23597
23583
  recap: String(row["recap"]),
23598
23584
  question: String(row["question"]),
23599
- options: JSON.parse(String(row["options"])),
23585
+ options: parseStoreColumn("decision.options", row["options"]),
23600
23586
  recommendation: String(row["recommendation"]),
23601
23587
  assignee: row["assignee"] === null ? null : String(row["assignee"]),
23602
23588
  deadline: row["deadline"] === null ? null : String(row["deadline"]),
@@ -23638,7 +23624,7 @@ function readPlanRevision(row) {
23638
23624
  kind: String(row["kind"]),
23639
23625
  authorityKind: String(row["authority_kind"]),
23640
23626
  authorityDigest: String(row["authority_digest"]),
23641
- changedFields: row["changed_fields"] === null || row["changed_fields"] === undefined ? [] : JSON.parse(String(row["changed_fields"])),
23627
+ changedFields: row["changed_fields"] === null || row["changed_fields"] === undefined ? [] : parseStoreColumn("plan_revision.changed_fields", row["changed_fields"]),
23642
23628
  status: String(row["status"]),
23643
23629
  createdAt: String(row["created_at"]),
23644
23630
  resolvedAt: row["resolved_at"] === null || row["resolved_at"] === undefined ? null : String(row["resolved_at"]),
@@ -23665,7 +23651,7 @@ function readRunCheckpoint(row) {
23665
23651
  run: Number(row["run"]),
23666
23652
  taskRef: Number(row["task_ref"]),
23667
23653
  planRevision: Number(row["plan_revision"]),
23668
- snapshot: JSON.parse(String(row["snapshot_json"])),
23654
+ snapshot: parseStoreColumn("run_checkpoint.snapshot_json", row["snapshot_json"]),
23669
23655
  createdAt: String(row["created_at"]),
23670
23656
  };
23671
23657
  }
@@ -23738,7 +23724,7 @@ function readTelegramConversationPart(row) {
23738
23724
  text: String(row["text"]),
23739
23725
  replyTo: text("reply_to"),
23740
23726
  proposal: row["proposal"] === null || row["proposal"] === undefined ? null : Number(row["proposal"]),
23741
- keyboard: row["keyboard_json"] === null || row["keyboard_json"] === undefined ? null : JSON.parse(String(row["keyboard_json"])),
23727
+ keyboard: row["keyboard_json"] === null || row["keyboard_json"] === undefined ? null : parseStoreColumn("telegram_conversation_part.keyboard_json", row["keyboard_json"]),
23742
23728
  state: String(row["state"]),
23743
23729
  messageId: text("message_id"),
23744
23730
  taskId: text("task_id"),
@@ -23804,7 +23790,7 @@ function readPublicationGrant(row) {
23804
23790
  remote: String(row["remote"]),
23805
23791
  headPrefix: String(row["head_prefix"]),
23806
23792
  base: String(row["base"]),
23807
- capabilities: JSON.parse(String(row["capabilities"])),
23793
+ capabilities: parseStoreColumn("publication_grant.capabilities", row["capabilities"]),
23808
23794
  selector: String(row["selector"]),
23809
23795
  draft: Number(row["draft"]) === 1,
23810
23796
  grantedBy: String(row["granted_by"]),
@@ -23879,8 +23865,8 @@ function readTaskRef(row) {
23879
23865
  ? null
23880
23866
  : String(row["coordinator_cid"]),
23881
23867
  assignedRunner: row["assigned_runner"] === null || row["assigned_runner"] === undefined ? null : String(row["assigned_runner"]),
23882
- zones: readJsonArray(row["zones"]),
23883
- capabilityRequirements: readJsonArray(row["capability_requirements"]),
23868
+ zones: readStoreTextList("task_ref.zones", row["zones"]),
23869
+ capabilityRequirements: readStoreTextList("task_ref.capability_requirements", row["capability_requirements"]),
23884
23870
  parkRate: Number(row["park_rate"]),
23885
23871
  plan: row["plan"] === null || row["plan"] === undefined
23886
23872
  ? null
@@ -23968,9 +23954,9 @@ function readRoutine(row) {
23968
23954
  repo: String(row["repo"]),
23969
23955
  goal: String(row["goal"]),
23970
23956
  outOfScope: row["out_of_scope"] === null ? null : String(row["out_of_scope"]),
23971
- touches: readJsonArray(row["touches"]),
23972
- acceptance: readAcceptance(row["acceptance_json"]),
23973
- requirements: readJsonArray(row["requirements"]),
23957
+ touches: readStoreTextList("routine.touches", row["touches"]),
23958
+ acceptance: readAcceptance("routine.acceptance_json", row["acceptance_json"]),
23959
+ requirements: readStoreTextList("routine.requirements", row["requirements"]),
23974
23960
  schedule: String(row["schedule"]),
23975
23961
  singleFlight: Number(row["single_flight"]) === 1,
23976
23962
  costCeilingUsd: row["cost_ceiling_usd"] === null ? null : Number(row["cost_ceiling_usd"]),
@@ -24004,7 +23990,7 @@ function readTournamentTerms(row) {
24004
23990
  active: Number(row["active"]) === 1,
24005
23991
  kind: String(row["kind"] ?? "race") === "comparison" ? "comparison" : "race",
24006
23992
  raceDigest: String(row["race_digest"]),
24007
- agents: JSON.parse(String(row["agents"])),
23993
+ agents: parseStoreColumn("tournament_terms.agents", row["agents"]),
24008
23994
  n: Number(row["n"]),
24009
23995
  perAgentBudgetMicrousd: Number(row["per_agent_budget_microusd"]),
24010
23996
  overrunReserveMicrousd: Number(row["overrun_reserve_microusd"]),
@@ -24169,12 +24155,12 @@ function readCoordinatorProposal(row) {
24169
24155
  name: String(row["name"]),
24170
24156
  repo: String(row["repo"]),
24171
24157
  kind: String(row["kind"]),
24172
- payload: JSON.parse(String(row["payload_json"])),
24158
+ payload: parseStoreColumn("coordinator_proposal.payload_json", row["payload_json"]),
24173
24159
  state: String(row["state"]),
24174
24160
  createdAt: String(row["created_at"]),
24175
24161
  resolvedAt: row["resolved_at"] === null ? null : String(row["resolved_at"]),
24176
24162
  resolvedBy: row["resolved_by"] === null ? null : String(row["resolved_by"]),
24177
- outcome: row["outcome_json"] === null ? null : JSON.parse(String(row["outcome_json"])),
24163
+ outcome: row["outcome_json"] === null ? null : parseStoreColumn("coordinator_proposal.outcome_json", row["outcome_json"]),
24178
24164
  };
24179
24165
  }
24180
24166
  function readMateProposal(row) {
@@ -24185,13 +24171,13 @@ function readMateProposal(row) {
24185
24171
  thread: Number(row["thread"]),
24186
24172
  turn: Number(row["turn"]),
24187
24173
  kind: String(row["kind"]),
24188
- payload: JSON.parse(String(row["payload_json"])),
24174
+ payload: parseStoreColumn("mate_proposal.payload_json", row["payload_json"]),
24189
24175
  ceilingDigest: String(row["ceiling_digest"]),
24190
24176
  state: String(row["state"]),
24191
24177
  createdAt: String(row["created_at"]),
24192
24178
  resolvedAt: maybe("resolved_at"),
24193
24179
  resolvedBy: maybe("resolved_by"),
24194
- outcome: outcome === null ? null : JSON.parse(outcome),
24180
+ outcome: outcome === null ? null : parseStoreColumn("mate_proposal.outcome_json", outcome),
24195
24181
  };
24196
24182
  }
24197
24183
  function readMateTurn(row) {
@@ -24261,9 +24247,10 @@ function readMatrixJson(raw) {
24261
24247
  if (raw === null || raw === undefined)
24262
24248
  return [];
24263
24249
  try {
24264
- const parsed = JSON.parse(String(raw));
24265
- if (!Array.isArray(parsed))
24250
+ const read = readStoreColumn("proof_verdict.matrix_json", raw);
24251
+ if (!read.ok)
24266
24252
  return [];
24253
+ const parsed = read.value;
24267
24254
  // `answered` and `review` are additive (post-v39/v40 review findings):
24268
24255
  // a row stored before either fix simply has none, never a throw at
24269
24256
  // render.
@@ -24274,15 +24261,7 @@ function readMatrixJson(raw) {
24274
24261
  }
24275
24262
  }
24276
24263
  function readProofVerdict(row) {
24277
- let reasons = [];
24278
- try {
24279
- const parsed = JSON.parse(String(row["reasons_json"]));
24280
- if (Array.isArray(parsed))
24281
- reasons = parsed.map(one => String(one));
24282
- }
24283
- catch {
24284
- reasons = [];
24285
- }
24264
+ const reasons = readStoreStringifiedList("proof_verdict.reasons_json", row["reasons_json"]);
24286
24265
  return {
24287
24266
  run: Number(row["run"]),
24288
24267
  verdict: String(row["verdict"]),
@@ -24296,10 +24275,10 @@ function readReviewBindingList(raw) {
24296
24275
  if (typeof raw !== "string")
24297
24276
  return [];
24298
24277
  try {
24299
- const parsed = JSON.parse(raw);
24300
- if (!Array.isArray(parsed))
24278
+ const read = readStoreColumn("criterion_review.screenshots_json", raw);
24279
+ if (!read.ok)
24301
24280
  return [];
24302
- return parsed
24281
+ return read.value
24303
24282
  .filter((one) => typeof one === "object" && one !== null)
24304
24283
  .map(one => ({
24305
24284
  artifact: Number(one["artifact"]),
@@ -24388,15 +24367,7 @@ export function revisionTermsOf(sourceRef, sourceScope, coverage, permissionDefa
24388
24367
  };
24389
24368
  }
24390
24369
  function readRepairChain(row) {
24391
- let unresolved = [];
24392
- try {
24393
- const parsed = JSON.parse(String(row["unresolved_json"]));
24394
- if (Array.isArray(parsed))
24395
- unresolved = parsed.map(one => String(one));
24396
- }
24397
- catch {
24398
- unresolved = [];
24399
- }
24370
+ const unresolved = readStoreStringifiedList("repair_chain.unresolved_json", row["unresolved_json"]);
24400
24371
  return {
24401
24372
  id: Number(row["id"]),
24402
24373
  rootTask: String(row["root_task"]),
@@ -24457,11 +24428,11 @@ export function contestantProfileOf(provider, model, repairModel, permissionMode
24457
24428
  /** The stored rubric, re-proved through the same strict parser that admits
24458
24429
  * one on the way in — never trusted bytes back out. Malformed or absent
24459
24430
  * reads back as `[]`, matching every rubric-less scope. */
24460
- function readAcceptance(value) {
24431
+ function readAcceptance(column, value) {
24461
24432
  if (value === null || value === undefined)
24462
24433
  return [];
24463
24434
  try {
24464
- return parseAcceptanceCriteria(JSON.parse(String(value))).criteria;
24435
+ return parseAcceptanceCriteria(parseStoreColumn(column, value)).criteria;
24465
24436
  }
24466
24437
  catch {
24467
24438
  return [];
@@ -24522,7 +24493,7 @@ function readScope(row) {
24522
24493
  taskId: String(row["task_id"]),
24523
24494
  goal: String(row["goal"]),
24524
24495
  outOfScope: row["out_of_scope"] === null ? null : String(row["out_of_scope"]),
24525
- touches: readJsonArray(row["touches"]),
24496
+ touches: readStoreTextList("task_scope.touches", row["touches"]),
24526
24497
  proposedAt: String(row["proposed_at"]),
24527
24498
  digest: String(row["digest"]),
24528
24499
  budgetMicrousd: row["budget_microusd"] === null || row["budget_microusd"] === undefined ? null : Number(row["budget_microusd"]),
@@ -24546,7 +24517,7 @@ function readScope(row) {
24546
24517
  proposedChainJson: row["proposed_chain_json"] === null || row["proposed_chain_json"] === undefined ? null : String(row["proposed_chain_json"]),
24547
24518
  approvedChainJson: row["approved_chain_json"] === null || row["approved_chain_json"] === undefined ? null : String(row["approved_chain_json"]),
24548
24519
  approvalKind: String(row["approval_kind"] ?? "profile") === "chain" ? "chain" : "profile",
24549
- acceptance: readAcceptance(row["acceptance_json"]),
24520
+ acceptance: readAcceptance("task_scope.acceptance_json", row["acceptance_json"]),
24550
24521
  qualityMode: row["quality_mode"] === "strict" ? "strict" : "default",
24551
24522
  riskLevel: isRiskLevel(row["risk_level"]) ? row["risk_level"] : "routine",
24552
24523
  proposedRouteJson: row["proposed_route_json"] === null || row["proposed_route_json"] === undefined ? null : String(row["proposed_route_json"]),
@@ -24561,8 +24532,8 @@ function readRunner(row) {
24561
24532
  name: String(row["name"]),
24562
24533
  host: String(row["host"]),
24563
24534
  capacity: Number(row["capacity"]),
24564
- repos: readJsonArray(row["repos"]),
24565
- agents: readJsonArray(row["agents"]),
24535
+ repos: readStoreTextList("runner.repos", row["repos"]),
24536
+ agents: readStoreTextList("runner.agents", row["agents"]),
24566
24537
  registeredAt: String(row["registered_at"]),
24567
24538
  heartbeatAt: String(row["heartbeat_at"]),
24568
24539
  retiredAt: row["retired_at"] === null ? null : String(row["retired_at"]),
@@ -24655,8 +24626,8 @@ function readGrant(row) {
24655
24626
  return {
24656
24627
  repo: String(row["repo"]),
24657
24628
  backend: String(row["backend"]),
24658
- paths: readJsonArray(row["paths"]),
24659
- mutations: readJsonArray(row["mutations"]),
24629
+ paths: readStoreTextList("backend_grant.paths", row["paths"]),
24630
+ mutations: readStoreTextList("backend_grant.mutations", row["mutations"]),
24660
24631
  selector: String(row["selector"]) === "all" ? "all" : "ours",
24661
24632
  credentialScope: row["credential_scope"] === null ? null : String(row["credential_scope"]),
24662
24633
  observedByGit: Number(row["observed_by_git"]) === 1,
@@ -24672,15 +24643,6 @@ function readGrant(row) {
24672
24643
  dispatchBlockedDetail: row["dispatch_blocked_detail"] === null || row["dispatch_blocked_detail"] === undefined ? null : String(row["dispatch_blocked_detail"]),
24673
24644
  };
24674
24645
  }
24675
- function readJsonArray(value) {
24676
- try {
24677
- const parsed = JSON.parse(String(value));
24678
- return Array.isArray(parsed) ? parsed.filter((one) => typeof one === "string") : [];
24679
- }
24680
- catch {
24681
- return [];
24682
- }
24683
- }
24684
24646
  export function verifiedAuthor(name) {
24685
24647
  return name;
24686
24648
  }