clearotron 0.5.0-beta.2 → 0.5.0-beta.4

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 (73) hide show
  1. package/.env.example +7 -0
  2. package/build-info.json +2 -2
  3. package/docs/architecture/03-run-lifecycle.md +6 -0
  4. package/docs/architecture/04-configuration-reference.md +1 -0
  5. package/docs/architecture/05-config-governance.md +1 -1
  6. package/driver/CHANGELOG.md +21 -0
  7. package/driver/band-shape.mjs +19 -11
  8. package/driver/contract-vocabulary.mjs +3 -3
  9. package/driver/coverage-form-io.mjs +3 -1
  10. package/driver/coverage-form.mjs +21 -2
  11. package/driver/coverage-ledger.mjs +7 -1
  12. package/driver/declination-call.mjs +33 -16
  13. package/driver/degraded-parts.mjs +6 -0
  14. package/driver/engine/mcp/band-server.mjs +12 -12
  15. package/driver/engine/mcp/clarivate-server.mjs +2 -0
  16. package/driver/engine/mcp/codex-config.mjs +12 -7
  17. package/driver/engine/mcp/corsearch-server.mjs +2 -0
  18. package/driver/engine/mcp/declination-server.mjs +4 -5
  19. package/driver/engine/mcp/dispositions-server.mjs +7 -0
  20. package/driver/engine/mcp/euipo-server.mjs +2 -0
  21. package/driver/engine/mcp/free-tier-server.mjs +2 -0
  22. package/driver/engine/mcp/gather-config.mjs +15 -0
  23. package/driver/engine/mcp/held-answers.mjs +11 -3
  24. package/driver/engine/mcp/perplexity-server.mjs +29 -1
  25. package/driver/engine/mcp/recording-server.mjs +16 -1
  26. package/driver/engine/mcp/signa-server.mjs +2 -0
  27. package/driver/engine/mcp/supplemental.mjs +11 -3
  28. package/driver/engine/mcp/uspto-local-server.mjs +2 -0
  29. package/driver/engine/openai-agent.mjs +48 -9
  30. package/driver/envelope-settle.mjs +13 -3
  31. package/driver/findings-model.mjs +17 -0
  32. package/driver/gateway.mjs +31 -10
  33. package/driver/package.json +1 -1
  34. package/driver/parts-not-completed.mjs +52 -11
  35. package/driver/pipeline-knockout.mjs +5 -4
  36. package/driver/pipeline.mjs +474 -81
  37. package/driver/placement-decisions.mjs +24 -9
  38. package/driver/placement-form-io.mjs +21 -1
  39. package/driver/placement-form.mjs +25 -15
  40. package/driver/placement-union.mjs +53 -8
  41. package/driver/predelivery-lint.mjs +8 -1
  42. package/driver/publish/index.mjs +7 -3
  43. package/driver/register-digest-record.mjs +6 -3
  44. package/driver/research-answers.mjs +54 -7
  45. package/driver/runner.mjs +4 -4
  46. package/driver/saved-findings-block.mjs +36 -0
  47. package/driver/skills/clearance-register/digest.md +1 -1
  48. package/driver/skills/clearance-search/phase2-execution.md +7 -5
  49. package/driver/skills/clearance-search/synthesis-rules.md +3 -3
  50. package/driver/skills/placement-inquiry/SKILL.md +3 -3
  51. package/driver/stage-context.mjs +18 -0
  52. package/driver/stages.mjs +41 -13
  53. package/driver/suite-census.json +140 -14
  54. package/driver/synthesis-record.mjs +167 -1
  55. package/driver/verify.mjs +6 -6
  56. package/mcp-server/CHANGELOG.md +8 -0
  57. package/mcp-server/package.json +1 -1
  58. package/package.json +1 -1
  59. package/portal-ui/package.json +1 -1
  60. package/providers/_shared/README.md +1 -0
  61. package/providers/_shared/enumerate.mjs +4 -3
  62. package/providers/_shared/execute-plan.mjs +33 -7
  63. package/providers/_shared/send-until.mjs +25 -0
  64. package/providers/oauth-mcp-bridge/CHANGELOG.md +8 -0
  65. package/providers/oauth-mcp-bridge/package.json +1 -1
  66. package/scripts/band-shape-probe.mjs +1 -1
  67. package/scripts/e2e-first-time.mjs +2 -1
  68. package/scripts/release-notes-lint.mjs +1 -1
  69. package/shared/inside-run.mjs +54 -0
  70. package/shared/names-in-force.mjs +1 -0
  71. package/shared/slices-taken-out.mjs +65 -0
  72. package/skills/clearotron-account/SKILL.md +1 -1
  73. package/skills/clearotron-ops/SKILL.md +1 -1
@@ -17,35 +17,40 @@
17
17
  //
