20397 Commits

Author SHA1 Message Date
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
abd7c877aa
fix: use padEnd for truly guaranteed 4-char random slug suffix
Previous approach ((random + '0000').slice(2,6)) still produced 3 chars
when Math.random() === 0 ('0'.toString(36) = '0', '00000'.slice(2,6) = '000').
padEnd(4, '0') guarantees exactly 4 characters for all inputs.
2026-03-18 14:50:23 -07:00
zeroaltitude
419249fabb
fix: pad random slug suffix to guarantee 4 characters
Math.random().toString(36).slice(2,6) can produce fewer than 4 chars
for short base-36 representations (e.g. 0.5 → '0.i' → 'i').
Append '0000' before slicing to guarantee the suffix is always 4 chars.
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
a0073bfb9b
fix: address remaining greptile 4/5 items — random guard, ENOENT comment, array snapshot
1. Math.random() === 0 guard: (0).toString(36).slice(2,6) returns ''
   producing a trailing-hyphen slug. Added || '0000' fallback.

2. Misleading ENOENT comment: said 'when blockSessionSave was set before
   writeFileWithinRoot' but that branch requires writtenEntry !== null
   (blockSessionSave was false). Real scenario: external file deletion
   between inline write and post-hook drain. Comment corrected.

3. Live-array drain: for...of on postHookActions is a live iterator —
   a self-scheduling action could loop infinitely. Snapshot the array
   with [...(event.postHookActions ?? [])] before draining.
2026-03-18 14:50:22 -07:00
zeroaltitude
c6110a3b15
fix: separate ENOENT handling for unlink vs writeFileWithinRoot in retraction
Split the single catch block into two distinct error-handling paths:

1. Pre-existing content restore (writeFileWithinRoot): errors are NOT
   swallowed — ENOENT here means memoryDir was removed after our inline
   write, which is a real filesystem inconsistency that must surface.

2. Unlink (no pre-existing content): ENOENT is expected when the inline
   write didn't happen. Non-ENOENT errors (EACCES, EROFS) are re-thrown
   for triggerInternalHook to log. Added comment clarifying that the
   retraction guarantee is best-effort under adversarial filesystem
   conditions (per-action isolation absorbs the error).
2026-03-18 14:50:21 -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
40a6a3ce76
fix(session-memory): use displaySessionKey instead of event.sessionKey in entry
The resolveDisplaySessionKey function correctly resolved workspace-based
agent IDs (e.g. agent:main:main → agent:navi:main when workspaceDir
matches the navi agent), but the entry template used event.sessionKey
directly, bypassing the resolution.
2026-03-18 14:50:20 -07:00
zeroaltitude
5a135d855c
docs: document known limitation for cleared sessionSaveContent
When an earlier hook pre-sets sessionSaveContent and a later hook clears
it, the transcript is not available for fallback — it was never loaded.
Hooks wanting to override should set their own content, not clear it.
2026-03-18 14:50:09 -07:00
zeroaltitude
71609fa108
ci: retrigger CI (oxfmt check may be stale) 2026-03-18 14:50:09 -07:00
zeroaltitude
ff014f96e4
fix: add postHookActions to boot-md test fixture
The InternalHookEvent interface now requires postHookActions (added by
this PR). The boot-md test's makeEvent helper was missing it, causing
a tsc error that fails CI.
2026-03-18 14:50:08 -07:00
zeroaltitude
1e11385cbd
fix: add random suffix to fallback slug to prevent same-second collisions
Second-resolution (HHMMSS) fallback slugs can collide when automated or
multi-channel setups emit rapid /new or /reset commands within the same
second — both writes target the same filename and the later one silently
overwrites the earlier memory entry.

Append a 4-char random alphanumeric suffix (e.g. 103022-x7f2) to make
collisions effectively impossible without LLM slug generation.
2026-03-18 14:50:08 -07:00
zeroaltitude
decdddbe3e
security: disable tools for LLM slug generation
The slug generator embeds up to 2000 chars of raw conversation content
in its prompt.  Without disableTools, the embedded agent inherits the
full tool set (exec, file write, messaging), meaning a crafted
conversation could prompt-inject the slug call into executing arbitrary
side-effects before the slug text is extracted.

Slug generation is pure text — it never needs tool access.  Add
disableTools: true to close this injection surface.
2026-03-18 14:50:08 -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
51fdf17867
fix: drain postHookActions even with no registered handlers
Remove early return in triggerInternalHook that skipped the post-hook
drain when allHandlers was empty. This was a latent footgun: any
postHookActions pre-populated on the event before calling
triggerInternalHook would be silently dropped if no handlers were
registered for that event type.

