okengine 0.22.0 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +7 -0
- package/manifest.v1.schema.json +44 -0
- package/package.json +32 -19
- package/site/content/docs/ai/mcp.mdx +14 -10
- package/site/content/docs/ai/skills.mdx +1 -1
- package/site/content/docs/elements/ai/agents.mdx +61 -13
- package/site/content/docs/elements/ai/decide.mdx +536 -0
- package/site/content/docs/elements/ai/events.mdx +373 -0
- package/site/content/docs/elements/ai/index.mdx +9 -3
- package/site/content/docs/elements/ai/meta.json +1 -1
- package/site/content/docs/elements/ai/prompts.mdx +4 -2
- package/site/content/docs/elements/channel/index.mdx +4 -4
- package/site/content/docs/elements/channel/receipts.mdx +7 -5
- package/site/content/docs/elements/channel/sms.mdx +2 -1
- package/site/content/docs/elements/flow/index.mdx +22 -18
- package/site/content/docs/elements/gate/auth.mdx +4 -1
- package/site/content/docs/elements/signal/index.mdx +15 -14
- package/site/content/docs/elements/store/index.mdx +1 -1
- package/site/content/docs/elements/store/kv.mdx +5 -5
- package/site/content/docs/plugins/otp.mdx +3 -3
- package/site/content/docs/recipes/dragonfly.mdx +5 -5
- package/site/content/docs/recipes/index.mdx +6 -6
- package/site/content/docs/recipes/postgres.mdx +12 -8
- package/site/content/docs/recipes/redis.mdx +4 -4
- package/site/content/docs/reference/cli.mdx +2 -1
- package/site/content/docs/reference/configuration.mdx +9 -7
- package/site/content/docs/reference/fx.mdx +19 -10
- package/src/auth/api-key-sql.test.ts +44 -0
- package/src/auth/api-key-sql.ts +1 -0
- package/src/auth/api-keys.ts +30 -12
- package/src/auth/config.ts +6 -2
- package/src/auth/gate-auth.test.ts +20 -0
- package/src/auth/tenants.ts +17 -0
- package/src/cli/agents-md.test.ts +25 -0
- package/src/cli/ask-vault-gaps.test.ts +38 -1
- package/src/cli/ask-vault-gaps.ts +35 -2
- package/src/cli/decide.ts +174 -0
- package/src/cli/decision-lock-watch.test.ts +45 -0
- package/src/cli/decision-lock-watch.ts +51 -0
- package/src/cli/dev-app-runner.ts +34 -8
- package/src/cli/dev-auth-secret.test.ts +49 -0
- package/src/cli/dev-auth-secret.ts +49 -0
- package/src/cli/dev-hot.test.ts +41 -0
- package/src/cli/dev-hot.ts +48 -0
- package/src/cli/dev.test.ts +1 -1
- package/src/cli/dev.ts +24 -6
- package/src/cli/doctor-diff.test.ts +29 -1
- package/src/cli/doctor-diff.ts +35 -0
- package/src/cli/eval.ts +142 -1
- package/src/cli/index.ts +5 -0
- package/src/cli/load-config.images.test.ts +2 -2
- package/src/cli/load-config.ts +1 -1
- package/src/cli/manifest-pr-diff.ts +16 -0
- package/src/cli/mcp-from-console.ts +48 -0
- package/src/cli/registry.ts +35 -1
- package/src/client/agent.ts +307 -0
- package/src/client/transport.test.ts +20 -0
- package/src/client/transport.ts +2 -1
- package/src/client/types.ts +1 -1
- package/src/client-react/index.ts +7 -1
- package/src/client-react/use-agent-run.test.ts +271 -0
- package/src/client-react/use-agent-run.ts +193 -0
- package/src/compiler/ai-approval.test.ts +36 -0
- package/src/compiler/ai-repair.test.ts +22 -0
- package/src/compiler/decisions.extract.test.ts +164 -0
- package/src/compiler/effects-fetch.test.ts +77 -0
- package/src/compiler/effects-infer.ts +114 -17
- package/src/compiler/effects-join.test.ts +48 -0
- package/src/compiler/extract.test.ts +28 -0
- package/src/compiler/extract.ts +279 -13
- package/src/compiler/fx-index.ts +58 -4
- package/src/compiler/response.ts +35 -2
- package/src/config/index.ts +21 -3
- package/src/console/index.ts +1 -1
- package/src/console/server/ai-runs-flows.ts +269 -0
- package/src/console/server/ai-runs.test.ts +323 -0
- package/src/console/server/ai-runs.ts +562 -0
- package/src/console/server/ai.ts +12 -1
- package/src/console/server/app.ts +10 -1
- package/src/console/server/decisions-flows.ts +157 -0
- package/src/console/server/decisions.test.ts +66 -0
- package/src/console/server/decisions.ts +229 -0
- package/src/console/server/flows.ts +50 -2
- package/src/console/server/invoke-user-flow.ts +9 -0
- package/src/console/server/runs-ingest.ts +16 -0
- package/src/console/server/serve.ts +5 -0
- package/src/console/server/state.ts +25 -1
- package/src/console/server/store-stats.test.ts +47 -0
- package/src/console/server/store-stats.ts +92 -18
- package/src/console/ui-next/dist/assets/access-page-C_N4qvai.js +4 -0
- package/src/console/ui-next/dist/assets/{agent-disclosure-Bkohc_8X.js → agent-disclosure-Cq1TxniW.js} +1 -1
- package/src/console/ui-next/dist/assets/{cache-glyph-rL0lHxat.js → cache-glyph-BaI4uxZz.js} +1 -1
- package/src/console/ui-next/dist/assets/{call-pii-button-Deo4PP18.js → call-pii-button-DNyK7IIY.js} +1 -1
- package/src/console/ui-next/dist/assets/{collapsible-CoJ6amHf.js → collapsible-CRhBJMZR.js} +1 -1
- package/src/console/ui-next/dist/assets/confirm-sheet-DZfuJXP4.js +1 -0
- package/src/console/ui-next/dist/assets/copy-inline-button-BTjPVjRT.js +1 -0
- package/src/console/ui-next/dist/assets/decisions-page-D9twj8Xy.js +1 -0
- package/src/console/ui-next/dist/assets/detail-header-Dk5LipF6.js +1 -0
- package/src/console/ui-next/dist/assets/dropdown-menu-CRGrj5JU.js +1 -0
- package/src/console/ui-next/dist/assets/duration-tone-CsHWlVPU.js +9 -0
- package/src/console/ui-next/dist/assets/element-icons-C6t8Vlml.js +1 -0
- package/src/console/ui-next/dist/assets/explorer-chrome-Dslh2yE8.js +1 -0
- package/src/console/ui-next/dist/assets/explorer-empty-CIVg7Jgb.js +1 -0
- package/src/console/ui-next/dist/assets/flows-page-ZaTJAhR9.js +1 -0
- package/src/console/ui-next/dist/assets/{highlighted-json-CPYBzDh0.js → highlighted-json-DQ-EMAfz.js} +1 -1
- package/src/console/ui-next/dist/assets/{http-method-DTPevKmM.js → http-method-Cn_Fl79s.js} +1 -1
- package/src/console/ui-next/dist/assets/index-BlrN49Hs.css +2 -0
- package/src/console/ui-next/dist/assets/index-CmRFeWdT.js +58 -0
- package/src/console/ui-next/dist/assets/observability-page-Cn6R_dXz.js +8 -0
- package/src/console/ui-next/dist/assets/{replica-lag-BM27lXld.js → replica-lag-Bx6EZhSe.js} +7 -7
- package/src/console/ui-next/dist/assets/request-meta-DmdiUrfh.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-keys-DTE1sjTC.js +1 -0
- package/src/console/ui-next/dist/assets/store-page-8P6vGXnn.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-BfGvpFSv.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-CWdUHbc0.js +55 -0
- package/src/console/ui-next/dist/assets/units-page-CZOFZzay.js +1 -0
- package/src/console/ui-next/dist/assets/use-vault-list-BAyQG8Re.js +1 -0
- package/src/console/ui-next/dist/assets/vault-page-ki_ishmX.js +2 -0
- package/src/console/ui-next/dist/index.html +4 -3
- package/src/console/ui-next/seed-parked-approval.ts +126 -0
- package/src/console/ui-next/src/client.ts +224 -1
- package/src/console/ui-next/src/components/explorer/detail-header.tsx +9 -0
- package/src/console/ui-next/src/components/explorer/explorer-start-toggle.tsx +5 -1
- package/src/console/ui-next/src/components/ui/sheet-form.tsx +21 -12
- package/src/console/ui-next/src/features/flows/decisions/decisions-page.tsx +216 -0
- package/src/console/ui-next/src/features/flows/decisions/resolve.test.ts +48 -0
- package/src/console/ui-next/src/features/flows/decisions/resolve.ts +111 -0
- package/src/console/ui-next/src/features/flows/flows-page.tsx +10 -0
- package/src/console/ui-next/src/features/flows/graph/element-map.ts +1 -0
- package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +6 -0
- package/src/console/ui-next/src/features/flows/traces/effect-summary.test.ts +24 -0
- package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +24 -0
- package/src/console/ui-next/src/features/flows/traces/http-status.ts +58 -0
- package/src/console/ui-next/src/features/flows/traces/request-client-card.tsx +355 -0
- package/src/console/ui-next/src/features/flows/traces/request-client.test.ts +114 -0
- package/src/console/ui-next/src/features/flows/traces/request-client.ts +423 -0
- package/src/console/ui-next/src/features/flows/traces/request-input-view.test.ts +52 -6
- package/src/console/ui-next/src/features/flows/traces/request-input-view.ts +85 -19
- package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +505 -80
- package/src/console/ui-next/src/features/flows/traces/trace-lanes.test.ts +61 -0
- package/src/console/ui-next/src/features/flows/traces/trace-lanes.ts +78 -0
- package/src/console/ui-next/src/features/flows/traces/trace-nav.test.ts +48 -0
- package/src/console/ui-next/src/features/flows/traces/trace-nav.ts +57 -0
- package/src/console/ui-next/src/features/flows/traces/trace-request-section.tsx +275 -96
- package/src/console/ui-next/src/features/flows/traces/traces-pane.tsx +16 -2
- package/src/console/ui-next/src/features/observability/detail/ai-runs.tsx +369 -0
- package/src/console/ui-next/src/features/observability/observability-page.tsx +31 -1
- package/src/console/ui-next/src/features/observability/state/observability-selection.ts +18 -3
- package/src/console/ui-next/src/features/store/performance/kv-performance-panel.tsx +12 -1
- package/src/console/ui-next/src/features/store/performance/performance-panel.tsx +14 -1
- package/src/console/ui-next/src/features/units/call/call-api-panel.tsx +1 -1
- package/src/console/ui-next/src/features/units/detail/flow-contract-panel.tsx +6 -2
- package/src/console/ui-next/src/features/units/explorer/units-tree.tsx +73 -18
- package/src/console/ui-next/src/features/units/lib/unit-tree.test.ts +21 -0
- package/src/console/ui-next/src/features/units/lib/unit-tree.ts +82 -0
- package/src/console/ui-next/src/features/units/units-page.tsx +25 -7
- package/src/console/ui-next/src/router.tsx +17 -0
- package/src/console/ui-next/ui-next-seed-runs.ts +23 -0
- package/src/console/ui-next/vite-console-kernel-plugin.ts +43 -3
- package/src/console/ui-next/vite.config.ts +9 -0
- package/src/docker/recipes/dragonfly.ts +3 -2
- package/src/docker/recipes/redis.ts +1 -1
- package/src/drivers/ai-anthropic.ts +212 -21
- package/src/drivers/ai-mock.ts +34 -1
- package/src/drivers/ai-openai-compatible.ts +67 -26
- package/src/drivers/ai-preconnect.ts +18 -0
- package/src/drivers/ai-stream.test.ts +62 -0
- package/src/drivers/ai-types.ts +20 -0
- package/src/drivers/journal-postgres.test.ts +22 -1
- package/src/drivers/journal-postgres.ts +511 -13
- package/src/drivers/memory-ddl.test.ts +18 -0
- package/src/drivers/memory.ts +21 -1
- package/src/drivers/pg-rls.test.ts +31 -0
- package/src/drivers/pg-rls.ts +15 -0
- package/src/drivers/postgres.test.ts +9 -0
- package/src/drivers/postgres.ts +49 -11
- package/src/drivers/signal-compete.test.ts +234 -0
- package/src/drivers/signal-postgres.ts +39 -19
- package/src/drivers/signal-redis.ts +167 -11
- package/src/drivers/signal-types.ts +12 -0
- package/src/elements/ai/agent-event-slot.ts +27 -0
- package/src/elements/ai/approval-http.ts +207 -0
- package/src/elements/ai/approval.test.ts +741 -0
- package/src/elements/ai/approval.ts +228 -0
- package/src/elements/ai/decisions/bind.ts +163 -0
- package/src/elements/ai/decisions/certificate.test.ts +291 -0
- package/src/elements/ai/decisions/certificate.ts +377 -0
- package/src/elements/ai/decisions/certify.ts +314 -0
- package/src/elements/ai/decisions/decide.test.ts +953 -0
- package/src/elements/ai/decisions/e2e.test.ts +399 -0
- package/src/elements/ai/decisions/export.test.ts +120 -0
- package/src/elements/ai/decisions/export.ts +146 -0
- package/src/elements/ai/decisions/fixtures/openrouter-request.json +24 -0
- package/src/elements/ai/decisions/fixtures/openrouter-response.json +22 -0
- package/src/elements/ai/decisions/fixtures/typesafe-request.json +19 -0
- package/src/elements/ai/decisions/fixtures/typesafe-response.json +13 -0
- package/src/elements/ai/decisions/http.test.ts +154 -0
- package/src/elements/ai/decisions/http.ts +240 -0
- package/src/elements/ai/decisions/labels.test.ts +282 -0
- package/src/elements/ai/decisions/labels.ts +257 -0
- package/src/elements/ai/decisions/openrouter.live.test.ts +136 -0
- package/src/elements/ai/decisions/openrouter.ts +36 -0
- package/src/elements/ai/decisions/provider.ts +102 -0
- package/src/elements/ai/decisions/typesafe.ts +36 -0
- package/src/elements/ai/declare.ts +252 -1
- package/src/elements/ai/events.test.ts +379 -0
- package/src/elements/ai/events.ts +138 -0
- package/src/elements/ai/mcp-protocol.ts +5 -6
- package/src/elements/ai/run-events.test.ts +461 -0
- package/src/elements/ai/run-events.ts +522 -0
- package/src/elements/ai/runtime.ts +1187 -105
- package/src/elements/ai/stream-turn.ts +214 -0
- package/src/elements/ai/stream.live.test.ts +76 -0
- package/src/elements/ai/subagent.test.ts +136 -0
- package/src/elements/ai.test.ts +497 -3
- package/src/elements/ai.ts +16 -1
- package/src/elements/channel/runtime.ts +11 -0
- package/src/elements/channel/sql-ledger.ts +251 -0
- package/src/elements/clock/durable.ts +27 -1
- package/src/elements/signal/runtime.ts +9 -0
- package/src/elements/signal.test.ts +1 -0
- package/src/elements/store/cache.test.ts +120 -15
- package/src/elements/store/cache.ts +200 -39
- package/src/elements/store/prepare-row.test.ts +6 -2
- package/src/elements/store/sql-session.test.ts +53 -0
- package/src/elements/store/sql-session.ts +206 -10
- package/src/elements/store.ts +1 -0
- package/src/i18n/catalogs/ar.ts +5 -0
- package/src/i18n/catalogs/en.ts +5 -0
- package/src/index.ts +2 -0
- package/src/kernel/agent-event-store.ts +448 -0
- package/src/kernel/app.ts +135 -17
- package/src/kernel/auto-cache.test.ts +228 -0
- package/src/kernel/boot-bind/ai.ts +3 -0
- package/src/kernel/boot-bind/channel.ts +63 -2
- package/src/kernel/boot-bind/honor-config.test.ts +40 -10
- package/src/kernel/boot-bind/signal.ts +61 -25
- package/src/kernel/boot-bind/store.test.ts +26 -0
- package/src/kernel/boot-bind/store.ts +82 -6
- package/src/kernel/boot.ts +31 -6
- package/src/kernel/builtin-errors.ts +2 -0
- package/src/kernel/capability.ts +3 -0
- package/src/kernel/decision-label-store.ts +441 -0
- package/src/kernel/effects.test.ts +2 -1
- package/src/kernel/effects.ts +13 -1
- package/src/kernel/element-registries.ts +3 -0
- package/src/kernel/errors-compiler.ts +11 -0
- package/src/kernel/errors-text.ts +20 -0
- package/src/kernel/errors.ts +10 -0
- package/src/kernel/flow.test.ts +2 -3
- package/src/kernel/flow.ts +33 -3
- package/src/kernel/fx-call-types.test.ts +32 -0
- package/src/kernel/fx-decide.ts +765 -0
- package/src/kernel/fx-sql-handle.ts +46 -7
- package/src/kernel/fx.test.ts +13 -8
- package/src/kernel/fx.ts +339 -45
- package/src/kernel/http-frame.test.ts +94 -0
- package/src/kernel/http-frame.ts +203 -0
- package/src/kernel/idempotency.test.ts +2 -1
- package/src/kernel/journal.test.ts +114 -0
- package/src/kernel/journal.ts +471 -51
- package/src/kernel/json-result.ts +10 -0
- package/src/kernel/pipeline-tenant.ts +5 -1
- package/src/kernel/pipeline.test.ts +45 -0
- package/src/kernel/pipeline.ts +5 -0
- package/src/kernel/signal-tx.ts +28 -0
- package/src/kernel/sse-id.ts +32 -0
- package/src/kernel/store-transaction.test.ts +95 -0
- package/src/manifest/diff.ts +1 -0
- package/src/manifest/types.ts +29 -2
- package/src/mcp/ai-tools.test.ts +202 -0
- package/src/mcp/authorization.ts +66 -0
- package/src/mcp/docs-server.ts +19 -2
- package/src/mcp/mcp.test.ts +83 -0
- package/src/mcp/protocol.ts +8 -2
- package/src/mcp/server.ts +23 -2
- package/src/mcp/session.ts +3 -0
- package/src/mcp/tools.ts +168 -0
- package/src/mcp/versions.ts +60 -0
- package/src/plugins/index.ts +1 -0
- package/src/plugins/mena.ts +56 -0
- package/src/release/http-graph.test.ts +2 -0
- package/src/release/http-graph.ts +5 -0
- package/src/release/measure.ts +39 -14
- package/src/runs/collect.ts +4 -1
- package/src/runs/parquet.test.ts +27 -1
- package/src/runs/parquet.ts +19 -0
- package/src/runs/types.ts +31 -0
- package/src/runtime/json-code-block.test.ts +106 -47
- package/src/runtime/json-code-block.ts +1209 -46
- package/src/term.test.ts +13 -0
- package/src/term.ts +81 -49
- package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +0 -1
- package/src/console/ui-next/dist/assets/access-page-Ze6ckjdd.js +0 -4
- package/src/console/ui-next/dist/assets/copy-inline-button-CWG5_ZOh.js +0 -1
- package/src/console/ui-next/dist/assets/detail-header-b_ZzIsmU.js +0 -1
- package/src/console/ui-next/dist/assets/dropdown-menu-nwXEO1Ac.js +0 -1
- package/src/console/ui-next/dist/assets/duration-tone-BbQ_8z50.js +0 -9
- package/src/console/ui-next/dist/assets/element-icons-CwyLpVXz.js +0 -1
- package/src/console/ui-next/dist/assets/explorer-empty-C8sSCqYT.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-D2E7BtKK.js +0 -1
- package/src/console/ui-next/dist/assets/index-VxoEz295.css +0 -2
- package/src/console/ui-next/dist/assets/index-vTuwmeQz.js +0 -58
- package/src/console/ui-next/dist/assets/observability-page-SJTSzHFJ.js +0 -4
- package/src/console/ui-next/dist/assets/request-meta-BjFT7DNl.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-CUNegEV4.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-Qj9ThRE-.js +0 -41
- package/src/console/ui-next/dist/assets/trace-detail-sheet-Cmitq3da.js +0 -2
- package/src/console/ui-next/dist/assets/tree-expand-toggle-CbDIB-7x.js +0 -55
- package/src/console/ui-next/dist/assets/units-page-_PuYqFty.js +0 -1
- package/src/console/ui-next/dist/assets/use-vault-list-Cs45Czkt.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-BOMr0go9.js +0 -2
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AG-UI agent event stream — overloads, event names, and the client import graph.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
7
|
+
import { dirname, resolve } from "node:path";
|
|
8
|
+
import { parseAgentEvent } from "../../client/agent.ts";
|
|
9
|
+
import { createMemoryAgentEventStore } from "../../kernel/agent-event-store.ts";
|
|
10
|
+
import { createFx } from "../../kernel/fx.ts";
|
|
11
|
+
import { createJournal, createMemoryJournalStore } from "../../kernel/journal.ts";
|
|
12
|
+
import { readSseId } from "../../kernel/sse-id.ts";
|
|
13
|
+
import { ai, createAiRuntime, type AgUiEvent } from "../ai.ts";
|
|
14
|
+
import { createMemoryAgentEventLog } from "./run-events.ts";
|
|
15
|
+
|
|
16
|
+
describe("fx.run stream overload", () => {
|
|
17
|
+
test("stream: true is an async iterable of AG-UI events", async () => {
|
|
18
|
+
const fx = createFx({
|
|
19
|
+
flow: "support.assist",
|
|
20
|
+
effects: { asks: ["support"] },
|
|
21
|
+
});
|
|
22
|
+
const events = fx.run("support", { message: "hi" }, { stream: true });
|
|
23
|
+
const typed: AsyncIterable<AgUiEvent> = events;
|
|
24
|
+
expect(typeof typed[Symbol.asyncIterator]).toBe("function");
|
|
25
|
+
const plain = fx.run("support", { message: "hi" });
|
|
26
|
+
expect(plain).toBeInstanceOf(Promise);
|
|
27
|
+
await expect(plain).resolves.toMatchObject({ ok: true });
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe("fx.run messages", () => {
|
|
32
|
+
test("history is sent to the model and both fields throw", async () => {
|
|
33
|
+
let seen = "";
|
|
34
|
+
const runtime = createAiRuntime({
|
|
35
|
+
models: [ai.model("smart")],
|
|
36
|
+
agents: [ai.agent("support", { model: "smart", tools: [], maxSteps: 1 })],
|
|
37
|
+
clients: {
|
|
38
|
+
smart: {
|
|
39
|
+
driverId: "mock",
|
|
40
|
+
model: "smart",
|
|
41
|
+
async complete(opts) {
|
|
42
|
+
seen = opts.messages.map((message) => message.content).join("|");
|
|
43
|
+
return { text: "ok", raw: {}, model: "smart", driverId: "mock" };
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
const result = await runtime.runAgent("support", {
|
|
49
|
+
messages: [
|
|
50
|
+
{ role: "user", content: "first" },
|
|
51
|
+
{ role: "assistant", content: "ack" },
|
|
52
|
+
{ role: "user", content: "second" },
|
|
53
|
+
],
|
|
54
|
+
});
|
|
55
|
+
expect(result.stopReason).toBe("completed");
|
|
56
|
+
expect(seen).toBe("first|ack|second");
|
|
57
|
+
await expect(
|
|
58
|
+
runtime.runAgent("support", { message: "a", messages: [{ role: "user", content: "b" }] }),
|
|
59
|
+
).rejects.toThrow(/message or messages/);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe("agent event stream", () => {
|
|
64
|
+
test("a non-durable approval ends with RUN_ERROR then the iterator ends", async () => {
|
|
65
|
+
const runtime = createAiRuntime({
|
|
66
|
+
models: [ai.model("smart")],
|
|
67
|
+
agents: [
|
|
68
|
+
ai.agent("support", {
|
|
69
|
+
model: "smart",
|
|
70
|
+
tools: [{ name: "refund", approval: true, gate: "public" }],
|
|
71
|
+
}),
|
|
72
|
+
],
|
|
73
|
+
clients: {
|
|
74
|
+
smart: {
|
|
75
|
+
driverId: "mock",
|
|
76
|
+
model: "smart",
|
|
77
|
+
async complete() {
|
|
78
|
+
return {
|
|
79
|
+
text: "",
|
|
80
|
+
raw: {},
|
|
81
|
+
model: "smart",
|
|
82
|
+
driverId: "mock",
|
|
83
|
+
toolCalls: [{ id: "c1", name: "refund", arguments: { amount: 10 } }],
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
const seen: AgUiEvent[] = [];
|
|
90
|
+
for await (const event of runtime.streamAgent("support", {
|
|
91
|
+
message: "refund",
|
|
92
|
+
flow: "assist",
|
|
93
|
+
})) {
|
|
94
|
+
seen.push(event);
|
|
95
|
+
}
|
|
96
|
+
const last = seen.at(-1);
|
|
97
|
+
expect(last?.type).toBe("RUN_ERROR");
|
|
98
|
+
if (last?.type === "RUN_ERROR") {
|
|
99
|
+
expect(last.code).toBe("AiDurableRequiredError");
|
|
100
|
+
expect(last.message).toContain("durable: true");
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("a thrown tool ends with RUN_FINISHED stopReason error", async () => {
|
|
105
|
+
const runtime = createAiRuntime({
|
|
106
|
+
models: [ai.model("smart")],
|
|
107
|
+
agents: [ai.agent("support", { model: "smart", tools: ["orders.get"], maxSteps: 2 })],
|
|
108
|
+
clients: {
|
|
109
|
+
smart: {
|
|
110
|
+
driverId: "mock",
|
|
111
|
+
model: "smart",
|
|
112
|
+
async complete() {
|
|
113
|
+
return {
|
|
114
|
+
text: "",
|
|
115
|
+
raw: { provider: "raw" },
|
|
116
|
+
model: "smart",
|
|
117
|
+
driverId: "mock",
|
|
118
|
+
toolCalls: [{ id: "c1", name: "orders.get", arguments: {} }],
|
|
119
|
+
};
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
callFlow: async () => {
|
|
124
|
+
throw new Error("provider said no");
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
const seen: AgUiEvent[] = [];
|
|
128
|
+
for await (const event of runtime.streamAgent("support", { message: "status" })) {
|
|
129
|
+
seen.push(event);
|
|
130
|
+
}
|
|
131
|
+
const finished = seen.at(-1);
|
|
132
|
+
expect(finished?.type).toBe("RUN_FINISHED");
|
|
133
|
+
if (finished?.type === "RUN_FINISHED") {
|
|
134
|
+
expect(finished.result?.stopReason).toBe("error");
|
|
135
|
+
expect(finished.result?.error).toBe("provider said no");
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("a tool call yields AG-UI events and RUN_FINISHED.result", async () => {
|
|
140
|
+
const runtime = createAiRuntime({
|
|
141
|
+
models: [ai.model("smart")],
|
|
142
|
+
agents: [ai.agent("support", { model: "smart", tools: ["orders.get"], maxSteps: 1 })],
|
|
143
|
+
clients: {
|
|
144
|
+
smart: {
|
|
145
|
+
driverId: "mock",
|
|
146
|
+
model: "smart",
|
|
147
|
+
async complete() {
|
|
148
|
+
return {
|
|
149
|
+
text: "Looking up.",
|
|
150
|
+
raw: {},
|
|
151
|
+
model: "smart",
|
|
152
|
+
driverId: "mock",
|
|
153
|
+
usage: { inputTokens: 3, outputTokens: 2, cost: 0.01 },
|
|
154
|
+
toolCalls: [{ id: "c1", name: "orders.get", arguments: { id: "14" } }],
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
callFlow: async () => ({ status: "open" }),
|
|
160
|
+
});
|
|
161
|
+
const seen: string[] = [];
|
|
162
|
+
let finished: AgUiEvent | undefined;
|
|
163
|
+
for await (const event of runtime.streamAgent("support", { message: "status" })) {
|
|
164
|
+
seen.push(event.type);
|
|
165
|
+
if (event.type === "RUN_FINISHED") finished = event;
|
|
166
|
+
}
|
|
167
|
+
expect(seen[0]).toBe("RUN_STARTED");
|
|
168
|
+
expect(seen).toContain("TEXT_MESSAGE_CONTENT");
|
|
169
|
+
expect(seen).toContain("TOOL_CALL_START");
|
|
170
|
+
expect(seen).toContain("TOOL_CALL_ARGS");
|
|
171
|
+
expect(seen).toContain("TOOL_CALL_RESULT");
|
|
172
|
+
expect(seen.at(-1)).toBe("RUN_FINISHED");
|
|
173
|
+
expect(finished?.type).toBe("RUN_FINISHED");
|
|
174
|
+
if (finished?.type === "RUN_FINISHED") {
|
|
175
|
+
expect(finished.result?.stopReason).toBe("max_steps");
|
|
176
|
+
expect(finished.result?.cost).toBe(0.01);
|
|
177
|
+
expect(finished.usage).toEqual([{ inputTokens: 3, outputTokens: 2 }]);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("split text and tool-call args arrive in order and replay makes no call", async () => {
|
|
182
|
+
let calls = 0;
|
|
183
|
+
const client = {
|
|
184
|
+
driverId: "mock" as const,
|
|
185
|
+
model: "smart",
|
|
186
|
+
async complete() {
|
|
187
|
+
calls++;
|
|
188
|
+
return { text: "", model: "smart", driverId: "mock" as const };
|
|
189
|
+
},
|
|
190
|
+
async *stream() {
|
|
191
|
+
calls++;
|
|
192
|
+
yield { text: "Look" };
|
|
193
|
+
yield { text: "ing." };
|
|
194
|
+
yield {
|
|
195
|
+
text: "",
|
|
196
|
+
toolCall: { index: 0, id: "c1", name: "orders.get", argumentsDelta: "" },
|
|
197
|
+
};
|
|
198
|
+
yield { text: "", toolCall: { index: 0, argumentsDelta: '{"id":' } };
|
|
199
|
+
yield { text: "", toolCall: { index: 0, argumentsDelta: '"14"}' } };
|
|
200
|
+
yield {
|
|
201
|
+
text: "",
|
|
202
|
+
done: true,
|
|
203
|
+
usage: { inputTokens: 3, outputTokens: 2, cost: 0.01 },
|
|
204
|
+
};
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
const runtime = createAiRuntime({
|
|
208
|
+
models: [ai.model("smart")],
|
|
209
|
+
agents: [ai.agent("support", { model: "smart", tools: ["orders.get"], maxSteps: 1 })],
|
|
210
|
+
clients: { smart: client },
|
|
211
|
+
callFlow: async () => ({ status: "open" }),
|
|
212
|
+
});
|
|
213
|
+
const journal = createJournal({ store: createMemoryJournalStore() });
|
|
214
|
+
const session = await journal.start("assist", { message: "status" });
|
|
215
|
+
const seen: AgUiEvent[] = [];
|
|
216
|
+
for await (const event of runtime.streamAgent("support", {
|
|
217
|
+
message: "status",
|
|
218
|
+
journal: session,
|
|
219
|
+
})) {
|
|
220
|
+
seen.push(event);
|
|
221
|
+
}
|
|
222
|
+
expect(calls).toBe(1);
|
|
223
|
+
const content = seen.filter((event) => event.type === "TEXT_MESSAGE_CONTENT");
|
|
224
|
+
expect(
|
|
225
|
+
content.map((event) => (event.type === "TEXT_MESSAGE_CONTENT" ? event.delta : "")),
|
|
226
|
+
).toEqual(["Look", "ing."]);
|
|
227
|
+
const args = seen.filter((event) => event.type === "TOOL_CALL_ARGS");
|
|
228
|
+
expect(args.map((event) => (event.type === "TOOL_CALL_ARGS" ? event.delta : "")).join("")).toBe(
|
|
229
|
+
'{"id":"14"}',
|
|
230
|
+
);
|
|
231
|
+
const start = seen.find((event) => event.type === "TOOL_CALL_START");
|
|
232
|
+
const textStart = seen.find((event) => event.type === "TEXT_MESSAGE_START");
|
|
233
|
+
expect(start?.type === "TOOL_CALL_START" ? start.parentMessageId : undefined).toBe(
|
|
234
|
+
textStart?.type === "TEXT_MESSAGE_START" ? textStart.messageId : undefined,
|
|
235
|
+
);
|
|
236
|
+
const resumed = await journal.resume(session.runId);
|
|
237
|
+
if (!resumed) throw new Error("expected a journal resume");
|
|
238
|
+
for await (const _event of runtime.streamAgent("support", {
|
|
239
|
+
message: "status",
|
|
240
|
+
journal: resumed,
|
|
241
|
+
})) {
|
|
242
|
+
// replay
|
|
243
|
+
}
|
|
244
|
+
expect(calls).toBe(1);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("flow.retry keeps the agent run id", async () => {
|
|
248
|
+
const client = {
|
|
249
|
+
driverId: "mock" as const,
|
|
250
|
+
model: "smart",
|
|
251
|
+
async complete() {
|
|
252
|
+
return { text: "ok", model: "smart", driverId: "mock" as const };
|
|
253
|
+
},
|
|
254
|
+
async *stream() {
|
|
255
|
+
yield { text: "ok" };
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
const runtime = createAiRuntime({
|
|
259
|
+
models: [ai.model("smart")],
|
|
260
|
+
agents: [ai.agent("support", { model: "smart", maxSteps: 1 })],
|
|
261
|
+
clients: { smart: client },
|
|
262
|
+
});
|
|
263
|
+
const journal = createJournal({ store: createMemoryJournalStore() });
|
|
264
|
+
const session = await journal.start("assist", {});
|
|
265
|
+
const ids: string[] = [];
|
|
266
|
+
for await (const event of runtime.streamAgent("support", { message: "hi", journal: session })) {
|
|
267
|
+
if (event.type === "RUN_STARTED") ids.push(event.runId);
|
|
268
|
+
}
|
|
269
|
+
session.rewind();
|
|
270
|
+
for await (const event of runtime.streamAgent("support", { message: "hi", journal: session })) {
|
|
271
|
+
if (event.type === "RUN_STARTED") ids.push(event.runId);
|
|
272
|
+
}
|
|
273
|
+
const first = ids[0];
|
|
274
|
+
if (!first) throw new Error("missing run id");
|
|
275
|
+
expect(ids).toEqual([first, first]);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test("a store that fails once still ends the stream", async () => {
|
|
279
|
+
const store = createMemoryAgentEventStore();
|
|
280
|
+
let failed = false;
|
|
281
|
+
const flaky = {
|
|
282
|
+
...store,
|
|
283
|
+
async append(runId: string, row: { seq: number; event: unknown }) {
|
|
284
|
+
if (!failed) {
|
|
285
|
+
failed = true;
|
|
286
|
+
throw new Error("store down");
|
|
287
|
+
}
|
|
288
|
+
return store.append(runId, row);
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
const runtime = createAiRuntime({
|
|
292
|
+
eventLog: createMemoryAgentEventLog(flaky),
|
|
293
|
+
models: [ai.model("smart")],
|
|
294
|
+
agents: [ai.agent("support", { model: "smart", maxSteps: 1 })],
|
|
295
|
+
clients: {
|
|
296
|
+
smart: {
|
|
297
|
+
driverId: "mock",
|
|
298
|
+
model: "smart",
|
|
299
|
+
async complete() {
|
|
300
|
+
return { text: "ok", raw: {}, model: "smart", driverId: "mock" as const };
|
|
301
|
+
},
|
|
302
|
+
async *stream() {
|
|
303
|
+
yield { text: "ok" };
|
|
304
|
+
yield { text: "", done: true as const };
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
const journal = createJournal({ store: createMemoryJournalStore() });
|
|
310
|
+
const session = await journal.start("assist", {});
|
|
311
|
+
const seen: AgUiEvent[] = [];
|
|
312
|
+
for await (const event of runtime.streamAgent("support", {
|
|
313
|
+
message: "hi",
|
|
314
|
+
journal: session,
|
|
315
|
+
})) {
|
|
316
|
+
seen.push(event);
|
|
317
|
+
}
|
|
318
|
+
expect(seen.at(-1)?.type).toBe("RUN_FINISHED");
|
|
319
|
+
expect(runtime.agentRuns.some((run) => run.ok === false && run.error === "store down")).toBe(
|
|
320
|
+
true,
|
|
321
|
+
);
|
|
322
|
+
const finished = seen.find((event) => event.type === "RUN_FINISHED");
|
|
323
|
+
expect(readSseId(finished)).toBeDefined();
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
describe("client agent parser", () => {
|
|
328
|
+
test("CUSTOM subagent names become typed variants and other CUSTOM stays", () => {
|
|
329
|
+
const started = parseAgentEvent({
|
|
330
|
+
type: "CUSTOM",
|
|
331
|
+
name: "oke.subagent.started",
|
|
332
|
+
value: { runId: "child-1", parentToolCallId: "c1" },
|
|
333
|
+
});
|
|
334
|
+
expect(started).toEqual({
|
|
335
|
+
type: "subagent.started",
|
|
336
|
+
runId: "child-1",
|
|
337
|
+
parentToolCallId: "c1",
|
|
338
|
+
});
|
|
339
|
+
const custom = parseAgentEvent({ type: "CUSTOM", name: "other", value: { a: 1 } });
|
|
340
|
+
expect(custom).toMatchObject({ type: "CUSTOM", name: "other" });
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
describe("okengine/client import graph", () => {
|
|
345
|
+
test("the core client entry does not import client/agent", () => {
|
|
346
|
+
const root = resolve(import.meta.dir, "../../client");
|
|
347
|
+
const files = walk(resolve(root, "index.ts"));
|
|
348
|
+
expect(
|
|
349
|
+
files.some((file) => file.endsWith(`${"/client/agent.ts"}`) || file.endsWith("/agent.ts")),
|
|
350
|
+
).toBe(false);
|
|
351
|
+
const budget = readFileSync(resolve(root, "budget-entry.ts"), "utf8");
|
|
352
|
+
expect(budget).not.toContain("client/agent");
|
|
353
|
+
expect(budget).not.toContain("./agent");
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test("agent and the react hook do not reach okengine/client", () => {
|
|
357
|
+
const agent = walk(resolve(import.meta.dir, "../../client/agent.ts"));
|
|
358
|
+
const hook = walk(resolve(import.meta.dir, "../../client-react/use-agent-run.ts"));
|
|
359
|
+
const clientIndex = resolve(import.meta.dir, "../../client/index.ts");
|
|
360
|
+
expect(agent.includes(clientIndex)).toBe(false);
|
|
361
|
+
expect(hook.includes(clientIndex)).toBe(false);
|
|
362
|
+
expect(hook.some((file) => file.endsWith("/use-agent-run.ts"))).toBe(true);
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
function walk(file: string, seen = new Set<string>()): string[] {
|
|
367
|
+
if (seen.has(file)) return [];
|
|
368
|
+
seen.add(file);
|
|
369
|
+
const text = readFileSync(file, "utf8");
|
|
370
|
+
const specs = [...text.matchAll(/from "(\.[^"]+)"/g)].map((match) => match[1] ?? "");
|
|
371
|
+
const out = [file];
|
|
372
|
+
for (const spec of specs) {
|
|
373
|
+
if (!spec) continue;
|
|
374
|
+
let next = resolve(dirname(file), spec);
|
|
375
|
+
if (!next.endsWith(".ts")) next += ".ts";
|
|
376
|
+
out.push(...walk(next, seen));
|
|
377
|
+
}
|
|
378
|
+
return out;
|
|
379
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AG-UI event shapes for agent streams.
|
|
3
|
+
*
|
|
4
|
+
* Field names match the AG-UI core events a strict parser accepts.
|
|
5
|
+
* Subagent notices are `CUSTOM` (`oke.subagent.*`), not extra event types.
|
|
6
|
+
* `cost` and `stopReason` ride on `RUN_FINISHED.result`.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Numeric token usage AG-UI allows on `RUN_FINISHED`. */
|
|
10
|
+
export interface AgUiUsage {
|
|
11
|
+
readonly inputTokens?: number;
|
|
12
|
+
readonly outputTokens?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** One AG-UI event yielded by a streaming agent run. */
|
|
16
|
+
export type AgUiEvent =
|
|
17
|
+
| {
|
|
18
|
+
readonly type: "RUN_STARTED";
|
|
19
|
+
readonly threadId: string;
|
|
20
|
+
readonly runId: string;
|
|
21
|
+
readonly parentRunId?: string;
|
|
22
|
+
}
|
|
23
|
+
| {
|
|
24
|
+
readonly type: "TEXT_MESSAGE_START";
|
|
25
|
+
readonly messageId: string;
|
|
26
|
+
readonly role: "assistant";
|
|
27
|
+
}
|
|
28
|
+
| {
|
|
29
|
+
readonly type: "TEXT_MESSAGE_CONTENT";
|
|
30
|
+
readonly messageId: string;
|
|
31
|
+
readonly delta: string;
|
|
32
|
+
}
|
|
33
|
+
| { readonly type: "TEXT_MESSAGE_END"; readonly messageId: string }
|
|
34
|
+
| {
|
|
35
|
+
readonly type: "TOOL_CALL_START";
|
|
36
|
+
readonly toolCallId: string;
|
|
37
|
+
readonly toolCallName: string;
|
|
38
|
+
readonly parentMessageId?: string;
|
|
39
|
+
}
|
|
40
|
+
| { readonly type: "TOOL_CALL_ARGS"; readonly toolCallId: string; readonly delta: string }
|
|
41
|
+
| { readonly type: "TOOL_CALL_END"; readonly toolCallId: string }
|
|
42
|
+
| {
|
|
43
|
+
readonly type: "TOOL_CALL_RESULT";
|
|
44
|
+
readonly messageId: string;
|
|
45
|
+
readonly toolCallId: string;
|
|
46
|
+
readonly content: string;
|
|
47
|
+
readonly role: "tool";
|
|
48
|
+
}
|
|
49
|
+
| { readonly type: "STEP_STARTED"; readonly stepName: string }
|
|
50
|
+
| { readonly type: "STEP_FINISHED"; readonly stepName: string }
|
|
51
|
+
| {
|
|
52
|
+
readonly type: "RUN_FINISHED";
|
|
53
|
+
readonly threadId: string;
|
|
54
|
+
readonly runId: string;
|
|
55
|
+
readonly result?: {
|
|
56
|
+
readonly cost: number;
|
|
57
|
+
readonly stopReason: string;
|
|
58
|
+
readonly output?: unknown;
|
|
59
|
+
/** Present when `stopReason` is `error`. */
|
|
60
|
+
readonly error?: string;
|
|
61
|
+
};
|
|
62
|
+
readonly usage?: readonly AgUiUsage[];
|
|
63
|
+
readonly outcome?: {
|
|
64
|
+
readonly type: "interrupt";
|
|
65
|
+
readonly interrupts: readonly {
|
|
66
|
+
readonly id: string;
|
|
67
|
+
readonly reason: string;
|
|
68
|
+
readonly payload?: unknown;
|
|
69
|
+
}[];
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
| { readonly type: "RUN_ERROR"; readonly message: string; readonly code?: string }
|
|
73
|
+
| { readonly type: "CUSTOM"; readonly name: string; readonly value: unknown };
|
|
74
|
+
|
|
75
|
+
/** Push one event into a live stream. */
|
|
76
|
+
export type AgentEventEmit = (event: AgUiEvent) => void;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Queue so a tool loop can emit while the caller iterates.
|
|
80
|
+
*
|
|
81
|
+
* @returns Emit handle plus the iterable the caller reads
|
|
82
|
+
*/
|
|
83
|
+
export function createEventQueue(): {
|
|
84
|
+
readonly emit: AgentEventEmit;
|
|
85
|
+
readonly finish: (error?: unknown) => void;
|
|
86
|
+
readonly events: AsyncIterable<AgUiEvent>;
|
|
87
|
+
} {
|
|
88
|
+
const pending: AgUiEvent[] = [];
|
|
89
|
+
let wake: (() => void) | undefined;
|
|
90
|
+
let done = false;
|
|
91
|
+
let error: unknown;
|
|
92
|
+
const poke = (): void => {
|
|
93
|
+
const waiting = wake;
|
|
94
|
+
wake = undefined;
|
|
95
|
+
waiting?.();
|
|
96
|
+
};
|
|
97
|
+
return {
|
|
98
|
+
emit(event) {
|
|
99
|
+
pending.push(event);
|
|
100
|
+
poke();
|
|
101
|
+
},
|
|
102
|
+
finish(err) {
|
|
103
|
+
done = true;
|
|
104
|
+
error = err;
|
|
105
|
+
poke();
|
|
106
|
+
},
|
|
107
|
+
events: {
|
|
108
|
+
async *[Symbol.asyncIterator]() {
|
|
109
|
+
for (;;) {
|
|
110
|
+
while (pending.length > 0) yield pending.shift()!;
|
|
111
|
+
if (done) {
|
|
112
|
+
if (error !== undefined) throw error;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
await new Promise<void>((resolve) => {
|
|
116
|
+
wake = resolve;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Emit one assistant text message as start / content / end.
|
|
126
|
+
*
|
|
127
|
+
* @param emit - Stream sink
|
|
128
|
+
* @param messageId - AG-UI message id
|
|
129
|
+
* @param text - Full assistant text for this step
|
|
130
|
+
* @returns Whether a message was emitted (`false` for empty text)
|
|
131
|
+
*/
|
|
132
|
+
export function emitAssistantText(emit: AgentEventEmit, messageId: string, text: string): boolean {
|
|
133
|
+
if (!text) return false;
|
|
134
|
+
emit({ type: "TEXT_MESSAGE_START", messageId, role: "assistant" });
|
|
135
|
+
emit({ type: "TEXT_MESSAGE_CONTENT", messageId, delta: text });
|
|
136
|
+
emit({ type: "TEXT_MESSAGE_END", messageId });
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MCP client protocol — 2026-07-28 happy path + dual-era fallback types.
|
|
3
3
|
*
|
|
4
|
-
* Reuses only the JSON-RPC envelope.
|
|
5
|
-
*
|
|
4
|
+
* Reuses only the JSON-RPC envelope. The client version is
|
|
5
|
+
* {@link MCP_CLIENT_PROTOCOL_VERSION} from the shared module, not the
|
|
6
|
+
* server's initialize advertisement.
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
export const MCP_CLIENT_PROTOCOL_VERSION = "2026-07-28";
|
|
9
|
+
import { MCP_CLIENT_PROTOCOL_VERSION, MCP_LEGACY_PROTOCOL_VERSION } from "../../mcp/versions.ts";
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
export const MCP_LEGACY_PROTOCOL_VERSION = "2024-11-05";
|
|
11
|
+
export { MCP_CLIENT_PROTOCOL_VERSION, MCP_LEGACY_PROTOCOL_VERSION };
|
|
13
12
|
|
|
14
13
|
/** JSON-RPC request id. */
|
|
15
14
|
export type JsonRpcId = string | number;
|