Commit Graph
190 Commits
Author SHA1 Message Date
can1357 cfa0cd84ba feat(agent): resolved partial json leakage by enforcing streaming cleanup
- Update scrubPartialJson to utilize clearStreamingPartialJson for consistent tool-call cleanup.
- Adjust execution order in streamProxy to ensure partial error messages are finalized before scrubbing.
- Remove redundant test expectation comment regarding partialJson leakage.
2026-06-27 12:26:11 +02:00
can1357 357c29224d feat: implemented symbol-based streaming state for isolated metadata
- Migrated internal streaming state from string-based properties to symbol-keyed properties for improved data isolation and safety.
- Replaced the deprecated `stripVariant` utility with centralized `clearStreamingPartialJson` and symbol-specific helper methods across all provider implementations.
- Implemented `stripStreamingBlockSymbols` and updated deep equality checks to ensure metadata does not interfere with content comparisons.
- Standardized streaming metadata access through a new `block-symbols` utility module.
2026-06-27 12:07:08 +02:00
can1357 0b8206a46d fix(compaction): preserve provider defaults for remote compaction 2026-06-27 01:39:34 +02:00
can1357 14cc9cba0f Merge PR #3106: fix(compaction): enable custom provider remote compaction (@roboomp) 2026-06-27 01:39:34 +02:00
can1357 3e5360ca4c Merge PR #3060: feat(provider): add GitLab Duo Agent provider (@jiwangyihao) 2026-06-27 01:39:31 +02:00
can1357 1ed343d9a4 Merge PR #3595: fix(agent): stop replaying provider refusals (@roboomp) 2026-06-26 23:27:40 +02:00
can1357 2899ce1bc2 Merge PR #3328: fix(proxy): scrub transient partialJson from final tool calls (@roboomp) 2026-06-26 23:27:38 +02:00
can1357 894d338ce8 docs(ai/dialect): clarified escaping and halting rules for tool dialects
- Explicitly prohibited HTML escaping in tool arguments for all dialects to ensure raw data transmission.
- Clarified that tool call bodies are delimiter-parsed rather than XML-parsed where applicable.
- Enforced strict requirements to complete tool call output before emitting stop sequences and halting.
2026-06-26 22:35:20 +02:00
roboomp d68980fe58 fix(agent): stopped replaying provider refusals
API-level refusals now stay visible as terminal errors without being sent back as assistant dialogue on the next provider request. Added core and coding-agent conversion coverage for Anthropic refusal metadata.

Fixes #3592
2026-06-26 17:56:07 +00:00
jiwangyihao af32c22ffe fix(agent): /move 后按会话实时 cwd 重新作用域 Duo 发现
机器人指出 agent.ts 的 #cwd 在构造时固定,/move 更新 SessionManager 与
进程 cwd 后不会重建 Agent,导致 GitLab Duo Agent 的 namespace/project 发现
持续读取旧仓库的 git remote。

按既有 resolver 模式(getReasoning/getServiceTier)修复:

- Agent 新增可选 cwdResolver;构造时存入 #cwdResolver。
- AgentLoopConfig 新增 getCwd 每调用解析器,config 同时携带静态 cwd 与
  getCwd。
- agent-loop 在 streamFunction 调用点计算 effectiveCwd = getCwd?.() ?? cwd,
  每次 LLM 调用读取一次,因此运行中途的 /move 也能被工作区级 provider 发现
  感知。
- sdk.ts 主 Agent 传入 cwdResolver: () => sessionManager.getCwd(),该值在
  /move 时由 SessionManager.#cwd 更新。

新增针对可观测契约的回归测试(mock streamFn 记录 options.cwd):resolver
覆盖静态 cwd、resolver 返回 undefined 时回退静态 cwd、以及运行中途变更可被
逐次调用读取(模拟 /move)。
2026-06-26 16:13:24 +08:00
roboomp 2b68785e82 fix(agent): include provider payloads in append-only digests
Responses-style providers serialize providerPayload history items instead of the
visible message blocks when replaying native history. Include providerPayload in
the append-only per-message digest so payload-only history rewrites stop the
stable-prefix walk and re-sync the changed message before any later divergent
tail.

Add a regression where an assistant message keeps identical visible content and
id but changes its openaiResponsesHistory providerPayload while a later message
also diverges; syncMessages must preserve the prefix before the assistant and
refresh the assistant payload.

