stageflow 0.36.0 → 0.40.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/README.md +61 -26
- package/dist/agent/claudeAdapter.js +21 -1
- package/dist/agent/claudeTools.d.ts +13 -0
- package/dist/agent/claudeTools.js +98 -0
- package/dist/agent/piAdapter.d.ts +9 -1
- package/dist/agent/piAdapter.js +59 -18
- package/dist/agent/piIsolatedMcp.js +14 -2
- package/dist/agent/piIsolatedMcpProbe.d.ts +5 -1
- package/dist/agent/piIsolatedMcpProbe.js +16 -30
- package/dist/agent/port.d.ts +5 -0
- package/dist/agent/sandboxPiTools.d.ts +99 -0
- package/dist/agent/sandboxPiTools.js +109 -0
- package/dist/agents/agentId.d.ts +8 -0
- package/dist/agents/agentId.js +27 -0
- package/dist/agents/approvalBroker.d.ts +16 -0
- package/dist/agents/approvalBroker.js +45 -0
- package/dist/agents/authorChat.d.ts +19 -0
- package/dist/agents/authorChat.js +63 -0
- package/dist/agents/chatTurn.d.ts +23 -0
- package/dist/agents/chatTurn.js +77 -0
- package/dist/agents/communications/agentmail.d.ts +19 -0
- package/dist/agents/communications/agentmail.js +46 -0
- package/dist/agents/communications/agentmailListener.d.ts +59 -0
- package/dist/agents/communications/agentmailListener.js +183 -0
- package/dist/agents/communications/allowlist.d.ts +10 -0
- package/dist/agents/communications/allowlist.js +36 -0
- package/dist/agents/communications/channelConnections.d.ts +3 -0
- package/dist/agents/communications/channelConnections.js +51 -0
- package/dist/agents/communications/delivery.d.ts +53 -0
- package/dist/agents/communications/delivery.js +142 -0
- package/dist/agents/communications/email.d.ts +26 -0
- package/dist/agents/communications/email.js +20 -0
- package/dist/agents/communications/identity.d.ts +13 -0
- package/dist/agents/communications/identity.js +18 -0
- package/dist/agents/communications/imessageListener.d.ts +41 -0
- package/dist/agents/communications/imessageListener.js +92 -0
- package/dist/agents/communications/inbound.d.ts +63 -0
- package/dist/agents/communications/inbound.js +143 -0
- package/dist/agents/communications/listener.d.ts +36 -0
- package/dist/agents/communications/listener.js +38 -0
- package/dist/agents/communications/photon.d.ts +19 -0
- package/dist/agents/communications/photon.js +38 -0
- package/dist/agents/communications/port.d.ts +27 -0
- package/dist/agents/communications/port.js +6 -0
- package/dist/agents/communications/slack.d.ts +28 -0
- package/dist/agents/communications/slack.js +65 -0
- package/dist/agents/communications/supervisor.d.ts +36 -0
- package/dist/agents/communications/supervisor.js +96 -0
- package/dist/agents/communications/telegram.d.ts +19 -0
- package/dist/agents/communications/telegram.js +52 -0
- package/dist/agents/definitionSummary.d.ts +13 -0
- package/dist/agents/definitionSummary.js +91 -0
- package/dist/agents/delegate.d.ts +3 -0
- package/dist/agents/delegate.js +8 -0
- package/dist/agents/errors.d.ts +10 -0
- package/dist/agents/errors.js +32 -0
- package/dist/agents/guardrails.d.ts +24 -0
- package/dist/agents/guardrails.js +39 -0
- package/dist/agents/historyFit.d.ts +10 -0
- package/dist/agents/historyFit.js +26 -0
- package/dist/agents/hostContext.d.ts +54 -0
- package/dist/agents/hostContext.js +156 -0
- package/dist/agents/ledgers.d.ts +29 -0
- package/dist/agents/ledgers.js +31 -0
- package/dist/agents/lineDiff.d.ts +12 -0
- package/dist/agents/lineDiff.js +46 -0
- package/dist/agents/ndjson.d.ts +9 -0
- package/dist/agents/ndjson.js +47 -0
- package/dist/agents/otlpExport.d.ts +9 -0
- package/dist/agents/otlpExport.js +58 -0
- package/dist/agents/otlpSink.d.ts +7 -0
- package/dist/agents/otlpSink.js +23 -0
- package/dist/agents/policy.d.ts +31 -0
- package/dist/agents/policy.js +84 -0
- package/dist/agents/projectRoot.d.ts +5 -0
- package/dist/agents/projectRoot.js +15 -0
- package/dist/agents/runtime/piRuntime.d.ts +52 -0
- package/dist/agents/runtime/piRuntime.js +474 -0
- package/dist/agents/runtime/types.d.ts +77 -0
- package/dist/agents/runtime/types.js +26 -0
- package/dist/agents/sandbox.d.ts +17 -0
- package/dist/agents/sandbox.js +10 -0
- package/dist/agents/scheduleSource.d.ts +32 -0
- package/dist/agents/scheduleSource.js +100 -0
- package/dist/agents/service.d.ts +298 -0
- package/dist/agents/service.js +1105 -0
- package/dist/agents/sessions.d.ts +38 -0
- package/dist/agents/sessions.js +321 -0
- package/dist/agents/store.d.ts +277 -0
- package/dist/agents/store.js +643 -0
- package/dist/agents/trace.d.ts +27 -0
- package/dist/agents/trace.js +29 -0
- package/dist/agents/turnLifecycle.d.ts +72 -0
- package/dist/agents/turnLifecycle.js +157 -0
- package/dist/agents/webChat/publicChat.d.ts +36 -0
- package/dist/agents/webChat/publicChat.js +290 -0
- package/dist/agents/webChat/rateLimit.d.ts +15 -0
- package/dist/agents/webChat/rateLimit.js +33 -0
- package/dist/agents/webChat/slug.d.ts +2 -0
- package/dist/agents/webChat/slug.js +9 -0
- package/dist/channels/providers/agentmail.js +14 -1
- package/dist/channels/providers/photonImessage.js +36 -0
- package/dist/channels/providers/slack.js +2 -1
- package/dist/channels/providers/telegram.js +2 -1
- package/dist/channels/types.d.ts +27 -0
- package/dist/cli/agentsCommand.d.ts +14 -0
- package/dist/cli/agentsCommand.js +291 -0
- package/dist/cli.js +9 -0
- package/dist/config/browseCatalog.js +4 -11
- package/dist/config/draftPackage.d.ts +1 -0
- package/dist/config/draftPackage.js +4 -0
- package/dist/config/hostConfig.js +9 -0
- package/dist/config/loadAgent.d.ts +266 -0
- package/dist/config/loadAgent.js +374 -0
- package/dist/config/loadPipeline.js +5 -2
- package/dist/config/loadTask.js +34 -1
- package/dist/config/mcpHostCatalog.d.ts +50 -0
- package/dist/config/mcpHostCatalog.js +405 -0
- package/dist/config/mcpOAuth.d.ts +37 -0
- package/dist/config/mcpOAuth.js +263 -0
- package/dist/config/mcpRedaction.d.ts +42 -0
- package/dist/config/mcpRedaction.js +314 -0
- package/dist/config/mcpToolCache.d.ts +20 -0
- package/dist/config/mcpToolCache.js +43 -0
- package/dist/config/mergePipelineIncludes.d.ts +2 -0
- package/dist/config/mergePipelineIncludes.js +25 -0
- package/dist/config/resolveStageMcpServers.d.ts +35 -3
- package/dist/config/resolveStageMcpServers.js +124 -9
- package/dist/config/validateCatalog.d.ts +1 -1
- package/dist/config/validateCatalog.js +22 -1
- package/dist/mcp/agentTools.d.ts +8 -0
- package/dist/mcp/agentTools.js +132 -0
- package/dist/mcp/catalogTools.js +17 -10
- package/dist/mcp/deps.d.ts +6 -0
- package/dist/mcp/pipelineAuthorSupport.d.ts +19 -0
- package/dist/mcp/pipelineAuthorSupport.js +10 -0
- package/dist/mcp/pipelineAuthorTools.d.ts +8 -0
- package/dist/mcp/pipelineAuthorTools.js +109 -0
- package/dist/mcp/server.js +5 -0
- package/dist/mcp/tools.js +4 -0
- package/dist/mcp/triggerTools.js +32 -0
- package/dist/memory/access.d.ts +36 -0
- package/dist/memory/access.js +116 -0
- package/dist/memory/agentIssues.d.ts +3 -0
- package/dist/memory/agentIssues.js +23 -0
- package/dist/memory/bashDeny.d.ts +6 -0
- package/dist/memory/bashDeny.js +35 -0
- package/dist/memory/host.d.ts +51 -0
- package/dist/memory/host.js +394 -0
- package/dist/memory/ids.d.ts +15 -0
- package/dist/memory/ids.js +31 -0
- package/dist/memory/worker.d.ts +1 -0
- package/dist/memory/worker.js +84 -0
- package/dist/models/catalog.d.ts +113 -0
- package/dist/models/catalog.js +238 -0
- package/dist/models/response.d.ts +29 -0
- package/dist/models/response.js +25 -0
- package/dist/operatorAgent/agentAuthorContext.d.ts +17 -0
- package/dist/operatorAgent/agentAuthorContext.js +32 -0
- package/dist/operatorAgent/fakeHost.js +2 -0
- package/dist/operatorAgent/host.js +6 -3
- package/dist/operatorAgent/index.d.ts +4 -0
- package/dist/operatorAgent/index.js +4 -0
- package/dist/operatorAgent/piModel.js +2 -0
- package/dist/operatorAgent/profiles/agentAuthor.d.ts +11 -0
- package/dist/operatorAgent/profiles/agentAuthor.js +192 -0
- package/dist/operatorAgent/profiles/agentAuthorPlaybook.d.ts +1 -0
- package/dist/operatorAgent/profiles/agentAuthorPlaybook.js +29 -0
- package/dist/operatorAgent/profiles/workshopAuthor.js +2 -2
- package/dist/operatorAgent/profiles/workshopAuthorTools.js +3 -3
- package/dist/operatorAgent/proposals.d.ts +1 -0
- package/dist/operatorAgent/proposals.js +23 -2
- package/dist/package-meta.d.ts +1 -1
- package/dist/package-meta.js +1 -1
- package/dist/preflight/pipelinePreflight.js +1 -0
- package/dist/runstore/createStore.d.ts +2 -0
- package/dist/runstore/createStore.js +8 -0
- package/dist/runstore/overviewSummary.d.ts +56 -0
- package/dist/runstore/overviewSummary.js +134 -0
- package/dist/runstore/port.d.ts +3 -0
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +2 -0
- package/dist/runstore/sqlite/SqliteRunStore.js +18 -0
- package/dist/runstore/sqlite/migrations/013-agents.d.ts +12 -0
- package/dist/runstore/sqlite/migrations/013-agents.js +156 -0
- package/dist/runstore/sqlite/migrations/014-agent-deliveries.d.ts +11 -0
- package/dist/runstore/sqlite/migrations/014-agent-deliveries.js +32 -0
- package/dist/runstore/sqlite/migrations/index.js +4 -0
- package/dist/runtime/gitCheckoutCapability.d.ts +1 -0
- package/dist/runtime/gitCheckoutCapability.js +1 -1
- package/dist/runtime/gitWorktreeCheckout.d.ts +22 -0
- package/dist/runtime/gitWorktreeCheckout.js +120 -0
- package/dist/runtime/sandboxContainer.d.ts +62 -0
- package/dist/runtime/sandboxContainer.js +151 -0
- package/dist/runtime/sandboxDesk.d.ts +28 -0
- package/dist/runtime/sandboxDesk.js +242 -0
- package/dist/runtime/sandboxFiles.d.ts +20 -0
- package/dist/runtime/sandboxFiles.js +150 -0
- package/dist/runtime/stageAttemptBootstrap.js +15 -1
- package/dist/runtime/stageEnvironment.d.ts +2 -0
- package/dist/runtime/stageEnvironment.js +8 -1
- package/dist/runtime/stageProcessLauncher.js +23 -0
- package/dist/runtime/stageRoots.d.ts +9 -0
- package/dist/runtime/stageRoots.js +14 -0
- package/dist/runtime/stageRunner.d.ts +5 -0
- package/dist/runtime/stageRunner.js +38 -4
- package/dist/runtime/stageSandbox.d.ts +16 -0
- package/dist/runtime/stageSandbox.js +54 -0
- package/dist/runtime/stageSecrets.d.ts +6 -1
- package/dist/runtime/stageSecrets.js +34 -1
- package/dist/runtime/stageWorker.js +138 -105
- package/dist/runtime/workspaceBinding.d.ts +2 -1
- package/dist/runtime/workspaceBinding.js +20 -4
- package/dist/server/agentAuthorRoutes.d.ts +5 -0
- package/dist/server/agentAuthorRoutes.js +64 -0
- package/dist/server/agentRoutes.d.ts +5 -0
- package/dist/server/agentRoutes.js +323 -0
- package/dist/server/bootstrap.d.ts +8 -0
- package/dist/server/bootstrap.js +36 -10
- package/dist/server/controlToken.js +8 -0
- package/dist/server/http.d.ts +8 -1
- package/dist/server/http.js +172 -20
- package/dist/server/mcpHost.js +1 -0
- package/dist/server/memoryRoutes.d.ts +5 -0
- package/dist/server/memoryRoutes.js +146 -0
- package/dist/server/projectMcpRoutes.d.ts +1 -0
- package/dist/server/projectMcpRoutes.js +241 -3
- package/dist/toolkit/guard.d.ts +27 -0
- package/dist/toolkit/guard.js +98 -0
- package/dist/toolkit/index.d.ts +48 -0
- package/dist/toolkit/index.js +82 -0
- package/dist/toolkit/meta.d.ts +36 -0
- package/dist/toolkit/meta.js +117 -0
- package/dist/toolkit/profiles.d.ts +13 -0
- package/dist/toolkit/profiles.js +25 -0
- package/dist/toolkit/record.d.ts +15 -0
- package/dist/toolkit/record.js +17 -0
- package/dist/tools/memoryTools.d.ts +95 -0
- package/dist/tools/memoryTools.js +179 -0
- package/dist/types/pipeline.d.ts +3 -0
- package/dist/types/stage.d.ts +2 -0
- package/dist/types/task.d.ts +11 -1
- package/dist/ui/assets/AssistantRuntimeProvider-CsiPFhCT.js +9 -0
- package/dist/ui/assets/MarkdownText-D7tYaIFO.js +29 -0
- package/dist/ui/assets/ReadonlyStore-BfXqHykm.js +6 -0
- package/dist/ui/assets/Tooltip-DrpoRRNi.js +1 -0
- package/dist/ui/assets/chat-BbLukq9W.js +5 -0
- package/dist/ui/assets/chat-zIZU_bP1.css +2 -0
- package/dist/ui/assets/jsx-runtime-iVoFAvQ_.js +1 -0
- package/dist/ui/assets/{index-Clv4sJpU.css → main-BFo79krC.css} +1 -1
- package/dist/ui/assets/main-CijYMeM0.js +259 -0
- package/dist/ui/chat.html +25 -0
- package/dist/ui/index.html +6 -2
- package/dist/workshop/modelSettings.d.ts +2 -2
- package/dist/workshop/modelSettings.js +5 -11
- package/examples/README.md +1 -0
- package/examples/channels-smoke/README.md +12 -1
- package/examples/github-release/README.md +1 -1
- package/examples/memory/README.md +101 -0
- package/examples/memory/desk-outage.task.yaml +15 -0
- package/examples/memory/desk-refund.task.yaml +15 -0
- package/examples/memory/desk-revisit-outage.task.yaml +11 -0
- package/examples/memory/desk-revisit.pipeline.yaml +123 -0
- package/examples/memory/desk.pipeline.yaml +192 -0
- package/examples/memory/file-card.pipeline.yaml +39 -0
- package/examples/memory/file-ledger.task.yaml +22 -0
- package/examples/memory/file-release.task.yaml +24 -0
- package/examples/memory/file-rich.pipeline.yaml +40 -0
- package/examples/memory/file-ticket.task.yaml +7 -0
- package/examples/memory/file-timeline.task.yaml +28 -0
- package/examples/memory/find-card.pipeline.yaml +46 -0
- package/examples/memory/find-ticket.task.yaml +7 -0
- package/examples/memory/missing.pipeline.yaml +16 -0
- package/examples/memory/probe-bash.task.yaml +3 -0
- package/examples/memory/recall.pipeline.yaml +24 -0
- package/examples/memory/recall.task.yaml +5 -0
- package/examples/memory/remember-ada.task.yaml +6 -0
- package/examples/memory/remember-grace.task.yaml +6 -0
- package/examples/memory/remember.pipeline.yaml +58 -0
- package/examples/memory/sandbox-memory-bash.pipeline.yaml +28 -0
- package/examples/memory/stageflow.yaml +12 -0
- package/examples/memory/update-card.pipeline.yaml +43 -0
- package/examples/memory/update-ledger.task.yaml +23 -0
- package/examples/memory/update-rich.pipeline.yaml +44 -0
- package/examples/memory/update-ticket.task.yaml +7 -0
- package/examples/ship-feature/README.md +15 -0
- package/examples/stage-mcp/README.md +1 -1
- package/package.json +2 -1
- package/dist/ui/assets/index-BOhqnPNg.js +0 -260
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { OtlpSink } from "./otlpSink.js";
|
|
2
|
+
import type { ConversationBackend } from "./runtime/types.js";
|
|
3
|
+
import type { AgentService, AgentSessionDetail } from "./service.js";
|
|
4
|
+
export type { ConversationBackend, ConversationMessage } from "./runtime/types.js";
|
|
5
|
+
export type SendTurnInput = {
|
|
6
|
+
projectRoot: string;
|
|
7
|
+
agentId: string;
|
|
8
|
+
sessionId?: string;
|
|
9
|
+
text: string;
|
|
10
|
+
/** Who sent the message (display name or channel identity). */
|
|
11
|
+
author?: string | null;
|
|
12
|
+
/** Where a new session came from: console, cli, mcp, schedule, or a provider id. */
|
|
13
|
+
source?: string;
|
|
14
|
+
onDelta?: (text: string) => void;
|
|
15
|
+
onSessionOpened?: (sessionId: string) => void;
|
|
16
|
+
/** Retry this earlier, interrupted turn: its user message is reused, not asked again. */
|
|
17
|
+
resumeTurnId?: string;
|
|
18
|
+
};
|
|
19
|
+
export type ConversationOptions = {
|
|
20
|
+
service: AgentService;
|
|
21
|
+
backend: ConversationBackend;
|
|
22
|
+
otlpSink?: OtlpSink | undefined;
|
|
23
|
+
logError?: (message: string) => void;
|
|
24
|
+
};
|
|
25
|
+
/** One turn: open (if needed) → record user message → model + tools → guardrails → record reply. */
|
|
26
|
+
export declare class AgentConversation {
|
|
27
|
+
private readonly options;
|
|
28
|
+
private readonly depth;
|
|
29
|
+
private readonly parentSignal?;
|
|
30
|
+
private controller;
|
|
31
|
+
constructor(options: ConversationOptions, depth?: number, parentSignal?: AbortSignal | undefined);
|
|
32
|
+
stop(): void;
|
|
33
|
+
send(input: SendTurnInput): Promise<AgentSessionDetail>;
|
|
34
|
+
private runTurn;
|
|
35
|
+
private score;
|
|
36
|
+
/** Best-effort push to the configured OTLP collector; a collector outage never fails a turn. */
|
|
37
|
+
private exportLive;
|
|
38
|
+
}
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { assertDelegateAllowed } from "./delegate.js";
|
|
3
|
+
import { AgentError, isAgentError } from "./errors.js";
|
|
4
|
+
import { guardedStream } from "./guardrails.js";
|
|
5
|
+
import { fitHistory } from "./historyFit.js";
|
|
6
|
+
import { isToolGranted, requiresAskFirst } from "./policy.js";
|
|
7
|
+
import { prepareSpan } from "./trace.js";
|
|
8
|
+
const GROUNDEDNESS_PROMPT = (reply, evidence) => [
|
|
9
|
+
"Rate how well the ANSWER is supported by the TOOL OUTPUTS, from 0 (unsupported or contradicted) to 1 (fully supported).",
|
|
10
|
+
"Reply with only the number.",
|
|
11
|
+
"",
|
|
12
|
+
"TOOL OUTPUTS:",
|
|
13
|
+
...evidence.map((e, i) => `[${i + 1}] ${e.slice(0, 4000)}`),
|
|
14
|
+
"",
|
|
15
|
+
"ANSWER:",
|
|
16
|
+
reply.slice(0, 8000),
|
|
17
|
+
].join("\n");
|
|
18
|
+
function spanId() {
|
|
19
|
+
return randomBytes(8).toString("hex");
|
|
20
|
+
}
|
|
21
|
+
function toJson(value) {
|
|
22
|
+
try {
|
|
23
|
+
return JSON.stringify(value);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return String(value);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** One turn: open (if needed) → record user message → model + tools → guardrails → record reply. */
|
|
30
|
+
export class AgentConversation {
|
|
31
|
+
options;
|
|
32
|
+
depth;
|
|
33
|
+
parentSignal;
|
|
34
|
+
controller = null;
|
|
35
|
+
constructor(options, depth = 0, parentSignal) {
|
|
36
|
+
this.options = options;
|
|
37
|
+
this.depth = depth;
|
|
38
|
+
this.parentSignal = parentSignal;
|
|
39
|
+
}
|
|
40
|
+
stop() {
|
|
41
|
+
this.controller?.abort();
|
|
42
|
+
}
|
|
43
|
+
async send(input) {
|
|
44
|
+
const { service, backend } = this.options;
|
|
45
|
+
const controller = new AbortController();
|
|
46
|
+
this.controller = controller;
|
|
47
|
+
const onParentAbort = () => controller.abort();
|
|
48
|
+
this.parentSignal?.addEventListener("abort", onParentAbort, { once: true });
|
|
49
|
+
try {
|
|
50
|
+
let sessionId = input.sessionId;
|
|
51
|
+
if (!sessionId) {
|
|
52
|
+
const opened = await service.openSession(input.projectRoot, input.agentId, {
|
|
53
|
+
source: input.source ?? "console",
|
|
54
|
+
sourceRef: input.author ?? null,
|
|
55
|
+
});
|
|
56
|
+
sessionId = opened.id;
|
|
57
|
+
input.onSessionOpened?.(sessionId);
|
|
58
|
+
}
|
|
59
|
+
const active = await service.beginTurn(input.projectRoot, sessionId, {
|
|
60
|
+
text: input.text,
|
|
61
|
+
author: input.author ?? null,
|
|
62
|
+
...(input.resumeTurnId ? { resumeTurnId: input.resumeTurnId } : {}),
|
|
63
|
+
});
|
|
64
|
+
if (active.session.agentId !== input.agentId) {
|
|
65
|
+
await service.completeTurn(input.projectRoot, active.turn.id, { status: "failed", errorCode: "agent.session_not_found" });
|
|
66
|
+
throw new AgentError("agent.session_not_found");
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
return await this.runTurn(input, sessionId, active, controller);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
// A failure outside the model call (storage, spans) must not leave the session stuck running.
|
|
73
|
+
const state = (await service.getSession(input.projectRoot, sessionId).catch(() => undefined))?.turns.find((t) => t.id === active.turn.id);
|
|
74
|
+
if (state && (state.status === "running" || state.status === "waiting")) {
|
|
75
|
+
await service.completeTurn(input.projectRoot, active.turn.id, {
|
|
76
|
+
status: "failed",
|
|
77
|
+
errorCode: isAgentError(error) ? error.code : "agent.backend_failed",
|
|
78
|
+
}).catch(() => undefined);
|
|
79
|
+
}
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
this.parentSignal?.removeEventListener("abort", onParentAbort);
|
|
85
|
+
if (this.controller === controller)
|
|
86
|
+
this.controller = null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async runTurn(input, sessionId, active, controller) {
|
|
90
|
+
const { service, backend } = this.options;
|
|
91
|
+
const { definition, turn } = active;
|
|
92
|
+
const capture = definition.observability.capture;
|
|
93
|
+
const rootId = spanId();
|
|
94
|
+
const startedAt = new Date();
|
|
95
|
+
const record = (draft) => {
|
|
96
|
+
const span = prepareSpan(capture, draft);
|
|
97
|
+
if (span)
|
|
98
|
+
service.recordSpan(input.projectRoot, span);
|
|
99
|
+
};
|
|
100
|
+
const toolStarts = new Map();
|
|
101
|
+
let stopReason;
|
|
102
|
+
let gateChain = Promise.resolve();
|
|
103
|
+
const gateOne = async (call) => {
|
|
104
|
+
if (!isToolGranted(definition, call.tool)) {
|
|
105
|
+
return { allow: false, reason: `Tool ${call.tool} is not granted to this agent.` };
|
|
106
|
+
}
|
|
107
|
+
if (!requiresAskFirst(definition, call.tool))
|
|
108
|
+
return { allow: true };
|
|
109
|
+
const gateStart = new Date();
|
|
110
|
+
const approval = await service.requestApproval(input.projectRoot, sessionId, {
|
|
111
|
+
tool: call.tool,
|
|
112
|
+
payload: call.input,
|
|
113
|
+
turnId: turn.id,
|
|
114
|
+
});
|
|
115
|
+
if (approval.status === "approved")
|
|
116
|
+
return { allow: true };
|
|
117
|
+
// Parked unless the decision already landed between the request and now.
|
|
118
|
+
const parked = await service.waitOnApproval(input.projectRoot, turn.id, approval.id);
|
|
119
|
+
let outcome;
|
|
120
|
+
if (parked === "waiting") {
|
|
121
|
+
outcome = await service.approvalBroker.wait(approval.id, { expiresAt: approval.expiresAt, signal: controller.signal, now: () => service.clockMs() });
|
|
122
|
+
if (outcome === "expired")
|
|
123
|
+
await service.pendingApprovals(input.projectRoot);
|
|
124
|
+
// A stopped turn must not leave its approval open: it could be approved later with nobody waiting.
|
|
125
|
+
if (outcome === "aborted")
|
|
126
|
+
await service.closeApproval(input.projectRoot, approval.id);
|
|
127
|
+
else
|
|
128
|
+
await service.resumeTurn(input.projectRoot, turn.id);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
outcome = parked === "inactive" ? "aborted" : parked;
|
|
132
|
+
}
|
|
133
|
+
record({
|
|
134
|
+
turnId: turn.id,
|
|
135
|
+
parentSpanId: rootId,
|
|
136
|
+
name: `approval ${call.tool}`,
|
|
137
|
+
kind: "gate",
|
|
138
|
+
status: outcome === "approved" ? "ok" : "error",
|
|
139
|
+
startedAt: gateStart,
|
|
140
|
+
endedAt: new Date(),
|
|
141
|
+
attributes: { "stageflow.approval.outcome": outcome, "gen_ai.tool.name": call.tool },
|
|
142
|
+
});
|
|
143
|
+
if (outcome === "approved")
|
|
144
|
+
return { allow: true };
|
|
145
|
+
if (outcome === "aborted")
|
|
146
|
+
return { allow: false, reason: "The turn was stopped.", stopTurn: true };
|
|
147
|
+
if (outcome === "denied")
|
|
148
|
+
return { allow: false, reason: `The operator denied ${call.tool}. Do not retry it; continue without it or explain.` };
|
|
149
|
+
if (definition.approvals.on_timeout === "deny") {
|
|
150
|
+
stopReason = { code: "agent.approval_expired", message: "Approval timed out" };
|
|
151
|
+
return { allow: false, reason: "Approval timed out; the turn was ended.", stopTurn: true };
|
|
152
|
+
}
|
|
153
|
+
return { allow: false, reason: `Approval for ${call.tool} timed out and the action was skipped. Continue without it.` };
|
|
154
|
+
};
|
|
155
|
+
const hooks = {
|
|
156
|
+
gate: (call) => {
|
|
157
|
+
const next = gateChain.then(() => gateOne(call));
|
|
158
|
+
gateChain = next.catch(() => undefined);
|
|
159
|
+
return next;
|
|
160
|
+
},
|
|
161
|
+
toolStarted: (call) => {
|
|
162
|
+
toolStarts.set(call.id, new Date());
|
|
163
|
+
},
|
|
164
|
+
toolFinished: (call, result) => {
|
|
165
|
+
const isAgent = call.tool.startsWith("agent:");
|
|
166
|
+
record({
|
|
167
|
+
turnId: turn.id,
|
|
168
|
+
parentSpanId: rootId,
|
|
169
|
+
name: isAgent ? `invoke_agent ${call.tool.slice(6)}` : `execute_tool ${call.tool}`,
|
|
170
|
+
kind: (isAgent ? "sub_agent" : "tool"),
|
|
171
|
+
status: result.ok ? "ok" : "error",
|
|
172
|
+
startedAt: toolStarts.get(call.id) ?? new Date(),
|
|
173
|
+
endedAt: new Date(),
|
|
174
|
+
attributes: { "gen_ai.tool.name": call.tool },
|
|
175
|
+
input: toJson(call.input),
|
|
176
|
+
output: result.output,
|
|
177
|
+
});
|
|
178
|
+
},
|
|
179
|
+
modelFinished: (info) => {
|
|
180
|
+
record({
|
|
181
|
+
turnId: turn.id,
|
|
182
|
+
parentSpanId: rootId,
|
|
183
|
+
name: "chat",
|
|
184
|
+
kind: "model",
|
|
185
|
+
status: "ok",
|
|
186
|
+
startedAt: info.startedAt,
|
|
187
|
+
endedAt: info.endedAt,
|
|
188
|
+
usage: { inputTokens: info.inputTokens, outputTokens: info.outputTokens },
|
|
189
|
+
attributes: {
|
|
190
|
+
"gen_ai.system": definition.model.backend,
|
|
191
|
+
...(info.model ? { "gen_ai.request.model": info.model } : {}),
|
|
192
|
+
"gen_ai.usage.cost_usd": info.costUsd,
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
},
|
|
196
|
+
delegate: async (agentId, message) => {
|
|
197
|
+
assertDelegateAllowed(definition, agentId, this.depth);
|
|
198
|
+
const child = new AgentConversation(this.options, this.depth + 1, controller.signal);
|
|
199
|
+
try {
|
|
200
|
+
const detail = await child.send({
|
|
201
|
+
projectRoot: input.projectRoot,
|
|
202
|
+
agentId,
|
|
203
|
+
text: message,
|
|
204
|
+
source: "agent",
|
|
205
|
+
author: active.session.agentId,
|
|
206
|
+
});
|
|
207
|
+
return [...detail.messages].reverse().find((m) => m.role === "assistant")?.text ?? "(no reply)";
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
return `Sub-agent ${agentId} failed: ${isAgentError(error) ? error.code : "error"}${error instanceof Error && error.message !== error.code ? ` (${error.message})` : ""}`;
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
const stream = guardedStream(definition.guardrails, input.onDelta);
|
|
215
|
+
let result;
|
|
216
|
+
try {
|
|
217
|
+
const transcript = [...active.history, active.userMessage].map((m) => ({ role: m.role, text: m.text }));
|
|
218
|
+
result = await backend.complete({
|
|
219
|
+
sessionId,
|
|
220
|
+
projectRoot: input.projectRoot,
|
|
221
|
+
definition,
|
|
222
|
+
messages: [...fitHistory(transcript.slice(0, -1), definition.limits.long_context), transcript.at(-1)],
|
|
223
|
+
signal: controller.signal,
|
|
224
|
+
onDelta: (text) => stream.push(text),
|
|
225
|
+
hooks,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
stream.discard();
|
|
230
|
+
const code = isAgentError(error) ? error.code : "agent.backend_failed";
|
|
231
|
+
record({ turnId: turn.id, name: `invoke_agent ${definition.id}`, kind: "agent", status: "error", startedAt, endedAt: new Date(), attributes: { "error.type": code } });
|
|
232
|
+
await service.completeTurn(input.projectRoot, turn.id, { status: "failed", errorCode: code });
|
|
233
|
+
this.exportLive(input.projectRoot, definition, turn.id);
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
const cancelled = controller.signal.aborted;
|
|
237
|
+
const usage = result.usage;
|
|
238
|
+
const endRoot = (status) => {
|
|
239
|
+
const rootSpan = prepareSpan(capture, {
|
|
240
|
+
turnId: turn.id,
|
|
241
|
+
name: `invoke_agent ${definition.id}`,
|
|
242
|
+
kind: "agent",
|
|
243
|
+
status,
|
|
244
|
+
startedAt,
|
|
245
|
+
endedAt: new Date(),
|
|
246
|
+
...(usage ? { usage: { inputTokens: usage.inputTokens, outputTokens: usage.outputTokens } } : {}),
|
|
247
|
+
attributes: {
|
|
248
|
+
"gen_ai.agent.name": definition.id,
|
|
249
|
+
"stageflow.agent.revision": active.session.revision,
|
|
250
|
+
...(definition.model.id ? { "gen_ai.request.model": definition.model.id } : {}),
|
|
251
|
+
},
|
|
252
|
+
input: input.text,
|
|
253
|
+
output: result.text,
|
|
254
|
+
});
|
|
255
|
+
if (rootSpan)
|
|
256
|
+
service.recordSpan(input.projectRoot, { ...rootSpan, id: rootId });
|
|
257
|
+
};
|
|
258
|
+
const guard = stream.release(result.text);
|
|
259
|
+
if (!guard.ok) {
|
|
260
|
+
record({ turnId: turn.id, parentSpanId: rootId, name: "guardrail", kind: "guardrail", status: "error", startedAt: new Date(), endedAt: new Date(), attributes: { "stageflow.guardrail.pattern": guard.pattern } });
|
|
261
|
+
endRoot("error");
|
|
262
|
+
await service.completeTurn(input.projectRoot, turn.id, {
|
|
263
|
+
status: "failed",
|
|
264
|
+
errorCode: "agent.guardrail_blocked",
|
|
265
|
+
...(usage ? { usage, costUsd: usage.costUsd } : {}),
|
|
266
|
+
});
|
|
267
|
+
this.exportLive(input.projectRoot, definition, turn.id);
|
|
268
|
+
throw new AgentError("agent.guardrail_blocked", `Reply blocked by guardrail ${guard.pattern}`);
|
|
269
|
+
}
|
|
270
|
+
const evalScores = await this.score(input.projectRoot, definition, turn.id, rootId, result, controller.signal, cancelled || stopReason !== undefined);
|
|
271
|
+
const failed = stopReason !== undefined;
|
|
272
|
+
endRoot(cancelled ? "cancelled" : failed ? "error" : "ok");
|
|
273
|
+
const detail = await service.completeTurn(input.projectRoot, turn.id, {
|
|
274
|
+
status: failed ? "failed" : cancelled ? "cancelled" : "succeeded",
|
|
275
|
+
...(failed ? { errorCode: stopReason.code } : {}),
|
|
276
|
+
assistantText: result.text,
|
|
277
|
+
...(usage ? { usage, costUsd: usage.costUsd } : {}),
|
|
278
|
+
...(evalScores.length > 0 ? { evalScores } : {}),
|
|
279
|
+
});
|
|
280
|
+
this.exportLive(input.projectRoot, definition, turn.id);
|
|
281
|
+
return detail;
|
|
282
|
+
}
|
|
283
|
+
async score(projectRoot, definition, turnId, rootId, result, signal, skip) {
|
|
284
|
+
const { backend, service } = this.options;
|
|
285
|
+
if (skip || !definition.evals.groundedness || !backend.judge || result.toolOutputs.length === 0 || !result.text.trim())
|
|
286
|
+
return [];
|
|
287
|
+
const startedAt = new Date();
|
|
288
|
+
try {
|
|
289
|
+
const raw = await backend.judge({ projectRoot, definition, prompt: GROUNDEDNESS_PROMPT(result.text, result.toolOutputs), signal });
|
|
290
|
+
const match = /(?:^|[^\d.])(1(?:\.0+)?|0?\.\d+|0)(?![\d])/.exec(raw.trim());
|
|
291
|
+
if (!match)
|
|
292
|
+
throw new Error("no score in judge output");
|
|
293
|
+
const score = Math.min(1, Math.max(0, Number(match[1])));
|
|
294
|
+
const span = prepareSpan(definition.observability.capture, {
|
|
295
|
+
turnId, parentSpanId: rootId, name: "eval groundedness", kind: "guardrail", status: "ok", startedAt, endedAt: new Date(),
|
|
296
|
+
attributes: { "gen_ai.evaluation.name": "groundedness", "gen_ai.evaluation.score.value": score },
|
|
297
|
+
});
|
|
298
|
+
if (span)
|
|
299
|
+
service.recordSpan(projectRoot, span);
|
|
300
|
+
return [{ metric: "groundedness", score }];
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
this.options.logError?.(`groundedness eval failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
304
|
+
return [];
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/** Best-effort push to the configured OTLP collector; a collector outage never fails a turn. */
|
|
308
|
+
exportLive(projectRoot, definition, turnId) {
|
|
309
|
+
const sink = this.options.otlpSink;
|
|
310
|
+
if (!sink || !definition.observability.otlp || definition.observability.capture === "off")
|
|
311
|
+
return;
|
|
312
|
+
void (async () => {
|
|
313
|
+
try {
|
|
314
|
+
await sink(this.options.service.exportTurnOtlp(projectRoot, turnId));
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
this.options.logError?.(`OTLP export failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
318
|
+
}
|
|
319
|
+
})();
|
|
320
|
+
}
|
|
321
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import type Database from "better-sqlite3";
|
|
2
|
+
import { type AgentDefinition, type AgentIssue } from "../config/loadAgent.js";
|
|
3
|
+
/**
|
|
4
|
+
* SQLite persistence for project agents (migration 013). Every method is a
|
|
5
|
+
* single statement or an explicit transaction; nothing here swallows a
|
|
6
|
+
* storage error. Business rules live in AgentService.
|
|
7
|
+
*/
|
|
8
|
+
export type RevisionSource = "console" | "file" | "author" | "restore" | "api";
|
|
9
|
+
/**
|
|
10
|
+
* The one list of session statuses. The database CHECK (migration 013), the HTTP filter and the MCP tool schema all
|
|
11
|
+
* read this. `held` is kept because the CHECK allows it and old rows may carry it; nothing writes it any more.
|
|
12
|
+
*/
|
|
13
|
+
export declare const SESSION_STATUSES: readonly ["open", "running", "waiting", "held", "closed"];
|
|
14
|
+
export type SessionStatus = (typeof SESSION_STATUSES)[number];
|
|
15
|
+
export declare function isSessionStatus(value: string): value is SessionStatus;
|
|
16
|
+
export type TurnStatus = "running" | "waiting" | "succeeded" | "failed" | "cancelled";
|
|
17
|
+
export type ApprovalStatus = "pending" | "approved" | "denied" | "expired";
|
|
18
|
+
export type ApprovalActor = {
|
|
19
|
+
kind: "operator";
|
|
20
|
+
} | {
|
|
21
|
+
kind: "channel";
|
|
22
|
+
channelId: string;
|
|
23
|
+
};
|
|
24
|
+
export type OutboundSendState = "submitted" | "unknown";
|
|
25
|
+
export type AgentDefinitionRow = {
|
|
26
|
+
agentId: string;
|
|
27
|
+
currentRevision: number;
|
|
28
|
+
archived: boolean;
|
|
29
|
+
fileHash: string | null;
|
|
30
|
+
issues: AgentIssue[];
|
|
31
|
+
createdAt: string;
|
|
32
|
+
updatedAt: string;
|
|
33
|
+
};
|
|
34
|
+
export type AgentRevision = {
|
|
35
|
+
agentId: string;
|
|
36
|
+
revision: number;
|
|
37
|
+
definition: AgentDefinition;
|
|
38
|
+
yaml: string;
|
|
39
|
+
source: RevisionSource;
|
|
40
|
+
author: string | null;
|
|
41
|
+
summary: string | null;
|
|
42
|
+
restoredFrom: number | null;
|
|
43
|
+
createdAt: string;
|
|
44
|
+
};
|
|
45
|
+
export type AgentRevisionMeta = Omit<AgentRevision, "definition" | "yaml">;
|
|
46
|
+
export type AgentSession = {
|
|
47
|
+
id: string;
|
|
48
|
+
agentId: string;
|
|
49
|
+
revision: number;
|
|
50
|
+
backend: "pi" | "claude";
|
|
51
|
+
status: SessionStatus;
|
|
52
|
+
source: string;
|
|
53
|
+
sourceRef: string | null;
|
|
54
|
+
threadKey: string | null;
|
|
55
|
+
title: string | null;
|
|
56
|
+
createdAt: string;
|
|
57
|
+
updatedAt: string;
|
|
58
|
+
};
|
|
59
|
+
/** A session row plus rollups over its turns, for list views. */
|
|
60
|
+
export type AgentSessionListItem = AgentSession & {
|
|
61
|
+
turnCount: number;
|
|
62
|
+
inputTokens: number;
|
|
63
|
+
outputTokens: number;
|
|
64
|
+
costUsd: number | null;
|
|
65
|
+
lastTurnStatus: TurnStatus | null;
|
|
66
|
+
};
|
|
67
|
+
export type AgentMessage = {
|
|
68
|
+
id: string;
|
|
69
|
+
sessionId: string;
|
|
70
|
+
seq: number;
|
|
71
|
+
turnId: string | null;
|
|
72
|
+
role: "user" | "assistant";
|
|
73
|
+
author: string | null;
|
|
74
|
+
text: string;
|
|
75
|
+
createdAt: string;
|
|
76
|
+
};
|
|
77
|
+
export type AgentTurn = {
|
|
78
|
+
id: string;
|
|
79
|
+
sessionId: string;
|
|
80
|
+
seq: number;
|
|
81
|
+
revision: number;
|
|
82
|
+
traceId: string;
|
|
83
|
+
status: TurnStatus;
|
|
84
|
+
errorCode: string | null;
|
|
85
|
+
inputTokens: number | null;
|
|
86
|
+
outputTokens: number | null;
|
|
87
|
+
costUsd: number | null;
|
|
88
|
+
startedAt: string;
|
|
89
|
+
endedAt: string | null;
|
|
90
|
+
};
|
|
91
|
+
export type DeliveryState = "pending" | "sending" | "delivered" | "failed" | "unknown";
|
|
92
|
+
/** A reply on a channel and how getting it to the person went. */
|
|
93
|
+
export type AgentDelivery = {
|
|
94
|
+
turnId: string;
|
|
95
|
+
sessionId: string;
|
|
96
|
+
connectionId: string;
|
|
97
|
+
provider: string;
|
|
98
|
+
replyTo: string;
|
|
99
|
+
text: string;
|
|
100
|
+
state: DeliveryState;
|
|
101
|
+
attempts: number;
|
|
102
|
+
errorCode: string | null;
|
|
103
|
+
errorMessage: string | null;
|
|
104
|
+
/** True when a later attempt may work (a definite failure that is not a configuration problem). */
|
|
105
|
+
retryable: boolean;
|
|
106
|
+
nextAttemptAt: string | null;
|
|
107
|
+
createdAt: string;
|
|
108
|
+
updatedAt: string;
|
|
109
|
+
};
|
|
110
|
+
export type AgentSpan = {
|
|
111
|
+
id: string;
|
|
112
|
+
turnId: string;
|
|
113
|
+
parentSpanId: string | null;
|
|
114
|
+
name: string;
|
|
115
|
+
kind: string;
|
|
116
|
+
status: string;
|
|
117
|
+
startedAt: string;
|
|
118
|
+
endedAt: string | null;
|
|
119
|
+
durationMs: number | null;
|
|
120
|
+
inputTokens: number | null;
|
|
121
|
+
outputTokens: number | null;
|
|
122
|
+
attributes: Record<string, unknown>;
|
|
123
|
+
inputText: string | null;
|
|
124
|
+
outputText: string | null;
|
|
125
|
+
};
|
|
126
|
+
export type AgentApproval = {
|
|
127
|
+
id: string;
|
|
128
|
+
sessionId: string;
|
|
129
|
+
turnId: string | null;
|
|
130
|
+
tool: string;
|
|
131
|
+
payload: Record<string, unknown>;
|
|
132
|
+
payloadHash: string;
|
|
133
|
+
status: ApprovalStatus;
|
|
134
|
+
createdAt: string;
|
|
135
|
+
expiresAt: string;
|
|
136
|
+
decidedAt: string | null;
|
|
137
|
+
actor: ApprovalActor | null;
|
|
138
|
+
};
|
|
139
|
+
export type AgentEvalScore = {
|
|
140
|
+
turnId: string;
|
|
141
|
+
metric: string;
|
|
142
|
+
score: number;
|
|
143
|
+
recordedAt: string;
|
|
144
|
+
};
|
|
145
|
+
export type SessionQuery = {
|
|
146
|
+
status?: SessionStatus;
|
|
147
|
+
source?: string;
|
|
148
|
+
search?: string;
|
|
149
|
+
limit: number;
|
|
150
|
+
/** Opaque cursor from a previous page. */
|
|
151
|
+
cursor?: string;
|
|
152
|
+
};
|
|
153
|
+
export type SessionStats = {
|
|
154
|
+
total: number;
|
|
155
|
+
active: number;
|
|
156
|
+
last7Days: number;
|
|
157
|
+
waiting: number;
|
|
158
|
+
lastActivityAt: string | null;
|
|
159
|
+
/** Turns that ended in the window, and how many of those succeeded. */
|
|
160
|
+
turnsFinished7d: number;
|
|
161
|
+
turnsSucceeded7d: number;
|
|
162
|
+
/** Sum of known turn costs in the window; null when no turn reported a cost. */
|
|
163
|
+
cost7d: number | null;
|
|
164
|
+
/** Sum of known turn costs across every session, all time; null when no turn reported a cost. */
|
|
165
|
+
costTotal: number | null;
|
|
166
|
+
};
|
|
167
|
+
export declare function agentProjectKey(projectRoot: string): string;
|
|
168
|
+
export declare class AgentStore {
|
|
169
|
+
private readonly db;
|
|
170
|
+
constructor(db: Database.Database);
|
|
171
|
+
transaction<T>(work: () => T): T;
|
|
172
|
+
listDefinitions(projectKey: string): AgentDefinitionRow[];
|
|
173
|
+
getDefinition(projectKey: string, agentId: string): AgentDefinitionRow | undefined;
|
|
174
|
+
/** Record the file's latest observed state without changing the revision (invalid edit, archive, restore). */
|
|
175
|
+
updateDefinitionState(projectKey: string, agentId: string, patch: {
|
|
176
|
+
archived?: boolean;
|
|
177
|
+
fileHash?: string | null;
|
|
178
|
+
issues?: AgentIssue[];
|
|
179
|
+
}, now: string): void;
|
|
180
|
+
/** Append the next revision and make it current. Returns the new revision number. */
|
|
181
|
+
appendRevision(projectKey: string, input: {
|
|
182
|
+
definition: AgentDefinition;
|
|
183
|
+
yaml: string;
|
|
184
|
+
source: RevisionSource;
|
|
185
|
+
author?: string | null;
|
|
186
|
+
summary?: string | null;
|
|
187
|
+
restoredFrom?: number | null;
|
|
188
|
+
fileHash: string;
|
|
189
|
+
now: string;
|
|
190
|
+
}): number;
|
|
191
|
+
/** First sighting of a file that never parsed: no revision yet, only issues. */
|
|
192
|
+
recordInvalidNewDefinition(projectKey: string, agentId: string, fileHash: string, issues: AgentIssue[], now: string): void;
|
|
193
|
+
getRevision(projectKey: string, agentId: string, revision: number): AgentRevision | undefined;
|
|
194
|
+
listRevisions(projectKey: string, agentId: string): AgentRevisionMeta[];
|
|
195
|
+
insertSession(projectKey: string, session: AgentSession): void;
|
|
196
|
+
getSession(sessionId: string): (AgentSession & {
|
|
197
|
+
projectKey: string;
|
|
198
|
+
}) | undefined;
|
|
199
|
+
findSessionByThread(projectKey: string, agentId: string, threadKey: string): AgentSession | undefined;
|
|
200
|
+
updateSession(sessionId: string, patch: Partial<Pick<AgentSession, "status" | "backend" | "title">>, now: string): void;
|
|
201
|
+
/** One agent's sessions, or every agent's when `agentId` is omitted. */
|
|
202
|
+
listSessions(projectKey: string, agentId: string | undefined, query: SessionQuery): {
|
|
203
|
+
sessions: AgentSessionListItem[];
|
|
204
|
+
nextCursor: string | null;
|
|
205
|
+
};
|
|
206
|
+
/** One visitor's sessions on one agent, newest first (public web chat). */
|
|
207
|
+
listSessionsForRef(projectKey: string, agentId: string, source: string, sourceRef: string, limit: number): AgentSession[];
|
|
208
|
+
/** Stats for one agent, or for every agent when `agentId` is omitted. */
|
|
209
|
+
sessionStats(projectKey: string, agentId: string | undefined, since: string): SessionStats;
|
|
210
|
+
/** Ids of every session an agent has had, for releasing what a runtime holds for them. */
|
|
211
|
+
sessionIdsOf(projectKey: string, agentId: string): string[];
|
|
212
|
+
appendMessage(message: Omit<AgentMessage, "seq">): AgentMessage;
|
|
213
|
+
listMessages(sessionId: string): AgentMessage[];
|
|
214
|
+
insertTurn(turn: Omit<AgentTurn, "seq">): AgentTurn;
|
|
215
|
+
finishTurn(turnId: string, patch: Pick<AgentTurn, "status" | "errorCode" | "inputTokens" | "outputTokens" | "costUsd" | "endedAt">): void;
|
|
216
|
+
/** Change a finished turn's outcome after the fact, for example when its reply could not be delivered. */
|
|
217
|
+
setTurnOutcome(turnId: string, status: TurnStatus, errorCode: string | null): void;
|
|
218
|
+
insertDelivery(delivery: AgentDelivery): void;
|
|
219
|
+
getDelivery(turnId: string): AgentDelivery | undefined;
|
|
220
|
+
listDeliveries(sessionId: string): AgentDelivery[];
|
|
221
|
+
updateDelivery(turnId: string, patch: Partial<Pick<AgentDelivery, "state" | "attempts" | "errorCode" | "errorMessage" | "retryable" | "nextAttemptAt">>, now: string): void;
|
|
222
|
+
/**
|
|
223
|
+
* Atomically take a delivery for sending: one UPDATE that only matches while it is still in an allowed state (and,
|
|
224
|
+
* for a retry, still due), so two overlapping attempts cannot both win. Returns the claimed row or undefined.
|
|
225
|
+
*/
|
|
226
|
+
claimDelivery(turnId: string, states: string[], now: string, options?: {
|
|
227
|
+
dueRetry?: boolean;
|
|
228
|
+
}): AgentDelivery | undefined;
|
|
229
|
+
/** Failed deliveries a retry is due for. */
|
|
230
|
+
dueDeliveries(now: string): AgentDelivery[];
|
|
231
|
+
/** Sends cut off mid-flight by a restart: whether they arrived is unknown. Returns the turns affected. */
|
|
232
|
+
settleInterruptedDeliveries(now: string): string[];
|
|
233
|
+
/** The most recent delivery of an agent that did not get through, for the channel status line. */
|
|
234
|
+
latestUndelivered(agentId: string, projectKey: string): AgentDelivery | undefined;
|
|
235
|
+
setTurnStatus(turnId: string, status: TurnStatus): void;
|
|
236
|
+
getTurn(turnId: string): AgentTurn | undefined;
|
|
237
|
+
listTurns(sessionId: string): AgentTurn[];
|
|
238
|
+
/** Turns left `running` by a host that stopped mid-turn. */
|
|
239
|
+
/** Turns a restart is about to fail: they were mid-flight when the Host went down. */
|
|
240
|
+
interruptedTurnIds(): string[];
|
|
241
|
+
/** Give an unanswered user message to a new turn, so a retry does not repeat it in the transcript. */
|
|
242
|
+
moveMessageToTurn(messageId: string, turnId: string): void;
|
|
243
|
+
failInterruptedTurns(now: string): number;
|
|
244
|
+
insertSpan(span: AgentSpan): void;
|
|
245
|
+
listSpansForTurn(turnId: string): AgentSpan[];
|
|
246
|
+
listSpansForSession(sessionId: string): AgentSpan[];
|
|
247
|
+
recordEvalScore(score: AgentEvalScore): void;
|
|
248
|
+
listEvalScores(sessionId: string): AgentEvalScore[];
|
|
249
|
+
insertApproval(approval: AgentApproval): void;
|
|
250
|
+
getPendingApproval(sessionId: string): AgentApproval | undefined;
|
|
251
|
+
findApprovalByPayload(sessionId: string, payloadHash: string): AgentApproval | undefined;
|
|
252
|
+
listApprovals(sessionId: string): AgentApproval[];
|
|
253
|
+
listPendingApprovals(projectKey: string): Array<AgentApproval & {
|
|
254
|
+
agentId: string;
|
|
255
|
+
}>;
|
|
256
|
+
decideApproval(approvalId: string, patch: {
|
|
257
|
+
status: Exclude<ApprovalStatus, "pending">;
|
|
258
|
+
decidedAt: string;
|
|
259
|
+
actor: ApprovalActor | null;
|
|
260
|
+
}): void;
|
|
261
|
+
/** Expire every pending approval past its deadline; returns the affected approvals. */
|
|
262
|
+
expireApprovals(now: string): Array<{
|
|
263
|
+
approvalId: string;
|
|
264
|
+
sessionId: string;
|
|
265
|
+
}>;
|
|
266
|
+
/** Close (as expired) the pending approvals raised by a turn that is over; returns their ids. */
|
|
267
|
+
closePendingApprovalsOfTurn(turnId: string, now: string): string[];
|
|
268
|
+
getApproval(approvalId: string): AgentApproval | undefined;
|
|
269
|
+
markPayloadExecuted(sessionId: string, payloadHash: string, now: string): void;
|
|
270
|
+
hasExecutedPayload(sessionId: string, payloadHash: string): boolean;
|
|
271
|
+
/** True the first time a provider event is seen for this project. */
|
|
272
|
+
rememberInboundEvent(projectKey: string, dedupeKey: string, now: string): boolean;
|
|
273
|
+
getOutboundState(sessionId: string, operationKey: string): OutboundSendState | undefined;
|
|
274
|
+
setOutboundState(sessionId: string, operationKey: string, state: OutboundSendState, now: string): void;
|
|
275
|
+
getScheduleNextRun(projectKey: string, agentId: string): string | undefined;
|
|
276
|
+
setScheduleNextRun(projectKey: string, agentId: string, nextRunAt: string): void;
|
|
277
|
+
}
|