lossless-openclaw-orchestrator 1.2.5 → 1.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/.codex-plugin/plugin.json +40 -0
  2. package/.codex-plugin/scripts/thread-title-finalize.mjs +31 -0
  3. package/README.md +14 -9
  4. package/VISION.md +9 -7
  5. package/dist/packages/adapters/src/index.js +3 -0
  6. package/dist/packages/cli/src/cli-mcp-product-smoke.js +323 -0
  7. package/dist/packages/cli/src/index.js +1284 -47
  8. package/dist/packages/cli/src/openclaw-live-control-smoke.js +69 -30
  9. package/dist/packages/cli/src/openclaw-tool-smoke.js +485 -14
  10. package/dist/packages/cli/src/qa-lab-desktop-contract.js +385 -0
  11. package/dist/packages/cli/src/qa-lab-live-control-matrix.js +383 -0
  12. package/dist/packages/cli/src/qa-lab-privacy-scan.js +244 -0
  13. package/dist/packages/cli/src/qa-lab-review.js +338 -0
  14. package/dist/packages/cli/src/qa-lab-run.js +548 -0
  15. package/dist/packages/cli/src/qa-lab-tool-coverage.js +545 -0
  16. package/dist/packages/cli/src/qa-lab-workflow.js +641 -0
  17. package/dist/packages/cli/src/release-bundle.js +2 -2
  18. package/dist/packages/cli/src/release-ga-smoke.js +244 -5
  19. package/dist/packages/cli/src/release-preflight.js +1 -1
  20. package/dist/packages/cli/src/scenario-sweep.js +8 -0
  21. package/dist/packages/cli/src/scorecard-sweep.js +4 -0
  22. package/dist/packages/core/src/index.js +2111 -311
  23. package/dist/packages/core/src/search.js +630 -0
  24. package/dist/packages/mcp-server/src/server.js +14 -2
  25. package/dist/packages/mcp-server/src/tools.js +113 -7
  26. package/dist/packages/openclaw-plugin/src/index.js +6 -3
  27. package/docs/BETA_RELEASE_RUNBOOK.md +1 -1
  28. package/docs/CLAIM_AUDIT.md +2 -2
  29. package/docs/CODEX_JSONL_DRIFT.md +37 -0
  30. package/docs/QA_LAB.md +62 -0
  31. package/docs/SETUP.md +35 -6
  32. package/docs/releases/CHANGELOG.md +73 -0
  33. package/docs/releases/RELEASE_NOTES_1.2.6.md +89 -0
  34. package/evals/scenarios/retrieval-goldens/v1/baseline-floors.json +61 -0
  35. package/evals/scenarios/retrieval-goldens/v1/goldens.json +361 -0
  36. package/hooks/hooks.json +15 -0
  37. package/openclaw.plugin.json +400 -3
  38. package/package.json +3 -1
  39. package/packages/adapters/src/index.ts +6 -0
  40. package/packages/cli/src/cli-mcp-product-smoke.ts +394 -0
  41. package/packages/cli/src/index.ts +1384 -68
  42. package/packages/cli/src/openclaw-live-control-smoke.ts +67 -30
  43. package/packages/cli/src/openclaw-tool-smoke.ts +490 -16
  44. package/packages/cli/src/qa-lab-desktop-contract.ts +471 -0
  45. package/packages/cli/src/qa-lab-live-control-matrix.ts +471 -0
  46. package/packages/cli/src/qa-lab-privacy-scan.ts +310 -0
  47. package/packages/cli/src/qa-lab-review.ts +461 -0
  48. package/packages/cli/src/qa-lab-run.ts +688 -0
  49. package/packages/cli/src/qa-lab-tool-coverage.ts +700 -0
  50. package/packages/cli/src/qa-lab-workflow.ts +737 -0
  51. package/packages/cli/src/release-bundle.ts +2 -2
  52. package/packages/cli/src/release-ga-smoke.ts +324 -6
  53. package/packages/cli/src/release-preflight.ts +1 -1
  54. package/packages/cli/src/scenario-sweep.ts +17 -0
  55. package/packages/cli/src/scorecard-sweep.ts +10 -0
  56. package/packages/core/src/index.ts +2652 -384
  57. package/packages/core/src/search.ts +770 -0
  58. package/packages/mcp-server/src/server.ts +14 -2
  59. package/packages/mcp-server/src/tools.ts +146 -7
  60. package/packages/openclaw-plugin/openclaw.plugin.json +400 -3
  61. package/packages/openclaw-plugin/src/index.ts +6 -2
  62. /package/docs/{RELEASE_NOTES_0.1.0-beta.0.md → releases/RELEASE_NOTES_0.1.0-beta.0.md} +0 -0
  63. /package/docs/{RELEASE_NOTES_0.1.0-beta.1.md → releases/RELEASE_NOTES_0.1.0-beta.1.md} +0 -0
  64. /package/docs/{RELEASE_NOTES_0.1.0-beta.10.md → releases/RELEASE_NOTES_0.1.0-beta.10.md} +0 -0
  65. /package/docs/{RELEASE_NOTES_0.1.0-beta.11.md → releases/RELEASE_NOTES_0.1.0-beta.11.md} +0 -0
  66. /package/docs/{RELEASE_NOTES_0.1.0-beta.12.md → releases/RELEASE_NOTES_0.1.0-beta.12.md} +0 -0
  67. /package/docs/{RELEASE_NOTES_0.1.0-beta.13.md → releases/RELEASE_NOTES_0.1.0-beta.13.md} +0 -0
  68. /package/docs/{RELEASE_NOTES_0.1.0-beta.14.md → releases/RELEASE_NOTES_0.1.0-beta.14.md} +0 -0
  69. /package/docs/{RELEASE_NOTES_0.1.0-beta.15.md → releases/RELEASE_NOTES_0.1.0-beta.15.md} +0 -0
  70. /package/docs/{RELEASE_NOTES_0.1.0-beta.16.md → releases/RELEASE_NOTES_0.1.0-beta.16.md} +0 -0
  71. /package/docs/{RELEASE_NOTES_0.1.0-beta.17.md → releases/RELEASE_NOTES_0.1.0-beta.17.md} +0 -0
  72. /package/docs/{RELEASE_NOTES_0.1.0-beta.18.md → releases/RELEASE_NOTES_0.1.0-beta.18.md} +0 -0
  73. /package/docs/{RELEASE_NOTES_0.1.0-beta.19.md → releases/RELEASE_NOTES_0.1.0-beta.19.md} +0 -0
  74. /package/docs/{RELEASE_NOTES_0.1.0-beta.2.md → releases/RELEASE_NOTES_0.1.0-beta.2.md} +0 -0
  75. /package/docs/{RELEASE_NOTES_0.1.0-beta.20.md → releases/RELEASE_NOTES_0.1.0-beta.20.md} +0 -0
  76. /package/docs/{RELEASE_NOTES_0.1.0-beta.21.md → releases/RELEASE_NOTES_0.1.0-beta.21.md} +0 -0
  77. /package/docs/{RELEASE_NOTES_0.1.0-beta.22.md → releases/RELEASE_NOTES_0.1.0-beta.22.md} +0 -0
  78. /package/docs/{RELEASE_NOTES_0.1.0-beta.23.md → releases/RELEASE_NOTES_0.1.0-beta.23.md} +0 -0
  79. /package/docs/{RELEASE_NOTES_0.1.0-beta.24.md → releases/RELEASE_NOTES_0.1.0-beta.24.md} +0 -0
  80. /package/docs/{RELEASE_NOTES_0.1.0-beta.25.md → releases/RELEASE_NOTES_0.1.0-beta.25.md} +0 -0
  81. /package/docs/{RELEASE_NOTES_0.1.0-beta.26.md → releases/RELEASE_NOTES_0.1.0-beta.26.md} +0 -0
  82. /package/docs/{RELEASE_NOTES_0.1.0-beta.27.md → releases/RELEASE_NOTES_0.1.0-beta.27.md} +0 -0
  83. /package/docs/{RELEASE_NOTES_0.1.0-beta.28.md → releases/RELEASE_NOTES_0.1.0-beta.28.md} +0 -0
  84. /package/docs/{RELEASE_NOTES_0.1.0-beta.29.md → releases/RELEASE_NOTES_0.1.0-beta.29.md} +0 -0
  85. /package/docs/{RELEASE_NOTES_0.1.0-beta.3.md → releases/RELEASE_NOTES_0.1.0-beta.3.md} +0 -0
  86. /package/docs/{RELEASE_NOTES_0.1.0-beta.30.md → releases/RELEASE_NOTES_0.1.0-beta.30.md} +0 -0
  87. /package/docs/{RELEASE_NOTES_0.1.0-beta.31.md → releases/RELEASE_NOTES_0.1.0-beta.31.md} +0 -0
  88. /package/docs/{RELEASE_NOTES_0.1.0-beta.32.md → releases/RELEASE_NOTES_0.1.0-beta.32.md} +0 -0
  89. /package/docs/{RELEASE_NOTES_0.1.0-beta.33.md → releases/RELEASE_NOTES_0.1.0-beta.33.md} +0 -0
  90. /package/docs/{RELEASE_NOTES_0.1.0-beta.34.md → releases/RELEASE_NOTES_0.1.0-beta.34.md} +0 -0
  91. /package/docs/{RELEASE_NOTES_0.1.0-beta.35.md → releases/RELEASE_NOTES_0.1.0-beta.35.md} +0 -0
  92. /package/docs/{RELEASE_NOTES_0.1.0-beta.36.md → releases/RELEASE_NOTES_0.1.0-beta.36.md} +0 -0
  93. /package/docs/{RELEASE_NOTES_0.1.0-beta.37.md → releases/RELEASE_NOTES_0.1.0-beta.37.md} +0 -0
  94. /package/docs/{RELEASE_NOTES_0.1.0-beta.38.md → releases/RELEASE_NOTES_0.1.0-beta.38.md} +0 -0
  95. /package/docs/{RELEASE_NOTES_0.1.0-beta.39.md → releases/RELEASE_NOTES_0.1.0-beta.39.md} +0 -0
  96. /package/docs/{RELEASE_NOTES_0.1.0-beta.4.md → releases/RELEASE_NOTES_0.1.0-beta.4.md} +0 -0
  97. /package/docs/{RELEASE_NOTES_0.1.0-beta.40.md → releases/RELEASE_NOTES_0.1.0-beta.40.md} +0 -0
  98. /package/docs/{RELEASE_NOTES_0.1.0-beta.41.md → releases/RELEASE_NOTES_0.1.0-beta.41.md} +0 -0
  99. /package/docs/{RELEASE_NOTES_0.1.0-beta.42.md → releases/RELEASE_NOTES_0.1.0-beta.42.md} +0 -0
  100. /package/docs/{RELEASE_NOTES_0.1.0-beta.43.md → releases/RELEASE_NOTES_0.1.0-beta.43.md} +0 -0
  101. /package/docs/{RELEASE_NOTES_0.1.0-beta.44.md → releases/RELEASE_NOTES_0.1.0-beta.44.md} +0 -0
  102. /package/docs/{RELEASE_NOTES_0.1.0-beta.45.md → releases/RELEASE_NOTES_0.1.0-beta.45.md} +0 -0
  103. /package/docs/{RELEASE_NOTES_0.1.0-beta.46.md → releases/RELEASE_NOTES_0.1.0-beta.46.md} +0 -0
  104. /package/docs/{RELEASE_NOTES_0.1.0-beta.47.md → releases/RELEASE_NOTES_0.1.0-beta.47.md} +0 -0
  105. /package/docs/{RELEASE_NOTES_0.1.0-beta.48.md → releases/RELEASE_NOTES_0.1.0-beta.48.md} +0 -0
  106. /package/docs/{RELEASE_NOTES_0.1.0-beta.49.md → releases/RELEASE_NOTES_0.1.0-beta.49.md} +0 -0
  107. /package/docs/{RELEASE_NOTES_0.1.0-beta.5.md → releases/RELEASE_NOTES_0.1.0-beta.5.md} +0 -0
  108. /package/docs/{RELEASE_NOTES_0.1.0-beta.6.md → releases/RELEASE_NOTES_0.1.0-beta.6.md} +0 -0
  109. /package/docs/{RELEASE_NOTES_0.1.0-beta.7.md → releases/RELEASE_NOTES_0.1.0-beta.7.md} +0 -0
  110. /package/docs/{RELEASE_NOTES_0.1.0-beta.8.md → releases/RELEASE_NOTES_0.1.0-beta.8.md} +0 -0
  111. /package/docs/{RELEASE_NOTES_0.1.0-beta.9.md → releases/RELEASE_NOTES_0.1.0-beta.9.md} +0 -0
  112. /package/docs/{RELEASE_NOTES_1.0.0-rc.1.md → releases/RELEASE_NOTES_1.0.0-rc.1.md} +0 -0
  113. /package/docs/{RELEASE_NOTES_1.0.0.md → releases/RELEASE_NOTES_1.0.0.md} +0 -0
  114. /package/docs/{RELEASE_NOTES_1.1.0.md → releases/RELEASE_NOTES_1.1.0.md} +0 -0
  115. /package/docs/{RELEASE_NOTES_1.1.1.md → releases/RELEASE_NOTES_1.1.1.md} +0 -0
  116. /package/docs/{RELEASE_NOTES_1.1.2.md → releases/RELEASE_NOTES_1.1.2.md} +0 -0
  117. /package/docs/{RELEASE_NOTES_1.1.3.md → releases/RELEASE_NOTES_1.1.3.md} +0 -0
  118. /package/docs/{RELEASE_NOTES_1.1.4.md → releases/RELEASE_NOTES_1.1.4.md} +0 -0
  119. /package/docs/{RELEASE_NOTES_1.2.0-beta.0.md → releases/RELEASE_NOTES_1.2.0-beta.0.md} +0 -0
  120. /package/docs/{RELEASE_NOTES_1.2.0-beta.1.md → releases/RELEASE_NOTES_1.2.0-beta.1.md} +0 -0
  121. /package/docs/{RELEASE_NOTES_1.2.0-beta.2.md → releases/RELEASE_NOTES_1.2.0-beta.2.md} +0 -0
  122. /package/docs/{RELEASE_NOTES_1.2.0-beta.3.md → releases/RELEASE_NOTES_1.2.0-beta.3.md} +0 -0
  123. /package/docs/{RELEASE_NOTES_1.2.0.md → releases/RELEASE_NOTES_1.2.0.md} +0 -0
  124. /package/docs/{RELEASE_NOTES_1.2.1.md → releases/RELEASE_NOTES_1.2.1.md} +0 -0
  125. /package/docs/{RELEASE_NOTES_1.2.2.md → releases/RELEASE_NOTES_1.2.2.md} +0 -0
  126. /package/docs/{RELEASE_NOTES_1.2.3.md → releases/RELEASE_NOTES_1.2.3.md} +0 -0
  127. /package/docs/{RELEASE_NOTES_1.2.4.md → releases/RELEASE_NOTES_1.2.4.md} +0 -0
  128. /package/docs/{RELEASE_NOTES_1.2.5.md → releases/RELEASE_NOTES_1.2.5.md} +0 -0
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "lossless-openclaw-orchestrator",
3
+ "version": "1.2.5",
4
+ "description": "Index, search, and prepare local Codex sessions with LCO, including one-shot thread title aliases for easier recall.",
5
+ "author": {
6
+ "name": "100yenadmin",
7
+ "email": "support@martian.engineering",
8
+ "url": "https://github.com/100yenadmin"
9
+ },
10
+ "homepage": "https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO",
11
+ "repository": "https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO",
12
+ "license": "PolyForm-Noncommercial-1.0.0",
13
+ "keywords": [
14
+ "codex",
15
+ "lco",
16
+ "thread-search",
17
+ "local-index"
18
+ ],
19
+ "interface": {
20
+ "displayName": "Lossless Codex Orchestrator",
21
+ "shortDescription": "Local Codex indexing and one-shot title aliases",
22
+ "longDescription": "LCO keeps Codex session recall local-first by indexing public-safe metadata and adding one-shot thread title aliases after assistant stops, so agents can find threads by generated names and IDs without reading raw histories.",
23
+ "developerName": "100yenadmin",
24
+ "category": "Developer Tools",
25
+ "capabilities": [
26
+ "Interactive",
27
+ "Read"
28
+ ],
29
+ "websiteURL": "https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO",
30
+ "privacyPolicyURL": "https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/blob/main/SECURITY.md",
31
+ "termsOfServiceURL": "https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/blob/main/LICENSE",
32
+ "defaultPrompt": [
33
+ "Search my local Codex sessions",
34
+ "Find the thread for this LCO task",
35
+ "Summarize recent Codex work"
36
+ ],
37
+ "brandColor": "#2563EB",
38
+ "screenshots": []
39
+ }
40
+ }
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync } from "node:fs";
3
+ import { spawnSync } from "node:child_process";
4
+ import { join } from "node:path";
5
+
6
+ const payload = await readStdin();
7
+ const localCli = join(process.cwd(), "dist", "packages", "cli", "src", "index.js");
8
+ const command = existsSync(localCli) ? process.execPath : "loo";
9
+ const args = existsSync(localCli)
10
+ ? [localCli, "hook", "thread-title-finalize", "--payload-stdin"]
11
+ : ["hook", "thread-title-finalize", "--payload-stdin"];
12
+
13
+ const result = spawnSync(command, args, {
14
+ input: payload,
15
+ encoding: "utf8",
16
+ stdio: ["pipe", "pipe", "pipe"]
17
+ });
18
+
19
+ if (result.stdout) process.stdout.write(result.stdout);
20
+ if (result.stderr) process.stderr.write(result.stderr);
21
+ if (result.error) {
22
+ process.stderr.write(`${result.error.message}\n`);
23
+ process.exit(1);
24
+ }
25
+ process.exit(result.status ?? 1);
26
+
27
+ async function readStdin() {
28
+ const chunks = [];
29
+ for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
30
+ return Buffer.concat(chunks).toString("utf8");
31
+ }
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Lossless OpenClaw Orchestrator
1
+ # Lossless Codex Orchestrator
2
2
 
