lossless-openclaw-orchestrator 1.1.2 → 1.1.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.
package/README.md CHANGED
@@ -14,7 +14,7 @@ safe action without rereading raw transcripts.
14
14
  [![License](https://img.shields.io/badge/license-PolyForm%20Noncommercial-blue)](LICENSE)
15
15
  [![PRs welcome](https://img.shields.io/badge/PRs-welcome-brightgreen)](CONTRIBUTING.md)
16
16
 
17
- [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.1.2.md) · [1.0 Notes](docs/RELEASE_NOTES_1.0.0.md) · [License](LICENSE)
17
+ [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.1.4.md) · [1.0 Notes](docs/RELEASE_NOTES_1.0.0.md) · [License](LICENSE)
18
18
 
19
19
  ## Why It Matters
20
20
 
@@ -16,7 +16,9 @@ import { runOpenClawGatewayLiveControlSmoke } from "./openclaw-live-control-smok
16
16
  import { runOpenClawPostActionRefreshSmoke } from "./openclaw-post-action-refresh-smoke.js";
17
17
  import { createScorecardSweep } from "./scorecard-sweep.js";
18
18
  import { createScenarioSweep } from "./scenario-sweep.js";
19
+ import { createRuntimeProofIssuePacket } from "./runtime-issue-packet.js";
19
20
  import { createOnboardingStatusReport, writeOnboardingStatusReport } from "./onboarding-status.js";
21
+ import { createRuntimeSweepSummary } from "./runtime-sweep-summary.js";
20
22
  import { normalizeReleaseClaimScope } from "./release-claim-scope.js";
21
23
  import { AppServerLiveControlSmokeClient, runLiveControlSmoke } from "./live-control-smoke.js";
22
24
  import { createLocalMacSearchUiShell, REQUIRED_LOCAL_MAC_SEARCH_UI_TOOLS, sampleLocalMacSearchUiShell, writeLocalMacSearchUiEvidence } from "../../local-mac-ui/src/shell.js";
@@ -380,6 +382,18 @@ async function main() {
380
382
  process.exitCode = 1;
381
383
  return;
382
384
  }
385
+ if (command === "runtime" && args[0] === "sweep-summary") {
386
+ if (hasHelpFlag(args.slice(1))) {
387
+ printRuntimeSweepSummaryHelp();
388
+ return;
389
+ }
390
+ const parsed = parseRuntimeSweepSummaryArgs(args.slice(1));
391
+ const report = createRuntimeSweepSummary(parsed);
392
+ console.log(JSON.stringify(report, null, 2));
393
+ if (parsed.strict && (!report.summaryReady || report.claimBoundary.supportedClaimScope === "none"))
394
+ process.exitCode = 1;
395
+ return;
396
+ }
383
397
  if (command === "ui" && args[0] === "local-mac-search") {
384
398
  if (hasHelpFlag(args.slice(1))) {
385
399
  printLocalMacSearchUiHelp();
@@ -456,6 +470,25 @@ async function main() {
456
470
  process.exitCode = 1;
457
471
  return;
458
472
  }
473
+ if (command === "runtime" && args[0] === "issue-packet") {
474
+ if (hasHelpFlag(args.slice(1))) {
475
+ printRuntimeIssuePacketHelp();
476
+ return;
477
+ }
478
+ const parsed = parseRuntimeIssuePacketArgs(args.slice(1));
479
+ const report = createRuntimeProofIssuePacket({
480
+ evidenceDir: parsed.evidenceDir,
481
+ failureReport: parsed.failureReport,
482
+ parentIssue: parsed.parentIssue,
483
+ operatingLoopIssue: parsed.operatingLoopIssue,
484
+ milestone: parsed.milestone,
485
+ now: parsed.now
486
+ });
487
+ console.log(JSON.stringify(report, null, 2));
488
+ if (parsed.strict && !report.issuePacketReady)
489
+ process.exitCode = 1;
490
+ return;
491
+ }
459
492
  if (command === "release" && args[0] === "preflight") {
460
493
  if (hasHelpFlag(args.slice(1))) {
461
494
  printReleasePreflightHelp();
@@ -747,12 +780,14 @@ function mainUsageText() {
747
780
  " loo openclaw dogfood [--dev] [--profile name] [--install-source path] [--link] [--force-install] [--evidence-path path] [--strict]",
748
781
  " loo openclaw tool-smoke [--openclaw-bin path] [--dev] [--profile name] [--gateway-url ws://127.0.0.1:port] [--token token] [--gateway-timeout-ms ms] [--session-key key] [--query text] [--thread-id id] [--expand-profile metadata|brief|evidence] [--token-budget n] [--required-tool name] [--evidence-path path] [--strict]",
749
782
  " loo openclaw published-smoke --evidence-dir path --dogfood-report path --tool-smoke-report path [--configured-tool-smoke-report path] [--npm-install-diagnostic-report path] [--registry-version version] [--registry-beta-version version] [--root path] [--now iso] [--strict]",
750
- " loo openclaw live-control-smoke --evidence-dir path --thread-id id [--openclaw-bin path] [--dev] [--profile name] [--gateway-url ws://127.0.0.1:port] [--token token] [--gateway-timeout-ms ms] [--session-key key] [--message text] [--strict]",
783
+ " loo openclaw live-control-smoke --evidence-dir path --thread-id id [--action send|resume] [--openclaw-bin path] [--dev] [--profile name] [--gateway-url ws://127.0.0.1:port] [--token token] [--gateway-timeout-ms ms] [--session-key key] [--message text] [--strict]",
751
784
  " loo openclaw post-action-refresh-smoke --evidence-dir path --thread-id id --live-proof-report path [--openclaw-bin path] [--dev] [--profile name] [--gateway-url ws://127.0.0.1:port] [--token token] [--gateway-timeout-ms ms] [--session-key key] [--query text] [--expand-profile metadata|brief|evidence] [--token-budget n] [--strict]",
752
785
  " loo scorecards sweep --evidence-dir path [--scorecard-dir path] [--claim-scope codex-live-control|codex-read-search-expand-dry-run|codex-working-app-proof] [--strict]",
786
+ " loo runtime sweep-summary --evidence-dir path --dry-run-scenarios path --runtime-scenarios path --scorecard-sweep path --published-smoke path [--runtime-proof-dir path] [--now iso] [--strict]",
753
787
  " loo ui local-mac-search --evidence-dir path [--sample] [--strict]",
754
788
  " loo eval retrieval --scenario-file path [--evidence-path path] [--strict]",
755
789
  " loo eval scenarios --evidence-dir path [--scenario-dir path] [--runtime-proof-dir path] [--strict]",
790
+ " loo runtime issue-packet --evidence-dir path --failure-report path [--parent-issue #n] [--operating-loop #n] [--milestone name] [--now iso] [--strict]",
756
791
  " loo release preflight [--evidence-dir path] [--claim-scope codex-live-control|codex-read-search-expand-dry-run|codex-working-app-proof] [--approved-live-control-evidence path] [--runtime-proof-dir path] [--strict]",
757
792
  " loo release bundle --evidence-dir path [--claim-scope codex-live-control|codex-read-search-expand-dry-run|codex-working-app-proof] [--approved-live-control-evidence path] [--runtime-proof-dir path] [--strict]",
758
793
  " loo release status --evidence-dir path --candidate-sha sha [--claim-scope codex-live-control|codex-read-search-expand-dry-run|codex-working-app-proof] [--approved-live-control-evidence path] [--runtime-proof-dir path] [--npm-publish-approval-evidence path] [--github-release-approval-evidence path] [--github-ci-evidence path] [--codeql-evidence path] [--desktop-gui-required --desktop-gui-approval-evidence path] [--now iso] [--strict]",
@@ -906,6 +941,48 @@ function printScenarioSweepHelp() {
906
941
  " It does not read raw Codex transcripts, run live Codex control, mutate a desktop GUI, publish npm, or create a GitHub Release."
907
942
  ].join("\n"));
908
943
  }
944
+ function printRuntimeIssuePacketHelp() {
945
+ console.log([
946
+ "Usage:",
947
+ " loo runtime issue-packet --evidence-dir path --failure-report path [--parent-issue #n] [--operating-loop #n] [--milestone name] [--now iso] [--strict]",
948
+ "",
949
+ "Writes a public-safe issue-ready handoff packet from a failed runtime proof or scenario sweep report.",
950
+ "",
951
+ "Required:",
952
+ " --evidence-dir is required and receives runtime-proof-issue-packet.json.",
953
+ " --failure-report points to the failed public-safe runtime proof, smoke, or scenario-sweep JSON report.",
954
+ "",
955
+ "Strict mode:",
956
+ " --strict exits non-zero when the failure report is missing, malformed, lacks blocker codes, or when packet redaction fails.",
957
+ "",
958
+ "Safety boundary:",
959
+ " The command never runs gh issue create and never writes to GitHub.",
960
+ " It records only blocker codes, scenario ids, duplicate-check query, acceptance criteria, proof boundary, and redaction categories.",
961
+ " It does not read raw Codex transcripts, run live Codex control, mutate a GUI, publish npm, or create a GitHub Release."
962
+ ].join("\n"));
963
+ }
964
+ function printRuntimeSweepSummaryHelp() {
965
+ console.log([
966
+ "Usage:",
967
+ " loo runtime sweep-summary --evidence-dir path --dry-run-scenarios path --runtime-scenarios path --scorecard-sweep path --published-smoke path [--runtime-proof-dir path] [--now iso] [--strict]",
968
+ "",
969
+ "Writes a public-safe summary that separates dry-run scenario readiness from missing runtime proof markers.",
970
+ "",
971
+ "Required:",
972
+ " --dry-run-scenarios points to the v1 dry-run scenario sweep report.",
973
+ " --runtime-scenarios points to the v1.1 runtime-required scenario sweep report.",
974
+ " --scorecard-sweep points to the working-app scorecard sweep report.",
975
+ " --published-smoke points to the published-package or gateway setup smoke report.",
976
+ "",
977
+ "Strict mode:",
978
+ " --strict exits non-zero when the summary itself cannot be produced safely or no claim scope is supported.",
979
+ " Missing runtime markers remain claim-boundary blockers, not packet-generation failures.",
980
+ "",
981
+ "Safety boundary:",
982
+ " The command consumes public-safe reports only.",
983
+ " It does not read raw Codex transcripts, run live Codex control, mutate a GUI, publish npm, create tags, or create a GitHub Release."
984
+ ].join("\n"));
985
+ }
909
986
  function printOnboardingStatusHelp() {
910
987
  console.log([
911
988
  "Usage:",
@@ -1095,9 +1172,9 @@ function printLiveControlSmokeHelp() {
1095
1172
  function printOpenClawLiveControlSmokeHelp() {
1096
1173
  console.log([
1097
1174
  "Usage:",
1098
- " loo openclaw live-control-smoke --evidence-dir path --thread-id id [--openclaw-bin path] [--dev] [--profile name] [--gateway-url ws://127.0.0.1:port] [--token token] [--gateway-timeout-ms ms] [--session-key key] [--message text] [--strict]",
1175
+ " loo openclaw live-control-smoke --evidence-dir path --thread-id id [--action send|resume] [--openclaw-bin path] [--dev] [--profile name] [--gateway-url ws://127.0.0.1:port] [--token token] [--gateway-timeout-ms ms] [--session-key key] [--message text] [--strict]",
1099
1176
  "",
1100
- "Runs one approval-gated live Codex send through the installed OpenClaw gateway tools.invoke path.",
1177
+ "Runs one approval-gated live Codex send or resume through the installed OpenClaw gateway tools.invoke path.",
1101
1178
  "",
1102
1179
  "Outputs:",
1103
1180
  " openclaw-gateway-live-codex-v1-1.runtime-proof.json",
@@ -1105,7 +1182,7 @@ function printOpenClawLiveControlSmokeHelp() {
1105
1182
  "",
1106
1183
  "Safety boundary:",
1107
1184
  " The command requires an explicit --thread-id target.",
1108
- " It invokes loo_codex_control_dry_run first, then uses the matching approval_audit_id for loo_codex_send_message with dry_run:false.",
1185
+ " It invokes loo_codex_control_dry_run first, then uses the matching approval_audit_id for the selected live tool with dry_run:false.",
1109
1186
  " It reads loo_audit_tail to prove matching dry-run/live audit metadata.",
1110
1187
  " Evidence contains refs, audit ids, hashes, tool names, and status only.",
1111
1188
  " It does not write raw prompt text, raw transcript spans, screenshots, SQLite DBs, tokens, or credentials.",
@@ -1640,6 +1717,47 @@ function parseScenarioSweepArgs(input) {
1640
1717
  throw new Error("eval scenarios requires --evidence-dir");
1641
1718
  return { evidenceDir, scenarioDir, runtimeProofDir, scenarioIds: scenarioIds.length ? scenarioIds : undefined, strict };
1642
1719
  }
1720
+ function parseRuntimeIssuePacketArgs(input) {
1721
+ let evidenceDir = "";
1722
+ let failureReport = "";
1723
+ let parentIssue;
1724
+ let operatingLoopIssue;
1725
+ let milestone;
1726
+ let now;
1727
+ let strict = false;
1728
+ for (let index = 0; index < input.length; index += 1) {
1729
+ const arg = input[index];
1730
+ if (arg === "--evidence-dir") {
1731
+ evidenceDir = requireOptionValue(input[++index], arg);
1732
+ }
1733
+ else if (arg === "--failure-report") {
1734
+ failureReport = requireOptionValue(input[++index], arg);
1735
+ }
1736
+ else if (arg === "--parent-issue") {
1737
+ parentIssue = requireOptionValue(input[++index], arg);
1738
+ }
1739
+ else if (arg === "--operating-loop") {
1740
+ operatingLoopIssue = requireOptionValue(input[++index], arg);
1741
+ }
1742
+ else if (arg === "--milestone") {
1743
+ milestone = requireOptionValue(input[++index], arg);
1744
+ }
1745
+ else if (arg === "--now") {
1746
+ now = requireOptionValue(input[++index], arg);
1747
+ }
1748
+ else if (arg === "--strict") {
1749
+ strict = true;
1750
+ }
1751
+ else {
1752
+ throw new Error(`Unknown runtime issue-packet option: ${arg}`);
1753
+ }
1754
+ }
1755
+ if (!evidenceDir)
1756
+ throw new Error("runtime issue-packet requires --evidence-dir");
1757
+ if (!failureReport)
1758
+ throw new Error("runtime issue-packet requires --failure-report");
1759
+ return { evidenceDir, failureReport, parentIssue, operatingLoopIssue, milestone, now, strict };
1760
+ }
1643
1761
  function readRetrievalScenarioFile(path) {
1644
1762
  const scenarioPath = resolve(path);
1645
1763
  if (!existsSync(scenarioPath))
@@ -1881,6 +1999,9 @@ function parseOpenClawLiveControlSmokeArgs(input) {
1881
1999
  else if (arg === "--thread-id") {
1882
2000
  parsed.threadId = requireOptionValue(input[++index], arg);
1883
2001
  }
2002
+ else if (arg === "--action") {
2003
+ parsed.action = parseOpenClawLiveControlAction(requireOptionValue(input[++index], arg));
2004
+ }
1884
2005
  else if (arg === "--message") {
1885
2006
  parsed.message = requireOptionValue(input[++index], arg);
1886
2007
  }
@@ -1900,6 +2021,11 @@ function parseOpenClawLiveControlSmokeArgs(input) {
1900
2021
  throw new Error("openclaw live-control-smoke requires --thread-id");
1901
2022
  return parsed;
1902
2023
  }
2024
+ function parseOpenClawLiveControlAction(value) {
2025
+ if (value === "send" || value === "resume")
2026
+ return value;
2027
+ throw new Error("--action must be send or resume");
2028
+ }
1903
2029
  function parseOpenClawPostActionRefreshSmokeArgs(input) {
1904
2030
  const parsed = {};
1905
2031
  for (let index = 0; index < input.length; index += 1) {
@@ -2066,6 +2192,57 @@ function parseScorecardSweepArgs(input) {
2066
2192
  throw new Error("scorecards sweep requires --evidence-dir");
2067
2193
  return { evidenceDir, scorecardDir, claimScope, strict };
2068
2194
  }
2195
+ function parseRuntimeSweepSummaryArgs(input) {
2196
+ let evidenceDir;
2197
+ let dryRunScenarios;
2198
+ let runtimeScenarios;
2199
+ let scorecardSweep;
2200
+ let publishedSmoke;
2201
+ let runtimeProofDir;
2202
+ let now;
2203
+ let strict = false;
2204
+ for (let index = 0; index < input.length; index += 1) {
2205
+ const arg = input[index];
2206
+ if (arg === "--evidence-dir") {
2207
+ evidenceDir = requireOptionValue(input[++index], arg);
2208
+ }
2209
+ else if (arg === "--dry-run-scenarios") {
2210
+ dryRunScenarios = requireOptionValue(input[++index], arg);
2211
+ }
2212
+ else if (arg === "--runtime-scenarios") {
2213
+ runtimeScenarios = requireOptionValue(input[++index], arg);
2214
+ }
2215
+ else if (arg === "--scorecard-sweep") {
2216
+ scorecardSweep = requireOptionValue(input[++index], arg);
2217
+ }
2218
+ else if (arg === "--published-smoke") {
2219
+ publishedSmoke = requireOptionValue(input[++index], arg);
2220
+ }
2221
+ else if (arg === "--runtime-proof-dir") {
2222
+ runtimeProofDir = requireOptionValue(input[++index], arg);
2223
+ }
2224
+ else if (arg === "--now") {
2225
+ now = requireOptionValue(input[++index], arg);
2226
+ }
2227
+ else if (arg === "--strict") {
2228
+ strict = true;
2229
+ }
2230
+ else {
2231
+ throw new Error(`Unknown runtime sweep-summary option: ${arg}`);
2232
+ }
2233
+ }
2234
+ if (!evidenceDir)
2235
+ throw new Error("runtime sweep-summary requires --evidence-dir");
2236
+ if (!dryRunScenarios)
2237
+ throw new Error("runtime sweep-summary requires --dry-run-scenarios");
2238
+ if (!runtimeScenarios)
2239
+ throw new Error("runtime sweep-summary requires --runtime-scenarios");
2240
+ if (!scorecardSweep)
2241
+ throw new Error("runtime sweep-summary requires --scorecard-sweep");
2242
+ if (!publishedSmoke)
2243
+ throw new Error("runtime sweep-summary requires --published-smoke");
2244
+ return { evidenceDir, dryRunScenarios, runtimeScenarios, scorecardSweep, publishedSmoke, runtimeProofDir, now, strict };
2245
+ }
2069
2246
  function parsePositiveInteger(value, name, max) {
2070
2247
  const parsed = Number(value);
2071
2248
  if (!Number.isInteger(parsed) || parsed < 1 || (max !== undefined && parsed > max)) {
@@ -4,7 +4,6 @@ import { basename, dirname, join } from "node:path";
4
4
  const SCENARIO_ID = "openclaw-gateway-live-codex-v1-1";
5
5
  const ACCEPTED_LIVE_TURN_STATUSES = new Set(["accepted", "completed", "in_progress", "pending", "queued", "running"]);
6
6
  const DEFAULT_MESSAGE = "LCO OpenClaw gateway live-control smoke. Reply with exactly: LCO gateway live smoke acknowledged. Do not run commands, edit files, or use tools.";
7
- const REQUIRED_TOOLS = ["loo_codex_control_dry_run", "loo_codex_send_message", "loo_audit_tail"];
8
7
  const PRIVATE_DATA_EXCLUSIONS = [
9
8
  "raw OpenClaw gateway stdout/stderr",
10
9
  "raw tool output",
@@ -32,48 +31,42 @@ export function runOpenClawGatewayLiveControlSmoke(options) {
32
31
  ];
33
32
  const callOptions = { timeoutMs: gatewayTimeoutMs, env: options.token ? { OPENCLAW_GATEWAY_TOKEN: options.token } : undefined };
34
33
  const sessionKey = options.sessionKey || "agent:main:lco-live-control-smoke";
34
+ const action = normalizeAction(options.action);
35
+ const liveToolName = liveToolForAction(action);
36
+ const requiredTools = ["loo_codex_control_dry_run", liveToolName, "loo_audit_tail"];
35
37
  const message = options.message ?? DEFAULT_MESSAGE;
36
38
  const targetRef = `codex_thread:${options.threadId}`;
37
39
  const blockers = [];
38
40
  const catalog = callGatewayJson(openclawBin, baseArgs, gatewayOptions, "tools.catalog", {}, callOptions);
39
41
  const catalogTools = catalog.status === 0 && catalog.parsed !== undefined ? extractCatalogToolNames(unwrapGatewayPayload(catalog.parsed)) : [];
40
42
  blockers.push(...gatewayCallBlockers(catalog, "openclaw_live_catalog_failed"));
41
- blockers.push(...REQUIRED_TOOLS.filter((tool) => !catalogTools.includes(tool)).map((tool) => `openclaw_live_catalog_missing_tool:${tool}`));
43
+ blockers.push(...requiredTools.filter((tool) => !catalogTools.includes(tool)).map((tool) => `openclaw_live_catalog_missing_tool:${tool}`));
42
44
  const dryRun = blockers.length === 0
43
45
  ? callGatewayJson(openclawBin, baseArgs, gatewayOptions, "tools.invoke", {
44
46
  name: "loo_codex_control_dry_run",
45
- args: {
46
- action: "send",
47
- thread_id: options.threadId,
48
- message
49
- },
47
+ args: liveDryRunArgs(action, options.threadId, message),
50
48
  sessionKey,
51
49
  confirm: false,
52
- idempotencyKey: `loo-live-smoke-dry-run-${options.threadId}`
50
+ idempotencyKey: `loo-live-smoke-dry-run-${action}-${options.threadId}`
53
51
  }, callOptions)
54
52
  : null;
55
53
  blockers.push(...(dryRun ? gatewayCallBlockers(dryRun, "openclaw_live_dry_run_failed") : []));
56
54
  const dryRunSummary = summarizeControl(dryRun);
57
- if (dryRun && !validDryRun(dryRunSummary))
55
+ if (dryRun && !validDryRun(action, dryRunSummary))
58
56
  blockers.push("openclaw_live_dry_run_not_proven");
59
57
  const live = blockers.length === 0
60
58
  ? callGatewayJson(openclawBin, baseArgs, gatewayOptions, "tools.invoke", {
61
- name: "loo_codex_send_message",
62
- args: {
63
- thread_id: options.threadId,
64
- message,
65
- dry_run: false,
66
- approval_audit_id: dryRunSummary.approvalAuditId
67
- },
59
+ name: liveToolName,
60
+ args: liveArgs(action, options.threadId, message, dryRunSummary.approvalAuditId),
68
61
  sessionKey,
69
62
  confirm: false,
70
- idempotencyKey: `loo-live-smoke-send-${options.threadId}-${dryRunSummary.approvalAuditId}`
63
+ idempotencyKey: `loo-live-smoke-${action}-${options.threadId}-${dryRunSummary.approvalAuditId}`
71
64
  }, callOptions)
72
65
  : null;
73
- blockers.push(...(live ? gatewayCallBlockers(live, "openclaw_live_send_failed") : []));
66
+ blockers.push(...(live ? gatewayCallBlockers(live, "openclaw_live_control_failed") : []));
74
67
  const liveSummary = summarizeControl(live);
75
- if (live && !validLive(liveSummary))
76
- blockers.push("openclaw_live_send_not_proven");
68
+ if (live && !validLive(action, liveSummary))
69
+ blockers.push(action === "resume" ? "openclaw_live_resume_not_proven" : "openclaw_live_send_not_proven");
77
70
  if (live && dryRunSummary.paramsHash && liveSummary.paramsHash && liveSummary.paramsHash !== dryRunSummary.paramsHash)
78
71
  blockers.push("openclaw_live_params_hash_mismatch");
79
72
  if (live && dryRunSummary.messageHash && liveSummary.messageHash && liveSummary.messageHash !== dryRunSummary.messageHash)
@@ -89,7 +82,7 @@ export function runOpenClawGatewayLiveControlSmoke(options) {
89
82
  args: { limit: 20 },
90
83
  sessionKey,
91
84
  confirm: false,
92
- idempotencyKey: `loo-live-smoke-audit-tail-${options.threadId}`
85
+ idempotencyKey: `loo-live-smoke-audit-tail-${action}-${options.threadId}`
93
86
  }, callOptions)
94
87
  : null;
95
88
  blockers.push(...(auditTail ? gatewayCallBlockers(auditTail, "openclaw_live_audit_tail_failed") : []));
@@ -110,7 +103,8 @@ export function runOpenClawGatewayLiveControlSmoke(options) {
110
103
  publicSafe: true,
111
104
  generatedAt: options.now ?? new Date().toISOString(),
112
105
  command: `${sanitizeCommandBinary(openclawBin)} ${[...baseArgs, "gateway", "call", "tools.invoke", "--json", "--params", "<redacted>"].join(" ")}`,
113
- requiredTools: REQUIRED_TOOLS,
106
+ action,
107
+ requiredTools,
114
108
  targetRef,
115
109
  dryRun: {
116
110
  approvalAuditId: dryRunSummary.approvalAuditId,
@@ -139,7 +133,7 @@ export function runOpenClawGatewayLiveControlSmoke(options) {
139
133
  rawTranscriptRead: false
140
134
  },
141
135
  privateDataExclusions: PRIVATE_DATA_EXCLUSIONS,
142
- proofBoundary: "This proves one approved harmless live Codex send through the installed OpenClaw gateway path only; it does not prove unattended live control, broad gateway scope approval, GUI mutation, Claude parity, or bypassed Codex approvals.",
136
+ proofBoundary: "This proves one approved harmless live Codex send/resume through the installed OpenClaw gateway path only; it does not prove unattended live control, broad gateway scope approval, GUI mutation, Claude parity, or bypassed Codex approvals.",
143
137
  nextAction: uniqueBlockers.length === 0
144
138
  ? "Run the v1.1 runtime scenario sweep against this runtime-proof directory, then continue #159 post-action refresh proof."
145
139
  : "Resolve the listed gateway live-control blockers before claiming #158 runtime proof."
@@ -170,19 +164,52 @@ function runtimeProofForReport(report) {
170
164
  raw_prompt_chars: 0
171
165
  };
172
166
  }
173
- function validDryRun(summary) {
167
+ function liveDryRunArgs(action, threadId, message) {
168
+ return action === "send"
169
+ ? {
170
+ action,
171
+ thread_id: threadId,
172
+ message
173
+ }
174
+ : {
175
+ action,
176
+ thread_id: threadId
177
+ };
178
+ }
179
+ function liveArgs(action, threadId, message, approvalAuditId) {
180
+ const common = {
181
+ thread_id: threadId,
182
+ dry_run: false,
183
+ approval_audit_id: approvalAuditId
184
+ };
185
+ return action === "send" ? { ...common, message } : common;
186
+ }
187
+ function liveToolForAction(action) {
188
+ return action === "send" ? "loo_codex_send_message" : "loo_codex_resume_thread";
189
+ }
190
+ function normalizeAction(action) {
191
+ if (action === undefined || action === "send")
192
+ return "send";
193
+ if (action === "resume")
194
+ return "resume";
195
+ throw new Error("action must be send or resume");
196
+ }
197
+ function validDryRun(action, summary) {
174
198
  return summary.live === false
175
199
  && safeAuditId(summary.approvalAuditId)
176
200
  && safeHash(summary.paramsHash)
177
- && safeHash(summary.messageHash);
201
+ && (action !== "send" || safeHash(summary.messageHash));
178
202
  }
179
- function validLive(summary) {
203
+ function validLive(action, summary) {
204
+ const actionAccepted = action === "resume"
205
+ ? summary.method === "thread/resume"
206
+ : liveTurnStatusProvesSendAccepted(summary.turnStatus);
180
207
  return summary.live === true
181
208
  && safeAuditId(summary.approvalAuditId)
182
209
  && safeHash(summary.paramsHash)
183
- && safeHash(summary.messageHash)
210
+ && (action !== "send" || safeHash(summary.messageHash))
184
211
  && summary.responseOk === true
185
- && liveTurnStatusProvesSendAccepted(summary.turnStatus);
212
+ && actionAccepted;
186
213
  }
187
214
  function liveTurnStatusProvesSendAccepted(value) {
188
215
  if (typeof value !== "string")