okengine 0.22.0 → 0.23.0
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 +13 -9
- 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/gate/auth.mdx +4 -1
- package/site/content/docs/elements/store/index.mdx +1 -1
- package/site/content/docs/elements/store/kv.mdx +5 -5
- 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 +7 -6
- 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/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/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/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-infer.ts +27 -1
- package/src/compiler/extract.ts +268 -12
- package/src/compiler/response.ts +35 -2
- package/src/config/index.ts +21 -3
- 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-DDKkhT9v.js +4 -0
- package/src/console/ui-next/dist/assets/{agent-disclosure-Bkohc_8X.js → agent-disclosure-62Xts8Xi.js} +1 -1
- package/src/console/ui-next/dist/assets/{cache-glyph-rL0lHxat.js → cache-glyph-DyeoKHKA.js} +1 -1
- package/src/console/ui-next/dist/assets/{call-pii-button-Deo4PP18.js → call-pii-button-DvfpLXsU.js} +1 -1
- package/src/console/ui-next/dist/assets/{collapsible-CoJ6amHf.js → collapsible-BY03SeCg.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-CgkKlA56.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-Y6HLjyJ5.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-DLPfNy-_.js +1 -0
- package/src/console/ui-next/dist/assets/{highlighted-json-CPYBzDh0.js → highlighted-json-D7nNzpgB.js} +1 -1
- package/src/console/ui-next/dist/assets/{http-method-DTPevKmM.js → http-method-DdL19zzh.js} +1 -1
- package/src/console/ui-next/dist/assets/index-BlrN49Hs.css +2 -0
- package/src/console/ui-next/dist/assets/index-C0lc_s9d.js +58 -0
- package/src/console/ui-next/dist/assets/observability-page-CRlcyLCC.js +8 -0
- package/src/console/ui-next/dist/assets/{replica-lag-BM27lXld.js → replica-lag-Bb3FWUu9.js} +7 -7
- package/src/console/ui-next/dist/assets/request-meta-DYBMWhX8.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-CcE-SXC8.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-CKMSEQ4s.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-QOfk0M0H.js +55 -0
- package/src/console/ui-next/dist/assets/units-page-DKC1CDDd.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-B54v4Yit.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.ts +289 -2
- 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/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/run-events.test.ts +461 -0
- package/src/elements/ai/run-events.ts +522 -0
- package/src/elements/ai/runtime.ts +1186 -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/clock/durable.ts +27 -1
- package/src/elements/store/sql-session.test.ts +51 -0
- package/src/elements/store/sql-session.ts +111 -1
- 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 +103 -1
- package/src/kernel/boot-bind/ai.ts +3 -0
- package/src/kernel/boot-bind/store.test.ts +26 -0
- package/src/kernel/boot-bind/store.ts +82 -6
- package/src/kernel/boot.ts +15 -4
- 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-text.ts +4 -0
- package/src/kernel/errors.ts +2 -0
- package/src/kernel/flow.test.ts +2 -3
- package/src/kernel/fx-decide.ts +761 -0
- package/src/kernel/fx.test.ts +12 -5
- package/src/kernel/fx.ts +312 -29
- package/src/kernel/http-frame.test.ts +94 -0
- package/src/kernel/http-frame.ts +203 -0
- package/src/kernel/journal.ts +104 -0
- package/src/kernel/json-result.ts +10 -0
- package/src/kernel/pipeline.test.ts +45 -0
- package/src/kernel/sse-id.ts +32 -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/session.ts +3 -0
- package/src/mcp/tools.ts +168 -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,562 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Console projection for agent runs and the tool-approval queue.
|
|
3
|
+
*
|
|
4
|
+
* Numbers come from the agent ledger, the follow log, and the journal.
|
|
5
|
+
* The UI must not recompute them.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { AgentEventStore } from "../../kernel/agent-event-store.ts";
|
|
9
|
+
import type { JournalRun, JournalStore } from "../../kernel/journal.ts";
|
|
10
|
+
import {
|
|
11
|
+
approvalStepName,
|
|
12
|
+
parseApprovalId,
|
|
13
|
+
type AgentApprovalRecord,
|
|
14
|
+
} from "../../elements/ai/approval.ts";
|
|
15
|
+
import type { AgUiEvent } from "../../elements/ai/events.ts";
|
|
16
|
+
import type { AgentRunHeader } from "../../elements/ai/run-events.ts";
|
|
17
|
+
import type {
|
|
18
|
+
AgentDenial,
|
|
19
|
+
AgentRunRecord,
|
|
20
|
+
AgentStopReason,
|
|
21
|
+
AgentToolEffect,
|
|
22
|
+
AiJournalEntry,
|
|
23
|
+
AiRuntime,
|
|
24
|
+
} from "../../elements/ai.ts";
|
|
25
|
+
|
|
26
|
+
/** Run lifecycle shown on the AI list. */
|
|
27
|
+
export type ConsoleAgentRunStatus = "running" | "finished" | "error" | "interrupted";
|
|
28
|
+
|
|
29
|
+
/** One tool line on a run. */
|
|
30
|
+
export interface ConsoleAgentTrailStep {
|
|
31
|
+
readonly tool: string;
|
|
32
|
+
readonly status: "ok" | "denied" | "pending";
|
|
33
|
+
readonly effects: readonly AgentToolEffect[];
|
|
34
|
+
readonly denial: AgentDenial | null;
|
|
35
|
+
readonly approver?: string;
|
|
36
|
+
readonly at: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** One schema-repair follow-up that landed inside the run window. */
|
|
40
|
+
export interface ConsoleAgentRepair {
|
|
41
|
+
readonly prompt: string;
|
|
42
|
+
readonly at: number;
|
|
43
|
+
readonly attempts: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Agent run row for the list and the subagent tree. */
|
|
47
|
+
export interface ConsoleAgentRunRow {
|
|
48
|
+
readonly id: string;
|
|
49
|
+
readonly agent: string;
|
|
50
|
+
readonly status: ConsoleAgentRunStatus;
|
|
51
|
+
readonly stopReason?: AgentStopReason;
|
|
52
|
+
readonly error?: string;
|
|
53
|
+
readonly steps: number;
|
|
54
|
+
readonly cost: number;
|
|
55
|
+
readonly inputTokens?: number;
|
|
56
|
+
readonly outputTokens?: number;
|
|
57
|
+
readonly threadId?: string;
|
|
58
|
+
readonly startedAt: number;
|
|
59
|
+
readonly finishedAt?: number;
|
|
60
|
+
readonly parentRunId?: string;
|
|
61
|
+
readonly tenant: string | null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Run detail: trail, repairs, and direct children. */
|
|
65
|
+
export interface ConsoleAgentRunDetail extends ConsoleAgentRunRow {
|
|
66
|
+
readonly message: string;
|
|
67
|
+
readonly trail: readonly ConsoleAgentTrailStep[];
|
|
68
|
+
readonly denials: readonly AgentDenial[];
|
|
69
|
+
readonly repairs: readonly ConsoleAgentRepair[];
|
|
70
|
+
readonly children: readonly ConsoleAgentRunRow[];
|
|
71
|
+
readonly events: readonly { readonly seq: number; readonly event: AgUiEvent }[];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** One pending tool approval. */
|
|
75
|
+
export interface ConsoleApprovalRow {
|
|
76
|
+
readonly id: string;
|
|
77
|
+
readonly agent: string;
|
|
78
|
+
readonly tool: string;
|
|
79
|
+
readonly args: unknown;
|
|
80
|
+
readonly requestedAt: number;
|
|
81
|
+
readonly ageMs: number;
|
|
82
|
+
readonly gate: string;
|
|
83
|
+
readonly tenant: string | null;
|
|
84
|
+
readonly runId: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Sources for {@link loadConsoleAgentRuns}. */
|
|
88
|
+
export interface LoadAgentRunsInput {
|
|
89
|
+
readonly runtime: AiRuntime | null;
|
|
90
|
+
readonly store: JournalStore | null;
|
|
91
|
+
readonly now: number;
|
|
92
|
+
/** When set, drop rows from every other tenant. */
|
|
93
|
+
readonly tenantId?: string | null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* List agent runs from the in-memory ledger and the follow log.
|
|
98
|
+
*
|
|
99
|
+
* @param input - Runtime, journal, clock
|
|
100
|
+
*/
|
|
101
|
+
export async function loadConsoleAgentRuns(
|
|
102
|
+
input: LoadAgentRunsInput,
|
|
103
|
+
): Promise<ConsoleAgentRunDetail[]> {
|
|
104
|
+
const events = input.store?.agentEvents;
|
|
105
|
+
const headers = events ? await events.listHeaders() : [];
|
|
106
|
+
const records = new Map<string, AgentRunRecord>();
|
|
107
|
+
for (const run of input.runtime?.agentRuns ?? []) records.set(run.id, run);
|
|
108
|
+
const journal = input.runtime?.journal ?? [];
|
|
109
|
+
const durable = await durableTrail(input.store);
|
|
110
|
+
const details: ConsoleAgentRunDetail[] = [];
|
|
111
|
+
const seen = new Set<string>();
|
|
112
|
+
|
|
113
|
+
for (const header of headers) {
|
|
114
|
+
seen.add(header.runId);
|
|
115
|
+
const record = records.get(header.runId);
|
|
116
|
+
const stored = await readEvents(events, header.runId);
|
|
117
|
+
details.push(detailFrom(header, record, stored, journal, durable, input.tenantId));
|
|
118
|
+
}
|
|
119
|
+
for (const record of records.values()) {
|
|
120
|
+
if (seen.has(record.id)) continue;
|
|
121
|
+
details.push(detailFrom(undefined, record, [], journal, durable, input.tenantId));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const visible = details.filter((row) => row.id.length > 0);
|
|
125
|
+
const byParent = new Map<string, ConsoleAgentRunRow[]>();
|
|
126
|
+
for (const row of visible) {
|
|
127
|
+
if (!row.parentRunId) continue;
|
|
128
|
+
const list = byParent.get(row.parentRunId) ?? [];
|
|
129
|
+
list.push(rowWithoutTree(row));
|
|
130
|
+
byParent.set(row.parentRunId, list);
|
|
131
|
+
}
|
|
132
|
+
return visible
|
|
133
|
+
.map((row) => ({ ...row, children: byParent.get(row.id) ?? [] }))
|
|
134
|
+
.sort((a, b) => b.startedAt - a.startedAt || a.id.localeCompare(b.id));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* One run, or undefined when neither ledger has it.
|
|
139
|
+
*
|
|
140
|
+
* @param input - Sources
|
|
141
|
+
* @param runId - Agent run id
|
|
142
|
+
*/
|
|
143
|
+
export async function loadConsoleAgentRun(
|
|
144
|
+
input: LoadAgentRunsInput,
|
|
145
|
+
runId: string,
|
|
146
|
+
): Promise<ConsoleAgentRunDetail | undefined> {
|
|
147
|
+
const runs = await loadConsoleAgentRuns(input);
|
|
148
|
+
return runs.find((row) => row.id === runId);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Pending tool approvals, newest age first.
|
|
153
|
+
*
|
|
154
|
+
* @param store - Durable journal
|
|
155
|
+
* @param now - Clock
|
|
156
|
+
* @param tenantId - When set, keep only this tenant
|
|
157
|
+
*/
|
|
158
|
+
export async function loadApprovalQueue(
|
|
159
|
+
store: JournalStore | null | undefined,
|
|
160
|
+
now: number,
|
|
161
|
+
tenantId?: string | null,
|
|
162
|
+
): Promise<ConsoleApprovalRow[]> {
|
|
163
|
+
if (!store) return [];
|
|
164
|
+
return projectApprovalQueue(await store.list(), now, tenantId);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Pending `ai-approval:` steps.
|
|
169
|
+
*
|
|
170
|
+
* @param runs - Journal runs
|
|
171
|
+
* @param now - Clock
|
|
172
|
+
* @param tenantId - When set, keep only this tenant
|
|
173
|
+
*/
|
|
174
|
+
export function projectApprovalQueue(
|
|
175
|
+
runs: readonly JournalRun[],
|
|
176
|
+
now: number,
|
|
177
|
+
tenantId?: string | null,
|
|
178
|
+
): ConsoleApprovalRow[] {
|
|
179
|
+
const rows: ConsoleApprovalRow[] = [];
|
|
180
|
+
for (const run of runs) {
|
|
181
|
+
for (const entry of run.entries) {
|
|
182
|
+
if (entry.kind !== "step" || !entry.name.startsWith("ai-approval:")) continue;
|
|
183
|
+
const id = entry.name.slice("ai-approval:".length);
|
|
184
|
+
const value = entry.value as AgentApprovalRecord;
|
|
185
|
+
if (value.status !== "pending") continue;
|
|
186
|
+
if (tenantId !== undefined && tenantId !== null && (value.tenant ?? null) !== tenantId) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const parsed = parseApprovalId(id);
|
|
190
|
+
const requestedAt = value.requestedAt ?? entry.at;
|
|
191
|
+
rows.push({
|
|
192
|
+
id,
|
|
193
|
+
agent: value.agent ?? run.flow,
|
|
194
|
+
tool: value.tool,
|
|
195
|
+
args: value.args,
|
|
196
|
+
requestedAt,
|
|
197
|
+
ageMs: Math.max(0, now - requestedAt),
|
|
198
|
+
gate: value.gate,
|
|
199
|
+
tenant: value.tenant ?? null,
|
|
200
|
+
runId: parsed?.runId ?? run.id,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return rows.sort((a, b) => b.ageMs - a.ageMs || a.id.localeCompare(b.id));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Step name helper re-exported for tests that seed a journal row.
|
|
209
|
+
*
|
|
210
|
+
* @param id - Approval id
|
|
211
|
+
*/
|
|
212
|
+
export function approvalStep(id: string): string {
|
|
213
|
+
return approvalStepName(id);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function rowWithoutTree(row: ConsoleAgentRunDetail): ConsoleAgentRunRow {
|
|
217
|
+
return {
|
|
218
|
+
id: row.id,
|
|
219
|
+
agent: row.agent,
|
|
220
|
+
status: row.status,
|
|
221
|
+
...(row.stopReason !== undefined ? { stopReason: row.stopReason } : {}),
|
|
222
|
+
...(row.error !== undefined ? { error: row.error } : {}),
|
|
223
|
+
steps: row.steps,
|
|
224
|
+
cost: row.cost,
|
|
225
|
+
...(row.inputTokens !== undefined ? { inputTokens: row.inputTokens } : {}),
|
|
226
|
+
...(row.outputTokens !== undefined ? { outputTokens: row.outputTokens } : {}),
|
|
227
|
+
...(row.threadId !== undefined ? { threadId: row.threadId } : {}),
|
|
228
|
+
startedAt: row.startedAt,
|
|
229
|
+
...(row.finishedAt !== undefined ? { finishedAt: row.finishedAt } : {}),
|
|
230
|
+
...(row.parentRunId !== undefined ? { parentRunId: row.parentRunId } : {}),
|
|
231
|
+
tenant: row.tenant,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function readEvents(
|
|
236
|
+
events: AgentEventStore | undefined,
|
|
237
|
+
runId: string,
|
|
238
|
+
): Promise<readonly { readonly seq: number; readonly event: AgUiEvent }[]> {
|
|
239
|
+
if (!events) return [];
|
|
240
|
+
const rows = await events.readAfter(runId, 0);
|
|
241
|
+
return rows.map((row) => ({ seq: row.seq, event: row.event as AgUiEvent }));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** One journal approval, addressed by its opaque id and by the tool call. */
|
|
245
|
+
interface DurableApproval {
|
|
246
|
+
readonly id: string;
|
|
247
|
+
/** Journal run that owns the step. This is the Flow run, not always the agent run. */
|
|
248
|
+
readonly journalRunId: string;
|
|
249
|
+
readonly agent?: string;
|
|
250
|
+
readonly toolCallId: string;
|
|
251
|
+
readonly step: ConsoleAgentTrailStep;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Agent name and tool lines recovered from journal approval steps. */
|
|
255
|
+
interface DurableTrail {
|
|
256
|
+
readonly agentByRun: ReadonlyMap<string, string>;
|
|
257
|
+
readonly stepsByRun: ReadonlyMap<string, readonly ConsoleAgentTrailStep[]>;
|
|
258
|
+
readonly byApprovalId: ReadonlyMap<string, DurableApproval>;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Approval steps are the durable trail when the in-memory ledger is on another runtime.
|
|
263
|
+
*
|
|
264
|
+
* @param store - Shared journal
|
|
265
|
+
*/
|
|
266
|
+
async function durableTrail(store: JournalStore | null): Promise<DurableTrail> {
|
|
267
|
+
const agentByRun = new Map<string, string>();
|
|
268
|
+
const stepsByRun = new Map<string, ConsoleAgentTrailStep[]>();
|
|
269
|
+
const byApprovalId = new Map<string, DurableApproval>();
|
|
270
|
+
if (!store) return { agentByRun, stepsByRun, byApprovalId };
|
|
271
|
+
for (const run of await store.list()) {
|
|
272
|
+
for (const entry of run.entries) {
|
|
273
|
+
if (entry.kind !== "step" || !entry.name.startsWith("ai-approval:")) continue;
|
|
274
|
+
const id = entry.name.slice("ai-approval:".length);
|
|
275
|
+
const parsed = parseApprovalId(id);
|
|
276
|
+
if (!parsed) continue;
|
|
277
|
+
const value = entry.value as AgentApprovalRecord;
|
|
278
|
+
if (value.agent) agentByRun.set(parsed.runId, value.agent);
|
|
279
|
+
const denied = value.status === "denied";
|
|
280
|
+
const step: ConsoleAgentTrailStep = {
|
|
281
|
+
tool: value.tool,
|
|
282
|
+
status: denied ? "denied" : value.status === "approved" ? "ok" : "pending",
|
|
283
|
+
effects: [],
|
|
284
|
+
denial: denied
|
|
285
|
+
? {
|
|
286
|
+
agent: value.agent ?? "",
|
|
287
|
+
tool: value.tool,
|
|
288
|
+
gate: value.gate,
|
|
289
|
+
reason: value.reason ?? "",
|
|
290
|
+
at: entry.at,
|
|
291
|
+
}
|
|
292
|
+
: null,
|
|
293
|
+
...(value.approver !== undefined ? { approver: value.approver } : {}),
|
|
294
|
+
at: value.requestedAt ?? entry.at,
|
|
295
|
+
};
|
|
296
|
+
const list = stepsByRun.get(parsed.runId) ?? [];
|
|
297
|
+
list.push(step);
|
|
298
|
+
stepsByRun.set(parsed.runId, list);
|
|
299
|
+
byApprovalId.set(id, {
|
|
300
|
+
id,
|
|
301
|
+
journalRunId: parsed.runId,
|
|
302
|
+
...(value.agent !== undefined ? { agent: value.agent } : {}),
|
|
303
|
+
toolCallId: parsed.toolCallId,
|
|
304
|
+
step,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return { agentByRun, stepsByRun, byApprovalId };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function detailFrom(
|
|
312
|
+
header: AgentRunHeader | undefined,
|
|
313
|
+
record: AgentRunRecord | undefined,
|
|
314
|
+
events: readonly { readonly seq: number; readonly event: AgUiEvent }[],
|
|
315
|
+
journal: readonly AiJournalEntry[],
|
|
316
|
+
durable: DurableTrail,
|
|
317
|
+
tenantId: string | null | undefined,
|
|
318
|
+
): ConsoleAgentRunDetail {
|
|
319
|
+
const finished = events.findLast(
|
|
320
|
+
(row) => row.event.type === "RUN_FINISHED" && row.event.outcome?.type !== "interrupt",
|
|
321
|
+
);
|
|
322
|
+
const interrupted = events.some(
|
|
323
|
+
(row) => row.event.type === "RUN_FINISHED" && row.event.outcome?.type === "interrupt",
|
|
324
|
+
);
|
|
325
|
+
const errored = events.findLast((row) => row.event.type === "RUN_ERROR");
|
|
326
|
+
const usage = finished?.event.type === "RUN_FINISHED" ? finished.event.usage?.[0] : undefined;
|
|
327
|
+
const result = finished?.event.type === "RUN_FINISHED" ? finished.event.result : undefined;
|
|
328
|
+
const stopReason = record?.stopReason ?? stopFrom(result?.stopReason);
|
|
329
|
+
const error =
|
|
330
|
+
record?.error ??
|
|
331
|
+
(result && "error" in result && typeof result.error === "string" ? result.error : undefined) ??
|
|
332
|
+
(errored?.event.type === "RUN_ERROR" ? errored.event.message : undefined);
|
|
333
|
+
const startedAt = header?.openedAt ?? record?.at ?? 0;
|
|
334
|
+
const finishedAt = header?.finishedAt ?? record?.finishedAt;
|
|
335
|
+
const tenant = header?.tenant ?? null;
|
|
336
|
+
const status = runStatus({
|
|
337
|
+
stopReason,
|
|
338
|
+
error,
|
|
339
|
+
finishedAt,
|
|
340
|
+
interrupted: interrupted && finished === undefined,
|
|
341
|
+
});
|
|
342
|
+
const inputTokens = record?.inputTokens ?? usage?.inputTokens;
|
|
343
|
+
const outputTokens = record?.outputTokens ?? usage?.outputTokens;
|
|
344
|
+
const id = header?.runId ?? record?.id ?? "";
|
|
345
|
+
const trail = trailFor(id, record, events, durable);
|
|
346
|
+
const parentRunId = header?.parentRunId ?? record?.parentRunId ?? parentFromEvents(events);
|
|
347
|
+
const row: ConsoleAgentRunDetail = {
|
|
348
|
+
id,
|
|
349
|
+
agent:
|
|
350
|
+
header?.agent ??
|
|
351
|
+
record?.agent ??
|
|
352
|
+
agentFromApprovals(id, events, durable) ??
|
|
353
|
+
agentFromEvents(events) ??
|
|
354
|
+
"(unknown)",
|
|
355
|
+
status,
|
|
356
|
+
...(stopReason !== undefined ? { stopReason } : {}),
|
|
357
|
+
...(error !== undefined ? { error } : {}),
|
|
358
|
+
steps: record?.steps ?? (countSteps(events) || trail.length),
|
|
359
|
+
cost: record?.cost ?? result?.cost ?? 0,
|
|
360
|
+
...(inputTokens !== undefined ? { inputTokens } : {}),
|
|
361
|
+
...(outputTokens !== undefined ? { outputTokens } : {}),
|
|
362
|
+
...((header?.threadId ?? record?.threadId) !== undefined
|
|
363
|
+
? { threadId: header?.threadId ?? record?.threadId }
|
|
364
|
+
: {}),
|
|
365
|
+
startedAt,
|
|
366
|
+
...(finishedAt !== undefined ? { finishedAt } : {}),
|
|
367
|
+
...(parentRunId !== undefined ? { parentRunId } : {}),
|
|
368
|
+
tenant,
|
|
369
|
+
message: record?.message ?? "",
|
|
370
|
+
trail,
|
|
371
|
+
denials: record?.denials ?? trail.flatMap((step) => (step.denial ? [step.denial] : [])),
|
|
372
|
+
repairs: repairsInWindow(journal, startedAt, finishedAt),
|
|
373
|
+
children: [],
|
|
374
|
+
events,
|
|
375
|
+
};
|
|
376
|
+
if (tenantId !== undefined && tenantId !== null && row.tenant !== tenantId) {
|
|
377
|
+
return { ...row, id: "" };
|
|
378
|
+
}
|
|
379
|
+
return row;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function runStatus(input: {
|
|
383
|
+
readonly stopReason?: AgentStopReason;
|
|
384
|
+
readonly error?: string;
|
|
385
|
+
readonly finishedAt?: number;
|
|
386
|
+
readonly interrupted: boolean;
|
|
387
|
+
}): ConsoleAgentRunStatus {
|
|
388
|
+
if (input.stopReason === "error") return "error";
|
|
389
|
+
if (input.error !== undefined && input.finishedAt === undefined && !input.interrupted)
|
|
390
|
+
return "error";
|
|
391
|
+
if (input.interrupted) return "interrupted";
|
|
392
|
+
if (input.finishedAt === undefined && input.stopReason === undefined) return "running";
|
|
393
|
+
return "finished";
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function stopFrom(value: string | undefined): AgentStopReason | undefined {
|
|
397
|
+
if (
|
|
398
|
+
value === "completed" ||
|
|
399
|
+
value === "max_steps" ||
|
|
400
|
+
value === "budget" ||
|
|
401
|
+
value === "denied" ||
|
|
402
|
+
value === "aborted" ||
|
|
403
|
+
value === "error"
|
|
404
|
+
) {
|
|
405
|
+
return value;
|
|
406
|
+
}
|
|
407
|
+
return undefined;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function parentFromEvents(events: readonly { readonly event: AgUiEvent }[]): string | undefined {
|
|
411
|
+
for (const row of events) {
|
|
412
|
+
if (row.event.type !== "CUSTOM") continue;
|
|
413
|
+
const value = row.event.value;
|
|
414
|
+
if (!value || typeof value !== "object" || !("parentRunId" in value)) continue;
|
|
415
|
+
const parent = (value as { parentRunId?: unknown }).parentRunId;
|
|
416
|
+
if (typeof parent === "string") return parent;
|
|
417
|
+
}
|
|
418
|
+
return undefined;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function agentFromEvents(events: readonly { readonly event: AgUiEvent }[]): string | undefined {
|
|
422
|
+
for (const row of events) {
|
|
423
|
+
if (row.event.type === "CUSTOM" && row.event.name === "oke.subagent.started") {
|
|
424
|
+
const value = row.event.value;
|
|
425
|
+
if (value && typeof value === "object" && "agent" in value) {
|
|
426
|
+
const agent = (value as { agent?: unknown }).agent;
|
|
427
|
+
if (typeof agent === "string") return agent;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return undefined;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function countSteps(events: readonly { readonly event: AgUiEvent }[]): number {
|
|
435
|
+
const started = events.filter((row) => row.event.type === "STEP_STARTED").length;
|
|
436
|
+
const finished = events.filter((row) => row.event.type === "STEP_FINISHED").length;
|
|
437
|
+
return Math.max(started, finished);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Tool lines for one agent run.
|
|
442
|
+
*
|
|
443
|
+
* The in-memory ledger wins. Otherwise the follow log's tool calls are joined
|
|
444
|
+
* to journal approvals by the interrupt id. The approval's journal run id is
|
|
445
|
+
* the Flow run, so it only matches the agent run id when they are the same.
|
|
446
|
+
*
|
|
447
|
+
* @param id - Agent run id
|
|
448
|
+
* @param record - In-memory ledger row
|
|
449
|
+
* @param events - Follow-log rows
|
|
450
|
+
* @param durable - Journal approvals
|
|
451
|
+
*/
|
|
452
|
+
function trailFor(
|
|
453
|
+
id: string,
|
|
454
|
+
record: AgentRunRecord | undefined,
|
|
455
|
+
events: readonly { readonly event: AgUiEvent }[],
|
|
456
|
+
durable: DurableTrail,
|
|
457
|
+
): ConsoleAgentTrailStep[] {
|
|
458
|
+
const recorded = record?.trail ?? [];
|
|
459
|
+
if (recorded.length > 0) {
|
|
460
|
+
return recorded.map((step) => ({
|
|
461
|
+
tool: step.tool,
|
|
462
|
+
status: step.status,
|
|
463
|
+
effects: step.effects,
|
|
464
|
+
denial: step.denial ?? null,
|
|
465
|
+
...(step.approver !== undefined ? { approver: step.approver } : {}),
|
|
466
|
+
at: step.at,
|
|
467
|
+
}));
|
|
468
|
+
}
|
|
469
|
+
const linked = approvalsForRun(id, events, durable);
|
|
470
|
+
const steps: ConsoleAgentTrailStep[] = [];
|
|
471
|
+
const seen = new Set<string>();
|
|
472
|
+
for (const row of events) {
|
|
473
|
+
if (row.event.type !== "TOOL_CALL_START") continue;
|
|
474
|
+
const toolCallId = row.event.toolCallId;
|
|
475
|
+
seen.add(toolCallId);
|
|
476
|
+
const approval = linked.get(toolCallId);
|
|
477
|
+
const resulted = events.some((stored) => {
|
|
478
|
+
const body = stored.event;
|
|
479
|
+
return body.type === "TOOL_CALL_RESULT" && body.toolCallId === toolCallId;
|
|
480
|
+
});
|
|
481
|
+
if (approval) {
|
|
482
|
+
steps.push(
|
|
483
|
+
resulted && approval.step.status === "pending"
|
|
484
|
+
? { ...approval.step, status: "ok" }
|
|
485
|
+
: approval.step,
|
|
486
|
+
);
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
steps.push({
|
|
490
|
+
tool: row.event.toolCallName,
|
|
491
|
+
status: resulted ? "ok" : "pending",
|
|
492
|
+
effects: [],
|
|
493
|
+
denial: null,
|
|
494
|
+
at: 0,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
for (const approval of linked.values()) {
|
|
498
|
+
if (seen.has(approval.toolCallId)) continue;
|
|
499
|
+
steps.push(approval.step);
|
|
500
|
+
}
|
|
501
|
+
if (steps.length > 0) return steps;
|
|
502
|
+
return [...(durable.stepsByRun.get(id) ?? [])];
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Approvals whose interrupt is on this follow log, or whose journal run id is this agent run.
|
|
507
|
+
*
|
|
508
|
+
* @param id - Agent run id
|
|
509
|
+
* @param events - Follow-log rows
|
|
510
|
+
* @param durable - Journal approvals
|
|
511
|
+
*/
|
|
512
|
+
function approvalsForRun(
|
|
513
|
+
id: string,
|
|
514
|
+
events: readonly { readonly event: AgUiEvent }[],
|
|
515
|
+
durable: DurableTrail,
|
|
516
|
+
): Map<string, DurableApproval> {
|
|
517
|
+
const linked = new Map<string, DurableApproval>();
|
|
518
|
+
for (const approval of durable.byApprovalId.values()) {
|
|
519
|
+
if (approval.journalRunId === id) linked.set(approval.toolCallId, approval);
|
|
520
|
+
}
|
|
521
|
+
for (const row of events) {
|
|
522
|
+
if (row.event.type !== "RUN_FINISHED" || row.event.outcome?.type !== "interrupt") continue;
|
|
523
|
+
for (const interrupt of row.event.outcome.interrupts) {
|
|
524
|
+
const approval = durable.byApprovalId.get(interrupt.id);
|
|
525
|
+
if (approval) linked.set(approval.toolCallId, approval);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return linked;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Agent name from an approval that belongs to this run.
|
|
533
|
+
*
|
|
534
|
+
* @param id - Agent run id
|
|
535
|
+
* @param events - Follow-log rows
|
|
536
|
+
* @param durable - Journal approvals
|
|
537
|
+
*/
|
|
538
|
+
function agentFromApprovals(
|
|
539
|
+
id: string,
|
|
540
|
+
events: readonly { readonly event: AgUiEvent }[],
|
|
541
|
+
durable: DurableTrail,
|
|
542
|
+
): string | undefined {
|
|
543
|
+
for (const approval of approvalsForRun(id, events, durable).values()) {
|
|
544
|
+
if (approval.agent) return approval.agent;
|
|
545
|
+
}
|
|
546
|
+
return durable.agentByRun.get(id);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function repairsInWindow(
|
|
550
|
+
journal: readonly AiJournalEntry[],
|
|
551
|
+
startedAt: number,
|
|
552
|
+
finishedAt: number | undefined,
|
|
553
|
+
): ConsoleAgentRepair[] {
|
|
554
|
+
const end = finishedAt ?? Number.POSITIVE_INFINITY;
|
|
555
|
+
const repairs: ConsoleAgentRepair[] = [];
|
|
556
|
+
for (const entry of journal) {
|
|
557
|
+
if (entry.attempts.length < 2) continue;
|
|
558
|
+
if (entry.at < startedAt || entry.at > end) continue;
|
|
559
|
+
repairs.push({ prompt: entry.prompt, at: entry.at, attempts: entry.attempts.length });
|
|
560
|
+
}
|
|
561
|
+
return repairs;
|
|
562
|
+
}
|
package/src/console/server/ai.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
createAiRuntime,
|
|
12
12
|
type AgentDenial,
|
|
13
13
|
type AgentRunRecord,
|
|
14
|
+
type AgentStopReason,
|
|
14
15
|
type AgentToolEffect,
|
|
15
16
|
type AiAskOutcome,
|
|
16
17
|
type AiFallbackAttempt,
|
|
@@ -94,6 +95,8 @@ export interface AgentRunRow {
|
|
|
94
95
|
readonly agent: string;
|
|
95
96
|
readonly message: string;
|
|
96
97
|
readonly ok: boolean;
|
|
98
|
+
readonly stopReason: AgentStopReason;
|
|
99
|
+
readonly error?: string;
|
|
97
100
|
readonly steps: number;
|
|
98
101
|
readonly cost: number;
|
|
99
102
|
readonly at: number;
|
|
@@ -158,7 +161,11 @@ export interface ProjectAiOptions {
|
|
|
158
161
|
*/
|
|
159
162
|
export function createManifestAiRuntime(
|
|
160
163
|
manifest: Manifest | null,
|
|
161
|
-
options: {
|
|
164
|
+
options: {
|
|
165
|
+
readonly now?: () => number;
|
|
166
|
+
readonly journalStore?: import("../../kernel/journal.ts").JournalStore | null;
|
|
167
|
+
readonly eventLog?: import("../../elements/ai/run-events.ts").AgentEventLog;
|
|
168
|
+
} = {},
|
|
162
169
|
): AiRuntime {
|
|
163
170
|
const models = Object.entries(manifest?.ai?.models ?? {}).map(([name, m]) =>
|
|
164
171
|
ai.model(name, {
|
|
@@ -200,6 +207,8 @@ export function createManifestAiRuntime(
|
|
|
200
207
|
defaultDriver: mockAiDriver,
|
|
201
208
|
effectsForFlow: (flowName) => effectsForFlowFromManifest(manifest, flowName),
|
|
202
209
|
now: options.now,
|
|
210
|
+
...(options.journalStore ? { journalStore: options.journalStore } : {}),
|
|
211
|
+
...(options.eventLog ? { eventLog: options.eventLog } : {}),
|
|
203
212
|
});
|
|
204
213
|
}
|
|
205
214
|
|
|
@@ -413,6 +422,8 @@ function projectAgentRuns(runs: readonly AgentRunRecord[]): readonly AgentRunRow
|
|
|
413
422
|
agent: r.agent,
|
|
414
423
|
message: r.message,
|
|
415
424
|
ok: r.ok,
|
|
425
|
+
stopReason: r.stopReason,
|
|
426
|
+
...(r.error !== undefined ? { error: r.error } : {}),
|
|
416
427
|
steps: r.steps,
|
|
417
428
|
cost: r.cost,
|
|
418
429
|
at: r.at,
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from "../../runs/index.ts";
|
|
16
16
|
import { piiFieldNamesFromManifest } from "./runs-pii.ts";
|
|
17
17
|
import { runConsoleRunsQuery } from "./runs-query.ts";
|
|
18
|
+
import { getAgentEventLog } from "../../elements/ai/agent-event-slot.ts";
|
|
18
19
|
import { createManifestAiRuntime } from "./ai.ts";
|
|
19
20
|
import { CONSOLE_GATES } from "./console-gates.ts";
|
|
20
21
|
import { createConsoleBindings } from "./flows.ts";
|
|
@@ -364,7 +365,15 @@ export function bindManifestAiRuntime(state: ConsoleState): void {
|
|
|
364
365
|
Object.keys(state.manifest.ai.agents ?? {}).length > 0 ||
|
|
365
366
|
Object.keys(state.manifest.ai.models ?? {}).length > 0);
|
|
366
367
|
if (!hasAi) return;
|
|
367
|
-
|
|
368
|
+
const eventLog = getAgentEventLog();
|
|
369
|
+
bindAiRuntime(
|
|
370
|
+
state,
|
|
371
|
+
createManifestAiRuntime(state.manifest, {
|
|
372
|
+
now: state.now,
|
|
373
|
+
...(state.journalStore ? { journalStore: state.journalStore } : {}),
|
|
374
|
+
...(eventLog ? { eventLog } : {}),
|
|
375
|
+
}),
|
|
376
|
+
);
|
|
368
377
|
}
|
|
369
378
|
|
|
370
379
|
/**
|