3
3
  **LCO turns local Codex sessions into searchable, bounded, approval-aware
4
4
  work objects for OpenClaw.**
@@ -23,7 +23,7 @@ safe action without rereading raw transcripts.
23
23
  | Approval-gated boundaries | Dry-run Codex actions and verify matching audit ids before any live control. |
24
24
  | OpenClaw/MCP tools | Use the same local-first recall and approval-bounded surfaces from agent workflows. |
25
25
 
26
- [Setup](docs/SETUP.md) · [Contributing](CONTRIBUTING.md) · [Agent Instructions](AGENTS.md) · [Agent Skill](skills/lossless-openclaw-orchestrator/SKILL.md) · [OpenClaw Plugin](docs/OPENCLAW_PLUGIN.md) · [Security](SECURITY.md) · [Code of Conduct](CODE_OF_CONDUCT.md) · [Vision](VISION.md) · [Privacy](docs/PRIVACY.md) · [Claude Boundary](docs/CLAUDE_ADAPTER_BOUNDARY.md) · [Claim Audit](docs/CLAIM_AUDIT.md) · [Release Notes](docs/RELEASE_NOTES_1.2.5.md) · [1.2.4 Notes](docs/RELEASE_NOTES_1.2.4.md) · [1.2.3 Notes](docs/RELEASE_NOTES_1.2.3.md) · [1.2.2 Notes](docs/RELEASE_NOTES_1.2.2.md) · [1.2.1 Notes](docs/RELEASE_NOTES_1.2.1.md) · [1.2.0 Notes](docs/RELEASE_NOTES_1.2.0.md) · [1.1.4 Notes](docs/RELEASE_NOTES_1.1.4.md) · [1.0 Notes](docs/RELEASE_NOTES_1.0.0.md) · [License](LICENSE)
26
+ [Setup](docs/SETUP.md) · [Contributing](CONTRIBUTING.md) · [Agent Instructions](AGENTS.md) · [Agent Skill](skills/lossless-openclaw-orchestrator/SKILL.md) · [OpenClaw Plugin](docs/OPENCLAW_PLUGIN.md) · [Security](SECURITY.md) · [Code of Conduct](CODE_OF_CONDUCT.md) · [Vision](VISION.md) · [Privacy](docs/PRIVACY.md) · [Claude Boundary](docs/CLAUDE_ADAPTER_BOUNDARY.md) · [Claim Audit](docs/CLAIM_AUDIT.md) · [QA Lab](docs/QA_LAB.md) · [Release Notes](docs/releases/CHANGELOG.md) · [License](LICENSE)
27
27
 