Fixes #3406
2026-06-24 23:11:12 +00:00
roboomp 5b9e009a8b fix(agent): bound append-only stable prefix by log length
Direct callers can clear AppendOnlyContextManager.log without resetting the
private sync cursor. The advisor reset path does this when recycling its helper
agent, leaving lastSyncCount and messageDigests describing the old transcript
while the physical log is empty.

Clamp the stable-prefix reuse count to the current log length before truncating
and appending. A direct log clear now forces the next sync to replay from index 0
instead of starting from a stale private cursor and dropping prefix messages from
the provider context.

Add a regression that clears the public log after syncing two messages, then
resyncs a context with the same first message and a rewritten second; both
messages must be present in the rebuilt append-only log.

Fixes #3406
2026-06-24 23:07:11 +00:00
roboomp cce627ee4b fix(agent): include tool result metadata in append-only digests
Track internal tool-result metadata in append-only per-message digests so
metadata-only rewrites of toolCallId, toolName, or isError stop the stable-prefix
walk and re-sync the changed tool result before any later divergent tail.

This prevents stale tool-result pairing or error state from being preserved when
the text content stays unchanged but provider-serialized metadata changes.

Fixes #3406
2026-06-24 22:42:51 +00:00
roboomp c803a3e30d style: bun run fix 2026-06-24 22:35:03 +00:00
roboomp 65a974aa90 fix(agent): preserve append-only log prefix on in-place message rewrites
`AppendOnlyContextManager.syncMessages` hashed a single rolling digest
over the entire synced prefix, so any in-place rewrite of an already-
synced message — per-turn `pruneSupersededToolResults` / `pruneToolOutputs`
collapsing a tool result, image stripping, or a `transformContext` re-render
— triggered `log.clear()` and re-appended the full conversation from
the current (mutated) view. The provider's cached bytes still matched
the prefix, but every position past the divergence had to be re-prefilled.
On llama.cpp / Ollama / LM Studio this re-prefilled tens of thousands
of tokens every few turns (`n_past \u2248 end-of-system-prompt` collapse,
~40k-token full re-prefill, GPU pinned >400W).

Replace the rolling digest with per-message digests in `#messageDigests`,
walk the new sync against them to find the longest byte-stable prefix,
truncate the log down to that prefix via a new `AppendOnlyLog.truncate(count)`,
and only re-append the diverged tail. Genuine compaction (`length <
lastSyncCount`) still clears the log.

- Tail-only rewrite: prefix stays byte-stable; only the trailing message
  re-syncs.
- Deep rewrite: prefix up to the divergence stays byte-stable; the
  provider re-prefills from the divergent message onward (architectural
  minimum).
- True compaction: unchanged, full replay.

Replace the now-misleading `detects in-place rewrite of already-synced
messages` / `detects in-place rewrite via digest mismatch` tests with
`preserves the byte-stable prefix when a deep message is rewritten (#3406)`,
`preserves the prefix when the tail is rewritten (#3406)`, `appended
new messages keep the prefix stable even when the prior tail also
diverged (#3406)`, and `rewriting the first message still re-syncs
from scratch` so each invariant is asserted directly.

Fixes #3406
2026-06-24 22:33:22 +00:00
can1357 c053afa087 test(agent): migrated yield tests to use YieldGate for stability
- Replaced global timer mocks with local `YieldGate` instances to avoid test flakiness from concurrent environment interference.
- Introduced an injected clock and counting sleep pattern to test gating logic without relying on `process` globals.
- Added a test case to ensure the gate correctly handles negative clock jumps without stalling.
2026-06-24 15:30:29 +02:00
oldschoola c210bdd391 fix(proxy): scrub partialJson in catch-block error path on stream disconnect
Address review feedback: when the SSE stream disconnects after a
toolcall_delta but before toolcall_end/done/error, the catch-block
at lines 183-192 pushes the partial message as the error result
without calling scrubPartialJson. This leaked the internal partialJson
field into the final error message.

Added scrubPartialJson(partial) call in the catch block, before
pushing the error event.

Added test verifying partialJson does not leak when server disconnects
mid-tool-call (toolcall_start + partial toolcall_delta, no terminal event).
2026-06-23 10:16:06 -07:00
oldschoola 2a2aa9fe59 fix(proxy): preserve partialJson on content during streaming, scrub at terminal events
Address review feedback: downstream renderers (event-controller.ts:535)
read content.partialJson during toolcall_delta to pace streaming
previews (bash env assignments, write/edit smooth streaming).

Revised approach:
- toolcall_start: initialize partialJson on content via typed
  ToolCall & { partialJson: string } intersection (not as any)
- toolcall_delta: accumulate in side-channel Map, write onto content
  via typed intersection cast
- toolcall_end: delete partialJson from content + side-channel map
- done/error: scrubPartialJson() cleans any remaining blocks that
  never got toolcall_end (the original leak bug, now fixed for all
  terminal paths)

Added test verifying partialJson IS present during streaming and
IS absent after completion.
2026-06-23 09:40:32 -07:00
oldschoola 9c795f886e fix(proxy): use side-channel Map for partialJson, eliminating as-any casts
streamProxy stored internal partialJson streaming state directly on typed
ToolCall objects via 4 'as any' casts. If toolcall_end was skipped (stream
error, early done), the field leaked into the final AssistantMessage content,
corrupting downstream serialization.

Replace with a side-channel Map<number, string> keyed by contentIndex:
- toolcall_start initializes the map entry
- toolcall_delta accumulates into it
- toolcall_end cleans it up

The typed ToolCall object never carries non-spec fields. All 4 'as any'
casts are eliminated.

Added 4 contract tests covering argument parsing, partialJson isolation on
normal completion, partialJson isolation when toolcall_end is missing, and
multiple concurrent tool calls with interleaved deltas.
2026-06-23 08:17:29 -07:00
can1357 5c21b28786 feat: optimized handoff generation and harden request safety
- Introduced `generateHandoffFromContext` to enable provider-aware oneshot generation and improved cache hit rates via the live-turn pipeline.
- Updated `buildSideRequestContext` to support pinning custom system prompts, preventing per-turn hook leakage during handoff.
- Added concurrency guards across CLI and RPC modes to block manual `/handoff` requests while a session is actively streaming.
- Standardized handoff execution to force `toolChoice: "none"` and enforce consistent cache-routing behavior.
2026-06-22 20:05:40 +02:00
can1357 0b5e2d8276 fix(ai-providers): normalized Anthropic tool call IDs
- Implemented `normalizeAnthropicTargetToolCallId` to define consistent ID validation and fallback logic.
- Integrated the normalization utility into the `transformMessages` function to ensure API compatibility.
- Refactored `transformMessages` to decouple mapping logic from message loop execution for better maintainability.
- Updated the changelog to reflect the correction of tool call ID handling for Anthropic-compatible models.
2026-06-21 06:04:49 +02:00
can1357 29f7b57fde feat: migrated core rendering and context transformation to async
- Updated `render`, `renderMany`, and native snapcompact methods to return promises, ensuring scalable async execution.
- Refactored `transformProviderContext` and `buildSideRequestContext` to support asynchronous operations in agent loops.
- Integrated `Promise.all` for improved concurrency when processing frame rendering and rendering batch operations.
- Updated all internal call sites, SDK hooks, and test suites to accommodate the asynchronous API signatures.
2026-06-21 00:32:13 +02:00
can1357 b91a15cbcf fix(coding-agent): ensured side-channel turns obfuscate secrets and tools
- Verified that side-channel turns maintain stable prompt cache parity with main turns.
- Applied secret and tool obfuscation to ephemeral assistant side-channel data to prevent leakage.
- Updated `Agent` and `AgentSession` test suites to validate consistency under native dialect environments.
2026-06-20 23:24:41 +02:00
can1357 b5437c2cad feat(coding-agent): improved prompt caching for ephemeral side-channel requests
- Added `buildSideRequestContext` to the `Agent` class to generate prompt-cache-friendly provider contexts.
- Updated ephemeral side-channel turns to forward the full tool catalog to maintain prompt cache hit rates.
- Injected a `developer` role reminder into ephemeral turns to instruct the model to suppress tool calls.
- Implemented automatic post-processing to strip any tool calls from ephemeral turn responses.
- Exported message and dialect helper functions in `agent-loop.ts` to support context construction.
2026-06-20 23:19:10 +02:00
roboomp d1066ae4ad fix(compaction): used Azure request shape for remote compaction
Azure Responses remote compaction was enabled by shouldUseOpenAiRemoteCompaction
but requestOpenAiRemoteCompaction still built OpenAI-style requests:
Authorization: Bearer plus a URL without api-version. Azure Responses uses an
api-key header and api-version query parameter, so normal Azure configs failed
and fell back to local summarization.

Derive Azure compact URLs from the Azure Responses base URL/resource-name
configuration, append api-version, and send api-key headers while preserving
custom headers. Existing OpenAI and Codex request shapes are unchanged.

Added a regression test that opts into azure-openai-responses compaction and
asserts the compact URL, api-key auth, absence of Authorization, custom header
preservation, and configured compaction model payload.

Fixes #3104
2026-06-20 07:48:33 +00:00
roboomp b7aefe0689 fix(compaction): enabled custom remote compaction
- Added provider/model remoteCompaction metadata and models.yml propagation.\n- Routed configured OpenAI-compatible compaction endpoints for custom providers.\n- Added compactionModel as a summary-only model selector that leaves the active session model unchanged.\n\nFixes #3104
2026-06-20 07:27:09 +00:00
roboomp 48a78f278c fix(agent): wire-encode normalizeTools parameters for omit-intent tools
normalizeTools only ran toolWireSchema/zodToWireSchema/arkToWireSchema
inside the if (doInjectIntent) branch, so any tool whose intentMode
resolved to "omit" (function intent, or intent: "omit" like the
builtin eval/resolve) had its live arktype/zod schema object left in
parameters. The pruneDescriptions branch already wire-encoded
unconditionally, so the two branches disagreed; first-party providers
re-encoded with toolWireSchema themselves and never noticed, but any
downstream consumer that serialized parameters as-is ended up with an
invalid schema (arktype AST: object-valued required, domain instead of
type, no properties).

Hoist toolWireSchema(t) out of the inject-intent branch so it always
runs and inject intent on top, mirroring the pruneDescriptions shape.
Drop the now-redundant isZodSchema/isArkSchema/zodToWireSchema/
arkToWireSchema imports.

