Commit Graph
63 Commits
Author SHA1 Message Date
Jack Chen 4fad8c19d3 feat(core): add eval CLI and report outputs (#405) 2026-07-19 00:02:29 +08:00
Jack Chen cedbd796e6 feat(core): add EvalSet offline runner (#404) 2026-07-18 23:26:28 +08:00
Jack Chen 452c193eff feat(core): add evaluation scorer contracts (#403) 2026-07-18 22:53:52 +08:00
Jack Chen 5400dcc65a feat(core): add per-run metadata channel (#396) 2026-07-18 15:39:32 +08:00
Jack Chen 4db6756eaf feat(core): add offline run viewer for result and trace data (#394)
* feat(core): add unified run viewer

* feat(cli): export run viewer from trace store

* docs(core): add deterministic run viewer demo

* fix(examples): resolve workspace paths in typecheck

* fix(dashboard): center detail status labels

* fix(dashboard): remove DAG status rails

* fix(dashboard): enlarge DAG task cards

* fix(dashboard): clarify run summary header

* docs: align readmes with run viewer
2026-07-18 11:06:27 +08:00
Jack Chen 9338bb4dec chore(observability): complete OBS-5 release readiness (#385)
* chore(observability): complete OBS-5 release readiness

* docs(observability): add v2 architecture to core readme
2026-07-16 18:00:16 +08:00
Jack Chen 6339ee5051 feat(observability): add FileTraceStore reference implementation (#384)
* feat(observability): add FileTraceStore reference

* fix(ci): keep benchmark out of core tarball
2026-07-16 16:50:59 +08:00
Jack Chen 1747afad6f docs(tool-gate): document onToolCall gate + classifier, add runnable example (#383)
Third split of #96 (follow-up to #379). The onToolCall gate shipped in #377 and
the classifyBashCommand classifier in #379; this documents both and adds a
runnable example.

- docs/tool-configuration.md: a "Per-call gating with onToolCall" section (where
  it sits, ToolCallContext/ToolCallDecision, deny returns an error ToolResult not
  a throw, agent overrides orchestrator default, orthogonal to onApproval,
  gated/gateAction/gateReason on the trace with gateReason redacted, not a
  security boundary) plus a shell risk classifier subsection.
- examples/patterns/risk-gated-bash.ts: a security-audit agent that classifies
  each bash command and routes safe/review/high to allow/human-approval/deny.
- AGENTS.md: cross-links in the tool-layer notes + /classifiers in the exports map.

Closes #380
2026-07-16 16:45:43 +08:00
Jack Chen 91eceeb004 Merge branch 'main' into agent/observability-v2-obs-3-otel 2026-07-16 14:53:47 +08:00
Scarab SystemsandJack Chen fa1606cad2 feat(agent): add generic process backend (#378)
* feat(agent): add process backend

* test(agent): cover process backend orchestration

* feat(agent): expose process backend entrypoint

* docs(agent): add process backend example

* docs(agent): document external backend variants

* fix(agent): harden process backend lifecycle

---------

Co-authored-by: Jack Chen <chenkaijie01@gmail.com>
2026-07-16 14:40:26 +08:00
Jack Chen 0d299213f0 fix(otel): resolve links and bound span state (#376) 2026-07-16 00:53:42 +08:00
Jack Chen 4d818a0b59 feat(otel): add optional OpenTelemetry adapter 2026-07-16 00:22:36 +08:00
Jack Chen fd1f515b69 feat(core): add trace store contract and memory reference 2026-07-15 23:59:18 +08:00
Jack Chen 5e049b95fc feat(core): add trace sink lifecycle 2026-07-15 23:06:02 +08:00
Jack Chen 3d29274b4f feat(core): add span lifecycle and hierarchy
Implement TraceRecord v2 start/event/end records, idempotent lifecycle closure, runtime hierarchy and links, legacy trace compatibility, contract coverage, and a reproducible no-sink benchmark.
2026-07-15 22:08:49 +08:00
Jack Chen 58096804a0 feat(core): add run identity and outcome semantics (#371)
* feat(core): add run identity and outcome semantics

* docs: sync Chinese observability guidance
2026-07-15 20:41:25 +08:00
Jack Chen ffb2905017 feat(agent): orchestrate external coding agents over ACP (#204) (#360)
* feat(agent): orchestrate external coding agents over ACP (#204)

Add an AgentBackend seam so an Agent can run on either the LLM AgentRunner
or an external agent. First backend: an ACP client that drives a coding CLI
(e.g. Claude Code via its ACP adapter) as a first-class team member — same
task DAG, shared memory, cascade, and token budget as LLM agents.

- Extract AgentBackend ({ run, stream }) that AgentRunner already implements;
  Agent.getBackend() branches on AgentConfig.backend.
- AcpBackend spawns the agent over stdio JSON-RPC and maps session updates to
  a RunResult (text, tool calls, usage); handles permissions and abort.
- ACP SDK as a lazy optional peer; new /acp subpath export.
- docs/external-agents.md, a hybrid-team example, and tests (in-process fake
  agent + subprocess integration).

Experimental: ACP is young; token accounting maps ACP's single context
figure and client-side fs proxying is not implemented yet.

* fix(agent): harden ACP backend after review (#204)

Follow-up fixes on the ACP external-agent backend (PR #360):

- Token accounting: usage_update.used is a cumulative context-size gauge, not a
  per-turn delta. Record each turn's increment so summing across a reused session
  telescopes to the latest figure instead of double-counting.
- systemPrompt: ACP has no system-prompt field, so prepend the agent's systemPrompt
  to the session's first turn (once). It was previously dropped and only fed
  coordinator routing.
- Abort: when the signal is already aborted before a turn starts, return empty
  output without prompting (no in-flight turn to cancel).
- Startup: clear the cached start() promise on failure so a later run retries
  instead of re-throwing the same rejection forever.
- Permissions: auto-approve now prefers least-privilege allow_once over a
  session-wide allow_always.

Docs (external-agents.md, CLAUDE.md) updated to match. ACP tests 10 to 15;
lint + full suite (1135) green.
2026-07-10 23:26:40 +08:00
Jack Chen fbc3116bff feat(agent): per-agent scoped tool credentials (#362)
Add AgentConfig.credentials, a per-agent secret bag threaded into
ToolUseContext.credentials. Tool code reads context.credentials?.KEY
instead of closing a shared secret over every tool, so each agent holds
only the credentials it was assigned; a compromised subagent can't reach
another agent's secrets. Per-agent bags flow through the existing config
spread, so no orchestrator changes are needed, and the coordinator (built
field-by-field) never receives user tool secrets.

Addresses the credential-scoping half of #19. Per-call risk gating stays
tracked in #96.
2026-07-10 22:10:31 +08:00
Jack Chen 0fab4bce60 feat(memory): redact secrets on the persistence path via RedactingStore (#339) (#359)
Redaction stopped at the observability layer, so secrets an agent emitted into its answer were persisted verbatim to shared memory and on-disk checkpoints. Add RedactingStore, a MemoryStore decorator that scrubs values on write, plus optional custom patterns on the redaction utilities. One wrap of the durable store closes both persistence sinks.

Closes #339.
2026-07-09 22:25:53 +08:00
Jack Chen f4c7bdd9e2 docs: document orchestrator cost budget and refresh cost example model (#361)
- README_zh.md: add the "Cap estimated cost" section and production-checklist
  row that #358 added only to the English package README, keeping EN/ZH in sync
- consensus.md: extend the budget invariant so maxCostBudget is documented at
  the same verify-hook boundary as maxTokenBudget; standalone runConsensus
  tracks tokens only
- README EN/ZH: switch the cost example to gpt-5.4-mini, pricing matching the
  repo's existing cost-tiered-pipeline example

Follow-up documentation for #358.
2026-07-09 17:16:03 +08:00
Jack Chen 2c6d6009a6 docs: remove MiniMax provider community offer and setup guide (#356)
Reverts the sponsor content added in #219:
- Drop the 'Provider community offers' section and MiniMax offer bullet
  from README.md, README_zh.md, and the packages/core mirrors
- Delete docs/providers/minimax.md (offer-specific setup guide)
- Delete .github/brand/minimax-banner.png (used only by that guide)

MiniMax stays supported as a neutral provider: the provider table in
docs/providers.md, the MINIMAX_BASE_URL note in docs/cli.md, the
examples/providers/minimax.ts sample, and contributor credits are all kept.
2026-07-06 15:08:03 +08:00
tlysanhuoandsanhuo11 ec7deb6ae5 feat(core): add trace span parent linkage (#354)
Closes #340.

add spanId and optional parentId to every TraceEventBase
generate stable agent span IDs so LLM/tool spans can point to their owning agent span
link team-run worker agents under task spans, stream events under worker agent spans, and plan_ready under coordinator decomposition
document the trace parentage model and add focused tests for span linkage

Co-authored-by: sanhuo11 <tlysanhuo@gmail.com>
2026-07-06 10:13:45 +08:00
Jack Chen 79c622a2c2 docs: add plan preview & replay guide and surface it in READMEs (#349) 2026-07-01 22:19:01 +08:00
Jack Chen caef4b31f1 feat(memory): ship FileStore, a zero-dependency durable MemoryStore (#347)
Zero-dependency, filesystem-backed MemoryStore with atomic fsync+rename
writes, so checkpoint/resume survives a process restart out of the box.
Node built-ins only; recommended as the checkpoint store while shared
memory stays in RAM. Includes load-time hardening that rejects non-object
metadata.

Closes #338
2026-07-01 18:27:03 +08:00
Jack Chen 0488362c0d feat(orchestrator): error-aware task retry (classify, jitter, honor abort) (#346)
Error-aware task retry (opt-in via maxRetries > 0):

- Skip retries on provably-terminal failures (4xx client errors except
  408/409/429, token-budget, invalid-message, aborted calls).
- Equal jitter on backoff to avoid lockstep retries against a rate-limited
  provider.
- Honor abortSignal between attempts (no full-backoff sleep or extra attempt
  after cancel).
- Non-streaming path preserves the structured error on AgentRunResult.error so
  classification works off the default runTeam path; test-covered.

Off by default; behavior changes only when retry is enabled.

Closes #337
2026-07-01 17:22:54 +08:00
Jack Chen 98dbc2025e docs(examples): prefix example run commands with packages/core/ (#330)
* docs(examples): prefix example run commands with packages/core/

Examples live under packages/core/, and both READMEs document running them from the repo root (npx tsx packages/core/examples/<path>.ts), but the in-file Run: headers and a few READMEs still used the bare examples/ path. The repo root has no top-level examples/, so copy-pasting those commands from root failed with file-not-found.

Prefixes 67 run/cd commands across 56 files (53 example scripts + examples/README.md + two integration READMEs). Markdown links that use the bare examples/ path are correct relative links and are left unchanged.

* docs: prefix two stale examples/ path references in docs/

context-management.md and tool-configuration.md referenced bare examples/ paths that do not resolve from the repo root. Make the first a relative link (matching the convention in model-routing.md) and prefix the second with packages/core/. Follow-up to the run-command fixes in this PR.
2026-06-24 17:14:23 +08:00
Jack Chen a1986a340a feat(checkpoint): cut snapshot volume and re-run synthesis on runTeam restore (#314)
Cuts the per-task O(N^2) shared-memory snapshot volume (reused-store skip) and re-runs coordinator synthesis on runTeam restore, plus docs/checkpoint.md and README coverage. Closes #20. Refs #294 #312 #313.
2026-06-19 15:23:11 +08:00
Jack Chen 1d37ce6026 refactor(repo): monorepo groundwork for an easier first run (#303)
* refactor(repo): monorepo groundwork for an easier first run

Trying OMA for the first time costs more than it should: a lot to read before anything runs, and no one-command way to watch a team actually work. The plan is to fix that with a `npm create oma` scaffold (a multi-agent demo that runs on the first try) and an npm README that leads with getting started. Neither lands cleanly until core lives in its own package, so this PR does that part first.

This is groundwork, not the payoff: nothing changes for anyone installing `@open-multi-agent/core` today. It turns the repo into an npm-workspaces monorepo with core in `packages/core/`, and splits the README into a GitHub landing page (root) and the npm package page (`packages/core/`). Doing it now, while the repo is small, avoids migrating twice.

The published package is byte-identical: same name, same three runtime dependencies, same entry points, guarded by an isolated-install smoke test. `npm ci` + lint + test (1060) + build pass; the tarball still ships only `dist/`, `README.md`, `LICENSE`, and `package.json`.

* build: restore cross-platform optional deps in package-lock.json

Regenerating the lockfile on macOS pruned the non-host rollup/esbuild optional binaries (npm/cli#4828), so Linux CI could not load @rollup/rollup-linux-x64-gnu and vitest failed to start. Rebuild the lock from the pre-migration base so every platform variant is present again. Verified with npm ci + lint + build + test (1060).

* fix(examples,docs): correct monorepo paths and anchors after the packages/core move

with-vercel-ai-sdk example (pre-existing, surfaced by the move):
- package.json: the @open-multi-agent/core file: link was file:../../, which
  resolves to packages/core/examples (no manifest). Corrected to file:../../..
  (packages/core); predev cd updated to match.
- package-lock.json regenerated: was linking ../.. and pinning a stale core
  1.4.0; now links ../../.. at 1.7.0.
- README: setup paths and the file: note updated.

docs missed by the move:
- docs/cli.md: repo-clone build output is now packages/core/dist/cli/oma.js;
  the quick-start anchor now lives in the package README.
- CONTRIBUTING.md: the architecture anchor and src/ entry-point paths now live
  under packages/core/.
2026-06-18 17:31:52 +08:00
nuthalapativarunandJack Chen 3cde8f900d feat(bedrock): implement native reasoning round-trip via signature (#223) (#302)
* chore: resolve .gitignore merge conflict

* feat(bedrock): implement native reasoning round-trip via signature (#223)

Inbound chat(): extract signature from reasoningText.signature and
redactedContent bytes into ReasoningBlock IR fields.
Inbound stream(): accumulate reasoningContent.signature delta into the
reasoning buffer; stamp onto the block at contentBlockStop.
Outbound: bedrock-provenance blocks with signature echo natively via
reasoningContent.reasoningText.{text,signature}; blocks with redactedData
echo via reasoningContent.redactedContent; unsigned foreign-provenance
blocks fall back to the existing thinking text path.
Upgrade capabilities.echoesReasoning from never to own-issued.
Add 9 new tests covering inbound/outbound round-trip for chat() and stream().
Update llm-adapter-capabilities test to reflect the new capability value.

Closes #223

* fix(bedrock): handle redactedContent delta in stream(), update docs

stream() handled the signature delta on ReasoningContentBlockDelta but
not redactedContent (the delta is a union of text/signature/redactedContent),
so a redacted thinking block over streaming was silently dropped even
though chat() already round-trips it. Buffer redactedContent per content
block index alongside text/signature and flush it into
ReasoningBlock.redactedData (base64), mirroring chat()'s handling, on both
the normal contentBlockStop path and the end-of-stream safety net.

Also updates docs/context-management.md, which still listed Bedrock under
capabilities.echoesReasoning === 'never' pending this exact follow-up; the
table and notes now reflect 'own-issued' with native round-trip on both
chat() and stream() (closes #223).

* Update .gitignore to remove markdown files

Remove specific markdown files from .gitignore

---------

Co-authored-by: Jack Chen <chenkaijie01@gmail.com>
2026-06-17 15:17:55 +08:00
Aarish AlamandJack Chen 7d802922e8 docs: add LiteLLM proxy to OpenAI-compatible providers table (#283)
Co-authored-by: Jack Chen <chenkaijie01@gmail.com>
2026-06-12 02:05:32 +08:00
Jack Chen 5e92601e5b docs: document the model routing policy (#293)
Add docs/model-routing.md covering the opt-in modelRouting policy:
match dimensions, route config, routed phases, and the opt-in /
no-mutation guarantees. Link it from README.md and README_zh.md
following the docs/consensus.md precedent.

Refs #286, #271
2026-06-09 00:28:47 +08:00
Jack Chen 95b769b6f6 fix(tools)!: make built-in tools opt-in (default-deny) (#289)
* fix(tools)!: make built-in tools opt-in (default-deny)

A no-tools agent received every registered built-in, including an unsandboxed
bash, on the default path (runAgent, runTeam/runTasks, and the runTeam
short-circuit). Tool output flows back to the model provider, so under prompt
injection this was a remotely triggerable exec + exfiltration primitive. #83
added the allowlist/preset mechanism but kept default-allow; #264 sandboxed the
filesystem tools but left bash unsandboxed; together they opened this.

Built-in tools now require a positive grant via tools or toolPreset; with
neither, an agent resolves to zero. resolveTools() is the chokepoint and the
runner gates execution on the granted set, so a registered-but-ungranted call
returns a "not granted" error instead of running. Custom/runtime tools
(customTools/addTool) stay available since registration is the grant, and still
honor disallowedTools. delegate_to_agent follows the same rule. Same default-deny
model #87 enforces for cross-agent context.

OrchestratorConfig.defaultToolPreset restores the old allow-all in one line;
per-agent tools/toolPreset override it.

BREAKING CHANGE: agents relying on implicit built-in tools get none until
granted. Restore with defaultToolPreset: 'full', or grant per agent via
tools/toolPreset.

* docs: clarify defaultToolPreset excludes coordinator, synthesis, and consensus agents
2026-06-08 00:27:30 +08:00
6ea377f494 feat(minimax): upgrade default model to MiniMax-M3 (#292)
* feat(minimax): upgrade default model to MiniMax-M3

- Switch the default minimax model hint in CLI from M2.7 to M3
- Update docs/providers.md, docs/providers/minimax.md, examples/providers/minimax.ts
  and README mentions to recommend M3 as the default
- Note that M2.7 and MiniMax-M2.7-highspeed remain selectable for callers
  that pin a model explicitly
- M3 specs: 512K context window, up to 128K output tokens, image input support

* docs(minimax): correct M3 context to 1M, drop unverified output-token figure

---------

Co-authored-by: octo-patch <octo-patch@github.com>
Co-authored-by: Jack Chen <chenkaijie01@gmail.com>
2026-06-07 20:12:18 +08:00
Nuoya Jiang 48c8e6aba3 feat(orchestrator): add consensus / adversarial verification primitive (#280)
`runConsensus()` runs a proposer→judge propose/refute/converge loop, and an opt-in per-task `verify` hook applies the same check to a task's own result. Judges run sequentially to a quorum (refute or lens mode); only an accepted revision supersedes the task output, and judge usage counts against the parent `maxTokenBudget`.

Closes #275
2026-06-07 18:50:03 +08:00
Jack Chen b82eca24e3 chore: release 1.6.0 (#291)
* chore: tidy post-1.5.0 docs/config nits

* chore: release 1.6.0
2026-06-07 01:20:40 +08:00
Kaitlyn FengandCursor 6062ba3c8e feat(llm): add Tencent Hunyuan provider adapter (#281)
* feat(llm): add Tencent Hunyuan provider adapter

Add a built-in `hunyuan` provider shortcut wrapping the OpenAI-compatible
Tencent Hunyuan API, following the existing doubao/qiniu thin-wrapper pattern.

- Default endpoint is the current Tencent MaaS / TokenHub surface
  (https://tokenhub.tencentmaas.com/v1, `sk-...` keys, models like
  `hy3-preview`). Tencent has announced the legacy Tencent Cloud endpoint
  (https://api.hunyuan.cloud.tencent.com/v1) is being retired (sales stop
  2026-06-30, full shutdown 2026-09-30); it remains reachable via
  HUNYUAN_BASE_URL until then.
- HUNYUAN_API_KEY for credentials, optional HUNYUAN_BASE_URL to switch
  clusters without code changes (mirrors the MiMo/MiniMax convention).
- Register in createAdapter factory, SupportedProvider, CLI provider
  reference and model hint.
- Add adapter unit tests, a provider example, and provider docs.

Verified chat + function calling end-to-end through the framework adapter
interface against both surfaces: `hy3-preview` on TokenHub and
`hunyuan-turbos-latest` on the legacy Tencent Cloud endpoint.

* docs(hunyuan): align example label and adapter comments with MaaS/hy3 default

The adapter now defaults to the Tencent MaaS / TokenHub endpoint with the
hy3-preview model. Update the example index label (TurboS -> MaaS, hy3) and the
adapter docstring's default model / endpoint references to match.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(hunyuan): scope tool-calling claims to verified models

* fix(llm): echo reasoning_content for Hunyuan interleaved thinking

hy3-preview (the default Hunyuan model) is a reasoning model whose
interleaved-thinking + tool-calling mode requires the prior turn's
reasoning_content to be backfilled on each tool-using turn per Tencent's
TokenHub spec; HunyuanAdapter inherited OpenAI's 'never'. Set
echoesReasoning: 'tool-use-only' (same as DeepSeek/MiMo). turbos/functioncall
emit no reasoning_content, so it is a no-op for them.

Refs #281

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 01:29:12 +08:00
Jack Chen 1a6021b9a5 feat(sandbox): narrow default root to .agent-workspace subdir
Sandbox default root narrows from `process.cwd()` to `<cwd>/.agent-workspace`, an auto-created subdirectory. Built-in filesystem tools (`file_read`, `file_write`, `file_edit`, `grep`, `glob`) enforce this root and require absolute paths. `bash` is intentionally out of scope; full caveat and the three-config knob (default / `process.cwd()` / `null`) live in `docs/tool-configuration.md`.

`defaultWorkspaceDir()` centralises the fallback; `OrchestratorConfig.defaultCwd`, `AgentConfig.cwd`, `RunnerOptions.cwd`, and the tool-layer fallbacks in `path-safety`, `grep`, and `glob` all route through it. Only `file_write` auto-creates the root via `resolvePathWithinCwd`'s new `ensureRoot` option; read-only tools error on a missing root.

22 examples switched from `/tmp/` to `<cwd>/.agent-workspace/<sub>` (basics x 3, providers x 18, integrations x 1). `mcp-bilig-workpaper` routes its existing tmpdir workDir into the Agent's `cwd`.

`README.md`, `README_zh.md`, `docs/tool-configuration.md`, `docs/cli.md`, `docs/providers/minimax.md` updated. JSDoc on the four `cwd` fields aligned with the new default. `.gitignore` ignores `.agent-workspace/`.

`v1.4.2` is current on npm; no released version is affected. Consumers wanting the prior `process.cwd()` default can set `defaultCwd: process.cwd()` explicitly.
2026-05-28 19:34:18 +08:00
Jack Chen 794da6fcc1 feat(tools): per-agent filesystem sandbox (#264)
Sandbox built-in filesystem tools (`file_read`, `file_write`, `file_edit`, `grep`, `glob`) to a per-agent working directory. Paths must be absolute and resolve inside that directory; symlinks are resolved via `realpath` before the check and skipped during recursive walks via `lstat`. New config: `AgentConfig.cwd`, `OrchestratorConfig.defaultCwd`, `CoordinatorConfig.cwd` (per-agent override > orchestrator default > `process.cwd()`).

`bash` is deliberately not sandboxed; pass `disallowedTools: ['bash']` if path containment matters. Process-group cleanup is now in place so backgrounded children no longer outlive timeouts or aborts; aborted runs exit 130.

`mcp.ts` forwards `ToolUseContext.abortSignal` into `callTool` so MCP tools observe agent cancellations.

`resolvePathWithinCwd` returns the realpath-resolved candidate (rather than the raw input) so callers hand a symlink-free path to fs APIs, closing the TOCTOU window between the sandbox check and the fs call. The new `realpathTolerant` helper handles non-existent paths (e.g. `file_write` creating a new file) by resolving the longest existing prefix and re-attaching the suffix.

**Behavior change.** Sandbox is on by default. Agents that previously read or wrote files outside \`process.cwd()\`, or used relative paths, now get a sandbox error. Set \`OrchestratorConfig.defaultCwd\` to your workspace root for multi-directory deployments, or pass \`defaultCwd: null\` to restore unrestricted access.
2026-05-28 17:20:14 +08:00
zihao Li 4bd2f639e8 feat: add MiMo provider adapter (#265)
First-class `provider: 'mimo'` backed by the OpenAI-compatible adapter, defaulting to https://api.xiaomimimo.com/v1 and MIMO_API_KEY. Token Plan keys (tp-*) read MIMO_BASE_URL for the cluster URL.

Overrides `echoesReasoning` to 'tool-use-only' so tool-calling agents replay reasoning_content correctly, matching DeepSeek V4 (#251).

Refs #25.
2026-05-28 15:47:53 +08:00
MyPrototypeWhat cb0b1ab667 feat(llm): cross-provider reasoning text fallback with opt-in (#223 Phase 2) (#260)
* feat(llm): cross-provider reasoning text fallback with opt-in (#223 Phase 2)

Phase 2 of #223 — wires the shared `<thinking>` text fallback into every
adapter's outbound path behind two new opt-in flags.

User-facing API (`AgentConfig` / `LLMChatOptions`):
- `preserveReasoningAsText?: boolean` — default `false`. When `true`,
  outbound IR-to-native conversion downgrades each `ReasoningBlock` that
  the target adapter cannot natively echo to inline `<thinking>` text and
  prepends it to the next text part (rather than dropping silently).
- `compressReasoningText?: boolean | { minChars?: number }` — defaults
  default-on when `preserveReasoningAsText` is true (per maintainer's #223
  guidance to "tie compress default-on to the preserve flag"). Mirrors the
  shape of `compressToolResults`; head+tail truncation defaults to 1200
  chars per block.

Adapter wiring:
- openai-common.ts: refactored to drop the never-wired (since #234)
  `OpenAIReasoningReplayOptions` type in favour of reading the AgentConfig
  flags. Replaced the private `reasoningBlockToThinkingText` /
  `truncateReasoningForReplay` helpers with the shared
  `reasoningBlockToInlineText` introduced in Phase 1.
- openai.ts / azure-openai.ts / copilot.ts pass the new flags through
  `buildOpenAIMessageList`. Subclasses (deepseek, grok, qiniu, minimax)
  inherit chat() so the wiring propagates automatically.
- anthropic.ts: foreign-provenance reasoning falls back to plain text when
  opt-in is set; own-provenance signed blocks still native-echo (Phase 1
  contract unchanged); own-provenance UNSIGNED blocks now also fall back
  to text rather than dropping silently (was always wasted otherwise).
- gemini.ts: same own/foreign-provenance pattern as anthropic, with the
  same outbound capability gate.
- bedrock.ts: capability is `'never'` (until the deferred signature
  follow-up restores native echo); all reasoning falls back to text when
  opt-in is set. Emits a standalone text block since Bedrock messages
  accept multiple content blocks.
- ai-sdk.ts: capability `'never'` — when opt-in is set, swaps the
  AI SDK structured `{type: 'reasoning'}` part for a text part wrapped
  in `<thinking>`. Default-off preserves pre-Phase-2 pass-through behaviour
  to avoid silently changing what existing users have today.

Tests:
- Refreshed openai-common.test.ts to use the new flag names. Added cases
  for `compressReasoningText: false` (no truncation) and the default-on
  semantics when preserve is true and compress is undefined.
- Added per-adapter `reasoning text fallback (#223 Phase 2)` describe
  blocks to anthropic, gemini, bedrock, and ai-sdk test files. Each
  covers the default-off drop, the opt-in text-fallback, the
  own-provenance native echo path, and (for anthropic) the redacted
  placeholder.
- Updated tests/reasoning-fallback.test.ts so `maxChars: Infinity` now
  asserts pass-through (no truncation) rather than clamp-to-1. The
  positive-Infinity sentinel is how callers signal
  `compressReasoningText: false`.

Docs + example:
- docs/context-management.md gains a "Preserving Reasoning Across
  Providers" section with capability table, default behaviour rules,
  and the loop-detector caveat for local OpenAI-compatible models.
- examples/patterns/cross-provider-reasoning.ts demonstrates the
  Agent.prompt() model-swap scenario and documents the loop-detector
  failure mode.

Refs #223

* test(llm): tighten reasoning-flag propagation test scope (#223 Phase 2 self-review)

Address self-review feedback before opening the Phase 2 PR:

- tests/runner-reasoning-flags.test.ts: rewrite header to honestly describe
  what is covered. `AgentRunner.stream()` internally drives turns via
  `adapter.chat()` (`runner.ts:766`) — `adapter.stream()` is on the
  `LLMAdapter` interface but unused by the runner. The previous "stream path"
  claim was therefore testing a non-existent code path. Drop the dead
  `capturedStream` instrumentation; replace it with a `runner.stream()`
  entrypoint test that exercises the same `adapter.chat()` propagation,
  pinning the contract for the stream-side flag flow.

- tests/reasoning-fallback.test.ts: add a runtime-invariant test for the
  `NO_TRUNCATION` constant. The Phase 2 commit exported the sentinel and
  documented it in JSDoc, but had no test that would CI-fail if a future
  refactor removed the `Number.POSITIVE_INFINITY` special-case branch in
  `resolveMaxChars`. The new assertion ties the constant value, the
  resolver branch, and the documented "no truncation" contract together.

Refs #223

* docs(context-management): acknowledge tool-use-only third capability state

After merging upstream/main (which added 'tool-use-only' for DeepSeek V4
via #251), update the capability table to include the new state and its
two sub-rules:
  - tool-calling conversation → native reasoning_content echo
  - non-tool conversation     → drop (DeepSeek spec)

Also clarify that 'tool-use-only' is the only capability where
same-provider native echo happens WITHOUT preserveReasoningAsText opt-in
(the DeepSeek API forces it for protocol compliance).

Refs #223, #251

* fix(llm): add missing nativeReasoningEchoProvider to ReasoningOutboundOptions

Repair an omission from merge commit 9ca9a91: the field was added to my
working tree alongside the openai-common.ts conflict resolution, but
never staged — so the merge commit landed openai-common.ts referencing
`outboundOptions?.nativeReasoningEchoProvider` while the type declaration
on the receiving end (`ReasoningOutboundOptions` in reasoning-fallback.ts)
did not carry the field.

Locally the bug was masked because vitest runs against the working tree
where the field exists; `npm test` reported 869/874 passing. The pushed
branch would compile-fail at the openai-common.ts access site for any
reviewer pulling the PR.

This commit just adds the field declaration + its JSDoc (already used
correctly throughout openai-common.ts and openai.ts). No behaviour change
relative to what the merge intended.

Refs #223, #251
2026-05-28 15:17:36 +08:00
Jack Chen 6b6330208c fix: redact secrets in traces, bash env, and dashboard payloads (#263)
* fix: redact secrets in traces, bash env, and dashboard payloads

Adds src/utils/redaction.ts and applies best-effort redaction at three
sinks where sensitive data could leak:
- tool_call trace events (input, output)
- the bash tool's output buffer
- the post-run dashboard's embedded JSON payload

The bash tool no longer inherits the parent process.env. Child processes
receive a fixed allowlist (HOME, LANG, LC_ALL, LOGNAME, PATH, SHELL,
TEMP, TERM, TMP, TMPDIR, USER), so provider API keys and similar
secrets are no longer visible to spawned shells.

The post-run dashboard no longer loads Tailwind CDN, Google Fonts, or
Material Symbols at view time. The page is self-contained and renders
without network access.

llmMessagesToAiSdkModelMessages no longer concatenates the opaque
redactedData payload into the reasoning text emitted to the AI SDK; it
emits only the [redacted_thinking] marker.

The MCP GitHub example uses --no-install instead of -y and passes an
explicit env allowlist instead of spreading process.env.

* fix(redaction): consume full value for quoted and unquoted assignments

The single ASSIGNMENT_PATTERN stopped at the first whitespace inside a
value, so values with spaces were only partially redacted and the
remainder leaked into trace events, dashboard payloads, and the bash
output buffer. Three reviewer-reported cases on PR #263:

  password=secret value     -> password=[redacted] value
  password="secret value"   -> password="[redacted]" value"
  apiKey: "abc def"         -> apiKey: "[redacted]" def"

Splits the single pattern into two passes. The quoted pass consumes
to the back-referenced closing quote so password="my pass" is fully
matched. The unquoted pass consumes to the next structural delimiter
(comma, semicolon, brace, bracket, newline) so password=my pass is
fully matched.

Adds tests/redaction.test.ts covering the three reviewer cases, the
existing Authorization orphan-bracket regression, common token literal
patterns, and idempotence.
2026-05-28 00:53:34 +08:00
zihao Li 28c7d5cff1 feat: add Doubao (Volcengine) provider shortcut (#261)
First-class `provider: 'doubao'` backed by the OpenAI-compatible adapter, defaulting to the Volcengine Ark endpoint and ARK_API_KEY env var.

Refs #25 (Doubao section). Original first-class attempt in #236 closed under the duplicate-claim rule when #232 was active.
2026-05-27 20:33:58 +08:00
Zhou Chunhao 289d6b8ebc docs: add Moonshot provider example (#259) 2026-05-27 19:46:29 +08:00
Zhou Chunhao 1d971211b6 docs: add qwen provider example (#257) 2026-05-27 19:32:42 +08:00
Jack Chen b5ad53ddaa feat(deepseek): migrate defaults to V4 model names (#250)
DeepSeek retires deepseek-chat / deepseek-reasoner on 2026-07-24.
Switch DEFAULT_MODEL_HINT and examples to deepseek-v4-flash; upgrade
the examples/providers/deepseek.ts architect to deepseek-v4-pro since
deepseek-reasoner officially maps to v4-flash thinking mode and v4-pro
is the upstream "best for coding" pick. Adapter code unchanged.
2026-05-26 15:03:16 +08:00
Jack Chen 665457d91b docs: integrations governance README + retire stale DECISIONS.md (#248)
* docs(examples/integrations): add governance README

Documents the two example categories under examples/integrations/:

- Reference integrations: wiring against widely used protocols or
  frameworks (MCP, observability, Express, AI SDK). OMA team maintains
  these once merged, regardless of who contributed them.
- Vendor integrations: examples paired with a specific third-party
  commercial product. Reciprocal listing condition; no payment involved.

Adds submission flow for both categories and out-of-scope guidance.
Existing entries are credited to their contributors inline.

* docs: remove stale DECISIONS.md

Last updated 2026-04-09. The "Open to Adoption" section listed MCP as
up-next with #86 tracking, but MCP shipped weeks ago: tool/mcp.ts, the
@open-multi-agent/core/mcp subpath, and the mcp-github.ts example are
all in. Tracked issues (#20, #86, and the related #17) are all closed.

The "Won't Do" rationales for state persistence and agent handoffs are
preserved in their respective closed issue threads. The A2A
network-effect framing belongs in the README or internal strategy
notes, not a standalone infrequently-maintained doc.

Zero references in the repo. No broken links.
2026-05-25 11:28:21 +08:00
Jack Chen 3d5e8301d0 docs(providers): re-add Doubao row dropped in #239 (#242)
#239 removed the Doubao row because it pointed to a missing example and a sunset model ID. Both fixed now: example landed in #232 using doubao-1-5-pro-32k-250115.
2026-05-20 02:40:36 +08:00
Jack Chen 6155cec22a docs(providers): drop Doubao row referencing missing example (#239)
#231 added a row pointing to examples/providers/doubao.ts (expected to
land via #232, still open) and using doubao-pro-32k (sunset by Volcengine
Ark on 2025-05-28). Users clicking through from the README hit both a 404
on the example link and an invalid model ID.
2026-05-19 00:35:52 +08:00
nuthalapativarun 3b55c4dd0e docs(context-management): document compressToolResults and maxToolOutputChars (#233) 2026-05-18 23:07:36 +08:00
nuthalapativarun b6af2d92bd feat(examples): add Zhipu GLM provider example (#231) 2026-05-18 18:53:30 +08:00