28
28
  ## Why It Matters
29
29
 
@@ -38,10 +38,13 @@ before editing files.
38
38
 
39
39
  ## What It Does
40
40
 
41
+ LCO's wedge is approval-gated orchestration over bounded, public-safe recall: an agent can safely operate a Codex fleet, not just remember it.
42
+
41
43
  LCO is a local-first orchestration layer for Codex-heavy work:
42
44
 
43
45
  - indexes local Codex session stores into a local SQLite database
44
46
  - searches sessions by safe summaries, plans, finals, touched files, and refs
47
+ - adds one-shot Codex thread title aliases for easier name/ID recall
45
48
  - describes one session without exposing the whole transcript
46
49
  - expands only the evidence an agent needs, with token-budgeted profiles
47
50
  - exposes `loo_*` tools through MCP and the OpenClaw plugin
@@ -310,6 +313,8 @@ connector URLs, or customer data into public issues or PRs. Use
310
313
  extraction, search, and expansion
311
314
  - `packages/mcp-server`: stdio MCP server exposing `loo_*` tools
312
315
  - `packages/openclaw-plugin`: OpenClaw plugin entry and manifest surface
316
+ - `.codex-plugin/` and `hooks/`: Codex plugin manifest, Stop hook config, and
317
+ wrapper for local thread title aliases
313
318
  - `packages/cli`: `loo` CLI for setup, indexing, recall, smoke, eval, and