Fixes #3074
2026-06-19 19:43:45 +00:00
roboomp 19d103b708 style: bun run fix 2026-06-19 15:14:50 +00:00
can1357 67f6518e42 feat: enhanced tool robustness, improve authentication flow, and update API parameters
- Implement JSON repair and strict argument validation to sanitize raw payloads and redact sensitive information from agent event logs.
- Add automatic authentication fallback for benchmark model resolution to ensure consistent performance testing across providers.
- Refactor search tool API parameters by replacing `i` with a case-sensitive `case` boolean flag for clarity.
- Update session history formatting to ensure empty objects are consistently serialized as `{}` instead of empty strings.
2026-06-19 16:46:07 +02:00
can1357 7d28c60c86 refactor: renamed intent field from _i to i
- Renamed the global `INTENT_FIELD` constant from `_i` to `i`.
- Updated documentation strings, type annotations, and test expectations across packages to reflect the new field name.
- Ensured consistent usage of the constant in tool schema construction and intent serialization.
2026-06-19 16:42:33 +02:00
can1357 d46bd0139b refactor(coding-agent): fixed system prompt customization path
- Fixed `SYSTEM.md` integration to correctly include custom-rendered sections like rules and skills.
- Consolidated system prompt validation by requiring `<skills>` tag presence instead of specific prose.
- Removed redundant system prompt math-formatting tests and orphaned task batch documentation tests.
2026-06-19 16:16:37 +02:00
can1357 865694df7e fix: resolved streaming stability and reference isolation issues
- Prevent object reference sharing between agent snapshots and stream events by deep-cloning tool-call arguments.
- Stabilize GFM tables and Mermaid diagrams during streaming by delaying transcript block commits until content finalization.
- Implement session resume safety to prevent crashes when working directories are missing.
- Add comprehensive test suites to verify streaming commit stability and immutable snapshot isolation.
2026-06-19 16:09:00 +02:00
can1357 ed73429faa feat: implemented soft tool requirement and escalation system
- Introduced `SoftToolRequirement` to support non-invasive tool enforcement with lifecycle management and escalation.
- Added `ToolChoiceDirective` to coordinate hard and soft tool requirements within the agent loop.
- Optimized preview workflows in `coding-agent` by replacing forced tool choices with non-forcing pending invokers.
- Enhanced `CompactionSummaryMessage` to prioritize structured rendering for tool requirement reminders.
2026-06-19 07:51:27 +02:00
can1357 7c6f325a1c test(agent): added coverage for pruneToolDescriptions behavior
- Added test cases to verify that native tool descriptions are pruned when pruneToolDescriptions is enabled.
- Confirmed that in-band tool descriptions persist for owned dialects (e.g., GLM) even when pruning is enabled for native tools.
2026-06-19 07:09:40 +02:00
can1357 dcb16d86f6 perf(compaction): kept per-turn tool-result pruning inside the warm prompt-cache prefix
- Added `keepBoundaryId` and `cacheWarmSuffixTokens` guards to `pruneToolOutputs` and `pruneSupersededToolResults` so superseded/useless results sitting in the already-sent cached prefix are no longer rewritten mid-session, with new `computeMessageSuffixTokens`/`resolveBoundaryIndex` helpers.
- Added a `keepBoundaryId` floor to `collectShakeRegions` so shake skips entries summarized away by the latest compaction.
- Threaded `firstKeptEntryId`, `PRUNE_CACHE_WARM_SUFFIX_TOKENS` (8k) and `PRUNE_IDLE_FLUSH_MS` (90m, above the 1h cache TTL) from `#pruneToolOutputs`, `#pruneStaleToolResults` and `shake` in `agent-session.ts`.
- Added six boundary tests in `supersede-prune.test.ts` covering warm-prefix protection, tail-case pruning, and the pre-boundary floor.
2026-06-19 06:57:53 +02:00
can1357 96151e6c30 feat: implemented tool description pruning to reduce token usage
- Added utility functions to strip descriptions from JSON schemas and tool definitions for optimized token output.
- Integrated `pruneToolDescriptions` configuration across agent loops and sessions to enable optional schema pruning.
- Updated agent context and snapshot logic to propagate pruning settings and maintain fingerprinting integrity.
- Verified schema structural integrity and removal of annotation descriptions through new unit tests.
2026-06-19 06:55:19 +02:00
can1357 4f03180ae6 refactor(deps): moved intent field constant to pi-wire
- Moved the `INTENT_FIELD` constant from `@oh-my-pi/pi-agent-core` to the specialized `@oh-my-pi/pi-wire` package to permit broader usage across the monorepo.
- Updated all references across `agent`, `ai`, `coding-agent`, `collab-web`, and `snapcompact` packages to import the constant from the new location.
- Added `@oh-my-pi/pi-wire` as a dependency to all affected packages.
2026-06-19 04:58:29 +02:00
can1357 67abdb1828 feat(agent): prevented internal URLs from appearing in file summaries
- Added an exclusion check to `extractFileOpsFromMessage` to ignore paths containing URL schemes (e.g., `artifact://`, `conflict://`, `https://`).
- Implemented `isUrlSchemePath` helper to identify non-filesystem resources that cannot be re-grounded by the agent.
- Added comprehensive unit tests to verify exclusion logic for various scheme-prefixed paths.
2026-06-18 19:38:31 +02:00
can1357 291b3c74c2 feat: enhanced model reasoning, schema normalization, and loop guarding
- Integrated comprehensive loop guard support for DeepSeek and assistant prose patterns, including configurable stream checks.
- Implemented Moonshot Flavored JSON Schema (MFJS) normalization for improved tool compatibility and enum type inference.
- Added support for Ollama reasoning effort backfilling and Grok-specific service tier cost tracking across providers.
- Expanded model catalog with new entries and unified compatibility logic for improved OpenRouter API integration.
2026-06-18 04:51:43 +02:00
can1357 7c295faa52 feat: added native support for ArkType schemas alongside Zod
- Added native support for parsing, normalized validation, and serialization of ArkType schemas throughout the agent pipeline.
- Implemented pruning of unconstrained union branches and normalization helpers to handle unrepresentable strict-mode branches.
- Patched ArkType's schema package to preserve declared object key order during serialization.
- Integrated ArkType schemas into `agent-loop` and migrated coding agent tool parameters to ArkType format.
2026-06-18 00:59:56 +02:00
can1357 e92db73ec6 feat(coding-agent/tools): added explicit ArkType schema descriptions to agent tools
- Added explicit ArkType schema descriptions across all coding agent tool definitions.
- Updated schema definitions in autoresearch and commit tools with descriptive wrappers.
- Documented tool schema enhancements in the packages/coding-agent CHANGELOG.
2026-06-18 00:59:55 +02:00
can1357 a050474af7 feat: migrated validation schemas and tool definitions from Zod to ArkType
- Migrated all wire protocol, schema definitions, and tools validation from Zod to ArkType across multiple packages.
- Updated extension runtimes, custom tools loader, and TypeBox compatibility shim to expose and use ArkType instances.
- Added a comprehensive ArkType migration guide, validation parity tests, and helper utilities.
- Removed redundant PDF asset routing and parsing implementations from the read tool.
2026-06-18 00:59:53 +02:00
can1357 0d9ec354e7 fix: fixed Gemini thinking-loop handling across streaming dispatch paths
- Added Gemini thinking-loop detection helpers for near-duplicate and verbatim output checks.
- Wrapped `stream`, `streamPiNative`, and `streamSimple` dispatches with the loop guard.
- Emitted retryable empty-content loop errors and stopped completion events on loop hits.
- Added `enableGeminiThinkingLoopGuard` options for OpenAI compatibility with Gemini defaults and overrides.
2026-06-17 13:44:33 +02:00
can1357 5157de5b16 fix(agent): enforce deadline at tool-execution, yield, and in-flight boundaries
The deadline was only checked at loop entry and the top of the inner loop,
so a provider response returning tool calls after the deadline still
executed them, and a deadline crossed during onBeforeYield could dequeue
then drop queued steering/follow-up messages.

