@wardby/cli 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +22 -3
- package/README.md +11 -9
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +1 -0
- package/dist/cli.js +68 -10
- package/dist/coding/base-commit.d.ts +6 -0
- package/dist/coding/base-commit.js +12 -0
- package/dist/coding/protocol.d.ts +17 -1
- package/dist/coding/protocol.js +17 -6
- package/dist/coding/provider.d.ts +6 -1
- package/dist/coding/provider.js +16 -9
- package/dist/config/providers.d.ts +25 -0
- package/dist/config/providers.js +71 -0
- package/dist/core/attribution.d.ts +101 -0
- package/dist/core/attribution.js +208 -0
- package/dist/core/budget-groups.d.ts +17 -10
- package/dist/core/budget-groups.js +15 -12
- package/dist/core/coding-queue.d.ts +3 -0
- package/dist/core/coding-queue.js +6 -2
- package/dist/core/coding-service-status.d.ts +11 -0
- package/dist/core/coding-service-status.js +17 -0
- package/dist/core/cost-report.d.ts +88 -0
- package/dist/core/cost-report.js +248 -0
- package/dist/core/dispatch.d.ts +42 -2
- package/dist/core/dispatch.js +144 -26
- package/dist/core/engine-native.js +17 -4
- package/dist/core/glob.d.ts +10 -0
- package/dist/core/glob.js +33 -0
- package/dist/core/host-events.d.ts +24 -1
- package/dist/core/host-events.js +341 -1
- package/dist/core/host-status.d.ts +19 -2
- package/dist/core/host-status.js +47 -30
- package/dist/core/issue-bridge.d.ts +60 -0
- package/dist/core/issue-bridge.js +189 -0
- package/dist/core/issue-dedupe.d.ts +70 -0
- package/dist/core/issue-dedupe.js +255 -0
- package/dist/core/issue-events.d.ts +42 -0
- package/dist/core/issue-events.js +155 -0
- package/dist/core/issue-status.d.ts +29 -0
- package/dist/core/issue-status.js +241 -0
- package/dist/core/issue-tracker-tools.d.ts +64 -0
- package/dist/core/issue-tracker-tools.js +850 -0
- package/dist/core/model-usage.d.ts +10 -0
- package/dist/core/model-usage.js +24 -0
- package/dist/core/reconciler.d.ts +8 -4
- package/dist/core/reconciler.js +15 -4
- package/dist/core/review-host-tools.js +10 -3
- package/dist/core/run-pricing.d.ts +61 -0
- package/dist/core/run-pricing.js +56 -0
- package/dist/core/runner.d.ts +5 -2
- package/dist/core/runner.js +178 -27
- package/dist/core/scheduler.d.ts +4 -1
- package/dist/core/scheduler.js +3 -2
- package/dist/core/self-defects.d.ts +80 -0
- package/dist/core/self-defects.js +180 -0
- package/dist/core/tool-names.js +3 -0
- package/dist/core/webhooks.d.ts +9 -1
- package/dist/core/webhooks.js +19 -1
- package/dist/env.js +6 -1
- package/dist/generated/prisma/browser.d.ts +66 -0
- package/dist/generated/prisma/client.d.ts +66 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +122 -52
- package/dist/generated/prisma/enums.d.ts +7 -0
- package/dist/generated/prisma/enums.js +6 -0
- package/dist/generated/prisma/internal/class.d.ts +99 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +826 -1
- package/dist/generated/prisma/internal/prismaNamespace.js +135 -2
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +142 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +135 -2
- package/dist/generated/prisma/models/Agent.d.ts +389 -1
- package/dist/generated/prisma/models/AgentIssueProject.d.ts +1838 -0
- package/dist/generated/prisma/models/AgentIssueProject.js +1 -0
- package/dist/generated/prisma/models/AgentRepository.d.ts +1 -1
- package/dist/generated/prisma/models/AuthUser.d.ts +1 -1
- package/dist/generated/prisma/models/CodingProxySession.d.ts +73 -1
- package/dist/generated/prisma/models/CodingRun.d.ts +130 -1
- package/dist/generated/prisma/models/CodingRunServiceStatus.d.ts +1404 -0
- package/dist/generated/prisma/models/CodingRunServiceStatus.js +1 -0
- package/dist/generated/prisma/models/IssueFingerprint.d.ts +1183 -0
- package/dist/generated/prisma/models/IssueFingerprint.js +1 -0
- package/dist/generated/prisma/models/IssuePullRequest.d.ts +1255 -0
- package/dist/generated/prisma/models/IssuePullRequest.js +1 -0
- package/dist/generated/prisma/models/ModelCatalogEntry.d.ts +1322 -0
- package/dist/generated/prisma/models/ModelCatalogEntry.js +1 -0
- package/dist/generated/prisma/models/Run.d.ts +933 -1
- package/dist/generated/prisma/models/RunAttribution.d.ts +1259 -0
- package/dist/generated/prisma/models/RunAttribution.js +1 -0
- package/dist/generated/prisma/models/RunIssueStatus.d.ts +1199 -0
- package/dist/generated/prisma/models/RunIssueStatus.js +1 -0
- package/dist/generated/prisma/models/RunModelUsage.d.ts +1316 -0
- package/dist/generated/prisma/models/RunModelUsage.js +1 -0
- package/dist/generated/prisma/models/WorkItem.d.ts +1408 -0
- package/dist/generated/prisma/models/WorkItem.js +1 -0
- package/dist/generated/prisma/models.d.ts +9 -0
- package/dist/help-index.json +355 -16
- package/dist/import/neutral-schema.d.ts +16 -16
- package/dist/knowledge/check.d.ts +13 -0
- package/dist/knowledge/check.js +69 -0
- package/dist/knowledge/cli.d.ts +14 -0
- package/dist/knowledge/cli.js +67 -0
- package/dist/knowledge/concept.d.ts +54 -0
- package/dist/knowledge/concept.js +78 -0
- package/dist/knowledge/note.d.ts +11 -0
- package/dist/knowledge/note.js +39 -0
- package/dist/knowledge/relevance.d.ts +11 -0
- package/dist/knowledge/relevance.js +14 -0
- package/dist/knowledge/span-hash.d.ts +3 -0
- package/dist/knowledge/span-hash.js +16 -0
- package/dist/mcp/auth/access.d.ts +4 -2
- package/dist/mcp/auth/ownership.d.ts +9 -9
- package/dist/mcp/auth/resource-server.d.ts +3 -1
- package/dist/mcp/auth/resource-server.js +18 -3
- package/dist/mcp/auth/self-hosted/credentials.d.ts +3 -3
- package/dist/mcp/auth/self-hosted/session.d.ts +5 -5
- package/dist/mcp/context.d.ts +3 -0
- package/dist/mcp/host-events/deliveries.d.ts +9 -0
- package/dist/mcp/host-events/deliveries.js +17 -0
- package/dist/mcp/host-events/github-ingress.d.ts +4 -2
- package/dist/mcp/host-events/github-ingress.js +4 -13
- package/dist/mcp/host-events/jira-ingress.d.ts +29 -0
- package/dist/mcp/host-events/jira-ingress.js +92 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +87 -9
- package/dist/mcp/server.js +5 -2
- package/dist/mcp/tools/agents.js +74 -3
- package/dist/mcp/tools/cost-report.d.ts +8 -0
- package/dist/mcp/tools/cost-report.js +60 -0
- package/dist/mcp/tools/issue-projects.d.ts +2 -0
- package/dist/mcp/tools/issue-projects.js +238 -0
- package/dist/mcp/tools/model-catalog.d.ts +22 -0
- package/dist/mcp/tools/model-catalog.js +423 -0
- package/dist/mcp/tools/repositories.js +2 -1
- package/dist/mcp/tools/tools.d.ts +2 -2
- package/dist/mcp/tools/trigger.js +33 -5
- package/dist/mcp/transport/streamable-http.d.ts +5 -0
- package/dist/mcp/transport/streamable-http.js +23 -1
- package/dist/mcp/webhooks/ingress.d.ts +2 -1
- package/dist/mcp/webhooks/ingress.js +9 -2
- package/dist/providers/auth/self-hosted.d.ts +8 -1
- package/dist/providers/auth/self-hosted.js +39 -2
- package/dist/providers/coding-proxy/memory-ledger.d.ts +1 -1
- package/dist/providers/coding-proxy/memory-ledger.js +10 -1
- package/dist/providers/coding-proxy/metering.d.ts +2 -1
- package/dist/providers/coding-proxy/metering.js +13 -2
- package/dist/providers/coding-proxy/prisma-ledger.js +59 -6
- package/dist/providers/coding-proxy/proxy.d.ts +12 -2
- package/dist/providers/coding-proxy/proxy.js +92 -30
- package/dist/providers/coding-proxy/types.d.ts +18 -1
- package/dist/providers/coding-proxy/types.js +12 -1
- package/dist/providers/engine/types.d.ts +19 -0
- package/dist/providers/executor/composition.js +9 -1
- package/dist/providers/executor/container.d.ts +30 -2
- package/dist/providers/executor/container.js +98 -17
- package/dist/providers/executor/dbos.d.ts +2 -0
- package/dist/providers/executor/dbos.js +7 -5
- package/dist/providers/executor/routing.d.ts +6 -0
- package/dist/providers/executor/routing.js +5 -0
- package/dist/providers/executor/types.d.ts +12 -0
- package/dist/providers/issue-tracker/adf.d.ts +31 -0
- package/dist/providers/issue-tracker/adf.js +181 -0
- package/dist/providers/issue-tracker/index.d.ts +5 -0
- package/dist/providers/issue-tracker/index.js +12 -0
- package/dist/providers/issue-tracker/jira-client.d.ts +41 -0
- package/dist/providers/issue-tracker/jira-client.js +151 -0
- package/dist/providers/issue-tracker/jira-events.d.ts +3 -0
- package/dist/providers/issue-tracker/jira-events.js +98 -0
- package/dist/providers/issue-tracker/jira.d.ts +116 -0
- package/dist/providers/issue-tracker/jira.js +502 -0
- package/dist/providers/issue-tracker/types.d.ts +269 -0
- package/dist/providers/issue-tracker/types.js +16 -0
- package/dist/providers/jobs/docker.d.ts +5 -1
- package/dist/providers/jobs/docker.js +61 -33
- package/dist/providers/jobs/kubernetes.d.ts +3 -0
- package/dist/providers/jobs/kubernetes.js +44 -4
- package/dist/providers/jobs/service-state.d.ts +22 -0
- package/dist/providers/jobs/service-state.js +17 -0
- package/dist/providers/llm/anthropic.d.ts +3 -3
- package/dist/providers/llm/anthropic.js +3 -9
- package/dist/providers/llm/bedrock.d.ts +3 -3
- package/dist/providers/llm/bedrock.js +3 -9
- package/dist/providers/llm/catalog-lookup.d.ts +10 -0
- package/dist/providers/llm/catalog-lookup.js +15 -0
- package/dist/providers/llm/catalog-shipped.d.ts +18 -0
- package/dist/providers/llm/catalog-shipped.js +197 -0
- package/dist/providers/llm/catalog-store.d.ts +58 -0
- package/dist/providers/llm/catalog-store.js +138 -0
- package/dist/providers/llm/catalog-types.d.ts +66 -0
- package/dist/providers/llm/catalog-types.js +64 -0
- package/dist/providers/llm/catalog.d.ts +61 -0
- package/dist/providers/llm/catalog.js +147 -0
- package/dist/providers/llm/claude-provider.d.ts +10 -14
- package/dist/providers/llm/claude-provider.js +11 -6
- package/dist/providers/llm/index.d.ts +9 -6
- package/dist/providers/llm/index.js +8 -5
- package/dist/providers/llm/openai.d.ts +14 -5
- package/dist/providers/llm/openai.js +24 -14
- package/dist/providers/llm/pricing-core.d.ts +5 -3
- package/dist/providers/llm/registration.js +8 -12
- package/dist/providers/llm/routing.d.ts +18 -17
- package/dist/providers/llm/routing.js +40 -24
- package/dist/providers/review-host/github-events.js +47 -1
- package/dist/providers/review-host/github.js +7 -6
- package/dist/providers/review-host/types.d.ts +25 -0
- package/dist/providers/vcs/git.js +2 -22
- package/dist/providers/vcs/github.d.ts +20 -0
- package/dist/providers/vcs/github.js +28 -2
- package/dist/providers/vcs/types.d.ts +6 -0
- package/dist/quickstart/index.d.ts +8 -0
- package/dist/quickstart/index.js +34 -34
- package/dist/serve.js +8 -2
- package/dist/viewer/api-schema.d.ts +2757 -0
- package/dist/viewer/api-schema.js +165 -0
- package/dist/viewer/build-schemas.d.ts +2 -0
- package/dist/viewer/build-schemas.js +18 -0
- package/dist/viewer/event-bus.d.ts +38 -0
- package/dist/viewer/event-bus.js +232 -0
- package/dist/viewer/graph.d.ts +40 -0
- package/dist/viewer/graph.js +243 -0
- package/dist/viewer/http.d.ts +30 -0
- package/dist/viewer/http.js +133 -0
- package/dist/viewer/run-detail.d.ts +4 -0
- package/dist/viewer/run-detail.js +61 -0
- package/dist/wardby-bin.js +5 -0
- package/docs/README.md +10 -0
- package/docs/agent-recipes.md +383 -0
- package/docs/code-review-agents.md +29 -2
- package/docs/coding-agent-setup.md +3 -0
- package/docs/coding-worker-isolation.md +39 -5
- package/docs/getting-started-gke.md +28 -11
- package/docs/getting-started-identity-provider.md +49 -38
- package/docs/getting-started.md +14 -0
- package/docs/jira-agents.md +649 -0
- package/docs/knowledge.md +387 -0
- package/docs/models.md +221 -0
- package/docs/security-deployment.md +19 -9
- package/docs/viewer-api.md +142 -0
- package/help/admin-viewer.md +39 -0
- package/help/agent-recipes.md +173 -0
- package/help/architecture-agent.md +189 -0
- package/help/builder-agent.md +80 -0
- package/help/code-review-agents.md +6 -0
- package/help/cost-attribution.md +67 -0
- package/help/creating-agents.md +22 -0
- package/help/deploy-gke.md +6 -0
- package/help/errors/model-unavailable.md +63 -0
- package/help/getting-started.md +1 -0
- package/help/github.md +18 -0
- package/help/identity-and-access.md +8 -3
- package/help/jira.md +135 -0
- package/help/knowledge.md +47 -0
- package/help/models.md +90 -0
- package/help/operating-agents.md +7 -1
- package/help/troubleshooting/budgets.md +6 -0
- package/package.json +5 -2
- package/prisma/migrations/20260930000000_jira_issue_projects/migration.sql +34 -0
- package/prisma/migrations/20261001000000_jira_phase2_allowlists/migration.sql +3 -0
- package/prisma/migrations/20261001010000_jira_link_types_allowlist/migration.sql +2 -0
- package/prisma/migrations/20261002000000_jira_coding_bridge/migration.sql +28 -0
- package/prisma/migrations/20261002010000_jira_issue_creation/migration.sql +25 -0
- package/prisma/migrations/20261003000000_issue_cost_attribution/migration.sql +56 -0
- package/prisma/migrations/20261003010000_coding_run_service_status/migration.sql +23 -0
- package/prisma/migrations/20261003020000_viewer_notify/migration.sql +54 -0
- package/prisma/migrations/20261003030000_viewer_notify_fixes/migration.sql +47 -0
- package/prisma/migrations/20261003040000_viewer_indexes/migration.sql +12 -0
- package/prisma/migrations/20261004000000_model_catalog/migration.sql +26 -0
- package/prisma/schema.prisma +258 -2
- package/dist/mcp/tools/models.d.ts +0 -8
- package/dist/mcp/tools/models.js +0 -15
- package/dist/providers/llm/pricing-anthropic.d.ts +0 -14
- package/dist/providers/llm/pricing-anthropic.js +0 -48
- package/dist/providers/llm/pricing-bedrock-claude.d.ts +0 -20
- package/dist/providers/llm/pricing-bedrock-claude.js +0 -46
- package/dist/providers/llm/pricing.d.ts +0 -30
- package/dist/providers/llm/pricing.js +0 -74
|
@@ -50,6 +50,19 @@ export interface LoadedTool {
|
|
|
50
50
|
export type StepRunner = <T>(name: string, fn: () => Promise<T>) => Promise<T>;
|
|
51
51
|
/** The no-checkpoint default: run the step body directly. */
|
|
52
52
|
export declare const runStepInline: StepRunner;
|
|
53
|
+
/**
|
|
54
|
+
* Live progress after a completed model turn: absolute cumulative totals,
|
|
55
|
+
* never deltas, so re-delivering one (a DBOS replay re-runs the loop over
|
|
56
|
+
* recorded steps) rewrites the same values instead of double counting.
|
|
57
|
+
*/
|
|
58
|
+
export interface EngineProgress {
|
|
59
|
+
turns: number;
|
|
60
|
+
usage: {
|
|
61
|
+
tokensIn: number;
|
|
62
|
+
tokensOut: number;
|
|
63
|
+
costUsd: number;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
53
66
|
export interface EngineRunContext {
|
|
54
67
|
agent: EngineAgent;
|
|
55
68
|
tools: LoadedTool[];
|
|
@@ -57,6 +70,8 @@ export interface EngineRunContext {
|
|
|
57
70
|
/** Host bridge: validates params (Zod-in-sandbox) then runs the tool body in the WASM sandbox. Never throws — a tool/validation failure is a JSON error result string, fed back to the model as the tool's result. */
|
|
58
71
|
runSandboxTool(name: string, argsJson: string): Promise<string>;
|
|
59
72
|
onText?: (delta: string) => void;
|
|
73
|
+
/** Called after each model turn that completed without error; the engine awaits it. */
|
|
74
|
+
onProgress?: (progress: EngineProgress) => Promise<void>;
|
|
60
75
|
/** See StepRunner. Optional; the in-process executor leaves it unset. */
|
|
61
76
|
step?: StepRunner;
|
|
62
77
|
}
|
|
@@ -69,6 +84,10 @@ export interface EngineResult {
|
|
|
69
84
|
tokensIn: number;
|
|
70
85
|
tokensOut: number;
|
|
71
86
|
costUsd: number;
|
|
87
|
+
/** Subset of tokensIn served from a prompt cache. */
|
|
88
|
+
cachedInputTokens?: number;
|
|
89
|
+
/** Tokens billed for writing prompt-cache entries (not part of tokensIn). */
|
|
90
|
+
cacheWriteTokens?: number;
|
|
72
91
|
};
|
|
73
92
|
error?: string;
|
|
74
93
|
}
|
|
@@ -3,6 +3,7 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { loadCodingConcurrencyConfig, loadContainerExecutorConfig, loadGitHubVcsConfig, loadKubernetesJobConfig, loadProviderConfig, } from "../../config/providers.js";
|
|
4
4
|
import { summarizeRegistryFetches } from "../../coding/registry/report.js";
|
|
5
5
|
import { drainCodingQueue } from "../../core/coding-queue.js";
|
|
6
|
+
import { prismaServiceStateReporter } from "../../core/coding-service-status.js";
|
|
6
7
|
import { logger } from "../../core/logger.js";
|
|
7
8
|
import { EnvironmentCredentialResolver } from "../coding-proxy/environment-credentials.js";
|
|
8
9
|
import { CodingProxy } from "../coding-proxy/proxy.js";
|
|
@@ -17,6 +18,7 @@ import { buildVcsProvider } from "../vcs/index.js";
|
|
|
17
18
|
import { ContainerExecutor, PrismaContainerExecutionStore, RunCapabilityVault } from "./container.js";
|
|
18
19
|
import { PrismaExecutionKindResolver, RoutingExecutor } from "./routing.js";
|
|
19
20
|
import { createRepoAccessGate } from "../../core/repo-access.js";
|
|
21
|
+
import { buildIssueTrackers } from "../issue-tracker/index.js";
|
|
20
22
|
import { buildReviewHosts } from "../review-host/index.js";
|
|
21
23
|
const compositionLog = logger.child({ module: "executor-composition" });
|
|
22
24
|
/** Builds container execution only when Docker or Kubernetes is explicitly selected. */
|
|
@@ -41,6 +43,7 @@ export function buildConfiguredExecutor(options) {
|
|
|
41
43
|
ledger: new PrismaProxyLedger(options.db),
|
|
42
44
|
credentials: new EnvironmentCredentialResolver(env),
|
|
43
45
|
});
|
|
46
|
+
const onServiceState = prismaServiceStateReporter(options.db);
|
|
44
47
|
let jobs;
|
|
45
48
|
if (providerConfig.jobs === "kubernetes") {
|
|
46
49
|
if (!isRegistryDigest(config.workerImage)) {
|
|
@@ -65,6 +68,7 @@ export function buildConfiguredExecutor(options) {
|
|
|
65
68
|
const api = options.kubernetesApi ?? new ClientNodeKubernetesApi({ context: kubernetes.context });
|
|
66
69
|
const workerImage = config.workerImage;
|
|
67
70
|
jobs = new KubernetesJobLauncher({
|
|
71
|
+
onServiceState,
|
|
68
72
|
api,
|
|
69
73
|
config: kubernetes,
|
|
70
74
|
workspaceRoot,
|
|
@@ -87,6 +91,7 @@ export function buildConfiguredExecutor(options) {
|
|
|
87
91
|
else {
|
|
88
92
|
const stateRoot = resolve(config.stateRoot ?? resolve(tmpdir(), "wardby-docker-jobs"));
|
|
89
93
|
jobs = new DockerJobLauncher({
|
|
94
|
+
onServiceState,
|
|
90
95
|
stateRoot,
|
|
91
96
|
workspaceRoot,
|
|
92
97
|
proxyContainer: proxyContainer,
|
|
@@ -100,12 +105,13 @@ export function buildConfiguredExecutor(options) {
|
|
|
100
105
|
},
|
|
101
106
|
});
|
|
102
107
|
}
|
|
108
|
+
const issueTrackers = buildIssueTrackers(env);
|
|
103
109
|
const concurrency = loadCodingConcurrencyConfig(env);
|
|
104
110
|
// The release hook needs the composed RoutingExecutor, which only exists
|
|
105
111
|
// after the ContainerExecutor it wraps is built. The closure reads
|
|
106
112
|
// `composed` only when a run finishes, which is after this function returns.
|
|
107
113
|
const coding = new ContainerExecutor({
|
|
108
|
-
store: new PrismaContainerExecutionStore(options.db, { maxConcurrent: concurrency.maxConcurrent }),
|
|
114
|
+
store: new PrismaContainerExecutionStore(options.db, { maxConcurrent: concurrency.maxConcurrent, issueTrackers }),
|
|
109
115
|
jobs,
|
|
110
116
|
vcs,
|
|
111
117
|
sessions,
|
|
@@ -121,6 +127,7 @@ export function buildConfiguredExecutor(options) {
|
|
|
121
127
|
limits: { cpus: config.cpus, memoryMb: config.memoryMb, pids: config.pids, diskMb: config.diskMb },
|
|
122
128
|
maxDiskMb: config.maxDiskMb,
|
|
123
129
|
repoAccess: options.repoAccess ?? createRepoAccessGate({ db: options.db, hosts: buildReviewHosts(env) }),
|
|
130
|
+
issueUrl: (provider, key) => (provider === "jira" ? issueTrackers.jira?.issueUrl(key) : undefined),
|
|
124
131
|
registryReport: async (runId) => {
|
|
125
132
|
const rows = await options.db.registryFetch.findMany({ where: { runId }, orderBy: { createdAt: "asc" } });
|
|
126
133
|
return summarizeRegistryFetches(rows);
|
|
@@ -131,6 +138,7 @@ export function buildConfiguredExecutor(options) {
|
|
|
131
138
|
executor: composed,
|
|
132
139
|
maxConcurrent: concurrency.maxConcurrent,
|
|
133
140
|
queueTimeoutSec: concurrency.queueTimeoutSec,
|
|
141
|
+
selfDefects: { db: options.db, issueTrackers },
|
|
134
142
|
}).catch((err) => {
|
|
135
143
|
// Never throws: the next scheduler tick drains again.
|
|
136
144
|
compositionLog.warn({ err }, "coding queue drain after a released slot failed");
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { PrismaClient } from "#prisma";
|
|
2
|
-
import type
|
|
2
|
+
import { type CodingProvider } from "../../coding/provider.js";
|
|
3
|
+
import type { IssueTrackerRegistry } from "../issue-tracker/types.js";
|
|
3
4
|
import { type RepoAccessGate } from "../../core/repo-access.js";
|
|
4
5
|
import { type CodingRunObserver } from "../../coding/observability.js";
|
|
5
6
|
import { type CodingRunResult } from "../../coding/protocol.js";
|
|
6
|
-
import type { ProxyProtocol } from "../coding-proxy/types.js";
|
|
7
|
+
import type { ProxyModelTerms, ProxyProtocol } from "../coding-proxy/types.js";
|
|
7
8
|
import type { JobHandle, JobResourceLimits, WorkspaceJobLauncher } from "../jobs/types.js";
|
|
8
9
|
import type { VcsProvider } from "../vcs/types.js";
|
|
9
10
|
import type { CodingImageSelector, ExecutionRecoveryResult, Executor, PersistedExecutionHandle } from "./types.js";
|
|
@@ -32,6 +33,9 @@ export interface ContainerRunSnapshot {
|
|
|
32
33
|
collectExclude: unknown;
|
|
33
34
|
/** Revision-in-place: set when this run continues another run's branch/PR. See preflight(). */
|
|
34
35
|
rootCodingRunId: string | null;
|
|
36
|
+
/** The issue that triggered this run (CodingRun.issueProvider/issueKey); null when none. */
|
|
37
|
+
issueProvider?: string | null;
|
|
38
|
+
issueKey?: string | null;
|
|
35
39
|
budgetUsd: number;
|
|
36
40
|
/** The agent's own per-run budget; `budgetUsd` is less when its budget group had less left. */
|
|
37
41
|
agentBudgetUsd?: number;
|
|
@@ -57,6 +61,9 @@ export interface ContainerRunSnapshot {
|
|
|
57
61
|
profileRepository: string | null;
|
|
58
62
|
/** How the profile's repository was authorized (CodingAgentProfile.repositoryAuthorizedVia). */
|
|
59
63
|
repositoryAuthorizedVia: string | null;
|
|
64
|
+
/** The run's catalog entry recorded at dispatch (Run.pricingSnapshot); null on runs from before the catalog. */
|
|
65
|
+
pricingVersion: string | null;
|
|
66
|
+
pricingSnapshot: unknown;
|
|
60
67
|
}
|
|
61
68
|
/**
|
|
62
69
|
* claimed: this caller owns provisioning. unavailable: someone else does, or
|
|
@@ -82,7 +89,13 @@ export declare class PrismaContainerExecutionStore implements ContainerExecution
|
|
|
82
89
|
private readonly options;
|
|
83
90
|
constructor(db: PrismaClient, options?: {
|
|
84
91
|
maxConcurrent?: number;
|
|
92
|
+
/** Configured issue trackers: a coding run this store ends failed/lost/budget_exhausted files a self-defect. */
|
|
93
|
+
issueTrackers?: IssueTrackerRegistry;
|
|
85
94
|
});
|
|
95
|
+
/** Best effort (bounded, never throws): only called by the write that made the row terminal, so it files once. */
|
|
96
|
+
private selfDefect;
|
|
97
|
+
/** An empty registry (no Jira site configured) means no self-defect queries at all. */
|
|
98
|
+
private get hasTrackers();
|
|
86
99
|
load(runId: string): Promise<ContainerRunSnapshot | null>;
|
|
87
100
|
claimProvisioning(runId: string, claimId: string): Promise<ProvisioningClaim>;
|
|
88
101
|
persistHandle(runId: string, claimId: string, handle: JobHandle): Promise<void>;
|
|
@@ -98,6 +111,8 @@ export interface CodingSessionController {
|
|
|
98
111
|
allowedModels: string[];
|
|
99
112
|
deadlineAt: Date;
|
|
100
113
|
budgetUsd: number;
|
|
114
|
+
/** The run's catalog entry; the proxy prices and shapes the run's requests from it. */
|
|
115
|
+
terms?: ProxyModelTerms;
|
|
101
116
|
}): Promise<{
|
|
102
117
|
id: string;
|
|
103
118
|
capability: string;
|
|
@@ -159,6 +174,8 @@ export interface ContainerExecutorOptions {
|
|
|
159
174
|
* reporting failure must never fail the run itself.
|
|
160
175
|
*/
|
|
161
176
|
registryReport?: (runId: string) => Promise<RegistryReport>;
|
|
177
|
+
/** Browse URL for an issue of a tracker provider (composition wires the issue-tracker registry); undefined when unconfigured. */
|
|
178
|
+
issueUrl?: (provider: string, key: string) => string | undefined;
|
|
162
179
|
/**
|
|
163
180
|
* Repository authorization: every run is checked, before its workspace is
|
|
164
181
|
* prepared, against the agent owner's current GitHub access (or a recorded
|
|
@@ -224,6 +241,10 @@ export declare class ContainerExecutor implements Executor {
|
|
|
224
241
|
* access. Throws RepoAccessError (refused before anything is cloned).
|
|
225
242
|
*/
|
|
226
243
|
private authorizeRepository;
|
|
244
|
+
/** The originating issue for the PR title/body: control-plane data from the CodingRun row, key re-validated. */
|
|
245
|
+
private issueFor;
|
|
246
|
+
/** The run's stored model terms; a run from before the catalog resolves them from the current catalog. */
|
|
247
|
+
private modelTerms;
|
|
227
248
|
private preflight;
|
|
228
249
|
private finishTerminal;
|
|
229
250
|
/** The package report is informational: any failure loading it (a
|
|
@@ -241,6 +262,13 @@ export declare class ContainerExecutor implements Executor {
|
|
|
241
262
|
repository: string;
|
|
242
263
|
baseRef: string;
|
|
243
264
|
}): Promise<string | null>;
|
|
265
|
+
/** One repository file at the run's base ref (Executor.readCodingRepositoryFile). */
|
|
266
|
+
readCodingRepositoryFile(input: {
|
|
267
|
+
repository: string;
|
|
268
|
+
baseRef: string;
|
|
269
|
+
path: string;
|
|
270
|
+
maxBytes: number;
|
|
271
|
+
}): Promise<string | null>;
|
|
244
272
|
/** The job launcher decides (Kubernetes and Docker start services for both providers). */
|
|
245
273
|
supportsCodingServices(provider: CodingProvider): boolean;
|
|
246
274
|
private requireCurrent;
|
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { mkdir, realpath, rename, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { join, resolve, sep } from "node:path";
|
|
4
|
+
import { withBaseCommit } from "../../coding/base-commit.js";
|
|
4
5
|
import { normalizeCollectExclusions } from "../../coding/collect-exclude.js";
|
|
6
|
+
import { codingProviderForModelProvider } from "../../coding/provider.js";
|
|
5
7
|
import { CodingProfileSchema } from "../../coding/profile.js";
|
|
6
8
|
import { parseStoredServices, storedServiceNames, workerServices } from "../../coding/services/catalog.js";
|
|
7
9
|
import { MAX_SERVICE_DECLARATION_BYTES, SERVICE_DECLARATION_PATH } from "../../coding/services/declaration.js";
|
|
8
10
|
import { SERVICE_UNREADY_CATEGORY, SERVICE_UNREADY_ERROR, serviceUnreadyName, serviceUnreadySentence, } from "../../coding/services/wording.js";
|
|
9
11
|
import { PROTECTED_PATH_CATEGORY, protectedPathFromError, protectedPathSentence, } from "../../coding/protected-path-wording.js";
|
|
10
12
|
import { budgetSentence } from "../../core/budget-wording.js";
|
|
13
|
+
import { fileSelfDefect } from "../../core/self-defects.js";
|
|
11
14
|
import { classifyProviderFailure, providerClassOfCategory, providerSentence, } from "../../core/provider-wording.js";
|
|
12
15
|
import { logger } from "../../core/logger.js";
|
|
13
16
|
import { requiredLevel } from "../../core/repo-access.js";
|
|
14
17
|
import { ensurePrivateDirectory } from "../../core/private-directory.js";
|
|
15
18
|
import { codingRunObserver } from "../../coding/observability.js";
|
|
16
19
|
import { CODING_PROTOCOL_VERSION, CodingRunResultSchema, CodingTaskInputSchema, parseCodingAgentOutputJson, redactAndTruncate, } from "../../coding/protocol.js";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
20
|
+
import { parseStoredEntry } from "../llm/catalog-types.js";
|
|
21
|
+
import { resolveCodingEntry } from "../../core/run-pricing.js";
|
|
19
22
|
import { CODING_PROXY_ALIAS, CODING_PROXY_PORT, isImmutableDockerImage } from "../jobs/docker-isolation.js";
|
|
20
23
|
import { normalizeRegistryLockfiles } from "../../coding/registry/lockfiles.js";
|
|
24
|
+
import { ISSUE_KEY, ISSUE_TRACKER_NAMES } from "../issue-tracker/types.js";
|
|
21
25
|
import { HEARTBEAT_TIMEOUT_MS } from "../../core/timing.js";
|
|
22
26
|
const containerLog = logger.child({ module: "container-executor" });
|
|
23
27
|
const TERMINAL_STATUSES = new Set(["succeeded", "failed", "refused", "lost", "budget_exhausted", "cancelled"]);
|
|
@@ -62,6 +66,15 @@ export class PrismaContainerExecutionStore {
|
|
|
62
66
|
this.db = db;
|
|
63
67
|
this.options = options;
|
|
64
68
|
}
|
|
69
|
+
/** Best effort (bounded, never throws): only called by the write that made the row terminal, so it files once. */
|
|
70
|
+
async selfDefect(run) {
|
|
71
|
+
if (this.hasTrackers)
|
|
72
|
+
await fileSelfDefect(this.db, this.options.issueTrackers, run);
|
|
73
|
+
}
|
|
74
|
+
/** An empty registry (no Jira site configured) means no self-defect queries at all. */
|
|
75
|
+
get hasTrackers() {
|
|
76
|
+
return Object.values(this.options.issueTrackers ?? {}).some(Boolean);
|
|
77
|
+
}
|
|
65
78
|
async load(runId) {
|
|
66
79
|
const row = await this.db.run.findUnique({
|
|
67
80
|
where: { id: runId },
|
|
@@ -88,6 +101,8 @@ export class PrismaContainerExecutionStore {
|
|
|
88
101
|
protectedPaths: row.codingRun.protectedPaths,
|
|
89
102
|
collectExclude: row.codingRun.collectExclude,
|
|
90
103
|
rootCodingRunId: row.codingRun.rootCodingRunId,
|
|
104
|
+
issueProvider: row.codingRun.issueProvider,
|
|
105
|
+
issueKey: row.codingRun.issueKey,
|
|
91
106
|
budgetUsd: Number(row.codingRun.budgetReservedUsd),
|
|
92
107
|
agentBudgetUsd: Number(row.agent.budgetUsd),
|
|
93
108
|
budgetGroupName: row.agent.budgetGroup?.name ?? null,
|
|
@@ -108,6 +123,8 @@ export class PrismaContainerExecutionStore {
|
|
|
108
123
|
failureCategory: row.codingRun.failureCategory,
|
|
109
124
|
profileRepository: row.agent.codingProfile?.repository ?? null,
|
|
110
125
|
repositoryAuthorizedVia: row.agent.codingProfile?.repositoryAuthorizedVia ?? null,
|
|
126
|
+
pricingVersion: row.pricingVersion,
|
|
127
|
+
pricingSnapshot: row.pricingSnapshot,
|
|
111
128
|
};
|
|
112
129
|
}
|
|
113
130
|
async claimProvisioning(runId, claimId) {
|
|
@@ -220,13 +237,14 @@ export class PrismaContainerExecutionStore {
|
|
|
220
237
|
});
|
|
221
238
|
}
|
|
222
239
|
async complete(runId, status, result) {
|
|
223
|
-
|
|
240
|
+
const finishedAt = new Date();
|
|
241
|
+
const finished = await this.db.$transaction(async (tx) => {
|
|
224
242
|
const run = await tx.run.findUnique({ where: { id: runId }, include: { codingRun: true } });
|
|
225
243
|
if (!run?.codingRun)
|
|
226
244
|
throw new Error("coding_run_not_found");
|
|
227
245
|
if (TERMINAL_STATUSES.has(run.status)) {
|
|
228
246
|
if (run.codingRun.result && stableJson(run.codingRun.result) === stableJson(result))
|
|
229
|
-
return;
|
|
247
|
+
return null;
|
|
230
248
|
throw new Error("coding_run_terminal_conflict");
|
|
231
249
|
}
|
|
232
250
|
await tx.codingRun.update({
|
|
@@ -238,26 +256,40 @@ export class PrismaContainerExecutionStore {
|
|
|
238
256
|
data: {
|
|
239
257
|
status,
|
|
240
258
|
finalText: result.summary,
|
|
241
|
-
finishedAt
|
|
259
|
+
finishedAt,
|
|
242
260
|
heartbeatAt: new Date(),
|
|
243
261
|
error: null,
|
|
244
262
|
},
|
|
245
263
|
});
|
|
264
|
+
return { id: runId, agentId: run.agentId };
|
|
246
265
|
});
|
|
266
|
+
if (finished && status === "budget_exhausted") {
|
|
267
|
+
await this.selfDefect({ ...finished, status, error: null, finishedAt });
|
|
268
|
+
}
|
|
247
269
|
}
|
|
248
270
|
async terminate(runId, status, error, audit) {
|
|
249
|
-
|
|
271
|
+
const finishedAt = new Date();
|
|
272
|
+
const agentId = await this.db.$transaction(async (tx) => {
|
|
250
273
|
const updated = await tx.run.updateMany({
|
|
251
274
|
where: { id: runId, status: { in: ["pending", "running"] } },
|
|
252
|
-
data: { status, error, finishedAt
|
|
275
|
+
data: { status, error, finishedAt, heartbeatAt: new Date() },
|
|
253
276
|
});
|
|
254
|
-
if (updated.count
|
|
277
|
+
if (!updated.count)
|
|
278
|
+
return null;
|
|
279
|
+
if (audit) {
|
|
255
280
|
await tx.codingRun.update({
|
|
256
281
|
where: { runId },
|
|
257
282
|
data: { failureCategory: audit.failureCategory, diagnosticId: audit.diagnosticId },
|
|
258
283
|
});
|
|
259
284
|
}
|
|
285
|
+
if (!this.hasTrackers)
|
|
286
|
+
return null;
|
|
287
|
+
const row = await tx.run.findUnique({ where: { id: runId }, select: { agentId: true } });
|
|
288
|
+
return row?.agentId ?? null;
|
|
260
289
|
});
|
|
290
|
+
// After commit, so the failure category is readable; cancelled/refused are skipped inside fileSelfDefect.
|
|
291
|
+
if (agentId)
|
|
292
|
+
await this.selfDefect({ id: runId, agentId, status, error, finishedAt });
|
|
261
293
|
}
|
|
262
294
|
}
|
|
263
295
|
/** Plain capabilities live only for the short provisioning window. */
|
|
@@ -470,11 +502,12 @@ export class ContainerExecutor {
|
|
|
470
502
|
allowedModels: [run.model],
|
|
471
503
|
deadlineAt,
|
|
472
504
|
budgetUsd: run.budgetUsd,
|
|
505
|
+
terms: this.modelTerms(run),
|
|
473
506
|
});
|
|
474
507
|
spendEnabled = true;
|
|
475
508
|
sessionId = session.id;
|
|
476
509
|
this.options.capabilities.set(runId, session.capability);
|
|
477
|
-
const inputArtifact = await this.writeInput(run, deadlineAt);
|
|
510
|
+
const inputArtifact = await this.writeInput(run, deadlineAt, workspace.baseCommit);
|
|
478
511
|
const beforeLaunch = await this.requireCurrent(runId);
|
|
479
512
|
if (TERMINAL_STATUSES.has(beforeLaunch.status))
|
|
480
513
|
throw new Error("coding_run_no_longer_active");
|
|
@@ -609,18 +642,50 @@ export class ContainerExecutor {
|
|
|
609
642
|
if (!decision.ok)
|
|
610
643
|
throw new RepoAccessError(`repo_access_${decision.reason}`);
|
|
611
644
|
}
|
|
645
|
+
/** The originating issue for the PR title/body: control-plane data from the CodingRun row, key re-validated. */
|
|
646
|
+
issueFor(run) {
|
|
647
|
+
const { issueProvider, issueKey } = run;
|
|
648
|
+
if (!issueProvider || !issueKey || !ISSUE_KEY.test(issueKey))
|
|
649
|
+
return {};
|
|
650
|
+
let url;
|
|
651
|
+
try {
|
|
652
|
+
url = this.options.issueUrl?.(issueProvider, issueKey);
|
|
653
|
+
}
|
|
654
|
+
catch {
|
|
655
|
+
url = undefined;
|
|
656
|
+
}
|
|
657
|
+
const trackerName = Object.hasOwn(ISSUE_TRACKER_NAMES, issueProvider)
|
|
658
|
+
? ISSUE_TRACKER_NAMES[issueProvider]
|
|
659
|
+
: undefined;
|
|
660
|
+
return {
|
|
661
|
+
issue: {
|
|
662
|
+
key: issueKey,
|
|
663
|
+
...(url?.startsWith("https://") ? { url } : {}),
|
|
664
|
+
...(trackerName ? { trackerName } : {}),
|
|
665
|
+
},
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
/** The run's stored model terms; a run from before the catalog resolves them from the current catalog. */
|
|
669
|
+
modelTerms(run) {
|
|
670
|
+
if (run.provider !== "codex" && run.provider !== "claude-code")
|
|
671
|
+
throw new Error("coding_provider_unsupported");
|
|
672
|
+
const stored = parseStoredEntry(run.pricingSnapshot);
|
|
673
|
+
if (stored && run.pricingVersion) {
|
|
674
|
+
if (stored.modelId !== run.model || codingProviderForModelProvider(stored.provider) !== run.provider) {
|
|
675
|
+
throw new Error("coding_run_pricing_mismatch");
|
|
676
|
+
}
|
|
677
|
+
return { version: run.pricingVersion, entry: stored };
|
|
678
|
+
}
|
|
679
|
+
const entry = resolveCodingEntry(run.provider, run.model);
|
|
680
|
+
return { version: entry.priceVersion, entry: parseStoredEntry(entry) };
|
|
681
|
+
}
|
|
612
682
|
preflight(run) {
|
|
613
683
|
try {
|
|
614
684
|
if (run.status !== "pending" && run.status !== "running")
|
|
615
685
|
throw new Error("coding_run_status_invalid");
|
|
616
686
|
if (run.agentKind !== "coding" || !run.ownerId)
|
|
617
687
|
throw new Error("coding_run_ownership_invalid");
|
|
618
|
-
|
|
619
|
-
getModelPricing(run.model);
|
|
620
|
-
else if (run.provider === "claude-code")
|
|
621
|
-
getAnthropicPricing(run.model);
|
|
622
|
-
else
|
|
623
|
-
throw new Error("coding_provider_unsupported");
|
|
688
|
+
this.modelTerms(run);
|
|
624
689
|
if (!Number.isFinite(run.budgetUsd) || run.budgetUsd <= 0 || run.costUsd > run.budgetUsd) {
|
|
625
690
|
throw new Error("coding_run_budget_invalid");
|
|
626
691
|
}
|
|
@@ -744,6 +809,7 @@ export class ContainerExecutor {
|
|
|
744
809
|
tests: output.tests,
|
|
745
810
|
tag: output.tag,
|
|
746
811
|
...report,
|
|
812
|
+
...this.issueFor(current),
|
|
747
813
|
});
|
|
748
814
|
const result = this.resultFor(output, current, finalized.outcome, finalized);
|
|
749
815
|
await this.options.store.complete(run.runId, "succeeded", result);
|
|
@@ -884,6 +950,17 @@ export class ContainerExecutor {
|
|
|
884
950
|
maxBytes: MAX_SERVICE_DECLARATION_BYTES,
|
|
885
951
|
});
|
|
886
952
|
}
|
|
953
|
+
/** One repository file at the run's base ref (Executor.readCodingRepositoryFile). */
|
|
954
|
+
async readCodingRepositoryFile(input) {
|
|
955
|
+
if (!this.options.vcs.readRepositoryFile)
|
|
956
|
+
return null;
|
|
957
|
+
return this.options.vcs.readRepositoryFile({
|
|
958
|
+
repository: input.repository,
|
|
959
|
+
ref: input.baseRef,
|
|
960
|
+
path: input.path,
|
|
961
|
+
maxBytes: input.maxBytes,
|
|
962
|
+
});
|
|
963
|
+
}
|
|
887
964
|
/** The job launcher decides (Kubernetes and Docker start services for both providers). */
|
|
888
965
|
supportsCodingServices(provider) {
|
|
889
966
|
return this.options.jobs.supportsServicesFor?.(provider) === true;
|
|
@@ -929,7 +1006,7 @@ export class ContainerExecutor {
|
|
|
929
1006
|
return this.options.anthropicCredentialRef;
|
|
930
1007
|
throw new Error("coding_provider_not_configured:claude-code");
|
|
931
1008
|
}
|
|
932
|
-
async writeInput(run, deadlineAt) {
|
|
1009
|
+
async writeInput(run, deadlineAt, baseCommit) {
|
|
933
1010
|
await ensurePrivateDirectory(this.artifactRoot);
|
|
934
1011
|
const rootReal = await realpath(this.artifactRoot);
|
|
935
1012
|
const directory = resolve(rootReal, run.runId);
|
|
@@ -941,13 +1018,17 @@ export class ContainerExecutor {
|
|
|
941
1018
|
const destination = join(directory, "input.json");
|
|
942
1019
|
const temporary = join(directory, `.input-${randomUUID()}.tmp`);
|
|
943
1020
|
const services = parseStoredServices(run.services);
|
|
1021
|
+
const task = withBaseCommit(run.task, baseCommit);
|
|
1022
|
+
if (task === run.task && /^[0-9a-f]{40}$/.test(baseCommit)) {
|
|
1023
|
+
containerLog.warn({ event: "coding.base_commit_omitted", runId: run.runId }, "the coding task left no room for the base commit line");
|
|
1024
|
+
}
|
|
944
1025
|
const input = CodingTaskInputSchema.parse({
|
|
945
1026
|
schemaVersion: CODING_PROTOCOL_VERSION,
|
|
946
1027
|
runId: run.runId,
|
|
947
1028
|
repository: run.repository,
|
|
948
1029
|
baseRef: run.baseRef,
|
|
949
1030
|
headRef: run.headRef,
|
|
950
|
-
task
|
|
1031
|
+
task,
|
|
951
1032
|
model: run.model,
|
|
952
1033
|
budgetUsd: run.budgetUsd,
|
|
953
1034
|
deadlineAt: deadlineAt.toISOString(),
|
|
@@ -38,6 +38,8 @@ export declare class DbosExecutor implements Executor {
|
|
|
38
38
|
get executorId(): string;
|
|
39
39
|
launch(): Promise<void>;
|
|
40
40
|
close(): Promise<void>;
|
|
41
|
+
/** Where a run this executor marks failed files its self-defect (no-op without a configured issue tracker). */
|
|
42
|
+
private get selfDefects();
|
|
41
43
|
start(runId: string): Promise<void>;
|
|
42
44
|
stop(runId: string, reason?: string): Promise<void>;
|
|
43
45
|
/**
|
|
@@ -133,6 +133,10 @@ export class DbosExecutor {
|
|
|
133
133
|
setActiveExecutor(undefined);
|
|
134
134
|
await DBOS.shutdown({ workflowCompletionTimeoutMS: 5_000 });
|
|
135
135
|
}
|
|
136
|
+
/** Where a run this executor marks failed files its self-defect (no-op without a configured issue tracker). */
|
|
137
|
+
get selfDefects() {
|
|
138
|
+
return { db: this.db, issueTrackers: this.providers.issueTrackers };
|
|
139
|
+
}
|
|
136
140
|
async start(runId) {
|
|
137
141
|
if (!this.launched)
|
|
138
142
|
await this.launch();
|
|
@@ -165,7 +169,7 @@ export class DbosExecutor {
|
|
|
165
169
|
// the same failure — dispatchRun's own `.catch` around `start()` calls
|
|
166
170
|
// it too — which is harmless precisely because it is conditional and
|
|
167
171
|
// idempotent: the second call matches zero rows.
|
|
168
|
-
await markRunFailedFromExecutorError(this.db, runId, err);
|
|
172
|
+
await markRunFailedFromExecutorError(this.db, runId, err, this.selfDefects);
|
|
169
173
|
throw err;
|
|
170
174
|
}
|
|
171
175
|
}
|
|
@@ -241,10 +245,8 @@ export class DbosExecutor {
|
|
|
241
245
|
return { state: "terminal" };
|
|
242
246
|
case "mark-failed":
|
|
243
247
|
this.resumeAttempts.delete(handle.runId);
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
data: { status: "failed", error: decision.error, finishedAt: new Date() },
|
|
247
|
-
});
|
|
248
|
+
// Conditional (only a pending/running row), so a self-defect is filed only when this made the run failed.
|
|
249
|
+
await markRunFailedFromExecutorError(this.db, handle.runId, decision.error, this.selfDefects);
|
|
248
250
|
return { state: "terminal" };
|
|
249
251
|
case "lost":
|
|
250
252
|
this.resumeAttempts.delete(handle.runId);
|
|
@@ -27,6 +27,12 @@ export declare class RoutingExecutor implements Executor {
|
|
|
27
27
|
repository: string;
|
|
28
28
|
baseRef: string;
|
|
29
29
|
}): Promise<string | null>;
|
|
30
|
+
readCodingRepositoryFile(input: {
|
|
31
|
+
repository: string;
|
|
32
|
+
baseRef: string;
|
|
33
|
+
path: string;
|
|
34
|
+
maxBytes: number;
|
|
35
|
+
}): Promise<string | null>;
|
|
30
36
|
supportsCodingServices(provider: CodingProvider): boolean;
|
|
31
37
|
}
|
|
32
38
|
export declare class PrismaExecutionKindResolver implements ExecutionKindResolver {
|
|
@@ -64,6 +64,11 @@ export class RoutingExecutor {
|
|
|
64
64
|
return null;
|
|
65
65
|
return this.coding.readCodingServiceDeclaration(input);
|
|
66
66
|
}
|
|
67
|
+
async readCodingRepositoryFile(input) {
|
|
68
|
+
if (!this.coding.readCodingRepositoryFile)
|
|
69
|
+
return null;
|
|
70
|
+
return this.coding.readCodingRepositoryFile(input);
|
|
71
|
+
}
|
|
67
72
|
supportsCodingServices(provider) {
|
|
68
73
|
return this.coding.supportsCodingServices?.(provider) === true;
|
|
69
74
|
}
|
|
@@ -68,6 +68,18 @@ export interface Executor {
|
|
|
68
68
|
repository: string;
|
|
69
69
|
baseRef: string;
|
|
70
70
|
}): Promise<string | null>;
|
|
71
|
+
/**
|
|
72
|
+
* The raw text of one repository file at `baseRef`, or null when absent.
|
|
73
|
+
* Throws (github_file_too_large, github_file_not_a_file, github_file_not_utf8)
|
|
74
|
+
* when the file is oversized or not UTF-8 text. Called by dispatch before its transaction (network call).
|
|
75
|
+
* Used for the knowledge note (src/knowledge/note.ts).
|
|
76
|
+
*/
|
|
77
|
+
readCodingRepositoryFile?(input: {
|
|
78
|
+
repository: string;
|
|
79
|
+
baseRef: string;
|
|
80
|
+
path: string;
|
|
81
|
+
maxBytes: number;
|
|
82
|
+
}): Promise<string | null>;
|
|
71
83
|
/**
|
|
72
84
|
* Whether this executor can start coding-run services for a run of `provider`: its job launcher
|
|
73
85
|
* says so (Kubernetes and Docker, for Codex and Claude Code).
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atlassian Document Format helpers. Jira Cloud's v3 API takes and returns
|
|
3
|
+
* rich text as ADF; webhook payloads use the v2 shape (wiki-markup strings),
|
|
4
|
+
* so the readers (adfToText, adfMentionIds) accept both. Agents write a
|
|
5
|
+
* small Markdown subset (markdownToAdf); the control plane reads issue text
|
|
6
|
+
* as plain text (adfToText). Nothing here
|
|
7
|
+
* produces HTML or mention nodes: a model can never ping a person.
|
|
8
|
+
*/
|
|
9
|
+
export interface AdfNode {
|
|
10
|
+
type: string;
|
|
11
|
+
version?: number;
|
|
12
|
+
attrs?: Record<string, unknown>;
|
|
13
|
+
content?: AdfNode[];
|
|
14
|
+
text?: string;
|
|
15
|
+
marks?: Array<{
|
|
16
|
+
type: string;
|
|
17
|
+
attrs?: Record<string, unknown>;
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Smart links (ADF inlineCard, what Jira's editor makes from a pasted issue link) for this site's
|
|
22
|
+
* issues: a bare `<siteUrl>/browse/KEY-1` URL, and a bare `KEY-1` whose project is in `projectKeys`
|
|
23
|
+
* (only those, so look-alikes such as UTF-8 or SHA-256 stay text).
|
|
24
|
+
*/
|
|
25
|
+
export interface SmartLinkOptions {
|
|
26
|
+
siteUrl: string;
|
|
27
|
+
projectKeys?: readonly string[];
|
|
28
|
+
}
|
|
29
|
+
export declare function markdownToAdf(markdown: string, smart?: SmartLinkOptions): AdfNode;
|
|
30
|
+
export declare function adfToText(node: unknown, maxChars?: number): string;
|
|
31
|
+
export declare function adfMentionIds(node: unknown): string[];
|