314
319
  release gates
315
320
  - `packages/adapters`: Codex transport, audit, redaction, CUA/Peekaboo
@@ -325,10 +330,10 @@ The stable public product is Codex-first local orchestration: index, search,
325
330
  describe, expand, prepared-state recall, OpenClaw/MCP tools, and
326
331
  approval-gated dry-run/control boundaries.
327
332
 
328
- The 1.2 prepared-state and summary-leaves lane is shipped in stable `1.2.5`.
329
- Current launch work is the M11 GA assurance sprint, tracked in GitHub and
330
- summarized in [VISION.md](VISION.md). Keep sprint and agent-operator details
331
- there, in [AGENTS.md](AGENTS.md), and in the packaged
333
+ The 1.2 prepared-state and summary-leaves lane is shipped in stable `1.2.6`.
334
+ Current launch-hardening proof is summarized in [VISION.md](VISION.md) and
335
+ [docs/QA_LAB.md](docs/QA_LAB.md). Keep sprint and agent-operator details there,
336
+ in [AGENTS.md](AGENTS.md), and in the packaged
332
337
  [agent skill](skills/lossless-openclaw-orchestrator/SKILL.md), not in this
333
338
  public landing page. The historical 1.2 architecture handoff remains in
334
339
  [docs/sprints/brief-lco-1.2-prepared-state-summary-leaves-2026-07-03.md](docs/sprints/brief-lco-1.2-prepared-state-summary-leaves-2026-07-03.md).
@@ -345,9 +350,9 @@ thread-level `targetCoverage` with opaque source refs, freshness, coverage, and
345
350
  reason codes such as `source_present_not_indexed` instead of hiding a miss
346
351
  behind healthy global cache counts.
347
352
  The hook sidecar CLI lives under `loo hook closeout-capture`,
348
- `loo hook state-prep`, and `loo hook compaction-capture --mode marker`; those
349
- commands write only LCO-owned derived cache and treat transcript paths as
350
- hash/redact-only inputs.
353
+ `loo hook state-prep`, `loo hook compaction-capture --mode marker`, and
354
+ `loo hook thread-title-finalize`; those commands write only LCO-owned derived
355
+ cache and treat transcript paths as hash/redact-only inputs.
351
356
 
352
357
  ## Maintainer Proof
353
358
 
package/VISION.md CHANGED
@@ -8,19 +8,21 @@ An OpenClaw agent can understand, search, summarize, and safely coordinate a use
8
8
 
9
9
  The stable product should feel like a local orchestration cockpit: OpenClaw can see what Codex sessions exist, what each session is working on, which plans and final messages matter, which files were touched, and which next action would be safe to dry-run or execute only after explicit approval.
10
10
 
11
- ## Current Milestone: 1.2.0 GA Assurance And Global Launch Readiness
11
+ ## Current Milestone: M12 Real-Product QA Lab And GA Release Gate
12
12
 
13
- The current release-readiness lane is Milestone 11: LCO 1.2.0 GA Assurance And Global Launch Readiness. GitHub tracker [#478](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/478) and children [#479](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/479)-[#493](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/493) own implementation truth; this file owns the product and eval boundary.
13
+ The current release-readiness lane is Milestone 12: Real-Product QA Lab And GA Release Gate. GitHub tracker [#513](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/513) and children [#514](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/514)-[#522](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/522) own implementation truth; this file owns the product and eval boundary.
14
14
 
15
- The stable 1.0.0, 1.1.0, 1.1.1, 1.1.2, 1.1.3, 1.1.4, 1.2.0, 1.2.1, 1.2.2, 1.2.3, and 1.2.4 packages have shipped on npm `latest` and their GitHub Releases are published. The 1.2.5 package is the current stable patch candidate; npm `latest` and GitHub Release finalization must be proven by release evidence before any global-ready claim treats it as published. The post-GA Desktop claim-validation lane [#306](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/306) records that Desktop-visible classification and fallback readiness/status are proven, while actual Codex GUI mutation remains excluded. Desktop parity [#307](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/307) added the coherence classifier; desktop fallback [#308](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/308) added the CUA-first and Peekaboo-secondary readiness report. The 1.1 collaboration cockpit [#309](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/309) is completed proof for read-only collaboration summaries and execute-false next steps, not the current active child-work list.
15
+ The stable 1.0.0, 1.1.0, 1.1.1, 1.1.2, 1.1.3, 1.1.4, 1.2.0, 1.2.1, 1.2.2, 1.2.3, 1.2.4, 1.2.5, and 1.2.6 packages have shipped on npm `latest` and their GitHub Releases are published as scoped stable releases. M11 proved that 1.2.5 was installable, public-safe, and honest for its declared scope. M12 raises the bar for broad/global GA: catalog presence, unit tests, and partial gateway smoke are no longer sufficient. Every declared `loo_*` tool needs tier-appropriate product evidence, and public facade tools must be exercised through OpenClaw gateway before a GA claim can include them. The 1.2.5 baseline had 60 declared `loo_*` tools and the release gateway smoke invoked 36/60, so M12 starts by making that gap explicit and blocking full-surface GA claims until covered. The post-GA Desktop claim-validation lane [#306](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/306) records that Desktop-visible classification and fallback readiness/status are proven, while actual Codex GUI mutation remains excluded. Desktop parity [#307](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/307) added the coherence classifier; desktop fallback [#308](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/308) added the CUA-first and Peekaboo-secondary readiness report. The 1.1 collaboration cockpit [#309](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/309) is completed proof for read-only collaboration summaries and execute-false next steps, not the current active child-work list.
16
16
 
17
17
  The Codex Autonomy Cockpit [#254](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/254) and Eva Operating Picture [#255](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/255) P0 lanes are completed beta foundation, not the current active child-work list. Completed P0 children include shared contracts [#256](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/256), source authority [#258](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/258), watcher/resume requests [#259](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/259), visible Codex map joins [#260](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/260), deterministic GitHub operating inputs [#264](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/264)/[#265](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/265), current-lane source balancing [#269](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/269), GitHub check-state fidelity [#270](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/270), cockpit card cleanup [#271](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/271), and end-to-end Eva cockpit dogfood [#272](https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/issues/272). The sprint brief remains the historical handoff for that P0 work: [docs/sprints/brief-lco-codex-autonomy-cockpit-sprint-2026-07-01.md](docs/sprints/brief-lco-codex-autonomy-cockpit-sprint-2026-07-01.md).
18
18
 
19
- The core Codex recall, M9 handoff paths, Codex Autonomy Cockpit, Eva Operating Picture P0 paths, 1.1 Desktop collaboration cockpit paths, 1.2 prepared-state and summary-leaf paths, release metadata, package/gateway setup proof, docs truth, release gates, and public-safe scorecards are no longer the main product gap. The current gap is no-bug-left-behind launch assurance: prove the `1.2.5` stable candidate is published, installable from npm `@latest`, discoverable, public-safe, and honest for global users before broad rollout.
19
+ The core Codex recall, M9 handoff paths, Codex Autonomy Cockpit, Eva Operating Picture P0 paths, 1.1 Desktop collaboration cockpit paths, 1.2 prepared-state and summary-leaf paths, release metadata, package/gateway setup proof, docs truth, release gates, and public-safe scorecards are no longer the main product gap. The current gap is real-product launch assurance: prove the published product through actual CLI, MCP, OpenClaw plugin, OpenClaw gateway, local Codex session data, and sacrificial approved runtime lanes before broad rollout.
20
20
 
21
21
  The current target is:
22
22
 
23
- - Execute the M11 GA assurance tracker and children using public-safe evidence under `/Volumes/LEXAR/Codex/lossless-openclaw-orchestrator/2026-07-05/m11-ga-assurance/`.
23
+ - Execute the M12 QA Lab tracker and children using public-safe evidence under `/Volumes/LEXAR/Codex/lossless-openclaw-orchestrator/2026-07-05/m12-ga-qa-lab/`.
24
+ - Treat `loo qa-lab tool-coverage --coverage-policy full --strict` as the first full-surface GA gate. Missing product evidence is a blocker, not a warning, unless release copy explicitly excludes that tool/workflow.
25
+ - Integrate QA Lab run, tool coverage, judge review, adversarial review, and privacy scan into `loo release ga-smoke` before any next broad GA claim.
24
26
  - Keep the launch claim scoped to local Codex prepared-state recall, search/describe/expand, summary leaves, prepared inbox/cards, OpenClaw gateway use, and approval-gated dry-run/control boundaries.
25
27
  - Fix or defer every P0-P3 launch finding with terminal disposition before calling the release globally ready.
26
28
  - Keep the 1.2 prepared-state tracker as completed proof, not the current active child-work list. The sprint brief remains the historical architecture handoff: [docs/sprints/brief-lco-1.2-prepared-state-summary-leaves-2026-07-03.md](docs/sprints/brief-lco-1.2-prepared-state-summary-leaves-2026-07-03.md).
@@ -38,7 +40,7 @@ The current target is:
38
40
  - Keep P1 source adapters, including Notion, support-control, Company Brain, Stripe, dashboard/export, and model summarization, behind separate adapters and proof gates; P0 tools report those sources as `not_configured` instead of fabricating summaries.
39
41
  - Use the [source authority profile](docs/SOURCE_AUTHORITY_PROFILE.md) to distinguish "this source returned data" from "this source owns the current truth"; unavailable or cache-only sources must degrade claims to `unknown` or low confidence.
40
42
 
41
- The sprint remains Codex-first, local-first, read-only-first, and public-safe by default. It does not claim full business truth, customer readiness, Claude Code parity, remote sync, generic GUI mutation, unattended desktop control, permission bypass, or enterprise/customer-ready security. npm `latest` promotion and GitHub Release creation for 1.2.5 are release gates, not assumptions. Desktop-visible classification and fallback readiness/status are proven by #307/#308; actual Codex GUI mutation remains excluded until a future action-bound proof gate records the exact backend, target, action hash, approval, and observation.
43
+ The sprint remains Codex-first, local-first, read-only-first, and public-safe by default. It does not claim full business truth, customer readiness, Claude Code parity, remote sync, generic GUI mutation, unattended desktop control, permission bypass, or enterprise/customer-ready security. npm `latest` promotion and GitHub Release creation for 1.2.6 are release gates, not assumptions. Desktop-visible classification and fallback readiness/status are proven by #307/#308; actual Codex GUI mutation remains excluded until a future action-bound proof gate records the exact backend, target, action hash, approval, and observation.
42
44
 
43
45
  What 1.2 should let a local OpenClaw agent do next:
44
46
 
@@ -295,7 +297,7 @@ Release candidates follow [docs/BETA_RELEASE_RUNBOOK.md](docs/BETA_RELEASE_RUNBO
295
297
 
296
298
  ## Proof Boundary
297
299
 
298
- Allowed stable 1.2.5 claim:
300
+ Allowed stable 1.2.6 claim:
299
301
 
300
302
  > Collaborate with local Codex sessions through OpenClaw using local indexing, prepared-state recall, bounded expansion, and approval-gated dry-run/control boundaries.
301
303
 
@@ -940,6 +940,9 @@ export function createDesktopLiveProofHarness(input = {}) {
940
940
  approvalArtifact,
941
941
  backendStatus: publicBackendStatus,
942
942
  blockers,
943
+ proofMarkers: {
944
+ noActionObserved: true
945
+ },
943
946
  actionsPerformed: {
944
947
  desktopGuiActionRun: false,
945
948
  screenshotCaptured: false
@@ -0,0 +1,323 @@
1
+ import { spawn, spawnSync } from "node:child_process";
2
+ import { mkdirSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ const DEFAULT_REQUIRED_TOOLS = [
5
+ "loo_doctor",
6
+ "loo_prepared_inbox",
7
+ "loo_describe_ref",
8
+ "loo_expand_query"
9
+ ];
10
+ const DEFAULT_TIMEOUT_MS = 5_000;
11
+ export const MAX_CLI_MCP_PRODUCT_SMOKE_TIMEOUT_MS = 10_000;
12
+ const PRIVATE_DATA_EXCLUSIONS = [
13
+ "raw CLI stdout/stderr",
14
+ "raw MCP stdout/stderr",
15
+ "raw local filesystem paths",
16
+ "raw Codex transcripts",
17
+ "raw prompts or message text",
18
+ "SQLite DBs",
19
+ "JSONL transcripts",
20
+ "screenshots or videos",
21
+ "tokens, credentials, API keys, cookies",
22
+ "customer data"
23
+ ];
24
+ export async function createCliMcpProductSmokeReport(options) {
25
+ const requiredTools = uniqueStrings(options.requiredTools?.length ? options.requiredTools : DEFAULT_REQUIRED_TOOLS);
26
+ const cliProbe = probeCliHelp(options.cliBin ?? "loo", options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
27
+ const toolCallName = options.toolCallName ?? "loo_doctor";
28
+ const mcpProbe = await probeMcpToolsListAndCall(options.mcpBin ?? "loo-mcp-server", toolCallName, options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
29
+ const requiredToolsPresent = requiredTools.filter((tool) => mcpProbe.tools.includes(tool));
30
+ const missingRequiredTools = requiredTools.filter((tool) => !mcpProbe.tools.includes(tool));
31
+ const blockers = uniqueStrings([
32
+ ...cliProbe.blockers,
33
+ ...mcpProbe.blockers,
34
+ ...(mcpProbe.ready && missingRequiredTools.length > 0 ? ["required_mcp_tools_missing"] : [])
35
+ ]);
36
+ const setupBlockers = uniqueStrings([...cliProbe.setupBlockers, ...mcpProbe.setupBlockers]);
37
+ const warnings = uniqueStrings([...cliProbe.warnings, ...mcpProbe.warnings]);
38
+ const cliReady = cliProbe.ready;
39
+ const mcpReady = mcpProbe.ready;
40
+ const mcpToolsCallReady = mcpProbe.toolCall.ok;
41
+ const ok = cliReady && mcpReady && mcpToolsCallReady && missingRequiredTools.length === 0 && blockers.length === 0 && setupBlockers.length === 0;
42
+ const report = {
43
+ schema: "lco.qaLab.cliMcpProductSmoke.v1",
44
+ ok,
45
+ publicSafe: true,
46
+ localOnly: true,
47
+ dryRun: true,
48
+ generatedAt: options.now ?? new Date().toISOString(),
49
+ packageName: "lossless-openclaw-orchestrator",
50
+ packageVersion: options.packageVersion,
51
+ candidateSha: options.candidateSha ?? null,
52
+ cliReady,
53
+ mcpReady,
54
+ mcpToolsCallReady,
55
+ toolsListed: mcpProbe.tools.length,
56
+ toolCallProbe: mcpProbe.toolCall,
57
+ requiredTools,
58
+ requiredToolsPresent,
59
+ missingRequiredTools,
60
+ blockers,
61
+ setupBlockers,
62
+ warnings,
63
+ actionsPerformed: {
64
+ npmPublished: false,
65
+ githubReleaseCreated: false,
66
+ liveCodexControlRun: false,
67
+ desktopGuiActionRun: false,
68
+ screenshotsCaptured: false
69
+ },
70
+ privateDataExclusions: PRIVATE_DATA_EXCLUSIONS,
71
+ proofBoundary: "This public-safe QA Lab product smoke proves CLI --help, MCP tools/list, and MCP tools/call for one safe representative tool from the selected published/fresh-install candidate binaries. The default representative call is loo_doctor with empty arguments, so deeper tools should be covered by workflow-specific QA Lab lanes. The CLI and MCP probes run sequentially with the configured timeout applied per probe. The smoke initializes MCP with protocolVersion 2024-11-05; initialize failures are reported as package/protocol-drift defects for the candidate under test. JSON-RPC id pairing is the primary request/response binding; name-mismatch detection applies only when a non-standard server echoes result.name or result.toolName. It does not run live Codex control, mutate a desktop GUI, capture screenshots, publish npm, create a GitHub Release, store raw CLI output, or store raw MCP output.",
72
+ nextSafeCommands: [
73
+ `loo qa-lab cli-mcp-smoke --evidence-dir <dir> --package-version ${options.packageVersion} --strict`,
74
+ "loo --help",
75
+ "loo-mcp-server # MCP initialize + tools/list + tools/call"
76
+ ]
77
+ };
78
+ if (options.evidenceDir)
79
+ writeCliMcpProductSmokeReport(report, options.evidenceDir);
80
+ return report;
81
+ }
82
+ export function writeCliMcpProductSmokeReport(report, evidenceDir) {
83
+ mkdirSync(evidenceDir, { recursive: true });
84
+ const outputPath = join(evidenceDir, "cli-mcp-product-smoke.json");
85
+ writeFileSync(outputPath, `${JSON.stringify(report, null, 2)}\n`);
86
+ return outputPath;
87
+ }
88
+ function probeCliHelp(cliBin, timeoutMs) {
89
+ const result = spawnSync(cliBin, ["--help"], {
90
+ encoding: "utf8",
91
+ maxBuffer: 1024 * 1024,
92
+ timeout: timeoutMs
93
+ });
94
+ if (result.error) {
95
+ if (isMissingExecutableError(result.error)) {
96
+ return setupRequired("cli_binary_not_found_or_not_executable");
97
+ }
98
+ if (isTimeoutError(result.error)) {
99
+ return packageDefect("cli_help_timeout");
100
+ }
101
+ return packageDefect("cli_help_spawn_failed");
102
+ }
103
+ if (result.status === 0)
104
+ return readyProbe();
105
+ return packageDefect("cli_help_failed");
106
+ }
107
+ function probeMcpToolsListAndCall(mcpBin, toolCallName, timeoutMs) {
108
+ return new Promise((resolve) => {
109
+ let resolved = false;
110
+ let stdoutBuffer = "";
111
+ let listedTools = [];
112
+ let timer = null;
113
+ let killTimer = null;
114
+ const child = spawn(mcpBin, [], {
115
+ stdio: ["pipe", "pipe", "pipe"]
116
+ });
117
+ const terminateChild = () => {
118
+ if (child.exitCode !== null || child.signalCode !== null)
119
+ return;
120
+ child.kill("SIGTERM");
121
+ killTimer = setTimeout(() => {
122
+ if (child.exitCode === null && child.signalCode === null)
123
+ child.kill("SIGKILL");
124
+ }, 250);
125
+ killTimer.unref?.();
126
+ };
127
+ const finish = (result) => {
128
+ if (resolved)
129
+ return;
130
+ resolved = true;
131
+ if (timer)
132
+ clearTimeout(timer);
133
+ terminateChild();
134
+ resolve(result);
135
+ };
136
+ timer = setTimeout(() => finish({ ...packageDefect("mcp_probe_timeout"), tools: [], toolCall: failedToolCall(toolCallName, "mcp_probe_timeout") }), timeoutMs);
137
+ timer.unref?.();
138
+ const writeMessage = (payload, failureCode) => {
139
+ try {
140
+ if (!child.stdin || child.stdin.destroyed || child.stdin.writableEnded)
141
+ throw new Error("stdin closed");
142
+ child.stdin.write(`${JSON.stringify(payload)}\n`);
143
+ return true;
144
+ }
145
+ catch {
146
+ finish({ ...packageDefect(failureCode), tools: listedTools, toolCall: failedToolCall(toolCallName, failureCode) });
147
+ return false;
148
+ }
149
+ };
150
+ child.on("error", (error) => {
151
+ if (isMissingExecutableError(error)) {
152
+ finish({ ...setupRequired("mcp_binary_not_found_or_not_executable"), tools: [], toolCall: failedToolCall(toolCallName, "mcp_binary_not_found_or_not_executable") });
153
+ return;
154
+ }
155
+ finish({ ...packageDefect("mcp_spawn_failed"), tools: [], toolCall: failedToolCall(toolCallName, "mcp_spawn_failed") });
156
+ });
157
+ child.on("close", () => {
158
+ if (killTimer)
159
+ clearTimeout(killTimer);
160
+ if (resolved)
161
+ return;
162
+ if (listedTools.length > 0) {
163
+ finish({ ...packageDefect("mcp_tools_call_failed"), tools: listedTools, toolCall: failedToolCall(toolCallName, "mcp_tools_call_failed") });
164
+ return;
165
+ }
166
+ finish({ ...packageDefect("mcp_tools_list_failed"), tools: [], toolCall: failedToolCall(toolCallName, "mcp_tools_list_failed") });
167
+ });
168
+ child.stdout?.on("data", (chunk) => {
169
+ stdoutBuffer += chunk.toString("utf8");
170
+ const lines = stdoutBuffer.split(/\r?\n/);
171
+ stdoutBuffer = lines.pop() ?? "";
172
+ for (const line of lines) {
173
+ const parsed = parseJsonRpcLine(line);
174
+ if (!parsed)
175
+ continue;
176
+ if (parsed.id === 1 && parsed.error) {
177
+ finish({ ...packageDefect("mcp_initialize_failed"), tools: [], toolCall: failedToolCall(toolCallName, "mcp_initialize_failed") });
178
+ return;
179
+ }
180
+ if (parsed.id === 1 && isRecord(parsed.result)) {
181
+ if (!writeMessage({ jsonrpc: "2.0", method: "notifications/initialized", params: {} }, "mcp_initialized_notification_failed"))
182
+ return;
183
+ if (!writeMessage({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} }, "mcp_tools_list_write_failed"))
184
+ return;
185
+ continue;
186
+ }
187
+ if (parsed.id === 2 && parsed.error) {
188
+ finish({ ...packageDefect("mcp_tools_list_failed"), tools: [], toolCall: failedToolCall(toolCallName, "mcp_tools_list_failed") });
189
+ return;
190
+ }
191
+ if (parsed.id === 2 && isRecord(parsed.result)) {
192
+ const tools = extractToolNames(parsed.result.tools);
193
+ listedTools = tools;
194
+ if (tools.length === 0) {
195
+ finish({ ...packageDefect("mcp_no_loo_tools_listed"), tools, toolCall: failedToolCall(toolCallName, "mcp_no_loo_tools_listed") });
196
+ return;
197
+ }
198
+ if (!tools.includes(toolCallName)) {
199
+ finish({
200
+ ready: true,
201
+ tools,
202
+ setupBlockers: [],
203
+ blockers: ["mcp_tool_call_tool_missing"],
204
+ warnings: [],
205
+ toolCall: failedToolCall(toolCallName, "mcp_tool_call_tool_missing")
206
+ });
207
+ return;
208
+ }
209
+ if (!writeMessage({
210
+ jsonrpc: "2.0",
211
+ id: 3,
212
+ method: "tools/call",
213
+ params: { name: toolCallName, arguments: {} }
214
+ }, "mcp_tools_call_write_failed"))
215
+ return;
216
+ continue;
217
+ }
218
+ if (parsed.id === 3 && parsed.error) {
219
+ finish({
220
+ ready: true,
221
+ tools: listedTools,
222
+ setupBlockers: [],
223
+ blockers: ["mcp_tools_call_failed"],
224
+ warnings: [],
225
+ toolCall: failedToolCall(toolCallName, "mcp_tools_call_failed")
226
+ });
227
+ return;
228
+ }
229
+ if (parsed.id === 3 && isRecord(parsed.result)) {
230
+ const toolCall = successfulToolCall(toolCallName, parsed.result);
231
+ finish({
232
+ ready: true,
233
+ tools: listedTools,
234
+ setupBlockers: [],
235
+ blockers: toolCall.ok ? [] : [toolCall.errorCode ?? "mcp_tools_call_failed"],
236
+ warnings: [],
237
+ toolCall
238
+ });
239
+ return;
240
+ }
241
+ }
242
+ });
243
+ writeMessage({
244
+ jsonrpc: "2.0",
245
+ id: 1,
246
+ method: "initialize",
247
+ params: {
248
+ protocolVersion: "2024-11-05",
249
+ capabilities: {},
250
+ clientInfo: { name: "lco-cli-mcp-product-smoke", version: "1.0.0" }
251
+ }
252
+ }, "mcp_initialize_write_failed");
253
+ });
254
+ }
255
+ function failedToolCall(toolName, errorCode) {
256
+ return {
257
+ toolName,
258
+ ok: false,
259
+ contentItemCount: 0,
260
+ contentKinds: [],
261
+ structuredContentPresent: false,
262
+ errorCode
263
+ };
264
+ }
265
+ function successfulToolCall(toolName, result) {
266
+ const content = Array.isArray(result.content) ? result.content : [];
267
+ const responseToolName = typeof result.toolName === "string"
268
+ ? result.toolName
269
+ : typeof result.name === "string"
270
+ ? result.name
271
+ : null;
272
+ const nameMatches = responseToolName === null || responseToolName === toolName;
273
+ return {
274
+ toolName,
275
+ ok: nameMatches,
276
+ contentItemCount: content.length,
277
+ contentKinds: uniqueStrings(content.map((item) => safeContentKind(item))).slice(0, 8),
278
+ structuredContentPresent: Object.prototype.hasOwnProperty.call(result, "structuredContent"),
279
+ errorCode: nameMatches ? null : "mcp_tools_call_name_mismatch"
280
+ };
281
+ }
282
+ function safeContentKind(item) {
283
+ const kind = isRecord(item) && typeof item.type === "string" ? item.type : "unknown";
284
+ return ["text", "image", "resource", "audio", "unknown"].includes(kind) ? kind : "unknown";
285
+ }
286
+ function readyProbe() {
287
+ return { ready: true, setupBlockers: [], blockers: [], warnings: [] };
288
+ }
289
+ function setupRequired(code) {
290
+ return { ready: false, setupBlockers: [code], blockers: [], warnings: [] };
291
+ }
292
+ function packageDefect(code) {
293
+ return { ready: false, setupBlockers: [], blockers: [code], warnings: [] };
294
+ }
295
+ function isMissingExecutableError(error) {
296
+ return error.code === "ENOENT" || error.code === "EACCES";
297
+ }
298
+ function isTimeoutError(error) {
299
+ return error.code === "ETIMEDOUT";
300
+ }
301
+ function parseJsonRpcLine(line) {
302
+ try {
303
+ const parsed = JSON.parse(line);
304
+ return isRecord(parsed) ? parsed : null;
305
+ }
306
+ catch {
307
+ return null;
308
+ }
309
+ }
310
+ function extractToolNames(value) {
311
+ if (!Array.isArray(value))
312
+ return [];
313
+ return uniqueStrings(value
314
+ .map((item) => isRecord(item) && typeof item.name === "string" ? item.name : null)
315
+ .filter((name) => Boolean(name))
316
+ .filter((name) => /^loo_[a-z0-9_]+$/.test(name)));
317
+ }
318
+ function isRecord(value) {
319
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
320
+ }
321
+ function uniqueStrings(values) {
322
+ return [...new Set(values)];
323
+ }