feat: short approval IDs for chat-friendly resume#48
feat: short approval IDs for chat-friendly resume#48coolmanns wants to merge 5 commits intoopenclaw:mainfrom
Conversation
When a workflow or pipeline hits an approval gate in tool mode, the response now includes an 'approvalId' (8-char hex string) alongside the existing base64url resumeToken. Consumers can resume using either: lobster resume --token <base64url> (existing, still works) lobster resume --id <8-char-hex> (new, chat-friendly) This solves the problem of passing opaque 120+ char base64 tokens through chat interfaces (Telegram, Discord, etc.) where they get mangled, truncated, or are painful to copy/paste. Implementation: - generateApprovalId() produces 8 hex chars via crypto.randomBytes - writeApprovalIndex() creates a small reverse-index file mapping approvalId → stateKey in the state directory - resolveApprovalId() looks up the index to reconstruct the token - Kind detection (workflow-file vs pipeline-resume) uses stateKey naming convention - Index files are cleaned up after resume or cancellation Fully backward-compatible: resumeToken is always present, approvalId is additive. No breaking changes to the envelope schema. 5 new tests covering: ID presence, resume-by-ID, cancellation-by-ID, invalid ID error, and backward compat with --token.
…d helper Bug fixes: - CLI multi-step pipeline now generates approvalId on second+ gates (was only done in tool_runtime path) - --token resume now cleans up orphaned approval index files via cleanupApprovalIndexByStateKey (scans state dir for matching index) Code quality: - Extract kindFromStateKey() shared helper in resume.ts (was duplicated) - Remove redundant dynamic import of encodeToken in tool_runtime.ts - Sanitize approvalId in writeApprovalIndex (was only done in read/delete) Tests: - Add --token orphan cleanup test (verifies index gone after --token resume) - Add double-resume test (second --id call returns clean 'not found' error) - Total: 73/73 passing
Bug: resumeToolRequest cleaned up approval index when using --id (via deleteApprovalId) but NOT when using --token. The CLI path had this fix but the API path didn't — orphan index files would accumulate in ~/.lobster/state/. Also aligned --id/--token priority: approvalId now wins in both CLI and API paths (was inconsistent — CLI checked approvalId first, API checked token first via 'approvalId && !token'). 73/73 tests passing.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b4f9f0e3ae
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
src/core/tool_runtime.ts
Outdated
| if (resolvedApprovalId) { | ||
| await deleteApprovalId({ env: runtime.env, approvalId: resolvedApprovalId }); |
There was a problem hiding this comment.
Delete approval index only after a successful resume
resumeToolRequest removes the short-ID index before running the resumed workflow/pipeline. When execution later hits the runtime_error return path, the resume state is still present for retry, but the approvalId alias is already gone, so a follow-up resume by --id fails even though the state is still resumable by token. This breaks the new approvalId path as a practical alternative to resumeToken in transient-failure scenarios; defer index deletion until the state is actually consumed/cancelled (and apply the same ordering fix in src/cli.ts).
Useful? React with 👍 / 👎.
OpenClaw Plugin Update NeededThe built-in OpenClaw Lobster plugin ( // current (hardcoded)
if (action === "resume") {
const token = typeof params.token === "string" ? params.token : "";
// ...
"resume", "--token", token,
}To support the new short approval IDs, if (action === "resume") {
if (params.id) {
return ["resume", "--id", params.id, approved ? "--approve" : "--deny"];
}
// fallback to --token
return ["resume", "--token", params.token, approved ? "--approve" : "--deny"];
}The tool schema should also add Without this, the short IDs only work via CLI — the OpenClaw tool call path can't use them. |
Addresses Codex review feedback on PR openclaw#48. Previously, the approval ID index was deleted before executing the resumed pipeline/workflow. If execution failed with a transient error, the state was still resumable by --token but the --id alias was already gone. Now: index cleanup happens after successful completion or explicit denial. On runtime_error, the index is preserved so the user can retry with the same --id.
Re: Codex Review — P2: Defer approval index cleanupGood catch 👍 — fixed in Index cleanup is now deferred to after successful completion or explicit denial. On Same fix applied to both |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fde0d0ca3a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (output.status === 'needs_approval') { | ||
| // Don't clean up index — next gate will issue a new approvalId | ||
| return okEnvelope('needs_approval', [], output.requiresApproval ?? null); |
There was a problem hiding this comment.
Remove consumed approval ID before returning next approval gate
When a resumed workflow hits another approval step, this early return skips cleanupIndex(), even though runWorkflowFile has already consumed (and deleted) the previous resume state. That leaves the prior approvalId mapped to a missing stateKey, so reusing the old ID produces a runtime_error instead of a clean “not found/expired” result, and the stale index file persists. This is reproducible in multi-approval workflows after the first successful resume.
Useful? React with 👍 / 👎.
| await cleanupIndex(); | ||
| await deleteStateJson({ env: process.env, key: previousStateKey }); |
There was a problem hiding this comment.
Write next approval index before deleting prior resume state
In the pipeline multi-approval path, the old alias/state are deleted before writing the new approval index. If writeApprovalIndex then fails (for example due to I/O errors like ENOSPC/EACCES), the handler returns runtime_error after removing the prior resumable state and without returning a usable new alias, leaving the user unable to continue from either --id or the old token.
Useful? React with 👍 / 👎.
|
[nullius-review] LGTM. Short approval IDs (8-hex) enable chat-friendly resume (--id abc123). Reverse index + cleanup. Tests confirm. Approve. 💬 Nice UX lift. |
Summary
Adds 8-char hex short approval IDs alongside existing base64url resumeTokens, making approval gates usable in chat interfaces (Telegram, Discord, etc.) where long tokens are impractical.
Changes
generateApprovalId,writeApprovalIndex,deleteApprovalId,findStateKeyByApprovalId,cleanupApprovalIndexByStateKeyapprovalIdinrequiresApprovalenvelope--idflag +kindFromStateKey()helper--tokenpathCompatibility
Fully backward-compatible. Existing
--tokenresume path unchanged. Short IDs are additive — if--idis used, it resolves to the full state key internally.Tests
73/73 passing (7 new + 66 existing, zero regressions).