18
18
  // ONCE PER PART. The catch never writes a part whose record already exists: a failure on the pass that delivers
19
19
  // without it ends the run as it always did, with the stop notice, and the record is removed then, so a later resume
20
- // tries the part again.
20
+ // tries the part again. The searches are the one part whose record grows: each failure names the slices it could not
21
+ // prove ran, and a failure that names a slice the record does not hold yet adds it; one that names none new ends the
22
+ // run, so the run still ends.
21
23
  //
22
- // A pure leaf: node:fs, node:path and shared/driver-dir.mjs only.
24
+ // A pure leaf: node:fs, node:path, shared/driver-dir.mjs and shared/slices-taken-out.mjs (node:fs and the same) only.
23
25
  import { readFileSync, writeFileSync, renameSync, rmSync, readdirSync } from "node:fs";
24
26
  import { join } from "node:path";
25
27
  import { driverDir, ensureDriverDir } from "../shared/driver-dir.mjs";
28
+ // Where the record sits is stated with the band readers outside the driver, which read the searches' record too.
29
+ import { NOT_COMPLETED_DIR, notCompletedPath } from "../shared/slices-taken-out.mjs";
26
30
 
27
- export const NOT_COMPLETED_DIR = "not-completed";
31
+ export { NOT_COMPLETED_DIR, notCompletedPath };
28
32
 
29
33
  // The covered parts. `assessment` is the run's overall assessment: placement, the register write-up and synthesis, and
30
34
  // everything built from them. `review` is the first review of the assessment, whose verdict the sign-off is. `overview`
31
35
  // is the report overview, the conclusion under the rating. `delivery` is the
32
36
  // changed-material check before publishing: it never delivers past staleness, and its record buys the report one more
33
- // assembly from the current material instead of the stop notice.
34
- export const NOT_COMPLETED_PARTS = Object.freeze(["assessment", "review", "overview", "delivery"]);
35
-
36
- /** The record's path for `part`. */
37
- export function notCompletedPath(runDir, part) {
38
- return driverDir(runDir, NOT_COMPLETED_DIR, `${part}.json`);
39
- }
37
+ // assembly from the current material instead of the stop notice. `searches` is the register's searches: the slices a
38
+ // check could not prove ran, each with its axis, its qid and the reason it is open, taken out of the band and
39
+ // delivered as open rows; the axes whose pass was killed at its time limit, read as the last pass left them and
40
+ // disclosed, their clean claims open (`disclosed_axes`); and the units that wrote no result once their attempts were
41
+ // spent, delivered as their bands stand and not sent again (`unfinished_units`).
42
+ export const NOT_COMPLETED_PARTS = Object.freeze(["assessment", "review", "overview", "delivery", "searches"]);
40
43
 
41
44
  /**
42
45
  * Record that `part` could not be completed. `stage` is the stage that failed, `reason` its cause for the run's record
43
46
  * (never a reader's cell), `sig` and `terminalKind` what the recovery decision said. Written through a temporary file
44
47
  * and a rename, so a reader never sees half a record. Returns the record.
45
48
  */
