源码快照 @ 2026-09-02 (source-only, 剔除非源码素材)

This commit is contained in:
agent-radar
2026-09-02 01:05:41 +08:00
commit 04bac1f6e2
2200 changed files with 489415 additions and 0 deletions
+127
View File
@@ -0,0 +1,127 @@
{
"name": "@openwork/types",
"version": "0.0.0",
"private": true,
"type": "module",
"exports": {
".": {
"types": "./src/index.ts",
"development": "./src/index.ts",
"default": "./src/index.ts"
},
"./agent-context-diagnostics": {
"types": "./src/agent-context-diagnostics.ts",
"development": "./src/agent-context-diagnostics.ts",
"default": "./src/agent-context-diagnostics.ts"
},
"./openwork-affordance": {
"types": "./src/openwork-affordance.ts",
"development": "./src/openwork-affordance.ts",
"default": "./src/openwork-affordance.ts"
},
"./openwork-context": {
"types": "./src/openwork-context.ts",
"development": "./src/openwork-context.ts",
"default": "./src/openwork-context.ts"
},
"./openwork-provider": {
"types": "./src/openwork-provider.ts",
"development": "./src/openwork-provider.ts",
"default": "./src/openwork-provider.ts"
},
"./automations": {
"types": "./src/automations.ts",
"development": "./src/automations.ts",
"default": "./src/automations.ts"
},
"./workflows": {
"types": "./src/workflows.ts",
"development": "./src/workflows.ts",
"default": "./src/workflows.ts"
},
"./skill-created-app": {
"types": "./src/skill-created-app.ts",
"development": "./src/skill-created-app.ts",
"default": "./src/skill-created-app.ts"
},
"./connection-action-app": {
"types": "./src/connection-action-app.ts",
"development": "./src/connection-action-app.ts",
"default": "./src/connection-action-app.ts"
},
"./plugin-flow-app": {
"types": "./src/plugin-flow-app.ts",
"development": "./src/plugin-flow-app.ts",
"default": "./src/plugin-flow-app.ts"
},
"./den/desktop-app-restrictions": {
"types": "./src/den/desktop-app-restrictions.ts",
"development": "./src/den/desktop-app-restrictions.ts",
"default": "./src/den/desktop-app-restrictions.ts"
},
"./den/desktop-policies": {
"types": "./src/den/desktop-policies.ts",
"development": "./src/den/desktop-policies.ts",
"default": "./src/den/desktop-policies.ts"
},
"./den/connect-diagnostics": {
"types": "./src/den/connect-diagnostics.ts",
"development": "./src/den/connect-diagnostics.ts",
"default": "./src/den/connect-diagnostics.ts"
},
"./den/egress-diagnostics": {
"types": "./src/den/egress-diagnostics.ts",
"development": "./src/den/egress-diagnostics.ts",
"default": "./src/den/egress-diagnostics.ts"
},
"./den/inference": {
"types": "./src/den/inference.ts",
"development": "./src/den/inference.ts",
"default": "./src/den/inference.ts"
},
"./den/microsoft-365": {
"types": "./src/den/microsoft-365.ts",
"development": "./src/den/microsoft-365.ts",
"default": "./src/den/microsoft-365.ts"
},
"./den/mcp-connection-action": {
"types": "./src/den/mcp-connection-action.ts",
"development": "./src/den/mcp-connection-action.ts",
"default": "./src/den/mcp-connection-action.ts"
},
"./workspace": {
"types": "./src/workspace.ts",
"development": "./src/workspace.ts",
"default": "./src/workspace.ts"
},
"./desktop-ipc": {
"types": "./src/desktop-ipc.ts",
"development": "./src/desktop-ipc.ts",
"default": "./src/desktop-ipc.ts"
},
"./connect-link": {
"types": "./src/connect-link.ts",
"development": "./src/connect-link.ts",
"default": "./src/connect-link.ts"
},
"./url": {
"types": "./src/url.ts",
"development": "./src/url.ts",
"default": "./src/url.ts"
}
},
"files": [
"src",
"dist"
],
"scripts": {
"build": "tsup"
},
"dependencies": {
"zod": "^4.3.6"
},
"devDependencies": {
"tsup": "^8.5.0",
"typescript": "^5.6.3"
}
}
@@ -0,0 +1,662 @@
import { z } from "zod"
export const AGENT_CONTEXT_DIAGNOSTICS_SCHEMA_VERSION = 1 as const
export const AGENT_CONTEXT_DIAGNOSTIC_CHECK_IDS = [
"request-safety",
"workspace-runtime",
"connect-steering-scope",
"agent-resolution",
"agent-prompt-markers",
"agent-connect-tool-permissions",
"plugin-registration",
"mcp-inventory",
"engine-config",
"engine-agent",
"engine-plugin-tools",
"engine-mcp-sync",
"engine-mcp-status",
"cloud-tool-catalog",
"cloud-endpoint-differential",
"cloud-endpoint-transport",
"organization-connections",
"report-safety",
] as const
export const agentContextDiagnosticCheckIdSchema = z.enum(AGENT_CONTEXT_DIAGNOSTIC_CHECK_IDS)
export type AgentContextDiagnosticCheckId = z.infer<typeof agentContextDiagnosticCheckIdSchema>
export const agentContextDiagnosticStatusSchema = z.enum(["passed", "warning", "failed", "skipped"])
export type AgentContextDiagnosticStatus = z.infer<typeof agentContextDiagnosticStatusSchema>
export const agentContextDiagnosticOverallSchema = z.enum(["passed", "warning", "failed"])
export type AgentContextDiagnosticOverall = z.infer<typeof agentContextDiagnosticOverallSchema>
export const agentContextDiagnosticEvidenceKindSchema = z.enum([
"observed",
"client-observed",
"expected",
"derived",
"unavailable",
])
export type AgentContextDiagnosticEvidenceKind = z.infer<typeof agentContextDiagnosticEvidenceKindSchema>
export const agentContextDiagnosticOwnerSchema = z.enum([
"openwork-client",
"openwork-server",
"opencode-engine",
"network-admin",
"organization-admin",
"member",
"member-and-organization-admin",
"openwork-support",
])
export type AgentContextDiagnosticOwner = z.infer<typeof agentContextDiagnosticOwnerSchema>
const forbiddenDiagnosticTextPattern = /[\u0000-\u001f\u007f-\u009f\p{Default_Ignorable_Code_Point}]/u
const forbiddenDiagnosticTextPatternGlobal = /[\u0000-\u001f\u007f-\u009f\p{Default_Ignorable_Code_Point}]/gu
const diagnosticUrlPattern = /\b[a-z][a-z0-9+.-]{0,31}:\s*\/\/[^\s<>"'`]+/iu
const diagnosticAuthorizationPattern = /(?:^|[^A-Za-z0-9])(?:b[\s._-]*e[\s._-]*a[\s._-]*r[\s._-]*e[\s._-]*r|b[\s._-]*a[\s._-]*s[\s._-]*i[\s._-]*c)(?:\s|[.:=+_-])+(?:"[^"\r\n]*"|'[^'\r\n]*'|[^\s,;)}\]])/iu
const diagnosticSecretAssignmentPattern = /(?:^|[^A-Za-z0-9])(?:a[\s._-]*u[\s._-]*t[\s._-]*h[\s._-]*o[\s._-]*r[\s._-]*i[\s._-]*z[\s._-]*a[\s._-]*t[\s._-]*i[\s._-]*o[\s._-]*n|p[\s._-]*r[\s._-]*o[\s._-]*x[\s._-]*y[\s._-]*a[\s._-]*u[\s._-]*t[\s._-]*h[\s._-]*o[\s._-]*r[\s._-]*i[\s._-]*z[\s._-]*a[\s._-]*t[\s._-]*i[\s._-]*o[\s._-]*n|a[\s._-]*c[\s._-]*c[\s._-]*e[\s._-]*s[\s._-]*s[\s._-]*t[\s._-]*o[\s._-]*k[\s._-]*e[\s._-]*n|r[\s._-]*e[\s._-]*f[\s._-]*r[\s._-]*e[\s._-]*s[\s._-]*h[\s._-]*t[\s._-]*o[\s._-]*k[\s._-]*e[\s._-]*n|i[\s._-]*d[\s._-]*t[\s._-]*o[\s._-]*k[\s._-]*e[\s._-]*n|a[\s._-]*p[\s._-]*i[\s._-]*k[\s._-]*e[\s._-]*y|x[\s._-]*a[\s._-]*p[\s._-]*i[\s._-]*k[\s._-]*e[\s._-]*y|c[\s._-]*l[\s._-]*i[\s._-]*e[\s._-]*n[\s._-]*t[\s._-]*s[\s._-]*e[\s._-]*c[\s._-]*r[\s._-]*e[\s._-]*t|p[\s._-]*a[\s._-]*s[\s._-]*s[\s._-]*w[\s._-]*o[\s._-]*r[\s._-]*d|p[\s._-]*a[\s._-]*s[\s._-]*s[\s._-]*w[\s._-]*d|s[\s._-]*e[\s._-]*c[\s._-]*r[\s._-]*e[\s._-]*t|t[\s._-]*o[\s._-]*k[\s._-]*e[\s._-]*n)[\s._-]*[:=][\s._-]*(?:"[^"\r\n]*"|'[^'\r\n]*'|[^\s,;)}\]])/iu
const diagnosticJwtPattern = /\beyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/u
const diagnosticKnownTokenPattern = /\b(?:owt_[A-Za-z0-9_-]+|ow_mcp_at_[A-Za-z0-9_-]+|sk-[A-Za-z0-9_-]+|gh[pousr]_[A-Za-z0-9_]+|github_pat_[A-Za-z0-9_]+|xox[baprs]-[A-Za-z0-9-]+)\b/iu
const diagnosticWindowsPathPattern = /(^|[\s("'=,:])(?:[A-Za-z]:[\\/]|\\\\)[^,;)}\]>"'`\r\n]+/mu
const diagnosticHomePathPattern = /(^|[\s("'=,:])~[\\/][^,;)}\]>"'`\r\n]+/mu
const diagnosticPosixPathPattern = /(^|[\s("'=,:])\/(?!mcp\/agent(?:$|[\s.,;:!)}\]"']))[^/\s<>:"'`][^,;)}\]>"'`\r\n]*/mu
const diagnosticEncodedStructuralPattern = /%(?:2f|3a|5c)/iu
const diagnosticPercentOctetPattern = /%[0-9a-f]{2}/iu
const DIAGNOSTIC_CLOUD_MCP_PATH_SUFFIX = "/mcp/agent"
const MAX_DIAGNOSTIC_PERCENT_DECODE_ROUNDS = 12
const sensitiveDiagnosticTextPatterns = [
diagnosticUrlPattern,
diagnosticAuthorizationPattern,
diagnosticSecretAssignmentPattern,
diagnosticJwtPattern,
diagnosticKnownTokenPattern,
diagnosticWindowsPathPattern,
diagnosticHomePathPattern,
diagnosticPosixPathPattern,
diagnosticEncodedStructuralPattern,
]
function decodeDiagnosticPercentEncoding(value: string): string {
try {
return decodeURIComponent(value)
} catch {
// One malformed UTF-8 run must not shield otherwise valid encoded ASCII
// delimiters or keywords elsewhere in the label.
return value.replace(/(?:%[0-9a-f]{2})+/giu, (run) => {
try {
return decodeURIComponent(run)
} catch {
return run.replace(/%([0-9a-f]{2})/giu, (encoded, hex: string) => {
const byte = Number.parseInt(hex, 16)
return byte <= 0x7f ? String.fromCharCode(byte) : encoded
})
}
})
}
}
function diagnosticTextVariants(value: string): {
variants: string[]
percentDecodingIncomplete: boolean
} {
const variants = new Set<string>()
const add = (candidate: string) => {
variants.add(candidate)
variants.add(candidate.normalize("NFKC"))
}
add(value)
let decodedCandidate = value.normalize("NFKC")
for (let round = 0; round < MAX_DIAGNOSTIC_PERCENT_DECODE_ROUNDS; round += 1) {
const decoded = decodeDiagnosticPercentEncoding(decodedCandidate).normalize("NFKC")
if (decoded === decodedCandidate) break
decodedCandidate = decoded
add(decodedCandidate)
}
// A label that still contains an encoded octet, or can be decoded again,
// is malformed or exceeded the bounded normalization we can safely reason
// about. Treat it as sensitive instead of allowing nesting to hide a secret.
const percentDecodingIncomplete = diagnosticPercentOctetPattern.test(decodedCandidate)
|| decodeDiagnosticPercentEncoding(decodedCandidate).normalize("NFKC") !== decodedCandidate
for (let round = 0; round < 4; round += 1) {
for (const candidate of [...variants]) {
if (candidate.includes("+")) add(candidate.replaceAll("+", " "))
const punctuationSpaced = candidate.replace(/[\p{P}\p{S}]/gu, (character) =>
character === ":" || character === "=" ? character : " ",
)
if (punctuationSpaced !== candidate) add(punctuationSpaced)
if (/%[0-9a-f]{2}/iu.test(candidate)) {
add(candidate.replace(/%[0-9a-f]{2}/giu, ""))
add(candidate.replace(/%[0-9a-f]{2}/giu, " "))
}
if (!forbiddenDiagnosticTextPattern.test(candidate)) continue
add(candidate.replace(forbiddenDiagnosticTextPatternGlobal, ""))
add(candidate.replace(forbiddenDiagnosticTextPatternGlobal, " "))
}
}
return { variants: [...variants], percentDecodingIncomplete }
}
function containsSensitiveDiagnosticText(value: string): boolean {
const normalized = value.normalize("NFKC")
if (normalized !== value && /[\\/]/u.test(normalized)) return true
const { variants, percentDecodingIncomplete } = diagnosticTextVariants(value)
if (percentDecodingIncomplete) return true
return variants.some((variant) =>
sensitiveDiagnosticTextPatterns.some((pattern) => pattern.test(variant)),
)
}
function globalDiagnosticPattern(pattern: RegExp): RegExp {
return new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`)
}
function scrubDiagnosticPathMatches(value: string, pattern: RegExp): string {
return value.replace(globalDiagnosticPattern(pattern), (match: string, boundary: string) => {
const pathWithSuffix = match.slice(boundary.length)
const suffixStart = pathWithSuffix.search(/\s/u)
const suffix = suffixStart >= 0 ? pathWithSuffix.slice(suffixStart) : ""
return `${boundary}[path]${suffix}`
})
}
export function scrubAgentContextDiagnosticText(value: string): string {
const withoutUrls = value.replace(globalDiagnosticPattern(diagnosticUrlPattern), "[url]")
return scrubDiagnosticPathMatches(
scrubDiagnosticPathMatches(
scrubDiagnosticPathMatches(withoutUrls, diagnosticWindowsPathPattern),
diagnosticHomePathPattern,
),
diagnosticPosixPathPattern,
)
}
function isDiagnosticOriginSafe(value: string): boolean {
try {
const url = new URL(value)
return value === url.origin
&& (url.protocol === "https:" || url.protocol === "http:")
&& !url.username
&& !url.password
&& !url.search
&& !url.hash
} catch {
return false
}
}
function isCloudMcpPathSafe(value: string): boolean {
if (!value.endsWith(DIAGNOSTIC_CLOUD_MCP_PATH_SUFFIX)) return false
const prefix = value.slice(0, -DIAGNOSTIC_CLOUD_MCP_PATH_SUFFIX.length)
return !forbiddenDiagnosticTextPattern.test(value)
&& (prefix.length === 0 || (prefix.length > 1 && prefix.startsWith("/")))
&& !prefix.includes("?")
&& !prefix.includes("#")
}
/**
* Dynamic diagnostics labels are untrusted display text. Preserve ordinary
* names, but replace recognizable credentials, URLs, and absolute paths so a
* user-controlled label cannot contradict the report's exclusion guarantees.
*/
export function sanitizeAgentContextDiagnosticText(value: string): string {
if (value.length > 500) return "[redacted-sensitive-label]"
if (containsSensitiveDiagnosticText(value)) return "[redacted-sensitive-label]"
return value.normalize("NFKC").replace(forbiddenDiagnosticTextPatternGlobal, "")
}
export function isAgentContextDiagnosticTextSafe(value: string): boolean {
if (value.length > 500) return false
if (forbiddenDiagnosticTextPattern.test(value)) return false
return !containsSensitiveDiagnosticText(value)
}
const safeTextSchema = z.string()
.max(500)
.refine(
isAgentContextDiagnosticTextSafe,
"diagnostic text cannot contain controls, credentials, URLs, or absolute paths",
)
const diagnosticOriginSchema = z.string().max(240).refine(isDiagnosticOriginSafe, "diagnostic origins must exclude path, query, hash, and credentials")
const cloudMcpPathSchema = z.string().max(240).refine(isCloudMcpPathSafe, "cloud MCP paths must end in /mcp/agent and exclude query or hash")
const transportCauseSchema = z.enum([
"tls_incomplete_chain",
"tls_untrusted_ca",
"tls_hostname_mismatch",
"tls_expired",
"tls_error",
"dns_error",
"connection_refused",
"connection_reset",
"timeout",
"http_error",
"auth_error",
"unknown",
])
const registrationFailureDetailSchema = z.object({
name: safeTextSchema.min(1).max(160),
status: z.enum(["connected", "disabled", "failed", "needs-auth", "needs-client-registration", "not-recorded"]),
source: z.enum(["transport_failure", "engine_status"]).nullable(),
recordAgeMs: z.number().int().nonnegative().nullable(),
errorSummary: safeTextSchema.max(400).nullable(),
transportCause: transportCauseSchema.nullable(),
engineReachableNow: z.boolean(),
}).strict()
const servedChainEntrySchema = z.object({
subjectCN: safeTextSchema.max(120).nullable(),
issuerCN: safeTextSchema.max(120).nullable(),
selfIssued: z.boolean(),
notAfter: safeTextSchema.max(120).nullable(),
}).strict()
const organizationConnectionNameSchema = z.string()
.min(1)
.max(160)
.refine((value) => value.trim().length > 0, "connection names cannot be blank")
.refine(
isAgentContextDiagnosticTextSafe,
"connection names cannot contain controls, credentials, URLs, or absolute paths",
)
const safeDetailValueSchema = z.union([
safeTextSchema,
diagnosticOriginSchema,
z.number().finite(),
z.boolean(),
z.null(),
z.array(safeTextSchema).max(100),
z.array(registrationFailureDetailSchema).max(100),
z.array(servedChainEntrySchema).max(5),
])
const toolIdSchema = z.string().min(1).max(160).regex(/^[A-Za-z][A-Za-z0-9_.:-]*$/)
function uniqueToolIdsSchema(max: number) {
return z.array(toolIdSchema).max(max).superRefine((toolIds, context) => {
const seen = new Set<string>()
toolIds.forEach((toolId, index) => {
if (seen.has(toolId)) {
context.addIssue({
code: "custom",
message: "tool IDs must be unique",
path: [index],
})
}
seen.add(toolId)
})
})
}
export const agentContextDiagnosticCheckSchema = z.object({
id: agentContextDiagnosticCheckIdSchema,
status: agentContextDiagnosticStatusSchema,
evidenceKind: agentContextDiagnosticEvidenceKindSchema,
code: safeTextSchema.min(1).max(120),
message: safeTextSchema.min(1).max(500),
owner: agentContextDiagnosticOwnerSchema,
action: safeTextSchema.min(1).max(500),
details: z.record(safeTextSchema.min(1).max(80), safeDetailValueSchema)
.refine((value) => Object.keys(value).length <= 30, "details may contain at most 30 keys"),
durationMs: z.number().int().nonnegative(),
}).strict()
export type AgentContextDiagnosticCheck = z.infer<typeof agentContextDiagnosticCheckSchema>
export const agentContextOrganizationConnectionSummarySchema = z.object({
id: z.string()
.min(1)
.max(160)
.regex(/^[A-Za-z0-9_.:-]+$/)
.refine(
isAgentContextDiagnosticTextSafe,
"connection IDs cannot contain credentials or token-shaped values",
),
name: organizationConnectionNameSchema,
credentialMode: z.enum(["shared", "per_member"]),
connected: z.boolean(),
connectedForMe: z.boolean(),
needsReconnect: z.boolean(),
missingFeatureCount: z.number().int().nonnegative().max(100),
}).strict()
export type AgentContextOrganizationConnectionSummary = z.infer<typeof agentContextOrganizationConnectionSummarySchema>
export const agentContextOrganizationConnectionsProbeSchema = z.object({
status: z.enum(["observed", "unavailable", "skipped"]),
code: z.enum(["signed_out", "list_failed", "not_attempted", "remote_workspace_privacy"]).nullable(),
totalCount: z.number().int().nonnegative().max(1_000_000),
truncated: z.boolean(),
}).strict().superRefine((value, context) => {
if (value.status === "observed" && value.code !== null) {
context.addIssue({ code: "custom", message: "observed probes cannot have an error code", path: ["code"] })
}
if (value.status === "unavailable" && value.code !== "list_failed") {
context.addIssue({ code: "custom", message: "unavailable probes require list_failed", path: ["code"] })
}
if (
value.status === "skipped"
&& value.code !== "signed_out"
&& value.code !== "not_attempted"
&& value.code !== "remote_workspace_privacy"
) {
context.addIssue({ code: "custom", message: "skipped probes require a skipped code", path: ["code"] })
}
if (value.status !== "observed" && (value.totalCount !== 0 || value.truncated)) {
context.addIssue({
code: "custom",
message: "unobserved probes cannot report connection counts",
path: ["totalCount"],
})
}
})
export type AgentContextOrganizationConnectionsProbe = z.infer<typeof agentContextOrganizationConnectionsProbeSchema>
export const agentContextDiagnosticsRequestSchema = z.object({
organizationConnectionsProbe: agentContextOrganizationConnectionsProbeSchema,
organizationConnections: z.array(agentContextOrganizationConnectionSummarySchema).max(200),
}).strict().superRefine((value, context) => {
if (value.organizationConnectionsProbe.status !== "observed" && value.organizationConnections.length > 0) {
context.addIssue({
code: "custom",
message: "organization connection rows require an observed organization probe",
path: ["organizationConnections"],
})
}
if (value.organizationConnectionsProbe.status === "observed") {
const { totalCount, truncated } = value.organizationConnectionsProbe
if (totalCount < value.organizationConnections.length) {
context.addIssue({
code: "custom",
message: "observed totalCount cannot be smaller than the reported rows",
path: ["organizationConnectionsProbe", "totalCount"],
})
}
if (truncated !== (totalCount > value.organizationConnections.length)) {
context.addIssue({
code: "custom",
message: "truncated must describe whether observed rows were omitted",
path: ["organizationConnectionsProbe", "truncated"],
})
}
}
const seenIds = new Set<string>()
value.organizationConnections.forEach((connection, index) => {
if (seenIds.has(connection.id)) {
context.addIssue({
code: "custom",
message: "organization connection IDs must be unique",
path: ["organizationConnections", index, "id"],
})
}
seenIds.add(connection.id)
})
})
export type AgentContextDiagnosticsRequest = z.infer<typeof agentContextDiagnosticsRequestSchema>
export const agentContextToolPermissionSchema = z.enum([
"allowed",
"approval-required",
"denied",
"unspecified",
])
export type AgentContextToolPermission = z.infer<typeof agentContextToolPermissionSchema>
export const agentContextPromptEvidenceSchema = z.object({
length: z.number().int().nonnegative(),
sha256: z.string().regex(/^[a-f0-9]{64}$/).nullable(),
markers: z.object({
searchCapabilities: z.boolean(),
executeCapability: z.boolean(),
memoryBank: z.boolean(),
}).strict(),
}).strict()
export type AgentContextPromptEvidence = z.infer<typeof agentContextPromptEvidenceSchema>
export const agentContextAgentEvidenceSchema = z.object({
evidenceSource: z.enum(["effective-engine", "configured-intent"]),
defaultAgent: safeTextSchema.max(160).nullable(),
configuredOpenworkAgent: z.object({
state: z.enum(["present", "missing", "configured-disabled"]),
mode: z.enum(["subagent", "primary", "all"]).nullable(),
prompt: agentContextPromptEvidenceSchema,
connectToolPermissions: z.object({
searchCapabilities: agentContextToolPermissionSchema,
executeCapability: agentContextToolPermissionSchema,
deniedRelevantToolCount: z.number().int().nonnegative().nullable(),
}).strict(),
}).strict(),
pluginLabels: z.array(safeTextSchema.min(1).max(160)).max(100),
}).strict()
export type AgentContextAgentEvidence = z.infer<typeof agentContextAgentEvidenceSchema>
export const agentContextMcpEvidenceSchema = z.object({
name: safeTextSchema.min(1).max(160),
source: z.enum(["engine.config", "config.project", "config.global", "config.remote"]),
type: z.enum(["local", "remote", "unknown"]),
enabled: z.boolean(),
disabledByTools: z.boolean(),
origin: diagnosticOriginSchema.nullable(),
path: cloudMcpPathSchema.nullable(),
hasHeaders: z.boolean(),
oauthMode: z.enum(["auto", "configured", "disabled", "none", "unknown"]),
syncStatus: z.enum([
"connected",
"disabled",
"failed",
"needs-auth",
"needs-client-registration",
"not-recorded",
"not-applicable",
]),
liveEngineStatus: z.literal("unavailable"),
}).strict()
export type AgentContextMcpEvidence = z.infer<typeof agentContextMcpEvidenceSchema>
export const agentContextConnectEvidenceSchema = z.object({
stateStatus: z.enum(["available", "missing", "invalid", "unreadable"]),
connectEnabled: z.boolean(),
legacyGoogleWorkspaceConfigured: z.boolean(),
expectedBranch: z.enum(["cloud-active", "cloud-disconnected", "extensions-only"]),
globalCloudMcpPresent: z.boolean(),
selectedWorkspaceCloudMcpPresent: z.boolean(),
crossWorkspaceSteeringDrift: z.boolean(),
}).strict()
export type AgentContextConnectEvidence = z.infer<typeof agentContextConnectEvidenceSchema>
export const agentContextDiagnosticsReportSchema = z.object({
schemaVersion: z.literal(AGENT_CONTEXT_DIAGNOSTICS_SCHEMA_VERSION),
runId: z.string().uuid(),
startedAt: z.string().datetime(),
completedAt: z.string().datetime(),
durationMs: z.number().int().nonnegative(),
overall: agentContextDiagnosticOverallSchema,
firstFailedCheck: agentContextDiagnosticCheckIdSchema.nullable(),
workspace: z.object({
id: safeTextSchema.min(1).max(160),
name: safeTextSchema.min(1).max(240),
type: z.enum(["local", "remote"]),
remoteType: z.enum(["opencode", "openwork"]).nullable(),
engineConfigured: z.boolean(),
}).strict(),
checks: z.array(agentContextDiagnosticCheckSchema).length(AGENT_CONTEXT_DIAGNOSTIC_CHECK_IDS.length),
agent: agentContextAgentEvidenceSchema,
mcps: z.array(agentContextMcpEvidenceSchema).max(200),
connect: agentContextConnectEvidenceSchema,
observedCloudToolIds: uniqueToolIdsSchema(100),
organizationConnectionsProbe: agentContextOrganizationConnectionsProbeSchema,
organizationConnections: z.array(agentContextOrganizationConnectionSummarySchema).max(200),
safety: z.object({
diagnosticsWorkspaceRuntimeConfigurationReadOnly: z.literal(true),
cloudCatalogToolsListPerformed: z.boolean(),
credentialFreeTransportProbePerformed: z.boolean(),
cloudSessionCleanupRequested: z.boolean(),
directNonCloudMcpFetchPerformed: z.literal(false),
directMcpToolCallPerformed: z.literal(false),
directProviderOperationPerformed: z.literal(false),
directConfigurationMutationPerformed: z.literal(false),
directEphemeralCredentialMintPerformed: z.literal(false),
engineApiReadPerformed: z.boolean(),
engineBootstrapMayHaveRun: z.boolean(),
engineBootstrapSideEffectsInspected: z.literal(false),
authSessionActivityMayBeRecorded: z.literal(true),
tokenValuesIncluded: z.literal(false),
authorizationHeaderValuesIncluded: z.literal(false),
credentialValuesIncluded: z.literal(false),
rawPromptsIncluded: z.literal(false),
providerResponsesIncluded: z.literal(false),
stackTracesIncluded: z.literal(false),
rawEngineErrorsIncluded: z.literal(false),
sanitizedEngineErrorSummariesIncluded: z.boolean(),
secretBearingUrlsIncluded: z.literal(false),
inputStrictlyValidated: z.literal(true),
}).strict(),
}).strict().superRefine((value, context) => {
const ids = value.checks.map((check) => check.id)
const unique = new Set(ids)
const expected = new Set(AGENT_CONTEXT_DIAGNOSTIC_CHECK_IDS)
if (unique.size !== expected.size || ids.some((id) => !expected.has(id))) {
context.addIssue({ code: "custom", message: "checks must contain each diagnostic check exactly once", path: ["checks"] })
}
ids.forEach((id, index) => {
if (id !== AGENT_CONTEXT_DIAGNOSTIC_CHECK_IDS[index]) {
context.addIssue({
code: "custom",
message: "checks must use canonical diagnostic order",
path: ["checks", index, "id"],
})
}
})
const expectedOverall = value.checks.some((check) => check.status === "failed")
? "failed"
: value.checks.some((check) => check.status === "warning")
? "warning"
: "passed"
if (value.overall !== expectedOverall) {
context.addIssue({
code: "custom",
message: "overall must match the highest-severity check",
path: ["overall"],
})
}
const expectedFirstFailedCheck = value.checks.find((check) => check.status === "failed")?.id ?? null
if (value.firstFailedCheck !== expectedFirstFailedCheck) {
context.addIssue({
code: "custom",
message: "firstFailedCheck must identify the first failed check",
path: ["firstFailedCheck"],
})
}
if (value.organizationConnectionsProbe.status !== "observed" && value.organizationConnections.length > 0) {
context.addIssue({
code: "custom",
message: "organization connection rows require an observed organization probe",
path: ["organizationConnections"],
})
}
if (value.organizationConnectionsProbe.status === "observed") {
const { totalCount, truncated } = value.organizationConnectionsProbe
if (totalCount < value.organizationConnections.length) {
context.addIssue({
code: "custom",
message: "observed totalCount cannot be smaller than the reported rows",
path: ["organizationConnectionsProbe", "totalCount"],
})
}
if (truncated !== (totalCount > value.organizationConnections.length)) {
context.addIssue({
code: "custom",
message: "truncated must describe whether observed rows were omitted",
path: ["organizationConnectionsProbe", "truncated"],
})
}
}
if (!value.safety.cloudCatalogToolsListPerformed && value.observedCloudToolIds.length > 0) {
context.addIssue({
code: "custom",
message: "observed cloud tools require a completed cloud tools/list request",
path: ["observedCloudToolIds"],
})
}
if (!value.safety.engineApiReadPerformed) {
value.checks.forEach((check, index) => {
if (check.id !== "engine-config" && check.id !== "engine-agent") return
if (check.status !== "warning" || check.evidenceKind !== "unavailable") {
context.addIssue({
code: "custom",
message: "engine config and agent checks require unavailable warnings when no engine API read ran",
path: ["checks", index],
})
}
})
}
const engineConfigCheck = value.checks.find((check) => check.id === "engine-config")
const engineAgentCheck = value.checks.find((check) => check.id === "engine-agent")
if (value.agent.evidenceSource === "effective-engine") {
if (!value.safety.engineApiReadPerformed) {
context.addIssue({
code: "custom",
message: "effective engine evidence requires an engine API read",
path: ["agent", "evidenceSource"],
})
}
if (engineConfigCheck?.status !== "passed" || engineConfigCheck.evidenceKind !== "observed") {
context.addIssue({
code: "custom",
message: "effective engine evidence requires an observed engine configuration",
path: ["checks"],
})
}
if (engineAgentCheck?.evidenceKind !== "observed") {
context.addIssue({
code: "custom",
message: "effective engine evidence requires an observed agent response",
path: ["checks"],
})
}
} else if (value.mcps.some((mcp) => mcp.source === "engine.config")) {
context.addIssue({
code: "custom",
message: "engine configuration MCP sources require effective engine evidence",
path: ["mcps"],
})
}
if (value.safety.engineBootstrapMayHaveRun !== value.safety.engineApiReadPerformed) {
context.addIssue({
code: "custom",
message: "engine bootstrap possibility must track whether an engine API read was attempted",
path: ["safety", "engineBootstrapMayHaveRun"],
})
}
// Engine registration state and agent tool policy are deliberately not
// preconditions here: the independent runtime probe exists to diagnose
// engine-side failures, and it never calls a tool. Retained runtime
// evidence must still prove which managed entry was probed.
if (
value.safety.cloudCatalogToolsListPerformed
|| value.safety.cloudSessionCleanupRequested
|| value.safety.credentialFreeTransportProbePerformed
) {
const runtimeCloudMcp = value.mcps.find((mcp) =>
mcp.source === "config.remote"
&& mcp.name === "openwork-cloud"
&& mcp.path !== null
)
if (!runtimeCloudMcp) {
context.addIssue({
code: "custom",
message: "cloud handshake evidence requires the retained runtime OpenWork Cloud entry",
path: ["mcps"],
})
}
}
})
export type AgentContextDiagnosticsReport = z.infer<typeof agentContextDiagnosticsReportSchema>
+492
View File
@@ -0,0 +1,492 @@
import { z } from "zod"
const idSchema = z.string().trim().min(1).max(160)
const timestampSchema = z.number().int().nonnegative()
const nullableTimestampSchema = timestampSchema.nullable()
const timezoneSchema = z.string().trim().min(1).max(120).refine((timezone) => {
try {
new Intl.DateTimeFormat("en", { timeZone: timezone }).format(new Date(0))
return true
} catch {
return false
}
}, "Expected a valid IANA timezone")
export const automationStateSchema = z.enum(["active", "inactive", "needs_attention", "archived"])
export type AutomationState = z.infer<typeof automationStateSchema>
export const automationRunStatusSchema = z.enum([
"queued", "claimed", "running", "succeeded", "failed", "cancelled", "skipped",
])
export type AutomationRunStatus = z.infer<typeof automationRunStatusSchema>
export const automationRunTriggerSchema = z.enum(["scheduled", "recovery", "manual"])
export type AutomationRunTrigger = z.infer<typeof automationRunTriggerSchema>
export const automationScheduleSchema = z.discriminatedUnion("kind", [
z.object({ kind: z.literal("once"), timezone: timezoneSchema, at: timestampSchema }),
z.object({
kind: z.literal("daily"),
timezone: timezoneSchema,
hour: z.number().int().min(0).max(23),
minute: z.number().int().min(0).max(59),
}),
z.object({
kind: z.literal("weekly"),
timezone: timezoneSchema,
daysOfWeek: z.array(z.number().int().min(0).max(6)).min(1).max(7)
.transform((days) => [...new Set(days)].sort((left, right) => left - right)),
hour: z.number().int().min(0).max(23),
minute: z.number().int().min(0).max(59),
}),
])
export type AutomationSchedule = z.infer<typeof automationScheduleSchema>
/**
* `variant` is the model's reasoning/thinking level, the same value the
* composer sends as its behavior pill. It is optional because most models
* expose no variants, and null means "whatever the provider defaults to".
*/
export const automationModelSchema = z.object({
providerId: idSchema,
modelId: idSchema,
variant: z.string().trim().min(1).max(60).nullable().optional(),
})
export type AutomationModel = z.infer<typeof automationModelSchema>
export const automationSavedScriptReferenceSchema = z.object({
pluginId: idSchema,
configObjectId: idSchema,
configObjectVersionId: idSchema,
}).strict()
export type AutomationSavedScriptReference = z.infer<typeof automationSavedScriptReferenceSchema>
export const automationActionSchema = z.discriminatedUnion("kind", [
z.object({
kind: z.literal("agent"),
instructions: z.string().trim().min(1).max(100_000),
model: automationModelSchema,
}).strict(),
z.object({
kind: z.literal("saved_script"),
script: automationSavedScriptReferenceSchema,
input: z.unknown().optional(),
}).strict(),
])
export type AutomationAction = z.infer<typeof automationActionSchema>
/**
* Canonical identity for the free Automation starter model. Runtime provider
* configuration belongs to the desktop's OpenCode installation, not Den.
*/
export const AUTOMATION_FREE_MODEL = {
providerId: "opencode",
modelId: "big-pickle",
providerName: "OpenCode Zen",
modelName: "Big Pickle",
} as const
export const automationNeedsAttentionReasonSchema = z.object({
code: z.enum([
"owner_membership_lost",
"model_access_lost",
"provider_unavailable",
"connect_access_unavailable",
"execution_runtime_unavailable",
]),
message: z.string().trim().min(1).max(2_000),
occurredAt: timestampSchema,
})
export type AutomationNeedsAttentionReason = z.infer<typeof automationNeedsAttentionReasonSchema>
export const automationSchema = z.object({
id: idSchema,
organizationId: idSchema,
ownerMemberId: idSchema,
name: z.string().trim().min(1).max(120),
state: automationStateSchema,
currentRevisionId: idSchema,
nextDueAt: nullableTimestampSchema,
latestRunAt: nullableTimestampSchema,
latestSuccessfulRunId: idSchema.nullable().optional(),
latestSuccessfulResult: z.unknown().optional(),
needsAttentionReason: automationNeedsAttentionReasonSchema.nullable(),
createdAt: timestampSchema,
updatedAt: timestampSchema,
archivedAt: nullableTimestampSchema,
})
export type Automation = z.infer<typeof automationSchema>
/**
* The workspace an Automation is pinned to at creation/update time.
*
* Before this field existed, both executors resolved the runner's *currently
* active* workspace at run time, so activating a different workspace silently
* retargeted every automation. A pinned id makes targeting explicit; null
* keeps the legacy active-workspace fallback for existing records.
*/
export const automationWorkspaceIdSchema = z.string().trim().min(1).max(240)
export const automationRevisionSchema = z.object({
id: idSchema,
automationId: idSchema,
version: z.number().int().positive(),
instructions: z.string().trim().min(1).max(100_000),
schedule: automationScheduleSchema,
model: automationModelSchema,
action: automationActionSchema.optional(),
executionTarget: z.enum(["desktop", "cloud"]).optional(),
workspaceId: automationWorkspaceIdSchema.nullable().optional(),
maximumRuntimeMs: z.number().int().min(10_000).max(60 * 60 * 1_000),
digest: z.string().trim().min(16).max(128),
createdAt: timestampSchema,
})
export type AutomationRevision = z.infer<typeof automationRevisionSchema>
export const automationErrorSchema = z.object({
code: z.enum([
"owner_membership_lost",
"model_access_lost",
"provider_unavailable",
"connect_access_unavailable",
"execution_runtime_unavailable",
"execution_failed",
"execution_timed_out",
"runner_unavailable",
"cancelled",
"lease_lost",
"internal_error",
]),
message: z.string().trim().min(1).max(2_000),
retryable: z.boolean(),
})
export type AutomationError = z.infer<typeof automationErrorSchema>
export const automationUsageSchema = z.object({
inputTokens: z.number().int().nonnegative().nullable(),
outputTokens: z.number().int().nonnegative().nullable(),
costMicros: z.number().int().nonnegative().nullable(),
})
export type AutomationUsage = z.infer<typeof automationUsageSchema>
export const automationExecutionThreadSchema = z.object({
id: idSchema,
threadKind: z.literal("automation"),
executionLocation: z.enum(["desktop", "cloud"]),
automationId: idSchema,
automationRunId: idSchema,
engineKind: idSchema,
/** Native OpenCode session identity for agent runs. */
nativeThreadId: idSchema.nullable().optional(),
workspaceId: idSchema.nullable().optional(),
})
export type AutomationExecutionThread = z.infer<typeof automationExecutionThreadSchema>
/** Creation surface fixes execution placement: Desktop stays local; Web runs in Cloud. */
export const automationExecutionTargetSchema = z.enum(["desktop", "cloud"])
export type AutomationExecutionTarget = z.infer<typeof automationExecutionTargetSchema>
export const AUTOMATION_MODEL_ATTENTION_CAPABILITY = "model_attention_v1" as const
export const REMOTE_SESSION_DESKTOP_RUNNER_CAPABILITY = "remote_session_v1"
export const AUTOMATION_MODEL_ATTENTION_CAPABILITY_HEADER = "x-openwork-automation-model-attention" as const
export const automationDesktopRunnerCapabilitySchema = z.enum([
AUTOMATION_MODEL_ATTENTION_CAPABILITY,
REMOTE_SESSION_DESKTOP_RUNNER_CAPABILITY,
])
export type AutomationDesktopRunnerCapability = z.infer<typeof automationDesktopRunnerCapabilitySchema>
export const automationDesktopRunnerRegistrationSchema = z.object({
runnerId: idSchema.min(8),
protocolVersion: z.literal(1),
supportedExecutionTargets: z.array(z.literal("desktop")).length(1),
capabilities: z.array(automationDesktopRunnerCapabilitySchema).max(2).default([]),
appVersion: z.string().trim().min(1).max(80),
platform: z.enum(["darwin", "win32", "linux"]),
concurrency: z.number().int().min(1).max(4),
})
export type AutomationDesktopRunnerRegistration = z.infer<typeof automationDesktopRunnerRegistrationSchema>
/**
* How long a desktop counts as connected after it was last seen. Registration
* refreshes presence every few minutes and idle event streams deliberately do
* not write to the database, so this is generous enough to survive an idle
* desktop and short enough to catch one that was closed.
*/
export const AUTOMATION_DESKTOP_RUNNER_PRESENCE_WINDOW_MS = 10 * 60_000
/** Whether a Desktop Automation has anywhere to run right now. */
export const automationDesktopRunnerPresenceSchema = z.object({
connected: z.boolean(),
lastSeenAt: timestampSchema.nullable(),
})
export type AutomationDesktopRunnerPresence = z.infer<typeof automationDesktopRunnerPresenceSchema>
export const automationRunnerNotificationSchema = z.object({
type: z.enum(["automation_work_available", "automation_cancellation_available"]),
cursor: z.string().trim().min(1).max(40),
}).strict()
export type AutomationRunnerNotification = z.infer<typeof automationRunnerNotificationSchema>
export const automationRunnerWorkItemSchema = z.union([
// The automation-run item shape predates remote-session commands and is
// consumed by released desktop runners: it must keep every field it has
// always carried.
z.object({ runId: idSchema, executionTarget: z.literal("desktop") }),
z.object({ kind: z.literal("remote_session_create"), commandId: idSchema }),
])
export const automationRunnerWorkResponseSchema = z.object({
items: z.array(automationRunnerWorkItemSchema).max(9),
})
export type AutomationRunnerWorkResponse = z.infer<typeof automationRunnerWorkResponseSchema>
export const remoteSessionCommandAssignmentSchema = z.object({
commandId: idSchema,
kind: z.literal("remote_session_create"),
title: z.string().trim().min(1).max(120),
prompt: z.string().min(1).max(100_000).nullable(),
model: z.object({
providerId: idSchema,
modelId: idSchema,
variant: z.string().trim().min(1).max(60).nullable(),
}).nullable(),
expiresAt: timestampSchema,
})
export const remoteSessionCommandClaimResponseSchema = z.object({
assignment: remoteSessionCommandAssignmentSchema,
})
const remoteSessionCommandResultSummarySchema = z.string().max(4096).optional()
const remoteSessionCommandErrorSchema = z.object({
code: z.string().trim().min(1).max(60),
message: z.string().trim().min(1).max(2000),
})
export const remoteSessionCommandCompleteRequestSchema = z.discriminatedUnion("status", [
z.object({
status: z.literal("delivered"),
sessionId: z.string().trim().min(1).max(240),
workspaceId: z.string().trim().min(1).max(240),
resultSummary: remoteSessionCommandResultSummarySchema,
error: z.never().optional(),
}),
z.object({
status: z.literal("failed"),
sessionId: z.never().optional(),
workspaceId: z.never().optional(),
resultSummary: remoteSessionCommandResultSummarySchema,
error: remoteSessionCommandErrorSchema,
}),
])
export type RemoteSessionCommandCompleteRequest = z.infer<typeof remoteSessionCommandCompleteRequestSchema>
export const remoteSessionCommandCompleteResponseSchema = z.object({
command: z.object({
id: idSchema,
status: z.enum(["delivered", "failed"]),
sessionId: z.string().max(240).nullable(),
workspaceId: z.string().max(240).nullable(),
}),
})
export const automationDesktopRunnerAssignmentSchema = z.object({
executionTarget: z.literal("desktop"),
runId: idSchema,
automationId: idSchema,
automationName: z.string().trim().min(1).max(120),
instructions: z.string().trim().min(1).max(100_000),
model: automationModelSchema,
timeoutMs: z.number().int().min(10_000).max(60 * 60 * 1_000),
leaseExpiresAt: timestampSchema,
attempt: z.number().int().positive(),
/** Pinned target workspace; absent for records created before pinning existed. */
workspaceId: automationWorkspaceIdSchema.nullable().optional(),
})
export type AutomationDesktopRunnerAssignment = z.infer<typeof automationDesktopRunnerAssignmentSchema>
export const automationDesktopRunnerResultSchema = z.object({
attempt: z.number().int().positive(),
status: z.enum(["succeeded", "failed", "cancelled"]),
sessionId: z.string().trim().min(1).max(240).nullable(),
workspaceId: z.string().trim().min(1).max(240).nullable(),
resultSummary: z.string().max(20_000).nullable(),
usage: automationUsageSchema,
error: automationErrorSchema.nullable(),
})
export type AutomationDesktopRunnerResult = z.infer<typeof automationDesktopRunnerResultSchema>
export const automationRunnerHeartbeatRequestSchema = z.object({ attempt: z.number().int().positive() })
export const automationRunnerHeartbeatResponseSchema = z.object({
attempt: z.number().int().positive(),
leaseValid: z.literal(true),
cancelRequested: z.boolean(),
leaseExpiresAt: timestampSchema,
})
export const automationRunnerEventRequestSchema = z.object({
attempt: z.number().int().positive(),
sequence: z.number().int().positive(),
type: z.enum(["user", "assistant", "capability_search", "capability_execution", "usage", "warning", "terminal"]),
payload: z.record(z.string(), z.unknown()),
createdAt: timestampSchema,
})
export const automationRunnerLeaseRejectionSchema = z.object({
error: z.literal("runner_lease_lost"),
})
export const automationRunnerUnavailableOutcomeSchema = z.object({
status: z.literal("skipped"),
reason: z.literal("runner_unavailable"),
executionTarget: automationExecutionTargetSchema,
})
export const automationRunnerTokenResponseSchema = z.object({
token: z.string().trim().min(32).max(512),
expiresAt: timestampSchema,
eventsPath: z.literal("/v1/automation-runners/events"),
})
export type AutomationRunnerTokenResponse = z.infer<typeof automationRunnerTokenResponseSchema>
export const automationRunSchema = z.object({
id: idSchema,
automationId: idSchema,
revisionId: idSchema,
trigger: automationRunTriggerSchema,
scheduledFor: nullableTimestampSchema,
idempotencyKey: z.string().trim().min(1).max(512),
status: automationRunStatusSchema,
leaseOwner: z.string().trim().min(1).max(240).nullable(),
leaseExpiresAt: nullableTimestampSchema,
heartbeatAt: nullableTimestampSchema,
attemptCount: z.number().int().min(0).max(2),
executionTarget: automationExecutionTargetSchema,
executionThread: automationExecutionThreadSchema.nullable(),
providerId: idSchema,
modelId: idSchema,
modelVariant: z.string().trim().min(1).max(60).nullable().default(null),
startedAt: nullableTimestampSchema,
finishedAt: nullableTimestampSchema,
error: automationErrorSchema.nullable(),
resultSummary: z.string().max(20_000).nullable(),
codemodeReceiptId: idSchema.nullable().optional(),
validatedResult: z.unknown().optional(),
usage: automationUsageSchema,
createdAt: timestampSchema,
updatedAt: timestampSchema,
})
export type AutomationRun = z.infer<typeof automationRunSchema>
export const automationRunEventTypeSchema = z.enum([
"user", "assistant", "capability_search", "capability_execution", "usage", "warning", "terminal",
])
export type AutomationRunEventType = z.infer<typeof automationRunEventTypeSchema>
export const automationRunEventSchema = z.object({
id: idSchema,
runId: idSchema,
attempt: z.number().int().positive(),
sequence: z.number().int().positive(),
type: automationRunEventTypeSchema,
payload: z.record(z.string(), z.unknown()),
createdAt: timestampSchema,
})
export type AutomationRunEvent = z.infer<typeof automationRunEventSchema>
const legacyCreateAutomationSchema = z.object({
name: z.string().trim().min(1).max(120),
instructions: z.string().trim().min(1).max(100_000),
schedule: automationScheduleSchema,
model: automationModelSchema,
workspaceId: automationWorkspaceIdSchema.nullable().optional(),
})
const actionCreateAutomationSchema = z.object({
name: z.string().trim().min(1).max(120),
schedule: automationScheduleSchema,
action: automationActionSchema,
executionTarget: automationExecutionTargetSchema,
}).superRefine((value, context) => {
const validPair = value.executionTarget === "cloud"
if (!validPair) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: "Action-based Automations are created by Web and run in OpenWork Cloud.",
path: ["executionTarget"],
})
}
})
/** Cloud Chat/Web creation cannot express Desktop placement. */
export const createCloudAutomationSchema = z.object({
name: z.string().trim().min(1).max(120),
schedule: automationScheduleSchema,
action: automationActionSchema,
}).strict().transform((value) => ({ ...value, executionTarget: "cloud" as const }))
export type CreateCloudAutomation = z.input<typeof createCloudAutomationSchema>
export const createAutomationSchema = z.union([actionCreateAutomationSchema, legacyCreateAutomationSchema])
/**
* Published desktop clients still construct the legacy agent definition.
* Keep that source-level contract stable while Den accepts the expanded
* canonical definition through the separately named server type.
*/
export type CreateAutomation = z.infer<typeof legacyCreateAutomationSchema>
export type CreateAutomationDefinition = z.infer<typeof createAutomationSchema>
/**
* What an in-app agent may hand back when a person describes recurring work.
*
* A proposal is inert: it names the Automation the person could create, and
* nothing more. Automations are active the moment they exist, so creation stays
* behind an explicit human action in the renderer, which owns the Den session.
* The model is optional because the renderer resolves the person's own default.
*/
export const automationProposalSchema = z.object({
name: z.string().trim().min(1).max(120),
instructions: z.string().trim().min(1).max(100_000),
schedule: automationScheduleSchema,
model: automationModelSchema.optional(),
/** Workspace the proposing conversation ran in; the renderer pins it on create. */
workspaceId: automationWorkspaceIdSchema.optional(),
})
export type AutomationProposal = z.infer<typeof automationProposalSchema>
export const updateAutomationSchema = z.object({
name: z.string().trim().min(1).max(120).optional(),
instructions: z.string().trim().min(1).max(100_000).optional(),
schedule: automationScheduleSchema.optional(),
model: automationModelSchema.optional(),
action: automationActionSchema.optional(),
/** Accepted for round-tripping; execution placement itself is immutable. */
executionTarget: automationExecutionTargetSchema.optional(),
/** Re-pin to a different workspace; null clears the pin (legacy active-workspace fallback). */
workspaceId: automationWorkspaceIdSchema.nullable().optional(),
}).strict().refine(
(input) => Object.keys(input).length > 0,
"At least one behavior-changing field is required",
)
export type UpdateAutomation = z.infer<typeof updateAutomationSchema>
export const automationListSchema = z.object({
items: z.array(z.object({
automation: automationSchema,
revision: automationRevisionSchema,
latestRun: automationRunSchema.nullable(),
})),
nextCursor: z.string().nullable(),
})
export type AutomationList = z.infer<typeof automationListSchema>
export const automationDetailSchema = z.object({
automation: automationSchema,
revision: automationRevisionSchema,
latestRun: automationRunSchema.nullable(),
})
export type AutomationDetail = z.infer<typeof automationDetailSchema>
export const automationRunReceiptSchema = z.object({
run: automationRunSchema,
automation: automationSchema,
revision: automationRevisionSchema,
events: z.array(automationRunEventSchema),
})
export type AutomationRunReceipt = z.infer<typeof automationRunReceiptSchema>
export const AUTOMATION_MAXIMUM_ATTEMPTS = 2
export const AUTOMATION_RETRY_DELAY_MS = 30_000
export const AUTOMATION_DEFAULT_MAXIMUM_RUNTIME_MS = 15 * 60_000
export const AUTOMATION_MAXIMUM_RUNTIME_MS = 60 * 60_000
+73
View File
@@ -0,0 +1,73 @@
// Shared contract for organization connect links. The default transport is a
// short-lived HTTPS exchange; an optional Ed25519 JWS transport is available
// when its public key ships in the desktop build. Both carry configuration
// provenance only — never authentication.
export const CONNECT_LINK_ROUTE = "connect";
export const CONNECT_LINK_AUDIENCE = "openwork-desktop-connect";
export const CONNECT_LINK_VERSION = 1;
export const CONNECT_LINK_ALGORITHM = "EdDSA";
export const CONNECT_LINK_DEFAULT_TTL_HOURS = 72;
export const CONNECT_LINK_MAX_TTL_HOURS = 168;
export const CONNECT_LINK_EXCHANGE_TTL_MINUTES = 5;
export type ConnectLinkOrg = {
name: string;
};
/** Maps one-for-one to desktop-bootstrap.json's managed branding fields. */
export type ConnectLinkBrand = {
appName: string;
logoUrl: string | null;
iconUrl: string | null;
};
export type ConnectLinkDenTarget = {
baseUrl: string;
apiBaseUrl?: string | null;
};
export type ConnectLinkClaims = {
iss: string;
aud: typeof CONNECT_LINK_AUDIENCE;
iat: number;
exp: number;
jti: string;
v: typeof CONNECT_LINK_VERSION;
org: ConnectLinkOrg;
brand: ConnectLinkBrand;
den: ConnectLinkDenTarget;
requireSignin: boolean;
};
export type ConnectLinkVerifyErrorCode =
| "invalid_token"
| "bad_signature"
| "unknown_kid"
| "expired"
| "not_yet_valid"
| "wrong_audience"
| "wrong_version"
| "insecure_url"
| "malformed_claims"
| "replayed"
| "unavailable";
export type ConnectLinkTransport = "signed" | "exchange";
export type ConnectLinkVerifySuccess = {
ok: true;
claims: ConnectLinkClaims;
transport: ConnectLinkTransport;
kid: string | null;
};
export type ConnectLinkVerifyFailure = {
ok: false;
code: ConnectLinkVerifyErrorCode;
message: string;
};
export type ConnectLinkVerifyResult =
| ConnectLinkVerifySuccess
| ConnectLinkVerifyFailure;
@@ -0,0 +1,51 @@
import { z } from "zod"
const idSchema = z.string().trim().min(1).max(160)
export const connectionActionAppSchemaVersion = "1" as const
export const connectionActionAppResourceUri = "ui://openwork/connection-action/v1/view.html"
export const connectionActionToolName = "connection_action"
/**
* Data contract for the first-party connection-action MCP App: one live
* status report for one Connect connection, including the exact human action
* (sign in, admin setup, provider fix) that unblocks it. `connected` is the
* healthy probe result; the other states mirror the gateway's
* ExternalConnectionStatus steering.
*/
export const connectionActionPayloadSchema = z.object({
schemaVersion: z.literal(connectionActionAppSchemaVersion),
connectionId: idSchema,
connectionName: z.string().trim().min(1).max(255),
state: z.enum(["connected", "needs_connection", "reauth_required", "provider_error"]),
actor: z.enum([
"member",
"organization_admin",
"provider_admin",
"network_admin",
"openwork",
]).nullable(),
message: z.string().trim().min(1).max(2_000),
action: z.object({
type: z.enum([
"connect",
"reconnect",
"update_credentials",
"inspect_connection",
"fix_provider",
"fix_network",
"contact_openwork",
]),
label: z.string().trim().min(1).max(255),
surface: z.enum([
"openwork_your_connections",
"openwork_organization_connections",
"provider_admin_console",
"network_infrastructure",
"openwork_support",
]),
url: z.string().url().optional(),
}).nullable(),
})
export type ConnectionActionPayload = z.infer<typeof connectionActionPayloadSchema>
@@ -0,0 +1,139 @@
import { z } from "zod"
export const CONNECT_DIAGNOSTIC_CLIENT_HEADER = "x-openwork-connect-client"
export const CONNECT_DIAGNOSTIC_RETENTION_SECONDS = 7 * 24 * 60 * 60
export const CONNECT_DIAGNOSTIC_CLIENT_RETENTION_MS = 24 * 60 * 60 * 1_000
export const CONNECT_DIAGNOSTIC_PHASES = [
"prerequisites",
"token_mint",
"desired_config",
"engine_delivery",
"transport_auth",
"tool_registration",
"provider_projection",
"plugin_load",
"steering",
"initialize",
"initialized_notice",
"tools_list",
"mcp_request",
] as const
export const connectDiagnosticPhaseSchema = z.enum(CONNECT_DIAGNOSTIC_PHASES)
export type ConnectDiagnosticPhase = z.infer<typeof connectDiagnosticPhaseSchema>
export const CONNECT_DIAGNOSTIC_NETWORK_CODES = [
"ENOTFOUND",
"EAI_AGAIN",
"ECONNREFUSED",
"ECONNRESET",
"ETIMEDOUT",
"CERT_HAS_EXPIRED",
"SELF_SIGNED_CERT_IN_CHAIN",
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
"ERR_TLS_CERT_ALTNAME_INVALID",
"ABORT_ERR",
"FETCH_FAILED",
] as const
export const connectDiagnosticNetworkCodeSchema = z.enum(CONNECT_DIAGNOSTIC_NETWORK_CODES)
export type ConnectDiagnosticNetworkCode = z.infer<typeof connectDiagnosticNetworkCodeSchema>
export const connectDiagnosticOutcomeSchema = z.enum(["ok", "failure", "recovered"])
export type ConnectDiagnosticOutcome = z.infer<typeof connectDiagnosticOutcomeSchema>
const safeCodeSchema = z
.string()
.trim()
.min(1)
.max(120)
.regex(/^[a-z0-9][a-z0-9_.-]*$/i)
const boundedVersionSchema = z
.string()
.trim()
.min(1)
.max(80)
.regex(/^[a-z0-9][a-z0-9.+_() -]*$/i)
const pseudonymSchema = z.string().regex(/^[a-f0-9]{64}$/)
const requestIdSchema = z
.string()
.trim()
.min(1)
.max(128)
.regex(/^[a-z0-9][a-z0-9_.:-]*$/i)
/**
* Metadata-only report produced by the desktop. Organization identity comes
* from the authenticated Den route, never from this caller-controlled body.
*/
export const connectDiagnosticClientEventSchema = z.object({
schemaVersion: z.literal(1),
eventId: z.string().uuid(),
attemptId: z.string().uuid(),
clientId: z.string().uuid(),
observedAt: z.string().datetime(),
phase: connectDiagnosticPhaseSchema,
outcome: connectDiagnosticOutcomeSchema,
errorCode: safeCodeSchema.nullable(),
networkCode: connectDiagnosticNetworkCodeSchema.nullable(),
httpStatus: z.number().int().min(100).max(599).nullable(),
retryable: z.boolean().nullable(),
deviceOnline: z.boolean().nullable(),
durationMs: z.number().int().min(0).max(10 * 60 * 1_000).nullable(),
consecutiveFailures: z.number().int().min(0).max(10_000),
maintenanceAttempt: z.number().int().min(1).max(20),
appVersion: boundedVersionSchema.nullable(),
platform: z.enum(["macos", "windows", "linux", "other"]).nullable(),
serverVersion: boundedVersionSchema.nullable(),
engineVersion: boundedVersionSchema.nullable(),
serverRequestId: requestIdSchema.nullable(),
}).strict()
export type ConnectDiagnosticClientEvent = z.infer<typeof connectDiagnosticClientEventSchema>
export const connectDiagnosticClientBatchSchema = z.object({
events: z.array(connectDiagnosticClientEventSchema).min(1).max(50),
}).strict()
export type ConnectDiagnosticClientBatch = z.infer<typeof connectDiagnosticClientBatchSchema>
/**
* Already-pseudonymized event accepted by the public diagnostics service.
* Den is the only producer and signs the request with its operator-owned
* diagnostics bearer. No member identity or customer content is represented.
*/
export const connectDiagnosticIncidentSchema = z.object({
schemaVersion: z.literal(1),
eventId: z.string().uuid(),
attemptId: z.string().uuid().nullable(),
source: z.enum(["desktop", "den"]),
observedAt: z.string().datetime(),
organizationHash: pseudonymSchema,
clientHash: pseudonymSchema.nullable(),
phase: connectDiagnosticPhaseSchema,
outcome: connectDiagnosticOutcomeSchema,
errorCode: safeCodeSchema.nullable(),
networkCode: connectDiagnosticNetworkCodeSchema.nullable(),
httpStatus: z.number().int().min(100).max(599).nullable(),
retryable: z.boolean().nullable(),
deviceOnline: z.boolean().nullable(),
durationMs: z.number().int().min(0).max(10 * 60 * 1_000).nullable(),
consecutiveFailures: z.number().int().min(0).max(10_000),
maintenanceAttempt: z.number().int().min(1).max(20).nullable(),
appVersion: boundedVersionSchema.nullable(),
platform: z.enum(["macos", "windows", "linux", "other"]).nullable(),
serverVersion: boundedVersionSchema.nullable(),
engineVersion: boundedVersionSchema.nullable(),
serverRequestId: requestIdSchema.nullable(),
}).strict()
export type ConnectDiagnosticIncident = z.infer<typeof connectDiagnosticIncidentSchema>
export const connectDiagnosticIncidentBatchSchema = z.object({
incidents: z.array(connectDiagnosticIncidentSchema).min(1).max(50),
}).strict()
export type ConnectDiagnosticIncidentBatch = z.infer<typeof connectDiagnosticIncidentBatchSchema>
export const storedConnectDiagnosticIncidentSchema = connectDiagnosticIncidentSchema.extend({
receivedAt: z.string().datetime(),
})
export type StoredConnectDiagnosticIncident = z.infer<typeof storedConnectDiagnosticIncidentSchema>
@@ -0,0 +1,75 @@
import { z } from "zod"
export const desktopAppRestrictionsSchema = z.object({
disallowNonCloudModels: z.boolean().optional(),
blockZenModel: z.boolean().optional(),
blockMultipleWorkspaces: z.boolean().optional(),
}).meta({ ref: "DenDesktopAppRestrictions" })
export type DesktopAppRestrictions = z.infer<typeof desktopAppRestrictionsSchema>
export const desktopConfigSchema = desktopAppRestrictionsSchema.extend({
allowedDesktopVersions: z.array(z.string().trim().min(1).max(32)).optional(),
}).meta({ ref: "DenDesktopConfig" })
export type DesktopConfig = z.infer<typeof desktopConfigSchema>
function normalizeDesktopVersionString(value: unknown) {
if (typeof value !== "string") {
return null
}
const normalized = value.trim().replace(/^v/i, "")
return /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(normalized)
? normalized
: null
}
function normalizeAllowedDesktopVersions(value: unknown): string[] | undefined {
if (!Array.isArray(value)) {
return undefined
}
const versions = [
...new Set(
value
.map((entry) => normalizeDesktopVersionString(entry))
.filter((entry): entry is string => Boolean(entry)),
),
]
return versions
}
export function normalizeDesktopAppRestrictions(value: unknown): DesktopAppRestrictions {
const parsed = desktopAppRestrictionsSchema.safeParse(value)
if (parsed.success) {
return {
...(parsed.data.disallowNonCloudModels === true ? { disallowNonCloudModels: true } : {}),
...(parsed.data.blockZenModel === true ? { blockZenModel: true } : {}),
...(parsed.data.blockMultipleWorkspaces === true ? { blockMultipleWorkspaces: true } : {}),
}
}
const legacy = value as {
models?: {
removeZen?: unknown
}
} | null
return {
...(legacy?.models?.removeZen === true ? { blockZenModel: true } : {}),
}
}
export function normalizeDesktopConfig(value: unknown): DesktopConfig {
const restrictions = normalizeDesktopAppRestrictions(value)
const allowedDesktopVersions = normalizeAllowedDesktopVersions(
(value as { allowedDesktopVersions?: unknown } | null)?.allowedDesktopVersions,
)
return {
...restrictions,
...(allowedDesktopVersions !== undefined ? { allowedDesktopVersions } : {}),
}
}
+579
View File
@@ -0,0 +1,579 @@
import { z } from "zod";
type DesktopPolicyDefinitionEntry = {
id: string;
name: string;
description: string;
userNotice: string;
defaultValue: boolean;
};
// Canonical desktop policy catalog.
//
// To add a new desktop policy item:
// 1. Add a matching entry to `desktopPolicyDefinitions` below.
// 2. Choose a safe `defaultValue` for orgs with missing/older policy data.
// 3. Wire desktop app behavior to read the key through the desktop config hooks.
// 4. If the key affects Den web editing copy, update the `name`, `description`,
// and `userNotice` here rather than duplicating that copy elsewhere.
// 5. Do not manually edit `desktopPolicyValueSchema`; it is generated from the
// IDs in this definition list.
//
// Policy booleans usually use allow-style names. For every policy item,
// `false` means the feature is restricted/disabled; `true` or an omitted value
// means the app should not block the feature locally unless a default/effective
// policy calculation supplies `false`.
export const desktopPolicyDefinitions = [
{
id: "allowCustomProviders",
name: "Custom providers",
description:
"Allow users to add and use models that are not deployed through OpenWork Cloud.",
userNotice:
"Your organization administrator has disabled adding custom providers.",
defaultValue: true,
},
{
id: "allowZenModel",
name: "Enable OpenCode Zen Models",
description: "Allow users to use the built in models provided by OpenCode.",
userNotice: "Your administrator has disabled access to OpenCode Models.",
defaultValue: true,
},
{
id: "allowMultipleWorkspaces",
name: "Multiple workspaces",
description:
"Allow users to create or configure more than one workspace on their machine.",
userNotice:
"Your organization administrator has restricted access to adding additional workspaces.",
defaultValue: true,
},
{
id: "allowControlSettings",
name: "Control Settings",
description: "Allow users to access and change the desktop app settings.",
userNotice:
"Your organization administrator has disabled changing desktop app settings.",
defaultValue: true,
},
{
id: "allowManageExtensions",
name: "Manage Extensions",
description: "Allow users to install and manage extensions locally.",
userNotice:
"Your organization administrator has disabled local extension management.",
defaultValue: true,
},
{
id: "allowBuiltInExtensions",
name: "Built-in Extensions",
description:
"Allow users to see and use OpenWork's built-in extensions, including browser, image, and local-provider extensions.",
userNotice:
"Your organization administrator has disabled built-in OpenWork extensions.",
defaultValue: true,
},
{
id: "allowAlphaUpdates",
name: "Alpha updates",
description:
"Allow users to opt into experimental Alpha desktop updates.",
userNotice:
"Your organization administrator has disabled Alpha desktop updates.",
defaultValue: true,
},
{
id: "showWelcomePage",
name: "Welcome Page",
description: "Show the Getting Started page to new users.",
userNotice:
"Your organization administrator has disabled the Getting Started page.",
defaultValue: true,
},
] as const satisfies readonly DesktopPolicyDefinitionEntry[];
export type DesktopPolicyKey = (typeof desktopPolicyDefinitions)[number]["id"];
export type DesktopPolicyDefinition = Omit<DesktopPolicyDefinitionEntry, "id"> & {
id: DesktopPolicyKey;
};
const desktopPolicyValueShape = Object.fromEntries(
desktopPolicyDefinitions.map((definition) => [
definition.id,
z.boolean().optional(),
]),
) as { [key in DesktopPolicyKey]: z.ZodOptional<z.ZodBoolean> };
export const desktopPolicyValueSchema = z
.object(desktopPolicyValueShape)
.meta({ ref: "DenDesktopPolicyValue" });
export type DesktopPolicyValue = z.infer<typeof desktopPolicyValueSchema>;
export const onboardingPromptsSchema = z
.array(z.string().trim().min(1).max(500))
.min(2)
.max(3);
export const onboardingPromptDescriptionsSchema = z
.array(z.string().trim().max(120))
.min(2)
.max(3);
export type OnboardingPromptConfig = {
onboardingPrompts: string[];
onboardingPromptDescriptions?: string[];
};
export const desktopPolicyDocumentSchema = desktopPolicyValueSchema
.extend({
onboardingPrompts: onboardingPromptsSchema.optional(),
onboardingPromptDescriptions: onboardingPromptDescriptionsSchema.optional(),
})
.meta({ ref: "DenDesktopPolicyDocument" });
export const desktopPolicyDocumentWriteSchema = desktopPolicyValueSchema
.extend({
onboardingPrompts: onboardingPromptsSchema.nullable().optional(),
onboardingPromptDescriptions: onboardingPromptDescriptionsSchema
.nullable()
.optional(),
})
.meta({ ref: "DenDesktopPolicyDocumentWrite" });
export type DesktopPolicyDocument = z.infer<typeof desktopPolicyDocumentSchema>;
export type DesktopPolicyDocumentWrite = z.infer<
typeof desktopPolicyDocumentWriteSchema
>;
export type DefaultDesktopPolicyDocument = Required<DesktopPolicyValue> & {
onboardingPrompts?: string[];
onboardingPromptDescriptions?: string[];
};
export const desktopPolicyKeys = desktopPolicyDefinitions.map(
(definition) => definition.id,
) as DesktopPolicyKey[];
export const desktopPolicyDefaults = Object.fromEntries(
desktopPolicyDefinitions.map((definition) => [
definition.id,
definition.defaultValue,
]),
) as Required<DesktopPolicyValue>;
// ---------------------------------------------------------------------------
// Radix color families that can be used as a brand accent.
// ---------------------------------------------------------------------------
export const brandAccentColorValues = [
"blue",
"crimson",
"cyan",
"gold",
"grass",
"green",
"indigo",
"iris",
"jade",
"lime",
"mint",
"orange",
"pink",
"plum",
"purple",
"red",
"ruby",
"sky",
"teal",
"tomato",
"violet",
"yellow",
] as const;
export type BrandAccentColor = (typeof brandAccentColorValues)[number];
export const desktopConfigSchema = desktopPolicyValueSchema
.extend({
allowedDesktopVersions: z
.array(z.string().trim().min(1).max(32))
.optional(),
brandAppName: z.string().trim().min(1).max(64).optional(),
brandLogoUrl: z.string().url().max(2048).optional(),
brandIconUrl: z.string().url().max(2048).optional(),
brandAccentColor: z.enum(brandAccentColorValues).optional(),
automationsEnabled: z.boolean().optional(),
dashboardEnabled: z.boolean().optional(),
connectEnabled: z.boolean().optional(),
onboardingPrompts: onboardingPromptsSchema.optional(),
onboardingPromptDescriptions: onboardingPromptDescriptionsSchema.optional(),
})
.meta({ ref: "DenDesktopConfig" });
export type DesktopConfig = z.infer<typeof desktopConfigSchema>;
function normalizeDesktopVersionString(value: unknown) {
if (typeof value !== "string") {
return null;
}
const normalized = value.trim().replace(/^v/i, "");
return /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(
normalized,
)
? normalized
: null;
}
function normalizeAllowedDesktopVersions(value: unknown): string[] | undefined {
if (!Array.isArray(value)) {
return undefined;
}
return [
...new Set(
value
.map((entry) => normalizeDesktopVersionString(entry))
.filter((entry): entry is string => Boolean(entry)),
),
];
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}
/** JSON columns arrive as strings on engines like MariaDB (JSON = LONGTEXT alias). */
function coerceJsonRecord(value: unknown): unknown {
if (typeof value !== "string") return value;
try {
return JSON.parse(value);
} catch {
return value;
}
}
export function normalizeOnboardingPrompts(value: unknown): string[] | undefined {
const parsed = onboardingPromptsSchema.safeParse(value);
return parsed.success ? parsed.data : undefined;
}
export function normalizeOnboardingPromptDescriptions(
value: unknown,
promptCount?: number,
): string[] | undefined {
const parsed = onboardingPromptDescriptionsSchema.safeParse(value);
if (!parsed.success) return undefined;
if (promptCount !== undefined && parsed.data.length !== promptCount) {
return undefined;
}
return parsed.data.some((description) => description.length > 0)
? parsed.data
: undefined;
}
export function normalizeOnboardingPromptConfig(
value: unknown,
): OnboardingPromptConfig | undefined {
const coerced = coerceJsonRecord(value);
const raw = isRecord(coerced) ? coerced : null;
const onboardingPrompts = normalizeOnboardingPrompts(raw?.onboardingPrompts);
if (onboardingPrompts === undefined) return undefined;
const onboardingPromptDescriptions = normalizeOnboardingPromptDescriptions(
raw?.onboardingPromptDescriptions,
onboardingPrompts.length,
);
return {
onboardingPrompts,
...(onboardingPromptDescriptions !== undefined
? { onboardingPromptDescriptions }
: {}),
};
}
export function normalizeDesktopPolicyValue(
value: unknown,
): DesktopPolicyValue {
const parsed = desktopPolicyValueSchema.safeParse(coerceJsonRecord(value));
if (parsed.success) {
return Object.fromEntries(
desktopPolicyKeys.flatMap((key) =>
typeof parsed.data[key] === "boolean"
? [[key, parsed.data[key]] as const]
: [],
),
) as DesktopPolicyValue;
}
return {};
}
export function normalizeDefaultDesktopPolicyValue(
value: unknown,
): Required<DesktopPolicyValue> {
const normalized = normalizeDesktopPolicyValue(value);
return Object.fromEntries(
desktopPolicyDefinitions.map((definition) => [
definition.id,
normalized[definition.id] ?? definition.defaultValue,
]),
) as Required<DesktopPolicyValue>;
}
export function normalizeDesktopPolicyDocument(
value: unknown,
): DesktopPolicyDocument {
const coerced = coerceJsonRecord(value);
const policy = normalizeDesktopPolicyValue(coerced);
const onboardingPromptConfig = normalizeOnboardingPromptConfig(coerced);
return {
...policy,
...(onboardingPromptConfig !== undefined ? onboardingPromptConfig : {}),
};
}
export function normalizeDesktopPolicyDocumentWrite(
value: unknown,
): DesktopPolicyDocumentWrite {
const coerced = coerceJsonRecord(value);
const policy = normalizeDesktopPolicyValue(coerced);
const raw = isRecord(coerced) ? coerced : null;
const rawPrompts = raw?.onboardingPrompts;
const rawDescriptions = raw?.onboardingPromptDescriptions;
const onboardingPrompts = normalizeOnboardingPrompts(rawPrompts);
const onboardingPromptDescriptions = normalizeOnboardingPromptDescriptions(
rawDescriptions,
onboardingPrompts?.length,
);
return {
...policy,
...(rawPrompts === null
? { onboardingPrompts: null, onboardingPromptDescriptions: null }
: onboardingPrompts !== undefined
? { onboardingPrompts }
: {}),
...(rawPrompts !== null && rawDescriptions === null
? { onboardingPromptDescriptions: null }
: onboardingPromptDescriptions !== undefined
? { onboardingPromptDescriptions }
: {}),
};
}
export function resolveDesktopPolicyDocumentWrite(input: {
value: unknown;
existingPolicy?: unknown;
isDefault?: boolean;
preserveExistingOnboardingPrompts?: boolean;
}): DesktopPolicyDocument {
const write = normalizeDesktopPolicyDocumentWrite(input.value);
const policy = input.isDefault === true
? normalizeDefaultDesktopPolicyValue(write)
: normalizeDesktopPolicyValue(write);
const existingDocument = input.preserveExistingOnboardingPrompts === true
? normalizeDesktopPolicyDocument(input.existingPolicy ?? {})
: undefined;
const onboardingPrompts = Array.isArray(write.onboardingPrompts)
? write.onboardingPrompts
: write.onboardingPrompts === undefined &&
input.preserveExistingOnboardingPrompts === true
? existingDocument?.onboardingPrompts
: undefined;
const onboardingPromptDescriptions = onboardingPrompts === undefined
? undefined
: Array.isArray(write.onboardingPromptDescriptions)
? normalizeOnboardingPromptDescriptions(
write.onboardingPromptDescriptions,
onboardingPrompts.length,
)
: write.onboardingPromptDescriptions === undefined &&
write.onboardingPrompts === undefined &&
input.preserveExistingOnboardingPrompts === true
? normalizeOnboardingPromptDescriptions(
existingDocument?.onboardingPromptDescriptions,
onboardingPrompts.length,
)
: undefined;
return {
...policy,
...(onboardingPrompts !== undefined ? { onboardingPrompts } : {}),
...(onboardingPromptDescriptions !== undefined
? { onboardingPromptDescriptions }
: {}),
};
}
export function normalizeDefaultDesktopPolicyDocument(
value: unknown,
): DefaultDesktopPolicyDocument {
const coerced = coerceJsonRecord(value);
const policy = normalizeDefaultDesktopPolicyValue(coerced);
const onboardingPromptConfig = normalizeOnboardingPromptConfig(coerced);
return {
...policy,
...(onboardingPromptConfig !== undefined ? onboardingPromptConfig : {}),
};
}
export function allDesktopPolicies(
value: boolean,
): Required<DesktopPolicyValue> {
return Object.fromEntries(
desktopPolicyDefinitions.map((definition) => [definition.id, value]),
) as Required<DesktopPolicyValue>;
}
export function calculateEffectiveDesktopPolicy(input: {
orgPolicyCount: number;
defaultPolicy?: unknown;
assignedPolicies: unknown[];
}): Required<DesktopPolicyValue> {
if (input.orgPolicyCount === 0) {
return allDesktopPolicies(true);
}
const calculated = allDesktopPolicies(false);
const policies = [
normalizeDefaultDesktopPolicyValue(input.defaultPolicy ?? {}),
...input.assignedPolicies.map((policy) =>
normalizeDesktopPolicyValue(policy),
),
];
for (const policy of policies) {
for (const key of desktopPolicyKeys) {
if (policy[key] === true) {
calculated[key] = true;
}
}
}
return calculated;
}
export type DesktopPolicyPromptCandidate = {
id: string;
priority: number;
createdAt: Date | string | number | null;
policy: unknown;
};
function getCreatedAtTime(value: Date | string | number | null) {
if (value instanceof Date) return value.getTime();
if (typeof value === "number") return value;
if (typeof value !== "string") return 0;
const parsed = new Date(value).getTime();
return Number.isNaN(parsed) ? 0 : parsed;
}
function comparePromptCandidates(
left: DesktopPolicyPromptCandidate,
right: DesktopPolicyPromptCandidate,
) {
if (left.priority !== right.priority) return right.priority - left.priority;
const leftCreatedAt = getCreatedAtTime(left.createdAt);
const rightCreatedAt = getCreatedAtTime(right.createdAt);
if (leftCreatedAt !== rightCreatedAt) return leftCreatedAt - rightCreatedAt;
return left.id.localeCompare(right.id);
}
export function selectEffectiveOnboardingPrompts(input: {
defaultPolicy?: unknown;
assignedPolicies: DesktopPolicyPromptCandidate[];
}): string[] | undefined {
return selectEffectiveOnboardingPromptConfig(input)?.onboardingPrompts;
}
export function selectEffectiveOnboardingPromptConfig(input: {
defaultPolicy?: unknown;
assignedPolicies: DesktopPolicyPromptCandidate[];
}): OnboardingPromptConfig | undefined {
const candidatesById = new Map<string, DesktopPolicyPromptCandidate>();
for (const candidate of input.assignedPolicies) {
if (!candidatesById.has(candidate.id)) {
candidatesById.set(candidate.id, candidate);
}
}
const targetedCandidates = [...candidatesById.values()]
.filter(
(candidate) =>
normalizeOnboardingPromptConfig(candidate.policy) !== undefined,
)
.sort(comparePromptCandidates);
const targetedConfig = targetedCandidates[0]
? normalizeOnboardingPromptConfig(targetedCandidates[0].policy)
: undefined;
return (
targetedConfig ??
normalizeOnboardingPromptConfig(input.defaultPolicy ?? {})
);
}
function normalizeBrandUrl(value: unknown): string | undefined {
if (typeof value !== "string") return undefined;
const trimmed = value.trim();
if (!trimmed) return undefined;
try {
new URL(trimmed);
return trimmed.slice(0, 2048);
} catch {
return undefined;
}
}
function normalizeBrandAppName(value: unknown): string | undefined {
if (typeof value !== "string") return undefined;
const trimmed = value.trim();
return trimmed ? trimmed.slice(0, 64) : undefined;
}
function normalizeBrandAccentColor(value: unknown): BrandAccentColor | undefined {
if (typeof value !== "string") return undefined;
const trimmed = value.trim().toLowerCase();
return brandAccentColorValues.find((color) => color === trimmed);
}
export function normalizeDesktopConfig(value: unknown): DesktopConfig {
const policy = normalizeDesktopPolicyValue(value);
const raw = isRecord(value) ? value : null;
const allowedDesktopVersions = normalizeAllowedDesktopVersions(
raw?.allowedDesktopVersions,
);
const brandAppName = normalizeBrandAppName(raw?.brandAppName);
const brandLogoUrl = normalizeBrandUrl(raw?.brandLogoUrl);
const brandIconUrl = normalizeBrandUrl(raw?.brandIconUrl);
const brandAccentColor = normalizeBrandAccentColor(raw?.brandAccentColor);
const automationsEnabled =
typeof raw?.automationsEnabled === "boolean"
? raw.automationsEnabled
: undefined;
const dashboardEnabled =
typeof raw?.dashboardEnabled === "boolean"
? raw.dashboardEnabled
: undefined;
const connectEnabled =
typeof raw?.connectEnabled === "boolean" ? raw.connectEnabled : undefined;
const onboardingPromptConfig = normalizeOnboardingPromptConfig(raw);
return {
...policy,
...(allowedDesktopVersions !== undefined ? { allowedDesktopVersions } : {}),
...(brandAppName !== undefined ? { brandAppName } : {}),
...(brandLogoUrl !== undefined ? { brandLogoUrl } : {}),
...(brandIconUrl !== undefined ? { brandIconUrl } : {}),
...(brandAccentColor !== undefined ? { brandAccentColor } : {}),
...(automationsEnabled !== undefined ? { automationsEnabled } : {}),
...(dashboardEnabled !== undefined ? { dashboardEnabled } : {}),
...(connectEnabled !== undefined ? { connectEnabled } : {}),
...(onboardingPromptConfig !== undefined ? onboardingPromptConfig : {}),
};
}
@@ -0,0 +1,76 @@
import { z } from "zod"
export const EGRESS_DIAGNOSTIC_RUN_HEADER = "x-openwork-diagnostic-run-id"
export const EGRESS_DIAGNOSTIC_STEP_HEADER = "x-openwork-diagnostic-step"
export const EGRESS_DIAGNOSTIC_SIGNATURE_HEADER = "x-openwork-diagnostic-signature"
export const EGRESS_DIAGNOSTIC_ID_HEADER = "x-openwork-diagnostic-id"
export const EGRESS_DIAGNOSTIC_STEP_IDS = [
"reachability",
"http-methods",
"redirect",
"oauth-discovery",
"oauth-token",
"mcp-handshake",
] as const
export const egressDiagnosticStepIdSchema = z.enum(EGRESS_DIAGNOSTIC_STEP_IDS)
export type EgressDiagnosticStepId = z.infer<typeof egressDiagnosticStepIdSchema>
export const egressDiagnosticStepStatusSchema = z.enum(["passed", "failed", "skipped"])
export type EgressDiagnosticStepStatus = z.infer<typeof egressDiagnosticStepStatusSchema>
export const egressDiagnosticCategorySchema = z.enum([
"connectivity",
"http",
"oauth",
"mcp",
])
export type EgressDiagnosticCategory = z.infer<typeof egressDiagnosticCategorySchema>
export const egressDiagnosticOwnerSchema = z.enum([
"den-operator",
"network-administrator",
"openwork-support",
])
export type EgressDiagnosticOwner = z.infer<typeof egressDiagnosticOwnerSchema>
export const egressDiagnosticStepSchema = z.object({
id: egressDiagnosticStepIdSchema,
label: z.string().min(1).max(120),
category: egressDiagnosticCategorySchema,
status: egressDiagnosticStepStatusSchema,
startedAt: z.string(),
completedAt: z.string(),
durationMs: z.number().int().nonnegative(),
httpStatuses: z.array(z.number().int().min(100).max(599)).max(16),
diagnosticIds: z.array(z.string().uuid()).max(16),
code: z.string().min(1).max(120).nullable(),
message: z.string().min(1).max(500),
owner: egressDiagnosticOwnerSchema,
action: z.string().min(1).max(500),
})
export type EgressDiagnosticStep = z.infer<typeof egressDiagnosticStepSchema>
export const egressDiagnosticRunSchema = z.object({
runId: z.string().uuid(),
targetOrigin: z.string().url(),
supportUrl: z.string().url(),
startedAt: z.string(),
completedAt: z.string(),
overallStatus: z.enum(["passed", "failed"]),
highestPassingStep: egressDiagnosticStepIdSchema.nullable(),
failedStep: egressDiagnosticStepIdSchema.nullable(),
steps: z.array(egressDiagnosticStepSchema).length(EGRESS_DIAGNOSTIC_STEP_IDS.length),
})
export type EgressDiagnosticRun = z.infer<typeof egressDiagnosticRunSchema>
export const egressDiagnosticConfigurationSchema = z.object({
available: z.boolean(),
targetOrigin: z.string().url().nullable(),
missingConfiguration: z.array(z.enum([
"DEN_DIAGNOSTICS_ORIGIN",
"DEN_DIAGNOSTICS_BEARER_TOKEN",
])).max(2),
})
export type EgressDiagnosticConfiguration = z.infer<typeof egressDiagnosticConfigurationSchema>
+118
View File
@@ -0,0 +1,118 @@
export const INFERENCE_USAGE_CONVERSION_FACTOR = 100_000_000;
export const INFERENCE_WINDOW_TYPES = [
"five_hour",
"weekly",
"monthly",
] as const;
export type InferenceWindowType = (typeof INFERENCE_WINDOW_TYPES)[number];
export const INFERENCE_TIERS = ["tier1", "tier2"] as const;
export type InferenceTier = (typeof INFERENCE_TIERS)[number];
export const INFERENCE_TIER_LIMITS: Record<
InferenceTier,
Record<InferenceWindowType, number>
> = {
tier1: {
five_hour: 100_000_000,
weekly: 500_000_000,
monthly: 1_000_000_000,
},
tier2: {
five_hour: 150_000_000,
weekly: 750_000_000,
monthly: 1_500_000_000,
},
} as const;
export const INFERENCE_RESET_STRATEGIES = [
"anchored",
"activity_based",
] as const;
export type InferenceResetStrategy =
(typeof INFERENCE_RESET_STRATEGIES)[number];
export const INFERENCE_RESET_STRATEGY_BY_WINDOW_TYPE: Record<
InferenceWindowType,
InferenceResetStrategy
> = {
five_hour: "activity_based",
weekly: "anchored",
monthly: "anchored",
} as const;
export const INFERENCE_WINDOW_DURATIONS_MS: Record<
InferenceWindowType,
number
> = {
five_hour: 5 * 60 * 60 * 1000,
weekly: 7 * 24 * 60 * 60 * 1000,
monthly: 30 * 24 * 60 * 60 * 1000,
} as const;
// For upstreamModel values, please get from models.dev/api.json provider = openrouter.models.id
export const INFERENCE_MODEL_ALIASES = {
"z-ai/glm-5.2": {
upstreamModel: "z-ai/glm-5.2",
displayName: "OpenWork: GLM-5.2",
enabled: true,
usageFactor: 1,
},
"moonshotai/kimi-k2.7-code": {
upstreamModel: "moonshotai/kimi-k2.7-code",
displayName: "OpenWork: Kimi K2.7 Code",
enabled: true,
usageFactor: 1,
},
"tencent/hy3-preview": {
upstreamModel: "tencent/hy3-preview",
displayName: "OpenWork: Hy3 preview",
enabled: true,
usageFactor: 1,
},
"moonshotai/kimi-k2.6": {
upstreamModel: "moonshotai/kimi-k2.6",
displayName: "OpenWork: Kimi K2.6",
enabled: true,
usageFactor: 1,
},
"deepseek/deepseek-v4-flash": {
upstreamModel: "deepseek/deepseek-v4-flash",
displayName: "OpenWork: DeepSeek V4 Flash",
enabled: true,
usageFactor: 1,
},
"minimax/minimax-m2.7": {
upstreamModel: "minimax/minimax-m2.7",
displayName: "OpenWork: MiniMax M2.7",
enabled: true,
usageFactor: 1,
},
"minimax/minimax-m3": {
upstreamModel: "minimax/minimax-m3",
displayName: "OpenWork: MiniMax-M3",
enabled: true,
usageFactor: 1,
},
"z-ai/glm-5.1": {
upstreamModel: "z-ai/glm-5.1",
displayName: "OpenWork: GLM-5.1",
enabled: true,
usageFactor: 1,
},
"moonshotai/kimi-k3": {
upstreamModel: "moonshotai/kimi-k3",
displayName: "OpenWork: Kimi K3",
enabled: true,
usageFactor: 1,
},
} as const;
export type InferenceModelAlias = keyof typeof INFERENCE_MODEL_ALIASES;
export type InferenceOrganizationMetadata = {
enabled: true;
tier: InferenceTier;
};
@@ -0,0 +1,62 @@
import { z } from "zod"
export const OPENWORK_CLOUD_MCP_CONNECTION_ACTION_VERSION = 1 as const
export const OPENWORK_CLOUD_MCP_CONNECTION_ACTION_KIND = "connection_action" as const
export const OPENWORK_CLOUD_MCP_CONNECTION_ACTION_SOURCE = "openwork-cloud" as const
export const openworkCloudMcpConnectionActionSchema = z.object({
version: z.literal(OPENWORK_CLOUD_MCP_CONNECTION_ACTION_VERSION),
kind: z.literal(OPENWORK_CLOUD_MCP_CONNECTION_ACTION_KIND),
source: z.literal(OPENWORK_CLOUD_MCP_CONNECTION_ACTION_SOURCE),
connectionId: z.string().min(1),
connectionName: z.string().min(1),
authType: z.enum(["oauth", "apikey", "none"]),
credentialMode: z.enum(["shared", "per_member"]),
state: z.enum(["needs_connection", "reauth_required", "provider_error"]),
actor: z.enum([
"member",
"organization_admin",
"provider_admin",
"network_admin",
"openwork",
]),
action: z.object({
type: z.enum([
"connect",
"reconnect",
"update_credentials",
"inspect_connection",
"fix_provider",
"fix_network",
"contact_openwork",
]),
surface: z.enum([
"openwork_your_connections",
"openwork_organization_connections",
"provider_admin_console",
"network_infrastructure",
"openwork_support",
]),
retry: z.literal("search_capabilities"),
}),
})
/**
* The only connection action that chat may execute directly. Shared OAuth,
* API-key, provider-admin, and support actions remain descriptive because the
* current member may not own the credential or have permission to repair it.
*/
export const openworkCloudMcpInlineReconnectSchema = openworkCloudMcpConnectionActionSchema.extend({
authType: z.literal("oauth"),
credentialMode: z.literal("per_member"),
state: z.literal("reauth_required"),
actor: z.literal("member"),
action: z.object({
type: z.literal("reconnect"),
surface: z.literal("openwork_your_connections"),
retry: z.literal("search_capabilities"),
}),
})
export type OpenworkCloudMcpConnectionAction = z.infer<typeof openworkCloudMcpConnectionActionSchema>
export type OpenworkCloudMcpInlineReconnect = z.infer<typeof openworkCloudMcpInlineReconnectSchema>
+30
View File
@@ -0,0 +1,30 @@
export type Microsoft365Feature =
| "mailRead"
| "mailDraft"
| "calendarRead"
| "calendarWrite"
| "filesRead"
| "filesWrite"
| "filesReadAll"
| "filesFull"
| "teamsChatRead"
| "teamsChatSend"
export const MICROSOFT_365_DEFAULT_FEATURES: readonly Microsoft365Feature[] = [
"mailRead",
"calendarRead",
"filesRead",
]
export const MICROSOFT_365_FEATURES: readonly Microsoft365Feature[] = [
"calendarRead",
"calendarWrite",
"mailDraft",
"mailRead",
"filesRead",
"filesWrite",
"filesReadAll",
"filesFull",
"teamsChatRead",
"teamsChatSend",
]
+628
View File
@@ -0,0 +1,628 @@
/**
* Shared contract for the Electron desktop IPC bridge.
*
* Producer: apps/desktop/electron/main.mjs — `desktopCommandHandlers`, typed
* via JSDoc against `DesktopCommandHandlers` so missing/extra/renamed
* commands fail `typecheck:electron`.
* Consumer: apps/app/src/app/lib/desktop.ts — the `desktopBridge` Proxy and
* its named exports derive per-command signatures from `DesktopCommandMap`.
*
* Every command sent over the `openwork:desktop` channel has exactly one
* entry here: `args` is the tuple the renderer passes, `result` what the
* main process resolves. Results marked `unknown` are not yet modeled —
* tighten them instead of widening call sites.
*/
import type { ConnectLinkVerifyFailure, ConnectLinkVerifyResult } from "./connect-link.js";
import type { WorkspaceWire } from "./workspace.js";
// ---------------------------------------------------------------------------
// Payload shapes (moved from apps/app/src/app/lib/desktop-types.ts, which
// re-exports them — keep that file as the app-side import path).
// ---------------------------------------------------------------------------
export type OpencodeExecutionEnvEntry = {
name: string;
value: string;
redacted: boolean;
};
export type OpencodeExecutionSnapshot = {
command: string;
args: string[];
cwd: string;
env: OpencodeExecutionEnvEntry[];
};
export type EngineInfo = {
running: boolean;
runtime: "direct";
managedByServer: boolean;
baseUrl: string | null;
projectDir: string | null;
hostname: string | null;
port: number | null;
opencodeUsername: string | null;
opencodePassword: string | null;
opencodeBinPath: string | null;
opencodeBinSource: string | null;
pid: number | null;
lastStdout: string | null;
lastStderr: string | null;
execution: OpencodeExecutionSnapshot | null;
};
export type DesktopNotificationInput = {
title: string;
body?: string;
href?: string;
silent?: boolean;
};
export type DesktopNotificationResult =
| { ok: true }
| { ok: false; reason: string };
export type DesktopIntegrationIssue =
| "appimage-path"
| "desktop-entry"
| "icon"
| "protocol-handler"
| "version";
export type DesktopIntegrationStatus = {
supported: boolean;
state: "unsupported" | "not_integrated" | "integrated" | "needs_repair" | "managed_externally";
ownership: "none" | "openwork" | "external";
appImagePath: string | null;
desktopEntryPath: string | null;
handlerDesktopId: string | null;
issues: DesktopIntegrationIssue[];
};
export type DesktopIntegrationResult = {
ok: boolean;
status: DesktopIntegrationStatus;
error?: string;
};
export type OpenworkServerInfo = {
running: boolean;
/**
* Monotonic per-start identity of the embedded server within this desktop
* process. Sticky ports and persisted tokens keep the connection details
* identical across restarts, so clients that must re-deliver state to a new
* server lifetime (e.g. the Connect policy) key on this value. Null when
* the bridge predates the field or no start completed yet.
*/
generation: number | null;
remoteAccessEnabled: boolean;
host: string | null;
port: number | null;
baseUrl: string | null;
connectUrl: string | null;
mdnsUrl: string | null;
lanUrl: string | null;
clientToken: string | null;
ownerToken: string | null;
hostToken: string | null;
managedOpencodeBinPath: string | null;
managedOpencodeBinSource: string | null;
pid: number | null;
lastStdout: string | null;
lastStderr: string | null;
managedOpencodeExecution: OpencodeExecutionSnapshot | null;
};
export type EngineDoctorResult = {
found: boolean;
inPath: boolean;
resolvedPath: string | null;
resolvedSource: string | null;
version: string | null;
supportsServe: boolean;
notes: string[];
serveHelpStatus: number | null;
serveHelpStdout: string | null;
serveHelpStderr: string | null;
};
export type WorkspaceList = {
selectedId?: string;
watchedId?: string | null;
activeId?: string | null;
workspaces: WorkspaceWire[];
};
export type WorkspaceExportSummary = {
outputPath: string;
included: number;
excluded: string[];
};
export type BrandIconApplyResult = { ok: boolean; reason?: string };
export type BrandIconState = { applied: boolean; sourceUrl: string | null; reason: string | null };
export type EvalRelaunchResult = { ok: true };
export type OpencodeCommandDraft = {
name: string;
description?: string;
template: string;
agent?: string;
model?: string;
subtask?: boolean;
};
export type WorkspaceOpenworkConfig = {
version: number;
workspace?: {
name?: string | null;
createdAt?: number | null;
preset?: string | null;
} | null;
authorizedRoots: string[];
reload?: {
auto?: boolean;
resume?: boolean;
} | null;
};
export type AppBuildInfo = {
version: string;
gitSha?: string | null;
buildEpoch?: string | null;
openworkDevMode?: boolean;
os?: string | null;
arch?: string | null;
};
export type DesktopDistributionInfo = {
flavor: "public" | "enterprise";
appName: string;
appIdentifier: string;
protocolScheme: string;
requireSignin: boolean;
requireActivation: boolean;
};
/** Org + first-skill identity shared by the handoff and prepared records. */
export type DesktopBootstrapOrgSkill = {
orgId: string;
orgName: string;
orgSlug: string;
skillId: string;
skillTitle: string;
};
export type DesktopBootstrapConfig = {
baseUrl: string;
apiBaseUrl?: string | null;
requireSignin: boolean;
requireActivation?: boolean;
brandAppName?: string | null;
brandLogoUrl?: string | null;
brandIconUrl?: string | null;
writtenAt?: string | null;
fromFile?: boolean;
claimLinks?: Array<{
id: string;
role: string;
token?: string;
url: string;
expiresAt: string;
}> | null;
handoff?: (DesktopBootstrapOrgSkill & {
grant: string;
denBaseUrl: string;
createdAt: string;
}) | null;
prepared?: (DesktopBootstrapOrgSkill & {
skillsDir: string;
skillPath: string;
preparedAt: string;
}) | null;
enterpriseActivation?: {
activatedAt: string;
denBaseUrl: string;
} | null;
};
export type OpenworkDockerCleanupResult = {
candidates: string[];
removed: string[];
errors: string[];
};
export type ExecResult = {
ok: boolean;
status: number;
stdout: string;
stderr: string;
};
export type LocalSkillCard = {
name: string;
path: string;
description?: string;
trigger?: string;
};
export type LocalSkillContent = {
path: string;
content: string;
};
export type OpencodeConfigFile = {
path: string;
exists: boolean;
content: string | null;
};
export type UpdaterEnvironment = {
supported: boolean;
reason: string | null;
executablePath: string | null;
appBundlePath: string | null;
};
export type CacheResetResult = {
removed: string[];
missing: string[];
errors: string[];
};
export type NukeManifestPreview = {
deletePaths: string[];
bootstrapPath: string;
preserveBootstrapPath: string | null;
partitions: string[];
};
export type NukeOptions = {
preserveBootstrap: boolean;
};
export type NukeReceiptError = {
path: string;
message: string;
code?: string;
};
export type NukeReceipt = {
deleted: string[];
pendingRetry: string[];
errors: NukeReceiptError[];
preservedBootstrap: boolean;
relaunchMode: "cleanup_worker" | "direct";
workerScheduled: boolean;
};
export type DesktopFetchInit = {
method?: string;
headers?: Record<string, string>;
body?: string;
timeoutMs?: number;
agentContextDiagnostics?: {
deadlineAtMs: number;
};
};
export type DesktopFetchResult = {
status: number;
statusText: string;
headers: [string, string][];
body: string;
};
export type DesktopMultipartUploadInput = {
transferId: string;
url: string;
bytes: ArrayBuffer;
filename: string;
size: number;
contentType?: string;
fieldName?: string;
fields?: Record<string, string>;
method?: string;
headers?: Record<string, string>;
timeoutMs?: number;
};
export type DesktopBinaryDownloadInput = {
transferId: string;
url: string;
destinationPath: string;
maxBytes?: number;
method?: string;
headers?: Record<string, string>;
timeoutMs?: number;
};
export type DesktopBinaryDownloadResult = {
status: number;
statusText: string;
headers: [string, string][];
path: string | null;
bytes: number;
body?: string;
};
export type WorkspaceCreateInput = {
folderPath: string;
name?: string | null;
preset?: string | null;
};
export type WorkspaceCreateRemoteInput = {
baseUrl: string;
remoteType?: "openwork" | "opencode" | null;
directory?: string | null;
displayName?: string | null;
openworkHostUrl?: string | null;
openworkToken?: string | null;
openworkClientToken?: string | null;
openworkHostToken?: string | null;
openworkWorkspaceId?: string | null;
openworkWorkspaceName?: string | null;
sandboxBackend?: string | null;
sandboxRunId?: string | null;
sandboxContainerName?: string | null;
};
export type WorkspaceUpdateRemoteInput = WorkspaceCreateRemoteInput & {
workspaceId: string;
};
export type UiControlBridgeInfo = {
baseUrl?: string;
token?: string;
};
export type ComputerUsePermissions = {
ok: boolean;
accessibility: boolean;
screenRecording: boolean;
error?: string;
};
export type RunningAppsResult = {
ok: boolean;
apps: string[];
};
// ---------------------------------------------------------------------------
// The command map
// ---------------------------------------------------------------------------
export type DesktopCommandMap = {
// Workspace state
workspaceBootstrap: { args: []; result: WorkspaceList };
workspaceSetSelected: { args: [workspaceId: string]; result: WorkspaceList };
workspaceSetRuntimeActive: { args: [workspaceId: string | null]; result: WorkspaceList };
workspaceCreate: { args: [input: WorkspaceCreateInput]; result: WorkspaceList };
workspaceCreateRemote: { args: [input: WorkspaceCreateRemoteInput]; result: WorkspaceList };
workspaceUpdateRemote: { args: [input: WorkspaceUpdateRemoteInput]; result: WorkspaceList };
workspaceUpdateDisplayName: {
args: [input: { workspaceId: string; displayName?: string | null }];
result: WorkspaceList;
};
workspaceForget: { args: [workspaceId: string]; result: WorkspaceList };
workspaceAddAuthorizedRoot: {
args: [input: { workspacePath: string; folderPath?: string; authorizedRoot?: string }];
result: unknown;
};
workspaceOpenworkRead: {
args: [input: { workspacePath: string }];
result: WorkspaceOpenworkConfig;
};
workspaceOpenworkWrite: {
args: [input: { workspacePath: string; config: WorkspaceOpenworkConfig }];
result: unknown;
};
workspaceExportConfig: {
args: [input: { workspaceId: string; outputPath: string }];
result: WorkspaceExportSummary;
};
workspaceImportConfig: {
args: [input: { archivePath: string; targetDir: string; name?: string | null }];
result: unknown;
};
// Opencode custom commands
opencodeCommandList: {
args: [input: { scope: string; projectDir?: string }];
result: string[];
};
opencodeCommandWrite: {
args: [input: { scope: string; projectDir?: string; command: OpencodeCommandDraft }];
result: unknown;
};
opencodeCommandDelete: {
args: [input: { scope: string; projectDir?: string; name: string }];
result: unknown;
};
// Engine / runtime lifecycle
engineStart: { args: [projectDir: string, options?: Record<string, unknown>]; result: EngineInfo };
prepareFreshRuntime: { args: []; result: unknown };
runtimeBootstrap: { args: []; result: unknown };
runtimeStatus: { args: []; result: unknown };
engineStop: { args: []; result: EngineInfo };
engineRestart: { args: [options?: Record<string, unknown>]; result: EngineInfo };
engineInfo: { args: []; result: EngineInfo };
engineDoctor: { args: [projectDir?: string]; result: EngineDoctorResult };
engineInstall: { args: []; result: unknown };
// App / bridge info
appBuildInfo: { args: []; result: AppBuildInfo };
desktopNotificationShow: {
args: [input: DesktopNotificationInput];
result: DesktopNotificationResult;
};
desktopSentrySetSession: {
args: [input: { userId: string; orgId: string }];
result: { enabled: boolean };
};
desktopSentryClearSession: { args: []; result: { enabled: boolean } };
desktopIntegrationStatus: { args: []; result: DesktopIntegrationStatus };
desktopIntegrationInstall: {
args: [options?: { useExternalLauncher?: boolean }];
result: DesktopIntegrationResult;
};
desktopIntegrationRemove: { args: []; result: DesktopIntegrationResult };
getUiControlBridgeInfo: { args: []; result: UiControlBridgeInfo | null };
getOpenworkUiMcpCommand: { args: []; result: string[] };
getComputerUseMcpCommand: { args: []; result: string[] };
getOpenworkUiMcpEnvironment: { args: []; result: Record<string, string> };
// Computer use
checkComputerUsePermissions: { args: []; result: ComputerUsePermissions };
listRunningApps: { args: []; result: RunningAppsResult };
openComputerUsePermissionSetup: { args: []; result: ComputerUsePermissions };
openComputerUsePermissionSettings: { args: []; result: unknown };
// Bootstrap config
getDesktopBootstrapConfig: { args: []; result: DesktopBootstrapConfig };
debugDesktopBootstrapConfig: { args: []; result: unknown };
clearDesktopBootstrapConfig: { args: []; result: unknown };
setDesktopBootstrapConfig: {
args: [config: Partial<DesktopBootstrapConfig>];
result: DesktopBootstrapConfig;
};
// Connect links use a short-lived HTTPS exchange by default and can use an
// embedded-key signed token when explicitly enabled. The renderer relays
// only the raw URL. `connectLinkAccept` resolves it again after confirmation,
// enforces one-time use, and persists the target as desktop bootstrap config.
connectLinkVerify: { args: [rawUrl: string]; result: ConnectLinkVerifyResult };
connectLinkAccept: {
args: [rawUrl: string];
result: { ok: true; config: DesktopBootstrapConfig } | ConnectLinkVerifyFailure;
};
nukeOpenworkAndOpencodeConfigPreview: { args: [options?: NukeOptions]; result: NukeManifestPreview };
nukeOpenworkAndOpencodeConfigAndExit: { args: [options?: NukeOptions]; result: NukeReceipt };
// Sandbox
sandboxCleanupOpenworkContainers: { args: []; result: OpenworkDockerCleanupResult };
// Openwork server sidecar
openworkServerInfo: { args: []; result: OpenworkServerInfo };
automationRunnerConfigure: {
args: [configuration: { baseUrl: string; token: string; runnerId: string } | null];
result: { connected: boolean };
};
openworkServerRestart: {
args: [options?: Record<string, unknown>];
result: OpenworkServerInfo;
};
// Dialogs
pickDirectory: {
args: [options?: { title?: string; defaultPath?: string; multiple?: boolean }];
result: string | string[] | null;
};
pickFile: {
args: [
options?: {
title?: string;
defaultPath?: string;
multiple?: boolean;
filters?: { name: string; extensions: string[] }[];
},
];
result: string | string[] | null;
};
saveFile: {
args: [options?: { title?: string; defaultPath?: string; filters?: { name: string; extensions: string[] }[] }];
result: string | null;
};
// Skills
importSkill: {
args: [projectDir: string, sourceDir: string, options?: { overwrite?: boolean }];
result: ExecResult;
};
installSkillTemplate: {
args: [projectDir: string, name: string, content: string, options?: { overwrite?: boolean }];
result: ExecResult;
};
listLocalSkills: { args: [projectDir: string]; result: LocalSkillCard[] };
readLocalSkill: { args: [projectDir: string, skillName: string]; result: LocalSkillContent };
writeLocalSkill: {
args: [projectDir: string, skillName: string, content: string];
result: ExecResult;
};
uninstallSkill: { args: [projectDir: string, skillName: string]; result: ExecResult };
// Updater / config / resets
updaterEnvironment: { args: []; result: UpdaterEnvironment };
readOpencodeConfig: { args: [scope: string, projectDir?: string]; result: OpencodeConfigFile };
writeOpencodeConfig: {
args: [scope: string, projectDir: string, content: string];
result: ExecResult;
};
/**
* The renderer passes its reset-modal mode, but the main process currently
* IGNORES it and always removes workspace state + bootstrap config; only
* the renderer's localStorage cleanup is mode-scoped. Follow-up: decide
* whether "onboarding" should preserve desktop workspace state.
*/
resetOpenworkState: { args: [mode?: "onboarding" | "all"]; result: unknown };
resetOpencodeCache: { args: []; result: CacheResetResult };
opencodeMcpAuth: { args: [action: string, name: string]; result: ExecResult };
setWindowDecorations: { args: [decorated: boolean]; result: unknown };
// Window / OS utilities (dunder commands)
__openPath: { args: [target: string]; result: unknown };
__revealItemInDir: { args: [target: string]; result: unknown };
__getFileIcon: { args: [target: string, size?: "small" | "normal" | "large"]; result: string | null };
__applyBrandAppName: { args: [appName: string | null]; result: { ok: true; appName: string } };
__applyBrandIcon: { args: [url: string | null]; result: BrandIconApplyResult };
__getBrandIconState: { args: []; result: BrandIconState };
__evalRelaunch: { args: []; result: EvalRelaunchResult };
__getApplicationsForFile: { args: [target: string]; result: { name: string; appPath: string; icon: string | null }[] };
__openWithApp: { args: [target: string, appPath: string]; result: unknown };
__fetch: { args: [url: string, init?: DesktopFetchInit]; result: DesktopFetchResult };
__uploadMultipart: { args: [input: DesktopMultipartUploadInput]; result: DesktopFetchResult };
__downloadBinary: { args: [input: DesktopBinaryDownloadInput]; result: DesktopBinaryDownloadResult };
__cancelTransfer: { args: [transferId: string]; result: boolean };
__homeDir: { args: []; result: string };
__joinPath: { args: [...segments: string[]]; result: string };
__setZoomFactor: { args: [factor: number]; result: boolean };
__setNativeTheme: { args: [theme: string]; result: unknown };
__setApplicationMenuVisible: { args: [visible: boolean]; result: unknown };
};
export type DesktopCommandName = keyof DesktopCommandMap;
export type DesktopCommandArgs<C extends DesktopCommandName> = DesktopCommandMap[C]["args"];
export type DesktopCommandResult<C extends DesktopCommandName> = DesktopCommandMap[C]["result"];
/**
* Main-process handler registry shape. `Event` is electron's
* IpcMainInvokeEvent (kept generic so this package does not depend on
* electron types).
*
* Args are deliberately loose (`any[]`) on this side: IPC input crosses a
* trust boundary, so handlers validate/normalize whatever arrives with
* defensive dynamic access (`String(args[0] ?? "")`, `input.foo ?? null`)
* rather than assuming the renderer's tuple. `unknown[]` would force ~50
* narrowing rewrites in the plain-JS main process for no runtime gain.
* Key parity and result types are still enforced.
*/
type DesktopCommandHandler<Event, C extends DesktopCommandName> = (
event: Event,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...args: any[]
) => Promise<DesktopCommandResult<C>>;
export type DesktopCommandHandlers<Event = unknown> = {
[C in Exclude<DesktopCommandName, "__evalRelaunch">]: DesktopCommandHandler<Event, C>;
} & {
__evalRelaunch?: DesktopCommandHandler<Event, "__evalRelaunch">;
};
/** Renderer-side bridge: one async function per command. */
export type DesktopCommandInvokers = {
[C in DesktopCommandName]: (...args: DesktopCommandArgs<C>) => Promise<DesktopCommandResult<C>>;
};
+13
View File
@@ -0,0 +1,13 @@
export * from "./agent-context-diagnostics"
export * from "./openwork-affordance"
export * from "./openwork-context"
export * from "./openwork-provider"
export * from "./automations"
export * from "./workflows"
export * from "./skill-created-app"
export * from "./den/connect-diagnostics"
export * from "./den/desktop-policies"
export * from "./den/egress-diagnostics"
export * from "./den/inference"
export * from "./den/microsoft-365"
export * from "./den/mcp-connection-action"
+89
View File
@@ -0,0 +1,89 @@
import { z } from "zod"
export const OPENWORK_AFFORDANCE_SCHEMA_VERSION = 1
export const openworkAffordanceKindSchema = z.enum(["query", "command", "guidance"])
export type OpenworkAffordanceKind = z.infer<typeof openworkAffordanceKindSchema>
export const openworkProviderKindSchema = z.enum(["builtin", "extension", "mcp", "connect"])
export type OpenworkProviderKind = z.infer<typeof openworkProviderKindSchema>
export const openworkProviderRefSchema = z.object({
id: z.string().trim().min(1),
kind: openworkProviderKindSchema,
})
export type OpenworkProviderRef = z.infer<typeof openworkProviderRefSchema>
export const openworkAffordanceArgumentSchema = z.object({
name: z.string().trim().min(1),
type: z.enum(["string", "number", "boolean", "object", "array", "unknown"]),
required: z.boolean(),
description: z.string().trim().min(1).optional(),
})
export type OpenworkAffordanceArgument = z.infer<typeof openworkAffordanceArgumentSchema>
export const openworkAffordanceEffectsSchema = z.object({
data: z.enum(["none", "read", "write"]),
ui: z.enum(["none", "focus", "navigate", "layout", "dialog"]),
external: z.boolean(),
})
export type OpenworkAffordanceEffects = z.infer<typeof openworkAffordanceEffectsSchema>
export const openworkAffordanceAvailabilitySchema = z.object({
enabled: z.boolean(),
reason: z.string().trim().min(1).optional(),
})
export type OpenworkAffordanceAvailability = z.infer<typeof openworkAffordanceAvailabilitySchema>
export const openworkAffordanceExecutorSchema = z.discriminatedUnion("kind", [
z.object({ kind: z.literal("openwork") }),
z.object({
kind: z.literal("tool"),
tool: z.string().trim().min(1),
}),
])
export type OpenworkAffordanceExecutor = z.infer<typeof openworkAffordanceExecutorSchema>
export const openworkAffordanceDescriptorSchema = z.object({
id: z.string().trim().min(1),
kind: openworkAffordanceKindSchema,
title: z.string().trim().min(1),
description: z.string().trim().min(1),
provider: openworkProviderRefSchema,
arguments: z.array(openworkAffordanceArgumentSchema),
effects: openworkAffordanceEffectsSchema,
confirmation: z.enum(["never", "destructive", "always"]),
availability: openworkAffordanceAvailabilitySchema,
executor: openworkAffordanceExecutorSchema,
})
export type OpenworkAffordanceDescriptor = z.infer<typeof openworkAffordanceDescriptorSchema>
export const openworkAffordanceRequestSchema = z.object({
id: z.string().trim().min(1),
args: z.record(z.string(), z.unknown()).optional(),
expectedRevision: z.number().int().nonnegative().optional(),
actor: z.string().trim().min(1).optional(),
})
export type OpenworkAffordanceRequest = z.infer<typeof openworkAffordanceRequestSchema>
const openworkAffordanceSuccessSchema = z.object({
ok: z.literal(true),
id: z.string(),
result: z.unknown().optional(),
revision: z.number().int().nonnegative().optional(),
effects: openworkAffordanceEffectsSchema,
})
const openworkAffordanceFailureSchema = z.object({
ok: z.literal(false),
id: z.string(),
error: z.string(),
code: z.enum(["unavailable", "invalid-args", "conflict", "failed"]),
revision: z.number().int().nonnegative().optional(),
})
export const openworkAffordanceResultSchema = z.discriminatedUnion("ok", [
openworkAffordanceSuccessSchema,
openworkAffordanceFailureSchema,
])
export type OpenworkAffordanceResult = z.infer<typeof openworkAffordanceResultSchema>
+105
View File
@@ -0,0 +1,105 @@
import { z } from "zod"
import {
openworkAffordanceDescriptorSchema,
openworkProviderRefSchema,
} from "./openwork-affordance.js"
import { openworkFeatureContributionSchema } from "./openwork-provider.js"
export const OPENWORK_CONTEXT_SCHEMA_VERSION = 1
export const openworkSessionRefSchema = z.object({
workspaceId: z.string().trim().min(1),
sessionId: z.string().trim().min(1),
title: z.string().optional(),
})
export type OpenworkSessionRef = z.infer<typeof openworkSessionRefSchema>
export const openworkScreenSchema = z.discriminatedUnion("kind", [
z.object({
kind: z.literal("conversation"),
route: z.string(),
workspaceId: z.string().optional(),
sessionId: z.string().optional(),
}),
z.object({
kind: z.literal("settings"),
route: z.string(),
workspaceId: z.string().optional(),
panel: z.string(),
}),
z.object({
kind: z.literal("other"),
route: z.string(),
}),
])
export type OpenworkScreen = z.infer<typeof openworkScreenSchema>
export const openworkConversationLayoutSchema = z.discriminatedUnion("kind", [
z.object({ kind: z.literal("empty") }),
z.object({
kind: z.literal("single"),
sessionId: z.string(),
workspaceId: z.string().optional(),
}),
z.object({
kind: z.literal("split"),
primarySessionId: z.string(),
primaryWorkspaceId: z.string().optional(),
secondarySessionId: z.string(),
secondaryWorkspaceId: z.string().optional(),
focused: z.enum(["primary", "secondary"]),
}),
])
export type OpenworkConversationLayout = z.infer<typeof openworkConversationLayoutSchema>
export const openworkPanelTabSchema = z.object({
id: z.string(),
kind: z.enum(["browser", "artifact"]),
label: z.string(),
url: z.string().optional(),
status: z.enum(["loading", "ready"]).optional(),
})
export type OpenworkPanelTab = z.infer<typeof openworkPanelTabSchema>
export const openworkResourceDescriptorSchema = z.object({
ref: z.string().trim().min(1),
kind: z.enum(["workspace", "session", "screen", "side-panel", "settings"]),
title: z.string(),
provider: openworkProviderRefSchema,
state: z.record(z.string(), z.unknown()),
})
export type OpenworkResourceDescriptor = z.infer<typeof openworkResourceDescriptorSchema>
export const openworkContextSnapshotSchema = z.object({
schemaVersion: z.literal(OPENWORK_CONTEXT_SCHEMA_VERSION),
revision: z.number().int().nonnegative(),
capturedAt: z.string(),
screen: openworkScreenSchema,
conversations: z.object({
tabs: z.array(openworkSessionRefSchema),
layout: openworkConversationLayoutSchema,
}),
chrome: z.object({
sidebarOpen: z.boolean(),
applicationMenuVisible: z.boolean(),
rightSidebarExpanded: z.boolean(),
}),
execution: z.object({
queries: z.literal("parallel"),
commands: z.literal("serialized"),
busyCommandId: z.string().nullable(),
busyActor: z.string().nullable(),
}),
sidePanel: z.object({
open: z.boolean(),
ownerSessionId: z.string().nullable(),
kind: z.enum(["panel", "extensions", "voice"]).nullable(),
tabs: z.array(openworkPanelTabSchema),
activeTabId: z.string().nullable(),
}),
resources: z.array(openworkResourceDescriptorSchema),
availableAffordances: z.array(openworkAffordanceDescriptorSchema),
contributions: z.array(openworkFeatureContributionSchema),
})
export type OpenworkContextSnapshot = z.infer<typeof openworkContextSnapshotSchema>
+52
View File
@@ -0,0 +1,52 @@
import { z } from "zod"
import {
openworkAffordanceDescriptorSchema,
openworkProviderRefSchema,
} from "./openwork-affordance.js"
export const openworkGuidanceDescriptorSchema = z.object({
ref: z.string().trim().min(1),
title: z.string().trim().min(1),
description: z.string().trim().min(1),
provider: openworkProviderRefSchema,
loading: z.enum(["eager", "catalog", "on-demand"]),
})
export type OpenworkGuidanceDescriptor = z.infer<typeof openworkGuidanceDescriptorSchema>
export const openworkFeatureContributionSchema = z.object({
featureId: z.string().trim().min(1),
provider: openworkProviderRefSchema,
affordances: z.array(openworkAffordanceDescriptorSchema),
guidance: z.array(openworkGuidanceDescriptorSchema),
})
export type OpenworkFeatureContribution = z.infer<typeof openworkFeatureContributionSchema>
export const openworkProviderCatalogSchema = z.object({
schemaVersion: z.literal(1),
contributions: z.array(openworkFeatureContributionSchema),
})
export type OpenworkProviderCatalog = z.infer<typeof openworkProviderCatalogSchema>
export const openworkCapabilityResultSchema = z.discriminatedUnion("status", [
z.object({
status: z.literal("completed"),
data: z.unknown(),
additionalContext: z.array(z.string()).optional(),
}),
z.object({
status: z.literal("guidance"),
instructions: z.array(z.string()),
}),
z.object({
status: z.literal("requires-user-action"),
message: z.string(),
action: z.string().optional(),
}),
z.object({
status: z.literal("failed"),
error: z.string(),
retryable: z.boolean(),
}),
])
export type OpenworkCapabilityResult = z.infer<typeof openworkCapabilityResultSchema>
+29
View File
@@ -0,0 +1,29 @@
import { z } from "zod"
const idSchema = z.string().trim().min(1).max(160)
export const pluginFlowAppSchemaVersion = "1" as const
/**
* Data contract for the first-party plugin-flow MCP App: a confirmation card
* for library sharing flows that previously ended as plain JSON text —
* attaching a plugin to a marketplace, granting plugin access, and granting
* marketplace access.
*/
export const pluginFlowPayloadSchema = z.object({
schemaVersion: z.literal(pluginFlowAppSchemaVersion),
mode: z.enum([
"marketplace_plugin_added",
"plugin_access_granted",
"marketplace_access_granted",
]),
pluginId: idSchema.nullable(),
marketplaceId: idSchema.nullable(),
recipient: z.object({
kind: z.enum(["member", "team", "org_wide"]),
id: idSchema.nullable(),
role: z.string().trim().min(1).max(60).nullable(),
}).nullable(),
})
export type PluginFlowPayload = z.infer<typeof pluginFlowPayloadSchema>
+16
View File
@@ -0,0 +1,16 @@
import { z } from "zod"
const idSchema = z.string().trim().min(1).max(160)
export const skillCreatedAppSchemaVersion = "1" as const
export const skillCreatedPayloadSchema = z.object({
schemaVersion: z.literal(skillCreatedAppSchemaVersion),
/** Distinguishes a first save from a new immutable version of an existing skill. Absent means "created". */
mode: z.enum(["created", "updated"]).optional(),
name: z.string().trim().min(1).max(255),
pluginId: idSchema,
skillId: idSchema,
description: z.string().trim().min(1).max(2_000),
libraryUrl: z.string().url().nullable(),
})
export type SkillCreatedPayload = z.infer<typeof skillCreatedPayloadSchema>
+28
View File
@@ -0,0 +1,28 @@
export type BaseUrlEnv = Record<string, string | undefined>;
export function normalizeBaseUrl(value: string | null | undefined): string {
const trimmed = (value ?? "").trim();
let end = trimmed.length;
while (end > 0 && trimmed[end - 1] === "/") end -= 1;
return end === trimmed.length ? trimmed : trimmed.slice(0, end);
}
export function joinBaseUrl(base: string | null | undefined, path: string): string {
const normalizedBase = normalizeBaseUrl(base);
const normalizedPath = path.trim().replace(/^\/+/, "");
return normalizedPath ? `${normalizedBase}/${normalizedPath}` : normalizedBase;
}
export function readBaseUrlEnv(env: BaseUrlEnv, key: string): string | null {
const normalized = normalizeBaseUrl(env[key]);
return normalized ? normalized : null;
}
export function hostLabel(value: string): string {
try {
return new URL(value).host;
} catch {
// Match existing UI fallback: remove a display-only http(s) prefix and trailing slashes.
return normalizeBaseUrl(value).replace(/^https?:\/\//, "");
}
}
+180
View File
@@ -0,0 +1,180 @@
import { z } from "zod"
const idSchema = z.string().trim().min(1).max(160)
const digestSchema = z.string().regex(/^sha256:[a-f0-9]{64}$/)
export const artifactFreshnessSchema = z.discriminatedUnion("state", [
z.object({ state: z.literal("never_run") }),
z.object({ state: z.literal("fresh"), ageMs: z.number().int().nonnegative() }),
z.object({
state: z.literal("stale"),
ageMs: z.number().int().nonnegative(),
maxAgeMs: z.number().int().positive(),
}),
z.object({
state: z.literal("needs_attention"),
ageMs: z.number().int().nonnegative().nullable(),
lastSuccessfulReceiptId: idSchema.nullable(),
reason: z.string().trim().min(1).max(2_000),
}),
])
export type ArtifactFreshness = z.infer<typeof artifactFreshnessSchema>
export const workflowCapabilitySchema = z.object({
capabilityName: z.string().trim().min(1).max(255),
scriptPath: z.string().trim().min(1).max(255),
})
export type WorkflowCapability = z.infer<typeof workflowCapabilitySchema>
export const workflowAutomationReferenceSchema = z.object({
id: idSchema,
name: z.string().trim().min(1).max(120),
state: z.enum(["active", "inactive", "needs_attention", "archived"]),
configObjectVersionId: idSchema,
input: z.unknown().optional(),
})
export type WorkflowAutomationReference = z.infer<typeof workflowAutomationReferenceSchema>
export const workflowVersionSchema = z.object({
id: idSchema,
// Authoring source and example input are OpenWork management data, not MCP
// runtime data. Non-manager detail responses intentionally return null.
code: z.string().nullable(),
inputSchema: z.unknown().nullable(),
outputSchema: z.unknown().nullable(),
exampleInput: z.unknown().nullable().optional(),
requiredCapabilities: z.array(workflowCapabilitySchema),
codeDigest: digestSchema,
inputSchemaDigest: digestSchema.nullable(),
outputSchemaDigest: digestSchema.nullable(),
createdAt: z.string().datetime(),
automationReferences: z.array(workflowAutomationReferenceSchema),
})
export type WorkflowVersion = z.infer<typeof workflowVersionSchema>
export const workflowArtifactSnapshotSchema = z.object({
receiptId: idSchema,
pluginId: idSchema,
configObjectId: idSchema,
configObjectVersionId: idSchema,
automationRunId: idSchema.nullable(),
value: z.unknown().nullable(),
markdown: z.string().nullable(),
codeDigest: digestSchema,
resultDigest: digestSchema.nullable(),
inputSchemaDigest: digestSchema.nullable(),
outputSchemaDigest: digestSchema.nullable(),
rendererVersion: z.literal("codemode-markdown-v1").nullable(),
status: z.enum(["succeeded", "failed"]),
errorKind: z.string().nullable(),
errorMessage: z.string().nullable(),
source: z.enum(["manual", "scheduled"]),
startedAt: z.string().datetime(),
finishedAt: z.string().datetime(),
contentDeletedAt: z.string().datetime().nullable(),
})
export type WorkflowArtifactSnapshot = z.infer<typeof workflowArtifactSnapshotSchema>
export const workflowDetailSchema = z.object({
pluginId: idSchema,
configObjectId: idSchema,
title: z.string().trim().min(1).max(255),
description: z.string().nullable(),
canRun: z.boolean(),
canManage: z.boolean(),
currentVersion: workflowVersionSchema,
versions: z.array(workflowVersionSchema),
latestSnapshot: workflowArtifactSnapshotSchema.nullable(),
latestSuccessfulSnapshot: workflowArtifactSnapshotSchema.nullable(),
freshness: artifactFreshnessSchema,
})
export type WorkflowDetail = z.infer<typeof workflowDetailSchema>
export const workflowTestResultSchema = z.object({
receiptId: idSchema,
value: z.unknown(),
markdown: z.string(),
codeDigest: digestSchema,
resultDigest: digestSchema,
inputSchemaDigest: digestSchema.nullable(),
outputSchemaDigest: digestSchema.nullable(),
rendererVersion: z.literal("codemode-markdown-v1"),
requiredCapabilities: z.array(workflowCapabilitySchema),
finishedAt: z.string().datetime(),
})
export type WorkflowTestResult = z.infer<typeof workflowTestResultSchema>
/**
* Stable data contract injected into the Workflow Artifact MCP App view.
*
* Keep this independent from the presentation resource so MCP hosts and other
* OpenWork surfaces can validate the same result without understanding the UI.
*/
export const workflowArtifactSchemaVersion = "1" as const
export const workflowArtifactPayloadSchema = z.object({
schemaVersion: z.literal(workflowArtifactSchemaVersion),
artifact: z.object({
title: z.string().trim().min(1).max(255),
description: z.string().nullable(),
pluginId: idSchema,
configObjectId: idSchema,
configObjectVersionId: idSchema,
receiptId: idSchema,
automationRunId: idSchema.nullable(),
source: z.enum(["manual", "scheduled"]),
generatedAt: z.string().datetime(),
resultDigest: digestSchema,
rendererVersion: z.literal("codemode-markdown-v1"),
freshness: artifactFreshnessSchema,
}),
data: z.unknown(),
})
export type WorkflowArtifactPayload = z.infer<typeof workflowArtifactPayloadSchema>
export const generatedArtifactViewCspSchema = z.object({
connectDomains: z.array(z.string()).length(0),
resourceDomains: z.array(z.string()).length(0),
frameDomains: z.array(z.string()).length(0),
baseUriDomains: z.array(z.string()).length(0),
})
export type GeneratedArtifactViewCsp = z.infer<typeof generatedArtifactViewCspSchema>
export const generatedArtifactViewBuildDiagnosticSchema = z.object({
level: z.enum(["error", "warning"]),
message: z.string().trim().min(1).max(4_000),
line: z.number().int().positive().nullable(),
column: z.number().int().nonnegative().nullable(),
})
export type GeneratedArtifactViewBuildDiagnostic = z.infer<typeof generatedArtifactViewBuildDiagnosticSchema>
export const generatedArtifactViewRevisionSchema = z.object({
id: idSchema,
artifactViewId: idSchema,
resourceUri: z.string().startsWith("ui://openwork/artifacts/"),
buildStatus: z.enum(["ready", "failed"]),
sourceDigest: digestSchema,
resourceDigest: digestSchema.nullable(),
outputSchemaDigest: digestSchema,
csp: generatedArtifactViewCspSchema,
diagnostics: z.array(generatedArtifactViewBuildDiagnosticSchema),
compilerName: z.string().trim().min(1),
compilerVersion: z.string().trim().min(1),
reactVersion: z.string().trim().min(1),
compiledHtmlBytes: z.number().int().nonnegative().nullable(),
retiredAt: z.string().datetime().nullable(),
createdAt: z.string().datetime(),
})
export type GeneratedArtifactViewRevision = z.infer<typeof generatedArtifactViewRevisionSchema>
export const generatedArtifactViewSchema = z.object({
id: idSchema,
configObjectId: idSchema,
title: z.string().trim().min(1).max(255),
description: z.string().trim().max(2_000).nullable(),
status: z.enum(["active", "retired"]),
activeRevisionId: idSchema.nullable(),
revisions: z.array(generatedArtifactViewRevisionSchema),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
})
export type GeneratedArtifactView = z.infer<typeof generatedArtifactViewSchema>
+53
View File
@@ -0,0 +1,53 @@
/**
* Shared wire contract for workspace records.
*
* Producers:
* - openwork-server (apps/server): `GET /workspaces` and friends — emits plain
* optionals (never null) plus the `opencode*` engine credential fields.
* - desktop Electron IPC bridge (apps/desktop main.mjs): emits explicit nulls
* and the desktop-managed `openworkClientToken`/`openworkHostToken`.
*
* Consumers (apps/app) must treat every optional field as possibly absent,
* undefined, or null. Producer-side types assert assignability against this
* shape (see apps/server/src/types.ts) so drift fails typecheck instead of
* surfacing as runtime undefined-field bugs.
*/
export type WorkspaceKind = "local" | "remote";
export type WorkspaceRemoteKind = "opencode" | "openwork";
export type WorkspaceWire = {
id: string;
name: string;
path: string;
preset: string;
workspaceType: WorkspaceKind;
remoteType?: WorkspaceRemoteKind | null;
baseUrl?: string | null;
directory?: string | null;
displayName?: string | null;
openworkHostUrl?: string | null;
openworkToken?: string | null;
/** Desktop IPC only: tokens for desktop-managed remote workspaces. */
openworkClientToken?: string | null;
openworkHostToken?: string | null;
openworkWorkspaceId?: string | null;
openworkWorkspaceName?: string | null;
/**
* Vocabulary differs per producer today ("docker" | "microsandbox" on the
* desktop, "none" | "docker" | "container" in openwork-server), so the wire
* stays a plain string until the backends converge.
*/
sandboxBackend?: string | null;
sandboxRunId?: string | null;
sandboxContainerName?: string | null;
/** openwork-server only: credentials for the proxied opencode engine. */
opencodeUsername?: string | null;
opencodePassword?: string | null;
opencode?: {
baseUrl?: string;
directory?: string;
username?: string;
password?: string;
} | null;
};
+38
View File
@@ -0,0 +1,38 @@
import { describe, expect, test } from "bun:test";
import { hostLabel, joinBaseUrl, normalizeBaseUrl, readBaseUrlEnv } from "@openwork/types/url";
describe("url primitives", () => {
test("normalizes trailing slashes canonically", () => {
expect(normalizeBaseUrl("http://host/")).toBe("http://host");
expect(normalizeBaseUrl("http://host//")).toBe("http://host");
expect(normalizeBaseUrl("http://host///")).toBe("http://host");
expect(normalizeBaseUrl(`http://host${"/".repeat(100_000)}`)).toBe("http://host");
});
test("trims whitespace and preserves missing schemes", () => {
expect(normalizeBaseUrl(" host/path// ")).toBe("host/path");
});
test("handles empty and missing values", () => {
expect(normalizeBaseUrl("")).toBe("");
expect(normalizeBaseUrl(" ")).toBe("");
expect(normalizeBaseUrl(undefined)).toBe("");
});
test("reads base URL environment values consistently", () => {
expect(readBaseUrlEnv({ DEN_API_BASE: " http://host/// " }, "DEN_API_BASE")).toBe("http://host");
expect(readBaseUrlEnv({ DEN_API_BASE: " " }, "DEN_API_BASE")).toBeNull();
expect(readBaseUrlEnv({}, "DEN_API_BASE")).toBeNull();
});
test("joins paths without producing a double slash", () => {
expect(joinBaseUrl("http://host//", "/opencode")).toBe("http://host/opencode");
});
test("formats host labels with fallback normalization", () => {
expect(hostLabel("https://example.test/workspace")).toBe("example.test");
expect(hostLabel("https://example.test///")).toBe("example.test");
expect(hostLabel("not a url///")).toBe("not a url");
});
});
+17
View File
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"rootDir": "src",
"outDir": "dist",
"declaration": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"resolveJsonModule": true
},
"include": [
"src/**/*"
]
}
+35
View File
@@ -0,0 +1,35 @@
import { defineConfig } from "tsup"
export default defineConfig({
entry: {
index: "src/index.ts",
"agent-context-diagnostics": "src/agent-context-diagnostics.ts",
"openwork-affordance": "src/openwork-affordance.ts",
"openwork-context": "src/openwork-context.ts",
"openwork-provider": "src/openwork-provider.ts",
"automations": "src/automations.ts",
workflows: "src/workflows.ts",
"skill-created-app": "src/skill-created-app.ts",
"connection-action-app": "src/connection-action-app.ts",
"plugin-flow-app": "src/plugin-flow-app.ts",
"den/desktop-app-restrictions": "src/den/desktop-app-restrictions.ts",
"den/desktop-policies": "src/den/desktop-policies.ts",
"den/connect-diagnostics": "src/den/connect-diagnostics.ts",
"den/egress-diagnostics": "src/den/egress-diagnostics.ts",
"den/inference": "src/den/inference.ts",
"den/mcp-connection-action": "src/den/mcp-connection-action.ts",
"den/microsoft-365": "src/den/microsoft-365.ts",
},
tsconfig: "./tsconfig.json",
format: ["esm"],
dts: {
tsconfig: "./tsconfig.json",
},
clean: true,
target: "es2022",
platform: "neutral",
sourcemap: false,
splitting: false,
treeshake: true,
external: ["zod"],
})