lossless-openclaw-orchestrator 1.1.3 → 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 +1 -1
- package/dist/packages/cli/src/index.js +12 -4
- package/dist/packages/cli/src/openclaw-live-control-smoke.js +55 -28
- package/dist/packages/cli/src/scenario-sweep.js +2 -1
- package/docs/RELEASE_NOTES_1.1.4.md +112 -0
- package/evals/scenarios/v1.1/openclaw-gateway-live-codex.json +5 -5
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/packages/cli/src/index.ts +13 -5
- package/packages/cli/src/openclaw-live-control-smoke.ts +60 -27
- package/packages/cli/src/scenario-sweep.ts +2 -1
- package/packages/openclaw-plugin/openclaw.plugin.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ safe action without rereading raw transcripts.
|
|
|
14
14
|
[](LICENSE)
|
|
15
15
|
[](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.
|
|
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
|
|
|
@@ -780,7 +780,7 @@ function mainUsageText() {
|
|
|
780
780
|
" loo openclaw dogfood [--dev] [--profile name] [--install-source path] [--link] [--force-install] [--evidence-path path] [--strict]",
|
|
781
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]",
|
|
782
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]",
|
|
783
|
-
" 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]",
|
|
784
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]",
|
|
785
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
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]",
|
|
@@ -1172,9 +1172,9 @@ function printLiveControlSmokeHelp() {
|
|
|
1172
1172
|
function printOpenClawLiveControlSmokeHelp() {
|
|
1173
1173
|
console.log([
|
|
1174
1174
|
"Usage:",
|
|
1175
|
-
" 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]",
|
|
1176
1176
|
"",
|
|
1177
|
-
"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.",
|
|
1178
1178
|
"",
|
|
1179
1179
|
"Outputs:",
|
|
1180
1180
|
" openclaw-gateway-live-codex-v1-1.runtime-proof.json",
|
|
@@ -1182,7 +1182,7 @@ function printOpenClawLiveControlSmokeHelp() {
|
|
|
1182
1182
|
"",
|
|
1183
1183
|
"Safety boundary:",
|
|
1184
1184
|
" The command requires an explicit --thread-id target.",
|
|
1185
|
-
" It invokes loo_codex_control_dry_run first, then uses the matching approval_audit_id for
|
|
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.",
|
|
1186
1186
|
" It reads loo_audit_tail to prove matching dry-run/live audit metadata.",
|
|
1187
1187
|
" Evidence contains refs, audit ids, hashes, tool names, and status only.",
|
|
1188
1188
|
" It does not write raw prompt text, raw transcript spans, screenshots, SQLite DBs, tokens, or credentials.",
|
|
@@ -1999,6 +1999,9 @@ function parseOpenClawLiveControlSmokeArgs(input) {
|
|
|
1999
1999
|
else if (arg === "--thread-id") {
|
|
2000
2000
|
parsed.threadId = requireOptionValue(input[++index], arg);
|
|
2001
2001
|
}
|
|
2002
|
+
else if (arg === "--action") {
|
|
2003
|
+
parsed.action = parseOpenClawLiveControlAction(requireOptionValue(input[++index], arg));
|
|
2004
|
+
}
|
|
2002
2005
|
else if (arg === "--message") {
|
|
2003
2006
|
parsed.message = requireOptionValue(input[++index], arg);
|
|
2004
2007
|
}
|
|
@@ -2018,6 +2021,11 @@ function parseOpenClawLiveControlSmokeArgs(input) {
|
|
|
2018
2021
|
throw new Error("openclaw live-control-smoke requires --thread-id");
|
|
2019
2022
|
return parsed;
|
|
2020
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
|
+
}
|
|
2021
2029
|
function parseOpenClawPostActionRefreshSmokeArgs(input) {
|
|
2022
2030
|
const parsed = {};
|
|
2023
2031
|
for (let index = 0; index < input.length; index += 1) {
|
|
@@ -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(...
|
|
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:
|
|
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
|
|
63
|
+
idempotencyKey: `loo-live-smoke-${action}-${options.threadId}-${dryRunSummary.approvalAuditId}`
|
|
71
64
|
}, callOptions)
|
|
72
65
|
: null;
|
|
73
|
-
blockers.push(...(live ? gatewayCallBlockers(live, "
|
|
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
|
-
|
|
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
|
|
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
|
-
&&
|
|
212
|
+
&& actionAccepted;
|
|
186
213
|
}
|
|
187
214
|
function liveTurnStatusProvesSendAccepted(value) {
|
|
188
215
|
if (typeof value !== "string")
|
|
@@ -228,6 +228,7 @@ function readRuntimeScenario(scenario, file, evidenceDir, runtimeProofDir) {
|
|
|
228
228
|
const fallbackId = safeEvidenceStem(basename(file, ".json")) || "scenario";
|
|
229
229
|
const id = isSafeScenarioId(requestedId) ? requestedId : fallbackId;
|
|
230
230
|
const requiredTools = stringArray(scenario.required_tools);
|
|
231
|
+
const allowedTools = stringArray(scenario.allowed_tools);
|
|
231
232
|
const forbiddenBehaviors = stringArray(scenario.forbidden_behaviors);
|
|
232
233
|
const expectedPublicSafeEvidence = stringArray(scenario.expected_public_safe_evidence);
|
|
233
234
|
const privateDataExclusions = stringArray(scenario.private_data_exclusions).length
|
|
@@ -270,7 +271,7 @@ function readRuntimeScenario(scenario, file, evidenceDir, runtimeProofDir) {
|
|
|
270
271
|
surface: stringValue(scenario.surface) || "unknown",
|
|
271
272
|
status: contractBlockers.length > 0 ? "invalid" : proof.blockers.length > 0 ? "runtime_proof_required" : "runtime_proof_ready",
|
|
272
273
|
evidencePath: join(evidenceDir, `${id}.json`),
|
|
273
|
-
allowedTools: requiredTools,
|
|
274
|
+
allowedTools: allowedTools.length > 0 ? allowedTools : requiredTools,
|
|
274
275
|
forbiddenBehaviors,
|
|
275
276
|
expectedPublicSafeEvidence,
|
|
276
277
|
privateDataExclusions,
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Release Notes 1.1.4
|
|
2
|
+
|
|
3
|
+
`1.1.4` is a stable patch release for the Codex-first collaboration cockpit and
|
|
4
|
+
OpenClaw gateway lane.
|
|
5
|
+
|
|
6
|
+
It keeps the `1.1.3` stable claim scope and adds the approved live-resume
|
|
7
|
+
gateway proof from #383 / #402. The release proves one harmless approved
|
|
8
|
+
`thread/resume` path through the installed OpenClaw gateway plus post-action
|
|
9
|
+
safe refresh/reasoning evidence.
|
|
10
|
+
|
|
11
|
+
## What Changed
|
|
12
|
+
|
|
13
|
+
- Added `--action send|resume` to the OpenClaw live-control smoke command so
|
|
14
|
+
release/runtime proof can validate either a message send or a no-message
|
|
15
|
+
Codex resume action through the same gateway surface.
|
|
16
|
+
- `loo openclaw live-control-smoke --action resume` now requires
|
|
17
|
+
`loo_codex_control_dry_run`, `loo_codex_resume_thread`, and `loo_audit_tail`
|
|
18
|
+
instead of the send-only live tool.
|
|
19
|
+
- Resume proof accepts the Codex app-server `thread/resume` response shape when
|
|
20
|
+
`ok: true` and `method: "thread/resume"` are present, even when there is no
|
|
21
|
+
turn-status payload.
|
|
22
|
+
- Resume proof fails closed when the live response is not ok, reports the wrong
|
|
23
|
+
method, or cannot be matched back to the dry-run approval audit record.
|
|
24
|
+
- Runtime action parsing now rejects malformed action values instead of
|
|
25
|
+
silently falling back to the higher-side-effect send path.
|
|
26
|
+
- The `openclaw-gateway-live-codex-v1-1` runtime scenario now keeps only the
|
|
27
|
+
common dry-run/audit tools in `required_tools` and lists `loo_codex_send_message`
|
|
28
|
+
and `loo_codex_resume_thread` as allowed alternatives, so scenario consumers
|
|
29
|
+
do not try to perform two live actions while the contract caps live actions at
|
|
30
|
+
one.
|
|
31
|
+
- Scenario sweep output preserves `allowed_tools` separately from the required
|
|
32
|
+
dry-run tool sequence for runtime-required scenarios.
|
|
33
|
+
- Regression tests cover the send path, resume happy path, status-less resume
|
|
34
|
+
success, resume negative paths, malformed actions, and scenario-contract
|
|
35
|
+
alternate live actions.
|
|
36
|
+
- Carries forward the #160 desktop proof-action release boundary:
|
|
37
|
+
`loo_desktop_proof_action` / `loo desktop proof-action` validates the
|
|
38
|
+
action-bound CUA Driver TextEdit scratch proof gate and does not prove
|
|
39
|
+
generic GUI mutation. The proof action still requires exact backend, target
|
|
40
|
+
app, target window, action hash, approval ref, permission state, scratch file
|
|
41
|
+
path, and `execute: true` before the backend can run. Generic gateway
|
|
42
|
+
invocation without exact proof args fails closed, using the same proof
|
|
43
|
+
boundary as beta.35.
|
|
44
|
+
Exact gate phrase: exact backend, target app, target window, action hash, approval ref, permission state, scratch file path, and `execute: true`.
|
|
45
|
+
Exact fail-closed phrase: generic gateway invocation without exact proof args fails closed.
|
|
46
|
+
The desktop proof action keeps the same proof boundary as beta.35.
|
|
47
|
+
- Carries forward strict OpenClaw gateway result handling:
|
|
48
|
+
plugin output with `output.details.ok: false` is reported as
|
|
49
|
+
`openclaw_tool_result_not_ok:<tool>` rather than successful tool proof.
|
|
50
|
+
|
|
51
|
+
## Stable Claim Scope
|
|
52
|
+
|
|
53
|
+
Allowed stable claim:
|
|
54
|
+
|
|
55
|
+
> Codex-first local orchestration through OpenClaw for local Codex sessions,
|
|
56
|
+
> including local indexing, search, describe, bounded expansion, dry-run control
|
|
57
|
+
> envelopes, read-only collaboration cockpit/autonomy cards, read-only Desktop
|
|
58
|
+
> visibility and fallback status, action-bound proof-packet validation,
|
|
59
|
+
> public-safe scorecards, runtime proof handoff packets, runtime sweep
|
|
60
|
+
> summaries, installed OpenClaw gateway dogfood, and one approved harmless live
|
|
61
|
+
> Codex resume proof through the installed OpenClaw gateway.
|
|
62
|
+
|
|
63
|
+
This release still does not claim generic GUI mutation, Codex GUI mutation,
|
|
64
|
+
unattended control, Claude Code parity, P1 business-adapter parity, cloud sync,
|
|
65
|
+
or enterprise/customer-ready security.
|
|
66
|
+
|
|
67
|
+
## Release Gate Notes
|
|
68
|
+
|
|
69
|
+
- Stable issue: #403.
|
|
70
|
+
- Included implementation issue: #383.
|
|
71
|
+
- Included implementation PR: #402.
|
|
72
|
+
- Parent product tracker: #309.
|
|
73
|
+
- Operating-loop tracker: #16.
|
|
74
|
+
- Baseline stable release: `v1.1.3`.
|
|
75
|
+
- Candidate package: `lossless-openclaw-orchestrator@1.1.4`.
|
|
76
|
+
- Expected git tag: `v1.1.4`.
|
|
77
|
+
- Expected GitHub Release:
|
|
78
|
+
`https://github.com/100yenadmin/Lossless-Codex-Orchestrator-LCO/releases/tag/v1.1.4`.
|
|
79
|
+
- Required pre-publish stable gates: `npm run check`, `npm pack --dry-run`,
|
|
80
|
+
strict runtime-required scenario sweep for `openclaw-gateway-live-codex-v1-1`
|
|
81
|
+
and `post-action-refresh-reasoning-v1-1`, strict scorecard sweep, release
|
|
82
|
+
preflight, release bundle, release demo-status, release status, OpenClaw
|
|
83
|
+
dogfood, and OpenClaw tool-smoke for the exact stable candidate SHA.
|
|
84
|
+
- Required PR gates: GitHub CI, CodeQL, current-head review threads clear, and
|
|
85
|
+
any actionable review feedback fixed before merge.
|
|
86
|
+
- Required post-publish stable gates: npm `latest` view, git tag, GitHub
|
|
87
|
+
non-prerelease Release, `loo release finalization-status --expected-dist-tag
|
|
88
|
+
latest --expected-github-prerelease false --strict`, fresh npm `@latest`
|
|
89
|
+
published-smoke, and strict general-readiness before #403 closes.
|
|
90
|
+
- Working-app status example:
|
|
91
|
+
`loo release status --claim-scope codex-working-app-proof --runtime-proof-dir <path> --approved-live-control-evidence <path> --npm-publish-approval-evidence <path> --github-release-approval-evidence <path> --candidate-sha <sha> --github-ci-evidence <path> --codeql-evidence <path> --evidence-dir <path> --strict`
|
|
92
|
+
- Reduced-scope status example:
|
|
93
|
+
`loo release status --claim-scope codex-read-search-expand-dry-run --npm-publish-approval-evidence <path> --github-release-approval-evidence <path> --candidate-sha <sha> --github-ci-evidence <path> --codeql-evidence <path> --evidence-dir <path> --strict`
|
|
94
|
+
- Post-publish finalization example:
|
|
95
|
+
`loo release finalization-status --evidence-dir <path> --candidate-sha <sha> --npm-publish-evidence <path> --git-tag-evidence <path> --github-release-evidence <path> --expected-dist-tag latest --expected-github-prerelease false --strict`
|
|
96
|
+
- `approved_live_control_smoke_missing` remains the expected blocker when a
|
|
97
|
+
working-app claim is attempted without approved live-control smoke evidence
|
|
98
|
+
for the exact candidate SHA.
|
|
99
|
+
|
|
100
|
+
## Explicit Non-Claims
|
|
101
|
+
|
|
102
|
+
No new live Codex control smoke is run by this release.
|
|
103
|
+
It does not run generic GUI mutation and does not run Codex GUI mutation.
|
|
104
|
+
No screenshot workflow.
|
|
105
|
+
No prompt typing. No clicking. No arbitrary app control.
|
|
106
|
+
No unattended desktop takeover.
|
|
107
|
+
No automatic gateway authorization and no broad gateway scope approval.
|
|
108
|
+
Claude Code remains an adapter stub, not adapter equivalence.
|
|
109
|
+
No Notion, support-control, Stripe, or Company Brain P1 adapter proof.
|
|
110
|
+
No cloud sync.
|
|
111
|
+
Bundle/status/finalization checks do not publish to npm and do not create a GitHub Release.
|
|
112
|
+
No release-grade enterprise security or customer-ready security claim.
|
|
@@ -9,16 +9,16 @@
|
|
|
9
9
|
"surface": "installed-openclaw-gateway",
|
|
10
10
|
"required_tools": [
|
|
11
11
|
"loo_codex_control_dry_run",
|
|
12
|
-
"loo_codex_send_message",
|
|
13
12
|
"loo_audit_tail"
|
|
14
13
|
],
|
|
15
14
|
"allowed_tools": [
|
|
16
15
|
"loo_codex_control_dry_run",
|
|
17
16
|
"loo_codex_send_message",
|
|
17
|
+
"loo_codex_resume_thread",
|
|
18
18
|
"loo_audit_tail"
|
|
19
19
|
],
|
|
20
20
|
"allowed_live_behaviors": [
|
|
21
|
-
"one approved harmless Codex
|
|
21
|
+
"one approved harmless Codex send or resume on a selected or disposable target thread"
|
|
22
22
|
],
|
|
23
23
|
"forbidden_behaviors": [
|
|
24
24
|
"unauthorized_live_control",
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
"action type",
|
|
41
41
|
"approval audit id prefix",
|
|
42
42
|
"params hash",
|
|
43
|
-
"message hash",
|
|
44
|
-
"Codex notification count and types",
|
|
45
|
-
"completion status",
|
|
43
|
+
"message hash when the live action sends a message",
|
|
44
|
+
"Codex notification count and types when available",
|
|
45
|
+
"completion status or response ok marker",
|
|
46
46
|
"rawPromptIncluded false"
|
|
47
47
|
],
|
|
48
48
|
"metrics": {
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "lossless-openclaw-orchestrator",
|
|
3
3
|
"name": "Lossless OpenClaw Orchestrator",
|
|
4
4
|
"description": "Control and collaborate with local Codex sessions through OpenClaw using local indexing, bounded recall, and approval-gated controls.",
|
|
5
|
-
"version": "1.1.
|
|
5
|
+
"version": "1.1.4",
|
|
6
6
|
"kind": "tool",
|
|
7
7
|
"tools": {
|
|
8
8
|
"prefix": "loo_"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lossless-openclaw-orchestrator",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "Index, search, and control local Codex sessions through OpenClaw with approval-gated safety; Claude Code remains an experimental adapter stub.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
@@ -44,7 +44,7 @@ import { createGeneralReleaseReadiness } from "./general-release-readiness.js";
|
|
|
44
44
|
import { runOpenClawDogfood } from "./openclaw-dogfood.js";
|
|
45
45
|
import { DEFAULT_REQUIRED_TOOL_CALLS, runOpenClawToolSmoke } from "./openclaw-tool-smoke.js";
|
|
46
46
|
import { createPublishedPackageSmokeReport } from "./published-package-smoke.js";
|
|
47
|
-
import { runOpenClawGatewayLiveControlSmoke } from "./openclaw-live-control-smoke.js";
|
|
47
|
+
import { runOpenClawGatewayLiveControlSmoke, type OpenClawGatewayLiveControlAction } from "./openclaw-live-control-smoke.js";
|
|
48
48
|
import { runOpenClawPostActionRefreshSmoke } from "./openclaw-post-action-refresh-smoke.js";
|
|
49
49
|
import { createScorecardSweep } from "./scorecard-sweep.js";
|
|
50
50
|
import { createScenarioSweep } from "./scenario-sweep.js";
|
|
@@ -803,7 +803,7 @@ function mainUsageText(): string {
|
|
|
803
803
|
" loo openclaw dogfood [--dev] [--profile name] [--install-source path] [--link] [--force-install] [--evidence-path path] [--strict]",
|
|
804
804
|
" 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]",
|
|
805
805
|
" 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]",
|
|
806
|
-
" 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]",
|
|
806
|
+
" 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]",
|
|
807
807
|
" 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]",
|
|
808
808
|
" 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]",
|
|
809
809
|
" 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]",
|
|
@@ -1212,9 +1212,9 @@ function printLiveControlSmokeHelp(): void {
|
|
|
1212
1212
|
function printOpenClawLiveControlSmokeHelp(): void {
|
|
1213
1213
|
console.log([
|
|
1214
1214
|
"Usage:",
|
|
1215
|
-
" 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]",
|
|
1215
|
+
" 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]",
|
|
1216
1216
|
"",
|
|
1217
|
-
"Runs one approval-gated live Codex send through the installed OpenClaw gateway tools.invoke path.",
|
|
1217
|
+
"Runs one approval-gated live Codex send or resume through the installed OpenClaw gateway tools.invoke path.",
|
|
1218
1218
|
"",
|
|
1219
1219
|
"Outputs:",
|
|
1220
1220
|
" openclaw-gateway-live-codex-v1-1.runtime-proof.json",
|
|
@@ -1222,7 +1222,7 @@ function printOpenClawLiveControlSmokeHelp(): void {
|
|
|
1222
1222
|
"",
|
|
1223
1223
|
"Safety boundary:",
|
|
1224
1224
|
" The command requires an explicit --thread-id target.",
|
|
1225
|
-
" It invokes loo_codex_control_dry_run first, then uses the matching approval_audit_id for
|
|
1225
|
+
" It invokes loo_codex_control_dry_run first, then uses the matching approval_audit_id for the selected live tool with dry_run:false.",
|
|
1226
1226
|
" It reads loo_audit_tail to prove matching dry-run/live audit metadata.",
|
|
1227
1227
|
" Evidence contains refs, audit ids, hashes, tool names, and status only.",
|
|
1228
1228
|
" It does not write raw prompt text, raw transcript spans, screenshots, SQLite DBs, tokens, or credentials.",
|
|
@@ -2026,6 +2026,7 @@ function parseOpenClawLiveControlSmokeArgs(input: string[]): {
|
|
|
2026
2026
|
token?: string;
|
|
2027
2027
|
sessionKey?: string;
|
|
2028
2028
|
threadId: string;
|
|
2029
|
+
action?: OpenClawGatewayLiveControlAction;
|
|
2029
2030
|
message?: string;
|
|
2030
2031
|
evidenceDir: string;
|
|
2031
2032
|
gatewayTimeoutMs?: number;
|
|
@@ -2050,6 +2051,8 @@ function parseOpenClawLiveControlSmokeArgs(input: string[]): {
|
|
|
2050
2051
|
parsed.sessionKey = requireOptionValue(input[++index], arg);
|
|
2051
2052
|
} else if (arg === "--thread-id") {
|
|
2052
2053
|
parsed.threadId = requireOptionValue(input[++index], arg);
|
|
2054
|
+
} else if (arg === "--action") {
|
|
2055
|
+
parsed.action = parseOpenClawLiveControlAction(requireOptionValue(input[++index], arg));
|
|
2053
2056
|
} else if (arg === "--message") {
|
|
2054
2057
|
parsed.message = requireOptionValue(input[++index], arg);
|
|
2055
2058
|
} else if (arg === "--evidence-dir") {
|
|
@@ -2065,6 +2068,11 @@ function parseOpenClawLiveControlSmokeArgs(input: string[]): {
|
|
|
2065
2068
|
return parsed as ReturnType<typeof parseOpenClawLiveControlSmokeArgs>;
|
|
2066
2069
|
}
|
|
2067
2070
|
|
|
2071
|
+
function parseOpenClawLiveControlAction(value: string): OpenClawGatewayLiveControlAction {
|
|
2072
|
+
if (value === "send" || value === "resume") return value;
|
|
2073
|
+
throw new Error("--action must be send or resume");
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2068
2076
|
function parseOpenClawPostActionRefreshSmokeArgs(input: string[]): {
|
|
2069
2077
|
openclawBin?: string;
|
|
2070
2078
|
dev?: boolean;
|
|
@@ -10,18 +10,22 @@ export type OpenClawGatewayLiveControlSmokeOptions = {
|
|
|
10
10
|
token?: string;
|
|
11
11
|
sessionKey?: string;
|
|
12
12
|
threadId: string;
|
|
13
|
+
action?: OpenClawGatewayLiveControlAction;
|
|
13
14
|
message?: string;
|
|
14
15
|
evidenceDir: string;
|
|
15
16
|
gatewayTimeoutMs?: number;
|
|
16
17
|
now?: string;
|
|
17
18
|
};
|
|
18
19
|
|
|
20
|
+
export type OpenClawGatewayLiveControlAction = "send" | "resume";
|
|
21
|
+
|
|
19
22
|
export type OpenClawGatewayLiveControlSmokeReport = {
|
|
20
23
|
ok: boolean;
|
|
21
24
|
proofReady: boolean;
|
|
22
25
|
publicSafe: boolean;
|
|
23
26
|
generatedAt: string;
|
|
24
27
|
command: string;
|
|
28
|
+
action: OpenClawGatewayLiveControlAction;
|
|
25
29
|
requiredTools: string[];
|
|
26
30
|
targetRef: string;
|
|
27
31
|
dryRun: {
|
|
@@ -84,7 +88,6 @@ type ControlSummary = {
|
|
|
84
88
|
const SCENARIO_ID = "openclaw-gateway-live-codex-v1-1";
|
|
85
89
|
const ACCEPTED_LIVE_TURN_STATUSES = new Set(["accepted", "completed", "in_progress", "pending", "queued", "running"]);
|
|
86
90
|
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.";
|
|
87
|
-
const REQUIRED_TOOLS = ["loo_codex_control_dry_run", "loo_codex_send_message", "loo_audit_tail"];
|
|
88
91
|
const PRIVATE_DATA_EXCLUSIONS = [
|
|
89
92
|
"raw OpenClaw gateway stdout/stderr",
|
|
90
93
|
"raw tool output",
|
|
@@ -113,6 +116,9 @@ export function runOpenClawGatewayLiveControlSmoke(options: OpenClawGatewayLiveC
|
|
|
113
116
|
];
|
|
114
117
|
const callOptions = { timeoutMs: gatewayTimeoutMs, env: options.token ? { OPENCLAW_GATEWAY_TOKEN: options.token } : undefined };
|
|
115
118
|
const sessionKey = options.sessionKey || "agent:main:lco-live-control-smoke";
|
|
119
|
+
const action = normalizeAction(options.action);
|
|
120
|
+
const liveToolName = liveToolForAction(action);
|
|
121
|
+
const requiredTools = ["loo_codex_control_dry_run", liveToolName, "loo_audit_tail"];
|
|
116
122
|
const message = options.message ?? DEFAULT_MESSAGE;
|
|
117
123
|
const targetRef = `codex_thread:${options.threadId}`;
|
|
118
124
|
const blockers: string[] = [];
|
|
@@ -120,42 +126,33 @@ export function runOpenClawGatewayLiveControlSmoke(options: OpenClawGatewayLiveC
|
|
|
120
126
|
const catalog = callGatewayJson(openclawBin, baseArgs, gatewayOptions, "tools.catalog", {}, callOptions);
|
|
121
127
|
const catalogTools = catalog.status === 0 && catalog.parsed !== undefined ? extractCatalogToolNames(unwrapGatewayPayload(catalog.parsed)) : [];
|
|
122
128
|
blockers.push(...gatewayCallBlockers(catalog, "openclaw_live_catalog_failed"));
|
|
123
|
-
blockers.push(...
|
|
129
|
+
blockers.push(...requiredTools.filter((tool) => !catalogTools.includes(tool)).map((tool) => `openclaw_live_catalog_missing_tool:${tool}`));
|
|
124
130
|
|
|
125
131
|
const dryRun = blockers.length === 0
|
|
126
132
|
? callGatewayJson(openclawBin, baseArgs, gatewayOptions, "tools.invoke", {
|
|
127
133
|
name: "loo_codex_control_dry_run",
|
|
128
|
-
args:
|
|
129
|
-
action: "send",
|
|
130
|
-
thread_id: options.threadId,
|
|
131
|
-
message
|
|
132
|
-
},
|
|
134
|
+
args: liveDryRunArgs(action, options.threadId, message),
|
|
133
135
|
sessionKey,
|
|
134
136
|
confirm: false,
|
|
135
|
-
idempotencyKey: `loo-live-smoke-dry-run-${options.threadId}`
|
|
137
|
+
idempotencyKey: `loo-live-smoke-dry-run-${action}-${options.threadId}`
|
|
136
138
|
}, callOptions)
|
|
137
139
|
: null;
|
|
138
140
|
blockers.push(...(dryRun ? gatewayCallBlockers(dryRun, "openclaw_live_dry_run_failed") : []));
|
|
139
141
|
const dryRunSummary = summarizeControl(dryRun);
|
|
140
|
-
if (dryRun && !validDryRun(dryRunSummary)) blockers.push("openclaw_live_dry_run_not_proven");
|
|
142
|
+
if (dryRun && !validDryRun(action, dryRunSummary)) blockers.push("openclaw_live_dry_run_not_proven");
|
|
141
143
|
|
|
142
144
|
const live = blockers.length === 0
|
|
143
145
|
? callGatewayJson(openclawBin, baseArgs, gatewayOptions, "tools.invoke", {
|
|
144
|
-
name:
|
|
145
|
-
args:
|
|
146
|
-
thread_id: options.threadId,
|
|
147
|
-
message,
|
|
148
|
-
dry_run: false,
|
|
149
|
-
approval_audit_id: dryRunSummary.approvalAuditId
|
|
150
|
-
},
|
|
146
|
+
name: liveToolName,
|
|
147
|
+
args: liveArgs(action, options.threadId, message, dryRunSummary.approvalAuditId),
|
|
151
148
|
sessionKey,
|
|
152
149
|
confirm: false,
|
|
153
|
-
idempotencyKey: `loo-live-smoke
|
|
150
|
+
idempotencyKey: `loo-live-smoke-${action}-${options.threadId}-${dryRunSummary.approvalAuditId}`
|
|
154
151
|
}, callOptions)
|
|
155
152
|
: null;
|
|
156
|
-
blockers.push(...(live ? gatewayCallBlockers(live, "
|
|
153
|
+
blockers.push(...(live ? gatewayCallBlockers(live, "openclaw_live_control_failed") : []));
|
|
157
154
|
const liveSummary = summarizeControl(live);
|
|
158
|
-
if (live && !validLive(liveSummary)) blockers.push("openclaw_live_send_not_proven");
|
|
155
|
+
if (live && !validLive(action, liveSummary)) blockers.push(action === "resume" ? "openclaw_live_resume_not_proven" : "openclaw_live_send_not_proven");
|
|
159
156
|
if (live && dryRunSummary.paramsHash && liveSummary.paramsHash && liveSummary.paramsHash !== dryRunSummary.paramsHash) blockers.push("openclaw_live_params_hash_mismatch");
|
|
160
157
|
if (live && dryRunSummary.messageHash && liveSummary.messageHash && liveSummary.messageHash !== dryRunSummary.messageHash) blockers.push("openclaw_live_message_hash_mismatch");
|
|
161
158
|
const approvalAuditIdUsed = live ? dryRunSummary.approvalAuditId : null;
|
|
@@ -172,7 +169,7 @@ export function runOpenClawGatewayLiveControlSmoke(options: OpenClawGatewayLiveC
|
|
|
172
169
|
args: { limit: 20 },
|
|
173
170
|
sessionKey,
|
|
174
171
|
confirm: false,
|
|
175
|
-
idempotencyKey: `loo-live-smoke-audit-tail-${options.threadId}`
|
|
172
|
+
idempotencyKey: `loo-live-smoke-audit-tail-${action}-${options.threadId}`
|
|
176
173
|
}, callOptions)
|
|
177
174
|
: null;
|
|
178
175
|
blockers.push(...(auditTail ? gatewayCallBlockers(auditTail, "openclaw_live_audit_tail_failed") : []));
|
|
@@ -196,7 +193,8 @@ export function runOpenClawGatewayLiveControlSmoke(options: OpenClawGatewayLiveC
|
|
|
196
193
|
publicSafe: true,
|
|
197
194
|
generatedAt: options.now ?? new Date().toISOString(),
|
|
198
195
|
command: `${sanitizeCommandBinary(openclawBin)} ${[...baseArgs, "gateway", "call", "tools.invoke", "--json", "--params", "<redacted>"].join(" ")}`,
|
|
199
|
-
|
|
196
|
+
action,
|
|
197
|
+
requiredTools,
|
|
200
198
|
targetRef,
|
|
201
199
|
dryRun: {
|
|
202
200
|
approvalAuditId: dryRunSummary.approvalAuditId,
|
|
@@ -225,7 +223,7 @@ export function runOpenClawGatewayLiveControlSmoke(options: OpenClawGatewayLiveC
|
|
|
225
223
|
rawTranscriptRead: false
|
|
226
224
|
},
|
|
227
225
|
privateDataExclusions: PRIVATE_DATA_EXCLUSIONS,
|
|
228
|
-
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.",
|
|
226
|
+
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.",
|
|
229
227
|
nextAction: uniqueBlockers.length === 0
|
|
230
228
|
? "Run the v1.1 runtime scenario sweep against this runtime-proof directory, then continue #159 post-action refresh proof."
|
|
231
229
|
: "Resolve the listed gateway live-control blockers before claiming #158 runtime proof."
|
|
@@ -259,20 +257,55 @@ function runtimeProofForReport(report: OpenClawGatewayLiveControlSmokeReport): R
|
|
|
259
257
|
};
|
|
260
258
|
}
|
|
261
259
|
|
|
262
|
-
function
|
|
260
|
+
function liveDryRunArgs(action: OpenClawGatewayLiveControlAction, threadId: string, message: string): Record<string, unknown> {
|
|
261
|
+
return action === "send"
|
|
262
|
+
? {
|
|
263
|
+
action,
|
|
264
|
+
thread_id: threadId,
|
|
265
|
+
message
|
|
266
|
+
}
|
|
267
|
+
: {
|
|
268
|
+
action,
|
|
269
|
+
thread_id: threadId
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function liveArgs(action: OpenClawGatewayLiveControlAction, threadId: string, message: string, approvalAuditId: string | null): Record<string, unknown> {
|
|
274
|
+
const common = {
|
|
275
|
+
thread_id: threadId,
|
|
276
|
+
dry_run: false,
|
|
277
|
+
approval_audit_id: approvalAuditId
|
|
278
|
+
};
|
|
279
|
+
return action === "send" ? { ...common, message } : common;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function liveToolForAction(action: OpenClawGatewayLiveControlAction): string {
|
|
283
|
+
return action === "send" ? "loo_codex_send_message" : "loo_codex_resume_thread";
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function normalizeAction(action: unknown): OpenClawGatewayLiveControlAction {
|
|
287
|
+
if (action === undefined || action === "send") return "send";
|
|
288
|
+
if (action === "resume") return "resume";
|
|
289
|
+
throw new Error("action must be send or resume");
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function validDryRun(action: OpenClawGatewayLiveControlAction, summary: ControlSummary): boolean {
|
|
263
293
|
return summary.live === false
|
|
264
294
|
&& safeAuditId(summary.approvalAuditId)
|
|
265
295
|
&& safeHash(summary.paramsHash)
|
|
266
|
-
&& safeHash(summary.messageHash);
|
|
296
|
+
&& (action !== "send" || safeHash(summary.messageHash));
|
|
267
297
|
}
|
|
268
298
|
|
|
269
|
-
function validLive(summary: ControlSummary): boolean {
|
|
299
|
+
function validLive(action: OpenClawGatewayLiveControlAction, summary: ControlSummary): boolean {
|
|
300
|
+
const actionAccepted = action === "resume"
|
|
301
|
+
? summary.method === "thread/resume"
|
|
302
|
+
: liveTurnStatusProvesSendAccepted(summary.turnStatus);
|
|
270
303
|
return summary.live === true
|
|
271
304
|
&& safeAuditId(summary.approvalAuditId)
|
|
272
305
|
&& safeHash(summary.paramsHash)
|
|
273
|
-
&& safeHash(summary.messageHash)
|
|
306
|
+
&& (action !== "send" || safeHash(summary.messageHash))
|
|
274
307
|
&& summary.responseOk === true
|
|
275
|
-
&&
|
|
308
|
+
&& actionAccepted;
|
|
276
309
|
}
|
|
277
310
|
|
|
278
311
|
function liveTurnStatusProvesSendAccepted(value: string | null): boolean {
|
|
@@ -351,6 +351,7 @@ function readRuntimeScenario(scenario: ScenarioJson, file: string, evidenceDir:
|
|
|
351
351
|
const fallbackId = safeEvidenceStem(basename(file, ".json")) || "scenario";
|
|
352
352
|
const id = isSafeScenarioId(requestedId) ? requestedId : fallbackId;
|
|
353
353
|
const requiredTools = stringArray(scenario.required_tools);
|
|
354
|
+
const allowedTools = stringArray(scenario.allowed_tools);
|
|
354
355
|
const forbiddenBehaviors = stringArray(scenario.forbidden_behaviors);
|
|
355
356
|
const expectedPublicSafeEvidence = stringArray(scenario.expected_public_safe_evidence);
|
|
356
357
|
const privateDataExclusions = stringArray(scenario.private_data_exclusions).length
|
|
@@ -393,7 +394,7 @@ function readRuntimeScenario(scenario: ScenarioJson, file: string, evidenceDir:
|
|
|
393
394
|
surface: stringValue(scenario.surface) || "unknown",
|
|
394
395
|
status: contractBlockers.length > 0 ? "invalid" : proof.blockers.length > 0 ? "runtime_proof_required" : "runtime_proof_ready",
|
|
395
396
|
evidencePath: join(evidenceDir, `${id}.json`),
|
|
396
|
-
allowedTools: requiredTools,
|
|
397
|
+
allowedTools: allowedTools.length > 0 ? allowedTools : requiredTools,
|
|
397
398
|
forbiddenBehaviors,
|
|
398
399
|
expectedPublicSafeEvidence,
|
|
399
400
|
privateDataExclusions,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "lossless-openclaw-orchestrator",
|
|
3
3
|
"name": "Lossless OpenClaw Orchestrator",
|
|
4
4
|
"description": "Control and collaborate with local Codex sessions through OpenClaw using local indexing, bounded recall, and approval-gated controls.",
|
|
5
|
-
"version": "1.1.
|
|
5
|
+
"version": "1.1.4",
|
|
6
6
|
"kind": "tool",
|
|
7
7
|
"tools": {
|
|
8
8
|
"prefix": "loo_"
|