Commit Graph
126 Commits
Author SHA1 Message Date
Yiğit ERDOĞANandClaude Opus 5 e88e8d3970 fix: cached LLM answers are returned when temperature or max output tokens change (#130)
* fix(llm): key the response cache on sampling parameters

--temperature and --max-output-tokens were sent to the adapter but left out
of the cache key, so changing either one replayed the previous answer
instead of calling the model. The same key drives --state-key reuse, so a
resumed workflow replayed the stale answer too.

Hash each parameter only when it is set, guarding on the same predicate
that decides whether it goes on the wire. stableStringify sorts keys, so an
invocation that sets neither serializes exactly as before and keeps its
current hash - the cache is content-addressed by filename and a blanket key
change would orphan every entry written by an earlier release.

metadata stays out: it is set unconditionally with --metadata-json and
carries correlation data rather than decode-time parameters, so hashing it
would invalidate existing caches and collapse the hit rate. --schema-version
already covers callers who want metadata to segment the cache.

* fix(llm): version the cache identity so old entries cannot replay sampling

Keying only the parameters that were supplied kept the omitted-parameter key
byte-identical to what earlier releases wrote. That preserved existing caches,
but it also left one collision: an entry those releases stored for a request
sampled at an explicit temperature sits under the key a request that omits
sampling computes, so after upgrading, an unsampled request could be served a
sampled answer.

Put both parameters in the payload unconditionally, with `null` as the identity
of an omitted one, and add a version field to the payload. Entries written under
the previous identity are unreachable rather than ambiguous: the cost is one
re-invocation per prompt after upgrade, never a wrong replay.

The key-stability test is replaced by its inverse — a cache entry written under
the pre-upgrade key is not returned to a request that omits sampling.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6UVfFPP39RkoYx5jZ3KKM

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 12:23:12 -07:00
Yiğit ERDOĞANandClaude Opus 5 9769237d36 fix: Lobster cannot create its cache or state directories on Windows (#126)
* fix(state): normalize extended-length mkdir paths

On Windows fs.mkdir(recursive) reports the first created directory as an
extended-length path (\?\C:\...) while the requested directory is a plain
drive path. path.resolve keeps the prefix, so the two never compare equal
and path.relative between them yields an absolute path. The chain walk then
takes "C:" as its next segment and syncs a directory that does not exist,
so ensureDirectory throws ENOENT every time it actually creates something.

That breaks LLM cache writes, state.set, diff snapshots, and approval index
publication on the first Windows run.

Strip the prefix before resolving so both ends of the chain share one root
form. On POSIX the prefixes never occur and the walk is unchanged.

* fix(state): keep device namespaces out of the path normalization

Stripping every \?\ prefix also rewrote namespaces that have no plain
equivalent, so an explicitly configured LOBSTER_STATE_DIR such as
\?\Volume{GUID}\lobster\state became relative and resolved against the
current drive. That regressed a form main handles today.

Map only the drive-letter and UNC namespaces, which do have a plain
equivalent, and return anything else untouched. Cover the mapping directly
so the device-namespace case is pinned.

* fix(state): recognise the UNC namespace whatever its case

Windows compares path namespace components case-insensitively, so
`\?\unc\server\share` names the same share as `\?\UNC\server\share`.
Matching only the uppercase form left the lowercase spelling extended
while the directory it walks toward is plain, so the sync walk could
never reach the requested final path on such a setup.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011gD5sJTbh2jn1uvgNCmkLq

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 12:22:56 -07:00
f14e22d94a fix: workflow run crashes when a step exits before reading its stdin (#122)
* fix: workflow run crashes when a step exits before reading its stdin

A step command that exits during its startup (for example a shell script
failing a preamble check under set -e) before draining a large piped stdin
leaves the engine's pending stdin write to fail with EPIPE. The stdin socket
had no 'error' listener, so Node raised an unhandled 'error' event and the
entire lobster process crashed -- losing the approval gate, the resume token,
and the step's real exit code and stderr.

Ignore stdin write errors in both shell-step and stdlib exec spawns: the
close handler already reports the true failure. Regression test drives a
300KB stdout through a fast-exiting step and asserts the run rejects with
'workflow command failed (1)' instead of crashing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* simplify: condense EPIPE inline comments to essential context

The 5-line comment blocks explaining EPIPE behavior were excessive for
1-line handlers. The test comment describing the 300KB mechanism was
similarly verbose. Trim each to the non-obvious why only.

* simplify: remove EPIPE inline comments

The child.stdin.on('error', () => {}) pattern is self-explanatory to
Node.js developers; the rationale is fully documented in the PR body.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-13 12:22:15 -07:00
Yiğit ERDOĞANandClaude Opus 5 f2438f52ad fix: schema validation failures cost one more model call than configured (#128)
* fix(llm): stop spending an extra call on validation retries

The retry budget was compared against a 1-based attempt counter plus one,
so the initial request never counted against it. Every configured value
bought one more billed adapter call than requested, and
--max-validation-retries 0 still issued a second call after the first
response failed the output schema.

Compare the attempt counter against the retry count directly: attempt
counts calls, max-validation-retries counts retries, so calls = retries+1.
Only the failure bound moves; the success path is unchanged.

* docs(llm): say what --max-validation-retries counts

The flag's own description said only "retries when schema validation fails",
which is exactly the ambiguity this fix resolves: whether the budget includes
the first call. The compatibility question in front of a maintainer is easier to
answer when the option states that it allows N extra calls after the first, and
that its default is 1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Bu6MvbjKGtfRiNesARMyZ

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 10:55:13 -07:00
xingzhouandPeter Steinberger c440ca57d1 fix(runtime): cancelled workflows no longer continue external commands (#119)
* fix(runtime): stop process-backed work on cancellation

* fix(runtime): invalidate cancelled resume state

* Revert "fix(runtime): invalidate cancelled resume state"

This reverts commit 0f7d291f98.

* fix(runtime): narrow cancellation to safe child processes

* fix(runtime): stop after completed search cancellation

* fix(runtime): halt direct pipelines after cancellation

Preserve completed in-flight stage results while preventing later direct pipeline stages from starting after parent cancellation.

* fix(runtime): consume aborted approval resumes

* fix(runtime): cancelled workflows no longer continue external commands

* fix(workflow): propagate custom parent cancellation

* fix(runtime): preserve pre-aborted resume state

* fix(runtime): stop lazy handoff after cancellation

* fix(workflow): close remaining cancellation boundaries

* fix(runtime): preserve workflow resumes during setup cancellation

* fix(runtime): close final cancellation persistence gaps

* fix(runtime): stop lazy handoff after cancellation

* fix(runtime): interrupt blocked lazy handoff reads

* fix(runtime): terminate cancellation process trees

* fix(runtime): await process tree termination

* fix(runtime): terminate workflow process trees

* fix(runtime): bridge CLI cancellation

* fix(cli): preserve cancellation lifecycle

* fix(cli): abort stalled signal-aware commands

* fix(cli): release aborted interactive prompts

* fix(cli): preserve sequential prompt input

* fix(cli): preserve buffered prompt input

* fix(cli): handle prompt EOF after buffered input

* fix(runtime): preserve UTF-8 subprocess output

* fix(workflow): preserve retryable resume before execution

* fix(workflow): roll back cancelled resume replacement

* fix(state): roll back cancelled monitor snapshot

* fix(resume): preserve cancelled gate capabilities

* fix(resume): close cancellation rollback windows

* fix(resume): harden cancellation state cleanup

* fix(runtime): close resumed cancellation gaps

* fix(runtime): preserve cancellation cleanup

* fix(runtime): close cancellation lifecycle gaps

* fix(runtime): harden resumed cancellation boundaries

* fix(workflow): consume timed-out resume capabilities

* fix(workflow): preserve resume policy boundaries

* fix(runtime): preserve cancellation cleanup liveness

* fix(runtime): harden cancellation cleanup

* fix(runtime): stop lazy output after cancellation

* fix(runtime): settle cancellation cleanup

* fix(runtime): preserve safe input resumes

* fix(runtime): prevent consumed resume replays

* fix(runtime): serialize approval resume consumption

* fix(runtime): prevent concurrent safe gate forks

* fix(runtime): close cancellation review gaps

* fix(llm): restore cache after cancelled refresh

* fix(runtime): close remaining cancellation windows

* fix(runtime): preserve cancellation recovery invariants

* fix(runtime): prevent stale resume recovery

* fix(runtime): preserve resume claim recovery

* fix(runtime): retry pre-dispatch claims safely

* fix(state): synchronize rollback-safe reads

* fix(resume): discard cancelled pipeline successors

* fix(runtime): harden cancellation and state locking

* fix(runtime): recover durable cancellation failures

* fix(runtime): preserve legacy workflow cancellation

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-13 10:54:20 -07:00
Vincent Koc 0ac962e90b fix(deps): override vulnerable fast-uri (#123) 2026-07-28 15:13:11 +08:00
Peter Steinberger 386a201ced ci: add ClawSweeper dispatch workflow 2026-07-26 18:53:47 -07:00
Hannes Rudolph c2ff7d0ace chore: align pull request template (#120) 2026-07-13 12:42:54 -06:00
Vincent Koc affca75be8 chore(repo): enforce pnpm runtime policy 2026-07-11 13:15:14 +08:00
Peter Steinberger 42b09e2077 fix(ci): pin TypeScript age exemptions 2026-07-09 15:03:09 +01:00
Peter Steinberger 1dfedd5ae8 fix(ci): enable TypeScript age exemptions 2026-07-09 14:56:20 +01:00
Peter Steinberger e77e279a01 fix(ci): match TypeScript age exemptions 2026-07-09 14:51:20 +01:00
Peter Steinberger 378f30beba chore(deps): adopt TypeScript 7 and update dependencies 2026-07-09 14:25:36 +01:00
Vincent Koc d9a0fe7e02 style: format lobster sources 2026-06-22 14:26:35 +08:00
Vincent Koc 7a57f25720 fix: harden lobster release and invoke paths 2026-06-22 13:53:21 +08:00
Peter Steinberger d759d5c3ba feat: add OpenClaw agent invocation (#118) 2026-06-18 08:01:23 +02:00
Peter Steinberger 7d3b6a1512 chore: reopen changelog after release 2026-06-11 02:58:26 +01:00
Peter Steinberger 86b8cc20a8 chore(release): 2026.6.11 v2026.6.11 2026-06-11 02:36:52 +01:00
Peter Steinberger 77b0a55692 feat: add command-level input requests
Adds state-backed command-level requestInput resume across CLI/tool/SDK pipelines and workflow pipeline steps. Closes #101.
2026-06-10 17:50:51 -07:00
Peter Steinberger ff0791b78b fix: warn on unknown LLM cost models (#115) 2026-06-10 02:59:02 -07:00
Andy Ye 69dcc88cad fix: harden disposable persistence writes
Harden LLM cache files, diff snapshots, and approval ID indexes against truncated or malformed JSON after process termination.

Disposable cache and snapshot corruption now recovers as a miss; authoritative resume state still surfaces malformed JSON. Approval short-ID indexes use atomic no-overwrite publication and degrade to full resume-token approval when the index cannot be published durably.

Closes #111.
Closes #112.
Closes #113.

Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
2026-06-10 00:36:09 -07:00
Peter Steinberger bd007370f9 chore(deps): update development tooling 2026-06-10 04:48:39 +01:00
Peter Steinberger 315e550f3d chore: update dependencies 2026-06-08 21:43:07 +01:00
Peter Steinberger d04ae974cf chore: require Node.js 22 2026-06-08 20:00:46 +01:00
Krasimir KralevandKrasimir Kralev 1679bed1a4 fix(state): write state atomically
Fixes #108 and #109.

- Replace direct state writes with same-directory atomic temp-file writes.
- Preserve existing file modes and create new state files as 0600.
- Clean up temp files on failed replacement paths.
- Add state/SDK regression coverage plus changelog entry.

Proof:
- pnpm run typecheck
- node --test dist/test/state.test.js dist/test/resume.test.js dist/test/multi_approval_resume.test.js dist/test/approve_preview.test.js
- pnpm run lint
- pnpm run test
- built SDK write/read proof preserved 0600 across replacement
- autoreview clean: no accepted/actionable findings

Co-authored-by: Krasimir Kralev <krasi@idrobots.com>
2026-06-03 14:05:28 -07:00
Peter Steinberger f0b63a4e54 docs: position README banner 2026-05-28 20:47:50 +01:00
Peter Steinberger 2c9ba7d604 docs: add README banner 2026-05-28 19:43:06 +01:00
930930a02c fix: retry timed-out workflow steps
* fix(retry): only propagate AbortError on external cancellation, not per-attempt timeout

Fixes #105.

withRetry unconditionally re-threw AbortErrors before calling shouldRetry,
causing timeout_ms + retry.max combinations to always result in a single
attempt regardless of retry configuration. Fix: check options?.signal?.aborted
before short-circuiting — external workflow cancellation still propagates
immediately, but per-attempt timeout AbortErrors now flow through shouldRetry.

* test(retry): update abort-error test to use aborted external signal; add timeout-retry unit test

Update withRetry test to properly simulate external cancellation (aborted
signal) rather than a bare AbortError without signal context.

Add unit test proving per-attempt timeout AbortErrors (no external signal)
are now retried as documented when timeout_ms + retry are combined.

* fix(retry): revert quote style to single-quote (match fork base)

* fix(retry): revert test quote style to single-quote (match fork base)

* test: add workflow-level proof that timeout_ms + retry retries on timeout

Integration test that runs a real step with timeout_ms=1500 + retry.max=3
where the command hangs past the timeout on attempts 1-2 (SIGKILLed) then
succeeds on attempt 3. Asserts status ok + attempt 3 + [RETRY] logs.

Verified the test fails against the pre-fix withRetry (short-circuit on
any AbortError) and passes with the fix. Addresses the review request for
real behavior proof at the workflow level, complementing the existing
withRetry unit tests.

* docs: credit timeout retry fix

* test: harden timeout retry workflow proof

* style: format timeout retry patch

---------

Co-authored-by: KrasimirKralev <krasi@idrobots.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-05-28 16:35:04 +01:00
Vincent Koc c1649457ce chore: add constrained Crabbox setup
Adds constrained Crabbox setup and the exact OpenClaw Crabbox skill for maintainer validation.
2026-05-23 05:59:21 +08:00
Peter Steinberger 66cbee5896 chore: reopen changelog after release 2026-05-22 13:15:49 +01:00
Peter Steinberger 57a9967d13 chore(release): 2026.5.22 v2026.5.22 2026-05-22 13:10:10 +01:00
Krasimir Kralev 042e833e62 fix(validation): memoize Ajv compile cache (#98)
Memoize repeated Ajv schema compilation across validation paths and add regression coverage for structurally equivalent schema cache hits.

Closes #96.

Co-authored-by: Krasimir Kralev <263465593+KrasimirKralev@users.noreply.github.com>
2026-05-22 13:09:07 +01:00
Peter Steinberger bddb44c0bb docs: add README emoji tagline 2026-05-15 20:24:20 +01:00
Peter Steinberger bec7fb81b4 ci: skip duplicate npm publishes 2026-05-15 05:17:37 +01:00
Peter Steinberger e4514ecad5 style: format with oxfmt 2026-05-04 01:56:15 +01:00
Vincent Koc 0af9ff116f fix(deps): update vulnerable parser dependencies 2026-04-30 14:58:13 -07:00
Vignesh Natarajan a95f133c2e fix(workflows): support legacy resume key aliases cleanly (#4) (thanks @brownetw-ai) 2026-04-11 16:42:58 -07:00
Bruce 14e46b388c Fix resume state lookup for workflow key variants 2026-04-11 16:42:58 -07:00
Vignesh Natarajan 595f862d73 docs: document workflow retry feature (#84) (thanks @scottgl9) 2026-04-11 16:40:17 -07:00
scottgl 7d6ecec694 feat: step-level retry with configurable backoff
Adds retry field to workflow steps with exponential/fixed backoff,
jitter, and configurable max attempts. Retry delays are signal-aware
(abort cancels immediately). Abort errors never trigger retries.

- New src/core/retry.ts: withRetry utility with backoff calculation
- Step execution wrapped in retry loop when retry.max > 1
- Retry attempts logged to stderr as [RETRY] messages
- Dry-run renders retry config (attempts, backoff, base delay)
- Comprehensive validation for all retry fields
2026-04-11 16:40:17 -07:00
Vignesh Natarajan 04e01b5027 feat: add approval identity constraints for workflow gates 2026-04-11 16:07:27 -07:00
Vignesh Natarajan 6d397c51c1 test/docs: clarify llm_task workflow stdin usage 2026-04-11 16:02:30 -07:00
Vignesh Natarajan 7d6a22a0c3 feat: add workflow graph visualization command 2026-04-11 15:54:17 -07:00
Vignesh Natarajan 60c976571a feat: add template filters for template command 2026-04-11 15:44:57 -07:00
Vignesh Natarajan 933fb49f6f feat: add for_each workflow step type 2026-04-11 15:43:40 -07:00
Vignesh Natarajan 503a2f6053 feat: add parallel workflow step execution 2026-04-11 15:37:35 -07:00
Vignesh Natarajan 373c447e39 feat: add llm cost tracking and spending limits 2026-04-11 15:35:00 -07:00
Vignesh Natarajan 05e34d741f feat: add comparison operators for workflow conditions 2026-04-11 15:33:39 -07:00
Vignesh Natarajan 425198e09d test: add on_error workflow coverage 2026-04-11 15:32:52 -07:00
Vignesh Natarajan 47eb7e81b3 feat: add workflow composition and step timeout controls 2026-04-11 15:31:54 -07:00