lossless-openclaw-orchestrator 1.2.4 → 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.
- package/.codex-plugin/plugin.json +40 -0
- package/.codex-plugin/scripts/thread-title-finalize.mjs +31 -0
- package/README.md +15 -9
- package/VISION.md +10 -7
- package/dist/packages/adapters/src/index.js +3 -0
- package/dist/packages/cli/src/cli-mcp-product-smoke.js +323 -0
- package/dist/packages/cli/src/index.js +1414 -29
- package/dist/packages/cli/src/openclaw-live-control-smoke.js +69 -30
- package/dist/packages/cli/src/openclaw-tool-smoke.js +485 -14
- package/dist/packages/cli/src/qa-lab-desktop-contract.js +385 -0
- package/dist/packages/cli/src/qa-lab-live-control-matrix.js +383 -0
- package/dist/packages/cli/src/qa-lab-privacy-scan.js +244 -0
- package/dist/packages/cli/src/qa-lab-review.js +338 -0
- package/dist/packages/cli/src/qa-lab-run.js +548 -0
- package/dist/packages/cli/src/qa-lab-tool-coverage.js +545 -0
- package/dist/packages/cli/src/qa-lab-workflow.js +641 -0
- package/dist/packages/cli/src/release-bundle.js +2 -2
- package/dist/packages/cli/src/release-ga-smoke.js +751 -0
- package/dist/packages/cli/src/release-preflight.js +1 -1
- package/dist/packages/cli/src/release-status.js +1 -0
- package/dist/packages/cli/src/scenario-sweep.js +8 -0
- package/dist/packages/cli/src/scorecard-sweep.js +4 -0
- package/dist/packages/core/src/index.js +2111 -311
- package/dist/packages/core/src/search.js +630 -0
- package/dist/packages/mcp-server/src/server.js +14 -2
- package/dist/packages/mcp-server/src/tools.js +113 -7
- package/dist/packages/openclaw-plugin/src/index.js +6 -3
- package/docs/BETA_RELEASE_RUNBOOK.md +20 -9
- package/docs/CLAIM_AUDIT.md +2 -2
- package/docs/CODEX_JSONL_DRIFT.md +37 -0
- package/docs/QA_LAB.md +62 -0
- package/docs/RELEASE_CHECKLIST.md +16 -4
- package/docs/SETUP.md +35 -6
- package/docs/releases/CHANGELOG.md +73 -0
- package/docs/releases/RELEASE_NOTES_1.2.5.md +94 -0
- package/docs/releases/RELEASE_NOTES_1.2.6.md +89 -0
- package/evals/scenarios/retrieval-goldens/v1/baseline-floors.json +61 -0
- package/evals/scenarios/retrieval-goldens/v1/goldens.json +361 -0
- package/hooks/hooks.json +15 -0
- package/openclaw.plugin.json +400 -3
- package/package.json +3 -1
- package/packages/adapters/src/index.ts +6 -0
- package/packages/cli/src/cli-mcp-product-smoke.ts +394 -0
- package/packages/cli/src/index.ts +1521 -41
- package/packages/cli/src/openclaw-live-control-smoke.ts +67 -30
- package/packages/cli/src/openclaw-tool-smoke.ts +490 -16
- package/packages/cli/src/qa-lab-desktop-contract.ts +471 -0
- package/packages/cli/src/qa-lab-live-control-matrix.ts +471 -0
- package/packages/cli/src/qa-lab-privacy-scan.ts +310 -0
- package/packages/cli/src/qa-lab-review.ts +461 -0
- package/packages/cli/src/qa-lab-run.ts +688 -0
- package/packages/cli/src/qa-lab-tool-coverage.ts +700 -0
- package/packages/cli/src/qa-lab-workflow.ts +737 -0
- package/packages/cli/src/release-bundle.ts +2 -2
- package/packages/cli/src/release-ga-smoke.ts +1017 -0
- package/packages/cli/src/release-preflight.ts +1 -1
- package/packages/cli/src/release-status.ts +2 -0
- package/packages/cli/src/scenario-sweep.ts +17 -0
- package/packages/cli/src/scorecard-sweep.ts +10 -0
- package/packages/core/src/index.ts +2652 -384
- package/packages/core/src/search.ts +770 -0
- package/packages/mcp-server/src/server.ts +14 -2
- package/packages/mcp-server/src/tools.ts +146 -7
- package/packages/openclaw-plugin/openclaw.plugin.json +400 -3
- package/packages/openclaw-plugin/src/index.ts +6 -2
- /package/docs/{RELEASE_NOTES_0.1.0-beta.0.md → releases/RELEASE_NOTES_0.1.0-beta.0.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.1.md → releases/RELEASE_NOTES_0.1.0-beta.1.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.10.md → releases/RELEASE_NOTES_0.1.0-beta.10.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.11.md → releases/RELEASE_NOTES_0.1.0-beta.11.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.12.md → releases/RELEASE_NOTES_0.1.0-beta.12.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.13.md → releases/RELEASE_NOTES_0.1.0-beta.13.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.14.md → releases/RELEASE_NOTES_0.1.0-beta.14.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.15.md → releases/RELEASE_NOTES_0.1.0-beta.15.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.16.md → releases/RELEASE_NOTES_0.1.0-beta.16.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.17.md → releases/RELEASE_NOTES_0.1.0-beta.17.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.18.md → releases/RELEASE_NOTES_0.1.0-beta.18.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.19.md → releases/RELEASE_NOTES_0.1.0-beta.19.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.2.md → releases/RELEASE_NOTES_0.1.0-beta.2.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.20.md → releases/RELEASE_NOTES_0.1.0-beta.20.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.21.md → releases/RELEASE_NOTES_0.1.0-beta.21.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.22.md → releases/RELEASE_NOTES_0.1.0-beta.22.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.23.md → releases/RELEASE_NOTES_0.1.0-beta.23.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.24.md → releases/RELEASE_NOTES_0.1.0-beta.24.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.25.md → releases/RELEASE_NOTES_0.1.0-beta.25.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.26.md → releases/RELEASE_NOTES_0.1.0-beta.26.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.27.md → releases/RELEASE_NOTES_0.1.0-beta.27.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.28.md → releases/RELEASE_NOTES_0.1.0-beta.28.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.29.md → releases/RELEASE_NOTES_0.1.0-beta.29.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.3.md → releases/RELEASE_NOTES_0.1.0-beta.3.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.30.md → releases/RELEASE_NOTES_0.1.0-beta.30.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.31.md → releases/RELEASE_NOTES_0.1.0-beta.31.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.32.md → releases/RELEASE_NOTES_0.1.0-beta.32.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.33.md → releases/RELEASE_NOTES_0.1.0-beta.33.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.34.md → releases/RELEASE_NOTES_0.1.0-beta.34.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.35.md → releases/RELEASE_NOTES_0.1.0-beta.35.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.36.md → releases/RELEASE_NOTES_0.1.0-beta.36.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.37.md → releases/RELEASE_NOTES_0.1.0-beta.37.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.38.md → releases/RELEASE_NOTES_0.1.0-beta.38.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.39.md → releases/RELEASE_NOTES_0.1.0-beta.39.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.4.md → releases/RELEASE_NOTES_0.1.0-beta.4.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.40.md → releases/RELEASE_NOTES_0.1.0-beta.40.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.41.md → releases/RELEASE_NOTES_0.1.0-beta.41.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.42.md → releases/RELEASE_NOTES_0.1.0-beta.42.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.43.md → releases/RELEASE_NOTES_0.1.0-beta.43.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.44.md → releases/RELEASE_NOTES_0.1.0-beta.44.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.45.md → releases/RELEASE_NOTES_0.1.0-beta.45.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.46.md → releases/RELEASE_NOTES_0.1.0-beta.46.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.47.md → releases/RELEASE_NOTES_0.1.0-beta.47.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.48.md → releases/RELEASE_NOTES_0.1.0-beta.48.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.49.md → releases/RELEASE_NOTES_0.1.0-beta.49.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.5.md → releases/RELEASE_NOTES_0.1.0-beta.5.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.6.md → releases/RELEASE_NOTES_0.1.0-beta.6.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.7.md → releases/RELEASE_NOTES_0.1.0-beta.7.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.8.md → releases/RELEASE_NOTES_0.1.0-beta.8.md} +0 -0
- /package/docs/{RELEASE_NOTES_0.1.0-beta.9.md → releases/RELEASE_NOTES_0.1.0-beta.9.md} +0 -0
- /package/docs/{RELEASE_NOTES_1.0.0-rc.1.md → releases/RELEASE_NOTES_1.0.0-rc.1.md} +0 -0
- /package/docs/{RELEASE_NOTES_1.0.0.md → releases/RELEASE_NOTES_1.0.0.md} +0 -0
- /package/docs/{RELEASE_NOTES_1.1.0.md → releases/RELEASE_NOTES_1.1.0.md} +0 -0
- /package/docs/{RELEASE_NOTES_1.1.1.md → releases/RELEASE_NOTES_1.1.1.md} +0 -0
- /package/docs/{RELEASE_NOTES_1.1.2.md → releases/RELEASE_NOTES_1.1.2.md} +0 -0
- /package/docs/{RELEASE_NOTES_1.1.3.md → releases/RELEASE_NOTES_1.1.3.md} +0 -0
- /package/docs/{RELEASE_NOTES_1.1.4.md → releases/RELEASE_NOTES_1.1.4.md} +0 -0
- /package/docs/{RELEASE_NOTES_1.2.0-beta.0.md → releases/RELEASE_NOTES_1.2.0-beta.0.md} +0 -0
- /package/docs/{RELEASE_NOTES_1.2.0-beta.1.md → releases/RELEASE_NOTES_1.2.0-beta.1.md} +0 -0
- /package/docs/{RELEASE_NOTES_1.2.0-beta.2.md → releases/RELEASE_NOTES_1.2.0-beta.2.md} +0 -0
- /package/docs/{RELEASE_NOTES_1.2.0-beta.3.md → releases/RELEASE_NOTES_1.2.0-beta.3.md} +0 -0
- /package/docs/{RELEASE_NOTES_1.2.0.md → releases/RELEASE_NOTES_1.2.0.md} +0 -0
- /package/docs/{RELEASE_NOTES_1.2.1.md → releases/RELEASE_NOTES_1.2.1.md} +0 -0
- /package/docs/{RELEASE_NOTES_1.2.2.md → releases/RELEASE_NOTES_1.2.2.md} +0 -0
- /package/docs/{RELEASE_NOTES_1.2.3.md → releases/RELEASE_NOTES_1.2.3.md} +0 -0
- /package/docs/{RELEASE_NOTES_1.2.4.md → releases/RELEASE_NOTES_1.2.4.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
|
|
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) · [
|
|
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.
|
|
329
|
-
Current launch
|
|
330
|
-
|
|
331
|
-
|
|
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`,
|
|
349
|
-
commands write only LCO-owned derived
|
|
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
|
|
|
@@ -376,6 +381,7 @@ loo release preflight --claim-scope codex-read-search-expand-dry-run --evidence-
|
|
|
376
381
|
loo release demo-status --claim-scope codex-read-search-expand-dry-run --evidence-dir /Volumes/LEXAR/Codex/lossless-openclaw-orchestrator/YYYY-MM-DD/demo --strict
|
|
377
382
|
loo release status --claim-scope codex-read-search-expand-dry-run --evidence-dir /Volumes/LEXAR/Codex/lossless-openclaw-orchestrator/YYYY-MM-DD/release-status --candidate-sha <release-candidate-sha> --npm-publish-approval-evidence npm-publish-approval.json --github-release-approval-evidence github-release-approval.json --github-ci-evidence github-ci.json --codeql-evidence codeql.json --strict
|
|
378
383
|
loo release general-readiness --evidence-dir /Volumes/LEXAR/Codex/lossless-openclaw-orchestrator/YYYY-MM-DD/general-readiness --fresh-npm-evidence published-package-smoke.json --agent-dogfood-evidence openclaw-tool-smoke.json --strict
|
|
384
|
+
loo release ga-smoke --evidence-dir /Volumes/LEXAR/Codex/lossless-openclaw-orchestrator/YYYY-MM-DD/release-ga-smoke --package-version <version> --candidate-sha <release-candidate-sha> --strict
|
|
379
385
|
```
|
|
380
386
|
|
|
381
387
|
## Development
|
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:
|
|
11
|
+
## Current Milestone: M12 Real-Product QA Lab And GA Release Gate
|
|
12
12
|
|
|
13
|
-
The current release-readiness lane is Milestone
|
|
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, and 1.2.
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
|
@@ -83,6 +85,7 @@ What a local OpenClaw agent can do today:
|
|
|
83
85
|
- Classify package and gateway readiness with `loo onboard status`, `loo openclaw dogfood`, `loo openclaw tool-smoke`, and `loo openclaw published-smoke`.
|
|
84
86
|
- Follow the packaged agent skill and M9 dogfood scenario to produce a public-safe recommendation from source refs, bounded expansion, detail lookups, and dry-run audit hashes.
|
|
85
87
|
- Use `loo release general-readiness --strict` to decide whether fresh npm install, clean-profile OpenClaw load, clean-profile gateway readiness, and agent dogfood evidence are enough for a stable/general release claim.
|
|
88
|
+
- Use `loo release ga-smoke --strict` after the individual release reports exist to aggregate the release-status, finalization, published-smoke, OpenClaw dogfood/tool-smoke, scenario, scorecard, bundle, preflight, and privacy packets into one public-safe blocker taxonomy. It is a report aggregator only; it must not publish npm, create tags, create GitHub Releases, run live control, mutate a GUI, or read raw transcripts.
|
|
86
89
|
|
|
87
90
|
## Completed Proof: Working App Runtime
|
|
88
91
|
|
|
@@ -294,7 +297,7 @@ Release candidates follow [docs/BETA_RELEASE_RUNBOOK.md](docs/BETA_RELEASE_RUNBO
|
|
|
294
297
|
|
|
295
298
|
## Proof Boundary
|
|
296
299
|
|
|
297
|
-
Allowed stable 1.2.
|
|
300
|
+
Allowed stable 1.2.6 claim:
|
|
298
301
|
|
|
299
302
|
> Collaborate with local Codex sessions through OpenClaw using local indexing, prepared-state recall, bounded expansion, and approval-gated dry-run/control boundaries.
|
|
300
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
|
+
}
|