Update test to verify post-hooks now drain in the no-handlers case.
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
64d52cf2ea
fix(session-memory): skip transcript loading and LLM call when blockSessionSave is pre-set
When blockSessionSave is already true, the handler was still loading
session content and potentially calling generateSlugViaLLM — sending
sensitive transcript text to a model provider despite an explicit
block. Short-circuit before transcript loading when the flag is set.

Credit: Codex review.
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
6b998ff746
fix(session-memory): use HHMMSS slug to prevent same-minute overwrites
Custom content and no-session paths fell through to HHMM (4-char)
timestamp slug. Two /new events in the same minute would overwrite.
HHMMSS makes collisions require same-second timing.
2026-03-18 14:50:05 -07:00
zeroaltitude
423dce7ccd
docs(session-memory): clarify hook ordering limitation for blockSessionSave
Bundled hooks register before managed/workspace hooks in FIFO order,
so blockSessionSave only works when set by typed plugin hooks (which
fire earlier in the lifecycle) or extraDirs hooks. Document this
limitation honestly rather than claiming incorrect ordering.
2026-03-18 14:50:04 -07:00
zeroaltitude
1010fb9002
fix(session-memory): remove duplicate comment, skip entryParts when custom content, clarify hook ordering
- Remove copy-paste duplicate 'Use custom content...' comment
- Move entryParts construction inside else branch (skip when hasCustomContent)
- Add comment explaining FIFO hook ordering for blockSessionSave consumers
2026-03-18 14:50:04 -07:00
zeroaltitude
9400e537f0
fix(session-memory): consolidate hasCustomContent / typeof redundancy
Use the hasCustomContent boolean (already computed for gating session
loading) instead of re-checking typeof at the write site.
2026-03-18 14:50:04 -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
Vincent Koc
91d37ccfc3 fix(auth): lazy-load provider oauth helpers 2026-03-18 13:40:28 -07:00
Vincent Koc
6ebcd853be fix(plugin-sdk): isolate provider entry surfaces 2026-03-18 13:20:46 -07:00
Vincent Koc
b526098eb2 docs: restore original Credits heading, disambiguate H1 2026-03-18 12:38:46 -07:00
Vincent Koc
c749957c93 docs: fix duplicate Credits heading in credits.md 2026-03-18 12:34:37 -07:00
Vincent Koc
e5a1185796 docs: add extensions section to docs hubs 2026-03-18 12:29:02 -07:00
Vincent Koc
be3f4a7966 docs: add Building Extensions guide and nav entry 2026-03-18 12:28:19 -07:00
Vincent Koc
198de10523 docs: add missing H1 headings and fix HEARTBEAT template 2026-03-18 12:27:07 -07:00
Vincent Koc
63e09f8267 chore(changelog): remove fragment workflow drift 2026-03-18 12:26:56 -07:00
Vincent Koc
2797ae1583 docs: add missing voice-call CLI commands and contract test section to testing 2026-03-18 12:26:18 -07:00
Vincent Koc
cc5bd57bd7 docs: add missing provider pages (google, modelstudio, perplexity, volcengine) and nav entries 2026-03-18 12:26:01 -07:00
Vincent Koc
e9903c9133 Tests: align unit sharding with unit config 2026-03-18 12:16:07 -07:00
Josh Avant
e6911f0448
Tests: restore deterministic plugins CLI coverage (#49955)
* Tests: restore deterministic plugins CLI coverage

* CLI: preserve plugins exit control-flow narrowing

* Tests: fix plugins CLI mock typing for tsgo

* Tests: fix provider usage mock typing in key normalization
2026-03-18 14:05:04 -05:00
Vincent Koc
ef1346e503 Plugin SDK: route reply payload through public subpath 2026-03-18 12:01:15 -07:00
Vincent Koc
ecfa79ee4c Tests: fix provider auth plugin mock spread 2026-03-18 12:01:05 -07:00
Tak Hoffman
600f57c979
test: add architecture smell detector 2026-03-18 13:28:13 -05:00
darkamenosa
4b5487ee85
LINE: avoid runtime lookup during onboarding (#49960) 2026-03-19 01:27:21 +07:00
Onur
8f0727d75c
Delete CNAME 2026-03-18 19:22:17 +01:00
Peter Steinberger
1746e130f9 test: fix imessage extension CI mocks 2026-03-18 18:20:04 +00:00
Peter Steinberger
a0d3dc94d0 perf: reduce unit test hot path overhead 2026-03-18 18:19:40 +00:00