@oscharko-dev/keiko-server 0.2.7 → 0.2.9
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/dist/.tsbuildinfo +1 -1
- package/dist/chat-handlers.d.ts +18 -2
- package/dist/chat-handlers.d.ts.map +1 -1
- package/dist/chat-handlers.js +185 -3
- package/dist/command-runner-errors.d.ts +17 -0
- package/dist/command-runner-errors.d.ts.map +1 -0
- package/dist/command-runner-errors.js +37 -0
- package/dist/command-runner-evidence.d.ts +23 -0
- package/dist/command-runner-evidence.d.ts.map +1 -0
- package/dist/command-runner-evidence.js +69 -0
- package/dist/command-runner-routes.d.ts +7 -0
- package/dist/command-runner-routes.d.ts.map +1 -0
- package/dist/command-runner-routes.js +175 -0
- package/dist/command-runner.d.ts +29 -0
- package/dist/command-runner.d.ts.map +1 -0
- package/dist/command-runner.js +348 -0
- package/dist/conversation-prompt.d.ts +2 -2
- package/dist/conversation-prompt.d.ts.map +1 -1
- package/dist/conversation-prompt.js +17 -1
- package/dist/csp.d.ts.map +1 -1
- package/dist/csp.js +3 -0
- package/dist/deps.d.ts +28 -1
- package/dist/deps.d.ts.map +1 -1
- package/dist/deps.js +288 -13
- package/dist/discussion-prompt.d.ts +4 -0
- package/dist/discussion-prompt.d.ts.map +1 -0
- package/dist/discussion-prompt.js +19 -0
- package/dist/editor/agentActionAudit.d.ts +18 -0
- package/dist/editor/agentActionAudit.d.ts.map +1 -0
- package/dist/editor/agentActionAudit.js +80 -0
- package/dist/editor/agentRoutes.d.ts +1 -0
- package/dist/editor/agentRoutes.d.ts.map +1 -1
- package/dist/editor/agentRoutes.js +292 -55
- package/dist/editor/agentSessionRegistry.d.ts +35 -0
- package/dist/editor/agentSessionRegistry.d.ts.map +1 -0
- package/dist/editor/agentSessionRegistry.js +243 -0
- package/dist/editor/completionRoutes.d.ts.map +1 -1
- package/dist/editor/completionRoutes.js +5 -10
- package/dist/editor/languageRoutes.d.ts +12 -1
- package/dist/editor/languageRoutes.d.ts.map +1 -1
- package/dist/editor/languageRoutes.js +71 -8
- package/dist/editor/languageService.d.ts +3 -2
- package/dist/editor/languageService.d.ts.map +1 -1
- package/dist/editor/languageService.js +41 -3
- package/dist/editor/languageServiceHost.d.ts.map +1 -1
- package/dist/editor/languageServiceHost.js +2 -2
- package/dist/editor/lsp/hostLanguageOperation.d.ts +17 -0
- package/dist/editor/lsp/hostLanguageOperation.d.ts.map +1 -0
- package/dist/editor/lsp/hostLanguageOperation.js +436 -0
- package/dist/editor/lsp/hostLanguageProviders.d.ts +26 -0
- package/dist/editor/lsp/hostLanguageProviders.d.ts.map +1 -0
- package/dist/editor/lsp/hostLanguageProviders.js +161 -0
- package/dist/editor/lsp/lspFrameCodec.d.ts +13 -0
- package/dist/editor/lsp/lspFrameCodec.d.ts.map +1 -0
- package/dist/editor/lsp/lspFrameCodec.js +164 -0
- package/dist/editor/lsp/lspJsonRpcClient.d.ts +34 -0
- package/dist/editor/lsp/lspJsonRpcClient.d.ts.map +1 -0
- package/dist/editor/lsp/lspJsonRpcClient.js +173 -0
- package/dist/editor/lsp/lspLanguageProvider.d.ts +7 -0
- package/dist/editor/lsp/lspLanguageProvider.d.ts.map +1 -0
- package/dist/editor/lsp/lspLanguageProvider.js +29 -0
- package/dist/editor/lsp/lspLifecycleLedger.d.ts +5 -0
- package/dist/editor/lsp/lspLifecycleLedger.d.ts.map +1 -0
- package/dist/editor/lsp/lspLifecycleLedger.js +37 -0
- package/dist/editor/lsp/lspNodeAdapter.d.ts +31 -0
- package/dist/editor/lsp/lspNodeAdapter.d.ts.map +1 -0
- package/dist/editor/lsp/lspNodeAdapter.js +230 -0
- package/dist/editor/lsp/lspProcessManager.d.ts +24 -0
- package/dist/editor/lsp/lspProcessManager.d.ts.map +1 -0
- package/dist/editor/lsp/lspProcessManager.js +255 -0
- package/dist/editor/lsp/lspRestartThrottle.d.ts +6 -0
- package/dist/editor/lsp/lspRestartThrottle.d.ts.map +1 -0
- package/dist/editor/lsp/lspRestartThrottle.js +24 -0
- package/dist/editor/lsp/lspStatusRoute.d.ts +8 -0
- package/dist/editor/lsp/lspStatusRoute.d.ts.map +1 -0
- package/dist/editor/lsp/lspStatusRoute.js +22 -0
- package/dist/editor/lsp/lspTransport.d.ts +19 -0
- package/dist/editor/lsp/lspTransport.d.ts.map +1 -0
- package/dist/editor/lsp/lspTransport.js +55 -0
- package/dist/editor/lsp/testing/fakeLspProcess.d.ts +23 -0
- package/dist/editor/lsp/testing/fakeLspProcess.d.ts.map +1 -0
- package/dist/editor/lsp/testing/fakeLspProcess.js +132 -0
- package/dist/files.d.ts +63 -0
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +799 -1
- package/dist/gateway-readiness.d.ts +6 -0
- package/dist/gateway-readiness.d.ts.map +1 -0
- package/dist/gateway-readiness.js +624 -0
- package/dist/gateway-setup.d.ts +2 -0
- package/dist/gateway-setup.d.ts.map +1 -1
- package/dist/gateway-setup.js +275 -11
- package/dist/gitDelivery/actionSheetProjection.d.ts +30 -0
- package/dist/gitDelivery/actionSheetProjection.d.ts.map +1 -0
- package/dist/gitDelivery/actionSheetProjection.js +206 -0
- package/dist/gitDelivery/actionSheetRoutes.d.ts +29 -0
- package/dist/gitDelivery/actionSheetRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/actionSheetRoutes.js +293 -0
- package/dist/gitDelivery/agentOperationsRoutes.d.ts +33 -0
- package/dist/gitDelivery/agentOperationsRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/agentOperationsRoutes.js +405 -0
- package/dist/gitDelivery/commitRoutes.d.ts +23 -0
- package/dist/gitDelivery/commitRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/commitRoutes.js +204 -0
- package/dist/gitDelivery/evidenceRoutes.d.ts +9 -0
- package/dist/gitDelivery/evidenceRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/evidenceRoutes.js +101 -0
- package/dist/gitDelivery/execution.d.ts +38 -0
- package/dist/gitDelivery/execution.d.ts.map +1 -0
- package/dist/gitDelivery/execution.js +117 -0
- package/dist/gitDelivery/localMutationRoutes.d.ts +30 -0
- package/dist/gitDelivery/localMutationRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/localMutationRoutes.js +165 -0
- package/dist/gitDelivery/mergeExecution.d.ts +63 -0
- package/dist/gitDelivery/mergeExecution.d.ts.map +1 -0
- package/dist/gitDelivery/mergeExecution.js +168 -0
- package/dist/gitDelivery/mergeRoutes.d.ts +12 -0
- package/dist/gitDelivery/mergeRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/mergeRoutes.js +218 -0
- package/dist/gitDelivery/mutationEvidenceLedger.d.ts +23 -0
- package/dist/gitDelivery/mutationEvidenceLedger.d.ts.map +1 -0
- package/dist/gitDelivery/mutationEvidenceLedger.js +87 -0
- package/dist/gitDelivery/prExecution.d.ts +54 -0
- package/dist/gitDelivery/prExecution.d.ts.map +1 -0
- package/dist/gitDelivery/prExecution.js +192 -0
- package/dist/gitDelivery/prRoutes.d.ts +12 -0
- package/dist/gitDelivery/prRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/prRoutes.js +256 -0
- package/dist/gitDelivery/pushExecution.d.ts +43 -0
- package/dist/gitDelivery/pushExecution.d.ts.map +1 -0
- package/dist/gitDelivery/pushExecution.js +124 -0
- package/dist/gitDelivery/pushRoutes.d.ts +12 -0
- package/dist/gitDelivery/pushRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/pushRoutes.js +200 -0
- package/dist/gitDelivery/requestGuards.d.ts +15 -0
- package/dist/gitDelivery/requestGuards.d.ts.map +1 -0
- package/dist/gitDelivery/requestGuards.js +97 -0
- package/dist/gitDelivery/syncEvidence.d.ts +37 -0
- package/dist/gitDelivery/syncEvidence.d.ts.map +1 -0
- package/dist/gitDelivery/syncEvidence.js +85 -0
- package/dist/gitDelivery/syncExecution.d.ts +30 -0
- package/dist/gitDelivery/syncExecution.d.ts.map +1 -0
- package/dist/gitDelivery/syncExecution.js +266 -0
- package/dist/gitDelivery/syncRoutes.d.ts +13 -0
- package/dist/gitDelivery/syncRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/syncRoutes.js +200 -0
- package/dist/gitPorcelainStatus.d.ts +15 -0
- package/dist/gitPorcelainStatus.d.ts.map +1 -0
- package/dist/gitPorcelainStatus.js +104 -0
- package/dist/gitRepositoryReads.d.ts +10 -0
- package/dist/gitRepositoryReads.d.ts.map +1 -0
- package/dist/gitRepositoryReads.js +314 -0
- package/dist/gitRepositoryRoutes.d.ts +7 -0
- package/dist/gitRepositoryRoutes.d.ts.map +1 -0
- package/dist/gitRepositoryRoutes.js +221 -0
- package/dist/gitRoutes.d.ts +66 -0
- package/dist/gitRoutes.d.ts.map +1 -0
- package/dist/gitRoutes.js +543 -0
- package/dist/governed-workflow.d.ts +2 -0
- package/dist/governed-workflow.d.ts.map +1 -1
- package/dist/governed-workflow.js +4 -0
- package/dist/grounded-qa-hybrid.d.ts.map +1 -1
- package/dist/grounded-qa-hybrid.js +2 -0
- package/dist/grounded-qa-multi-source.d.ts.map +1 -1
- package/dist/grounded-qa-multi-source.js +1 -0
- package/dist/grounded-qa.d.ts +11 -0
- package/dist/grounded-qa.d.ts.map +1 -1
- package/dist/grounded-qa.js +14 -4
- package/dist/headers.d.ts +4 -1
- package/dist/headers.d.ts.map +1 -1
- package/dist/headers.js +11 -4
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/local-knowledge-grounded-qa.d.ts.map +1 -1
- package/dist/local-knowledge-grounded-qa.js +11 -2
- package/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts +1 -1
- package/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts.map +1 -1
- package/dist/qualityIntelligence/figmaSnapshotRoutes.js +1 -1
- package/dist/read-handlers.d.ts +5 -0
- package/dist/read-handlers.d.ts.map +1 -1
- package/dist/read-handlers.js +57 -1
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +260 -12
- package/dist/run-engine.d.ts.map +1 -1
- package/dist/run-engine.js +3 -0
- package/dist/run-handlers.d.ts +0 -1
- package/dist/run-handlers.d.ts.map +1 -1
- package/dist/run-handlers.js +64 -211
- package/dist/run-request.d.ts +11 -0
- package/dist/run-request.d.ts.map +1 -1
- package/dist/run-request.js +158 -10
- package/dist/runtime/capabilityDetector.d.ts +38 -0
- package/dist/runtime/capabilityDetector.d.ts.map +1 -0
- package/dist/runtime/capabilityDetector.js +443 -0
- package/dist/runtime/capabilityRoutes.d.ts +9 -0
- package/dist/runtime/capabilityRoutes.d.ts.map +1 -0
- package/dist/runtime/capabilityRoutes.js +45 -0
- package/dist/runtime/containerEngineDetector.d.ts +17 -0
- package/dist/runtime/containerEngineDetector.d.ts.map +1 -0
- package/dist/runtime/containerEngineDetector.js +222 -0
- package/dist/runtime/containerRoutes.d.ts +8 -0
- package/dist/runtime/containerRoutes.d.ts.map +1 -0
- package/dist/runtime/containerRoutes.js +207 -0
- package/dist/runtime/containerRunner-errors.d.ts +18 -0
- package/dist/runtime/containerRunner-errors.d.ts.map +1 -0
- package/dist/runtime/containerRunner-errors.js +42 -0
- package/dist/runtime/containerRunner-evidence.d.ts +24 -0
- package/dist/runtime/containerRunner-evidence.d.ts.map +1 -0
- package/dist/runtime/containerRunner-evidence.js +74 -0
- package/dist/runtime/containerRunner.d.ts +37 -0
- package/dist/runtime/containerRunner.d.ts.map +1 -0
- package/dist/runtime/containerRunner.js +443 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +24 -4
- package/dist/store/db.d.ts.map +1 -1
- package/dist/store/db.js +2 -1
- package/dist/store/index.d.ts +1 -1
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/messages.d.ts +2 -1
- package/dist/store/messages.d.ts.map +1 -1
- package/dist/store/messages.js +46 -4
- package/dist/store/schema.d.ts +1 -1
- package/dist/store/schema.d.ts.map +1 -1
- package/dist/store/schema.js +68 -1
- package/dist/store/types.d.ts +3 -2
- package/dist/store/types.d.ts.map +1 -1
- package/dist/task-workspace/active-store.d.ts +21 -0
- package/dist/task-workspace/active-store.d.ts.map +1 -0
- package/dist/task-workspace/active-store.js +55 -0
- package/dist/task-workspace/authorization.d.ts +7 -0
- package/dist/task-workspace/authorization.d.ts.map +1 -0
- package/dist/task-workspace/authorization.js +54 -0
- package/dist/task-workspace/binding.d.ts +3 -0
- package/dist/task-workspace/binding.d.ts.map +1 -0
- package/dist/task-workspace/binding.js +22 -0
- package/dist/task-workspace/cleanup.d.ts +4 -0
- package/dist/task-workspace/cleanup.d.ts.map +1 -0
- package/dist/task-workspace/cleanup.js +428 -0
- package/dist/task-workspace/errors.d.ts +14 -0
- package/dist/task-workspace/errors.d.ts.map +1 -0
- package/dist/task-workspace/errors.js +81 -0
- package/dist/task-workspace/evidence.d.ts +32 -0
- package/dist/task-workspace/evidence.d.ts.map +1 -0
- package/dist/task-workspace/evidence.js +52 -0
- package/dist/task-workspace/field-safety.d.ts +3 -0
- package/dist/task-workspace/field-safety.d.ts.map +1 -0
- package/dist/task-workspace/field-safety.js +42 -0
- package/dist/task-workspace/health.d.ts +4 -0
- package/dist/task-workspace/health.d.ts.map +1 -0
- package/dist/task-workspace/health.js +163 -0
- package/dist/task-workspace/lifecycle.d.ts +3 -0
- package/dist/task-workspace/lifecycle.d.ts.map +1 -0
- package/dist/task-workspace/lifecycle.js +248 -0
- package/dist/task-workspace/locks.d.ts +13 -0
- package/dist/task-workspace/locks.d.ts.map +1 -0
- package/dist/task-workspace/locks.js +44 -0
- package/dist/task-workspace/managed-root.d.ts +7 -0
- package/dist/task-workspace/managed-root.d.ts.map +1 -0
- package/dist/task-workspace/managed-root.js +98 -0
- package/dist/task-workspace/mutex.d.ts +8 -0
- package/dist/task-workspace/mutex.d.ts.map +1 -0
- package/dist/task-workspace/mutex.js +82 -0
- package/dist/task-workspace/naming.d.ts +15 -0
- package/dist/task-workspace/naming.d.ts.map +1 -0
- package/dist/task-workspace/naming.js +0 -0
- package/dist/task-workspace/provisioning.d.ts +3 -0
- package/dist/task-workspace/provisioning.d.ts.map +1 -0
- package/dist/task-workspace/provisioning.js +528 -0
- package/dist/task-workspace/reconciliation.d.ts +15 -0
- package/dist/task-workspace/reconciliation.d.ts.map +1 -0
- package/dist/task-workspace/reconciliation.js +274 -0
- package/dist/task-workspace/repair.d.ts +3 -0
- package/dist/task-workspace/repair.d.ts.map +1 -0
- package/dist/task-workspace/repair.js +286 -0
- package/dist/task-workspace/routes.d.ts +19 -0
- package/dist/task-workspace/routes.d.ts.map +1 -0
- package/dist/task-workspace/routes.js +481 -0
- package/dist/task-workspace/store.d.ts +12 -0
- package/dist/task-workspace/store.d.ts.map +1 -0
- package/dist/task-workspace/store.js +128 -0
- package/dist/task-workspace/types.d.ts +170 -0
- package/dist/task-workspace/types.d.ts.map +1 -0
- package/dist/task-workspace/types.js +5 -0
- package/dist/voice-action-governance.d.ts +23 -0
- package/dist/voice-action-governance.d.ts.map +1 -0
- package/dist/voice-action-governance.js +126 -0
- package/dist/voice-handlers.d.ts +6 -0
- package/dist/voice-handlers.d.ts.map +1 -0
- package/dist/voice-handlers.js +570 -0
- package/dist/voice-realtime-grounded-tool.d.ts +31 -0
- package/dist/voice-realtime-grounded-tool.d.ts.map +1 -0
- package/dist/voice-realtime-grounded-tool.js +322 -0
- package/dist/voice-realtime.d.ts +69 -0
- package/dist/voice-realtime.d.ts.map +1 -0
- package/dist/voice-realtime.js +787 -0
- package/dist/workspace-state-handlers.d.ts +5 -0
- package/dist/workspace-state-handlers.d.ts.map +1 -0
- package/dist/workspace-state-handlers.js +106 -0
- package/package.json +20 -19
- package/dist/grounded-handoff.d.ts +0 -4
- package/dist/grounded-handoff.d.ts.map +0 -1
- package/dist/grounded-handoff.js +0 -445
package/dist/run-handlers.js
CHANGED
|
@@ -5,17 +5,19 @@
|
|
|
5
5
|
// redacted final report projection (or status:"running"); apply is the ONLY write path, re-invoking
|
|
6
6
|
// the same workflow with apply:true through the existing gated path. No model is ever called
|
|
7
7
|
// directly; no guard is reimplemented; no secret reaches any response (live payloads are redacted).
|
|
8
|
-
import { randomUUID } from "node:crypto";
|
|
9
8
|
import { parseRunRequest } from "./run-request.js";
|
|
10
9
|
import { startRun, applyRun } from "./run-engine.js";
|
|
11
10
|
import { ActiveRunLimitError } from "./runs.js";
|
|
12
11
|
import { SSE_HEADERS, writeEvent, readyMessage } from "./sse.js";
|
|
13
12
|
import { errorBody, STREAMING } from "./routes.js";
|
|
14
13
|
import { currentRedactionSecrets } from "./deps.js";
|
|
15
|
-
import {
|
|
14
|
+
import { VOICE_TRANSCRIPT_SCHEMA_VERSION, } from "@oscharko-dev/keiko-contracts";
|
|
15
|
+
import { validateWorkflowHandoffRequest } from "@oscharko-dev/keiko-contracts/workflow-handoff";
|
|
16
16
|
import { WorkspaceError } from "@oscharko-dev/keiko-workspace";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
17
|
+
import { approvalTokenInputFor, createApprovalToken } from "./governed-workflow.js";
|
|
18
|
+
import { isVoiceDictationCapable, isVoiceRealtimeCapable } from "./read-handlers.js";
|
|
19
|
+
import { evaluateSpokenActionGovernance } from "./voice-action-governance.js";
|
|
20
|
+
import { resolveRegisteredOrManagedWorkspaceRoot } from "./task-workspace/authorization.js";
|
|
19
21
|
const MAX_BODY_BYTES = 1_000_000;
|
|
20
22
|
const VERIFY_NOOP_MODEL = {
|
|
21
23
|
call: () => Promise.reject(new Error("verify runs must not call the model")),
|
|
@@ -64,7 +66,7 @@ function readBody(req) {
|
|
|
64
66
|
// directories. Returns a RouteResult to return, or null when the check passes.
|
|
65
67
|
function rejectUnregisteredWorkspace(parsed, deps) {
|
|
66
68
|
const root = typeof parsed.input.workspaceRoot === "string" ? parsed.input.workspaceRoot : "";
|
|
67
|
-
const registered = deps
|
|
69
|
+
const registered = resolveRegisteredOrManagedWorkspaceRoot(deps, root) !== undefined;
|
|
68
70
|
return registered
|
|
69
71
|
? null
|
|
70
72
|
: {
|
|
@@ -75,111 +77,68 @@ function rejectUnregisteredWorkspace(parsed, deps) {
|
|
|
75
77
|
function resolveRunModel(parsed, deps) {
|
|
76
78
|
return parsed.kind === "verify" ? VERIFY_NOOP_MODEL : deps.modelPortFactory(parsed.modelId);
|
|
77
79
|
}
|
|
78
|
-
function
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
function requireBodyString(body, name) {
|
|
82
|
-
const value = body[name];
|
|
83
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
84
|
-
return { status: 400, body: errorBody("BAD_REQUEST", `Field "${name}" is required.`) };
|
|
80
|
+
function validateTextGovernedHandoff(parsed) {
|
|
81
|
+
if (parsed.governedHandoff === undefined || parsed.governedHandoffVoiceOrigin !== undefined) {
|
|
82
|
+
return null;
|
|
85
83
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
const validation = validateWorkflowHandoffRequest(parsed.governedHandoff);
|
|
85
|
+
if (!validation.ok) {
|
|
86
|
+
return { status: 400, body: errorBody("BAD_REQUEST", validation.reasons.join("; ")) };
|
|
87
|
+
}
|
|
88
|
+
const expectedToken = createApprovalToken(approvalTokenInputFor(parsed.governedHandoff));
|
|
89
|
+
if (parsed.governedHandoff.userApprovalToken !== expectedToken) {
|
|
91
90
|
return {
|
|
92
91
|
status: 400,
|
|
93
|
-
body: errorBody("BAD_REQUEST",
|
|
92
|
+
body: errorBody("BAD_REQUEST", "governedHandoff approval token does not match the request."),
|
|
94
93
|
};
|
|
95
94
|
}
|
|
96
|
-
return
|
|
95
|
+
return null;
|
|
97
96
|
}
|
|
98
|
-
function
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return { status: 400, body: errorBody("BAD_REQUEST", `Field "${name}" must be an object.`) };
|
|
102
|
-
}
|
|
103
|
-
return value;
|
|
104
|
-
}
|
|
105
|
-
function parseJsonRecord(raw) {
|
|
106
|
-
let parsed;
|
|
107
|
-
try {
|
|
108
|
-
parsed = JSON.parse(raw);
|
|
109
|
-
}
|
|
110
|
-
catch {
|
|
111
|
-
return { status: 400, body: errorBody("BAD_REQUEST", "Request body is not valid JSON.") };
|
|
97
|
+
function serverTrustedVoiceProfile(deps) {
|
|
98
|
+
if (isVoiceRealtimeCapable(deps)) {
|
|
99
|
+
return "full-realtime";
|
|
112
100
|
}
|
|
113
|
-
if (
|
|
114
|
-
return
|
|
101
|
+
if (isVoiceDictationCapable(deps)) {
|
|
102
|
+
return "speech-to-text";
|
|
115
103
|
}
|
|
116
|
-
return
|
|
104
|
+
return "none";
|
|
117
105
|
}
|
|
118
|
-
function
|
|
119
|
-
return
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
106
|
+
function committedProjectionFor(origin) {
|
|
107
|
+
return {
|
|
108
|
+
schemaVersion: VOICE_TRANSCRIPT_SCHEMA_VERSION,
|
|
109
|
+
segments: [],
|
|
110
|
+
text: origin.committedText,
|
|
111
|
+
segmentCount: origin.committedSegments,
|
|
112
|
+
};
|
|
123
113
|
}
|
|
124
|
-
function
|
|
125
|
-
if (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
114
|
+
function applyVoiceGovernance(parsed, deps) {
|
|
115
|
+
if (parsed.governedHandoffVoiceOrigin === undefined) {
|
|
116
|
+
const invalidTextHandoff = validateTextGovernedHandoff(parsed);
|
|
117
|
+
return invalidTextHandoff ?? parsed;
|
|
118
|
+
}
|
|
119
|
+
if (parsed.governedHandoff === undefined) {
|
|
120
|
+
return { status: 400, body: errorBody("BAD_REQUEST", "voiceOrigin requires governedHandoff.") };
|
|
121
|
+
}
|
|
122
|
+
const trustedProfile = serverTrustedVoiceProfile(deps);
|
|
123
|
+
const decision = evaluateSpokenActionGovernance({
|
|
124
|
+
projection: committedProjectionFor(parsed.governedHandoffVoiceOrigin),
|
|
125
|
+
profile: trustedProfile,
|
|
126
|
+
turnIndex: parsed.governedHandoffVoiceOrigin.turnIndex,
|
|
127
|
+
source: parsed.governedHandoffVoiceOrigin.source,
|
|
128
|
+
request: parsed.governedHandoff,
|
|
129
|
+
providedConfirmationDigest: parsed.governedHandoffVoiceOrigin.confirmationDigest,
|
|
130
|
+
});
|
|
131
|
+
if (!decision.allowed) {
|
|
132
|
+
return { status: 403, body: errorBody("VOICE_ACTION_DENIED", decision.reason) };
|
|
133
133
|
}
|
|
134
|
-
return {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
return user;
|
|
140
|
-
const summary = requireBodyRecord(body, "summary");
|
|
141
|
-
if (isRouteResult(summary))
|
|
142
|
-
return summary;
|
|
143
|
-
const userContent = requireBodyString(user, "content");
|
|
144
|
-
if (typeof userContent !== "string")
|
|
145
|
-
return userContent;
|
|
146
|
-
const userTimestamp = requireBodyNumber(user, "timestamp");
|
|
147
|
-
if (typeof userTimestamp !== "number")
|
|
148
|
-
return userTimestamp;
|
|
149
|
-
const summaryContent = requireBodyString(summary, "content");
|
|
150
|
-
if (typeof summaryContent !== "string")
|
|
151
|
-
return summaryContent;
|
|
152
|
-
const summaryTimestamp = requireBodyNumber(summary, "timestamp");
|
|
153
|
-
if (typeof summaryTimestamp !== "number")
|
|
154
|
-
return summaryTimestamp;
|
|
155
|
-
const discriminator = runSummaryDiscriminator(request);
|
|
156
|
-
return [
|
|
157
|
-
{
|
|
158
|
-
chatId,
|
|
159
|
-
role: "user",
|
|
160
|
-
content: userContent,
|
|
161
|
-
timestamp: userTimestamp,
|
|
162
|
-
runId: undefined,
|
|
163
|
-
workflowId: undefined,
|
|
164
|
-
workflowStatus: undefined,
|
|
165
|
-
shortResult: undefined,
|
|
166
|
-
taskType: undefined,
|
|
167
|
-
},
|
|
168
|
-
{
|
|
169
|
-
chatId,
|
|
170
|
-
role: "system",
|
|
171
|
-
content: summaryContent,
|
|
172
|
-
timestamp: summaryTimestamp,
|
|
173
|
-
runId,
|
|
174
|
-
workflowId: discriminator.workflowId,
|
|
175
|
-
workflowStatus: "running",
|
|
176
|
-
shortResult: undefined,
|
|
177
|
-
taskType: discriminator.taskType,
|
|
178
|
-
},
|
|
179
|
-
];
|
|
134
|
+
return {
|
|
135
|
+
...parsed,
|
|
136
|
+
governedHandoffVoiceOrigin: undefined,
|
|
137
|
+
governedHandoffVoiceAction: decision.audit,
|
|
138
|
+
};
|
|
180
139
|
}
|
|
181
|
-
function
|
|
182
|
-
return
|
|
140
|
+
function isRecord(value) {
|
|
141
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
183
142
|
}
|
|
184
143
|
// Static, path-safe message for workspace errors surfaced during run launch. The underlying
|
|
185
144
|
// WorkspaceError messages may carry absolute paths — we never echo them (ADR-0005, CWE-209).
|
|
@@ -190,86 +149,6 @@ function workspaceRunErrorResult() {
|
|
|
190
149
|
body: errorBody("WORKSPACE_UNAVAILABLE", WORKSPACE_RUN_ERROR_MESSAGE),
|
|
191
150
|
};
|
|
192
151
|
}
|
|
193
|
-
function markSummaryFailed(deps, message, shortResult) {
|
|
194
|
-
try {
|
|
195
|
-
deps.store.updateMessage(message.id, { workflowStatus: "failed", shortResult });
|
|
196
|
-
}
|
|
197
|
-
catch {
|
|
198
|
-
// Best-effort compensation only. The original start error remains the response source.
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
function parseChatRunEnvelope(raw, deps) {
|
|
202
|
-
const body = parseJsonRecord(raw);
|
|
203
|
-
if (isRouteResult(body))
|
|
204
|
-
return body;
|
|
205
|
-
const chatId = requireBodyString(body, "chatId");
|
|
206
|
-
if (isRouteResult(chatId))
|
|
207
|
-
return chatId;
|
|
208
|
-
const projectPath = requireBodyString(body, "projectPath");
|
|
209
|
-
if (isRouteResult(projectPath))
|
|
210
|
-
return projectPath;
|
|
211
|
-
if (!chatBelongsToProject(deps, projectPath, chatId)) {
|
|
212
|
-
return { status: 404, body: errorBody("NOT_FOUND", "Chat not found.") };
|
|
213
|
-
}
|
|
214
|
-
const runBody = requireBodyRecord(body, "run");
|
|
215
|
-
if (isRouteResult(runBody))
|
|
216
|
-
return runBody;
|
|
217
|
-
return { body, chatId, projectPath, runBody };
|
|
218
|
-
}
|
|
219
|
-
function validateChatRunRequest(runBody, deps) {
|
|
220
|
-
const parsed = parseRunRequest(JSON.stringify(runBody));
|
|
221
|
-
if ("code" in parsed) {
|
|
222
|
-
return { status: 400, body: errorBody(parsed.code, parsed.message) };
|
|
223
|
-
}
|
|
224
|
-
const unregistered = rejectUnregisteredWorkspace(parsed, deps);
|
|
225
|
-
if (unregistered !== null)
|
|
226
|
-
return unregistered;
|
|
227
|
-
const model = resolveRunModel(parsed, deps);
|
|
228
|
-
return model === undefined
|
|
229
|
-
? { status: 400, body: errorBody("NO_MODEL", "No model provider is configured.") }
|
|
230
|
-
: { request: parsed, model };
|
|
231
|
-
}
|
|
232
|
-
function engineContextFor(deps, request, model) {
|
|
233
|
-
return {
|
|
234
|
-
request,
|
|
235
|
-
model,
|
|
236
|
-
registry: deps.registry,
|
|
237
|
-
evidence: {
|
|
238
|
-
store: deps.evidenceStore,
|
|
239
|
-
env: deps.env,
|
|
240
|
-
additionalSecrets: currentRedactionSecrets(deps),
|
|
241
|
-
},
|
|
242
|
-
memoryVault: deps.memoryVault,
|
|
243
|
-
memoryAuditRedactString: createAuditRedactor({ additionalSecrets: currentRedactionSecrets(deps) }, deps.env),
|
|
244
|
-
memoryCustomerIdentifierMatchers: memoryCaptureCustomerMatchers(deps),
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
function persistChatRunMessages(deps, envelope, request, runId) {
|
|
248
|
-
const messagesInput = buildChatRunMessages(envelope.body, request, envelope.chatId, runId);
|
|
249
|
-
if (isRouteResult(messagesInput))
|
|
250
|
-
return messagesInput;
|
|
251
|
-
try {
|
|
252
|
-
return deps.store.createMessages(messagesInput);
|
|
253
|
-
}
|
|
254
|
-
catch (error) {
|
|
255
|
-
if (error instanceof UiStoreError)
|
|
256
|
-
return storeErrorResult(error);
|
|
257
|
-
throw error;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
function startPersistedChatRun(deps, request, model, runId, messages) {
|
|
261
|
-
try {
|
|
262
|
-
const run = startRun(engineContextFor(deps, request, model), deps.redactor, { runId });
|
|
263
|
-
return { status: 202, body: { run, messages } };
|
|
264
|
-
}
|
|
265
|
-
catch (error) {
|
|
266
|
-
const summary = messages[1];
|
|
267
|
-
if (summary !== undefined) {
|
|
268
|
-
markSummaryFailed(deps, summary, "Run could not be started.");
|
|
269
|
-
}
|
|
270
|
-
return mapRunStartError(error);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
152
|
function mapRunStartError(error) {
|
|
274
153
|
if (error instanceof ActiveRunLimitError) {
|
|
275
154
|
return { status: 429, body: errorBody("TOO_MANY_RUNS", "The active run limit is reached.") };
|
|
@@ -298,16 +177,20 @@ export async function handleCreateRun(ctx, deps) {
|
|
|
298
177
|
if ("code" in parsed) {
|
|
299
178
|
return { status: 400, body: errorBody(parsed.code, parsed.message) };
|
|
300
179
|
}
|
|
301
|
-
const
|
|
180
|
+
const governed = applyVoiceGovernance(parsed, deps);
|
|
181
|
+
if ("status" in governed) {
|
|
182
|
+
return governed;
|
|
183
|
+
}
|
|
184
|
+
const unregistered = rejectUnregisteredWorkspace(governed, deps);
|
|
302
185
|
if (unregistered !== null) {
|
|
303
186
|
return unregistered;
|
|
304
187
|
}
|
|
305
|
-
const model = resolveRunModel(
|
|
188
|
+
const model = resolveRunModel(governed, deps);
|
|
306
189
|
if (model === undefined) {
|
|
307
190
|
return { status: 400, body: errorBody("NO_MODEL", "No model provider is configured.") };
|
|
308
191
|
}
|
|
309
192
|
const engineCtx = {
|
|
310
|
-
request:
|
|
193
|
+
request: governed,
|
|
311
194
|
model,
|
|
312
195
|
registry: deps.registry,
|
|
313
196
|
evidence: {
|
|
@@ -324,36 +207,6 @@ export async function handleCreateRun(ctx, deps) {
|
|
|
324
207
|
return mapRunStartError(error);
|
|
325
208
|
}
|
|
326
209
|
}
|
|
327
|
-
// Route — POST /api/chats/runs. Composer-specific path that makes Issue #66's chat invariant
|
|
328
|
-
// explicit: a successful workflow launch first reserves a runId and persists exactly one user
|
|
329
|
-
// message plus one system run summary, then starts the run with that reserved runId. If persistence
|
|
330
|
-
// fails, no run is started; if the start is refused, the summary is terminalized as failed.
|
|
331
|
-
export async function handleCreateChatRun(ctx, deps) {
|
|
332
|
-
let raw;
|
|
333
|
-
try {
|
|
334
|
-
raw = await readBody(ctx.req);
|
|
335
|
-
}
|
|
336
|
-
catch (error) {
|
|
337
|
-
if (error instanceof BodyTooLargeError) {
|
|
338
|
-
return {
|
|
339
|
-
status: 413,
|
|
340
|
-
body: errorBody("PAYLOAD_TOO_LARGE", "Request body exceeds the size limit."),
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
throw error;
|
|
344
|
-
}
|
|
345
|
-
const envelope = parseChatRunEnvelope(raw, deps);
|
|
346
|
-
if (isRouteResult(envelope))
|
|
347
|
-
return envelope;
|
|
348
|
-
const validated = validateChatRunRequest(envelope.runBody, deps);
|
|
349
|
-
if (isRouteResult(validated))
|
|
350
|
-
return validated;
|
|
351
|
-
const runId = randomUUID();
|
|
352
|
-
const messages = persistChatRunMessages(deps, envelope, validated.request, runId);
|
|
353
|
-
if (isRouteResult(messages))
|
|
354
|
-
return messages;
|
|
355
|
-
return startPersistedChatRun(deps, validated.request, validated.model, runId, messages);
|
|
356
|
-
}
|
|
357
210
|
function lastEventId(req) {
|
|
358
211
|
const header = req.headers["last-event-id"];
|
|
359
212
|
const value = Array.isArray(header) ? header[0] : header;
|
package/dist/run-request.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
import { type SpokenActionAuditRecord, type VoiceProfile, type VoiceTranscriptSource } from "@oscharko-dev/keiko-contracts";
|
|
1
2
|
import type { WorkflowHandoffRequest } from "@oscharko-dev/keiko-contracts/workflow-handoff";
|
|
2
3
|
export type RunKind = "unit-tests" | "bug-investigation" | "explain-plan" | "verify";
|
|
4
|
+
export interface RunVoiceOrigin {
|
|
5
|
+
readonly profile: VoiceProfile;
|
|
6
|
+
readonly turnIndex: number;
|
|
7
|
+
readonly source: VoiceTranscriptSource;
|
|
8
|
+
readonly committedSegments: number;
|
|
9
|
+
readonly committedText: string;
|
|
10
|
+
readonly confirmationDigest: string | undefined;
|
|
11
|
+
}
|
|
3
12
|
export interface RunRequest {
|
|
4
13
|
readonly kind: RunKind;
|
|
5
14
|
readonly modelId: string;
|
|
@@ -8,6 +17,8 @@ export interface RunRequest {
|
|
|
8
17
|
readonly limits: Record<string, unknown> | undefined;
|
|
9
18
|
readonly governedHandoff?: WorkflowHandoffRequest | undefined;
|
|
10
19
|
readonly governedHandoffSourceGroundedRunId?: string | undefined;
|
|
20
|
+
readonly governedHandoffVoiceOrigin?: RunVoiceOrigin | undefined;
|
|
21
|
+
readonly governedHandoffVoiceAction?: SpokenActionAuditRecord | undefined;
|
|
11
22
|
}
|
|
12
23
|
export interface RunRequestError {
|
|
13
24
|
readonly code: "BAD_REQUEST";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-request.d.ts","sourceRoot":"","sources":["../src/run-request.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gDAAgD,CAAC;AAE7F,MAAM,MAAM,OAAO,GAAG,YAAY,GAAG,mBAAmB,GAAG,cAAc,GAAG,QAAQ,CAAC;AAErF,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IAExB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAExC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAErD,QAAQ,CAAC,eAAe,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAC9D,QAAQ,CAAC,kCAAkC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"run-request.d.ts","sourceRoot":"","sources":["../src/run-request.ts"],"names":[],"mappings":"AASA,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAC3B,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gDAAgD,CAAC;AAE7F,MAAM,MAAM,OAAO,GAAG,YAAY,GAAG,mBAAmB,GAAG,cAAc,GAAG,QAAQ,CAAC;AAErF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IACvC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;CACjD;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IAExB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAExC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAErD,QAAQ,CAAC,eAAe,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAC9D,QAAQ,CAAC,kCAAkC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAGjE,QAAQ,CAAC,0BAA0B,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;IAGjE,QAAQ,CAAC,0BAA0B,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;CAC3E;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAubD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,eAAe,CAczE"}
|
package/dist/run-request.js
CHANGED
|
@@ -6,9 +6,107 @@
|
|
|
6
6
|
// the gated apply route (D8). The deeper guards (`isSensitivePath`, patch limits, target validation)
|
|
7
7
|
// are enforced by the workflow/harness entry points the engine calls; the BFF never reimplements
|
|
8
8
|
// them.
|
|
9
|
+
import {} from "@oscharko-dev/keiko-contracts";
|
|
9
10
|
function isRecord(value) {
|
|
10
11
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11
12
|
}
|
|
13
|
+
const VOICE_PROFILES = [
|
|
14
|
+
"none",
|
|
15
|
+
"speech-to-text",
|
|
16
|
+
"speech-output",
|
|
17
|
+
"full-realtime",
|
|
18
|
+
];
|
|
19
|
+
const VOICE_TRANSCRIPT_SOURCES = ["dictation", "realtime"];
|
|
20
|
+
const MAX_VOICE_COMMITTED_TEXT_CHARS = 8192;
|
|
21
|
+
const MAX_VOICE_TURN_INDEX = 1_000_000;
|
|
22
|
+
const MAX_VOICE_COMMITTED_SEGMENTS = 10_000;
|
|
23
|
+
const CONFIRMATION_DIGEST_PATTERN = /^[0-9a-f]{64}$/;
|
|
24
|
+
function badRequest(message) {
|
|
25
|
+
return { code: "BAD_REQUEST", message };
|
|
26
|
+
}
|
|
27
|
+
function isRunRequestError(value) {
|
|
28
|
+
return isRecord(value) && value.code === "BAD_REQUEST" && typeof value.message === "string";
|
|
29
|
+
}
|
|
30
|
+
function isVoiceProfile(value) {
|
|
31
|
+
return typeof value === "string" && VOICE_PROFILES.includes(value);
|
|
32
|
+
}
|
|
33
|
+
function isRunVoiceTranscriptSource(value) {
|
|
34
|
+
return (typeof value === "string" && VOICE_TRANSCRIPT_SOURCES.includes(value));
|
|
35
|
+
}
|
|
36
|
+
function isBoundedNonNegativeInteger(value, max) {
|
|
37
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= max;
|
|
38
|
+
}
|
|
39
|
+
function parseOptionalString(body, name) {
|
|
40
|
+
const value = body[name];
|
|
41
|
+
if (value === undefined) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
return typeof value === "string"
|
|
45
|
+
? value
|
|
46
|
+
: badRequest(`${name} must be a string when provided.`);
|
|
47
|
+
}
|
|
48
|
+
function parseConfirmationDigest(value) {
|
|
49
|
+
if (value === undefined) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
if (typeof value !== "string" || !CONFIRMATION_DIGEST_PATTERN.test(value)) {
|
|
53
|
+
return badRequest("voiceOrigin.confirmationDigest must be a 64-char lowercase sha256 hex.");
|
|
54
|
+
}
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
function parseVoiceOriginFields(value) {
|
|
58
|
+
if (!isVoiceProfile(value.profile)) {
|
|
59
|
+
return badRequest("voiceOrigin.profile is invalid.");
|
|
60
|
+
}
|
|
61
|
+
if (!isBoundedNonNegativeInteger(value.turnIndex, MAX_VOICE_TURN_INDEX)) {
|
|
62
|
+
return badRequest("voiceOrigin.turnIndex must be a bounded non-negative integer.");
|
|
63
|
+
}
|
|
64
|
+
if (!isRunVoiceTranscriptSource(value.source)) {
|
|
65
|
+
return badRequest("voiceOrigin.source is invalid.");
|
|
66
|
+
}
|
|
67
|
+
if (!isBoundedNonNegativeInteger(value.committedSegments, MAX_VOICE_COMMITTED_SEGMENTS)) {
|
|
68
|
+
return badRequest("voiceOrigin.committedSegments must be a bounded non-negative integer.");
|
|
69
|
+
}
|
|
70
|
+
if (typeof value.committedText !== "string" ||
|
|
71
|
+
value.committedText.length > MAX_VOICE_COMMITTED_TEXT_CHARS) {
|
|
72
|
+
return badRequest("voiceOrigin.committedText must be a string within the size limit.");
|
|
73
|
+
}
|
|
74
|
+
const confirmationDigest = parseConfirmationDigest(value.confirmationDigest);
|
|
75
|
+
if (typeof confirmationDigest === "object") {
|
|
76
|
+
return confirmationDigest;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
profile: value.profile,
|
|
80
|
+
turnIndex: value.turnIndex,
|
|
81
|
+
source: value.source,
|
|
82
|
+
committedSegments: value.committedSegments,
|
|
83
|
+
committedText: value.committedText,
|
|
84
|
+
confirmationDigest,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function parseGovernedHandoff(body) {
|
|
88
|
+
const value = body.governedHandoff;
|
|
89
|
+
if (value === undefined) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
if (!isRecord(value)) {
|
|
93
|
+
return badRequest("governedHandoff must be an object when provided.");
|
|
94
|
+
}
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
function parseVoiceOrigin(body, governedHandoff) {
|
|
98
|
+
const value = body.voiceOrigin;
|
|
99
|
+
if (value === undefined) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
if (governedHandoff === undefined) {
|
|
103
|
+
return badRequest("voiceOrigin requires governedHandoff.");
|
|
104
|
+
}
|
|
105
|
+
if (!isRecord(value)) {
|
|
106
|
+
return badRequest("voiceOrigin must be an object when provided.");
|
|
107
|
+
}
|
|
108
|
+
return parseVoiceOriginFields(value);
|
|
109
|
+
}
|
|
12
110
|
function resolveKind(body) {
|
|
13
111
|
const workflowId = body.workflowId;
|
|
14
112
|
const taskType = body.taskType;
|
|
@@ -176,18 +274,16 @@ function validateRunInput(kind, input) {
|
|
|
176
274
|
}
|
|
177
275
|
return validateBugInvestigationInput(input);
|
|
178
276
|
}
|
|
179
|
-
|
|
180
|
-
export function parseRunRequest(raw) {
|
|
181
|
-
let parsed;
|
|
277
|
+
function parseJsonObject(raw) {
|
|
182
278
|
try {
|
|
183
|
-
parsed = JSON.parse(raw);
|
|
279
|
+
const parsed = JSON.parse(raw);
|
|
280
|
+
return isRecord(parsed) ? parsed : badRequest("Request body must be a JSON object.");
|
|
184
281
|
}
|
|
185
282
|
catch {
|
|
186
|
-
return
|
|
187
|
-
}
|
|
188
|
-
if (!isRecord(parsed)) {
|
|
189
|
-
return { code: "BAD_REQUEST", message: "Request body must be a JSON object." };
|
|
283
|
+
return badRequest("Request body is not valid JSON.");
|
|
190
284
|
}
|
|
285
|
+
}
|
|
286
|
+
function parseRunRequestCore(parsed) {
|
|
191
287
|
const kind = resolveKind(parsed);
|
|
192
288
|
if (typeof kind !== "string") {
|
|
193
289
|
return kind;
|
|
@@ -208,12 +304,64 @@ export function parseRunRequest(raw) {
|
|
|
208
304
|
return {
|
|
209
305
|
kind,
|
|
210
306
|
modelId,
|
|
307
|
+
input,
|
|
308
|
+
limits: isRecord(limits) ? limits : undefined,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
function parseGovernedHandoffMetadata(parsed) {
|
|
312
|
+
if (parsed.governedHandoffVoiceAction !== undefined) {
|
|
313
|
+
return badRequest("governedHandoffVoiceAction is server-generated and must not be provided.");
|
|
314
|
+
}
|
|
315
|
+
const governedHandoff = parseGovernedHandoff(parsed);
|
|
316
|
+
if (isRunRequestError(governedHandoff)) {
|
|
317
|
+
return governedHandoff;
|
|
318
|
+
}
|
|
319
|
+
const governedHandoffSourceGroundedRunId = parseOptionalString(parsed, "governedHandoffSourceGroundedRunId");
|
|
320
|
+
if (isRunRequestError(governedHandoffSourceGroundedRunId)) {
|
|
321
|
+
return governedHandoffSourceGroundedRunId;
|
|
322
|
+
}
|
|
323
|
+
if (governedHandoffSourceGroundedRunId !== undefined && governedHandoff === undefined) {
|
|
324
|
+
return badRequest("governedHandoffSourceGroundedRunId requires governedHandoff.");
|
|
325
|
+
}
|
|
326
|
+
const governedHandoffVoiceOrigin = parseVoiceOrigin(parsed, governedHandoff);
|
|
327
|
+
if (isRunRequestError(governedHandoffVoiceOrigin)) {
|
|
328
|
+
return governedHandoffVoiceOrigin;
|
|
329
|
+
}
|
|
330
|
+
return { governedHandoff, governedHandoffSourceGroundedRunId, governedHandoffVoiceOrigin };
|
|
331
|
+
}
|
|
332
|
+
function buildRunRequest(core, metadata) {
|
|
333
|
+
return {
|
|
334
|
+
kind: core.kind,
|
|
335
|
+
modelId: core.modelId,
|
|
211
336
|
// Dry-run-first (ADR-0011 D8 / security M1): the create route NEVER applies, even if the client
|
|
212
337
|
// body carries `apply:true`. Applying is the sole responsibility of POST /api/runs/:runId/apply
|
|
213
338
|
// (route 9), which re-invokes the workflow through the gated path. A one-shot create-with-apply
|
|
214
339
|
// would bypass the explicit review→apply step, so the body flag is deliberately ignored here.
|
|
215
340
|
apply: false,
|
|
216
|
-
input,
|
|
217
|
-
|
|
341
|
+
input: core.input,
|
|
342
|
+
limits: core.limits,
|
|
343
|
+
...(metadata.governedHandoff === undefined ? {} : { governedHandoff: metadata.governedHandoff }),
|
|
344
|
+
...(metadata.governedHandoffSourceGroundedRunId === undefined
|
|
345
|
+
? {}
|
|
346
|
+
: { governedHandoffSourceGroundedRunId: metadata.governedHandoffSourceGroundedRunId }),
|
|
347
|
+
...(metadata.governedHandoffVoiceOrigin === undefined
|
|
348
|
+
? {}
|
|
349
|
+
: { governedHandoffVoiceOrigin: metadata.governedHandoffVoiceOrigin }),
|
|
218
350
|
};
|
|
219
351
|
}
|
|
352
|
+
// Parses the raw JSON text into a validated RunRequest, or a typed BAD_REQUEST error.
|
|
353
|
+
export function parseRunRequest(raw) {
|
|
354
|
+
const parsed = parseJsonObject(raw);
|
|
355
|
+
if (isRunRequestError(parsed)) {
|
|
356
|
+
return parsed;
|
|
357
|
+
}
|
|
358
|
+
const core = parseRunRequestCore(parsed);
|
|
359
|
+
if (isRunRequestError(core)) {
|
|
360
|
+
return core;
|
|
361
|
+
}
|
|
362
|
+
const metadata = parseGovernedHandoffMetadata(parsed);
|
|
363
|
+
if (isRunRequestError(metadata)) {
|
|
364
|
+
return metadata;
|
|
365
|
+
}
|
|
366
|
+
return buildRunRequest(core, metadata);
|
|
367
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type RuntimeCapabilitiesResponse, type RuntimeCapabilityKind, type RuntimeCapabilityState, type RuntimeCapabilityUnavailableReason } from "@oscharko-dev/keiko-contracts";
|
|
2
|
+
import { type WorkspaceInfo } from "@oscharko-dev/keiko-workspace";
|
|
3
|
+
import type { WorkspaceFs } from "@oscharko-dev/keiko-workspace";
|
|
4
|
+
export declare const DEFAULT_RUNTIME_CAPABILITY_DEADLINE_MS: 250;
|
|
5
|
+
export interface HostExecutableSpec {
|
|
6
|
+
readonly id: string;
|
|
7
|
+
readonly kind: Exclude<RuntimeCapabilityKind, "command-source">;
|
|
8
|
+
readonly executable: string;
|
|
9
|
+
readonly label: string;
|
|
10
|
+
readonly remediationHint: string;
|
|
11
|
+
}
|
|
12
|
+
export interface HostExecutableProbeResult {
|
|
13
|
+
readonly state: RuntimeCapabilityState;
|
|
14
|
+
readonly unavailableReason?: RuntimeCapabilityUnavailableReason | undefined;
|
|
15
|
+
readonly version?: string | undefined;
|
|
16
|
+
}
|
|
17
|
+
export interface HostExecutableProbe {
|
|
18
|
+
probe(executable: string, workspaceRoot: string | undefined): HostExecutableProbeResult;
|
|
19
|
+
}
|
|
20
|
+
export interface RuntimeCapabilityDetectorOptions {
|
|
21
|
+
readonly workspace?: WorkspaceInfo | undefined;
|
|
22
|
+
readonly fs?: WorkspaceFs | undefined;
|
|
23
|
+
readonly env?: NodeJS.ProcessEnv | undefined;
|
|
24
|
+
readonly platform?: NodeJS.Platform | undefined;
|
|
25
|
+
readonly now?: (() => number) | undefined;
|
|
26
|
+
readonly deadlineMs?: number | undefined;
|
|
27
|
+
readonly probe?: HostExecutableProbe | undefined;
|
|
28
|
+
readonly specs?: readonly HostExecutableSpec[] | undefined;
|
|
29
|
+
}
|
|
30
|
+
export declare const RUNTIME_HOST_EXECUTABLE_SPECS: readonly HostExecutableSpec[];
|
|
31
|
+
export declare class PathHostExecutableProbe implements HostExecutableProbe {
|
|
32
|
+
private readonly env;
|
|
33
|
+
private readonly platform;
|
|
34
|
+
constructor(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform);
|
|
35
|
+
probe(executable: string, workspaceRoot: string | undefined): HostExecutableProbeResult;
|
|
36
|
+
}
|
|
37
|
+
export declare function detectRuntimeCapabilities(options?: RuntimeCapabilityDetectorOptions): RuntimeCapabilitiesResponse;
|
|
38
|
+
//# sourceMappingURL=capabilityDetector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilityDetector.d.ts","sourceRoot":"","sources":["../../src/runtime/capabilityDetector.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,KAAK,2BAA2B,EAEhC,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,kCAAkC,EAGxC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAqB,KAAK,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAGtF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAEjE,eAAO,MAAM,sCAAsC,EAAG,GAAY,CAAC;AAKnE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,qBAAqB,EAAE,gBAAgB,CAAC,CAAC;IAChE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,KAAK,EAAE,sBAAsB,CAAC;IACvC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,kCAAkC,GAAG,SAAS,CAAC;IAC5E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,yBAAyB,CAAC;CACzF;AAED,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC/C,QAAQ,CAAC,EAAE,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,GAAG,SAAS,CAAC;IAChD,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,kBAAkB,EAAE,GAAG,SAAS,CAAC;CAC5D;AAED,eAAO,MAAM,6BAA6B,EAAE,SAAS,kBAAkB,EA4FrE,CAAC;AA6GH,qBAAa,uBAAwB,YAAW,mBAAmB;IACjE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;gBAGzC,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,QAAQ,GAAE,MAAM,CAAC,QAA2B;IAMvC,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,yBAAyB;CAqB/F;AAwRD,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,gCAAqC,GAC7C,2BAA2B,CAsB7B"}
|