okengine 0.13.0 → 0.15.2
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/manifest.v1.schema.json +7 -1
- package/package.json +4 -5
- package/site/content/docs/ai/mcp.mdx +27 -2
- package/site/content/docs/elements/ai.mdx +59 -11
- package/site/content/docs/elements/channel.mdx +1 -0
- package/site/content/docs/elements/clock.mdx +30 -12
- package/site/content/docs/elements/flow.mdx +9 -3
- package/site/content/docs/elements/gate.mdx +25 -22
- package/site/content/docs/elements/signal.mdx +71 -25
- package/site/content/docs/elements/store.mdx +87 -28
- package/site/content/docs/elements/vault.mdx +1 -0
- package/site/content/docs/get-started/basic-usage.mdx +22 -13
- package/site/content/docs/get-started/index.mdx +5 -0
- package/site/content/docs/get-started/installation.mdx +2 -2
- package/site/content/docs/get-started/introduction.mdx +11 -3
- package/site/content/docs/get-started/meta.json +1 -1
- package/site/content/docs/get-started/project-structure.mdx +932 -0
- package/site/content/docs/reference/cli.md +13 -10
- package/site/content/docs/reference/client.mdx +25 -21
- package/site/content/docs/reference/fx.mdx +27 -22
- package/site/content/docs/reference/security.md +1 -1
- package/src/auth/bindings.ts +14 -5
- package/src/auth/cross-plane.ts +10 -3
- package/src/cli/ai-setup/ai-setup.test.ts +62 -0
- package/src/cli/ai-setup/apply.ts +32 -1
- package/src/cli/ai-setup/recommend.test.ts +25 -0
- package/src/cli/ai-setup/recommend.ts +8 -3
- package/src/cli/attach-host-console.test.ts +3 -2
- package/src/cli/attach-host-console.ts +3 -0
- package/src/cli/build.test.ts +4 -2
- package/src/cli/db-seed.ts +48 -15
- package/src/cli/db.test.ts +104 -0
- package/src/cli/db.ts +122 -10
- package/src/cli/dev-controls.test.ts +20 -0
- package/src/cli/dev-controls.ts +13 -0
- package/src/cli/dev.test.ts +15 -4
- package/src/cli/dev.ts +102 -29
- package/src/cli/ensure-drizzle-config.ts +2 -1
- package/src/cli/tui/DevLive.tsx +12 -2
- package/src/cli/tui/keys.test.ts +2 -1
- package/src/cli/tui/keys.ts +3 -2
- package/src/compiler/aot.test.ts +1 -1
- package/src/compiler/effects-infer.ts +58 -3
- package/src/compiler/extract.test.ts +458 -29
- package/src/compiler/extract.ts +387 -69
- package/src/compiler/flow-path.test.ts +96 -0
- package/src/compiler/flow-path.ts +173 -0
- package/src/compiler/generate-adopt.test.ts +112 -3
- package/src/compiler/generate-adopt.ts +280 -26
- package/src/compiler/response.ts +45 -1
- package/src/compiler/schema-from-ast.ts +609 -0
- package/src/console/server/ai.ts +5 -2
- package/src/console/server/bind.ts +2 -2
- package/src/console/server/console.test.ts +3 -0
- package/src/console/server/flows.ts +121 -40
- package/src/console/server/invoke-user-flow.test.ts +3 -1
- package/src/console/server/invoke-user-flow.ts +17 -2
- package/src/console/server/iso-at.test.ts +9 -0
- package/src/console/server/iso-at.ts +11 -0
- package/src/console/server/security-headers.ts +6 -3
- package/src/console/server/serve.ts +17 -2
- package/src/console/server/spa-proxy.test.ts +1 -0
- package/src/console/server/sql-catalog.ts +147 -7
- package/src/console/server/sql-rls.test.ts +16 -0
- package/src/console/server/state.ts +8 -0
- package/src/console/server/store.test.ts +7 -4
- package/src/console/server/store.ts +142 -58
- package/src/console/ui-next/dist/assets/agent-disclosure-C0X1fbWF.js +1 -0
- package/src/console/ui-next/dist/assets/cache-glyph-F1FI122b.js +1 -0
- package/src/console/ui-next/dist/assets/{call-pii-button-bqkxMrJH.js → call-pii-button-DEDCl_j3.js} +1 -1
- package/src/console/ui-next/dist/assets/collapsible-LPqGvfoz.js +1 -0
- package/src/console/ui-next/dist/assets/{confirm-sheet-DDCRmG62.js → confirm-sheet-3ptDzXbz.js} +1 -1
- package/src/console/ui-next/dist/assets/{duration-tone-oiRxPVsZ.js → duration-tone-sC3lGABz.js} +1 -1
- package/src/console/ui-next/dist/assets/{explorer-empty-HjTnVQoR.js → explorer-empty-BvYrygyO.js} +1 -1
- package/src/console/ui-next/dist/assets/flows-page-Dluo1Bd7.js +1 -0
- package/src/console/ui-next/dist/assets/{highlighted-json-CvDPvveV.js → highlighted-json-CS_O8L-r.js} +1 -1
- package/src/console/ui-next/dist/assets/http-method--sWDdXSB.js +1 -0
- package/src/console/ui-next/dist/assets/index-Ca3HZMVq.js +66 -0
- package/src/console/ui-next/dist/assets/index-UtTDRQpS.css +2 -0
- package/src/console/ui-next/dist/assets/{link-DF7SZ9Ek.js → link-COeyggt-.js} +1 -1
- package/src/console/ui-next/dist/assets/observability-page-HK9-BO8z.js +4 -0
- package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +1 -0
- package/src/console/ui-next/dist/assets/replica-lag-DBIFFf7d.js +18 -0
- package/src/console/ui-next/dist/assets/sheet-form-Dnwa5oOR.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-keys-CoqwEIi0.js +1 -0
- package/src/console/ui-next/dist/assets/skeleton-BHcfDYcb.js +1 -0
- package/src/console/ui-next/dist/assets/store-page-BSohH3wM.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-CFIGRnnA.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-BgLl34w7.js +54 -0
- package/src/console/ui-next/dist/assets/units-page-B0cFE76A.js +1 -0
- package/src/console/ui-next/dist/assets/{use-vault-list-Cl79j_ku.js → use-vault-list-BLbZhzyF.js} +1 -1
- package/src/console/ui-next/dist/assets/vault-page-B1SbYe10.js +2 -0
- package/src/console/ui-next/dist/index.html +6 -6
- package/src/console/ui-next/seed-invoke-host.ts +2 -0
- package/src/console/ui-next/src/client.ts +35 -7
- package/src/console/ui-next/src/components/motion/table/index.tsx +16 -0
- package/src/console/ui-next/src/components/motion/table/types.ts +4 -0
- package/src/console/ui-next/src/components/ui/kbd.tsx +2 -2
- package/src/console/ui-next/src/features/flows/graph/build-flow-graph.test.ts +18 -0
- package/src/console/ui-next/src/features/flows/graph/build-flow-graph.ts +14 -1
- package/src/console/ui-next/src/features/flows/graph/neighborhood.test.ts +17 -0
- package/src/console/ui-next/src/features/flows/graph/neighborhood.ts +16 -3
- package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +3 -1
- package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +24 -0
- package/src/console/ui-next/src/features/flows/traces/http-method.ts +19 -0
- package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +9 -3
- package/src/console/ui-next/src/features/flows/traces/trace-detail.test.ts +29 -2
- package/src/console/ui-next/src/features/flows/traces/traces-pane.tsx +10 -3
- package/src/console/ui-next/src/features/observability/lib/ask-count.test.ts +25 -0
- package/src/console/ui-next/src/features/observability/lib/ask-count.ts +4 -1
- package/src/console/ui-next/src/features/store/detail/browse-section.tsx +45 -1
- package/src/console/ui-next/src/features/store/detail/resource-panel.tsx +78 -16
- package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +155 -14
- package/src/console/ui-next/src/features/store/explorer/store-tree.tsx +33 -133
- package/src/console/ui-next/src/features/store/files/file-preview.tsx +4 -1
- package/src/console/ui-next/src/features/store/grid/rls-policy-sheet.tsx +326 -165
- package/src/console/ui-next/src/features/store/grid/sql-insert-sheet.tsx +6 -0
- package/src/console/ui-next/src/features/store/grid/store-data-grid.tsx +50 -3
- package/src/console/ui-next/src/features/store/lib/files-meta.test.ts +3 -0
- package/src/console/ui-next/src/features/store/lib/files-meta.ts +12 -0
- package/src/console/ui-next/src/features/store/lib/query-defaults.test.ts +25 -0
- package/src/console/ui-next/src/features/store/lib/query-defaults.ts +85 -3
- package/src/console/ui-next/src/features/store/lib/query-gate.ts +30 -4
- package/src/console/ui-next/src/features/store/lib/rls-policy.test.ts +139 -0
- package/src/console/ui-next/src/features/store/lib/rls-policy.ts +223 -21
- package/src/console/ui-next/src/features/store/lib/schema-graph.ts +4 -0
- package/src/console/ui-next/src/features/store/lib/sql-catalog.test.ts +24 -0
- package/src/console/ui-next/src/features/store/lib/sql-catalog.ts +19 -0
- package/src/console/ui-next/src/features/store/lib/store-tree.test.ts +6 -0
- package/src/console/ui-next/src/features/store/lib/store-tree.ts +13 -0
- package/src/console/ui-next/src/features/store/query/query-console.tsx +48 -14
- package/src/console/ui-next/src/features/store/query/query-gate-menu.tsx +19 -20
- package/src/console/ui-next/src/features/store/query/query-gate-parts.tsx +2 -1
- package/src/console/ui-next/src/features/store/query/query-results.tsx +110 -83
- package/src/console/ui-next/src/features/store/schema/schema-table-node.tsx +17 -0
- package/src/console/ui-next/src/features/units/call/call-api-panel.tsx +112 -72
- package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +12 -4
- package/src/console/ui-next/src/features/units/lib/contract-input.test.ts +206 -1
- package/src/console/ui-next/src/features/units/lib/contract-input.ts +55 -0
- package/src/console/ui-next/src/features/vault/lib/types.ts +1 -1
- package/src/docker/compose-health.test.ts +27 -0
- package/src/docker/compose-health.ts +19 -3
- package/src/docker/docker.test.ts +4 -1
- package/src/docker/dockerfile.ts +1 -1
- package/src/docker/recipes/pgdog.ts +2 -0
- package/src/drivers/ai-anthropic.ts +5 -0
- package/src/drivers/ai-ollama.ts +49 -30
- package/src/drivers/ai-openai-compatible.ts +57 -46
- package/src/drivers/ai-providers.test.ts +3 -0
- package/src/drivers/bun-native-completeness.test.ts +7 -9
- package/src/drivers/clock-postgres.ts +13 -9
- package/src/drivers/instances-postgres.ts +4 -6
- package/src/drivers/journal-postgres.ts +13 -9
- package/src/drivers/pg-rls.test.ts +87 -0
- package/src/drivers/pg-rls.ts +233 -7
- package/src/drivers/pglite.ts +26 -3
- package/src/drivers/postgres.test.ts +23 -0
- package/src/drivers/postgres.ts +133 -9
- package/src/drivers/redis.ts +11 -4
- package/src/drivers/signal-redis.ts +24 -14
- package/src/drivers/signal-types.ts +2 -1
- package/src/drivers/types.ts +10 -0
- package/src/elements/ai/declare.ts +109 -0
- package/src/elements/ai/errors.test.ts +5 -1
- package/src/elements/ai/errors.ts +30 -2
- package/src/elements/ai/eval.ts +4 -6
- package/src/elements/ai/mcp-client.test.ts +206 -0
- package/src/elements/ai/mcp-client.ts +362 -0
- package/src/elements/ai/mcp-http.ts +159 -0
- package/src/elements/ai/mcp-mock.ts +134 -0
- package/src/elements/ai/mcp-protocol.ts +234 -0
- package/src/elements/ai/mcp-stdio.test.ts +50 -0
- package/src/elements/ai/mcp-stdio.ts +212 -0
- package/src/elements/ai/mcp-transport.ts +70 -0
- package/src/elements/ai/runtime.ts +159 -29
- package/src/elements/ai.test.ts +139 -0
- package/src/elements/ai.ts +16 -0
- package/src/elements/clock/declare.ts +26 -1
- package/src/elements/clock/health.test.ts +43 -0
- package/src/elements/clock/runtime.ts +55 -2
- package/src/elements/clock/schedule.ts +71 -142
- package/src/elements/clock.test.ts +9 -40
- package/src/elements/clock.ts +6 -1
- package/src/elements/gate/boot.ts +5 -5
- package/src/elements/gate/declare.ts +36 -4
- package/src/elements/gate/flatten.ts +1 -1
- package/src/elements/gate/runtime.ts +1 -1
- package/src/elements/index.ts +2 -0
- package/src/elements/signal/declare.ts +2 -1
- package/src/elements/signal/runtime.ts +16 -1
- package/src/elements/signal.ts +1 -0
- package/src/elements/store/cache.test.ts +2 -0
- package/src/elements/store/cache.ts +3 -3
- package/src/elements/store/declare.ts +9 -6
- package/src/elements/store/emit-drizzle.ts +44 -8
- package/src/elements/store/resource.test.ts +1 -1
- package/src/elements/store/rls-identity.test.ts +95 -0
- package/src/elements/store/rls-identity.ts +116 -0
- package/src/elements/store/runtime.ts +4 -0
- package/src/elements/store/schema-decl.test.ts +37 -0
- package/src/elements/store/schema-decl.ts +161 -0
- package/src/elements/store/seed.ts +1 -1
- package/src/elements/store/sql-condition.test.ts +12 -0
- package/src/elements/store/sql-condition.ts +26 -2
- package/src/elements/store/sql-rls-isolation.test.ts +165 -0
- package/src/elements/store/sql-rls-stamp.test.ts +117 -0
- package/src/elements/store/sql-session.ts +81 -3
- package/src/elements/store/upsert-app.test.ts +1 -1
- package/src/elements/store.ts +14 -0
- package/src/elements/vault/builtin-adapter.ts +17 -0
- package/src/full.ts +2 -0
- package/src/http.ts +20 -3
- package/src/index.ts +14 -1
- package/src/kernel/adopt-barrel-fresh.test.ts +1 -1
- package/src/kernel/adopt-routes.ts +30 -2
- package/src/kernel/app.ts +227 -77
- package/src/kernel/auto-cache.test.ts +8 -8
- package/src/kernel/auto-registry.test.ts +54 -9
- package/src/kernel/boot-bind/ai.ts +24 -0
- package/src/kernel/boot-bind/clock.ts +2 -0
- package/src/kernel/boot.test.ts +4 -4
- package/src/kernel/boot.ts +5 -3
- package/src/kernel/effects-stamping.test.ts +3 -3
- package/src/kernel/element-registries.ts +15 -5
- package/src/kernel/errors.ts +24 -0
- package/src/kernel/flow-units.ts +64 -0
- package/src/kernel/flow.ts +29 -17
- package/src/kernel/fx-dead-letters.test.ts +77 -0
- package/src/kernel/fx.test.ts +22 -0
- package/src/kernel/fx.ts +135 -6
- package/src/kernel/horizontal-child.ts +4 -4
- package/src/kernel/http-path-pending.ts +20 -0
- package/src/kernel/http-resource.ts +4 -0
- package/src/kernel/http-stream.test.ts +174 -0
- package/src/kernel/index.ts +2 -0
- package/src/kernel/pipeline.test.ts +2 -2
- package/src/kernel/plugin-elements.test.ts +1 -1
- package/src/kernel/plugin-needs.test.ts +1 -1
- package/src/kernel/ready.test.ts +2 -2
- package/src/kernel/resource-mount.test.ts +14 -0
- package/src/kernel/run-duration.test.ts +2 -2
- package/src/kernel/stamp-http.test.ts +79 -0
- package/src/kernel/stamp-http.ts +75 -0
- package/src/kernel/triggers.ts +52 -27
- package/src/manifest/mcp-ref.ts +88 -0
- package/src/manifest/types.ts +41 -2
- package/src/manifest/validate.test.ts +20 -0
- package/src/mcp/docs-server.ts +1 -1
- package/src/mcp/server.ts +1 -1
- package/src/plugins/compression.test.ts +21 -0
- package/src/plugins/compression.ts +1 -0
- package/src/release/measure.ts +2 -2
- package/src/runtime/bun.ts +41 -4
- package/src/runtime/json-code-block.test.ts +2 -2
- package/src/runtime/serve.test.ts +4 -4
- package/src/term.test.ts +52 -4
- package/src/term.ts +125 -17
- package/src/test/reset-element-registries.ts +10 -3
- package/src/cli/ask-seed.test.ts +0 -96
- package/src/cli/ask-seed.ts +0 -82
- package/src/console/ui-next/dist/assets/cache-glyph-CLPBqZeb.js +0 -1
- package/src/console/ui-next/dist/assets/collapsible-DYb0xU8C.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-Dg8CTE29.js +0 -1
- package/src/console/ui-next/dist/assets/http-method-BJ92Z_ke.js +0 -1
- package/src/console/ui-next/dist/assets/index-Bp-R7jtM.js +0 -66
- package/src/console/ui-next/dist/assets/index-D4Ldtj79.css +0 -2
- package/src/console/ui-next/dist/assets/observability-page-DAnpEaq1.js +0 -4
- package/src/console/ui-next/dist/assets/query-gate-parts-1m8m1iNp.js +0 -2
- package/src/console/ui-next/dist/assets/react-B1ML8gxg.js +0 -1
- package/src/console/ui-next/dist/assets/replica-lag-C8_BRt2x.js +0 -18
- package/src/console/ui-next/dist/assets/sheet-form-D-ata7jy.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-DO4IsVqv.js +0 -1
- package/src/console/ui-next/dist/assets/skeleton-CL_X0GCj.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-v3LXdYpr.js +0 -45
- package/src/console/ui-next/dist/assets/trace-detail-sheet-D16lWQMt.js +0 -2
- package/src/console/ui-next/dist/assets/units-page-BtQ0bqMe.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-Ca-MvcmJ.js +0 -2
|
@@ -37,18 +37,18 @@
|
|
|
37
37
|
} catch (e) {}
|
|
38
38
|
})();
|
|
39
39
|
</script>
|
|
40
|
-
<script type="module" crossorigin src="/assets/index-
|
|
40
|
+
<script type="module" crossorigin src="/assets/index-Ca3HZMVq.js"></script>
|
|
41
41
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-hePW80VL.js">
|
|
42
42
|
<link rel="modulepreload" crossorigin href="/assets/react-dom-Bph1y7z7.js">
|
|
43
43
|
<link rel="modulepreload" crossorigin href="/assets/xyflow-D7n4g6go.js">
|
|
44
|
-
<link rel="modulepreload" crossorigin href="/assets/shortcut-keys-
|
|
45
|
-
<link rel="modulepreload" crossorigin href="/assets/link-
|
|
46
|
-
<link rel="modulepreload" crossorigin href="/assets/skeleton-
|
|
47
|
-
<link rel="modulepreload" crossorigin href="/assets/react-
|
|
44
|
+
<link rel="modulepreload" crossorigin href="/assets/shortcut-keys-CoqwEIi0.js">
|
|
45
|
+
<link rel="modulepreload" crossorigin href="/assets/link-COeyggt-.js">
|
|
46
|
+
<link rel="modulepreload" crossorigin href="/assets/skeleton-BHcfDYcb.js">
|
|
47
|
+
<link rel="modulepreload" crossorigin href="/assets/react-D8E3mtu1.js">
|
|
48
48
|
<link rel="modulepreload" crossorigin href="/assets/preload-helper-oH4irX4C.js">
|
|
49
49
|
<link rel="modulepreload" crossorigin href="/assets/shortcut-Cgzuv4k1.js">
|
|
50
50
|
<link rel="stylesheet" crossorigin href="/assets/xyflow-DZ0Ws1xk.css">
|
|
51
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
51
|
+
<link rel="stylesheet" crossorigin href="/assets/index-UtTDRQpS.css">
|
|
52
52
|
</head>
|
|
53
53
|
<body>
|
|
54
54
|
<div id="root"></div>
|
|
@@ -17,6 +17,7 @@ import type { Manifest } from "../../manifest/types.ts";
|
|
|
17
17
|
import {
|
|
18
18
|
aiAgentRegistry,
|
|
19
19
|
aiEmbedRegistry,
|
|
20
|
+
aiMcpServerRegistry,
|
|
20
21
|
aiModelRegistry,
|
|
21
22
|
aiPromptRegistry,
|
|
22
23
|
channelTemplateRegistry,
|
|
@@ -92,6 +93,7 @@ function clearElementRegistries(): void {
|
|
|
92
93
|
aiPromptRegistry.length = 0;
|
|
93
94
|
aiEmbedRegistry.length = 0;
|
|
94
95
|
aiAgentRegistry.length = 0;
|
|
96
|
+
aiMcpServerRegistry.length = 0;
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
const SeedIn = z.record(z.string(), z.unknown()).optional();
|
|
@@ -446,7 +446,7 @@ export type TracesReplayResult = {
|
|
|
446
446
|
readonly ok: true;
|
|
447
447
|
readonly rootId: string;
|
|
448
448
|
readonly dryRun: boolean;
|
|
449
|
-
readonly at:
|
|
449
|
+
readonly at: string;
|
|
450
450
|
readonly flow: string;
|
|
451
451
|
};
|
|
452
452
|
|
|
@@ -506,6 +506,11 @@ export type FlowsInvokeResult = {
|
|
|
506
506
|
readonly flowId: string;
|
|
507
507
|
readonly asUserId: string;
|
|
508
508
|
readonly asGate?: string | null;
|
|
509
|
+
readonly rls?: {
|
|
510
|
+
readonly gate: string | null;
|
|
511
|
+
readonly userId: string;
|
|
512
|
+
readonly applied: boolean;
|
|
513
|
+
};
|
|
509
514
|
readonly trigger: string;
|
|
510
515
|
readonly response: unknown;
|
|
511
516
|
/** True when classified PII keys were redacted. */
|
|
@@ -674,7 +679,7 @@ export type ClockRunNowResult = {
|
|
|
674
679
|
readonly ok: true;
|
|
675
680
|
readonly name: string;
|
|
676
681
|
readonly ran: boolean;
|
|
677
|
-
readonly at:
|
|
682
|
+
readonly at: string;
|
|
678
683
|
};
|
|
679
684
|
|
|
680
685
|
/**
|
|
@@ -721,6 +726,8 @@ export type StoreListChild = {
|
|
|
721
726
|
readonly columnDescriptions: Readonly<Record<string, string>>;
|
|
722
727
|
/** Live RLS when the engine reported `pg_class.relrowsecurity`. */
|
|
723
728
|
readonly rls?: boolean;
|
|
729
|
+
/** Live + declared RLS policy count when the child is a SQL table. */
|
|
730
|
+
readonly rlsPolicyCount?: number;
|
|
724
731
|
};
|
|
725
732
|
|
|
726
733
|
/** One store row from `GET /console/store` (matches server `StoreListOut`). */
|
|
@@ -772,6 +779,8 @@ export type StoreQueryInput = {
|
|
|
772
779
|
readonly topK?: number;
|
|
773
780
|
/** When true, SQL browse returns PII cleartext (audited). Default masks. */
|
|
774
781
|
readonly revealPii?: boolean;
|
|
782
|
+
readonly asGate?: string;
|
|
783
|
+
readonly asUserId?: string;
|
|
775
784
|
};
|
|
776
785
|
|
|
777
786
|
/** Success payload from `QUERY /console/store/query` (matches server `StoreQueryOut`). */
|
|
@@ -793,6 +802,11 @@ export type StoreQueryResult = {
|
|
|
793
802
|
}>;
|
|
794
803
|
readonly masked: boolean;
|
|
795
804
|
readonly routedRole?: "primary" | "replica";
|
|
805
|
+
readonly rls?: {
|
|
806
|
+
readonly gate: string | null;
|
|
807
|
+
readonly userId: string;
|
|
808
|
+
readonly applied: boolean;
|
|
809
|
+
};
|
|
796
810
|
};
|
|
797
811
|
|
|
798
812
|
/**
|
|
@@ -822,7 +836,7 @@ export type StoreRevealInput = {
|
|
|
822
836
|
export type StoreRevealResult = {
|
|
823
837
|
readonly ok: true;
|
|
824
838
|
readonly value: unknown;
|
|
825
|
-
readonly at:
|
|
839
|
+
readonly at: string;
|
|
826
840
|
};
|
|
827
841
|
|
|
828
842
|
/**
|
|
@@ -851,6 +865,8 @@ export type StoreEditInput = {
|
|
|
851
865
|
readonly confirmation?: string;
|
|
852
866
|
readonly reason?: string;
|
|
853
867
|
readonly commit?: boolean;
|
|
868
|
+
readonly asGate?: string;
|
|
869
|
+
readonly asUserId?: string;
|
|
854
870
|
};
|
|
855
871
|
|
|
856
872
|
/** Success payload from `POST /console/store/edit` (matches server). */
|
|
@@ -863,7 +879,12 @@ export type StoreEditResult = {
|
|
|
863
879
|
readonly kind: "send" | "ask";
|
|
864
880
|
readonly resource: string;
|
|
865
881
|
}>;
|
|
866
|
-
readonly at:
|
|
882
|
+
readonly at: string;
|
|
883
|
+
readonly rls?: {
|
|
884
|
+
readonly gate: string | null;
|
|
885
|
+
readonly userId: string;
|
|
886
|
+
readonly applied: boolean;
|
|
887
|
+
};
|
|
867
888
|
};
|
|
868
889
|
|
|
869
890
|
/**
|
|
@@ -907,7 +928,7 @@ export type StoreDeleteInput = {
|
|
|
907
928
|
export type StoreDeleteResult = {
|
|
908
929
|
readonly ok: true;
|
|
909
930
|
readonly deleted: number;
|
|
910
|
-
readonly at:
|
|
931
|
+
readonly at: string;
|
|
911
932
|
};
|
|
912
933
|
|
|
913
934
|
/**
|
|
@@ -934,6 +955,7 @@ export type StoreSqlInput = {
|
|
|
934
955
|
readonly revealPii?: boolean;
|
|
935
956
|
/** View rows as this Gate (`oke.gate` on postgres / pglite). */
|
|
936
957
|
readonly asGate?: string;
|
|
958
|
+
readonly asUserId?: string;
|
|
937
959
|
};
|
|
938
960
|
|
|
939
961
|
/** Success payload from `POST /console/store/sql` (matches server `StoreSqlOut`). */
|
|
@@ -942,7 +964,13 @@ export type StoreSqlResult = {
|
|
|
942
964
|
readonly masked: boolean;
|
|
943
965
|
readonly routedRole: "primary" | "replica";
|
|
944
966
|
readonly asGate: string | null;
|
|
967
|
+
readonly asUserId: string | null;
|
|
945
968
|
readonly gateApplied: boolean;
|
|
969
|
+
readonly rls?: {
|
|
970
|
+
readonly gate: string | null;
|
|
971
|
+
readonly userId: string;
|
|
972
|
+
readonly applied: boolean;
|
|
973
|
+
};
|
|
946
974
|
};
|
|
947
975
|
|
|
948
976
|
/**
|
|
@@ -1267,7 +1295,7 @@ export type VaultWriteResult = {
|
|
|
1267
1295
|
readonly ok: true;
|
|
1268
1296
|
readonly name: string;
|
|
1269
1297
|
readonly fingerprint: string | null;
|
|
1270
|
-
readonly at:
|
|
1298
|
+
readonly at: string;
|
|
1271
1299
|
};
|
|
1272
1300
|
|
|
1273
1301
|
/**
|
|
@@ -1331,7 +1359,7 @@ export type VaultRotateMasterResult = {
|
|
|
1331
1359
|
readonly kekVersion: number;
|
|
1332
1360
|
readonly remaining: number;
|
|
1333
1361
|
readonly masterKey: string | null;
|
|
1334
|
-
readonly at:
|
|
1362
|
+
readonly at: string;
|
|
1335
1363
|
};
|
|
1336
1364
|
|
|
1337
1365
|
/**
|
|
@@ -61,6 +61,8 @@ export function Table<T>({
|
|
|
61
61
|
onInsertColumn,
|
|
62
62
|
onDeleteColumn,
|
|
63
63
|
onRowDoubleClick,
|
|
64
|
+
onRowContextMenu,
|
|
65
|
+
onViewportContextMenu,
|
|
64
66
|
cellRange = null,
|
|
65
67
|
onCellRangeChange,
|
|
66
68
|
onCellActivate,
|
|
@@ -192,6 +194,13 @@ export function Table<T>({
|
|
|
192
194
|
onPointerMove={cellSelect.onScrollerPointerMove}
|
|
193
195
|
onPointerUp={cellSelect.onPointerUp}
|
|
194
196
|
onPointerCancel={cellSelect.onPointerUp}
|
|
197
|
+
onContextMenu={(event) => {
|
|
198
|
+
if (!onViewportContextMenu) return;
|
|
199
|
+
const target = event.target;
|
|
200
|
+
if (target instanceof Element && target.closest("[data-slot='table-row']")) return;
|
|
201
|
+
event.preventDefault();
|
|
202
|
+
onViewportContextMenu();
|
|
203
|
+
}}
|
|
195
204
|
className={cn("overflow-auto", onCellRangeChange ? "select-none" : undefined)}
|
|
196
205
|
style={{ height }}
|
|
197
206
|
>
|
|
@@ -273,12 +282,19 @@ export function Table<T>({
|
|
|
273
282
|
ref={(el) => {
|
|
274
283
|
rowRefs.current[entry.id] = el;
|
|
275
284
|
}}
|
|
285
|
+
data-slot="table-row"
|
|
276
286
|
data-selected={isSelected}
|
|
277
287
|
style={{ height: rowHeight }}
|
|
278
288
|
onPointerEnter={
|
|
279
289
|
hasRowMenu ? () => activateRow(entry.id, vItem.index) : undefined
|
|
280
290
|
}
|
|
281
291
|
onPointerLeave={hasRowMenu ? deactivateRow : undefined}
|
|
292
|
+
onContextMenu={(event) => {
|
|
293
|
+
if (!onRowContextMenu) return;
|
|
294
|
+
event.preventDefault();
|
|
295
|
+
event.stopPropagation();
|
|
296
|
+
onRowContextMenu(entry.row, entry.id);
|
|
297
|
+
}}
|
|
282
298
|
onDoubleClick={(e) => {
|
|
283
299
|
if (!onRowDoubleClick) return;
|
|
284
300
|
const target = e.target;
|
|
@@ -78,6 +78,10 @@ export interface TableProps<T> {
|
|
|
78
78
|
readonly onDeleteColumn?: (columnKey: string, index: number) => void;
|
|
79
79
|
/** Double-click a body row. */
|
|
80
80
|
readonly onRowDoubleClick?: (row: T, id: string) => void;
|
|
81
|
+
/** Two-finger / right-click a body row (caller should `preventDefault`). */
|
|
82
|
+
readonly onRowContextMenu?: (row: T, id: string) => void;
|
|
83
|
+
/** Two-finger / right-click empty viewport (not a row). */
|
|
84
|
+
readonly onViewportContextMenu?: () => void;
|
|
81
85
|
/** Controlled cell range (visible row index × column index). */
|
|
82
86
|
readonly cellRange?: CellRange | null;
|
|
83
87
|
/** Enables drag / Shift+click cell-range selection. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Keyboard key — shadcn Kbd.
|
|
2
|
+
* Keyboard key — shadcn Kbd. Buttons inherit ink; tooltips invert.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { ComponentProps, JSX } from "react";
|
|
@@ -15,7 +15,7 @@ function Kbd({ className, ...props }: ComponentProps<"kbd">): JSX.Element {
|
|
|
15
15
|
<kbd
|
|
16
16
|
data-slot="kbd"
|
|
17
17
|
className={cn(
|
|
18
|
-
"pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-none bg-muted px-1 font-sans text-xs font-medium text-muted-foreground select-none in-data-[slot=tooltip-content]:bg-background/20 in-data-[slot=tooltip-content]:text-background dark:in-data-[slot=tooltip-content]:bg-background/10 [&_svg:not([class*='size-'])]:size-3",
|
|
18
|
+
"pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-none bg-muted px-1 font-sans text-xs font-medium text-muted-foreground select-none in-[.bg-primary]:bg-primary-foreground/20 in-[.bg-primary]:text-primary-foreground in-data-[slot=tooltip-content]:bg-background/20 in-data-[slot=tooltip-content]:text-background dark:in-data-[slot=tooltip-content]:bg-background/10 [&_svg:not([class*='size-'])]:size-3",
|
|
19
19
|
className,
|
|
20
20
|
)}
|
|
21
21
|
{...props}
|
|
@@ -150,6 +150,24 @@ describe("buildFlowGraph", () => {
|
|
|
150
150
|
expect(callersOfFlow(GRAPH_MANIFEST, "payments.chargeBooking")).toEqual([]);
|
|
151
151
|
});
|
|
152
152
|
|
|
153
|
+
test("mcp: calls become one AI node per server with a call edge", () => {
|
|
154
|
+
const manifest: Manifest = {
|
|
155
|
+
...GRAPH_MANIFEST,
|
|
156
|
+
flows: {
|
|
157
|
+
"support.triage": {
|
|
158
|
+
effects: { calls: ["mcp:github/create_issue"], asks: ["ticket-triage"] },
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
const { nodes, edges } = buildFlowGraph(manifest);
|
|
163
|
+
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
164
|
+
expect(byId.get("mcp:github")?.data).toMatchObject({ kind: "ai", label: "github" });
|
|
165
|
+
const edge = edges.find((e) => e.id === "e:flow:support.triage->mcp:github");
|
|
166
|
+
expect(edge?.data?.kind).toBe("calls");
|
|
167
|
+
expect(edge?.animated).toBe(true);
|
|
168
|
+
expect(nodes.some((n) => n.id === "flow:mcp:github/create_issue")).toBe(false);
|
|
169
|
+
});
|
|
170
|
+
|
|
153
171
|
test("unit groups hug content bounds instead of a fixed dead-space box", () => {
|
|
154
172
|
const unit = nodes.find((n) => n.id === "unit:fulfillment");
|
|
155
173
|
const flow = nodes.find((n) => n.id === "flow:fulfillment.onOrder");
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import dagre from "@dagrejs/dagre";
|
|
11
11
|
import { MarkerType, type Edge, type Node } from "@xyflow/react";
|
|
12
12
|
import type { Flow, Manifest } from "../../../../../../manifest/types.ts";
|
|
13
|
+
import { mcpServerNodeId, parseMcpToolRef } from "../../../../../../manifest/mcp-ref.ts";
|
|
13
14
|
import type { OkeElement } from "@/lib/element-icons.ts";
|
|
14
15
|
import {
|
|
15
16
|
EDGE_STROKE,
|
|
@@ -270,6 +271,14 @@ export function buildFlowGraph(manifest: Manifest | null | undefined): {
|
|
|
270
271
|
edges.push(styledEdge(`e:flow:${flowId}->${ref}`, `flow:${flowId}`, ref, "secrets"));
|
|
271
272
|
}
|
|
272
273
|
for (const callee of e.calls ?? []) {
|
|
274
|
+
const mcp = parseMcpToolRef(callee);
|
|
275
|
+
if (mcp) {
|
|
276
|
+
const id = mcpServerNodeId(mcp.server);
|
|
277
|
+
targets.set(id, { id, kind: "ai", label: mcp.server });
|
|
278
|
+
effectCount.set(id, (effectCount.get(id) ?? 0) + 1);
|
|
279
|
+
edges.push(styledEdge(`e:flow:${flowId}->${id}`, `flow:${flowId}`, id, "calls", true));
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
273
282
|
edges.push(
|
|
274
283
|
styledEdge(
|
|
275
284
|
`e:flow:${flowId}->flow:${callee}`,
|
|
@@ -349,7 +358,11 @@ function layoutWithDagre(
|
|
|
349
358
|
for (const a of e.asks ?? []) link(`flow:${flowId}`, `ai:${a}`);
|
|
350
359
|
for (const s of e.sends ?? []) link(`flow:${flowId}`, `channel:${s}`);
|
|
351
360
|
for (const s of e.secrets ?? []) link(`flow:${flowId}`, `vault:${s}`);
|
|
352
|
-
for (const callee of e.calls ?? [])
|
|
361
|
+
for (const callee of e.calls ?? []) {
|
|
362
|
+
const mcp = parseMcpToolRef(callee);
|
|
363
|
+
if (mcp) link(`flow:${flowId}`, mcpServerNodeId(mcp.server));
|
|
364
|
+
else link(`flow:${flowId}`, `flow:${callee}`);
|
|
365
|
+
}
|
|
353
366
|
}
|
|
354
367
|
}
|
|
355
368
|
|
|
@@ -52,6 +52,23 @@ describe("sliceManifestForFocus", () => {
|
|
|
52
52
|
expect(Object.keys(sliced.flows ?? {})).toEqual(["fulfillment.onOrder"]);
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
+
test("mcp: callees are not treated as flow ids", () => {
|
|
56
|
+
const manifest = {
|
|
57
|
+
...FLOWS_TEST_MANIFEST,
|
|
58
|
+
flows: {
|
|
59
|
+
...FLOWS_TEST_MANIFEST.flows,
|
|
60
|
+
"support.triage": {
|
|
61
|
+
effects: { calls: ["mcp:github/create_issue"] },
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
const sliced = sliceManifestForFocus(manifest, { kind: "unit", unit: "support" });
|
|
66
|
+
expect(Object.keys(sliced.flows ?? {})).toEqual(["support.triage"]);
|
|
67
|
+
expect(
|
|
68
|
+
flowTouchesNode(manifest.flows!["support.triage"]!, "support.triage", "mcp:github"),
|
|
69
|
+
).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
|
|
55
72
|
test("sliced Manifest lays out a small graph", () => {
|
|
56
73
|
const sliced = sliceManifestForFocus(FLOWS_TEST_MANIFEST, {
|
|
57
74
|
kind: "unit",
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { Flow, Manifest } from "../../../../../../manifest/types.ts";
|
|
7
|
+
import { mcpServerNodeId, parseMcpToolRef } from "../../../../../../manifest/mcp-ref.ts";
|
|
7
8
|
import { unitOfFlowId } from "./build-flow-graph.ts";
|
|
8
9
|
|
|
9
10
|
/** Focus that expands the detailed graph (not the bipartite map). */
|
|
@@ -29,13 +30,17 @@ export function sliceManifestForFocus(manifest: Manifest, focus: NeighborhoodFoc
|
|
|
29
30
|
}
|
|
30
31
|
const callees: string[] = [];
|
|
31
32
|
for (const flowId of keep) {
|
|
32
|
-
for (const callee of flows[flowId]?.effects?.calls ?? [])
|
|
33
|
+
for (const callee of flows[flowId]?.effects?.calls ?? []) {
|
|
34
|
+
if (!parseMcpToolRef(callee)) callees.push(callee);
|
|
35
|
+
}
|
|
33
36
|
}
|
|
34
37
|
for (const callee of callees) keep.add(callee);
|
|
35
38
|
} else if (focus.kind === "flow") {
|
|
36
39
|
keep.add(focus.flowId);
|
|
37
40
|
const flow = flows[focus.flowId];
|
|
38
|
-
for (const callee of flow?.effects?.calls ?? [])
|
|
41
|
+
for (const callee of flow?.effects?.calls ?? []) {
|
|
42
|
+
if (!parseMcpToolRef(callee)) keep.add(callee);
|
|
43
|
+
}
|
|
39
44
|
for (const [id, other] of Object.entries(flows)) {
|
|
40
45
|
if (other.effects?.calls?.includes(focus.flowId)) keep.add(id);
|
|
41
46
|
}
|
|
@@ -77,6 +82,14 @@ export function flowTouchesNode(flow: Flow, flowId: string, nodeId: string): boo
|
|
|
77
82
|
if (effects.sends?.some((name) => nodeId === `channel:${name}`)) return true;
|
|
78
83
|
if (effects.asks?.some((name) => nodeId === `ai:${name}`)) return true;
|
|
79
84
|
if (effects.secrets?.some((name) => nodeId === `vault:${name}`)) return true;
|
|
80
|
-
if (
|
|
85
|
+
if (
|
|
86
|
+
effects.calls?.some((callee) => {
|
|
87
|
+
const mcp = parseMcpToolRef(callee);
|
|
88
|
+
if (mcp) return nodeId === mcpServerNodeId(mcp.server);
|
|
89
|
+
return nodeId === `flow:${callee}`;
|
|
90
|
+
})
|
|
91
|
+
) {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
81
94
|
return false;
|
|
82
95
|
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { RunEffect } from "@/client.ts";
|
|
12
|
+
import { parseMcpToolRef } from "../../../../../../manifest/mcp-ref.ts";
|
|
12
13
|
import { ELEMENT_ICONS, type ElementHugeIcon } from "@/lib/element-icons.ts";
|
|
13
14
|
import { EDGE_STROKE } from "../graph/flow-graph-theme.ts";
|
|
14
15
|
|
|
@@ -87,7 +88,8 @@ export function effectBarColor(kind: RunEffectKind): string {
|
|
|
87
88
|
*
|
|
88
89
|
* @param kind - Effect kind
|
|
89
90
|
*/
|
|
90
|
-
export function effectKindIcon(kind: RunEffectKind): ElementHugeIcon {
|
|
91
|
+
export function effectKindIcon(kind: RunEffectKind, resource?: string): ElementHugeIcon {
|
|
92
|
+
if (resource && parseMcpToolRef(resource)) return ELEMENT_ICONS.ai.icon;
|
|
91
93
|
switch (kind) {
|
|
92
94
|
case "read":
|
|
93
95
|
case "write":
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { RunEffect, RunRow } from "@/client.ts";
|
|
11
|
+
import { parseMcpToolRef } from "../../../../../../manifest/mcp-ref.ts";
|
|
11
12
|
import { formatDuration } from "./format-duration.ts";
|
|
12
13
|
import { effectKindSummaryLabel, type RunEffectKind } from "./effect-kind.ts";
|
|
13
14
|
|
|
@@ -164,6 +165,26 @@ export function effectSummaryChips(
|
|
|
164
165
|
*
|
|
165
166
|
* @param effect - Ledger entry
|
|
166
167
|
*/
|
|
168
|
+
/**
|
|
169
|
+
* Units / graph chip label for an `mcp:<server>/<tool>` call.
|
|
170
|
+
*
|
|
171
|
+
* @param ref - Effect resource
|
|
172
|
+
*/
|
|
173
|
+
export function mcpCallChipLabel(ref: string): string | null {
|
|
174
|
+
const mcp = parseMcpToolRef(ref);
|
|
175
|
+
if (!mcp) return null;
|
|
176
|
+
return `Call ${mcp.server} → ${mcp.tool}`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* True when an effect targets an MCP tool (`mcp:…`).
|
|
181
|
+
*
|
|
182
|
+
* @param resource - Effect resource ref
|
|
183
|
+
*/
|
|
184
|
+
export function isMcpResource(resource: string): boolean {
|
|
185
|
+
return parseMcpToolRef(resource) !== null;
|
|
186
|
+
}
|
|
187
|
+
|
|
167
188
|
export function effectEventLabel(effect: Pick<RunEffect, "kind" | "resource">): string {
|
|
168
189
|
if (isComputedCacheKey(effect.resource)) {
|
|
169
190
|
return "Cache read";
|
|
@@ -171,6 +192,9 @@ export function effectEventLabel(effect: Pick<RunEffect, "kind" | "resource">):
|
|
|
171
192
|
if (isSqlResource(effect.resource)) {
|
|
172
193
|
return effect.kind === "write" ? "DB write" : "DB query";
|
|
173
194
|
}
|
|
195
|
+
if (effect.kind === "call" && isMcpResource(effect.resource)) {
|
|
196
|
+
return "MCP call";
|
|
197
|
+
}
|
|
174
198
|
const labels: Record<RunEffectKind, string> = {
|
|
175
199
|
read: "Read",
|
|
176
200
|
write: "Write",
|
|
@@ -18,6 +18,25 @@ import type { ElementHugeIcon } from "@/lib/element-icons.ts";
|
|
|
18
18
|
/** Known HTTP methods from Manifest `HttpTrigger.method`. */
|
|
19
19
|
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS" | "QUERY";
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Whether the verb carries a JSON request body on the wire.
|
|
23
|
+
*
|
|
24
|
+
* Matches the HTTP compiler (`GET` / `HEAD` / `DELETE` are path-or-query
|
|
25
|
+
* only) and the typed client (`GET` / `HEAD` leftover fields → query).
|
|
26
|
+
*
|
|
27
|
+
* @param method - HTTP method string (case-insensitive) or null
|
|
28
|
+
*/
|
|
29
|
+
export function httpMethodHasBody(method: string | null | undefined): boolean {
|
|
30
|
+
switch ((method ?? "").toUpperCase()) {
|
|
31
|
+
case "GET":
|
|
32
|
+
case "HEAD":
|
|
33
|
+
case "DELETE":
|
|
34
|
+
return false;
|
|
35
|
+
default:
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
21
40
|
/**
|
|
22
41
|
* Units-tree display rank: GET → POST → QUERY → PATCH/PUT → DELETE.
|
|
23
42
|
*
|
|
@@ -504,7 +504,7 @@ export function TraceDetailSheet({
|
|
|
504
504
|
)}
|
|
505
505
|
/>
|
|
506
506
|
<div className="flex items-center gap-2">
|
|
507
|
-
<EffectKindGlyph kind={effect.kind} />
|
|
507
|
+
<EffectKindGlyph kind={effect.kind} resource={effect.resource} />
|
|
508
508
|
<span className="w-[4.5rem] shrink-0 font-medium text-foreground/90">
|
|
509
509
|
{effectEventLabel(effect)}
|
|
510
510
|
</span>
|
|
@@ -565,11 +565,17 @@ export function TraceDetailSheet({
|
|
|
565
565
|
*
|
|
566
566
|
* @param props - Effect kind
|
|
567
567
|
*/
|
|
568
|
-
function EffectKindGlyph({
|
|
568
|
+
function EffectKindGlyph({
|
|
569
|
+
kind,
|
|
570
|
+
resource,
|
|
571
|
+
}: {
|
|
572
|
+
readonly kind: RunEffectKind;
|
|
573
|
+
readonly resource?: string;
|
|
574
|
+
}): JSX.Element {
|
|
569
575
|
const color = effectBarColor(kind);
|
|
570
576
|
return (
|
|
571
577
|
<span className={EXPLORER_ICON_CLASS} style={{ color }} aria-hidden>
|
|
572
|
-
<HugeiconsIcon icon={effectKindIcon(kind)} className="size-3.5" />
|
|
578
|
+
<HugeiconsIcon icon={effectKindIcon(kind, resource)} className="size-3.5" />
|
|
573
579
|
</span>
|
|
574
580
|
);
|
|
575
581
|
}
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
* Unit tests for effect summary chips and request metadata helpers.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { effectSummaryChips, effectEventLabel } from "./effect-summary.ts";
|
|
5
|
+
import { effectSummaryChips, effectEventLabel, mcpCallChipLabel } from "./effect-summary.ts";
|
|
6
6
|
import { effectBarColor, effectKindIcon, effectKindSummaryLabel } from "./effect-kind.ts";
|
|
7
7
|
import { ELEMENT_ICONS } from "@/lib/element-icons.ts";
|
|
8
8
|
import { EDGE_STROKE } from "../graph/flow-graph-theme.ts";
|
|
9
9
|
import {
|
|
10
10
|
httpMethodBadgeClass,
|
|
11
|
+
httpMethodHasBody,
|
|
11
12
|
httpMethodIcon,
|
|
12
13
|
httpMethodRailClass,
|
|
13
14
|
httpMethodSortRank,
|
|
@@ -110,6 +111,23 @@ describe("effectKindIcon", () => {
|
|
|
110
111
|
});
|
|
111
112
|
});
|
|
112
113
|
|
|
114
|
+
describe("httpMethodHasBody", () => {
|
|
115
|
+
test("GET, HEAD, and DELETE are path/query only", () => {
|
|
116
|
+
expect(httpMethodHasBody("GET")).toBe(false);
|
|
117
|
+
expect(httpMethodHasBody("HEAD")).toBe(false);
|
|
118
|
+
expect(httpMethodHasBody("DELETE")).toBe(false);
|
|
119
|
+
expect(httpMethodHasBody("get")).toBe(false);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("POST, PUT, PATCH, QUERY, and OPTIONS carry JSON", () => {
|
|
123
|
+
expect(httpMethodHasBody("POST")).toBe(true);
|
|
124
|
+
expect(httpMethodHasBody("PUT")).toBe(true);
|
|
125
|
+
expect(httpMethodHasBody("PATCH")).toBe(true);
|
|
126
|
+
expect(httpMethodHasBody("QUERY")).toBe(true);
|
|
127
|
+
expect(httpMethodHasBody("OPTIONS")).toBe(true);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
113
131
|
describe("httpMethodBadgeClass", () => {
|
|
114
132
|
test("assigns distinct accents per method", () => {
|
|
115
133
|
expect(httpMethodBadgeClass("GET")).toContain("emerald");
|
|
@@ -305,6 +323,15 @@ describe("effectEventLabel", () => {
|
|
|
305
323
|
expect(effectEventLabel({ kind: "read", resource: "sql:bookings" })).toBe("DB query");
|
|
306
324
|
expect(effectEventLabel({ kind: "write", resource: "sql:bookings" })).toBe("DB write");
|
|
307
325
|
expect(effectEventLabel({ kind: "emit", resource: "order-placed" })).toBe("Emit");
|
|
326
|
+
expect(effectEventLabel({ kind: "call", resource: "mcp:github/create_issue" })).toBe(
|
|
327
|
+
"MCP call",
|
|
328
|
+
);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test("mcp call chips and glyphs", () => {
|
|
332
|
+
expect(mcpCallChipLabel("mcp:github/create_issue")).toBe("Call github → create_issue");
|
|
333
|
+
expect(effectKindIcon("call", "mcp:github/create_issue")).toBe(ELEMENT_ICONS.ai.icon);
|
|
334
|
+
expect(effectKindIcon("call")).toBe(ELEMENT_ICONS.flow.icon);
|
|
308
335
|
});
|
|
309
336
|
|
|
310
337
|
test("labels computed: cache keys as Cache read, not DB query", () => {
|
|
@@ -343,7 +370,7 @@ describe("Sheet Replay path", () => {
|
|
|
343
370
|
ok: true as const,
|
|
344
371
|
rootId: run.id,
|
|
345
372
|
dryRun: false,
|
|
346
|
-
at:
|
|
373
|
+
at: "2026-08-21T09:36:51.525Z",
|
|
347
374
|
flow: run.flow,
|
|
348
375
|
},
|
|
349
376
|
error: null,
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
EXPLORER_STRIP_TOKEN_IDLE_CLASS,
|
|
16
16
|
EXPLORER_TOOLBAR_CLASS,
|
|
17
17
|
} from "@/components/explorer/explorer-chrome.ts";
|
|
18
|
+
import { AgentDisclosure } from "@/components/agents/agent-disclosure.tsx";
|
|
18
19
|
import { ExplorerSearch } from "@/components/explorer/explorer-search.tsx";
|
|
19
20
|
import {
|
|
20
21
|
Empty,
|
|
@@ -236,10 +237,16 @@ export function TracesPane({
|
|
|
236
237
|
) : null}
|
|
237
238
|
</div>
|
|
238
239
|
|
|
239
|
-
{runs.length > 0
|
|
240
|
-
<
|
|
240
|
+
{runs.length > 0 ? (
|
|
241
|
+
<AgentDisclosure
|
|
242
|
+
id="traces-advanced-panel"
|
|
243
|
+
role="region"
|
|
244
|
+
aria-label="Advanced filters"
|
|
245
|
+
open={advancedOpen}
|
|
246
|
+
className="shrink-0"
|
|
247
|
+
>
|
|
241
248
|
<AdvancedFilters query={filters.advanced} runs={runs} onChange={setAdvanced} />
|
|
242
|
-
</
|
|
249
|
+
</AgentDisclosure>
|
|
243
250
|
) : null}
|
|
244
251
|
|
|
245
252
|
<div className="flex min-h-0 flex-1 flex-col overflow-y-auto">
|
|
@@ -44,4 +44,29 @@ describe("askCountInWindow", () => {
|
|
|
44
44
|
windowMs: 60_000,
|
|
45
45
|
});
|
|
46
46
|
});
|
|
47
|
+
|
|
48
|
+
test("counts MCP call effects next to asks", () => {
|
|
49
|
+
const now = 1_000;
|
|
50
|
+
const runs = [
|
|
51
|
+
monitoringRun({
|
|
52
|
+
id: "a",
|
|
53
|
+
flow: "x",
|
|
54
|
+
startedAt: now - 10,
|
|
55
|
+
effects: [
|
|
56
|
+
{
|
|
57
|
+
kind: "call",
|
|
58
|
+
resource: "mcp:github/create_issue",
|
|
59
|
+
timestamp: now - 10,
|
|
60
|
+
duration: 8,
|
|
61
|
+
reversibility: "irreversible",
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
}),
|
|
65
|
+
];
|
|
66
|
+
expect(askCountInWindow(runs, now, 60_000)).toEqual({
|
|
67
|
+
kind: "summary",
|
|
68
|
+
asks: 1,
|
|
69
|
+
windowMs: 60_000,
|
|
70
|
+
});
|
|
71
|
+
});
|
|
47
72
|
});
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { RunRow } from "@/client.ts";
|
|
6
|
+
import { parseMcpToolRef } from "../../../../../../manifest/mcp-ref.ts";
|
|
6
7
|
import { runsInWindow } from "./window-stats.ts";
|
|
7
8
|
|
|
8
9
|
/** Honest empty — no ask effects in the window. */
|
|
@@ -36,7 +37,9 @@ export function askCountInWindow(
|
|
|
36
37
|
let asks = 0;
|
|
37
38
|
for (const run of inWindow) {
|
|
38
39
|
for (const effect of run.effects) {
|
|
39
|
-
if (effect.kind === "ask"
|
|
40
|
+
if (effect.kind === "ask" || (effect.kind === "call" && parseMcpToolRef(effect.resource))) {
|
|
41
|
+
asks += 1;
|
|
42
|
+
}
|
|
40
43
|
}
|
|
41
44
|
}
|
|
42
45
|
if (asks === 0) return { kind: "empty" };
|