Skip to content

feat: short approval IDs for chat-friendly resume#48

Open
coolmanns wants to merge 5 commits intoopenclaw:mainfrom
coolmanns:feat/short-approval-ids
Open

feat: short approval IDs for chat-friendly resume#48
coolmanns wants to merge 5 commits intoopenclaw:mainfrom
coolmanns:feat/short-approval-ids

Conversation

@coolmanns
Copy link
Copy Markdown

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

  • src/state/store.ts: generateApprovalId, writeApprovalIndex, deleteApprovalId, findStateKeyByApprovalId, cleanupApprovalIndexByStateKey
  • src/workflows/file.ts: Include approvalId in requiresApproval envelope
  • src/resume.ts: --id flag + kindFromStateKey() helper
  • src/cli.ts: Multi-step gate fix (second gate generates approvalId), orphan index cleanup in --token path
  • src/core/tool_runtime.ts: Consistent approvalId-first priority, orphan cleanup
  • test/approval_id.test.ts: 7 new tests

Compatibility

Fully backward-compatible. Existing --token resume path unchanged. Short IDs are additive — if --id is used, it resolves to the full state key internally.

Tests

73/73 passing (7 new + 66 existing, zero regressions).

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.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +199 to +200
if (resolvedApprovalId) {
await deleteApprovalId({ env: runtime.env, approvalId: resolvedApprovalId });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@coolmanns
Copy link
Copy Markdown
Author

OpenClaw Plugin Update Needed

The built-in OpenClaw Lobster plugin (dist/extensions/lobster/index.js) currently only passes --token to the resume CLI:

// current (hardcoded)
if (action === "resume") {
    const token = typeof params.token === "string" ? params.token : "";
    // ...
    "resume", "--token", token,
}

To support the new short approval IDs, buildLobsterArgv needs to accept an id parameter and pass --id instead of (or in addition to) --token:

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 id as an optional string parameter alongside token.

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.
@coolmanns
Copy link
Copy Markdown
Author

Re: Codex Review — P2: Defer approval index cleanup

Good catch 👍 — fixed in fde0d0c.

Index cleanup is now deferred to after successful completion or explicit denial. On runtime_error, the approval ID index is preserved so the user can retry with --id.

Same fix applied to both src/core/tool_runtime.ts (tool path) and src/cli.ts (CLI path). 73/73 tests passing.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines 227 to 229
if (output.status === 'needs_approval') {
// Don't clean up index — next gate will issue a new approvalId
return okEnvelope('needs_approval', [], output.requiresApproval ?? null);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment on lines +435 to 436
await cleanupIndex();
await deleteStateJson({ env: process.env, key: previousStateKey });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@happyclaw-agent
Copy link
Copy Markdown

[nullius-review]

LGTM. Short approval IDs (8-hex) enable chat-friendly resume (--id abc123). Reverse index + cleanup. Tests confirm.

Approve.

💬 Nice UX lift.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants