Commit Graph
9778 Commits
Author SHA1 Message Date
roboomp 184f6dd809 style: bun run fix 2026-06-25 11:41:56 +00:00
roboomp 8506fbdf52 fix(coding-agent): switched ctrl-z handler to SIGSTOP-self to defeat brush tokio SIGTSTP hijack
brush-core's Process::wait calls tokio::signal::unix::signal(SIGTSTP) to
detect when its children get stopped. Per tokio's documented contract,
the first call for a SignalKind permanently replaces the kernel-default
handler for the lifetime of the process. So once omp has executed any
bash tool call — even /usr/bin/true — SIGTSTP's default "stop" action
is gone, and InputController.handleCtrlZ's process.kill(0, "SIGTSTP")
gets swallowed by tokio. The TUI tore down via ui.stop() but the process
kept running in Sl+ state, leaving the user with a dead terminal that
only kill -9 could recover.

Send SIGSTOP to our own PID instead. SIGSTOP can't be caught, blocked,
or ignored — it stops the process at the kernel regardless of installed
handlers. Targeting self (not pgid=0) also leaves long-lived children
(MCP stdio servers, the persistent brush native shell) running across
the suspend, so they no longer freeze mid-IPC during a quick fg/bg
detour.

Fixes #3461
2026-06-25 11:33:35 +00:00
can1357 451af61280 chore: bump version to 16.1.19 2026-06-25 13:14:44 +02:00
can1357 c46216742b feat(hashline): implemented tag-based patch path recovery
- Automatically rebind edits to the correct file when an authored path does not exist but the filename and snapshot tag uniquely match a file read earlier in the session.
- Prevent path recovery for paths that would escalate write privileges, ensuring compatibility with read-only internal URL targets.
- Surface warning messages to the model and user upon successful path recovery to encourage correct future path usage.
2026-06-25 13:14:05 +02:00
can1357 57e9848c8c refactor(coding-agent): removed automatic file attachment for non-image paste paths
- Removed `onPasteFilePath` handler to prevent automatic background file attachment when pasting paths.
- Updated `CustomEditor` to treat non-image paths as literal text input.
- Cleaned up unused file system utilities and paste path resolution logic.
2026-06-25 12:57:54 +02:00
can1357 c03a78917a test(coding-agent): disabled fsmonitor during test repo initialization
- Added core.fsmonitor configuration to the git initialization process in test setup to prevent potential environment-specific conflicts.
2026-06-25 12:55:01 +02:00
can1357 a13c8d2698 fix(utils): prevented winston warnings when transports are disabled
- Set logger to silent mode when no transports are active to avoid "no transports" errors during log emission.
- Added a regression test to verify that disabling all transports suppresses warnings and that log output resumes after re-enabling transports.
2026-06-25 12:46:49 +02:00
can1357 2c80863e55 fix(utils): silenced logger when no transports are configured
- Added a check for empty transport arrays during logger initialization.
- Set the logger to silent mode if no transports exist to prevent unnecessary warning messages on every log emit.
2026-06-25 12:45:02 +02:00
can1357 5b439dfe55 test(coding-agent): updated model selection test settings
- Enabled anthropic models in the isolated settings for the session creation test to ensure correct configuration.
2026-06-25 12:44:49 +02:00
can1357 abdfcf8ac7 Merge remote-tracking branch 'origin/farm/3b99853a/fix-auto-learn-stop-cache' 2026-06-25 12:44:37 +02:00
can1357 1ec407b255 Merge remote-tracking branch 'origin/farm/398962fe/sanitize-empty-tool-calls' 2026-06-25 12:44:34 +02:00
can1357 3c0a908733 Merge remote-tracking branch 'origin/farm/30e0ba62/local-binary-attachments' 2026-06-25 12:19:34 +02:00
can1357 e47a1a8449 Merge remote-tracking branch 'origin/farm/c26b81e0/fix-bundled-pi-ai-subpath-resolution' 2026-06-25 12:19:25 +02:00
can1357 05a4a3401e Merge remote-tracking branch 'origin/farm/e1e7fefb/codex-input-image-lite-bypass' 2026-06-25 12:19:21 +02:00
roboomp c35319f99a fix(ai): clear malformed tool-call queues at turn boundaries
Review feedback on #3459: the per-id FIFO queue still carried a stale
malformed occurrence when its rejection toolResult never arrived before
conversation history moved on. If the same id was reused later by a
valid call, that valid call's real result shifted the stale `true` and
was dropped, forcing `transformMessages` to synthesize `No result
provided` despite real output existing.

Scope malformed-result pairing to a single assistant-to-tool-result
window: clear queues at assistant/user/developer/other non-result
boundaries, push current assistant occurrences, and pop only contiguous
matching tool results. Missing results remain handled by the existing
transform passes instead of poisoning later duplicate-id calls.

Adds a regression test for malformed-without-result followed by valid
reuse of the same id.
2026-06-25 09:54:19 +00:00
roboomp 8d0667d400 fix(ai): match malformed tool calls to results positionally by occurrence
Review feedback on #3459: the set-based `malformedIds` drop killed
EVERY `toolResult` for the malformed id, including ones tied to a
separate VALID call replaying the same id. OpenAI-Responses composite
ids (`callId|itemId`) collapse on the wire to the same `callId`, and
`deduplicateToolCallIds` already supports that history shape — so a
sibling occurrence being malformed regressed the surviving valid
duplicate from "real result reaches the wire" to "synthetic
`No result provided` from the second pass".

Switch to a per-id FIFO queue: each `toolCall` occurrence pushes its
malformed-ness onto its id's queue in document order; each `toolResult`
pops the first entry for its id and drops only when the popped entry
was malformed. Other results — including the surviving valid
duplicate's real output — are left alone.

Adds a regression test covering shared id across two assistant turns
where one is malformed.
2026-06-25 09:46:00 +00:00
roboomp 7d8f1ed027 fix(ai): drop malformed (empty-name) tool calls in transformMessages
Models occasionally emit `{ "name": "", "arguments": "{}" }` (observed:
GLM-5.2 + thinking on long turns). The agent loop rejected the call at
execution time with `Tool  not found`, but the malformed block plus its
error `toolResult` stayed in `currentContext.messages`, so every
subsequent request replayed them and 400'd on `tool_use.name` /
`tool_calls[i].function.name` validation. Sessions wedged in an
unrecoverable 400 loop until manual `/clear`.

`transformMessages` is the canonical sanitize boundary every provider
funnels through (`amazon-bedrock`, `anthropic`, `google-shared`,
`ollama`, `openai-codex-responses`, `openai-completions`,
`openai-shared`), so the defensive filter lives there. It drops
`toolCall` blocks with empty/whitespace `name`, drops matched
`toolResult` messages by `toolCallId`, and drops the assistant turn
when nothing replayable is left. Idempotent and provider-agnostic; one
round-trip after the fix lands self-heals an already-poisoned session.

Fixes #3458
2026-06-25 09:39:49 +00:00
roboomp f9cb009324 fix(ai): kept agent-owned developer reminders on system
Route mid-conversation developer messages by attribution: only user-attributed entries (auto-learn nudge, advisor cards, file mentions) drop to user; agent-owned reminders (retry, checkpoint, todo) stay on system role.

Refs #3456
2026-06-25 08:54:18 +00:00
roboomp 208d24de6a style: bun run fix 2026-06-25 08:47:06 +00:00
roboomp 3db3d6d135 fix(ai): preserved ollama cache for capture turns
Serialize Ollama context developer messages after the static prompt as user turns so llama.cpp can reuse its warm prefix for auto-learn capture follow-ups.

Fixes #3456
2026-06-25 08:46:53 +00:00
roboomp 93f1019ead fix(coding-agent): extended binary refusal to cover newline-less blobs
The streaming reader's NUL check only walked completed lines collected
from streamLinesFromFile, so a binary blob whose first newline lay past
the byte budget (videos, archives, packed JSON) left collectedLines
empty and slipped through to the firstLineExceedsLimit branch — which
emitted the decoded preview as text instead of the intended refusal.

Sniff firstLinePreview alongside collectedLines so the existing refusal
fires uniformly. Also added a regression test that uses a 256 KiB blob
with no 0x0A bytes to actually exercise the firstLineExceedsLimit
path — the previous 6-byte test fit in one collected line and never
covered the bug.

Fixes #3448
2026-06-25 07:58:20 +00:00
roboomp 83c392482a style: bun run fix 2026-06-25 07:37:51 +00:00
roboomp 4331217af8 fix(coding-agent): refused local binary attachment text reads
Routed file-backed '/data/workspaces/can1357__oh-my-pi__3448/.omp-session/2026-06-25T07-25-13-303Z_019efdab-28d7-7000-a7a4-e20282508056/local' reads through the normal filesystem reader so binary detection, document/image handling, and streaming safeguards apply before content is materialized.

Hardened the local protocol handler to return metadata-only refusals for binary/container resources instead of decoding them with Bun.file().text().

Fixes #3448
2026-06-25 07:37:30 +00:00
roboomp c7d9abbcfd fix(plugins): bundle wildcard-exported pi-* subpaths
The first pass only enumerated non-wildcard `exports` entries, so
patterns like pi-ai's `./oauth/*` left every concrete target
(`@oh-my-pi/pi-ai/oauth/anthropic` and friends) outside the
bundled registry. Compiled-mode resolution then fell back through
`Bun.resolveSync` → original peer specifier → missing peer dep,
reproducing the original `Cannot find module` failure for any
plugin that imports a wildcard-only subpath (e.g.
`@mariozechner/pi-ai/utils/oauth/anthropic`, remapped via
PI_SUBPATH_REMAPS).

The generator now runs a second pass over wildcard exports,
parses each single-asterisk pattern into prefix/suffix halves,
globs the matching source directory, and emits a registry entry
per concrete `.ts` file. Root catch-all wildcards (`./*` /
`./*.js`) are skipped on purpose — they'd static-import top-level
files like the coding-agent's own `cli.ts` and explode the bundle
through the binary entry's transitive graph. Test, `.d`,
`.generated`, `.bench` files and `index` basenames are filtered
out so the registry stays focused on importable surfaces.

A new test case in
test/extensibility/legacy-pi-bundled-subpath-overrides.test.ts
asserts the reviewer's cited `@oh-my-pi/pi-ai/oauth/anthropic`
key now routes through the virtual namespace and that root
catch-all wildcards remain unbundled.

Fixes #3442
2026-06-25 06:13:38 +00:00
roboomp 2a74e74a0f fix(plugins): build collab-web tool views before compile
legacy-pi-bundled-registry.ts now static-imports
@oh-my-pi/pi-coding-agent/export/html, whose source pulls
./tool-views.generated.js. The root 'prepare' hook builds that
file on bun install, but a clean binary build that skips install
hooks would 'bun build --compile' against the registry entry and
fail resolving the missing generated bundle. build-binary.ts now
runs collab-web's build:tool-views before the compile, matching
what prepack already does for the npm bundle.

Fixes #3442
2026-06-25 06:02:37 +00:00
roboomp 897cce792f fix(coding-agent): split mixed file mentions so text stays on developer
Reviewer caught that demoting the whole mixed payload to `user` (`@notes.md
@screenshot.png`) regressed the developer-priority treatment text-only
mentions still get for image-free turns. `generateFileMentionMessages` packs
every `@…` into one `fileMention`, so the previous `hasImage` toggle
collapsed the source-file context into the user slot whenever an image was
attached.

`convertToLlm` now returns up to two messages per `fileMention` via
`flatMap`: text-only files keep their existing `developer` envelope, and
image-bearing files emit a separate `user` envelope that carries their
`<file>` wrappers plus the `input_image` block. Pure-text and pure-image
turns still collapse to a single message.

Tests cover the mixed case (split into developer + user), the image-only case
(single user message), and the existing text-only case (single developer
message).

Fixes #3443
2026-06-25 05:54:15 +00:00
roboomp aa29b79642 fix(plugins): route bundled pi-* subpath imports through the virtual registry
Compiled-binary extension validation rewrote @(scope)/pi-ai/oauth →
@oh-my-pi/pi-ai/oauth, but LEGACY_PI_PACKAGE_ROOT_OVERRIDES only
covered bare package roots. resolveCanonicalPiSpecifier therefore
fell through to Bun.resolveSync, which fails inside bunfs on Bun
1.3.14+, then the rewriteLegacyPiImports catch left the original
specifier alone. Bun's native resolver then failed because most
plugins (e.g. @charmland/pi-hyper-provider) declare @(scope)/pi-ai
as a peerDependency only and never materialize a real install.

A new scripts/generate-legacy-pi-bundled-registry.ts reads every
bundled pi-* package's non-wildcard exports field and emits both
the heavy legacy-pi-bundled-registry.ts (static imports + map) and
a light legacy-pi-bundled-keys.ts. legacy-pi-compat.ts statically
imports the keys file to seed the override map without paying the
legacy-pi-coding-agent-shim → ../index → export/html/... cascade,
so subpath imports now route to the same omp-legacy-pi-bundled:
virtual namespace that already serves the roots.
scripts/build-binary.ts runs the generator before bun build
--compile so new pi-* subpaths added under packages/*/package.json
ship without manual regeneration; --check verifies the committed
output stays in sync.

Fixes #3442
2026-06-25 05:52:38 +00:00
roboomp c2174a87b2 fix(coding-agent): routed image-bearing @ mentions as user-role messages
Codex GPT models on chatgpt.com /codex/responses rejected `@image` turns with
`Codex error event: [OneOfParam] [input[N].content[M]] [invalid_enum_value]
Invalid value: 'input_image'. Supported values are: 'input_text'.` —
`convertToLlm`'s `fileMention` arm always emitted a `developer`-role
Responses message, but a developer-role content slot only accepts
`input_text`. #3421's prior fix only suppressed the Codex Responses Lite
header on image-bearing turns; the full transport kept rejecting the same body.

`fileMention` now uses `user` role when any attached file carries an image;
text-only mentions keep `developer` so the auto-read context still rides at
instruction priority for the agent.

Fixes #3443
2026-06-25 05:47:07 +00:00
can1357 74c45e6d54 fix(ai): corrected bedrock cross-region inference profile routing
- Resolve the runtime region for geo-prefixed inference profiles by honoring the profile's geographical prefix instead of defaulting to `us-east-1`.
- Update regional resolution logic to use ambient `AWS_REGION` only when compatible with the model's geo, falling back to a regional default otherwise.
- Prevent HTTP 400 errors for non-US geo-prefixed models caused by improper routing to US-based endpoints.
2026-06-25 06:55:18 +02:00
can1357 b2b818af6f chore: bump models 2026-06-25 06:46:36 +02:00
can1357 76c0f14b4e Merge remote-tracking branch 'origin/farm/f80f626f/tui-fullwidth-cursor-drift' 2026-06-25 06:46:07 +02:00
can1357 150adc1112 Merge remote-tracking branch 'origin/farm/76a7a4e5/fix-skill-url-resolution' 2026-06-25 06:45:55 +02:00
can1357 434ee28f6e test(ai): align cross-API reasoning repro comments with threaded resolved-compat design 2026-06-25 06:32:18 +02:00
can1357 cf4da4d786 Merge #3437 + reconcile with #3439: unified cross-API reasoning preservation
#3437 (preserve cross-api reasoning replay) and #3439 (preserve 3p thinking blocks across cross-API switches) both rewrite the thinking-block path of transform-messages.ts and the encoder in openai-completions.ts to fix issues #3433/#3434, and overlap structurally. Reconciled into one resolved-compat design:

- transform-messages.ts threads the request-time resolved compat (from #3437) into the predicate, replacing #3439's base+whenThinking union traversal. resolveOpenAICompatPolicy already swaps in whenThinking for thinking-on requests, so the predicate and encoder stay in lockstep (closes both Codex 'use resolved compat' reviews and keeps the #1071/#1484 OpenCode paths correct).

- The predicate keeps #3439's broad coverage: preserve a native, signature-stripped thinking block when the resolved openai-completions target is reasoning-capable, not requiresThinkingAsText, and either requiresReasoningContentForToolCalls or thinkingFormat==='zai'. Other cross-API targets text-demote.

- openai-completions.ts keeps #3439's Z.AI-format encoder branch and #3437's compat threading, but drops #3439's text-fold fallback and isSameModelHistory: under threading they are unreachable (any preserved cross-model block is consumed by the requiresReasoningContentForToolCalls or zai branch; thinking-off targets text-demote in transform). Markup-healed same-model thinking still falls through unemitted, as before.

Regression tests from both PRs retained. Fixes #3433, #3434.
2026-06-25 06:31:22 +02:00
roboomp 57882ac307 fix(ai): skip text-fold fallback for same-model unsigned thinking
Markup-healed streams (MiniMax M3 \`<think>…</think>\`, Kimi K2 healed
reasoning, every \`getStreamMarkupHealingPattern === "thinking"\` host)
reconstruct thinking blocks from raw text deltas, so the resulting
\`ThinkingContent\` lands with \`thinkingSignature: undefined\`. On a
same-model continuation those blocks are PRIVATE reasoning the source
emitted, not cross-API preserved reasoning — promoting them into the next
request's visible \`content\` would leak hidden chain-of-thought.

The text-fold fallback added in 8f7fb1d for cross-API preserved blocks now
gates on \`!isSameModelHistory\` before firing. Same-model history keeps the
legacy behavior of silently dropping unsigned thinking when no per-compat
branch consumes it, matching pre-#3434 semantics. Cross-API replays
(different api, provider, or model id) still fold to content so the
reasoning chain at least survives as conversation context.

Tests: \`issue-3434-repro.test.ts\` adds a pin for same-model unsigned
thinking on \`opencode-go\`/Kimi — visible content stays exactly the model's
visible answer, no reasoning_content / reasoning / reasoning_text on the
wire.

\`bun test packages/ai\`: 2297 pass / 1 fail (the same \`proxy.test.ts\`
\`Bun.env\` leak that fails on \`main\`). \`bun check\` passes.

Addresses chatgpt-codex-connector P2 review on #3439.
2026-06-25 04:20:06 +00:00
roboomp 8f7fb1d074 fix(ai): consult compat.whenThinking when gating cross-API thinking replay
The cross-API preservation predicate added in the parent commit read only
\`model.compat\`, so it missed the resolved \`compat.whenThinking\` view that
OpenCode-hosted reasoning models (Kimi/GLM/DeepSeek/Qwen/MiMo via \`opencode-go\`
/ \`opencode-zen\`) require:

- Base compat keeps \`requiresReasoningContentForToolCalls: false\` so
  thinking-disabled requests dodge \`Extra inputs are not permitted\` (#1071).
- \`compat.whenThinking\` reactivates the flag for thinking-engaged requests
  so they dodge \`thinking is enabled but reasoning_content is missing\` (#1484).

The old predicate read the base compat, so a cross-API switch into an
OpenCode reasoning model still demoted the prior thinking to text and the
next thinking-on request re-triggered #1484. The predicate now folds both
views (base + \`whenThinking\`) through a shared \`acceptsReasoningContentReplay\`
helper; if either view permits replay, the block is preserved.

Encoder companion fix in \`openai-completions.ts\`: when preserved cross-API
thinking reaches a request whose resolved compat can't surface
\`reasoning_content\` (most often OpenCode in thinking-disabled mode), fold
the reasoning into the visible content. Same shape as
\`requiresThinkingAsText\`, but gated on signature-stripped blocks only, so
same-model replays (which keep the streaming signature) are untouched. This
preserves the pre-#3434 text-demotion semantics for that branch without
silently dropping the reasoning chain.

Tests: \`packages/ai/test/issue-3434-repro.test.ts\` now also pins:
- OpenCode \`whenThinking\` compat surfaces \`reasoning_content\` from the
  preserved block (guards against re-opening #1484 on cross-API switch).
- OpenCode base compat (thinking off) folds the preserved block into
  content (guards against silently dropping the reasoning chain).

\`bun test packages/ai\`: 2296 pass / 1 fail; the lone failure
(\`proxy.test.ts > normalizes hyphenated provider ids to underscores\`)
is the same pre-existing \`Bun.env\` leak from a sibling test that fails on
\`main\`. \`bun check\` passes.

Addresses chatgpt-codex-connector review on #3439.
2026-06-25 04:11:30 +00:00
roboomp 57f2c646c6 fix(ai): used resolved compat for reasoning replay
Threaded the resolved OpenAI completions compat into transformMessages so thinking-enabled request policy controls cross-API reasoning preservation.

Added coverage for Kimi/OpenCode replay where base compat is false but the thinking-enabled policy requires reasoning_content.

Fixes #3433
2026-06-25 03:57:32 +00:00
roboomp c30e647ec5 fix(ai): preserve 3p thinking blocks across cross-API switches
On a mid-session switch between an Anthropic-compatible 3p endpoint and an
OpenAI-compatible 3p endpoint on the same vendor (Z.AI Anthropic → Z.AI OpenAI,
Kimi Anthropic → Kimi OpenAI, custom \`models.yaml\` switches that cross API
types), \`transformMessages\` demoted every prior \`thinking\` block to plain
text on the cross-API path. The next request shipped the reasoning chain as
conversation text instead of structured \`reasoning_content\`, so the target
model lost the prior reasoning context and the source-side reasoning tokens
were paid for twice.

Two-half fix:

1. \`packages/ai/src/providers/transform-messages.ts\` — the cross-API thinking
   path now preserves the prior reasoning as a native, signature-stripped
   \`thinking\` block whenever the \`openai-completions\` target can re-emit it
   on the wire (\`requiresReasoningContentForToolCalls\` or
   \`thinkingFormat: "zai"\`). The source wire-format signature is always
   stripped because it is bound to the source API (Anthropic crypto sig /
   OpenAI Responses encrypted blob) and useless to the target.
2. \`packages/ai/src/providers/openai-completions.ts\` — the encoder now
   surfaces preserved thinking blocks via \`reasoningContentField\` for
   Z.AI-format hosts that don't strictly require the field, so a preserved
   block with a stripped/opaque signature still ships as
   \`reasoning_content\` on the wire.

Other cross-API targets (openai-responses encrypted blobs, google signed
thought parts, anthropic-from-foreign-source) keep the existing text-demotion
fallback so reasoning still survives as visible conversation context.

Tests:
- New \`packages/ai/test/issue-3434-repro.test.ts\` pins the Z.AI → Z.AI,
  Anthropic 3p → DeepSeek, and Anthropic 3p → official-OpenAI-non-reasoning
  wire shapes; all 4 tests fail on \`main\` and pass after the fix.
- \`bun test packages/ai\`: 2294 pass / 1 fail; the one failure
  (\`proxy.test.ts > normalizes hyphenated provider ids to underscores\`) also
  fails on \`main\` from a \`Bun.env.PI_PROXY_*\` leak in a sibling test and is
  unrelated to this diff.

Fixes #3434
2026-06-25 03:57:17 +00:00
roboomp 3ce34621c0 fix(agent): preserved skill url session context
Threaded the caller's loaded skills through internal URL resolution so skill:// handlers do not depend on process-global skill state during tool execution.

Fixes #3436
2026-06-25 03:54:15 +00:00
roboomp 256631bbf5 fix(ai): preserved cross-api reasoning replay
Preserved cross-API thinking blocks for OpenAI-compatible reasoning targets so the completions encoder can write them to the configured reasoning_content field after signatures are stripped.

Added regression coverage for Anthropic-message history replayed through DeepSeek-compatible chat completions.

Fixes #3433
2026-06-25 03:48:07 +00:00
roboomp 040846336d fix(tui): shrunk bordered Editor right chrome by cursor overflow cells, not a 1-cell boolean
When the end-of-line cursor glyph lands past a wide trailing grapheme
(CJK comma ',', emoji, etc.), the inverse-video cursor extends past
'lineContentWidth' by the full overflow. Prior cursorInPadding logic
dropped exactly one space of right padding but always re-emitted '─'
before '╯', so 'paddingX=1' overflow=1 stayed 1 cell too wide and
'paddingX=2' overflow=2 stayed 2 cells too wide. In the user's
terminal the '╯' wrapped to a new row, looking like the editor box
collapsed onto two lines.

Replaced the boolean with the actual overflow cell count and recomputed
the right chrome budget (padding + optional '─' + corner) from
'paddingX + 1 - overflow', floored at 1 so the corner/vertical bar
survives. The else branch (non-last lines) follows the same budget for
symmetry.

Fixes #3431
2026-06-25 03:34:24 +00:00
can1357 1a89d784e7 chore: bump version to 16.1.18 2026-06-25 05:17:47 +02:00
can1357 366313df33 test(coding-agent): improved stdout geometry stubs in tests
- Added no-op setters to process.stdout properties in test geometry stubs.
- Prevented potential errors when code under test attempts to reassign stdout dimensions.
2026-06-25 05:15:40 +02:00
can1357 72f121ee2c chore: update changelogs 2026-06-25 05:13:23 +02:00
can1357 fb43fbe9a7 refactor(coding-agent): drop dead legacy-pi bundled test seams
Remove `__getLegacyPiBundledRegistry` and `__synthesizeLegacyPiBundledSource`:
both reject outside compiled-binary mode, so no unit test can call them — they
were untested test seams. Real coverage runs through the pure
`__synthesizeLegacyPiBundledSourceWithRegistry` + `__getLegacyPiBundledRegistryGlobal`.

Also document why the synthesized virtual module must bridge back to the host
registry through `globalThis` (separate ES module, no shared closure scope,
live non-serializable exports) so the indirection isn't mistaken for cruft.
2026-06-25 05:12:14 +02:00
can1357 0cd852053e Merge branch 'farm/6a62fa2b/fix-bunfs-override-validation-fallback'
Serve bundled pi-* and TypeBox through an in-process virtual namespace
on Bun 1.3.14+ where `--compile` extras are unreachable via any
filesystem API (issue #3423).

Merge resolution:
- Reconciled `TYPEBOX_SHIM_PATH` with main's #3414 fall-through:
  `__resolveTypeBoxShimPath(isCompiled, sourcePath, exists)` returns the
  `omp-legacy-pi-bundled:` virtual specifier in compiled mode (no FS
  probe) and the on-disk source path otherwise, dropping to null when
  the shim file is missing so bare typebox imports fall through to native
  resolution.
- Removed the now-dead `--compile` extras path: dropped
  `LEGACY_COMPAT_BUILD_ENTRYPOINTS` usage from both build-binary.ts and
  ci-release-build-binaries.ts and deleted scripts/binary-entrypoints.ts;
  the bundler reaches every surface via legacy-pi-bundled-registry.ts.
- Deleted obsolete tests for the removed bunfs machinery
  (legacy-pi-compat-entrypoints, legacy-pi-typebox-shim-validation) and
  added regression coverage for __resolveTypeBoxShimPath.
- Dropped the binary-compiling smoke driver per maintainer request.

Verified: bun check clean; 77 extensibility tests pass; instrumented
compiled-binary run confirmed onLoad fires for all bundled specifiers.
2026-06-25 05:08:36 +02:00
can1357 3e1d5fce12 feat(coding-agent): filtered out non-reportable accounts in usage CLI
- Add `usageProviderFor` helper to `AuthStorage` to distinguish between providers without usage endpoints and missing data.
- Implement account filtering in the usage CLI to exclude providers that do not support usage reporting unless explicitly requested.
- Improve CLI diagnostic messages when no usage data is retrieved by differentiating between missing credentials and unsupported providers.
2026-06-25 04:44:36 +02:00
roboomp 294d41bd3b fix(coding-agent): served bundled pi-* through virtual ns on bun 1.3.14
Bun 1.3.14 stopped exposing `--compile` extras through every filesystem-style API: `fs.existsSync`, `Bun.file().exists()`, `Bun.resolveSync`, and `await import()` on `/$bunfs/...` or `file:///$bunfs/...` all fail; only `/$bunfs/root/<binary-name>` itself answers. The pre-existing legacy-pi rewrite emitted `file:///$bunfs/...` URLs that Bun then could not load, so every legacy extension that imported `@oh-my-pi/pi-*` or `@sinclair/typebox` failed on the `omp-darwin-arm64` release binary.

`legacy-pi-compat.ts` now keeps a JS-heap reference to every bundled pi-* surface in a lazy-loaded sibling `legacy-pi-bundled-registry.ts` and serves them through an `omp-legacy-pi-bundled:` virtual namespace whose `Bun.plugin().onLoad` synthesizes a re-export module — no bunfs path ever leaves the module in compiled mode. Dev / source-link / installed-package modes keep the historical `file://` rewrite (source files exist on disk). The matching `--compile` extras in `scripts/build-binary.ts` are gone; `BUNFS_PACKAGE_ROOT`, `bunfsPath`, `__computeBunfsPackageRoot`, and `__joinBunfsPath` are deleted as dead code. `scripts/smoke-3423.ts` compiles a tiny binary that loads a fixture extension end-to-end through the new path.

Fixes #3423
2026-06-25 02:35:50 +00:00
can1357 591f1c5765 feat(ai): implemented idempotent credential refresh with CAS logic
- Added id-based credential persistence and CAS-based invalidation to prevent cross-process race conditions.
- Categorized OAuth failure responses into transient and definitive types for more resilient refresh orchestration.
- Enabled stable credential lookups during refresh to ensure integrity during peer rotation or index shifts.
- Validated new persistence and error handling logic through comprehensive concurrency and failure-mode tests.
2026-06-25 04:31:58 +02:00
can1357 61b430da9d feat(ai): added focused oauth account selection and listing
- Added listOAuthAccounts and getOAuthAccessAt to permit targeted credential retrieval without impacting sibling accounts.
- Implemented sameOAuthIdentity helper to ensure resolution consistency when manually selecting accounts.
- Added --list and --account CLI flags to token command for inspecting and choosing specific credentials.
2026-06-25 04:18:10 +02:00