@wardby/cli 0.7.0 → 0.9.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/.env.example +5 -5
- package/README.md +6 -1
- package/deploy/local/docker-compose.quickstart-coding.yml +11 -0
- package/dist/claude-coding-worker/driver.d.ts +12 -0
- package/dist/claude-coding-worker/driver.js +29 -7
- package/dist/claude-coding-worker/main.js +5 -1
- package/dist/claude-coding-worker/sdk.js +17 -1
- package/dist/claude-plugin/command.d.ts +64 -0
- package/dist/claude-plugin/command.js +379 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +2 -1
- package/dist/cli.js +71 -3
- package/dist/coding/code-host.d.ts +16 -0
- package/dist/coding/code-host.js +60 -0
- package/dist/coding/mcp-token.d.ts +12 -0
- package/dist/coding/mcp-token.js +26 -0
- package/dist/coding/protocol.js +2 -0
- package/dist/coding/redact.js +6 -1
- package/dist/coding/transcript/recorder.d.ts +5 -4
- package/dist/coding/transcript/recorder.js +3 -9
- package/dist/coding-worker/debug-trace.d.ts +3 -1
- package/dist/coding-worker/debug-trace.js +4 -2
- package/dist/coding-worker/driver.js +8 -1
- package/dist/coding-worker/main.js +5 -1
- package/dist/coding-worker/mcp-config.d.ts +27 -0
- package/dist/coding-worker/mcp-config.js +32 -0
- package/dist/coding-worker/sdk.d.ts +36 -0
- package/dist/coding-worker/sdk.js +28 -1
- package/dist/coding-worker/types.d.ts +4 -0
- package/dist/config/mcp-call-limit.d.ts +8 -0
- package/dist/config/mcp-call-limit.js +18 -0
- package/dist/config/mcp-servers.d.ts +8 -0
- package/dist/config/mcp-servers.js +12 -0
- package/dist/config/test-simulators.d.ts +10 -0
- package/dist/config/test-simulators.js +28 -0
- package/dist/config/tool-timeout.d.ts +1 -0
- package/dist/config/tool-timeout.js +18 -0
- package/dist/core/coding-mcp.d.ts +36 -0
- package/dist/core/coding-mcp.js +253 -0
- package/dist/core/coding-repository.d.ts +46 -0
- package/dist/core/coding-repository.js +92 -0
- package/dist/core/delegation-slots.d.ts +27 -0
- package/dist/core/delegation-slots.js +44 -0
- package/dist/core/dispatch.d.ts +6 -2
- package/dist/core/dispatch.js +39 -11
- package/dist/core/engine-native.d.ts +2 -0
- package/dist/core/engine-native.js +13 -0
- package/dist/core/error-code.js +2 -0
- package/dist/core/grants.d.ts +28 -3
- package/dist/core/grants.js +46 -4
- package/dist/core/host-events.js +7 -3
- package/dist/core/host-status.js +2 -2
- package/dist/core/issue-status.js +4 -3
- package/dist/core/listen-address.d.ts +10 -0
- package/dist/core/listen-address.js +14 -0
- package/dist/core/mark-ready.d.ts +1 -1
- package/dist/core/mark-ready.js +1 -1
- package/dist/core/mcp-client/client.d.ts +47 -0
- package/dist/core/mcp-client/client.js +141 -0
- package/dist/core/mcp-client/jsonrpc.d.ts +15 -0
- package/dist/core/mcp-client/jsonrpc.js +54 -0
- package/dist/core/mcp-client/results.d.ts +13 -0
- package/dist/core/mcp-client/results.js +42 -0
- package/dist/core/mcp-servers.d.ts +90 -0
- package/dist/core/mcp-servers.js +230 -0
- package/dist/core/mcp-tools.d.ts +108 -0
- package/dist/core/mcp-tools.js +287 -0
- package/dist/core/merge-order-check.js +38 -19
- package/dist/core/notification-dispatcher.js +2 -1
- package/dist/core/power-idle.d.ts +8 -2
- package/dist/core/power-idle.js +17 -3
- package/dist/core/power.d.ts +5 -0
- package/dist/core/power.js +5 -0
- package/dist/core/related-pull-requests.js +26 -10
- package/dist/core/retention-days.d.ts +5 -0
- package/dist/core/retention-days.js +14 -0
- package/dist/core/review-fix.js +2 -2
- package/dist/core/review-host-tools.js +3 -1
- package/dist/core/run-steps/config.d.ts +25 -0
- package/dist/core/run-steps/config.js +40 -0
- package/dist/core/run-steps/control-chars.d.ts +16 -0
- package/dist/core/run-steps/control-chars.js +26 -0
- package/dist/core/run-steps/detail.d.ts +32 -0
- package/dist/core/run-steps/detail.js +240 -0
- package/dist/core/run-steps/extract.d.ts +52 -0
- package/dist/core/run-steps/extract.js +467 -0
- package/dist/core/run-steps/ingress.d.ts +51 -0
- package/dist/core/run-steps/ingress.js +160 -0
- package/dist/core/run-steps/last-step.d.ts +15 -0
- package/dist/core/run-steps/last-step.js +35 -0
- package/dist/core/run-steps/native.d.ts +48 -0
- package/dist/core/run-steps/native.js +164 -0
- package/dist/core/run-steps/seen.d.ts +16 -0
- package/dist/core/run-steps/seen.js +35 -0
- package/dist/core/run-steps/summarize.d.ts +57 -0
- package/dist/core/run-steps/summarize.js +304 -0
- package/dist/core/run-steps/sweep.d.ts +21 -0
- package/dist/core/run-steps/sweep.js +41 -0
- package/dist/core/run-steps/types.d.ts +89 -0
- package/dist/core/run-steps/types.js +39 -0
- package/dist/core/run-steps/wire.d.ts +35 -0
- package/dist/core/run-steps/wire.js +47 -0
- package/dist/core/run-steps/writer.d.ts +49 -0
- package/dist/core/run-steps/writer.js +279 -0
- package/dist/core/runner.d.ts +70 -16
- package/dist/core/runner.js +296 -74
- package/dist/core/secret-broker-config.d.ts +2 -0
- package/dist/core/secret-broker-config.js +1 -1
- package/dist/core/secret-guard.d.ts +5 -2
- package/dist/core/secret-guard.js +22 -7
- package/dist/core/secrets.d.ts +11 -1
- package/dist/core/secrets.js +27 -1
- package/dist/core/serializable-retry.d.ts +18 -0
- package/dist/core/serializable-retry.js +43 -0
- package/dist/core/tool-admin.d.ts +5 -1
- package/dist/core/tool-admin.js +7 -2
- package/dist/core/tool-names.d.ts +13 -0
- package/dist/core/tool-names.js +20 -0
- package/dist/core/webhooks.d.ts +4 -0
- package/dist/core/webhooks.js +68 -49
- package/dist/core/workflow-run-events.js +2 -1
- package/dist/generated/prisma/browser.d.ts +39 -0
- package/dist/generated/prisma/client.d.ts +39 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +80 -50
- package/dist/generated/prisma/enums.d.ts +6 -0
- package/dist/generated/prisma/enums.js +5 -0
- package/dist/generated/prisma/internal/class.d.ts +77 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +632 -1
- package/dist/generated/prisma/internal/prismaNamespace.js +94 -3
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +98 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +94 -3
- package/dist/generated/prisma/models/Agent.d.ts +372 -1
- package/dist/generated/prisma/models/AgentMcpServer.d.ts +1412 -0
- package/dist/generated/prisma/models/AgentMcpServer.js +1 -0
- package/dist/generated/prisma/models/AgentRepository.d.ts +40 -1
- package/dist/generated/prisma/models/AuthUser.d.ts +1 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +0 -7
- package/dist/generated/prisma/models/CodingProxySession.d.ts +159 -2
- package/dist/generated/prisma/models/CodingRun.d.ts +76 -1
- package/dist/generated/prisma/models/HostProvider.d.ts +2176 -0
- package/dist/generated/prisma/models/HostProvider.js +1 -0
- package/dist/generated/prisma/models/HostProviderCredential.d.ts +1285 -0
- package/dist/generated/prisma/models/HostProviderCredential.js +1 -0
- package/dist/generated/prisma/models/HostProviderOperation.d.ts +1470 -0
- package/dist/generated/prisma/models/HostProviderOperation.js +1 -0
- package/dist/generated/prisma/models/HostProviderTrustedAccount.d.ts +1145 -0
- package/dist/generated/prisma/models/HostProviderTrustedAccount.js +1 -0
- package/dist/generated/prisma/models/McpServer.d.ts +1500 -0
- package/dist/generated/prisma/models/McpServer.js +1 -0
- package/dist/generated/prisma/models/NativeGatewayCall.d.ts +39 -1
- package/dist/generated/prisma/models/Principal.d.ts +167 -0
- package/dist/generated/prisma/models/ResourceGrant.d.ts +1 -1
- package/dist/generated/prisma/models/Run.d.ts +503 -1
- package/dist/generated/prisma/models/RunStep.d.ts +1453 -0
- package/dist/generated/prisma/models/RunStep.js +1 -0
- package/dist/generated/prisma/models/Secret.d.ts +387 -0
- package/dist/generated/prisma/models/Tool.d.ts +223 -1
- package/dist/generated/prisma/models.d.ts +7 -0
- package/dist/help/search.js +12 -3
- package/dist/help-index.json +812 -52
- package/dist/import/create.js +16 -1
- package/dist/import/neutral-schema.d.ts +1 -0
- package/dist/import/neutral-schema.js +2 -0
- package/dist/mcp/auth/access.d.ts +1 -0
- package/dist/mcp/auth/grants-cli.d.ts +6 -4
- package/dist/mcp/auth/grants-cli.js +42 -3
- package/dist/mcp/auth/resource-server.d.ts +3 -1
- package/dist/mcp/auth/resource-server.js +17 -2
- package/dist/mcp/context.d.ts +6 -0
- package/dist/mcp/index.js +21 -0
- package/dist/mcp/tools/agents.js +135 -20
- package/dist/mcp/tools/grants.js +66 -28
- package/dist/mcp/tools/host-providers.d.ts +2 -0
- package/dist/mcp/tools/host-providers.js +215 -0
- package/dist/mcp/tools/mcp-servers.d.ts +26 -0
- package/dist/mcp/tools/mcp-servers.js +481 -0
- package/dist/mcp/tools/power.js +2 -2
- package/dist/mcp/tools/repositories.js +50 -10
- package/dist/mcp/tools/run-access.d.ts +7 -0
- package/dist/mcp/tools/run-access.js +14 -0
- package/dist/mcp/tools/run-steps.d.ts +16 -0
- package/dist/mcp/tools/run-steps.js +85 -0
- package/dist/mcp/tools/runs.js +186 -18
- package/dist/mcp/tools/secret-elicitation.d.ts +2 -0
- package/dist/mcp/tools/secret-elicitation.js +25 -2
- package/dist/mcp/tools/secrets.js +38 -4
- package/dist/mcp/tools/tools.d.ts +6 -4
- package/dist/mcp/tools/tools.js +40 -8
- package/dist/mcp/tools/trigger.js +28 -6
- package/dist/mcp/transport/http-limits.d.ts +2 -0
- package/dist/mcp/transport/http-limits.js +6 -2
- package/dist/mcp/webhooks/ingress.js +10 -0
- package/dist/native-worker/gateway-server.d.ts +18 -1
- package/dist/native-worker/gateway-server.js +38 -9
- package/dist/native-worker/gateway.d.ts +5 -0
- package/dist/native-worker/gateway.js +13 -1
- package/dist/native-worker/http-server.d.ts +6 -4
- package/dist/native-worker/http-server.js +53 -8
- package/dist/native-worker/ledger.d.ts +6 -2
- package/dist/native-worker/ledger.js +5 -4
- package/dist/native-worker/protocol.d.ts +1 -0
- package/dist/native-worker/protocol.js +4 -1
- package/dist/providers/coding-proxy/capability-hash.d.ts +2 -0
- package/dist/providers/coding-proxy/capability-hash.js +5 -0
- package/dist/providers/coding-proxy/mcp-route.d.ts +34 -0
- package/dist/providers/coding-proxy/mcp-route.js +166 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +2 -0
- package/dist/providers/coding-proxy/memory-ledger.js +16 -0
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +1 -0
- package/dist/providers/coding-proxy/prisma-ledger.js +36 -2
- package/dist/providers/coding-proxy/proxy.d.ts +33 -1
- package/dist/providers/coding-proxy/proxy.js +266 -75
- package/dist/providers/coding-proxy/run-step-forwarder.d.ts +42 -0
- package/dist/providers/coding-proxy/run-step-forwarder.js +206 -0
- package/dist/providers/coding-proxy/runtime.d.ts +13 -0
- package/dist/providers/coding-proxy/runtime.js +87 -4
- package/dist/providers/coding-proxy/server.d.ts +3 -0
- package/dist/providers/coding-proxy/server.js +10 -0
- package/dist/providers/coding-proxy/types.d.ts +18 -0
- package/dist/providers/engine/types.d.ts +26 -0
- package/dist/providers/executor/composition.js +5 -0
- package/dist/providers/executor/container.d.ts +47 -1
- package/dist/providers/executor/container.js +84 -8
- package/dist/providers/host-tools/invoke.d.ts +37 -0
- package/dist/providers/host-tools/invoke.js +117 -0
- package/dist/providers/host-tools/operations.d.ts +65 -0
- package/dist/providers/host-tools/operations.js +149 -0
- package/dist/providers/host-tools/registry.d.ts +79 -0
- package/dist/providers/host-tools/registry.js +362 -0
- package/dist/providers/host-tools/request-guard.d.ts +17 -0
- package/dist/providers/host-tools/request-guard.js +99 -0
- package/dist/providers/host-tools/self-test.d.ts +64 -0
- package/dist/providers/host-tools/self-test.js +142 -0
- package/dist/providers/jobs/docker-isolation.d.ts +2 -2
- package/dist/providers/jobs/docker-isolation.js +30 -8
- package/dist/providers/jobs/docker.d.ts +8 -0
- package/dist/providers/jobs/docker.js +33 -9
- package/dist/providers/jobs/kubernetes-isolation.d.ts +3 -1
- package/dist/providers/jobs/kubernetes-isolation.js +15 -2
- package/dist/providers/jobs/kubernetes.d.ts +13 -0
- package/dist/providers/jobs/kubernetes.js +28 -5
- package/dist/providers/jobs/types.d.ts +6 -0
- package/dist/providers/review-host/types.d.ts +6 -4
- package/dist/providers/review-host/types.js +1 -1
- package/dist/providers/vcs/git.d.ts +6 -2
- package/dist/providers/vcs/git.js +15 -4
- package/dist/providers/vcs/github.d.ts +2 -71
- package/dist/providers/vcs/github.js +8 -11
- package/dist/providers/vcs/index.d.ts +1 -1
- package/dist/providers/vcs/index.js +1 -1
- package/dist/providers/vcs/remote.d.ts +4 -0
- package/dist/providers/vcs/remote.js +7 -1
- package/dist/providers/vcs/routing.d.ts +12 -6
- package/dist/providers/vcs/routing.js +21 -6
- package/dist/providers/vcs/types.d.ts +71 -3
- package/dist/quickstart/index.d.ts +5 -0
- package/dist/quickstart/index.js +21 -0
- package/dist/quickstart-images.json +1 -1
- package/dist/sandbox/eval-core.d.ts +12 -2
- package/dist/sandbox/eval-core.js +20 -4
- package/dist/sandbox/host-functions.d.ts +4 -0
- package/dist/sandbox/host-functions.js +7 -2
- package/dist/sandbox/limits.d.ts +4 -1
- package/dist/sandbox/limits.js +4 -1
- package/dist/sandbox/run-in-sandbox.d.ts +6 -0
- package/dist/sandbox/run-in-sandbox.js +14 -2
- package/dist/sandbox/safe-fetch.d.ts +2 -0
- package/dist/sandbox/safe-fetch.js +1 -1
- package/dist/sandbox/secret-broker.d.ts +1 -0
- package/dist/sandbox/secret-broker.js +1 -1
- package/dist/sandbox/tool-timeout.d.ts +32 -0
- package/dist/sandbox/tool-timeout.js +52 -0
- package/dist/sandbox/user-tool.d.ts +8 -1
- package/dist/sandbox/user-tool.js +9 -3
- package/dist/serve.js +26 -18
- package/dist/viewer/api-schema.d.ts +66 -0
- package/dist/viewer/api-schema.js +31 -0
- package/dist/viewer/graph.js +2 -1
- package/dist/viewer/openapi.js +2 -1
- package/dist/viewer/run-detail.d.ts +2 -0
- package/dist/viewer/run-detail.js +26 -1
- package/dist/wardby-bin.js +5 -0
- package/docs/README.md +11 -1
- package/docs/agent-recipes.md +37 -4
- package/docs/architecture-runtime.md +1 -1
- package/docs/claude-code-plugin.md +208 -0
- package/docs/code-review-agents.md +6 -0
- package/docs/coding-agent-setup.md +94 -1
- package/docs/coding-worker-byo-images.md +4 -2
- package/docs/coding-worker-isolation.md +94 -7
- package/docs/debugging-agent-runs.md +354 -0
- package/docs/getting-started-gke.md +33 -11
- package/docs/getting-started-identity-provider.md +43 -37
- package/docs/getting-started.md +8 -0
- package/docs/host-providers.md +294 -0
- package/docs/mcp-servers.md +263 -0
- package/docs/native-sandbox.md +31 -9
- package/docs/security-deployment.md +161 -31
- package/docs/tool-secrets.md +3 -0
- package/docs/viewer-api.md +27 -12
- package/docs/viewer-api.openapi.yml +78 -2
- package/help/admin-viewer.md +5 -0
- package/help/agent-recipes.md +33 -2
- package/help/brokered-secrets.md +3 -0
- package/help/build-claude-code-image.md +1 -1
- package/help/claude-code-run-pane.md +63 -0
- package/help/deploy-gke.md +5 -0
- package/help/errors/coding-mcp-gateway-unavailable.md +46 -0
- package/help/errors/coding-mcp-pins-unavailable.md +35 -0
- package/help/errors/database-auth-invalid.md +1 -1
- package/help/errors/host-provider-error.md +40 -0
- package/help/errors/host-tool-failed.md +36 -0
- package/help/errors/mcp-call-limit.md +22 -0
- package/help/errors/mcp-call-timeout.md +22 -0
- package/help/errors/mcp-server-auth-failed.md +22 -0
- package/help/errors/mcp-server-credentials-unavailable.md +45 -0
- package/help/errors/mcp-server-error.md +28 -0
- package/help/errors/mcp-server-unreachable.md +22 -0
- package/help/errors/mcp-servers-off.md +21 -0
- package/help/errors/mcp-tool-not-allowed.md +26 -0
- package/help/errors/mcp-tool-removed.md +22 -0
- package/help/errors/mcp-unavailable.md +25 -0
- package/help/errors/native-sandbox-capacity.md +1 -1
- package/help/errors/native-sandbox-deadline-exceeded.md +1 -1
- package/help/errors/native-sandbox-docker-failed.md +1 -1
- package/help/errors/native-sandbox-gateway-error.md +1 -1
- package/help/errors/native-sandbox-gateway-unavailable.md +1 -1
- package/help/errors/native-sandbox-image-not-pinned.md +1 -1
- package/help/errors/native-sandbox-isolation-mismatch.md +1 -1
- package/help/errors/native-sandbox-network-unenforced.md +1 -1
- package/help/errors/native-sandbox-requires-catalog.md +1 -1
- package/help/errors/native-sandbox-unavailable.md +1 -1
- package/help/errors/native-sandbox-warm-delivery-failed.md +1 -1
- package/help/errors/native-sandbox-worker-exited.md +1 -1
- package/help/errors/native-sandbox-worker-lost.md +1 -1
- package/help/errors/native-sandbox-worker-unready.md +1 -1
- package/help/errors/repository-not-listed.md +36 -0
- package/help/errors/repository-required.md +36 -0
- package/help/errors/tool-timeout.md +38 -0
- package/help/host-providers.md +40 -0
- package/help/identity-and-access.md +4 -1
- package/help/low-power-mode.md +19 -8
- package/help/mcp-servers.md +73 -0
- package/help/mcp.md +9 -2
- package/help/native-capabilities.md +27 -1
- package/help/native-sandbox.md +1 -1
- package/help/operating-agents.md +16 -0
- package/help/related-pull-requests.md +7 -3
- package/help/repo-instructions-and-skills.md +3 -1
- package/help/resume-runs-locally.md +7 -0
- package/help/run-steps.md +140 -0
- package/help/stop-run.md +1 -1
- package/help/troubleshooting/coding-workers.md +9 -1
- package/help/troubleshooting/debugging-agent-runs.md +29 -0
- package/package.json +2 -1
- package/prisma/migrations/20261102000000_host_providers/migration.sql +56 -0
- package/prisma/migrations/20261104000000_coding_agent_repository_list/migration.sql +5 -0
- package/prisma/migrations/20261106000000_tool_timeout/migration.sql +4 -0
- package/prisma/migrations/20261107000000_mcp_servers/migration.sql +33 -0
- package/prisma/migrations/20261108000000_coding_mcp/migration.sql +5 -0
- package/prisma/migrations/20261109000000_task_run_index/migration.sql +3 -0
- package/prisma/migrations/20261110000000_run_steps/migration.sql +26 -0
- package/prisma/migrations/20261111000000_run_steps_viewer_notify/migration.sql +59 -0
- package/prisma/migrations/20261112000000_run_steps_detail/migration.sql +9 -0
- package/prisma/schema.prisma +211 -14
- package/scripts/git-askpass.sh +1 -1
|
@@ -95,7 +95,11 @@ export function createPrivilegedHost(options) {
|
|
|
95
95
|
},
|
|
96
96
|
async __bridge_fetch(argsJson) {
|
|
97
97
|
const [url, init = {}, secretNames] = args(argsJson);
|
|
98
|
-
const policy = {
|
|
98
|
+
const policy = {
|
|
99
|
+
...sandboxFetchPolicy(allowedFetchHosts ?? []),
|
|
100
|
+
signal,
|
|
101
|
+
...(options.fetchTimeoutMs !== undefined ? { timeoutMs: options.fetchTimeoutMs } : {}),
|
|
102
|
+
};
|
|
99
103
|
if (secretNames === undefined || (Array.isArray(secretNames) && secretNames.length === 0)) {
|
|
100
104
|
return fetchImpl(url, init, policy);
|
|
101
105
|
}
|
|
@@ -177,13 +181,14 @@ export function createPrivilegedHost(options) {
|
|
|
177
181
|
/** Registers the whole sandbox API: the local bridges directly, the privileged ones through `privileged`. */
|
|
178
182
|
export function installSandboxApi(context, runtime, options) {
|
|
179
183
|
const { privileged, signal } = options;
|
|
184
|
+
const sleepLimitMs = options.wallTimeLimitMs ?? WALL_TIME_LIMIT_MS;
|
|
180
185
|
const register = (name, fn) => registerJsonAsyncFunction(context, runtime, name, fn, signal);
|
|
181
186
|
const parserPool = options.parserPool ?? (sharedParserPool ??= createParserWorkerPool());
|
|
182
187
|
for (const name of PRIVILEGED_BRIDGE_NAMES)
|
|
183
188
|
register(name, (argsJson) => privileged[name](argsJson));
|
|
184
189
|
register("__bridge_sleep", async (argsJson) => {
|
|
185
190
|
const [ms] = args(argsJson);
|
|
186
|
-
if (!Number.isFinite(ms) || ms < 0 || ms >
|
|
191
|
+
if (!Number.isFinite(ms) || ms < 0 || ms > sleepLimitMs)
|
|
187
192
|
throw new Error("sleep_limit");
|
|
188
193
|
await sleep(ms, undefined, { signal });
|
|
189
194
|
return null;
|
package/dist/sandbox/limits.d.ts
CHANGED
|
@@ -15,7 +15,10 @@ export declare const MAX_INTERRUPT_CHECKS = 20000000;
|
|
|
15
15
|
* awaiting a host async call (e.g. a slow `fetch`) — this does.
|
|
16
16
|
*/
|
|
17
17
|
export declare const WALL_TIME_LIMIT_MS = 10000;
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* safeFetch's timeout when its caller gives none. A sandboxed tool's fetches get
|
|
20
|
+
* fetchTimeoutFor(the tool's limit) instead (tool-timeout.ts), just past its wall time.
|
|
21
|
+
*/
|
|
19
22
|
export declare const FETCH_TIMEOUT_MS = 8000;
|
|
20
23
|
/** QuickJS interpreter stack size cap (bytes). */
|
|
21
24
|
export declare const MAX_STACK_SIZE_BYTES: number;
|
package/dist/sandbox/limits.js
CHANGED
|
@@ -15,7 +15,10 @@ export const MAX_INTERRUPT_CHECKS = 20_000_000;
|
|
|
15
15
|
* awaiting a host async call (e.g. a slow `fetch`) — this does.
|
|
16
16
|
*/
|
|
17
17
|
export const WALL_TIME_LIMIT_MS = 10_000;
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* safeFetch's timeout when its caller gives none. A sandboxed tool's fetches get
|
|
20
|
+
* fetchTimeoutFor(the tool's limit) instead (tool-timeout.ts), just past its wall time.
|
|
21
|
+
*/
|
|
19
22
|
export const FETCH_TIMEOUT_MS = 8_000;
|
|
20
23
|
/** QuickJS interpreter stack size cap (bytes). */
|
|
21
24
|
export const MAX_STACK_SIZE_BYTES = 1024 * 1024; // 1 MiB
|
|
@@ -27,6 +27,8 @@ export interface SandboxInvocation {
|
|
|
27
27
|
toolName: string;
|
|
28
28
|
/** Overrides the default limits (limits.ts) — mainly for fast, deterministic tests. */
|
|
29
29
|
limits?: Partial<SandboxLimits>;
|
|
30
|
+
/** The tool's time limit (effectiveToolTimeoutMs); its fetches time out just before it. Overrides limits.wallTimeLimitMs. */
|
|
31
|
+
timeoutMs?: number;
|
|
30
32
|
/** Omitted for a dry run (no real agent) — secrets.get always resolves undefined. */
|
|
31
33
|
secrets?: SecretsAccessor;
|
|
32
34
|
/** Overrides the default shared logger — mainly for tests that need to capture forwarded console output. */
|
|
@@ -42,12 +44,16 @@ export interface ToolCodeInvocation {
|
|
|
42
44
|
code: string;
|
|
43
45
|
params: unknown;
|
|
44
46
|
limits?: Partial<SandboxLimits>;
|
|
47
|
+
/** Stops the call early (its run was stopped): it fails as `cancelled`, and its host calls are aborted. */
|
|
48
|
+
signal?: AbortSignal;
|
|
45
49
|
/** Built once per invocation, with that invocation's abort signal (fetch needs it). */
|
|
46
50
|
privileged: (signal: AbortSignal) => PrivilegedHost;
|
|
47
51
|
parserPool?: ParserWorkerPool;
|
|
48
52
|
}
|
|
49
53
|
/** Evaluates one tool body in a fresh QuickJS context against the given privileged host. */
|
|
50
54
|
export declare function runToolCode(invocation: ToolCodeInvocation): Promise<SandboxResult>;
|
|
55
|
+
/** The sandbox limits for a tool whose time limit is `timeoutMs` (absent: the defaults). */
|
|
56
|
+
export declare function toolLimits(timeoutMs: number | undefined, base?: Partial<SandboxLimits>): Partial<SandboxLimits>;
|
|
51
57
|
/** The in-process privileged host for a sandbox invocation. */
|
|
52
58
|
export declare function privilegedHostFor(invocation: SandboxInvocation, signal: AbortSignal): PrivilegedHost;
|
|
53
59
|
export declare function runInSandbox(invocation: SandboxInvocation): Promise<SandboxResult>;
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { createPrivilegedHost, installSandboxApi } from "./host-functions.js";
|
|
11
11
|
import { SANDBOX_PRELUDE } from "./prelude.js";
|
|
12
|
-
import { evalToJson, NON_SERIALIZABLE_MARKER } from "./eval-core.js";
|
|
12
|
+
import { DEFAULT_SANDBOX_LIMITS, evalToJson, NON_SERIALIZABLE_MARKER, } from "./eval-core.js";
|
|
13
|
+
import { fetchTimeoutFor, TOOL_RUN_STOPPED_MESSAGE, toolTimeoutMessage } from "./tool-timeout.js";
|
|
13
14
|
import { boundedJson, boundedString } from "./bounded-json.js";
|
|
14
15
|
import { BRIDGE_INPUT_BYTES } from "./limits.js";
|
|
15
16
|
/** Evaluates one tool body in a fresh QuickJS context against the given privileged host. */
|
|
@@ -39,14 +40,24 @@ export async function runToolCode(invocation) {
|
|
|
39
40
|
throw new Error("${NON_SERIALIZABLE_MARKER}" + (__serializeErr && __serializeErr.message));
|
|
40
41
|
}
|
|
41
42
|
})()`;
|
|
43
|
+
const wallTimeLimitMs = invocation.limits?.wallTimeLimitMs ?? DEFAULT_SANDBOX_LIMITS.wallTimeLimitMs;
|
|
42
44
|
return evalToJson(code, invocation.limits, (context, runtime, signal) => {
|
|
43
45
|
installSandboxApi(context, runtime, {
|
|
44
46
|
privileged: invocation.privileged(signal),
|
|
45
47
|
signal,
|
|
46
48
|
parserPool: invocation.parserPool,
|
|
49
|
+
wallTimeLimitMs,
|
|
47
50
|
});
|
|
51
|
+
}, {
|
|
52
|
+
signal: invocation.signal,
|
|
53
|
+
aborted: () => ({ ok: false, errorKind: "cancelled", errorMessage: TOOL_RUN_STOPPED_MESSAGE }),
|
|
54
|
+
timeoutMessage: toolTimeoutMessage,
|
|
48
55
|
});
|
|
49
56
|
}
|
|
57
|
+
/** The sandbox limits for a tool whose time limit is `timeoutMs` (absent: the defaults). */
|
|
58
|
+
export function toolLimits(timeoutMs, base) {
|
|
59
|
+
return timeoutMs === undefined ? { ...base } : { ...base, wallTimeLimitMs: timeoutMs };
|
|
60
|
+
}
|
|
50
61
|
/** The in-process privileged host for a sandbox invocation. */
|
|
51
62
|
export function privilegedHostFor(invocation, signal) {
|
|
52
63
|
return createPrivilegedHost({
|
|
@@ -59,13 +70,14 @@ export function privilegedHostFor(invocation, signal) {
|
|
|
59
70
|
allowedFetchHosts: invocation.allowedFetchHosts,
|
|
60
71
|
fetchImpl: invocation.fetchImpl,
|
|
61
72
|
signal,
|
|
73
|
+
fetchTimeoutMs: fetchTimeoutFor(invocation.timeoutMs ?? invocation.limits?.wallTimeLimitMs ?? DEFAULT_SANDBOX_LIMITS.wallTimeLimitMs),
|
|
62
74
|
});
|
|
63
75
|
}
|
|
64
76
|
export async function runInSandbox(invocation) {
|
|
65
77
|
return runToolCode({
|
|
66
78
|
code: invocation.code,
|
|
67
79
|
params: invocation.params,
|
|
68
|
-
limits: invocation.limits,
|
|
80
|
+
limits: toolLimits(invocation.timeoutMs, invocation.limits),
|
|
69
81
|
privileged: (signal) => privilegedHostFor(invocation, signal),
|
|
70
82
|
});
|
|
71
83
|
}
|
|
@@ -11,6 +11,8 @@ export declare function pinnedLookup(destination: Destination): LookupFunction;
|
|
|
11
11
|
export declare function requestPinned(destination: Destination, init: SafeFetchInit, signal: AbortSignal): Promise<IncomingMessage>;
|
|
12
12
|
export interface SafeFetchOptions extends FetchPolicyOptions {
|
|
13
13
|
signal?: AbortSignal;
|
|
14
|
+
/** This fetch's own timeout; FETCH_TIMEOUT_MS when absent. */
|
|
15
|
+
timeoutMs?: number;
|
|
14
16
|
connect?: typeof requestPinned;
|
|
15
17
|
/** False: a 3xx is returned to the caller as-is (brokered requests re-check every hop themselves). */
|
|
16
18
|
followRedirects?: boolean;
|
|
@@ -39,7 +39,7 @@ async function untilAbort(promise, signal) {
|
|
|
39
39
|
}
|
|
40
40
|
export async function safeFetch(input, init = {}, options = {}) {
|
|
41
41
|
const abort = new AbortController();
|
|
42
|
-
const timer = setTimeout(() => abort.abort(new Error("fetch_timeout")), FETCH_TIMEOUT_MS);
|
|
42
|
+
const timer = setTimeout(() => abort.abort(new Error("fetch_timeout")), options.timeoutMs ?? FETCH_TIMEOUT_MS);
|
|
43
43
|
const signal = options.signal ? AbortSignal.any([options.signal, abort.signal]) : abort.signal;
|
|
44
44
|
let url = input;
|
|
45
45
|
let method = init.method ?? "GET";
|
|
@@ -26,6 +26,7 @@ export declare function consoleRedactionValues(entry: {
|
|
|
26
26
|
value: string;
|
|
27
27
|
broker: SecretBrokerConfig | null;
|
|
28
28
|
}): string[];
|
|
29
|
+
export declare function encodedForms(value: string): string[];
|
|
29
30
|
export declare function scrubBrokeredResponse<T extends {
|
|
30
31
|
headers: Record<string, string>;
|
|
31
32
|
bodyBase64: string;
|
|
@@ -121,7 +121,7 @@ export function consoleRedactionValues(entry) {
|
|
|
121
121
|
return [entry.value];
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
|
-
function encodedForms(value) {
|
|
124
|
+
export function encodedForms(value) {
|
|
125
125
|
const buf = Buffer.from(value, "utf8");
|
|
126
126
|
// Query placement goes through URLSearchParams, which encodes ~ ! ' ( ) differently and space as "+",
|
|
127
127
|
// so both percent-encodings are scrubbed, each also in lowercase-hex form (a response may echo either).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A sandboxed tool's time limit. A tool may ask for its own `timeoutMs`; the operator's
|
|
3
|
+
* WARDBY_TOOL_MAX_TIMEOUT_MS (config/tool-timeout.ts) caps it. Without one a tool keeps the
|
|
4
|
+
* default wall-time cap (limits.ts). Pure and worker-safe: no configuration is read here.
|
|
5
|
+
*/
|
|
6
|
+
/** A tool that sets no `timeoutMs`. */
|
|
7
|
+
export declare const DEFAULT_TOOL_TIMEOUT_MS = 10000;
|
|
8
|
+
/** The shortest `timeoutMs` a tool may ask for. */
|
|
9
|
+
export declare const MIN_TOOL_TIMEOUT_MS = 1000;
|
|
10
|
+
/** WARDBY_TOOL_MAX_TIMEOUT_MS when unset: five minutes. */
|
|
11
|
+
export declare const DEFAULT_TOOL_MAX_TIMEOUT_MS = 300000;
|
|
12
|
+
/**
|
|
13
|
+
* How far past the tool's limit each fetch's own timeout lies. The wall time ends the call (and
|
|
14
|
+
* aborts its fetches) first, so a slow request fails with the wall-time error that names the
|
|
15
|
+
* limit, not a fetch error.
|
|
16
|
+
*/
|
|
17
|
+
export declare const FETCH_TIMEOUT_MARGIN_MS = 1000;
|
|
18
|
+
/** The fetch timeout for a tool limit of `wallTimeLimitMs`. */
|
|
19
|
+
export declare function fetchTimeoutFor(wallTimeLimitMs: number): number;
|
|
20
|
+
/**
|
|
21
|
+
* The limit a run uses for a tool: its own `timeoutMs` (or the default when it has none), never
|
|
22
|
+
* above the current ceiling, so lowering the ceiling also covers tools that already exist.
|
|
23
|
+
*/
|
|
24
|
+
export declare function effectiveToolTimeoutMs(timeoutMs: number | null | undefined, ceilingMs: number): number;
|
|
25
|
+
/** Why `timeoutMs` can't be stored on a tool under `ceilingMs`, or undefined when it can. */
|
|
26
|
+
export declare function toolTimeoutProblem(timeoutMs: unknown, ceilingMs: number): string | undefined;
|
|
27
|
+
/** The model-facing text for a tool that ran out of time. */
|
|
28
|
+
export declare function toolTimeoutMessage(limitMs: number): string;
|
|
29
|
+
/** The model-facing text for a tool call cut off at its run's deadline. */
|
|
30
|
+
export declare const TOOL_RUN_DEADLINE_MESSAGE = "Stopped at the run's deadline while this tool call was still running.";
|
|
31
|
+
/** The model-facing text for a tool call aborted because its run was stopped. */
|
|
32
|
+
export declare const TOOL_RUN_STOPPED_MESSAGE = "The run was stopped while this tool call was running.";
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A sandboxed tool's time limit. A tool may ask for its own `timeoutMs`; the operator's
|
|
3
|
+
* WARDBY_TOOL_MAX_TIMEOUT_MS (config/tool-timeout.ts) caps it. Without one a tool keeps the
|
|
4
|
+
* default wall-time cap (limits.ts). Pure and worker-safe: no configuration is read here.
|
|
5
|
+
*/
|
|
6
|
+
import { WALL_TIME_LIMIT_MS } from "./limits.js";
|
|
7
|
+
/** A tool that sets no `timeoutMs`. */
|
|
8
|
+
export const DEFAULT_TOOL_TIMEOUT_MS = WALL_TIME_LIMIT_MS;
|
|
9
|
+
/** The shortest `timeoutMs` a tool may ask for. */
|
|
10
|
+
export const MIN_TOOL_TIMEOUT_MS = 1_000;
|
|
11
|
+
/** WARDBY_TOOL_MAX_TIMEOUT_MS when unset: five minutes. */
|
|
12
|
+
export const DEFAULT_TOOL_MAX_TIMEOUT_MS = 300_000;
|
|
13
|
+
/**
|
|
14
|
+
* How far past the tool's limit each fetch's own timeout lies. The wall time ends the call (and
|
|
15
|
+
* aborts its fetches) first, so a slow request fails with the wall-time error that names the
|
|
16
|
+
* limit, not a fetch error.
|
|
17
|
+
*/
|
|
18
|
+
export const FETCH_TIMEOUT_MARGIN_MS = 1_000;
|
|
19
|
+
/** The fetch timeout for a tool limit of `wallTimeLimitMs`. */
|
|
20
|
+
export function fetchTimeoutFor(wallTimeLimitMs) {
|
|
21
|
+
return wallTimeLimitMs + FETCH_TIMEOUT_MARGIN_MS;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The limit a run uses for a tool: its own `timeoutMs` (or the default when it has none), never
|
|
25
|
+
* above the current ceiling, so lowering the ceiling also covers tools that already exist.
|
|
26
|
+
*/
|
|
27
|
+
export function effectiveToolTimeoutMs(timeoutMs, ceilingMs) {
|
|
28
|
+
if (timeoutMs === null || timeoutMs === undefined)
|
|
29
|
+
return DEFAULT_TOOL_TIMEOUT_MS;
|
|
30
|
+
return Math.min(timeoutMs, ceilingMs);
|
|
31
|
+
}
|
|
32
|
+
/** Why `timeoutMs` can't be stored on a tool under `ceilingMs`, or undefined when it can. */
|
|
33
|
+
export function toolTimeoutProblem(timeoutMs, ceilingMs) {
|
|
34
|
+
if (typeof timeoutMs !== "number" || !Number.isSafeInteger(timeoutMs)) {
|
|
35
|
+
return "timeoutMs must be a whole number of milliseconds.";
|
|
36
|
+
}
|
|
37
|
+
if (timeoutMs < MIN_TOOL_TIMEOUT_MS)
|
|
38
|
+
return `timeoutMs must be at least ${MIN_TOOL_TIMEOUT_MS} ms.`;
|
|
39
|
+
if (timeoutMs > ceilingMs) {
|
|
40
|
+
return `timeoutMs ${timeoutMs} is above this server's ceiling of ${ceilingMs} ms (WARDBY_TOOL_MAX_TIMEOUT_MS).`;
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
/** The model-facing text for a tool that ran out of time. */
|
|
45
|
+
export function toolTimeoutMessage(limitMs) {
|
|
46
|
+
return (`Exceeded the ${limitMs}ms wall-time limit. A tool that needs longer can set timeoutMs ` +
|
|
47
|
+
"(create_tool or update_tool), up to the server's WARDBY_TOOL_MAX_TIMEOUT_MS.");
|
|
48
|
+
}
|
|
49
|
+
/** The model-facing text for a tool call cut off at its run's deadline. */
|
|
50
|
+
export const TOOL_RUN_DEADLINE_MESSAGE = "Stopped at the run's deadline while this tool call was still running.";
|
|
51
|
+
/** The model-facing text for a tool call aborted because its run was stopped. */
|
|
52
|
+
export const TOOL_RUN_STOPPED_MESSAGE = "The run was stopped while this tool call was running.";
|
|
@@ -10,5 +10,12 @@ import type { SandboxLimits } from "./eval-core.js";
|
|
|
10
10
|
export interface UserToolDefinition {
|
|
11
11
|
code: string;
|
|
12
12
|
paramsZod: string;
|
|
13
|
+
/** The time limit this run gives the tool (effectiveToolTimeoutMs); absent = the default. */
|
|
14
|
+
timeoutMs?: number;
|
|
13
15
|
}
|
|
14
|
-
export
|
|
16
|
+
export interface UserToolCallOptions {
|
|
17
|
+
limits?: Partial<SandboxLimits>;
|
|
18
|
+
/** Aborts the call when its run is stopped. */
|
|
19
|
+
signal?: AbortSignal;
|
|
20
|
+
}
|
|
21
|
+
export declare function runUserToolCall(tool: UserToolDefinition, argsJson: string, privileged: (signal: AbortSignal) => PrivilegedHost, options?: UserToolCallOptions): Promise<string>;
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* JSON error result. Worker-safe: it touches no database or provider, only the
|
|
6
6
|
* privileged host it is given, so the native sandbox worker runs it as is.
|
|
7
7
|
*/
|
|
8
|
-
import { runToolCode } from "./run-in-sandbox.js";
|
|
8
|
+
import { runToolCode, toolLimits } from "./run-in-sandbox.js";
|
|
9
9
|
import { validateParams } from "./zod-params.js";
|
|
10
|
-
export async function runUserToolCall(tool, argsJson, privileged,
|
|
10
|
+
export async function runUserToolCall(tool, argsJson, privileged, options = {}) {
|
|
11
11
|
let parsedArgs;
|
|
12
12
|
try {
|
|
13
13
|
// Some providers stream no JSON delta at all for a zero-parameter
|
|
@@ -24,7 +24,13 @@ export async function runUserToolCall(tool, argsJson, privileged, limits) {
|
|
|
24
24
|
if (!validation.ok) {
|
|
25
25
|
return JSON.stringify({ error: "validation_failed", message: validation.errorMessage });
|
|
26
26
|
}
|
|
27
|
-
const result = await runToolCode({
|
|
27
|
+
const result = await runToolCode({
|
|
28
|
+
code: tool.code,
|
|
29
|
+
params: validation.value,
|
|
30
|
+
limits: toolLimits(tool.timeoutMs, options.limits),
|
|
31
|
+
signal: options.signal,
|
|
32
|
+
privileged,
|
|
33
|
+
});
|
|
28
34
|
if (!result.ok) {
|
|
29
35
|
return JSON.stringify({ error: result.errorKind, message: result.errorMessage });
|
|
30
36
|
}
|
package/dist/serve.js
CHANGED
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `wardby serve`: the complete wardby process. Runs the MCP server, the
|
|
3
|
-
* scheduler, and the reconciler together on ONE provider set and ONE
|
|
4
|
-
* executor, which is what a single-container deployment (Cloud Run, a lone
|
|
5
|
-
* VM) needs - `wardby mcp` alone never fires a schedule.
|
|
6
|
-
*
|
|
7
|
-
* Why one executor: DbosExecutor is a per-process singleton and refuses to
|
|
8
|
-
* launch twice under different ids (providers/executor/dbos.ts), and its
|
|
9
|
-
* recovery decisions key on that id. Building providers once and sharing
|
|
10
|
-
* them is the whole trick; it also gives scheduled native runs the same
|
|
11
|
-
* `providers.executor` wiring MCP-triggered runs get, so sub-agent dispatch
|
|
12
|
-
* from a scheduled run works (it does not from `wardby scheduler`).
|
|
13
|
-
*
|
|
14
|
-
* Why the scheduler and reconciler can run in every replica: the scheduler
|
|
15
|
-
* elects one ticker via a Postgres lease and enforces at-most-once with row
|
|
16
|
-
* locks regardless (core/scheduler.ts); the reconciler is deliberately not
|
|
17
|
-
* lease-gated and is safe under concurrency (core/reconciler.ts).
|
|
18
|
-
*/
|
|
19
1
|
import { loadCodingConcurrencyConfig, loadMcpConfig } from "./config/providers.js";
|
|
20
2
|
import { drainCodingQueue } from "./core/coding-queue.js";
|
|
21
3
|
import { prisma } from "./core/db.js";
|
|
@@ -28,6 +10,8 @@ import { markRunFailedFromExecutorError } from "./core/dispatch.js";
|
|
|
28
10
|
import { startScheduler } from "./core/scheduler.js";
|
|
29
11
|
import { drainDeferredRuns } from "./providers/executor/deferred.js";
|
|
30
12
|
import { resolveDeferredCodingRun } from "./core/deferred-coding.js";
|
|
13
|
+
import { RunStepSweeper } from "./core/run-steps/sweep.js";
|
|
14
|
+
import { parseRunStepsListen, startRunStepIngress } from "./core/run-steps/ingress.js";
|
|
31
15
|
import { buildMcpProviders, startMcp } from "./mcp/index.js";
|
|
32
16
|
const serveLog = logger.child({ module: "serve" });
|
|
33
17
|
export async function startServe(options = {}) {
|
|
@@ -39,6 +23,8 @@ export async function startServe(options = {}) {
|
|
|
39
23
|
// Parsed before anything starts, so a malformed CODING_MAX_CONCURRENT or
|
|
40
24
|
// CODING_QUEUE_TIMEOUT_SEC fails fast instead of after HTTP is listening.
|
|
41
25
|
const concurrency = loadCodingConcurrencyConfig();
|
|
26
|
+
// Run steps from the coding proxy (POST /internal/run-steps); parsed early so a bad value stops start-up.
|
|
27
|
+
const runStepsListen = parseRunStepsListen(process.env.WARDBY_RUN_STEPS_LISTEN);
|
|
42
28
|
const providers = options.providers ?? buildMcpProviders().providers;
|
|
43
29
|
const power = providers.power ?? DISABLED_POWER;
|
|
44
30
|
if (power.enabled) {
|
|
@@ -48,6 +34,21 @@ export async function startServe(options = {}) {
|
|
|
48
34
|
}, (err) => serveLog.warn({ err }, "low_power_access_check_failed"));
|
|
49
35
|
}
|
|
50
36
|
const mcp = await startMcp({ providers, schedulerAttached: true });
|
|
37
|
+
// Its own port (not the MCP listener's): only the coding proxy reaches it.
|
|
38
|
+
let runStepIngress;
|
|
39
|
+
if (runStepsListen && providers.runSteps) {
|
|
40
|
+
try {
|
|
41
|
+
runStepIngress = await startRunStepIngress(runStepsListen, { db: prisma, sink: providers.runSteps });
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
await mcp.close();
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
serveLog.info({ host: runStepsListen.host, port: runStepsListen.port }, "run steps listener started");
|
|
48
|
+
}
|
|
49
|
+
else if (runStepsListen) {
|
|
50
|
+
serveLog.warn("WARDBY_RUN_STEPS_LISTEN is set but this server has no run-steps writer; not listening");
|
|
51
|
+
}
|
|
51
52
|
const reviewHosts = providers.reviewHosts;
|
|
52
53
|
const reconciler = startReconciler({
|
|
53
54
|
executor: providers.executor,
|
|
@@ -67,6 +68,8 @@ export async function startServe(options = {}) {
|
|
|
67
68
|
});
|
|
68
69
|
const notifications = await startNotifications({ db: prisma, chat: providers.chat ?? {} });
|
|
69
70
|
const selfDefects = { db: prisma, issueTrackers: providers.issueTrackers };
|
|
71
|
+
// Expired run steps (RUN_STEP_RETENTION_DAYS), from the scheduler leader only.
|
|
72
|
+
const runStepSweeper = new RunStepSweeper(prisma);
|
|
70
73
|
const scheduler = startScheduler({
|
|
71
74
|
executor: providers.executor,
|
|
72
75
|
scope: options.scope,
|
|
@@ -82,6 +85,7 @@ export async function startServe(options = {}) {
|
|
|
82
85
|
});
|
|
83
86
|
// Scale idle coding-proxy/native-gateway Deployments to zero (no-op unless WARDBY_LOW_POWER=1).
|
|
84
87
|
await power.sleepIfIdle().catch((err) => serveLog.warn({ err }, "power_sleep_failed"));
|
|
88
|
+
await runStepSweeper.maybeSweep();
|
|
85
89
|
},
|
|
86
90
|
});
|
|
87
91
|
return {
|
|
@@ -92,6 +96,10 @@ export async function startServe(options = {}) {
|
|
|
92
96
|
scheduler.stop();
|
|
93
97
|
reconciler.stop();
|
|
94
98
|
notifications.stop();
|
|
99
|
+
// Before mcp.close(), which flushes the run-steps writer: no batch arrives after the flush.
|
|
100
|
+
const ingress = runStepIngress;
|
|
101
|
+
if (ingress)
|
|
102
|
+
await new Promise((resolve) => ingress.close(() => resolve()));
|
|
95
103
|
await mcp.close();
|
|
96
104
|
},
|
|
97
105
|
};
|
|
@@ -320,6 +320,21 @@ export declare const GraphSnapshotSchema: z.ZodObject<{
|
|
|
320
320
|
}, z.core.$strip>>;
|
|
321
321
|
}, z.core.$strip>;
|
|
322
322
|
}, z.core.$strip>;
|
|
323
|
+
/**
|
|
324
|
+
* One recorded run step (docs/viewer-api.md): a tool call or what the model said, one line. Stored
|
|
325
|
+
* already redacted (src/core/run-steps/writer.ts); a step's call id and detail text are never sent.
|
|
326
|
+
*/
|
|
327
|
+
export declare const RunStepSchema: z.ZodObject<{
|
|
328
|
+
seq: z.ZodNumber;
|
|
329
|
+
at: z.ZodString;
|
|
330
|
+
kind: z.ZodEnum<{
|
|
331
|
+
tool: "tool";
|
|
332
|
+
said: "said";
|
|
333
|
+
}>;
|
|
334
|
+
tool: z.ZodNullable<z.ZodString>;
|
|
335
|
+
summary: z.ZodString;
|
|
336
|
+
status: z.ZodNullable<z.ZodString>;
|
|
337
|
+
}, z.core.$strip>;
|
|
323
338
|
export declare const RunDetailSchema: z.ZodObject<{
|
|
324
339
|
id: z.ZodString;
|
|
325
340
|
parentRunId: z.ZodNullable<z.ZodString>;
|
|
@@ -429,6 +444,19 @@ export declare const RunDetailSchema: z.ZodObject<{
|
|
|
429
444
|
error: z.ZodNullable<z.ZodString>;
|
|
430
445
|
finalText: z.ZodNullable<z.ZodString>;
|
|
431
446
|
childRunIds: z.ZodArray<z.ZodString>;
|
|
447
|
+
steps: z.ZodArray<z.ZodObject<{
|
|
448
|
+
seq: z.ZodNumber;
|
|
449
|
+
at: z.ZodString;
|
|
450
|
+
kind: z.ZodEnum<{
|
|
451
|
+
tool: "tool";
|
|
452
|
+
said: "said";
|
|
453
|
+
}>;
|
|
454
|
+
tool: z.ZodNullable<z.ZodString>;
|
|
455
|
+
summary: z.ZodString;
|
|
456
|
+
status: z.ZodNullable<z.ZodString>;
|
|
457
|
+
}, z.core.$strip>>;
|
|
458
|
+
stepsOmitted: z.ZodNumber;
|
|
459
|
+
stepsDropped: z.ZodNumber;
|
|
432
460
|
coding: z.ZodNullable<z.ZodObject<{
|
|
433
461
|
provider: z.ZodString;
|
|
434
462
|
repository: z.ZodString;
|
|
@@ -484,6 +512,18 @@ export declare const ViewerEventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
484
512
|
issue_status: "issue_status";
|
|
485
513
|
host_check: "host_check";
|
|
486
514
|
}>;
|
|
515
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
516
|
+
kind: z.ZodLiteral<"step">;
|
|
517
|
+
runId: z.ZodString;
|
|
518
|
+
seq: z.ZodNumber;
|
|
519
|
+
stepKind: z.ZodEnum<{
|
|
520
|
+
tool: "tool";
|
|
521
|
+
said: "said";
|
|
522
|
+
}>;
|
|
523
|
+
tool: z.ZodNullable<z.ZodString>;
|
|
524
|
+
summary: z.ZodString;
|
|
525
|
+
status: z.ZodNullable<z.ZodString>;
|
|
526
|
+
at: z.ZodString;
|
|
487
527
|
}, z.core.$strip>], "kind">;
|
|
488
528
|
export type RunTriggerInfo = z.infer<typeof RunTriggerSchema>;
|
|
489
529
|
export type Outcome = z.infer<typeof OutcomeSchema>;
|
|
@@ -491,6 +531,7 @@ export type ServiceStatus = z.infer<typeof ServiceStatusSchema>;
|
|
|
491
531
|
export type GraphRun = z.infer<typeof GraphRunSchema>;
|
|
492
532
|
export type GraphSnapshot = z.infer<typeof GraphSnapshotSchema>;
|
|
493
533
|
export type RunDetail = z.infer<typeof RunDetailSchema>;
|
|
534
|
+
export type RunStepView = z.infer<typeof RunStepSchema>;
|
|
494
535
|
export type ViewerEvent = z.infer<typeof ViewerEventSchema>;
|
|
495
536
|
/** How this deployment runs coding jobs, for the desktop viewer's Infrastructure view (GET /admin/api/infra). */
|
|
496
537
|
export declare const InfraInfoSchema: z.ZodObject<{
|
|
@@ -758,6 +799,19 @@ export declare const VIEWER_SCHEMAS: {
|
|
|
758
799
|
error: z.ZodNullable<z.ZodString>;
|
|
759
800
|
finalText: z.ZodNullable<z.ZodString>;
|
|
760
801
|
childRunIds: z.ZodArray<z.ZodString>;
|
|
802
|
+
steps: z.ZodArray<z.ZodObject<{
|
|
803
|
+
seq: z.ZodNumber;
|
|
804
|
+
at: z.ZodString;
|
|
805
|
+
kind: z.ZodEnum<{
|
|
806
|
+
tool: "tool";
|
|
807
|
+
said: "said";
|
|
808
|
+
}>;
|
|
809
|
+
tool: z.ZodNullable<z.ZodString>;
|
|
810
|
+
summary: z.ZodString;
|
|
811
|
+
status: z.ZodNullable<z.ZodString>;
|
|
812
|
+
}, z.core.$strip>>;
|
|
813
|
+
stepsOmitted: z.ZodNumber;
|
|
814
|
+
stepsDropped: z.ZodNumber;
|
|
761
815
|
coding: z.ZodNullable<z.ZodObject<{
|
|
762
816
|
provider: z.ZodString;
|
|
763
817
|
repository: z.ZodString;
|
|
@@ -813,6 +867,18 @@ export declare const VIEWER_SCHEMAS: {
|
|
|
813
867
|
issue_status: "issue_status";
|
|
814
868
|
host_check: "host_check";
|
|
815
869
|
}>;
|
|
870
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
871
|
+
kind: z.ZodLiteral<"step">;
|
|
872
|
+
runId: z.ZodString;
|
|
873
|
+
seq: z.ZodNumber;
|
|
874
|
+
stepKind: z.ZodEnum<{
|
|
875
|
+
tool: "tool";
|
|
876
|
+
said: "said";
|
|
877
|
+
}>;
|
|
878
|
+
tool: z.ZodNullable<z.ZodString>;
|
|
879
|
+
summary: z.ZodString;
|
|
880
|
+
status: z.ZodNullable<z.ZodString>;
|
|
881
|
+
at: z.ZodString;
|
|
816
882
|
}, z.core.$strip>], "kind">;
|
|
817
883
|
readonly "infra-info": z.ZodObject<{
|
|
818
884
|
launcher: z.ZodEnum<{
|
|
@@ -123,10 +123,30 @@ export const GraphSnapshotSchema = z.object({
|
|
|
123
123
|
runs: z.array(GraphRunSchema),
|
|
124
124
|
spend: z.object({ todayUsd: z.number(), groups: z.array(BudgetGroupSpendSchema) }),
|
|
125
125
|
});
|
|
126
|
+
/**
|
|
127
|
+
* One recorded run step (docs/viewer-api.md): a tool call or what the model said, one line. Stored
|
|
128
|
+
* already redacted (src/core/run-steps/writer.ts); a step's call id and detail text are never sent.
|
|
129
|
+
*/
|
|
130
|
+
export const RunStepSchema = z.object({
|
|
131
|
+
seq: z.number().int(),
|
|
132
|
+
at: z.string(),
|
|
133
|
+
kind: z.enum(["tool", "said"]),
|
|
134
|
+
/** The tool's display name (`Bash`, `Edit`, `mcp:<tool>`); null for said. */
|
|
135
|
+
tool: z.string().nullable(),
|
|
136
|
+
summary: z.string(),
|
|
137
|
+
/** `ok`, `error` or `exit N` once the result is back; null until then and for said. */
|
|
138
|
+
status: z.string().nullable(),
|
|
139
|
+
});
|
|
126
140
|
export const RunDetailSchema = GraphRunSchema.extend({
|
|
127
141
|
error: z.string().nullable(),
|
|
128
142
|
finalText: z.string().nullable(),
|
|
129
143
|
childRunIds: z.array(z.string()),
|
|
144
|
+
/** The run's newest steps (at most 200), oldest first; see `stepsOmitted` for earlier ones. */
|
|
145
|
+
steps: z.array(RunStepSchema),
|
|
146
|
+
/** Earlier recorded steps not included in `steps`. */
|
|
147
|
+
stepsOmitted: z.number().int(),
|
|
148
|
+
/** Steps past the per-run limit, never recorded. */
|
|
149
|
+
stepsDropped: z.number().int(),
|
|
130
150
|
coding: z
|
|
131
151
|
.object({
|
|
132
152
|
provider: z.string(),
|
|
@@ -170,6 +190,17 @@ export const ViewerEventSchema = z.discriminatedUnion("kind", [
|
|
|
170
190
|
/** Which table changed (the viewer NOTIFY trigger names them). */
|
|
171
191
|
source: z.enum(["pull_request", "host_status", "issue_status", "host_check"]),
|
|
172
192
|
}),
|
|
193
|
+
/** A run recorded a step, or a step's status arrived (the RunStep NOTIFY trigger). */
|
|
194
|
+
z.object({
|
|
195
|
+
kind: z.literal("step"),
|
|
196
|
+
runId: z.string(),
|
|
197
|
+
seq: RunStepSchema.shape.seq,
|
|
198
|
+
stepKind: RunStepSchema.shape.kind,
|
|
199
|
+
tool: RunStepSchema.shape.tool,
|
|
200
|
+
summary: RunStepSchema.shape.summary,
|
|
201
|
+
status: RunStepSchema.shape.status,
|
|
202
|
+
at: RunStepSchema.shape.at,
|
|
203
|
+
}),
|
|
173
204
|
]);
|
|
174
205
|
/** How this deployment runs coding jobs, for the desktop viewer's Infrastructure view (GET /admin/api/infra). */
|
|
175
206
|
export const InfraInfoSchema = z.object({
|
package/dist/viewer/graph.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { periodStart } from "../core/budget-groups.js";
|
|
2
|
+
import { codeProviderOf } from "../coding/code-host.js";
|
|
2
3
|
import { publicCodingRunResult } from "../coding/protocol.js";
|
|
3
4
|
const DEFAULT_SINCE_MS = 60 * 60 * 1000;
|
|
4
5
|
const RELATIVE_SINCE_MS = {
|
|
@@ -102,7 +103,7 @@ function outcomesFor(row, pullRequests, sites) {
|
|
|
102
103
|
!pullRequests.some((pr) => pr.url === result.pullRequestUrl)) {
|
|
103
104
|
outcomes.push({
|
|
104
105
|
kind: "pull_request",
|
|
105
|
-
provider:
|
|
106
|
+
provider: codeProviderOf(result.repository),
|
|
106
107
|
repository: result.repository,
|
|
107
108
|
number: result.pullRequestNumber,
|
|
108
109
|
url: result.pullRequestUrl,
|
package/dist/viewer/openapi.js
CHANGED
|
@@ -59,7 +59,7 @@ export const VIEWER_ROUTES = [
|
|
|
59
59
|
path: "/admin/api/runs/{id}",
|
|
60
60
|
operationId: "getRun",
|
|
61
61
|
summary: "Run detail",
|
|
62
|
-
description: "Full detail for one run, including final text.",
|
|
62
|
+
description: "Full detail for one run, including final text and its newest steps.",
|
|
63
63
|
parameters: [{ name: "id", in: "path", required: true, description: "Run id.", schema: { type: "string" } }],
|
|
64
64
|
responses: {
|
|
65
65
|
"200": { description: "The run.", content: { "application/json": { schema: ref("run-detail") } } },
|
|
@@ -85,6 +85,7 @@ export const VIEWER_ROUTES = [
|
|
|
85
85
|
"- `retry: 3000`: suggested client reconnect delay in milliseconds.",
|
|
86
86
|
'- `event: hello`: sent first. Data is `{"connected": <bool>}`, whether the server\'s live-event connection is up.',
|
|
87
87
|
"- `event: run`, `event: service`, `event: outcome`: a run, coding-run service, or outcome changed. Each carries an `id:` line. Payloads match the `viewer-event` schema (see `x-event-schema`).",
|
|
88
|
+
"- `event: step`: a run recorded a step, or a step's status arrived. Carries an `id:` line. Data: the step's `seq`, `stepKind` (`tool` or `said`), `tool`, `summary` (redacted when it was recorded), `status` and `at`, never tool output.",
|
|
88
89
|
'- `event: status`: the server\'s live-event connection changed. Data is `{"connected": <bool>}`.',
|
|
89
90
|
"- `event: resync`: the live-event connection is up (again). Events may have been missed: refetch the graph.",
|
|
90
91
|
`- \`: ping\`: comment sent every ${SSE_HEARTBEAT_MS / 1000} seconds to keep the connection open.`,
|
|
@@ -2,4 +2,6 @@
|
|
|
2
2
|
import type { PrismaClient } from "#prisma";
|
|
3
3
|
import type { RunDetail } from "./api-schema.js";
|
|
4
4
|
import { type IssueSites } from "./graph.js";
|
|
5
|
+
/** The most steps a run detail carries: the newest, oldest first. */
|
|
6
|
+
export declare const RUN_DETAIL_STEPS = 200;
|
|
5
7
|
export declare function loadRunDetail(db: PrismaClient, runId: string, issueSites?: IssueSites): Promise<RunDetail | null>;
|
|
@@ -11,16 +11,19 @@ function serviceDetails(snapshot) {
|
|
|
11
11
|
envNames: isRecord(s.testEnv) ? Object.keys(s.testEnv).sort() : [],
|
|
12
12
|
}));
|
|
13
13
|
}
|
|
14
|
+
/** The most steps a run detail carries: the newest, oldest first. */
|
|
15
|
+
export const RUN_DETAIL_STEPS = 200;
|
|
14
16
|
export async function loadRunDetail(db, runId, issueSites = {}) {
|
|
15
17
|
const [graphRun] = await loadGraphRuns(db, [runId], issueSites);
|
|
16
18
|
if (!graphRun)
|
|
17
19
|
return null;
|
|
18
|
-
const [row, children] = await Promise.all([
|
|
20
|
+
const [row, children, newest, stepCount] = await Promise.all([
|
|
19
21
|
db.run.findUniqueOrThrow({
|
|
20
22
|
where: { id: runId },
|
|
21
23
|
select: {
|
|
22
24
|
error: true,
|
|
23
25
|
finalText: true,
|
|
26
|
+
stepsDropped: true,
|
|
24
27
|
codingRun: {
|
|
25
28
|
select: {
|
|
26
29
|
provider: true,
|
|
@@ -39,13 +42,35 @@ export async function loadRunDetail(db, runId, issueSites = {}) {
|
|
|
39
42
|
select: { id: true },
|
|
40
43
|
orderBy: [{ startedAt: "asc" }, { id: "asc" }],
|
|
41
44
|
}),
|
|
45
|
+
// Stored already redacted; the call id and the detail text are never selected.
|
|
46
|
+
db.runStep.findMany({
|
|
47
|
+
where: { runId },
|
|
48
|
+
orderBy: { seq: "desc" },
|
|
49
|
+
take: RUN_DETAIL_STEPS,
|
|
50
|
+
select: { seq: true, at: true, kind: true, tool: true, summary: true, status: true },
|
|
51
|
+
}),
|
|
52
|
+
db.runStep.count({ where: { runId } }),
|
|
42
53
|
]);
|
|
54
|
+
const steps = newest
|
|
55
|
+
.reverse()
|
|
56
|
+
.filter((s) => s.kind === "tool" || s.kind === "said")
|
|
57
|
+
.map((s) => ({
|
|
58
|
+
seq: s.seq,
|
|
59
|
+
at: s.at.toISOString(),
|
|
60
|
+
kind: s.kind,
|
|
61
|
+
tool: s.tool,
|
|
62
|
+
summary: s.summary,
|
|
63
|
+
status: s.status,
|
|
64
|
+
}));
|
|
43
65
|
const coding = row.codingRun;
|
|
44
66
|
return {
|
|
45
67
|
...graphRun,
|
|
46
68
|
error: row.error,
|
|
47
69
|
finalText: row.finalText,
|
|
48
70
|
childRunIds: children.map((c) => c.id),
|
|
71
|
+
steps,
|
|
72
|
+
stepsOmitted: Math.max(0, stepCount - newest.length),
|
|
73
|
+
stepsDropped: row.stepsDropped,
|
|
49
74
|
coding: coding
|
|
50
75
|
? {
|
|
51
76
|
provider: coding.provider,
|