40 Commits

Author SHA1 Message Date
zeroaltitude
e034064ee8
fix: add explicit crypto import, tighten privacy warning to sessionContent check
- Import crypto from 'node:crypto' for consistency with codebase
  conventions (every other file uses explicit import, not global)
- Tighten late-block privacy warning to only fire when sessionContent
  was actually loaded (non-null) — prevents misleading warning when
  no transcript was ever read from disk or sent to LLM
- Add matching crypto import in test file so vi.spyOn mocks the
  correct module reference
2026-03-18 14:50:29 -07:00
zeroaltitude
63a1b1d05e
fix: use crypto.randomUUID for slug suffix, use blockPreSet variable
- Replace Math.random().toString(36) with crypto.randomUUID() for
  uniformly-distributed 4-char hex suffix — eliminates weak entropy
  from floating-point base-36 conversion edge cases
- Use already-captured blockPreSet variable instead of re-reading
  context.blockSessionSave for consistency and clarity
- Update test to mock crypto.randomUUID instead of Math.random

Addresses greptile review: weak entropy risk + redundant context read.
2026-03-18 14:50:26 -07:00
zeroaltitude
65e0158bf8
fix: rethrow in test drain helper, fix slug comment, clarify drain JSDoc
- drainActions test helper now rethrows errors so test failures surface
  the actual error instead of a confusing downstream assertion
- Fix misleading comment: slug collision is via fallback timestamp+random
  path (LLM slug generation disabled in VITEST=true), not LLM mock
- Clarify drainPostHookActions JSDoc: re-drain is only a no-op when no
  actions push new callbacks during execution; mid-drain pushes persist

Addresses greptile review feedback for confidence score improvement.
2026-03-18 14:50:25 -07:00
zeroaltitude
f0958de191
fix: add diagnostic for cleared sessionSaveContent, export drainPostHookActions
- Add log.debug when a post-hook clears pre-set sessionSaveContent, making
  the silent no-op visible to plugin authors (symmetric with blockSessionSave
  cleared warning)
- Extract drainPostHookActions as an exported utility from internal-hooks.ts
  so handler tests share the exact production drain semantics instead of
  maintaining a divergent copy
- Update handler.test.ts to import and use the shared drain utility

Addresses greptile review feedback for confidence score improvement.
2026-03-18 14:50:25 -07:00
zeroaltitude
45ec7ec672
fix: use vi.spyOn for Math.random, simplify postHookActions null-guards
- Replace direct Math.random mutation with vi.spyOn(Math, 'random').mockReturnValue(0.5)
  for idiomatic Vitest cleanup integration
- Fix comment: collision is driven by identical LLM slug, not timestamp fallback;
  Math.random pin is a backstop for null sessionContent edge case
- Remove unnecessary nullish-coalescing and conditional guard on postHookActions
  (field is required in interface and always initialized by createInternalHookEvent)

Addresses greptile review feedback for confidence score improvement.
2026-03-18 14:50:25 -07:00
zeroaltitude
8d7bd9e461
fix(hooks): pin timestamp in collision test, avoid dead entry construction, reuse blockPreSet
1. Slug-collision test: Pin event.timestamp to a fixed Date in addition
   to Math.random, preventing flaky behavior when wall-clock crosses a
   second boundary between event1 and event2.

2. handler.ts: Skip entry construction when blockPreSet is true (the
   value is discarded — writtenEntry will be null regardless). Avoids
   misleading dead code.

3. handler.ts: Reuse blockPreSet in the inline-write guard instead of
   re-evaluating context.blockSessionSave === true. Single source of
   truth for the pre-set block condition.
2026-03-18 14:50:24 -07:00
zeroaltitude
8381771827
fix(hooks): make postHookActions drain idempotent, clarify block-then-clear warning
1. Clear postHookActions array after draining (both production
   triggerInternalHook and test helper). Re-triggering the same event
   is now a safe no-op instead of re-executing every action.

2. Expand warning log in block-then-clear scenario to explain that
   transcript was skipped because sessionSaveContent OR blockSessionSave
   was pre-set, and that plugin authors must provide sessionSaveContent
   when clearing blockSessionSave.

3. Add test: 'clears postHookActions after drain — re-trigger is a no-op'
   verifying the idempotency contract.
2026-03-18 14:50:24 -07:00
zeroaltitude
c643651199
test: exercise slug-collision restoration path with deterministic Math.random
The previous test couldn't trigger a slug collision because the random
suffix made same-filename generation virtually impossible. Pin Math.random
to a fixed value so both handler calls produce the same fallback slug,
exercising the preExistingContent !== null restoration branch.