- Re-check the deadline after streamAssistantResponse before running tools;
  pair leftover tool calls with aborted "Deadline exceeded" placeholders to
  keep the tool_use/tool_result contract valid.
- Re-check after emitTurnEnd and after onBeforeYield before draining the
  steering/aside/follow-up queues so queued messages are never dequeued and
  dropped.
- Merge a deadline AbortController into the loop signal so in-flight provider
  requests and tools are cancelled at the boundary.
2026-06-17 12:44:23 +02:00
usr_bin_roygbivandcan1357 39169c8878 feat: add max-time deadline support 2026-06-17 12:44:23 +02:00
Alexander Kirilinandcan1357 89d2ed8f66 fix(minimax): expose owned dialect selectors 2026-06-17 12:24:49 +02:00
can1357 c081eb22d9 Merge PR #1879: fix(agent): handle string systemPrompt in telemetry content capture 2026-06-15 19:46:11 +02:00
can1357 c3d6c9d652 fix(agent): renamed owned dialect env var to PI_DIALECT
- Replaced `PI_OWNED_TOOLS` lookups and owned-dialect documentation in `agent-loop.ts` and `types.ts` with `PI_DIALECT`.
- Added `prompt-tools-loop.test.ts` coverage that an unset `config.dialect` still selects Hermes from `Bun.env.PI_DIALECT`.
- Documented the breaking `PI_DIALECT` rename in `packages/agent/CHANGELOG.md`.
2026-06-15 14:01:12 +02:00
can1357 e1814d9a08 refactor: renamed grammar module to dialect with unified transcript rendering
- Renamed ToolCallSyntax type to Dialect and Grammar interface to DialectDefinition across all packages.
- Moved grammar directory to dialect and updated all import paths in agent, ai, catalog, and coding-agent packages.
- Added renderTranscript and renderThinking methods to DialectDefinition, enabling native dialect-aware conversation serialization.
- Consolidated rendering utilities into new dialect/rendering.ts with shared helpers for ChatML, legacy text, and dialect-specific formatting.
- Updated conversation serialization in agent and coding-agent to use dialect.renderTranscript() for native turn envelope rendering.
2026-06-15 13:58:12 +02:00
can1357 f40e528073 fix(agent/compaction): skipped truncating tiny tool outputs during pruning
- Added a 50-token minimum floor constant for pruning decisions.
- Allowed non-superseded, non-useless tool results below that floor to avoid truncation.
2026-06-15 12:45:21 +02:00