46
- export function writePartNotCompleted(runDir, part, { stage = null, reason = null, sig = null, terminalKind = null } = {}) {
49
+ export function writePartNotCompleted(runDir, part, { stage = null, reason = null, sig = null, terminalKind = null, slices = null, disclosedAxes = null, unfinishedUnits = null } = {}) {
47
50
  if (!NOT_COMPLETED_PARTS.includes(part)) throw new Error(`parts-not-completed: "${part}" is not a covered part`);
48
51
  const record = { part, stage, reason: reason == null ? null : String(reason).slice(0, 600), sig, terminalKind,
52
+ ...(Array.isArray(slices) ? { slices } : {}), ...(Array.isArray(disclosedAxes) && disclosedAxes.length ? { disclosed_axes: disclosedAxes } : {}),
53
+ ...(Array.isArray(unfinishedUnits) && unfinishedUnits.length ? { unfinished_units: unfinishedUnits } : {}),
49
54
  ts: new Date().toISOString() };
50
55
  const path = notCompletedPath(runDir, part);
51
56
  ensureDriverDir(runDir, NOT_COMPLETED_DIR);
@@ -76,6 +81,42 @@ export function partsNotCompleted(runDir) {
76
81
  return NOT_COMPLETED_PARTS.filter((p) => names.includes(`${p}.json`)).map((p) => readPartNotCompleted(runDir, p));
77
82
  }
78
83
 
84
+ /**
85
+ * The slices of `failed` that `record` does not hold yet, each `{axis, qid, reason}`, or `{axis, query, reason}` for a
86
+ * slice with no qid, which its query names: what a failure of the searches adds to their record. [] when it adds
87
+ * none. PURE.
88
+ */
89
+ export function slicesNotYetRecorded(record, failed) {
90
+ const keyOf = (s) => `${s?.axis}\u0000${s?.qid ? `qid:${s.qid}` : `query:${s?.query}`}`;
91
+ const held = new Set((Array.isArray(record?.slices) ? record.slices : []).map(keyOf));
92
+ const out = [];
93
+ for (const s of Array.isArray(failed) ? failed : []) {
94
+ if (!s?.axis || (!s?.qid && !s?.query) || held.has(keyOf(s))) continue;
95
+ held.add(keyOf(s));
96
+ out.push(s.qid ? { axis: String(s.axis), qid: String(s.qid), reason: String(s.reason ?? "") }
97
+ : { axis: String(s.axis), query: String(s.query), reason: String(s.reason ?? "") });
98
+ }
99
+ return out;
100
+ }
101
+
102
+ /**
103
+ * The axes of `failed` that `record` does not list as disclosed yet: a pass on each was killed at its time limit and its
104
+ * recovery is spent, so the delivering pass reads its band as it stands and discloses it. [] when it adds none. PURE.
105
+ */
106
+ export function axesNotYetDisclosed(record, failed) {
107
+ const held = new Set(Array.isArray(record?.disclosed_axes) ? record.disclosed_axes : []);
108
+ return [...new Set((Array.isArray(failed) ? failed : []).map((a) => String(a ?? "").trim()).filter((a) => a && !held.has(a)))];
109
+ }
110
+
111
+ /**
112
+ * The units of `failed` that `record` does not list as unfinished yet: each wrote no result once its attempts were spent,
113
+ * so the delivering pass takes it as its band stands instead of sending it again. [] when it adds none. PURE.
114
+ */
115
+ export function unitsNotYetRecorded(record, failed) {
116
+ const held = new Set(Array.isArray(record?.unfinished_units) ? record.unfinished_units : []);
117
+ return [...new Set((Array.isArray(failed) ? failed : []).map((a) => String(a ?? "").trim()).filter((a) => a && !held.has(a)))];
118
+ }
119
+
79
120
  /** Remove one part's record, once that part has completed after all. */
80
121
  export function clearPartNotCompleted(runDir, part) {
81
122
  try { rmSync(notCompletedPath(runDir, part), { force: true }); } catch { /* none */ }
@@ -64,7 +64,7 @@ import { writeRunStatus, readRunStatus, rollupStatus, atomicWrite, identitySeed
64
64
  import { batchMarkName } from "./mark-name.mjs";
65
65
  import { runLog, note, outputMeta } from "./log.mjs";
66
66
  import { defaultTerritoryState } from "./effective-scope.mjs"; // the stored-defaults reading — one producer, shared with the clearance lane
67
- import { AGENT_WHATSAPP, whatsappRouting } from "./stages.mjs";
67
+ import { whatsappRouting } from "./stages.mjs";
68
68
  import { writeOutboxPacket } from "./outbox.mjs";
69
69
  import { rollupTokens, stampTokenRollup } from "./tokens.mjs";
70
70
  import { recordRunConsumption } from "./consumption-ledger.mjs";
@@ -1205,8 +1205,9 @@ export async function knockoutInner(ctx, job, opts = {}) {
1205
1205
  // two existing. Where no number is held the packet now SAYS so in `whatsappToReason` rather than
1206
1206
  // substituting the operator silently.
1207
1207
  //
1208
- // The failure notice below deliberately keeps `AGENT_WHATSAPP[agent]`: 289 ruled on the COMPLETION
1209
- // notice, and a stage failure is an operational page rather than an answer the requester ordered.
1208
+ // The failure notice below routes the same way (buildFailurePacket). It used to keep `AGENT_WHATSAPP[agent]` as an
1209
+ // operational page, but the courier relays it to the requester, who is owed it as they are owed a report, and the
1210
+ // operator keeps the page as the copy beside it.
1210
1211
  ...whatsappRouting(job, agent),
1211
1212
  // ONE LINE, N LINKS. This said "Report: <one url>" and on a batch that url is now null, which would
1212
1213
  // have read "Report: null" — the fail-visible shape doing its job, and still not a line to send. A
@@ -1350,7 +1351,7 @@ export async function knockoutInner(ctx, job, opts = {}) {
1350
1351
  try {
1351
1352
  const rich = buildFailurePacket({
1352
1353
  runId, agent, job, failedStage, shortReason, terminalKind,
1353
- reasonVerbatim: String(reason).slice(0, 1000), whatsappTo: AGENT_WHATSAPP[agent] ?? null,
1354
+ reasonVerbatim: String(reason).slice(0, 1000),
1354
1355
  });
1355
1356
  const packet = {
1356
1357
  kind: "run-failed", ...rich,