Verifies: second handler overwrites first file → late blockSessionSave
retracts second write → first session's content is restored (not deleted).
2026-03-18 14:50:23 -07:00
zeroaltitude
c032cfbec5
fix(test): snapshot postHookActions array in test helper to match production drain
The test drainPostHookActions helper iterated the live array, but
triggerInternalHook snapshots with [...(event.postHookActions ?? [])]
before draining. Align test helper to match production semantics.
2026-03-18 14:50:22 -07:00
zeroaltitude
25093b7024
fix: warn when blockSessionSave is cleared without sessionSaveContent
When blockSessionSave is pre-set, the handler skips transcript loading
and LLM slug generation (avoiding unnecessary I/O and model calls).
If a later hook clears blockSessionSave without setting sessionSaveContent,
no file can be produced because the transcript was never loaded.

Previously this was a silent no-op. Now emits log.warn so plugin authors
know to supply sessionSaveContent when un-blocking a pre-set block.

Adds test: 'blockSessionSave pre-set then cleared without sessionSaveContent
warns and writes nothing' — verifies the edge case produces no file.
2026-03-18 14:50:21 -07:00
zeroaltitude
2509356e08
fix(session-memory): restore pre-existing file on late-block retraction instead of deleting
When a later hook sets blockSessionSave=true, the retraction previously
unconditionally deleted memoryFilePath. If the filename collided with a
pre-existing memory file (e.g. same LLM slug on the same day), the prior
session's content was erased.

Now snapshots any pre-existing file content before the inline write and
restores it on retraction instead of deleting. Non-colliding writes still
get deleted as before.
2026-03-18 14:50:21 -07:00
zeroaltitude
edcc079583
cleanup: address greptile review nits for 5/5
- Remove redundant length guard on postHookActions drain loop
- Add per-action error isolation to drainPostHookActions test helper,
  matching actual triggerInternalHook behaviour
- Remove double-catch in session-memory post-hook callback; let errors
  propagate to the framework's per-action catch for consistent logging
2026-03-18 14:50:07 -07:00
zeroaltitude
4040a25bb1
fix(session-memory): ensure memoryDir exists in post-hook write path
When blockSessionSave is true initially, the inline write is skipped —
including the fs.mkdir that creates memoryDir. If a later hook clears
blockSessionSave and sets sessionSaveContent, the post-hook
writeFileWithinRoot call would fail with ENOENT because the directory
was never created. The error was silently swallowed, causing the content
override to be lost.

Add fs.mkdir(memoryDir, { recursive: true }) before the post-hook write.
Add regression test for the block-then-clear-with-content scenario.
2026-03-18 14:50:06 -07:00
zeroaltitude
0621de4773
fix(session-memory): blockSessionSave takes precedence over sessionSaveContent
When both flags are set, blockSessionSave must win — a blocked save
should never create a file, even if sessionSaveContent is also present.

Bug: if blockSessionSave was pre-set (writtenEntry=null), the post-hook
sessionSaveContent check would pass and create a new file, violating
the block intent.

Fix: guard the sessionSaveContent overwrite with blockSessionSave !== true.

Tests: 2 new tests covering both-flags-pre-set and both-flags-late-set.
Credit: Greptile review.
2026-03-18 14:50:06 -07:00
zeroaltitude
1ea1aa75df
feat(hooks): add postHookActions for order-independent hook coordination
Adds a generic postHookActions mechanism to InternalHookEvent:
handlers push deferred callbacks that triggerInternalHook drains
after all handlers complete. This eliminates FIFO registration-order
dependencies — any hook can set context flags regardless of when it
registered.

Session-memory handler updated to use fail-safe pattern:
- Writes file inline (preserves data if postHookActions never runs)
- Pushes post-hook action for retraction (blockSessionSave) and
  content replacement (sessionSaveContent)
- Pre-set flags still work inline; late-set flags work via post-hook

New tests:
- 5 tests for postHookActions mechanism (ordering, error isolation,
  late-context visibility)
- 2 tests for late-set blockSessionSave/sessionSaveContent
- 1 fail-safe test (file preserved when postHookActions not drained)
2026-03-18 14:50:05 -07:00
zeroaltitude
7a81cc94d3
fix(session-memory): add debug log for custom content, tighten test assertion
- Add log.debug when sessionSaveContent override is used (symmetric with blockSessionSave)
- Change override test assertion from toContain to toBe for precision
2026-03-18 14:50:03 -07:00
zeroaltitude
65fae19fc8
feat(hooks): add blockSessionSave and sessionSaveContent to session memory handler
Two new context fields for upstream hooks (e.g. security plugins) to
control session memory persistence:

- blockSessionSave (boolean): prevent session from being saved to memory
- sessionSaveContent (string): override saved content with custom text
  (empty string is valid — persists a blank marker without transcript)

When sessionSaveContent is set, LLM slug generation and session content
loading are skipped (unnecessary when content is overridden).

Split from #35567 — sessionSaveRedirectPath follows separately as it
requires path canonicalization, symlink resolution, and filesystem
write policy review.
2026-03-18 14:50:03 -07:00
Hermione
c942655451
fix(hooks): use resolveAgentIdFromSessionKey in runBeforeReset (#39875)
Merged via squash.

Prepared head SHA: 00a2b241df1cf4fa7089d6ee98b5d7e2c7acf105
Co-authored-by: rbutera <6047293+rbutera@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
2026-03-08 19:07:28 +03:00
Peter Steinberger
2287d1ec13 test: micro-optimize slow suites and CLI command setup 2026-03-02 23:00:49 +00:00
Peter Steinberger
d116bcfb14 refactor(runtime): consolidate followup, gateway, and provider dedupe paths 2026-02-22 14:08:51 +00:00
Peter Steinberger
185fba1d22 refactor(agents): dedupe plugin hooks and test helpers 2026-02-22 07:44:57 +00:00
Vignesh Natarajan
d583399c92 Hooks: persist session memory on /reset 2026-02-20 20:19:29 -08:00
Peter Steinberger
1e2b367e1e test(hooks): dedupe session-memory handler test setup 2026-02-18 12:28:30 +00:00
Peter Steinberger
262472ba20 test: remove duplicated scenario scaffolding across runtime tests 2026-02-18 04:04:14 +00:00
Peter Steinberger
b8b43175c5 style: align formatting with oxfmt 0.33 2026-02-18 01:34:35 +00:00
Peter Steinberger
31f9be126c style: run oxfmt and fix gate failures 2026-02-18 01:29:02 +00:00
cpojer
238718c1d8
chore: Fix types in tests 37/N. 2026-02-17 15:50:07 +09:00
cpojer
d0cb8c19b2
chore: wtf. 2026-02-17 13:36:48 +09:00
Sebastian
ed11e93cf2 chore(format) 2026-02-16 23:20:16 -05:00
cpojer
01ea808876
chore: Format files. 2026-02-17 10:57:31 +09:00
Sebastian
3fff266d52 fix(session-memory): harden reset transcript recovery 2026-02-16 20:39:06 -05:00
cpojer
90ef2d6bdf
chore: Update formatting. 2026-02-17 09:18:40 +09:00
Tomas Hajek
19ae7a4e17 fix(session-memory): fallback to rotated transcript after /new
When /new rotates <session>.jsonl to <session>.jsonl.reset.*, the session-memory hook may read an empty active transcript and write header-only memory entries.

Add fallback logic to read the latest .jsonl.reset.* sibling when the primary file has no usable content.

Also add a unit test covering the rotated transcript path.

Fixes #18088
Refs #17563
2026-02-16 23:49:41 +01:00
Peter Steinberger
beffb6fe48 refactor(test): dedupe session-memory hook setup 2026-02-15 15:09:26 +00:00
Peter Steinberger
85409e401b fix: preserve inter-session input provenance (thanks @anbecker) 2026-02-13 02:02:01 +01:00
Peter Steinberger
4ba9809f18 test(hooks): stabilize session-memory hook tests 2026-02-07 00:22:34 -08:00
cpojer
f06dd8df06
chore: Enable "experimentalSortImports" in Oxfmt and reformat all imorts. 2026-02-01 10:03:47 +09:00
cpojer
15792b153f
chore: Enable more lint rules, disable some that trigger a lot. Will clean up later. 2026-01-31 16:04:04 +09:00
Peter Steinberger
9a7160786a refactor: rename to openclaw 2026-01-30 03:16:21 +01:00
Roopak Nijhara
39b7f9d581 feat(hooks): make session-memory message count configurable (#2681)
Adds `messages` config option to session-memory hook (default: 15).
Fixes filter order bug - now filters user/assistant messages first,
then slices to get exactly N messages. Previously sliced first which
could result in fewer messages when non-message entries were present.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 16:42:39 +05:30