diff --git a/packages/paperclip-runner/protocol/manifest.json b/packages/paperclip-runner/protocol/manifest.json index 83ac18ab79d..45171d81730 100644 --- a/packages/paperclip-runner/protocol/manifest.json +++ b/packages/paperclip-runner/protocol/manifest.json @@ -20,12 +20,12 @@ { "path": "schemas/command-v2.schema.json", "id": "https://paperclip.dev/schemas/prp/v2/command.schema.json", - "sha256": "06bb3b3b0b420119007494c64e64a736fd5d58cb68d04ced843ada00c54668be" + "sha256": "7de0dd11ff77ae2bb39b048875dd44349946a0be47039382b12d81e95c9fbfed" }, { "path": "schemas/command.schema.json", "id": "https://paperclip.dev/schemas/prp/v1/command.schema.json", - "sha256": "fc813e6b4d5b7e6890b496bd69848ada25dba58adbf7cf04163ca9e9bf2bcb79" + "sha256": "a35f8eddf3f0d36f01acac5023b848fc3feff1bfd45d2bb02c395d778e496bc9" }, { "path": "schemas/conformance-fixture.schema.json", @@ -40,12 +40,12 @@ { "path": "schemas/event-v2.schema.json", "id": "https://paperclip.dev/schemas/prp/v2/event.schema.json", - "sha256": "d7528ee2a46ad20de831fd4ee4a0a51e8325a9855fed5a9b8c428dcebc39cc11" + "sha256": "5e60de9761dfc5568a331fd5de0190929c0be3a025c64486d296b6041a4cec64" }, { "path": "schemas/event.schema.json", "id": "https://paperclip.dev/schemas/prp/v1/event.schema.json", - "sha256": "d48916630976ff7e850b6e2d7d94131761fda322f5d8a05212651ec46b0db19d" + "sha256": "40ecafed388a65d116e988251dc809768a3f8e39c3db122f2b0072f33871c61d" }, { "path": "schemas/fixture.schema.json", @@ -62,6 +62,11 @@ "id": "https://paperclip.dev/schemas/prp/v1/native-execution.schema.json", "sha256": "7894a809005354c5abdf81d5333a094750c8267473cd426a2c1bcccda41e0214" }, + { + "path": "schemas/output-body-reference.schema.json", + "id": "https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json", + "sha256": "40b5fdec157b3dfe8e49e280cbd7204e6f5f66e333bdaf536876ba7c81e95c19" + }, { "path": "schemas/provider-descriptor.schema.json", "id": "https://paperclip.dev/schemas/prp/v1/provider-descriptor.schema.json", @@ -70,7 +75,7 @@ { "path": "schemas/provider-event.schema.json", "id": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json", - "sha256": "0b138a41bd3bd0b1a115f5536a2d7349537e79e36ede3b51d003f9f21ce73b50" + "sha256": "70f45e12b9fc893c4cd8e82d74865a1f1a4fff7e3563d59b0dc14cf11b4c6ae9" }, { "path": "schemas/question-adapter-fixture.schema.json", diff --git a/packages/paperclip-runner/protocol/schemas/command-v2.schema.json b/packages/paperclip-runner/protocol/schemas/command-v2.schema.json index 1cbde1a467a..bfa88f3a6f3 100644 --- a/packages/paperclip-runner/protocol/schemas/command-v2.schema.json +++ b/packages/paperclip-runner/protocol/schemas/command-v2.schema.json @@ -8,6 +8,7 @@ "schema": { "const": "paperclip.prp.command.v2" }, "commandId": { "type": "string", "minLength": 1, "maxLength": 160 }, "controllerSeq": { "type": "integer", "minimum": 1 }, + "controllerEpoch": { "type": "string", "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$", "description": "Requires negotiated transport.command_epochs.v1; absent is the retained numeric namespace." }, "type": { "enum": [ "run.prepare", "run.attach", "session.open", "turn.start", "turn.steer", "turn.interrupt", "turn.stop", diff --git a/packages/paperclip-runner/protocol/schemas/command.schema.json b/packages/paperclip-runner/protocol/schemas/command.schema.json index b6ae97cca73..c6284642403 100644 --- a/packages/paperclip-runner/protocol/schemas/command.schema.json +++ b/packages/paperclip-runner/protocol/schemas/command.schema.json @@ -8,6 +8,7 @@ "schema": { "const": "paperclip.prp.command.v1" }, "commandId": { "type": "string", "minLength": 1, "maxLength": 160 }, "controllerSeq": { "type": "integer", "minimum": 1 }, + "controllerEpoch": { "type": "string", "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$", "description": "Requires negotiated transport.command_epochs.v1; absent is the retained numeric namespace." }, "type": { "enum": [ "run.prepare", diff --git a/packages/paperclip-runner/protocol/schemas/event-v2.schema.json b/packages/paperclip-runner/protocol/schemas/event-v2.schema.json index 8cfc4558de7..a0f9c204a40 100644 --- a/packages/paperclip-runner/protocol/schemas/event-v2.schema.json +++ b/packages/paperclip-runner/protocol/schemas/event-v2.schema.json @@ -4,60 +4,300 @@ "title": "PRP v2 native event", "type": "object", "required": [ - "schema", "sourceEventId", "sourceSeq", "sourceInstanceId", "sourceKind", "runId", - "eventType", "schemaVersion", "priority", "emittedAt", "payload" + "schema", + "sourceEventId", + "sourceSeq", + "sourceInstanceId", + "sourceKind", + "runId", + "eventType", + "schemaVersion", + "priority", + "emittedAt", + "payload" ], "properties": { - "schema": { "const": "paperclip.prp.event.v2" }, - "sourceEventId": { "type": "string", "minLength": 1, "maxLength": 160 }, - "sourceSeq": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, - "sourceInstanceId": { "type": "string", "minLength": 1, "maxLength": 160 }, - "sourceKind": { "enum": ["runner", "control_plane"] }, - "runId": { "type": "string", "minLength": 1, "maxLength": 160 }, - "normalizedSessionId": { "type": "string", "minLength": 1, "maxLength": 160 }, - "turnId": { "type": "string", "minLength": 1, "maxLength": 160 }, - "itemId": { "type": "string", "minLength": 1, "maxLength": 160 }, - "eventType": { + "schema": { + "const": "paperclip.prp.event.v2" + }, + "sourceEventId": { + "type": "string", + "minLength": 1, + "maxLength": 160 + }, + "sourceEpoch": { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" + }, + "sourceSeq": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "sourceInstanceId": { + "type": "string", + "minLength": 1, + "maxLength": 160 + }, + "sourceKind": { "enum": [ - "runner.connected", "runner.reconnected", "runner.reconciled", "runner.disconnected", "runner.draining", "runner.suspending", "runner.suspended", "runner.stopped", "runner.diagnostic", - "runtime.phase.changed", "sandbox.metric", "workspace.ready", "workspace.change.updated", "workspace.diff.recorded", "workspace.file.referenced", - "harness.starting", "harness.ready", "harness.exited", "harness.diagnostic", "plan.updated", - "tool.execution.started", "tool.execution.progressed", "tool.execution.completed", "research.started", "research.progressed", "research.completed", - "delegation.started", "delegation.updated", "delegation.completed", "model.route.changed", "model.verification.updated", "context.compacted", - "artifact.viewed", "artifact.generated", "review.mode.changed", "hook.started", "hook.completed", "memory.citation.referenced", - "safety.review.started", "safety.review.completed", "terminal.input.sent", "wait.started", "wait.completed", "provider.notice.recorded", - "session.starting", "session.started", "session.resuming", "session.resumed", "session.reconciled", "session.updated", "session.closed", "session.failed", - "session.capabilities.updated", "session.goal.snapshot", "session.goal.updated", "session.goal.cleared", - "turn.submitted", "turn.accepted", "turn.started", "turn.completed", "turn.failed", "turn.interrupted", "turn.cancelled", - "item.started", "item.delta", "item.completed", "item.failed", "usage.reported", "semantic_tool.input", "semantic_tool.result", - "mcp_app.discovered", "mcp_app.resource.resolved", "mcp_app.initializing", "mcp_app.ready", "mcp_app.tool_input", "mcp_app.tool_result", "mcp_app.action.requested", "mcp_app.action.resolved", "mcp_app.host_context.changed", "mcp_app.failed", "mcp_app.teardown", - "runtime_request.created", "runtime_request.resolved", "runtime_request.expired", "runtime_request.cancelled", - "interaction.request.proposed", "interaction.request.materialized", "interaction.request.rejected", "interaction.response.progressed", "interaction.response.resolved", "interaction.response.delivered", - "run.attached", "run.detached", "run.result.proposed", "run.result.accepted", "run.result.rejected", - "attention.request.proposed", "attention.request.routed", "attention.request.resolved", "attention.request.expired", "attention.request.superseded", - "work.assessment.recorded", "issue.status.decision.recorded", "issue.status.decision.applied", "issue.status.decision.rejected", "issue.status.decision.superseded", "run.terminal" + "runner", + "control_plane" ] }, - "schemaVersion": { "const": 2 }, - "priority": { "enum": [0, 1, 2] }, - "emittedAt": { "type": "string", "format": "date-time" }, - "observedAt": { "type": "string", "format": "date-time" }, - "payload": { "type": "object", "additionalProperties": true }, - "debug": { "type": "object", "additionalProperties": true } + "runId": { + "type": "string", + "minLength": 1, + "maxLength": 160 + }, + "normalizedSessionId": { + "type": "string", + "minLength": 1, + "maxLength": 160 + }, + "turnId": { + "type": "string", + "minLength": 1, + "maxLength": 160 + }, + "itemId": { + "type": "string", + "minLength": 1, + "maxLength": 160 + }, + "eventType": { + "enum": [ + "runner.connected", + "runner.reconnected", + "runner.reconciled", + "runner.disconnected", + "runner.draining", + "runner.suspending", + "runner.suspended", + "runner.stopped", + "runner.diagnostic", + "runtime.phase.changed", + "sandbox.metric", + "workspace.ready", + "workspace.change.updated", + "workspace.diff.recorded", + "workspace.file.referenced", + "harness.starting", + "harness.ready", + "harness.exited", + "harness.diagnostic", + "plan.updated", + "tool.execution.started", + "tool.execution.progressed", + "tool.execution.completed", + "research.started", + "research.progressed", + "research.completed", + "delegation.started", + "delegation.updated", + "delegation.completed", + "model.route.changed", + "model.verification.updated", + "context.compacted", + "artifact.viewed", + "artifact.generated", + "review.mode.changed", + "hook.started", + "hook.completed", + "memory.citation.referenced", + "safety.review.started", + "safety.review.completed", + "terminal.input.sent", + "wait.started", + "wait.completed", + "provider.notice.recorded", + "session.starting", + "session.started", + "session.resuming", + "session.resumed", + "session.reconciled", + "session.updated", + "session.closed", + "session.failed", + "session.capabilities.updated", + "session.goal.snapshot", + "session.goal.updated", + "session.goal.cleared", + "turn.submitted", + "turn.accepted", + "turn.started", + "turn.completed", + "turn.failed", + "turn.interrupted", + "turn.cancelled", + "output.body.chunk", + "item.started", + "item.delta", + "item.completed", + "item.failed", + "usage.reported", + "semantic_tool.input", + "semantic_tool.result", + "mcp_app.discovered", + "mcp_app.resource.resolved", + "mcp_app.initializing", + "mcp_app.ready", + "mcp_app.tool_input", + "mcp_app.tool_result", + "mcp_app.action.requested", + "mcp_app.action.resolved", + "mcp_app.host_context.changed", + "mcp_app.failed", + "mcp_app.teardown", + "runtime_request.created", + "runtime_request.resolved", + "runtime_request.expired", + "runtime_request.cancelled", + "interaction.request.proposed", + "interaction.request.materialized", + "interaction.request.rejected", + "interaction.response.progressed", + "interaction.response.resolved", + "interaction.response.delivered", + "run.attached", + "run.detached", + "run.result.proposed", + "run.result.accepted", + "run.result.rejected", + "attention.request.proposed", + "attention.request.routed", + "attention.request.resolved", + "attention.request.expired", + "attention.request.superseded", + "work.assessment.recorded", + "issue.status.decision.recorded", + "issue.status.decision.applied", + "issue.status.decision.rejected", + "issue.status.decision.superseded", + "run.terminal" + ] + }, + "schemaVersion": { + "const": 2 + }, + "priority": { + "enum": [ + 0, + 1, + 2 + ] + }, + "emittedAt": { + "type": "string", + "format": "date-time" + }, + "observedAt": { + "type": "string", + "format": "date-time" + }, + "payload": { + "type": "object", + "additionalProperties": true + }, + "debug": { + "type": "object", + "additionalProperties": true + }, + "sourceEpochTransition": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema", + "runId", + "transitionId", + "fromEpoch", + "nextEpoch", + "finalOrdinal" + ], + "properties": { + "schema": { + "const": "paperclip.prp.event-epoch.v1" + }, + "runId": { + "type": "string", + "minLength": 1, + "maxLength": 240 + }, + "transitionId": { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" + }, + "fromEpoch": { + "anyOf": [ + { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" + }, + { + "type": "null" + } + ] + }, + "nextEpoch": { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" + }, + "finalOrdinal": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + } + } + } }, "allOf": [ { - "if": { "properties": { "eventType": { "enum": ["session.goal.snapshot", "session.goal.updated"] } } }, + "if": { + "properties": { + "eventType": { + "enum": [ + "tool.execution.started", + "tool.execution.progressed", + "tool.execution.completed" + ] + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/toolExecution" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "enum": [ + "session.goal.snapshot", + "session.goal.updated" + ] + } + } + }, "then": { "properties": { "payload": { "type": "object", - "required": ["goal"], + "required": [ + "goal" + ], "properties": { "goal": { "oneOf": [ - { "$ref": "https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot" }, - { "type": "null" } + { + "$ref": "https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot" + }, + { + "type": "null" + } ] } }, @@ -67,19 +307,102 @@ } }, { - "if": { "properties": { "eventType": { "const": "session.capabilities.updated" } } }, + "if": { + "properties": { + "eventType": { + "const": "session.capabilities.updated" + } + } + }, "then": { "properties": { "payload": { "type": "object", - "required": ["sessionGoals"], + "required": [ + "sessionGoals" + ], "properties": { - "sessionGoals": { "$ref": "https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability" } + "sessionGoals": { + "$ref": "https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability" + } }, "additionalProperties": true } } } + }, + { + "if": { + "properties": { + "eventType": { + "const": "output.body.chunk" + } + } + }, + "then": { + "properties": { + "payload": { + "type": "object", + "required": [ + "schema", + "body", + "offset", + "sha256", + "text" + ], + "properties": { + "schema": { + "const": "paperclip.output.body.chunk.v1" + }, + "body": { + "type": "object", + "required": [ + "schema", + "bodyId", + "sha256", + "byteLength", + "mediaType" + ], + "properties": { + "schema": { + "const": "paperclip.output.body.v1" + }, + "bodyId": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "byteLength": { + "type": "string", + "pattern": "^[1-9][0-9]{0,6}$" + }, + "mediaType": { + "const": "text/plain; charset=utf-8" + } + }, + "additionalProperties": false + }, + "offset": { + "type": "string", + "pattern": "^(0|[1-9][0-9]{0,6})$" + }, + "sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "text": { + "type": "string", + "minLength": 1, + "maxLength": 32766 + } + }, + "additionalProperties": false + } + } + } } ], "additionalProperties": true diff --git a/packages/paperclip-runner/protocol/schemas/event.schema.json b/packages/paperclip-runner/protocol/schemas/event.schema.json index 5beda544478..1f59806f748 100644 --- a/packages/paperclip-runner/protocol/schemas/event.schema.json +++ b/packages/paperclip-runner/protocol/schemas/event.schema.json @@ -17,105 +17,606 @@ "payload" ], "properties": { - "schema": { "const": "paperclip.prp.event.v1" }, - "sourceEventId": { "type": "string", "minLength": 1, "maxLength": 160 }, - "sourceSeq": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, - "sourceInstanceId": { "type": "string", "minLength": 1, "maxLength": 160 }, - "sourceKind": { "enum": ["runner", "control_plane"] }, - "runId": { "type": "string", "minLength": 1, "maxLength": 160 }, - "normalizedSessionId": { "type": "string", "minLength": 1, "maxLength": 160 }, - "turnId": { "type": "string", "minLength": 1, "maxLength": 240 }, - "itemId": { "type": "string", "minLength": 1, "maxLength": 240 }, + "schema": { + "const": "paperclip.prp.event.v1" + }, + "sourceEventId": { + "type": "string", + "minLength": 1, + "maxLength": 160 + }, + "sourceEpoch": { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" + }, + "sourceSeq": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "sourceInstanceId": { + "type": "string", + "minLength": 1, + "maxLength": 160 + }, + "sourceKind": { + "enum": [ + "runner", + "control_plane" + ] + }, + "runId": { + "type": "string", + "minLength": 1, + "maxLength": 160 + }, + "normalizedSessionId": { + "type": "string", + "minLength": 1, + "maxLength": 160 + }, + "turnId": { + "type": "string", + "minLength": 1, + "maxLength": 240 + }, + "itemId": { + "type": "string", + "minLength": 1, + "maxLength": 240 + }, "eventType": { "enum": [ - "runner.connected", "runner.reconnected", "runner.reconciled", "runner.disconnected", "runner.draining", "runner.backpressure", "runner.suspending", "runner.suspended", "runner.stopped", "runner.diagnostic", - "runtime.phase.changed", "sandbox.metric", "workspace.ready", "workspace.change.updated", "workspace.diff.recorded", "workspace.file.referenced", - "harness.starting", "harness.ready", "harness.exited", "harness.diagnostic", + "runner.connected", + "runner.reconnected", + "runner.reconciled", + "runner.disconnected", + "runner.draining", + "runner.backpressure", + "runner.suspending", + "runner.suspended", + "runner.stopped", + "runner.diagnostic", + "runtime.phase.changed", + "sandbox.metric", + "workspace.ready", + "workspace.change.updated", + "workspace.diff.recorded", + "workspace.file.referenced", + "harness.starting", + "harness.ready", + "harness.exited", + "harness.diagnostic", "plan.updated", - "tool.execution.started", "tool.execution.progressed", "tool.execution.completed", - "research.started", "research.progressed", "research.completed", - "delegation.started", "delegation.updated", "delegation.completed", - "model.route.changed", "model.verification.updated", "context.compacted", - "artifact.viewed", "artifact.generated", "review.mode.changed", - "hook.started", "hook.completed", "memory.citation.referenced", - "safety.review.started", "safety.review.completed", "terminal.input.sent", - "wait.started", "wait.completed", "provider.notice.recorded", - "session.starting", "session.started", "session.resuming", "session.resumed", "session.reconciled", "session.updated", "session.closed", "session.failed", - "turn.submitted", "turn.accepted", "turn.started", "turn.completed", "turn.failed", "turn.interrupted", "turn.cancelled", - "item.started", "item.delta", "item.completed", "item.failed", "usage.reported", - "semantic_tool.input", "semantic_tool.result", "semantic_tool.reconciled", - "mcp_app.discovered", "mcp_app.resource.resolved", "mcp_app.initializing", "mcp_app.ready", "mcp_app.tool_input", "mcp_app.tool_result", "mcp_app.action.requested", "mcp_app.action.resolved", "mcp_app.host_context.changed", "mcp_app.failed", "mcp_app.teardown", - "runtime_request.created", "runtime_request.resolved", "runtime_request.expired", "runtime_request.cancelled", - "interaction.request.proposed", "interaction.request.materialized", "interaction.request.rejected", "interaction.response.progressed", "interaction.response.resolved", "interaction.response.delivered", - "run.attached", "run.detached", "run.result.proposed", "run.result.accepted", "run.result.rejected", - "attention.request.proposed", "attention.request.routed", "attention.request.resolved", "attention.request.expired", "attention.request.superseded", - "work.assessment.recorded", "issue.status.decision.recorded", "issue.status.decision.applied", "issue.status.decision.rejected", "issue.status.decision.superseded", + "tool.execution.started", + "tool.execution.progressed", + "tool.execution.completed", + "research.started", + "research.progressed", + "research.completed", + "delegation.started", + "delegation.updated", + "delegation.completed", + "model.route.changed", + "model.verification.updated", + "context.compacted", + "artifact.viewed", + "artifact.generated", + "review.mode.changed", + "hook.started", + "hook.completed", + "memory.citation.referenced", + "safety.review.started", + "safety.review.completed", + "terminal.input.sent", + "wait.started", + "wait.completed", + "provider.notice.recorded", + "session.starting", + "session.started", + "session.resuming", + "session.resumed", + "session.reconciled", + "session.updated", + "session.closed", + "session.failed", + "turn.submitted", + "turn.accepted", + "turn.started", + "turn.completed", + "turn.failed", + "turn.interrupted", + "turn.cancelled", + "output.body.chunk", + "item.started", + "item.delta", + "item.completed", + "item.failed", + "usage.reported", + "semantic_tool.input", + "semantic_tool.result", + "semantic_tool.reconciled", + "mcp_app.discovered", + "mcp_app.resource.resolved", + "mcp_app.initializing", + "mcp_app.ready", + "mcp_app.tool_input", + "mcp_app.tool_result", + "mcp_app.action.requested", + "mcp_app.action.resolved", + "mcp_app.host_context.changed", + "mcp_app.failed", + "mcp_app.teardown", + "runtime_request.created", + "runtime_request.resolved", + "runtime_request.expired", + "runtime_request.cancelled", + "interaction.request.proposed", + "interaction.request.materialized", + "interaction.request.rejected", + "interaction.response.progressed", + "interaction.response.resolved", + "interaction.response.delivered", + "run.attached", + "run.detached", + "run.result.proposed", + "run.result.accepted", + "run.result.rejected", + "attention.request.proposed", + "attention.request.routed", + "attention.request.resolved", + "attention.request.expired", + "attention.request.superseded", + "work.assessment.recorded", + "issue.status.decision.recorded", + "issue.status.decision.applied", + "issue.status.decision.rejected", + "issue.status.decision.superseded", "run.terminal" ] }, - "schemaVersion": { "const": 1 }, - "priority": { "enum": [0, 1, 2] }, - "emittedAt": { "type": "string", "format": "date-time" }, - "observedAt": { "type": "string", "format": "date-time" }, + "schemaVersion": { + "const": 1 + }, + "priority": { + "enum": [ + 0, + 1, + 2 + ] + }, + "emittedAt": { + "type": "string", + "format": "date-time" + }, + "observedAt": { + "type": "string", + "format": "date-time" + }, "payload": { "type": "object", "properties": { - "channel": { "enum": ["progress", "final", "summary", "detail", "unknown"] }, - "providerPhase": { "type": "string" }, - "providerMethod": { "type": "string" } + "channel": { + "enum": [ + "progress", + "final", + "summary", + "detail", + "unknown" + ] + }, + "providerPhase": { + "type": "string" + }, + "providerMethod": { + "type": "string" + } }, "additionalProperties": true }, - "debug": { "type": "object", "additionalProperties": true } + "debug": { + "type": "object", + "additionalProperties": true + }, + "sourceEpochTransition": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema", + "runId", + "transitionId", + "fromEpoch", + "nextEpoch", + "finalOrdinal" + ], + "properties": { + "schema": { + "const": "paperclip.prp.event-epoch.v1" + }, + "runId": { + "type": "string", + "minLength": 1, + "maxLength": 240 + }, + "transitionId": { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" + }, + "fromEpoch": { + "anyOf": [ + { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" + }, + { + "type": "null" + } + ] + }, + "nextEpoch": { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" + }, + "finalOrdinal": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + } + } + } }, "allOf": [ - { "if": { "properties": { "eventType": { "const": "plan.updated" } } }, "then": { "properties": { "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/plan" } } } }, - { "if": { "properties": { "eventType": { "enum": ["tool.execution.started", "tool.execution.progressed", "tool.execution.completed"] } } }, "then": { "properties": { "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/toolExecution" } } } }, - { "if": { "properties": { "eventType": { "enum": ["research.started", "research.progressed", "research.completed"] } } }, "then": { "properties": { "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/research" } } } }, - { "if": { "properties": { "eventType": { "enum": ["delegation.started", "delegation.updated", "delegation.completed"] } } }, "then": { "properties": { "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/delegation" } } } }, - { "if": { "properties": { "eventType": { "const": "model.route.changed" } } }, "then": { "properties": { "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/modelRoute" } } } }, - { "if": { "properties": { "eventType": { "const": "model.verification.updated" } } }, "then": { "properties": { "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/modelVerification" } } } }, - { "if": { "properties": { "eventType": { "const": "context.compacted" } } }, "then": { "properties": { "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/contextCompacted" } } } }, - { "if": { "properties": { "eventType": { "const": "artifact.viewed" } } }, "then": { "properties": { "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/artifactViewed" } } } }, - { "if": { "properties": { "eventType": { "const": "artifact.generated" } } }, "then": { "properties": { "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/artifactGenerated" } } } }, - { "if": { "properties": { "eventType": { "const": "review.mode.changed" } } }, "then": { "properties": { "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/reviewMode" } } } }, - { "if": { "properties": { "eventType": { "enum": ["hook.started", "hook.completed"] } } }, "then": { "properties": { "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/hook" } } } }, - { "if": { "properties": { "eventType": { "const": "memory.citation.referenced" } } }, "then": { "properties": { "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/memoryCitation" } } } }, - { "if": { "properties": { "eventType": { "enum": ["safety.review.started", "safety.review.completed"] } } }, "then": { "properties": { "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/safetyReview" } } } }, - { "if": { "properties": { "eventType": { "const": "terminal.input.sent" } } }, "then": { "properties": { "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/terminalInput" } } } }, - { "if": { "properties": { "eventType": { "enum": ["wait.started", "wait.completed"] } } }, "then": { "properties": { "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/wait" } } } }, - { "if": { "properties": { "eventType": { "const": "provider.notice.recorded" } } }, "then": { "properties": { "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/providerNotice" } } } }, { - "if": { "properties": { "eventType": { "const": "workspace.file.referenced" } } }, + "if": { + "properties": { + "eventType": { + "const": "plan.updated" + } + } + }, "then": { "properties": { - "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json" } + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/plan" + } } } }, { - "if": { "properties": { "eventType": { "enum": ["workspace.change.updated", "workspace.diff.recorded"] } } }, + "if": { + "properties": { + "eventType": { + "enum": [ + "tool.execution.started", + "tool.execution.progressed", + "tool.execution.completed" + ] + } + } + }, "then": { "properties": { - "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json" } + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/toolExecution" + } } } }, { - "if": { "properties": { "eventType": { "const": "workspace.diff.recorded" } } }, + "if": { + "properties": { + "eventType": { + "enum": [ + "research.started", + "research.progressed", + "research.completed" + ] + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/research" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "enum": [ + "delegation.started", + "delegation.updated", + "delegation.completed" + ] + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/delegation" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "const": "model.route.changed" + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/modelRoute" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "const": "model.verification.updated" + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/modelVerification" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "const": "context.compacted" + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/contextCompacted" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "const": "artifact.viewed" + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/artifactViewed" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "const": "artifact.generated" + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/artifactGenerated" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "const": "review.mode.changed" + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/reviewMode" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "enum": [ + "hook.started", + "hook.completed" + ] + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/hook" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "const": "memory.citation.referenced" + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/memoryCitation" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "enum": [ + "safety.review.started", + "safety.review.completed" + ] + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/safetyReview" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "const": "terminal.input.sent" + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/terminalInput" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "enum": [ + "wait.started", + "wait.completed" + ] + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/wait" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "const": "provider.notice.recorded" + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/providerNotice" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "const": "workspace.file.referenced" + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "enum": [ + "workspace.change.updated", + "workspace.diff.recorded" + ] + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "const": "workspace.diff.recorded" + } + } + }, "then": { "properties": { "payload": { "allOf": [ - { "$ref": "https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json" }, - { "type": "object", "properties": { "complete": { "const": true } } } + { + "$ref": "https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json" + }, + { + "type": "object", + "properties": { + "complete": { + "const": true + } + } + } ] } } } }, { - "if": { "properties": { "eventType": { "enum": ["semantic_tool.input", "mcp_app.tool_input"] } } }, + "if": { + "properties": { + "eventType": { + "enum": [ + "semantic_tool.input", + "mcp_app.tool_input" + ] + } + } + }, "then": { "properties": { "payload": { @@ -123,8 +624,17 @@ "properties": { "semantic_tool": { "allOf": [ - { "$ref": "https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json" }, - { "type": "object", "properties": { "phase": { "const": "input" } } } + { + "$ref": "https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json" + }, + { + "type": "object", + "properties": { + "phase": { + "const": "input" + } + } + } ] } }, @@ -134,7 +644,16 @@ } }, { - "if": { "properties": { "eventType": { "enum": ["semantic_tool.result", "mcp_app.tool_result"] } } }, + "if": { + "properties": { + "eventType": { + "enum": [ + "semantic_tool.result", + "mcp_app.tool_result" + ] + } + } + }, "then": { "properties": { "payload": { @@ -142,8 +661,17 @@ "properties": { "semantic_tool": { "allOf": [ - { "$ref": "https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json" }, - { "type": "object", "properties": { "phase": { "const": "result" } } } + { + "$ref": "https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json" + }, + { + "type": "object", + "properties": { + "phase": { + "const": "result" + } + } + } ] } }, @@ -153,7 +681,13 @@ } }, { - "if": { "properties": { "eventType": { "const": "semantic_tool.reconciled" } } }, + "if": { + "properties": { + "eventType": { + "const": "semantic_tool.reconciled" + } + } + }, "then": { "properties": { "payload": { @@ -161,8 +695,17 @@ "properties": { "semantic_tool": { "allOf": [ - { "$ref": "https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json" }, - { "type": "object", "properties": { "phase": { "const": "reconciled" } } } + { + "$ref": "https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json" + }, + { + "type": "object", + "properties": { + "phase": { + "const": "reconciled" + } + } + } ] } }, @@ -172,18 +715,107 @@ } }, { - "if": { "properties": { "eventType": { "const": "run.terminal" } } }, + "if": { + "properties": { + "eventType": { + "const": "run.terminal" + } + } + }, "then": { "properties": { - "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/terminal.schema.json" } + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/terminal.schema.json" + } } } }, { - "if": { "properties": { "eventType": { "const": "run.result.proposed" } } }, + "if": { + "properties": { + "eventType": { + "const": "run.result.proposed" + } + } + }, "then": { "properties": { - "payload": { "$ref": "https://paperclip.dev/schemas/prp/v1/result.schema.json" } + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/result.schema.json" + } + } + } + }, + { + "if": { + "properties": { + "eventType": { + "const": "output.body.chunk" + } + } + }, + "then": { + "properties": { + "payload": { + "type": "object", + "required": [ + "schema", + "body", + "offset", + "sha256", + "text" + ], + "properties": { + "schema": { + "const": "paperclip.output.body.chunk.v1" + }, + "body": { + "type": "object", + "required": [ + "schema", + "bodyId", + "sha256", + "byteLength", + "mediaType" + ], + "properties": { + "schema": { + "const": "paperclip.output.body.v1" + }, + "bodyId": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "byteLength": { + "type": "string", + "pattern": "^[1-9][0-9]{0,6}$" + }, + "mediaType": { + "const": "text/plain; charset=utf-8" + } + }, + "additionalProperties": false + }, + "offset": { + "type": "string", + "pattern": "^(0|[1-9][0-9]{0,6})$" + }, + "sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "text": { + "type": "string", + "minLength": 1, + "maxLength": 32766 + } + }, + "additionalProperties": false + } } } } diff --git a/packages/paperclip-runner/protocol/schemas/output-body-reference.schema.json b/packages/paperclip-runner/protocol/schemas/output-body-reference.schema.json new file mode 100644 index 00000000000..824f91e1557 --- /dev/null +++ b/packages/paperclip-runner/protocol/schemas/output-body-reference.schema.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json", + "title": "Immutable output body reference", + "type": "object", + "required": ["schema", "bodyId", "sha256", "byteLength", "mediaType"], + "properties": { + "schema": { "const": "paperclip.output.body.v1" }, + "bodyId": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, + "sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, + "byteLength": { "type": "string", "pattern": "^[1-9][0-9]{0,6}$" }, + "mediaType": { "const": "text/plain; charset=utf-8" } + }, + "additionalProperties": false +} diff --git a/packages/paperclip-runner/protocol/schemas/provider-event.schema.json b/packages/paperclip-runner/protocol/schemas/provider-event.schema.json index 110f0ede5c4..f08fe313178 100644 --- a/packages/paperclip-runner/protocol/schemas/provider-event.schema.json +++ b/packages/paperclip-runner/protocol/schemas/provider-event.schema.json @@ -62,7 +62,8 @@ "output": { "type": ["string", "null"], "maxLength": 65536 }, "outputBytes": { "type": "integer", "minimum": 0 }, "outputTruncated": { "type": "boolean" }, - "outputDigest": { "type": ["string", "null"], "pattern": "^sha256:[a-f0-9]{64}$" } + "outputDigest": { "type": ["string", "null"], "pattern": "^sha256:[a-f0-9]{64}$" }, + "outputBody": { "$ref": "https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json" } }, "additionalProperties": false }, diff --git a/packages/paperclip-runner/runner/Cargo.lock b/packages/paperclip-runner/runner/Cargo.lock index 0d730210424..f60b1517c36 100644 --- a/packages/paperclip-runner/runner/Cargo.lock +++ b/packages/paperclip-runner/runner/Cargo.lock @@ -940,6 +940,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fancy-regex" version = "0.19.0" @@ -1162,6 +1174,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash", +] + [[package]] name = "hashbrown" version = "0.17.1" @@ -1173,6 +1194,15 @@ dependencies = [ "foldhash", ] +[[package]] +name = "hashlink" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a596f1b20ed2cc5ecac41a164aaebc7258057060f06c0cf7a2ba3991ee7990fb" +dependencies = [ + "hashbrown 0.17.1", +] + [[package]] name = "heck" version = "0.5.0" @@ -1445,7 +1475,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07aa2048142242915a31d35844fb311e0e53fcca590c3a0a40dcf1b841fa09eb" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.17.1", ] [[package]] @@ -1548,6 +1578,17 @@ version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" +[[package]] +name = "libsqlite3-sys" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1d20bef17f513b9b3004532233187769cd072d790971f4e4da0e346eb6401e8" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -1581,7 +1622,7 @@ version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d317b4b9eb398e6acce275758ec6125535505e7a146fb1a9b8bda2451b0ff4c" dependencies = [ - "hashbrown", + "hashbrown 0.17.1", ] [[package]] @@ -1761,6 +1802,7 @@ dependencies = [ "num-bigint", "num-traits", "reqwest", + "rusqlite", "rustix", "rustls", "rustls-native-certs", @@ -2074,7 +2116,7 @@ dependencies = [ "ahash", "fluent-uri", "getrandom 0.3.4", - "hashbrown", + "hashbrown 0.17.1", "itoa", "micromap", "parking_lot", @@ -2181,6 +2223,31 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rsqlite-vfs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c" +dependencies = [ + "hashbrown 0.16.1", + "thiserror", +] + +[[package]] +name = "rusqlite" +version = "0.40.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f2a97da3e3873c73cb2a2e71b35c40ff95e0b1eefa8d72d8499a6928c3b5b3" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", + "sqlite-wasm-rs", +] + [[package]] name = "rustc-hash" version = "2.1.3" @@ -2490,6 +2557,18 @@ dependencies = [ "der", ] +[[package]] +name = "sqlite-wasm-rs" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc3efc0da82635d7e1ced0053bbbfa8c7ab9645d0bf36ceb4f7127bb85315d75" +dependencies = [ + "cc", + "js-sys", + "rsqlite-vfs", + "wasm-bindgen", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -2889,6 +2968,12 @@ dependencies = [ "vsimd", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" diff --git a/packages/paperclip-runner/runner/Cargo.toml b/packages/paperclip-runner/runner/Cargo.toml index f922269a720..8d2d00a9e58 100644 --- a/packages/paperclip-runner/runner/Cargo.toml +++ b/packages/paperclip-runner/runner/Cargo.toml @@ -30,6 +30,7 @@ rustls-native-certs = "0.8" rustls-pemfile = "2.2" reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "rustls-tls"] } rustix = { version = "1.1", features = ["fs"] } +rusqlite = { version = "=0.40.2", features = ["bundled"] } sha1 = "0.10" tungstenite = { version = "0.28", default-features = false, features = ["handshake", "rustls-tls-native-roots"] } toml_edit = "0.22" diff --git a/packages/paperclip-runner/runner/crates/runner-core/Cargo.toml b/packages/paperclip-runner/runner/crates/runner-core/Cargo.toml index 286beddb598..3762da1eacd 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/Cargo.toml +++ b/packages/paperclip-runner/runner/crates/runner-core/Cargo.toml @@ -27,6 +27,7 @@ rustls-native-certs.workspace = true rustls-pemfile.workspace = true reqwest.workspace = true rustix.workspace = true +rusqlite.workspace = true sha1.workspace = true tungstenite.workspace = true toml_edit.workspace = true diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_event_payload.rs b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_event_payload.rs index 7ab36472b12..f97af9389ca 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_event_payload.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_event_payload.rs @@ -1,3 +1,4 @@ +use serde::{Deserialize, Serialize}; use std::collections::BTreeSet; use serde_json::Value; @@ -30,7 +31,7 @@ pub enum AcpxRuntimeEventKind { Done, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum AcpxTurnStatus { Completed, Failed, diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_event_scope.rs b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_event_scope.rs index 8b05b618d1b..44a98c60aca 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_event_scope.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_event_scope.rs @@ -1,3 +1,4 @@ +use serde::{Deserialize, Serialize}; use std::collections::BTreeSet; use crate::acpx_sidecar_transport::AcpxSidecarEvent; @@ -12,7 +13,7 @@ const MAX_SETTLED_TURN_IDS: usize = 4_096; /// The sidecar transport validates framing and sequence identity. This scope /// validates that a well-formed event still belongs to the run and turn that /// runnerd is currently executing. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct AcpxEventScope { run_id: String, active_turn_id: Option, diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs index 41aa785e585..dfd1cd06968 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs @@ -378,6 +378,10 @@ impl AcpxProviderDescriptor { #[serde(rename_all = "camelCase", deny_unknown_fields)] struct AcpxDurableState { schema: String, + #[serde(default)] + indexed_receipts: bool, + #[serde(default)] + last_receipt_batch: Option, launch_profile_digest: String, lifecycle: String, descriptor: AcpxProviderDescriptor, @@ -410,6 +414,8 @@ impl AcpxDurableState { ) -> Self { Self { schema: ACPX_PROVIDER_STATE_SCHEMA.to_owned(), + indexed_receipts: false, + last_receipt_batch: None, launch_profile_digest, lifecycle: "prepared".to_owned(), descriptor, @@ -455,6 +461,12 @@ impl AcpxDurableState { | "suspended" | "closed" ) + || self.last_receipt_batch.as_ref().is_some_and(|batch| { + !self.indexed_receipts + || batch.parse::().ok().is_none_or(|value| { + value == 0 || value > i64::MAX as u64 || value.to_string() != *batch + }) + }) || self.next_event_sequence == 0 || self.pending_events.len() > MAX_PENDING_EVENTS || self.pending_events.iter().any(|event| { @@ -520,6 +532,7 @@ pub struct AcpxCommandExecutor { session: Option, restore_checked: bool, restore_error: Option, + persistence_error: Option, launch_profile: Option, } @@ -538,6 +551,7 @@ impl AcpxCommandExecutor { session: None, restore_checked: false, restore_error: None, + persistence_error: None, launch_profile: config.acpx_launch_profile.clone(), } } @@ -558,6 +572,7 @@ impl AcpxCommandExecutor { } fn restore(&mut self) -> Result<(), DurableRunnerError> { + self.assert_persistence_known()?; if self.restore_checked { return Ok(()); } @@ -698,7 +713,15 @@ impl AcpxCommandExecutor { expected, self.launch_profile.as_ref(), )?; - let mut session = AcpxProviderSession::start(&config).map_err(|error| { + let started = if state.indexed_receipts { + AcpxProviderSession::start_with_indexed_receipts( + &config, + &self.state_dir.join("acpx-provider-receipts.sqlite"), + ) + } else { + AcpxProviderSession::start(&config) + }; + let mut session = started.map_err(|error| { DurableRunnerError::invalid(format!("failed to start ACPX provider: {error}")) })?; if session.identity().profile_digest != state.descriptor.command_digest { @@ -710,7 +733,32 @@ impl AcpxCommandExecutor { Ok(session) } - fn save_state(&self) -> Result<(), DurableRunnerError> { + fn assert_persistence_known(&self) -> Result<(), DurableRunnerError> { + match &self.persistence_error { + Some(error) => Err(error.clone()), + None => Ok(()), + } + } + + fn save_state(&mut self) -> Result<(), DurableRunnerError> { + self.assert_persistence_known()?; + let result = self.persist_state(); + if self + .state + .as_ref() + .is_some_and(|state| state.indexed_receipts) + { + if let Err(error) = &result { + // The in-memory batch ID is not evidence of a durable outbox. + // A failed rename/fsync may be ambiguous. No later poll, drain + // or ACK may publish that candidate without recovery. + self.persistence_error = Some(error.clone()); + } + } + result + } + + fn persist_state(&self) -> Result<(), DurableRunnerError> { let state = self .state .as_ref() @@ -764,6 +812,14 @@ impl AcpxCommandExecutor { let tool_set = authorized_tool_set(payload)?; let launch_profile_digest = self.launch_profile_digest()?; if let Some(state) = self.state.as_ref() { + if state.indexed_receipts + != (payload.get("durability").and_then(Value::as_str) + == Some("durability.indexed_state.v1")) + { + return Err(DurableRunnerError::invalid( + "ACPX receipt format cannot change across a retained run", + )); + } if state.descriptor != descriptor || state.tool_set != tool_set { return Err(DurableRunnerError::invalid( "ACPX provider or authorized tool contract changed across the durable run", @@ -780,6 +836,11 @@ impl AcpxCommandExecutor { tool_set, launch_profile_digest, )); + self.state + .as_mut() + .expect("prepared ACPX state exists") + .indexed_receipts = payload.get("durability").and_then(Value::as_str) + == Some("durability.indexed_state.v1"); self.save_state()?; } Ok(CommandExecution::result(json!({ @@ -1406,6 +1467,24 @@ impl AcpxCommandExecutor { DurableRunnerError::invalid(format!("ACPX provider failed: {error}")) })?; let Some(events) = events else { break }; + let batch = self + .session + .as_ref() + .and_then(AcpxProviderSession::pending_event_batch); + if batch.is_some() + && self + .state + .as_ref() + .and_then(|state| state.last_receipt_batch.as_ref()) + == batch.as_ref() + { + self.session + .as_mut() + .expect("polled session exists") + .acknowledge_event_batch() + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?; + continue; + } let mut provider_turn_settled = false; for event in events { let normalized = project_acpx_state_event(&self.context, &event) @@ -1505,7 +1584,19 @@ impl AcpxCommandExecutor { if provider_turn_settled { self.context.provider_turn_id = None; } + self.state + .as_mut() + .expect("polled ACPX state exists") + .last_receipt_batch = batch; self.save_state()?; + // The bounded provider batch and receipts survive until the outer + // provider outbox is durable. A lost acknowledgement replays the + // same batch ID rather than allocating a second event prefix. + self.session + .as_mut() + .expect("polled session exists") + .acknowledge_event_batch() + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?; } Ok(()) } @@ -1610,6 +1701,7 @@ impl CommandExecutor for AcpxCommandExecutor { } fn retained_events(&mut self) -> Result, DurableRunnerError> { + self.assert_persistence_known()?; // Explicit drain runs while control traffic suppresses provider polling. // Expose the already-retained suffix so runnerd can commit and ACK it // before suspension, without restoring or advancing the provider. @@ -1623,6 +1715,7 @@ impl CommandExecutor for AcpxCommandExecutor { } fn acknowledge_events(&mut self, count: usize) -> Result<(), DurableRunnerError> { + self.assert_persistence_known()?; if count == 0 { return Ok(()); } @@ -1640,6 +1733,19 @@ impl CommandExecutor for AcpxCommandExecutor { } fn shutdown(&mut self) -> Result<(), DurableRunnerError> { + if let Some(error) = self.persistence_error.clone() { + // Still reap our own live child, but never publish successful + // retirement after an uncertain durability boundary. + if let Some(session) = self.session.as_mut() { + session + .shutdown("ACPX persistence failed") + .map_err(|cleanup| { + DurableRunnerError::invalid(format!("{error}; cleanup failed: {cleanup}")) + })?; + } + self.session = None; + return Err(error); + } // A replacement durable runner may reach terminal reconciliation // before any provider command or event poll. Restore the persisted // session first so cleanup cannot succeed merely because this process @@ -1942,6 +2048,59 @@ mod tests { fs::remove_dir_all(directory).unwrap(); } + #[test] + fn failed_indexed_outbox_publication_cannot_be_drained_or_acknowledged() { + let directory = temporary_directory("indexed-outbox-failure"); + let profile = AcpxLaunchProfile { + authority_digest: format!("sha256:{}", "d".repeat(64)), + command: directory.join("sidecar"), + args: Vec::new(), + artifacts: Vec::new(), + }; + let config = test_config(&directory, Some(profile.clone())); + let mut executor = AcpxCommandExecutor::with_runner_config(&directory, &config); + let operations = Vec::new(); + let mut state = AcpxDurableState::new( + serde_json::from_value(descriptor("codex")).unwrap(), + AuthorizedToolSet { + schema: TOOL_SET_SCHEMA.to_owned(), + schema_version: 1, + catalog_digest: authorized_tool_catalog_digest(&operations).unwrap(), + operations, + }, + profile.canonical_digest().unwrap(), + ); + state.indexed_receipts = true; + executor.state = Some(state); + executor.restore_checked = true; + executor.save_state().unwrap(); + let original = fs::read(executor.state_path()).unwrap(); + let state = executor.state.as_mut().unwrap(); + state.last_receipt_batch = Some("17".to_owned()); + state + .push(NormalizedProviderEvent { + event_type: "harness.diagnostic".to_owned(), + priority: EventPriority::P0, + payload: json!({"code":"unpublished"}), + }) + .unwrap(); + // A real publication error after writing and syncing the candidate. + let previous = directory.join("previous.json"); + fs::rename(executor.state_path(), &previous).unwrap(); + fs::create_dir(executor.state_path()).unwrap(); + let error = executor.save_state().unwrap_err().to_string(); + assert!(error.contains("failed to atomically replace"), "{error}"); + fs::remove_dir(executor.state_path()).unwrap(); + fs::rename(previous, executor.state_path()).unwrap(); + assert!(executor.poll_events().is_err()); + assert!(executor.retained_events().is_err()); + assert!(executor.acknowledge_events(1).is_err()); + assert!(executor.save_state().is_err()); + assert_eq!(executor.state.as_ref().unwrap().pending_events.len(), 1); + assert_eq!(fs::read(executor.state_path()).unwrap(), original); + fs::remove_dir_all(directory).unwrap(); + } + #[test] fn admits_only_exact_qualified_claude_and_codex_descriptors() { for agent in ["claude", "codex"] { @@ -2218,6 +2377,7 @@ mod tests { assert!(!marker.exists()); let non_attach_error = attached .execute(&Command { + controller_epoch: None, schema: "paperclip.prp.command.v1".to_owned(), command_id: "command-before-attach".to_owned(), controller_seq: 1, @@ -2349,6 +2509,7 @@ mod tests { let mut drifted = AcpxCommandExecutor::with_runner_config(&directory, &drifted_config); let drift_error = drifted .execute(&Command { + controller_epoch: None, schema: "paperclip.prp.command.v1".to_owned(), command_id: "command-drift".to_owned(), controller_seq: 1, @@ -2364,6 +2525,7 @@ mod tests { .contains("launch profile digest does not match runner startup")); let retry_error = drifted .execute(&Command { + controller_epoch: None, schema: "paperclip.prp.command.v1".to_owned(), command_id: "command-drift-retry".to_owned(), controller_seq: 2, @@ -2382,6 +2544,7 @@ mod tests { let mut recovered = AcpxCommandExecutor::with_runner_config(&directory, &config); let snapshot = recovered .execute(&Command { + controller_epoch: None, schema: "paperclip.prp.command.v1".to_owned(), command_id: "command-1".to_owned(), controller_seq: 1, diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_session.rs b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_session.rs index 09038b02424..2b591218b8e 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_session.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_session.rs @@ -8,6 +8,7 @@ use crate::acpx_provider_state::{ is_reserved_terminal_operation, AcpxProviderState, AcpxProviderStateEvent, PRP_BLOCK_TOOL_NAME, PRP_COMPLETION_TOOL_NAME, }; +use crate::acpx_receipt_ledger::AcpxReceiptLedger; use crate::acpx_sidecar_transport::{AcpxSidecarTransport, AcpxSidecarTransportConfig}; use crate::generated_acpx_sidecar_contract::{ GeneratedAcpxSidecarCommand, GENERATED_ACPX_SIDECAR_PROTOCOL_VERSION, @@ -219,10 +220,29 @@ pub struct AcpxProviderSession { working_directory: PathBuf, closed: bool, transport_terminated: bool, + receipt_ledger: Option, } impl AcpxProviderSession { pub fn start(config: &AcpxProviderSessionConfig) -> Result { + Self::start_inner(config, None) + } + + /// Indexed sessions retain exact receipts on disk and require their caller + /// to acknowledge each event batch after its own durable handoff. + pub fn start_with_indexed_receipts( + config: &AcpxProviderSessionConfig, + path: &Path, + ) -> Result { + config.validate()?; + let ledger = AcpxReceiptLedger::open(path, config)?; + Self::start_inner(config, Some(ledger)) + } + + fn start_inner( + config: &AcpxProviderSessionConfig, + mut receipt_ledger: Option, + ) -> Result { config.validate()?; let mut tool_bridge = ProviderToolBridge::default(); tool_bridge @@ -230,7 +250,7 @@ impl AcpxProviderSession { .map_err(|error| { LocalRunnerError::invalid(format!("ACPX authorized tools are invalid: {error}")) })?; - let reserved_tool_bridge = reserved_terminal_tool_bridge()?; + let mut reserved_tool_bridge = reserved_terminal_tool_bridge()?; let mut transport = AcpxSidecarTransport::start_for_agent(&config.transport, &config.agent)?; let bootstrap = bootstrap(&mut transport, config); @@ -241,6 +261,19 @@ impl AcpxProviderSession { return Err(with_cleanup_error(error, cleanup)); } }; + if let Some(ledger) = &mut receipt_ledger { + if let Err(error) = ledger.checkpoint( + &identity, + &state, + &mut tool_bridge, + &mut reserved_tool_bridge, + vec![], + None, + ) { + let cleanup = transport.shutdown(); + return Err(with_cleanup_error(error, cleanup)); + } + } Ok(Self { transport, config: config.clone(), @@ -252,6 +285,7 @@ impl AcpxProviderSession { working_directory: config.working_directory.clone(), closed: false, transport_terminated: false, + receipt_ledger, }) } @@ -267,6 +301,20 @@ impl AcpxProviderSession { &self.state } + pub fn pending_event_batch(&self) -> Option { + self.receipt_ledger + .as_ref() + .and_then(AcpxReceiptLedger::pending_batch) + } + + /// The executor must save its projected event outbox before calling this. + pub fn acknowledge_event_batch(&mut self) -> Result<(), LocalRunnerError> { + if let Some(ledger) = &mut self.receipt_ledger { + ledger.acknowledge()?; + } + Ok(()) + } + pub fn catalog_revision(&self) -> u64 { self.catalog_revision } @@ -308,6 +356,14 @@ impl AcpxProviderSession { "ACPX provider session already has an active turn", )); } + if let Some(ledger) = &self.receipt_ledger { + if ledger.pending_batch().is_some() { + return Err(LocalRunnerError::invalid( + "ACPX previous event batch has not been durably acknowledged", + )); + } + ledger.validate_turn(turn_id)?; + } let rotate_turn_identity_ledger = self.state.settled_turn_identity_capacity_reached(); let identity_validation = if rotate_turn_identity_ledger { self.state @@ -365,6 +421,28 @@ impl AcpxProviderSession { "ACPX reserved tool receipt rotation failed: {error}" )))); } + let mut next_state = self.state.clone(); + next_state.begin_turn(turn_id)?; + if let Some(ledger) = &mut self.receipt_ledger { + next_tool_bridge = ProviderToolBridge::default(); + next_tool_bridge + .prepare(self.config.tool_set.clone()) + .map_err(|error| LocalRunnerError::invalid(error.to_string()))?; + next_reserved_tool_bridge = reserved_terminal_tool_bridge()?; + ledger.bind( + turn_id, + &mut next_tool_bridge, + &mut next_reserved_tool_bridge, + )?; + ledger.checkpoint( + &self.identity, + &next_state, + &mut next_tool_bridge, + &mut next_reserved_tool_bridge, + vec![], + None, + )?; + } let response = match self.transport.request( GeneratedAcpxSidecarCommand::TurnStart, json!({"turnId":turn_id,"message":message}), @@ -377,9 +455,7 @@ impl AcpxProviderSession { "ACPX sidecar did not confirm the requested turn", ))); } - if let Err(error) = self.state.begin_turn(turn_id) { - return Err(self.fail_closed(error)); - } + self.state = next_state; self.tool_bridge = next_tool_bridge; self.reserved_tool_bridge = next_reserved_tool_bridge; Ok(response) @@ -417,6 +493,13 @@ impl AcpxProviderSession { timeout: Duration, ) -> Result>, LocalRunnerError> { self.ensure_open()?; + if let Some(events) = self + .receipt_ledger + .as_ref() + .and_then(AcpxReceiptLedger::events) + { + return Ok(Some(events.to_vec())); + } let event = match self.transport.poll_event(timeout) { Ok(event) => event, Err(error) => return Err(self.fail_closed(error)), @@ -450,14 +533,21 @@ impl AcpxProviderSession { // the server for completion feedback before resolving // the provider call. The result is still reconciled // by the reserved receipt ledger below. - if next_bridge.has_call_receipt(call_id) { + if next_bridge.has_call_receipt(call_id).map_err(|error| { + self.fail_closed(LocalRunnerError::invalid(error.to_string())) + })? { return Err(self.fail_closed(LocalRunnerError::invalid( "ACPX reused a dynamic call id for a reserved terminal invocation", ))); } &mut next_reserved_bridge } else { - if next_reserved_bridge.has_call_receipt(call_id) { + if next_reserved_bridge + .has_call_receipt(call_id) + .map_err(|error| { + self.fail_closed(LocalRunnerError::invalid(error.to_string())) + })? + { return Err(self.fail_closed(LocalRunnerError::invalid( "ACPX reused a reserved call id for a dynamic tool invocation", ))); @@ -490,7 +580,12 @@ impl AcpxProviderSession { )))); } } else { - let replayed = next_bridge.has_completed_call(&result.call_id); + let replayed = + next_bridge + .has_completed_call(&result.call_id) + .map_err(|error| { + self.fail_closed(LocalRunnerError::invalid(error.to_string())) + })?; if let Err(error) = next_bridge.apply_result(crate::provider_bridge::ToolResult { call_id: result.call_id.clone(), @@ -571,6 +666,18 @@ impl AcpxProviderSession { reconciled_events.push(event); } } + if let Some(ledger) = &mut self.receipt_ledger { + if let Err(error) = ledger.checkpoint( + &self.identity, + &next_state, + &mut next_bridge, + &mut next_reserved_bridge, + reconciled_events.clone(), + None, + ) { + return Err(self.fail_closed(error)); + } + } self.state = next_state; self.tool_bridge = next_bridge; self.reserved_tool_bridge = next_reserved_bridge; @@ -614,6 +721,18 @@ impl AcpxProviderSession { "error":Value::Null, }) }; + if let Some(ledger) = &mut self.receipt_ledger { + if let Err(error) = ledger.checkpoint( + &self.identity, + &next_state, + &mut next_bridge, + &mut next_reserved_bridge, + vec![], + Some(result.clone()), + ) { + return Err(self.fail_closed(error)); + } + } let response = match self .transport .request(GeneratedAcpxSidecarCommand::ToolResolve, resolution) @@ -622,6 +741,18 @@ impl AcpxProviderSession { Err(error) => return Err(self.fail_closed(error)), }; self.verify_resolution(&response, "tool")?; + if let Some(ledger) = &mut self.receipt_ledger { + if let Err(error) = ledger.checkpoint( + &self.identity, + &next_state, + &mut next_bridge, + &mut next_reserved_bridge, + vec![], + None, + ) { + return Err(self.fail_closed(error)); + } + } self.state = next_state; self.tool_bridge = next_bridge; self.reserved_tool_bridge = next_reserved_bridge; @@ -661,6 +792,18 @@ impl AcpxProviderSession { Err(error) => return Err(self.fail_closed(error)), }; self.verify_resolution(&response, "input")?; + if let Some(ledger) = &mut self.receipt_ledger { + if let Err(error) = ledger.checkpoint( + &self.identity, + &next_state, + &mut self.tool_bridge, + &mut self.reserved_tool_bridge, + vec![], + None, + ) { + return Err(self.fail_closed(error)); + } + } self.state = next_state; Ok(()) } diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_state.rs b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_state.rs index 2a68007e99c..90e531f8e2f 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_state.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_state.rs @@ -1,3 +1,4 @@ +use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use serde_json::Value; @@ -24,7 +25,7 @@ pub(crate) fn is_reserved_terminal_operation(operation_id: &str) -> bool { matches!(operation_id, PRP_COMPLETION_TOOL_NAME | PRP_BLOCK_TOOL_NAME) } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct AcpxPendingTool { pub operation_id: String, pub input: Value, @@ -32,7 +33,7 @@ pub struct AcpxPendingTool { input_bytes: usize, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct AcpxSemanticResult { pub call_id: String, pub operation_id: String, @@ -41,7 +42,7 @@ pub struct AcpxSemanticResult { pub(crate) result_digest: String, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub enum AcpxProviderStateEvent { Activity(NormalizedProviderEvent), ToolCall { @@ -79,7 +80,7 @@ pub enum AcpxProviderStateEvent { }, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] struct PendingInput { runtime_request_id: String, value_bytes: usize, @@ -92,7 +93,7 @@ struct PendingInput { /// turn authority before payload decoding. Transport commands remain outside /// this reducer so callers can commit a pending resolution only after the /// corresponding sidecar request succeeds. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct AcpxProviderState { scope: AcpxEventScope, provider_requests: u64, diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_receipt_ledger.rs b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_receipt_ledger.rs new file mode 100644 index 00000000000..d870b50dddf --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_receipt_ledger.rs @@ -0,0 +1,199 @@ +//! Bounded ACPX current state, durable delivery intent and exact tool receipts. +//! The session publishes candidates only after the receipt transaction commits. +use crate::indexed_revision::Revision; +use std::path::Path; + +use serde::{Deserialize, Serialize}; + +use crate::acpx_provider_session::{AcpxProviderSessionConfig, AcpxProviderSessionIdentity}; +use crate::acpx_provider_state::{AcpxProviderState, AcpxProviderStateEvent}; +use crate::indexed_store::{ExactReceipt, IndexedStore}; +use crate::local_runner::LocalRunnerError; +use crate::provider_bridge::{ProviderToolBridge, ToolResult}; + +const SCHEMA: &str = "paperclip.runner.acpx-receipts.v1"; +const STATE_KEY: &str = "acpx-session"; + +#[derive(Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct Checkpoint { + schema: String, + run_id: String, + identity: AcpxProviderSessionIdentity, + state: AcpxProviderState, + dynamic: ProviderToolBridge, + reserved: ProviderToolBridge, + events: Vec, + delivery: Option, +} + +pub(crate) struct AcpxReceiptLedger { + store: IndexedStore, + generation: Revision, + run_id: String, + previous: Option, +} + +fn invalid(error: impl std::fmt::Display) -> LocalRunnerError { + LocalRunnerError::invalid(format!("ACPX receipt storage: {error}")) +} + +impl AcpxReceiptLedger { + /// Called before spawning. An interrupted active lifetime must pass the + /// executor's recovery admission; opening receipts cannot grant it a new + /// provider process or silently discard unfinished delivery. + pub(crate) fn open( + path: &Path, + config: &AcpxProviderSessionConfig, + ) -> Result { + let binding = format!("acpx/{}", config.normalized_session_id); + let store = IndexedStore::open(path, &binding, config.expected_identity.is_none()) + .map_err(invalid)?; + let current = store.read_state(STATE_KEY).map_err(invalid)?; + if current.is_none() && config.expected_identity.is_some() { + return Err(invalid("retained current receipt authority is missing")); + } + let previous: Option = current + .as_ref() + .map(|value| serde_json::from_slice(&value.bytes).map_err(invalid)) + .transpose()?; + if let Some(previous) = &previous { + if previous.schema != SCHEMA + || previous.identity.normalized_session_id != config.normalized_session_id + || previous.state.run_id() != previous.run_id + || previous.state.active_turn_id().is_some() + || previous.state.has_pending_requests() + || previous.dynamic.pending_calls().next().is_some() + || previous.reserved.pending_calls().next().is_some() + || !previous.events.is_empty() + || previous.delivery.is_some() + { + return Err(invalid( + "prior session requires reconciliation before restart", + )); + } + if config.expected_identity.as_ref() != Some(&previous.identity) { + return Err(invalid("retained session identity was not admitted")); + } + } + Ok(Self { + store, + generation: current.map_or(Revision::Absent, |value| value.generation), + run_id: config.run_id.clone(), + previous, + }) + } + + pub(crate) fn validate_turn(&self, turn_id: &str) -> Result<(), LocalRunnerError> { + if self + .store + .receipt(&format!("acpx-turns/{}", self.run_id), turn_id) + .map_err(invalid)? + .is_some() + { + return Err(invalid("reused a settled turn identity")); + } + Ok(()) + } + + pub(crate) fn bind( + &self, + turn_id: &str, + dynamic: &mut ProviderToolBridge, + reserved: &mut ProviderToolBridge, + ) -> Result<(), LocalRunnerError> { + let namespace = format!("acpx/{}/{turn_id}", self.run_id); + dynamic + .bind_indexed_receipts(self.store.clone(), format!("{namespace}/dynamic")) + .map_err(invalid)?; + reserved + .bind_indexed_receipts(self.store.clone(), format!("{namespace}/reserved")) + .map_err(invalid) + } + + pub(crate) fn events(&self) -> Option<&[AcpxProviderStateEvent]> { + self.previous + .as_ref() + .filter(|value| !value.events.is_empty()) + .map(|value| value.events.as_slice()) + } + + pub(crate) fn pending_batch(&self) -> Option { + self.events().map(|_| self.generation.to_string()) + } + + pub(crate) fn acknowledge(&mut self) -> Result<(), LocalRunnerError> { + let Some(mut next) = self.previous.clone() else { + return Ok(()); + }; + if next.events.is_empty() { + return Ok(()); + } + next.events.clear(); + self.commit(next, vec![]) + } + + pub(crate) fn checkpoint( + &mut self, + identity: &AcpxProviderSessionIdentity, + state: &AcpxProviderState, + dynamic: &mut ProviderToolBridge, + reserved: &mut ProviderToolBridge, + events: Vec, + delivery: Option, + ) -> Result<(), LocalRunnerError> { + if self.events().is_some() { + return Err(invalid( + "previous event batch has not been durably acknowledged", + )); + } + let mut receipts = dynamic.checkpoint_indexed_receipts().map_err(invalid)?; + receipts.extend(reserved.checkpoint_indexed_receipts().map_err(invalid)?); + if let Some(previous) = &self.previous { + if let Some(turn) = previous + .state + .active_turn_id() + .filter(|_| state.active_turn_id().is_none()) + { + receipts.push(ExactReceipt { + namespace: format!("acpx-turns/{}", previous.run_id), + key: turn.to_owned(), + bytes: b"settled".to_vec(), + }); + } + } + let next = Checkpoint { + schema: SCHEMA.to_owned(), + run_id: self.run_id.clone(), + identity: identity.clone(), + state: state.clone(), + dynamic: dynamic.clone(), + reserved: reserved.clone(), + events, + delivery, + }; + self.commit(next, receipts) + } + + fn commit( + &mut self, + next: Checkpoint, + receipts: Vec, + ) -> Result<(), LocalRunnerError> { + let bytes = serde_json::to_vec(&next).map_err(invalid)?; + self.generation = match self + .store + .commit(STATE_KEY, self.generation, bytes, receipts) + { + Ok(generation) => generation, + Err(error) => { + // A response can be lost after durable commit. This writer may + // neither retry with a stale candidate nor release a result. + self.generation = Revision::Fenced; + return Err(invalid(error)); + } + }; + self.previous = Some(next); + Ok(()) + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/bin/fake-acpx-sidecar.rs b/packages/paperclip-runner/runner/crates/runner-core/src/bin/fake-acpx-sidecar.rs index 2d84a6dbe70..93c1ca380a3 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/bin/fake-acpx-sidecar.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/bin/fake-acpx-sidecar.rs @@ -20,6 +20,12 @@ fn run() -> Result<(), Box> { .find(|pair| pair[0] == "--mode") .map(|pair| pair[1].as_str()) .unwrap_or("happy"); + let history_calls: usize = args + .windows(2) + .find(|pair| pair[0] == "--calls") + .map(|pair| pair[1].parse()) + .transpose()? + .unwrap_or(64); let profile_digest = args .windows(2) .find(|pair| pair[0] == "--profile-digest") @@ -39,6 +45,82 @@ fn run() -> Result<(), Box> { .get("command") .and_then(Value::as_str) .ok_or("request command is missing")?; + if mode == "indexed-many-tools" { + if command == "tool.resolve" { + if let Some(marker) = args + .windows(2) + .find(|pair| pair[0] == "--resolution-marker") + { + std::fs::write(&marker[1], b"tool.resolve received")?; + } + } + write_json( + &mut stdout, + &bootstrap_success(id, command, &request, mode, profile_digest), + )?; + let params = request.get("params").unwrap_or(&Value::Null); + let turn = params + .get("turnId") + .and_then(Value::as_str) + .unwrap_or("turn-1"); + let next_call = if command == "turn.start" { + Some(0) + } else if command == "tool.resolve" { + let call = params["callId"].as_str().ok_or("missing call identity")?; + write_turn_event( + &mut stdout, + next_sequence, + "runtime.event", + "run-1", + turn, + json!({ + "type":"semantic_result", "callId":call, "operationId":"issues.read", "ok":true, + "result": params["result"], + }), + )?; + next_sequence += 1; + let index: usize = call + .strip_prefix("history-call-") + .ok_or("bad call identity")? + .parse()?; + Some(index + 1) + } else { + None + }; + if let Some(index) = next_call { + if index < history_calls { + write_turn_event( + &mut stdout, + next_sequence, + "runtime.tool_called", + "run-1", + turn, + json!({"callId":format!("history-call-{index}"),"operationId":"issues.read","input":{"index":index}}), + )?; + } else { + // Ancient exact replay after crossing the former 4096-call ceiling. + write_turn_event( + &mut stdout, + next_sequence, + "runtime.event", + "run-1", + turn, + json!({"type":"semantic_result","callId":"history-call-0","operationId":"issues.read","ok":true,"result":{"index":0}}), + )?; + next_sequence += 1; + write_turn_event( + &mut stdout, + next_sequence, + "runtime.turn_terminal", + "run-1", + turn, + json!({"status":"completed"}), + )?; + } + next_sequence += 1; + } + continue; + } if mode == "goals" && command.starts_with("session.goal.") { let params = request.get("params").cloned().unwrap_or_else(|| json!({})); match command { diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/bin/fake-codex-app-server.rs b/packages/paperclip-runner/runner/crates/runner-core/src/bin/fake-codex-app-server.rs index bac103d0ff0..d4cab3fc198 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/bin/fake-codex-app-server.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/bin/fake-codex-app-server.rs @@ -26,6 +26,25 @@ fn argument(args: &[String], name: &str) -> Option { .cloned() } +fn opened_thread(state: &FakeState, args: &[String]) -> Value { + let mut thread = json!({ + "id": state.thread_id, + "sessionId": "codex-account-session", + "historyMode": if args.iter().any(|arg| arg == "--legacy-history-mode") + || argument(args, "--state-file").is_some_and(|path| { + Path::new(&path).with_file_name("legacy-history-mode").exists() + }) { + "legacy" + } else { + "paginated" + }, + }); + if args.iter().any(|arg| arg == "--missing-history-mode") { + thread.as_object_mut().unwrap().remove("historyMode"); + } + thread +} + fn send(value: Value) -> io::Result<()> { let mut stdout = io::stdout().lock(); serde_json::to_writer(&mut stdout, &value)?; @@ -563,6 +582,25 @@ fn send_structured_activity(state: &FakeState) -> io::Result<()> { })) } +// Explicit credential-free history qualification. Keep each provider frame +// bounded and emit ordinary completed message items on one active turn. +fn send_history_growth(state: &FakeState, mib: u64) -> io::Result<()> { + for index in 0..mib * 4 { + let prefix = format!("history-{index}: "); + let text = format!("{prefix}{}", "h".repeat(256 * 1024 - prefix.len())); + let item = if index % 2 == 0 { + json!({"id":format!("history-{index}"),"type":"agentMessage","status":"completed","text":text}) + } else { + json!({"id":format!("history-{index}"),"type":"commandExecution","status":"completed","command":"qualification-output","aggregatedOutput":text,"exitCode":0}) + }; + send(json!({"method":"item/completed", "params":{ + "threadId":state.thread_id,"turnId":state.active_turn_id, + "item":item + }}))?; + } + Ok(()) +} + fn send_runtime_request_flood( state: &FakeState, interrupt_count: u64, @@ -617,6 +655,15 @@ fn run() -> Result<(), Box> { .iter() .any(|value| value == "--opencode-proxy-runtime-question"); let emit_runtime_elicitation = args.iter().any(|value| value == "--runtime-elicitation"); + let history_growth_mib = argument(&args, "--history-growth-mib") + .map(|value| value.parse::()) + .transpose()? + .unwrap_or(0); + let soak_output_on_steer = args.iter().any(|value| value == "--soak-output-on-steer"); + let mut soak_output_sequence = 0_u64; + if history_growth_mib > 1024 * 1024 { + return Err("qualification size exceeds 1 TiB".into()); + } let emit_structured_activity = args.iter().any(|value| value == "--structured-activity"); let emit_split_event_burst = args.iter().any(|value| value == "--split-event-burst"); let split_event_suffix_count = argument(&args, "--split-event-suffix-count") @@ -1024,6 +1071,11 @@ fn run() -> Result<(), Box> { } "initialized" => {} "thread/start" => { + if args.iter().any(|arg| arg == "--require-paginated-history") + && message.pointer("/params/historyMode") != Some(&json!("paginated")) + { + return Err("thread/start must explicitly select paginated history".into()); + } if require_external_sandbox && (message.pointer("/params/sandbox") != Some(&json!("danger-full-access")) || message.pointer("/params/permissions").is_some()) @@ -1066,7 +1118,7 @@ fn run() -> Result<(), Box> { } send(json!({ "id": id, - "result": {"thread": {"id": state.thread_id, "sessionId": "codex-account-session"}} + "result": {"thread": opened_thread(&state, &args)} }))?; if agent_created_goal { send(json!({ @@ -1116,7 +1168,7 @@ fn run() -> Result<(), Box> { } send(json!({ "id": id, - "result": {"thread": {"id": state.thread_id, "sessionId": "codex-account-session"}} + "result": {"thread": opened_thread(&state, &args)} }))?; if args.iter().any(|arg| arg == "--resume-usage-snapshot") && state.active_turn_id.is_none() @@ -1152,6 +1204,25 @@ fn run() -> Result<(), Box> { } } "thread/turns/list" => { + if args + .iter() + .any(|arg| arg == "--unbounded-completed-history") + { + if args.iter().any(|arg| arg == "--settle-during-history") { + state.active_turn_id = None; + save_state(&state_path, &state)?; + } + let page = message + .pointer("/params/cursor") + .and_then(Value::as_str) + .and_then(|cursor| cursor.parse::().ok()) + .unwrap_or(0); + send(json!({"id": id, "result": { + "data": [{"id": format!("old-turn-{page}"), "status": "completed", "items": [], "itemsView": "notLoaded"}], + "nextCursor": (page + 1).to_string(), + }}))?; + continue; + } if args .iter() .any(|arg| arg == "--require-lightweight-history") @@ -1665,6 +1736,11 @@ fn run() -> Result<(), Box> { send_opencode_proxy_runtime_question(&state)?; } else if emit_runtime_elicitation { send_runtime_elicitation(&state)?; + } else if history_growth_mib > 0 { + if turn_start_count == 1 { + send_history_growth(&state, history_growth_mib)?; + } + finish_turn(&state_path, &mut state, "completed")?; } else if emit_structured_activity { send_structured_activity(&state)?; finish_turn(&state_path, &mut state, "completed")?; @@ -1788,6 +1864,15 @@ fn run() -> Result<(), Box> { } "turn/steer" => { send(json!({"id": id, "result": {"accepted": true}}))?; + if soak_output_on_steer && state.active_turn_id.is_some() { + soak_output_sequence += 1; + let text = format!("soak-output-{soak_output_sequence}: {}", "s".repeat(8192)); + send(json!({"method":"item/completed", "params": { + "threadId": state.thread_id, "turnId": state.active_turn_id, + "item": {"id": format!("soak-output-{soak_output_sequence}"), + "type":"agentMessage", "text":text} + }}))?; + } if linger_after_turn_start { send(json!({ "method": "item/completed", diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/bin/paperclip-runnerd.rs b/packages/paperclip-runner/runner/crates/runner-core/src/bin/paperclip-runnerd.rs index 5b1c7231c84..6f8aeb8995b 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/bin/paperclip-runnerd.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/bin/paperclip-runnerd.rs @@ -8,8 +8,9 @@ use std::time::Duration; use std::os::unix::fs::{OpenOptionsExt, PermissionsExt}; use paperclip_runner_core::durable::{ - capture_bootstrap_ticket, redact_diagnostic_text, run_durable_runner, AcpxLaunchProfile, - DurableRunnerConfig, OpenCodeLaunchProfile, QualifiedLaunchArtifact, + capture_bootstrap_ticket, redact_diagnostic_text, run_durable_runner, + run_indexed_durable_runner, AcpxLaunchProfile, DurableRunnerConfig, OpenCodeLaunchProfile, + QualifiedLaunchArtifact, }; use paperclip_runner_core::local_runner::{run_local_runner, LocalRunnerError, RunnerConfig}; use paperclip_runner_core::native_provider_backend::NativeProviderCommandExecutor; @@ -261,14 +262,7 @@ fn usize_value(args: &[String], name: &str, default: usize) -> Result Result<(), LocalRunnerError> { - let ticket = capture_bootstrap_ticket() - .map_err(|error| LocalRunnerError::invalid(error.to_string()))? - .ok_or_else(|| { - LocalRunnerError::invalid( - "PAPERCLIP_RUNNER_BOOTSTRAP_TICKET is required for durable mode", - ) - })?; +fn durable_config(args: &[String]) -> Result { let duration = |name: &str, default: u64| { optional_u64(args, name).map(|value| Duration::from_millis(value.unwrap_or(default))) }; @@ -312,7 +306,7 @@ fn run_durable(args: &[String]) -> Result<(), LocalRunnerError> { "--ca-bundle-path is accepted only with wss://", )); } - let config = DurableRunnerConfig { + Ok(DurableRunnerConfig { connect_url, ca_bundle_path, state_dir: state_dir.clone(), @@ -332,10 +326,26 @@ fn run_durable(args: &[String]) -> Result<(), LocalRunnerError> { reconnect_delay: duration("--reconnect-delay-ms", 250)?, reconnect_grace: optional_u64(args, "--reconnect-grace-ms")?.map(Duration::from_millis), max_runtime: duration("--max-runtime-ms", 0)?, - }; + }) +} + +fn run_durable(args: &[String]) -> Result<(), LocalRunnerError> { + let ticket = capture_bootstrap_ticket() + .map_err(|error| LocalRunnerError::invalid(error.to_string()))? + .ok_or_else(|| { + LocalRunnerError::invalid( + "PAPERCLIP_RUNNER_BOOTSTRAP_TICKET is required for durable mode", + ) + })?; + let config = durable_config(args)?; + let state_dir = config.state_dir.clone(); let executor = NativeProviderCommandExecutor::with_runner_config(state_dir, &config); - run_durable_runner(config, ticket, executor) - .map_err(|error| LocalRunnerError::invalid(error.to_string())) + (if args.iter().any(|argument| argument == "--indexed-state") { + run_indexed_durable_runner(config, ticket, executor) + } else { + run_durable_runner(config, ticket, executor) + }) + .map_err(|error| LocalRunnerError::invalid(error.to_string())) } fn run(args: &[String]) -> Result<(), LocalRunnerError> { @@ -343,6 +353,9 @@ fn run(args: &[String]) -> Result<(), LocalRunnerError> { println!("{}", build_metadata()); return Ok(()); } + if args.first().is_some_and(|argument| argument == "storage") { + return run_storage(args); + } if args.iter().any(|argument| argument == "--connect-url") || args.iter().any(|argument| argument == "--listen-address") || args.iter().any(|argument| argument == "--listen-port") @@ -368,6 +381,208 @@ fn run(args: &[String]) -> Result<(), LocalRunnerError> { }) } +fn run_storage(args: &[String]) -> Result<(), LocalRunnerError> { + use base64::Engine; + use paperclip_runner_core::indexed_store::IndexedStore; + let operation = args.get(1).map(String::as_str).unwrap_or(""); + if operation == "snapshot-session" { + use paperclip_runner_core::indexed_session_snapshot::{snapshot_session, ExpectedState}; + let states = [ + ExpectedState { + generation: value(args, "--runner-generation")?, + sha256: value(args, "--runner-sha256")?, + }, + ExpectedState { + generation: value(args, "--provider-generation")?, + sha256: value(args, "--provider-sha256")?, + }, + ]; + let result = snapshot_session( + &PathBuf::from(value(args, "--directory")?), + &PathBuf::from(value(args, "--destination")?), + states, + ) + .map_err(|e| LocalRunnerError::invalid(e.to_string()))?; + println!("{result}"); + return Ok(()); + } + if operation == "inspect-state" { + return paperclip_runner_core::indexed_inspection::stream( + &PathBuf::from(value(args, "--path")?), + std::io::stdout().lock(), + ) + .map_err(|error| LocalRunnerError::invalid(error.to_string())); + } + if operation == "read-receipt" { + let bytes = paperclip_runner_core::indexed_store::read_only_receipt( + &PathBuf::from(value(args, "--path")?), + &value(args, "--binding")?, + value(args, "--generation")? + .parse::() + .map_err(|_| LocalRunnerError::invalid("invalid proof generation"))?, + &value(args, "--namespace")?, + &value(args, "--key")?, + ) + .map_err(|e| LocalRunnerError::invalid(e.to_string()))?; + println!( + "{}", + json!(bytes.map(|b| base64::engine::general_purpose::STANDARD.encode(b))) + ); + return Ok(()); + } + if operation == "rpc" { + let store = IndexedStore::open( + &PathBuf::from(value(args, "--path")?), + &value(args, "--binding")?, + optional_value(args, "--create")?.as_deref() == Some("true"), + ) + .map_err(|e| LocalRunnerError::invalid(e.to_string()))?; + return paperclip_runner_core::indexed_rpc::serve( + store, + std::io::stdin().lock(), + std::io::stdout().lock(), + ) + .map_err(|e| LocalRunnerError::invalid(e.to_string())); + } + if operation == "archive-controller" { + paperclip_runner_core::indexed_archive::controller( + &PathBuf::from(value(args, "--directory")?), + &PathBuf::from(value(args, "--destination")?), + &value(args, "--digest")?, + ) + .map_err(|e| LocalRunnerError::invalid(e.to_string()))?; + println!("{}", json!({"archive":"complete"})); + return Ok(()); + } + if operation == "seal" { + paperclip_runner_core::indexed_archive::seal( + &PathBuf::from(value(args, "--directory")?), + &value(args, "--generation")?, + ) + .map_err(|e| LocalRunnerError::invalid(e.to_string()))?; + println!("{}", json!({"seal":"complete"})); + return Ok(()); + } + if operation == "archive-prepare" || operation == "archive-finish" { + let runner = PathBuf::from(value(args, "--directory")?); + let archive = PathBuf::from(value(args, "--destination")?); + let map = |e: paperclip_runner_core::durable::DurableRunnerError| { + LocalRunnerError::invalid(e.to_string()) + }; + if operation == "archive-prepare" { + paperclip_runner_core::indexed_archive::prepare( + &runner, + &archive, + &value(args, "--generation")?, + &value(args, "--digest")?, + ) + .map_err(map)?; + println!("{}", json!({"archive":"prepared"})); + } else { + let finished = + paperclip_runner_core::indexed_archive::finish(&runner, &archive).map_err(map)?; + println!( + "{}", + json!({"archive":if finished {"complete"} else {"absent"}}) + ); + } + return Ok(()); + } + if operation == "stage-legacy" { + let config = durable_config(args)?; + let destination = PathBuf::from(value(args, "--destination")?); + let fence_id = value(args, "--fence-id")?; + let map = |e: paperclip_runner_core::durable::DurableRunnerError| { + LocalRunnerError::invalid(e.to_string()) + }; + let runner = paperclip_runner_core::durable::DurableStateStore::stage_legacy( + &config, + &destination, + &fence_id, + ) + .map_err(map)?; + let provider = + paperclip_runner_core::provider_backend::CodexCommandExecutor::with_runner_config( + &config.state_dir, + &config, + ) + .stage_legacy(&destination, &fence_id) + .map_err(map)?; + println!( + "{}", + json!({"schema":"paperclip.runner.prepared-legacy-session.v1","runner":runner,"provider":provider}) + ); + return Ok(()); + } + if !["partitions", "maintain", "relocate", "backup"].contains(&operation) { + return Err(LocalRunnerError::invalid( + "storage requires partitions, maintain, relocate, or backup", + )); + } + let path = PathBuf::from(value(args, "--path")?); + let binding = value(args, "--binding")?; + let store = IndexedStore::open(&path, &binding, false) + .map_err(|e| LocalRunnerError::invalid(e.to_string()))?; + let decode = |value: String| -> Result { + if value.len() > 2800 { + return Err(LocalRunnerError::invalid( + "partition cursor exceeds capacity", + )); + } + let bytes = base64::engine::general_purpose::STANDARD + .decode(value) + .map_err(|_| LocalRunnerError::invalid("invalid base64 partition cursor"))?; + String::from_utf8(bytes) + .map_err(|_| LocalRunnerError::invalid("invalid partition cursor UTF-8")) + }; + let mapped = |error: paperclip_runner_core::durable::DurableRunnerError| { + LocalRunnerError::invalid(error.to_string()) + }; + match operation { + "partitions" => { + let after = optional_value(args, "--after")?.map(decode).transpose()?; + let entries = store + .partitions(after.as_deref(), usize_value(args, "--limit", 32)?) + .map_err(mapped)?; + let rows:Vec<_>=entries.iter().map(|entry|json!({"start":base64::engine::general_purpose::STANDARD.encode(&entry.lower_key),"file":entry.file,"records":entry.records.to_string(),"bytes":entry.bytes.to_string()})).collect(); + println!( + "{}", + json!({"schema":"paperclip.receipt-partitions.v1","partitions":rows,"nextAfter":entries.last().map(|entry|base64::engine::general_purpose::STANDARD.encode(&entry.lower_key))}) + ); + } + "maintain" => { + let steps = usize_value(args, "--steps", 1)?; + if !(1..=1000).contains(&steps) { + return Err(LocalRunnerError::invalid( + "maintenance steps must be 1 through 1000", + )); + } + for _ in 0..steps { + store.maintain().map_err(mapped)?; + std::thread::yield_now(); + } + println!("{}", json!({"maintenanceSteps":steps})); + } + "relocate" => { + let lower = decode(value(args, "--partition-start")?)?; + store + .relocate_partition(&lower, &PathBuf::from(value(args, "--destination")?)) + .map_err(mapped)?; + println!("{}", json!({"relocation":"started"})); + } + "backup" => { + let destination = PathBuf::from(value(args, "--destination")?); + store.begin_backup(&destination).map_err(mapped)?; + while !store.backup_step().map_err(mapped)? { + std::thread::sleep(Duration::from_millis(10)); + } + println!("{}", json!({"backup":"complete","destination":destination})); + } + _ => unreachable!(), + } + Ok(()) +} + fn run_main(args: Vec) -> ExitCode { let diagnostics_directory = diagnostic_directory(&args); install_diagnostic_panic_hook(diagnostics_directory.clone()); @@ -394,6 +609,70 @@ fn run_main(args: Vec) -> ExitCode { mod tests { use super::*; + #[test] + fn storage_commands_back_up_and_inspect_an_existing_private_store() { + use paperclip_runner_core::indexed_store::{ExactReceipt, IndexedStore}; + let directory = + std::env::temp_dir().join(format!("runner-storage-cli-{}", uuid::Uuid::new_v4())); + let path = directory.join("state.sqlite"); + let store = IndexedStore::open(&path, "operator-test", true).unwrap(); + store + .commit( + "current", + 0, + b"checkpoint".to_vec(), + vec![ExactReceipt { + namespace: "effects".into(), + key: "ancient".into(), + bytes: b"receipt".to_vec(), + }], + ) + .unwrap(); + drop(store); + let args = |operation: &str, extra: Vec| { + let mut arguments = vec![ + "storage".into(), + operation.into(), + "--path".into(), + path.to_str().unwrap().into(), + "--binding".into(), + "operator-test".into(), + ]; + arguments.extend(extra); + arguments + }; + run(&args("partitions", vec![])).unwrap(); + run(&args("maintain", vec!["--steps".into(), "2".into()])).unwrap(); + let destination = directory.join("backup"); + run(&args( + "backup", + vec!["--destination".into(), destination.to_str().unwrap().into()], + )) + .unwrap(); + let restored = IndexedStore::open( + &destination.join("authority.sqlite"), + "operator-test", + false, + ) + .unwrap(); + assert_eq!( + restored.read_state("current").unwrap().unwrap().bytes, + b"checkpoint" + ); + assert_eq!( + restored.receipt("effects", "ancient").unwrap(), + Some(b"receipt".to_vec()) + ); + drop(restored); + assert!(run(&args("maintain", vec!["--steps".into(), "1001".into()])).is_err()); + assert!(run(&args( + "backup", + vec!["--destination".into(), destination.to_str().unwrap().into()] + )) + .is_err()); + fs::remove_dir_all(directory).unwrap(); + } + #[test] fn build_metadata_advertises_the_remote_transport_contract() { let metadata = build_metadata(); diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/codex_provider.rs b/packages/paperclip-runner/runner/crates/runner-core/src/codex_provider.rs index 549b6d88908..3ebdfb54f13 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/codex_provider.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/codex_provider.rs @@ -1,3 +1,6 @@ +use crate::indexed_revision::Revision; +use crate::indexed_store::{ExactReceipt, IndexedStore}; +use crate::process_generation::ProcessGeneration; use std::collections::{BTreeMap, BTreeSet, VecDeque}; use std::fs::{self, OpenOptions}; use std::io::Write; @@ -514,14 +517,14 @@ pub enum CodexProviderEvent { completed_turn_authoritative: bool, completed_turn_observed_by_process: bool, completion_reconciles_exit: bool, - process_generation: u64, - completed_turn_process_generation: Option, + process_generation: ProcessGeneration, + completed_turn_process_generation: Option, }, } #[derive(Clone, Debug, PartialEq, Eq)] struct CompletedTurnAuthority { - process_generation: u64, + process_generation: ProcessGeneration, provider_turn_id: String, } @@ -529,29 +532,72 @@ struct CompletedTurnAuthority { struct SettledProviderTurnIds { ids: BTreeSet, filter: DurableReplayFilter, + index: Option<(IndexedStore, String, Revision)>, } impl SettledProviderTurnIds { - fn insert(&mut self, provider_turn_id: String) -> bool { - if self.contains(&provider_turn_id) { - return true; + fn bind(&mut self, store: IndexedStore, namespace: String) -> Result<(), LocalRunnerError> { + if !self.filter.is_empty() { + return Err(LocalRunnerError::invalid( + "legacy turn filter requires fenced migration", + )); } - if self.ids.len() >= MAX_SETTLED_PROVIDER_TURN_IDS { - return false; + let generation = store + .read_state(&format!("identities/{namespace}")) + .map_err(|error| LocalRunnerError::invalid(error.to_string()))? + .map_or(Revision::Absent, |state| state.generation); + self.index = Some((store, namespace, generation)); + for id in std::mem::take(&mut self.ids) { + self.insert(id)?; } - self.ids.insert(provider_turn_id) + Ok(()) } - fn contains(&self, provider_turn_id: &str) -> bool { - self.ids.contains(provider_turn_id) + fn insert(&mut self, provider_turn_id: String) -> Result { + if self.contains(&provider_turn_id)? { + return Ok(true); + } + if let Some((store, namespace, generation)) = self.index.as_mut() { + *generation = store + .commit( + &format!("identities/{namespace}"), + *generation, + b"{}".to_vec(), + vec![ExactReceipt { + namespace: namespace.clone(), + key: provider_turn_id, + bytes: b"settled".to_vec(), + }], + ) + .map_err(|error| LocalRunnerError::invalid(error.to_string()))?; + return Ok(true); + } + if self.ids.len() >= MAX_SETTLED_PROVIDER_TURN_IDS { + return Ok(false); + } + Ok(self.ids.insert(provider_turn_id)) + } + + fn contains(&self, provider_turn_id: &str) -> Result { + if self.ids.contains(provider_turn_id) { + return Ok(true); + } + if let Some((store, namespace, _)) = &self.index { + return store + .receipt(namespace, provider_turn_id) + .map(|record| record.is_some()) + .map_err(|error| LocalRunnerError::invalid(error.to_string())); + } + Ok(false) } fn at_capacity(&self) -> bool { - self.ids.len() >= MAX_SETTLED_PROVIDER_TURN_IDS || !self.filter.is_empty() + self.index.is_none() + && (self.ids.len() >= MAX_SETTLED_PROVIDER_TURN_IDS || !self.filter.is_empty()) } fn restore(&mut self, provider_turn_id: String) -> Result<(), LocalRunnerError> { - if !self.insert(provider_turn_id) { + if !self.insert(provider_turn_id)? { return Err(LocalRunnerError::invalid( "Codex restored provider turn identity epoch exceeded its exact capacity", )); @@ -569,7 +615,7 @@ impl SettledProviderTurnIds { .map_err(|error| LocalRunnerError::invalid(error.to_string()))?; self.filter = replay_filter; for provider_turn_id in provider_turn_ids { - if !self.insert(provider_turn_id) { + if !self.insert(provider_turn_id)? { return Err(LocalRunnerError::invalid( "Codex restored provider turn identity epoch exceeded its exact capacity", )); @@ -634,7 +680,6 @@ pub struct CodexProvider { stderr_tail: BoundedLogBuffer, config: CodexProviderConfig, authorized_tools: Vec, - next_request_id: u64, thread_id: String, provider_session_id: Option, active_provider_turn_id: Option, @@ -645,20 +690,20 @@ pub struct CodexProvider { pending_tool_requests: BTreeMap, completed_tool_call_ids: BTreeSet, durable_tool_call_replays: bool, + require_paginated_history: bool, pending_tool_request_bytes: usize, pending_runtime_requests: BTreeMap, pending_runtime_request_bytes: usize, runtime_request_scope: [u8; 16], - next_runtime_request_sequence: u64, expected_shutdown: bool, - process_generation: u64, + process_generation: ProcessGeneration, completed_turn_authority: Option, active_turn_result_authoritative: bool, completion_reconciliation_pending: bool, goal_allows_autonomous_turns: bool, ambiguous_turn_start_pending: bool, settled_provider_turn_ids: SettledProviderTurnIds, - descendant_thread_ids: BTreeSet, + descendant_thread_ids: SettledProviderTurnIds, notification_identity_diagnostics: usize, rejected_accepted_turn: Option, quarantined: bool, @@ -671,7 +716,7 @@ pub struct CodexProvider { } struct ProviderExitDrain { - process_generation: u64, + process_generation: ProcessGeneration, deadline: std::time::Instant, timed_out: bool, warning_emitted: bool, @@ -804,7 +849,7 @@ impl CodexProvider { config, std::iter::empty(), resume_thread_id, - 1, + 1.into(), None, None, ) @@ -819,7 +864,7 @@ impl CodexProvider { config, authorized_tools, resume_thread_id, - 1, + 1.into(), None, None, ) @@ -829,7 +874,7 @@ impl CodexProvider { config: &CodexProviderConfig, authorized_tools: impl IntoIterator, resume_thread_id: Option<&str>, - process_generation: u64, + process_generation: ProcessGeneration, opencode_launch_profile: Option<&OpenCodeLaunchProfile>, completion_contract: Option<(&str, &[String])>, ) -> Result { @@ -840,6 +885,7 @@ impl CodexProvider { process_generation, opencode_launch_profile, completion_contract, + false, &mut |_| Ok(()), ) } @@ -848,9 +894,10 @@ impl CodexProvider { config: &CodexProviderConfig, authorized_tools: impl IntoIterator, resume_thread_id: Option<&str>, - process_generation: u64, + process_generation: ProcessGeneration, opencode_launch_profile: Option<&OpenCodeLaunchProfile>, completion_contract: Option<(&str, &[String])>, + require_paginated_history: bool, observe: &mut dyn FnMut(ProviderStartupObservation) -> Result<(), LocalRunnerError>, ) -> Result { config.validate()?; @@ -964,7 +1011,6 @@ impl CodexProvider { ), config: config.clone(), authorized_tools, - next_request_id: 1, thread_id: String::new(), provider_session_id: None, active_provider_turn_id: None, @@ -975,11 +1021,11 @@ impl CodexProvider { pending_tool_requests: BTreeMap::new(), completed_tool_call_ids: BTreeSet::new(), durable_tool_call_replays: false, + require_paginated_history: require_paginated_history && config.provider == "codex", pending_tool_request_bytes: 0, pending_runtime_requests: BTreeMap::new(), pending_runtime_request_bytes: 0, runtime_request_scope, - next_runtime_request_sequence: 1, expected_shutdown: false, process_generation, completed_turn_authority: None, @@ -988,7 +1034,7 @@ impl CodexProvider { goal_allows_autonomous_turns: false, ambiguous_turn_start_pending: false, settled_provider_turn_ids: SettledProviderTurnIds::default(), - descendant_thread_ids: BTreeSet::new(), + descendant_thread_ids: SettledProviderTurnIds::default(), notification_identity_diagnostics: 0, rejected_accepted_turn: None, quarantined: false, @@ -1063,10 +1109,27 @@ impl CodexProvider { "thread/resume" } else { params_object.insert("experimentalRawEvents".to_owned(), json!(false)); + if provider.require_paginated_history { + params_object.insert("historyMode".to_owned(), json!("paginated")); + } "thread/start" }; stage = ProviderStartupStage::ThreadOpen; let opened = provider.request(method, params)?; + // Indexed Paperclip storage must not silently create or resume a + // legacy provider history. This verifies only the history API; + // paginated history alone does not prove bounded model-context + // reconstruction inside the provider on a cold resume. + if provider.require_paginated_history + && opened + .pointer("/thread/historyMode") + .and_then(Value::as_str) + != Some("paginated") + { + return Err(LocalRunnerError::invalid( + "codex_history_mode_unsupported: indexed sessions require paginated provider history", + )); + } provider.thread_id = opened .pointer("/thread/id") .or_else(|| opened.get("threadId")) @@ -1117,7 +1180,7 @@ impl CodexProvider { self.process.id() } - pub(crate) fn process_generation(&self) -> u64 { + pub(crate) fn process_generation(&self) -> ProcessGeneration { self.process_generation } @@ -1363,13 +1426,13 @@ impl CodexProvider { pub(crate) fn restore_completed_turn_authority( &mut self, authoritative: bool, - process_generation: Option, + process_generation: Option, provider_turn_id: Option<&str>, ) -> Result<(), LocalRunnerError> { self.completed_turn_authority = authoritative.then(|| CompletedTurnAuthority { // Legacy state did not record the generation. Generation zero is // deliberately older than every supervised process generation. - process_generation: process_generation.unwrap_or(0), + process_generation: process_generation.unwrap_or_default(), provider_turn_id: provider_turn_id .unwrap_or("durable-completed-turn") .to_owned(), @@ -1391,11 +1454,54 @@ impl CodexProvider { Ok(()) } - pub(crate) fn restore_descendant_thread_identities(&mut self, identities: &BTreeSet) { - // Exact provider-confirmed lineage lives as long as the root session. - // Evicting it would turn later child progress into a root integrity fault. + pub(crate) fn restore_descendant_thread_identities( + &mut self, + identities: &BTreeSet, + ) -> Result<(), LocalRunnerError> { + for id in identities { + self.descendant_thread_ids.restore(id.clone())?; + } + Ok(()) + } + + pub(crate) fn bind_indexed_turn_receipts( + &mut self, + store: IndexedStore, + binding: String, + ) -> Result<(), LocalRunnerError> { + self.settled_provider_turn_ids + .bind(store.clone(), format!("turns/{binding}"))?; self.descendant_thread_ids - .extend(identities.iter().cloned()); + .bind(store, format!("descendants/{binding}")) + } + + // Resolve only identities carried by this frame. Restoring lineage never + // enumerates all children, and a failed storage read is not an unknown child. + fn notification_descendants( + &self, + params: &Value, + ) -> Result, LocalRunnerError> { + let mut known = BTreeSet::new(); + for path in [ + "/threadId", + "/thread/id", + "/turn/threadId", + "/thread/parentThreadId", + "/thread/source/subAgent/thread_spawn/parent_thread_id", + "/thread/source/subAgent/threadSpawn/parentThreadId", + "/thread/source/subagent/thread_spawn/parent_thread_id", + ] { + if let Some(id) = params + .pointer(path) + .and_then(Value::as_str) + .filter(|id| !id.is_empty() && id.len() <= 240) + { + if self.descendant_thread_ids.contains(id)? { + known.insert(id.to_owned()); + } + } + } + Ok(known) } pub(crate) fn restore_settled_turn_identities( @@ -1407,7 +1513,7 @@ impl CodexProvider { .restore_all(provider_turn_ids, replay_filter) } - pub(crate) fn completed_turn_authority(&self) -> Option<(u64, &str)> { + pub(crate) fn completed_turn_authority(&self) -> Option<(ProcessGeneration, &str)> { self.completed_turn_authority.as_ref().map(|authority| { ( authority.process_generation, @@ -1436,11 +1542,16 @@ impl CodexProvider { "durable provider rollover requires its startup ownership observer", )); } - self.restart_idle_identity_epoch_observed(&mut |_| Ok(())) + let next = self + .process_generation + .next() + .ok_or_else(|| LocalRunnerError::invalid("fresh provider lifetime unavailable"))?; + self.restart_idle_identity_epoch_observed(next, &mut |_| Ok(())) } pub(crate) fn restart_idle_identity_epoch_observed( &mut self, + next_generation: ProcessGeneration, observe: &mut dyn FnMut(ProviderStartupObservation) -> Result<(), LocalRunnerError>, ) -> Result<(), LocalRunnerError> { if self.active_provider_turn_id.is_some() || self.ambiguous_turn_start_pending { @@ -1449,9 +1560,11 @@ impl CodexProvider { )); } - let next_generation = self.process_generation.checked_add(1).ok_or_else(|| { - LocalRunnerError::invalid("Codex process generation exhausted during epoch rollover") - })?; + if !next_generation.is_successor_of(self.process_generation) { + return Err(LocalRunnerError::invalid( + "provider rollover differs from its prepared lifetime", + )); + } let config = self.config.clone(); let authorized_tools = self.authorized_tools.clone(); let thread_id = self.thread_id.clone(); @@ -1477,6 +1590,7 @@ impl CodexProvider { contract.criterion_ids.as_slice(), ) }), + self.require_paginated_history, observe, )?; replacement.durable_tool_call_replays = durable_tool_call_replays; @@ -1737,7 +1851,7 @@ impl CodexProvider { return Err(error); } }; - if self.settled_provider_turn_ids.contains(&provider_turn_id) { + if self.settled_provider_turn_ids.contains(&provider_turn_id)? { return Err(self.reject_accepted_reused_turn_identity(provider_turn_id)); } // Only a validated provider turn identity proves that replacement @@ -1771,7 +1885,7 @@ impl CodexProvider { )) })?; - if self.settled_provider_turn_ids.contains(&provider_turn_id) { + if self.settled_provider_turn_ids.contains(&provider_turn_id)? { return Err(self.reject_accepted_reused_turn_identity(provider_turn_id)); } @@ -1893,7 +2007,10 @@ impl CodexProvider { let mut cursor = Value::Null; let mut cursors = BTreeSet::new(); let mut turns = BTreeMap::new(); - for _ in 0..10_000 { + // An active turn belongs to the newest metadata window. If it settles + // between the status read and paging, reread status rather than walking + // arbitrarily old completed turns looking for work that no longer exists. + for _ in 0..4 { let page = self .request( "thread/turns/list", @@ -1910,6 +2027,11 @@ impl CodexProvider { let data = page.get("data").and_then(Value::as_array).ok_or_else(|| { LocalRunnerError::invalid("codex_history_incomplete: turn page omitted data") })?; + if data.len() > 100 { + return Err(LocalRunnerError::invalid( + "codex_history_incomplete: oversized turn metadata page", + )); + } for turn in data { let id = bounded_provider_turn_id(turn.get("id").and_then(Value::as_str))?; if !matches!( @@ -1928,9 +2050,7 @@ impl CodexProvider { let next = page.get("nextCursor").cloned().unwrap_or(Value::Null); if next.is_null() || found_active { if !found_active { - return Err(LocalRunnerError::invalid( - "codex_history_incomplete: active thread has no active turn", - )); + break; } snapshot["thread"]["turns"] = Value::Array(turns.into_values().collect()); return Ok(snapshot); @@ -1948,8 +2068,25 @@ impl CodexProvider { } cursor = next; } + let mut current = self.request( + "thread/read", + json!({"threadId": self.thread_id, "includeTurns": false}), + )?; + if current.pointer("/thread/id").and_then(Value::as_str) != Some(self.thread_id.as_str()) { + return Err(LocalRunnerError::invalid( + "Codex thread/read returned a different thread", + )); + } + if current + .pointer("/thread/status/type") + .and_then(Value::as_str) + == Some("idle") + { + current["thread"]["turns"] = json!([]); + return Ok(current); + } Err(LocalRunnerError::invalid( - "codex_history_incomplete: turn page limit exceeded", + "codex_history_incomplete: active turn absent from current history window", )) } @@ -2306,7 +2443,9 @@ impl CodexProvider { if params .get("threadId") .and_then(Value::as_str) - .is_some_and(|id| self.descendant_thread_ids.contains(id)) + .map(|id| self.descendant_thread_ids.contains(id)) + .transpose()? + .unwrap_or(false) { return self.reject_descendant_request(rpc_id, method); } @@ -2320,7 +2459,7 @@ impl CodexProvider { self.active_provider_turn_id.as_deref(), &self.settled_provider_turn_ids, ¶ms, - ) { + )? { return self.reject_post_terminal_request(rpc_id, method); } let active_turn_id = self.active_provider_turn_id.as_deref().ok_or_else(|| { @@ -2406,7 +2545,8 @@ impl CodexProvider { "Codex emitted too many pending tool calls", )); } - if !completed_replay + if !self.durable_tool_call_replays + && !completed_replay && self.completed_tool_call_ids.len() >= MAX_COMPLETED_TOOL_CALL_IDS { return Err(LocalRunnerError::invalid( @@ -2437,7 +2577,9 @@ impl CodexProvider { if params .get("threadId") .and_then(Value::as_str) - .is_some_and(|id| self.descendant_thread_ids.contains(id)) + .map(|id| self.descendant_thread_ids.contains(id)) + .transpose()? + .unwrap_or(false) { return self.reject_descendant_request(rpc_id, method); } @@ -2451,7 +2593,7 @@ impl CodexProvider { self.active_provider_turn_id.as_deref(), &self.settled_provider_turn_ids, ¶ms, - ) { + )? { return self.reject_post_terminal_request(rpc_id, method); } let active_turn_id = self.active_provider_turn_id.clone().ok_or_else(|| { @@ -2521,18 +2663,14 @@ impl CodexProvider { }), })); } - let request_sequence = self.next_runtime_request_sequence; - self.next_runtime_request_sequence = self - .next_runtime_request_sequence - .checked_add(1) - .ok_or_else(|| { - LocalRunnerError::invalid("Codex runtime request sequence overflowed") - })?; + // This is an identity, not event order. Pending duplicate RPCs + // returned above keep their original durable request identity. + let request_nonce = new_runtime_request_scope()?; let request_id = scoped_runtime_request_id( &self.runtime_request_scope, &active_turn_id, &provider_request_id, - request_sequence, + &request_nonce, ); self.pending_runtime_requests .insert(request_id.clone(), pending); @@ -2558,7 +2696,7 @@ impl CodexProvider { let identity = match classify_notification_thread( method, &self.thread_id, - &self.descendant_thread_ids, + &self.notification_descendants(¶ms)?, ¶ms, ) { Ok(identity) => identity, @@ -2569,28 +2707,31 @@ impl CodexProvider { let candidate = notification_thread_id(¶ms) .filter(|id| !id.is_empty() && id.len() <= 240 && *id != self.thread_id) .map(str::to_owned); - let verified = candidate.as_ref().is_some_and(|candidate| { - self.request( + let verified = if let Some(candidate) = candidate.as_ref() { + if let Ok(metadata) = self.request( "thread/read", json!({"threadId": candidate, "includeTurns": false}), - ) - .ok() - .is_some_and(|metadata| { + ) { + let known = self.notification_descendants(&metadata)?; metadata.pointer("/thread/id").and_then(Value::as_str) == Some(candidate.as_str()) && matches!( classify_notification_thread( "thread/started", &self.thread_id, - &self.descendant_thread_ids, + &known, &metadata ), Ok(NotificationThread::Descendant) ) - }) - }); + } else { + false + } + } else { + false + }; if verified { - let mut known = self.descendant_thread_ids.clone(); + let mut known = self.notification_descendants(¶ms)?; known.insert(candidate.expect("verified candidate")); match classify_notification_thread(method, &self.thread_id, &known, ¶ms) { @@ -2615,22 +2756,14 @@ impl CodexProvider { if identity == NotificationThread::Descendant { let id = notification_thread_id(¶ms).expect("classified descendant has an identity"); - let newly_known = match remember_descendant_thread( - &mut self.descendant_thread_ids, - id, - ) { - Ok(newly_known) => newly_known, - Err(code) => { - return Ok(Some(CodexProviderEvent::ResourceLimit { - diagnostic: json!({ - "code": code, "recoverable": false, "classification": "resource_capacity", - "message": "Codex reached the child-thread inventory limit. Reconcile child work before continuing in a fresh provider session.", - "method": bounded_method(method), "limit": MAX_DESCENDANT_THREAD_IDS, - "expectedThreadId": self.thread_id, "receivedThreadId": id, - }), - })) - } - }; + let newly_known = !self.descendant_thread_ids.contains(id)?; + if !self.descendant_thread_ids.insert(id.to_owned())? { + return Ok(Some(CodexProviderEvent::ResourceLimit { + diagnostic: json!({"code": "provider_descendant_capacity_exhausted", "recoverable": false, + "classification": "resource_capacity", "limit": MAX_DESCENDANT_THREAD_IDS, + "message": "Codex reached the legacy child-thread inventory limit."}), + })); + } // Retain each discovered child's effect inventory independently of // the informational diagnostic budget, then bound repeated progress. if !newly_known && self.notification_identity_diagnostics >= 32 { @@ -2670,7 +2803,9 @@ impl CodexProvider { if notification_turn_id.is_some() && notification_turn_id != self.active_provider_turn_id.as_deref() && notification_turn_id - .is_some_and(|turn_id| self.settled_provider_turn_ids.contains(turn_id)) + .map(|turn_id| self.settled_provider_turn_ids.contains(turn_id)) + .transpose()? + .unwrap_or(false) { self.notification_identity_diagnostics += 1; if self.notification_identity_diagnostics > 32 { @@ -2716,12 +2851,10 @@ impl CodexProvider { "turn_binding_mismatch", ))); } - if let Err(code) = remember_spawned_descendants( - &mut self.descendant_thread_ids, - &self.thread_id, - method, - ¶ms, - ) { + let mut spawned = BTreeSet::new(); + if let Err(code) = + remember_spawned_descendants(&mut spawned, &self.thread_id, method, ¶ms) + { if code == "provider_descendant_capacity_exhausted" { return Ok(Some(CodexProviderEvent::ResourceLimit { diagnostic: json!({"code": code, "recoverable": false, @@ -2732,6 +2865,9 @@ impl CodexProvider { } return Ok(Some(self.identity_failure(method, ¶ms, code))); } + for id in spawned { + self.descendant_thread_ids.restore(id)?; + } if let Some(terminal_event_type) = terminal_event_type { if self.active_provider_turn_id.is_none() { return Err(LocalRunnerError::invalid( @@ -2754,7 +2890,7 @@ impl CodexProvider { }; if !self .settled_provider_turn_ids - .insert(provider_turn_id.clone()) + .insert(provider_turn_id.clone())? { return Err(LocalRunnerError::invalid( "Codex provider turn identity epoch reached its exact capacity", @@ -2815,7 +2951,11 @@ impl CodexProvider { self.pending_tool_request_bytes = self .pending_tool_request_bytes .saturating_sub(completed.retained_bytes); - self.completed_tool_call_ids.insert(result.call_id.clone()); + // Durable backends own exact replay authority. Retaining a second + // per-turn set here would reintroduce a lifetime call allowance. + if !self.durable_tool_call_replays { + self.completed_tool_call_ids.insert(result.call_id.clone()); + } } Ok(()) } @@ -2919,10 +3059,7 @@ impl CodexProvider { method: &str, params: Value, ) -> Result { - let request_id = self.next_request_id; - self.next_request_id = self.next_request_id.checked_add(1).ok_or_else(|| { - ProviderRequestError::Rejected(LocalRunnerError::invalid("Codex request id exhausted")) - })?; + let request_id = format!("paperclip-rpc-{}", uuid::Uuid::new_v4()); self.send_frame(&json!({"id": request_id, "method": method, "params": params})) .map_err(ProviderRequestError::Ambiguous)?; loop { @@ -2964,7 +3101,7 @@ impl CodexProvider { } ProviderRequestError::Ambiguous(error) })?; - if message.get("id").and_then(Value::as_u64) == Some(request_id) + if message.get("id").and_then(Value::as_str) == Some(request_id.as_str()) && message.get("method").is_none() { if let (Some(trace), Some(frame_id)) = (self.trace.as_mut(), trace_frame_id) { @@ -3507,11 +3644,11 @@ fn request_targets_non_active_turn( active_turn_id: Option<&str>, settled_turn_ids: &SettledProviderTurnIds, params: &Value, -) -> bool { - let requested_turn_id = runtime_request_turn_id(params); - requested_turn_id.is_some_and(|requested| { - settled_turn_ids.contains(requested) || active_turn_id != Some(requested) - }) +) -> Result { + let Some(requested) = runtime_request_turn_id(params) else { + return Ok(false); + }; + Ok(active_turn_id != Some(requested) || settled_turn_ids.contains(requested)?) } fn runtime_request_turn_id(params: &Value) -> Option<&str> { @@ -3835,7 +3972,7 @@ fn scoped_runtime_request_id( scope: &[u8; 16], turn_id: &str, provider_request_id: &str, - request_sequence: u64, + request_nonce: &[u8; 16], ) -> String { let mut digest = Sha256::new(); digest.update(scope); @@ -3844,7 +3981,7 @@ fn scoped_runtime_request_id( digest.update([0]); digest.update(provider_request_id.as_bytes()); digest.update([0]); - digest.update(request_sequence.to_be_bytes()); + digest.update(request_nonce); format!("runtime-request-{:x}", digest.finalize()) } @@ -3990,16 +4127,18 @@ mod tests { let script = r#" turn=0 while IFS= read -r line; do + request_id="${line#*\"id\":\"}" + request_id="${request_id%%\"*}" case "$line" in - *'"method":"initialize"'*) printf '%s\n' '{"id":1,"result":{}}' ;; - *'"method":"thread/start"'*) printf '%s\n' '{"id":2,"result":{"thread":{"id":"reader-tail-thread"}}}' ;; + *'"method":"initialize"'*) printf '{"id":"%s","result":{}}\n' "$request_id" ;; + *'"method":"thread/start"'*) printf '{"id":"%s","result":{"thread":{"id":"reader-tail-thread"}}}\n' "$request_id" ;; *'"method":"turn/start"'*) turn=$((turn + 1)) if [ "$turn" = 1 ]; then - printf '%s\n' '{"id":3,"result":{"turn":{"id":"reader-tail-1"}}}' + printf '{"id":"%s","result":{"turn":{"id":"reader-tail-1"}}}\n' "$request_id" printf '%s\n' '{"method":"turn/completed","params":{"turn":{"id":"reader-tail-1","status":"completed"}}}' else - printf '%s\n' '{"id":4,"error":{}}' + printf '{"id":"%s","error":{}}\n' "$request_id" printf '%s\n' '{"method":"turn/started","params":{"turn":{"id":"reader-tail-2"}}}' printf '%s\n' '{"method":"turn/completed","params":{"turn":{"id":"reader-tail-2","status":"completed"}}}' if __FLOOD_STDOUT__; then @@ -4048,7 +4187,7 @@ done fn warm_attachment_accepts_normalized_usage_for_the_completed_turn() { let mut provider = completion_tail_provider(); provider - .restore_completed_turn_authority(true, Some(1), Some("reader-tail-1")) + .restore_completed_turn_authority(true, Some(1.into()), Some("reader-tail-1")) .unwrap(); provider.active_provider_turn_id = None; provider @@ -4250,7 +4389,7 @@ done assert_eq!(process_generation, 1); assert_eq!( completed_turn_process_generation, - (boundary != "turn/completed").then_some(1) + (boundary != "turn/completed").then_some(1.into()) ); break; } @@ -4319,7 +4458,7 @@ done matches!(provider.poll().unwrap(), Some(CodexProviderEvent::Notification { method, params }) if method == "configWarning" && params["code"] == "provider_stdout_drain_timeout") ); // A stale generation's timeout is not inherited by a new owned epoch. - provider.exit_drain.as_mut().unwrap().process_generation = 0; + provider.exit_drain.as_mut().unwrap().process_generation = 0.into(); assert!( matches!(provider.poll().unwrap(), Some(CodexProviderEvent::Notification { method, .. }) if method == "turn/started") ); @@ -4422,9 +4561,10 @@ done &config, [], None, - 1, + 1.into(), None, None, + false, &mut |observation| match observation { ProviderStartupObservation::Spawned { process_id, @@ -4641,20 +4781,20 @@ done .is_err()); let scope = [7u8; 16]; assert_eq!( - scoped_runtime_request_id(&scope, "turn-1", "41", 1), - scoped_runtime_request_id(&scope, "turn-1", "41", 1), + scoped_runtime_request_id(&scope, "turn-1", "41", &[1; 16]), + scoped_runtime_request_id(&scope, "turn-1", "41", &[1; 16]), ); assert_ne!( - scoped_runtime_request_id(&scope, "turn-1", "41", 1), - scoped_runtime_request_id(&scope, "turn-2", "41", 1), + scoped_runtime_request_id(&scope, "turn-1", "41", &[1; 16]), + scoped_runtime_request_id(&scope, "turn-2", "41", &[1; 16]), ); assert_ne!( - scoped_runtime_request_id(&scope, "turn-1", "41", 1), - scoped_runtime_request_id(&[8u8; 16], "turn-1", "41", 1), + scoped_runtime_request_id(&scope, "turn-1", "41", &[1; 16]), + scoped_runtime_request_id(&[8u8; 16], "turn-1", "41", &[1; 16]), ); assert_ne!( - scoped_runtime_request_id(&scope, "turn-1", "41", 1), - scoped_runtime_request_id(&scope, "turn-1", "41", 2), + scoped_runtime_request_id(&scope, "turn-1", "41", &[1; 16]), + scoped_runtime_request_id(&scope, "turn-1", "41", &[2; 16]), ); assert!(codex_question_response( &pending, @@ -4780,6 +4920,68 @@ done .contains("option ids must be unique")); } + #[test] + fn indexed_turn_and_descendant_identities_outlive_the_memory_epoch() { + let directory = + std::env::temp_dir().join(format!("paperclip-identities-{}", uuid::Uuid::new_v4())); + let path = directory.join("provider.sqlite"); + { + let store = IndexedStore::open(&path, "session", true).unwrap(); + let mut turns = SettledProviderTurnIds::default(); + let mut children = SettledProviderTurnIds::default(); + turns.bind(store.clone(), "turns/session".into()).unwrap(); + children.bind(store, "descendants/session".into()).unwrap(); + for index in 0..5_000 { + assert!(turns.insert(format!("turn-{index}")).unwrap()); + assert!(children.insert(format!("child-{index}")).unwrap()); + } + assert!(turns.ids.is_empty() && children.ids.is_empty()); + assert!(!turns.at_capacity() && !children.at_capacity()); + } + let store = IndexedStore::open(&path, "session", false).unwrap(); + let mut turns = SettledProviderTurnIds::default(); + let mut children = SettledProviderTurnIds::default(); + turns.bind(store.clone(), "turns/session".into()).unwrap(); + children + .bind(store.clone(), "descendants/session".into()) + .unwrap(); + assert!(turns.contains("turn-0").unwrap()); + assert!(children.contains("child-0").unwrap()); + assert!(!children.contains("turn-0").unwrap()); + // Damaged storage must never turn an ancient identity into a new one. + let db = rusqlite::Connection::open(&path).unwrap(); + let target = "descendants/session\0child-0"; + let mut cursor: Option = None; + let mut partition = None; + loop { + let page = crate::indexed_partitions::partitions(&db, cursor.as_deref(), 128).unwrap(); + if page.is_empty() { + break; + } + for route in &page { + if route.lower_key.as_str() <= target { + partition = Some(route.file.clone()); + } else { + break; + } + } + let passed_target = page + .last() + .is_some_and(|route| route.lower_key.as_str() > target); + cursor = page.last().map(|route| route.lower_key.clone()); + if passed_target { + break; + } + } + let partition = partition.expect("routing page covers the descendant receipt key"); + let shard = rusqlite::Connection::open(directory.join(partition)).unwrap(); + shard.execute("UPDATE receipt_rows SET digest=zeroblob(32) WHERE namespace='descendants/session' AND receipt_key='child-0'", []).unwrap(); + drop(shard); + assert!(children.contains("child-0").is_err()); + drop((db, children, turns, store)); + std::fs::remove_dir_all(directory).unwrap(); + } + #[test] fn finds_only_active_turns_during_resume() { let snapshot = json!({"thread": {"turns": [ @@ -4814,67 +5016,74 @@ done #[test] fn rejects_requests_bound_to_any_non_active_turn_nonfatally() { let mut turn_one_settled = SettledProviderTurnIds::default(); - turn_one_settled.insert("turn-1".to_owned()); + turn_one_settled.insert("turn-1".to_owned()).unwrap(); let mut turn_two_settled = SettledProviderTurnIds::default(); - turn_two_settled.insert("turn-2".to_owned()); + turn_two_settled.insert("turn-2".to_owned()).unwrap(); let no_settled_turns = SettledProviderTurnIds::default(); assert!(request_targets_non_active_turn( Some("turn-2"), &turn_one_settled, &json!({"turnId": "turn-1"}), - )); + ) + .unwrap()); assert!(request_targets_non_active_turn( Some("turn-2"), &turn_one_settled, &json!({"turnId": "turn-0"}), - )); + ) + .unwrap()); assert!(request_targets_non_active_turn( None, &turn_two_settled, &json!({"turnId": "turn-1"}), - )); + ) + .unwrap()); assert!(request_targets_non_active_turn( Some("turn-1"), &turn_one_settled, &json!({"turnId": "turn-1"}), - )); + ) + .unwrap()); assert!(!request_targets_non_active_turn( Some("turn-2"), &turn_one_settled, &json!({"turnId": "turn-2"}), - )); - assert!(!request_targets_non_active_turn( - None, - &no_settled_turns, - &json!({}), - )); + ) + .unwrap()); + assert!(!request_targets_non_active_turn(None, &no_settled_turns, &json!({}),).unwrap()); assert!(request_targets_non_active_turn( Some("turn-2"), &turn_one_settled, &json!({"request": {"turnId": "turn-1"}}), - )); + ) + .unwrap()); assert!(!request_targets_non_active_turn( Some("turn-2"), &turn_one_settled, &json!({"request": {"turnId": "turn-2"}}), - )); + ) + .unwrap()); } #[test] fn settled_provider_turn_history_never_evicts_exact_identities() { let mut settled = SettledProviderTurnIds::default(); for index in 0..MAX_SETTLED_PROVIDER_TURN_IDS { - assert!(settled.insert(format!("turn-{index}"))); + assert!(settled.insert(format!("turn-{index}")).unwrap()); } assert_eq!(settled.ids.len(), MAX_SETTLED_PROVIDER_TURN_IDS); - assert!(settled.contains("turn-0")); - assert!(settled.contains("turn-1")); + assert!(settled.contains("turn-0").unwrap()); + assert!(settled.contains("turn-1").unwrap()); assert!(settled.at_capacity()); - assert!(!settled.insert(format!("turn-{MAX_SETTLED_PROVIDER_TURN_IDS}"))); - assert!(!settled.contains(&format!("turn-{MAX_SETTLED_PROVIDER_TURN_IDS}"))); + assert!(!settled + .insert(format!("turn-{MAX_SETTLED_PROVIDER_TURN_IDS}")) + .unwrap()); + assert!(!settled + .contains(&format!("turn-{MAX_SETTLED_PROVIDER_TURN_IDS}")) + .unwrap()); assert_eq!(settled.ids.len(), MAX_SETTLED_PROVIDER_TURN_IDS); - assert!(settled.contains("turn-0")); + assert!(settled.contains("turn-0").unwrap()); assert!(settled.filter.is_empty()); } @@ -4884,7 +5093,7 @@ done settled.restore("turn-restored".to_owned()).unwrap(); - assert!(settled.contains("turn-restored")); + assert!(settled.contains("turn-restored").unwrap()); } #[test] @@ -4898,8 +5107,8 @@ done ) .unwrap(); - assert!(settled.contains("turn-older")); - assert!(settled.contains("turn-latest")); + assert!(settled.contains("turn-older").unwrap()); + assert!(settled.contains("turn-latest").unwrap()); } #[test] diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/durable/command_epochs.rs b/packages/paperclip-runner/runner/crates/runner-core/src/durable/command_epochs.rs new file mode 100644 index 00000000000..f525923ed80 --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/durable/command_epochs.rs @@ -0,0 +1,46 @@ +use serde::{Deserialize, Serialize}; + +use super::DurableRunnerError; + +pub(crate) const CAPABILITY: &str = "transport.command_epochs.v1"; + +pub(crate) fn valid_epoch(value: &str) -> bool { + uuid::Uuid::parse_str(value).is_ok_and(|id| { + id.get_version_num() == 4 + && id.get_variant() == uuid::Variant::RFC4122 + && id.to_string() == value + }) +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub(crate) struct CommandEpochTransition { + pub schema: String, + pub run_id: String, + pub transition_id: String, + pub from_epoch: Option, + pub next_epoch: String, + pub final_ordinal: u64, +} + +impl CommandEpochTransition { + pub fn validate(&self, run_id: &str) -> Result<(), DurableRunnerError> { + if self.schema != "paperclip.prp.command-epoch.v1" + || self.run_id != run_id + || !valid_epoch(&self.transition_id) + || !valid_epoch(&self.next_epoch) + || self + .from_epoch + .as_deref() + .is_some_and(|id| !valid_epoch(id)) + || self.from_epoch.as_ref() == Some(&self.next_epoch) + || self.final_ordinal == 0 + || self.final_ordinal > 9_007_199_254_740_991 + { + return Err(DurableRunnerError::invalid( + "invalid command epoch transition", + )); + } + Ok(()) + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/durable/event_epochs.rs b/packages/paperclip-runner/runner/crates/runner-core/src/durable/event_epochs.rs new file mode 100644 index 00000000000..6cc3d3ce32d --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/durable/event_epochs.rs @@ -0,0 +1,49 @@ +use serde::{Deserialize, Serialize}; + +use super::DurableRunnerError; + +pub(crate) const CAPABILITY: &str = "transport.event_epochs.v1"; + +pub(crate) fn valid_epoch(value: &str) -> bool { + uuid::Uuid::parse_str(value).is_ok_and(|id| { + id.get_version_num() == 4 + && id.get_variant() == uuid::Variant::RFC4122 + && id.to_string() == value + }) +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub(crate) struct EventEpochTransition { + pub schema: String, + pub run_id: String, + pub transition_id: String, + pub from_epoch: Option, + pub next_epoch: String, + pub final_ordinal: u64, +} + +impl EventEpochTransition { + pub fn validate(&self, run_id: &str) -> Result<(), DurableRunnerError> { + if self.schema != "paperclip.prp.event-epoch.v1" + || self.run_id != run_id + || !valid_epoch(&self.transition_id) + || !valid_epoch(&self.next_epoch) + || self + .from_epoch + .as_deref() + .is_some_and(|id| !valid_epoch(id)) + || self.from_epoch.as_ref() == Some(&self.next_epoch) + || self.final_ordinal == 0 + || self.final_ordinal > 9_007_199_254_740_991 + { + return Err(DurableRunnerError::invalid( + "invalid event epoch transition", + )); + } + Ok(()) + } +} + +pub(crate) const DEFAULT_LIMIT: u64 = 1_048_576; +pub(crate) const MAX_ORDINAL: u64 = 9_007_199_254_740_991; diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/durable/mod.rs b/packages/paperclip-runner/runner/crates/runner-core/src/durable/mod.rs index bc28290fef0..ce581dfa155 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/durable/mod.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/durable/mod.rs @@ -1,5 +1,8 @@ +mod command_epochs; +mod event_epochs; mod runner; mod state; +mod storage_control; mod transport; use std::error::Error; @@ -12,11 +15,11 @@ use sha2::{Digest, Sha256}; use crate::stable_identity::{is_stable_id, DURABLE_STABLE_ID_CHARS, SHORT_STABLE_ID_CHARS}; pub use runner::{ - run_durable_runner, CommandExecution, CommandExecutor, PolledEvent, + run_durable_runner, run_indexed_durable_runner, CommandExecution, CommandExecutor, PolledEvent, TerminalDeliveryReconciliation, }; pub(crate) use state::{ - create_private_temporary_file, open_private_regular_file, redact_text, + create_private_temporary_file, open_private_regular_file, redact_output_text, redact_text, sanitize_semantic_tool_input, sanitize_value, verify_private_directory, }; pub use state::{ @@ -38,17 +41,64 @@ pub fn redact_diagnostic_text(input: &str) -> String { } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct DurableRunnerError(String); +pub struct DurableRunnerError { + message: String, + storage_capacity: bool, + storage_wait_cancelled: bool, + transport_unavailable: bool, +} impl DurableRunnerError { pub fn invalid(message: impl Into) -> Self { - Self(message.into()) + Self { + message: message.into(), + storage_capacity: false, + storage_wait_cancelled: false, + transport_unavailable: false, + } + } + + pub(crate) fn storage_capacity(message: impl Into) -> Self { + Self { + message: message.into(), + storage_capacity: true, + storage_wait_cancelled: false, + transport_unavailable: false, + } + } + + pub(crate) fn storage_wait_cancelled(message: impl Into) -> Self { + Self { + message: message.into(), + storage_capacity: false, + storage_wait_cancelled: true, + transport_unavailable: false, + } + } + + pub(crate) fn transport_unavailable(message: impl Into) -> Self { + Self { + message: message.into(), + storage_capacity: false, + storage_wait_cancelled: false, + transport_unavailable: true, + } + } + pub(crate) fn is_transport_unavailable(&self) -> bool { + self.transport_unavailable + } + + pub(crate) fn is_storage_capacity(&self) -> bool { + self.storage_capacity + } + pub(crate) fn is_storage_wait_cancelled(&self) -> bool { + self.storage_wait_cancelled } } impl Display for DurableRunnerError { fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result { - formatter.write_str(&self.0) + formatter.write_str(&self.message) } } diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/durable/runner.rs b/packages/paperclip-runner/runner/crates/runner-core/src/durable/runner.rs index 8f4eab51cab..c0b0fc2bde8 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/durable/runner.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/durable/runner.rs @@ -228,6 +228,7 @@ fn apply_authority_rotation( let reconnect_count = state.reconnect_count.saturating_add(1); let mut diagnostics = state.diagnostics.clone(); let mut rotated = DurableState::new(&next); + state.retain_indexed_authority(&mut rotated); if let Some(mut transition) = state.warm_transition.clone() { transition.phase = "activating".to_owned(); rotated.warm_transition = Some(transition); @@ -362,12 +363,33 @@ impl CumulativeAckPersistence { } pub fn run_durable_runner( + config: DurableRunnerConfig, + bootstrap_ticket: BootstrapTicket, + executor: E, +) -> Result<(), DurableRunnerError> { + run_durable_runner_with_storage(config, bootstrap_ticket, executor, false) +} + +pub fn run_indexed_durable_runner( + config: DurableRunnerConfig, + bootstrap_ticket: BootstrapTicket, + executor: E, +) -> Result<(), DurableRunnerError> { + run_durable_runner_with_storage(config, bootstrap_ticket, executor, true) +} + +fn run_durable_runner_with_storage( mut config: DurableRunnerConfig, bootstrap_ticket: BootstrapTicket, mut executor: E, + indexed: bool, ) -> Result<(), DurableRunnerError> { config.validate()?; - let store = DurableStateStore::new(&config.state_dir)?; + let store = if indexed { + DurableStateStore::new_indexed(&config.state_dir)? + } else { + DurableStateStore::new(&config.state_dir)? + }; let (mut state, recovered) = store.load_or_create(&config)?; if state.lifecycle == "revoked" || (state.lifecycle == "stopped" @@ -376,7 +398,11 @@ pub fn run_durable_runner( { return Ok(()); } - if recovered && state.warm_transition.is_none() { + if recovered + && state.warm_transition.is_none() + && state.event_epoch_transition.is_none() + && state.next_source_seq < super::event_epochs::MAX_ORDINAL - 32_768 + { state.reconnect_count = state.reconnect_count.saturating_add(1); state.record_diagnostic("runner restored its durable identity after process recovery"); state.enqueue_event( @@ -392,6 +418,11 @@ pub fn run_durable_runner( // retry after the trust decision. let mut endpoint = RunnerTransportEndpoint::new(&config.connect_url, &config.run_id)?; let started = Instant::now(); + let runtime_deadline = if config.max_runtime.is_zero() { + None + } else { + started.checked_add(config.max_runtime) + }; let mut bootstrap_ticket = Some(bootstrap_ticket); let mut lease: Option = None; let mut authenticated_once = false; @@ -620,7 +651,39 @@ pub fn run_durable_runner( } state.lifecycle = "ready".to_owned(); state.recoverable_failure = None; - store.save(&state)?; + let (saved, lane) = super::storage_control::run( + (transport, connection, lease, next_lease_renewal), + pressure_control_identity(&state, &config), + welcome.lease_renewal_version == Some(1), + runtime_deadline, + || store.save(&state), + ); + (transport, connection, lease, next_lease_renewal) = lane; + if let Err(error) = saved { + if error.is_storage_wait_cancelled() { + let _ = shutdown_preserving_cleanup(&state, &mut executor); + } + return Err(error); + } + let (rotated, lane) = rotate_event_epoch_if_needed( + (transport, connection, lease, next_lease_renewal), + &mut state, + &store, + &config, + welcome.event_epoch_limit, + runtime_deadline, + ); + (transport, connection, lease, next_lease_renewal) = lane; + if let Err(error) = rotated { + if error.is_storage_wait_cancelled() { + let _ = shutdown_preserving_cleanup(&state, &mut executor); + return Err(error); + } + state.record_diagnostic(format!("event epoch reconnect scheduled: {error}")); + store.save(&state)?; + disconnected_since = Some(Instant::now()); + continue; + } let mut sent_source_seq = state.acked_source_seq; let mut ack_persistence = CumulativeAckPersistence::default(); @@ -634,17 +697,19 @@ pub fn run_durable_runner( welcome.warm_transition_version, connection.protocol_version, )?; - let (result, lifecycle) = - process_command(&mut state, &store, &config, &mut executor, &command)?; + let (executed, lane) = execute_command_with_capacity_control( + (transport, connection, lease, next_lease_renewal), + &mut state, + &store, + &config, + &mut executor, + &command, + welcome.lease_renewal_version == Some(1), + runtime_deadline, + ); + (transport, connection, lease, next_lease_renewal) = lane; + let (result, lifecycle) = executed?; let next_authority = next_authority.filter(|_| completed_attachment(&result)); - if let Some(durable_lifecycle) = lifecycle.durable_state() { - persist_lifecycle_before_command_delivery( - &mut state, - &store, - durable_lifecycle, - &result, - )?; - } lifecycle_after_reply = lifecycle_after_reply.merge(lifecycle); let delivery = (|| { if let Some(next) = &next_authority { @@ -753,6 +818,32 @@ pub fn run_durable_runner( if !config.max_runtime.is_zero() && started.elapsed() >= config.max_runtime { break; } + let (rotated, lane) = rotate_event_epoch_if_needed( + (transport, connection, lease, next_lease_renewal), + &mut state, + &store, + &config, + welcome.event_epoch_limit, + runtime_deadline, + ); + (transport, connection, lease, next_lease_renewal) = lane; + match rotated { + Ok(true) => { + sent_source_seq = state.acked_source_seq; + ack_persistence = CumulativeAckPersistence::default(); + } + Ok(false) => {} + Err(error) => { + if error.is_storage_wait_cancelled() { + let _ = shutdown_preserving_cleanup(&state, &mut executor); + return Err(error); + } + state.record_diagnostic(format!("event epoch reconnect scheduled: {error}")); + store.save(&state)?; + disconnected_since = Some(Instant::now()); + break; + } + } if let Err(error) = send_outbox( &mut transport, &state, @@ -813,14 +904,29 @@ pub fn run_durable_runner( // Consuming the last cumulative ACK must not let a new output // suffix overtake the stop/suspend already queued behind it. let control_message = transport.receive_json(); - poll_executor_events_when_control_idle( - &mut state, - &store, - &config, - &mut executor, - sent_source_seq, - &control_message, - )?; + let (polled, lane) = super::storage_control::run( + (transport, connection, lease, next_lease_renewal), + pressure_control_identity(&state, &config), + welcome.lease_renewal_version == Some(1), + runtime_deadline, + || { + poll_executor_events_when_control_idle( + &mut state, + &store, + &config, + &mut executor, + sent_source_seq, + &control_message, + ) + }, + ); + (transport, connection, lease, next_lease_renewal) = lane; + if let Err(error) = polled { + if error.is_storage_wait_cancelled() { + let _ = shutdown_preserving_cleanup(&state, &mut executor); + } + return Err(error); + } let message = match control_message { Ok(Some(message)) => message, Ok(None) => continue, @@ -851,16 +957,78 @@ pub fn run_durable_runner( lease_renewal_deadline(current_unix_ms()?, connection.expires_at_unix_ms); } Some("ack") => { + if !state.ack_is_current(message.get("payload").unwrap_or(&Value::Null))? { + continue; + } let acked = message .pointer("/payload/ackedSourceSeq") .and_then(Value::as_u64) .ok_or_else(|| DurableRunnerError::invalid("ACK cursor is required"))?; - ack_persistence.apply( - &mut state, - &store, - acked, - connection.protocol_version, - )?; + let version = connection.protocol_version; + let mut pressure_identity = pressure_control_identity(&state, &config); + pressure_identity.acked_source_seq = acked; + let (acked, lane) = super::storage_control::run( + (transport, connection, lease, next_lease_renewal), + pressure_identity, + welcome.lease_renewal_version == Some(1), + runtime_deadline, + || ack_persistence.apply(&mut state, &store, acked, version), + ); + (transport, connection, lease, next_lease_renewal) = lane; + if let Err(error) = acked { + if error.is_storage_wait_cancelled() { + let _ = shutdown_preserving_cleanup(&state, &mut executor); + } + return Err(error); + } + } + Some("event_epoch_committed") => { + let transition: super::event_epochs::EventEpochTransition = + serde_json::from_value( + message.get("payload").cloned().unwrap_or(Value::Null), + ) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?; + if welcome.event_epoch_limit.is_none() + || state.last_event_epoch_transition.as_ref() != Some(&transition) + { + return Err(DurableRunnerError::invalid( + "unsolicited event epoch commit receipt", + )); + } + } + Some("command_epoch_rotate") => { + if !welcome.command_epochs { + return Err(DurableRunnerError::invalid( + "command epoch rotation was not negotiated", + )); + } + let transition: super::command_epochs::CommandEpochTransition = + serde_json::from_value( + message.get("payload").cloned().unwrap_or(Value::Null), + ) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?; + let (rotated, lane) = super::storage_control::run( + (transport, connection, lease, next_lease_renewal), + pressure_control_identity(&state, &config), + welcome.lease_renewal_version == Some(1), + runtime_deadline, + || store.rotate_command_epoch(&mut state, &transition), + ); + (transport, connection, lease, next_lease_renewal) = lane; + rotated?; + if let Err(error) = transport.send_json(&control_envelope( + &state, + &connection, + "command_epoch_committed", + serde_json::to_value(&transition) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?, + )) { + disconnected_since.get_or_insert_with(Instant::now); + state.record_diagnostic(error.to_string()); + state.reconnect_count = state.reconnect_count.saturating_add(1); + store.save(&state)?; + break; + } } Some("command") => { let command: Command = @@ -876,17 +1044,19 @@ pub fn run_durable_runner( welcome.warm_transition_version, connection.protocol_version, )?; - let (result, lifecycle) = - process_command(&mut state, &store, &config, &mut executor, &command)?; + let (executed, lane) = execute_command_with_capacity_control( + (transport, connection, lease, next_lease_renewal), + &mut state, + &store, + &config, + &mut executor, + &command, + welcome.lease_renewal_version == Some(1), + runtime_deadline, + ); + (transport, connection, lease, next_lease_renewal) = lane; + let (result, lifecycle) = executed?; let next_authority = next_authority.filter(|_| completed_attachment(&result)); - if let Some(durable_lifecycle) = lifecycle.durable_state() { - persist_lifecycle_before_command_delivery( - &mut state, - &store, - durable_lifecycle, - &result, - )?; - } let delivery = (|| { if let Some(next) = &next_authority { return deliver_warm_attachment( @@ -1036,11 +1206,212 @@ pub fn run_durable_runner( } } -fn lease_renewal_deadline(now: u64, expires_at: u64) -> u64 { +/// No provider callback runs while an event namespace is closing. Pending RPCs +/// remain live; new commands wait in the bounded transport backlog. Reading the +/// socket directly prevents a deferred command from hiding the commit receipt. +fn rotate_event_epoch_if_needed( + mut lane: super::storage_control::Lane, + state: &mut DurableState, + store: &DurableStateStore, + config: &DurableRunnerConfig, + limit: Option, + deadline: Option, +) -> ( + Result, + super::storage_control::Lane, +) { + if state.event_epoch_transition.is_none() + && limit.is_none_or(|limit| state.highest_source_seq() < limit) + { + return (Ok(false), lane); + } + if limit.is_none() { + return ( + Err(DurableRunnerError::invalid( + "pending event epoch capability was downgraded", + )), + lane, + ); + } + let (prepared, next_lane) = super::storage_control::run( + lane, + pressure_control_identity(state, config), + true, + deadline, + || store.prepare_event_epoch(state), + ); + lane = next_lane; + let transition = match prepared { + Ok(t) => t, + Err(e) => return (Err(e), lane), + }; + let mut sent = state.acked_source_seq; + let mut announced = false; + let mut stop_deadline: Option = None; + loop { + let (transport, connection, lease, renewal) = &mut lane; + let polled = (|| { + let now = current_unix_ms()?; + if deadline.is_some_and(|d| Instant::now() >= d) + || stop_deadline.is_some_and(|d| Instant::now() >= d) + || now >= connection.expires_at_unix_ms + { + return Err(DurableRunnerError::storage_wait_cancelled( + "event epoch wait exceeded authenticated lifetime", + )); + } + send_outbox(transport, state, &mut sent, connection.protocol_version)?; + if now >= *renewal { + let credential = lease.as_mut().ok_or_else(|| { + DurableRunnerError::invalid("event epoch requires live lease") + })?; + credential.renewal_requested = true; + transport.send_json(&control_envelope( + state, + connection, + "lease_renew", + json!({ + "connectionLeaseExpiresAtUnixMs": connection.expires_at_unix_ms, + "connectionLeaseRevocationEpoch": connection.revocation_epoch, + }), + ))?; + *renewal = now.saturating_add( + 5000.min(connection.expires_at_unix_ms.saturating_sub(now) / 2) + .max(1), + ); + } + if !announced && state.acked_source_seq == transition.final_ordinal { + transport.send_json(&control_envelope( + state, + connection, + "event_epoch_rotate", + serde_json::to_value(&transition) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?, + ))?; + announced = true; + } + let Some(message) = transport.receive_control_during_epoch()? else { + return Ok(None); + }; + validate_control_identity(&message, state, Some(connection))?; + match message.get("kind").and_then(Value::as_str) { + Some("ack") => { + if state.ack_is_current(message.get("payload").unwrap_or(&Value::Null))? { + let ack = message.pointer("/payload/ackedSourceSeq").and_then(Value::as_u64).ok_or_else(|| DurableRunnerError::invalid("ACK required"))?; + state.apply_ack(ack, connection.protocol_version)?; + } + } + Some("event_epoch_committed") => { + let received: super::event_epochs::EventEpochTransition = serde_json::from_value(message.get("payload").cloned().unwrap_or(Value::Null)).map_err(|e| DurableRunnerError::invalid(e.to_string()))?; + if !announced || received != transition { return Err(DurableRunnerError::invalid("event epoch acknowledgement changed its exact intent")); } + return Ok(Some(received)); + } + Some("lease_renewed") => { + apply_lease_renewal(&message, connection, lease.as_mut().ok_or_else(|| DurableRunnerError::invalid("live lease required"))?)?; + *renewal = lease_renewal_deadline(current_unix_ms()?, connection.expires_at_unix_ms); + } + Some("ping") => transport.send_json(&control_envelope(state, connection, "pong", json!({"lifecycle":state.lifecycle,"sourceEpoch":state.source_epoch,"ackedSourceSeq":state.acked_source_seq})))?, + Some("revoke") => { + if message.pointer("/payload/revocationEpoch").and_then(Value::as_u64).is_none_or(|epoch| epoch <= connection.revocation_epoch) { + return Err(DurableRunnerError::invalid("invalid revocation during event epoch transition")); + } + return Err(DurableRunnerError::storage_wait_cancelled("event epoch authority was revoked")); + }, + Some("command") => { + let command: Command = serde_json::from_value(message.get("payload").cloned().unwrap_or(Value::Null)).map_err(|e| DurableRunnerError::invalid(e.to_string()))?; + command.validate()?; + if super::storage_control::fresh_stop(&command, state.controller_epoch.as_deref(), state.last_controller_command_seq) { + stop_deadline.get_or_insert_with(|| Instant::now() + Duration::from_secs(10)); + } + transport.defer_control(message)?; + } + Some("command_epoch_rotate") => transport.defer_control(message)?, + _ => return Err(DurableRunnerError::invalid("unexpected control during event epoch transition")), + } + Ok(None) + })(); + match polled { + Err(error) => return (Err(error), lane), + Ok(Some(receipt)) => { + let (committed, next_lane) = super::storage_control::run( + lane, + pressure_control_identity(state, config), + true, + deadline, + || store.commit_event_epoch(state, &receipt), + ); + return (committed.map(|()| true), next_lane); + } + Ok(None) => {} + } + } +} + +pub(super) fn lease_renewal_deadline(now: u64, expires_at: u64) -> u64 { now.saturating_add(expires_at.saturating_sub(now) / 2) } -fn apply_lease_renewal( +fn pressure_control_identity(state: &DurableState, config: &DurableRunnerConfig) -> DurableState { + let mut identity = DurableState::new(config); + identity + .runner_instance_id + .clone_from(&state.runner_instance_id); + identity + .environment_lease_id + .clone_from(&state.environment_lease_id); + identity.run_id.clone_from(&state.run_id); + identity + .normalized_session_id + .clone_from(&state.normalized_session_id); + identity.turn_id.clone_from(&state.turn_id); + identity.item_id.clone_from(&state.item_id); + state.copy_event_ack_authority(&mut identity); + identity.acked_source_seq = state.acked_source_seq; + identity.next_source_seq = state.next_source_seq; + identity.last_controller_command_seq = state.last_controller_command_seq; + identity + .controller_epoch + .clone_from(&state.controller_epoch); + identity.lifecycle.clone_from(&state.lifecycle); + identity +} + +fn execute_command_with_capacity_control( + lane: super::storage_control::Lane, + state: &mut DurableState, + store: &DurableStateStore, + config: &DurableRunnerConfig, + executor: &mut E, + command: &Command, + renew: bool, + deadline: Option, +) -> ( + Result<(StoredCommandResult, CommandLifecycle), DurableRunnerError>, + super::storage_control::Lane, +) { + let mut identity = pressure_control_identity(state, config); + if command.controller_epoch == identity.controller_epoch { + identity.last_controller_command_seq = identity + .last_controller_command_seq + .max(command.controller_seq); + } + let (result, lane) = super::storage_control::run(lane, identity, renew, deadline, || { + let (result, lifecycle) = process_command(state, store, config, executor, command)?; + if let Some(durable_lifecycle) = lifecycle.durable_state() { + persist_lifecycle_before_command_delivery(state, store, durable_lifecycle, &result)?; + } + Ok((result, lifecycle)) + }); + if result + .as_ref() + .is_err_and(|error| error.is_storage_wait_cancelled()) + { + let _ = shutdown_preserving_cleanup(state, executor); + } + (result, lane) +} + +pub(super) fn apply_lease_renewal( message: &Value, connection: &mut ConnectionMetadata, credential: &mut LeaseCredential, @@ -1204,6 +1575,9 @@ fn wait_for_terminal_result_ack( .pointer("/payload/ackedSourceSeq") .and_then(Value::as_u64) .ok_or_else(|| DurableRunnerError::invalid("ACK cursor is required"))?; + if !state.ack_is_current(message.get("payload").unwrap_or(&Value::Null))? { + continue; + } ack_persistence.apply(state, store, acked, connection.protocol_version)?; } Some("ping") => transport.send_json(&control_envelope( @@ -1268,6 +1642,9 @@ fn wait_for_old_authority_outbox_ack( .pointer("/payload/ackedSourceSeq") .and_then(Value::as_u64) .ok_or_else(|| DurableRunnerError::invalid("ACK cursor is required"))?; + if !state.ack_is_current(message.get("payload").unwrap_or(&Value::Null))? { + continue; + } state.apply_ack(acked, connection.protocol_version)?; store.save(state)?; } @@ -1382,6 +1759,7 @@ fn deliver_warm_attachment( connection.lease_id.clone(), connection.expires_at_unix_ms, connection.revocation_epoch, + state.source_epoch.clone(), )?; if let Some(pending) = &state.warm_transition { if pending.phase != "prepared" @@ -1458,7 +1836,8 @@ fn deliver_warm_attachment( if message .pointer("/payload/ackedSourceSeq") .and_then(Value::as_u64) - == Some(state.acked_source_seq) => {} + == Some(state.acked_source_seq) + && state.ack_is_current(message.get("payload").unwrap_or(&Value::Null))? => {} _ => { return Err(DurableRunnerError::invalid( "warm attachment result fence received unrelated control", @@ -1725,15 +2104,17 @@ fn process_command( } match state.begin_command(command)? { CommandDisposition::Replay(result) => { - let lifecycle = - if result.status == "pending" || state.pending_provider_cleanup.is_some() { - // Its terminal delivery was already reconciled. Replaying - // the old receipt must not replace the newer command cursor - // with another terminal-delivery fence at an older sequence. - CommandLifecycle::Continue - } else { - CommandLifecycle::for_terminal(command) - }; + let lifecycle = if result.status == "pending" + || state.pending_provider_cleanup.is_some() + || command.controller_epoch != state.controller_epoch + { + // Its terminal delivery was already reconciled. Replaying + // the old receipt must not replace the newer command cursor + // with another terminal-delivery fence at an older sequence. + CommandLifecycle::Continue + } else { + CommandLifecycle::for_terminal(command) + }; return Ok((result, lifecycle)); } CommandDisposition::Reject(result) => { @@ -1747,6 +2128,7 @@ fn process_command( store.save(state)?; let mut execution = match executor.execute(command) { Ok(execution) => execution, + Err(error) if error.is_storage_wait_cancelled() => return Err(error), Err(error) => { // Failure facts may follow a full retained provider backlog. Only // the non-restoring FIFO is legal here; regular poll can launch. @@ -1909,7 +2291,7 @@ fn command_result_envelope( }) } -fn control_envelope( +pub(super) fn control_envelope( state: &DurableState, connection: &ConnectionMetadata, kind: &str, @@ -2214,6 +2596,7 @@ mod tests { fn command(command_type: &str) -> Command { Command { + controller_epoch: None, schema: "paperclip.prp.command.v1".to_owned(), command_id: "command_1".to_owned(), controller_seq: 1, @@ -4426,6 +4809,47 @@ mod tests { fs::remove_dir_all(directory).unwrap(); } + #[test] + fn closed_command_epoch_terminal_replay_cannot_stop_current_work() { + for kind in ["runner.suspend", "runner.shutdown"] { + let directory = + std::env::temp_dir().join(format!("historical-terminal-{}", uuid::Uuid::new_v4())); + let config = config(directory.clone()); + let store = DurableStateStore::new_indexed(&directory).unwrap(); + let (mut state, _) = store.load_or_create(&config).unwrap(); + let old = command(kind); + state.begin_command(&old).unwrap(); + store.save(&state).unwrap(); + let original = state + .complete_command(&old, json!({"exact": "settled terminal"})) + .unwrap(); + // The prior process completed terminal delivery and cleanup. New + // work is live under this same run, then its command lane rotates. + state.lifecycle = "ready".into(); + store.save(&state).unwrap(); + let transition = super::super::command_epochs::CommandEpochTransition { + schema: "paperclip.prp.command-epoch.v1".into(), + run_id: state.run_id.clone(), + transition_id: uuid::Uuid::new_v4().to_string(), + from_epoch: None, + next_epoch: uuid::Uuid::new_v4().to_string(), + final_ordinal: 1, + }; + store.rotate_command_epoch(&mut state, &transition).unwrap(); + let before = state.clone(); + let mut executor = CountingExecutor { calls: 0 }; + let (result, disposition) = + process_command(&mut state, &store, &config, &mut executor, &old).unwrap(); + assert_eq!(result, original); + assert_eq!(disposition, CommandLifecycle::Continue); + assert_eq!(state, before); + assert_eq!(executor.calls, 0); + drop(state); + drop(store); + fs::remove_dir_all(directory).unwrap(); + } + } + #[test] fn completed_shutdown_replay_still_stops_after_delivery() { let directory = std::env::temp_dir().join(format!( diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/durable/state.rs b/packages/paperclip-runner/runner/crates/runner-core/src/durable/state.rs index e137a20af78..e213d8e09bd 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/durable/state.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/durable/state.rs @@ -1,3 +1,8 @@ +use super::command_epochs::{valid_epoch, CommandEpochTransition}; +use super::event_epochs::EventEpochTransition; +use crate::indexed_revision::Revision; +use crate::indexed_store::{ExactReceipt, IndexedStore}; +use std::cell::RefCell; use std::collections::{BTreeMap, HashSet}; use std::fs::{self, File, OpenOptions}; use std::io::{self, Read, Write}; @@ -58,6 +63,8 @@ fn v2_replay_key(event_type: &str) -> Option<&'static str> { #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Command { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub controller_epoch: Option, pub schema: String, pub command_id: String, pub controller_seq: u64, @@ -107,6 +114,15 @@ impl Command { "command timestamps are empty, oversized, or contain control characters", )); } + if self + .controller_epoch + .as_deref() + .is_some_and(|id| !valid_epoch(id)) + { + return Err(DurableRunnerError::invalid( + "invalid command sequence epoch", + )); + } if self.controller_seq == 0 { return Err(DurableRunnerError::invalid( "command controllerSeq must be positive", @@ -232,6 +248,8 @@ pub(crate) struct WarmRunTransition { pub command_fingerprint: String, pub result_digest: String, pub old_acked_source_seq: u64, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub old_source_epoch: Option, pub connection: Value, pub runner_version: String, pub runner_digest: String, @@ -250,6 +268,7 @@ impl WarmRunTransition { lease_id: String, lease_expires_at_unix_ms: u64, lease_revocation_epoch: u64, + old_source_epoch: Option, ) -> Result { let mut receipt = Self { schema: "paperclip.runner.warm-transition.v1".to_owned(), @@ -264,6 +283,7 @@ impl WarmRunTransition { .map_err(|error| DurableRunnerError::invalid(error.to_string()))?, ), old_acked_source_seq: ack, + old_source_epoch, connection: command .payload .pointer("/paperclipNextAuthority/connection") @@ -297,6 +317,8 @@ pub(crate) struct PendingWarmRunTransition { #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] struct ExecutorEventReceipt { + #[serde(default, skip_serializing_if = "Option::is_none")] + source_epoch: Option, fingerprint: String, source_seq: u64, } @@ -319,15 +341,28 @@ pub struct DurableState { pub turn_id: String, pub item_id: String, pub lifecycle: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub source_epoch: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub(crate) event_epoch_transition: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub(crate) last_event_epoch_transition: Option, pub next_source_seq: u64, pub acked_source_seq: u64, pub last_controller_command_seq: u64, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub controller_epoch: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub(crate) last_command_epoch_transition: Option, pub compacted_through_controller_seq: u64, pub reconnect_count: u64, pub max_outbox_bytes: usize, pub p0_reserve_bytes: usize, pub peak_outbox_bytes: usize, + #[serde(default)] pub outbox: Vec, + #[serde(skip)] + indexed_receipts: Option, pub processed_commands: BTreeMap, #[serde(default)] pub processed_command_fingerprints: BTreeMap, @@ -349,6 +384,9 @@ pub struct DurableState { } impl DurableState { + pub(crate) fn indexed_durability(&self) -> bool { + self.indexed_receipts.is_some() + } pub(crate) fn new(config: &DurableRunnerConfig) -> Self { Self { schema: STATE_SCHEMA.to_owned(), @@ -359,15 +397,21 @@ impl DurableState { turn_id: config.turn_id.clone(), item_id: config.item_id.clone(), lifecycle: "connecting".to_owned(), + source_epoch: None, + event_epoch_transition: None, + last_event_epoch_transition: None, next_source_seq: 1, acked_source_seq: 0, last_controller_command_seq: 0, + controller_epoch: None, + last_command_epoch_transition: None, compacted_through_controller_seq: 0, reconnect_count: 0, max_outbox_bytes: config.max_outbox_bytes, p0_reserve_bytes: config.p0_reserve_bytes, peak_outbox_bytes: 0, outbox: Vec::new(), + indexed_receipts: None, processed_commands: BTreeMap::new(), processed_command_fingerprints: BTreeMap::new(), pending_terminal_delivery: None, @@ -386,6 +430,60 @@ impl DurableState { self.outbox.iter().map(|event| event.byte_size).sum() } + fn outbox_namespace(&self) -> String { + match &self.source_epoch { + Some(epoch) => format!("outbox/{}/{epoch}", self.run_id), + None => format!("outbox/{}", self.run_id), + } + } + + pub(crate) fn retain_indexed_authority(&self, target: &mut DurableState) { + target.indexed_receipts.clone_from(&self.indexed_receipts); + } + + pub(crate) fn copy_event_ack_authority(&self, target: &mut DurableState) { + target.source_epoch.clone_from(&self.source_epoch); + target.indexed_receipts.clone_from(&self.indexed_receipts); + } + + pub(crate) fn event_resume(&self) -> Value { + json!({"sourceEpoch": self.source_epoch, "nextSourceEventSeq": self.next_source_seq, + "ackedSourceSeq": self.acked_source_seq, "transition": self.event_epoch_transition}) + } + + /// An old authenticated ACK is accepted only as evidence about its exact + /// closed namespace. It never advances this namespace's current cursor. + pub(crate) fn ack_is_current(&self, payload: &Value) -> Result { + let epoch = match payload.get("sourceEpoch") { + None | Some(Value::Null) => None, + Some(Value::String(epoch)) if valid_epoch(epoch) => Some(epoch.as_str()), + _ => return Err(DurableRunnerError::invalid("invalid ACK epoch")), + }; + let ack = payload + .get("ackedSourceSeq") + .and_then(Value::as_u64) + .ok_or_else(|| DurableRunnerError::invalid("ACK cursor is required"))?; + if epoch == self.source_epoch.as_deref() { + return Ok(true); + } + let receipt = self + .indexed_receipts + .as_ref() + .ok_or_else(|| DurableRunnerError::invalid("ACK epoch is not current"))? + .receipt( + &format!("event-epochs/{}", self.run_id), + epoch.unwrap_or("legacy"), + )? + .ok_or_else(|| DurableRunnerError::invalid("ACK has no closed epoch receipt"))?; + let transition: EventEpochTransition = serde_json::from_slice(&receipt) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?; + transition.validate(&self.run_id)?; + if transition.from_epoch.as_deref() != epoch || ack > transition.final_ordinal { + return Err(DurableRunnerError::invalid("ACK exceeds its closed epoch")); + } + Ok(false) + } + pub fn highest_source_seq(&self) -> u64 { self.next_source_seq.saturating_sub(1) } @@ -397,10 +495,14 @@ impl DurableState { priority: EventPriority, payload: Value, ) -> Result { - let source_event_id = format!( - "event_{}_{:016}", - self.runner_instance_id, self.next_source_seq - ); + let source_event_id = if self.source_epoch.is_some() { + format!("event_{}", uuid::Uuid::new_v4()) + } else { + format!( + "event_{}_{:016}", + self.runner_instance_id, self.next_source_seq + ) + }; self.enqueue_event_with_source_event_id( config, source_event_id, @@ -449,7 +551,23 @@ impl DurableState { ) -> Result { self.source_event_id_for_executor(executor_event_id)?; validate_semantic_tool_input_digest(event_type, payload)?; - let Some(existing) = self.executor_event_receipts.get(executor_event_id) else { + let existing = if let Some(existing) = self.executor_event_receipts.get(executor_event_id) { + Some(existing.clone()) + } else if let Some(store) = &self.indexed_receipts { + store + .receipt( + &format!("executor-events/{}", self.run_id), + executor_event_id, + )? + .map(|bytes| { + serde_json::from_slice::(&bytes) + .map_err(|error| DurableRunnerError::invalid(error.to_string())) + }) + .transpose()? + } else { + None + }; + let Some(existing) = existing else { return Ok(false); }; if existing.fingerprint != executor_event_fingerprint(event_type, priority, payload) { @@ -485,6 +603,7 @@ impl DurableState { self.executor_event_receipts.insert( executor_event_id, ExecutorEventReceipt { + source_epoch: self.source_epoch.clone(), fingerprint, source_seq, }, @@ -526,7 +645,11 @@ impl DurableState { } validate_semantic_tool_input_digest(event_type.as_str(), &payload)?; - let sanitized_payload = sanitize_value(&payload); + let sanitized_payload = if event_type == "output.body.chunk" { + crate::output_body::sanitize_chunk(&payload)? + } else { + sanitize_value(&payload) + }; if durable_semantics_changed_by_sanitization(&payload, &sanitized_payload) { return Err(DurableRunnerError::invalid( "durable identity or validation semantics contain credential-shaped material", @@ -535,6 +658,13 @@ impl DurableState { let sanitized_payload = finalize_semantic_tool_input_payload(event_type.as_str(), &payload, sanitized_payload)?; + if self.event_epoch_transition.is_some() + || self.next_source_seq >= super::event_epochs::MAX_ORDINAL + { + return Err(DurableRunnerError::invalid( + "event admission requires a completed epoch rotation", + )); + } let source_seq = self.next_source_seq; let emitted_at = current_timestamp()?; let schema_version = if matches!( @@ -548,7 +678,7 @@ impl DurableState { } else { 1 }; - let envelope = json!({ + let mut envelope = json!({ "protocol": PROTOCOL, "version": PROTOCOL_VERSION, "kind": "event", @@ -575,6 +705,9 @@ impl DurableState { "payload": sanitized_payload, }, }); + if let Some(epoch) = &self.source_epoch { + envelope["payload"]["sourceEpoch"] = json!(epoch); + } let byte_size = serde_json::to_vec(&envelope) .map_err(|error| DurableRunnerError::invalid(error.to_string()))? .len(); @@ -715,6 +848,26 @@ impl DurableState { } return Ok(CommandDisposition::Replay(previous.clone())); } + if let Some(store) = &self.indexed_receipts { + if let Some(bytes) = + store.receipt(&format!("commands/{}", self.run_id), &command.command_id)? + { + let (previous_fingerprint, result): (String, StoredCommandResult) = + serde_json::from_slice(&bytes) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?; + if previous_fingerprint != fingerprint { + return Err(DurableRunnerError::invalid( + "commandId was reused with different command data", + )); + } + return Ok(CommandDisposition::Replay(result)); + } + } + if command.controller_epoch != self.controller_epoch { + return Err(DurableRunnerError::invalid( + "command sequence epoch differs from durable head", + )); + } if command.controller_seq <= self.compacted_through_controller_seq { return Ok(CommandDisposition::Reject(command_result( command, @@ -978,6 +1131,89 @@ fn canonical_json(value: &Value) -> String { #[derive(Clone, Debug)] pub struct DurableStateStore { path: PathBuf, + indexed: bool, + index: RefCell>, +} + +#[derive(Clone, Debug)] +struct RunnerIndex { + store: IndexedStore, + generation: Revision, + epoch: String, + next_source_seq: u64, + source_epoch: Option, + completed_commands: HashSet, +} + +/// Excludes payloads without cloning or serializing the pending backlog. +struct RunnerMetadata<'a>(&'a DurableState); +impl Serialize for RunnerMetadata<'_> { + fn serialize(&self, serializer: S) -> Result { + use serde::ser::SerializeMap; + let state = self.0; + let mut map = serializer.serialize_map(None)?; + macro_rules! field { + ($key:literal, $value:ident) => { + map.serialize_entry($key, &state.$value)?; + }; + } + field!("schema", schema); + field!("runnerInstanceId", runner_instance_id); + field!("environmentLeaseId", environment_lease_id); + field!("runId", run_id); + field!("normalizedSessionId", normalized_session_id); + field!("turnId", turn_id); + field!("itemId", item_id); + field!("lifecycle", lifecycle); + field!("nextSourceSeq", next_source_seq); + if state.source_epoch.is_some() { + field!("sourceEpoch", source_epoch); + } + if state.event_epoch_transition.is_some() { + field!("eventEpochTransition", event_epoch_transition); + } + if state.last_event_epoch_transition.is_some() { + field!("lastEventEpochTransition", last_event_epoch_transition); + } + field!("ackedSourceSeq", acked_source_seq); + field!("lastControllerCommandSeq", last_controller_command_seq); + if state.controller_epoch.is_some() { + field!("controllerEpoch", controller_epoch); + } + if state.last_command_epoch_transition.is_some() { + field!("lastCommandEpochTransition", last_command_epoch_transition); + } + field!( + "compactedThroughControllerSeq", + compacted_through_controller_seq + ); + field!("reconnectCount", reconnect_count); + field!("maxOutboxBytes", max_outbox_bytes); + field!("p0ReserveBytes", p0_reserve_bytes); + field!("peakOutboxBytes", peak_outbox_bytes); + field!("processedCommands", processed_commands); + field!( + "processedCommandFingerprints", + processed_command_fingerprints + ); + field!("pendingTerminalDelivery", pending_terminal_delivery); + if state.pending_provider_cleanup.is_some() { + field!("pendingProviderCleanup", pending_provider_cleanup); + } + if state.warm_transition.is_some() { + field!("warmTransition", warm_transition); + } + field!("executorEventReceipts", executor_event_receipts); + field!("v2ReplayEvents", v2_replay_events); + field!( + "lastConnectionProtocolVersion", + last_connection_protocol_version + ); + field!("diagnostics", diagnostics); + field!("backpressure", backpressure); + field!("recoverableFailure", recoverable_failure); + map.end() + } } impl DurableStateStore { @@ -1006,9 +1242,558 @@ impl DurableStateStore { verify_private_directory(state_dir)?; Ok(Self { path: state_dir.join(STATE_FILE), + indexed: false, + index: RefCell::new(None), }) } + /// Explicit format choice; an activated missing database is never recreated. + pub fn new_indexed(state_dir: &Path) -> Result { + let mut store = Self::new(state_dir)?; + store.indexed = true; + Ok(store) + } + + /// Prepare under an externally-held session fence. Does not modify the + /// source, start a peer, or activate the staged directory. + pub fn stage_legacy( + config: &DurableRunnerConfig, + destination: &Path, + fence_id: &str, + ) -> Result { + use crate::legacy_indexed_import::{publish_staged_locator, read_legacy, ImportWriter}; + verify_private_directory(&config.state_dir)?; + if destination == config.state_dir + || (destination.exists() + && destination.canonicalize().ok() == config.state_dir.canonicalize().ok()) + { + return Err(DurableRunnerError::invalid( + "legacy source cannot be the import destination", + )); + } + let (state, source): (DurableState, _) = read_legacy( + &config.state_dir.join(STATE_FILE), + config.max_outbox_bytes.saturating_add(STATE_OVERHEAD_BYTES) as u64, + )?; + if state.has_legacy_command_journal() { + return Err(DurableRunnerError::invalid( + "legacy command fingerprints are unavailable; migration requires reconciliation", + )); + } + validate_binding(&state, config, false)?; + let binding = format!( + "runner/{}/{}", + config.runner_instance_id, config.normalized_session_id + ); + let mut writer = ImportWriter::open( + &destination.join("runner-state.sqlite"), + &binding, + fence_id, + &source.digest, + )?; + for event in &state.outbox { + writer.add(ExactReceipt { + namespace: format!("outbox/{}", state.run_id), + key: format!("{:020}", event.source_seq), + bytes: serde_json::to_vec(event) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?, + })?; + } + for result in state + .processed_commands + .values() + .filter(|entry| entry.status != "pending") + { + let fingerprint = state + .processed_command_fingerprints + .get(&result.command_id) + .ok_or_else(|| { + DurableRunnerError::invalid("legacy exact command fingerprint is missing") + })?; + writer.add(ExactReceipt { + namespace: format!("commands/{}", state.run_id), + key: result.command_id.clone(), + bytes: serde_json::to_vec(&(fingerprint, result)) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?, + })?; + } + for (id, receipt) in &state.executor_event_receipts { + writer.add(ExactReceipt { + namespace: format!("executor-events/{}", state.run_id), + key: id.clone(), + bytes: serde_json::to_vec(receipt) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?, + })?; + } + source.verify()?; + let proof = writer.finish( + "runner", + serde_json::to_vec(&RunnerMetadata(&state)) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?, + )?; + publish_staged_locator( + &destination.join(STATE_FILE), + &serde_json::to_vec(&json!({ + "schema": "paperclip.runner.durable.state.indexed.v1", "binding": binding + })) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?, + )?; + Ok(proof) + } + + fn load_indexed( + &self, + config: &DurableRunnerConfig, + ) -> Result<(DurableState, bool), DurableRunnerError> { + let binding = format!( + "runner/{}/{}", + config.runner_instance_id, config.normalized_session_id + ); + let locator = + json!({"schema":"paperclip.runner.durable.state.indexed.v1", "binding":binding}); + let existing = match open_private_regular_file(&self.path) { + Ok(mut file) => { + let mut bytes = Vec::new(); + std::io::Read::by_ref(&mut file) + .take(4097) + .read_to_end(&mut bytes) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?; + if bytes.len() > 4096 + || serde_json::from_slice::(&bytes).ok() != Some(locator.clone()) + { + return Err(DurableRunnerError::invalid( + "indexed runner activation requires a fenced legacy migration", + )); + } + true + } + Err(error) if error.kind() == io::ErrorKind::NotFound => false, + Err(error) => return Err(DurableRunnerError::invalid(error.to_string())), + }; + let store = IndexedStore::open(&self.path.with_extension("sqlite"), &binding, !existing)?; + crate::legacy_indexed_import::require_prepared(&store)?; + let snapshot = store.read_state("runner")?; + if existing && snapshot.is_none() { + return Err(DurableRunnerError::invalid( + "activated runner authority is missing", + )); + } + let recovered = snapshot.is_some(); + let generation = snapshot + .as_ref() + .map_or(Revision::Absent, |snapshot| snapshot.generation); + let mut state = match snapshot { + Some(snapshot) => serde_json::from_slice::(&snapshot.bytes) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?, + None => DurableState::new(config), + }; + let namespace = state.outbox_namespace(); + let mut after = format!("{:020}", state.acked_source_seq); + let mut pending_bytes = 0usize; + loop { + let page = store.receipt_page(&namespace, &after, 128, 32 * 1024 * 1024)?; + if page.is_empty() { + break; + } + for receipt in page { + after = receipt.key; + let event: StoredOutboxEvent = serde_json::from_slice(&receipt.bytes) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?; + if event.source_seq >= state.next_source_seq { + return Err(DurableRunnerError::invalid( + "outbox exceeds committed cursor", + )); + } + pending_bytes = pending_bytes + .checked_add(event.byte_size) + .ok_or_else(|| DurableRunnerError::invalid("outbox size overflow"))?; + if pending_bytes > state.max_outbox_bytes { + return Err(DurableRunnerError::invalid( + "pending outbox exceeds its admission bound", + )); + } + state.outbox.push(event); + } + } + validate_binding(&state, config, false)?; + if let Some(transition) = &state.last_command_epoch_transition { + let expected = serde_json::to_vec(transition) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?; + if store + .receipt( + &format!("command-epoch-transitions/{}", state.run_id), + &transition.transition_id, + )? + .as_ref() + != Some(&expected) + || store + .receipt( + &format!("command-epochs/{}", state.run_id), + transition.from_epoch.as_deref().unwrap_or("legacy"), + )? + .as_ref() + != Some(&expected) + { + return Err(DurableRunnerError::invalid( + "command epoch head has no exact committed transition", + )); + } + } + if let Some(t) = &state.last_event_epoch_transition { + let expected = + serde_json::to_vec(t).map_err(|e| DurableRunnerError::invalid(e.to_string()))?; + for (namespace, key) in [ + ( + format!("event-epoch-transitions/{}", state.run_id), + t.transition_id.as_str(), + ), + ( + format!("event-epochs/{}", state.run_id), + t.from_epoch.as_deref().unwrap_or("legacy"), + ), + ] { + if store.receipt(&namespace, key)?.as_ref() != Some(&expected) { + return Err(DurableRunnerError::invalid( + "event epoch head has no exact committed transition", + )); + } + } + } + state.indexed_receipts = Some(store.clone()); + *self.index.borrow_mut() = Some(RunnerIndex { + store, + generation, + epoch: state.run_id.clone(), + next_source_seq: state.next_source_seq, + source_epoch: state.source_epoch.clone(), + completed_commands: state + .processed_commands + .values() + .filter(|entry| entry.status != "pending") + .map(|entry| entry.command_id.clone()) + .collect(), + }); + if state.reconcile_pending_commands() || !recovered { + self.save(&state)?; + } + if !existing { + let (temporary, mut file) = create_private_temporary_file(&self.path)?; + file.write_all( + &serde_json::to_vec(&locator) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?, + ) + .and_then(|_| file.sync_all()) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?; + drop(file); + fs::rename(temporary, &self.path) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?; + #[cfg(unix)] + File::open(self.path.parent().unwrap()) + .and_then(|file| file.sync_all()) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?; + } + Ok((state, recovered)) + } + + pub(crate) fn prepare_event_epoch( + &self, + state: &mut DurableState, + ) -> Result { + if let Some(pending) = &state.event_epoch_transition { + return Ok(pending.clone()); + } + let receipts = state.indexed_receipts.as_ref().ok_or_else(|| { + DurableRunnerError::invalid("event epochs require indexed durability") + })?; + if state.highest_source_seq() == 0 + || state.pending_terminal_delivery.is_some() + || state.pending_provider_cleanup.is_some() + || state.warm_transition.is_some() + { + return Err(DurableRunnerError::invalid( + "event epoch cannot rotate this lifecycle", + )); + } + let mut next_epoch = None; + for _ in 0..32 { + let id = uuid::Uuid::new_v4().to_string(); + if state.source_epoch.as_ref() != Some(&id) + && receipts + .receipt(&format!("event-epochs/{}", state.run_id), &id)? + .is_none() + { + next_epoch = Some(id); + break; + } + } + let transition = EventEpochTransition { + schema: "paperclip.prp.event-epoch.v1".into(), + run_id: state.run_id.clone(), + transition_id: uuid::Uuid::new_v4().to_string(), + from_epoch: state.source_epoch.clone(), + next_epoch: next_epoch + .ok_or_else(|| DurableRunnerError::invalid("event epoch allocation collision"))?, + final_ordinal: state.highest_source_seq(), + }; + let mut candidate = state.clone(); + candidate.event_epoch_transition = Some(transition.clone()); + self.save(&candidate)?; + *state = candidate; + Ok(transition) + } + + pub(crate) fn commit_event_epoch( + &self, + state: &mut DurableState, + transition: &EventEpochTransition, + ) -> Result<(), DurableRunnerError> { + transition.validate(&state.run_id)?; + let receipts = state.indexed_receipts.as_ref().ok_or_else(|| { + DurableRunnerError::invalid("event epochs require indexed durability") + })?; + if let Some(bytes) = receipts.receipt( + &format!("event-epoch-transitions/{}", state.run_id), + &transition.transition_id, + )? { + if serde_json::from_slice::(&bytes) + .ok() + .as_ref() + != Some(transition) + { + return Err(DurableRunnerError::invalid( + "event transition identity was reused", + )); + } + return Ok(()); + } + if state.event_epoch_transition.as_ref() != Some(transition) + || transition.from_epoch != state.source_epoch + || transition.final_ordinal != state.acked_source_seq + || !state.outbox.is_empty() + || !state.v2_replay_events.is_empty() + || state.last_connection_protocol_version != Some(2) + { + return Err(DurableRunnerError::invalid( + "event epoch requires its exactly acknowledged outbox", + )); + } + // The immutable receipts survive this bounded cache retirement. + for (id, receipt) in &state.executor_event_receipts { + let expected = serde_json::to_vec(receipt) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?; + if receipts + .receipt(&format!("executor-events/{}", state.run_id), id)? + .as_ref() + != Some(&expected) + { + return Err(DurableRunnerError::invalid( + "event epoch cannot retire uncommitted executor evidence", + )); + } + } + let mut candidate = state.clone(); + candidate.source_epoch = Some(transition.next_epoch.clone()); + candidate.last_event_epoch_transition = Some(transition.clone()); + candidate.event_epoch_transition = None; + candidate.next_source_seq = 1; + candidate.acked_source_seq = 0; + candidate.executor_event_receipts.clear(); + self.save(&candidate)?; + *state = candidate; + Ok(()) + } + + /// Persist the exact close receipt and successor head atomically. No provider + /// callback, event cursor, semantic identity or run lifecycle changes here. + pub(crate) fn rotate_command_epoch( + &self, + state: &mut DurableState, + transition: &CommandEpochTransition, + ) -> Result<(), DurableRunnerError> { + transition.validate(&state.run_id)?; + let receipts = state.indexed_receipts.as_ref().ok_or_else(|| { + DurableRunnerError::invalid("command epochs require indexed durability") + })?; + if let Some(bytes) = receipts.receipt( + &format!("command-epoch-transitions/{}", state.run_id), + &transition.transition_id, + )? { + let previous: CommandEpochTransition = serde_json::from_slice(&bytes) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?; + if previous != *transition { + return Err(DurableRunnerError::invalid( + "command epoch transition identity was reused", + )); + } + return Ok(()); + } + if transition.from_epoch != state.controller_epoch + || transition.final_ordinal != state.last_controller_command_seq + || state + .processed_commands + .values() + .any(|c| c.status == "pending") + || state.pending_terminal_delivery.is_some() + || state.pending_provider_cleanup.is_some() + || state.warm_transition.is_some() + { + return Err(DurableRunnerError::invalid( + "command epoch transition does not match the settled current namespace", + )); + } + if receipts + .receipt( + &format!("command-epochs/{}", state.run_id), + &transition.next_epoch, + )? + .is_some() + { + return Err(DurableRunnerError::invalid( + "command epoch successor was already closed", + )); + } + for result in state.processed_commands.values() { + let fingerprint = state + .processed_command_fingerprints + .get(&result.command_id) + .ok_or_else(|| { + DurableRunnerError::invalid("command fingerprint missing during epoch rotation") + })?; + let expected = serde_json::to_vec(&(fingerprint, result)) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?; + if receipts + .receipt(&format!("commands/{}", state.run_id), &result.command_id)? + .as_ref() + != Some(&expected) + { + return Err(DurableRunnerError::invalid( + "command epoch cannot retire an uncommitted receipt", + )); + } + } + // Every removed command is already an immutable exact receipt. This is + // independent of outstanding provider/semantic work, which remains live. + let mut candidate = state.clone(); + candidate.controller_epoch = Some(transition.next_epoch.clone()); + candidate.last_command_epoch_transition = Some(transition.clone()); + candidate.last_controller_command_seq = 0; + candidate.compacted_through_controller_seq = 0; + candidate.processed_commands.clear(); + candidate.processed_command_fingerprints.clear(); + self.save(&candidate)?; + *state = candidate; + Ok(()) + } + + fn save_indexed(&self, state: &DurableState) -> Result<(), DurableRunnerError> { + let mut index = self.index.borrow_mut(); + let index = index.as_mut().ok_or_else(|| { + DurableRunnerError::invalid("indexed runner must be opened before saving") + })?; + let same_epoch = state.run_id == index.epoch; + let mut receipts = Vec::new(); + if let Some(transition) = &state.last_command_epoch_transition { + let bytes = serde_json::to_vec(transition) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?; + receipts.push(ExactReceipt { + namespace: format!("command-epoch-transitions/{}", state.run_id), + key: transition.transition_id.clone(), + bytes: bytes.clone(), + }); + receipts.push(ExactReceipt { + namespace: format!("command-epochs/{}", state.run_id), + key: transition + .from_epoch + .clone() + .unwrap_or_else(|| "legacy".into()), + bytes, + }); + } + if let Some(t) = &state.last_event_epoch_transition { + let bytes = + serde_json::to_vec(t).map_err(|e| DurableRunnerError::invalid(e.to_string()))?; + receipts.push(ExactReceipt { + namespace: format!("event-epoch-transitions/{}", state.run_id), + key: t.transition_id.clone(), + bytes: bytes.clone(), + }); + receipts.push(ExactReceipt { + namespace: format!("event-epochs/{}", state.run_id), + key: t.from_epoch.clone().unwrap_or_else(|| "legacy".into()), + bytes, + }); + } + for event in &state.outbox { + if same_epoch + && state.source_epoch == index.source_epoch + && event.source_seq < index.next_source_seq + { + continue; + } + receipts.push(ExactReceipt { + namespace: state.outbox_namespace(), + key: format!("{:020}", event.source_seq), + bytes: serde_json::to_vec(event) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?, + }); + } + for result in state.processed_commands.values() { + if result.status == "pending" + || (same_epoch && index.completed_commands.contains(&result.command_id)) + { + continue; + } + let fingerprint = state + .processed_command_fingerprints + .get(&result.command_id) + .ok_or_else(|| { + DurableRunnerError::invalid("exact command fingerprint is missing") + })?; + receipts.push(ExactReceipt { + namespace: format!("commands/{}", state.run_id), + key: result.command_id.clone(), + bytes: serde_json::to_vec(&(fingerprint, result)) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?, + }); + } + for (id, receipt) in &state.executor_event_receipts { + if same_epoch + && state.source_epoch == index.source_epoch + && receipt.source_seq < index.next_source_seq + { + continue; + } + receipts.push(ExactReceipt { + namespace: format!("executor-events/{}", state.run_id), + key: id.clone(), + bytes: serde_json::to_vec(receipt) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?, + }); + } + let bytes = serde_json::to_vec(&RunnerMetadata(state)) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?; + match index + .store + .commit("runner", index.generation, bytes, receipts) + { + Ok(generation) => index.generation = generation, + Err(error) => { + index.generation = Revision::Fenced; + return Err(error); + } + } + index.epoch = state.run_id.clone(); + index.next_source_seq = state.next_source_seq; + index.source_epoch.clone_from(&state.source_epoch); + index.completed_commands = state + .processed_commands + .values() + .filter(|entry| entry.status != "pending") + .map(|entry| entry.command_id.clone()) + .collect(); + Ok(()) + } + pub fn path(&self) -> &Path { &self.path } @@ -1017,6 +1802,15 @@ impl DurableStateStore { &self, config: &DurableRunnerConfig, ) -> Result<(DurableState, bool), DurableRunnerError> { + crate::legacy_indexed_import::require_active_directory(&config.state_dir)?; + if self.indexed { + return self.load_indexed(config); + } + if fs::symlink_metadata(self.path.with_extension("sqlite")).is_ok() { + return Err(DurableRunnerError::invalid( + "indexed runner storage requires indexed invocation; legacy fallback is forbidden", + )); + } let mut bytes = Vec::new(); match open_private_regular_file(&self.path) { Ok(mut file) => { @@ -1075,6 +1869,10 @@ impl DurableStateStore { } pub fn save(&self, state: &DurableState) -> Result<(), DurableRunnerError> { + crate::legacy_indexed_import::require_active_directory(self.path.parent().unwrap())?; + if self.indexed { + return self.save_indexed(state); + } let bytes = serde_json::to_vec_pretty(state).map_err(|error| { DurableRunnerError::invalid(format!("failed to serialize durable state: {error}")) })?; @@ -1171,6 +1969,7 @@ fn validate_binding( transition.receipt.lease_id.clone(), transition.receipt.lease_expires_at_unix_ms, transition.receipt.lease_revocation_epoch, + transition.receipt.old_source_epoch.clone(), )?; if expected != transition.receipt || WarmRunIdentity::from_config(config) @@ -1180,7 +1979,8 @@ fn validate_binding( expected.new_identity } || (transition.phase == "prepared" - && (state.acked_source_seq != expected.old_acked_source_seq + && (state.source_epoch != expected.old_source_epoch + || state.acked_source_seq != expected.old_acked_source_seq || state.processed_commands.get(&transition.command.command_id) != Some(&transition.result))) { @@ -1204,6 +2004,11 @@ fn validate_binding( .pointer("/payload/sourceSeq") .and_then(Value::as_u64) != Some(event.source_seq) + || event + .envelope + .pointer("/payload/sourceEpoch") + .and_then(Value::as_str) + != state.source_epoch.as_deref() || event.priority > 2 { return Err(DurableRunnerError::invalid( @@ -1267,6 +2072,7 @@ fn validate_binding( .fingerprint .bytes() .all(|byte| byte.is_ascii_hexdigit()) + && receipt.source_epoch == state.source_epoch && receipt.source_seq > 0 && receipt.source_seq <= state.highest_source_seq() && executor_receipt_sequences.insert(receipt.source_seq) @@ -1344,7 +2150,36 @@ fn validate_binding( _ => false, }; - if state.next_source_seq == 0 + if state + .controller_epoch + .as_deref() + .is_some_and(|id| !valid_epoch(id)) + || match ( + &state.controller_epoch, + &state.last_command_epoch_transition, + ) { + (None, None) => false, + (Some(epoch), Some(transition)) => { + transition.validate(&state.run_id).is_err() || *epoch != transition.next_epoch + } + _ => true, + } + || state + .source_epoch + .as_deref() + .is_some_and(|id| !valid_epoch(id)) + || match (&state.source_epoch, &state.last_event_epoch_transition) { + (None, None) => false, + (Some(epoch), Some(t)) => t.validate(&state.run_id).is_err() || *epoch != t.next_epoch, + _ => true, + } + || state.event_epoch_transition.as_ref().is_some_and(|t| { + t.validate(&state.run_id).is_err() + || t.from_epoch != state.source_epoch + || t.final_ordinal != state.highest_source_seq() + }) + || state.next_source_seq == 0 + || state.next_source_seq > super::event_epochs::MAX_ORDINAL || state.acked_source_seq > state.highest_source_seq() || !outbox_cursors_are_valid || state @@ -1451,6 +2286,14 @@ pub(crate) fn create_private_temporary_file( match options.open(&temporary) { Ok(file) => return Ok((temporary, file)), Err(error) if error.kind() == io::ErrorKind::AlreadyExists => continue, + Err(error) + if matches!( + error.kind(), + io::ErrorKind::StorageFull | io::ErrorKind::QuotaExceeded + ) => + { + return Err(crate::indexed_store::storage_error(error)); + } Err(error) => { return Err(DurableRunnerError::invalid(format!( "failed to create private durable state temporary file: {error}" @@ -1741,6 +2584,10 @@ pub(crate) fn redact_text(input: &str) -> String { redacted } +pub(crate) fn redact_output_text(input: &str) -> String { + redact_sensitive_text_values(input) +} + fn redact_sensitive_text_values(input: &str) -> String { redact_sensitive_text_values_with_context(input, false) } @@ -2356,6 +3203,7 @@ mod tests { fn command(id: &str, sequence: u64) -> Command { Command { + controller_epoch: None, schema: "paperclip.prp.command.v1".to_owned(), command_id: id.to_owned(), controller_seq: sequence, @@ -2367,6 +3215,345 @@ mod tests { } } + #[test] + fn legacy_runner_staging_preserves_pending_and_exact_replay() { + let root = + std::env::temp_dir().join(format!("legacy-runner-import-{}", uuid::Uuid::new_v4())); + let source = root.join("source"); + let destination = root.join("prepared"); + let original_config = config(source.clone()); + let legacy = DurableStateStore::new(&source).unwrap(); + let (mut state, _) = legacy.load_or_create(&original_config).unwrap(); + let cmd = command("completed", 1); + state.begin_command(&cmd).unwrap(); + state + .complete_command(&cmd, json!({"original":true})) + .unwrap(); + state + .enqueue_executor_event( + &original_config, + "executor-first".into(), + "runner.test".into(), + EventPriority::P1, + json!({"n":1}), + ) + .unwrap(); + state.begin_command(&command("unfinished", 2)).unwrap(); + legacy.save(&state).unwrap(); + let original = fs::read(legacy.path()).unwrap(); + let proof = + DurableStateStore::stage_legacy(&original_config, &destination, "migration-1").unwrap(); + assert_eq!(proof.receipts, 3); + assert_eq!( + DurableStateStore::stage_legacy(&original_config, &destination, "migration-1").unwrap(), + proof + ); + assert_eq!(fs::read(legacy.path()).unwrap(), original); + assert!(DurableStateStore::stage_legacy(&original_config, &source, "migration-1").is_err()); + let recovered_store = DurableStateStore::new_indexed(&destination).unwrap(); + let (mut recovered, _) = recovered_store + .load_or_create(&config(destination.clone())) + .unwrap(); + assert_eq!(recovered.outbox, state.outbox); + assert_eq!( + recovered.processed_commands["unfinished"].status, + "indeterminate" + ); + assert!(matches!( + recovered.begin_command(&cmd).unwrap(), + CommandDisposition::Replay(_) + )); + let mut conflict = cmd; + conflict.payload = json!({"changed":true}); + assert!(recovered.begin_command(&conflict).is_err()); + drop(recovered); + drop(recovered_store); + drop(legacy); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn event_epochs_rotate_with_exact_replay_and_crashes_at_the_old_numeric_boundary() { + let directory = std::env::temp_dir().join(format!("event-epochs-{}", uuid::Uuid::new_v4())); + let config = config(directory.clone()); + let mut store = DurableStateStore::new_indexed(&directory).unwrap(); + let (mut state, _) = store.load_or_create(&config).unwrap(); + let boundary = super::super::event_epochs::MAX_ORDINAL - 1; + state.next_source_seq = boundary; + state.acked_source_seq = boundary - 1; + state.last_connection_protocol_version = Some(2); + store.save(&state).unwrap(); + let mut first_transition = None; + let mut original_event = None; + for round in 0..4 { + let payload = json!({"round":round}); + let ordinal = state + .enqueue_executor_event( + &config, + format!("executor-{round}"), + "harness.diagnostic".into(), + EventPriority::P1, + payload.clone(), + ) + .unwrap(); + store.save(&state).unwrap(); + if round == 0 { + original_event = Some(state.outbox[0].clone()); + assert_eq!(ordinal, boundary); + } + let transition = store.prepare_event_epoch(&mut state).unwrap(); + if round == 0 { + first_transition = Some(transition.clone()); + } + assert!(state + .enqueue_event(&config, "harness.diagnostic", EventPriority::P1, json!({})) + .is_err()); + assert!(store.commit_event_epoch(&mut state, &transition).is_err()); // outstanding ACK + drop(state); + drop(store); + store = DurableStateStore::new_indexed(&directory).unwrap(); + state = store.load_or_create(&config).unwrap().0; + assert_eq!(store.prepare_event_epoch(&mut state).unwrap(), transition); + state.apply_ack(transition.final_ordinal, 2).unwrap(); + let mut changed = transition.clone(); + changed.next_epoch = uuid::Uuid::new_v4().to_string(); + assert!(store.commit_event_epoch(&mut state, &changed).is_err()); + store.commit_event_epoch(&mut state, &transition).unwrap(); + drop(state); + drop(store); + store = DurableStateStore::new_indexed(&directory).unwrap(); + state = store.load_or_create(&config).unwrap().0; + store.commit_event_epoch(&mut state, &transition).unwrap(); + assert_eq!(state.next_source_seq, 1); + assert_eq!(state.acked_source_seq, 0); + assert!(state + .has_executor_event_receipt( + &format!("executor-{round}"), + "harness.diagnostic", + EventPriority::P1, + &payload + ) + .unwrap()); + assert!(!state.ack_is_current(&json!({"sourceEpoch":transition.from_epoch,"ackedSourceSeq":transition.final_ordinal})).unwrap()); + assert!(state + .ack_is_current(&json!({"sourceEpoch":transition.next_epoch,"ackedSourceSeq":0})) + .unwrap()); + } + let head = state.source_epoch.clone(); + store + .commit_event_epoch(&mut state, &first_transition.unwrap()) + .unwrap(); + assert_eq!(state.source_epoch, head); + let bytes = state + .indexed_receipts + .as_ref() + .unwrap() + .receipt( + &format!("outbox/{}", config.run_id), + &format!("{boundary:020}"), + ) + .unwrap() + .unwrap(); + assert_eq!( + serde_json::from_slice::(&bytes).unwrap(), + original_event.unwrap() + ); + drop(state); + drop(store); + fs::remove_dir_all(directory).unwrap(); + } + + #[test] + fn command_epochs_rotate_at_the_old_boundary_with_exact_replay_and_reopen() { + let directory = + std::env::temp_dir().join(format!("command-epochs-{}", uuid::Uuid::new_v4())); + let config = config(directory.clone()); + let mut store = DurableStateStore::new_indexed(&directory).unwrap(); + let (mut state, _) = store.load_or_create(&config).unwrap(); + let boundary = 9_007_199_254_740_990; + state.last_controller_command_seq = boundary - 1; + state.compacted_through_controller_seq = boundary - 1; + store.save(&state).unwrap(); + let first = command("earliest-command", boundary); + state.begin_command(&first).unwrap(); + store.save(&state).unwrap(); + let original = state + .complete_command(&first, json!({"original": true})) + .unwrap(); + store.save(&state).unwrap(); + let mut first_transition = None; + for index in 0..4 { + let transition = CommandEpochTransition { + schema: "paperclip.prp.command-epoch.v1".into(), + run_id: state.run_id.clone(), + transition_id: uuid::Uuid::new_v4().to_string(), + from_epoch: state.controller_epoch.clone(), + next_epoch: uuid::Uuid::new_v4().to_string(), + final_ordinal: state.last_controller_command_seq, + }; + if index == 0 { + first_transition = Some(transition.clone()); + } + let mut wrong = transition.clone(); + wrong.final_ordinal += 1; + assert!(store.rotate_command_epoch(&mut state, &wrong).is_err()); + let outbox = state.outbox.clone(); + let lifecycle = state.lifecycle.clone(); + store.rotate_command_epoch(&mut state, &transition).unwrap(); + assert_eq!(state.outbox, outbox); + assert_eq!(state.lifecycle, lifecycle); + assert_eq!(state.last_controller_command_seq, 0); + // Receiver crash after commit and before its reply: reopen and retry + // the identical intent, without creating another namespace. + drop(state); + drop(store); + store = DurableStateStore::new_indexed(&directory).unwrap(); + state = store.load_or_create(&config).unwrap().0; + store.rotate_command_epoch(&mut state, &transition).unwrap(); + assert_eq!( + state.controller_epoch.as_ref(), + Some(&transition.next_epoch) + ); + let mut fork = transition.clone(); + fork.next_epoch = uuid::Uuid::new_v4().to_string(); + assert!(store.rotate_command_epoch(&mut state, &fork).is_err()); + assert!( + matches!(state.begin_command(&first).unwrap(), CommandDisposition::Replay(ref value) if value == &original) + ); + let mut changed = first.clone(); + changed.payload = json!({"changed": true}); + assert!(state.begin_command(&changed).is_err()); + let mut next = command(&format!("fresh-{index}"), 1); + assert!(state.begin_command(&next).is_err()); // old namespace cannot execute a fresh ID + next.controller_epoch = state.controller_epoch.clone(); + assert!(matches!( + state.begin_command(&next).unwrap(), + CommandDisposition::Execute + )); + store.save(&state).unwrap(); + let blocked = CommandEpochTransition { + transition_id: uuid::Uuid::new_v4().to_string(), + from_epoch: state.controller_epoch.clone(), + next_epoch: uuid::Uuid::new_v4().to_string(), + final_ordinal: 1, + ..transition.clone() + }; + assert!(store.rotate_command_epoch(&mut state, &blocked).is_err()); + state + .complete_command(&next, json!({"index": index})) + .unwrap(); + assert!(store.rotate_command_epoch(&mut state, &blocked).is_err()); // no durable result yet + store.save(&state).unwrap(); + } + let current = state.controller_epoch.clone(); + let first_transition = first_transition.unwrap(); + let reused = CommandEpochTransition { + transition_id: uuid::Uuid::new_v4().to_string(), + from_epoch: current.clone(), + next_epoch: first_transition.next_epoch.clone(), + final_ordinal: state.last_controller_command_seq, + ..first_transition.clone() + }; + assert!(store.rotate_command_epoch(&mut state, &reused).is_err()); + store + .rotate_command_epoch(&mut state, &first_transition) + .unwrap(); + assert_eq!(state.controller_epoch, current); // ancient retry cannot move the head backwards + drop(state); + drop(store); + fs::remove_dir_all(directory).unwrap(); + } + + #[test] + fn indexed_runner_reopens_only_pending_outbox_and_replays_ancient_commands() { + let directory = + std::env::temp_dir().join(format!("paperclip-indexed-runner-{}", uuid::Uuid::new_v4())); + let config = config(directory.clone()); + { + let store = DurableStateStore::new_indexed(&directory).unwrap(); + let (mut state, recovered) = store.load_or_create(&config).unwrap(); + assert!(!recovered); + for sequence in 1..=1_000 { + let cmd = command(&format!("command-{sequence}"), sequence); + assert!(matches!( + state.begin_command(&cmd).unwrap(), + CommandDisposition::Execute + )); + store.save(&state).unwrap(); + state + .complete_command(&cmd, json!({"original":sequence})) + .unwrap(); + state + .enqueue_executor_event( + &config, + format!("executor-{sequence}"), + "runner.test".into(), + EventPriority::P1, + json!({"n":sequence}), + ) + .unwrap(); + store.save(&state).unwrap(); + if sequence < 1_000 { + state.apply_ack(sequence, PROTOCOL_VERSION).unwrap(); + store.save(&state).unwrap(); + } + } + assert!(state.processed_commands.len() <= MAX_RECENT_COMMANDS); + let snapshot = store + .index + .borrow() + .as_ref() + .unwrap() + .store + .read_state("runner") + .unwrap() + .unwrap(); + let metadata: Value = serde_json::from_slice(&snapshot.bytes).unwrap(); + assert!(metadata.get("outbox").is_none()); + assert!(snapshot.bytes.len() < 128 * 1024); + } + let store = DurableStateStore::new_indexed(&directory).unwrap(); + let (mut state, recovered) = store.load_or_create(&config).unwrap(); + assert!(recovered); + assert_eq!(state.outbox.len(), 1); + assert_eq!(state.outbox[0].source_seq, 1_000); + assert!(state + .has_executor_event_receipt( + "executor-1", + "runner.test", + EventPriority::P1, + &json!({"n":1}) + ) + .unwrap()); + assert!(state + .has_executor_event_receipt( + "executor-1", + "runner.test", + EventPriority::P1, + &json!({"n":2}) + ) + .is_err()); + match state.begin_command(&command("command-1", 1)).unwrap() { + CommandDisposition::Replay(result) => assert_eq!(result.result, json!({"original":1})), + _ => panic!("ancient exact receipt must replay"), + } + let mut changed = command("command-1", 1); + changed.payload = json!({"changed":true}); + assert!(state.begin_command(&changed).is_err()); + assert!(DurableStateStore::new(&directory) + .unwrap() + .load_or_create(&config) + .is_err()); + drop(state); + drop(store); + fs::remove_file(directory.join("runner-state.sqlite")).unwrap(); + assert!(DurableStateStore::new_indexed(&directory) + .unwrap() + .load_or_create(&config) + .is_err()); + fs::remove_dir_all(directory).unwrap(); + } + #[test] fn session_goal_commands_require_and_accept_the_v2_schema() { let mut goal = command("goal-command", 1); @@ -2413,6 +3600,7 @@ mod tests { "lease_exact".to_owned(), 1_800_000_000_000, 0, + None, ) .unwrap(); state.schema = TRANSITION_STATE_SCHEMA.to_owned(); diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/durable/storage_control.rs b/packages/paperclip-runner/runner/crates/runner-core/src/durable/storage_control.rs new file mode 100644 index 00000000000..70036e854a1 --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/durable/storage_control.rs @@ -0,0 +1,194 @@ +//! Capacity recovery retains the synchronous command/effect stack, while the +//! authenticated control lane remains live. Deferred messages are bounded and +//! replayed in order only after the original storage operation is durable. +use super::runner::{apply_lease_renewal, control_envelope, lease_renewal_deadline}; +use super::transport::{ + current_unix_ms, validate_control_identity, AuthenticatedTransport, ConnectionMetadata, + LeaseCredential, +}; +use super::{Command, DurableRunnerError, DurableState}; +use serde_json::json; +use std::{ + cell::RefCell, + rc::Rc, + time::{Duration, Instant}, +}; + +pub(super) type Lane = ( + AuthenticatedTransport, + ConnectionMetadata, + Option, + u64, +); +pub(super) fn run( + lane: Lane, + identity: DurableState, + renew: bool, + deadline: Option, + operation: impl FnOnce() -> Result, +) -> (Result, Lane) { + let context = Rc::new(RefCell::new(Some(lane))); + let callback_context = context.clone(); + let mut ack_cursor = identity.acked_source_seq; + let mut disconnected = false; + let result = crate::storage_capacity::with_wait( + move || { + let mut context = callback_context.borrow_mut(); + let (transport, connection, lease, next_renewal) = + context.as_mut().expect("live pressure control lane"); + let now = current_unix_ms()?; + if deadline.is_some_and(|deadline| Instant::now() >= deadline) + || now >= connection.expires_at_unix_ms + { + return Err(DurableRunnerError::invalid( + "execution or authenticated lease expired during storage pressure", + )); + } + let started = Instant::now(); + let polled = if disconnected { + Ok(()) + } else { + (|| { + if renew && now >= *next_renewal { + let credential = lease.as_mut().ok_or_else(|| { + DurableRunnerError::invalid("storage wait requires live lease") + })?; + credential.renewal_requested = true; + transport.send_json(&control_envelope( + &identity, + connection, + "lease_renew", + json!({ + "connectionLeaseExpiresAtUnixMs": connection.expires_at_unix_ms, + "connectionLeaseRevocationEpoch": connection.revocation_epoch, + }), + ))?; + *next_renewal = now.saturating_add( + 5_000.min( + connection + .expires_at_unix_ms + .saturating_sub(now) + .saturating_div(2) + .max(1), + ), + ); + } + if let Some(message) = transport.receive_control_during_storage_wait()? { + validate_control_identity(&message, &identity, Some(connection))?; + match message.get("kind").and_then(serde_json::Value::as_str) { + Some("lease_renewed") => { + apply_lease_renewal(&message, connection, lease.as_mut().ok_or_else(|| DurableRunnerError::invalid("lease renewal requires live credential"))?)?; + *next_renewal = lease_renewal_deadline(current_unix_ms()?, connection.expires_at_unix_ms); + } + Some("ping") => transport.send_json(&control_envelope(&identity, connection, "pong", json!({ + "lifecycle": identity.lifecycle, "ackedSourceSeq": identity.acked_source_seq, + "storagePressure": true, + })))?, + Some("revoke") => { + if message.pointer("/payload/revocationEpoch").and_then(serde_json::Value::as_u64).is_none_or(|epoch| epoch <= connection.revocation_epoch) { + return Err(DurableRunnerError::invalid("invalid revocation during storage pressure")); + } + return Err(DurableRunnerError::invalid("authenticated capability revoked during storage pressure")); + } + Some("command") => { + let command: Command = serde_json::from_value(message.get("payload").cloned().unwrap_or_default()).map_err(|error| DurableRunnerError::invalid(error.to_string()))?; + command.validate()?; + if fresh_stop(&command, identity.controller_epoch.as_deref(), identity.last_controller_command_seq) { + // This is an authenticated request to stop, not evidence + // that the command committed. Unwind to physical owned + // cleanup; never send a successful terminal receipt. + return Err(DurableRunnerError::invalid("authenticated stop requested during storage pressure")); + } + transport.defer_control(message)?; + } + Some("command_epoch_rotate") => { + // Rotation is durable work. Keep it behind the current + // storage operation; the normal loop validates capability + // and the exact current namespace before committing it. + let transition: super::command_epochs::CommandEpochTransition = serde_json::from_value(message.get("payload").cloned().unwrap_or_default()).map_err(|e| DurableRunnerError::invalid(e.to_string()))?; + transition.validate(&identity.run_id)?; + transport.defer_control(message)?; + } + Some("event_epoch_committed") => { + let transition: super::event_epochs::EventEpochTransition = serde_json::from_value(message.get("payload").cloned().unwrap_or_default()).map_err(|e| DurableRunnerError::invalid(e.to_string()))?; + transition.validate(&identity.run_id)?; + transport.defer_control(message)?; + } + Some("ack") => { + if !identity.ack_is_current(message.get("payload").unwrap_or(&serde_json::Value::Null))? { return Ok(()); } + let acked = message.pointer("/payload/ackedSourceSeq").and_then(serde_json::Value::as_u64).ok_or_else(|| DurableRunnerError::invalid("ACK cursor is required"))?; + if acked < ack_cursor || acked > identity.highest_source_seq() { + return Err(DurableRunnerError::invalid("cumulative ACK is outside the durable pressure snapshot")); + } + ack_cursor = acked; + transport.defer_control(message)?; + } + _ => return Err(DurableRunnerError::invalid("unexpected control during storage pressure")), + } + } + Ok(()) + })() + }; + match polled { + // Keep the exact write/effect stack while its existing lease is + // valid. After capacity returns, the ordinary runner loop + // reconnects and replays its durable result. A lost socket does + // not renew authority, suppress revocation, or admit new work. + Err(error) if error.is_transport_unavailable() => disconnected = true, + Err(error) => return Err(error), + Ok(()) => {} + } + // Busy ping/ACK traffic cannot turn failed writes into a spin loop. + std::thread::sleep(Duration::from_millis(25).saturating_sub(started.elapsed())); + Ok(()) + }, + operation, + ); + let lane = context + .borrow_mut() + .take() + .expect("pressure control lane restored"); + (result, lane) +} + +pub(super) fn fresh_stop(command: &Command, epoch: Option<&str>, ordinal: u64) -> bool { + command.controller_epoch.as_deref() == epoch + && command.controller_seq > ordinal + && matches!( + command.command_type.as_str(), + "turn.stop" + | "turn.interrupt" + | "run.cancel" + | "session.close" + | "session.destroy" + | "runner.shutdown" + | "runner.suspend" + ) +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn storage_pressure_stop_cannot_cross_a_command_epoch() { + let epoch = uuid::Uuid::new_v4().to_string(); + for kind in [ + "turn.stop", + "turn.interrupt", + "run.cancel", + "session.close", + "session.destroy", + "runner.shutdown", + "runner.suspend", + ] { + let mut command: Command = serde_json::from_value(json!({"schema":"paperclip.prp.command.v1", "commandId":"old-stop", "controllerSeq":9007199254740990_u64, "type":kind, "issuedAt":"2026-09-29T00:00:00Z", "payload":{}})).unwrap(); + assert!(!fresh_stop(&command, Some(&epoch), 1)); + command.controller_epoch = Some(epoch.clone()); + command.controller_seq = 2; + assert!(fresh_stop(&command, Some(&epoch), 1)); + assert!(!fresh_stop(&command, Some(&epoch), 2)); + command.controller_epoch = Some(uuid::Uuid::new_v4().to_string()); + assert!(!fresh_stop(&command, Some(&epoch), 1)); + } + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/durable/storage_pressure_tests.rs b/packages/paperclip-runner/runner/crates/runner-core/src/durable/storage_pressure_tests.rs new file mode 100644 index 00000000000..b1bfb87087f --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/durable/storage_pressure_tests.rs @@ -0,0 +1,448 @@ +// Included in transport's private test module to exercise the actual mutually +// authenticated, encrypted lane and normal durable runner command loop. +#[test] +fn storage_wait_distinguishes_socket_loss_from_invalid_protocol() { + assert!(map_websocket_error(tungstenite::Error::ConnectionClosed).is_transport_unavailable()); + assert!( + map_websocket_error(tungstenite::Error::Io(std::io::Error::from( + std::io::ErrorKind::BrokenPipe + ))) + .is_transport_unavailable() + ); + assert!(map_websocket_error(tungstenite::Error::Protocol( + tungstenite::error::ProtocolError::ResetWithoutClosingHandshake + )) + .is_transport_unavailable()); + assert!(!map_websocket_error(tungstenite::Error::Protocol( + tungstenite::error::ProtocolError::SendAfterClosing + )) + .is_transport_unavailable()); + assert!(!DurableRunnerError::invalid("authentication failed").is_transport_unavailable()); +} + +#[test] +fn storage_pressure_coalesces_ack_debt_without_reordering_commands() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let client = TcpStream::connect(listener.local_addr().unwrap()).unwrap(); + let (_server, _) = listener.accept().unwrap(); + let mut transport = AuthenticatedTransport { + socket: RunnerSocket::Listen(WebSocket::from_raw_socket( + client, + tungstenite::protocol::Role::Client, + None, + )), + secure_channel: SecureChannel::client(&[1; 32], b"challenge", b"server", b"client") + .unwrap(), + max_frame_bytes: 16 * 1024 * 1024, + deferred: Default::default(), + deferred_bytes: 0, + }; + for seq in 0..10_000 { + transport + .defer_control(json!({"kind":"ack","payload":{"ackedSourceSeq":seq}})) + .unwrap(); + } + let command = json!({"kind":"command","payload":{"commandId":"after-acks"}}); + transport.defer_control(command.clone()).unwrap(); + for seq in 10_000..20_000 { + transport + .defer_control(json!({"kind":"ack","payload":{"ackedSourceSeq":seq}})) + .unwrap(); + } + assert_eq!(transport.deferred.len(), 3); + assert!(transport + .defer_control(json!({"kind":"ack","payload":{"ackedSourceSeq":7}})) + .is_err()); + assert_eq!( + transport.receive_json().unwrap().unwrap()["payload"]["ackedSourceSeq"], + 9_999 + ); + assert_eq!(transport.receive_json().unwrap().unwrap(), command); + assert_eq!( + transport.receive_json().unwrap().unwrap()["payload"]["ackedSourceSeq"], + 19_999 + ); + assert_eq!(transport.deferred_bytes, 0); + for _ in 0..128 { + transport.defer_control(command.clone()).unwrap(); + } + assert!(transport.defer_control(command).is_err()); + assert_eq!(transport.deferred.len(), 128); +} + +#[test] +fn storage_pressure_recovers_without_reexecuting_effects_or_losing_the_lease() { + storage_pressure_transport_case(None, false, None); +} + +#[test] +fn storage_pressure_revocation_stops_owned_execution_while_sqlite_remains_full() { + storage_pressure_transport_case(Some("revoke"), false, None); +} + +#[test] +fn storage_pressure_stop_is_responsive_without_a_false_terminal_receipt() { + storage_pressure_transport_case(Some("stop"), false, None); +} + +#[test] +fn storage_pressure_recovers_a_command_delivered_in_the_welcome() { + storage_pressure_transport_case(None, true, None); +} + +#[test] +fn storage_pressure_recovers_after_a_socket_disconnect_without_repeating_the_effect() { + storage_pressure_transport_case(None, false, Some(false)); +} + +#[test] +fn storage_pressure_disconnect_cannot_extend_the_authenticated_lease() { + storage_pressure_transport_case(None, false, Some(true)); +} + +fn storage_pressure_transport_case( + cancel: Option<&'static str>, + pending_welcome: bool, + disconnected_expiry: Option, +) { + use crate::indexed_store::{ExactReceipt, IndexedStore}; + fn accept_before(listener: &TcpListener, deadline: std::time::Instant) -> TcpStream { + listener.set_nonblocking(true).unwrap(); + loop { + match listener.accept() { + Ok((stream, _)) => { + stream.set_nonblocking(false).unwrap(); + return stream; + } + Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => { + assert!( + std::time::Instant::now() < deadline, + "runner did not reconnect before the test deadline" + ); + thread::sleep(Duration::from_millis(10)); + } + Err(error) => panic!("controller accept failed: {error}"), + } + } + } + fn receive_before( + socket: &mut WebSocket, + secure: &mut SecureChannel, + config: &DurableRunnerConfig, + deadline: std::time::Instant, + ) -> Value { + // Runtime storage waits use the test's execution deadline, not the + // transport helper's short authentication deadline. Parallel fsync + // qualifications can legitimately exceed the latter after admission. + socket + .get_mut() + .set_read_timeout(Some(Duration::from_secs(1))) + .unwrap(); + loop { + if let Some(frame) = + receive_plain_optional_until(socket, config.max_frame_bytes, Some(deadline)) + .unwrap() + { + return secure.decrypt(&frame, true).unwrap(); + } + } + } + struct Executor { + store: IndexedStore, + effects: Arc, + shutdowns: Arc, + } + impl super::super::CommandExecutor for Executor { + fn execute( + &mut self, + command: &Command, + ) -> Result { + if command.command_type == "session.open" { + self.effects.fetch_add(1, Ordering::SeqCst); + self.store.commit( + "provider", + 1, + b"completed".to_vec(), + vec![ExactReceipt { + namespace: "effects".into(), + key: "one-effect".into(), + bytes: vec![b'x'; 2 * 1024 * 1024], + }], + )?; + } + Ok(super::super::CommandExecution::result( + json!({"status":"completed"}), + )) + } + fn shutdown(&mut self) -> Result<(), DurableRunnerError> { + self.shutdowns.fetch_add(1, Ordering::SeqCst); + Ok(()) + } + } + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let mut cfg = config(listener.local_addr().unwrap().port()); + cfg.max_runtime = Duration::from_secs(90); + let directory = + std::env::temp_dir().join(format!("paperclip-capacity-{}", uuid::Uuid::new_v4())); + cfg.state_dir = directory.join("runner"); + let store = IndexedStore::open( + &directory.join("provider/state.sqlite"), + "pressure-test", + true, + ) + .unwrap(); + store + .commit("provider", 0, b"before".to_vec(), vec![]) + .unwrap(); + store.test_capacity(true); + let server_store = store.clone(); + let server_cfg = cfg.clone(); + let state = test_state(&cfg); + let server = thread::spawn(move || { + let deadline = std::time::Instant::now() + server_cfg.max_runtime; + let stream = accept_before(&listener, deadline); + stream + .set_read_timeout(Some(Duration::from_secs(15))) + .unwrap(); + let mut socket = accept(stream).unwrap(); + let mut connection_id = "connection_1"; + // Keep the intentional expiry case short. Recovery cases leave time + // for a real FULL-synchronous commit under the full suite's I/O load. + let expires = current_unix_ms().unwrap() + + if disconnected_expiry == Some(true) { + 4_000 + } else { + 30_000 + }; + let mut secure = server_authenticate( + &mut socket, + &server_cfg, + &state, + ServerCredential { + token: "bootstrap-secret", + kind: "bootstrap", + lease_id: None, + expires_at_unix_ms: expires, + revocation_epoch: 0, + }, + ); + let open = json!({ + "schema":"paperclip.prp.command.v1", "commandId":"command_open", "controllerSeq":1, + "type":"session.open", "issuedAt":"2026-09-28T00:00:00Z", "payload":{}, + }); + let mut greeting = welcome( + &state, + connection_id, + Some("lease-secret"), + expires, + 0, + if pending_welcome { + vec![open.clone()] + } else { + vec![] + }, + ); + greeting["payload"]["connectionLeaseRenewalVersion"] = json!(1); + send_secure(&mut socket, &mut secure, &server_cfg, &greeting); + if !pending_welcome { + send_secure( + &mut socket, + &mut secure, + &server_cfg, + &control(&state, connection_id, "command", open.clone()), + ); + } + send_secure( + &mut socket, + &mut secure, + &server_cfg, + &control(&state, connection_id, "ack", json!({"ackedSourceSeq":0})), + ); + send_secure( + &mut socket, + &mut secure, + &server_cfg, + &control(&state, connection_id, "ping", json!({})), + ); + let mut pong = false; + loop { + let message = receive_before(&mut socket, &mut secure, &server_cfg, deadline); + match message["kind"].as_str().unwrap() { + "pong" => { + assert_eq!(message["payload"]["storagePressure"], true); + pong = true; + if let Some(expire) = disconnected_expiry { + drop(socket); + if expire { + return; + } + thread::sleep(Duration::from_millis(200)); + server_store.test_capacity(false); + let stream = accept_before(&listener, deadline); + stream + .set_read_timeout(Some(Duration::from_secs(15))) + .unwrap(); + socket = accept(stream).unwrap(); + secure = server_authenticate( + &mut socket, + &server_cfg, + &state, + ServerCredential { + token: "lease-secret", + kind: "lease", + lease_id: Some("lease_1"), + expires_at_unix_ms: expires, + revocation_epoch: 1, + }, + ); + connection_id = "connection_2"; + send_secure( + &mut socket, + &mut secure, + &server_cfg, + &welcome(&state, connection_id, None, expires, 0, vec![open.clone()]), + ); + continue; + } + if let Some(cancel) = cancel { + let message = if cancel == "revoke" { + control( + &state, + connection_id, + "revoke", + json!({"revocationEpoch":2}), + ) + } else { + control( + &state, + connection_id, + "command", + json!({ + "schema":"paperclip.prp.command.v1", "commandId":"command_stop", "controllerSeq":2, + "type":"turn.stop", "issuedAt":"2026-09-28T00:00:01Z", "payload":{}, + }), + ) + }; + send_secure(&mut socket, &mut secure, &server_cfg, &message); + return; + } + } + "lease_renew" => { + assert!(pong, "the ping must remain responsive before renewal"); + send_secure( + &mut socket, + &mut secure, + &server_cfg, + &control( + &state, + connection_id, + "lease_renewed", + json!({ + "previousExpiresAtUnixMs":expires, "connectionLeaseExpiresAtUnixMs":current_unix_ms().unwrap()+60_000, + "connectionLeaseRevocationEpoch":1, + }), + ), + ); + server_store.test_capacity(false); + } + "command_result" => { + assert!(pong); + assert_eq!(message["payload"]["commandId"], "command_open"); + assert_eq!(message["payload"]["status"], "completed"); + break; + } + other => panic!("unexpected pressure response: {other}"), + } + } + send_secure( + &mut socket, + &mut secure, + &server_cfg, + &control( + &state, + connection_id, + "command", + json!({ + "schema":"paperclip.prp.command.v1", "commandId":"command_shutdown", "controllerSeq":2, + "type":"runner.shutdown", "issuedAt":"2026-09-28T00:00:01Z", "payload":{}, + }), + ), + ); + let message = receive_before(&mut socket, &mut secure, &server_cfg, deadline); + assert_eq!(message["payload"]["commandId"], "command_shutdown"); + send_secure( + &mut socket, + &mut secure, + &server_cfg, + &control( + &state, + connection_id, + "command_result_ack", + json!({ + "commandId":"command_shutdown", "commandType":"runner.shutdown", "controllerSeq":2, "status":"completed", + }), + ), + ); + }); + let effects = Arc::new(AtomicUsize::new(0)); + let shutdowns = Arc::new(AtomicUsize::new(0)); + let result = super::super::run_indexed_durable_runner( + cfg.clone(), + BootstrapTicket::new("bootstrap-secret".into()).unwrap(), + Executor { + store: store.clone(), + effects: effects.clone(), + shutdowns: shutdowns.clone(), + }, + ); + if let Err(error) = &result { + eprintln!("pressure case cancel={cancel:?} welcome={pending_welcome} disconnected_expiry={disconnected_expiry:?}: {error}"); + } + assert!( + server.join().is_ok(), + "pressure controller failed; runner result: {result:?}" + ); + assert_eq!(effects.load(Ordering::SeqCst), 1); + assert_eq!(shutdowns.load(Ordering::SeqCst), 1); + let current = store.read_state("provider").unwrap().unwrap(); + if cancel.is_some() || disconnected_expiry == Some(true) { + let error = result.unwrap_err(); + assert!(error.is_storage_wait_cancelled()); + assert!( + error.to_string().contains(if cancel == Some("revoke") { + "capability revoked" + } else if cancel == Some("stop") { + "stop requested" + } else { + "lease expired" + }), + "{error}" + ); + assert_eq!(current.generation, 1); + assert_eq!(current.bytes, b"before"); + assert!(store.receipt("effects", "one-effect").unwrap().is_none()); + // The effect happened before capacity failed. A stopped runner cannot + // label it completed or execute it again when it later reopens. + let runner = super::super::DurableStateStore::new_indexed(&cfg.state_dir).unwrap(); + let (mut recovered, existed) = runner.load_or_create(&cfg).unwrap(); + assert!(existed); + let open: Command = serde_json::from_value(json!({ + "schema":"paperclip.prp.command.v1", "commandId":"command_open", "controllerSeq":1, + "type":"session.open", "issuedAt":"2026-09-28T00:00:00Z", "payload":{}, + })) + .unwrap(); + assert!( + matches!(recovered.begin_command(&open).unwrap(), super::super::CommandDisposition::Replay(result) if result.status == "indeterminate") + ); + } else { + result.unwrap(); + assert_eq!(current.generation, 2); + assert_eq!(current.bytes, b"completed"); + assert_eq!( + store.receipt("effects", "one-effect").unwrap().unwrap(), + vec![b'x'; 2 * 1024 * 1024] + ); + } + drop(store); + std::fs::remove_dir_all(directory).unwrap(); +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/durable/transport.rs b/packages/paperclip-runner/runner/crates/runner-core/src/durable/transport.rs index cc4b9d79c14..155acb12c59 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/durable/transport.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/durable/transport.rs @@ -25,6 +25,9 @@ use super::{ }; const SECURE_FRAME_SCHEMA: &str = "paperclip.runner.secure-frame.v1"; +// Reconnect with fresh challenge-derived keys before a connection-local nonce +// counter can become a lifetime limit or exceed the exact JSON representation. +const MAX_SECURE_CHANNEL_FRAMES: u64 = 1_048_576; const AUTH_TIMEOUT: Duration = Duration::from_secs(2); const WELCOME_TIMEOUT: Duration = Duration::from_secs(2); const RUNTIME_READ_TIMEOUT: Duration = Duration::from_millis(250); @@ -735,6 +738,9 @@ pub(crate) struct Welcome { pub(crate) acked_source_seq: Option, pub(crate) pending_commands: Vec, pub(crate) warm_transition_version: Option, + pub(crate) command_epochs: bool, + pub(crate) event_epoch_limit: Option, + pub(crate) source_epoch: Option, pub(crate) lease_renewal_version: Option, pub(crate) warm_transition: Option, pub(crate) warm_transition_phase: Option, @@ -826,6 +832,9 @@ impl SecureChannel { server_direction: bool, ) -> Result { let counter = self.send_counter; + if counter >= MAX_SECURE_CHANNEL_FRAMES { + return Err(DurableRunnerError::invalid("secure channel rekey required")); + } let direction = if server_direction { b"P3S1" } else { b"P3C1" }; let label = if server_direction { "core_to_client" @@ -860,6 +869,9 @@ impl SecureChannel { frame: &Value, server_direction: bool, ) -> Result { + if self.receive_counter >= MAX_SECURE_CHANNEL_FRAMES { + return Err(DurableRunnerError::invalid("secure channel rekey required")); + } if frame.get("schema").and_then(Value::as_str) != Some(SECURE_FRAME_SCHEMA) { return Err(DurableRunnerError::invalid( "unauthenticated plaintext control frame was rejected", @@ -911,6 +923,8 @@ pub(crate) struct AuthenticatedTransport { socket: RunnerSocket, secure_channel: SecureChannel, max_frame_bytes: usize, + deferred: std::collections::VecDeque<(Value, usize)>, + deferred_bytes: usize, } #[derive(Debug)] @@ -998,6 +1012,8 @@ impl AuthenticatedTransport { "protocolMin": PROTOCOL_MIN_VERSION, "protocolMax": PROTOCOL_VERSION, "warmTransitionVersion": 1, + "durability": "durability.indexed_state.v1", + "outputBodies": crate::output_body::CAPABILITY, "runnerInstanceId": state.runner_instance_id, "environmentLeaseId": state.environment_lease_id, "runId": state.run_id, @@ -1013,6 +1029,14 @@ impl AuthenticatedTransport { }, }, }); + if state.indexed_durability() { + hello["payload"]["commandEpochs"] = json!(super::command_epochs::CAPABILITY); + hello["payload"]["eventEpochs"] = json!(super::event_epochs::CAPABILITY); + hello["payload"]["eventResume"] = state.event_resume(); + } + if let Some(epoch) = &state.controller_epoch { + hello["payload"]["resume"]["controllerEpoch"] = json!(epoch); + } if let Some(transition) = &state.warm_transition { hello["payload"]["warmTransitionId"] = json!(transition.receipt.transition_id); } @@ -1088,6 +1112,8 @@ impl AuthenticatedTransport { socket, secure_channel, max_frame_bytes: config.max_frame_bytes, + deferred: Default::default(), + deferred_bytes: 0, }; // Ticket validation and lease persistence happen before the server // emits welcome. Keep that work bounded without assuming a loaded @@ -1113,6 +1139,20 @@ impl AuthenticatedTransport { expected_lease, challenge.selected_version, )?; + if welcome.command_epochs + != (challenge.command_epochs.as_deref() == Some(super::command_epochs::CAPABILITY)) + { + return Err(DurableRunnerError::invalid( + "welcome changed the command epoch capability", + )); + } + if welcome.event_epoch_limit != challenge.event_epoch_limit + || welcome.source_epoch != state.source_epoch + { + return Err(DurableRunnerError::invalid( + "welcome changed the event epoch contract", + )); + } // Authentication can wait longer for control-plane validation, but // the steady-state runner loop must return to provider polling // promptly when no control message is available. @@ -1142,9 +1182,99 @@ impl AuthenticatedTransport { } pub(crate) fn receive_json(&mut self) -> Result, DurableRunnerError> { + if let Some((value, bytes)) = self.deferred.pop_front() { + self.deferred_bytes -= bytes; + return Ok(Some(value)); + } + self.receive_control_during_storage_wait() + } + + pub(crate) fn receive_control_during_storage_wait( + &mut self, + ) -> Result, DurableRunnerError> { self.receive_json_until(None) } + pub(crate) fn receive_control_during_epoch( + &mut self, + ) -> Result, DurableRunnerError> { + if let Some(index) = self.deferred.iter().position(|(value, _)| { + !matches!( + value.get("kind").and_then(Value::as_str), + Some("command" | "command_epoch_rotate") + ) + }) { + let (value, bytes) = self + .deferred + .remove(index) + .expect("located deferred epoch control"); + self.deferred_bytes -= bytes; + return Ok(Some(value)); + } + self.receive_json_until(None) + } + + pub(crate) fn defer_control(&mut self, value: Value) -> Result<(), DurableRunnerError> { + let bytes = serde_json::to_vec(&value) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))? + .len(); + // Authenticated cumulative ACKs with no intervening command can be + // collapsed. A large sent prefix must not fill the control backlog + // simply because its receipts arrive while local storage is full. + if value.get("kind").and_then(Value::as_str) == Some("ack") { + let next = value + .pointer("/payload/ackedSourceSeq") + .and_then(Value::as_u64); + let previous = self.deferred.iter().rev().find_map(|(previous, _)| { + (previous.get("kind").and_then(Value::as_str) == Some("ack") + && previous.pointer("/payload/sourceEpoch") + == value.pointer("/payload/sourceEpoch")) + .then(|| { + previous + .pointer("/payload/ackedSourceSeq") + .and_then(Value::as_u64) + }) + .flatten() + }); + if next.is_none() || previous.zip(next).is_some_and(|(old, new)| new < old) { + return Err(DurableRunnerError::invalid( + "cumulative ACK regressed in storage control backlog", + )); + } + if let Some((previous, previous_bytes)) = self.deferred.back_mut() { + if previous.get("kind").and_then(Value::as_str) == Some("ack") + && previous.pointer("/payload/sourceEpoch") + == value.pointer("/payload/sourceEpoch") + && value + .pointer("/payload/ackedSourceSeq") + .and_then(Value::as_u64) + .zip( + previous + .pointer("/payload/ackedSourceSeq") + .and_then(Value::as_u64), + ) + .is_some_and(|(new, old)| new >= old) + && self.deferred_bytes - *previous_bytes + bytes <= 32 * 1024 * 1024 + { + self.deferred_bytes = self.deferred_bytes - *previous_bytes + bytes; + *previous = value; + *previous_bytes = bytes; + return Ok(()); + } + } + } + if self.deferred.len() >= 128 + || self.deferred_bytes.saturating_add(bytes) > 32 * 1024 * 1024 + { + return Err(DurableRunnerError::invalid( + "storage pressure control backlog is full", + )); + } + self.deferred.push_back((value, bytes)); + self.deferred_bytes += bytes; + Ok(()) + } + fn receive_json_until( &mut self, deadline: Option, @@ -1182,6 +1312,20 @@ struct AuthChallenge { #[serde(default)] warm_transition_version: Option, #[serde(default)] + durability: Option, + #[serde(default)] + output_bodies: Option, + #[serde(default)] + command_epochs: Option, + #[serde(default)] + event_epochs: Option, + #[serde(default)] + event_resume: Option, + #[serde(default)] + event_epoch_limit: Option, + #[serde(default)] + command_resume: Option, + #[serde(default)] warm_transition_id: Option, } @@ -1249,6 +1393,55 @@ fn validate_challenge( "authentication challenge is expired or selected an unsupported protocol", )); } + if state.indexed_durability() + && (challenge.durability.as_deref() != Some("durability.indexed_state.v1") + || challenge.output_bodies.as_deref() != Some(crate::output_body::CAPABILITY)) + { + return Err(DurableRunnerError::invalid( + "indexed durability capability was not authenticated", + )); + } + if challenge + .command_epochs + .as_deref() + .is_some_and(|v| v != super::command_epochs::CAPABILITY) + || (state.controller_epoch.is_some() + && challenge.command_epochs.as_deref() != Some(super::command_epochs::CAPABILITY)) + { + return Err(DurableRunnerError::invalid( + "command epoch capability was not authenticated", + )); + } + if challenge.command_epochs.is_some() + && challenge.command_resume + != Some(json!({ + "controllerEpoch": state.controller_epoch, "lastControllerCommandSeq": state.last_controller_command_seq, + })) + { + return Err(DurableRunnerError::invalid( + "authentication challenge changed the command resume cursor", + )); + } + if challenge + .event_epochs + .as_deref() + .is_some_and(|v| v != super::event_epochs::CAPABILITY) + || ((state.source_epoch.is_some() || state.event_epoch_transition.is_some()) + && challenge.event_epochs.is_none()) + || (challenge.event_epochs.is_some() + && (challenge.selected_version < 2 + || !state.indexed_durability() + || challenge.event_resume != Some(state.event_resume()) + || challenge.event_epoch_limit.is_none_or(|limit| { + !(4..=super::event_epochs::DEFAULT_LIMIT).contains(&limit) + }))) + || (challenge.event_epochs.is_none() + && (challenge.event_resume.is_some() || challenge.event_epoch_limit.is_some())) + { + return Err(DurableRunnerError::invalid( + "event epoch contract was not authenticated exactly", + )); + } if state.warm_transition.as_ref().is_some_and(|transition| { challenge.warm_transition_version != Some(1) || challenge.warm_transition_id.as_deref() @@ -1296,6 +1489,27 @@ fn challenge_signing_bytes(challenge: &AuthChallenge) -> Vec { "credentialExpiresAtUnixMs": challenge.credential_expires_at_unix_ms, "revocationEpoch": challenge.revocation_epoch, }); + if let Some(capability) = &challenge.event_epochs { + body["eventEpochs"] = json!(capability); + } + if let Some(resume) = &challenge.event_resume { + body["eventResume"] = resume.clone(); + } + if let Some(limit) = challenge.event_epoch_limit { + body["eventEpochLimit"] = json!(limit); + } + if let Some(resume) = &challenge.command_resume { + body["commandResume"] = resume.clone(); + } + if let Some(capability) = &challenge.command_epochs { + body["commandEpochs"] = json!(capability); + } + if let Some(durability) = &challenge.durability { + body["durability"] = json!(durability); + } + if let Some(capability) = &challenge.output_bodies { + body["outputBodies"] = json!(capability); + } if let Some(version) = challenge.warm_transition_version { body["warmTransitionVersion"] = json!(version); } @@ -1429,8 +1643,21 @@ fn validate_welcome( }, lease, acked_source_seq: payload.get("ackedSourceSeq").and_then(Value::as_u64), + source_epoch: payload + .get("sourceEpoch") + .and_then(Value::as_str) + .map(str::to_owned), + event_epoch_limit: if payload.get("eventEpochs").and_then(Value::as_str) + == Some(super::event_epochs::CAPABILITY) + { + payload.get("eventEpochLimit").and_then(Value::as_u64) + } else { + None + }, pending_commands, warm_transition_version: payload.get("warmTransitionVersion").and_then(Value::as_u64), + command_epochs: payload.get("commandEpochs").and_then(Value::as_str) + == Some(super::command_epochs::CAPABILITY), lease_renewal_version: payload .get("connectionLeaseRenewalVersion") .and_then(Value::as_u64), @@ -1594,7 +1821,7 @@ fn receive_plain_optional_until( .map_err(map_websocket_error)?, Ok(Message::Pong(_)) => {} Ok(Message::Close(_)) => { - return Err(DurableRunnerError::invalid( + return Err(DurableRunnerError::transport_unavailable( "WebSocket peer closed the connection", )) } @@ -1617,7 +1844,28 @@ fn receive_plain_optional_until( } fn map_websocket_error(error: tungstenite::Error) -> DurableRunnerError { - DurableRunnerError::invalid(format!("WebSocket transport failed: {error}")) + let disconnected = match &error { + tungstenite::Error::ConnectionClosed + | tungstenite::Error::AlreadyClosed + | tungstenite::Error::Protocol( + tungstenite::error::ProtocolError::ResetWithoutClosingHandshake, + ) => true, + tungstenite::Error::Io(error) => matches!( + error.kind(), + io::ErrorKind::ConnectionReset + | io::ErrorKind::ConnectionAborted + | io::ErrorKind::BrokenPipe + | io::ErrorKind::UnexpectedEof + | io::ErrorKind::NotConnected + ), + _ => false, + }; + let message = format!("WebSocket transport failed: {error}"); + if disconnected { + DurableRunnerError::transport_unavailable(message) + } else { + DurableRunnerError::invalid(message) + } } fn random_nonce() -> Result { @@ -1710,6 +1958,7 @@ fn hex_nibble(byte: u8) -> Result { #[cfg(test)] mod tests { + include!("storage_pressure_tests.rs"); use std::net::{IpAddr, Ipv4Addr, TcpListener}; use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -1788,6 +2037,13 @@ mod tests { revocation_epoch: server_credential.revocation_epoch, server_proof: String::new(), warm_transition_version: None, + command_epochs: None, + event_epochs: None, + event_resume: None, + event_epoch_limit: None, + command_resume: None, + durability: Some("durability.indexed_state.v1".into()), + output_bodies: Some(crate::output_body::CAPABILITY.into()), warm_transition_id: None, }; let signing = challenge_signing_bytes(&challenge); @@ -1821,6 +2077,8 @@ mod tests { "credentialLeaseId": &challenge.credential_lease_id, "revocationEpoch": challenge.revocation_epoch, "serverProof": &challenge.server_proof, + "durability": &challenge.durability, + "outputBodies": &challenge.output_bodies, }, }), config.max_frame_bytes, @@ -2265,6 +2523,36 @@ mod tests { assert!(server.decrypt(&json!({"kind": "command"}), true).is_err()); } + #[test] + fn secure_channel_rekeys_before_counter_exhaustion_and_rejects_old_ciphertext() { + let key = [7_u8; 32]; + let mut client = SecureChannel::client(&key, b"challenge", b"server", b"client").unwrap(); + let mut server = SecureChannel::server(&key, b"challenge", b"server", b"client").unwrap(); + let old_frame = client.encrypt(br#"{"old":true}"#, false).unwrap(); + client.send_counter = MAX_SECURE_CHANNEL_FRAMES - 1; + server.receive_counter = MAX_SECURE_CHANNEL_FRAMES - 1; + let last = client.encrypt(br#"{"last":true}"#, false).unwrap(); + assert_eq!(server.decrypt(&last, true).unwrap(), json!({"last": true})); + assert!(client + .encrypt(br#"{}"#, false) + .unwrap_err() + .to_string() + .contains("rekey required")); + assert!(server + .decrypt(&last, true) + .unwrap_err() + .to_string() + .contains("rekey required")); + let mut client = + SecureChannel::client(&key, b"new-challenge", b"new-server", b"client").unwrap(); + let mut server = + SecureChannel::server(&key, b"new-challenge", b"new-server", b"client").unwrap(); + assert!(server.decrypt(&old_frame, true).is_err()); + let new = client.encrypt(br#"{"new":true}"#, false).unwrap(); + assert_eq!(new["counter"], 0); + assert_eq!(server.decrypt(&new, true).unwrap(), json!({"new": true})); + } + #[test] fn control_identity_mismatch_fails_closed() { let config = config(3000); @@ -2318,6 +2606,13 @@ mod tests { revocation_epoch: 0, server_proof: String::new(), warm_transition_version: None, + command_epochs: None, + event_epochs: None, + event_resume: None, + event_epoch_limit: None, + command_resume: None, + durability: None, + output_bodies: None, warm_transition_id: None, }; let signing = challenge_signing_bytes(&challenge); diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/indexed_archive.rs b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_archive.rs new file mode 100644 index 00000000000..262e710776c --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_archive.rs @@ -0,0 +1,624 @@ +//! Bounded cold-epoch transfer. The durable intent fences new connections and +//! a lifetime lock excludes every live runner/maintenance/backup connection. +use crate::durable::{open_private_regular_file, verify_private_directory, DurableRunnerError}; +use crate::indexed_lifetime::{archive_fence, StoreLifetime}; +use crate::indexed_revision::Revision; +use crate::indexed_store::storage_error; +use rusqlite::{Connection, OpenFlags, OptionalExtension}; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use std::{ + fs, + io::{Read, Write}, + path::{Path, PathBuf}, +}; + +type Result = std::result::Result; +const LOCATOR: &str = "runner-state.json"; +const FILES: [&str; 6] = [ + "runner-state.sqlite", + "runner-state.sqlite-wal", + "runner-state.sqlite-shm", + "runner-state.sqlite.receipts", + "runner-state.sqlite.routing", + "runner-state.json", +]; +#[derive(Deserialize, Serialize, PartialEq, Debug)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct Transfer { + schema: String, + generation: String, + state_sha256: String, + files: Vec, +} +fn invalid(message: &str) -> DurableRunnerError { + DurableRunnerError::invalid(message) +} +fn present(path: &Path) -> Result { + match fs::symlink_metadata(path) { + Ok(_) => Ok(true), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false), + Err(e) => Err(storage_error(e)), + } +} +fn sync(path: &Path) -> Result<()> { + fs::File::open(path) + .and_then(|f| f.sync_all()) + .map_err(storage_error) +} +fn read_json(path: &Path) -> Result { + let file = open_private_regular_file(path).map_err(storage_error)?; + let mut bytes = Vec::new(); + file.take(4097) + .read_to_end(&mut bytes) + .map_err(storage_error)?; + if bytes.len() > 4096 { + return Err(invalid("native_indexed_archive_oversized")); + } + serde_json::from_slice(&bytes).map_err(storage_error) +} +fn publish(path: &Path, value: &impl Serialize) -> Result<()> { + let temporary = path.with_extension(format!("{}.tmp", uuid::Uuid::new_v4())); + let mut options = fs::OpenOptions::new(); + options.write(true).create_new(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + options.mode(0o600); + } + let mut file = options.open(&temporary).map_err(storage_error)?; + let result = (|| { + file.write_all(&serde_json::to_vec(value).map_err(storage_error)?) + .map_err(storage_error)?; + file.sync_all().map_err(storage_error)?; + // A conflicting owner cannot replace either a file or a dangling link. + fs::hard_link(&temporary, path).map_err(storage_error)?; + sync(path.parent().unwrap()) + })(); + let _ = fs::remove_file(temporary); + result +} +fn verify_file(path: &Path) -> Result<()> { + if path.file_name().is_some_and(|name| { + name == "runner-state.sqlite.receipts" || name == "runner-state.sqlite.routing" + }) { + verify_private_directory(path) + } else { + open_private_regular_file(path) + .map(|_| ()) + .map_err(storage_error) + } +} +fn database(directory: &Path) -> Result { + for file in &FILES[..3] { + let path = directory.join(file); + if *file == FILES[0] || present(&path)? { + verify_file(&path)?; + } + } + let db = Connection::open_with_flags( + directory.join(FILES[0]), + OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_NOFOLLOW, + ) + .map_err(storage_error)?; + db.execute_batch("PRAGMA query_only=ON; PRAGMA trusted_schema=OFF; PRAGMA cache_size=-4096; PRAGMA busy_timeout=5000;").map_err(storage_error)?; + Ok(db) +} +fn verify_snapshot( + directory: &Path, + generation: &str, + digest: &str, + allow_backup: bool, +) -> Result<()> { + let locator: serde_json::Value = read_json(&directory.join("runner-state.json"))?; + if locator["schema"] != "paperclip.runner.durable.state.indexed.v1" { + return Err(invalid("native_indexed_archive_locator_invalid")); + } + let db = database(directory)?; + let binding: (i64, String) = db + .query_row( + "SELECT schema_version,binding FROM store_binding WHERE singleton=1", + [], + |r| Ok((r.get(0)?, r.get(1)?)), + ) + .map_err(storage_error)?; + if ![1, 2].contains(&binding.0) || locator["binding"].as_str() != Some(&binding.1) { + return Err(invalid("native_indexed_archive_binding_changed")); + } + let has_routing_tree: bool = db.query_row("SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE type='table' AND name='receipt_routing_heads')", [], |r| r.get(0)).map_err(storage_error)?; + if has_routing_tree { + verify_private_directory(&directory.join("runner-state.sqlite.routing"))?; + } + crate::indexed_partitions::validate_backup(&db)?; + if !allow_backup + && db + .query_row( + "SELECT 1 FROM sqlite_master WHERE type='table' AND name='receipt_backup'", + [], + |_| Ok(()), + ) + .optional() + .map_err(storage_error)? + .is_some() + && db + .query_row("SELECT 1 FROM receipt_backup LIMIT 1", [], |_| Ok(())) + .optional() + .map_err(storage_error)? + .is_some() + { + return Err(invalid( + "storage_pressure: finish the indexed backup before archiving its source", + )); + } + let (stored_generation, bytes, stored_digest): (Revision, Vec, Vec) = db.query_row("SELECT generation,CASE WHEN length(bytes)<=33554432 THEN bytes END,CASE WHEN length(digest)=32 THEN digest END FROM current_state WHERE key='runner'", [], |r| Ok((r.get(0)?,r.get(1)?,r.get(2)?))).map_err(storage_error)?; + let actual = Sha256::digest(&bytes); + let actual_hex: String = actual.iter().map(|byte| format!("{byte:02x}")).collect(); + if stored_generation.to_string() != generation + || stored_digest != actual.as_slice() + || actual_hex != digest + { + return Err(invalid("native_indexed_archive_changed")); + } + let state: serde_json::Value = serde_json::from_slice(&bytes).map_err(storage_error)?; + if state["lifecycle"] != "suspended" { + return Err(invalid("native_indexed_archive_unsettled")); + } + Ok(()) +} +fn transfer(archive: &Path) -> Result> { + let path = archive.join("runner-transfer.json"); + if !present(&path)? { + return Ok(None); + } + let value: Transfer = read_json(&path)?; + if value.schema != "paperclip.runner.indexed-archive.v1" + || value + .generation + .parse::() + .ok() + .filter(|n| *n != Revision::Absent) + .map(|n| n.to_string()) + .as_ref() + != Some(&value.generation) + || value.state_sha256.len() != 64 + || !value + .state_sha256 + .bytes() + .all(|c| c.is_ascii_digit() || (b'a'..=b'f').contains(&c)) + || !value.files.iter().any(|f| f == FILES[0]) + || !value.files.iter().any(|f| f == LOCATOR) + || value.files.len() > FILES.len() + || value + .files + .iter() + .enumerate() + .any(|(i, f)| !FILES.contains(&f.as_str()) || value.files[..i].contains(f)) + { + return Err(invalid("native_indexed_archive_invalid")); + } + Ok(Some(value)) +} +fn fence(runner: &Path, archive: &Path) -> Result { + let path = archive_fence(&runner.join(FILES[0])); + let value = serde_json::json!({"schema":"paperclip.runner.archive-fence.v1","archive":archive}); + if present(&path)? { + if read_json::(&path)? != value { + return Err(invalid("native_indexed_archive_conflict")); + } + } else { + publish(&path, &value)?; + } + Ok(path) +} + +/// Caller has already proven process-tree death and controller ownership. This +/// independently excludes storage operators and checks the exact current row. +pub fn prepare(runner: &Path, archive: &Path, generation: &str, digest: &str) -> Result<()> { + verify_private_directory(runner)?; + verify_private_directory(archive)?; + let runner = runner.canonicalize().map_err(storage_error)?; + let archive = archive.canonicalize().map_err(storage_error)?; + let _lifetime = StoreLifetime::exclusive(&runner.join(FILES[0]))?; + let _operation = crate::indexed_partitions::PartitionLock::acquire(&runner.join(FILES[0]))?; + verify_snapshot(&runner, generation, digest, false)?; + let mut files = Vec::new(); + for name in FILES { + let path = runner.join(name); + if present(&path)? { + verify_file(&path)?; + files.push(name.to_owned()); + } + } + let intent = Transfer { + schema: "paperclip.runner.indexed-archive.v1".into(), + generation: generation.into(), + state_sha256: digest.into(), + files, + }; + if let Some(existing) = transfer(&archive)? { + if existing != intent { + return Err(invalid("native_indexed_archive_changed")); + } + } else { + publish(&archive.join("runner-transfer.json"), &intent)?; + } + fence(&runner, &archive)?; + Ok(()) +} +fn rename_new(source: &Path, target: &Path) -> Result<()> { + #[cfg(any(target_os = "linux", target_os = "android", target_vendor = "apple"))] + { + rustix::fs::renameat_with( + rustix::fs::CWD, + source, + rustix::fs::CWD, + target, + rustix::fs::RenameFlags::NOREPLACE, + ) + .map_err(storage_error) + } + #[cfg(not(any(target_os = "linux", target_os = "android", target_vendor = "apple")))] + { + let _ = (source, target); + Err(invalid("indexed archive is unqualified on this platform")) + } +} +pub fn finish(runner: &Path, archive: &Path) -> Result { + verify_private_directory(runner)?; + verify_private_directory(archive)?; + let runner = runner.canonicalize().map_err(storage_error)?; + let archive = archive.canonicalize().map_err(storage_error)?; + let Some(intent) = transfer(&archive)? else { + return Ok(false); + }; + if !present(&archive.join("control-plane"))? { + return Err(invalid("native_indexed_archive_controller_missing")); + } + verify_private_directory(&archive.join("control-plane"))?; + let _source_lifetime = StoreLifetime::exclusive(&runner.join(FILES[0]))?; + let _target_lifetime = StoreLifetime::exclusive(&archive.join(FILES[0]))?; + let _operation = crate::indexed_partitions::PartitionLock::acquire(&runner.join(FILES[0]))?; + let published = present(&archive.join(LOCATOR))?; + if published { + for name in &intent.files { + if present(&runner.join(name))? { + return Err(invalid("native_indexed_archive_conflict")); + } + } + } + let marker = fence(&runner, &archive)?; + if !published { + for name in FILES + .into_iter() + .filter(|name| intent.files.iter().any(|f| f == name)) + { + let source = runner.join(name); + let target = archive.join(name); + if present(&source)? { + verify_file(&source)?; + rename_new(&source, &target)?; + sync(&runner)?; + sync(&archive)?; + } else { + verify_file(&target)?; + } + } + } + verify_snapshot(&archive, &intent.generation, &intent.state_sha256, false)?; + fs::remove_file(marker).map_err(storage_error)?; + sync(&runner)?; + Ok(true) +} + +/// Transfer a standalone controller while excluding its storage subprocess and +/// online backup. The directory rename is atomic; a retry observes the archived +/// locator. PostgreSQL locators have no local database and are moved by the host. +pub fn controller(source: &Path, target: &Path, expected_digest: &str) -> Result<()> { + verify_private_directory(source)?; + verify_private_directory( + source + .parent() + .ok_or_else(|| invalid("controller parent missing"))?, + )?; + verify_private_directory( + target + .parent() + .ok_or_else(|| invalid("archive parent missing"))?, + )?; + let source = source.canonicalize().map_err(storage_error)?; + let target = target + .parent() + .unwrap() + .canonicalize() + .map_err(storage_error)? + .join( + target + .file_name() + .ok_or_else(|| invalid("archive filename missing"))?, + ); + let locator: serde_json::Value = read_json(&source.join("control-plane-state.json"))?; + let file = locator["location"]["file"] + .as_str() + .ok_or_else(|| invalid("controller locator missing"))?; + if locator["schema"] != "paperclip.runner.authority-locator.v1" + || locator["location"]["kind"] != "sqlite" + || !file.ends_with(".sqlite") + || file.is_empty() + || !file + .bytes() + .all(|c| c.is_ascii_alphanumeric() || b"_.-".contains(&c)) + { + return Err(invalid("controller archive locator invalid")); + } + let path = source.join(file); + let _lifetime = StoreLifetime::exclusive(&path)?; + let _operation = crate::indexed_partitions::PartitionLock::acquire(&path)?; + for suffix in ["", "-wal", "-shm"] { + let sidecar = PathBuf::from(format!("{}{suffix}", path.display())); + if suffix.is_empty() || present(&sidecar)? { + crate::indexed_store::verify_sqlite_file(&sidecar).map_err(storage_error)?; + } + } + let db = Connection::open_with_flags( + &path, + OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_NOFOLLOW, + ) + .map_err(storage_error)?; + db.execute_batch("PRAGMA query_only=ON; PRAGMA trusted_schema=OFF; PRAGMA cache_size=-4096;") + .map_err(storage_error)?; + let native = db + .query_row( + "SELECT 1 FROM sqlite_master WHERE type='table' AND name='store_binding'", + [], + |_| Ok(()), + ) + .optional() + .map_err(storage_error)? + .is_some(); + let (binding, bytes, digest): (String, Vec, Vec) = if native { + crate::indexed_partitions::validate_backup(&db)?; + if db + .query_row("SELECT 1 FROM receipt_backup LIMIT 1", [], |_| Ok(())) + .optional() + .map_err(storage_error)? + .is_some() + { + return Err(invalid( + "storage_pressure: finish the controller backup before archiving", + )); + } + db.query_row("SELECT binding,CASE WHEN length(bytes)<=16777216 THEN bytes END,digest FROM store_binding,current_state WHERE singleton=1 AND key='authority'", [], |r| Ok((r.get(0)?,r.get(1)?,r.get(2)?))).map_err(storage_error)? + } else { + let (binding,body,digest):(String,String,String)=db.query_row("SELECT binding,CASE WHEN length(body)<=16777216 THEN body END,digest FROM authority_binding,authority_state WHERE authority_binding.singleton=1 AND authority_state.singleton=1", [], |r| Ok((r.get(0)?,r.get(1)?,r.get(2)?))).map_err(storage_error)?; + let actual = format!("{:x}", Sha256::digest(body.as_bytes())); + if digest != actual { + return Err(invalid("controller archive digest mismatch")); + } + ( + binding, + body.as_bytes().to_vec(), + Sha256::digest(body.as_bytes()).to_vec(), + ) + }; + if locator["location"]["binding"].as_str() != Some(&binding) + || Sha256::digest(&bytes).as_slice() != digest + || format!("{:x}", Sha256::digest(&bytes)) != expected_digest + { + return Err(invalid("controller archive authority changed")); + } + drop(db); + rename_new(&source, &target)?; + sync(source.parent().unwrap())?; + sync(target.parent().unwrap()) +} + +/// The server proves process-tree death; this fence also excludes operator +/// connections and file transfer while sealing the exact empty checkpoint. +pub fn seal(runner: &Path, expected_generation: &str) -> Result<()> { + verify_private_directory(runner)?; + let runner = runner.canonicalize().map_err(storage_error)?; + crate::legacy_indexed_import::require_active_directory(&runner)?; + let path = runner.join(FILES[0]); + let _lifetime = StoreLifetime::exclusive(&path)?; + if present(&archive_fence(&path))? { + return Err(invalid( + "storage_pressure: indexed archive must finish before sealing", + )); + } + let _operation = crate::indexed_partitions::PartitionLock::acquire(&path)?; + let locator: serde_json::Value = read_json(&runner.join(LOCATOR))?; + if locator["schema"] != "paperclip.runner.durable.state.indexed.v1" { + return Err(invalid("indexed_state_locator_invalid")); + } + for name in &FILES[..3] { + let file = runner.join(name); + if *name == FILES[0] || present(&file)? { + verify_file(&file)?; + } + } + let mut db = Connection::open_with_flags( + &path, + OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_NOFOLLOW, + ) + .map_err(storage_error)?; + db.execute_batch("PRAGMA trusted_schema=OFF; PRAGMA synchronous=FULL; PRAGMA cache_size=-4096; PRAGMA busy_timeout=5000;").map_err(storage_error)?; + let tx = db + .transaction_with_behavior(rusqlite::TransactionBehavior::Immediate) + .map_err(storage_error)?; + let binding: (i64, String) = tx + .query_row( + "SELECT schema_version,binding FROM store_binding WHERE singleton=1", + [], + |r| Ok((r.get(0)?, r.get(1)?)), + ) + .map_err(storage_error)?; + if ![1, 2].contains(&binding.0) || locator["binding"].as_str() != Some(&binding.1) { + return Err(invalid("indexed_state_binding_mismatch")); + } + crate::indexed_partitions::validate_backup(&tx) + .map_err(|_| invalid("indexed_state_backup_incomplete"))?; + let (generation,bytes,digest):(Revision,Vec,Vec)=tx.query_row("SELECT generation,CASE WHEN length(bytes)<=33554432 THEN bytes END,CASE WHEN length(digest)=32 THEN digest END FROM current_state WHERE key='runner'",[],|r|Ok((r.get(0)?,r.get(1)?,r.get(2)?))).map_err(storage_error)?; + if Sha256::digest(&bytes).as_slice() != digest { + return Err(invalid("indexed_state_digest_mismatch")); + } + let mut state: serde_json::Value = serde_json::from_slice(&bytes).map_err(storage_error)?; + let next = state["nextSourceSeq"].as_u64(); + let acked = state["ackedSourceSeq"].as_u64(); + if generation.to_string() != expected_generation + || generation == Revision::Absent + || ![Some("ready"), Some("suspended")].contains(&state["lifecycle"].as_str()) + || next.is_none() + || next.unwrap() > 9_007_199_254_740_991 + || acked.and_then(|n| n.checked_add(1)) != next + || !state["pendingTerminalDelivery"].is_null() + || !state["pendingProviderCleanup"].is_null() + { + return Err(invalid("indexed_state_seal_stale_or_unsettled")); + } + if binding.0 == 2 { + let pending:Option<(Revision,Vec,Vec)>=tx.query_row("SELECT generation,CASE WHEN length(body)<=33554432 THEN body END,digest FROM receipt_prepare WHERE state_key='runner'",[],|r|Ok((r.get(0)?,r.get(1)?,r.get(2)?))).optional().map_err(storage_error)?; + if pending.is_some_and(|row| row != (generation, bytes.clone(), digest.clone())) { + return Err(invalid("indexed_state_prepared_commit_mismatch")); + } + } + state["lifecycle"] = serde_json::json!("suspended"); + let body = serde_json::to_vec(&state).map_err(storage_error)?; + if body.len() > 33_554_432 { + return Err(invalid("indexed_state_snapshot_invalid")); + } + let digest = Sha256::digest(&body).to_vec(); + let next_revision = generation.next()?; + tx.execute( + "UPDATE current_state SET bytes=?1,digest=?2,generation=?3 WHERE key='runner'", + rusqlite::params![body, digest, next_revision], + ) + .map_err(storage_error)?; + if binding.0 == 2 { + tx.execute( + "UPDATE receipt_prepare SET body=?1,digest=?2,generation=?3 WHERE state_key='runner'", + rusqlite::params![body, digest, next_revision], + ) + .map_err(storage_error)?; + } + tx.commit().map_err(storage_error) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::indexed_store::IndexedStore; + #[test] + fn pending_session_migration_fences_sealing_before_storage_is_opened() { + let root = + std::env::temp_dir().join(format!("indexed-seal-migration-{}", uuid::Uuid::new_v4())); + let runner = root.join("runner"); + fs::create_dir_all(&runner).unwrap(); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + for path in [&root, &runner] { + fs::set_permissions(path, fs::Permissions::from_mode(0o700)).unwrap(); + } + } + publish( + &root.join("indexed-migration.json"), + &serde_json::json!({ + "schema": "paperclip.runner.legacy-activation.v1", "phase": "activating" + }), + ) + .unwrap(); + assert!(seal(&runner, "1") + .unwrap_err() + .to_string() + .contains("migration is pending")); + assert_eq!(fs::read_dir(&runner).unwrap().count(), 0); + fs::remove_dir_all(root).unwrap(); + } + #[test] + fn active_connections_and_interrupted_backups_block_archive_without_moving_history() { + for opaque in [false, true] { + let root = + std::env::temp_dir().join(format!("indexed-archive-{}", uuid::Uuid::new_v4())); + fs::create_dir(&root).unwrap(); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + fs::set_permissions(&root, fs::Permissions::from_mode(0o700)).unwrap(); + } + let archive = root.join("archive"); + fs::create_dir(&archive).unwrap(); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + fs::set_permissions(&archive, fs::Permissions::from_mode(0o700)).unwrap(); + } + let mut store = IndexedStore::open(&root.join(FILES[0]), "runner/test", true).unwrap(); + let bytes = br#"{"lifecycle":"suspended"}"#; + let mut generation = store + .commit("runner", 0, bytes.to_vec(), vec![]) + .unwrap() + .to_string(); + if opaque { + drop(store); + let db = Connection::open(root.join(FILES[0])).unwrap(); + db.execute( + "UPDATE current_state SET generation=?1 WHERE key='runner'", + [i64::MAX], + ) + .unwrap(); + drop(db); + store = IndexedStore::open(&root.join(FILES[0]), "runner/test", false).unwrap(); + generation = store + .commit("runner", i64::MAX as u64, bytes.to_vec(), vec![]) + .unwrap() + .to_string(); + assert!(generation.starts_with("r:")); + } + publish(&root.join(LOCATOR),&serde_json::json!({"schema":"paperclip.runner.durable.state.indexed.v1","binding":"runner/test"})).unwrap(); + let digest: String = Sha256::digest(bytes) + .iter() + .map(|b| format!("{b:02x}")) + .collect(); + assert!(prepare(&root, &archive, &generation, &digest) + .unwrap_err() + .to_string() + .contains("open connection")); + store.begin_backup(&root.join("backup")).unwrap(); + drop(store); + assert!(prepare(&root, &archive, &generation, &digest) + .unwrap_err() + .to_string() + .contains("finish the indexed backup")); + let store = IndexedStore::open(&root.join(FILES[0]), "runner/test", false).unwrap(); + while !store.backup_step().unwrap() {} + drop(store); + prepare(&root, &archive, &generation, &digest).unwrap(); + assert!( + IndexedStore::open(&root.join(FILES[0]), "runner/test", false) + .unwrap_err() + .to_string() + .contains("archive must finish") + ); + assert!(!archive.join(FILES[0]).exists()); + fs::create_dir(archive.join("control-plane")).unwrap(); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + fs::set_permissions( + archive.join("control-plane"), + fs::Permissions::from_mode(0o700), + ) + .unwrap(); + } + // A crash between root and companion/locator movement rolls forward. + rename_new(&root.join(FILES[0]), &archive.join(FILES[0])).unwrap(); + assert!(finish(&root, &archive).unwrap()); + assert!(finish(&root, &archive).unwrap()); + let archived = + IndexedStore::open(&archive.join(FILES[0]), "runner/test", false).unwrap(); + assert_eq!(archived.read_state("runner").unwrap().unwrap().bytes, bytes); + drop(archived); + fs::remove_dir_all(root).unwrap(); + } + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/indexed_backup.rs b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_backup.rs new file mode 100644 index 00000000000..543982ffdf2 --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_backup.rs @@ -0,0 +1,627 @@ +//! Online, resumable snapshots. A short root snapshot pins immutable source +//! partitions; bounded copies select only the insertion ordinals present at +//! that snapshot. Appends, splits and relocation continue between copy steps. +//! The destination is published only after every range verifies exactly. +use super::*; + +struct Backup { + id: String, + destination: PathBuf, + staging: PathBuf, + phase: String, + filename: String, +} +fn valid_filename(filename: &str) -> bool { + filename.len() <= 120 + && filename.ends_with(".sqlite") + && !filename.starts_with('.') + && filename + .bytes() + .all(|b| b.is_ascii_alphanumeric() || b"-_.".contains(&b)) +} +fn job(db: &Connection) -> Result> { + db.query_row( + "SELECT id,destination,staging,phase,COALESCE((SELECT filename FROM receipt_backup_filename WHERE singleton=1),'authority.sqlite') FROM receipt_backup WHERE singleton=1", + [], + |r| { + Ok(Backup { + id: r.get(0)?, + destination: PathBuf::from(r.get::<_, String>(1)?), + staging: PathBuf::from(r.get::<_, String>(2)?), + phase: r.get(3)?, + filename: r.get(4)?, + }) + }, + ) + .optional() + .map_err(error).and_then(|job| { + if job.as_ref().is_some_and(|job| !valid_filename(&job.filename)) { + return Err(invalid("invalid backup database filename")); + } + Ok(job) + }) +} +fn private_directory(path: &Path) -> Result<()> { + let mut builder = fs::DirBuilder::new(); + #[cfg(unix)] + { + use std::os::unix::fs::DirBuilderExt; + builder.mode(0o700); + } + builder.create(path).map_err(error)?; + sync_directory(path.parent().unwrap()) +} +fn sync_directory(path: &Path) -> Result<()> { + fs::File::open(path) + .and_then(|f| f.sync_all()) + .map_err(error) +} +fn publish_directory(source: &Path, destination: &Path) -> Result<()> { + // The earlier existence check is only diagnostic. Publication itself must + // refuse a directory/symlink created by another owner during the copy. + #[cfg(any(target_os = "linux", target_os = "android", target_vendor = "apple"))] + { + rustix::fs::renameat_with( + rustix::fs::CWD, + source, + rustix::fs::CWD, + destination, + rustix::fs::RenameFlags::NOREPLACE, + ) + .map_err(error) + } + #[cfg(not(any(target_os = "linux", target_os = "android", target_vendor = "apple")))] + { + let _ = (source, destination); + Err(invalid( + "atomic backup publication is unqualified on this platform", + )) + } +} +fn create_file(path: &Path) -> Result<()> { + let mut options = fs::OpenOptions::new(); + options.create_new(true).write(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + options.mode(0o600); + } + options.open(path).and_then(|f| f.sync_all()).map_err(error) +} +fn root(path: &Path) -> Result { + crate::durable::verify_private_directory(path.parent().unwrap())?; + for suffix in ["", "-wal", "-shm"] { + match crate::indexed_store::verify_sqlite_file(Path::new(&format!( + "{}{suffix}", + path.display() + ))) { + Ok(_) => {} + Err(e) if !suffix.is_empty() && e.kind() == std::io::ErrorKind::NotFound => {} + Err(e) => return Err(error(e)), + } + } + let db = Connection::open_with_flags( + path, + rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE | rusqlite::OpenFlags::SQLITE_OPEN_NOFOLLOW, + ) + .map_err(error)?; + db.execute_batch("PRAGMA journal_mode=WAL; PRAGMA synchronous=FULL; PRAGMA trusted_schema=OFF; PRAGMA cache_size=-4096;").map_err(error)?; + Ok(db) +} +/// Called before any activated store can serve a snapshot. +pub(crate) fn validate_backup(db: &Connection) -> Result<()> { + let marker: Option = db + .query_row( + "SELECT 1 FROM sqlite_master WHERE type='table' AND name='backup_manifest'", + [], + |r| r.get(0), + ) + .optional() + .map_err(error)?; + if marker.is_some() + && db + .query_row( + "SELECT complete FROM backup_manifest WHERE singleton=1", + [], + |r| r.get::<_, i64>(0), + ) + .map_err(error)? + != 1 + { + return Err(invalid( + "incomplete backup cannot serve execution authority", + )); + } + Ok(()) +} + +pub(crate) fn begin_backup(db: &mut Connection, destination: &Path, filename: &str) -> Result<()> { + if !valid_filename(filename) { + return Err(invalid("invalid backup database filename")); + } + let parent = destination + .parent() + .ok_or_else(|| invalid("backup has no parent"))?; + crate::durable::verify_private_directory(parent)?; + let destination = parent.canonicalize().map_err(error)?.join( + destination + .file_name() + .ok_or_else(|| invalid("backup has no filename"))?, + ); + if let Some(active) = job(db)? { + if active.destination != destination || active.filename != filename { + return Err(invalid("storage_pressure: another backup is active")); + } + return Ok(()); + } + if destination.try_exists().map_err(error)? { + return Err(invalid("backup destination already exists")); + } + let id = uuid::Uuid::new_v4().to_string(); + let staging = parent + .canonicalize() + .map_err(error)? + .join(format!(".receipt-backup-{id}")); + private_directory(&staging)?; + let tx = db + .transaction_with_behavior(TransactionBehavior::Immediate) + .map_err(error)?; + tx.execute( + "INSERT INTO receipt_backup VALUES(1,?1,?2,?3,'preparing')", + params![id, destination.to_str(), staging.to_str()], + ) + .map_err(error)?; + tx.execute( + "INSERT OR REPLACE INTO receipt_backup_filename VALUES(1,?1)", + [filename], + ) + .map_err(error)?; + tx.commit().map_err(error)?; + Ok(()) +} + +fn prepare(db: &mut Connection, job: &Backup) -> Result<()> { + crate::durable::verify_private_directory(&job.staging)?; + let path = job.staging.join(&job.filename); + // Preparing has not published a snapshot. Restart only this owned temporary + // root; no receipt files have been copied yet and no destination is visible. + for suffix in ["-wal", "-shm", ""] { + let file = PathBuf::from(format!("{}{suffix}", path.display())); + match crate::indexed_store::verify_sqlite_file(&file) { + Ok(_) => fs::remove_file(file).map_err(error)?, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} + Err(e) => return Err(error(e)), + } + } + create_file(&path)?; + // Only bounded current/redo state and two routing pointers live here. The + // immutable tree root is the snapshot inventory; no O(H) pin/backlog copy + // happens while the operation fence is held. + db.execute("VACUUM INTO ?1", [path.to_str()]) + .map_err(error)?; + let target = root(&path)?; + // A backup of a restored store may already contain a completed marker. + target.execute_batch("BEGIN IMMEDIATE; + DROP TABLE IF EXISTS backup_manifest; + DROP TABLE IF EXISTS backup_copy; + CREATE TABLE backup_manifest(singleton INTEGER PRIMARY KEY CHECK(singleton=1), id TEXT NOT NULL, complete INTEGER NOT NULL CHECK(complete IN(0,1))); + CREATE TABLE backup_copy(singleton INTEGER PRIMARY KEY CHECK(singleton=1), after_lower TEXT, lower_key TEXT, source TEXT, target TEXT, records INTEGER, bytes INTEGER, fingerprint BLOB, cursor INTEGER NOT NULL DEFAULT 0, verifying INTEGER NOT NULL DEFAULT 0, verified_after TEXT); + INSERT INTO backup_copy(singleton) VALUES(1); + DELETE FROM receipt_migration; + DELETE FROM receipt_backup; DELETE FROM receipt_backup_filename; + COMMIT;").map_err(error)?; + routes::rebase_snapshot(&target, db)?; + target + .execute("INSERT INTO backup_manifest VALUES(1,?1,0)", [&job.id]) + .map_err(error)?; + target + .execute_batch("PRAGMA wal_checkpoint(TRUNCATE)") + .map_err(error)?; + drop(target); + crate::durable::open_private_regular_file(&path) + .and_then(|f| f.sync_all()) + .map_err(error)?; + sync_directory(&job.staging)?; + db.execute( + "UPDATE receipt_backup SET phase='copying' WHERE singleton=1", + [], + ) + .map_err(error)?; + Ok(()) +} + +fn release(db: &mut Connection, job: &Backup) -> Result { + let target = root(&job.destination.join(&job.filename))?; + let marker: (String, i64) = target + .query_row( + "SELECT id,complete FROM backup_manifest WHERE singleton=1", + [], + |r| Ok((r.get(0)?, r.get(1)?)), + ) + .map_err(error)?; + if marker != (job.id.clone(), 1) { + return Err(invalid("published backup identity differs")); + } + sync_directory(job.destination.parent().unwrap())?; + db.execute_batch( + "BEGIN IMMEDIATE; DELETE FROM receipt_backup; DELETE FROM receipt_backup_filename; COMMIT;", + ) + .map_err(error)?; + Ok(true) +} + +/// Returns true only after an independently openable snapshot is published. +/// The caller yields its operation fence after each <=128-record/4-MiB page. +pub(crate) fn backup_step(db: &mut Connection) -> Result { + let Some(job) = job(db)? else { + return Ok(true); + }; + if job.destination.try_exists().map_err(error)? { + return release(db, &job); + } + if job.phase == "preparing" { + prepare(db, &job)?; + return Ok(false); + } + if job.phase != "copying" { + return Err(invalid("invalid backup phase")); + } + let path = job.staging.join(&job.filename); + let mut target_root = root(&path)?; + let marker: (String, i64) = target_root + .query_row( + "SELECT id,complete FROM backup_manifest WHERE singleton=1", + [], + |r| Ok((r.get(0)?, r.get(1)?)), + ) + .map_err(error)?; + if marker.0 != job.id { + return Err(invalid("backup identity differs")); + } + if marker.1 == 1 { + target_root + .execute_batch("PRAGMA wal_checkpoint(TRUNCATE)") + .map_err(error)?; + drop(target_root); + crate::durable::open_private_regular_file(&path) + .and_then(|f| f.sync_all()) + .map_err(error)?; + sync_directory(&job.staging)?; + publish_directory(&job.staging, &job.destination)?; + return release(db, &job); + } + let (verifying, verified_after): (bool, Option) = target_root + .query_row( + "SELECT verifying,verified_after FROM backup_copy WHERE singleton=1", + [], + |r| Ok((r.get(0)?, r.get(1)?)), + ) + .map_err(error)?; + if !verifying { + type CopyRow = (String, String, Option, i64, i64, Vec, i64); + let active: bool = target_root + .query_row( + "SELECT lower_key IS NOT NULL FROM backup_copy WHERE singleton=1", + [], + |r| r.get(0), + ) + .map_err(error)?; + if !active { + let after: Option = target_root + .query_row( + "SELECT after_lower FROM backup_copy WHERE singleton=1", + [], + |r| r.get(0), + ) + .map_err(error)?; + if let Some(row) = routes::next(&target_root, "routes", after.as_deref())? { + target_root.execute("UPDATE backup_copy SET lower_key=?1,source=?2,target=NULL,records=?3,bytes=?4,fingerprint=?5,cursor=0 WHERE singleton=1", + params![row.lower, row.file, row.records, row.bytes, row.fingerprint]).map_err(error)?; + } + } + let next: Option = target_root.query_row("SELECT lower_key,source,target,records,bytes,CASE WHEN length(fingerprint)=32 THEN fingerprint END,cursor FROM backup_copy WHERE lower_key IS NOT NULL AND singleton=1", [], |r| Ok((r.get(0)?,r.get(1)?,r.get(2)?,r.get(3)?,r.get(4)?,r.get(5)?,r.get(6)?))).optional().map_err(error)?; + if let Some((lower, source, target, count, size, sha, cursor)) = next { + if count < 0 || cursor < 0 || cursor > count { + return Err(invalid("invalid backup cursor")); + } + let target = match target { + Some(file) => file, + None => { + let file = local_name(&path, &new_shard(&directory(&path)?)?)?; + target_root + .execute( + "UPDATE backup_copy SET target=?2 WHERE lower_key=?1 AND singleton=1", + params![lower, file], + ) + .map_err(error)?; + file + } + }; + // Resolve source using the live root, including relocated private volumes. + let source_db = open_shard(&resolve_file(db, &source)?, false)?; + let mut query=source_db.prepare("SELECT ordinal,namespace,receipt_key,CASE WHEN length(body)<=33554432 THEN body END,CASE WHEN length(digest)=32 THEN digest END FROM receipt_rows WHERE ordinal>?1 AND ordinal<=?2 ORDER BY ordinal LIMIT 128").map_err(error)?; + let mut rows = query.query(params![cursor, count]).map_err(error)?; + let mut batch = Vec::new(); + let mut bytes = 0; + let mut after = cursor; + while let Some(row) = rows.next().map_err(error)? { + let ordinal: i64 = row.get(0).map_err(error)?; + let body: Vec = row.get(3).map_err(error)?; + let digest: Vec = row.get(4).map_err(error)?; + verify(&body, &digest)?; + if !batch.is_empty() && bytes + body.len() > PAGE_BYTES { + break; + } + if ordinal != after + 1 { + return Err(invalid("backup source ordinal gap")); + } + after = ordinal; + bytes += body.len(); + batch.push(ExactReceipt { + namespace: row.get(1).map_err(error)?, + key: row.get(2).map_err(error)?, + bytes: body, + }); + } + drop(rows); + drop(query); + drop(source_db); + if after == cursor && cursor < count { + return Err(invalid("backup source is incomplete")); + } + insert_batch( + &mut open_shard(&resolve_file(&target_root, &target)?, false)?, + &batch, + )?; + if after < count { + target_root + .execute( + "UPDATE backup_copy SET cursor=?2 WHERE lower_key=?1 AND singleton=1", + params![lower, after], + ) + .map_err(error)?; + return Ok(false); + } + if totals(&target_root, &target)? != (count, size, sha.clone()) { + return Err(invalid("backup partition verification failed")); + } + let tx = target_root + .transaction_with_behavior(TransactionBehavior::Immediate) + .map_err(error)?; + routes::put( + &tx, + "routes", + routes::Route { + lower: lower.clone(), + file: target, + records: count, + bytes: size, + fingerprint: sha, + }, + )?; + tx.execute( + "UPDATE backup_copy SET after_lower=?1,lower_key=NULL,source=NULL,target=NULL,records=NULL,bytes=NULL,fingerprint=NULL,cursor=0 WHERE singleton=1", + [&lower], + ) + .map_err(error)?; + tx.commit().map_err(error)?; + return Ok(false); + } + target_root + .execute("UPDATE backup_copy SET verifying=1 WHERE singleton=1", []) + .map_err(error)?; + return Ok(false); + } + if let Some(row) = routes::next_local(&target_root, verified_after.as_deref())? { + target_root + .execute( + "UPDATE backup_copy SET verified_after=?1 WHERE singleton=1", + [row.lower], + ) + .map_err(error)?; + return Ok(false); + } + target_root + .execute_batch( + "BEGIN IMMEDIATE; + UPDATE backup_manifest SET complete=1 WHERE singleton=1; + DROP TABLE backup_copy; + COMMIT; PRAGMA wal_checkpoint(TRUNCATE);", + ) + .map_err(error)?; + drop(target_root); + crate::durable::open_private_regular_file(&path) + .and_then(|f| f.sync_all()) + .map_err(error)?; + sync_directory(&job.staging)?; + publish_directory(&job.staging, &job.destination)?; + release(db, &job) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::indexed_store::IndexedStore; + #[test] + fn publication_does_not_replace_a_racing_empty_directory_or_symlink() { + let home = std::env::temp_dir().join(format!( + "paperclip-backup-publication-{}", + uuid::Uuid::new_v4() + )); + fs::create_dir(&home).unwrap(); + let source = home.join("prepared"); + let destination = home.join("backup"); + fs::create_dir(&source).unwrap(); + fs::write(source.join("evidence"), b"verified snapshot").unwrap(); + fs::create_dir(&destination).unwrap(); + assert!(publish_directory(&source, &destination).is_err()); + assert!(source.join("evidence").exists()); + assert!(fs::read_dir(&destination).unwrap().next().is_none()); + fs::remove_dir(&destination).unwrap(); + #[cfg(unix)] + { + std::os::unix::fs::symlink(home.join("missing"), &destination).unwrap(); + assert!(publish_directory(&source, &destination).is_err()); + assert!(fs::symlink_metadata(&destination).unwrap().is_symlink()); + fs::remove_file(&destination).unwrap(); + } + publish_directory(&source, &destination).unwrap(); + assert_eq!( + fs::read(destination.join("evidence")).unwrap(), + b"verified snapshot" + ); + fs::remove_dir_all(home).unwrap(); + } + fn receipt(i: u64) -> ExactReceipt { + ExactReceipt { + namespace: "effects".into(), + key: format!("effect-{i:020}"), + bytes: vec![(i % 251) as u8; 1024], + } + } + #[test] + fn snapshot_restores_exact_prefix_while_live_writes_and_splits_continue_across_restarts() { + let home = std::env::temp_dir().join(format!("paperclip-backup-{}", uuid::Uuid::new_v4())); + let path = home.join("source.sqlite"); + let mut store = IndexedStore::open(&path, "backup-test", true).unwrap(); + store + .commit( + "authority", + 0, + b"snapshot-state".to_vec(), + (0..500).map(receipt).collect(), + ) + .unwrap(); + let db = Connection::open(&path).unwrap(); + db.execute( + "UPDATE receipt_storage_config SET partition_bytes=32768", + [], + ) + .unwrap(); + for _ in 0..80 { + store.maintain().unwrap(); + } + let destination = home.join("backup"); + store.begin_backup(&destination).unwrap(); + assert!(!store.backup_step().unwrap()); + assert!(!destination.exists()); + let staging: String = db + .query_row("SELECT staging FROM receipt_backup", [], |r| r.get(0)) + .unwrap(); + assert!(IndexedStore::open( + &Path::new(&staging).join("authority.sqlite"), + "backup-test", + false + ) + .is_err()); + let mut complete = false; + let mut generation = 1; + let mut verification_steps = 0; + for i in 0..200 { + store + .commit( + "authority", + generation, + b"live-state".to_vec(), + vec![receipt(500 + i)], + ) + .unwrap(); + generation += 1; + for _ in 0..4 { + store.maintain().unwrap(); + } + if i % 2 == 0 { + drop(store); + store = IndexedStore::open(&path, "backup-test", false).unwrap(); + store.begin_backup(&destination).unwrap(); + } + let snapshot = root(&Path::new(&staging).join("authority.sqlite")).unwrap(); + if snapshot + .query_row("SELECT verifying FROM backup_copy", [], |r| { + r.get::<_, bool>(0) + }) + .unwrap() + { + verification_steps += 1; + } + drop(snapshot); + if store.backup_step().unwrap() { + complete = true; + break; + } + } + assert!(complete); + assert!( + verification_steps > 2, + "verification must yield across routing pages and restarts" + ); + assert_eq!( + db.query_row("SELECT count(*) FROM receipt_backup", [], |r| r + .get::<_, i64>(0)) + .unwrap(), + 0 + ); + let restored = + IndexedStore::open(&destination.join("authority.sqlite"), "backup-test", false) + .unwrap(); + let state = restored.read_state("authority").unwrap().unwrap(); + assert_eq!(state.generation, 1); + assert_eq!(state.bytes, b"snapshot-state"); + for i in [0, 127, 499] { + assert_eq!( + restored.receipt("effects", &receipt(i).key).unwrap(), + Some(receipt(i).bytes) + ); + } + assert_eq!( + restored.receipt("effects", &receipt(500).key).unwrap(), + None + ); + assert_eq!( + store.read_state("authority").unwrap().unwrap().generation, + generation + ); + assert!(store + .receipt("effects", &receipt(500).key) + .unwrap() + .is_some()); + drop(restored); + drop(store); + drop(db); + fs::remove_dir_all(home).unwrap(); + } + #[test] + fn interrupted_preparation_can_restart_and_incomplete_source_cannot_publish() { + let home = + std::env::temp_dir().join(format!("paperclip-backup-fault-{}", uuid::Uuid::new_v4())); + let path = home.join("source.sqlite"); + let store = IndexedStore::open(&path, "backup-test", true).unwrap(); + store + .commit("authority", 0, b"state".to_vec(), vec![receipt(0)]) + .unwrap(); + let destination = home.join("backup"); + store.begin_backup(&destination).unwrap(); + drop(store); + let db = Connection::open(&path).unwrap(); + let active = job(&db).unwrap().unwrap(); + create_file(&active.staging.join("authority.sqlite")).unwrap(); + let store = IndexedStore::open(&path, "backup-test", false).unwrap(); + store.begin_backup(&destination).unwrap(); + assert!(!store.backup_step().unwrap()); + let (_, file) = route(&db, "effects\0effect").unwrap(); + let shard = open_shard(&resolve_file(&db, &file).unwrap(), false).unwrap(); + shard.execute("DELETE FROM receipt_rows", []).unwrap(); + drop(shard); + assert!(store.backup_step().is_err()); + assert!(!destination.exists()); + assert_eq!( + db.query_row("SELECT count(*) FROM receipt_backup", [], |r| r + .get::<_, i64>(0)) + .unwrap(), + 1 + ); + drop(store); + drop(db); + fs::remove_dir_all(home).unwrap(); + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/indexed_commit.rs b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_commit.rs new file mode 100644 index 00000000000..3e6d8f20ea1 --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_commit.rs @@ -0,0 +1,198 @@ +//! One retry identity per current row, never a lifetime list. It shares the +//! root transaction with current state and receipt redo. A caller retries the +//! same storage request, not the command that produced it. +use crate::indexed_revision::Revision; +use crate::{ + durable::DurableRunnerError, + indexed_store::{storage_error, ExactReceipt}, + indexed_work::WorkChange, +}; +use rusqlite::{params, Connection, OptionalExtension}; +type Result = std::result::Result; + +pub(crate) fn initialize(db: &Connection) -> Result<()> { + db.execute_batch("CREATE TABLE IF NOT EXISTS current_commit (state_key TEXT PRIMARY KEY, operation_id TEXT NOT NULL, generation INTEGER NOT NULL, digest BLOB NOT NULL, expected_generation TEXT) WITHOUT ROWID;").map_err(storage_error)?; + let has_predecessor: bool = db.query_row("SELECT EXISTS(SELECT 1 FROM pragma_table_info('current_commit') WHERE name='expected_generation')", [], |r| r.get(0)).map_err(storage_error)?; + if !has_predecessor { + db.execute_batch("ALTER TABLE current_commit ADD COLUMN expected_generation TEXT;") + .map_err(storage_error)?; + } + Ok(()) +} + +fn fingerprint( + key: &str, + generation: Revision, + predecessor: Option, + bytes: &[u8], + receipts: &[ExactReceipt], + work: &[WorkChange], +) -> Vec { + use sha2::{Digest, Sha256}; + let mut hash = Sha256::new(); + let mut field = |bytes: &[u8]| { + hash.update((bytes.len() as u64).to_be_bytes()); + hash.update(bytes); + }; + field(if predecessor.is_some() { + b"paperclip.indexed-commit.v2" + } else { + b"paperclip.indexed-commit.v1" + }); + field(key.as_bytes()); + field(&generation.fingerprint()); + if let Some(previous) = predecessor { + field(&previous.fingerprint()); + } + field(bytes); + field(&(receipts.len() as u64).to_be_bytes()); + for receipt in receipts { + field(receipt.namespace.as_bytes()); + field(receipt.key.as_bytes()); + field(&receipt.bytes); + } + field(&(work.len() as u64).to_be_bytes()); + for change in work { + field(change.collection.as_bytes()); + field(change.key.as_bytes()); + for value in [&change.expected_digest, &change.bytes] { + field(&[u8::from(value.is_some())]); + if let Some(bytes) = value { + field(bytes); + } + } + } + hash.finalize().to_vec() +} + +pub(crate) fn record( + db: &Connection, + key: &str, + operation: &str, + expected: Revision, + generation: Revision, + bytes: &[u8], + receipts: &[ExactReceipt], + work: &[WorkChange], +) -> Result<()> { + db.execute("INSERT INTO current_commit(state_key,operation_id,generation,digest,expected_generation) VALUES(?1,?2,?3,?4,?5) ON CONFLICT(state_key) DO UPDATE SET operation_id=excluded.operation_id,generation=excluded.generation,digest=excluded.digest,expected_generation=excluded.expected_generation", params![key,operation,generation,fingerprint(key,generation,Some(expected),bytes,receipts,work),expected.to_string()]).map_err(storage_error)?; + Ok(()) +} + +pub(crate) fn replay( + db: &Connection, + key: &str, + operation: &str, + expected: Revision, + bytes: &[u8], + receipts: &[ExactReceipt], + work: &[WorkChange], +) -> Result> { + let stamp: Option<(String, Revision, Vec, Option)> = db.query_row("SELECT operation_id,generation,CASE WHEN length(digest)=32 THEN digest END,expected_generation FROM current_commit WHERE state_key=?1", [key], |r| Ok((r.get(0)?,r.get(1)?,r.get(2)?,r.get(3)?))).optional().map_err(storage_error)?; + let Some((id, generation, digest, predecessor)) = stamp else { + return Ok(None); + }; + if id != operation { + return Ok(None); + } + let predecessor = predecessor + .map(|value| value.parse::()) + .transpose()?; + let follows = match predecessor { + Some(value) => value == expected && generation != expected, + None => { + matches!((expected.legacy(), generation.legacy()), (Some(old), Some(new)) if old.checked_add(1) == Some(new)) + } + }; + if !follows || digest != fingerprint(key, generation, predecessor, bytes, receipts, work) { + return Err(DurableRunnerError::invalid( + "indexed commit retry identity changed", + )); + } + let current = crate::indexed_store::read_state(db, key)?; + if !current.is_some_and(|state| state.generation == generation && state.bytes == bytes) { + return Err(DurableRunnerError::invalid( + "indexed commit retry was superseded", + )); + } + Ok(Some(generation)) +} + +#[cfg(test)] +mod tests { + use super::*; + use sha2::{Digest, Sha256}; + + #[test] + fn retained_retry_stamp_survives_schema_upgrade() { + let db = Connection::open_in_memory().unwrap(); + db.execute_batch("CREATE TABLE current_commit(state_key TEXT PRIMARY KEY,operation_id TEXT,generation INTEGER,digest BLOB); CREATE TABLE current_state(key TEXT PRIMARY KEY,generation INTEGER,bytes BLOB,digest BLOB);").unwrap(); + db.execute( + "INSERT INTO current_state VALUES('state',2,?1,?2)", + params![b"accepted", Sha256::digest(b"accepted").as_slice()], + ) + .unwrap(); + db.execute( + "INSERT INTO current_commit VALUES('state','old-operation',2,?1)", + [fingerprint("state", 2.into(), None, b"accepted", &[], &[])], + ) + .unwrap(); + initialize(&db).unwrap(); + initialize(&db).unwrap(); + assert_eq!( + replay( + &db, + "state", + "old-operation", + 1.into(), + b"accepted", + &[], + &[] + ) + .unwrap(), + Some(2.into()) + ); + assert!(replay( + &db, + "state", + "old-operation", + 0.into(), + b"accepted", + &[], + &[] + ) + .is_err()); + } + + #[test] + fn opaque_retry_stamp_binds_the_predecessor_and_rejects_its_corruption() { + let db = Connection::open_in_memory().unwrap(); + initialize(&db).unwrap(); + db.execute_batch("CREATE TABLE current_state(key TEXT PRIMARY KEY,generation INTEGER,bytes BLOB,digest BLOB);").unwrap(); + let previous = Revision::from(i64::MAX as u64); + let next = previous.next().unwrap(); + db.execute( + "INSERT INTO current_state VALUES('state',?1,?2,?3)", + params![next, b"accepted", Sha256::digest(b"accepted").as_slice()], + ) + .unwrap(); + record( + &db, + "state", + "operation", + previous, + next, + b"accepted", + &[], + &[], + ) + .unwrap(); + assert_eq!( + replay(&db, "state", "operation", previous, b"accepted", &[], &[]).unwrap(), + Some(next) + ); + db.execute("UPDATE current_commit SET expected_generation='1'", []) + .unwrap(); + assert!(replay(&db, "state", "operation", 1.into(), b"accepted", &[], &[]).is_err()); + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/indexed_inspection.rs b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_inspection.rs new file mode 100644 index 00000000000..63abcefbfd3 --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_inspection.rs @@ -0,0 +1,295 @@ +//! Read-only current-state inspection for remote recovery. No receipt/history +//! enumeration, owner recovery, SQLite checkpoint, or writable connection. +use crate::indexed_revision::Revision; +use crate::{ + durable::{open_private_regular_file, verify_private_directory, DurableRunnerError}, + indexed_store::{read_state, storage_error}, +}; +use base64::{engine::general_purpose::STANDARD, Engine}; +use rusqlite::{Connection, OpenFlags, OptionalExtension}; +use serde_json::{json, Value}; +use sha2::{Digest, Sha256}; +use std::{ + io::{Read, Write}, + path::{Path, PathBuf}, +}; + +type Result = std::result::Result; +fn invalid(message: &str) -> DurableRunnerError { + DurableRunnerError::invalid(message) +} +pub const INSPECTION_CHUNK_BYTES: usize = 192 * 1024; + +pub fn inspect(path: &Path) -> Result { + let parent = path + .parent() + .ok_or_else(|| invalid("indexed inspection has no parent"))?; + verify_private_directory(parent)?; + let key = match path.file_name().and_then(|name| name.to_str()) { + Some("runner-state.json") => "runner", + Some("codex-provider-state.json") => "codex-provider", + _ => return Err(invalid("unsupported indexed inspection locator")), + }; + let mut bytes = Vec::new(); + open_private_regular_file(path) + .map_err(storage_error)? + .take(4097) + .read_to_end(&mut bytes) + .map_err(storage_error)?; + if bytes.len() > 4096 { + return Err(invalid("indexed inspection locator exceeds capacity")); + } + let locator: Value = serde_json::from_slice(&bytes).map_err(storage_error)?; + let schema = if key == "runner" { + "paperclip.runner.durable.state.indexed.v1" + } else { + "paperclip.runner.codex-provider-state.indexed.v1" + }; + let binding = locator["binding"] + .as_str() + .filter(|s| !s.is_empty() && s.len() <= 1024 && !s.chars().any(char::is_control)) + .ok_or_else(|| invalid("invalid indexed inspection binding"))?; + if locator["schema"] != schema { + return Err(invalid("invalid indexed inspection schema")); + } + let database = parent + .canonicalize() + .map_err(storage_error)? + .join(path.with_extension("sqlite").file_name().unwrap()); + let _lifetime = crate::indexed_lifetime::StoreLifetime::existing_shared(&database)?; + for suffix in ["", "-wal", "-shm"] { + match crate::indexed_store::verify_sqlite_file(&PathBuf::from(format!( + "{}{suffix}", + database.display() + ))) { + Ok(_) => {} + Err(e) if !suffix.is_empty() && e.kind() == std::io::ErrorKind::NotFound => {} + Err(e) => return Err(storage_error(e)), + } + } + let db = Connection::open_with_flags( + &database, + OpenFlags::SQLITE_OPEN_READ_ONLY + | OpenFlags::SQLITE_OPEN_NOFOLLOW + | OpenFlags::SQLITE_OPEN_NO_MUTEX, + ) + .map_err(storage_error)?; + db.busy_timeout(std::time::Duration::from_secs(5)) + .map_err(storage_error)?; + db.execute_batch( + "PRAGMA query_only=ON; PRAGMA trusted_schema=OFF; PRAGMA cache_size=-4096; BEGIN;", + ) + .map_err(storage_error)?; + let stored: (i64, String) = db + .query_row( + "SELECT schema_version,binding FROM store_binding WHERE singleton=1", + [], + |r| Ok((r.get(0)?, r.get(1)?)), + ) + .map_err(storage_error)?; + if ![1, 2].contains(&stored.0) || stored.1 != binding { + return Err(invalid("indexed inspection binding mismatch")); + } + crate::indexed_partitions::validate_backup(&db)?; + let preparation = match read_state(&db, "legacy-import")? { + None => Value::Null, + Some(imported) => { + if imported.bytes.len() > 16384 { + return Err(invalid("indexed inspection import exceeds capacity")); + } + let progress: Value = serde_json::from_slice(&imported.bytes).map_err(storage_error)?; + if progress["schema"] != "paperclip.runner.local-import.v1" + || progress["binding"] != binding + || !progress["prepared"].is_object() + { + return Err(invalid("indexed inspection import is incomplete")); + } + progress["prepared"].clone() + } + }; + let snapshot = read_state(&db, key)? + .ok_or_else(|| invalid("indexed inspection current state is missing"))?; + if snapshot.generation == 0 { + return Err(invalid("indexed inspection generation is invalid")); + } + let digest = Sha256::digest(&snapshot.bytes); + if stored.0 == 2 { + let pending: Option<(Revision, Vec, Vec)> = db.query_row( + "SELECT generation,CASE WHEN length(body)<=33554432 THEN body END,CASE WHEN length(digest)=32 THEN digest END FROM receipt_prepare WHERE state_key=?1", + [key], |r| Ok((r.get(0)?, r.get(1)?, r.get(2)?))).optional().map_err(storage_error)?; + if pending.is_some_and(|(generation, body, hash)| { + generation != snapshot.generation || body != snapshot.bytes || hash != digest.as_slice() + }) { + return Err(invalid( + "indexed inspection has conflicting prepared receipts", + )); + } + } + let mut state: Value = serde_json::from_slice(&snapshot.bytes).map_err(storage_error)?; + if !state.is_object() { + return Err(invalid("indexed inspection current state is invalid")); + } + if key == "runner" { + let next = state["nextSourceSeq"] + .as_u64() + .filter(|n| *n <= 9_007_199_254_740_991); + let acked = state["ackedSourceSeq"] + .as_u64() + .filter(|n| *n <= 9_007_199_254_740_991); + let (Some(next), Some(acked)) = (next, acked) else { + return Err(invalid("indexed inspection cursor is invalid")); + }; + if next <= acked { + return Err(invalid("indexed inspection cursor is invalid")); + } + // A proof must preserve pending delivery as a negative fence, without + // restoring or reading the historical outbox bodies. + state["outbox"] = if next == acked + 1 { + json!([]) + } else { + json!([{ "indexedPending": true }]) + }; + } + Ok( + json!({ "state": state, "generation": snapshot.generation.to_string(), "stateDigest": format!("{digest:x}"), "preparation": preparation }), + ) +} + +/// Emit one consistent snapshot in bounded wire frames. EOF, a missing footer, +/// or a changed digest cannot be interpreted as an empty/settled authority. +pub fn stream(path: &Path, mut output: impl Write) -> Result<()> { + let bytes = serde_json::to_vec(&inspect(path)?).map_err(storage_error)?; + let header = json!({ "schema": "paperclip.indexed-inspection.v1", "byteLength": bytes.len(), "sha256": format!("{:x}", Sha256::digest(&bytes)) }); + serde_json::to_writer(&mut output, &header).map_err(storage_error)?; + output.write_all(b"\n").map_err(storage_error)?; + for (index, chunk) in bytes.chunks(INSPECTION_CHUNK_BYTES).enumerate() { + serde_json::to_writer( + &mut output, + &json!({ "index": index, "bytes": STANDARD.encode(chunk) }), + ) + .map_err(storage_error)?; + output.write_all(b"\n").map_err(storage_error)?; + } + output + .write_all(b"{\"complete\":true}\n") + .and_then(|_| output.flush()) + .map_err(storage_error) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::indexed_store::{ExactReceipt, IndexedStore}; + struct Fixture { + root: PathBuf, + path: PathBuf, + store: IndexedStore, + } + impl Fixture { + fn new(next: u64) -> Self { + let root = + std::env::temp_dir().join(format!("paperclip-inspection-{}", uuid::Uuid::new_v4())); + let store = + IndexedStore::open(&root.join("runner-state.sqlite"), "inspection", true).unwrap(); + let path = root.join("runner-state.json"); + let mut file = std::fs::OpenOptions::new(); + file.write(true).create_new(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + file.mode(0o600); + } + file.open(&path).unwrap().write_all(br#"{"schema":"paperclip.runner.durable.state.indexed.v1","binding":"inspection"}"#).unwrap(); + store.commit("runner", 0, serde_json::to_vec(&json!({"nextSourceSeq":next,"ackedSourceSeq":1,"lifecycle":"ready","current":"exact"})).unwrap(), vec![ExactReceipt { + namespace: "old-output".into(), key: "ancient".into(), bytes: vec![b'x'; 2 * 1024 * 1024], + }]).unwrap(); + Self { root, path, store } + } + } + impl Drop for Fixture { + fn drop(&mut self) { + let _ = std::fs::remove_dir_all(&self.root); + } + } + #[test] + fn current_inspection_preserves_pending_delivery_and_does_not_restore_history() { + for (next, outbox) in [(2, json!([])), (3, json!([{"indexedPending":true}]))] { + let f = Fixture::new(next); + let before = f.store.read_state("runner").unwrap().unwrap(); + let observed = inspect(&f.path).unwrap(); + assert_eq!(observed["state"]["outbox"], outbox); + assert_eq!(observed["generation"], "1"); + assert!(serde_json::to_vec(&observed).unwrap().len() < 1000); + assert_eq!(f.store.read_state("runner").unwrap().unwrap(), before); + assert_eq!( + f.store + .receipt("old-output", "ancient") + .unwrap() + .unwrap() + .len(), + 2 * 1024 * 1024 + ); + } + } + #[test] + fn inspection_rejects_conflicting_redo_without_recovering_it() { + let f = Fixture::new(2); + let db = Connection::open(f.root.join("runner-state.sqlite")).unwrap(); + // Copy the actual schema's redo columns, changing only the current + // generation. No side-effect/receipt repair is authorized by inspection. + db.execute("INSERT INTO receipt_prepare(singleton,state_key,generation,body,digest,records,fingerprint) SELECT 1,key,generation+1,bytes,digest,0,zeroblob(32) FROM current_state WHERE key='runner'", []).unwrap(); + assert!(inspect(&f.path) + .unwrap_err() + .to_string() + .contains("conflicting prepared")); + assert_eq!( + db.query_row( + "SELECT generation FROM current_state WHERE key='runner'", + [], + |r| r.get::<_, i64>(0) + ) + .unwrap(), + 1 + ); + } + #[test] + fn missing_activated_state_is_not_an_empty_snapshot() { + let f = Fixture::new(2); + let db = Connection::open(f.root.join("runner-state.sqlite")).unwrap(); + db.execute("DELETE FROM current_state WHERE key='runner'", []) + .unwrap(); + assert!(inspect(&f.path) + .unwrap_err() + .to_string() + .contains("current state is missing")); + } + #[test] + fn streams_one_snapshot_in_bounded_ordered_digest_checked_frames() { + let f = Fixture::new(2); + f.store.commit("runner", 1, serde_json::to_vec(&json!({"nextSourceSeq":2,"ackedSourceSeq":1,"activeInput":"界".repeat(500_000)})).unwrap(), vec![]).unwrap(); + let mut wire = Vec::new(); + stream(&f.path, &mut wire).unwrap(); + let rows: Vec<_> = wire + .split(|b| *b == b'\n') + .filter(|line| !line.is_empty()) + .collect(); + let header: Value = serde_json::from_slice(rows[0]).unwrap(); + let mut snapshot = Vec::new(); + for (index, row) in rows[1..rows.len() - 1].iter().enumerate() { + assert!(row.len() <= INSPECTION_CHUNK_BYTES * 4 / 3 + 128); + let chunk: Value = serde_json::from_slice(row).unwrap(); + assert_eq!(chunk["index"], index); + snapshot.extend(STANDARD.decode(chunk["bytes"].as_str().unwrap()).unwrap()); + } + assert_eq!(header["byteLength"], snapshot.len()); + assert_eq!(header["sha256"], format!("{:x}", Sha256::digest(&snapshot))); + assert_eq!( + serde_json::from_slice::(rows.last().unwrap()).unwrap(), + json!({"complete":true}) + ); + assert_eq!( + serde_json::from_slice::(&snapshot).unwrap()["generation"], + "2" + ); + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/indexed_lifetime.rs b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_lifetime.rs new file mode 100644 index 00000000000..b16f9448881 --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_lifetime.rs @@ -0,0 +1,97 @@ +//! A connection owns a shared lifetime fence until SQLite has closed. Moving a +//! store requires the exclusive fence; per-operation locks alone are too short. +use crate::durable::{open_private_regular_file, verify_private_directory, DurableRunnerError}; +use crate::indexed_store::storage_error; +use std::{ + fs, + path::{Path, PathBuf}, +}; + +type Result = std::result::Result; +pub(crate) struct StoreLifetime(fs::File); +pub(crate) fn archive_fence(path: &Path) -> PathBuf { + let mut name = path.as_os_str().to_os_string(); + name.push(".archive"); + PathBuf::from(name) +} +impl StoreLifetime { + pub(crate) fn existing_exclusive(path: &Path) -> Result { + let file = + open_private_regular_file(&PathBuf::from(format!("{}.lifetime", path.display()))) + .map_err(storage_error)?; + file.try_lock().map_err(|_| { + DurableRunnerError::invalid( + "storage_pressure: indexed store still has an open connection", + ) + })?; + Ok(Self(file)) + } + pub(crate) fn existing_shared(path: &Path) -> Result { + let file = + open_private_regular_file(&PathBuf::from(format!("{}.lifetime", path.display()))) + .map_err(storage_error)?; + file.try_lock_shared().map_err(|_| { + DurableRunnerError::invalid("storage_pressure: indexed store is being archived") + })?; + let lock = Self(file); + match fs::symlink_metadata(archive_fence(path)) { + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(lock), + _ => Err(DurableRunnerError::invalid( + "indexed archive must finish before reading receipts", + )), + } + } + fn file(path: &Path) -> Result { + verify_private_directory( + path.parent() + .ok_or_else(|| DurableRunnerError::invalid("store has no parent"))?, + )?; + let mut name = path.as_os_str().to_os_string(); + name.push(".lifetime"); + let path = PathBuf::from(name); + let mut options = fs::OpenOptions::new(); + options.write(true).create_new(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + options.mode(0o600); + } + match options.open(&path) { + Ok(file) => file.sync_all().map_err(storage_error)?, + Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {} + Err(e) => return Err(storage_error(e)), + } + open_private_regular_file(&path).map_err(storage_error) + } + pub(crate) fn shared(path: &Path) -> Result { + let file = Self::file(path)?; + file.try_lock_shared().map_err(|_| { + DurableRunnerError::invalid("storage_pressure: indexed store is being archived") + })?; + let lock = Self(file); + match fs::symlink_metadata(archive_fence(path)) { + Ok(_) => { + return Err(DurableRunnerError::invalid( + "storage_pressure: indexed archive must finish before opening the store", + )) + } + Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} + Err(e) => return Err(storage_error(e)), + } + Ok(lock) + } + pub(crate) fn exclusive(path: &Path) -> Result { + let file = Self::file(path)?; + file.try_lock().map_err(|_| { + DurableRunnerError::invalid( + "storage_pressure: indexed store still has an open connection", + ) + })?; + Ok(Self(file)) + } +} +impl Drop for StoreLifetime { + fn drop(&mut self) { + let _ = self.0.unlock(); + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/indexed_partitions.rs b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_partitions.rs new file mode 100644 index 00000000000..5a7669e08f7 --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_partitions.rs @@ -0,0 +1,1784 @@ +//! Range-routed immutable receipts. Only bounded routing heads/current authority +//! live in the root database. A durable prepare record makes a multi-file commit +//! recoverable without assuming atomic transactions across SQLite WAL files. +use crate::durable::DurableRunnerError; +use crate::indexed_revision::Revision; +use crate::indexed_store::ExactReceipt; +use rusqlite::{params, Connection, OptionalExtension, TransactionBehavior}; +use sha2::{Digest, Sha256}; +use std::{ + fs, + path::{Path, PathBuf}, +}; + +#[path = "indexed_backup.rs"] +mod backup; +pub(crate) use backup::{backup_step, begin_backup, validate_backup}; +#[path = "indexed_routes.rs"] +mod routes; +pub(crate) use routes::RoutingCollector; + +type Result = std::result::Result; +const DEFAULT_PARTITION_BYTES: u64 = 256 * 1024 * 1024; +const PAGE_BYTES: usize = 4 * 1024 * 1024; +const MAX_BODY: usize = 32 * 1024 * 1024; +fn error(e: impl std::fmt::Display + std::any::Any) -> DurableRunnerError { + crate::indexed_store::storage_error(e) +} +fn invalid(e: &str) -> DurableRunnerError { + DurableRunnerError::invalid(e) +} +fn logical(namespace: &str, key: &str) -> String { + format!("{namespace}\0{key}") +} +fn digest(bytes: &[u8]) -> Vec { + Sha256::digest(bytes).to_vec() +} +fn verify(bytes: &[u8], expected: &[u8]) -> Result<()> { + if bytes.len() > MAX_BODY || digest(bytes) != expected { + return Err(invalid("indexed receipt digest/size mismatch")); + } + Ok(()) +} +fn xor(a: &[u8], b: &[u8]) -> Vec { + a.iter().zip(b).map(|(a, b)| a ^ b).collect() +} +fn identity_digest(key: &str, body: &[u8]) -> Vec { + let mut hash = Sha256::new(); + hash.update((key.len() as u64).to_be_bytes()); + hash.update(key.as_bytes()); + hash.update(digest(body)); + hash.finalize().to_vec() +} + +/// The advisory lock covers one operation, including all shard commits and +/// cutovers. Separate store handles/processes cannot race recovery or unlink a +/// shard used by a reader. No lock is held while waiting for provider activity. +pub(crate) struct PartitionLock(fs::File); +impl PartitionLock { + pub(crate) fn existing(path: &Path) -> Result { + let lock_path = PathBuf::from(format!("{}.lock", path.display())); + let file = crate::durable::open_private_regular_file(&lock_path).map_err(error)?; + file.lock().map_err(error)?; + Ok(Self(file)) + } + pub(crate) fn acquire(path: &Path) -> Result { + let lock_path = PathBuf::from(format!("{}.lock", path.display())); + let mut options = fs::OpenOptions::new(); + options.write(true).create_new(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + options.mode(0o600); + } + match options.open(&lock_path) { + Ok(file) => file.sync_all().map_err(error)?, + Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {} + Err(e) => return Err(error(e)), + } + let file = crate::durable::open_private_regular_file(&lock_path).map_err(error)?; + file.lock().map_err(error)?; + Ok(Self(file)) + } +} +impl Drop for PartitionLock { + fn drop(&mut self) { + let _ = self.0.unlock(); + } +} + +fn directory(path: &Path) -> Result { + let directory = PathBuf::from(format!("{}.receipts", path.display())); + if !directory.exists() { + let mut builder = fs::DirBuilder::new(); + #[cfg(unix)] + { + use std::os::unix::fs::DirBuilderExt; + builder.mode(0o700); + } + builder.create(&directory).map_err(error)?; + fs::File::open(path.parent().unwrap()) + .and_then(|f| f.sync_all()) + .map_err(error)?; + } + crate::durable::verify_private_directory(&directory)?; + Ok(directory) +} + +fn open_shard(path: &Path, create: bool) -> Result { + open_shard_mode(path, create, false) +} +fn open_shard_mode(path: &Path, create: bool, read_only: bool) -> Result { + crate::durable::verify_private_directory( + path.parent() + .ok_or_else(|| invalid("shard has no parent"))?, + )?; + if create { + let mut options = fs::OpenOptions::new(); + options.create_new(true).write(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + options.mode(0o600); + } + options + .open(path) + .and_then(|f| f.sync_all()) + .map_err(error)?; + } + for suffix in ["", "-wal", "-shm"] { + match crate::indexed_store::verify_sqlite_file(Path::new(&format!( + "{}{suffix}", + path.display() + ))) { + Ok(_) => {} + Err(e) if !suffix.is_empty() && e.kind() == std::io::ErrorKind::NotFound => {} + Err(e) => return Err(error(e)), + } + } + let db = Connection::open_with_flags( + path, + (if read_only { + rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY + } else { + rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE + }) | rusqlite::OpenFlags::SQLITE_OPEN_NOFOLLOW + | rusqlite::OpenFlags::SQLITE_OPEN_NO_MUTEX, + ) + .map_err(error)?; + db.busy_timeout(std::time::Duration::from_secs(5)) + .map_err(error)?; + if read_only { + db.execute_batch( + "PRAGMA query_only=ON; PRAGMA trusted_schema=OFF; PRAGMA cache_size=-1024;", + ) + .map_err(error)?; + } else { + db.execute_batch("PRAGMA journal_mode=WAL; PRAGMA synchronous=FULL; PRAGMA trusted_schema=OFF; PRAGMA cache_size=-1024; PRAGMA cache_spill=OFF; PRAGMA wal_autocheckpoint=256; PRAGMA journal_size_limit=4194304;").map_err(error)?; + } + if create { + db.execute_batch("BEGIN IMMEDIATE; CREATE TABLE receipt_rows (id TEXT PRIMARY KEY, namespace TEXT NOT NULL, receipt_key TEXT NOT NULL, body BLOB NOT NULL, digest BLOB NOT NULL, ordinal INTEGER NOT NULL UNIQUE) WITHOUT ROWID; CREATE TABLE receipt_totals(singleton INTEGER PRIMARY KEY CHECK(singleton=1), records INTEGER NOT NULL, bytes INTEGER NOT NULL, fingerprint BLOB NOT NULL); INSERT INTO receipt_totals VALUES(1,0,0,zeroblob(32)); COMMIT;").map_err(error)?; + fs::File::open(path.parent().unwrap()) + .and_then(|f| f.sync_all()) + .map_err(error)?; + } + Ok(db) +} +fn resolve_file(db: &Connection, file: &str) -> Result { + let file = Path::new(file); + if file.is_absolute() { + return Ok(file.to_owned()); + } + if file + .components() + .any(|part| !matches!(part, std::path::Component::Normal(_))) + { + return Err(invalid("invalid receipt partition path")); + } + let root = Path::new( + db.path() + .ok_or_else(|| invalid("receipt root has no path"))?, + ); + Ok(root.parent().unwrap().join(file)) +} +fn new_shard(dir: &Path) -> Result { + let path = dir + .canonicalize() + .map_err(error)? + .join(format!("{}.sqlite", uuid::Uuid::new_v4())); + open_shard(&path, true)?; + Ok(path + .to_str() + .ok_or_else(|| invalid("invalid shard path"))? + .to_owned()) +} + +/// Full backup verification is deliberate background I/O. Read one bounded +/// receipt at a time, including its exact key/digest, and compare every range +/// with the frozen root. This is never called by ordinary current-state reads. +pub(crate) fn verify_backup_receipts(db: &Connection) -> Result<()> { + routes::verify_local(db)?; + let root = Path::new( + db.path() + .ok_or_else(|| invalid("backup has no database path"))?, + ); + let expected_directory = format!("{}.receipts", root.file_name().unwrap().to_string_lossy()); + let mut after = None; + while let Some(range) = routes::next(db, "routes", after.as_deref())? { + after = Some(range.lower); + let file = range.file; + let parts: Vec<_> = Path::new(&file).components().collect(); + if parts.len() != 2 + || parts[0].as_os_str() != std::ffi::OsStr::new(&expected_directory) + || !matches!(parts[1], std::path::Component::Normal(_)) + { + return Err(invalid("backup references an external receipt partition")); + } + let expected = (range.records, range.bytes, range.fingerprint); + let shard = open_shard_mode(&resolve_file(db, &file)?, false, true)?; + let mut query = shard.prepare("SELECT ordinal,CASE WHEN length(id)<=2049 THEN id END,CASE WHEN length(namespace)<=1024 THEN namespace END,CASE WHEN length(receipt_key)<=1024 THEN receipt_key END,CASE WHEN length(body)<=33554432 THEN body END,CASE WHEN length(digest)=32 THEN digest END FROM receipt_rows ORDER BY ordinal").map_err(error)?; + let mut rows = query.query([]).map_err(error)?; + let mut count = 0i64; + let mut bytes = 0i64; + let mut fingerprint = vec![0u8; 32]; + while let Some(row) = rows.next().map_err(error)? { + let ordinal: i64 = row.get(0).map_err(error)?; + let key: String = row.get(1).map_err(error)?; + let namespace: String = row.get(2).map_err(error)?; + let receipt_key: String = row.get(3).map_err(error)?; + let body: Vec = row.get(4).map_err(error)?; + let sha: Vec = row.get(5).map_err(error)?; + count = count + .checked_add(1) + .ok_or_else(|| invalid("backup receipt count overflow"))?; + bytes = bytes + .checked_add((body.len() + key.len() + 64) as i64) + .ok_or_else(|| invalid("backup receipt bytes overflow"))?; + if ordinal != count || key != logical(&namespace, &receipt_key) { + return Err(invalid("backup receipt identity differs")); + } + verify(&body, &sha)?; + fingerprint = xor(&fingerprint, &identity_digest(&key, &body)); + } + if (count, bytes, fingerprint) != expected { + return Err(invalid("backup receipt range differs")); + } + } + Ok(()) +} + +/// Published snapshots use rollback journals so read-only inspection does not +/// create or mutate WAL/SHM files covered by the outer archive digest. Execution +/// restores WAL mode through the ordinary IndexedStore open path. +pub(crate) fn seal_backup_files(path: &Path) -> Result<()> { + fn seal(path: &Path) -> Result { + crate::durable::verify_private_directory( + path.parent() + .ok_or_else(|| invalid("snapshot file has no parent"))?, + )?; + for suffix in ["", "-wal", "-shm"] { + match crate::indexed_store::verify_sqlite_file(&PathBuf::from(format!( + "{}{suffix}", + path.display() + ))) { + Ok(_) => {} + Err(e) if !suffix.is_empty() && e.kind() == std::io::ErrorKind::NotFound => {} + Err(e) => return Err(error(e)), + } + } + let db = Connection::open_with_flags( + path, + rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE | rusqlite::OpenFlags::SQLITE_OPEN_NOFOLLOW, + ) + .map_err(error)?; + db.execute_batch("PRAGMA trusted_schema=OFF; PRAGMA synchronous=FULL; PRAGMA cache_size=-4096; PRAGMA busy_timeout=5000").map_err(error)?; + let busy: i64 = db + .query_row("PRAGMA wal_checkpoint(TRUNCATE)", [], |row| row.get(0)) + .map_err(error)?; + if busy != 0 { + return Err(invalid("snapshot checkpoint is busy")); + } + let mode: String = db + .query_row("PRAGMA journal_mode=DELETE", [], |row| row.get(0)) + .map_err(error)?; + if mode != "delete" { + return Err(invalid("snapshot journal was not sealed")); + } + // Release SQLite's descriptors before opening a separate fsync handle: + // closing that handle must never cancel locks on a live connection. + drop(db); + crate::durable::open_private_regular_file(path) + .and_then(|f| f.sync_all()) + .map_err(error)?; + Connection::open_with_flags( + path, + rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY | rusqlite::OpenFlags::SQLITE_OPEN_NOFOLLOW, + ) + .map_err(error) + } + let db = seal(path)?; + routes::verify_local(&db)?; + let mut after = None; + while let Some(row) = routes::next(&db, "routes", after.as_deref())? { + after = Some(row.lower); + let file = row.file; + // verify_backup_receipts has already rejected external routes. + let parts: Vec<_> = Path::new(&file).components().collect(); + let expected = format!("{}.receipts", path.file_name().unwrap().to_string_lossy()); + if parts.len() != 2 + || parts[0].as_os_str() != std::ffi::OsStr::new(&expected) + || !matches!(parts[1], std::path::Component::Normal(_)) + { + return Err(invalid("snapshot cannot seal external receipts")); + } + drop(seal(&resolve_file(&db, &file)?)?); + } + for directory in [ + path.parent().unwrap().to_owned(), + PathBuf::from(format!("{}.receipts", path.display())), + ] { + fs::File::open(directory) + .and_then(|f| f.sync_all()) + .map_err(error)?; + } + Ok(()) +} + +pub(crate) fn initialize(db: &mut Connection, path: &Path) -> Result<()> { + let dir = directory(path)?; + // The flat indexed preview format was never enabled by default. Retained + // roots require explicit maintenance migration, never an implicit O(H) + // routing rebuild while resuming an agent. + let legacy: bool = db.query_row("SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE type='table' AND name='receipt_routes')", [], |r| r.get(0)).map_err(error)?; + if legacy { + return Err(invalid( + "flat receipt routing requires explicit maintenance migration", + )); + } + let routing_exists: bool = db.query_row("SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE type='table' AND name='receipt_routing_heads')", [], |r| r.get(0)).map_err(error)?; + let tx = db + .transaction_with_behavior(TransactionBehavior::Immediate) + .map_err(error)?; + let db = &tx; + routes::initialize(db)?; + db.execute_batch(" + CREATE TABLE IF NOT EXISTS receipt_prepare(singleton INTEGER PRIMARY KEY CHECK(singleton=1), state_key TEXT NOT NULL, generation INTEGER NOT NULL, body BLOB NOT NULL, digest BLOB NOT NULL, records INTEGER NOT NULL, fingerprint BLOB NOT NULL); + CREATE TABLE IF NOT EXISTS receipt_prepare_rows(id TEXT PRIMARY KEY, namespace TEXT NOT NULL, receipt_key TEXT NOT NULL, body BLOB NOT NULL, digest BLOB NOT NULL) WITHOUT ROWID; + CREATE TABLE IF NOT EXISTS receipt_prepare_routes(lower_key TEXT PRIMARY KEY, file TEXT NOT NULL, records INTEGER NOT NULL, bytes INTEGER NOT NULL, fingerprint BLOB NOT NULL) WITHOUT ROWID; + CREATE TABLE IF NOT EXISTS receipt_migration(singleton INTEGER PRIMARY KEY CHECK(singleton=1), lower_key TEXT NOT NULL, source TEXT NOT NULL, pivot TEXT, left_file TEXT NOT NULL, right_file TEXT, cursor TEXT, upper_key TEXT NOT NULL); + CREATE TABLE IF NOT EXISTS receipt_backup(singleton INTEGER PRIMARY KEY CHECK(singleton=1), id TEXT NOT NULL, destination TEXT NOT NULL, staging TEXT NOT NULL, phase TEXT NOT NULL); + CREATE TABLE IF NOT EXISTS receipt_backup_filename(singleton INTEGER PRIMARY KEY CHECK(singleton=1), filename TEXT NOT NULL); + CREATE TABLE IF NOT EXISTS receipt_cleanup_cursor(singleton INTEGER PRIMARY KEY CHECK(singleton=1), after_file TEXT NOT NULL); + INSERT OR IGNORE INTO receipt_cleanup_cursor VALUES(1,''); + CREATE TABLE IF NOT EXISTS receipt_storage_config(singleton INTEGER PRIMARY KEY CHECK(singleton=1), partition_bytes INTEGER NOT NULL);").map_err(error)?; + db.execute( + "INSERT OR IGNORE INTO receipt_storage_config VALUES(1,?1)", + [DEFAULT_PARTITION_BYTES as i64], + ) + .map_err(error)?; + if routes::next(db, "routes", None)?.is_none() { + if routing_exists { + return Err(invalid("receipt routing authority is missing")); + } + let file = local_name(path, &new_shard(&dir)?)?; + routes::put(db, "routes", routes::Route::empty(String::new(), file))?; + } + tx.commit().map_err(error) +} +fn local_name(root: &Path, file: &str) -> Result { + Ok(Path::new(file) + .strip_prefix(root.parent().unwrap()) + .map_err(error)? + .to_str() + .ok_or_else(|| invalid("invalid shard path"))? + .to_owned()) +} + +/// Validate one bounded path when reopening. A missing committed head cannot +/// become a fresh empty store, even when the caller only asks for current state. +pub(crate) fn validate_routing(db: &Connection) -> Result<()> { + if !routes::next(db, "routes", None)?.is_some_and(|r| r.lower.is_empty()) { + return Err(invalid("receipt routing authority is missing")); + } + Ok(()) +} +fn route(db: &Connection, id: &str) -> Result<(String, String)> { + let row = + routes::floor(db, "routes", id)?.ok_or_else(|| invalid("receipt route is missing"))?; + Ok((row.lower, row.file)) +} + +/// A route names an expected receipt set, not merely a pathname. Validate that +/// set before using a negative lookup as permission to accept a new identity. +fn checked_shard(db: &Connection, lower: &str, file: &str) -> Result { + checked_shard_mode(db, lower, file, false) +} +fn checked_shard_mode( + db: &Connection, + lower: &str, + file: &str, + read_only: bool, +) -> Result { + let expected = routes::exact(db, "routes", lower)? + .filter(|r| r.file == file) + .ok_or_else(|| invalid("receipt route binding mismatch"))?; + let shard = open_shard_mode(&resolve_file(db, file)?, false, read_only)?; + if shard_totals(&shard)? != (expected.records, expected.bytes, expected.fingerprint) { + return Err(invalid( + "receipt partition differs from its authoritative route", + )); + } + Ok(shard) +} +fn shard_receipt(db: &Connection, id: &str) -> Result>> { + let row: Option<(Vec,Vec)> = db.query_row("SELECT CASE WHEN length(body)<=?2 THEN body END, CASE WHEN length(digest)=32 THEN digest END FROM receipt_rows WHERE id=?1", params![id,MAX_BODY as i64], |r| Ok((r.get(0)?,r.get(1)?))).optional().map_err(error)?; + row.map(|(body, sha)| { + verify(&body, &sha)?; + Ok(body) + }) + .transpose() +} +pub(crate) fn receipt(db: &Connection, namespace: &str, key: &str) -> Result>> { + let id = logical(namespace, key); + let (lower, file) = route(db, &id)?; + shard_receipt(&checked_shard(db, &lower, &file)?, &id) +} +pub(crate) fn read_only_receipt( + db: &Connection, + namespace: &str, + key: &str, +) -> Result>> { + let id = logical(namespace, key); + let (lower, file) = route(db, &id)?; + shard_receipt(&checked_shard_mode(db, &lower, &file, true)?, &id) +} +fn insert_batch(db: &mut Connection, records: &[ExactReceipt]) -> Result<()> { + let tx = db + .transaction_with_behavior(TransactionBehavior::Immediate) + .map_err(error)?; + for record in records { + let id = logical(&record.namespace, &record.key); + if let Some(previous) = shard_receipt(&tx, &id)? { + if previous != record.bytes { + return Err(invalid("exact receipt replay conflict")); + } + } else { + let fingerprint: Vec = tx + .query_row( + "SELECT fingerprint FROM receipt_totals WHERE singleton=1", + [], + |r| r.get(0), + ) + .map_err(error)?; + if fingerprint.len() != 32 { + return Err(invalid("receipt totals digest malformed")); + } + tx.execute( + "INSERT INTO receipt_rows SELECT ?1,?2,?3,?4,?5,records+1 FROM receipt_totals WHERE singleton=1", + params![ + id, + record.namespace, + record.key, + record.bytes, + digest(&record.bytes) + ], + ) + .map_err(error)?; + tx.execute("UPDATE receipt_totals SET records=records+1,bytes=bytes+?1,fingerprint=?2 WHERE singleton=1", params![(record.bytes.len()+id.len()+64) as i64,xor(&fingerprint,&identity_digest(&id,&record.bytes))]).map_err(error)?; + } + } + tx.commit().map_err(error) +} +fn totals(db: &Connection, file: &str) -> Result<(i64, i64, Vec)> { + shard_totals(&open_shard(&resolve_file(db, file)?, false)?) +} +fn shard_totals(db: &Connection) -> Result<(i64, i64, Vec)> { + db + .query_row( + "SELECT records,bytes,CASE WHEN length(fingerprint)=32 THEN fingerprint END FROM receipt_totals WHERE singleton=1 AND records>=0 AND bytes>=0", + [], + |r| Ok((r.get(0)?, r.get(1)?, r.get(2)?)), + ) + .map_err(error) +} +#[derive(Clone)] +struct Migration { + lower: String, + source: String, + pivot: Option, + left: String, + right: Option, + cursor: Option, + upper: String, +} +fn migration(db: &Connection) -> Result> { + db.query_row("SELECT lower_key,source,pivot,left_file,right_file,cursor,upper_key FROM receipt_migration WHERE singleton=1", [], |r| Ok(Migration { lower:r.get(0)?,source:r.get(1)?,pivot:r.get(2)?,left:r.get(3)?,right:r.get(4)?,cursor:r.get(5)?,upper:r.get(6)? })).optional().map_err(error) +} +fn target<'a>(migration: &'a Migration, id: &str) -> &'a str { + if migration + .pivot + .as_ref() + .is_some_and(|pivot| id >= pivot.as_str()) + { + migration.right.as_deref().unwrap() + } else { + &migration.left + } +} + +/// Complete a prepared commit before serving any operation. It is safe to +/// repeat after failure at every boundary: receipt inserts compare exact bytes. +pub(crate) fn recover(db: &mut Connection) -> Result<()> { + let prepared: Option<(String,Revision,Vec,Vec,i64,Vec)> = db.query_row("SELECT state_key,generation,CASE WHEN length(body)<=33554432 THEN body END,digest,records,fingerprint FROM receipt_prepare WHERE singleton=1", [], |r| Ok((r.get(0)?,r.get(1)?,r.get(2)?,r.get(3)?,r.get(4)?,r.get(5)?))).optional().map_err(error)?; + let Some((state_key, generation, body, sha, expected_records, expected_fingerprint)) = prepared + else { + if db + .query_row("SELECT 1 FROM receipt_prepare_rows UNION ALL SELECT 1 FROM receipt_prepare_routes LIMIT 1", [], |r| { + r.get::<_, i64>(0) + }) + .optional() + .map_err(error)? + .is_some() + { + return Err(invalid("receipt redo rows have no owning commit")); + } + return Ok(()); + }; + verify(&body, &sha)?; + let current: (Revision, Vec, Vec) = db + .query_row( + "SELECT generation,CASE WHEN length(bytes)<=33554432 THEN bytes END,CASE WHEN length(digest)=32 THEN digest END FROM current_state WHERE key=?1", + [&state_key], + |r| Ok((r.get(0)?, r.get(1)?, r.get(2)?)), + ) + .map_err(error)?; + if current != (generation, body.clone(), sha.clone()) + || expected_records < 0 + || expected_records > 4096 + || expected_fingerprint.len() != 32 + { + return Err(invalid( + "prepared receipt commit differs from current authority", + )); + } + let mut observed_records = 0i64; + let mut observed_fingerprint = vec![0; 32]; + let migrating = migration(db)?; + let mut cursor = String::new(); + let mut touched = std::collections::BTreeSet::new(); + loop { + let mut groups = std::collections::BTreeMap::>::new(); + let mut bytes = 0; + let mut count = 0; + while count < 128 && bytes < PAGE_BYTES { + let row: Option<(String,String,String,Vec,Vec)> = db.query_row("SELECT id,namespace,receipt_key,CASE WHEN length(body)<=33554432 THEN body END,digest FROM receipt_prepare_rows WHERE id>?1 ORDER BY id LIMIT 1", [&cursor], |r| Ok((r.get(0)?,r.get(1)?,r.get(2)?,r.get(3)?,r.get(4)?))).optional().map_err(error)?; + let Some((id, namespace, key, body, sha)) = row else { + break; + }; + verify(&body, &sha)?; + observed_records += 1; + observed_fingerprint = xor(&observed_fingerprint, &identity_digest(&id, &body)); + if id != logical(&namespace, &key) { + return Err(invalid("prepared receipt binding mismatch")); + } + let (lower, file) = route(db, &id)?; + bytes += body.len(); + count += 1; + let record = ExactReceipt { + namespace, + key, + bytes: body, + }; + if let Some(migration) = &migrating { + if migration.source == file { + groups + .entry(target(migration, &id).into()) + .or_default() + .push(record.clone()); + } + } + groups.entry(file).or_default().push(record); + touched.insert(lower); + cursor = id; + } + if count == 0 { + break; + } + for (file, records) in groups { + insert_batch(&mut open_shard(&resolve_file(db, &file)?, false)?, &records)?; + } + } + if observed_records != expected_records || observed_fingerprint != expected_fingerprint { + return Err(invalid("prepared receipt set is incomplete or changed")); + } + let tx = db + .transaction_with_behavior(TransactionBehavior::Immediate) + .map_err(error)?; + let expected_routes: i64 = tx + .query_row("SELECT count(*) FROM receipt_prepare_routes", [], |r| { + r.get(0) + }) + .map_err(error)?; + if expected_routes != touched.len() as i64 { + return Err(invalid( + "prepared receipt routing set is incomplete or changed", + )); + } + for lower in touched { + let (_, file) = route(&tx, &lower)?; + let expected: (String,i64,i64,Vec) = tx.query_row("SELECT file,records,bytes,CASE WHEN length(fingerprint)=32 THEN fingerprint END FROM receipt_prepare_routes WHERE lower_key=?1", [&lower], |r| Ok((r.get(0)?,r.get(1)?,r.get(2)?,r.get(3)?))).map_err(error)?; + let (records, bytes, fingerprint) = totals(&tx, &file)?; + if expected != (file.clone(), records, bytes, fingerprint.clone()) { + return Err(invalid( + "prepared receipt partition is incomplete or changed", + )); + } + routes::put( + &tx, + "routes", + routes::Route { + lower, + file, + records, + bytes, + fingerprint, + }, + )?; + } + tx.execute("INSERT INTO current_state(key,generation,bytes,digest) VALUES(?1,?2,?3,?4) ON CONFLICT(key) DO UPDATE SET generation=excluded.generation,bytes=excluded.bytes,digest=excluded.digest",params![state_key,generation,body,sha]).map_err(error)?; + tx.execute_batch("DELETE FROM receipt_prepare_rows; DELETE FROM receipt_prepare_routes; DELETE FROM receipt_prepare;") + .map_err(error)?; + tx.commit().map_err(error) +} + +pub(crate) fn commit( + db: &mut Connection, + key: &str, + expected: impl Into, + bytes: &[u8], + records: &[ExactReceipt], +) -> Result { + let expected = expected.into(); + recover(db)?; + let generation = prepare(db, key, expected, bytes, records)?; + recover(db)?; + Ok(generation) +} + +pub(crate) fn commit_with_work( + db: &mut Connection, + key: &str, + expected: impl Into, + bytes: &[u8], + records: &[ExactReceipt], + work: &[crate::indexed_work::WorkChange], +) -> Result { + let expected = expected.into(); + commit_operation(db, key, expected, bytes, records, work, None) +} + +pub(crate) fn commit_operation( + db: &mut Connection, + key: &str, + expected: impl Into, + bytes: &[u8], + records: &[ExactReceipt], + work: &[crate::indexed_work::WorkChange], + operation: Option<&str>, +) -> Result { + let expected = expected.into(); + recover(db)?; + if let Some(operation) = operation { + if let Some(generation) = + crate::indexed_commit::replay(db, key, operation, expected, bytes, records, work)? + { + return Ok(generation); + } + } + let generation = prepare_with_operation(db, key, expected, bytes, records, work, operation)?; + recover(db)?; + Ok(generation) +} + +fn prepare( + db: &mut Connection, + key: &str, + expected: impl Into, + bytes: &[u8], + records: &[ExactReceipt], +) -> Result { + let expected = expected.into(); + prepare_with_work(db, key, expected, bytes, records, &[]) +} + +fn prepare_with_work( + db: &mut Connection, + key: &str, + expected: impl Into, + bytes: &[u8], + records: &[ExactReceipt], + work: &[crate::indexed_work::WorkChange], +) -> Result { + let expected = expected.into(); + prepare_with_operation(db, key, expected, bytes, records, work, None) +} + +fn prepare_with_operation( + db: &mut Connection, + key: &str, + expected: impl Into, + bytes: &[u8], + records: &[ExactReceipt], + work: &[crate::indexed_work::WorkChange], + operation: Option<&str>, +) -> Result { + let expected = expected.into(); + let generation = expected.next()?; + let tx = db + .transaction_with_behavior(TransactionBehavior::Immediate) + .map_err(error)?; + let current: Option = tx + .query_row( + "SELECT generation FROM current_state WHERE key=?1", + [key], + |r| r.get(0), + ) + .optional() + .map_err(error)?; + if current.unwrap_or_default() != expected { + return Err(invalid("stale indexed store generation")); + } + if !work.is_empty() { + crate::indexed_work::apply(&tx, work)?; + } + let mut previous_shard: Option<(String, Connection)> = None; + for record in records { + let id = logical(&record.namespace, &record.key); + let (lower, file) = route(&tx, &id)?; + if previous_shard + .as_ref() + .is_none_or(|(path, _)| path != &file) + { + previous_shard = Some((file.clone(), checked_shard(&tx, &lower, &file)?)); + } + let existing = shard_receipt(&previous_shard.as_ref().unwrap().1, &id)?; + if let Some(previous) = &existing { + if previous != &record.bytes { + return Err(invalid("exact receipt replay conflict")); + } + } + // Duplicate identities inside one transaction must compare too. + let id = logical(&record.namespace, &record.key); + let previous: Option> = tx + .query_row( + "SELECT body FROM receipt_prepare_rows WHERE id=?1", + [&id], + |r| r.get(0), + ) + .optional() + .map_err(error)?; + if previous.as_ref().is_some_and(|body| body != &record.bytes) { + return Err(invalid("exact receipt replay conflict")); + } + let expected_route = routes::exact(&tx, "routes", &lower)? + .ok_or_else(|| invalid("prepared route is missing"))?; + tx.execute( + "INSERT OR IGNORE INTO receipt_prepare_routes VALUES(?1,?2,?3,?4,?5)", + params![ + lower, + expected_route.file, + expected_route.records, + expected_route.bytes, + expected_route.fingerprint + ], + ) + .map_err(error)?; + if existing.is_none() && previous.is_none() { + let fingerprint: Vec = tx + .query_row( + "SELECT fingerprint FROM receipt_prepare_routes WHERE lower_key=?1", + [&lower], + |r| r.get(0), + ) + .map_err(error)?; + tx.execute("UPDATE receipt_prepare_routes SET records=records+1,bytes=bytes+?2,fingerprint=?3 WHERE lower_key=?1", params![lower,(record.bytes.len()+id.len()+64) as i64,xor(&fingerprint,&identity_digest(&id,&record.bytes))]).map_err(error)?; + } + tx.execute( + "INSERT OR IGNORE INTO receipt_prepare_rows VALUES(?1,?2,?3,?4,?5)", + params![ + id, + record.namespace, + record.key, + record.bytes, + digest(&record.bytes) + ], + ) + .map_err(error)?; + } + let mut prepared_count = 0i64; + let mut prepared_fingerprint = vec![0; 32]; + { + let mut query = tx + .prepare("SELECT id,body FROM receipt_prepare_rows ORDER BY id") + .map_err(error)?; + let mut rows = query.query([]).map_err(error)?; + while let Some(row) = rows.next().map_err(error)? { + let id: String = row.get(0).map_err(error)?; + let body: Vec = row.get(1).map_err(error)?; + prepared_count += 1; + prepared_fingerprint = xor(&prepared_fingerprint, &identity_digest(&id, &body)); + } + } + tx.execute( + "INSERT INTO receipt_prepare VALUES(1,?1,?2,?3,?4,?5,?6)", + params![ + key, + generation, + bytes, + digest(bytes), + prepared_count, + prepared_fingerprint + ], + ) + .map_err(error)?; + // The root transaction is the durability point: current state and every + // referenced new receipt body are durable together. Leaf materialization + // can be repeated after death without replaying historical work. + tx.execute("INSERT INTO current_state(key,generation,bytes,digest) VALUES(?1,?2,?3,?4) ON CONFLICT(key) DO UPDATE SET generation=excluded.generation,bytes=excluded.bytes,digest=excluded.digest",params![key,generation,bytes,digest(bytes)]).map_err(error)?; + if let Some(operation) = operation { + crate::indexed_commit::record( + &tx, key, operation, expected, generation, bytes, records, work, + )?; + } + tx.commit().map_err(error)?; + Ok(generation) +} + +pub(crate) fn page( + db: &Connection, + namespace: &str, + after: &str, + limit: usize, + budget: usize, +) -> Result> { + let mut cursor = logical(namespace, after); + let (mut lower, mut file) = route(db, &cursor)?; + let end = format!("{namespace}\u{1}"); + let mut records = Vec::new(); + let mut bytes = 0; + loop { + let shard = checked_shard(db, &lower, &file)?; + let mut statement=shard.prepare("SELECT id,receipt_key,length(body) FROM receipt_rows WHERE id>?1 AND id MAX_BODY { + return Err(invalid("invalid receipt size")); + } + if bytes + length as usize > budget { + if records.is_empty() { + return Err(invalid( + "storage_pressure: receipt exceeds page byte capacity", + )); + } + return Ok(records); + } + cursor = row.get(0).map_err(error)?; + let key: String = row.get(1).map_err(error)?; + let body = + shard_receipt(&shard, &cursor)?.ok_or_else(|| invalid("receipt disappeared"))?; + bytes += body.len(); + records.push(ExactReceipt { + namespace: namespace.into(), + key, + bytes: body, + }); + if records.len() == limit { + return Ok(records); + } + } + let next = routes::next(db, "routes", Some(&lower))? + .filter(|r| r.lower < end) + .map(|r| (r.lower, r.file)); + let Some((next_lower, next_file)) = next else { + return Ok(records); + }; + lower = next_lower; + file = next_file; + } +} + +/// Copy at most one bounded page per call. Concurrent writes are mirrored by +/// recover(); the source remains authoritative until verified atomic cutover. +pub(crate) fn maintain(db: &mut Connection, path: &Path) -> Result<()> { + if let Some(m) = migration(db)? { + let source = checked_shard(db, &m.lower, &m.source)?; + let after = m.cursor.clone().unwrap_or_default(); + let mut statement=source.prepare("SELECT id,namespace,receipt_key,length(body) FROM receipt_rows WHERE id>?1 AND id<=?2 ORDER BY id LIMIT 128").map_err(error)?; + let mut rows = statement.query(params![after, m.upper]).map_err(error)?; + let mut cursor = None; + let mut bytes = 0; + let mut groups = std::collections::BTreeMap::>::new(); + while let Some(row) = rows.next().map_err(error)? { + let length: i64 = row.get(3).map_err(error)?; + if length < 0 || length as usize > MAX_BODY { + return Err(invalid("invalid migration record size")); + } + if cursor.is_some() && bytes + length as usize > PAGE_BYTES { + break; + } + let id: String = row.get(0).map_err(error)?; + let body = shard_receipt(&source, &id)? + .ok_or_else(|| invalid("migration source disappeared"))?; + groups + .entry(target(&m, &id).into()) + .or_default() + .push(ExactReceipt { + namespace: row.get(1).map_err(error)?, + key: row.get(2).map_err(error)?, + bytes: body, + }); + bytes += length as usize; + cursor = Some(id); + } + drop(rows); + drop(statement); + drop(source); + for (file, records) in groups { + insert_batch(&mut open_shard(&resolve_file(db, &file)?, false)?, &records)?; + } + if let Some(cursor) = cursor { + db.execute( + "UPDATE receipt_migration SET cursor=?1 WHERE singleton=1", + [cursor], + ) + .map_err(error)?; + return Ok(()); + } + let (count, size, sha) = totals(db, &m.source)?; + let (a_count, a_size, a_sha) = totals(db, &m.left)?; + let (b_count, b_size, b_sha) = match &m.right { + Some(file) => totals(db, file)?, + None => (0, 0, vec![0; 32]), + }; + if count != a_count + b_count || size != a_size + b_size || sha != xor(&a_sha, &b_sha) { + return Err(invalid("receipt partition verification failed")); + } + let tx = db + .transaction_with_behavior(TransactionBehavior::Immediate) + .map_err(error)?; + routes::put( + &tx, + "routes", + routes::Route { + lower: m.lower.clone(), + file: m.left, + records: a_count, + bytes: a_size, + fingerprint: a_sha, + }, + )?; + if let (Some(pivot), Some(right)) = (&m.pivot, &m.right) { + routes::put( + &tx, + "routes", + routes::Route { + lower: pivot.clone(), + file: right.clone(), + records: b_count, + bytes: b_size, + fingerprint: b_sha, + }, + )?; + } + routes::put( + &tx, + "retired", + routes::Route::empty(m.source.clone(), m.source), + )?; + tx.execute("DELETE FROM receipt_migration", []) + .map_err(error)?; + tx.commit().map_err(error)?; + return Ok(()); + } + let backup_active: bool = db + .query_row("SELECT EXISTS(SELECT 1 FROM receipt_backup)", [], |r| { + r.get(0) + }) + .map_err(error)?; + // An immutable backup root pins its entire reachable receipt prefix. There + // is no foreground enumeration or ever-growing pin list in the root DB. + let retired = if backup_active { + None + } else { + routes::next(db, "retired", None)?.map(|r| r.file) + }; + if let Some(file) = retired { + let absolute = resolve_file(db, &file)?; + for suffix in ["-wal", "-shm", ""] { + match fs::remove_file(format!("{}{suffix}", absolute.display())) { + Ok(()) => {} + Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} + Err(e) => return Err(error(e)), + } + } + fs::File::open(absolute.parent().unwrap()) + .and_then(|f| f.sync_all()) + .map_err(error)?; + routes::remove(db, "retired", &file)?; + } + let threshold = db + .query_row( + "SELECT partition_bytes FROM receipt_storage_config WHERE singleton=1", + [], + |r| r.get(0), + ) + .map_err(error)?; + let oversized = routes::oversized(db, threshold)?.map(|r| (r.lower, r.file, r.records)); + if let Some((lower, file, count)) = oversized { + let pivot: String = checked_shard(db, &lower, &file)? + .query_row( + "SELECT id FROM receipt_rows ORDER BY id LIMIT 1 OFFSET ?1", + [count / 2], + |r| r.get(0), + ) + .map_err(error)?; + let dir = directory(path)?; + let left = local_name(path, &new_shard(&dir)?)?; + let right = local_name(path, &new_shard(&dir)?)?; + let upper: String = checked_shard(db, &lower, &file)? + .query_row( + "SELECT id FROM receipt_rows ORDER BY id DESC LIMIT 1", + [], + |r| r.get(0), + ) + .map_err(error)?; + db.execute( + "INSERT INTO receipt_migration VALUES(1,?1,?2,?3,?4,?5,NULL,?6)", + params![lower, file, pivot, left, right, upper], + ) + .map_err(error)?; + } + Ok(()) +} + +pub(crate) fn relocate(db: &Connection, lower: &str, destination: &Path) -> Result<()> { + if migration(db)?.is_some() { + return Err(invalid( + "storage_pressure: a receipt migration is already active", + )); + } + crate::durable::verify_private_directory(destination)?; + let source = routes::exact(db, "routes", lower)? + .ok_or_else(|| invalid("relocation route is missing"))? + .file; + let target = new_shard(destination)?; + let upper: String = checked_shard(db, lower, &source)? + .query_row("SELECT coalesce(max(id),'') FROM receipt_rows", [], |r| { + r.get(0) + }) + .map_err(error)?; + db.execute( + "INSERT INTO receipt_migration VALUES(1,?1,?2,NULL,?3,NULL,NULL,?4)", + params![lower, source, target, upper], + ) + .map_err(error)?; + Ok(()) +} + +pub(crate) fn partitions( + db: &Connection, + after: Option<&str>, + limit: usize, +) -> Result> { + let mut cursor = after.map(str::to_owned); + let mut result = Vec::new(); + while result.len() < limit { + let Some(row) = routes::next(db, "routes", cursor.as_deref())? else { + break; + }; + cursor = Some(row.lower.clone()); + result.push(crate::indexed_store::ReceiptPartition { + lower_key: row.lower, + file: row.file, + records: row.records as u64, + bytes: row.bytes as u64, + }); + } + Ok(result) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::indexed_store::IndexedStore; + fn fixture() -> (PathBuf, IndexedStore) { + let root = + std::env::temp_dir().join(format!("paperclip-partitions-{}", uuid::Uuid::new_v4())); + let path = root.join("authority.sqlite"); + let store = IndexedStore::open(&path, "partition-test", true).unwrap(); + (path, store) + } + + #[test] + fn missing_committed_routing_head_cannot_bootstrap_an_empty_history() { + let (path, store) = fixture(); + store + .commit("authority", 0, b"current".to_vec(), vec![record(0)]) + .unwrap(); + drop(store); + let db = Connection::open(&path).unwrap(); + db.execute("DELETE FROM receipt_routing_heads WHERE name='routes'", []) + .unwrap(); + drop(db); + let error = IndexedStore::open(&path, "partition-test", false).unwrap_err(); + assert!(error.to_string().contains("routing authority is missing")); + fs::remove_dir_all(path.parent().unwrap()).unwrap(); + } + fn record(i: u64) -> ExactReceipt { + ExactReceipt { + namespace: "session/tools".into(), + key: format!("call-{i:020}"), + bytes: format!("result-{i}").into_bytes(), + } + } + + #[test] + fn revision_boundary_recovers_redo_replays_exactly_and_rejects_stale_writers() { + let (path, store) = fixture(); + store + .commit("authority", 0, b"before".to_vec(), vec![record(0)]) + .unwrap(); + drop(store); + let mut db = Connection::open(&path).unwrap(); + db.execute( + "UPDATE current_state SET generation=?1 WHERE key='authority'", + [i64::MAX], + ) + .unwrap(); + let last = Revision::from(i64::MAX as u64); + let current = prepare_with_operation( + &mut db, + "authority", + last, + b"accepted", + &[record(1)], + &[], + Some("boundary-operation"), + ) + .unwrap(); + assert!(matches!(current, Revision::Opaque(_))); + // Root commit survived; the receipt leaf and original reply did not. + drop(db); + let store = IndexedStore::open(&path, "partition-test", false).unwrap(); + assert_eq!( + store.read_state("authority").unwrap().unwrap().generation, + current + ); + assert_eq!( + store.receipt("session/tools", &record(0).key).unwrap(), + Some(record(0).bytes) + ); + assert_eq!( + store.receipt("session/tools", &record(1).key).unwrap(), + Some(record(1).bytes) + ); + drop(store); + let mut db = Connection::open(&path).unwrap(); + assert_eq!( + commit_operation( + &mut db, + "authority", + last, + b"accepted", + &[record(1)], + &[], + Some("boundary-operation") + ) + .unwrap(), + current + ); + assert!(commit_operation( + &mut db, + "authority", + last, + b"changed", + &[record(1)], + &[], + Some("boundary-operation") + ) + .is_err()); + assert!(commit_operation( + &mut db, + "authority", + Revision::from(1), + b"accepted", + &[record(1)], + &[], + Some("boundary-operation") + ) + .is_err()); + assert!(commit_operation( + &mut db, + "authority", + last, + b"accepted", + &[record(1)], + &[], + Some("stale-writer") + ) + .is_err()); + let next = commit_operation( + &mut db, + "authority", + current, + b"continued", + &[], + &[], + Some("next-operation"), + ) + .unwrap(); + assert!(matches!(next, Revision::Opaque(_))); + assert_ne!(current, next); + assert!(commit_operation( + &mut db, + "authority", + last, + b"accepted", + &[record(1)], + &[], + Some("boundary-operation") + ) + .is_err()); + assert_eq!( + db.query_row("SELECT count(*) FROM current_commit", [], |r| r + .get::<_, i64>(0)) + .unwrap(), + 1 + ); + drop(db); + let store = IndexedStore::open(&path, "partition-test", false).unwrap(); + assert_eq!( + store.read_state("authority").unwrap().unwrap().generation, + next + ); + assert_eq!( + store.receipt("session/tools", &record(0).key).unwrap(), + Some(record(0).bytes) + ); + drop(store); + fs::remove_dir_all(path.parent().unwrap()).unwrap(); + } + + #[test] + fn capacity_retry_reconciles_prepared_commit_and_does_not_apply_work_twice() { + let (path, store) = fixture(); + store + .commit("authority", 0, b"before".to_vec(), vec![]) + .unwrap(); + drop(store); + let mut db = Connection::open(&path).unwrap(); + let work = [crate::indexed_work::WorkChange { + collection: "owners".into(), + key: "owner-1".into(), + expected_digest: None, + bytes: Some(b"live".to_vec()), + }]; + prepare_with_operation( + &mut db, + "authority", + 1, + b"accepted", + &[record(1)], + &work, + Some("attempt-1"), + ) + .unwrap(); + // Simulate a lost reply after the durability point, before the receipt + // leaf was materialized. Retry must recover redo and preserve gen 2. + assert_eq!( + commit_operation( + &mut db, + "authority", + 1, + b"accepted", + &[record(1)], + &work, + Some("attempt-1") + ) + .unwrap(), + 2 + ); + assert_eq!( + receipt(&db, "session/tools", &record(1).key).unwrap(), + Some(record(1).bytes) + ); + assert_eq!( + crate::indexed_work::get(&db, "owners", "owner-1") + .unwrap() + .unwrap() + .bytes, + b"live" + ); + assert!(commit_operation( + &mut db, + "authority", + 1, + b"changed", + &[record(1)], + &work, + Some("attempt-1") + ) + .is_err()); + assert!(commit_operation( + &mut db, + "authority", + 1, + b"accepted", + &[record(2)], + &work, + Some("attempt-1") + ) + .is_err()); + assert!(commit_operation( + &mut db, + "authority", + 1, + b"accepted", + &[record(1)], + &[], + Some("attempt-1") + ) + .is_err()); + // A different writer cannot reuse the operation's old expected gen. + assert!(commit_operation( + &mut db, + "authority", + 1, + b"accepted", + &[record(1)], + &work, + Some("different-owner") + ) + .is_err()); + commit_operation( + &mut db, + "authority", + 2, + b"new owner", + &[], + &[], + Some("attempt-2"), + ) + .unwrap(); + assert!(commit_operation( + &mut db, + "authority", + 1, + b"accepted", + &[record(1)], + &work, + Some("attempt-1") + ) + .is_err()); + assert_eq!( + db.query_row("SELECT count(*) FROM current_commit", [], |row| row + .get::<_, i64>(0)) + .unwrap(), + 1 + ); + drop(db); + fs::remove_dir_all(path.parent().unwrap()).unwrap(); + } + + #[cfg(unix)] + #[test] + fn process_death_after_prepare_and_partial_leaf_commit_recovers_exactly() { + use std::os::unix::process::ExitStatusExt; + const CHILD: &str = "PAPERCLIP_RECEIPT_CRASH_TEST"; + if let Ok(path) = std::env::var(CHILD) { + let path = PathBuf::from(path); + let store = IndexedStore::open(&path, "crash-test", true).unwrap(); + store + .commit("authority", 0, b"old".to_vec(), vec![record(0)]) + .unwrap(); + drop(store); + let mut db = Connection::open(&path).unwrap(); + prepare( + &mut db, + "authority", + 1, + b"accepted", + &[record(1), record(2)], + ) + .unwrap(); + if std::env::var("PAPERCLIP_RECEIPT_CRASH_PHASE").unwrap() == "leaf" { + let (_, file) = route(&db, &logical("session/tools", &record(1).key)).unwrap(); + insert_batch( + &mut open_shard(&resolve_file(&db, &file).unwrap(), false).unwrap(), + &[record(1)], + ) + .unwrap(); + } + // SIGKILL the isolated test child: no destructors or final database + // checkpoint can rescue durability on this path. + std::process::Command::new("/bin/kill") + .args(["-KILL", &std::process::id().to_string()]) + .status() + .unwrap(); + panic!("test child survived SIGKILL"); + } + for phase in ["prepare", "leaf"] { + let root = std::env::temp_dir().join(format!("receipt-crash-{}", uuid::Uuid::new_v4())); + let path = root.join("state.sqlite"); + let status=std::process::Command::new(std::env::current_exe().unwrap()) + .args(["--exact","indexed_partitions::tests::process_death_after_prepare_and_partial_leaf_commit_recovers_exactly","--nocapture"]) + .env(CHILD,&path).env("PAPERCLIP_RECEIPT_CRASH_PHASE",phase) + .stdout(std::process::Stdio::null()).stderr(std::process::Stdio::null()).status().unwrap(); + assert_eq!(status.signal(), Some(9)); + let store = IndexedStore::open(&path, "crash-test", false).unwrap(); + assert_eq!( + store.read_state("authority").unwrap().unwrap().bytes, + b"accepted" + ); + for i in 0..3 { + assert_eq!( + store.receipt("session/tools", &record(i).key).unwrap(), + Some(record(i).bytes) + ); + } + drop(store); + fs::remove_dir_all(root).unwrap(); + } + } + + #[test] + fn exhausted_root_capacity_preserves_previous_authority_and_reports_pressure() { + let (path, store) = fixture(); + store + .commit("authority", 0, b"old".to_vec(), vec![record(0)]) + .unwrap(); + drop(store); + let mut db = Connection::open(&path).unwrap(); + let pages: i64 = db.query_row("PRAGMA page_count", [], |r| r.get(0)).unwrap(); + db.execute_batch(&format!("PRAGMA max_page_count={pages};")) + .unwrap(); + let large = ExactReceipt { + bytes: vec![8; 2 * 1024 * 1024], + ..record(1) + }; + let failure = prepare(&mut db, "authority", 1, b"new", &[large]).unwrap_err(); + assert!( + failure.to_string().starts_with("storage_pressure:"), + "{failure}" + ); + assert_eq!( + db.query_row( + "SELECT generation FROM current_state WHERE key='authority'", + [], + |r| r.get::<_, i64>(0) + ) + .unwrap(), + 1 + ); + assert_eq!( + db.query_row("SELECT count(*) FROM receipt_prepare", [], |r| r + .get::<_, i64>(0)) + .unwrap(), + 0 + ); + assert_eq!( + receipt(&db, "session/tools", &record(0).key).unwrap(), + Some(record(0).bytes) + ); + drop(db); + fs::remove_dir_all(path.parent().unwrap()).unwrap(); + } + + #[test] + fn tiny_receipt_batches_do_not_outgrow_partition_maintenance() { + let (path, store) = fixture(); + let db = Connection::open(&path).unwrap(); + const PARTITION_BYTES: i64 = 64 * 1024; + db.execute( + "UPDATE receipt_storage_config SET partition_bytes=?1", + [PARTITION_BYTES], + ) + .unwrap(); + // Each batch is the admitted row limit but has no body bytes. Byte-only + // maintenance credits copy just 128 rows while admitting another 4096. + // Check immediately after writes, without idle time or manual drains. + for batch in 0..8u64 { + let records = (batch * 4096..(batch + 1) * 4096) + .map(|id| ExactReceipt { + namespace: "small".into(), + key: format!("receipt-{id:020}"), + bytes: Vec::new(), + }) + .collect(); + store + .commit("authority", batch, b"current".to_vec(), records) + .unwrap(); + let mut largest = 0; + let mut cursor = None; + while let Some(row) = routes::next(&db, "routes", cursor.as_deref()).unwrap() { + cursor = Some(row.lower); + largest = largest.max(row.bytes); + } + assert!( + largest <= PARTITION_BYTES * 8, + "batch {batch}: live partition grew to {largest} bytes" + ); + } + assert_eq!( + store + .receipt("small", "receipt-00000000000000000000") + .unwrap(), + Some(Vec::new()) + ); + assert_eq!( + store + .receipt("small", "receipt-00000000000000032767") + .unwrap(), + Some(Vec::new()) + ); + drop(db); + drop(store); + let reopened = IndexedStore::open(&path, "partition-test", false).unwrap(); + assert_eq!( + reopened + .read_state("authority") + .unwrap() + .unwrap() + .generation, + 8 + ); + assert_eq!( + reopened + .receipt("small", "receipt-00000000000000000000") + .unwrap(), + Some(Vec::new()) + ); + drop(reopened); + fs::remove_dir_all(path.parent().unwrap()).unwrap(); + } + + #[test] + fn tiny_partitions_split_with_concurrent_writes_and_keep_ancient_replay_and_pages() { + let (path, store) = fixture(); + let db = Connection::open(&path).unwrap(); + db.execute("UPDATE receipt_storage_config SET partition_bytes=1024", []) + .unwrap(); + for generation in 0..100 { + store + .commit( + "authority", + generation, + b"bounded".to_vec(), + (generation * 10..generation * 10 + 10) + .map(record) + .collect(), + ) + .unwrap(); + } + for _ in 0..800 { + store.maintain().unwrap(); + } + let partitions = partitions(&db, None, 1000).unwrap().len(); + assert!(partitions > 20, "only {partitions} partitions"); + assert_eq!( + store.receipt("session/tools", &record(0).key).unwrap(), + Some(record(0).bytes) + ); + let mut cursor = String::new(); + let mut seen = 0; + loop { + let page = store + .receipt_page("session/tools", &cursor, 17, MAX_BODY) + .unwrap(); + if page.is_empty() { + break; + } + for entry in &page { + assert_eq!(entry.bytes, record(seen).bytes); + seen += 1; + } + cursor = page.last().unwrap().key.clone(); + } + assert_eq!(seen, 1000); + drop(db); + drop(store); + let reopened = IndexedStore::open(&path, "partition-test", false).unwrap(); + assert_eq!( + reopened + .read_state("authority") + .unwrap() + .unwrap() + .generation, + 100 + ); + assert_eq!( + reopened.receipt("session/tools", &record(0).key).unwrap(), + Some(record(0).bytes) + ); + let mut conflict = record(0); + conflict.bytes = b"different".to_vec(); + assert!(reopened + .commit("authority", 100, b"bad".to_vec(), vec![conflict]) + .is_err()); + drop(reopened); + fs::remove_dir_all(path.parent().unwrap()).unwrap(); + } + + #[test] + fn prepared_commit_rolls_forward_after_reopen_and_missing_partition_never_means_absence() { + let (path, store) = fixture(); + store + .commit("authority", 0, b"old".to_vec(), vec![record(0)]) + .unwrap(); + drop(store); + // Simulate death after durable intent, before all leaf writes/current + // publication. Opening must finish the same accepted transaction. + let mut db = Connection::open(&path).unwrap(); + let r = record(1); + prepare(&mut db, "authority", 1, b"new", std::slice::from_ref(&r)).unwrap(); + drop(db); + let reopened = IndexedStore::open(&path, "partition-test", false).unwrap(); + assert_eq!( + reopened.read_state("authority").unwrap().unwrap().bytes, + b"new" + ); + assert_eq!( + reopened.receipt(&r.namespace, &r.key).unwrap(), + Some(r.bytes) + ); + drop(reopened); + let db = Connection::open(&path).unwrap(); + let (_, file) = route(&db, "session/tools\0call").unwrap(); + fs::remove_file(resolve_file(&db, &file).unwrap()).unwrap(); + drop(db); + let reopened = IndexedStore::open(&path, "partition-test", false).unwrap(); + assert!(reopened.receipt("session/tools", &record(0).key).is_err()); + drop(reopened); + fs::remove_dir_all(path.parent().unwrap()).unwrap(); + } + + #[test] + fn relocation_survives_restart_mid_copy_and_preserves_relative_archive_paths() { + let (path, store) = fixture(); + store + .commit( + "authority", + 0, + b"current".to_vec(), + (0..300).map(record).collect(), + ) + .unwrap(); + let volume = path.parent().unwrap().join("volume"); + fs::create_dir(&volume).unwrap(); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + fs::set_permissions(&volume, fs::Permissions::from_mode(0o700)).unwrap(); + } + store.relocate_partition("", &volume).unwrap(); + store.maintain().unwrap(); + drop(store); + let reopened = IndexedStore::open(&path, "partition-test", false).unwrap(); + reopened + .commit("authority", 1, b"continued".to_vec(), vec![record(301)]) + .unwrap(); + for _ in 0..10 { + reopened.maintain().unwrap(); + } + assert_eq!( + reopened.receipt("session/tools", &record(0).key).unwrap(), + Some(record(0).bytes) + ); + assert_eq!( + reopened.receipt("session/tools", &record(301).key).unwrap(), + Some(record(301).bytes) + ); + let db = Connection::open(&path).unwrap(); + assert_eq!(migration(&db).unwrap().map(|_| ()), None); + let (_, file) = route(&db, "session/tools\0call").unwrap(); + assert!(Path::new(&file).starts_with(volume.canonicalize().unwrap())); + drop(db); + drop(reopened); + fs::remove_dir_all(path.parent().unwrap()).unwrap(); + } + #[test] + fn incomplete_redo_and_stale_current_generation_fail_closed() { + for fault in [ + "missing_receipt", + "changed_generation", + "orphan_receipt", + "missing_route", + ] { + let (path, store) = fixture(); + store + .commit("authority", 0, b"old".to_vec(), vec![record(0)]) + .unwrap(); + drop(store); + let mut db = Connection::open(&path).unwrap(); + prepare(&mut db, "authority", 1, b"new", &[record(1)]).unwrap(); + match fault { + "missing_receipt" => db.execute("DELETE FROM receipt_prepare_rows", []).unwrap(), + "changed_generation" => db + .execute("UPDATE current_state SET generation=3", []) + .unwrap(), + "missing_route" => db + .execute("DELETE FROM receipt_prepare_routes", []) + .unwrap(), + _ => db.execute("DELETE FROM receipt_prepare", []).unwrap(), + }; + drop(db); + assert!( + IndexedStore::open(&path, "partition-test", false).is_err(), + "accepted {fault}" + ); + fs::remove_dir_all(path.parent().unwrap()).unwrap(); + } + } + + #[test] + fn replaced_partition_cannot_turn_ancient_replay_into_a_new_effect() { + for during_redo in [false, true] { + let (path, store) = fixture(); + store + .commit("authority", 0, b"current".to_vec(), vec![record(0)]) + .unwrap(); + drop(store); + let mut db = Connection::open(&path).unwrap(); + if during_redo { + prepare(&mut db, "authority", 1, b"new", &[record(1)]).unwrap(); + } + let (_, file) = route(&db, "session/tools\0call").unwrap(); + let shard = open_shard(&resolve_file(&db, &file).unwrap(), false).unwrap(); + shard.execute_batch("DELETE FROM receipt_rows; UPDATE receipt_totals SET records=0,bytes=0,fingerprint=zeroblob(32);").unwrap(); + drop(shard); + drop(db); + let reopened = IndexedStore::open(&path, "partition-test", false); + if during_redo { + assert!(reopened.is_err()); + } else { + let reopened = reopened.unwrap(); + assert!(reopened.receipt("session/tools", &record(0).key).is_err()); + assert!(reopened + .commit("authority", 1, b"new".to_vec(), vec![record(0)]) + .is_err()); + assert_eq!( + reopened + .read_state("authority") + .unwrap() + .unwrap() + .generation, + 1 + ); + } + fs::remove_dir_all(path.parent().unwrap()).unwrap(); + } + } + + #[test] + fn local_partition_routes_survive_directory_archive_without_rewriting_history() { + let (path, store) = fixture(); + store + .commit("authority", 0, b"current".to_vec(), vec![record(0)]) + .unwrap(); + drop(store); + let archived = path.parent().unwrap().with_extension("archived"); + fs::rename(path.parent().unwrap(), &archived).unwrap(); + let reopened = + IndexedStore::open(&archived.join("authority.sqlite"), "partition-test", false) + .unwrap(); + assert_eq!( + reopened.receipt("session/tools", &record(0).key).unwrap(), + Some(record(0).bytes) + ); + drop(reopened); + fs::remove_dir_all(archived).unwrap(); + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/indexed_revision.rs b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_revision.rs new file mode 100644 index 00000000000..04f83a31743 --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_revision.rs @@ -0,0 +1,164 @@ +//! Exact CAS identities. Retained integer revisions keep their representation; +//! at the old boundary they switch permanently to opaque commit identities. +//! Revision order is never used to infer event or receipt order. +use crate::durable::DurableRunnerError; +use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, Value, ValueRef}; +use rusqlite::ToSql; +use std::{fmt, str::FromStr}; +use uuid::Uuid; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +pub enum Revision { + #[default] + Absent, + Legacy(u64), + Opaque(Uuid), + /// A writer with an indeterminate outcome cannot re-admit writes. + Fenced, +} + +impl Revision { + pub fn next(self) -> Result { + match self { + Self::Absent => Ok(Self::Legacy(1)), + Self::Legacy(n) if n > 0 && n < i64::MAX as u64 => Ok(Self::Legacy(n + 1)), + Self::Legacy(n) if n == i64::MAX as u64 => Ok(Self::Opaque(Uuid::new_v4())), + Self::Opaque(previous) => { + let next = Uuid::new_v4(); + if next == previous { + return Err(DurableRunnerError::invalid( + "fresh indexed revision unavailable", + )); + } + Ok(Self::Opaque(next)) + } + _ => Err(DurableRunnerError::invalid("indexed writer is fenced")), + } + } + + pub(crate) fn legacy(self) -> Option { + match self { + Self::Absent => Some(0), + Self::Legacy(n) => Some(n), + _ => None, + } + } + + pub(crate) fn fingerprint(self) -> Vec { + // Preserve v1 retry fingerprints for retained integer commits. + match self.legacy() { + Some(n) => n.to_be_bytes().to_vec(), + None => self.to_string().into_bytes(), + } + } +} + +impl From for Revision { + fn from(value: u64) -> Self { + match value { + 0 => Self::Absent, + n if n <= i64::MAX as u64 => Self::Legacy(n), + _ => Self::Fenced, + } + } +} +impl PartialEq for Revision { + fn eq(&self, other: &u64) -> bool { + self.legacy() == Some(*other) + } +} +impl fmt::Display for Revision { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Absent => f.write_str("0"), + Self::Legacy(n) => write!(f, "{n}"), + Self::Opaque(id) => write!(f, "r:{id}"), + Self::Fenced => f.write_str("fenced"), + } + } +} +impl FromStr for Revision { + type Err = DurableRunnerError; + fn from_str(value: &str) -> Result { + if let Some(id) = value + .strip_prefix("r:") + .and_then(|s| Uuid::parse_str(s).ok()) + { + if id.get_version_num() == 4 + && id.get_variant() == uuid::Variant::RFC4122 + && value == format!("r:{id}") + { + return Ok(Self::Opaque(id)); + } + } else if let Ok(n) = value.parse::() { + if n <= i64::MAX as u64 && n.to_string() == value { + return Ok(n.into()); + } + } + Err(DurableRunnerError::invalid("invalid indexed revision")) + } +} +impl FromSql for Revision { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { + match value { + ValueRef::Integer(n) if n > 0 => Ok(Self::Legacy(n as u64)), + ValueRef::Text(bytes) => { + match std::str::from_utf8(bytes).ok().and_then(|s| s.parse().ok()) { + Some(revision @ Self::Opaque(_)) => Ok(revision), + _ => Err(FromSqlError::InvalidType), + } + } + _ => Err(FromSqlError::InvalidType), + } + } +} +impl ToSql for Revision { + fn to_sql(&self) -> rusqlite::Result> { + match self { + Self::Legacy(n) if *n > 0 && *n <= i64::MAX as u64 => { + Ok(ToSqlOutput::Owned(Value::Integer(*n as i64))) + } + Self::Opaque(_) => Ok(ToSqlOutput::Owned(Value::Text(self.to_string()))), + _ => Err(rusqlite::Error::InvalidParameterName( + "invalid persisted revision".into(), + )), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn transitions_at_exact_boundary_and_never_wraps() { + let last = Revision::from(i64::MAX as u64); + let first = last.next().unwrap(); + let second = first.next().unwrap(); + assert!(matches!(first, Revision::Opaque(_))); + assert_ne!(first, second); + for revision in [ + Revision::Absent, + Revision::from(9_007_199_254_740_993), + last, + first, + second, + ] { + assert_eq!(revision.to_string().parse::().unwrap(), revision); + } + assert!(Revision::Fenced.next().is_err()); + } + #[test] + fn rejects_ambiguous_or_invalid_encodings() { + for value in [ + "01", + "-1", + "9223372036854775808", + "r:00000000-0000-0000-0000-000000000001", + "fenced", + ] { + assert!(value.parse::().is_err(), "{value}"); + } + assert!(Revision::column_result(ValueRef::Integer(0)).is_err()); + assert!(Revision::column_result(ValueRef::Text(b"1")).is_err()); + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/indexed_routes.rs b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_routes.rs new file mode 100644 index 00000000000..65960b6ffa6 --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_routes.rs @@ -0,0 +1,924 @@ +//! Immutable, bounded B+tree pages. SQLite publishes only a root reference in +//! the same transaction as current authority. New pages are synced first, so a +//! rollback can leave unreachable pages but never a partially published tree. +//! Old pages stay immutable while an online snapshot holds their root. +use super::*; +use serde::{Deserialize, Serialize}; +use std::io::{Read, Write}; + +const FANOUT: usize = 32; +// Includes worst-case JSON escaping of every admitted key/path in 32 rows. +const MAX_PAGE_BYTES: usize = 2 * 1024 * 1024; + +#[path = "indexed_routes_gc.rs"] +mod gc; +pub(crate) use gc::RoutingCollector; + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub(super) struct Route { + pub lower: String, + pub file: String, + pub records: i64, + pub bytes: i64, + pub fingerprint: Vec, +} +impl Route { + pub(super) fn empty(lower: String, file: String) -> Self { + Self { + lower, + file, + records: 0, + bytes: 0, + fingerprint: vec![0; 32], + } + } + fn splittable_bytes(&self) -> i64 { + if self.records > 1 { + self.bytes + } else { + 0 + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +struct PageRef { + sha: String, + /// The current root fits in one bounded SQLite value. Updating the root + /// alone must not create/fsync a new filesystem object on every receipt. + #[serde(default, skip_serializing_if = "Option::is_none")] + inline: Option, + // Only an incomplete backup may reference another store. A finished + // backup verifies every reachable page is local before publication. + #[serde(default, skip_serializing_if = "Option::is_none")] + origin: Option, +} +#[derive(Clone, Debug, Serialize, Deserialize)] +struct Edge { + lower: String, + largest: i64, + page: PageRef, +} +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(tag = "kind")] +enum Node { + Leaf { rows: Vec }, + Branch { level: u16, children: Vec }, +} +impl Node { + fn level(&self) -> u16 { + match self { + Self::Leaf { .. } => 0, + Self::Branch { level, .. } => *level, + } + } + fn edge(&self, page: PageRef) -> Edge { + match self { + Self::Leaf { rows } => Edge { + lower: rows[0].lower.clone(), + largest: rows.iter().map(Route::splittable_bytes).max().unwrap(), + page, + }, + Self::Branch { children, .. } => Edge { + lower: children[0].lower.clone(), + largest: children.iter().map(|r| r.largest).max().unwrap(), + page, + }, + } + } + fn validate(&self) -> Result<()> { + let keys: Vec<&str> = match self { + Self::Leaf { rows } => { + if rows.iter().any(|r| { + r.lower.len() > 4096 + || r.file.len() > 4096 + || r.records < 0 + || r.bytes < 0 + || r.fingerprint.len() != 32 + }) { + return Err(invalid("invalid receipt routing row")); + } + rows.iter().map(|r| r.lower.as_str()).collect() + } + Self::Branch { level, children } => { + if *level == 0 + || children + .iter() + .any(|r| r.largest < 0 || !valid_ref(&r.page) || r.page.inline.is_some()) + { + return Err(invalid("invalid receipt routing branch")); + } + children.iter().map(|r| r.lower.as_str()).collect() + } + }; + if keys.is_empty() + || keys.len() > FANOUT + || keys.iter().any(|k| k.len() > 4096) + || keys.windows(2).any(|p| p[0] >= p[1]) + { + return Err(invalid("invalid receipt routing order/fanout")); + } + Ok(()) + } +} + +fn valid_ref(page: &PageRef) -> bool { + page.sha.len() == 64 + && page + .sha + .bytes() + .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b)) + && page + .inline + .as_ref() + .is_none_or(|value| value.len() <= MAX_PAGE_BYTES) + && page + .origin + .as_ref() + .is_none_or(|p| p.len() <= 4096 && Path::new(p).is_absolute()) +} +fn root_directory(db: &Connection) -> Result { + Ok(PathBuf::from(format!( + "{}.routing", + db.path() + .ok_or_else(|| invalid("routing root has no path"))? + ))) +} +fn private_directory(path: &Path) -> Result<()> { + let mut builder = fs::DirBuilder::new(); + #[cfg(unix)] + { + use std::os::unix::fs::DirBuilderExt; + builder.mode(0o700); + } + match builder.create(path) { + Ok(()) => fs::File::open(path.parent().unwrap()) + .and_then(|f| f.sync_all()) + .map_err(error)?, + Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {} + Err(e) => return Err(error(e)), + } + crate::durable::verify_private_directory(path) +} +fn page_path(db: &Connection, page: &PageRef, create: bool) -> Result { + if !valid_ref(page) { + return Err(invalid("invalid receipt routing reference")); + } + let root = page + .origin + .as_ref() + .map(PathBuf::from) + .unwrap_or(root_directory(db)?); + let directory = root.join(&page.sha[..2]).join(&page.sha[2..4]); + for p in [&root, &root.join(&page.sha[..2]), &directory] { + if create { + private_directory(p)?; + } else { + crate::durable::verify_private_directory(p)?; + } + } + Ok(directory.join(format!("{}.json", &page.sha[4..]))) +} +fn read_node(db: &Connection, page: &PageRef) -> Result { + #[cfg(test)] + READ_PAGES.with(|reads| reads.set(reads.get() + 1)); + if !valid_ref(page) { + return Err(invalid("invalid receipt routing reference")); + } + let bytes = if let Some(value) = &page.inline { + value.as_bytes().to_vec() + } else { + let path = page_path(db, page, false)?; + let file = crate::durable::open_private_regular_file(&path).map_err(error)?; + let mut bytes = Vec::new(); + file.take((MAX_PAGE_BYTES + 1) as u64) + .read_to_end(&mut bytes) + .map_err(error)?; + bytes + }; + if bytes.len() > MAX_PAGE_BYTES || format!("{:x}", Sha256::digest(&bytes)) != page.sha { + return Err(invalid("receipt routing page digest/size mismatch")); + } + let mut node: Node = serde_json::from_slice(&bytes).map_err(error)?; + node.validate()?; + if let Node::Branch { children, .. } = &mut node { + for child in children { + if child.page.origin.is_none() { + child.page.origin = page.origin.clone(); + } + } + } + Ok(node) +} +fn write_node(db: &Connection, node: &Node) -> Result { + node.validate()?; + let bytes = serde_json::to_vec(node).map_err(error)?; + if bytes.len() > MAX_PAGE_BYTES { + return Err(invalid("receipt routing page exceeds bound")); + } + let page = PageRef { + sha: format!("{:x}", Sha256::digest(&bytes)), + inline: None, + origin: None, + }; + let path = page_path(db, &page, true)?; + // A deterministic file name makes interrupted/repeated staging idempotent. + // Write through a fresh temporary inode and publish without replacing a + // pre-existing page. A conflicting existing file is an integrity failure. + let temporary = path.with_extension(format!("{}.tmp", uuid::Uuid::new_v4())); + let mut options = fs::OpenOptions::new(); + options.create_new(true).write(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + options.mode(0o600); + } + let mut file = options.open(&temporary).map_err(error)?; + file.write_all(&bytes) + .and_then(|_| file.sync_all()) + .map_err(error)?; + drop(file); + match fs::hard_link(&temporary, &path) { + Ok(()) => {} + Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => { + read_node(db, &page)?; + } + Err(e) => return Err(error(e)), + } + fs::remove_file(&temporary).map_err(error)?; + fs::File::open(path.parent().unwrap()) + .and_then(|f| f.sync_all()) + .map_err(error)?; + Ok(node.edge(page)) +} +fn inline_node(db: &Connection, node: &Node) -> Result { + node.validate()?; + let value = serde_json::to_string(node).map_err(error)?; + if value.len() > MAX_PAGE_BYTES { + return Err(invalid("receipt routing root exceeds bound")); + } + // Keep an owned directory even before the first root subdivision; archive + // and backup publication always carry the same complete store shape. + private_directory(&root_directory(db)?)?; + Ok(node.edge(PageRef { + sha: format!("{:x}", Sha256::digest(value.as_bytes())), + inline: Some(value), + origin: None, + })) +} + +pub(super) fn initialize(db: &Connection) -> Result<()> { + db.execute_batch("CREATE TABLE IF NOT EXISTS receipt_routing_heads(name TEXT PRIMARY KEY, root TEXT NOT NULL) WITHOUT ROWID;").map_err(error) +} +fn head(db: &Connection, name: &str) -> Result> { + let value: Option = db.query_row("SELECT CASE WHEN length(root)<=8388608 THEN root END FROM receipt_routing_heads WHERE name=?1", [name], |r| r.get(0)).optional().map_err(error)?; + value + .map(|v| serde_json::from_str::(&v).map_err(error)) + .transpose() +} +fn publish(db: &Connection, name: &str, edge: Option) -> Result<()> { + if let Some(edge) = edge { + db.execute("INSERT INTO receipt_routing_heads VALUES(?1,?2) ON CONFLICT(name) DO UPDATE SET root=excluded.root", params![name, serde_json::to_string(&edge).map_err(error)?]).map_err(error)?; + } else { + db.execute("DELETE FROM receipt_routing_heads WHERE name=?1", [name]) + .map_err(error)?; + } + Ok(()) +} +fn checked_child(db: &Connection, edge: &Edge, level: Option) -> Result { + let node = read_node(db, &edge.page)?; + let actual = node.edge(edge.page.clone()); + if actual.lower != edge.lower + || actual.largest != edge.largest + || level.is_some_and(|l| l != node.level()) + { + return Err(invalid("receipt routing child differs from parent")); + } + Ok(node) +} +pub(super) fn floor(db: &Connection, name: &str, key: &str) -> Result> { + let Some(mut edge) = head(db, name)? else { + return Ok(None); + }; + let mut level = None; + loop { + match checked_child(db, &edge, level)? { + Node::Leaf { rows } => { + return Ok(rows.into_iter().rev().find(|r| r.lower.as_str() <= key)) + } + Node::Branch { level: l, children } => { + let Some(next) = children.into_iter().rev().find(|r| r.lower.as_str() <= key) + else { + return Ok(None); + }; + edge = next; + level = Some(l - 1); + } + } + } +} +pub(super) fn exact(db: &Connection, name: &str, key: &str) -> Result> { + Ok(floor(db, name, key)?.filter(|r| r.lower == key)) +} +pub(super) fn next(db: &Connection, name: &str, after: Option<&str>) -> Result> { + next_page(db, name, after, false) +} +/// Verify only the next route's path. Background publication persists its +/// cursor between calls instead of holding the live source lock for a tree walk. +pub(super) fn next_local(db: &Connection, after: Option<&str>) -> Result> { + next_page(db, "routes", after, true) +} +fn next_page( + db: &Connection, + name: &str, + after: Option<&str>, + local: bool, +) -> Result> { + fn search( + db: &Connection, + edge: &Edge, + level: Option, + after: Option<&str>, + local: bool, + ) -> Result> { + if local && edge.page.origin.is_some() { + return Err(invalid("snapshot references external routing pages")); + } + match checked_child(db, edge, level)? { + Node::Leaf { rows } => Ok(rows + .into_iter() + .find(|r| after.is_none_or(|a| r.lower.as_str() > a))), + Node::Branch { level, children } => { + let start = after + .map(|a| { + children + .partition_point(|r| r.lower.as_str() <= a) + .saturating_sub(1) + }) + .unwrap_or(0); + for child in &children[start..] { + if let Some(row) = search(db, child, Some(level - 1), after, local)? { + return Ok(Some(row)); + } + } + Ok(None) + } + } + } + match head(db, name)? { + Some(edge) => search(db, &edge, None, after, local), + None if local => Err(invalid("snapshot has no routing head")), + None => Ok(None), + } +} +pub(super) fn oversized(db: &Connection, threshold: i64) -> Result> { + let Some(mut edge) = head(db, "routes")? else { + return Ok(None); + }; + let mut level = None; + loop { + let node = checked_child(db, &edge, level)?; + if edge.largest <= threshold { + return Ok(None); + } + match node { + Node::Leaf { rows } => { + return Ok(rows.into_iter().find(|r| r.splittable_bytes() > threshold)) + } + Node::Branch { level: l, children } => { + edge = children + .into_iter() + .find(|r| r.largest > threshold) + .ok_or_else(|| invalid("missing oversized route"))?; + level = Some(l - 1); + } + } + } +} +fn save_nodes(db: &Connection, node: Node, inline: bool) -> Result> { + match node { + Node::Leaf { mut rows } if rows.len() > FANOUT => { + let right = rows.split_off(rows.len() / 2); + Ok(vec![ + write_node(db, &Node::Leaf { rows })?, + write_node(db, &Node::Leaf { rows: right })?, + ]) + } + Node::Branch { + level, + mut children, + } if children.len() > FANOUT => { + let right = children.split_off(children.len() / 2); + Ok(vec![ + write_node(db, &Node::Branch { level, children })?, + write_node( + db, + &Node::Branch { + level, + children: right, + }, + )?, + ]) + } + node => Ok(vec![if inline { + inline_node(db, &node)? + } else { + write_node(db, &node)? + }]), + } +} +pub(super) fn put(db: &Connection, name: &str, row: Route) -> Result<()> { + fn insert( + db: &Connection, + edge: &Edge, + expected: Option, + row: Route, + inline: bool, + ) -> Result<(u16, Vec)> { + let mut node = checked_child(db, edge, expected)?; + let level = node.level(); + match &mut node { + Node::Leaf { rows } => { + let i = rows.partition_point(|r| r.lower < row.lower); + if rows.get(i).is_some_and(|r| r.lower == row.lower) { + rows[i] = row; + } else { + rows.insert(i, row); + } + } + Node::Branch { level, children } => { + let i = children + .partition_point(|r| r.lower <= row.lower) + .saturating_sub(1); + let (_, replacements) = insert(db, &children[i], Some(*level - 1), row, false)?; + children.splice(i..=i, replacements); + } + } + Ok((level, save_nodes(db, node, inline)?)) + } + let root = match head(db, name)? { + Some(edge) => { + let (level, mut replacements) = insert(db, &edge, None, row, true)?; + if replacements.len() == 1 { + replacements.remove(0) + } else { + inline_node( + db, + &Node::Branch { + level: level + .checked_add(1) + .ok_or_else(|| invalid("invalid routing depth"))?, + children: replacements, + }, + )? + } + } + None => inline_node(db, &Node::Leaf { rows: vec![row] })?, + }; + publish(db, name, Some(root)) +} +pub(super) fn remove(db: &Connection, name: &str, key: &str) -> Result<()> { + fn delete( + db: &Connection, + edge: Edge, + expected: Option, + key: &str, + inline: bool, + ) -> Result> { + let mut node = checked_child(db, &edge, expected)?; + match &mut node { + Node::Leaf { rows } => { + rows.retain(|r| r.lower != key); + if rows.is_empty() { + return Ok(None); + } + } + Node::Branch { level, children } => { + let i = children + .partition_point(|r| r.lower.as_str() <= key) + .saturating_sub(1); + let replacement = delete(db, children[i].clone(), Some(*level - 1), key, false)?; + children.splice(i..=i, replacement); + if children.is_empty() { + return Ok(None); + } + } + } + Ok(Some(if inline { + inline_node(db, &node)? + } else { + write_node(db, &node)? + })) + } + let root = match head(db, name)? { + Some(edge) => delete(db, edge, None, key, true)?, + None => None, + }; + publish(db, name, root) +} +pub(super) fn clear(db: &Connection, name: &str) -> Result<()> { + publish(db, name, None) +} +pub(super) fn rebase_snapshot(db: &Connection, source: &Connection) -> Result<()> { + let origin = root_directory(source)? + .canonicalize() + .map_err(error)? + .to_str() + .ok_or_else(|| invalid("invalid routing source path"))? + .to_owned(); + let mut edge = head(db, "routes")?.ok_or_else(|| invalid("missing snapshot routing head"))?; + if edge.page.origin.is_some() { + return Err(invalid( + "snapshot source references an external routing store", + )); + } + edge.page.origin = Some(origin); + publish(db, "routes", Some(edge))?; + clear(db, "retired") +} +pub(super) fn verify_local(db: &Connection) -> Result<()> { + fn walk(db: &Connection, edge: &Edge, level: Option) -> Result<()> { + if edge.page.origin.is_some() { + return Err(invalid("snapshot references external routing pages")); + } + if let Node::Branch { level, children } = checked_child(db, edge, level)? { + for child in children { + walk(db, &child, Some(level - 1))?; + } + } + Ok(()) + } + walk( + db, + &head(db, "routes")?.ok_or_else(|| invalid("snapshot has no routing head"))?, + None, + ) +} + +#[cfg(test)] +thread_local! { static READ_PAGES: std::cell::Cell = const { std::cell::Cell::new(0) }; } + +#[cfg(test)] +mod tests { + use super::*; + + fn fixture() -> (PathBuf, Connection) { + let directory = + std::env::temp_dir().join(format!("paperclip-routing-{}", uuid::Uuid::new_v4())); + private_directory(&directory).unwrap(); + let path = directory.join("root.sqlite"); + let mut options = fs::OpenOptions::new(); + options.create_new(true).write(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + options.mode(0o600); + } + options.open(&path).unwrap(); + let db = Connection::open(path).unwrap(); + initialize(&db).unwrap(); + db.execute_batch("CREATE TABLE receipt_backup(singleton INTEGER PRIMARY KEY); CREATE TABLE receipt_prepare(singleton INTEGER PRIMARY KEY);").unwrap(); + (directory, db) + } + fn row(id: usize) -> Route { + Route { + lower: format!("range-{id:08}"), + file: format!("root.sqlite.receipts/{id}.sqlite"), + records: 2, + bytes: (id + 1) as i64, + fingerprint: vec![id as u8; 32], + } + } + #[test] + fn routing_root_subdivides_and_reads_one_path_after_thousands_of_ranges() { + let (directory, mut db) = fixture(); + let tx = db.transaction().unwrap(); + for i in 0..1200 { + put(&tx, "routes", row(i)).unwrap(); + } + tx.commit().unwrap(); + let edge = head(&db, "routes").unwrap().unwrap(); + let height = checked_child(&db, &edge, None).unwrap().level(); + assert!( + height >= 2, + "test must split the routing root, not only leaves" + ); + assert_eq!( + db.query_row("SELECT count(*) FROM receipt_routing_heads", [], |r| r + .get::<_, i64>(0)) + .unwrap(), + 1 + ); + assert!( + db.query_row("SELECT length(root) FROM receipt_routing_heads", [], |r| { + r.get::<_, i64>(0) + }) + .unwrap() + < 32768 + ); + for id in [0, 599, 1199] { + READ_PAGES.with(|n| n.set(0)); + assert_eq!(exact(&db, "routes", &row(id).lower).unwrap(), Some(row(id))); + assert_eq!(READ_PAGES.with(|n| n.get()), height as usize + 1); + } + assert_eq!(oversized(&db, 1199).unwrap(), Some(row(1199))); + let mut cursor = None; + for id in 0..1200 { + READ_PAGES.with(|n| n.set(0)); + let found = next_local(&db, cursor.as_deref()).unwrap().unwrap(); + assert_eq!(found, row(id)); + assert!(READ_PAGES.with(|n| n.get()) <= 2 * (height as usize + 1)); + cursor = Some(found.lower); + } + assert_eq!(next(&db, "routes", cursor.as_deref()).unwrap(), None); + drop(db); + let db = Connection::open(directory.join("root.sqlite")).unwrap(); + assert_eq!(exact(&db, "routes", &row(0).lower).unwrap(), Some(row(0))); + fs::remove_dir_all(directory).unwrap(); + } + #[test] + fn root_rollback_preserves_exact_routes_and_missing_pages_are_not_misses() { + let (directory, mut db) = fixture(); + for i in 0..80 { + put(&db, "routes", row(i)).unwrap(); + } + let before = head(&db, "routes").unwrap().unwrap(); + { + let tx = db.transaction().unwrap(); + for i in 80..160 { + put(&tx, "routes", row(i)).unwrap(); + } + // Crash/rollback before the root transaction commits. + } + assert_eq!(head(&db, "routes").unwrap().unwrap().page, before.page); + assert_eq!(exact(&db, "routes", &row(0).lower).unwrap(), Some(row(0))); + assert_eq!(exact(&db, "routes", &row(159).lower).unwrap(), None); + let Node::Branch { children, .. } = read_node(&db, &before.page).unwrap() else { + panic!("root did not subdivide"); + }; + let path = page_path(&db, &children.last().unwrap().page, false).unwrap(); + fs::write(&path, b"changed routing node").unwrap(); + assert!(exact(&db, "routes", &row(159).lower).is_err()); + fs::remove_file(&path).unwrap(); + assert!(exact(&db, "routes", &row(159).lower).is_err()); + fs::remove_dir_all(directory).unwrap(); + } + #[test] + fn immutable_snapshot_root_survives_writes_and_becomes_independent_before_publication() { + let (source_directory, source) = fixture(); + let (target_directory, target) = fixture(); + for i in 0..80 { + put(&source, "routes", row(i)).unwrap(); + } + publish(&target, "routes", head(&source, "routes").unwrap()).unwrap(); + rebase_snapshot(&target, &source).unwrap(); + assert!(verify_local(&target).is_err()); + assert!(next_local(&target, None).is_err()); + for i in 80..120 { + put(&source, "routes", row(i)).unwrap(); + } + assert_eq!(exact(&target, "routes", &row(119).lower).unwrap(), None); + for i in 0..80 { + let mut expected = row(i); + expected.file = format!("root.sqlite.receipts/copied-{i}.sqlite"); + assert_eq!( + exact(&target, "routes", &row(i).lower).unwrap(), + Some(row(i)) + ); + put(&target, "routes", expected).unwrap(); + if i == 39 { + assert!(next_local(&target, Some(&row(78).lower)).is_err()); + } + } + verify_local(&target).unwrap(); + drop(source); + fs::remove_dir_all(source_directory).unwrap(); + assert_eq!( + exact(&target, "routes", &row(0).lower) + .unwrap() + .unwrap() + .file, + "root.sqlite.receipts/copied-0.sqlite" + ); + drop(target); + fs::remove_dir_all(target_directory).unwrap(); + } + #[test] + fn retired_inventory_is_paged_and_can_release_every_entry() { + let (directory, db) = fixture(); + for i in 0..80 { + put(&db, "retired", row(i)).unwrap(); + } + for i in 0..80 { + assert_eq!(next(&db, "retired", None).unwrap(), Some(row(i))); + remove(&db, "retired", &row(i).lower).unwrap(); + } + assert!(head(&db, "retired").unwrap().is_none()); + fs::remove_dir_all(directory).unwrap(); + } + + fn collect_cycle(db: &Connection, collector: &mut RoutingCollector) -> usize { + let mut most_reads = 0; + for _ in 0..100_000 { + READ_PAGES.with(|n| n.set(0)); + let complete = collector.step(db).unwrap(); + most_reads = most_reads.max(READ_PAGES.with(|n| n.get())); + if complete { + return most_reads; + } + } + panic!("routing collection did not finish its bounded steps"); + } + + fn page_count(directory: &Path) -> usize { + fs::read_dir(directory) + .unwrap() + .map(|entry| { + let path = entry.unwrap().path(); + if path.is_dir() { + page_count(&path) + } else { + 1 + } + }) + .sum() + } + + #[test] + fn collection_reclaims_rolled_back_and_replaced_pages_but_preserves_both_live_trees() { + let (directory, mut db) = fixture(); + for i in 0..1200 { + put(&db, "routes", row(i)).unwrap(); + } + for i in 0..80 { + put(&db, "retired", row(i + 2000)).unwrap(); + } + { + let tx = db.transaction().unwrap(); + for i in 1200..1280 { + put(&tx, "routes", row(i)).unwrap(); + } + assert!(RoutingCollector::default().step(&tx).is_err()); + } + let path = root_directory(&db).unwrap(); + let before = page_count(&path); + let mut collector = RoutingCollector::default(); + let most_reads = collect_cycle(&db, &mut collector); + assert!(most_reads <= 7, "one collection step traversed history"); + let after = page_count(&path); + assert!( + after < before / 4, + "obsolete pages were not reclaimed: {before} -> {after}" + ); + for i in 0..1200 { + assert_eq!(exact(&db, "routes", &row(i).lower).unwrap(), Some(row(i))); + } + for i in 0..80 { + assert_eq!( + exact(&db, "retired", &row(i + 2000).lower).unwrap(), + Some(row(i + 2000)) + ); + } + assert!(exact(&db, "routes", &row(1279).lower).unwrap().is_none()); + // Restart and a second complete sweep retain precisely the same live pages. + drop(collector); + drop(db); + let db = Connection::open(directory.join("root.sqlite")).unwrap(); + collect_cycle(&db, &mut RoutingCollector::default()); + assert_eq!(page_count(&path), after); + fs::remove_dir_all(directory).unwrap(); + } + + #[test] + fn collection_checks_fresh_roots_and_holds_all_pages_until_backup_finishes() { + let (directory, db) = fixture(); + let (target_directory, target) = fixture(); + for i in 0..80 { + put(&db, "routes", row(i)).unwrap(); + } + publish(&target, "routes", head(&db, "routes").unwrap()).unwrap(); + rebase_snapshot(&target, &db).unwrap(); + db.execute("INSERT INTO receipt_backup VALUES(1)", []) + .unwrap(); + let mut collector = RoutingCollector::default(); + for i in 0..80 { + let mut updated = row(i); + updated.bytes += 1000; + put(&db, "routes", updated).unwrap(); + assert!(!collector.step(&db).unwrap()); + } + for i in 0..80 { + assert_eq!( + exact(&target, "routes", &row(i).lower).unwrap(), + Some(row(i)) + ); + // Localize the backup before releasing its pin. + put(&target, "routes", row(i)).unwrap(); + } + verify_local(&target).unwrap(); + db.execute("DELETE FROM receipt_backup", []).unwrap(); + // Begin enumeration, then republish historical content. Liveness comes + // from today's roots, never a stale mark captured when a scan started. + assert!(!collector.step(&db).unwrap()); + for i in 0..80 { + put(&db, "routes", row(i)).unwrap(); + } + collect_cycle(&db, &mut collector); + for i in 0..80 { + assert_eq!(exact(&db, "routes", &row(i).lower).unwrap(), Some(row(i))); + } + drop(db); + fs::remove_dir_all(directory).unwrap(); + assert_eq!( + exact(&target, "routes", &row(0).lower).unwrap(), + Some(row(0)) + ); + fs::remove_dir_all(target_directory).unwrap(); + } + + #[test] + fn collection_holds_pending_prepares_and_rejects_corrupt_pages_without_deleting_them() { + let (directory, db) = fixture(); + for i in 0..80 { + put(&db, "routes", row(i)).unwrap(); + } + let obsolete = write_node( + &db, + &Node::Leaf { + rows: vec![row(9000)], + }, + ) + .unwrap(); + let path = page_path(&db, &obsolete.page, false).unwrap(); + db.execute("INSERT INTO receipt_prepare VALUES(1)", []) + .unwrap(); + let mut collector = RoutingCollector::default(); + for _ in 0..100 { + assert!(!collector.step(&db).unwrap()); + } + assert!(path.exists()); + db.execute("DELETE FROM receipt_prepare", []).unwrap(); + fs::write(&path, b"damaged").unwrap(); + let mut rejected = false; + for _ in 0..10_000 { + match collector.step(&db) { + Err(_) => { + rejected = true; + break; + } + Ok(true) => break, + Ok(false) => {} + } + } + assert!(rejected); + assert!(path.exists()); + assert_eq!(exact(&db, "routes", &row(0).lower).unwrap(), Some(row(0))); + fs::remove_dir_all(directory).unwrap(); + } + + #[test] + fn collection_removes_only_owned_temporaries_and_refuses_unknown_roots() { + let (directory, db) = fixture(); + put(&db, "routes", row(0)).unwrap(); + let page = write_node(&db, &Node::Leaf { rows: vec![row(9)] }).unwrap(); + let path = page_path(&db, &page.page, false).unwrap(); + let temporary = path.with_extension(format!("{}.tmp", uuid::Uuid::new_v4())); + fs::hard_link(&path, &temporary).unwrap(); + publish(&db, "future-root", head(&db, "routes").unwrap()).unwrap(); + assert!(RoutingCollector::default().step(&db).is_err()); + assert!(temporary.exists()); + clear(&db, "future-root").unwrap(); + collect_cycle(&db, &mut RoutingCollector::default()); + assert!(!temporary.exists()); + assert!(!path.exists()); + assert_eq!(exact(&db, "routes", &row(0).lower).unwrap(), Some(row(0))); + fs::remove_dir_all(directory).unwrap(); + } + + #[cfg(unix)] + #[test] + fn collection_never_follows_a_page_symlink() { + let (directory, db) = fixture(); + put(&db, "routes", row(0)).unwrap(); + let page = write_node(&db, &Node::Leaf { rows: vec![row(9)] }).unwrap(); + let path = page_path(&db, &page.page, false).unwrap(); + let outside = directory.join("preserve-me"); + fs::rename(&path, &outside).unwrap(); + std::os::unix::fs::symlink(&outside, &path).unwrap(); + let mut collector = RoutingCollector::default(); + let mut rejected = false; + for _ in 0..100 { + if collector.step(&db).is_err() { + rejected = true; + break; + } + } + assert!(rejected); + assert!(fs::symlink_metadata(&path) + .unwrap() + .file_type() + .is_symlink()); + assert!(outside.exists()); + fs::remove_dir_all(directory).unwrap(); + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/indexed_routes_gc.rs b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_routes_gc.rs new file mode 100644 index 00000000000..05134067cef --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_routes_gc.rs @@ -0,0 +1,166 @@ +//! Collect obsolete index pages without a history-sized mark set or inventory. +//! A page contains its immutable lower key and level, so two current-root point +//! lookups establish reachability. The caller holds the partition operation lock +//! through that check and unlink. An online backup pins every source page until +//! its independent copy is published. Receipt contents are never collected here. +use super::*; + +#[derive(Default)] +pub(crate) struct RoutingCollector { + // At most three open directory iterators: root, two-digit prefix, leaf. + // Restarting loses only scan progress, never a deletion authorization. + directories: Vec<(PathBuf, fs::ReadDir)>, +} + +fn hex(value: &str, length: usize) -> bool { + value.len() == length + && value + .bytes() + .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b)) +} + +/// A corrupt current path is an error, never evidence that a page is garbage. +fn reachable(db: &Connection, name: &str, candidate: &Edge, level: u16) -> Result { + let Some(mut edge) = head(db, name)? else { + if name == "routes" { + return Err(invalid("missing routing authority prevents collection")); + } + return Ok(false); + }; + let mut expected = None; + loop { + if edge.page.origin.is_some() { + return Err(invalid("collection requires local routing authority")); + } + let node = checked_child(db, &edge, expected)?; + if node.level() <= level { + return Ok(node.level() == level + && edge.page.inline.is_none() + && edge.page.sha == candidate.page.sha); + } + let Node::Branch { level, children } = node else { + unreachable!("leaf level is zero"); + }; + let Some(next) = children + .into_iter() + .rev() + .find(|child| child.lower <= candidate.lower) + else { + return Ok(false); + }; + edge = next; + expected = Some(level - 1); + } +} + +impl RoutingCollector { + /// Inspect at most one directory entry and one bounded page. Returns true at + /// the end of a scan. Caller must recover pending writes first and retain + /// PartitionLock; no current-root transaction may span this call. + pub(crate) fn step(&mut self, db: &Connection) -> Result { + if !db.is_autocommit() { + return Err(invalid( + "routing collection cannot run inside a transaction", + )); + } + let held: bool = db + .query_row( + "SELECT EXISTS(SELECT 1 FROM receipt_backup) OR EXISTS(SELECT 1 FROM receipt_prepare)", + [], + |r| r.get(0), + ) + .map_err(error)?; + if held { + return Ok(false); + } + let unknown_head: bool = db + .query_row( + "SELECT EXISTS(SELECT 1 FROM receipt_routing_heads WHERE name NOT IN ('routes','retired'))", + [], + |r| r.get(0), + ) + .map_err(error)?; + if unknown_head { + return Err(invalid("unknown routing root prevents collection")); + } + if self.directories.is_empty() { + let root = root_directory(db)?; + crate::durable::verify_private_directory(&root)?; + let entries = fs::read_dir(&root).map_err(error)?; + self.directories.push((root, entries)); + return Ok(false); + } + let depth = self.directories.len(); + let (_, entries) = self.directories.last_mut().unwrap(); + let Some(entry) = entries.next() else { + self.directories.pop(); + return Ok(self.directories.is_empty()); + }; + let entry = entry.map_err(error)?; + let name = entry.file_name(); + let name = name + .to_str() + .ok_or_else(|| invalid("invalid routing directory entry"))?; + let path = entry.path(); + // A different store handle may have collected this entry between steps. + let metadata = match fs::symlink_metadata(&path) { + Ok(metadata) => metadata, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(false), + Err(e) => return Err(error(e)), + }; + if depth < 3 { + if !hex(name, 2) { + return Err(invalid("invalid routing directory prefix")); + } + crate::durable::verify_private_directory(&path)?; + self.directories + .push((path.clone(), fs::read_dir(path).map_err(error)?)); + return Ok(false); + } + if !metadata.is_file() || metadata.file_type().is_symlink() { + return Err(invalid("routing collection encountered a nonregular page")); + } + let parent = path.parent().unwrap(); + let prefix = parent + .parent() + .unwrap() + .file_name() + .unwrap() + .to_str() + .unwrap(); + let suffix = parent.file_name().unwrap().to_str().unwrap(); + if let Some(stem) = name.strip_suffix(".json").filter(|s| hex(s, 60)) { + let page = PageRef { + sha: format!("{prefix}{suffix}{stem}"), + inline: None, + origin: None, + }; + let node = read_node(db, &page)?; + let candidate = node.edge(page); + if reachable(db, "routes", &candidate, node.level())? + || reachable(db, "retired", &candidate, node.level())? + { + return Ok(false); + } + } else { + // Only our exact unpublished temporary filename can be reclaimed. + // Writers hold the same lock, so none can still be writing it. + let Some((stem, temporary)) = name.split_once('.') else { + return Err(invalid("invalid routing page filename")); + }; + if !hex(stem, 60) + || !temporary.strip_suffix(".tmp").is_some_and(|id| { + uuid::Uuid::parse_str(id).is_ok_and(|uuid| uuid.to_string() == id) + }) + { + return Err(invalid("invalid routing temporary filename")); + } + drop(crate::durable::open_private_regular_file(&path).map_err(error)?); + } + fs::remove_file(&path).map_err(error)?; + fs::File::open(parent) + .and_then(|f| f.sync_all()) + .map_err(error)?; + Ok(false) + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/indexed_rpc.rs b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_rpc.rs new file mode 100644 index 00000000000..96a1a2c87cf --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_rpc.rs @@ -0,0 +1,137 @@ +//! Private stdio port for standalone controllers. Bounded requests reuse the +//! same partitioned store as runner/provider state, including its redo protocol. +use crate::{ + durable::DurableRunnerError, + indexed_store::{ExactReceipt, IndexedStore}, +}; +use base64::{engine::general_purpose::STANDARD, Engine}; +use serde_json::{json, Value}; +use std::io::{BufRead, Write}; + +type Result = std::result::Result; +fn invalid(message: &str) -> DurableRunnerError { + DurableRunnerError::invalid(message) +} +fn field<'a>(value: &'a Value, key: &str) -> Result<&'a str> { + value[key] + .as_str() + .ok_or_else(|| invalid("invalid storage RPC field")) +} +fn number(value: &Value, key: &str) -> Result { + let text = field(value, key)?; + let number = text + .parse::() + .map_err(|_| invalid("invalid storage RPC integer"))?; + if text != number.to_string() { + return Err(invalid("invalid storage RPC integer")); + } + Ok(number) +} +fn bytes(value: &Value, key: &str) -> Result> { + STANDARD + .decode(field(value, key)?) + .map_err(|_| invalid("invalid storage RPC bytes")) +} +fn dispatch(store: &IndexedStore, value: &Value) -> Result { + let input = &value["input"]; + Ok(match field(value,"operation")? { + "ready"=>Value::Null, + "load"=>store.read_state(field(input,"key")?)?.map(|state|json!({"generation":state.generation.to_string(),"bytes":STANDARD.encode(state.bytes)})).unwrap_or(Value::Null), + "get"=>store.receipt(field(input,"namespace")?,field(input,"key")?)?.map(|body|json!(STANDARD.encode(body))).unwrap_or(Value::Null), + "getWork"=>store.read_work(field(input,"collection")?,field(input,"key")?)?.map(work_value).unwrap_or(Value::Null), + "workPage"=>json!(store.work_page(field(input,"collection")?,field(input,"after")?,number(input,"limit")? as usize)?.into_iter().map(work_value).collect::>()), + "page"=>{ + let records=store.receipt_page(field(input,"namespace")?,field(input,"after")?,number(input,"limit")? as usize,number(input,"byteBudget")? as usize)?; + json!(records.into_iter().map(|r|json!({"key":r.key,"bytes":STANDARD.encode(r.bytes)})).collect::>()) + }, + "commit"=>{ + let records=input["receipts"].as_array().ok_or_else(||invalid("invalid storage RPC receipts"))?; + if records.len()>4096 { return Err(invalid("storage_pressure: receipt transaction bound")); } + let receipts=records.iter().map(|r|Ok(ExactReceipt{namespace:field(r,"namespace")?.into(),key:field(r,"key")?.into(),bytes:bytes(r,"bytes")?})).collect::>>()?; + let work=match input.get("work") { + None=>vec![], + Some(work)=>work.as_array().ok_or_else(||invalid("invalid outstanding-work list"))?.iter().map(|r|Ok(crate::indexed_work::WorkChange { + collection:field(r,"collection")?.into(),key:field(r,"key")?.into(), + expected_digest:if r["expectedDigest"].is_null() {None} else {Some(bytes(r,"expectedDigest")?)}, + bytes:if r["bytes"].is_null() {None} else {Some(bytes(r,"bytes")?)}, + })).collect::>>()?, + }; + json!(store.commit_with_work(field(input,"key")?,field(input,"expectedGeneration")?.parse::()?,bytes(input,"bytes")?,receipts,work)?.to_string()) + }, + _=>return Err(invalid("invalid storage RPC operation")), + }) +} + +fn work_value(record: crate::indexed_work::WorkRecord) -> Value { + json!({"key":record.key,"bytes":STANDARD.encode(record.bytes),"digest":STANDARD.encode(record.digest)}) +} + +pub fn serve(store: IndexedStore, input: impl BufRead, mut output: impl Write) -> Result<()> { + let mut input = input; + loop { + let mut line = Vec::new(); + loop { + let available = input + .fill_buf() + .map_err(crate::indexed_store::storage_error)?; + if available.is_empty() { + if line.is_empty() { + return Ok(()); + } + return Err(invalid("incomplete storage RPC frame")); + } + let count = available + .iter() + .position(|b| *b == b'\n') + .map(|n| n + 1) + .unwrap_or(available.len()); + if line.len() + count > 96 * 1024 * 1024 { + return Err(invalid("storage_pressure: storage RPC frame bound")); + } + let complete = available[count - 1] == b'\n'; + line.extend_from_slice(&available[..count]); + input.consume(count); + if complete { + break; + } + } + let request: Value = + serde_json::from_slice(&line).map_err(crate::indexed_store::storage_error)?; + let id = request["id"] + .as_u64() + .ok_or_else(|| invalid("invalid storage RPC id"))?; + let close = request["operation"] == "close"; + let result = if close { + Ok(Value::Null) + } else { + dispatch(&store, &request) + }; + let response = match result { + Ok(value) => json!({"id":id,"value":value}), + Err(error) => { + let message = error.to_string(); + let code = if message.contains("stale indexed store generation") + || message.contains("stale outstanding-work digest") + { + "stale_authority" + } else if message.contains("exact receipt replay conflict") { + "receipt_conflict" + } else if message.contains("storage_pressure:") { + "storage_pressure" + } else { + "storage_unavailable" + }; + json!({"id":id,"error":{"code":code,"message":message}}) + } + }; + serde_json::to_writer(&mut output, &response) + .map_err(crate::indexed_store::storage_error)?; + output + .write_all(b"\n") + .and_then(|_| output.flush()) + .map_err(crate::indexed_store::storage_error)?; + if close { + return Ok(()); + } + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/indexed_session_snapshot.rs b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_session_snapshot.rs new file mode 100644 index 00000000000..d2b679200a8 --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_session_snapshot.rs @@ -0,0 +1,704 @@ +//! A resumable, exact two-store snapshot at an admitted quiet boundary. The +//! controller must separately fence its authority and provider checkpoint. This +//! operation does not infer process-tree death or authorize a restored session. +use crate::{ + durable::{open_private_regular_file, verify_private_directory, DurableRunnerError}, + indexed_store::{storage_error, IndexedStore}, +}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use sha2::{Digest, Sha256}; +use std::{ + fs, + io::{Read, Write}, + path::{Path, PathBuf}, +}; + +type Result = std::result::Result; +const MANIFEST: &str = "indexed-session-snapshot.json"; +const NAMES: [&str; 2] = ["runner-state", "codex-provider-state"]; +const KEYS: [&str; 2] = ["runner", "codex-provider"]; +const SCHEMAS: [&str; 2] = [ + "paperclip.runner.durable.state.indexed.v1", + "paperclip.runner.codex-provider-state.indexed.v1", +]; +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct ExpectedState { + pub generation: String, + pub sha256: String, +} +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct Snapshot { + schema: String, + source: PathBuf, + states: [ExpectedState; 2], + locators: [String; 2], +} +fn invalid(s: &str) -> DurableRunnerError { + DurableRunnerError::invalid(s) +} +fn present(path: &Path) -> Result { + match fs::symlink_metadata(path) { + Ok(_) => Ok(true), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false), + Err(e) => Err(storage_error(e)), + } +} +fn read(path: &Path) -> Result> { + let mut bytes = Vec::new(); + open_private_regular_file(path) + .map_err(storage_error)? + .take(16385) + .read_to_end(&mut bytes) + .map_err(storage_error)?; + if bytes.len() > 16384 { + return Err(invalid("snapshot metadata exceeds capacity")); + } + Ok(bytes) +} +fn sync(path: &Path) -> Result<()> { + fs::File::open(path) + .and_then(|f| f.sync_all()) + .map_err(storage_error) +} +fn directory(path: &Path) -> Result<()> { + let mut builder = fs::DirBuilder::new(); + #[cfg(unix)] + { + use std::os::unix::fs::DirBuilderExt; + builder.mode(0o700); + } + builder.create(path).map_err(storage_error)?; + sync(path.parent().unwrap()) +} +fn publish_file(path: &Path, bytes: &[u8]) -> Result<()> { + if present(path)? { + if read(path)? != bytes { + return Err(invalid("snapshot metadata changed")); + } + return Ok(()); + } + let temporary = path.with_extension(format!("{}.tmp", uuid::Uuid::new_v4())); + let mut options = fs::OpenOptions::new(); + options.create_new(true).write(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + options.mode(0o600); + } + let mut file = options.open(&temporary).map_err(storage_error)?; + let result = (|| { + file.write_all(bytes) + .and_then(|_| file.sync_all()) + .map_err(storage_error)?; + fs::hard_link(&temporary, path).map_err(storage_error)?; + sync(path.parent().unwrap()) + })(); + let _ = fs::remove_file(temporary); + result +} +fn move_once(source: &Path, destination: &Path) -> Result<()> { + if present(destination)? { + if present(source)? { + return Err(invalid("snapshot move destination conflicts")); + } + return Ok(()); + } + #[cfg(any(target_os = "linux", target_os = "android", target_vendor = "apple"))] + rustix::fs::renameat_with( + rustix::fs::CWD, + source, + rustix::fs::CWD, + destination, + rustix::fs::RenameFlags::NOREPLACE, + ) + .map_err(storage_error)?; + #[cfg(not(any(target_os = "linux", target_os = "android", target_vendor = "apple")))] + return Err(invalid( + "snapshot publication is unqualified on this platform", + )); + sync(destination.parent().unwrap())?; + sync(source.parent().unwrap()) +} +fn binding(locator: &str, index: usize) -> Result { + let value: Value = serde_json::from_str(locator).map_err(storage_error)?; + if locator.len() > 4096 || value["schema"] != SCHEMAS[index] { + return Err(invalid("snapshot locator is invalid")); + } + value["binding"] + .as_str() + .filter(|s| !s.is_empty() && s.len() <= 1024 && !s.chars().any(char::is_control)) + .map(str::to_owned) + .ok_or_else(|| invalid("snapshot binding is invalid")) +} +fn verify(store: &IndexedStore, index: usize, expected: &ExpectedState) -> Result { + let snapshot = store + .read_state(KEYS[index])? + .ok_or_else(|| invalid("snapshot current state is missing"))?; + if snapshot.generation.to_string() != expected.generation + || format!("{:x}", Sha256::digest(&snapshot.bytes)) != expected.sha256 + { + return Err(invalid("snapshot current state changed")); + } + serde_json::from_slice(&snapshot.bytes).map_err(storage_error) +} +fn verify_backup(directory: &Path, index: usize, snapshot: &Snapshot) -> Result<()> { + verify_private_directory(directory)?; + let path = directory.join(format!("{}.sqlite", NAMES[index])); + for suffix in ["", "-wal", "-shm"] { + let file = PathBuf::from(format!("{}{suffix}", path.display())); + if suffix.is_empty() || present(&file)? { + crate::indexed_store::verify_sqlite_file(&file).map_err(storage_error)?; + } + } + let db = rusqlite::Connection::open_with_flags( + &path, + rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY | rusqlite::OpenFlags::SQLITE_OPEN_NOFOLLOW, + ) + .map_err(storage_error)?; + db.execute_batch( + "PRAGMA query_only=ON; PRAGMA trusted_schema=OFF; PRAGMA cache_size=-4096; BEGIN", + ) + .map_err(storage_error)?; + let stored: (i64, String) = db + .query_row( + "SELECT schema_version,binding FROM store_binding WHERE singleton=1", + [], + |row| Ok((row.get(0)?, row.get(1)?)), + ) + .map_err(storage_error)?; + if stored != (2, binding(&snapshot.locators[index], index)?) { + return Err(invalid("snapshot database binding differs")); + } + let complete: i64 = db + .query_row( + "SELECT complete FROM backup_manifest WHERE singleton=1", + [], + |row| row.get(0), + ) + .map_err(storage_error)?; + if complete != 1 { + return Err(invalid("snapshot backup is incomplete")); + } + crate::indexed_partitions::verify_backup_receipts(&db)?; + let state = crate::indexed_store::read_state(&db, KEYS[index])? + .ok_or_else(|| invalid("snapshot current state missing"))?; + if state.generation.to_string() != snapshot.states[index].generation + || format!("{:x}", Sha256::digest(&state.bytes)) != snapshot.states[index].sha256 + { + return Err(invalid("snapshot current state changed")); + } + Ok(()) +} +fn verify_completed(directory: &Path, snapshot: &Snapshot) -> Result<()> { + verify_private_directory(directory)?; + let _lifetimes = [ + crate::indexed_lifetime::StoreLifetime::existing_exclusive( + &directory.join("runner-state.sqlite"), + )?, + crate::indexed_lifetime::StoreLifetime::existing_exclusive( + &directory.join("codex-provider-state.sqlite"), + )?, + ]; + if read(&directory.join(MANIFEST))? != serde_json::to_vec(snapshot).map_err(storage_error)? { + return Err(invalid("snapshot publication conflicts")); + } + for i in 0..2 { + if read(&directory.join(format!("{}.json", NAMES[i])))? != snapshot.locators[i].as_bytes() { + return Err(invalid("snapshot locator changed")); + } + verify_backup(directory, i, snapshot)?; + } + Ok(()) +} +/// The expected generations and hashes are supplied by current-state admission, +/// never reconstructed from history. Both lifetime fences stay owned throughout +/// the copy. No new provider/runner writer may enter between the two snapshots. +pub fn snapshot_session( + source: &Path, + destination: &Path, + states: [ExpectedState; 2], +) -> Result { + snapshot_with_hook(source, destination, states, |_| Ok(())) +} +fn snapshot_with_hook( + source: &Path, + destination: &Path, + states: [ExpectedState; 2], + hook: impl Fn(&str) -> Result<()>, +) -> Result { + verify_private_directory(source)?; + let requested_destination = destination.to_owned(); + let parent = destination + .parent() + .ok_or_else(|| invalid("snapshot has no parent"))?; + verify_private_directory(parent)?; + let source = source.canonicalize().map_err(storage_error)?; + let destination = parent.canonicalize().map_err(storage_error)?.join( + destination + .file_name() + .ok_or_else(|| invalid("snapshot has no filename"))?, + ); + if destination.starts_with(&source) || source.starts_with(&destination) { + return Err(invalid("snapshot overlaps its source")); + } + for state in &states { + if state + .generation + .parse::() + .ok() + .filter(|n| *n != crate::indexed_revision::Revision::Absent) + .map(|n| n.to_string()) + .as_ref() + != Some(&state.generation) + || state.sha256.len() != 64 + || !state + .sha256 + .bytes() + .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b)) + { + return Err(invalid("snapshot expectation is invalid")); + } + } + let snapshot = Snapshot { + schema: "paperclip.runner.indexed-session-snapshot.v1".into(), + source: source.clone(), + states, + locators: [ + String::from_utf8(read(&source.join("runner-state.json"))?).map_err(storage_error)?, + String::from_utf8(read(&source.join("codex-provider-state.json"))?) + .map_err(storage_error)?, + ], + }; + if present(&destination)? { + verify_completed(&destination, &snapshot)?; + return Ok( + serde_json::json!({"snapshot":"complete","destination":requested_destination,"states":snapshot.states}), + ); + } + // Always acquire in the same order; failure releases the first fence. + let stores = [ + IndexedStore::open_quiescent( + &source.join("runner-state.sqlite"), + &binding(&snapshot.locators[0], 0)?, + )?, + IndexedStore::open_quiescent( + &source.join("codex-provider-state.sqlite"), + &binding(&snapshot.locators[1], 1)?, + )?, + ]; + let runner = verify(&stores[0], 0, &snapshot.states[0])?; + let provider = verify(&stores[1], 1, &snapshot.states[1])?; + if runner["lifecycle"] != "suspended" + || runner["ackedSourceSeq"] + .as_u64() + .and_then(|n| n.checked_add(1)) + != runner["nextSourceSeq"].as_u64() + || runner["nextSourceSeq"].as_u64().is_none() + || provider["activeProviderTurnId"].as_str().is_some() + || provider["startupAttempt"].is_object() + || provider["ambiguousTurnStartPending"] == true + || ["pendingEvents", "queuedEvents"].iter().any(|key| { + provider[key] + .as_array() + .is_none_or(|items| !items.is_empty()) + }) + { + return Err(invalid("snapshot requires a settled native session")); + } + for key in ["runnerInstanceId", "normalizedSessionId"] { + if runner[key].as_str().is_none_or(|value| { + value.is_empty() || value.len() > 512 || value.chars().any(char::is_control) + }) { + return Err(invalid("snapshot session identity is invalid")); + } + } + for (index, expected) in [ + ( + 0, + format!( + "runner/{}/{}", + runner["runnerInstanceId"].as_str().unwrap_or(""), + runner["normalizedSessionId"].as_str().unwrap_or("") + ), + ), + ( + 1, + format!( + "provider/{}/{}", + runner["runnerInstanceId"].as_str().unwrap_or(""), + runner["normalizedSessionId"].as_str().unwrap_or("") + ), + ), + ] { + if binding(&snapshot.locators[index], index)? != expected { + return Err(invalid("snapshot session binding differs")); + } + } + let staging = destination.with_file_name(format!( + ".{}.preparing", + destination.file_name().unwrap().to_string_lossy() + )); + if !present(&staging)? { + directory(&staging)?; + } + verify_private_directory(&staging)?; + // A second snapshot process cannot race assembly or publish this job. + let _job_lock = crate::indexed_partitions::PartitionLock::acquire(&staging.join("snapshot"))?; + publish_file( + &staging.join("request.json"), + &serde_json::to_vec(&snapshot).map_err(storage_error)?, + )?; + hook("intent")?; + let merged = staging.join("runner"); + let provider_root = staging.join("provider"); + let assembling = staging.join("assembling.json"); + if !present(&assembling)? { + for (i, target) in [&merged, &provider_root].iter().enumerate() { + if !present(target)? { + stores[i].begin_named_backup(target, &format!("{}.sqlite", NAMES[i]))?; + while !stores[i].backup_step()? { + hook("copy")?; + std::thread::yield_now(); + } + } else { + // Release any source pin left after the atomic publication. + stores[i].backup_step()?; + } + verify_backup(target, i, &snapshot)?; + hook(if i == 0 { "runner" } else { "provider" })?; + } + publish_file(&assembling, b"{\"assembling\":true}")?; + } else if read(&assembling)? != b"{\"assembling\":true}" { + return Err(invalid("snapshot assembly marker differs")); + } + for suffix in [ + ".sqlite", + ".sqlite.receipts", + ".sqlite.routing", + ".sqlite-wal", + ".sqlite-shm", + ] { + let name = format!("codex-provider-state{suffix}"); + if suffix == ".sqlite" + || suffix == ".sqlite.receipts" + || suffix == ".sqlite.routing" + || present(&provider_root.join(&name))? + || present(&merged.join(&name))? + { + move_once(&provider_root.join(&name), &merged.join(&name))?; + } + hook("merge")?; + } + for i in 0..2 { + publish_file( + &merged.join(format!("{}.json", NAMES[i])), + snapshot.locators[i].as_bytes(), + )?; + } + for name in NAMES { + crate::indexed_partitions::seal_backup_files(&merged.join(format!("{name}.sqlite")))?; + hook("sealed")?; + } + for name in NAMES { + // Read-only inspection uses existing lifetime/operation fences before + // the first execution open of a restored snapshot. + for suffix in [".sqlite.lifetime", ".sqlite.lock"] { + publish_file(&merged.join(format!("{name}{suffix}")), b"")?; + } + } + publish_file( + &merged.join(MANIFEST), + &serde_json::to_vec(&snapshot).map_err(storage_error)?, + )?; + verify_completed(&merged, &snapshot)?; + hook("verified")?; + move_once(&merged, &destination)?; + hook("published")?; + Ok( + serde_json::json!({"snapshot":"complete","destination":requested_destination,"states":snapshot.states}), + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::indexed_store::ExactReceipt; + use serde_json::json; + use std::sync::atomic::{AtomicUsize, Ordering}; + struct Fixture { + root: PathBuf, + source: PathBuf, + destination: PathBuf, + states: [ExpectedState; 2], + } + impl Fixture { + fn new() -> Self { + let root = std::env::temp_dir().join(format!( + "paperclip-session-snapshot-{}", + uuid::Uuid::new_v4() + )); + directory(&root).unwrap(); + let source = root.join("source"); + directory(&source).unwrap(); + let mut states = Vec::new(); + for i in 0..2 { + let binding = if i == 0 { "runner/r/s" } else { "provider/r/s" }; + publish_file( + &source.join(format!("{}.json", NAMES[i])), + &serde_json::to_vec(&json!({"schema":SCHEMAS[i],"binding":binding})).unwrap(), + ) + .unwrap(); + let store = + IndexedStore::open(&source.join(format!("{}.sqlite", NAMES[i])), binding, true) + .unwrap(); + let bytes=serde_json::to_vec(&if i==0 {json!({"lifecycle":"suspended","runnerInstanceId":"r","normalizedSessionId":"s","nextSourceSeq":3,"ackedSourceSeq":2})} else {json!({"lifecycle":"ready","pendingEvents":[],"queuedEvents":[]})}).unwrap(); + let generation = store + .commit( + KEYS[i], + 0, + bytes.clone(), + (0..270) + .map(|n| ExactReceipt { + namespace: "ancient".into(), + key: format!("{n:08}"), + bytes: vec![i as u8 + 20; 4096], + }) + .collect(), + ) + .unwrap(); + states.push(ExpectedState { + generation: generation.to_string(), + sha256: format!("{:x}", Sha256::digest(&bytes)), + }); + } + Self { + destination: root.join("published"), + root, + source, + states: states.try_into().unwrap(), + } + } + fn restore(&self) { + for i in 0..2 { + let store = IndexedStore::open( + &self.destination.join(format!("{}.sqlite", NAMES[i])), + if i == 0 { "runner/r/s" } else { "provider/r/s" }, + false, + ) + .unwrap(); + verify(&store, i, &self.states[i]).unwrap(); + for n in [0, 127, 269] { + assert_eq!( + store.receipt("ancient", &format!("{n:08}")).unwrap(), + Some(vec![i as u8 + 20; 4096]) + ); + } + let partition = store.partitions(None, 1).unwrap(); + assert!(partition[0] + .file + .starts_with(&format!("{}.sqlite.receipts/", NAMES[i]))); + } + } + } + impl Drop for Fixture { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.root); + } + } + #[test] + fn exact_two_store_snapshot_restores_after_relocated_source_volume_is_lost() { + let f = Fixture::new(); + let volume = f.root.join("volume"); + directory(&volume).unwrap(); + let path = f.source.join("codex-provider-state.sqlite"); + let store = IndexedStore::open(&path, "provider/r/s", false).unwrap(); + store.relocate_partition("", &volume).unwrap(); + for _ in 0..100 { + store.maintain().unwrap(); + } + assert!(store.partitions(None, 1).unwrap()[0] + .file + .starts_with(volume.canonicalize().unwrap().to_str().unwrap())); + drop(store); + snapshot_session(&f.source, &f.destination, f.states.clone()).unwrap(); + snapshot_session(&f.source, &f.destination, f.states.clone()).unwrap(); + fs::remove_dir_all(&f.source).unwrap(); + fs::remove_dir_all(volume).unwrap(); + f.restore(); + } + #[test] + fn opaque_revisions_restore_both_stores_and_ancient_receipts_after_source_loss() { + let mut f = Fixture::new(); + for i in 0..2 { + let path = f.source.join(format!("{}.sqlite", NAMES[i])); + let db = rusqlite::Connection::open(&path).unwrap(); + db.execute( + "UPDATE current_state SET generation=?1 WHERE key=?2", + rusqlite::params![i64::MAX, KEYS[i]], + ) + .unwrap(); + drop(db); + let store = IndexedStore::open( + &path, + if i == 0 { "runner/r/s" } else { "provider/r/s" }, + false, + ) + .unwrap(); + let current = store.read_state(KEYS[i]).unwrap().unwrap(); + let revision = store + .commit(KEYS[i], current.generation, current.bytes, vec![]) + .unwrap(); + assert!(matches!( + revision, + crate::indexed_revision::Revision::Opaque(_) + )); + f.states[i].generation = revision.to_string(); + } + snapshot_session(&f.source, &f.destination, f.states.clone()).unwrap(); + fs::remove_dir_all(&f.source).unwrap(); + f.restore(); + } + + #[test] + fn every_copy_and_publication_boundary_resumes_without_partial_activation() { + let boundary_count = { + let baseline = Fixture::new(); + let calls = AtomicUsize::new(0); + snapshot_with_hook( + &baseline.source, + &baseline.destination, + baseline.states.clone(), + |_| { + calls.fetch_add(1, Ordering::SeqCst); + Ok(()) + }, + ) + .unwrap(); + let count = calls.load(Ordering::SeqCst); + assert!( + count >= 10, + "snapshot boundary coverage unexpectedly shrank to {count}" + ); + baseline.restore(); + count + }; + let mut interruptions = 0; + let mut completed = false; + for stop_at in 0..=boundary_count { + let f = Fixture::new(); + let calls = AtomicUsize::new(0); + let result = snapshot_with_hook(&f.source, &f.destination, f.states.clone(), |_| { + if calls.fetch_add(1, Ordering::SeqCst) == stop_at { + Err(invalid("injected snapshot interruption")) + } else { + Ok(()) + } + }); + if result.is_ok() { + assert_eq!( + stop_at, boundary_count, + "snapshot completed before the last observed boundary" + ); + completed = true; + break; + } + assert!(result + .unwrap_err() + .to_string() + .contains("injected snapshot interruption")); + interruptions += 1; + if f.destination.exists() { + f.restore(); + } + snapshot_session(&f.source, &f.destination, f.states.clone()).unwrap(); + f.restore(); + } + assert!( + completed, + "fault matrix did not complete after every snapshot boundary" + ); + assert_eq!( + interruptions, boundary_count, + "fault matrix did not interrupt every boundary exactly once" + ); + } + #[test] + fn rejects_live_connections_changed_generations_and_conflicting_destinations() { + let f = Fixture::new(); + for i in 0..2 { + let store = IndexedStore::open( + &f.source.join(format!("{}.sqlite", NAMES[i])), + if i == 0 { "runner/r/s" } else { "provider/r/s" }, + false, + ) + .unwrap(); + assert!( + snapshot_session(&f.source, &f.destination, f.states.clone()) + .unwrap_err() + .to_string() + .contains("open connection") + ); + assert!(!f.destination.exists()); + drop(store); + } + let mut changed = f.states.clone(); + changed[0].generation = "2".into(); + assert!(snapshot_session(&f.source, &f.destination, changed) + .unwrap_err() + .to_string() + .contains("state changed")); + directory(&f.destination).unwrap(); + assert!(snapshot_session(&f.source, &f.destination, f.states.clone()).is_err()); + assert!(fs::read_dir(&f.destination).unwrap().next().is_none()); + } + #[test] + fn completed_snapshot_retry_rejects_missing_or_corrupted_historical_receipts() { + for missing in [false, true] { + let f = Fixture::new(); + snapshot_session(&f.source, &f.destination, f.states.clone()).unwrap(); + let db = rusqlite::Connection::open(f.destination.join("runner-state.sqlite")).unwrap(); + let relative = crate::indexed_partitions::partitions(&db, None, 1) + .unwrap() + .into_iter() + .next() + .expect("snapshot has a receipt route") + .file; + drop(db); + let partition = f.destination.join(relative); + if missing { + fs::remove_file(partition).unwrap(); + } else { + let db = rusqlite::Connection::open(partition).unwrap(); + db.execute("UPDATE receipt_rows SET body=x'00' WHERE ordinal=1", []) + .unwrap(); + } + assert!(snapshot_session(&f.source, &f.destination, f.states.clone()).is_err()); + } + } + #[test] + fn rejects_unsettled_delivery_before_creating_a_backup() { + let mut f = Fixture::new(); + let store = + IndexedStore::open(&f.source.join("runner-state.sqlite"), "runner/r/s", false).unwrap(); + let mut state: Value = + serde_json::from_slice(&store.read_state("runner").unwrap().unwrap().bytes).unwrap(); + state["nextSourceSeq"] = json!(4); + let bytes = serde_json::to_vec(&state).unwrap(); + store.commit("runner", 1, bytes.clone(), vec![]).unwrap(); + drop(store); + f.states[0] = ExpectedState { + generation: "2".into(), + sha256: format!("{:x}", Sha256::digest(bytes)), + }; + assert!( + snapshot_session(&f.source, &f.destination, f.states.clone()) + .unwrap_err() + .to_string() + .contains("settled native session") + ); + assert!(!f.destination.exists()); + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/indexed_store.rs b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_store.rs new file mode 100644 index 00000000000..ba228b5598b --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_store.rs @@ -0,0 +1,1267 @@ +//! Transactional current state and exact, disk-backed replay receipts. +//! +//! The connection lives on one bounded storage executor. A successful reply is +//! a FULL-synchronous WAL commit, never just admission to an in-memory queue. +//! Reading current state does not enumerate receipts. Callers own their +//! authenticated namespace and must not turn storage errors into cache misses. + +use crate::indexed_revision::Revision; +use std::fs::{self, OpenOptions}; +use std::path::{Path, PathBuf}; +use std::sync::{mpsc, Arc, Mutex}; +use std::thread::{self, JoinHandle}; +use std::time::Duration; + +use rusqlite::{params, Connection, OpenFlags, OptionalExtension, TransactionBehavior}; +use sha2::{Digest, Sha256}; + +use crate::durable::verify_private_directory; +use crate::durable::DurableRunnerError; + +const MAX_RECORD_BYTES: usize = 32 * 1024 * 1024; +const MAX_COMMIT_BYTES: usize = 64 * 1024 * 1024; +const MAX_COMMIT_RECEIPTS: usize = 4_096; +const MAX_KEY_BYTES: usize = 1_024; + +/// SQLite must own every descriptor for its database and sidecars. On POSIX, +/// closing even a read-only probe descriptor cancels this process's SQLite +/// locks, including the WAL dead-man switch. A later reader can then truncate +/// SHM underneath a live writer. Metadata checks do not open those files; the +/// SQLite connection itself supplies NOFOLLOW and the surrounding lifetime +/// fence prevents managed relocation. See sqlite.org/howtocorrupt.html#_2_2. +pub(crate) fn verify_sqlite_file(path: &Path) -> std::io::Result<()> { + let metadata = fs::symlink_metadata(path)?; + if !metadata.is_file() || metadata.file_type().is_symlink() { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "durable state path is not a regular file", + )); + } + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + if metadata.permissions().mode() & 0o077 != 0 { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "durable state file is accessible by group or other users", + )); + } + } + Ok(()) +} + +type Result = std::result::Result; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct StoredSnapshot { + pub generation: Revision, + pub bytes: Vec, +} + +#[derive(Clone, Debug)] +pub struct ExactReceipt { + pub namespace: String, + pub key: String, + pub bytes: Vec, +} + +#[derive(Clone, Debug)] +pub struct ReceiptPartition { + pub lower_key: String, + pub file: String, + pub records: u64, + pub bytes: u64, +} + +enum Request { + #[cfg(test)] + TestCapacity(bool, mpsc::Sender>), + ReadWork( + String, + String, + mpsc::Sender>>, + ), + WorkPage( + String, + String, + usize, + mpsc::Sender>>, + ), + ReadState(String, mpsc::Sender>>), + ReadReceipt(String, String, mpsc::Sender>>>), + ReadPage( + String, + String, + usize, + usize, + mpsc::Sender>>, + ), + Commit { + operation: String, + key: String, + expected_generation: Revision, + bytes: Vec, + receipts: Vec, + work: Vec, + reply: mpsc::Sender>, + }, + Maintain(mpsc::Sender>), + Relocate(String, PathBuf, mpsc::Sender>), + BeginBackup(PathBuf, String, mpsc::Sender>), + BackupStep(mpsc::Sender>), + Partitions( + Option, + usize, + mpsc::Sender>>, + ), + Close, +} + +struct Executor { + sender: mpsc::SyncSender, + thread: Mutex>>, +} + +impl Drop for Executor { + fn drop(&mut self) { + let _ = self.sender.send(Request::Close); + if let Ok(thread) = self.thread.get_mut() { + if let Some(thread) = thread.take() { + let _ = thread.join(); + } + } + } +} + +/// Clones share an executor, not an independently writable receipt cache. +#[derive(Clone)] +pub struct IndexedStore { + path: PathBuf, + executor: Arc, +} + +impl std::fmt::Debug for IndexedStore { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("IndexedStore") + .field("path", &self.path) + .finish() + } +} + +impl PartialEq for IndexedStore { + fn eq(&self, other: &Self) -> bool { + self.path == other.path + } +} + +impl IndexedStore { + #[cfg(test)] + pub(crate) fn test_capacity(&self, full: bool) { + let (tx, rx) = mpsc::channel(); + self.executor + .sender + .send(Request::TestCapacity(full, tx)) + .unwrap(); + rx.recv().unwrap().unwrap(); + } + fn retry(&self, request: impl Fn(mpsc::Sender>) -> Request) -> Result { + loop { + crate::storage_capacity::check_cancelled()?; + let (tx, rx) = mpsc::channel(); + self.executor + .sender + .send(request(tx)) + .map_err(storage_error)?; + match rx.recv().map_err(storage_error)? { + Err(error) if crate::storage_capacity::wait(&error)? => continue, + result => return result, + } + } + } + /// `create` is false when reopening an activated store: missing durable + /// storage must never be silently replaced with an empty receipt ledger. + pub fn open(path: &Path, binding: &str, create: bool) -> Result { + crate::storage_capacity::retry(|| Self::open_once(path, binding, create, false)) + } + + /// Maintenance admission requires every execution connection to be closed. + /// The exclusive lifetime remains owned until this handle and its worker close. + pub(crate) fn open_quiescent(path: &Path, binding: &str) -> Result { + Self::open_once(path, binding, false, true) + } + + fn open_once(path: &Path, binding: &str, create: bool, exclusive: bool) -> Result { + validate_key(binding)?; + let parent = path + .parent() + .ok_or_else(|| invalid("store has no parent"))?; + if create && !parent.exists() { + let mut builder = fs::DirBuilder::new(); + builder.recursive(true); + #[cfg(unix)] + { + use std::os::unix::fs::DirBuilderExt; + builder.mode(0o700); + } + builder.create(parent).map_err(storage_error)?; + } + verify_private_directory(parent)?; + // Acquire before reserving/opening any SQLite file and keep it through + // the worker's final checkpoint and Connection::drop. + let lifetime = if exclusive { + crate::indexed_lifetime::StoreLifetime::exclusive(path)? + } else { + crate::indexed_lifetime::StoreLifetime::shared(path)? + }; + if exclusive + && match fs::symlink_metadata(crate::indexed_lifetime::archive_fence(path)) { + Err(error) if error.kind() == std::io::ErrorKind::NotFound => false, + _ => true, + } + { + return Err(invalid("indexed archive must finish before snapshot")); + } + // Reserve with private permissions before SQLite can create sidecars. + // Never follow a pre-existing symlink, including one for WAL or SHM. + if create { + let mut options = OpenOptions::new(); + options.write(true).create_new(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + options.mode(0o600); + } + match options.open(path) { + Ok(file) => file.sync_all().map_err(storage_error)?, + Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => {} + Err(error) => return Err(storage_error(error)), + } + } + for suffix in ["", "-wal", "-shm"] { + let mut name = path.as_os_str().to_os_string(); + name.push(suffix); + match crate::indexed_store::verify_sqlite_file(Path::new(&name)) { + Ok(_) => {} + Err(error) + if !suffix.is_empty() && error.kind() == std::io::ErrorKind::NotFound => {} + Err(error) => return Err(storage_error(error)), + } + } + let (sender, receiver) = mpsc::sync_channel(8); + let (ready_tx, ready_rx) = mpsc::channel(); + // macOS exposes /var through a symlink. Resolve the already-verified + // directory, while retaining NOFOLLOW protection for the database. + let worker_path = parent.canonicalize().map_err(storage_error)?.join( + path.file_name() + .ok_or_else(|| invalid("store has no filename"))?, + ); + let binding = binding.to_owned(); + let worker = thread::Builder::new() + .name("runner-storage".into()) + .spawn(move || { + let _lifetime = lifetime; + let startup_lock = + match crate::indexed_partitions::PartitionLock::acquire(&worker_path) { + Ok(lock) => lock, + Err(error) => { + let _ = ready_tx.send(Err(error)); + return; + } + }; + let mut db = match open_database(&worker_path, &binding, create) { + Ok(db) => { + let _ = ready_tx.send(Ok(())); + db + } + Err(error) => { + let _ = ready_tx.send(Err(error)); + return; + } + }; + drop(startup_lock); + let partitioned = db + .query_row( + "SELECT schema_version FROM store_binding WHERE singleton=1", + [], + |row| row.get::<_, i64>(0), + ) + .unwrap_or(0) + == 2; + let mut routing_collector = crate::indexed_partitions::RoutingCollector::default(); + let mut pending_request = None; + loop { + let request = match pending_request + .take() + .map(Ok) + .unwrap_or_else(|| receiver.recv_timeout(Duration::from_millis(100))) + { + Ok(request) => request, + Err(mpsc::RecvTimeoutError::Disconnected) => break, + Err(mpsc::RecvTimeoutError::Timeout) => { + if partitioned { + if let Ok(_lock) = + crate::indexed_partitions::PartitionLock::acquire(&worker_path) + { + if crate::indexed_partitions::recover(&mut db).is_ok() { + let _ = crate::indexed_partitions::maintain( + &mut db, + &worker_path, + ); + for _ in 0..16 { + // Yield between GC pages if foreground work + // arrived after the idle timer fired. + match receiver.try_recv() { + Ok(request) => { + pending_request = Some(request); + break; + } + Err(mpsc::TryRecvError::Disconnected) => break, + Err(mpsc::TryRecvError::Empty) => {} + } + if routing_collector.step(&db).is_err() { + break; + } + } + } + } + } + continue; + } + }; + let lock = crate::indexed_partitions::PartitionLock::acquire(&worker_path); + let ready = lock + .as_ref() + .map(|_| ()) + .map_err(|e| invalid(&e.to_string())) + .and_then(|_| { + if partitioned { + crate::indexed_partitions::recover(&mut db) + } else { + Ok(()) + } + }); + match request { + #[cfg(test)] + Request::TestCapacity(full, reply) => { + let result = (|| { + let pages: i64 = if full { + db.query_row("PRAGMA page_count", [], |row| row.get(0)) + .map_err(storage_error)? + } else { + 1_073_741_823 + }; + db.pragma_update(None, "max_page_count", pages) + .map_err(storage_error) + })(); + let _ = reply.send(result); + } + Request::ReadWork(collection, key, reply) => { + let _ = reply + .send(ready.and_then(|_| { + crate::indexed_work::get(&db, &collection, &key) + })); + } + Request::WorkPage(collection, after, limit, reply) => { + let _ = reply.send(ready.and_then(|_| { + crate::indexed_work::page(&db, &collection, &after, limit) + })); + } + Request::ReadState(key, reply) => { + let _ = reply.send(ready.and_then(|_| read_state(&db, &key))); + } + Request::ReadReceipt(namespace, key, reply) => { + let _ = reply.send(ready.and_then(|_| { + if partitioned { + crate::indexed_partitions::receipt(&db, &namespace, &key) + } else { + read_receipt(&db, &namespace, &key) + } + })); + } + Request::ReadPage(namespace, after, limit, budget, reply) => { + let _ = reply.send(ready.and_then(|_| { + if partitioned { + crate::indexed_partitions::page( + &db, &namespace, &after, limit, budget, + ) + } else { + read_page(&db, &namespace, &after, limit, budget) + } + })); + } + Request::Commit { + operation, + key, + expected_generation, + bytes, + receipts, + work, + reply, + } => { + let _ = reply.send(ready.and_then(|_| { + if partitioned { + let generation = crate::indexed_partitions::commit_operation( + &mut db, + &key, + expected_generation, + &bytes, + &receipts, + &work, + Some(&operation), + )?; + // A copy page stops at either 128 rows or 4 MiB. Reserve + // work for both limits: small receipts can exhaust the row + // budget without consuming a byte-derived credit. Fourfold + // headroom also covers split publication and retired-file + // cleanup. Work depends on this bounded commit, not history. + let receipt_bytes = receipts + .iter() + .map(|record| record.bytes.len()) + .sum::(); + let maintenance_pages = 1 + + (receipt_bytes / (1024 * 1024)).max(receipts.len() / 32); + for _ in 0..maintenance_pages { + crate::indexed_partitions::maintain(&mut db, &worker_path)?; + } + // Charge collection to write maintenance before + // replying. Reads never trigger filesystem cleanup + // or leave it queued in front of the next read. + // A cleanup failure cannot rewrite a committed + // action's outcome; explicit maintenance reports it. + for _ in 0..2 { + if routing_collector.step(&db).is_err() { + break; + } + } + Ok(generation) + } else { + if !work.is_empty() { + return Err(invalid( + "outstanding work requires indexed format 2", + )); + } + commit_operation( + &mut db, + &key, + expected_generation, + &bytes, + &receipts, + Some(&operation), + ) + } + })); + } + Request::Maintain(reply) => { + let _ = reply.send(ready.and_then(|_| { + if !partitioned { + return Err(invalid( + "partition maintenance requires indexed format 2", + )); + } + crate::indexed_partitions::maintain(&mut db, &worker_path)?; + routing_collector.step(&db).map(|_| ()) + })); + } + Request::Relocate(lower, destination, reply) => { + let _ = reply.send(ready.and_then(|_| { + if !partitioned { + return Err(invalid( + "partition relocation requires indexed format 2", + )); + } + crate::indexed_partitions::relocate(&db, &lower, &destination) + })); + } + Request::BeginBackup(destination, filename, reply) => { + let _ = reply.send(ready.and_then(|_| { + if !partitioned { + return Err(invalid("online backup requires indexed format 2")); + } + crate::indexed_partitions::begin_backup( + &mut db, + &destination, + &filename, + ) + })); + } + Request::BackupStep(reply) => { + let _ = reply.send(ready.and_then(|_| { + if !partitioned { + return Err(invalid("online backup requires indexed format 2")); + } + crate::indexed_partitions::backup_step(&mut db) + })); + } + Request::Partitions(after, limit, reply) => { + let _ = reply.send(ready.and_then(|_| { + if !partitioned { + return Err(invalid( + "partition inspection requires indexed format 2", + )); + } + crate::indexed_partitions::partitions(&db, after.as_deref(), limit) + })); + } + Request::Close => break, + } + } + // No transaction escapes a request, so a reader cannot pin the WAL. + let _ = db.execute_batch("PRAGMA wal_checkpoint(TRUNCATE)"); + }) + .map_err(storage_error)?; + if let Err(error) = ready_rx.recv().map_err(storage_error)? { + let _ = worker.join(); + return Err(error); + } + Ok(Self { + path: path.to_owned(), + executor: Arc::new(Executor { + sender, + thread: Mutex::new(Some(worker)), + }), + }) + } + + /// Bounded maintenance is also driven between ordinary storage requests. + pub fn maintain(&self) -> Result<()> { + let (reply, receive) = mpsc::channel(); + self.executor + .sender + .send(Request::Maintain(reply)) + .map_err(storage_error)?; + receive.recv().map_err(storage_error)? + } + + /// Start or resume a snapshot into a new directory under a private parent. + /// Live writes continue between bounded backup_step calls. The destination + /// becomes visible only when every snapshot partition is verified. + pub fn begin_backup(&self, destination: &Path) -> Result<()> { + self.begin_named_backup(destination, "authority.sqlite") + } + pub(crate) fn begin_named_backup(&self, destination: &Path, filename: &str) -> Result<()> { + let (reply, receive) = mpsc::channel(); + self.executor + .sender + .send(Request::BeginBackup( + destination.to_owned(), + filename.to_owned(), + reply, + )) + .map_err(storage_error)?; + receive.recv().map_err(storage_error)? + } + pub fn backup_step(&self) -> Result { + let (reply, receive) = mpsc::channel(); + self.executor + .sender + .send(Request::BackupStep(reply)) + .map_err(storage_error)?; + receive.recv().map_err(storage_error)? + } + + pub fn partitions(&self, after: Option<&str>, limit: usize) -> Result> { + if !(1..=128).contains(&limit) || after.is_some_and(|value| value.len() > 2049) { + return Err(invalid("invalid partition page capacity")); + } + let (reply, receive) = mpsc::channel(); + self.executor + .sender + .send(Request::Partitions(after.map(str::to_owned), limit, reply)) + .map_err(storage_error)?; + receive.recv().map_err(storage_error)? + } + + /// Operator-selected private storage volume; logical receipt keys and the + /// provider session remain unchanged throughout relocation. + pub fn relocate_partition(&self, lower_key: &str, destination: &Path) -> Result<()> { + let (reply, receive) = mpsc::channel(); + self.executor + .sender + .send(Request::Relocate( + lower_key.into(), + destination.to_owned(), + reply, + )) + .map_err(storage_error)?; + receive.recv().map_err(storage_error)? + } + + /// Cursor pagination bounds allocation before reading each body. Keys are + /// opaque; sequence callers use fixed-width decimal keys. + pub fn receipt_page( + &self, + namespace: &str, + after: &str, + limit: usize, + byte_budget: usize, + ) -> Result> { + validate_key(namespace)?; + if after.len() > MAX_KEY_BYTES + || !(1..=128).contains(&limit) + || byte_budget == 0 + || byte_budget > MAX_COMMIT_BYTES + { + return Err(invalid("invalid receipt page capacity")); + } + self.retry(|tx| Request::ReadPage(namespace.into(), after.into(), limit, byte_budget, tx)) + } + + pub fn read_state(&self, key: &str) -> Result> { + validate_key(key)?; + self.retry(|tx| Request::ReadState(key.into(), tx)) + } + + pub fn receipt(&self, namespace: &str, key: &str) -> Result>> { + validate_key(namespace)?; + validate_key(key)?; + self.retry(|tx| Request::ReadReceipt(namespace.into(), key.into(), tx)) + } + + /// Commit current state and immutable receipts atomically. Generation zero + /// means create, not overwrite. A stale owner cannot overwrite newer state. + pub fn commit( + &self, + key: &str, + expected_generation: impl Into, + bytes: Vec, + receipts: Vec, + ) -> Result { + self.commit_with_work(key, expected_generation, bytes, receipts, vec![]) + } + + pub fn read_work( + &self, + collection: &str, + key: &str, + ) -> Result> { + validate_key(collection)?; + validate_key(key)?; + self.retry(|tx| Request::ReadWork(collection.into(), key.into(), tx)) + } + + pub fn work_page( + &self, + collection: &str, + after: &str, + limit: usize, + ) -> Result> { + validate_key(collection)?; + if after.len() > 1024 || !(1..=128).contains(&limit) { + return Err(invalid("invalid outstanding-work page")); + } + self.retry(|tx| Request::WorkPage(collection.into(), after.into(), limit, tx)) + } + + pub fn commit_with_work( + &self, + key: &str, + expected_generation: impl Into, + bytes: Vec, + receipts: Vec, + work: Vec, + ) -> Result { + let expected_generation = expected_generation.into(); + validate_key(key)?; + validate_bytes(&bytes)?; + crate::indexed_work::validate(&work)?; + let mut size = bytes.len() + + work + .iter() + .map(|change| change.bytes.as_ref().map_or(0, Vec::len)) + .sum::(); + if receipts.len() > MAX_COMMIT_RECEIPTS { + return Err(invalid("storage_pressure: transaction receipt bound")); + } + for receipt in &receipts { + validate_key(&receipt.namespace)?; + validate_key(&receipt.key)?; + validate_bytes(&receipt.bytes)?; + size = size + .checked_add(receipt.bytes.len()) + .ok_or_else(|| invalid("transaction size overflow"))?; + } + if size > MAX_COMMIT_BYTES { + return Err(invalid("storage_pressure: transaction byte bound")); + } + // Keep one exact storage request across capacity retries. The receipt + // transaction owns this token; identical current bytes alone cannot + // establish whether our write or another owner's write committed. + let operation = uuid::Uuid::new_v4().to_string(); + self.retry(|tx| Request::Commit { + operation: operation.clone(), + key: key.into(), + expected_generation, + bytes: bytes.clone(), + receipts: receipts.clone(), + work: work.clone(), + reply: tx, + }) + } +} + +/// Resolve one proof reference without creating, recovering or checkpointing a +/// store. The same lifetime and operation fences used by writers cover routing, +/// partition fingerprints and the expected current generation. +pub fn read_only_receipt( + path: &Path, + binding: &str, + generation: impl Into, + namespace: &str, + key: &str, +) -> Result>> { + let generation = generation.into(); + validate_key(namespace)?; + validate_key(key)?; + let parent = path + .parent() + .ok_or_else(|| invalid("store has no parent"))?; + verify_private_directory(parent)?; + let path = parent.canonicalize().map_err(storage_error)?.join( + path.file_name() + .ok_or_else(|| invalid("store has no filename"))?, + ); + let _lifetime = crate::indexed_lifetime::StoreLifetime::existing_shared(&path)?; + let _operation = crate::indexed_partitions::PartitionLock::existing(&path)?; + for suffix in ["", "-wal", "-shm"] { + match crate::indexed_store::verify_sqlite_file(Path::new(&format!( + "{}{suffix}", + path.display() + ))) { + Ok(_) => {} + Err(e) if !suffix.is_empty() && e.kind() == std::io::ErrorKind::NotFound => {} + Err(e) => return Err(storage_error(e)), + } + } + let db = Connection::open_with_flags( + &path, + OpenFlags::SQLITE_OPEN_READ_ONLY + | OpenFlags::SQLITE_OPEN_NOFOLLOW + | OpenFlags::SQLITE_OPEN_NO_MUTEX, + ) + .map_err(storage_error)?; + db.execute_batch("PRAGMA query_only=ON; PRAGMA trusted_schema=OFF; PRAGMA cache_size=-4096;") + .map_err(storage_error)?; + let stored: (i64, String) = db + .query_row( + "SELECT schema_version,binding FROM store_binding WHERE singleton=1", + [], + |r| Ok((r.get(0)?, r.get(1)?)), + ) + .map_err(storage_error)?; + if stored.0 != 2 || stored.1 != binding { + return Err(invalid("indexed store binding or schema mismatch")); + } + crate::indexed_partitions::validate_backup(&db)?; + if db + .query_row("SELECT 1 FROM receipt_prepare LIMIT 1", [], |_| Ok(())) + .optional() + .map_err(storage_error)? + .is_some() + { + return Err(invalid( + "indexed receipt preparation requires owner recovery", + )); + } + if read_state(&db, "authority")?.map(|s| s.generation) != Some(generation) { + return Err(invalid("stale indexed proof generation")); + } + crate::indexed_partitions::read_only_receipt(&db, namespace, key) +} + +fn open_database(path: &Path, binding: &str, create: bool) -> Result { + // The bundled engine is qualified independently of host Node/system SQLite. + if rusqlite::version_number() < 3_051_003 { + return Err(invalid("SQLite build lacks the WAL-reset fix")); + } + let mut db = Connection::open_with_flags( + path, + OpenFlags::SQLITE_OPEN_READ_WRITE + | OpenFlags::SQLITE_OPEN_NO_MUTEX + | OpenFlags::SQLITE_OPEN_NOFOLLOW, + ) + .map_err(storage_error)?; + db.busy_timeout(Duration::from_secs(5)) + .map_err(storage_error)?; + // Avoid repeated mid-transaction fsyncs when a bounded admitted batch is + // larger than the read cache. Dirty pages can exceed that cache only for + // the current <=64 MiB transaction, never as a function of stored history. + db.execute_batch("PRAGMA journal_mode=WAL; PRAGMA synchronous=FULL; PRAGMA foreign_keys=ON; PRAGMA trusted_schema=OFF; PRAGMA cache_size=-4096; PRAGMA cache_spill=OFF; PRAGMA wal_autocheckpoint=256; PRAGMA journal_size_limit=4194304;").map_err(storage_error)?; + if create { + let tx = db + .transaction_with_behavior(TransactionBehavior::Immediate) + .map_err(storage_error)?; + tx.execute_batch("CREATE TABLE IF NOT EXISTS store_binding (singleton INTEGER PRIMARY KEY CHECK(singleton=1), schema_version INTEGER NOT NULL, binding TEXT NOT NULL); + CREATE TABLE IF NOT EXISTS current_state (key TEXT PRIMARY KEY, generation INTEGER NOT NULL CHECK(generation>0), bytes BLOB NOT NULL, digest BLOB NOT NULL) WITHOUT ROWID; + CREATE TABLE IF NOT EXISTS receipts (namespace TEXT NOT NULL, key TEXT NOT NULL, bytes BLOB NOT NULL, digest BLOB NOT NULL, PRIMARY KEY(namespace,key)) WITHOUT ROWID;").map_err(storage_error)?; + tx.execute( + "INSERT OR IGNORE INTO store_binding VALUES (1,2,?1)", + [binding], + ) + .map_err(storage_error)?; + tx.commit().map_err(storage_error)?; + #[cfg(unix)] + fs::File::open(path.parent().unwrap()) + .and_then(|file| file.sync_all()) + .map_err(storage_error)?; + } + let stored: (i64, String) = db + .query_row( + "SELECT schema_version,binding FROM store_binding WHERE singleton=1", + [], + |row| Ok((row.get(0)?, row.get(1)?)), + ) + .map_err(storage_error)?; + if ![1, 2].contains(&stored.0) || stored.1 != binding { + return Err(invalid("indexed store binding or schema mismatch")); + } + crate::indexed_partitions::validate_backup(&db)?; + crate::indexed_commit::initialize(&db)?; + if stored.0 == 2 { + crate::indexed_work::initialize(&db)?; + if create { + crate::indexed_partitions::initialize(&mut db, path)?; + } + crate::indexed_partitions::validate_routing(&db)?; + crate::indexed_partitions::recover(&mut db)?; + } + Ok(db) +} + +pub(crate) fn read_state(db: &Connection, key: &str) -> Result> { + // SQL guards allocation in the same snapshot, including a damaged or + // externally modified file. Checking Vec::len after SELECT is too late. + let row: Option<(Revision, Vec, Vec)> = db.query_row("SELECT generation,CASE WHEN length(bytes)<=?2 THEN bytes END,CASE WHEN length(digest)=32 THEN digest END FROM current_state WHERE key=?1", params![key, MAX_RECORD_BYTES as i64], |row| Ok((row.get(0)?,row.get(1)?,row.get(2)?))).optional().map_err(storage_error)?; + row.map(|(generation, bytes, digest)| { + verify_digest(&bytes, &digest)?; + Ok(StoredSnapshot { + generation: Revision::try_from(generation).map_err(storage_error)?, + bytes, + }) + }) + .transpose() +} + +fn read_receipt(db: &Connection, namespace: &str, key: &str) -> Result>> { + let row: Option<(Vec, Vec)> = db.query_row("SELECT CASE WHEN length(bytes)<=?3 THEN bytes END,CASE WHEN length(digest)=32 THEN digest END FROM receipts WHERE namespace=?1 AND key=?2", params![namespace,key,MAX_RECORD_BYTES as i64], |row| Ok((row.get(0)?,row.get(1)?))).optional().map_err(storage_error)?; + row.map(|(bytes, digest)| { + verify_digest(&bytes, &digest)?; + Ok(bytes) + }) + .transpose() +} + +fn read_page( + db: &Connection, + namespace: &str, + after: &str, + limit: usize, + byte_budget: usize, +) -> Result> { + let mut statement = db.prepare("SELECT key,length(bytes) FROM receipts WHERE namespace=?1 AND key>?2 ORDER BY key LIMIT ?3").map_err(storage_error)?; + let mut rows = statement + .query(params![namespace, after, limit as i64]) + .map_err(storage_error)?; + let mut result = Vec::new(); + let mut bytes = 0usize; + while let Some(row) = rows.next().map_err(storage_error)? { + let length = + usize::try_from(row.get::<_, i64>(1).map_err(storage_error)?).map_err(storage_error)?; + bytes = bytes + .checked_add(length) + .ok_or_else(|| invalid("receipt page size overflow"))?; + if bytes > byte_budget { + if result.is_empty() { + return Err(invalid( + "storage_pressure: receipt exceeds page byte capacity", + )); + } + break; + } + let key: String = row.get(0).map_err(storage_error)?; + let body = read_receipt(db, namespace, &key)? + .ok_or_else(|| invalid("indexed receipt disappeared"))?; + result.push(ExactReceipt { + namespace: namespace.into(), + key, + bytes: body, + }); + } + Ok(result) +} + +fn commit_operation( + db: &mut Connection, + key: &str, + expected: Revision, + bytes: &[u8], + receipts: &[ExactReceipt], + operation: Option<&str>, +) -> Result { + if let Some(operation) = operation { + if let Some(generation) = + crate::indexed_commit::replay(db, key, operation, expected, bytes, receipts, &[])? + { + return Ok(generation); + } + } + let generation = expected.next()?; + let tx = db + .transaction_with_behavior(TransactionBehavior::Immediate) + .map_err(storage_error)?; + let current: Option = tx + .query_row( + "SELECT generation FROM current_state WHERE key=?1", + [key], + |row| row.get(0), + ) + .optional() + .map_err(storage_error)?; + if current.unwrap_or_default() != expected { + return Err(invalid("stale indexed store generation")); + } + for receipt in receipts { + if let Some(existing) = read_receipt(&tx, &receipt.namespace, &receipt.key)? { + if existing != receipt.bytes { + return Err(invalid("exact receipt replay conflict")); + } + } else { + tx.execute( + "INSERT INTO receipts(namespace,key,bytes,digest) VALUES (?1,?2,?3,?4)", + params![ + receipt.namespace, + receipt.key, + receipt.bytes, + Sha256::digest(&receipt.bytes).as_slice() + ], + ) + .map_err(storage_error)?; + } + } + tx.execute("INSERT INTO current_state(key,generation,bytes,digest) VALUES (?1,?2,?3,?4) ON CONFLICT(key) DO UPDATE SET generation=excluded.generation,bytes=excluded.bytes,digest=excluded.digest", params![key,generation,bytes,Sha256::digest(bytes).as_slice()]).map_err(storage_error)?; + if let Some(operation) = operation { + crate::indexed_commit::record( + &tx, + key, + operation, + expected, + generation, + bytes, + receipts, + &[], + )?; + } + tx.commit().map_err(storage_error)?; + Ok(generation) +} + +fn verify_digest(bytes: &[u8], digest: &[u8]) -> Result<()> { + validate_bytes(bytes)?; + if Sha256::digest(bytes).as_slice() != digest { + return Err(invalid("indexed store record digest mismatch")); + } + Ok(()) +} + +fn validate_key(key: &str) -> Result<()> { + if key.is_empty() || key.len() > MAX_KEY_BYTES || key.chars().any(char::is_control) { + return Err(invalid("invalid indexed store key")); + } + Ok(()) +} + +fn validate_bytes(bytes: &[u8]) -> Result<()> { + if bytes.len() > MAX_RECORD_BYTES { + return Err(invalid("storage_pressure: record byte bound")); + } + Ok(()) +} + +fn invalid(message: &str) -> DurableRunnerError { + DurableRunnerError::invalid(message) +} +pub(crate) fn storage_error(error: impl std::fmt::Display + std::any::Any) -> DurableRunnerError { + let any = &error as &dyn std::any::Any; + let full = any.downcast_ref::().is_some_and(|error| matches!(error, rusqlite::Error::SqliteFailure(code, _) if code.code == rusqlite::ErrorCode::DiskFull)) + || any.downcast_ref::().is_some_and(|error| matches!(error.kind(), std::io::ErrorKind::StorageFull | std::io::ErrorKind::QuotaExceeded)) + || any.downcast_ref::().is_some_and(|error| matches!(std::io::Error::from_raw_os_error(error.raw_os_error()).kind(), std::io::ErrorKind::StorageFull | std::io::ErrorKind::QuotaExceeded)); + if full { + DurableRunnerError::storage_capacity(format!("storage_pressure: {error}")) + } else { + DurableRunnerError::invalid(format!("storage_unavailable: {error}")) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn path() -> PathBuf { + std::env::temp_dir() + .join(format!("paperclip-indexed-{}", uuid::Uuid::new_v4())) + .join("state.sqlite") + } + + fn receipt(id: &str, bytes: &[u8]) -> ExactReceipt { + ExactReceipt { + namespace: "provider/session-1".into(), + key: id.into(), + bytes: bytes.to_vec(), + } + } + + #[test] + fn proof_receipts_are_generation_bound_and_do_not_repair_missing_state() { + let path = path(); + let store = IndexedStore::open(&path, "proof-owner", true).unwrap(); + store + .commit( + "authority", + 0, + b"current".to_vec(), + vec![receipt("ancient", b"original")], + ) + .unwrap(); + assert_eq!( + read_only_receipt(&path, "proof-owner", 1, "provider/session-1", "ancient").unwrap(), + Some(b"original".to_vec()) + ); + assert!( + read_only_receipt(&path, "other-owner", 1, "provider/session-1", "ancient").is_err() + ); + store + .commit("authority", 1, b"new current".to_vec(), vec![]) + .unwrap(); + assert!( + read_only_receipt(&path, "proof-owner", 1, "provider/session-1", "ancient").is_err() + ); + assert_eq!( + read_only_receipt(&path, "proof-owner", 2, "provider/session-1", "missing").unwrap(), + None + ); + drop(store); + let missing = path.parent().unwrap().join("missing.sqlite"); + assert!( + read_only_receipt(&missing, "proof-owner", 2, "provider/session-1", "ancient").is_err() + ); + assert!(!missing.exists()); + assert!(!PathBuf::from(format!("{}.lifetime", missing.display())).exists()); + fs::remove_dir_all(path.parent().unwrap()).unwrap(); + } + + #[test] + fn outstanding_work_mutations_share_the_authority_boundary_and_page_after_restart() { + use crate::indexed_work::WorkChange; + let path = path(); + let store = IndexedStore::open(&path, "work-owner", true).unwrap(); + let change = |key: &str, expected: Option>, bytes: Option<&[u8]>| WorkChange { + collection: "process-owners".into(), + key: key.into(), + expected_digest: expected, + bytes: bytes.map(Vec::from), + }; + let generation = store + .commit_with_work( + "authority", + 0, + b"one".to_vec(), + vec![receipt("start", b"intent")], + vec![change("launch-1", None, Some(b"started"))], + ) + .unwrap(); + let owner = store + .read_work("process-owners", "launch-1") + .unwrap() + .unwrap(); + assert!(store + .commit_with_work( + "authority", + generation, + b"wrong".to_vec(), + vec![receipt("uncommitted", b"outcome")], + vec![ + change("launch-2", None, Some(b"new")), + change("launch-1", None, None) + ] + ) + .unwrap_err() + .to_string() + .contains("stale outstanding-work")); + assert!(store + .read_work("process-owners", "launch-2") + .unwrap() + .is_none()); + assert!(store + .receipt("provider/session-1", "uncommitted") + .unwrap() + .is_none()); + assert_eq!( + store.read_state("authority").unwrap().unwrap().bytes, + b"one" + ); + let work = (2..=129) + .map(|n| change(&format!("launch-{n:04}"), None, Some(b"unresolved"))) + .collect(); + store + .commit_with_work("authority", 1, b"two".to_vec(), vec![], work) + .unwrap(); + drop(store); + let store = IndexedStore::open(&path, "work-owner", false).unwrap(); + let first = store.work_page("process-owners", "", 128).unwrap(); + assert_eq!(first.len(), 128); + assert_eq!( + store + .work_page("process-owners", &first.last().unwrap().key, 128) + .unwrap() + .len(), + 1 + ); + store + .commit_with_work( + "authority", + 2, + b"three".to_vec(), + vec![receipt("retired", b"exact stop proof")], + vec![change("launch-1", Some(owner.digest), None)], + ) + .unwrap(); + assert!(store + .read_work("process-owners", "launch-1") + .unwrap() + .is_none()); + assert_eq!( + store + .receipt("provider/session-1", "start") + .unwrap() + .unwrap(), + b"intent" + ); + assert_eq!( + store + .receipt("provider/session-1", "retired") + .unwrap() + .unwrap(), + b"exact stop proof" + ); + drop(store); + fs::remove_dir_all(path.parent().unwrap()).unwrap(); + } + + #[test] + fn state_and_receipts_commit_together_and_conflicts_rollback() { + let path = path(); + { + let store = IndexedStore::open(&path, "binding-1", true).unwrap(); + assert_eq!( + store + .commit( + "authority", + 0, + b"current".to_vec(), + vec![receipt("first", b"result")] + ) + .unwrap(), + 1 + ); + assert!(store + .commit( + "authority", + 1, + b"wrong".to_vec(), + vec![receipt("second", b"new"), receipt("first", b"conflict")] + ) + .is_err()); + assert_eq!( + store.read_state("authority").unwrap().unwrap().bytes, + b"current" + ); + assert_eq!(store.receipt("provider/session-1", "second").unwrap(), None); + assert!(store + .commit("authority", 0, b"stale".to_vec(), vec![]) + .is_err()); + } + let store = IndexedStore::open(&path, "binding-1", false).unwrap(); + assert_eq!( + store + .receipt("provider/session-1", "first") + .unwrap() + .unwrap(), + b"result" + ); + assert!(IndexedStore::open(&path, "wrong-binding", false).is_err()); + drop(store); + fs::remove_dir_all(path.parent().unwrap()).unwrap(); + } + + #[test] + fn missing_activated_store_is_not_a_cache_miss() { + let path = path(); + assert!(IndexedStore::open(&path, "binding-1", false).is_err()); + } + + #[test] + fn hundred_thousand_receipts_do_not_enter_current_state() { + let path = path(); + { + let store = IndexedStore::open(&path, "binding-1", true).unwrap(); + for batch in 0..100u64 { + let receipts = (batch * 1_000..(batch + 1) * 1_000) + .map(|id| receipt(&format!("call-{id}"), format!("result-{id}").as_bytes())) + .collect(); + store + .commit( + "authority", + batch, + b"bounded-current-state".to_vec(), + receipts, + ) + .unwrap(); + } + } + let store = IndexedStore::open(&path, "binding-1", false).unwrap(); + assert_eq!( + store.read_state("authority").unwrap().unwrap(), + StoredSnapshot { + generation: Revision::from(100), + bytes: b"bounded-current-state".to_vec() + } + ); + assert_eq!( + store + .receipt("provider/session-1", "call-0") + .unwrap() + .unwrap(), + b"result-0" + ); + assert_eq!(store.receipt("other-session", "call-0").unwrap(), None); + drop(store); + let db = Connection::open(&path).unwrap(); + let (routing_heads, largest_head_bytes): (i64, i64) = db + .query_row( + "SELECT count(*), coalesce(max(length(root)), 0) FROM receipt_routing_heads", + [], + |row| Ok((row.get(0)?, row.get(1)?)), + ) + .unwrap(); + assert!( + routing_heads <= 1, + "unexpected routing head count: {routing_heads}" + ); + assert!( + largest_head_bytes <= 16_384, + "routing head grew to {largest_head_bytes} bytes" + ); + let mut cursor: Option = None; + let mut count = 0_u64; + loop { + let page = crate::indexed_partitions::partitions(&db, cursor.as_deref(), 128).unwrap(); + if page.is_empty() { + break; + } + count += page.iter().map(|partition| partition.records).sum::(); + cursor = page.last().map(|partition| partition.lower_key.clone()); + } + assert_eq!(count, 100_000); + drop(db); + fs::remove_dir_all(path.parent().unwrap()).unwrap(); + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/indexed_work.rs b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_work.rs new file mode 100644 index 00000000000..f345b00ba40 --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/indexed_work.rs @@ -0,0 +1,111 @@ +//! Outstanding work is separate from immutable history and from the one-row +//! current checkpoint. Exact mutations share the authority commit boundary. +use crate::{durable::DurableRunnerError, indexed_store::storage_error}; +use rusqlite::{params, Connection, OptionalExtension}; +use sha2::{Digest, Sha256}; +type Result = std::result::Result; + +#[derive(Clone, Debug)] +pub struct WorkChange { + pub collection: String, + pub key: String, + pub expected_digest: Option>, + pub bytes: Option>, +} +#[derive(Clone, Debug, PartialEq)] +pub struct WorkRecord { + pub key: String, + pub bytes: Vec, + pub digest: Vec, +} +fn invalid(message: &str) -> DurableRunnerError { + DurableRunnerError::invalid(message) +} +pub(crate) fn initialize(db: &Connection) -> Result<()> { + db.execute_batch("CREATE TABLE IF NOT EXISTS current_work(collection TEXT NOT NULL,key TEXT NOT NULL,bytes BLOB NOT NULL,digest BLOB NOT NULL,PRIMARY KEY(collection,key)) WITHOUT ROWID;").map_err(storage_error) +} +pub(crate) fn validate(changes: &[WorkChange]) -> Result<()> { + if changes.len() > 128 { + return Err(invalid( + "storage_pressure: outstanding-work transaction bound", + )); + } + for (index, change) in changes.iter().enumerate() { + if change.collection.is_empty() + || change.collection.len() > 128 + || change.key.is_empty() + || change.key.len() > 1024 + || change.collection.contains('\0') + || change.key.contains('\0') + || change + .expected_digest + .as_ref() + .is_some_and(|d| d.len() != 32) + || change.bytes.as_ref().is_some_and(|b| b.len() > 16384) + || changes[..index] + .iter() + .any(|prior| prior.collection == change.collection && prior.key == change.key) + { + return Err(invalid("invalid outstanding-work mutation")); + } + } + Ok(()) +} +pub(crate) fn get(db: &Connection, collection: &str, key: &str) -> Result> { + let value:Option<(Vec,Vec)>=db.query_row("SELECT CASE WHEN length(bytes)<=16384 THEN bytes END,CASE WHEN length(digest)=32 THEN digest END FROM current_work WHERE collection=?1 AND key=?2",params![collection,key],|r|Ok((r.get(0)?,r.get(1)?))).optional().map_err(storage_error)?; + value + .map(|(bytes, digest)| { + if Sha256::digest(&bytes).as_slice() != digest { + return Err(invalid("outstanding-work digest mismatch")); + } + Ok(WorkRecord { + key: key.into(), + bytes, + digest, + }) + }) + .transpose() +} +pub(crate) fn page( + db: &Connection, + collection: &str, + after: &str, + limit: usize, +) -> Result> { + if !(1..=128).contains(&limit) || collection.len() > 128 || after.len() > 1024 { + return Err(invalid("invalid outstanding-work page")); + } + let mut query = db + .prepare( + "SELECT key FROM current_work WHERE collection=?1 AND key>?2 ORDER BY key LIMIT ?3", + ) + .map_err(storage_error)?; + let keys = query + .query_map(params![collection, after, limit as i64], |r| { + r.get::<_, String>(0) + }) + .map_err(storage_error)? + .collect::, _>>() + .map_err(storage_error)?; + keys.iter() + .map(|key| get(db, collection, key)?.ok_or_else(|| invalid("outstanding work disappeared"))) + .collect() +} +pub(crate) fn apply(db: &Connection, changes: &[WorkChange]) -> Result<()> { + validate(changes)?; + for change in changes { + if get(db, &change.collection, &change.key)?.map(|r| r.digest) != change.expected_digest { + return Err(invalid("stale outstanding-work digest")); + } + if let Some(bytes) = &change.bytes { + db.execute("INSERT INTO current_work VALUES(?1,?2,?3,?4) ON CONFLICT(collection,key) DO UPDATE SET bytes=excluded.bytes,digest=excluded.digest",params![change.collection,change.key,bytes,Sha256::digest(bytes).to_vec()]).map_err(storage_error)?; + } else { + db.execute( + "DELETE FROM current_work WHERE collection=?1 AND key=?2", + params![change.collection, change.key], + ) + .map_err(storage_error)?; + } + } + Ok(()) +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/legacy_indexed_import.rs b/packages/paperclip-runner/runner/crates/runner-core/src/legacy_indexed_import.rs new file mode 100644 index 00000000000..a883173fdd8 --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/legacy_indexed_import.rs @@ -0,0 +1,448 @@ +//! Fenced, restartable preparation of legacy local authority. The caller owns +//! the session fence; this module never replaces a live locator or source file. + +use crate::indexed_revision::Revision; +use std::fs::{self, File}; +use std::io::{self, BufReader, Read, Write}; +use std::path::{Path, PathBuf}; + +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use sha2::{Digest, Sha256}; + +use crate::durable::{ + create_private_temporary_file, open_private_regular_file, DurableRunnerError, +}; +use crate::indexed_store::{ExactReceipt, IndexedStore}; + +type Result = std::result::Result; +const PROGRESS: &str = "legacy-import"; +const PAGE_BYTES: usize = 8 * 1024 * 1024; + +fn invalid(message: impl Into) -> DurableRunnerError { + DurableRunnerError::invalid(message) +} + +struct HashReader { + inner: R, + digest: Sha256, + remaining: u64, +} +impl Read for HashReader { + fn read(&mut self, bytes: &mut [u8]) -> io::Result { + let count = self.inner.read(bytes)?; + self.remaining = self + .remaining + .checked_sub(count as u64) + .ok_or_else(|| io::Error::other("legacy source exceeded its admission bound"))?; + self.digest.update(&bytes[..count]); + Ok(count) + } +} + +fn same_file(a: &fs::Metadata, b: &fs::Metadata) -> bool { + #[cfg(unix)] + { + use std::os::unix::fs::MetadataExt; + a.dev() == b.dev() + && a.ino() == b.ino() + && a.len() == b.len() + && a.mtime() == b.mtime() + && a.mtime_nsec() == b.mtime_nsec() + && a.ctime() == b.ctime() + && a.ctime_nsec() == b.ctime_nsec() + } + #[cfg(not(unix))] + { + a.len() == b.len() && a.modified().ok() == b.modified().ok() + } +} + +/// The bound is the old format's admitted pending state, not indexed history. +/// Hash and decode the source once without allocating a second whole-file copy. +pub(crate) struct LegacySource { + pub digest: String, + path: PathBuf, + metadata: fs::Metadata, +} +impl LegacySource { + pub fn verify(&self) -> Result<()> { + let current = open_private_regular_file(&self.path) + .and_then(|file| file.metadata()) + .map_err(|e| invalid(e.to_string()))?; + if !same_file(&self.metadata, ¤t) { + return Err(invalid("legacy source changed during import")); + } + Ok(()) + } +} + +pub(crate) fn read_legacy( + path: &Path, + maximum: u64, +) -> Result<(T, LegacySource)> { + let file = open_private_regular_file(path).map_err(|e| invalid(e.to_string()))?; + let before = file.metadata().map_err(|e| invalid(e.to_string()))?; + if before.len() > maximum { + return Err(invalid("legacy source exceeds its admission bound")); + } + let mut reader = HashReader { + inner: BufReader::with_capacity(64 * 1024, file), + digest: Sha256::new(), + remaining: maximum, + }; + let state = serde_json::from_reader(&mut reader) + .map_err(|e| invalid(format!("invalid legacy source: {e}")))?; + let after = reader + .inner + .get_ref() + .metadata() + .map_err(|e| invalid(e.to_string()))?; + let current = open_private_regular_file(path) + .and_then(|file| file.metadata()) + .map_err(|e| invalid(e.to_string()))?; + if !same_file(&before, &after) || !same_file(&before, ¤t) { + return Err(invalid("legacy source changed during import")); + } + Ok(( + state, + LegacySource { + digest: format!("{:x}", reader.digest.finalize()), + path: path.into(), + metadata: before, + }, + )) +} + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct PreparedLegacyAuthority { + pub schema: String, + pub binding: String, + pub fence_id: String, + pub source_digest: String, + pub current_digest: String, + pub current_key: String, + pub generation: u64, + pub receipts: u64, +} + +#[derive(Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +struct Progress { + schema: String, + binding: String, + fence_id: String, + source_digest: String, + receipts: u64, + receipt_digest: String, + prepared: Option, +} + +pub(crate) struct ImportWriter { + pub store: IndexedStore, + progress: Progress, + generation: Revision, + seen: u64, + digest: Sha256, + pending: Vec, + bytes: usize, +} + +impl ImportWriter { + pub fn open(path: &Path, binding: &str, fence: &str, source_digest: &str) -> Result { + if fence.is_empty() || fence.len() > 240 || fence.chars().any(char::is_control) { + return Err(invalid( + "legacy import requires a bounded exclusive fence identity", + )); + } + let store = IndexedStore::open(path, binding, true)?; + let saved = store.read_state(PROGRESS)?; + let mut generation = saved.as_ref().map_or(Revision::Absent, |s| s.generation); + let progress: Progress = match saved { + Some(saved) => { + serde_json::from_slice(&saved.bytes).map_err(|e| invalid(e.to_string()))? + } + None => Progress { + schema: "paperclip.runner.local-import.v1".into(), + binding: binding.into(), + fence_id: fence.into(), + source_digest: source_digest.into(), + receipts: 0, + receipt_digest: format!("{:x}", Sha256::digest([])), + prepared: None, + }, + }; + if progress.schema != "paperclip.runner.local-import.v1" + || progress.binding != binding + || progress.fence_id != fence + || progress.source_digest != source_digest + { + return Err(invalid("legacy import binding, fence, or source changed")); + } + if generation == 0 { + // Never adopt a live store as an import destination. + if store.read_state("runner")?.is_some() + || store.read_state("codex-provider")?.is_some() + { + return Err(invalid( + "legacy import destination is already an execution authority", + )); + } + generation = store.commit( + PROGRESS, + 0, + serde_json::to_vec(&progress).map_err(|e| invalid(e.to_string()))?, + vec![], + )?; + } + Ok(Self { + store, + progress, + generation, + seen: 0, + digest: Sha256::new(), + pending: vec![], + bytes: 0, + }) + } + + pub fn add(&mut self, receipt: ExactReceipt) -> Result<()> { + if receipt.bytes.len() > 32 * 1024 * 1024 { + return Err(invalid("legacy receipt exceeds per-record admission bound")); + } + for part in [ + receipt.namespace.as_bytes(), + receipt.key.as_bytes(), + receipt.bytes.as_slice(), + ] { + self.digest.update((part.len() as u64).to_be_bytes()); + self.digest.update(part); + } + self.seen = self + .seen + .checked_add(1) + .ok_or_else(|| invalid("legacy receipt cursor overflow"))?; + if self.seen <= self.progress.receipts { + if self.seen == self.progress.receipts + && format!("{:x}", self.digest.clone().finalize()) != self.progress.receipt_digest + { + return Err(invalid("legacy receipt prefix changed")); + } + return Ok(()); + } + self.bytes += receipt.bytes.len(); + self.pending.push(receipt); + if self.pending.len() >= 128 || self.bytes >= PAGE_BYTES { + self.flush()?; + } + Ok(()) + } + + fn flush(&mut self) -> Result<()> { + if self.pending.is_empty() { + return Ok(()); + } + self.progress.receipts = self.seen; + self.progress.receipt_digest = format!("{:x}", self.digest.clone().finalize()); + self.generation = self.store.commit( + PROGRESS, + self.generation, + serde_json::to_vec(&self.progress).map_err(|e| invalid(e.to_string()))?, + std::mem::take(&mut self.pending), + )?; + self.bytes = 0; + Ok(()) + } + + pub fn finish(mut self, key: &str, current: Vec) -> Result { + if self.seen < self.progress.receipts { + return Err(invalid("legacy receipt source is incomplete")); + } + self.flush()?; + let generation = match self.store.read_state(key)? { + None => self.store.commit(key, 0, current.clone(), vec![])?, + Some(existing) if existing.generation == 1 && existing.bytes == current => { + Revision::from(1) + } + Some(_) => return Err(invalid("legacy import destination has advanced or changed")), + }; + let proof = PreparedLegacyAuthority { + schema: "paperclip.runner.prepared-legacy-authority.v1".into(), + binding: self.progress.binding.clone(), + fence_id: self.progress.fence_id.clone(), + source_digest: self.progress.source_digest.clone(), + current_digest: format!("{:x}", Sha256::digest(¤t)), + current_key: key.into(), + generation: generation + .legacy() + .ok_or_else(|| invalid("legacy preparation revision is not initial"))?, + receipts: self.seen, + }; + if self + .progress + .prepared + .as_ref() + .is_some_and(|old| old != &proof) + { + return Err(invalid("prepared legacy authority changed")); + } + self.progress.prepared = Some(proof.clone()); + self.store.commit( + PROGRESS, + self.generation, + serde_json::to_vec(&self.progress).map_err(|e| invalid(e.to_string()))?, + vec![], + )?; + Ok(proof) + } +} + +pub(crate) fn require_prepared(store: &IndexedStore) -> Result<()> { + if let Some(saved) = store.read_state(PROGRESS)? { + let progress: Progress = + serde_json::from_slice(&saved.bytes).map_err(|e| invalid(e.to_string()))?; + if progress.prepared.is_none() { + return Err(invalid("legacy import has not prepared its authority")); + } + } + Ok(()) +} + +pub(crate) fn require_active_directory(directory: &Path) -> Result<()> { + let Some(root) = directory.parent() else { + return Ok(()); + }; + let path = root.join("indexed-migration.json"); + let mut file = match open_private_regular_file(&path) { + Ok(file) => file, + Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(()), + Err(e) => return Err(invalid(e.to_string())), + }; + let mut bytes = Vec::new(); + Read::by_ref(&mut file) + .take(16 * 1024 + 1) + .read_to_end(&mut bytes) + .map_err(|e| invalid(e.to_string()))?; + let marker: serde_json::Value = serde_json::from_slice(&bytes) + .map_err(|_| invalid("legacy migration marker is invalid"))?; + if bytes.len() > 16 * 1024 + || marker["schema"] != "paperclip.runner.legacy-activation.v1" + || marker["phase"] != "active" + { + return Err(invalid("legacy migration is pending; execution is fenced")); + } + Ok(()) +} + +/// Used only inside a staging directory. Existing bytes must be identical. +pub(crate) fn publish_staged_locator(path: &Path, bytes: &[u8]) -> Result<()> { + match open_private_regular_file(path) { + Ok(mut file) => { + let mut existing = Vec::new(); + Read::by_ref(&mut file) + .take(8193) + .read_to_end(&mut existing) + .map_err(|e| invalid(e.to_string()))?; + return if existing == bytes { + Ok(()) + } else { + Err(invalid( + "staged locator already contains different authority", + )) + }; + } + Err(e) if e.kind() == io::ErrorKind::NotFound => (), + Err(e) => return Err(invalid(e.to_string())), + } + let (temporary, mut file) = create_private_temporary_file(path)?; + file.write_all(bytes) + .and_then(|_| file.sync_all()) + .map_err(|e| invalid(e.to_string()))?; + // Linking is no-clobber, unlike rename over a concurrently-created locator. + fs::hard_link(&temporary, path).map_err(|e| invalid(e.to_string()))?; + fs::remove_file(temporary).map_err(|e| invalid(e.to_string()))?; + File::open( + path.parent() + .ok_or_else(|| invalid("locator lacks parent"))?, + ) + .and_then(|f| f.sync_all()) + .map_err(|e| invalid(e.to_string())) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn legacy_import_resumes_exact_pages_without_publishing_partial_authority() { + let root = std::env::temp_dir().join(format!("local-import-{}", uuid::Uuid::new_v4())); + let path = root.join("runner-state.sqlite"); + let receipt = |n| ExactReceipt { + namespace: "tools".into(), + key: format!("call-{n:04}"), + bytes: vec![n as u8; 512], + }; + { + let mut writer = ImportWriter::open(&path, "binding", "fence", "source").unwrap(); + for n in 0..150 { + writer.add(receipt(n)).unwrap(); + } + assert!(writer.store.read_state("runner").unwrap().is_none()); + assert!(require_prepared(&writer.store).is_err()); + // Simulated importer loss after a committed page and before the + // buffered remainder. No current authority exists yet. + } + assert!(ImportWriter::open(&path, "binding", "other-fence", "source").is_err()); + assert!(ImportWriter::open(&path, "binding", "fence", "other-source").is_err()); + let mut writer = ImportWriter::open(&path, "binding", "fence", "source").unwrap(); + for n in 0..500 { + writer.add(receipt(n)).unwrap(); + } + let proof = writer.finish("runner", b"current".to_vec()).unwrap(); + assert_eq!(proof.receipts, 500); + let store = IndexedStore::open(&path, "binding", false).unwrap(); + require_prepared(&store).unwrap(); + for n in [0, 127, 128, 499] { + assert_eq!( + store.receipt("tools", &format!("call-{n:04}")).unwrap(), + Some(receipt(n).bytes) + ); + } + // Commit-before-publication retry is identical, not a new generation. + let mut writer = ImportWriter::open(&path, "binding", "fence", "source").unwrap(); + for n in 0..500 { + writer.add(receipt(n)).unwrap(); + } + assert_eq!(writer.finish("runner", b"current".to_vec()).unwrap(), proof); + store + .commit("runner", 1, b"advanced".to_vec(), vec![]) + .unwrap(); + let mut writer = ImportWriter::open(&path, "binding", "fence", "source").unwrap(); + for n in 0..500 { + writer.add(receipt(n)).unwrap(); + } + assert!(writer.finish("runner", b"current".to_vec()).is_err()); + drop(store); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn legacy_source_reader_hashes_exact_bytes_and_rejects_changed_or_oversized_input() { + let root = + std::env::temp_dir().join(format!("local-import-source-{}", uuid::Uuid::new_v4())); + fs::create_dir(&root).unwrap(); + let path = root.join("source.json"); + let (temporary, mut file) = create_private_temporary_file(&path).unwrap(); + let bytes = b"{\"pending\":[1,2,3]}\n"; + file.write_all(bytes).unwrap(); + drop(file); + fs::rename(temporary, &path).unwrap(); + let (value, source): (serde_json::Value, _) = read_legacy(&path, 1024).unwrap(); + assert_eq!(value["pending"][2], 3); + assert_eq!(source.digest, format!("{:x}", Sha256::digest(bytes))); + assert!(read_legacy::(&path, 2).is_err()); + fs::write(&path, b"{}").unwrap(); + assert!(source.verify().is_err()); + fs::remove_dir_all(root).unwrap(); + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/lib.rs b/packages/paperclip-runner/runner/crates/runner-core/src/lib.rs index c81cd7a5eb9..db1c5ccff2a 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/lib.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/lib.rs @@ -6,6 +6,7 @@ pub mod acpx_provider_backend; pub mod acpx_provider_checkpoint; pub mod acpx_provider_session; pub mod acpx_provider_state; +mod acpx_receipt_ledger; pub mod acpx_sidecar_transport; pub mod aws_agentcore_provider; pub mod claude_managed_provider; @@ -14,6 +15,17 @@ mod codex_startup_trust; pub mod durable; pub mod fake_harness; pub mod generated_acpx_sidecar_contract; +pub mod indexed_archive; +mod indexed_commit; +pub mod indexed_inspection; +mod indexed_lifetime; +mod indexed_partitions; +pub mod indexed_revision; +pub mod indexed_rpc; +pub mod indexed_session_snapshot; +pub mod indexed_store; +pub mod indexed_work; +pub mod legacy_indexed_import; pub mod local_runner; pub mod managed_provider; pub mod managed_provider_backend; @@ -26,6 +38,7 @@ pub mod qualified_launch; pub mod question_response; pub mod replay; mod stable_identity; +mod storage_capacity; use std::error::Error; use std::fmt::{self, Display, Formatter}; @@ -362,3 +375,7 @@ mod tests { ); } } + +mod output_body; + +mod process_generation; diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/managed_provider_backend.rs b/packages/paperclip-runner/runner/crates/runner-core/src/managed_provider_backend.rs index 2c9dedeb26e..80166f3a06a 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/managed_provider_backend.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/managed_provider_backend.rs @@ -2372,6 +2372,7 @@ mod tests { fn test_command(sequence: u64, command_type: &str, payload: Value) -> Command { Command { + controller_epoch: None, schema: "paperclip.prp.command.v1".to_owned(), command_id: format!("command-{sequence}"), controller_seq: sequence, diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/output_body.rs b/packages/paperclip-runner/runner/crates/runner-core/src/output_body.rs new file mode 100644 index 00000000000..df09c7ab1d5 --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/output_body.rs @@ -0,0 +1,190 @@ +//! Lossless, credential-scrubbed output, carried in independently bounded frames. +//! The preview is display-only. Chunks precede its reference and share the +//! authenticated run/session envelope; neither grants mutation authority. +use crate::durable::{redact_output_text, DurableRunnerError, EventPriority}; +use crate::provider_events::NormalizedProviderEvent; +use serde_json::{json, Value}; +use sha2::{Digest, Sha256}; + +pub const CAPABILITY: &str = "history.output_bodies.v1"; +pub const CHUNK_BYTES: usize = 32 * 1024 - 2; +const MAX_BODY_BYTES: usize = 4 * 1024 * 1024; + +fn digest(bytes: &[u8]) -> String { + format!("{:x}", Sha256::digest(bytes)) +} + +pub fn add_codex_body(method: &str, params: &Value, events: &mut Vec) { + let text = match method { + "item/agentMessage/delta" | "item/commandExecution/outputDelta" => params.get("delta"), + "item/started" | "item/completed" => params + .pointer("/item/text") + .or_else(|| params.pointer("/item/aggregatedOutput")) + .or_else(|| params.pointer("/item/output")), + _ => None, + } + .and_then(Value::as_str); + let Some(text) = text.filter(|text| text.len() > 4_000) else { + return; + }; + if events.is_empty() { + return; + } + // Admission already bounded the provider frame. Redact before splitting so + // a credential crossing a chunk boundary cannot escape the scrubber. + let safe = redact_output_text(text); + let mut chunks = Vec::new(); + let mut rest = safe.as_str(); + while !rest.is_empty() { + let mut end = rest.len().min(CHUNK_BYTES); + while !rest.is_char_boundary(end) { + end -= 1; + } + // Include JSON escaping in the per-frame budget (e.g. NUL or controls). + while serde_json::to_vec(&rest[..end]) + .expect("text serializes") + .len() + > 32 * 1024 + { + end /= 2; + while !rest.is_char_boundary(end) { + end -= 1; + } + } + // Scrubbing is idempotent; apply it to the independently readable chunk + // as well, including credential-like suffixes produced by the split. + chunks.push(redact_output_text(&rest[..end])); + rest = &rest[end..]; + } + let mut hash = Sha256::new(); + let mut length = 0usize; + for chunk in &chunks { + hash.update(chunk.as_bytes()); + length += chunk.len(); + } + let sha = format!("{:x}", hash.finalize()); + let reference = json!({"schema":"paperclip.output.body.v1", "bodyId":sha, + "sha256":sha, "byteLength":length.to_string(), "mediaType":"text/plain; charset=utf-8"}); + events[0].payload["outputBody"] = reference.clone(); + let mut prefix = Vec::with_capacity(chunks.len() + events.len()); + let mut offset = 0usize; + for chunk in chunks { + prefix.push(NormalizedProviderEvent { + event_type: "output.body.chunk".into(), + priority: EventPriority::P1, + payload: json!({"schema":"paperclip.output.body.chunk.v1", "body":reference, + "offset":offset.to_string(), "sha256":digest(chunk.as_bytes()), "text":chunk}), + }); + offset += chunk.len(); + } + prefix.append(events); + *events = prefix; +} + +pub fn sanitize_chunk(payload: &Value) -> Result { + let invalid = || DurableRunnerError::invalid("invalid durable output body chunk"); + let body = payload.get("body").ok_or_else(invalid)?; + let text = payload + .get("text") + .and_then(Value::as_str) + .ok_or_else(invalid)?; + let integer = |value: Option<&Value>| -> Result { + let s = value.and_then(Value::as_str).ok_or_else(invalid)?; + let n = s.parse::().map_err(|_| invalid())?; + if n.to_string() != s { + return Err(invalid()); + } + Ok(n) + }; + let offset = integer(payload.get("offset"))?; + let length = integer(body.get("byteLength"))?; + let sha = body + .get("sha256") + .and_then(Value::as_str) + .ok_or_else(invalid)?; + if payload["schema"] != "paperclip.output.body.chunk.v1" + || body["schema"] != "paperclip.output.body.v1" + || body["mediaType"] != "text/plain; charset=utf-8" + || body["bodyId"] != sha + || sha.len() != 64 + || !sha + .bytes() + .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b)) + || text.is_empty() + || text.len() > CHUNK_BYTES + || length > MAX_BODY_BYTES + || offset + .checked_add(text.len()) + .is_none_or(|end| end > length) + || payload["sha256"] != digest(text.as_bytes()) + || redact_output_text(text) != text + || payload.as_object().is_none_or(|o| o.len() != 5) + || body.as_object().is_none_or(|o| o.len() != 5) + { + return Err(invalid()); + } + Ok(payload.clone()) +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn preserves_large_command_delta_without_waiting_for_completion() { + let text = "tool-output-🌳".repeat(10_000); + let params = json!({"itemId":"exec-1", "delta":text}); + let mut events = crate::provider_events::normalize_codex_notification( + "item/commandExecution/outputDelta", + ¶ms, + ); + add_codex_body("item/commandExecution/outputDelta", ¶ms, &mut events); + let last = events.last().unwrap(); + assert_eq!(last.event_type, "item.delta"); + assert_eq!(last.payload["kind"], "commandExecution"); + let mut body = String::new(); + for event in &events[..events.len() - 1] { + sanitize_chunk(&event.payload).unwrap(); + body.push_str(event.payload["text"].as_str().unwrap()); + } + assert_eq!(body, text); + assert_eq!( + last.payload["outputBody"]["sha256"], + digest(body.as_bytes()) + ); + } + + #[test] + fn chunks_preserve_large_unicode_output_and_scrub_boundary_credentials() { + let text = format!( + "{} api_key=super-secret-value {}", + "🌳".repeat(8200), + "z".repeat(25_000) + ); + let mut events = vec![NormalizedProviderEvent { + event_type: "item.completed".into(), + priority: EventPriority::P1, + payload: json!({"text":"preview"}), + }]; + add_codex_body( + "item/completed", + &json!({"item":{"text":text}}), + &mut events, + ); + let mut output = String::new(); + for event in &events[..events.len() - 1] { + sanitize_chunk(&event.payload).unwrap(); + assert_eq!(event.payload["offset"], output.len().to_string()); + assert!(serde_json::to_vec(&event.payload).unwrap().len() < 60_000); + output.push_str(event.payload["text"].as_str().unwrap()); + } + assert!(!output.contains("super-secret-value")); + assert_eq!(output.matches('🌳').count(), 8200); + assert_eq!( + events.last().unwrap().payload["outputBody"]["sha256"], + digest(output.as_bytes()) + ); + let mut altered = events[0].payload.clone(); + altered["text"] = json!("altered"); + assert!(sanitize_chunk(&altered).is_err()); + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/process_generation.rs b/packages/paperclip-runner/runner/crates/runner-core/src/process_generation.rs new file mode 100644 index 00000000000..801d3163298 --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/process_generation.rs @@ -0,0 +1,164 @@ +//! Provider lifetime identity. Retained small ordinals keep their exact JSON; +//! exhausting that namespace switches permanently to equality-only UUIDs. +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use uuid::Uuid; + +pub const LEGACY_PROCESS_GENERATION_MAX: u64 = 9_007_199_254_740_991; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ProcessGeneration { + Legacy(u64), + Opaque(Uuid), +} +impl Default for ProcessGeneration { + fn default() -> Self { + Self::Legacy(0) + } +} +impl From for ProcessGeneration { + fn from(value: u64) -> Self { + Self::Legacy(value) + } +} +impl PartialEq for ProcessGeneration { + fn eq(&self, value: &u64) -> bool { + matches!(self, Self::Legacy(n) if n == value) + } +} +impl ProcessGeneration { + pub fn next(self) -> Option { + if let Self::Legacy(n) = self { + if n < LEGACY_PROCESS_GENERATION_MAX { + return Some(Self::Legacy(n + 1)); + } + } + for _ in 0..32 { + let next = Self::Opaque(Uuid::new_v4()); + if next != self { + return Some(next); + } + } + None + } + pub fn opaque(self) -> bool { + matches!(self, Self::Opaque(_)) + } + pub fn is_successor_of(self, previous: Self) -> bool { + match (previous, self) { + (Self::Legacy(a), Self::Legacy(b)) => a < LEGACY_PROCESS_GENERATION_MAX && b == a + 1, + (Self::Legacy(a), Self::Opaque(_)) => a >= LEGACY_PROCESS_GENERATION_MAX, + (Self::Opaque(a), Self::Opaque(b)) => a != b, + _ => false, + } + } + /// Retained state used ordinal ordering as an additional corruption check. + /// An opaque lifetime is never ordered against another opaque lifetime; + /// completion authority can intentionally belong to an older process. + pub fn rejects_completed(self, completed: Self) -> bool { + match (self, completed) { + (Self::Legacy(a), Self::Legacy(b)) => b > a, + (Self::Legacy(_), Self::Opaque(_)) => true, + _ => false, + } + } +} +impl Serialize for ProcessGeneration { + fn serialize(&self, serializer: S) -> Result { + match self { + Self::Legacy(n) => serializer.serialize_u64(*n), + Self::Opaque(id) => serializer.serialize_str(&format!("p:{id}")), + } + } +} +impl<'de> Deserialize<'de> for ProcessGeneration { + fn deserialize>(deserializer: D) -> Result { + #[derive(Deserialize)] + #[serde(untagged)] + enum Wire { + Legacy(u64), + Opaque(String), + } + match Wire::deserialize(deserializer)? { + Wire::Legacy(n) if n <= LEGACY_PROCESS_GENERATION_MAX => Ok(Self::Legacy(n)), + Wire::Legacy(_) => Err(serde::de::Error::custom( + "legacy provider process generation exceeds the safe integer boundary", + )), + Wire::Opaque(s) => { + if let Some(id) = s.strip_prefix("p:").and_then(|s| Uuid::parse_str(s).ok()) { + if id.get_version_num() == 4 + && id.get_variant() == uuid::Variant::RFC4122 + && s == format!("p:{id}") + { + return Ok(Self::Opaque(id)); + } + } + Err(serde::de::Error::custom( + "invalid provider process generation", + )) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn opaque_lifetimes_preserve_exact_legacy_bytes_and_never_downgrade() { + let old = ProcessGeneration::from(LEGACY_PROCESS_GENERATION_MAX); + assert_eq!( + serde_json::to_string(&old).unwrap(), + LEGACY_PROCESS_GENERATION_MAX.to_string() + ); + let first = old.next().unwrap(); + let second = first.next().unwrap(); + assert!(first.opaque() && second.opaque()); + assert_ne!(first, second); + assert!(first.is_successor_of(old)); + assert!(second.is_successor_of(first)); + assert!(!old.is_successor_of(second)); + assert!(!first.is_successor_of(first)); + for generation in [ProcessGeneration::default(), old, first, second] { + assert_eq!( + serde_json::from_str::( + &serde_json::to_string(&generation).unwrap() + ) + .unwrap(), + generation + ); + } + assert!(old.rejects_completed(first)); + assert!(!second.rejects_completed(first)); + for invalid in [ + "\"p:bad\"", + "\"p:00000000-0000-0000-0000-000000000000\"", + "-1", + "1.5", + ] { + assert!(serde_json::from_str::(invalid).is_err()); + } + } + + #[test] + fn legacy_wire_rejects_values_above_the_javascript_safe_integer_boundary() { + let max = LEGACY_PROCESS_GENERATION_MAX.to_string(); + assert_eq!( + serde_json::from_str::(&max).unwrap(), + ProcessGeneration::Legacy(LEGACY_PROCESS_GENERATION_MAX) + ); + assert_eq!( + serde_json::to_string(&ProcessGeneration::Legacy(LEGACY_PROCESS_GENERATION_MAX)) + .unwrap(), + max + ); + assert!(serde_json::from_str::("9007199254740992").is_err()); + assert!(serde_json::from_str::("18446744073709551615").is_err()); + + // Defensive in-memory conversion can still advance an old oversized + // value without wrapping; persisted/wire values above the boundary are + // rejected because JavaScript cannot represent them exactly. + let oversized_legacy = ProcessGeneration::from(LEGACY_PROCESS_GENERATION_MAX + 1); + let successor = oversized_legacy.next().unwrap(); + assert!(successor.opaque()); + assert!(successor.is_successor_of(oversized_legacy)); + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/provider_backend.rs b/packages/paperclip-runner/runner/crates/runner-core/src/provider_backend.rs index cc86427b96d..676ab193911 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/provider_backend.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/provider_backend.rs @@ -1,7 +1,10 @@ +use crate::indexed_revision::Revision; +use crate::process_generation::ProcessGeneration; +use std::cell::RefCell; use std::collections::{HashSet, VecDeque}; use std::fs; -use std::io::{Read, Write}; -use std::path::PathBuf; +use std::io::{Read, Seek, Write}; +use std::path::{Path, PathBuf}; #[cfg(unix)] use std::fs::File; @@ -23,6 +26,7 @@ use crate::durable::{ CommandExecution, CommandExecutor, DurableRunnerConfig, DurableRunnerError, EventPriority, OpenCodeLaunchProfile, PolledEvent, TerminalDeliveryReconciliation, }; +use crate::indexed_store::{ExactReceipt, IndexedStore}; use crate::provider_bridge::{ authorized_tool_catalog_digest, semantic_value_digest, AuthorizedToolSet, DurableReplayFilter, PendingToolCall, ProviderBridgeError, ProviderToolBridge, ToolResult, MAX_PENDING_CALLS, @@ -36,6 +40,8 @@ use crate::stable_identity::{is_stable_id, DURABLE_STABLE_ID_CHARS, SHORT_STABLE const PROVIDER_STATE_SCHEMA: &str = "paperclip.runner.codex-provider-state.v1"; pub const CODEX_PROVIDER_STATE_FILE: &str = "codex-provider-state.json"; +const INDEXED_PROVIDER_STATE_FILE: &str = "codex-provider-state.sqlite"; +const INDEXED_PROVIDER_LOCATOR_SCHEMA: &str = "paperclip.runner.codex-provider-state.indexed.v1"; const MAX_PROVIDER_STATE_BYTES: u64 = 16 * 1024 * 1024; const MAX_EVENTS_PER_POLL: usize = 128; // One accepted semantic call can produce an input and a result event. Normal @@ -124,7 +130,9 @@ struct ProviderStartupAttempt { launch_id: String, phase: ProviderStartupPhase, trigger: ProviderStartupTrigger, - attempted_process_generation: u64, + attempted_process_generation: ProcessGeneration, + #[serde(default, skip_serializing_if = "Option::is_none")] + previous_process_generation: Option, origin: Option, command: Option, configuration_fingerprint: String, @@ -139,6 +147,14 @@ struct ProviderStartupAttempt { process_tree_retired: bool, } +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct ProcessGenerationTransition { + from: ProcessGeneration, + to: ProcessGeneration, + launch_id: String, +} + impl ProviderStartupAttempt { fn validate(&self) -> Result<(), DurableRunnerError> { let identifier = |value: &str, limit: usize| { @@ -154,6 +170,7 @@ impl ProviderStartupAttempt { "paperclip.prp.command.v1" }; Command { + controller_epoch: None, schema: schema.to_owned(), command_id: command.command_id.clone(), controller_seq: command.controller_seq, @@ -190,6 +207,12 @@ impl ProviderStartupAttempt { if self.schema != "paperclip.provider_startup.v1" || uuid::Uuid::parse_str(&self.launch_id).is_err() || self.attempted_process_generation == 0 + || (self.attempted_process_generation.opaque() + && self.previous_process_generation.is_none_or(|previous| { + !self.attempted_process_generation.is_successor_of(previous) + })) + || (!self.attempted_process_generation.opaque() + && self.previous_process_generation.is_some()) || self.authenticated_thread_id.is_some() || self.process_tree_retired || !phase_valid @@ -281,6 +304,17 @@ fn provider_event_sequence(event_id: &str) -> Option { (provider_event_id(sequence) == event_id).then_some(sequence) } +fn opaque_provider_event_id(event_id: &str) -> bool { + event_id + .strip_prefix("codex_provider_r:") + .and_then(|id| uuid::Uuid::parse_str(id).ok().map(|value| (id, value))) + .is_some_and(|(id, value)| { + value.get_version_num() == 4 + && value.get_variant() == uuid::Variant::RFC4122 + && value.to_string() == id + }) +} + fn completion_contract( payload: &Value, ) -> Result, DurableRunnerError> { @@ -966,11 +1000,13 @@ struct CodexProviderState { #[serde(default)] completed_turn_authoritative: bool, #[serde(default)] - provider_process_generation: u64, + provider_process_generation: ProcessGeneration, + #[serde(default, skip_serializing_if = "Option::is_none")] + process_generation_transition: Option, #[serde(default)] startup_attempt: Option, #[serde(default)] - completed_turn_process_generation: Option, + completed_turn_process_generation: Option, #[serde(default)] completed_provider_turn_id: Option, // Unlike the live provider process, the durable run survives restarts. @@ -1007,6 +1043,8 @@ struct CodexProviderState { pending_events: VecDeque, #[serde(default)] queued_events: VecDeque, + // Exclusive upper bound for retained numeric IDs only. New IDs have no + // lifetime counter; pending/queued order is already durable in the deque. #[serde(default = "initial_provider_event_seq")] next_provider_event_seq: u64, } @@ -1044,6 +1082,39 @@ fn remember_settled_provider_turn( } impl CodexProviderState { + fn install_process_generation( + &mut self, + generation: ProcessGeneration, + ) -> Result<(), DurableRunnerError> { + if generation == self.provider_process_generation { + return Ok(()); + } + if !generation.is_successor_of(self.provider_process_generation) { + return Err(DurableRunnerError::invalid( + "provider process identity is not its prepared successor", + )); + } + if generation.opaque() { + let attempt = self.startup_attempt.as_ref().ok_or_else(|| { + DurableRunnerError::invalid("provider lifetime has no startup intent") + })?; + if attempt.attempted_process_generation != generation + || attempt.previous_process_generation != Some(self.provider_process_generation) + { + return Err(DurableRunnerError::invalid( + "provider lifetime differs from its startup intent", + )); + } + self.process_generation_transition = Some(ProcessGenerationTransition { + from: self.provider_process_generation, + to: generation, + launch_id: attempt.launch_id.clone(), + }); + } + self.provider_process_generation = generation; + Ok(()) + } + fn new( config: CodexProviderConfig, completion_contract: Option, @@ -1062,7 +1133,8 @@ impl CodexProviderState { active_provider_turn_id: None, ambiguous_turn_start_pending: false, completed_turn_authoritative: false, - provider_process_generation: 0, + provider_process_generation: 0.into(), + process_generation_transition: None, startup_attempt: None, completed_turn_process_generation: None, completed_provider_turn_id: None, @@ -1186,7 +1258,21 @@ impl CodexProviderState { }) || self .completed_turn_process_generation - .is_some_and(|generation| generation > self.provider_process_generation) + .is_some_and(|generation| { + self.provider_process_generation + .rejects_completed(generation) + }) + || (self.provider_process_generation.opaque() + && self + .process_generation_transition + .as_ref() + .is_none_or(|transition| { + transition.to != self.provider_process_generation + || !transition.to.is_successor_of(transition.from) + || uuid::Uuid::parse_str(&transition.launch_id).is_err() + })) + || (!self.provider_process_generation.opaque() + && self.process_generation_transition.is_some()) || (self.receipt_limit_diagnostic_emitted && self.active_provider_turn_id.is_none()) || (self.receipt_limit_interrupt_pending && (!self.receipt_limit_diagnostic_emitted @@ -1228,8 +1314,10 @@ impl CodexProviderState { .iter() .chain(self.queued_events.iter()) .any(|event| { - provider_event_sequence(&event.executor_event_id) - .is_none_or(|sequence| sequence >= self.next_provider_event_seq) + (!opaque_provider_event_id(&event.executor_event_id) + && provider_event_sequence(&event.executor_event_id).is_none_or( + |sequence| sequence == 0 || sequence >= self.next_provider_event_seq, + )) || !pending_event_ids.insert(event.executor_event_id.as_str()) || event.event_type.is_empty() || event.event_type.len() > 160 @@ -1256,12 +1344,8 @@ impl CodexProviderState { "Codex provider event backlog exceeds its durable limit", )); } - let sequence = self.next_provider_event_seq; - self.next_provider_event_seq = sequence - .checked_add(1) - .ok_or_else(|| DurableRunnerError::invalid("provider event sequence exhausted"))?; let event = PolledEvent { - executor_event_id: provider_event_id(sequence), + executor_event_id: format!("codex_provider_r:{}", uuid::Uuid::new_v4()), event_type: event.event_type, priority: event.priority, payload: event.payload, @@ -1488,6 +1572,7 @@ pub struct CodexCommandExecutor { opencode_launch_profile: Option, startup_command: Option, startup_evidence_error: Option, + indexed_storage: RefCell>, } impl CodexCommandExecutor { @@ -1502,6 +1587,7 @@ impl CodexCommandExecutor { opencode_launch_profile: None, startup_command: None, startup_evidence_error: None, + indexed_storage: RefCell::new(None), } } @@ -1547,6 +1633,178 @@ impl CodexCommandExecutor { self.state_dir.join(CODEX_PROVIDER_STATE_FILE) } + fn indexed_binding(&self) -> Result { + let identity = self.event_identity.as_ref().ok_or_else(|| { + DurableRunnerError::invalid( + "indexed provider state requires an authenticated session binding", + ) + })?; + Ok(format!( + "provider/{}/{}", + identity.runner_instance_id, identity.normalized_session_id + )) + } + + /// Prepare legacy Codex state without starting or changing its provider. + /// Publication into the live session belongs to the fenced coordinator. + pub fn stage_legacy( + &self, + destination: &Path, + fence_id: &str, + ) -> Result { + use crate::legacy_indexed_import::{publish_staged_locator, read_legacy, ImportWriter}; + verify_private_directory(&self.state_dir)?; + if destination == self.state_dir + || (destination.exists() + && destination.canonicalize().ok() == self.state_dir.canonicalize().ok()) + { + return Err(DurableRunnerError::invalid( + "legacy source cannot be the import destination", + )); + } + let (mut candidate, source): (CodexProviderState, _) = + read_legacy(&self.state_path(), MAX_PROVIDER_STATE_BYTES)?; + candidate + .tool_bridge + .attach_existing_run() + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?; + candidate.validate()?; + if candidate.config.provider != "codex" + || !candidate.settled_provider_turn_filter.is_empty() + { + return Err(DurableRunnerError::invalid( + "legacy provider migration requires exact Codex authority", + )); + } + candidate + .tool_bridge + .require_exact_migration_source() + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?; + let binding = self.indexed_binding()?; + let mut writer = ImportWriter::open( + &destination.join(INDEXED_PROVIDER_STATE_FILE), + &binding, + fence_id, + &source.digest, + )?; + candidate + .tool_bridge + .bind_indexed_receipts(writer.store.clone(), binding.clone()) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?; + for receipt in candidate + .tool_bridge + .checkpoint_indexed_receipts() + .map_err(|e| DurableRunnerError::invalid(e.to_string()))? + { + writer.add(receipt)?; + } + for turn in &candidate.settled_provider_turn_ids { + writer.add(ExactReceipt { + namespace: format!("turns/{binding}"), + key: turn.clone(), + bytes: b"settled".to_vec(), + })?; + } + candidate + .settled_provider_turn_ids + .retain(|turn| Some(turn) == candidate.completed_provider_turn_id.as_ref()); + for child in std::mem::take(&mut candidate.descendant_thread_ids) { + writer.add(ExactReceipt { + namespace: format!("descendants/{binding}"), + key: child, + bytes: b"settled".to_vec(), + })?; + } + candidate.validate()?; + source.verify()?; + let proof = writer.finish( + "codex-provider", + serde_json::to_vec(&candidate) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?, + )?; + publish_staged_locator( + &destination.join(CODEX_PROVIDER_STATE_FILE), + &serde_json::to_vec(&json!({ + "schema": INDEXED_PROVIDER_LOCATOR_SCHEMA, "binding": binding + })) + .map_err(|e| DurableRunnerError::invalid(e.to_string()))?, + )?; + Ok(proof) + } + + fn open_indexed_storage(&self, create: bool) -> Result { + if let Some((store, _)) = self.indexed_storage.borrow().as_ref() { + return Ok(store.clone()); + } + let store = IndexedStore::open( + &self.state_dir.join(INDEXED_PROVIDER_STATE_FILE), + &self.indexed_binding()?, + create, + )?; + let generation = store + .read_state("codex-provider")? + .map_or(Revision::Absent, |state| state.generation); + *self.indexed_storage.borrow_mut() = Some((store.clone(), generation)); + Ok(store) + } + + fn publish_indexed_locator(&self) -> Result<(), DurableRunnerError> { + let path = self.state_path(); + let locator = json!({ "schema": INDEXED_PROVIDER_LOCATOR_SCHEMA, "binding": self.indexed_binding()? }); + match open_private_regular_file(&path) { + Ok(mut file) => { + let mut bytes = Vec::new(); + std::io::Read::by_ref(&mut file) + .take(4097) + .read_to_end(&mut bytes) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?; + if bytes.len() <= 4096 + && serde_json::from_slice::(&bytes).ok().as_ref() == Some(&locator) + { + return Ok(()); + } + return Err(DurableRunnerError::invalid( + "cannot overwrite legacy provider authority during indexed activation", + )); + } + Err(error) if error.kind() == std::io::ErrorKind::NotFound => {} + Err(error) => { + return Err(DurableRunnerError::invalid(format!( + "indexed provider locator is unsafe: {error}" + ))) + } + } + let (temporary, mut file) = + crate::storage_capacity::retry(|| create_private_temporary_file(&path))?; + let bytes = serde_json::to_vec(&locator) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?; + // One owned spool survives all capacity retries. Restart each partial + // write at offset zero; never accumulate temporary files while full. + let written = crate::storage_capacity::retry(|| { + file.rewind() + .and_then(|_| file.write_all(&bytes)) + .and_then(|_| file.sync_all()) + .map_err(crate::indexed_store::storage_error) + }); + drop(file); + let published = written.and_then(|_| { + crate::storage_capacity::retry(|| { + fs::rename(&temporary, &path).map_err(crate::indexed_store::storage_error) + }) + }); + if published.is_err() { + let _ = fs::remove_file(&temporary); + } + published?; + #[cfg(unix)] + crate::storage_capacity::retry(|| { + File::open(&self.state_dir) + .and_then(|directory| directory.sync_all()) + .map_err(crate::indexed_store::storage_error) + })?; + Ok(()) + } + fn assert_startup_admitted(&self) -> Result<(), DurableRunnerError> { if let Some(error) = &self.startup_evidence_error { return Err(error.clone()); @@ -1589,7 +1847,7 @@ impl CodexCommandExecutor { fn begin_startup( &mut self, trigger: ProviderStartupTrigger, - generation: u64, + generation: ProcessGeneration, ) -> Result<(), DurableRunnerError> { self.assert_startup_admitted()?; let state = self @@ -1605,6 +1863,9 @@ impl CodexCommandExecutor { phase: ProviderStartupPhase::Intent, trigger, attempted_process_generation: generation, + previous_process_generation: generation + .opaque() + .then_some(state.provider_process_generation), origin: self.event_identity.clone(), command: self.startup_command.clone(), configuration_fingerprint: format!("sha256:{:x}", Sha256::digest(configuration)), @@ -1653,13 +1914,14 @@ impl CodexCommandExecutor { fn start_observed_provider( &mut self, trigger: ProviderStartupTrigger, - generation: u64, + generation: ProcessGeneration, ) -> Result { let state = self .state .clone() .ok_or_else(|| DurableRunnerError::invalid("provider startup state missing"))?; let profile = self.opencode_launch_profile.clone(); + let require_paginated_history = self.indexed_storage.borrow().is_some(); self.begin_startup(trigger, generation)?; CodexProvider::start_with_tools_observed( &state.config, @@ -1673,15 +1935,24 @@ impl CodexCommandExecutor { contract.criterion_ids.as_slice(), ) }), + require_paginated_history, &mut |observation| { self.observe_startup(observation).map_err(|error| { crate::local_runner::LocalRunnerError::invalid(error.to_string()) }) }, ) - .map(|mut provider| { - provider.restore_descendant_thread_identities(&state.descendant_thread_ids); - provider + .and_then(|mut provider| { + provider.restore_descendant_thread_identities(&state.descendant_thread_ids)?; + if let Some((store, _)) = self.indexed_storage.borrow().as_ref() { + provider.bind_indexed_turn_receipts( + store.clone(), + self.indexed_binding().map_err(|error| { + crate::local_runner::LocalRunnerError::invalid(error.to_string()) + })?, + )?; + } + Ok(provider) }) .map_err(|error| { DurableRunnerError::invalid(format!( @@ -1699,7 +1970,7 @@ impl CodexCommandExecutor { .clone() .ok_or_else(|| DurableRunnerError::invalid("provider startup state missing"))?; next.startup_attempt = None; - self.persist_state(&next)?; + self.persist_state(&mut next)?; self.state = Some(next); Ok(()) } @@ -1739,7 +2010,43 @@ impl CodexCommandExecutor { } fn load_state_without_provider(&mut self) -> Result<(), DurableRunnerError> { + crate::legacy_indexed_import::require_active_directory(&self.state_dir)?; let path = self.state_path(); + if self + .state_dir + .join(INDEXED_PROVIDER_STATE_FILE) + .try_exists() + .map_err(|error| DurableRunnerError::invalid(error.to_string()))? + { + let store = self.open_indexed_storage(false)?; + crate::legacy_indexed_import::require_prepared(&store)?; + let snapshot = store.read_state("codex-provider")?.ok_or_else(|| { + DurableRunnerError::invalid("indexed provider authority is missing") + })?; + let mut state: CodexProviderState = + serde_json::from_slice(&snapshot.bytes).map_err(|_| { + DurableRunnerError::invalid("indexed provider authority is malformed") + })?; + state + .tool_bridge + .bind_indexed_receipts(store, self.indexed_binding()?) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?; + state + .tool_bridge + .attach_existing_run() + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?; + state.validate()?; + let expected_launch_profile_digest = + self.bind_opencode_launch_profile(&state.config)?; + if state.opencode_launch_profile_digest != expected_launch_profile_digest { + return Err(DurableRunnerError::invalid( + "OpenCode runner launch profile changed across indexed recovery", + )); + } + self.publish_indexed_locator()?; + self.state = Some(state); + return Ok(()); + } let mut file = match open_private_regular_file(&path) { Ok(file) => file, Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(()), @@ -1806,12 +2113,9 @@ impl CodexCommandExecutor { )) })?; let previous_active_turn_id = state.active_provider_turn_id.clone(); - let process_generation = state - .provider_process_generation - .checked_add(1) - .ok_or_else(|| { - DurableRunnerError::invalid(format!("{provider_name} process generation exhausted")) - })?; + let process_generation = state.provider_process_generation.next().ok_or_else(|| { + DurableRunnerError::invalid(format!("{provider_name} process generation exhausted")) + })?; let completed_turn_authoritative = state.completed_turn_authoritative; let completed_turn_process_generation = state.completed_turn_process_generation; let completed_provider_turn_id = state.completed_provider_turn_id.clone(); @@ -1866,7 +2170,7 @@ impl CodexCommandExecutor { .state .as_mut() .expect("Codex state remains available during legacy recovery"); - state.provider_process_generation = process_generation; + state.install_process_generation(process_generation)?; state.settled_provider_turn_ids = settled_provider_turn_ids; state.settled_provider_turn_filter = settled_provider_turn_filter; state.active_provider_turn_id = None; @@ -1920,7 +2224,7 @@ impl CodexCommandExecutor { .state .as_mut() .expect("Codex state remains available during recovery"); - state.provider_process_generation = process_generation; + state.install_process_generation(process_generation)?; state.settled_provider_turn_ids = settled_provider_turn_ids; state.settled_provider_turn_filter = settled_provider_turn_filter; state.active_provider_turn_id = None; @@ -2008,7 +2312,7 @@ impl CodexCommandExecutor { .state .as_mut() .expect("Codex state remains available during recovery"); - state.provider_process_generation = process_generation; + state.install_process_generation(process_generation)?; state.provider_session_id = resumed_provider_session_id.clone(); state.settled_provider_turn_ids = settled_provider_turn_ids; state.settled_provider_turn_filter = settled_provider_turn_filter; @@ -2141,16 +2445,65 @@ impl CodexCommandExecutor { admission } - fn save_state(&self) -> Result<(), DurableRunnerError> { - let state = self + fn save_state(&mut self) -> Result<(), DurableRunnerError> { + let mut state = self .state .as_ref() - .ok_or_else(|| DurableRunnerError::invalid("Codex provider state is unavailable"))?; - self.persist_state(state) + .ok_or_else(|| DurableRunnerError::invalid("Codex provider state is unavailable"))? + .clone(); + self.persist_state(&mut state)?; + self.state = Some(state); + Ok(()) } - fn persist_state(&self, state: &CodexProviderState) -> Result<(), DurableRunnerError> { + fn persist_state(&self, state: &mut CodexProviderState) -> Result<(), DurableRunnerError> { + crate::legacy_indexed_import::require_active_directory(&self.state_dir)?; state.validate()?; + if let Some((store, generation)) = self.indexed_storage.borrow_mut().as_mut() { + let mut candidate = state.clone(); + candidate + .tool_bridge + .bind_indexed_receipts(store.clone(), self.indexed_binding()?) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?; + let mut receipts = candidate + .tool_bridge + .checkpoint_indexed_receipts() + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?; + for turn in &candidate.settled_provider_turn_ids { + receipts.push(ExactReceipt { + namespace: format!("turns/{}", self.indexed_binding()?), + key: turn.clone(), + bytes: b"settled".to_vec(), + }); + } + candidate + .settled_provider_turn_ids + .retain(|turn| Some(turn) == candidate.completed_provider_turn_id.as_ref()); + for child in std::mem::take(&mut candidate.descendant_thread_ids) { + receipts.push(ExactReceipt { + namespace: format!("descendants/{}", self.indexed_binding()?), + key: child, + bytes: b"settled".to_vec(), + }); + } + let bytes = serde_json::to_vec(&candidate) + .map_err(|error| DurableRunnerError::invalid(error.to_string()))?; + *generation = match store.commit("codex-provider", *generation, bytes, receipts) { + Ok(next) => next, + Err(error) => { + *generation = Revision::Fenced; + return Err(error); + } + }; + if let Err(error) = self.publish_indexed_locator() { + // The DB commit may be durable but activation did not finish. + // Poison this writer until a fresh executor reconciles it. + *generation = Revision::Fenced; + return Err(error); + } + *state = candidate; + return Ok(()); + } fs::create_dir_all(&self.state_dir).map_err(|error| { DurableRunnerError::invalid(format!( "failed to create provider state directory: {error}" @@ -2200,6 +2553,15 @@ impl CodexCommandExecutor { } fn prepare(&mut self, payload: &Value) -> Result { + if payload.get("durability").and_then(Value::as_str) == Some("durability.indexed_state.v1") + { + if self.state.is_some() && self.indexed_storage.borrow().is_none() { + return Err(DurableRunnerError::invalid( + "legacy provider state requires a fenced migration before indexed activation", + )); + } + self.open_indexed_storage(true)?; + } let mut config: CodexProviderConfig = serde_json::from_value( payload .get("provider") @@ -2294,7 +2656,7 @@ impl CodexCommandExecutor { } let process_generation = state .provider_process_generation - .checked_add(1) + .next() .ok_or_else(|| DurableRunnerError::invalid("Codex process generation exhausted"))?; let (settled_provider_turn_ids, settled_provider_turn_filter) = state.recovered_settled_provider_turn_ids()?; @@ -2324,7 +2686,7 @@ impl CodexCommandExecutor { .state .as_mut() .expect("prepared state remains available after provider start"); - state.provider_process_generation = process_generation; + state.install_process_generation(process_generation)?; state.lifecycle = "closed".to_owned(); let _ = state.push_terminal_event(NormalizedProviderEvent { event_type: "harness.diagnostic".to_owned(), @@ -2358,7 +2720,7 @@ impl CodexCommandExecutor { .state .as_mut() .expect("Codex state remains available after provider start"); - state.provider_process_generation = process_generation; + state.install_process_generation(process_generation)?; state.thread_id = Some(provider.thread_id().to_owned()); state.provider_session_id = provider.provider_session_id().map(str::to_owned); state.lifecycle = "session_open".to_owned(); @@ -2538,7 +2900,7 @@ impl CodexCommandExecutor { )) })? } else if next_state.lifecycle == "prepared" - && next_state.provider_process_generation > 0 + && next_state.provider_process_generation != 0 && next_state.pending_events.iter().all(is_startup_audit_event) { // turn.stop deliberately terminates the exact old process and @@ -2573,7 +2935,7 @@ impl CodexCommandExecutor { // with the rotated tool/completion authority. "prepared".to_owned() }; - self.persist_state(&next_state)?; + self.persist_state(&mut next_state)?; self.state = Some(next_state); Ok(()) } @@ -2722,7 +3084,7 @@ impl CodexCommandExecutor { .as_mut() .expect("Codex state remains available after rejected provider acceptance"); if let Some(provider_process_generation) = provider_process_generation { - state.provider_process_generation = provider_process_generation; + state.install_process_generation(provider_process_generation)?; } state.active_provider_turn_id = None; state.ambiguous_turn_start_pending = false; @@ -2797,7 +3159,7 @@ impl CodexCommandExecutor { let next_generation = self .state .as_ref() - .and_then(|state| state.provider_process_generation.checked_add(1)) + .and_then(|state| state.provider_process_generation.next()) .ok_or_else(|| DurableRunnerError::invalid("provider process generation exhausted"))?; self.begin_startup(ProviderStartupTrigger::Rollover, next_generation)?; let (restart_result, process_generation, rejected_accepted_turn) = { @@ -2806,12 +3168,14 @@ impl CodexCommandExecutor { "Codex provider identity epoch cannot rotate without an attached process", ) })?; - let restart_result = - provider.restart_idle_identity_epoch_observed(&mut |observation| { + let restart_result = provider.restart_idle_identity_epoch_observed( + next_generation, + &mut |observation| { self.observe_startup(observation).map_err(|error| { crate::local_runner::LocalRunnerError::invalid(error.to_string()) }) - }); + }, + ); let result = ( restart_result, provider.process_generation(), @@ -2840,7 +3204,7 @@ impl CodexCommandExecutor { .state .as_mut() .expect("Codex state remains available during identity epoch rollover"); - state.provider_process_generation = process_generation; + state.install_process_generation(process_generation)?; state.settled_provider_turn_ids.clear(); if let Some(completed_provider_turn_id) = state.completed_provider_turn_id.clone() { // The replacement process restored this still-authoritative @@ -3091,7 +3455,7 @@ impl CodexCommandExecutor { for result in cancelled { next_state.push_terminal_event(semantic_result_event(&identity, &result))?; } - self.persist_state(&next_state)?; + self.persist_state(&mut next_state)?; self.state = Some(next_state); } self.ensure_provider()?.interrupt_turn().map_err(|error| { @@ -3750,7 +4114,10 @@ impl CodexCommandExecutor { let was_completed = self .state .as_ref() - .is_some_and(|state| state.tool_bridge.has_completed_call(&result.call_id)); + .map(|state| state.tool_bridge.has_completed_call(&result.call_id)) + .transpose() + .map_err(|error| DurableRunnerError::invalid(error.to_string()))? + .unwrap_or(false); let mut next_state = self .state .clone() @@ -3786,7 +4153,7 @@ impl CodexCommandExecutor { admit_terminal_tool_authority(&mut next_state, &operation_id, &input, result.is_error)?; } next_state.push_event(semantic_result_event(&identity, &result))?; - self.persist_state(&next_state)?; + self.persist_state(&mut next_state)?; self.state = Some(next_state); let provider = self.ensure_provider()?; if terminal_tool_authoritative { @@ -3906,6 +4273,7 @@ impl CodexCommandExecutor { } fn poll_current_provider(&mut self) -> Result<(), DurableRunnerError> { + let preserve_output_bodies = self.indexed_storage.borrow().is_some(); // Receipt-limit interruption is autonomous recovery. It must advance // even while older durable events await acknowledgement, otherwise a // slow or disconnected controller can keep an exhausted provider turn @@ -3926,6 +4294,15 @@ impl CodexCommandExecutor { return self.settle_receipt_limit_interrupt_if_deadline_elapsed(); } for _ in 0..MAX_EVENTS_PER_POLL { + if preserve_output_bodies + && !receipt_limit_terminal_poll + && self + .state + .as_ref() + .is_some_and(|state| !state.pending_events.is_empty()) + { + break; + } let event = self .provider .as_mut() @@ -4126,7 +4503,10 @@ impl CodexCommandExecutor { })?; } } - let normalized = normalize_provider_notification(state, &method, ¶ms)?; + let mut normalized = normalize_provider_notification(state, &method, ¶ms)?; + if preserve_output_bodies { + crate::output_body::add_codex_body(&method, ¶ms, &mut normalized); + } let normalized_event_count = normalized.len(); if terminal_event_type.is_some() { state.settle_active_provider_turn_identity()?; @@ -4250,7 +4630,7 @@ impl CodexCommandExecutor { } } } - let trace_first_event_sequence = state.next_provider_event_seq; + let trace_first_event = state.pending_events.len() + state.queued_events.len(); if let Some(ref event_type) = terminal_event_type { let goal_status = state.goal.as_ref().map(|goal| goal.status.as_str()); let outcome = terminal_events(state, event_type, goal_status); @@ -4262,13 +4642,15 @@ impl CodexCommandExecutor { } else { state.extend_events(normalized)?; } - let trace_last_event_sequence = state.next_provider_event_seq; let trace_emitted_event_ids = identity .as_ref() .map(|identity| { - (trace_first_event_sequence..trace_last_event_sequence) - .map(provider_event_id) - .map(|event_id| identity.source_event_id(&event_id)) + state + .pending_events + .iter() + .chain(state.queued_events.iter()) + .skip(trace_first_event) + .map(|event| identity.source_event_id(&event.executor_event_id)) .collect::>() }) .unwrap_or_default(); @@ -4518,7 +4900,7 @@ impl CommandExecutor for CodexCommandExecutor { } next_state.pending_events.drain(..count); next_state.refill_pending_events(); - self.persist_state(&next_state)?; + self.persist_state(&mut next_state)?; self.state = Some(next_state); Ok(()) } @@ -4573,6 +4955,95 @@ impl CommandExecutor for CodexCommandExecutor { #[cfg(test)] mod tests { + #[test] + fn opaque_provider_generation_transition_repeats_and_survives_reopen() { + let previous = + ProcessGeneration::from(crate::process_generation::LEGACY_PROCESS_GENERATION_MAX); + let first = previous.next().unwrap(); + let first_launch = uuid::Uuid::new_v4().to_string(); + let mut state = opencode_result_state(); + state.active_provider_turn_id = None; + state.lifecycle = "prepared".to_owned(); + state.provider_process_generation = previous; + state.startup_attempt = Some(ProviderStartupAttempt { + schema: "paperclip.provider_startup.v1".to_owned(), + launch_id: first_launch.clone(), + phase: ProviderStartupPhase::Intent, + trigger: ProviderStartupTrigger::Ensure, + attempted_process_generation: first, + previous_process_generation: Some(previous), + origin: None, + command: None, + configuration_fingerprint: format!("sha256:{}", "a".repeat(64)), + requested_thread_id: state.thread_id.clone(), + authenticated_thread_id: None, + process_id: None, + process_group_id: None, + failed_stage: None, + direct_child_exit_observed: false, + exit_code: None, + signal: None, + process_tree_retired: false, + }); + + state.install_process_generation(first).unwrap(); + let installed_transition = state.process_generation_transition.clone().unwrap(); + state.install_process_generation(first).unwrap(); + assert_eq!( + state.process_generation_transition.as_ref(), + Some(&installed_transition) + ); + + let mut reopened: CodexProviderState = + serde_json::from_slice(&serde_json::to_vec(&state).unwrap()).unwrap(); + reopened.validate().unwrap(); + assert_eq!(reopened.provider_process_generation, first); + assert_eq!( + reopened.process_generation_transition.as_ref(), + Some(&installed_transition) + ); + + let second = first.next().unwrap(); + let second_launch = uuid::Uuid::new_v4().to_string(); + reopened.startup_attempt = Some(ProviderStartupAttempt { + schema: "paperclip.provider_startup.v1".to_owned(), + launch_id: second_launch.clone(), + phase: ProviderStartupPhase::Intent, + trigger: ProviderStartupTrigger::Ensure, + attempted_process_generation: second, + previous_process_generation: Some(first), + origin: None, + command: None, + configuration_fingerprint: format!("sha256:{}", "a".repeat(64)), + requested_thread_id: reopened.thread_id.clone(), + authenticated_thread_id: None, + process_id: None, + process_group_id: None, + failed_stage: None, + direct_child_exit_observed: false, + exit_code: None, + signal: None, + process_tree_retired: false, + }); + reopened.install_process_generation(second).unwrap(); + reopened.validate().unwrap(); + let transition = reopened.process_generation_transition.unwrap(); + assert_eq!(transition.from, first); + assert_eq!(transition.to, second); + assert_eq!(transition.launch_id, second_launch); + assert_ne!(transition.launch_id, first_launch); + } + + #[test] + fn unsafe_legacy_provider_generation_cannot_be_reopened_from_json() { + let mut state = opencode_result_state(); + state.active_provider_turn_id = None; + state.lifecycle = "prepared".to_owned(); + let mut value = serde_json::to_value(&state).unwrap(); + value["providerProcessGeneration"] = json!(9_007_199_254_740_992_u64); + assert!(serde_json::from_value::(value).is_err()); + } + #[test] fn startup_evidence_save_failure_cannot_become_an_empty_drain_or_admission() { let directory = std::env::temp_dir().join(format!( @@ -4588,7 +5059,7 @@ mod tests { state.lifecycle = "prepared".to_owned(); executor.state = Some(state); executor - .begin_startup(ProviderStartupTrigger::Ensure, 1) + .begin_startup(ProviderStartupTrigger::Ensure, 1.into()) .unwrap(); let path = executor.state_path(); fs::rename(&path, directory.join("preserved-intent.json")).unwrap(); @@ -4622,7 +5093,7 @@ mod tests { command_type: command_type.to_owned(), }); executor - .begin_startup(ProviderStartupTrigger::Ensure, 4) + .begin_startup(ProviderStartupTrigger::Ensure, 4.into()) .unwrap(); executor .observe_startup(ProviderStartupObservation::Spawned { @@ -4648,7 +5119,8 @@ mod tests { launch_id: uuid::Uuid::new_v4().to_string(), phase: ProviderStartupPhase::Intent, trigger: ProviderStartupTrigger::Ensure, - attempted_process_generation: 1, + attempted_process_generation: 1.into(), + previous_process_generation: None, origin: None, command: None, configuration_fingerprint: format!("sha256:{}", "a".repeat(64)), @@ -4714,7 +5186,7 @@ mod tests { state.lifecycle = "prepared".to_owned(); executor.state = Some(state); executor - .begin_startup(ProviderStartupTrigger::Ensure, 1) + .begin_startup(ProviderStartupTrigger::Ensure, 1.into()) .unwrap(); executor .observe_startup(ProviderStartupObservation::Spawned { @@ -4729,7 +5201,7 @@ mod tests { let state = executor.state.as_mut().unwrap(); state.thread_id = Some("authenticated-thread".to_owned()); state.provider_session_id = Some("authenticated-account".to_owned()); - state.provider_process_generation = 1; + state.provider_process_generation = 1.into(); state.lifecycle = "session_open".to_owned(); assert!(executor.commit_startup_admission().is_err()); assert!(executor.assert_startup_admitted().is_err()); @@ -4913,6 +5385,58 @@ mod tests { state } + #[test] + fn provider_event_identity_crosses_legacy_maximum_without_reordering_or_replacing_pending_events( + ) { + let mut state = opencode_result_state(); + state.next_provider_event_seq = u64::MAX; + let legacy = PolledEvent { + executor_event_id: provider_event_id(u64::MAX - 1), + event_type: "harness.diagnostic".to_owned(), + priority: EventPriority::P0, + payload: json!({"old": true}), + }; + state.pending_events.push_back(legacy.clone()); + for ordinal in 0..MAX_EVENTS_PER_POLL + 2 { + state + .push_event(NormalizedProviderEvent { + event_type: "harness.diagnostic".to_owned(), + priority: EventPriority::P0, + payload: json!({"ordinal": ordinal}), + }) + .unwrap(); + } + state.validate().unwrap(); + assert_eq!(state.next_provider_event_seq, u64::MAX); + let ids: Vec<_> = state + .pending_events + .iter() + .chain(state.queued_events.iter()) + .map(|event| event.executor_event_id.clone()) + .collect(); + assert_eq!(ids[0], legacy.executor_event_id); + assert!(ids[1..].iter().all(|id| opaque_provider_event_id(id))); + let mut recovered: CodexProviderState = + serde_json::from_slice(&serde_json::to_vec(&state).unwrap()).unwrap(); + recovered.validate().unwrap(); + assert_eq!(recovered, state); + recovered.pending_events.pop_front(); + recovered.refill_pending_events(); + assert_eq!(recovered.pending_events[0].executor_event_id, ids[1]); + assert_eq!(recovered.active_provider_turn_id, Some("turn-1".to_owned())); + recovered.validate().unwrap(); + let duplicate = recovered.pending_events[0].clone(); + recovered.queued_events.push_back(duplicate); + assert!(recovered.validate().is_err()); + for invalid in [ + "codex_provider_r:missing", + "codex_provider_r:00000000-0000-1000-8000-000000000001", + "codex_provider_r:00000000-0000-4000-0000-000000000001", + ] { + assert!(!opaque_provider_event_id(invalid)); + } + } + fn valid_opencode_result() -> Value { json!({ "schema": "paperclip.run_result.v1", @@ -4975,10 +5499,10 @@ mod tests { state.config.command = PathBuf::from("must-not-start-during-attachment"); state.config.model = None; state.active_provider_turn_id = None; - state.provider_process_generation = 1; + state.provider_process_generation = 1.into(); state.lifecycle = "prepared".to_owned(); let writer = CodexCommandExecutor::new(&directory); - writer.persist_state(&state).unwrap(); + writer.persist_state(&mut state).unwrap(); let mut executor = CodexCommandExecutor::new(&directory); executor.restore().unwrap(); assert!(executor.provider.is_none()); @@ -5001,7 +5525,7 @@ mod tests { settled.config.command = PathBuf::from("must-not-start-during-attachment"); settled.config.model = None; settled.active_provider_turn_id = None; - settled.provider_process_generation = 1; + settled.provider_process_generation = 1.into(); settled.lifecycle = "prepared".to_owned(); for change in [ "closed", @@ -5034,7 +5558,7 @@ mod tests { } } "thread_missing" => state.thread_id = None, - "generation_missing" => state.provider_process_generation = 0, + "generation_missing" => state.provider_process_generation = 0.into(), "profile_changed" => { let mut config = state.config.clone(); config.model = Some("different-model".to_owned()); @@ -5360,7 +5884,8 @@ mod tests { active_provider_turn_id: None, ambiguous_turn_start_pending: false, completed_turn_authoritative: false, - provider_process_generation: 0, + provider_process_generation: 0.into(), + process_generation_transition: None, completed_turn_process_generation: None, completed_provider_turn_id: None, settled_provider_turn_ids: std::collections::BTreeSet::new(), @@ -5410,8 +5935,8 @@ mod tests { state.thread_id = Some("thread-1".to_owned()); state.lifecycle = "session_open".to_owned(); state.completed_turn_authoritative = true; - state.provider_process_generation = 1; - state.completed_turn_process_generation = Some(1); + state.provider_process_generation = 1.into(); + state.completed_turn_process_generation = Some(1.into()); state.completed_provider_turn_id = Some("turn-1".to_owned()); state.last_agent_message = Some("old turn output".to_owned()); @@ -6045,7 +6570,7 @@ mod tests { bridge.settle_turn("provider_turn_terminated").unwrap(); assert!(bridge.retained_result_bytes_for_test() > 0); - let state = CodexProviderState::new( + let mut state = CodexProviderState::new( CodexProviderConfig { provider: "codex".to_owned(), driver: "codex_app_server".to_owned(), @@ -6067,7 +6592,7 @@ mod tests { bridge, ); let writer = CodexCommandExecutor::new(&directory); - writer.persist_state(&state).unwrap(); + writer.persist_state(&mut state).unwrap(); let mut recovered = CodexCommandExecutor::new(&directory); recovered.restore().unwrap(); @@ -6107,9 +6632,9 @@ mod tests { ProviderToolBridge::default(), ); state.completed_turn_authoritative = true; - state.completed_turn_process_generation = Some(1); + state.completed_turn_process_generation = Some(1.into()); state.completed_provider_turn_id = Some("provider-turn-legacy".to_owned()); - state.provider_process_generation = 1; + state.provider_process_generation = 1.into(); let (recovered, recovered_filter) = state.recovered_settled_provider_turn_ids().unwrap(); @@ -6120,6 +6645,89 @@ mod tests { )); } + #[test] + fn legacy_provider_staging_imports_receipts_in_pages_and_preserves_live_work() { + let root = + std::env::temp_dir().join(format!("legacy-provider-import-{}", uuid::Uuid::new_v4())); + fs::create_dir(&root).unwrap(); + let source = root.join("source"); + let destination = root.join("prepared"); + let mut executor = CodexCommandExecutor::new(&source); + executor.event_identity = Some(ProviderEventIdentity { + runner_instance_id: "runner-1".into(), + run_id: "run-1".into(), + normalized_session_id: "session-1".into(), + turn_id: "turn-1".into(), + item_id: "item-1".into(), + }); + let mut state = CodexProviderState::new( + CodexProviderConfig { + provider: "codex".into(), + driver: "codex_app_server".into(), + provider_version: "test".into(), + command: PathBuf::from("codex"), + args: vec!["app-server".into()], + cwd: root.to_string_lossy().into(), + model: None, + provider_session_id: None, + instructions: String::new(), + approval_policy: "never".into(), + externally_sandboxed: false, + include_skill_instructions: None, + }, + None, + ProviderToolBridge::default(), + ); + state.thread_id = Some("thread-1".into()); + state.lifecycle = "turn_active".into(); + state.active_provider_turn_id = Some("unfinished-turn".into()); + for n in 0..4_000 { + state.descendant_thread_ids.insert(format!("child-{n:05}")); + } + for n in 0..1_100 { + state + .settled_provider_turn_ids + .insert(format!("turn-{n:05}")); + } + executor.persist_state(&mut state).unwrap(); + let original = fs::read(executor.state_path()).unwrap(); + let proof = executor.stage_legacy(&destination, "migration-1").unwrap(); + assert_eq!(proof.receipts, 5_100); + assert_eq!( + executor.stage_legacy(&destination, "migration-1").unwrap(), + proof + ); + assert_eq!(fs::read(executor.state_path()).unwrap(), original); + let mut recovered = CodexCommandExecutor::new(&destination); + recovered.event_identity = executor.event_identity.clone(); + recovered.load_state_without_provider().unwrap(); + let current = recovered.state.as_ref().unwrap(); + assert_eq!( + current.active_provider_turn_id.as_deref(), + Some("unfinished-turn") + ); + assert!(current.descendant_thread_ids.is_empty()); + assert!(current.settled_provider_turn_ids.is_empty()); + assert!(recovered.provider.is_none()); + let store = recovered.open_indexed_storage(false).unwrap(); + assert_eq!( + store + .receipt("descendants/provider/runner-1/session-1", "child-00000") + .unwrap(), + Some(b"settled".to_vec()) + ); + assert_eq!( + store + .receipt("turns/provider/runner-1/session-1", "turn-00000") + .unwrap(), + Some(b"settled".to_vec()) + ); + drop(store); + drop(recovered); + drop(executor); + fs::remove_dir_all(root).unwrap(); + } + #[test] fn durable_provider_turn_ledger_never_evicts_within_an_epoch() { let mut state = CodexProviderState::new( diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/provider_bridge.rs b/packages/paperclip-runner/runner/crates/runner-core/src/provider_bridge.rs index 77307873814..4214b7c5b14 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/provider_bridge.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/provider_bridge.rs @@ -2,6 +2,7 @@ use std::collections::{BTreeMap, BTreeSet, VecDeque}; use std::error::Error; use std::fmt::{self, Display, Formatter}; +use crate::indexed_store::{ExactReceipt, IndexedStore}; use serde::de::{self, MapAccess, Visitor}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; @@ -86,6 +87,15 @@ struct CompletedToolCall { result: ToolResult, } +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +#[serde(tag = "kind", rename_all = "snake_case")] +enum IndexedToolReceipt { + Completed { receipt: CompletedToolCall }, + // Legacy checkpoints sometimes retained only an identity. Preserve that + // denial; absence of an old result must never authorize executing it again. + LegacyTombstone { call_id: String }, +} + #[derive(Clone, Debug, Default, PartialEq)] struct SettledCallIds { order: VecDeque, @@ -187,6 +197,10 @@ impl DurableReplayFilter { #[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct ProviderToolBridge { + #[serde(default, skip_serializing_if = "Option::is_none")] + indexed_receipt_namespace: Option, + #[serde(skip)] + receipt_store: Option, authorized: BTreeMap, #[serde(default)] catalog_operations: Vec, @@ -310,6 +324,98 @@ fn is_false(value: &bool) -> bool { } impl ProviderToolBridge { + pub(crate) fn require_exact_migration_source(&self) -> Result<(), ProviderBridgeError> { + if !self.settled_call_filter.is_empty() { + return Err(ProviderBridgeError::invalid( + "legacy provider has probabilistic receipts; migration requires reconciliation", + )); + } + Ok(()) + } + + pub(crate) fn bind_indexed_receipts( + &mut self, + store: IndexedStore, + namespace: String, + ) -> Result<(), ProviderBridgeError> { + if self + .indexed_receipt_namespace + .as_ref() + .is_some_and(|current| current != &namespace) + { + return Err(ProviderBridgeError::invalid( + "provider receipt namespace changed", + )); + } + self.indexed_receipt_namespace = Some(namespace); + self.receipt_store = Some(store); + Ok(()) + } + + fn indexed_receipt( + &self, + call_id: &str, + ) -> Result, ProviderBridgeError> { + let Some(namespace) = &self.indexed_receipt_namespace else { + return Ok(None); + }; + let store = self + .receipt_store + .as_ref() + .ok_or_else(|| ProviderBridgeError::invalid("provider receipt store is unavailable"))?; + store + .receipt(namespace, call_id) + .map_err(|error| ProviderBridgeError::invalid(error.to_string()))? + .map(|bytes| { + serde_json::from_slice(&bytes).map_err(|_| { + ProviderBridgeError::invalid("provider indexed receipt is malformed") + }) + }) + .transpose() + } + + /// Build a bounded checkpoint candidate. The caller must commit these + /// receipts and the candidate state in ONE store transaction before using + /// the candidate or releasing a provider result. This function does no I/O. + pub(crate) fn checkpoint_indexed_receipts( + &mut self, + ) -> Result, ProviderBridgeError> { + let Some(namespace) = &self.indexed_receipt_namespace else { + return Ok(vec![]); + }; + let mut receipts = Vec::new(); + for (key, receipt) in self.completed.iter().chain(self.settled_results.iter()) { + receipts.push(ExactReceipt { + namespace: namespace.clone(), + key: key.clone(), + bytes: serde_json::to_vec(&IndexedToolReceipt::Completed { + receipt: receipt.clone(), + }) + .map_err(|_| ProviderBridgeError::invalid("cannot encode provider receipt"))?, + }); + } + for key in self.settled_call_ids.iter() { + if !self.completed.contains_key(key) && !self.settled_results.contains_key(key) { + receipts.push(ExactReceipt { + namespace: namespace.clone(), + key: key.clone(), + bytes: serde_json::to_vec(&IndexedToolReceipt::LegacyTombstone { + call_id: key.clone(), + }) + .map_err(|_| { + ProviderBridgeError::invalid("cannot encode provider tombstone") + })?, + }); + } + } + self.completed.clear(); + self.settled_results.clear(); + self.settled_call_ids.clear(); + self.retained_result_bytes = 0; + self.durable_run_receipt_limit_reached = !self.settled_call_filter.is_empty(); + Ok(receipts) + } + pub fn prepare(&mut self, tool_set: AuthorizedToolSet) -> Result<(), ProviderBridgeError> { self.prepare_internal(tool_set, false) } @@ -620,15 +726,33 @@ impl ProviderToolBridge { } return Ok(Some(completed.result.clone())); } - Ok(None) + match self.indexed_receipt(call_id)? { + Some(IndexedToolReceipt::Completed { receipt }) => { + if receipt.call.call_id != call_id + || receipt.call.operation_id != operation_id + || &receipt.call.input != input + { + return Err(ProviderBridgeError::invalid( + "provider replayed an indexed tool call with different input", + )); + } + Ok(Some(receipt.result)) + } + Some(IndexedToolReceipt::LegacyTombstone { .. }) => Err(ProviderBridgeError::invalid( + "legacy settled tool result cannot be replayed", + )), + None => Ok(None), + } } - pub fn has_completed_call(&self, call_id: &str) -> bool { - self.completed.contains_key(call_id) || self.has_settled_call_id(call_id) + pub fn has_completed_call(&self, call_id: &str) -> Result { + Ok(self.completed.contains_key(call_id) + || self.has_settled_call_id(call_id) + || self.indexed_receipt(call_id)?.is_some()) } - pub(crate) fn has_call_receipt(&self, call_id: &str) -> bool { - self.pending.contains_key(call_id) || self.has_completed_call(call_id) + pub(crate) fn has_call_receipt(&self, call_id: &str) -> Result { + Ok(self.pending.contains_key(call_id) || self.has_completed_call(call_id)?) } pub fn begin_call( @@ -682,6 +806,7 @@ impl ProviderToolBridge { if self.completed.contains_key(&call_id) || self.settled_results.contains_key(&call_id) || self.has_settled_call_id(&call_id) + || self.indexed_receipt(&call_id)?.is_some() { return Err(ProviderBridgeError::invalid( "provider reused a completed tool call id", @@ -783,6 +908,16 @@ impl ProviderToolBridge { )) }; } + if let Some(receipt) = self.indexed_receipt(&result.call_id)? { + return match receipt { + IndexedToolReceipt::Completed { receipt } if receipt.result == result => { + Ok(result.result) + } + _ => Err(ProviderBridgeError::invalid( + "conflicting or incomplete indexed tool result", + )), + }; + } if self.has_settled_call_id(&result.call_id) { return Err(ProviderBridgeError::invalid( "legacy settled tool result cannot be replayed", @@ -1510,6 +1645,100 @@ mod tests { use super::*; use serde_json::json; + #[test] + #[ignore = "explicit 100,000-call storage qualification"] + fn indexed_receipts_allow_one_hundred_thousand_calls_without_a_turn_reset() { + let directory = + std::env::temp_dir().join(format!("paperclip-bridge-indexed-{}", uuid::Uuid::new_v4())); + let path = directory.join("state.sqlite"); + let store = IndexedStore::open(&path, "provider-session", true).unwrap(); + let operation = AuthorizedTool { + operation_id: "get_task_context".into(), + version: 1, + description: "Read task context".into(), + input_schema: json!({"type":"object"}), + response_schema: json!({"type":"object"}), + }; + let mut bridge = ProviderToolBridge::default(); + bridge + .prepare(AuthorizedToolSet { + schema: TOOL_SET_SCHEMA.into(), + schema_version: 1, + catalog_digest: authorized_tool_catalog_digest(std::slice::from_ref(&operation)) + .unwrap(), + operations: vec![operation], + }) + .unwrap(); + bridge + .bind_indexed_receipts(store.clone(), "semantic/session-1".into()) + .unwrap(); + for index in 0..100_000u64 { + let id = format!("call-{index}"); + bridge + .begin_call( + id.clone(), + "get_task_context".into(), + json!({"index":index}), + ) + .unwrap(); + bridge + .apply_result(ToolResult { + call_id: id, + operation_id: "get_task_context".into(), + result: json!({"index":index}), + is_error: false, + }) + .unwrap(); + let mut candidate = bridge.clone(); + let receipts = candidate.checkpoint_indexed_receipts().unwrap(); + let bytes = serde_json::to_vec(&candidate).unwrap(); + assert!(bytes.len() < 2_048); + store.commit("bridge", index, bytes, receipts).unwrap(); + bridge = candidate; + assert!(!bridge.durable_run_receipt_limit_reached()); + } + assert!( + bridge.pending.is_empty() + && bridge.completed.is_empty() + && bridge.settled_call_ids.is_empty() + ); + drop(bridge); + drop(store); + let store = IndexedStore::open(&path, "provider-session", false).unwrap(); + let snapshot = store.read_state("bridge").unwrap().unwrap(); + assert_eq!(snapshot.generation, 100_000); + let mut recovered: ProviderToolBridge = serde_json::from_slice(&snapshot.bytes).unwrap(); + // Missing storage is an error even when the in-memory cache is empty. + assert!(recovered + .replay_result("call-0", "get_task_context", &json!({"index":0})) + .is_err()); + recovered + .bind_indexed_receipts(store.clone(), "semantic/session-1".into()) + .unwrap(); + recovered.attach_existing_run().unwrap(); + assert_eq!( + recovered + .replay_result("call-0", "get_task_context", &json!({"index":0})) + .unwrap() + .unwrap() + .result, + json!({"index":0}) + ); + assert!(recovered + .replay_result("call-0", "get_task_context", &json!({"index":1})) + .is_err()); + assert!(recovered + .begin_call( + "call-0".into(), + "get_task_context".into(), + json!({"index":0}) + ) + .is_err()); + drop(recovered); + drop(store); + std::fs::remove_dir_all(directory).unwrap(); + } + fn completion_bridge() -> ProviderToolBridge { let operation = AuthorizedTool { operation_id: "paperclip_finish".to_owned(), @@ -1558,7 +1787,7 @@ mod tests { error.safe_provider_message(), Some(COMPLETION_INPUT_SCHEMA_HINT) ); - assert!(!over_limit.has_call_receipt("call-over-limit")); + assert!(!over_limit.has_call_receipt("call-over-limit").unwrap()); } #[test] @@ -1575,7 +1804,7 @@ mod tests { assert!(message.contains("/properties/summary/type"), "{message}"); assert!(!message.contains("PRIVATE_FIELD")); assert!(!message.contains("PRIVATE_VALUE")); - assert!(!bridge.has_call_receipt("bad-summary")); + assert!(!bridge.has_call_receipt("bad-summary").unwrap()); bridge .begin_call( "corrected-summary".to_owned(), @@ -1684,7 +1913,7 @@ mod tests { }) .unwrap(); - assert!(!bridge.has_call_receipt("shared-call")); + assert!(!bridge.has_call_receipt("shared-call").unwrap()); bridge .begin_call( "shared-call".to_owned(), @@ -1692,7 +1921,7 @@ mod tests { json!({}), ) .unwrap(); - assert!(bridge.has_call_receipt("shared-call")); + assert!(bridge.has_call_receipt("shared-call").unwrap()); } #[test] @@ -1826,7 +2055,7 @@ mod tests { let recovered: ProviderToolBridge = serde_json::from_str(&serde_json::to_string(&bridge).unwrap()).unwrap(); recovered.validate_recovered().unwrap(); - assert!(recovered.has_completed_call("call-0")); + assert!(recovered.has_completed_call("call-0").unwrap()); } #[test] diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/provider_events.rs b/packages/paperclip-runner/runner/crates/runner-core/src/provider_events.rs index 1e4fb43f607..48089c6adcd 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/provider_events.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/provider_events.rs @@ -1,3 +1,4 @@ +use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use sha2::{Digest, Sha256}; @@ -12,7 +13,7 @@ use crate::stable_identity::{ const MAX_TEXT_CHARS: usize = 4_000; -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct NormalizedProviderEvent { pub event_type: String, pub priority: EventPriority, @@ -741,15 +742,15 @@ pub fn normalize_codex_notification(method: &str, params: &Value) -> Vec push( + "item/agentMessage/delta" | "item/commandExecution/outputDelta" => push( &mut events, "item.delta", EventPriority::P2, json!({ "provider": "codex", "itemId": stable_id(string(params.get("itemId")), "codex-message"), - "kind": "agentMessage", - "channel": "progress", + "kind": if method == "item/commandExecution/outputDelta" { "commandExecution" } else { "agentMessage" }, + "channel": if method == "item/commandExecution/outputDelta" { "detail" } else { "progress" }, "providerMethod": method, "text": bounded_text(string(params.get("delta")), MAX_TEXT_CHARS), }), @@ -1470,6 +1471,31 @@ mod tests { assert!(!events[0].payload.to_string().contains("secret-value")); } + #[test] + fn preserves_command_output_deltas_as_tool_output() { + // A provider completion snapshot can retain only part of its output. + // Preserve every admitted delta independently; never relabel it as prose. + let events = normalize_codex_notification( + "item/commandExecution/outputDelta", + &json!({"threadId":"thread", "turnId":"turn", "itemId":"exec-1", + "delta":"tool output Authorization: Bearer secret-value"}), + ); + assert_eq!(events.len(), 1); + assert_eq!(events[0].event_type, "item.delta"); + assert_eq!(events[0].payload["kind"], "commandExecution"); + assert_eq!(events[0].payload["channel"], "detail"); + assert_eq!(events[0].payload["itemId"], "exec-1"); + assert_eq!( + events[0].payload["providerMethod"], + "item/commandExecution/outputDelta" + ); + assert!(events[0].payload["text"] + .as_str() + .unwrap() + .starts_with("tool output")); + assert!(!events[0].payload.to_string().contains("secret-value")); + } + #[test] fn bounds_and_redacts_command_output() { let events = normalize_codex_notification( diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/storage_capacity.rs b/packages/paperclip-runner/runner/crates/runner-core/src/storage_capacity.rs new file mode 100644 index 00000000000..28150eb020d --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/src/storage_capacity.rs @@ -0,0 +1,122 @@ +//! A bounded, synchronous write can wait for capacity without unwinding its +//! command/effect stack. The owning runner supplies a control-only callback; +//! storage-only callers without one still receive the original error promptly. +use crate::durable::DurableRunnerError; +use std::{cell::RefCell, rc::Rc}; + +type Result = std::result::Result; +type Callback = Box Result<()>>; + +/// Use only for an idempotent storage step or an exact token-bound commit. +/// Never wrap a provider/business operation in this retry helper. +pub(crate) fn retry(mut operation: impl FnMut() -> Result) -> Result { + loop { + check_cancelled()?; + match operation() { + Err(error) if wait(&error)? => continue, + result => return result, + } + } +} +struct Waiter { + callback: RefCell, + cancelled: RefCell>, +} +thread_local! { static CURRENT: RefCell>> = const { RefCell::new(None) }; } + +/// The callback owns its transport state, so no borrowed pointer or unsafe +/// lifetime extension crosses this scope. Drop restores the prior scope even +/// when an operation panics. A child storage thread never inherits this hook. +pub(crate) fn with_wait( + callback: impl FnMut() -> Result<()> + 'static, + operation: impl FnOnce() -> Result, +) -> Result { + struct Restore(Option>); + impl Drop for Restore { + fn drop(&mut self) { + CURRENT.with(|slot| { + slot.replace(self.0.take()); + }); + } + } + let waiter = Rc::new(Waiter { + callback: RefCell::new(Box::new(callback)), + cancelled: RefCell::new(None), + }); + let _restore = Restore(CURRENT.with(|slot| slot.replace(Some(waiter.clone())))); + let result = operation(); + let cancelled = waiter.cancelled.borrow().clone(); + match cancelled { + Some(error) => Err(error), + None => result, + } +} + +pub(crate) fn check_cancelled() -> Result<()> { + CURRENT.with(|slot| { + slot.borrow() + .as_ref() + .and_then(|waiter| waiter.cancelled.borrow().clone()) + .map_or(Ok(()), Err) + }) +} + +pub(crate) fn wait(error: &DurableRunnerError) -> Result { + check_cancelled()?; + if !error.is_storage_capacity() { + return Ok(false); + } + let waiter = CURRENT.with(|slot| slot.borrow().clone()); + let Some(waiter) = waiter else { + return Ok(false); + }; + let result = waiter.callback.borrow_mut()(); + match result { + Ok(()) => Ok(true), + Err(error) => { + let error = DurableRunnerError::storage_wait_cancelled(format!( + "storage_pressure: control wait stopped: {error}" + )); + *waiter.cancelled.borrow_mut() = Some(error.clone()); + Err(error) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn only_physical_capacity_retries_and_cancel_is_sticky() { + let full = DurableRunnerError::storage_capacity("full"); + assert!(!wait(&full).unwrap()); + with_wait( + || Ok(()), + || { + assert!(wait(&full)?); + assert!(!wait(&DurableRunnerError::invalid( + "storage_pressure: record bound" + ))?); + let result = with_wait( + || Err(DurableRunnerError::invalid("revoked")), + || { + assert!(wait(&full).unwrap_err().is_storage_wait_cancelled()); + assert!(check_cancelled().is_err()); + Ok(()) + }, + ); + assert!(result.is_err()); + assert!(wait(&full)?); + Ok(()) + }, + ) + .unwrap(); + assert!(!wait(&full).unwrap()); + } + #[test] + fn panic_does_not_leak_a_callback() { + let _ = + std::panic::catch_unwind(|| with_wait(|| Ok(()), || -> Result<()> { panic!("test") })); + assert!(!wait(&DurableRunnerError::storage_capacity("full")).unwrap()); + } +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/tests/acpx_indexed_receipts.rs b/packages/paperclip-runner/runner/crates/runner-core/tests/acpx_indexed_receipts.rs new file mode 100644 index 00000000000..756174f81be --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/tests/acpx_indexed_receipts.rs @@ -0,0 +1,297 @@ +use std::path::PathBuf; +use std::time::Duration; + +use paperclip_runner_core::acpx_provider_session::{ + AcpxPermissionMode, AcpxProviderSession, AcpxProviderSessionConfig, +}; +use paperclip_runner_core::acpx_provider_state::AcpxProviderStateEvent; +use paperclip_runner_core::acpx_sidecar_transport::AcpxSidecarTransportConfig; +use paperclip_runner_core::provider_bridge::{ + authorized_tool_catalog_digest, AuthorizedTool, AuthorizedToolSet, ToolResult, +}; +use serde_json::json; + +fn tool_set() -> AuthorizedToolSet { + let operations = vec![AuthorizedTool { + operation_id: "issues.read".to_owned(), + version: 1, + description: "Read an issue.".to_owned(), + input_schema: json!({"type":"object"}), + response_schema: json!({"type":"object"}), + }]; + AuthorizedToolSet { + schema: "paperclip.runner.authorized-tools.v1".to_owned(), + schema_version: 1, + catalog_digest: authorized_tool_catalog_digest(&operations).unwrap(), + operations, + } +} + +fn config(mode: &str) -> AcpxProviderSessionConfig { + AcpxProviderSessionConfig { + transport: AcpxSidecarTransportConfig { + command: PathBuf::from(env!("CARGO_BIN_EXE_fake-acpx-sidecar")), + args: vec!["--mode".to_owned(), mode.to_owned()], + verified_launch: None, + request_timeout: Duration::from_secs(1), + shutdown_grace: Duration::from_millis(100), + }, + agent: "codex".to_owned(), + model: "gpt-5.6-sol".to_owned(), + run_id: "run-1".to_owned(), + catalog_revision: 1, + runtime_directory: std::env::temp_dir(), + normalized_session_id: "session-1".to_owned(), + working_directory: std::env::temp_dir(), + permission_mode: AcpxPermissionMode::ApproveReads, + permission_mode_pinned: true, + system_instructions: "Complete the supplied task.".to_owned(), + runtime_context: serde_json::Value::Null, + tool_set: tool_set(), + expected_identity: None, + } +} + +use paperclip_runner_core::indexed_store::IndexedStore; +use serde_json::Value; +use std::fs; + +struct Directory(PathBuf); +impl Directory { + fn new() -> Self { + let path = + std::env::temp_dir().join(format!("paperclip-acpx-indexed-{}", uuid::Uuid::new_v4())); + fs::create_dir(&path).unwrap(); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + fs::set_permissions(&path, fs::Permissions::from_mode(0o700)).unwrap(); + } + Self(path) + } + fn path(&self) -> PathBuf { + self.0.join("acpx-provider-receipts.sqlite") + } +} +impl Drop for Directory { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.0); + } +} + +#[test] +fn retains_exact_receipts_beyond_the_active_turn_limit_without_growing_current_state() { + let directory = Directory::new(); + let mut config = config("indexed-many-tools"); + let expected_calls: usize = std::env::var("PAPERCLIP_ACPX_RECEIPT_QUALIFICATION") + .map(|value| value.parse().expect("invalid qualification call count")) + .unwrap_or(64); + assert!(expected_calls >= 64); + config + .transport + .args + .extend(["--calls".to_owned(), expected_calls.to_string()]); + let mut session = + AcpxProviderSession::start_with_indexed_receipts(&config, &directory.path()).unwrap(); + let pid = session.process_id(); + session + .start_turn( + "turn-1", + "Run many sequential calls", + &config.working_directory, + ) + .unwrap(); + let reader = IndexedStore::open(&directory.path(), "acpx/session-1", false).unwrap(); + let initial_current = reader + .read_state("acpx-session") + .unwrap() + .unwrap() + .bytes + .len(); + let mut calls = 0; + let mut peak = 0; + while session.state().active_turn_id().is_some() { + let events = session + .poll_event(Duration::from_secs(10)) + .unwrap() + .unwrap(); + if !events.is_empty() { + let batch = session.pending_event_batch().unwrap(); + assert_eq!(session.poll_event(Duration::ZERO).unwrap().unwrap(), events); + assert_eq!(session.pending_event_batch().as_ref(), Some(&batch)); + } + // This consumer's acceptance is deliberately explicit. The production + // executor does this only after saving its normalized outbox. + session.acknowledge_event_batch().unwrap(); + for event in events { + if let AcpxProviderStateEvent::ToolCall { + call_id, + operation_id, + input, + } = event + { + assert_eq!(input["index"], calls); + session + .deliver_tool_result(&ToolResult { + call_id, + operation_id, + result: json!({"index":calls}), + is_error: false, + }) + .unwrap(); + calls += 1; + } + } + peak = peak.max( + reader + .read_state("acpx-session") + .unwrap() + .unwrap() + .bytes + .len(), + ); + assert_eq!(session.process_id(), pid); + } + assert_eq!(calls, expected_calls); + println!("ACPX indexed receipts: calls={calls}, initial_current_bytes={initial_current}, peak_current_bytes={peak}"); + assert!( + peak <= initial_current + 4096, + "current checkpoint grew from {initial_current} to {peak} bytes" + ); + let ancient = reader + .receipt("acpx/run-1/turn-1/dynamic", "history-call-0") + .unwrap() + .unwrap(); + let ancient: Value = serde_json::from_slice(&ancient).unwrap(); + assert_eq!(ancient["receipt"]["result"]["result"], json!({"index":0})); + assert!(reader + .receipt("acpx-turns/run-1", "turn-1") + .unwrap() + .is_some()); + let mut recovered_config = config.clone(); + recovered_config.expected_identity = Some(session.identity().clone()); + session.shutdown("finished").unwrap(); + drop(session); + let mut recovered = + AcpxProviderSession::start_with_indexed_receipts(&recovered_config, &directory.path()) + .unwrap(); + assert!(recovered + .start_turn( + "turn-1", + "Must reject ancient turn", + &config.working_directory + ) + .unwrap_err() + .to_string() + .contains("settled turn identity")); + recovered.shutdown("finished").unwrap(); +} + +#[test] +fn rejects_unacknowledged_history_before_spawning_a_replacement() { + let directory = Directory::new(); + let mut config = config("indexed-many-tools"); + let mut session = + AcpxProviderSession::start_with_indexed_receipts(&config, &directory.path()).unwrap(); + session + .start_turn("turn-1", "One pending call", &config.working_directory) + .unwrap(); + let events = session + .poll_event(Duration::from_secs(10)) + .unwrap() + .unwrap(); + assert!(!events.is_empty()); + config.expected_identity = Some(session.identity().clone()); + session.shutdown("simulated interrupted consumer").unwrap(); + drop(session); + // A valid executable with an observable side effect proves that the + // retained-state guard runs before process creation. + let command = directory.0.join("must-not-spawn"); + fs::write(&command, "#!/bin/sh\n: > \"$0.spawned\"\nexit 72\n").unwrap(); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + fs::set_permissions(&command, fs::Permissions::from_mode(0o700)).unwrap(); + } + config.transport.command = command; + let error = match AcpxProviderSession::start_with_indexed_receipts(&config, &directory.path()) { + Ok(_) => panic!("unacknowledged batch granted a replacement"), + Err(error) => error, + }; + assert!(error.to_string().contains("requires reconciliation")); + assert!(!directory.0.join("must-not-spawn.spawned").exists()); + let reader = IndexedStore::open(&directory.path(), "acpx/session-1", false).unwrap(); + let current = reader.read_state("acpx-session").unwrap().unwrap(); + let value: Value = serde_json::from_slice(¤t.bytes).unwrap(); + assert!(!value["events"].as_array().unwrap().is_empty()); +} + +#[test] +fn an_unconfirmed_resolution_retains_its_exact_receipt_and_delivery_intent() { + let directory = Directory::new(); + let config = config("resolutions-wrong-ack"); + let mut session = + AcpxProviderSession::start_with_indexed_receipts(&config, &directory.path()).unwrap(); + session + .start_turn("turn-1", "Resolve one call", &config.working_directory) + .unwrap(); + session + .poll_event(Duration::from_secs(10)) + .unwrap() + .unwrap(); + session.acknowledge_event_batch().unwrap(); + let result = ToolResult { + call_id: "call-1".into(), + operation_id: "issues.read".into(), + result: json!({"id":"issue-1"}), + is_error: false, + }; + assert!(session.deliver_tool_result(&result).is_err()); + let reader = IndexedStore::open(&directory.path(), "acpx/session-1", false).unwrap(); + assert!(reader + .receipt("acpx/run-1/turn-1/dynamic", "call-1") + .unwrap() + .is_some()); + let value: Value = + serde_json::from_slice(&reader.read_state("acpx-session").unwrap().unwrap().bytes).unwrap(); + assert_eq!(value["delivery"], serde_json::to_value(&result).unwrap()); + session.shutdown("finished").unwrap(); +} + +#[test] +fn a_failed_receipt_commit_never_releases_the_tool_result_to_the_provider() { + let directory = Directory::new(); + let mut config = config("indexed-many-tools"); + let marker = directory.0.join("resolution-received"); + config.transport.args.extend([ + "--resolution-marker".to_owned(), + marker.to_string_lossy().into_owned(), + ]); + let mut session = + AcpxProviderSession::start_with_indexed_receipts(&config, &directory.path()).unwrap(); + session + .start_turn("turn-1", "One pending call", &config.working_directory) + .unwrap(); + session + .poll_event(Duration::from_secs(10)) + .unwrap() + .unwrap(); + session.acknowledge_event_batch().unwrap(); + let database = rusqlite::Connection::open(directory.path()).unwrap(); + database.execute_batch("CREATE TRIGGER reject_checkpoint BEFORE UPDATE ON current_state BEGIN SELECT RAISE(ABORT, 'fixture checkpoint failure'); END;").unwrap(); + let error = session + .deliver_tool_result(&ToolResult { + call_id: "history-call-0".into(), + operation_id: "issues.read".into(), + result: json!({"index":0}), + is_error: false, + }) + .unwrap_err(); + assert!(error.to_string().contains("fixture checkpoint failure")); + assert!( + !marker.exists(), + "tool result escaped before its durable receipt" + ); + assert!(session.state().pending_tool("history-call-0").is_some()); + session.shutdown("finished").unwrap(); +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/tests/acpx_warm_attachment.rs b/packages/paperclip-runner/runner/crates/runner-core/tests/acpx_warm_attachment.rs index 252953d7d6a..90f9aab0e7c 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/tests/acpx_warm_attachment.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/tests/acpx_warm_attachment.rs @@ -24,6 +24,7 @@ impl Drop for Fixture { fn command(sequence: u64, kind: &str, payload: Value) -> Command { Command { + controller_epoch: None, schema: "paperclip.prp.command.v1".to_owned(), command_id: format!("command-{sequence}"), controller_seq: sequence, diff --git a/packages/paperclip-runner/runner/crates/runner-core/tests/codex_provider.rs b/packages/paperclip-runner/runner/crates/runner-core/tests/codex_provider.rs index ff4edc2e33b..c55a123e094 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/tests/codex_provider.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/tests/codex_provider.rs @@ -166,6 +166,7 @@ fn durable_config(directory: &Path) -> DurableRunnerConfig { fn command(id: &str, sequence: u64, command_type: &str, payload: Value) -> Command { Command { + controller_epoch: None, schema: "paperclip.prp.command.v1".to_owned(), command_id: id.to_owned(), controller_seq: sequence, @@ -3282,8 +3283,10 @@ fn durable_backend_replays_pending_tool_calls_without_mutating_the_event_queue() let resume_count = call_count(&directory, "thread/resume"); let mut next = CodexCommandExecutor::with_runner_config(&directory, &runner_config); let mut session_resumed_events = 0; + let mut replay_events = Vec::new(); for _ in 0..32 { let events = poll_and_ack(&mut next).expect("poll exact pending replay"); + replay_events.extend(events.iter().cloned()); session_resumed_events += events .iter() .filter(|event| event.event_type == "session.resumed") @@ -3303,6 +3306,7 @@ fn durable_backend_replays_pending_tool_calls_without_mutating_the_event_queue() ); for _ in 0..4 { let events = poll_and_ack(&mut next).expect("poll exact pending replay"); + replay_events.extend(events.iter().cloned()); session_resumed_events += events .iter() .filter(|event| event.event_type == "session.resumed") @@ -3331,8 +3335,28 @@ fn durable_backend_replays_pending_tool_calls_without_mutating_the_event_queue() ); assert_eq!( after["nextProviderEventSeq"].as_u64(), - before["nextProviderEventSeq"].as_u64().map(|sequence| sequence + 3), - "exact restore adds one session.resumed and two durable startup facts, never duplicate tool inputs, during replay {replay}" + before["nextProviderEventSeq"].as_u64(), + "new identities do not consume the retained numeric namespace" + ); + assert_eq!(replay_events.len(), 3, "exact restore adds one session.resumed and two durable startup facts during replay {replay}"); + assert_eq!( + replay_events + .iter() + .filter(|event| { + event.event_type == "harness.diagnostic" + && event.payload["code"] == "provider_startup_ownership" + }) + .count(), + 2 + ); + let ids: std::collections::BTreeSet<_> = replay_events + .iter() + .map(|event| event.executor_event_id.as_str()) + .collect(); + assert_eq!( + ids.len(), + 3, + "each restored fact has a distinct exact identity" ); recovered = Some(next); if replay < 3 { @@ -4915,21 +4939,58 @@ fn receipt_limit_rejects_the_call_and_keeps_polling_when_interrupt_fails() { saturate_provider_tool_receipts(&directory); let mut recovered = CodexCommandExecutor::with_runner_config(&directory, &runner_config); - let resumed = wait_for_executor_event(&mut recovered, "session.resumed"); - assert_eq!(resumed.payload["provider"], "codex"); - let diagnostic = wait_for_executor_event(&mut recovered, "harness.diagnostic"); - assert_eq!( - diagnostic.payload["code"], - "semantic_tool_turn_receipt_limit" - ); + // Debug builds can spend the entire two-second courtesy window persisting + // the deliberately saturated legacy JSON ledger. Both outcomes are valid: + // an authoritative interrupt after retries, or conservative failure after + // the exact provider is reaped. Collect the entire acknowledged batch so a + // terminal delivered alongside its diagnostic is not discarded by a helper. + let deadline = std::time::Instant::now() + std::time::Duration::from_secs(45); + let mut resumed = false; + let mut diagnostic = false; + let mut terminal = None; + while std::time::Instant::now() < deadline && terminal.is_none() { + for event in poll_and_ack(&mut recovered).expect("poll receipt-limit settlement") { + resumed |= event.event_type == "session.resumed"; + diagnostic |= event.event_type == "harness.diagnostic" + && event.payload["code"] == "semantic_tool_turn_receipt_limit"; + assert_ne!( + event.event_type, "semantic_tool.input", + "overflowing call must never execute" + ); + if matches!( + event.event_type.as_str(), + "turn.interrupted" | "turn.failed" + ) { + terminal = Some(event); + } + } + if terminal.is_none() { + std::thread::sleep(std::time::Duration::from_millis(1)); + } + } + assert!(resumed && diagnostic); assert_eq!(call_count(&directory, "tool-response:failure"), 1); - assert_eq!(call_count(&directory, "turn/interrupt"), 1); - let interrupted = wait_for_executor_event(&mut recovered, "turn.interrupted"); - assert_eq!( - interrupted.payload["provider"], "codex", - "the retry must settle the receipt-exhausted turn" - ); - assert_eq!(call_count(&directory, "turn/interrupt"), 3); + let terminal = terminal.expect("receipt-limit interruption must settle before the deadline"); + assert_eq!(terminal.payload["provider"], "codex"); + let attempts = call_count(&directory, "turn/interrupt"); + assert!((1..=3).contains(&attempts)); + if terminal.event_type == "turn.failed" { + assert_eq!( + terminal.payload["code"], + "semantic_tool_turn_receipt_limit_interrupt_unconfirmed" + ); + assert_eq!(terminal.payload["interruptAccepted"], false); + assert_eq!(terminal.payload["providerTerminalObserved"], false); + assert_eq!(terminal.payload["providerShutdownFailed"], false); + assert_eq!(attempts, 1, "only the configured first RPC is rejected"); + } else if attempts < 3 { + assert_eq!( + terminal.payload["code"], + "semantic_tool_turn_receipt_limit_interrupt_deadline" + ); + assert_eq!(terminal.payload["interruptAccepted"], true); + assert_eq!(terminal.payload["providerShutdownFailed"], false); + } recovered.shutdown().expect("stop recovered provider"); fs::remove_dir_all(directory).expect("remove Codex integration-test directory"); @@ -6217,6 +6278,38 @@ fn lightweight_history_repeated_cursor_is_not_idle_evidence() { fs::remove_dir_all(directory).unwrap(); } +#[test] +fn lightweight_history_settlement_race_does_not_scan_completed_history() { + for settled in [false, true] { + let directory = temporary_directory("history-settlement-race"); + let mut args = vec![ + "--hold-turn", + "--require-lightweight-history", + "--unbounded-completed-history", + ]; + if settled { + args.push("--settle-during-history"); + } + let config = provider_config(&directory, &args); + let mut provider = CodexProvider::start(&config, None).unwrap(); + provider.start_turn("Keep working", &config.cwd).unwrap(); + let result = provider.read_thread(); + if settled { + assert_eq!(result.unwrap()["thread"]["turns"], json!([])); + } else { + assert!(result + .unwrap_err() + .to_string() + .contains("active turn absent from current history window")); + } + let calls = fs::read_to_string(directory.join("calls.log")).unwrap(); + assert_eq!(calls.matches("thread/turns/list").count(), 4); + assert_eq!(calls.matches("thread/read").count(), 2); + provider.shutdown().unwrap(); + fs::remove_dir_all(directory).unwrap(); + } +} + fn skill_wire_requests(directory: &Path) -> Vec { fs::read_to_string(directory.join("requests.ndjson")) .unwrap() @@ -6225,6 +6318,164 @@ fn skill_wire_requests(directory: &Path) -> Vec { .collect() } +fn indexed_provider_history_directory(label: &str) -> PathBuf { + let directory = temporary_directory(label); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + fs::set_permissions(&directory, fs::Permissions::from_mode(0o700)).unwrap(); + } + directory +} + +#[test] +fn indexed_provider_history_selects_paginated_start_and_verifies_cold_resume() { + let directory = indexed_provider_history_directory("indexed-provider-history"); + let log = directory.join("requests.ndjson"); + let config = provider_config( + &directory, + &[ + "--require-paginated-history", + "--request-log", + log.to_str().unwrap(), + ], + ); + let runner_config = durable_config(&directory); + let mut executor = CodexCommandExecutor::with_runner_config(&directory, &runner_config); + executor + .execute(&command( + "prepare", + 1, + "run.prepare", + json!({ + "provider": config, "durability": "durability.indexed_state.v1" + }), + )) + .unwrap(); + executor + .execute(&command("open", 2, "session.open", json!({}))) + .unwrap(); + executor.shutdown().unwrap(); + drop(executor); + let mut restored = CodexCommandExecutor::with_runner_config(&directory, &runner_config); + restored + .execute(&command( + "turn", + 3, + "turn.start", + json!({"text": "Continue"}), + )) + .unwrap(); + restored.shutdown().unwrap(); + drop(restored); + let frames = skill_wire_requests(&directory); + let start = frames + .iter() + .find(|frame| frame["method"] == "thread/start") + .unwrap(); + let resume = frames + .iter() + .find(|frame| frame["method"] == "thread/resume") + .unwrap(); + assert_eq!(start["params"]["historyMode"], "paginated"); + assert_eq!(resume["params"]["threadId"], "codex-thread-1"); + assert_eq!(resume["params"]["excludeTurns"], true); + // Resume verifies the persisted contract; it cannot convert it by request. + assert!(resume["params"].get("historyMode").is_none()); + fs::remove_dir_all(directory).unwrap(); +} + +#[test] +fn indexed_provider_history_rejects_unsupported_mode_before_a_turn() { + for switch in ["--legacy-history-mode", "--missing-history-mode"] { + let directory = indexed_provider_history_directory("unsupported-indexed-provider-history"); + let config = provider_config(&directory, &[switch]); + let mut executor = + CodexCommandExecutor::with_runner_config(&directory, &durable_config(&directory)); + executor + .execute(&command( + "prepare", + 1, + "run.prepare", + json!({ + "provider": config, "durability": "durability.indexed_state.v1" + }), + )) + .unwrap(); + assert!(executor + .execute(&command("open", 2, "session.open", json!({}))) + .unwrap_err() + .to_string() + .contains("codex_history_mode_unsupported")); + assert_eq!(call_count(&directory, "turn/start"), 0); + drop(executor); + fs::remove_dir_all(directory).unwrap(); + } +} + +#[test] +fn indexed_provider_history_rejects_a_legacy_cold_resume_without_starting_fresh() { + let directory = indexed_provider_history_directory("unsupported-resumed-provider-history"); + let config = provider_config(&directory, &[]); + let runner_config = durable_config(&directory); + let mut executor = CodexCommandExecutor::with_runner_config(&directory, &runner_config); + executor + .execute(&command( + "prepare", + 1, + "run.prepare", + json!({ + "provider": config, "durability": "durability.indexed_state.v1" + }), + )) + .unwrap(); + executor + .execute(&command("open", 2, "session.open", json!({}))) + .unwrap(); + executor.shutdown().unwrap(); + drop(executor); + fs::write(directory.join("legacy-history-mode"), "legacy").unwrap(); + let mut restored = CodexCommandExecutor::with_runner_config(&directory, &runner_config); + assert!(restored + .execute(&command( + "turn", + 3, + "turn.start", + json!({"text": "Continue"}) + )) + .unwrap_err() + .to_string() + .contains("codex_history_mode_unsupported")); + assert_eq!(call_count(&directory, "thread/start"), 1); + assert_eq!(call_count(&directory, "thread/resume"), 1); + assert_eq!(call_count(&directory, "turn/start"), 0); + drop(restored); + fs::remove_dir_all(directory).unwrap(); +} + +#[test] +fn legacy_provider_history_preserves_the_provider_default() { + let directory = temporary_directory("legacy-provider-history-default"); + let log = directory.join("requests.ndjson"); + let config = provider_config( + &directory, + &[ + "--legacy-history-mode", + "--request-log", + log.to_str().unwrap(), + ], + ); + let mut provider = CodexProvider::start(&config, None).unwrap(); + provider.shutdown().unwrap(); + let frames = skill_wire_requests(&directory); + let start = frames + .iter() + .find(|frame| frame["method"] == "thread/start") + .unwrap(); + assert!(start["params"].get("historyMode").is_none()); + fs::remove_dir_all(directory).unwrap(); +} + #[test] fn skill_instructions_flag_reaches_start_and_resume_and_preserves_absent_config() { for flag in [Some(true), Some(false), None] { diff --git a/packages/paperclip-runner/runner/crates/runner-core/tests/durable_recovery.rs b/packages/paperclip-runner/runner/crates/runner-core/tests/durable_recovery.rs index dbb7ee18f80..686f1dd9003 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/tests/durable_recovery.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/tests/durable_recovery.rs @@ -36,6 +36,7 @@ fn config(state_dir: PathBuf) -> DurableRunnerConfig { fn command() -> Command { Command { + controller_epoch: None, schema: "paperclip.prp.command.v1".to_owned(), command_id: "command_1".to_owned(), controller_seq: 1, diff --git a/packages/paperclip-runner/runner/crates/runner-core/tests/indexed_history_growth.rs b/packages/paperclip-runner/runner/crates/runner-core/tests/indexed_history_growth.rs new file mode 100644 index 00000000000..c1c21ef6093 --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/tests/indexed_history_growth.rs @@ -0,0 +1,166 @@ +use paperclip_runner_core::indexed_store::{ExactReceipt, IndexedStore}; +use std::{fs, path::Path, time::Instant}; + +fn tree_metrics(path: &Path, depth: usize) -> (u64, u64, u64) { + assert!(depth <= 2, "partition directory fanout exceeded the bound"); + let root_metadata = fs::symlink_metadata(path).unwrap(); + assert!(root_metadata.is_dir() && !root_metadata.file_type().is_symlink()); + let mut physical = 0u64; + let mut allocated = 0u64; + let mut largest_file = 0u64; + // Iterate entries as a stream: routing history is a bounded-depth hash + // fanout, so qualification does not materialize the file list in memory. + for entry in fs::read_dir(path).unwrap() { + let entry = entry.unwrap(); + let kind = entry.file_type().unwrap(); + assert!(!kind.is_symlink(), "partition tree contains a symlink"); + if kind.is_dir() { + let (child_physical, child_allocated, child_largest) = + tree_metrics(&entry.path(), depth + 1); + physical += child_physical; + allocated += child_allocated; + largest_file = largest_file.max(child_largest); + continue; + } + assert!(kind.is_file(), "partition entry is not a regular file"); + let metadata = fs::symlink_metadata(entry.path()).unwrap(); + assert!(metadata.is_file() && !metadata.file_type().is_symlink()); + physical += metadata.len(); + largest_file = largest_file.max(metadata.len()); + #[cfg(unix)] + { + use std::os::unix::fs::MetadataExt; + allocated += metadata.blocks() * 512; + } + #[cfg(not(unix))] + { + allocated += metadata.len(); + } + } + (physical, allocated, largest_file) +} + +#[test] +#[ignore = "explicit 10 GiB storage qualification; requires local disk space"] +fn ten_gib_history_keeps_resume_and_ancient_replay_indexed() { + let root = + std::env::temp_dir().join(format!("paperclip-history-growth-{}", uuid::Uuid::new_v4())); + let path = root.join("state.sqlite"); + let mut store = IndexedStore::open(&path, "growth-qualification", true).unwrap(); + let mut payload = vec![0u8; 64 * 1024]; + let mut random = 0x12345678u32; + for byte in &mut payload { + random ^= random << 13; + random ^= random >> 17; + random ^= random << 5; + *byte = random as u8; + } + let current = b"bounded-current-state"; + let mut generation = paperclip_runner_core::indexed_revision::Revision::Absent; + let mut written = 0u64; + let started = Instant::now(); + let gib = std::env::var("PAPERCLIP_HISTORY_QUALIFICATION_GIB") + .unwrap_or_else(|_| "10".into()) + .parse::() + .unwrap(); + assert!( + [10, 100].contains(&gib), + "qualification size must be 10 or 100 GiB" + ); + let mut targets = vec![ + 16 * 1024 * 1024u64, + 1024 * 1024 * 1024, + 10 * 1024 * 1024 * 1024, + ]; + if gib == 100 { + targets.push(100 * 1024 * 1024 * 1024); + } + for target in targets { + let mut commits = Vec::new(); + while written < target { + let first = written / payload.len() as u64; + let receipts = (first..first + 256) + .map(|id| ExactReceipt { + namespace: "history".into(), + key: format!("event-{id:020}"), + bytes: payload.clone(), + }) + .collect(); + let now = Instant::now(); + generation = store + .commit("authority", generation, current.to_vec(), receipts) + .unwrap(); + commits.push(now.elapsed().as_secs_f64() * 1000.0); + written += 256 * payload.len() as u64; + } + drop(store); + let now = Instant::now(); + store = IndexedStore::open(&path, "growth-qualification", false).unwrap(); + let restored = store.read_state("authority").unwrap().unwrap(); + let reopen_ms = now.elapsed().as_secs_f64() * 1000.0; + assert_eq!(restored.bytes, current); + assert_eq!(restored.generation, generation); + assert_eq!( + store + .receipt("history", "event-00000000000000000000") + .unwrap() + .unwrap(), + payload + ); + let mut reads = Vec::new(); + for _ in 0..100 { + let now = Instant::now(); + assert_eq!( + store.read_state("authority").unwrap().unwrap().bytes, + current + ); + reads.push(now.elapsed().as_secs_f64() * 1000.0); + } + reads.sort_by(f64::total_cmp); + commits.sort_by(f64::total_cmp); + let mut partitions = 0usize; + let mut largest_logical_partition = 0u64; + let mut after: Option = None; + loop { + let page = store.partitions(after.as_deref(), 128).unwrap(); + if page.is_empty() { + break; + } + partitions += page.len(); + largest_logical_partition = largest_logical_partition.max( + page.iter() + .map(|partition| partition.bytes) + .max() + .unwrap_or(0), + ); + after = page.last().map(|partition| partition.lower_key.clone()); + if page.len() < 128 { + break; + } + } + let root_database_bytes = fs::metadata(&path).unwrap().len(); + let (partition_files_bytes, allocated_partition_bytes, largest_file) = + tree_metrics(&root.join("state.sqlite.receipts"), 0); + let (routing_page_files_bytes, allocated_routing_page_bytes, largest_routing_page_file) = + tree_metrics(&root.join("state.sqlite.routing"), 0); + if target >= 1024 * 1024 * 1024 { + assert!(partitions > 1); + assert!( + largest_logical_partition < 1024 * 1024 * 1024, + "partition grew beyond the maintenance envelope" + ); + } + println!( + "history_bytes={written} root_database_bytes={root_database_bytes} receipt_partition_files_bytes={partition_files_bytes} allocated_receipt_partition_bytes={allocated_partition_bytes} receipt_partitions={partitions} largest_receipt_partition_file_bytes={largest_file} routing_page_files_bytes={routing_page_files_bytes} allocated_routing_page_bytes={allocated_routing_page_bytes} largest_routing_page_file_bytes={largest_routing_page_file} largest_logical_partition_bytes={largest_logical_partition} current_bytes={} reopen_ms={reopen_ms:.3} read_p95_ms={:.3} batch_commit_p95_ms={:.3}", + current.len(), + reads[94], + commits[(commits.len() - 1) * 95 / 100] + ); + } + drop(store); + fs::remove_dir_all(root).unwrap(); + println!( + "qualification_elapsed_seconds={:.3}", + started.elapsed().as_secs_f64() + ); +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/tests/indexed_session_snapshot.rs b/packages/paperclip-runner/runner/crates/runner-core/tests/indexed_session_snapshot.rs new file mode 100644 index 00000000000..23dcbe2788a --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/tests/indexed_session_snapshot.rs @@ -0,0 +1,163 @@ +use paperclip_runner_core::indexed_store::{ExactReceipt, IndexedStore}; +use rusqlite::Connection; +use serde_json::json; +use sha2::{Digest, Sha256}; +use std::{ + fs, + path::PathBuf, + process::{Command, Stdio}, + time::{Duration, Instant}, +}; + +#[test] +fn sigkill_during_native_snapshot_resumes_exact_receipts_and_restores_without_source() { + let root = + std::env::temp_dir().join(format!("paperclip-snapshot-crash-{}", uuid::Uuid::new_v4())); + fs::create_dir(&root).unwrap(); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + fs::set_permissions(&root, fs::Permissions::from_mode(0o700)).unwrap(); + } + let source = root.join("source"); + let destination = root.join("snapshot"); + let names = ["runner-state", "codex-provider-state"]; + let bindings = ["runner/r/s", "provider/r/s"]; + let keys = ["runner", "codex-provider"]; + let mut hashes = vec![]; + for i in 0..2 { + let store = IndexedStore::open( + &source.join(format!("{}.sqlite", names[i])), + bindings[i], + true, + ) + .unwrap(); + let locator = json!({"schema":if i==0 {"paperclip.runner.durable.state.indexed.v1"} else {"paperclip.runner.codex-provider-state.indexed.v1"},"binding":bindings[i]}); + let file = source.join(format!("{}.json", names[i])); + fs::write(&file, serde_json::to_vec(&locator).unwrap()).unwrap(); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + fs::set_permissions(file, fs::Permissions::from_mode(0o600)).unwrap(); + } + let state = if i == 0 { + json!({"lifecycle":"suspended","runnerInstanceId":"r","normalizedSessionId":"s","nextSourceSeq":2,"ackedSourceSeq":1}) + } else { + json!({"lifecycle":"ready","pendingEvents":[],"queuedEvents":[]}) + }; + let bytes = serde_json::to_vec(&state).unwrap(); + hashes.push(format!("{:x}", Sha256::digest(&bytes))); + store + .commit( + keys[i], + 0, + bytes, + (0..2000) + .map(|n| ExactReceipt { + namespace: "ancient".into(), + key: format!("{n:08}"), + bytes: vec![(n % 251) as u8; 16 * 1024], + }) + .collect(), + ) + .unwrap(); + } + let args = vec![ + "storage".to_owned(), + "snapshot-session".into(), + "--directory".into(), + source.to_string_lossy().into_owned(), + "--destination".into(), + destination.to_string_lossy().into_owned(), + "--runner-generation".into(), + "1".into(), + "--runner-sha256".into(), + hashes[0].clone(), + "--provider-generation".into(), + "1".into(), + "--provider-sha256".into(), + hashes[1].clone(), + ]; + let mut child = Command::new(env!("CARGO_BIN_EXE_paperclip-runnerd")) + .args(&args) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .unwrap(); + let deadline = Instant::now() + Duration::from_secs(30); + let mut copied = false; + while Instant::now() < deadline { + let db = Connection::open_with_flags( + source.join("runner-state.sqlite"), + rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY, + ) + .unwrap(); + let staging = db.query_row( + "SELECT staging FROM receipt_backup WHERE phase='copying'", + [], + |row| row.get::<_, String>(0), + ); + if let Ok(staging) = staging { + if let Ok(target) = Connection::open_with_flags( + PathBuf::from(staging).join("runner-state.sqlite"), + rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY, + ) { + copied = target + .query_row( + "SELECT EXISTS(SELECT 1 FROM backup_copy WHERE cursor>0)", + [], + |row| row.get::<_, bool>(0), + ) + .unwrap_or(false); + } + } + if copied { + break; + } + if let Some(status) = child.try_wait().unwrap() { + panic!("snapshot exited before crash boundary: {status}"); + } + std::thread::sleep(Duration::from_millis(1)); + } + // Kill only the exact child owned by this test, and reap it before reopening. + child.kill().unwrap(); + let status = child.wait().unwrap(); + assert!(!status.success()); + assert!(copied, "no durable partial copy observed"); + assert!( + !destination.exists(), + "partial snapshot must not be published" + ); + let resumed = Command::new(env!("CARGO_BIN_EXE_paperclip-runnerd")) + .args(&args) + .output() + .unwrap(); + assert!( + resumed.status.success(), + "{}", + String::from_utf8_lossy(&resumed.stderr) + ); + fs::remove_dir_all(&source).unwrap(); + for i in 0..2 { + let restored = IndexedStore::open( + &destination.join(format!("{}.sqlite", names[i])), + bindings[i], + false, + ) + .unwrap(); + assert_eq!( + format!( + "{:x}", + Sha256::digest(restored.read_state(keys[i]).unwrap().unwrap().bytes) + ), + hashes[i] + ); + for n in [0, 127, 1024, 1999] { + assert_eq!( + restored.receipt("ancient", &format!("{n:08}")).unwrap(), + Some(vec![(n % 251) as u8; 16 * 1024]) + ); + } + } + fs::remove_dir_all(root).unwrap(); +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/tests/indexed_sqlite_locks.rs b/packages/paperclip-runner/runner/crates/runner-core/tests/indexed_sqlite_locks.rs new file mode 100644 index 00000000000..41ad5238105 --- /dev/null +++ b/packages/paperclip-runner/runner/crates/runner-core/tests/indexed_sqlite_locks.rs @@ -0,0 +1,101 @@ +use std::{fs, path::PathBuf, process::Command}; + +use paperclip_runner_core::indexed_store::{read_only_receipt, ExactReceipt, IndexedStore}; +use rusqlite::{Connection, OpenFlags}; + +const TEST: &str = "preserves_sqlite_locks_across_same_process_opens_and_external_readers"; + +#[test] +fn preserves_sqlite_locks_across_same_process_opens_and_external_readers() { + let role = std::env::var("PAPERCLIP_SQLITE_LOCK_TEST_ROLE").ok(); + let root = std::env::var_os("PAPERCLIP_SQLITE_LOCK_TEST_ROOT").map(PathBuf::from); + if role.as_deref() == Some("reader") { + let db = Connection::open_with_flags( + fs::canonicalize(root.unwrap().join("authority.sqlite")).unwrap(), + OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_NOFOLLOW, + ) + .unwrap(); + assert_eq!( + db.query_row("SELECT count(*) FROM current_state", [], |r| r + .get::<_, i64>(0)) + .unwrap(), + 1 + ); + return; + } + if role.as_deref() == Some("writer") { + let root = root.unwrap(); + let path = root.join("authority.sqlite"); + let store = IndexedStore::open(&path, "lock-regression", true).unwrap(); + let mut generation = paperclip_runner_core::indexed_revision::Revision::Absent; + for sequence in 0..24 { + generation = store + .commit( + "authority", + generation, + serde_json::to_vec( + &serde_json::json!({"sequence":sequence,"padding":"x".repeat(64 * 1024)}), + ) + .unwrap(), + vec![ExactReceipt { + namespace: "receipts".into(), + key: sequence.to_string(), + bytes: sequence.to_string().into_bytes(), + }], + ) + .unwrap(); + // These public paths used to open and close SQLite's database/SHM + // descriptors outside its VFS, silently cancelling POSIX locks. + let second = IndexedStore::open(&path, "lock-regression", false).unwrap(); + assert_eq!( + second.read_state("authority").unwrap().unwrap().generation, + generation + ); + drop(second); + assert_eq!( + read_only_receipt(&path, "lock-regression", generation, "receipts", "0").unwrap(), + Some(b"0".to_vec()) + ); + let result = Command::new(std::env::current_exe().unwrap()) + .args(["--exact", TEST, "--nocapture"]) + .env("PAPERCLIP_SQLITE_LOCK_TEST_ROLE", "reader") + .env("PAPERCLIP_SQLITE_LOCK_TEST_ROOT", &root) + .output() + .unwrap(); + assert!( + result.status.success(), + "external reader failed: {}", + String::from_utf8_lossy(&result.stderr) + ); + } + assert_eq!( + store.read_state("authority").unwrap().unwrap().generation, + 24 + ); + return; + } + let root = + std::env::temp_dir().join(format!("paperclip-sqlite-locks-{}", uuid::Uuid::new_v4())); + fs::create_dir(&root).unwrap(); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + fs::set_permissions(&root, fs::Permissions::from_mode(0o700)).unwrap(); + } + // A regression can SIGBUS on macOS or corrupt state on other systems. Keep + // it in a child so the test reports the failure instead of losing its suite. + let result = Command::new(std::env::current_exe().unwrap()) + .args(["--exact", TEST, "--nocapture"]) + .env("PAPERCLIP_SQLITE_LOCK_TEST_ROLE", "writer") + .env("PAPERCLIP_SQLITE_LOCK_TEST_ROOT", &root) + .output() + .unwrap(); + assert!( + result.status.success(), + "writer exited {:?}; retained evidence at {}: {}", + result.status, + root.display(), + String::from_utf8_lossy(&result.stderr) + ); + fs::remove_dir_all(root).unwrap(); +} diff --git a/packages/paperclip-runner/runner/crates/runner-core/tests/native_provider_backend.rs b/packages/paperclip-runner/runner/crates/runner-core/tests/native_provider_backend.rs index c23930eeffa..864f400143c 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/tests/native_provider_backend.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/tests/native_provider_backend.rs @@ -150,6 +150,7 @@ fn assert_valid_terminal(payload: &Value) { fn command(sequence: u64, command_type: &str, payload: Value) -> Command { Command { + controller_epoch: None, schema: "paperclip.prp.command.v1".to_owned(), command_id: format!("command-{sequence}"), controller_seq: sequence, diff --git a/packages/paperclip-runner/runner/crates/runner-core/tests/process_supervisor.rs b/packages/paperclip-runner/runner/crates/runner-core/tests/process_supervisor.rs index d1fac57753f..4d0d07b7357 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/tests/process_supervisor.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/tests/process_supervisor.rs @@ -110,7 +110,9 @@ fn verified_launch_uses_open_command_and_script_after_atomic_path_replacement() .unwrap(); assert_eq!( process - .receive_stdout_line(Duration::from_secs(1)) + // This checks the authenticated artifact, not a one-second process + // startup SLA. Loaded hosts can delay the first scheduled output. + .receive_stdout_line(Duration::from_secs(5)) .unwrap() .as_deref(), Some("old-command") diff --git a/packages/paperclip-runner/runner/crates/runner-core/tests/provider_bridge.rs b/packages/paperclip-runner/runner/crates/runner-core/tests/provider_bridge.rs index 2504f383ed1..0631fdb2da7 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/tests/provider_bridge.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/tests/provider_bridge.rs @@ -758,8 +758,8 @@ fn exact_identity_overflow_saturates_the_durable_run() { .settle_turn("provider_turn_terminated") .expect("the controlled turn stop retains replay protection"); assert!(recovered.durable_run_receipt_limit_reached()); - assert!(recovered.has_completed_call("settled-0")); - assert!(recovered.has_completed_call("last-call")); + assert!(recovered.has_completed_call("settled-0").unwrap()); + assert!(recovered.has_completed_call("last-call").unwrap()); let stopped_turn_receipt = recovered .replay_result("last-call", "get_task_context", &json!({})) .unwrap() @@ -780,8 +780,8 @@ fn exact_identity_overflow_saturates_the_durable_run() { .replay_result("last-call", "get_task_context", &json!({})) .unwrap() .is_none()); - assert!(recovered.has_completed_call("last-call")); - assert!(recovered.has_completed_call("settled-0")); + assert!(recovered.has_completed_call("last-call").unwrap()); + assert!(recovered.has_completed_call("settled-0").unwrap()); assert!(recovered.durable_run_receipt_limit_reached()); let saturation = recovered .begin_call("next-call".into(), "get_task_context".into(), json!({})) @@ -791,8 +791,8 @@ fn exact_identity_overflow_saturates_the_durable_run() { let encoded = serde_json::to_string(&recovered).unwrap(); let mut recovered: ProviderToolBridge = serde_json::from_str(&encoded).unwrap(); recovered.attach_existing_run().unwrap(); - assert!(recovered.has_completed_call("settled-0")); - assert!(recovered.has_completed_call("last-call")); + assert!(recovered.has_completed_call("settled-0").unwrap()); + assert!(recovered.has_completed_call("last-call").unwrap()); assert!(recovered.durable_run_receipt_limit_reached()); recovered.attach_run(tools("computed")).unwrap(); @@ -857,7 +857,7 @@ fn settled_result_byte_exhaustion_recovers_after_turn_cleanup() { .replay_result("large-settled-0", "get_task_context", &json!({})) .unwrap() .is_none()); - assert!(recovered.has_completed_call("large-settled-0")); + assert!(recovered.has_completed_call("large-settled-0").unwrap()); assert!(!recovered.durable_run_receipt_limit_reached()); recovered .begin_call( @@ -1034,9 +1034,9 @@ fn full_identity_ledger_fails_closed_across_turn_and_recovery() { .expect_err("a full exact ledger must stop fresh work"); assert!(saturation.is_active_turn_receipt_limit()); assert!(recovered.durable_run_receipt_limit_reached()); - assert!(recovered.has_completed_call("settled-65535")); - assert!(recovered.has_completed_call("settled-00000")); - assert!(!recovered.has_completed_call("current-call")); + assert!(recovered.has_completed_call("settled-65535").unwrap()); + assert!(recovered.has_completed_call("settled-00000").unwrap()); + assert!(!recovered.has_completed_call("current-call").unwrap()); assert!(recovered .begin_call("settled-00000".into(), "get_task_context".into(), json!({})) .is_err()); @@ -1054,7 +1054,7 @@ fn full_identity_ledger_fails_closed_across_turn_and_recovery() { let mut recovered_again: ProviderToolBridge = serde_json::from_value(round_trip).unwrap(); recovered_again.attach_existing_run().unwrap(); assert!(recovered_again.durable_run_receipt_limit_reached()); - assert!(recovered_again.has_completed_call("settled-00000")); + assert!(recovered_again.has_completed_call("settled-00000").unwrap()); recovered_again.attach_run(tools("computed")).unwrap(); assert!(!recovered_again.durable_run_receipt_limit_reached()); diff --git a/packages/paperclip-runner/scripts/generate-protocol-schema-module.mjs b/packages/paperclip-runner/scripts/generate-protocol-schema-module.mjs index f65c4da2b6f..73ea2bc7e25 100644 --- a/packages/paperclip-runner/scripts/generate-protocol-schema-module.mjs +++ b/packages/paperclip-runner/scripts/generate-protocol-schema-module.mjs @@ -13,6 +13,7 @@ const validatorsOutputPath = resolve( "src/protocol/generated/standalone-validators.ts", ); const schemaNames = [ + "output-body-reference", "identity", "capabilities", "capabilities-v2", diff --git a/packages/paperclip-runner/scripts/stage-runner-binary.mjs b/packages/paperclip-runner/scripts/stage-runner-binary.mjs index d75f4bbb6de..83c6e859201 100644 --- a/packages/paperclip-runner/scripts/stage-runner-binary.mjs +++ b/packages/paperclip-runner/scripts/stage-runner-binary.mjs @@ -1,5 +1,6 @@ import { execFile } from "node:child_process"; -import { chmod, copyFile, mkdir } from "node:fs/promises"; +import { chmod, copyFile, mkdir, rename, rm } from "node:fs/promises"; +import { randomUUID } from "node:crypto"; import path from "node:path"; import { promisify } from "node:util"; import { fileURLToPath } from "node:url"; @@ -8,17 +9,30 @@ const execFileAsync = promisify(execFile); const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const executable = process.platform === "win32" ? "paperclip-runnerd.exe" : "paperclip-runnerd"; -const source = path.join(packageRoot, "runner", "target", "release", executable); +// Match Cargo's explicit target directory so isolated qualification builds +// cannot accidentally stage an older default-target executable. +const targetDirectory = process.env.CARGO_TARGET_DIR + ? path.resolve(process.cwd(), process.env.CARGO_TARGET_DIR) + : path.join(packageRoot, "runner", "target"); +const source = path.join(targetDirectory, "release", executable); const destinationDirectory = path.join(packageRoot, "dist", "bin"); const destination = path.join(destinationDirectory, executable); await mkdir(destinationDirectory, { recursive: true }); -await copyFile(source, destination); -if (process.platform !== "win32") await chmod(destination, 0o755); -// Rust's linker emits an ad-hoc Mach-O signature. Copying that executable to -// its package location preserves the bytes but can leave the kernel rejecting -// the new inode with SIGKILL. Re-sign the staged inode so local packaged-runner -// evals execute the same artifact that was just built. -if (process.platform === "darwin") { - await execFileAsync("codesign", ["--force", "--sign", "-", destination]); -} +// Existing agents can keep this executable mapped for days. Never overwrite +// their inode (including during code signing); publish a new one atomically. +const temporary = `${destination}.${randomUUID()}.tmp`; +try { + await copyFile(source, temporary); + if (process.platform !== "win32") await chmod(temporary, 0o755); + // Rust's linker emits an ad-hoc Mach-O signature. Copying that executable to + // its package location preserves the bytes but can leave the kernel rejecting + // the new inode with SIGKILL. Re-sign the staged inode so local packaged-runner + // evals execute the same artifact that was just built. + if (process.platform === "darwin") { + // The staging filename is random, but the signed identifier must be + // stable: identical code must retain its artifact digest across rebuilds. + await execFileAsync("codesign", ["--force", "--sign", "-", "--identifier", "paperclip-runnerd", temporary]); + } + await rename(temporary, destination); +} finally { await rm(temporary, { force: true }); } diff --git a/packages/paperclip-runner/src/protocol/generated/schema-bundle.ts b/packages/paperclip-runner/src/protocol/generated/schema-bundle.ts index aac57ca6c91..bcabacf349c 100644 --- a/packages/paperclip-runner/src/protocol/generated/schema-bundle.ts +++ b/packages/paperclip-runner/src/protocol/generated/schema-bundle.ts @@ -1,5 +1,40 @@ // Generated by scripts/generate-protocol-schema-module.mjs. Do not edit. +export const outputBodyReferenceSchema = { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json", + "title": "Immutable output body reference", + "type": "object", + "required": [ + "schema", + "bodyId", + "sha256", + "byteLength", + "mediaType" + ], + "properties": { + "schema": { + "const": "paperclip.output.body.v1" + }, + "bodyId": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "byteLength": { + "type": "string", + "pattern": "^[1-9][0-9]{0,6}$" + }, + "mediaType": { + "const": "text/plain; charset=utf-8" + } + }, + "additionalProperties": false +} as const; + export const identitySchema = { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://paperclip.dev/schemas/prp/v1/identity.schema.json", @@ -343,6 +378,11 @@ export const commandSchema = { "type": "integer", "minimum": 1 }, + "controllerEpoch": { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$", + "description": "Requires negotiated transport.command_epochs.v1; absent is the retained numeric namespace." + }, "type": { "enum": [ "run.prepare", @@ -437,6 +477,11 @@ export const commandV2Schema = { "type": "integer", "minimum": 1 }, + "controllerEpoch": { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$", + "description": "Requires negotiated transport.command_epochs.v1; absent is the retained numeric namespace." + }, "type": { "enum": [ "run.prepare", @@ -1142,6 +1187,9 @@ export const providerEventSchema = { "null" ], "pattern": "^sha256:[a-f0-9]{64}$" + }, + "outputBody": { + "$ref": "https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json" } }, "additionalProperties": false @@ -3454,6 +3502,10 @@ export const eventSchema = { "minLength": 1, "maxLength": 160 }, + "sourceEpoch": { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" + }, "sourceSeq": { "type": "integer", "minimum": 1, @@ -3552,6 +3604,7 @@ export const eventSchema = { "turn.failed", "turn.interrupted", "turn.cancelled", + "output.body.chunk", "item.started", "item.delta", "item.completed", @@ -3641,6 +3694,52 @@ export const eventSchema = { "debug": { "type": "object", "additionalProperties": true + }, + "sourceEpochTransition": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema", + "runId", + "transitionId", + "fromEpoch", + "nextEpoch", + "finalOrdinal" + ], + "properties": { + "schema": { + "const": "paperclip.prp.event-epoch.v1" + }, + "runId": { + "type": "string", + "minLength": 1, + "maxLength": 240 + }, + "transitionId": { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" + }, + "fromEpoch": { + "anyOf": [ + { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" + }, + { + "type": "null" + } + ] + }, + "nextEpoch": { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" + }, + "finalOrdinal": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + } + } } }, "allOf": [ @@ -4123,6 +4222,79 @@ export const eventSchema = { } } } + }, + { + "if": { + "properties": { + "eventType": { + "const": "output.body.chunk" + } + } + }, + "then": { + "properties": { + "payload": { + "type": "object", + "required": [ + "schema", + "body", + "offset", + "sha256", + "text" + ], + "properties": { + "schema": { + "const": "paperclip.output.body.chunk.v1" + }, + "body": { + "type": "object", + "required": [ + "schema", + "bodyId", + "sha256", + "byteLength", + "mediaType" + ], + "properties": { + "schema": { + "const": "paperclip.output.body.v1" + }, + "bodyId": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "byteLength": { + "type": "string", + "pattern": "^[1-9][0-9]{0,6}$" + }, + "mediaType": { + "const": "text/plain; charset=utf-8" + } + }, + "additionalProperties": false + }, + "offset": { + "type": "string", + "pattern": "^(0|[1-9][0-9]{0,6})$" + }, + "sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "text": { + "type": "string", + "minLength": 1, + "maxLength": 32766 + } + }, + "additionalProperties": false + } + } + } } ], "additionalProperties": true @@ -4155,6 +4327,10 @@ export const eventV2Schema = { "minLength": 1, "maxLength": 160 }, + "sourceEpoch": { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" + }, "sourceSeq": { "type": "integer", "minimum": 1, @@ -4256,6 +4432,7 @@ export const eventV2Schema = { "turn.failed", "turn.interrupted", "turn.cancelled", + "output.body.chunk", "item.started", "item.delta", "item.completed", @@ -4327,9 +4504,75 @@ export const eventV2Schema = { "debug": { "type": "object", "additionalProperties": true + }, + "sourceEpochTransition": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema", + "runId", + "transitionId", + "fromEpoch", + "nextEpoch", + "finalOrdinal" + ], + "properties": { + "schema": { + "const": "paperclip.prp.event-epoch.v1" + }, + "runId": { + "type": "string", + "minLength": 1, + "maxLength": 240 + }, + "transitionId": { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" + }, + "fromEpoch": { + "anyOf": [ + { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" + }, + { + "type": "null" + } + ] + }, + "nextEpoch": { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" + }, + "finalOrdinal": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + } + } } }, "allOf": [ + { + "if": { + "properties": { + "eventType": { + "enum": [ + "tool.execution.started", + "tool.execution.progressed", + "tool.execution.completed" + ] + } + } + }, + "then": { + "properties": { + "payload": { + "$ref": "https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/toolExecution" + } + } + } + }, { "if": { "properties": { @@ -4389,6 +4632,79 @@ export const eventV2Schema = { } } } + }, + { + "if": { + "properties": { + "eventType": { + "const": "output.body.chunk" + } + } + }, + "then": { + "properties": { + "payload": { + "type": "object", + "required": [ + "schema", + "body", + "offset", + "sha256", + "text" + ], + "properties": { + "schema": { + "const": "paperclip.output.body.chunk.v1" + }, + "body": { + "type": "object", + "required": [ + "schema", + "bodyId", + "sha256", + "byteLength", + "mediaType" + ], + "properties": { + "schema": { + "const": "paperclip.output.body.v1" + }, + "bodyId": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "byteLength": { + "type": "string", + "pattern": "^[1-9][0-9]{0,6}$" + }, + "mediaType": { + "const": "text/plain; charset=utf-8" + } + }, + "additionalProperties": false + }, + "offset": { + "type": "string", + "pattern": "^(0|[1-9][0-9]{0,6})$" + }, + "sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "text": { + "type": "string", + "minLength": 1, + "maxLength": 32766 + } + }, + "additionalProperties": false + } + } + } } ], "additionalProperties": true @@ -4651,6 +4967,7 @@ export const fixtureSchema = { } as const; export const prpSchemaBundle = { + "output-body-reference": outputBodyReferenceSchema, "identity": identitySchema, "capabilities": capabilitiesSchema, "capabilities-v2": capabilitiesV2Schema, diff --git a/packages/paperclip-runner/src/protocol/generated/standalone-validators.ts b/packages/paperclip-runner/src/protocol/generated/standalone-validators.ts index 3eba6e57ee2..c03b3f69e8f 100644 --- a/packages/paperclip-runner/src/protocol/generated/standalone-validators.ts +++ b/packages/paperclip-runner/src/protocol/generated/standalone-validators.ts @@ -4,4 +4,4 @@ import equalModule from "ajv/dist/runtime/equal.js"; import ucs2LengthModule from "ajv/dist/runtime/ucs2length.js"; -"use strict";export const fixtureValidator: unknown = validate20;const schema31 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/fixture.schema.json","title":"PRP scripted replay fixture","type":"object","required":["schema","fixtureVersion","protocolVersion","name","description","identity","capabilities","commands","events","result"],"properties":{"schema":{"const":"paperclip.prp.fixture.v1"},"fixtureVersion":{"const":1},"protocolVersion":{"enum":[1,2]},"name":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","minLength":1,"maxLength":1000},"identity":{"$ref":"https://paperclip.dev/schemas/prp/v1/identity.schema.json"},"capabilities":{"oneOf":[{"$ref":"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json"},{"$ref":"https://paperclip.dev/schemas/prp/v2/capabilities.schema.json"}]},"commands":{"type":"array","items":{"oneOf":[{"$ref":"https://paperclip.dev/schemas/prp/v1/command.schema.json"},{"$ref":"https://paperclip.dev/schemas/prp/v2/command.schema.json"}]}},"events":{"type":"array","minItems":1,"items":{"oneOf":[{"$ref":"https://paperclip.dev/schemas/prp/v1/event.schema.json"},{"$ref":"https://paperclip.dev/schemas/prp/v2/event.schema.json"}]}},"requests":{"type":"array","items":{"$ref":"https://paperclip.dev/schemas/prp/v1/request.schema.json"}},"result":{"$ref":"https://paperclip.dev/schemas/prp/v1/result.schema.json"}},"additionalProperties":true};const schema41 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json","title":"PRP negotiated capabilities","type":"object","required":["schema","sessionReusePolicy","driver","steer","interrupt","resume","runtimeRequests","structuredResult","typedEvents"],"properties":{"schema":{"const":"paperclip.prp.capabilities.v1"},"sessionReusePolicy":{"enum":["new_per_run","reuse_per_issue","reuse_per_workspace"]},"driver":{"type":"object","required":["kind","version"],"properties":{"kind":{"type":"string","minLength":1,"maxLength":80},"version":{"type":"string","minLength":1,"maxLength":80}},"additionalProperties":true},"steer":{"type":"boolean"},"interrupt":{"type":"boolean"},"resume":{"type":"boolean"},"runtimeRequests":{"type":"boolean"},"structuredResult":{"type":"boolean"},"typedEvents":{"type":"boolean"},"typedEventFamilies":{"type":"array","maxItems":64,"items":{"type":"object","required":["family","version","availability","detailLevel"],"properties":{"family":{"type":"string","minLength":1,"maxLength":160,"pattern":"^[a-z][a-z0-9_.-]*$"},"version":{"const":1},"availability":{"enum":["available","unsupported","policy_disabled"]},"detailLevel":{"enum":["summary","structured"]}},"additionalProperties":false}},"semanticTools":{"type":"object","required":["schema","schemaVersion","operations"],"properties":{"schema":{"const":"paperclip.prp.semantic_tools.v1"},"schemaVersion":{"const":1},"operations":{"type":"array","items":{"type":"object","required":["operationId","version","availability","redactionDisposition"],"properties":{"operationId":{"type":"string","minLength":1,"maxLength":160,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]*$"},"version":{"const":1},"availability":{"enum":["available","denied","unsupported"]},"redactionDisposition":{"const":"digest_only"},"requiredClaims":{"type":"array","items":{"type":"string","minLength":1,"maxLength":160},"uniqueItems":true},"reasonCode":{"type":"string","minLength":1,"maxLength":160,"pattern":"^[a-z][a-z0-9_.:-]*$"}},"additionalProperties":true}}},"additionalProperties":true},"unsupported":{"type":"array","items":{"type":"string","minLength":1},"uniqueItems":true}},"additionalProperties":true};const schema45 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/command.schema.json","title":"PRP runner command","type":"object","required":["schema","commandId","controllerSeq","type","issuedAt","payload"],"properties":{"schema":{"const":"paperclip.prp.command.v1"},"commandId":{"type":"string","minLength":1,"maxLength":160},"controllerSeq":{"type":"integer","minimum":1},"type":{"enum":["run.prepare","run.attach","session.open","turn.start","turn.steer","turn.interrupt","turn.stop","request.resolve","interaction.receipt","semantic_tool.result","session.snapshot","session.close","session.budget.increase","session.destroy","run.cancel","runner.drain","runner.suspend","runner.shutdown"]},"issuedAt":{"type":"string","format":"date-time"},"deadlineAt":{"type":"string","format":"date-time"},"precondition":{"type":"object","properties":{"runnerState":{"type":"array","items":{"type":"string"}},"runState":{"type":"array","items":{"type":"string"}},"sessionState":{"type":"array","items":{"type":"string"}},"activeTurnId":{"type":["string","null"]}},"additionalProperties":true},"payload":{"type":"object","additionalProperties":true}},"additionalProperties":true};const schema46 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v2/command.schema.json","title":"PRP v2 runner command","type":"object","required":["schema","commandId","controllerSeq","type","issuedAt","payload"],"properties":{"schema":{"const":"paperclip.prp.command.v2"},"commandId":{"type":"string","minLength":1,"maxLength":160},"controllerSeq":{"type":"integer","minimum":1},"type":{"enum":["run.prepare","run.attach","session.open","turn.start","turn.steer","turn.interrupt","turn.stop","request.resolve","interaction.receipt","semantic_tool.result","session.snapshot","session.close","session.budget.increase","session.destroy","run.cancel","runner.drain","runner.suspend","runner.shutdown","session.goal.get","session.goal.set","session.goal.clear"]},"issuedAt":{"type":"string","format":"date-time"},"deadlineAt":{"type":"string","format":"date-time"},"precondition":{"type":"object","properties":{"runnerState":{"type":"array","items":{"type":"string"}},"runState":{"type":"array","items":{"type":"string"}},"sessionState":{"type":"array","items":{"type":"string"}},"activeTurnId":{"type":["string","null"]}},"additionalProperties":true},"payload":{"type":"object","additionalProperties":true}},"allOf":[{"if":{"properties":{"type":{"const":"session.goal.set"}}},"then":{"properties":{"payload":{"type":"object","properties":{"requestId":{"type":"string","minLength":1,"maxLength":160},"objective":{"type":"string","minLength":1,"maxLength":4000},"status":{"enum":["active","paused"]},"tokenBudget":{"type":["integer","null"],"minimum":1}},"additionalProperties":true}}}}],"additionalProperties":true};const schema199 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/result.schema.json","title":"PRP structured run result","type":"object","required":["schema","reportedWorkDisposition","summary","completionClaim","evidence","verification","attentionRequests","artifacts"],"properties":{"schema":{"const":"paperclip.run_result.v1"},"reportedWorkDisposition":{"enum":["done","blocked","needs_review","yielded"]},"summary":{"type":"string","minLength":1,"maxLength":12000},"completionClaim":{"type":"object","required":["contractRevision","objectiveSatisfied","criteria","remainingWork"],"properties":{"contractRevision":{"type":"string","minLength":1},"objectiveSatisfied":{"type":"boolean"},"criteria":{"type":"array","items":{"type":"object","required":["criterionId","status","evidenceRefs"],"properties":{"criterionId":{"type":"string","minLength":1},"status":{"enum":["satisfied","not_satisfied","unknown"]},"evidenceRefs":{"type":"array","items":{"type":"string"}},"explanation":{"type":"string"}},"additionalProperties":true}},"remainingWork":{"type":"array","items":{"type":"object","required":["description","blocksCompletion"],"properties":{"description":{"type":"string","minLength":1},"blocksCompletion":{"type":"boolean"}},"additionalProperties":true}}},"additionalProperties":true},"evidence":{"type":"array","items":{"type":"object"}},"verification":{"type":"array","description":"Checks used to verify the work. Use failed only when the check actually ran and found a defect in the work. If a check could not run or complete because the environment, tool, dependency, permission, credential, policy, external service, or budget was unavailable, use not_run with reasonCode even when the attempted command exited non-zero.","items":{"type":"object","required":["commandOrCheck","status"],"properties":{"commandOrCheck":{"type":"string","minLength":1},"status":{"enum":["passed","failed","not_run"],"description":"passed means the check ran and succeeded; failed means it ran to a meaningful verdict and found the work incorrect; not_run means no meaningful verdict was possible because the check was not attempted or could not complete."},"reasonCode":{"description":"Required for not_run; identify why the check had no meaningful verdict.","enum":["environment_unavailable","tool_unavailable","dependency_missing","permission_denied","credential_missing","policy_restricted","external_service_unavailable","budget_exhausted","other"]},"detail":{"type":"string"},"artifactRef":{"type":"string"}},"additionalProperties":true}},"blocker":{"type":"object","required":["reasonCode","owner","unblockAction","scope"],"properties":{"reasonCode":{"type":"string","minLength":1},"owner":{"type":"object","required":["kind","name"],"properties":{"kind":{"enum":["agent","user","system","external"]},"name":{"type":"string","minLength":1}},"additionalProperties":true},"unblockAction":{"type":"string","minLength":1},"scope":{"enum":["current_track","task_wide"]}},"additionalProperties":true},"attentionRequests":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","minLength":1},"summary":{"type":"string","minLength":1},"ownerClass":{"enum":["human","agent","external_system"]},"targetAgentId":{"type":"string","minLength":1}},"additionalProperties":true}},"artifacts":{"type":"array","items":{"type":"object","required":["kind","ref"],"properties":{"kind":{"type":"string","minLength":1},"ref":{"type":"string","minLength":1},"title":{"type":"string"}},"additionalProperties":true}},"continuation":{"type":"object","required":["kind","summary","idempotencyKey"],"properties":{"kind":{"enum":["same_agent","retry","delegated_issue","response_wake","monitor"]},"summary":{"type":"string","minLength":1},"idempotencyKey":{"type":"string","minLength":1}},"additionalProperties":true}},"allOf":[{"if":{"properties":{"reportedWorkDisposition":{"const":"blocked"}}},"then":{"required":["blocker"]}},{"if":{"properties":{"reportedWorkDisposition":{"const":"yielded"}}},"then":{"required":["continuation"]}}],"additionalProperties":true};const func1 = typeof ucs2LengthModule === "function" ? ucs2LengthModule : ucs2LengthModule.default;const schema32 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/identity.schema.json","title":"PRP identity graph","type":"object","required":["schema","companyId","issueId","runId","environmentLeaseId","runnerInstanceId","normalizedSessionId","driverSessionId"],"properties":{"schema":{"const":"paperclip.prp.identity.v1"},"companyId":{"$ref":"#/$defs/stableId"},"issueId":{"$ref":"#/$defs/stableId"},"runId":{"$ref":"#/$defs/stableId"},"environmentLeaseId":{"$ref":"#/$defs/stableId"},"runnerInstanceId":{"$ref":"#/$defs/stableId"},"normalizedSessionId":{"$ref":"#/$defs/stableId"},"driverSessionId":{"$ref":"#/$defs/stableId"},"providerSessionId":{"$ref":"#/$defs/stableId"}},"additionalProperties":true,"$defs":{"stableId":{"type":"string","minLength":1,"maxLength":160,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]*$"}}};const schema33 = {"type":"string","minLength":1,"maxLength":160,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]*$"};const pattern4 = new RegExp("^[A-Za-z0-9][A-Za-z0-9._:-]*$", "u");function validate21(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){/*# sourceURL="https://paperclip.dev/schemas/prp/v1/identity.schema.json" */;let vErrors = null;let errors = 0;const evaluated0 = validate21.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.companyId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "companyId"},message:"must have required property '"+"companyId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.issueId === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "issueId"},message:"must have required property '"+"issueId"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.runId === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "runId"},message:"must have required property '"+"runId"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.environmentLeaseId === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "environmentLeaseId"},message:"must have required property '"+"environmentLeaseId"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.runnerInstanceId === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "runnerInstanceId"},message:"must have required property '"+"runnerInstanceId"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.normalizedSessionId === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "normalizedSessionId"},message:"must have required property '"+"normalizedSessionId"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(data.driverSessionId === undefined){const err7 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "driverSessionId"},message:"must have required property '"+"driverSessionId"+"'"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(data.schema !== undefined){if("paperclip.prp.identity.v1" !== data.schema){const err8 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.identity.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}if(data.companyId !== undefined){let data1 = data.companyId;if(typeof data1 === "string"){if(func1(data1) > 160){const err9 = {instancePath:instancePath+"/companyId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(func1(data1) < 1){const err10 = {instancePath:instancePath+"/companyId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(!pattern4.test(data1)){const err11 = {instancePath:instancePath+"/companyId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}else {const err12 = {instancePath:instancePath+"/companyId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.issueId !== undefined){let data2 = data.issueId;if(typeof data2 === "string"){if(func1(data2) > 160){const err13 = {instancePath:instancePath+"/issueId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(func1(data2) < 1){const err14 = {instancePath:instancePath+"/issueId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(!pattern4.test(data2)){const err15 = {instancePath:instancePath+"/issueId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}else {const err16 = {instancePath:instancePath+"/issueId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}if(data.runId !== undefined){let data3 = data.runId;if(typeof data3 === "string"){if(func1(data3) > 160){const err17 = {instancePath:instancePath+"/runId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}if(func1(data3) < 1){const err18 = {instancePath:instancePath+"/runId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}if(!pattern4.test(data3)){const err19 = {instancePath:instancePath+"/runId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}else {const err20 = {instancePath:instancePath+"/runId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}if(data.environmentLeaseId !== undefined){let data4 = data.environmentLeaseId;if(typeof data4 === "string"){if(func1(data4) > 160){const err21 = {instancePath:instancePath+"/environmentLeaseId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(func1(data4) < 1){const err22 = {instancePath:instancePath+"/environmentLeaseId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}if(!pattern4.test(data4)){const err23 = {instancePath:instancePath+"/environmentLeaseId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}else {const err24 = {instancePath:instancePath+"/environmentLeaseId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}if(data.runnerInstanceId !== undefined){let data5 = data.runnerInstanceId;if(typeof data5 === "string"){if(func1(data5) > 160){const err25 = {instancePath:instancePath+"/runnerInstanceId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}if(func1(data5) < 1){const err26 = {instancePath:instancePath+"/runnerInstanceId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}if(!pattern4.test(data5)){const err27 = {instancePath:instancePath+"/runnerInstanceId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}}else {const err28 = {instancePath:instancePath+"/runnerInstanceId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}if(data.normalizedSessionId !== undefined){let data6 = data.normalizedSessionId;if(typeof data6 === "string"){if(func1(data6) > 160){const err29 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}if(func1(data6) < 1){const err30 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}if(!pattern4.test(data6)){const err31 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}}else {const err32 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}}if(data.driverSessionId !== undefined){let data7 = data.driverSessionId;if(typeof data7 === "string"){if(func1(data7) > 160){const err33 = {instancePath:instancePath+"/driverSessionId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}if(func1(data7) < 1){const err34 = {instancePath:instancePath+"/driverSessionId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}if(!pattern4.test(data7)){const err35 = {instancePath:instancePath+"/driverSessionId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}}else {const err36 = {instancePath:instancePath+"/driverSessionId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}}if(data.providerSessionId !== undefined){let data8 = data.providerSessionId;if(typeof data8 === "string"){if(func1(data8) > 160){const err37 = {instancePath:instancePath+"/providerSessionId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}if(func1(data8) < 1){const err38 = {instancePath:instancePath+"/providerSessionId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}if(!pattern4.test(data8)){const err39 = {instancePath:instancePath+"/providerSessionId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}}else {const err40 = {instancePath:instancePath+"/providerSessionId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}}}else {const err41 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}validate21.errors = vErrors;return errors === 0;}validate21.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema42 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v2/capabilities.schema.json","title":"PRP v2 negotiated capabilities","type":"object","required":["schema","sessionReusePolicy","driver","steer","interrupt","resume","runtimeRequests","structuredResult","typedEvents","sessionGoals"],"properties":{"schema":{"const":"paperclip.prp.capabilities.v2"},"sessionReusePolicy":{"enum":["new_per_run","reuse_per_issue","reuse_per_workspace"]},"driver":{"type":"object","required":["kind","version"],"properties":{"kind":{"type":"string","minLength":1,"maxLength":80},"version":{"type":"string","minLength":1,"maxLength":80}},"additionalProperties":true},"steer":{"type":"boolean"},"interrupt":{"type":"boolean"},"resume":{"type":"boolean"},"runtimeRequests":{"type":"boolean"},"structuredResult":{"type":"boolean"},"typedEvents":{"type":"boolean"},"sessionGoals":{"$ref":"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability"},"unsupported":{"type":"array","items":{"type":"string","minLength":1},"uniqueItems":true}},"additionalProperties":true};const schema44 = {"type":"object","required":["availability","actions","autonomousUpdates","persistentAcrossResume","maxObjectiveChars","tokenBudgetControl","usageReporting"],"properties":{"availability":{"enum":["available","unsupported","policy_disabled"]},"actions":{"type":"array","items":{"enum":["set","pause","resume","clear"]},"uniqueItems":true},"autonomousUpdates":{"type":"boolean"},"persistentAcrossResume":{"type":"boolean"},"maxObjectiveChars":{"type":"integer","minimum":1,"maximum":4000},"tokenBudgetControl":{"type":"boolean"},"usageReporting":{"type":"boolean"},"reasonCode":{"type":"string","minLength":1,"maxLength":160},"reason":{"type":"string","minLength":1,"maxLength":1000}},"additionalProperties":true};const func0 = typeof equalModule === "function" ? equalModule : equalModule.default;function validate23(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){/*# sourceURL="https://paperclip.dev/schemas/prp/v2/capabilities.schema.json" */;let vErrors = null;let errors = 0;const evaluated0 = validate23.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.sessionReusePolicy === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sessionReusePolicy"},message:"must have required property '"+"sessionReusePolicy"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.driver === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "driver"},message:"must have required property '"+"driver"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.steer === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "steer"},message:"must have required property '"+"steer"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.interrupt === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "interrupt"},message:"must have required property '"+"interrupt"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.resume === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "resume"},message:"must have required property '"+"resume"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.runtimeRequests === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "runtimeRequests"},message:"must have required property '"+"runtimeRequests"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(data.structuredResult === undefined){const err7 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "structuredResult"},message:"must have required property '"+"structuredResult"+"'"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(data.typedEvents === undefined){const err8 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "typedEvents"},message:"must have required property '"+"typedEvents"+"'"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(data.sessionGoals === undefined){const err9 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sessionGoals"},message:"must have required property '"+"sessionGoals"+"'"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(data.schema !== undefined){if("paperclip.prp.capabilities.v2" !== data.schema){const err10 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.capabilities.v2"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.sessionReusePolicy !== undefined){let data1 = data.sessionReusePolicy;if(!(((data1 === "new_per_run") || (data1 === "reuse_per_issue")) || (data1 === "reuse_per_workspace"))){const err11 = {instancePath:instancePath+"/sessionReusePolicy",schemaPath:"#/properties/sessionReusePolicy/enum",keyword:"enum",params:{allowedValues: schema42.properties.sessionReusePolicy.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.driver !== undefined){let data2 = data.driver;if(data2 && typeof data2 == "object" && !Array.isArray(data2)){if(data2.kind === undefined){const err12 = {instancePath:instancePath+"/driver",schemaPath:"#/properties/driver/required",keyword:"required",params:{missingProperty: "kind"},message:"must have required property '"+"kind"+"'"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(data2.version === undefined){const err13 = {instancePath:instancePath+"/driver",schemaPath:"#/properties/driver/required",keyword:"required",params:{missingProperty: "version"},message:"must have required property '"+"version"+"'"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(data2.kind !== undefined){let data3 = data2.kind;if(typeof data3 === "string"){if(func1(data3) > 80){const err14 = {instancePath:instancePath+"/driver/kind",schemaPath:"#/properties/driver/properties/kind/maxLength",keyword:"maxLength",params:{limit: 80},message:"must NOT have more than 80 characters"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(func1(data3) < 1){const err15 = {instancePath:instancePath+"/driver/kind",schemaPath:"#/properties/driver/properties/kind/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}else {const err16 = {instancePath:instancePath+"/driver/kind",schemaPath:"#/properties/driver/properties/kind/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}if(data2.version !== undefined){let data4 = data2.version;if(typeof data4 === "string"){if(func1(data4) > 80){const err17 = {instancePath:instancePath+"/driver/version",schemaPath:"#/properties/driver/properties/version/maxLength",keyword:"maxLength",params:{limit: 80},message:"must NOT have more than 80 characters"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}if(func1(data4) < 1){const err18 = {instancePath:instancePath+"/driver/version",schemaPath:"#/properties/driver/properties/version/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}else {const err19 = {instancePath:instancePath+"/driver/version",schemaPath:"#/properties/driver/properties/version/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}}else {const err20 = {instancePath:instancePath+"/driver",schemaPath:"#/properties/driver/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}if(data.steer !== undefined){if(typeof data.steer !== "boolean"){const err21 = {instancePath:instancePath+"/steer",schemaPath:"#/properties/steer/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}}if(data.interrupt !== undefined){if(typeof data.interrupt !== "boolean"){const err22 = {instancePath:instancePath+"/interrupt",schemaPath:"#/properties/interrupt/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}if(data.resume !== undefined){if(typeof data.resume !== "boolean"){const err23 = {instancePath:instancePath+"/resume",schemaPath:"#/properties/resume/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}if(data.runtimeRequests !== undefined){if(typeof data.runtimeRequests !== "boolean"){const err24 = {instancePath:instancePath+"/runtimeRequests",schemaPath:"#/properties/runtimeRequests/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}if(data.structuredResult !== undefined){if(typeof data.structuredResult !== "boolean"){const err25 = {instancePath:instancePath+"/structuredResult",schemaPath:"#/properties/structuredResult/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}if(data.typedEvents !== undefined){if(typeof data.typedEvents !== "boolean"){const err26 = {instancePath:instancePath+"/typedEvents",schemaPath:"#/properties/typedEvents/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}}if(data.sessionGoals !== undefined){let data11 = data.sessionGoals;if(data11 && typeof data11 == "object" && !Array.isArray(data11)){if(data11.availability === undefined){const err27 = {instancePath:instancePath+"/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "availability"},message:"must have required property '"+"availability"+"'"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if(data11.actions === undefined){const err28 = {instancePath:instancePath+"/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "actions"},message:"must have required property '"+"actions"+"'"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}if(data11.autonomousUpdates === undefined){const err29 = {instancePath:instancePath+"/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "autonomousUpdates"},message:"must have required property '"+"autonomousUpdates"+"'"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}if(data11.persistentAcrossResume === undefined){const err30 = {instancePath:instancePath+"/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "persistentAcrossResume"},message:"must have required property '"+"persistentAcrossResume"+"'"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}if(data11.maxObjectiveChars === undefined){const err31 = {instancePath:instancePath+"/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "maxObjectiveChars"},message:"must have required property '"+"maxObjectiveChars"+"'"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}if(data11.tokenBudgetControl === undefined){const err32 = {instancePath:instancePath+"/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "tokenBudgetControl"},message:"must have required property '"+"tokenBudgetControl"+"'"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}if(data11.usageReporting === undefined){const err33 = {instancePath:instancePath+"/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "usageReporting"},message:"must have required property '"+"usageReporting"+"'"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}if(data11.availability !== undefined){let data12 = data11.availability;if(!(((data12 === "available") || (data12 === "unsupported")) || (data12 === "policy_disabled"))){const err34 = {instancePath:instancePath+"/sessionGoals/availability",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/availability/enum",keyword:"enum",params:{allowedValues: schema44.properties.availability.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}}if(data11.actions !== undefined){let data13 = data11.actions;if(Array.isArray(data13)){const len0 = data13.length;for(let i0=0; i0 1){outer0:for(;i1--;){for(j0 = i1; j0--;){if(func0(data13[i1], data13[j0])){const err36 = {instancePath:instancePath+"/sessionGoals/actions",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/actions/uniqueItems",keyword:"uniqueItems",params:{i: i1, j: j0},message:"must NOT have duplicate items (items ## "+j0+" and "+i1+" are identical)"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;break outer0;}}}}}else {const err37 = {instancePath:instancePath+"/sessionGoals/actions",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/actions/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}}if(data11.autonomousUpdates !== undefined){if(typeof data11.autonomousUpdates !== "boolean"){const err38 = {instancePath:instancePath+"/sessionGoals/autonomousUpdates",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/autonomousUpdates/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}}if(data11.persistentAcrossResume !== undefined){if(typeof data11.persistentAcrossResume !== "boolean"){const err39 = {instancePath:instancePath+"/sessionGoals/persistentAcrossResume",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/persistentAcrossResume/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}}if(data11.maxObjectiveChars !== undefined){let data17 = data11.maxObjectiveChars;if(!(((typeof data17 == "number") && (!(data17 % 1) && !isNaN(data17))) && (isFinite(data17)))){const err40 = {instancePath:instancePath+"/sessionGoals/maxObjectiveChars",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/maxObjectiveChars/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}if((typeof data17 == "number") && (isFinite(data17))){if(data17 > 4000 || isNaN(data17)){const err41 = {instancePath:instancePath+"/sessionGoals/maxObjectiveChars",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/maxObjectiveChars/maximum",keyword:"maximum",params:{comparison: "<=", limit: 4000},message:"must be <= 4000"};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}if(data17 < 1 || isNaN(data17)){const err42 = {instancePath:instancePath+"/sessionGoals/maxObjectiveChars",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/maxObjectiveChars/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}}}if(data11.tokenBudgetControl !== undefined){if(typeof data11.tokenBudgetControl !== "boolean"){const err43 = {instancePath:instancePath+"/sessionGoals/tokenBudgetControl",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/tokenBudgetControl/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}}if(data11.usageReporting !== undefined){if(typeof data11.usageReporting !== "boolean"){const err44 = {instancePath:instancePath+"/sessionGoals/usageReporting",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/usageReporting/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err44];}else {vErrors.push(err44);}errors++;}}if(data11.reasonCode !== undefined){let data20 = data11.reasonCode;if(typeof data20 === "string"){if(func1(data20) > 160){const err45 = {instancePath:instancePath+"/sessionGoals/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reasonCode/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err45];}else {vErrors.push(err45);}errors++;}if(func1(data20) < 1){const err46 = {instancePath:instancePath+"/sessionGoals/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reasonCode/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err46];}else {vErrors.push(err46);}errors++;}}else {const err47 = {instancePath:instancePath+"/sessionGoals/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reasonCode/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err47];}else {vErrors.push(err47);}errors++;}}if(data11.reason !== undefined){let data21 = data11.reason;if(typeof data21 === "string"){if(func1(data21) > 1000){const err48 = {instancePath:instancePath+"/sessionGoals/reason",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reason/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err48];}else {vErrors.push(err48);}errors++;}if(func1(data21) < 1){const err49 = {instancePath:instancePath+"/sessionGoals/reason",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reason/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err49];}else {vErrors.push(err49);}errors++;}}else {const err50 = {instancePath:instancePath+"/sessionGoals/reason",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reason/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err50];}else {vErrors.push(err50);}errors++;}}}else {const err51 = {instancePath:instancePath+"/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err51];}else {vErrors.push(err51);}errors++;}}if(data.unsupported !== undefined){let data22 = data.unsupported;if(Array.isArray(data22)){const len1 = data22.length;for(let i2=0; i2 1){const indices0 = {};for(;i3--;){let item0 = data22[i3];if(typeof item0 !== "string"){continue;}if(typeof indices0[item0] == "number"){j1 = indices0[item0];const err54 = {instancePath:instancePath+"/unsupported",schemaPath:"#/properties/unsupported/uniqueItems",keyword:"uniqueItems",params:{i: i3, j: j1},message:"must NOT have duplicate items (items ## "+j1+" and "+i3+" are identical)"};if(vErrors === null){vErrors = [err54];}else {vErrors.push(err54);}errors++;break;}indices0[item0] = i3;}}}else {const err55 = {instancePath:instancePath+"/unsupported",schemaPath:"#/properties/unsupported/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err55];}else {vErrors.push(err55);}errors++;}}}else {const err56 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err56];}else {vErrors.push(err56);}errors++;}validate23.errors = vErrors;return errors === 0;}validate23.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema47 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/event.schema.json","title":"PRP native event","type":"object","required":["schema","sourceEventId","sourceSeq","sourceInstanceId","sourceKind","runId","eventType","schemaVersion","priority","emittedAt","payload"],"properties":{"schema":{"const":"paperclip.prp.event.v1"},"sourceEventId":{"type":"string","minLength":1,"maxLength":160},"sourceSeq":{"type":"integer","minimum":1,"maximum":9007199254740991},"sourceInstanceId":{"type":"string","minLength":1,"maxLength":160},"sourceKind":{"enum":["runner","control_plane"]},"runId":{"type":"string","minLength":1,"maxLength":160},"normalizedSessionId":{"type":"string","minLength":1,"maxLength":160},"turnId":{"type":"string","minLength":1,"maxLength":240},"itemId":{"type":"string","minLength":1,"maxLength":240},"eventType":{"enum":["runner.connected","runner.reconnected","runner.reconciled","runner.disconnected","runner.draining","runner.backpressure","runner.suspending","runner.suspended","runner.stopped","runner.diagnostic","runtime.phase.changed","sandbox.metric","workspace.ready","workspace.change.updated","workspace.diff.recorded","workspace.file.referenced","harness.starting","harness.ready","harness.exited","harness.diagnostic","plan.updated","tool.execution.started","tool.execution.progressed","tool.execution.completed","research.started","research.progressed","research.completed","delegation.started","delegation.updated","delegation.completed","model.route.changed","model.verification.updated","context.compacted","artifact.viewed","artifact.generated","review.mode.changed","hook.started","hook.completed","memory.citation.referenced","safety.review.started","safety.review.completed","terminal.input.sent","wait.started","wait.completed","provider.notice.recorded","session.starting","session.started","session.resuming","session.resumed","session.reconciled","session.updated","session.closed","session.failed","turn.submitted","turn.accepted","turn.started","turn.completed","turn.failed","turn.interrupted","turn.cancelled","item.started","item.delta","item.completed","item.failed","usage.reported","semantic_tool.input","semantic_tool.result","semantic_tool.reconciled","mcp_app.discovered","mcp_app.resource.resolved","mcp_app.initializing","mcp_app.ready","mcp_app.tool_input","mcp_app.tool_result","mcp_app.action.requested","mcp_app.action.resolved","mcp_app.host_context.changed","mcp_app.failed","mcp_app.teardown","runtime_request.created","runtime_request.resolved","runtime_request.expired","runtime_request.cancelled","interaction.request.proposed","interaction.request.materialized","interaction.request.rejected","interaction.response.progressed","interaction.response.resolved","interaction.response.delivered","run.attached","run.detached","run.result.proposed","run.result.accepted","run.result.rejected","attention.request.proposed","attention.request.routed","attention.request.resolved","attention.request.expired","attention.request.superseded","work.assessment.recorded","issue.status.decision.recorded","issue.status.decision.applied","issue.status.decision.rejected","issue.status.decision.superseded","run.terminal"]},"schemaVersion":{"const":1},"priority":{"enum":[0,1,2]},"emittedAt":{"type":"string","format":"date-time"},"observedAt":{"type":"string","format":"date-time"},"payload":{"type":"object","properties":{"channel":{"enum":["progress","final","summary","detail","unknown"]},"providerPhase":{"type":"string"},"providerMethod":{"type":"string"}},"additionalProperties":true},"debug":{"type":"object","additionalProperties":true}},"allOf":[{"if":{"properties":{"eventType":{"const":"plan.updated"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/plan"}}}},{"if":{"properties":{"eventType":{"enum":["tool.execution.started","tool.execution.progressed","tool.execution.completed"]}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/toolExecution"}}}},{"if":{"properties":{"eventType":{"enum":["research.started","research.progressed","research.completed"]}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/research"}}}},{"if":{"properties":{"eventType":{"enum":["delegation.started","delegation.updated","delegation.completed"]}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/delegation"}}}},{"if":{"properties":{"eventType":{"const":"model.route.changed"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/modelRoute"}}}},{"if":{"properties":{"eventType":{"const":"model.verification.updated"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/modelVerification"}}}},{"if":{"properties":{"eventType":{"const":"context.compacted"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/contextCompacted"}}}},{"if":{"properties":{"eventType":{"const":"artifact.viewed"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/artifactViewed"}}}},{"if":{"properties":{"eventType":{"const":"artifact.generated"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/artifactGenerated"}}}},{"if":{"properties":{"eventType":{"const":"review.mode.changed"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/reviewMode"}}}},{"if":{"properties":{"eventType":{"enum":["hook.started","hook.completed"]}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/hook"}}}},{"if":{"properties":{"eventType":{"const":"memory.citation.referenced"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/memoryCitation"}}}},{"if":{"properties":{"eventType":{"enum":["safety.review.started","safety.review.completed"]}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/safetyReview"}}}},{"if":{"properties":{"eventType":{"const":"terminal.input.sent"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/terminalInput"}}}},{"if":{"properties":{"eventType":{"enum":["wait.started","wait.completed"]}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/wait"}}}},{"if":{"properties":{"eventType":{"const":"provider.notice.recorded"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/providerNotice"}}}},{"if":{"properties":{"eventType":{"const":"workspace.file.referenced"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json"}}}},{"if":{"properties":{"eventType":{"enum":["workspace.change.updated","workspace.diff.recorded"]}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json"}}}},{"if":{"properties":{"eventType":{"const":"workspace.diff.recorded"}}},"then":{"properties":{"payload":{"allOf":[{"$ref":"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json"},{"type":"object","properties":{"complete":{"const":true}}}]}}}},{"if":{"properties":{"eventType":{"enum":["semantic_tool.input","mcp_app.tool_input"]}}},"then":{"properties":{"payload":{"type":"object","properties":{"semantic_tool":{"allOf":[{"$ref":"https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json"},{"type":"object","properties":{"phase":{"const":"input"}}}]}},"additionalProperties":true}}}},{"if":{"properties":{"eventType":{"enum":["semantic_tool.result","mcp_app.tool_result"]}}},"then":{"properties":{"payload":{"type":"object","properties":{"semantic_tool":{"allOf":[{"$ref":"https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json"},{"type":"object","properties":{"phase":{"const":"result"}}}]}},"additionalProperties":true}}}},{"if":{"properties":{"eventType":{"const":"semantic_tool.reconciled"}}},"then":{"properties":{"payload":{"type":"object","properties":{"semantic_tool":{"allOf":[{"$ref":"https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json"},{"type":"object","properties":{"phase":{"const":"reconciled"}}}]}},"additionalProperties":true}}}},{"if":{"properties":{"eventType":{"const":"run.terminal"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/terminal.schema.json"}}}},{"if":{"properties":{"eventType":{"const":"run.result.proposed"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/result.schema.json"}}}}],"additionalProperties":true};const schema171 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json","title":"Paperclip workspace file reference","type":"object","required":["schema","referenceId","source","path","displayName","mediaType","presentation","line","preview","previewTruncated","contentDigest"],"properties":{"schema":{"const":"paperclip.workspace.file_reference.v1"},"referenceId":{"type":"string","minLength":1,"maxLength":240},"source":{"enum":["harness_reported","runner_verified"]},"path":{"type":"string","minLength":1,"maxLength":1024,"pattern":"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"},"displayName":{"type":"string","minLength":1,"maxLength":255},"mediaType":{"type":["string","null"],"maxLength":160},"presentation":{"enum":["document","code","image","generic"]},"line":{"type":["integer","null"],"minimum":1},"preview":{"type":["string","null"],"maxLength":262144},"previewTruncated":{"type":"boolean"},"contentDigest":{"oneOf":[{"type":"null"},{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"}]}},"additionalProperties":false};const schema172 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json","title":"Paperclip workspace diff","type":"object","required":["schema","changeSetId","revision","source","complete","files","totals","patchArtifactRef"],"properties":{"schema":{"const":"paperclip.workspace.diff.v1"},"changeSetId":{"type":"string","minLength":1,"maxLength":200},"revision":{"type":"integer","minimum":1},"source":{"enum":["harness_reported","runner_verified"]},"complete":{"type":"boolean"},"files":{"type":"array","maxItems":2000,"items":{"type":"object","required":["path","operation","previousPath","additions","deletions","binary","diff"],"properties":{"path":{"type":"string","minLength":1,"maxLength":1024,"pattern":"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"},"operation":{"enum":["create","modify","delete","rename","mode_change"]},"previousPath":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":1024,"pattern":"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"}]},"additions":{"type":["integer","null"],"minimum":0},"deletions":{"type":["integer","null"],"minimum":0},"binary":{"type":"boolean"},"diff":{"type":["string","null"],"maxLength":262144}},"additionalProperties":false}},"totals":{"type":"object","required":["files","additions","deletions"],"properties":{"files":{"type":"integer","minimum":0},"additions":{"type":["integer","null"],"minimum":0},"deletions":{"type":["integer","null"],"minimum":0}},"additionalProperties":false},"patchArtifactRef":{"type":["string","null"],"maxLength":2048}},"additionalProperties":false};const schema49 = {"type":"object","description":"A complete provider-authored within-turn checklist snapshot. Consumers replace the prior snapshot with the same planId in PRP sourceSeq order; this is not Paperclip's durable Plan document.","required":["schema","planId","revision","steps","complete","syncStatus"],"properties":{"schema":{"const":"paperclip.plan.updated.v1"},"planId":{"$ref":"#/$defs/id"},"revision":{"type":"integer","minimum":1},"explanation":{"$ref":"#/$defs/nullableShortText"},"steps":{"type":"array","maxItems":256,"items":{"type":"object","required":["stepId","body","status"],"properties":{"stepId":{"$ref":"#/$defs/id"},"body":{"$ref":"#/$defs/shortText"},"status":{"enum":["pending","in_progress","completed","blocked"]}},"additionalProperties":false}},"complete":{"type":"boolean"},"syncStatus":{"description":"Legacy compatibility field. Within-turn checklist snapshots use not_applicable.","enum":["streaming","pending","synchronized","conflict","not_applicable"]},"documentRevision":{"description":"Legacy compatibility field. Within-turn checklist snapshots use null.","type":["integer","null"],"minimum":1}},"additionalProperties":false};const schema50 = {"type":"string","minLength":1,"maxLength":160,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]*$"};const schema51 = {"type":["string","null"],"maxLength":4000};const schema53 = {"type":"string","maxLength":4000};function validate60(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate60.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.planId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "planId"},message:"must have required property '"+"planId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.revision === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "revision"},message:"must have required property '"+"revision"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.steps === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "steps"},message:"must have required property '"+"steps"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.complete === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "complete"},message:"must have required property '"+"complete"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.syncStatus === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "syncStatus"},message:"must have required property '"+"syncStatus"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}for(const key0 in data){if(!((((((((key0 === "schema") || (key0 === "planId")) || (key0 === "revision")) || (key0 === "explanation")) || (key0 === "steps")) || (key0 === "complete")) || (key0 === "syncStatus")) || (key0 === "documentRevision"))){const err6 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.schema !== undefined){if("paperclip.plan.updated.v1" !== data.schema){const err7 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.plan.updated.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.planId !== undefined){let data1 = data.planId;if(typeof data1 === "string"){if(func1(data1) > 160){const err8 = {instancePath:instancePath+"/planId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(func1(data1) < 1){const err9 = {instancePath:instancePath+"/planId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(!pattern4.test(data1)){const err10 = {instancePath:instancePath+"/planId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}else {const err11 = {instancePath:instancePath+"/planId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.revision !== undefined){let data2 = data.revision;if(!(((typeof data2 == "number") && (!(data2 % 1) && !isNaN(data2))) && (isFinite(data2)))){const err12 = {instancePath:instancePath+"/revision",schemaPath:"#/properties/revision/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if((typeof data2 == "number") && (isFinite(data2))){if(data2 < 1 || isNaN(data2)){const err13 = {instancePath:instancePath+"/revision",schemaPath:"#/properties/revision/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}}if(data.explanation !== undefined){let data3 = data.explanation;if((typeof data3 !== "string") && (data3 !== null)){const err14 = {instancePath:instancePath+"/explanation",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(typeof data3 === "string"){if(func1(data3) > 4000){const err15 = {instancePath:instancePath+"/explanation",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}}if(data.steps !== undefined){let data4 = data.steps;if(Array.isArray(data4)){if(data4.length > 256){const err16 = {instancePath:instancePath+"/steps",schemaPath:"#/properties/steps/maxItems",keyword:"maxItems",params:{limit: 256},message:"must NOT have more than 256 items"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}const len0 = data4.length;for(let i0=0; i0 160){const err21 = {instancePath:instancePath+"/steps/" + i0+"/stepId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(func1(data6) < 1){const err22 = {instancePath:instancePath+"/steps/" + i0+"/stepId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}if(!pattern4.test(data6)){const err23 = {instancePath:instancePath+"/steps/" + i0+"/stepId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}else {const err24 = {instancePath:instancePath+"/steps/" + i0+"/stepId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}if(data5.body !== undefined){let data7 = data5.body;if(typeof data7 === "string"){if(func1(data7) > 4000){const err25 = {instancePath:instancePath+"/steps/" + i0+"/body",schemaPath:"#/$defs/shortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}else {const err26 = {instancePath:instancePath+"/steps/" + i0+"/body",schemaPath:"#/$defs/shortText/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}}if(data5.status !== undefined){let data8 = data5.status;if(!((((data8 === "pending") || (data8 === "in_progress")) || (data8 === "completed")) || (data8 === "blocked"))){const err27 = {instancePath:instancePath+"/steps/" + i0+"/status",schemaPath:"#/properties/steps/items/properties/status/enum",keyword:"enum",params:{allowedValues: schema49.properties.steps.items.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}}}else {const err28 = {instancePath:instancePath+"/steps/" + i0,schemaPath:"#/properties/steps/items/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}}else {const err29 = {instancePath:instancePath+"/steps",schemaPath:"#/properties/steps/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}}if(data.complete !== undefined){if(typeof data.complete !== "boolean"){const err30 = {instancePath:instancePath+"/complete",schemaPath:"#/properties/complete/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}}if(data.syncStatus !== undefined){let data10 = data.syncStatus;if(!(((((data10 === "streaming") || (data10 === "pending")) || (data10 === "synchronized")) || (data10 === "conflict")) || (data10 === "not_applicable"))){const err31 = {instancePath:instancePath+"/syncStatus",schemaPath:"#/properties/syncStatus/enum",keyword:"enum",params:{allowedValues: schema49.properties.syncStatus.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}}if(data.documentRevision !== undefined){let data11 = data.documentRevision;if((!(((typeof data11 == "number") && (!(data11 % 1) && !isNaN(data11))) && (isFinite(data11)))) && (data11 !== null)){const err32 = {instancePath:instancePath+"/documentRevision",schemaPath:"#/properties/documentRevision/type",keyword:"type",params:{type: schema49.properties.documentRevision.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}if((typeof data11 == "number") && (isFinite(data11))){if(data11 < 1 || isNaN(data11)){const err33 = {instancePath:instancePath+"/documentRevision",schemaPath:"#/properties/documentRevision/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}}}}else {const err34 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}validate60.errors = vErrors;return errors === 0;}validate60.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema54 = {"type":"object","required":["schema","executionId","transport","operation","status","output","outputBytes","outputTruncated","outputDigest"],"properties":{"schema":{"const":"paperclip.tool.execution.v1"},"executionId":{"$ref":"#/$defs/id"},"transport":{"enum":["process","mcp","dynamic","builtin"]},"operation":{"enum":["read","search","list","execute","edit","unknown"]},"name":{"$ref":"#/$defs/nullableShortText"},"target":{"$ref":"#/$defs/safePath"},"namespace":{"type":["string","null"],"maxLength":240},"readOnly":{"type":["boolean","null"]},"inputUpdated":{"type":"boolean"},"status":{"enum":["running","completed","failed","cancelled","interrupted"]},"durationMs":{"type":["integer","null"],"minimum":0},"exitCode":{"type":["integer","null"]},"progress":{"$ref":"#/$defs/nullableShortText"},"output":{"type":["string","null"],"maxLength":65536},"outputBytes":{"type":"integer","minimum":0},"outputTruncated":{"type":"boolean"},"outputDigest":{"type":["string","null"],"pattern":"^sha256:[a-f0-9]{64}$"}},"additionalProperties":false};const schema57 = {"type":["string","null"],"maxLength":4096,"pattern":"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$"};const func58 = Object.prototype.hasOwnProperty;const pattern18 = new RegExp("^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$", "u");const pattern19 = new RegExp("^sha256:[a-f0-9]{64}$", "u");function validate62(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate62.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.executionId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "executionId"},message:"must have required property '"+"executionId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.transport === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "transport"},message:"must have required property '"+"transport"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.operation === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "operation"},message:"must have required property '"+"operation"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.status === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.output === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "output"},message:"must have required property '"+"output"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.outputBytes === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "outputBytes"},message:"must have required property '"+"outputBytes"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(data.outputTruncated === undefined){const err7 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "outputTruncated"},message:"must have required property '"+"outputTruncated"+"'"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(data.outputDigest === undefined){const err8 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "outputDigest"},message:"must have required property '"+"outputDigest"+"'"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}for(const key0 in data){if(!(func58.call(schema54.properties, key0))){const err9 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}if(data.schema !== undefined){if("paperclip.tool.execution.v1" !== data.schema){const err10 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.tool.execution.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.executionId !== undefined){let data1 = data.executionId;if(typeof data1 === "string"){if(func1(data1) > 160){const err11 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(func1(data1) < 1){const err12 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(!pattern4.test(data1)){const err13 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}else {const err14 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}if(data.transport !== undefined){let data2 = data.transport;if(!((((data2 === "process") || (data2 === "mcp")) || (data2 === "dynamic")) || (data2 === "builtin"))){const err15 = {instancePath:instancePath+"/transport",schemaPath:"#/properties/transport/enum",keyword:"enum",params:{allowedValues: schema54.properties.transport.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}if(data.operation !== undefined){let data3 = data.operation;if(!((((((data3 === "read") || (data3 === "search")) || (data3 === "list")) || (data3 === "execute")) || (data3 === "edit")) || (data3 === "unknown"))){const err16 = {instancePath:instancePath+"/operation",schemaPath:"#/properties/operation/enum",keyword:"enum",params:{allowedValues: schema54.properties.operation.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}if(data.name !== undefined){let data4 = data.name;if((typeof data4 !== "string") && (data4 !== null)){const err17 = {instancePath:instancePath+"/name",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}if(typeof data4 === "string"){if(func1(data4) > 4000){const err18 = {instancePath:instancePath+"/name",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}}if(data.target !== undefined){let data5 = data.target;if((typeof data5 !== "string") && (data5 !== null)){const err19 = {instancePath:instancePath+"/target",schemaPath:"#/$defs/safePath/type",keyword:"type",params:{type: schema57.type},message:"must be string,null"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}if(typeof data5 === "string"){if(func1(data5) > 4096){const err20 = {instancePath:instancePath+"/target",schemaPath:"#/$defs/safePath/maxLength",keyword:"maxLength",params:{limit: 4096},message:"must NOT have more than 4096 characters"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}if(!pattern18.test(data5)){const err21 = {instancePath:instancePath+"/target",schemaPath:"#/$defs/safePath/pattern",keyword:"pattern",params:{pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$"},message:"must match pattern \""+"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$"+"\""};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}}}if(data.namespace !== undefined){let data6 = data.namespace;if((typeof data6 !== "string") && (data6 !== null)){const err22 = {instancePath:instancePath+"/namespace",schemaPath:"#/properties/namespace/type",keyword:"type",params:{type: schema54.properties.namespace.type},message:"must be string,null"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}if(typeof data6 === "string"){if(func1(data6) > 240){const err23 = {instancePath:instancePath+"/namespace",schemaPath:"#/properties/namespace/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}}if(data.readOnly !== undefined){let data7 = data.readOnly;if((typeof data7 !== "boolean") && (data7 !== null)){const err24 = {instancePath:instancePath+"/readOnly",schemaPath:"#/properties/readOnly/type",keyword:"type",params:{type: schema54.properties.readOnly.type},message:"must be boolean,null"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}if(data.inputUpdated !== undefined){if(typeof data.inputUpdated !== "boolean"){const err25 = {instancePath:instancePath+"/inputUpdated",schemaPath:"#/properties/inputUpdated/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}if(data.status !== undefined){let data9 = data.status;if(!(((((data9 === "running") || (data9 === "completed")) || (data9 === "failed")) || (data9 === "cancelled")) || (data9 === "interrupted"))){const err26 = {instancePath:instancePath+"/status",schemaPath:"#/properties/status/enum",keyword:"enum",params:{allowedValues: schema54.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}}if(data.durationMs !== undefined){let data10 = data.durationMs;if((!(((typeof data10 == "number") && (!(data10 % 1) && !isNaN(data10))) && (isFinite(data10)))) && (data10 !== null)){const err27 = {instancePath:instancePath+"/durationMs",schemaPath:"#/properties/durationMs/type",keyword:"type",params:{type: schema54.properties.durationMs.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if((typeof data10 == "number") && (isFinite(data10))){if(data10 < 0 || isNaN(data10)){const err28 = {instancePath:instancePath+"/durationMs",schemaPath:"#/properties/durationMs/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}}if(data.exitCode !== undefined){let data11 = data.exitCode;if((!(((typeof data11 == "number") && (!(data11 % 1) && !isNaN(data11))) && (isFinite(data11)))) && (data11 !== null)){const err29 = {instancePath:instancePath+"/exitCode",schemaPath:"#/properties/exitCode/type",keyword:"type",params:{type: schema54.properties.exitCode.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}}if(data.progress !== undefined){let data12 = data.progress;if((typeof data12 !== "string") && (data12 !== null)){const err30 = {instancePath:instancePath+"/progress",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}if(typeof data12 === "string"){if(func1(data12) > 4000){const err31 = {instancePath:instancePath+"/progress",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}}}if(data.output !== undefined){let data13 = data.output;if((typeof data13 !== "string") && (data13 !== null)){const err32 = {instancePath:instancePath+"/output",schemaPath:"#/properties/output/type",keyword:"type",params:{type: schema54.properties.output.type},message:"must be string,null"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}if(typeof data13 === "string"){if(func1(data13) > 65536){const err33 = {instancePath:instancePath+"/output",schemaPath:"#/properties/output/maxLength",keyword:"maxLength",params:{limit: 65536},message:"must NOT have more than 65536 characters"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}}}if(data.outputBytes !== undefined){let data14 = data.outputBytes;if(!(((typeof data14 == "number") && (!(data14 % 1) && !isNaN(data14))) && (isFinite(data14)))){const err34 = {instancePath:instancePath+"/outputBytes",schemaPath:"#/properties/outputBytes/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}if((typeof data14 == "number") && (isFinite(data14))){if(data14 < 0 || isNaN(data14)){const err35 = {instancePath:instancePath+"/outputBytes",schemaPath:"#/properties/outputBytes/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}}}if(data.outputTruncated !== undefined){if(typeof data.outputTruncated !== "boolean"){const err36 = {instancePath:instancePath+"/outputTruncated",schemaPath:"#/properties/outputTruncated/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}}if(data.outputDigest !== undefined){let data16 = data.outputDigest;if((typeof data16 !== "string") && (data16 !== null)){const err37 = {instancePath:instancePath+"/outputDigest",schemaPath:"#/properties/outputDigest/type",keyword:"type",params:{type: schema54.properties.outputDigest.type},message:"must be string,null"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}if(typeof data16 === "string"){if(!pattern19.test(data16)){const err38 = {instancePath:instancePath+"/outputDigest",schemaPath:"#/properties/outputDigest/pattern",keyword:"pattern",params:{pattern: "^sha256:[a-f0-9]{64}$"},message:"must match pattern \""+"^sha256:[a-f0-9]{64}$"+"\""};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}}}}else {const err39 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}validate62.errors = vErrors;return errors === 0;}validate62.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema59 = {"type":"object","required":["schema","researchId","action","status","sources"],"properties":{"schema":{"const":"paperclip.research.v1"},"researchId":{"$ref":"#/$defs/id"},"action":{"enum":["search","open_page","find_in_page","other"]},"status":{"enum":["running","completed","failed","cancelled"]},"query":{"$ref":"#/$defs/nullableShortText"},"url":{"$ref":"#/$defs/safeUrl"},"pattern":{"$ref":"#/$defs/nullableShortText"},"sources":{"type":"array","maxItems":64,"items":{"type":"object","required":["sourceId","title","url","snippet"],"properties":{"sourceId":{"$ref":"#/$defs/id"},"title":{"$ref":"#/$defs/shortText"},"url":{"$ref":"#/$defs/safeUrl"},"snippet":{"$ref":"#/$defs/nullableShortText"}},"additionalProperties":false}}},"additionalProperties":false};const schema62 = {"type":["string","null"],"maxLength":8192,"pattern":"^https?://"};const pattern21 = new RegExp("^https?://", "u");function validate64(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate64.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.researchId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "researchId"},message:"must have required property '"+"researchId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.action === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "action"},message:"must have required property '"+"action"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.status === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.sources === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sources"},message:"must have required property '"+"sources"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}for(const key0 in data){if(!((((((((key0 === "schema") || (key0 === "researchId")) || (key0 === "action")) || (key0 === "status")) || (key0 === "query")) || (key0 === "url")) || (key0 === "pattern")) || (key0 === "sources"))){const err5 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}if(data.schema !== undefined){if("paperclip.research.v1" !== data.schema){const err6 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.research.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.researchId !== undefined){let data1 = data.researchId;if(typeof data1 === "string"){if(func1(data1) > 160){const err7 = {instancePath:instancePath+"/researchId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(func1(data1) < 1){const err8 = {instancePath:instancePath+"/researchId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(!pattern4.test(data1)){const err9 = {instancePath:instancePath+"/researchId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}else {const err10 = {instancePath:instancePath+"/researchId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.action !== undefined){let data2 = data.action;if(!((((data2 === "search") || (data2 === "open_page")) || (data2 === "find_in_page")) || (data2 === "other"))){const err11 = {instancePath:instancePath+"/action",schemaPath:"#/properties/action/enum",keyword:"enum",params:{allowedValues: schema59.properties.action.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.status !== undefined){let data3 = data.status;if(!((((data3 === "running") || (data3 === "completed")) || (data3 === "failed")) || (data3 === "cancelled"))){const err12 = {instancePath:instancePath+"/status",schemaPath:"#/properties/status/enum",keyword:"enum",params:{allowedValues: schema59.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.query !== undefined){let data4 = data.query;if((typeof data4 !== "string") && (data4 !== null)){const err13 = {instancePath:instancePath+"/query",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(typeof data4 === "string"){if(func1(data4) > 4000){const err14 = {instancePath:instancePath+"/query",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}}if(data.url !== undefined){let data5 = data.url;if((typeof data5 !== "string") && (data5 !== null)){const err15 = {instancePath:instancePath+"/url",schemaPath:"#/$defs/safeUrl/type",keyword:"type",params:{type: schema62.type},message:"must be string,null"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}if(typeof data5 === "string"){if(func1(data5) > 8192){const err16 = {instancePath:instancePath+"/url",schemaPath:"#/$defs/safeUrl/maxLength",keyword:"maxLength",params:{limit: 8192},message:"must NOT have more than 8192 characters"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}if(!pattern21.test(data5)){const err17 = {instancePath:instancePath+"/url",schemaPath:"#/$defs/safeUrl/pattern",keyword:"pattern",params:{pattern: "^https?://"},message:"must match pattern \""+"^https?://"+"\""};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}}if(data.pattern !== undefined){let data6 = data.pattern;if((typeof data6 !== "string") && (data6 !== null)){const err18 = {instancePath:instancePath+"/pattern",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}if(typeof data6 === "string"){if(func1(data6) > 4000){const err19 = {instancePath:instancePath+"/pattern",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}}if(data.sources !== undefined){let data7 = data.sources;if(Array.isArray(data7)){if(data7.length > 64){const err20 = {instancePath:instancePath+"/sources",schemaPath:"#/properties/sources/maxItems",keyword:"maxItems",params:{limit: 64},message:"must NOT have more than 64 items"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}const len0 = data7.length;for(let i0=0; i0 160){const err26 = {instancePath:instancePath+"/sources/" + i0+"/sourceId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}if(func1(data9) < 1){const err27 = {instancePath:instancePath+"/sources/" + i0+"/sourceId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if(!pattern4.test(data9)){const err28 = {instancePath:instancePath+"/sources/" + i0+"/sourceId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}else {const err29 = {instancePath:instancePath+"/sources/" + i0+"/sourceId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}}if(data8.title !== undefined){let data10 = data8.title;if(typeof data10 === "string"){if(func1(data10) > 4000){const err30 = {instancePath:instancePath+"/sources/" + i0+"/title",schemaPath:"#/$defs/shortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}}else {const err31 = {instancePath:instancePath+"/sources/" + i0+"/title",schemaPath:"#/$defs/shortText/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}}if(data8.url !== undefined){let data11 = data8.url;if((typeof data11 !== "string") && (data11 !== null)){const err32 = {instancePath:instancePath+"/sources/" + i0+"/url",schemaPath:"#/$defs/safeUrl/type",keyword:"type",params:{type: schema62.type},message:"must be string,null"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}if(typeof data11 === "string"){if(func1(data11) > 8192){const err33 = {instancePath:instancePath+"/sources/" + i0+"/url",schemaPath:"#/$defs/safeUrl/maxLength",keyword:"maxLength",params:{limit: 8192},message:"must NOT have more than 8192 characters"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}if(!pattern21.test(data11)){const err34 = {instancePath:instancePath+"/sources/" + i0+"/url",schemaPath:"#/$defs/safeUrl/pattern",keyword:"pattern",params:{pattern: "^https?://"},message:"must match pattern \""+"^https?://"+"\""};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}}}if(data8.snippet !== undefined){let data12 = data8.snippet;if((typeof data12 !== "string") && (data12 !== null)){const err35 = {instancePath:instancePath+"/sources/" + i0+"/snippet",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}if(typeof data12 === "string"){if(func1(data12) > 4000){const err36 = {instancePath:instancePath+"/sources/" + i0+"/snippet",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}}}}else {const err37 = {instancePath:instancePath+"/sources/" + i0,schemaPath:"#/properties/sources/items/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}}}else {const err38 = {instancePath:instancePath+"/sources",schemaPath:"#/properties/sources/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}}}else {const err39 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}validate64.errors = vErrors;return errors === 0;}validate64.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema68 = {"type":"object","required":["schema","delegationId","action","status","children"],"properties":{"schema":{"const":"paperclip.delegation.v1"},"delegationId":{"$ref":"#/$defs/id"},"action":{"enum":["spawn","message","resume","wait","close"]},"status":{"enum":["running","waiting","completed","failed","interrupted","closed"]},"children":{"type":"array","maxItems":64,"items":{"type":"object","required":["childId","role","model","status","summary","activitySummary"],"properties":{"childId":{"$ref":"#/$defs/id"},"role":{"type":["string","null"],"maxLength":160},"model":{"type":["string","null"],"maxLength":240},"status":{"enum":["running","waiting","completed","failed","interrupted","closed"]},"summary":{"$ref":"#/$defs/nullableShortText"},"activitySummary":{"$ref":"#/$defs/nullableShortText"}},"additionalProperties":false}}},"additionalProperties":false};function validate66(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate66.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.delegationId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "delegationId"},message:"must have required property '"+"delegationId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.action === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "action"},message:"must have required property '"+"action"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.status === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.children === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "children"},message:"must have required property '"+"children"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}for(const key0 in data){if(!(((((key0 === "schema") || (key0 === "delegationId")) || (key0 === "action")) || (key0 === "status")) || (key0 === "children"))){const err5 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}if(data.schema !== undefined){if("paperclip.delegation.v1" !== data.schema){const err6 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.delegation.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.delegationId !== undefined){let data1 = data.delegationId;if(typeof data1 === "string"){if(func1(data1) > 160){const err7 = {instancePath:instancePath+"/delegationId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(func1(data1) < 1){const err8 = {instancePath:instancePath+"/delegationId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(!pattern4.test(data1)){const err9 = {instancePath:instancePath+"/delegationId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}else {const err10 = {instancePath:instancePath+"/delegationId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.action !== undefined){let data2 = data.action;if(!(((((data2 === "spawn") || (data2 === "message")) || (data2 === "resume")) || (data2 === "wait")) || (data2 === "close"))){const err11 = {instancePath:instancePath+"/action",schemaPath:"#/properties/action/enum",keyword:"enum",params:{allowedValues: schema68.properties.action.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.status !== undefined){let data3 = data.status;if(!((((((data3 === "running") || (data3 === "waiting")) || (data3 === "completed")) || (data3 === "failed")) || (data3 === "interrupted")) || (data3 === "closed"))){const err12 = {instancePath:instancePath+"/status",schemaPath:"#/properties/status/enum",keyword:"enum",params:{allowedValues: schema68.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.children !== undefined){let data4 = data.children;if(Array.isArray(data4)){if(data4.length > 64){const err13 = {instancePath:instancePath+"/children",schemaPath:"#/properties/children/maxItems",keyword:"maxItems",params:{limit: 64},message:"must NOT have more than 64 items"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}const len0 = data4.length;for(let i0=0; i0 160){const err21 = {instancePath:instancePath+"/children/" + i0+"/childId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(func1(data6) < 1){const err22 = {instancePath:instancePath+"/children/" + i0+"/childId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}if(!pattern4.test(data6)){const err23 = {instancePath:instancePath+"/children/" + i0+"/childId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}else {const err24 = {instancePath:instancePath+"/children/" + i0+"/childId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}if(data5.role !== undefined){let data7 = data5.role;if((typeof data7 !== "string") && (data7 !== null)){const err25 = {instancePath:instancePath+"/children/" + i0+"/role",schemaPath:"#/properties/children/items/properties/role/type",keyword:"type",params:{type: schema68.properties.children.items.properties.role.type},message:"must be string,null"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}if(typeof data7 === "string"){if(func1(data7) > 160){const err26 = {instancePath:instancePath+"/children/" + i0+"/role",schemaPath:"#/properties/children/items/properties/role/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}}}if(data5.model !== undefined){let data8 = data5.model;if((typeof data8 !== "string") && (data8 !== null)){const err27 = {instancePath:instancePath+"/children/" + i0+"/model",schemaPath:"#/properties/children/items/properties/model/type",keyword:"type",params:{type: schema68.properties.children.items.properties.model.type},message:"must be string,null"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if(typeof data8 === "string"){if(func1(data8) > 240){const err28 = {instancePath:instancePath+"/children/" + i0+"/model",schemaPath:"#/properties/children/items/properties/model/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}}if(data5.status !== undefined){let data9 = data5.status;if(!((((((data9 === "running") || (data9 === "waiting")) || (data9 === "completed")) || (data9 === "failed")) || (data9 === "interrupted")) || (data9 === "closed"))){const err29 = {instancePath:instancePath+"/children/" + i0+"/status",schemaPath:"#/properties/children/items/properties/status/enum",keyword:"enum",params:{allowedValues: schema68.properties.children.items.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}}if(data5.summary !== undefined){let data10 = data5.summary;if((typeof data10 !== "string") && (data10 !== null)){const err30 = {instancePath:instancePath+"/children/" + i0+"/summary",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}if(typeof data10 === "string"){if(func1(data10) > 4000){const err31 = {instancePath:instancePath+"/children/" + i0+"/summary",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}}}if(data5.activitySummary !== undefined){let data11 = data5.activitySummary;if((typeof data11 !== "string") && (data11 !== null)){const err32 = {instancePath:instancePath+"/children/" + i0+"/activitySummary",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}if(typeof data11 === "string"){if(func1(data11) > 4000){const err33 = {instancePath:instancePath+"/children/" + i0+"/activitySummary",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}}}}else {const err34 = {instancePath:instancePath+"/children/" + i0,schemaPath:"#/properties/children/items/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}}}else {const err35 = {instancePath:instancePath+"/children",schemaPath:"#/properties/children/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}}}else {const err36 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}validate66.errors = vErrors;return errors === 0;}validate66.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema73 = {"type":"object","required":["schema","routeId","provider","requestedModel","fromModel","effectiveModel","reason"],"properties":{"schema":{"const":"paperclip.model.route_changed.v1"},"routeId":{"$ref":"#/$defs/id"},"provider":{"type":"string","minLength":1,"maxLength":160},"requestedModel":{"type":"string","minLength":1,"maxLength":240},"fromModel":{"type":["string","null"],"maxLength":240},"effectiveModel":{"type":"string","minLength":1,"maxLength":240},"reason":{"$ref":"#/$defs/shortText"}},"additionalProperties":false};function validate68(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate68.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.routeId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "routeId"},message:"must have required property '"+"routeId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.provider === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "provider"},message:"must have required property '"+"provider"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.requestedModel === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "requestedModel"},message:"must have required property '"+"requestedModel"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.fromModel === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "fromModel"},message:"must have required property '"+"fromModel"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.effectiveModel === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "effectiveModel"},message:"must have required property '"+"effectiveModel"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.reason === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reason"},message:"must have required property '"+"reason"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}for(const key0 in data){if(!(((((((key0 === "schema") || (key0 === "routeId")) || (key0 === "provider")) || (key0 === "requestedModel")) || (key0 === "fromModel")) || (key0 === "effectiveModel")) || (key0 === "reason"))){const err7 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.schema !== undefined){if("paperclip.model.route_changed.v1" !== data.schema){const err8 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.model.route_changed.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}if(data.routeId !== undefined){let data1 = data.routeId;if(typeof data1 === "string"){if(func1(data1) > 160){const err9 = {instancePath:instancePath+"/routeId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(func1(data1) < 1){const err10 = {instancePath:instancePath+"/routeId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(!pattern4.test(data1)){const err11 = {instancePath:instancePath+"/routeId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}else {const err12 = {instancePath:instancePath+"/routeId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.provider !== undefined){let data2 = data.provider;if(typeof data2 === "string"){if(func1(data2) > 160){const err13 = {instancePath:instancePath+"/provider",schemaPath:"#/properties/provider/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(func1(data2) < 1){const err14 = {instancePath:instancePath+"/provider",schemaPath:"#/properties/provider/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}else {const err15 = {instancePath:instancePath+"/provider",schemaPath:"#/properties/provider/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}if(data.requestedModel !== undefined){let data3 = data.requestedModel;if(typeof data3 === "string"){if(func1(data3) > 240){const err16 = {instancePath:instancePath+"/requestedModel",schemaPath:"#/properties/requestedModel/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}if(func1(data3) < 1){const err17 = {instancePath:instancePath+"/requestedModel",schemaPath:"#/properties/requestedModel/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}else {const err18 = {instancePath:instancePath+"/requestedModel",schemaPath:"#/properties/requestedModel/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}if(data.fromModel !== undefined){let data4 = data.fromModel;if((typeof data4 !== "string") && (data4 !== null)){const err19 = {instancePath:instancePath+"/fromModel",schemaPath:"#/properties/fromModel/type",keyword:"type",params:{type: schema73.properties.fromModel.type},message:"must be string,null"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}if(typeof data4 === "string"){if(func1(data4) > 240){const err20 = {instancePath:instancePath+"/fromModel",schemaPath:"#/properties/fromModel/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}}if(data.effectiveModel !== undefined){let data5 = data.effectiveModel;if(typeof data5 === "string"){if(func1(data5) > 240){const err21 = {instancePath:instancePath+"/effectiveModel",schemaPath:"#/properties/effectiveModel/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(func1(data5) < 1){const err22 = {instancePath:instancePath+"/effectiveModel",schemaPath:"#/properties/effectiveModel/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}else {const err23 = {instancePath:instancePath+"/effectiveModel",schemaPath:"#/properties/effectiveModel/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}if(data.reason !== undefined){let data6 = data.reason;if(typeof data6 === "string"){if(func1(data6) > 4000){const err24 = {instancePath:instancePath+"/reason",schemaPath:"#/$defs/shortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}else {const err25 = {instancePath:instancePath+"/reason",schemaPath:"#/$defs/shortText/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}}else {const err26 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}validate68.errors = vErrors;return errors === 0;}validate68.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema76 = {"type":"object","required":["schema","verificationId","status","classes","buffering","summary"],"properties":{"schema":{"const":"paperclip.model.verification.v1"},"verificationId":{"$ref":"#/$defs/id"},"status":{"enum":["running","completed","failed"]},"classes":{"type":"array","maxItems":32,"items":{"type":"string","maxLength":160}},"buffering":{"type":"boolean"},"summary":{"$ref":"#/$defs/nullableShortText"}},"additionalProperties":false};function validate70(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate70.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.verificationId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "verificationId"},message:"must have required property '"+"verificationId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.status === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.classes === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "classes"},message:"must have required property '"+"classes"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.buffering === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "buffering"},message:"must have required property '"+"buffering"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.summary === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "summary"},message:"must have required property '"+"summary"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}for(const key0 in data){if(!((((((key0 === "schema") || (key0 === "verificationId")) || (key0 === "status")) || (key0 === "classes")) || (key0 === "buffering")) || (key0 === "summary"))){const err6 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.schema !== undefined){if("paperclip.model.verification.v1" !== data.schema){const err7 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.model.verification.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.verificationId !== undefined){let data1 = data.verificationId;if(typeof data1 === "string"){if(func1(data1) > 160){const err8 = {instancePath:instancePath+"/verificationId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(func1(data1) < 1){const err9 = {instancePath:instancePath+"/verificationId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(!pattern4.test(data1)){const err10 = {instancePath:instancePath+"/verificationId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}else {const err11 = {instancePath:instancePath+"/verificationId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.status !== undefined){let data2 = data.status;if(!(((data2 === "running") || (data2 === "completed")) || (data2 === "failed"))){const err12 = {instancePath:instancePath+"/status",schemaPath:"#/properties/status/enum",keyword:"enum",params:{allowedValues: schema76.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.classes !== undefined){let data3 = data.classes;if(Array.isArray(data3)){if(data3.length > 32){const err13 = {instancePath:instancePath+"/classes",schemaPath:"#/properties/classes/maxItems",keyword:"maxItems",params:{limit: 32},message:"must NOT have more than 32 items"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}const len0 = data3.length;for(let i0=0; i0 160){const err14 = {instancePath:instancePath+"/classes/" + i0,schemaPath:"#/properties/classes/items/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}else {const err15 = {instancePath:instancePath+"/classes/" + i0,schemaPath:"#/properties/classes/items/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}}else {const err16 = {instancePath:instancePath+"/classes",schemaPath:"#/properties/classes/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}if(data.buffering !== undefined){if(typeof data.buffering !== "boolean"){const err17 = {instancePath:instancePath+"/buffering",schemaPath:"#/properties/buffering/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}if(data.summary !== undefined){let data6 = data.summary;if((typeof data6 !== "string") && (data6 !== null)){const err18 = {instancePath:instancePath+"/summary",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}if(typeof data6 === "string"){if(func1(data6) > 4000){const err19 = {instancePath:instancePath+"/summary",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}}}else {const err20 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}validate70.errors = vErrors;return errors === 0;}validate70.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema79 = {"type":"object","required":["schema","compactionId","reason","preTokens","postTokens","sameSession"],"properties":{"schema":{"const":"paperclip.context.compacted.v1"},"compactionId":{"$ref":"#/$defs/id"},"reason":{"enum":["context_window","manual","provider","unknown"]},"preTokens":{"type":["integer","null"],"minimum":0},"postTokens":{"type":["integer","null"],"minimum":0},"sameSession":{"type":"boolean"}},"additionalProperties":false};function validate72(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate72.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.compactionId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "compactionId"},message:"must have required property '"+"compactionId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.reason === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reason"},message:"must have required property '"+"reason"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.preTokens === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "preTokens"},message:"must have required property '"+"preTokens"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.postTokens === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "postTokens"},message:"must have required property '"+"postTokens"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.sameSession === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sameSession"},message:"must have required property '"+"sameSession"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}for(const key0 in data){if(!((((((key0 === "schema") || (key0 === "compactionId")) || (key0 === "reason")) || (key0 === "preTokens")) || (key0 === "postTokens")) || (key0 === "sameSession"))){const err6 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.schema !== undefined){if("paperclip.context.compacted.v1" !== data.schema){const err7 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.context.compacted.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.compactionId !== undefined){let data1 = data.compactionId;if(typeof data1 === "string"){if(func1(data1) > 160){const err8 = {instancePath:instancePath+"/compactionId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(func1(data1) < 1){const err9 = {instancePath:instancePath+"/compactionId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(!pattern4.test(data1)){const err10 = {instancePath:instancePath+"/compactionId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}else {const err11 = {instancePath:instancePath+"/compactionId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.reason !== undefined){let data2 = data.reason;if(!((((data2 === "context_window") || (data2 === "manual")) || (data2 === "provider")) || (data2 === "unknown"))){const err12 = {instancePath:instancePath+"/reason",schemaPath:"#/properties/reason/enum",keyword:"enum",params:{allowedValues: schema79.properties.reason.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.preTokens !== undefined){let data3 = data.preTokens;if((!(((typeof data3 == "number") && (!(data3 % 1) && !isNaN(data3))) && (isFinite(data3)))) && (data3 !== null)){const err13 = {instancePath:instancePath+"/preTokens",schemaPath:"#/properties/preTokens/type",keyword:"type",params:{type: schema79.properties.preTokens.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if((typeof data3 == "number") && (isFinite(data3))){if(data3 < 0 || isNaN(data3)){const err14 = {instancePath:instancePath+"/preTokens",schemaPath:"#/properties/preTokens/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}}if(data.postTokens !== undefined){let data4 = data.postTokens;if((!(((typeof data4 == "number") && (!(data4 % 1) && !isNaN(data4))) && (isFinite(data4)))) && (data4 !== null)){const err15 = {instancePath:instancePath+"/postTokens",schemaPath:"#/properties/postTokens/type",keyword:"type",params:{type: schema79.properties.postTokens.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}if((typeof data4 == "number") && (isFinite(data4))){if(data4 < 0 || isNaN(data4)){const err16 = {instancePath:instancePath+"/postTokens",schemaPath:"#/properties/postTokens/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}}if(data.sameSession !== undefined){if(typeof data.sameSession !== "boolean"){const err17 = {instancePath:instancePath+"/sameSession",schemaPath:"#/properties/sameSession/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}}else {const err18 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}validate72.errors = vErrors;return errors === 0;}validate72.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema81 = {"type":"object","required":["schema","artifactId","reference","mediaType","title"],"properties":{"schema":{"const":"paperclip.artifact.viewed.v1"},"artifactId":{"$ref":"#/$defs/id"},"reference":{"$ref":"#/$defs/safePath"},"mediaType":{"type":["string","null"],"maxLength":160},"title":{"$ref":"#/$defs/nullableShortText"}},"additionalProperties":false};function validate74(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate74.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.artifactId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "artifactId"},message:"must have required property '"+"artifactId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.reference === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reference"},message:"must have required property '"+"reference"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.mediaType === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "mediaType"},message:"must have required property '"+"mediaType"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.title === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "title"},message:"must have required property '"+"title"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}for(const key0 in data){if(!(((((key0 === "schema") || (key0 === "artifactId")) || (key0 === "reference")) || (key0 === "mediaType")) || (key0 === "title"))){const err5 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}if(data.schema !== undefined){if("paperclip.artifact.viewed.v1" !== data.schema){const err6 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.artifact.viewed.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.artifactId !== undefined){let data1 = data.artifactId;if(typeof data1 === "string"){if(func1(data1) > 160){const err7 = {instancePath:instancePath+"/artifactId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(func1(data1) < 1){const err8 = {instancePath:instancePath+"/artifactId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(!pattern4.test(data1)){const err9 = {instancePath:instancePath+"/artifactId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}else {const err10 = {instancePath:instancePath+"/artifactId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.reference !== undefined){let data2 = data.reference;if((typeof data2 !== "string") && (data2 !== null)){const err11 = {instancePath:instancePath+"/reference",schemaPath:"#/$defs/safePath/type",keyword:"type",params:{type: schema57.type},message:"must be string,null"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(typeof data2 === "string"){if(func1(data2) > 4096){const err12 = {instancePath:instancePath+"/reference",schemaPath:"#/$defs/safePath/maxLength",keyword:"maxLength",params:{limit: 4096},message:"must NOT have more than 4096 characters"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(!pattern18.test(data2)){const err13 = {instancePath:instancePath+"/reference",schemaPath:"#/$defs/safePath/pattern",keyword:"pattern",params:{pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$"},message:"must match pattern \""+"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$"+"\""};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}}if(data.mediaType !== undefined){let data3 = data.mediaType;if((typeof data3 !== "string") && (data3 !== null)){const err14 = {instancePath:instancePath+"/mediaType",schemaPath:"#/properties/mediaType/type",keyword:"type",params:{type: schema81.properties.mediaType.type},message:"must be string,null"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(typeof data3 === "string"){if(func1(data3) > 160){const err15 = {instancePath:instancePath+"/mediaType",schemaPath:"#/properties/mediaType/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}}if(data.title !== undefined){let data4 = data.title;if((typeof data4 !== "string") && (data4 !== null)){const err16 = {instancePath:instancePath+"/title",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}if(typeof data4 === "string"){if(func1(data4) > 4000){const err17 = {instancePath:instancePath+"/title",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}}}else {const err18 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}validate74.errors = vErrors;return errors === 0;}validate74.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema85 = {"type":"object","required":["schema","artifactId","status","reference","mediaType","registered","failure"],"properties":{"schema":{"const":"paperclip.artifact.generated.v1"},"artifactId":{"$ref":"#/$defs/id"},"status":{"enum":["running","completed","failed"]},"reference":{"$ref":"#/$defs/safePath"},"mediaType":{"type":["string","null"],"maxLength":160},"registered":{"type":"boolean"},"transparentBackground":{"type":["boolean","null"]},"failure":{"$ref":"#/$defs/nullableShortText"}},"additionalProperties":false};function validate76(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate76.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.artifactId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "artifactId"},message:"must have required property '"+"artifactId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.status === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.reference === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reference"},message:"must have required property '"+"reference"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.mediaType === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "mediaType"},message:"must have required property '"+"mediaType"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.registered === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "registered"},message:"must have required property '"+"registered"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.failure === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "failure"},message:"must have required property '"+"failure"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}for(const key0 in data){if(!((((((((key0 === "schema") || (key0 === "artifactId")) || (key0 === "status")) || (key0 === "reference")) || (key0 === "mediaType")) || (key0 === "registered")) || (key0 === "transparentBackground")) || (key0 === "failure"))){const err7 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.schema !== undefined){if("paperclip.artifact.generated.v1" !== data.schema){const err8 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.artifact.generated.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}if(data.artifactId !== undefined){let data1 = data.artifactId;if(typeof data1 === "string"){if(func1(data1) > 160){const err9 = {instancePath:instancePath+"/artifactId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(func1(data1) < 1){const err10 = {instancePath:instancePath+"/artifactId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(!pattern4.test(data1)){const err11 = {instancePath:instancePath+"/artifactId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}else {const err12 = {instancePath:instancePath+"/artifactId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.status !== undefined){let data2 = data.status;if(!(((data2 === "running") || (data2 === "completed")) || (data2 === "failed"))){const err13 = {instancePath:instancePath+"/status",schemaPath:"#/properties/status/enum",keyword:"enum",params:{allowedValues: schema85.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}if(data.reference !== undefined){let data3 = data.reference;if((typeof data3 !== "string") && (data3 !== null)){const err14 = {instancePath:instancePath+"/reference",schemaPath:"#/$defs/safePath/type",keyword:"type",params:{type: schema57.type},message:"must be string,null"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(typeof data3 === "string"){if(func1(data3) > 4096){const err15 = {instancePath:instancePath+"/reference",schemaPath:"#/$defs/safePath/maxLength",keyword:"maxLength",params:{limit: 4096},message:"must NOT have more than 4096 characters"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}if(!pattern18.test(data3)){const err16 = {instancePath:instancePath+"/reference",schemaPath:"#/$defs/safePath/pattern",keyword:"pattern",params:{pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$"},message:"must match pattern \""+"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$"+"\""};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}}if(data.mediaType !== undefined){let data4 = data.mediaType;if((typeof data4 !== "string") && (data4 !== null)){const err17 = {instancePath:instancePath+"/mediaType",schemaPath:"#/properties/mediaType/type",keyword:"type",params:{type: schema85.properties.mediaType.type},message:"must be string,null"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}if(typeof data4 === "string"){if(func1(data4) > 160){const err18 = {instancePath:instancePath+"/mediaType",schemaPath:"#/properties/mediaType/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}}if(data.registered !== undefined){if(typeof data.registered !== "boolean"){const err19 = {instancePath:instancePath+"/registered",schemaPath:"#/properties/registered/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}if(data.transparentBackground !== undefined){let data6 = data.transparentBackground;if((typeof data6 !== "boolean") && (data6 !== null)){const err20 = {instancePath:instancePath+"/transparentBackground",schemaPath:"#/properties/transparentBackground/type",keyword:"type",params:{type: schema85.properties.transparentBackground.type},message:"must be boolean,null"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}if(data.failure !== undefined){let data7 = data.failure;if((typeof data7 !== "string") && (data7 !== null)){const err21 = {instancePath:instancePath+"/failure",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(typeof data7 === "string"){if(func1(data7) > 4000){const err22 = {instancePath:instancePath+"/failure",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}}}else {const err23 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}validate76.errors = vErrors;return errors === 0;}validate76.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema89 = {"type":"object","required":["schema","reviewId","state","scope"],"properties":{"schema":{"const":"paperclip.review.mode_changed.v1"},"reviewId":{"$ref":"#/$defs/id"},"state":{"enum":["entered","exited"]},"scope":{"$ref":"#/$defs/nullableShortText"}},"additionalProperties":false};function validate78(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate78.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.reviewId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reviewId"},message:"must have required property '"+"reviewId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.state === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "state"},message:"must have required property '"+"state"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.scope === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "scope"},message:"must have required property '"+"scope"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}for(const key0 in data){if(!((((key0 === "schema") || (key0 === "reviewId")) || (key0 === "state")) || (key0 === "scope"))){const err4 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}}if(data.schema !== undefined){if("paperclip.review.mode_changed.v1" !== data.schema){const err5 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.review.mode_changed.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}if(data.reviewId !== undefined){let data1 = data.reviewId;if(typeof data1 === "string"){if(func1(data1) > 160){const err6 = {instancePath:instancePath+"/reviewId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(func1(data1) < 1){const err7 = {instancePath:instancePath+"/reviewId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(!pattern4.test(data1)){const err8 = {instancePath:instancePath+"/reviewId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}else {const err9 = {instancePath:instancePath+"/reviewId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}if(data.state !== undefined){let data2 = data.state;if(!((data2 === "entered") || (data2 === "exited"))){const err10 = {instancePath:instancePath+"/state",schemaPath:"#/properties/state/enum",keyword:"enum",params:{allowedValues: schema89.properties.state.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.scope !== undefined){let data3 = data.scope;if((typeof data3 !== "string") && (data3 !== null)){const err11 = {instancePath:instancePath+"/scope",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(typeof data3 === "string"){if(func1(data3) > 4000){const err12 = {instancePath:instancePath+"/scope",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}}}else {const err13 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}validate78.errors = vErrors;return errors === 0;}validate78.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema92 = {"type":"object","required":["schema","hookId","event","scope","status","blocking","durationMs","summary"],"properties":{"schema":{"const":"paperclip.hook.v1"},"hookId":{"$ref":"#/$defs/id"},"event":{"type":"string","minLength":1,"maxLength":160},"scope":{"type":"string","minLength":1,"maxLength":160},"status":{"enum":["running","completed","failed","cancelled"]},"blocking":{"type":"boolean"},"durationMs":{"type":["integer","null"],"minimum":0},"summary":{"$ref":"#/$defs/nullableShortText"}},"additionalProperties":false};function validate80(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate80.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.hookId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "hookId"},message:"must have required property '"+"hookId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.event === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "event"},message:"must have required property '"+"event"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.scope === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "scope"},message:"must have required property '"+"scope"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.status === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.blocking === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "blocking"},message:"must have required property '"+"blocking"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.durationMs === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "durationMs"},message:"must have required property '"+"durationMs"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(data.summary === undefined){const err7 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "summary"},message:"must have required property '"+"summary"+"'"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}for(const key0 in data){if(!((((((((key0 === "schema") || (key0 === "hookId")) || (key0 === "event")) || (key0 === "scope")) || (key0 === "status")) || (key0 === "blocking")) || (key0 === "durationMs")) || (key0 === "summary"))){const err8 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}if(data.schema !== undefined){if("paperclip.hook.v1" !== data.schema){const err9 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.hook.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}if(data.hookId !== undefined){let data1 = data.hookId;if(typeof data1 === "string"){if(func1(data1) > 160){const err10 = {instancePath:instancePath+"/hookId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(func1(data1) < 1){const err11 = {instancePath:instancePath+"/hookId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(!pattern4.test(data1)){const err12 = {instancePath:instancePath+"/hookId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}else {const err13 = {instancePath:instancePath+"/hookId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}if(data.event !== undefined){let data2 = data.event;if(typeof data2 === "string"){if(func1(data2) > 160){const err14 = {instancePath:instancePath+"/event",schemaPath:"#/properties/event/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(func1(data2) < 1){const err15 = {instancePath:instancePath+"/event",schemaPath:"#/properties/event/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}else {const err16 = {instancePath:instancePath+"/event",schemaPath:"#/properties/event/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}if(data.scope !== undefined){let data3 = data.scope;if(typeof data3 === "string"){if(func1(data3) > 160){const err17 = {instancePath:instancePath+"/scope",schemaPath:"#/properties/scope/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}if(func1(data3) < 1){const err18 = {instancePath:instancePath+"/scope",schemaPath:"#/properties/scope/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}else {const err19 = {instancePath:instancePath+"/scope",schemaPath:"#/properties/scope/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}if(data.status !== undefined){let data4 = data.status;if(!((((data4 === "running") || (data4 === "completed")) || (data4 === "failed")) || (data4 === "cancelled"))){const err20 = {instancePath:instancePath+"/status",schemaPath:"#/properties/status/enum",keyword:"enum",params:{allowedValues: schema92.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}if(data.blocking !== undefined){if(typeof data.blocking !== "boolean"){const err21 = {instancePath:instancePath+"/blocking",schemaPath:"#/properties/blocking/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}}if(data.durationMs !== undefined){let data6 = data.durationMs;if((!(((typeof data6 == "number") && (!(data6 % 1) && !isNaN(data6))) && (isFinite(data6)))) && (data6 !== null)){const err22 = {instancePath:instancePath+"/durationMs",schemaPath:"#/properties/durationMs/type",keyword:"type",params:{type: schema92.properties.durationMs.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}if((typeof data6 == "number") && (isFinite(data6))){if(data6 < 0 || isNaN(data6)){const err23 = {instancePath:instancePath+"/durationMs",schemaPath:"#/properties/durationMs/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}}if(data.summary !== undefined){let data7 = data.summary;if((typeof data7 !== "string") && (data7 !== null)){const err24 = {instancePath:instancePath+"/summary",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}if(typeof data7 === "string"){if(func1(data7) > 4000){const err25 = {instancePath:instancePath+"/summary",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}}}else {const err26 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}validate80.errors = vErrors;return errors === 0;}validate80.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema95 = {"type":"object","required":["schema","citationId","messageItemId","label","available","reference"],"properties":{"schema":{"const":"paperclip.memory.citation.v1"},"citationId":{"$ref":"#/$defs/id"},"messageItemId":{"$ref":"#/$defs/id"},"label":{"$ref":"#/$defs/shortText"},"available":{"type":"boolean"},"reference":{"type":["string","null"],"maxLength":4096}},"additionalProperties":false};function validate82(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate82.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.citationId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "citationId"},message:"must have required property '"+"citationId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.messageItemId === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "messageItemId"},message:"must have required property '"+"messageItemId"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.label === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "label"},message:"must have required property '"+"label"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.available === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "available"},message:"must have required property '"+"available"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.reference === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reference"},message:"must have required property '"+"reference"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}for(const key0 in data){if(!((((((key0 === "schema") || (key0 === "citationId")) || (key0 === "messageItemId")) || (key0 === "label")) || (key0 === "available")) || (key0 === "reference"))){const err6 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.schema !== undefined){if("paperclip.memory.citation.v1" !== data.schema){const err7 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.memory.citation.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.citationId !== undefined){let data1 = data.citationId;if(typeof data1 === "string"){if(func1(data1) > 160){const err8 = {instancePath:instancePath+"/citationId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(func1(data1) < 1){const err9 = {instancePath:instancePath+"/citationId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(!pattern4.test(data1)){const err10 = {instancePath:instancePath+"/citationId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}else {const err11 = {instancePath:instancePath+"/citationId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.messageItemId !== undefined){let data2 = data.messageItemId;if(typeof data2 === "string"){if(func1(data2) > 160){const err12 = {instancePath:instancePath+"/messageItemId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(func1(data2) < 1){const err13 = {instancePath:instancePath+"/messageItemId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(!pattern4.test(data2)){const err14 = {instancePath:instancePath+"/messageItemId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}else {const err15 = {instancePath:instancePath+"/messageItemId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}if(data.label !== undefined){let data3 = data.label;if(typeof data3 === "string"){if(func1(data3) > 4000){const err16 = {instancePath:instancePath+"/label",schemaPath:"#/$defs/shortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}else {const err17 = {instancePath:instancePath+"/label",schemaPath:"#/$defs/shortText/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}if(data.available !== undefined){if(typeof data.available !== "boolean"){const err18 = {instancePath:instancePath+"/available",schemaPath:"#/properties/available/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}if(data.reference !== undefined){let data5 = data.reference;if((typeof data5 !== "string") && (data5 !== null)){const err19 = {instancePath:instancePath+"/reference",schemaPath:"#/properties/reference/type",keyword:"type",params:{type: schema95.properties.reference.type},message:"must be string,null"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}if(typeof data5 === "string"){if(func1(data5) > 4096){const err20 = {instancePath:instancePath+"/reference",schemaPath:"#/properties/reference/maxLength",keyword:"maxLength",params:{limit: 4096},message:"must NOT have more than 4096 characters"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}}}else {const err21 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}validate82.errors = vErrors;return errors === 0;}validate82.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema99 = {"type":"object","required":["schema","reviewId","targetExecutionId","status","decision","summary"],"properties":{"schema":{"const":"paperclip.safety.review.v1"},"reviewId":{"$ref":"#/$defs/id"},"targetExecutionId":{"anyOf":[{"$ref":"#/$defs/id"},{"type":"null"}]},"status":{"enum":["running","completed","failed"]},"decision":{"enum":["pending","allowed","denied","user_required","unknown"]},"summary":{"$ref":"#/$defs/nullableShortText"}},"additionalProperties":false};function validate84(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate84.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.reviewId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reviewId"},message:"must have required property '"+"reviewId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.targetExecutionId === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "targetExecutionId"},message:"must have required property '"+"targetExecutionId"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.status === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.decision === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "decision"},message:"must have required property '"+"decision"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.summary === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "summary"},message:"must have required property '"+"summary"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}for(const key0 in data){if(!((((((key0 === "schema") || (key0 === "reviewId")) || (key0 === "targetExecutionId")) || (key0 === "status")) || (key0 === "decision")) || (key0 === "summary"))){const err6 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.schema !== undefined){if("paperclip.safety.review.v1" !== data.schema){const err7 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.safety.review.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.reviewId !== undefined){let data1 = data.reviewId;if(typeof data1 === "string"){if(func1(data1) > 160){const err8 = {instancePath:instancePath+"/reviewId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(func1(data1) < 1){const err9 = {instancePath:instancePath+"/reviewId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(!pattern4.test(data1)){const err10 = {instancePath:instancePath+"/reviewId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}else {const err11 = {instancePath:instancePath+"/reviewId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.targetExecutionId !== undefined){let data2 = data.targetExecutionId;const _errs7 = errors;let valid2 = false;const _errs8 = errors;if(typeof data2 === "string"){if(func1(data2) > 160){const err12 = {instancePath:instancePath+"/targetExecutionId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(func1(data2) < 1){const err13 = {instancePath:instancePath+"/targetExecutionId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(!pattern4.test(data2)){const err14 = {instancePath:instancePath+"/targetExecutionId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}else {const err15 = {instancePath:instancePath+"/targetExecutionId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}var _valid0 = _errs8 === errors;valid2 = valid2 || _valid0;const _errs11 = errors;if(data2 !== null){const err16 = {instancePath:instancePath+"/targetExecutionId",schemaPath:"#/properties/targetExecutionId/anyOf/1/type",keyword:"type",params:{type: "null"},message:"must be null"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}var _valid0 = _errs11 === errors;valid2 = valid2 || _valid0;if(!valid2){const err17 = {instancePath:instancePath+"/targetExecutionId",schemaPath:"#/properties/targetExecutionId/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}else {errors = _errs7;if(vErrors !== null){if(_errs7){vErrors.length = _errs7;}else {vErrors = null;}}}}if(data.status !== undefined){let data3 = data.status;if(!(((data3 === "running") || (data3 === "completed")) || (data3 === "failed"))){const err18 = {instancePath:instancePath+"/status",schemaPath:"#/properties/status/enum",keyword:"enum",params:{allowedValues: schema99.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}if(data.decision !== undefined){let data4 = data.decision;if(!(((((data4 === "pending") || (data4 === "allowed")) || (data4 === "denied")) || (data4 === "user_required")) || (data4 === "unknown"))){const err19 = {instancePath:instancePath+"/decision",schemaPath:"#/properties/decision/enum",keyword:"enum",params:{allowedValues: schema99.properties.decision.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}if(data.summary !== undefined){let data5 = data.summary;if((typeof data5 !== "string") && (data5 !== null)){const err20 = {instancePath:instancePath+"/summary",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}if(typeof data5 === "string"){if(func1(data5) > 4000){const err21 = {instancePath:instancePath+"/summary",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}}}}else {const err22 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}validate84.errors = vErrors;return errors === 0;}validate84.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema103 = {"type":"object","required":["schema","executionId","origin","inputClass","byteCount"],"properties":{"schema":{"const":"paperclip.terminal.input_sent.v1"},"executionId":{"$ref":"#/$defs/id"},"origin":{"enum":["agent","user","system"]},"inputClass":{"enum":["text","control","eof"]},"byteCount":{"type":"integer","minimum":0}},"additionalProperties":false};function validate86(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate86.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.executionId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "executionId"},message:"must have required property '"+"executionId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.origin === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "origin"},message:"must have required property '"+"origin"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.inputClass === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "inputClass"},message:"must have required property '"+"inputClass"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.byteCount === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "byteCount"},message:"must have required property '"+"byteCount"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}for(const key0 in data){if(!(((((key0 === "schema") || (key0 === "executionId")) || (key0 === "origin")) || (key0 === "inputClass")) || (key0 === "byteCount"))){const err5 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}if(data.schema !== undefined){if("paperclip.terminal.input_sent.v1" !== data.schema){const err6 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.terminal.input_sent.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.executionId !== undefined){let data1 = data.executionId;if(typeof data1 === "string"){if(func1(data1) > 160){const err7 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(func1(data1) < 1){const err8 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(!pattern4.test(data1)){const err9 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}else {const err10 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.origin !== undefined){let data2 = data.origin;if(!(((data2 === "agent") || (data2 === "user")) || (data2 === "system"))){const err11 = {instancePath:instancePath+"/origin",schemaPath:"#/properties/origin/enum",keyword:"enum",params:{allowedValues: schema103.properties.origin.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.inputClass !== undefined){let data3 = data.inputClass;if(!(((data3 === "text") || (data3 === "control")) || (data3 === "eof"))){const err12 = {instancePath:instancePath+"/inputClass",schemaPath:"#/properties/inputClass/enum",keyword:"enum",params:{allowedValues: schema103.properties.inputClass.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.byteCount !== undefined){let data4 = data.byteCount;if(!(((typeof data4 == "number") && (!(data4 % 1) && !isNaN(data4))) && (isFinite(data4)))){const err13 = {instancePath:instancePath+"/byteCount",schemaPath:"#/properties/byteCount/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if((typeof data4 == "number") && (isFinite(data4))){if(data4 < 0 || isNaN(data4)){const err14 = {instancePath:instancePath+"/byteCount",schemaPath:"#/properties/byteCount/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}}}else {const err15 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}validate86.errors = vErrors;return errors === 0;}validate86.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema105 = {"type":"object","required":["schema","waitId","reason","status","plannedDurationMs","elapsedDurationMs"],"properties":{"schema":{"const":"paperclip.wait.v1"},"waitId":{"$ref":"#/$defs/id"},"reason":{"enum":["timer","provider","rate_limit","unknown"]},"status":{"enum":["running","completed","interrupted","cancelled"]},"plannedDurationMs":{"type":["integer","null"],"minimum":0},"elapsedDurationMs":{"type":["integer","null"],"minimum":0}},"additionalProperties":false};function validate88(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate88.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.waitId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "waitId"},message:"must have required property '"+"waitId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.reason === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reason"},message:"must have required property '"+"reason"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.status === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.plannedDurationMs === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "plannedDurationMs"},message:"must have required property '"+"plannedDurationMs"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.elapsedDurationMs === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "elapsedDurationMs"},message:"must have required property '"+"elapsedDurationMs"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}for(const key0 in data){if(!((((((key0 === "schema") || (key0 === "waitId")) || (key0 === "reason")) || (key0 === "status")) || (key0 === "plannedDurationMs")) || (key0 === "elapsedDurationMs"))){const err6 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.schema !== undefined){if("paperclip.wait.v1" !== data.schema){const err7 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.wait.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.waitId !== undefined){let data1 = data.waitId;if(typeof data1 === "string"){if(func1(data1) > 160){const err8 = {instancePath:instancePath+"/waitId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(func1(data1) < 1){const err9 = {instancePath:instancePath+"/waitId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(!pattern4.test(data1)){const err10 = {instancePath:instancePath+"/waitId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}else {const err11 = {instancePath:instancePath+"/waitId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.reason !== undefined){let data2 = data.reason;if(!((((data2 === "timer") || (data2 === "provider")) || (data2 === "rate_limit")) || (data2 === "unknown"))){const err12 = {instancePath:instancePath+"/reason",schemaPath:"#/properties/reason/enum",keyword:"enum",params:{allowedValues: schema105.properties.reason.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.status !== undefined){let data3 = data.status;if(!((((data3 === "running") || (data3 === "completed")) || (data3 === "interrupted")) || (data3 === "cancelled"))){const err13 = {instancePath:instancePath+"/status",schemaPath:"#/properties/status/enum",keyword:"enum",params:{allowedValues: schema105.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}if(data.plannedDurationMs !== undefined){let data4 = data.plannedDurationMs;if((!(((typeof data4 == "number") && (!(data4 % 1) && !isNaN(data4))) && (isFinite(data4)))) && (data4 !== null)){const err14 = {instancePath:instancePath+"/plannedDurationMs",schemaPath:"#/properties/plannedDurationMs/type",keyword:"type",params:{type: schema105.properties.plannedDurationMs.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if((typeof data4 == "number") && (isFinite(data4))){if(data4 < 0 || isNaN(data4)){const err15 = {instancePath:instancePath+"/plannedDurationMs",schemaPath:"#/properties/plannedDurationMs/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}}if(data.elapsedDurationMs !== undefined){let data5 = data.elapsedDurationMs;if((!(((typeof data5 == "number") && (!(data5 % 1) && !isNaN(data5))) && (isFinite(data5)))) && (data5 !== null)){const err16 = {instancePath:instancePath+"/elapsedDurationMs",schemaPath:"#/properties/elapsedDurationMs/type",keyword:"type",params:{type: schema105.properties.elapsedDurationMs.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}if((typeof data5 == "number") && (isFinite(data5))){if(data5 < 0 || isNaN(data5)){const err17 = {instancePath:instancePath+"/elapsedDurationMs",schemaPath:"#/properties/elapsedDurationMs/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}}}else {const err18 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}validate88.errors = vErrors;return errors === 0;}validate88.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema107 = {"type":"object","required":["schema","noticeId","severity","category","scope","recoverable","userActionable","summary"],"properties":{"schema":{"const":"paperclip.provider.notice.v1"},"noticeId":{"$ref":"#/$defs/id"},"severity":{"enum":["info","warning","error"]},"category":{"type":"string","minLength":1,"maxLength":160},"scope":{"enum":["turn","session","environment","account"]},"recoverable":{"type":"boolean"},"userActionable":{"type":"boolean"},"summary":{"$ref":"#/$defs/shortText"}},"additionalProperties":false};function validate90(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate90.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.noticeId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "noticeId"},message:"must have required property '"+"noticeId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.severity === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "severity"},message:"must have required property '"+"severity"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.category === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "category"},message:"must have required property '"+"category"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.scope === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "scope"},message:"must have required property '"+"scope"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.recoverable === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "recoverable"},message:"must have required property '"+"recoverable"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.userActionable === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "userActionable"},message:"must have required property '"+"userActionable"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(data.summary === undefined){const err7 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "summary"},message:"must have required property '"+"summary"+"'"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}for(const key0 in data){if(!((((((((key0 === "schema") || (key0 === "noticeId")) || (key0 === "severity")) || (key0 === "category")) || (key0 === "scope")) || (key0 === "recoverable")) || (key0 === "userActionable")) || (key0 === "summary"))){const err8 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}if(data.schema !== undefined){if("paperclip.provider.notice.v1" !== data.schema){const err9 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.provider.notice.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}if(data.noticeId !== undefined){let data1 = data.noticeId;if(typeof data1 === "string"){if(func1(data1) > 160){const err10 = {instancePath:instancePath+"/noticeId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(func1(data1) < 1){const err11 = {instancePath:instancePath+"/noticeId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(!pattern4.test(data1)){const err12 = {instancePath:instancePath+"/noticeId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}else {const err13 = {instancePath:instancePath+"/noticeId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}if(data.severity !== undefined){let data2 = data.severity;if(!(((data2 === "info") || (data2 === "warning")) || (data2 === "error"))){const err14 = {instancePath:instancePath+"/severity",schemaPath:"#/properties/severity/enum",keyword:"enum",params:{allowedValues: schema107.properties.severity.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}if(data.category !== undefined){let data3 = data.category;if(typeof data3 === "string"){if(func1(data3) > 160){const err15 = {instancePath:instancePath+"/category",schemaPath:"#/properties/category/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}if(func1(data3) < 1){const err16 = {instancePath:instancePath+"/category",schemaPath:"#/properties/category/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}else {const err17 = {instancePath:instancePath+"/category",schemaPath:"#/properties/category/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}if(data.scope !== undefined){let data4 = data.scope;if(!((((data4 === "turn") || (data4 === "session")) || (data4 === "environment")) || (data4 === "account"))){const err18 = {instancePath:instancePath+"/scope",schemaPath:"#/properties/scope/enum",keyword:"enum",params:{allowedValues: schema107.properties.scope.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}if(data.recoverable !== undefined){if(typeof data.recoverable !== "boolean"){const err19 = {instancePath:instancePath+"/recoverable",schemaPath:"#/properties/recoverable/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}if(data.userActionable !== undefined){if(typeof data.userActionable !== "boolean"){const err20 = {instancePath:instancePath+"/userActionable",schemaPath:"#/properties/userActionable/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}if(data.summary !== undefined){let data7 = data.summary;if(typeof data7 === "string"){if(func1(data7) > 4000){const err21 = {instancePath:instancePath+"/summary",schemaPath:"#/$defs/shortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}}else {const err22 = {instancePath:instancePath+"/summary",schemaPath:"#/$defs/shortText/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}}else {const err23 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}validate90.errors = vErrors;return errors === 0;}validate90.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema174 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json","title":"PRP provider-neutral semantic tool envelope","type":"object","required":["schema","schemaVersion","phase","operationId","callId","correlation","idempotencyKey","content"],"properties":{"schema":{"const":"paperclip.prp.semantic_tool.v1"},"schemaVersion":{"const":1},"phase":{"enum":["input","result","reconciled"]},"operationId":{"$ref":"#/$defs/stableId"},"callId":{"$ref":"#/$defs/stableId"},"correlation":{"type":"object","required":["runId","normalizedSessionId","turnId","itemId"],"properties":{"runId":{"$ref":"#/$defs/stableId"},"normalizedSessionId":{"$ref":"#/$defs/stableId"},"turnId":{"$ref":"#/$defs/stableId"},"itemId":{"$ref":"#/$defs/stableId"},"requestId":{"$ref":"#/$defs/stableId"}},"additionalProperties":true},"idempotencyKey":{"type":["string","null"],"minLength":1,"maxLength":240},"content":{"$ref":"#/$defs/safeContent"},"outcome":{"enum":["succeeded","denied","conflict","duplicate","unavailable","failed"]},"code":{"$ref":"#/$defs/stableCode"},"retryable":{"type":"boolean"},"authorizationBoundary":{"enum":["company","actor","active_task","grant","governed_action","lock","revision"]},"operationReceiptId":{"$ref":"#/$defs/stableId"},"auditReceiptId":{"$ref":"#/$defs/stableId"},"currentRevision":{"oneOf":[{"type":"integer","minimum":0},{"$ref":"#/$defs/stableId"}]},"duplicateOfReceiptId":{"$ref":"#/$defs/stableId"},"artifactRefs":{"type":"array","items":{"allOf":[{"$ref":"#/$defs/safeReference"},{"type":"object","properties":{"kind":{"enum":["artifact","work_product"]}}}]},"uniqueItems":true},"targets":{"type":"array","items":{"$ref":"#/$defs/immutableTarget"},"uniqueItems":true},"causalRefs":{"type":"array","items":{"$ref":"#/$defs/safeReference"},"uniqueItems":true}},"allOf":[{"if":{"properties":{"phase":{"enum":["result","reconciled"]}},"required":["phase"]},"then":{"required":["outcome","code","retryable","authorizationBoundary","operationReceiptId"]}}],"additionalProperties":true,"$defs":{"stableId":{"type":"string","minLength":1,"maxLength":240,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]*$"},"stableCode":{"type":"string","minLength":1,"maxLength":160,"pattern":"^[a-z][a-z0-9_.:-]*$"},"safeContent":{"type":"object","required":["digest","redactionDisposition","references"],"properties":{"digest":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"redactionDisposition":{"enum":["digest_only","allowlisted_references","redacted"]},"references":{"type":"array","items":{"$ref":"#/$defs/safeReference"},"uniqueItems":true}},"additionalProperties":true},"safeReference":{"type":"object","required":["kind","id"],"properties":{"kind":{"enum":["task","document_revision","interaction","approval","decision","artifact","work_product","wake","monitor","audit","operation"]},"id":{"$ref":"#/$defs/stableId"}},"additionalProperties":true},"immutableTarget":{"type":"object","required":["kind","id","immutable"],"properties":{"kind":{"enum":["document_revision","interaction","approval","decision"]},"id":{"$ref":"#/$defs/stableId"},"immutable":{"const":true},"revisionId":{"$ref":"#/$defs/stableId"},"decisionId":{"$ref":"#/$defs/stableId"}},"additionalProperties":true}}};const schema175 = {"type":"string","minLength":1,"maxLength":240,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]*$"};const schema185 = {"type":"string","minLength":1,"maxLength":160,"pattern":"^[a-z][a-z0-9_.:-]*$"};const pattern14 = new RegExp("^[a-z][a-z0-9_.:-]*$", "u");const schema182 = {"type":"object","required":["digest","redactionDisposition","references"],"properties":{"digest":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"redactionDisposition":{"enum":["digest_only","allowlisted_references","redacted"]},"references":{"type":"array","items":{"$ref":"#/$defs/safeReference"},"uniqueItems":true}},"additionalProperties":true};const schema183 = {"type":"object","required":["kind","id"],"properties":{"kind":{"enum":["task","document_revision","interaction","approval","decision","artifact","work_product","wake","monitor","audit","operation"]},"id":{"$ref":"#/$defs/stableId"}},"additionalProperties":true};function validate94(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate94.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.kind === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "kind"},message:"must have required property '"+"kind"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.id === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "id"},message:"must have required property '"+"id"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.kind !== undefined){let data0 = data.kind;if(!(((((((((((data0 === "task") || (data0 === "document_revision")) || (data0 === "interaction")) || (data0 === "approval")) || (data0 === "decision")) || (data0 === "artifact")) || (data0 === "work_product")) || (data0 === "wake")) || (data0 === "monitor")) || (data0 === "audit")) || (data0 === "operation"))){const err2 = {instancePath:instancePath+"/kind",schemaPath:"#/properties/kind/enum",keyword:"enum",params:{allowedValues: schema183.properties.kind.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}}if(data.id !== undefined){let data1 = data.id;if(typeof data1 === "string"){if(func1(data1) > 240){const err3 = {instancePath:instancePath+"/id",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(func1(data1) < 1){const err4 = {instancePath:instancePath+"/id",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(!pattern4.test(data1)){const err5 = {instancePath:instancePath+"/id",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}else {const err6 = {instancePath:instancePath+"/id",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}}else {const err7 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}validate94.errors = vErrors;return errors === 0;}validate94.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};function validate93(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate93.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.digest === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "digest"},message:"must have required property '"+"digest"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.redactionDisposition === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "redactionDisposition"},message:"must have required property '"+"redactionDisposition"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.references === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "references"},message:"must have required property '"+"references"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.digest !== undefined){let data0 = data.digest;if(typeof data0 === "string"){if(!pattern19.test(data0)){const err3 = {instancePath:instancePath+"/digest",schemaPath:"#/properties/digest/pattern",keyword:"pattern",params:{pattern: "^sha256:[a-f0-9]{64}$"},message:"must match pattern \""+"^sha256:[a-f0-9]{64}$"+"\""};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}}else {const err4 = {instancePath:instancePath+"/digest",schemaPath:"#/properties/digest/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}}if(data.redactionDisposition !== undefined){let data1 = data.redactionDisposition;if(!(((data1 === "digest_only") || (data1 === "allowlisted_references")) || (data1 === "redacted"))){const err5 = {instancePath:instancePath+"/redactionDisposition",schemaPath:"#/properties/redactionDisposition/enum",keyword:"enum",params:{allowedValues: schema182.properties.redactionDisposition.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}if(data.references !== undefined){let data2 = data.references;if(Array.isArray(data2)){const len0 = data2.length;for(let i0=0; i0 1){outer0:for(;i1--;){for(j0 = i1; j0--;){if(func0(data2[i1], data2[j0])){const err6 = {instancePath:instancePath+"/references",schemaPath:"#/properties/references/uniqueItems",keyword:"uniqueItems",params:{i: i1, j: j0},message:"must NOT have duplicate items (items ## "+j0+" and "+i1+" are identical)"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;break outer0;}}}}}else {const err7 = {instancePath:instancePath+"/references",schemaPath:"#/properties/references/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}}else {const err8 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}validate93.errors = vErrors;return errors === 0;}validate93.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema190 = {"type":"object","required":["kind","id","immutable"],"properties":{"kind":{"enum":["document_revision","interaction","approval","decision"]},"id":{"$ref":"#/$defs/stableId"},"immutable":{"const":true},"revisionId":{"$ref":"#/$defs/stableId"},"decisionId":{"$ref":"#/$defs/stableId"}},"additionalProperties":true};function validate98(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate98.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.kind === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "kind"},message:"must have required property '"+"kind"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.id === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "id"},message:"must have required property '"+"id"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.immutable === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "immutable"},message:"must have required property '"+"immutable"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.kind !== undefined){let data0 = data.kind;if(!((((data0 === "document_revision") || (data0 === "interaction")) || (data0 === "approval")) || (data0 === "decision"))){const err3 = {instancePath:instancePath+"/kind",schemaPath:"#/properties/kind/enum",keyword:"enum",params:{allowedValues: schema190.properties.kind.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}}if(data.id !== undefined){let data1 = data.id;if(typeof data1 === "string"){if(func1(data1) > 240){const err4 = {instancePath:instancePath+"/id",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(func1(data1) < 1){const err5 = {instancePath:instancePath+"/id",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(!pattern4.test(data1)){const err6 = {instancePath:instancePath+"/id",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}else {const err7 = {instancePath:instancePath+"/id",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.immutable !== undefined){if(true !== data.immutable){const err8 = {instancePath:instancePath+"/immutable",schemaPath:"#/properties/immutable/const",keyword:"const",params:{allowedValue: true},message:"must be equal to constant"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}if(data.revisionId !== undefined){let data3 = data.revisionId;if(typeof data3 === "string"){if(func1(data3) > 240){const err9 = {instancePath:instancePath+"/revisionId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(func1(data3) < 1){const err10 = {instancePath:instancePath+"/revisionId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(!pattern4.test(data3)){const err11 = {instancePath:instancePath+"/revisionId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}else {const err12 = {instancePath:instancePath+"/revisionId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.decisionId !== undefined){let data4 = data.decisionId;if(typeof data4 === "string"){if(func1(data4) > 240){const err13 = {instancePath:instancePath+"/decisionId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(func1(data4) < 1){const err14 = {instancePath:instancePath+"/decisionId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(!pattern4.test(data4)){const err15 = {instancePath:instancePath+"/decisionId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}else {const err16 = {instancePath:instancePath+"/decisionId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}}else {const err17 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}validate98.errors = vErrors;return errors === 0;}validate98.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};function validate92(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){/*# sourceURL="https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json" */;let vErrors = null;let errors = 0;const evaluated0 = validate92.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}const _errs2 = errors;let valid1 = true;const _errs3 = errors;if(data && typeof data == "object" && !Array.isArray(data)){let missing0;if((data.phase === undefined) && (missing0 = "phase")){const err0 = {};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}else {if(data.phase !== undefined){let data0 = data.phase;if(!((data0 === "result") || (data0 === "reconciled"))){const err1 = {};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}}}}var _valid0 = _errs3 === errors;errors = _errs2;if(vErrors !== null){if(_errs2){vErrors.length = _errs2;}else {vErrors = null;}}if(_valid0){const _errs5 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.outcome === undefined){const err2 = {instancePath,schemaPath:"#/allOf/0/then/required",keyword:"required",params:{missingProperty: "outcome"},message:"must have required property '"+"outcome"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.code === undefined){const err3 = {instancePath,schemaPath:"#/allOf/0/then/required",keyword:"required",params:{missingProperty: "code"},message:"must have required property '"+"code"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.retryable === undefined){const err4 = {instancePath,schemaPath:"#/allOf/0/then/required",keyword:"required",params:{missingProperty: "retryable"},message:"must have required property '"+"retryable"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.authorizationBoundary === undefined){const err5 = {instancePath,schemaPath:"#/allOf/0/then/required",keyword:"required",params:{missingProperty: "authorizationBoundary"},message:"must have required property '"+"authorizationBoundary"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.operationReceiptId === undefined){const err6 = {instancePath,schemaPath:"#/allOf/0/then/required",keyword:"required",params:{missingProperty: "operationReceiptId"},message:"must have required property '"+"operationReceiptId"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}var _valid0 = _errs5 === errors;valid1 = _valid0;}if(!valid1){const err7 = {instancePath,schemaPath:"#/allOf/0/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err8 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(data.schemaVersion === undefined){const err9 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schemaVersion"},message:"must have required property '"+"schemaVersion"+"'"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(data.phase === undefined){const err10 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "phase"},message:"must have required property '"+"phase"+"'"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(data.operationId === undefined){const err11 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "operationId"},message:"must have required property '"+"operationId"+"'"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(data.callId === undefined){const err12 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "callId"},message:"must have required property '"+"callId"+"'"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(data.correlation === undefined){const err13 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "correlation"},message:"must have required property '"+"correlation"+"'"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(data.idempotencyKey === undefined){const err14 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "idempotencyKey"},message:"must have required property '"+"idempotencyKey"+"'"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(data.content === undefined){const err15 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "content"},message:"must have required property '"+"content"+"'"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}if(data.schema !== undefined){if("paperclip.prp.semantic_tool.v1" !== data.schema){const err16 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.semantic_tool.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}if(data.schemaVersion !== undefined){if(1 !== data.schemaVersion){const err17 = {instancePath:instancePath+"/schemaVersion",schemaPath:"#/properties/schemaVersion/const",keyword:"const",params:{allowedValue: 1},message:"must be equal to constant"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}if(data.phase !== undefined){let data3 = data.phase;if(!(((data3 === "input") || (data3 === "result")) || (data3 === "reconciled"))){const err18 = {instancePath:instancePath+"/phase",schemaPath:"#/properties/phase/enum",keyword:"enum",params:{allowedValues: schema174.properties.phase.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}if(data.operationId !== undefined){let data4 = data.operationId;if(typeof data4 === "string"){if(func1(data4) > 240){const err19 = {instancePath:instancePath+"/operationId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}if(func1(data4) < 1){const err20 = {instancePath:instancePath+"/operationId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}if(!pattern4.test(data4)){const err21 = {instancePath:instancePath+"/operationId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}}else {const err22 = {instancePath:instancePath+"/operationId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}if(data.callId !== undefined){let data5 = data.callId;if(typeof data5 === "string"){if(func1(data5) > 240){const err23 = {instancePath:instancePath+"/callId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}if(func1(data5) < 1){const err24 = {instancePath:instancePath+"/callId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}if(!pattern4.test(data5)){const err25 = {instancePath:instancePath+"/callId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}else {const err26 = {instancePath:instancePath+"/callId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}}if(data.correlation !== undefined){let data6 = data.correlation;if(data6 && typeof data6 == "object" && !Array.isArray(data6)){if(data6.runId === undefined){const err27 = {instancePath:instancePath+"/correlation",schemaPath:"#/properties/correlation/required",keyword:"required",params:{missingProperty: "runId"},message:"must have required property '"+"runId"+"'"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if(data6.normalizedSessionId === undefined){const err28 = {instancePath:instancePath+"/correlation",schemaPath:"#/properties/correlation/required",keyword:"required",params:{missingProperty: "normalizedSessionId"},message:"must have required property '"+"normalizedSessionId"+"'"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}if(data6.turnId === undefined){const err29 = {instancePath:instancePath+"/correlation",schemaPath:"#/properties/correlation/required",keyword:"required",params:{missingProperty: "turnId"},message:"must have required property '"+"turnId"+"'"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}if(data6.itemId === undefined){const err30 = {instancePath:instancePath+"/correlation",schemaPath:"#/properties/correlation/required",keyword:"required",params:{missingProperty: "itemId"},message:"must have required property '"+"itemId"+"'"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}if(data6.runId !== undefined){let data7 = data6.runId;if(typeof data7 === "string"){if(func1(data7) > 240){const err31 = {instancePath:instancePath+"/correlation/runId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}if(func1(data7) < 1){const err32 = {instancePath:instancePath+"/correlation/runId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}if(!pattern4.test(data7)){const err33 = {instancePath:instancePath+"/correlation/runId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}}else {const err34 = {instancePath:instancePath+"/correlation/runId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}}if(data6.normalizedSessionId !== undefined){let data8 = data6.normalizedSessionId;if(typeof data8 === "string"){if(func1(data8) > 240){const err35 = {instancePath:instancePath+"/correlation/normalizedSessionId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}if(func1(data8) < 1){const err36 = {instancePath:instancePath+"/correlation/normalizedSessionId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}if(!pattern4.test(data8)){const err37 = {instancePath:instancePath+"/correlation/normalizedSessionId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}}else {const err38 = {instancePath:instancePath+"/correlation/normalizedSessionId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}}if(data6.turnId !== undefined){let data9 = data6.turnId;if(typeof data9 === "string"){if(func1(data9) > 240){const err39 = {instancePath:instancePath+"/correlation/turnId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}if(func1(data9) < 1){const err40 = {instancePath:instancePath+"/correlation/turnId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}if(!pattern4.test(data9)){const err41 = {instancePath:instancePath+"/correlation/turnId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}}else {const err42 = {instancePath:instancePath+"/correlation/turnId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}}if(data6.itemId !== undefined){let data10 = data6.itemId;if(typeof data10 === "string"){if(func1(data10) > 240){const err43 = {instancePath:instancePath+"/correlation/itemId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}if(func1(data10) < 1){const err44 = {instancePath:instancePath+"/correlation/itemId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err44];}else {vErrors.push(err44);}errors++;}if(!pattern4.test(data10)){const err45 = {instancePath:instancePath+"/correlation/itemId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err45];}else {vErrors.push(err45);}errors++;}}else {const err46 = {instancePath:instancePath+"/correlation/itemId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err46];}else {vErrors.push(err46);}errors++;}}if(data6.requestId !== undefined){let data11 = data6.requestId;if(typeof data11 === "string"){if(func1(data11) > 240){const err47 = {instancePath:instancePath+"/correlation/requestId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err47];}else {vErrors.push(err47);}errors++;}if(func1(data11) < 1){const err48 = {instancePath:instancePath+"/correlation/requestId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err48];}else {vErrors.push(err48);}errors++;}if(!pattern4.test(data11)){const err49 = {instancePath:instancePath+"/correlation/requestId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err49];}else {vErrors.push(err49);}errors++;}}else {const err50 = {instancePath:instancePath+"/correlation/requestId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err50];}else {vErrors.push(err50);}errors++;}}}else {const err51 = {instancePath:instancePath+"/correlation",schemaPath:"#/properties/correlation/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err51];}else {vErrors.push(err51);}errors++;}}if(data.idempotencyKey !== undefined){let data12 = data.idempotencyKey;if((typeof data12 !== "string") && (data12 !== null)){const err52 = {instancePath:instancePath+"/idempotencyKey",schemaPath:"#/properties/idempotencyKey/type",keyword:"type",params:{type: schema174.properties.idempotencyKey.type},message:"must be string,null"};if(vErrors === null){vErrors = [err52];}else {vErrors.push(err52);}errors++;}if(typeof data12 === "string"){if(func1(data12) > 240){const err53 = {instancePath:instancePath+"/idempotencyKey",schemaPath:"#/properties/idempotencyKey/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err53];}else {vErrors.push(err53);}errors++;}if(func1(data12) < 1){const err54 = {instancePath:instancePath+"/idempotencyKey",schemaPath:"#/properties/idempotencyKey/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err54];}else {vErrors.push(err54);}errors++;}}}if(data.content !== undefined){if(!(validate93(data.content, {instancePath:instancePath+"/content",parentData:data,parentDataProperty:"content",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate93.errors : vErrors.concat(validate93.errors);errors = vErrors.length;}}if(data.outcome !== undefined){let data14 = data.outcome;if(!((((((data14 === "succeeded") || (data14 === "denied")) || (data14 === "conflict")) || (data14 === "duplicate")) || (data14 === "unavailable")) || (data14 === "failed"))){const err55 = {instancePath:instancePath+"/outcome",schemaPath:"#/properties/outcome/enum",keyword:"enum",params:{allowedValues: schema174.properties.outcome.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err55];}else {vErrors.push(err55);}errors++;}}if(data.code !== undefined){let data15 = data.code;if(typeof data15 === "string"){if(func1(data15) > 160){const err56 = {instancePath:instancePath+"/code",schemaPath:"#/$defs/stableCode/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err56];}else {vErrors.push(err56);}errors++;}if(func1(data15) < 1){const err57 = {instancePath:instancePath+"/code",schemaPath:"#/$defs/stableCode/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err57];}else {vErrors.push(err57);}errors++;}if(!pattern14.test(data15)){const err58 = {instancePath:instancePath+"/code",schemaPath:"#/$defs/stableCode/pattern",keyword:"pattern",params:{pattern: "^[a-z][a-z0-9_.:-]*$"},message:"must match pattern \""+"^[a-z][a-z0-9_.:-]*$"+"\""};if(vErrors === null){vErrors = [err58];}else {vErrors.push(err58);}errors++;}}else {const err59 = {instancePath:instancePath+"/code",schemaPath:"#/$defs/stableCode/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err59];}else {vErrors.push(err59);}errors++;}}if(data.retryable !== undefined){if(typeof data.retryable !== "boolean"){const err60 = {instancePath:instancePath+"/retryable",schemaPath:"#/properties/retryable/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err60];}else {vErrors.push(err60);}errors++;}}if(data.authorizationBoundary !== undefined){let data17 = data.authorizationBoundary;if(!(((((((data17 === "company") || (data17 === "actor")) || (data17 === "active_task")) || (data17 === "grant")) || (data17 === "governed_action")) || (data17 === "lock")) || (data17 === "revision"))){const err61 = {instancePath:instancePath+"/authorizationBoundary",schemaPath:"#/properties/authorizationBoundary/enum",keyword:"enum",params:{allowedValues: schema174.properties.authorizationBoundary.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err61];}else {vErrors.push(err61);}errors++;}}if(data.operationReceiptId !== undefined){let data18 = data.operationReceiptId;if(typeof data18 === "string"){if(func1(data18) > 240){const err62 = {instancePath:instancePath+"/operationReceiptId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err62];}else {vErrors.push(err62);}errors++;}if(func1(data18) < 1){const err63 = {instancePath:instancePath+"/operationReceiptId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err63];}else {vErrors.push(err63);}errors++;}if(!pattern4.test(data18)){const err64 = {instancePath:instancePath+"/operationReceiptId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err64];}else {vErrors.push(err64);}errors++;}}else {const err65 = {instancePath:instancePath+"/operationReceiptId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err65];}else {vErrors.push(err65);}errors++;}}if(data.auditReceiptId !== undefined){let data19 = data.auditReceiptId;if(typeof data19 === "string"){if(func1(data19) > 240){const err66 = {instancePath:instancePath+"/auditReceiptId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err66];}else {vErrors.push(err66);}errors++;}if(func1(data19) < 1){const err67 = {instancePath:instancePath+"/auditReceiptId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err67];}else {vErrors.push(err67);}errors++;}if(!pattern4.test(data19)){const err68 = {instancePath:instancePath+"/auditReceiptId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err68];}else {vErrors.push(err68);}errors++;}}else {const err69 = {instancePath:instancePath+"/auditReceiptId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err69];}else {vErrors.push(err69);}errors++;}}if(data.currentRevision !== undefined){let data20 = data.currentRevision;const _errs51 = errors;let valid15 = false;let passing0 = null;const _errs52 = errors;if(!(((typeof data20 == "number") && (!(data20 % 1) && !isNaN(data20))) && (isFinite(data20)))){const err70 = {instancePath:instancePath+"/currentRevision",schemaPath:"#/properties/currentRevision/oneOf/0/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err70];}else {vErrors.push(err70);}errors++;}if((typeof data20 == "number") && (isFinite(data20))){if(data20 < 0 || isNaN(data20)){const err71 = {instancePath:instancePath+"/currentRevision",schemaPath:"#/properties/currentRevision/oneOf/0/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err71];}else {vErrors.push(err71);}errors++;}}var _valid1 = _errs52 === errors;if(_valid1){valid15 = true;passing0 = 0;}const _errs54 = errors;if(typeof data20 === "string"){if(func1(data20) > 240){const err72 = {instancePath:instancePath+"/currentRevision",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err72];}else {vErrors.push(err72);}errors++;}if(func1(data20) < 1){const err73 = {instancePath:instancePath+"/currentRevision",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err73];}else {vErrors.push(err73);}errors++;}if(!pattern4.test(data20)){const err74 = {instancePath:instancePath+"/currentRevision",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err74];}else {vErrors.push(err74);}errors++;}}else {const err75 = {instancePath:instancePath+"/currentRevision",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err75];}else {vErrors.push(err75);}errors++;}var _valid1 = _errs54 === errors;if(_valid1 && valid15){valid15 = false;passing0 = [passing0, 1];}else {if(_valid1){valid15 = true;passing0 = 1;}}if(!valid15){const err76 = {instancePath:instancePath+"/currentRevision",schemaPath:"#/properties/currentRevision/oneOf",keyword:"oneOf",params:{passingSchemas: passing0},message:"must match exactly one schema in oneOf"};if(vErrors === null){vErrors = [err76];}else {vErrors.push(err76);}errors++;}else {errors = _errs51;if(vErrors !== null){if(_errs51){vErrors.length = _errs51;}else {vErrors = null;}}}}if(data.duplicateOfReceiptId !== undefined){let data21 = data.duplicateOfReceiptId;if(typeof data21 === "string"){if(func1(data21) > 240){const err77 = {instancePath:instancePath+"/duplicateOfReceiptId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err77];}else {vErrors.push(err77);}errors++;}if(func1(data21) < 1){const err78 = {instancePath:instancePath+"/duplicateOfReceiptId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err78];}else {vErrors.push(err78);}errors++;}if(!pattern4.test(data21)){const err79 = {instancePath:instancePath+"/duplicateOfReceiptId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err79];}else {vErrors.push(err79);}errors++;}}else {const err80 = {instancePath:instancePath+"/duplicateOfReceiptId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err80];}else {vErrors.push(err80);}errors++;}}if(data.artifactRefs !== undefined){let data22 = data.artifactRefs;if(Array.isArray(data22)){const len0 = data22.length;for(let i0=0; i0 1){outer0:for(;i1--;){for(j0 = i1; j0--;){if(func0(data22[i1], data22[j0])){const err83 = {instancePath:instancePath+"/artifactRefs",schemaPath:"#/properties/artifactRefs/uniqueItems",keyword:"uniqueItems",params:{i: i1, j: j0},message:"must NOT have duplicate items (items ## "+j0+" and "+i1+" are identical)"};if(vErrors === null){vErrors = [err83];}else {vErrors.push(err83);}errors++;break outer0;}}}}}else {const err84 = {instancePath:instancePath+"/artifactRefs",schemaPath:"#/properties/artifactRefs/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err84];}else {vErrors.push(err84);}errors++;}}if(data.targets !== undefined){let data25 = data.targets;if(Array.isArray(data25)){const len1 = data25.length;for(let i2=0; i2 1){outer1:for(;i3--;){for(j1 = i3; j1--;){if(func0(data25[i3], data25[j1])){const err85 = {instancePath:instancePath+"/targets",schemaPath:"#/properties/targets/uniqueItems",keyword:"uniqueItems",params:{i: i3, j: j1},message:"must NOT have duplicate items (items ## "+j1+" and "+i3+" are identical)"};if(vErrors === null){vErrors = [err85];}else {vErrors.push(err85);}errors++;break outer1;}}}}}else {const err86 = {instancePath:instancePath+"/targets",schemaPath:"#/properties/targets/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err86];}else {vErrors.push(err86);}errors++;}}if(data.causalRefs !== undefined){let data27 = data.causalRefs;if(Array.isArray(data27)){const len2 = data27.length;for(let i4=0; i4 1){outer2:for(;i5--;){for(j2 = i5; j2--;){if(func0(data27[i5], data27[j2])){const err87 = {instancePath:instancePath+"/causalRefs",schemaPath:"#/properties/causalRefs/uniqueItems",keyword:"uniqueItems",params:{i: i5, j: j2},message:"must NOT have duplicate items (items ## "+j2+" and "+i5+" are identical)"};if(vErrors === null){vErrors = [err87];}else {vErrors.push(err87);}errors++;break outer2;}}}}}else {const err88 = {instancePath:instancePath+"/causalRefs",schemaPath:"#/properties/causalRefs/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err88];}else {vErrors.push(err88);}errors++;}}}else {const err89 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err89];}else {vErrors.push(err89);}errors++;}validate92.errors = vErrors;return errors === 0;}validate92.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema194 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/terminal.schema.json","title":"PRP run terminal state","type":"object","required":["schema","turnTerminalState","runTerminalState","reportedWorkDisposition"],"properties":{"schema":{"const":"paperclip.prp.terminal.v1"},"turnTerminalState":{"enum":["completed","failed","interrupted","cancelled"]},"runTerminalState":{"enum":["succeeded","failed","cancelled"]},"reportedWorkDisposition":{"enum":["done","blocked","needs_review","yielded"]},"workAssessmentId":{"type":"string","minLength":1},"statusDecisionId":{"type":"string","minLength":1},"stopReason":{"$ref":"https://paperclip.dev/schemas/prp/v1/stop-reason.schema.json"}},"additionalProperties":true};const schema195 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/stop-reason.schema.json","title":"PRP terminal stop reason receipt","type":"object","required":["schema","schemaVersion","receiptId","kind","code","retryable","decisionId","limitClass","aggregate"],"properties":{"schema":{"const":"paperclip.prp.stop_reason.v1"},"schemaVersion":{"const":1},"receiptId":{"$ref":"#/$defs/stableId"},"kind":{"enum":["budget","cost"]},"code":{"type":"string","minLength":1,"maxLength":160,"pattern":"^[a-z][a-z0-9_.:-]*$"},"retryable":{"type":"boolean"},"decisionId":{"$ref":"#/$defs/stableId"},"limitClass":{"enum":["company_monthly","actor_monthly","project_monthly","run_cost","provider_quota"]},"aggregate":{"type":"object","required":["unit","observed","limit","window"],"properties":{"unit":{"enum":["cents","usd_micros","tokens"]},"observed":{"type":"integer","minimum":0},"limit":{"type":"integer","minimum":0},"window":{"enum":["run","monthly_utc","provider_window"]}},"additionalProperties":true},"relatedReceiptIds":{"type":"array","items":{"$ref":"#/$defs/stableId"},"uniqueItems":true}},"additionalProperties":true,"$defs":{"stableId":{"type":"string","minLength":1,"maxLength":240,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]*$"}}};const schema196 = {"type":"string","minLength":1,"maxLength":240,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]*$"};function validate105(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){/*# sourceURL="https://paperclip.dev/schemas/prp/v1/stop-reason.schema.json" */;let vErrors = null;let errors = 0;const evaluated0 = validate105.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.schemaVersion === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schemaVersion"},message:"must have required property '"+"schemaVersion"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.receiptId === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "receiptId"},message:"must have required property '"+"receiptId"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.kind === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "kind"},message:"must have required property '"+"kind"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.code === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "code"},message:"must have required property '"+"code"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.retryable === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "retryable"},message:"must have required property '"+"retryable"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.decisionId === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "decisionId"},message:"must have required property '"+"decisionId"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(data.limitClass === undefined){const err7 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "limitClass"},message:"must have required property '"+"limitClass"+"'"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(data.aggregate === undefined){const err8 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "aggregate"},message:"must have required property '"+"aggregate"+"'"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(data.schema !== undefined){if("paperclip.prp.stop_reason.v1" !== data.schema){const err9 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.stop_reason.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}if(data.schemaVersion !== undefined){if(1 !== data.schemaVersion){const err10 = {instancePath:instancePath+"/schemaVersion",schemaPath:"#/properties/schemaVersion/const",keyword:"const",params:{allowedValue: 1},message:"must be equal to constant"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.receiptId !== undefined){let data2 = data.receiptId;if(typeof data2 === "string"){if(func1(data2) > 240){const err11 = {instancePath:instancePath+"/receiptId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(func1(data2) < 1){const err12 = {instancePath:instancePath+"/receiptId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(!pattern4.test(data2)){const err13 = {instancePath:instancePath+"/receiptId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}else {const err14 = {instancePath:instancePath+"/receiptId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}if(data.kind !== undefined){let data3 = data.kind;if(!((data3 === "budget") || (data3 === "cost"))){const err15 = {instancePath:instancePath+"/kind",schemaPath:"#/properties/kind/enum",keyword:"enum",params:{allowedValues: schema195.properties.kind.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}if(data.code !== undefined){let data4 = data.code;if(typeof data4 === "string"){if(func1(data4) > 160){const err16 = {instancePath:instancePath+"/code",schemaPath:"#/properties/code/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}if(func1(data4) < 1){const err17 = {instancePath:instancePath+"/code",schemaPath:"#/properties/code/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}if(!pattern14.test(data4)){const err18 = {instancePath:instancePath+"/code",schemaPath:"#/properties/code/pattern",keyword:"pattern",params:{pattern: "^[a-z][a-z0-9_.:-]*$"},message:"must match pattern \""+"^[a-z][a-z0-9_.:-]*$"+"\""};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}else {const err19 = {instancePath:instancePath+"/code",schemaPath:"#/properties/code/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}if(data.retryable !== undefined){if(typeof data.retryable !== "boolean"){const err20 = {instancePath:instancePath+"/retryable",schemaPath:"#/properties/retryable/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}if(data.decisionId !== undefined){let data6 = data.decisionId;if(typeof data6 === "string"){if(func1(data6) > 240){const err21 = {instancePath:instancePath+"/decisionId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(func1(data6) < 1){const err22 = {instancePath:instancePath+"/decisionId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}if(!pattern4.test(data6)){const err23 = {instancePath:instancePath+"/decisionId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}else {const err24 = {instancePath:instancePath+"/decisionId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}if(data.limitClass !== undefined){let data7 = data.limitClass;if(!(((((data7 === "company_monthly") || (data7 === "actor_monthly")) || (data7 === "project_monthly")) || (data7 === "run_cost")) || (data7 === "provider_quota"))){const err25 = {instancePath:instancePath+"/limitClass",schemaPath:"#/properties/limitClass/enum",keyword:"enum",params:{allowedValues: schema195.properties.limitClass.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}if(data.aggregate !== undefined){let data8 = data.aggregate;if(data8 && typeof data8 == "object" && !Array.isArray(data8)){if(data8.unit === undefined){const err26 = {instancePath:instancePath+"/aggregate",schemaPath:"#/properties/aggregate/required",keyword:"required",params:{missingProperty: "unit"},message:"must have required property '"+"unit"+"'"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}if(data8.observed === undefined){const err27 = {instancePath:instancePath+"/aggregate",schemaPath:"#/properties/aggregate/required",keyword:"required",params:{missingProperty: "observed"},message:"must have required property '"+"observed"+"'"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if(data8.limit === undefined){const err28 = {instancePath:instancePath+"/aggregate",schemaPath:"#/properties/aggregate/required",keyword:"required",params:{missingProperty: "limit"},message:"must have required property '"+"limit"+"'"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}if(data8.window === undefined){const err29 = {instancePath:instancePath+"/aggregate",schemaPath:"#/properties/aggregate/required",keyword:"required",params:{missingProperty: "window"},message:"must have required property '"+"window"+"'"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}if(data8.unit !== undefined){let data9 = data8.unit;if(!(((data9 === "cents") || (data9 === "usd_micros")) || (data9 === "tokens"))){const err30 = {instancePath:instancePath+"/aggregate/unit",schemaPath:"#/properties/aggregate/properties/unit/enum",keyword:"enum",params:{allowedValues: schema195.properties.aggregate.properties.unit.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}}if(data8.observed !== undefined){let data10 = data8.observed;if(!(((typeof data10 == "number") && (!(data10 % 1) && !isNaN(data10))) && (isFinite(data10)))){const err31 = {instancePath:instancePath+"/aggregate/observed",schemaPath:"#/properties/aggregate/properties/observed/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}if((typeof data10 == "number") && (isFinite(data10))){if(data10 < 0 || isNaN(data10)){const err32 = {instancePath:instancePath+"/aggregate/observed",schemaPath:"#/properties/aggregate/properties/observed/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}}}if(data8.limit !== undefined){let data11 = data8.limit;if(!(((typeof data11 == "number") && (!(data11 % 1) && !isNaN(data11))) && (isFinite(data11)))){const err33 = {instancePath:instancePath+"/aggregate/limit",schemaPath:"#/properties/aggregate/properties/limit/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}if((typeof data11 == "number") && (isFinite(data11))){if(data11 < 0 || isNaN(data11)){const err34 = {instancePath:instancePath+"/aggregate/limit",schemaPath:"#/properties/aggregate/properties/limit/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}}}if(data8.window !== undefined){let data12 = data8.window;if(!(((data12 === "run") || (data12 === "monthly_utc")) || (data12 === "provider_window"))){const err35 = {instancePath:instancePath+"/aggregate/window",schemaPath:"#/properties/aggregate/properties/window/enum",keyword:"enum",params:{allowedValues: schema195.properties.aggregate.properties.window.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}}}else {const err36 = {instancePath:instancePath+"/aggregate",schemaPath:"#/properties/aggregate/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}}if(data.relatedReceiptIds !== undefined){let data13 = data.relatedReceiptIds;if(Array.isArray(data13)){const len0 = data13.length;for(let i0=0; i0 240){const err37 = {instancePath:instancePath+"/relatedReceiptIds/" + i0,schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}if(func1(data14) < 1){const err38 = {instancePath:instancePath+"/relatedReceiptIds/" + i0,schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}if(!pattern4.test(data14)){const err39 = {instancePath:instancePath+"/relatedReceiptIds/" + i0,schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}}else {const err40 = {instancePath:instancePath+"/relatedReceiptIds/" + i0,schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}}let i1 = data13.length;let j0;if(i1 > 1){outer0:for(;i1--;){for(j0 = i1; j0--;){if(func0(data13[i1], data13[j0])){const err41 = {instancePath:instancePath+"/relatedReceiptIds",schemaPath:"#/properties/relatedReceiptIds/uniqueItems",keyword:"uniqueItems",params:{i: i1, j: j0},message:"must NOT have duplicate items (items ## "+j0+" and "+i1+" are identical)"};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;break outer0;}}}}}else {const err42 = {instancePath:instancePath+"/relatedReceiptIds",schemaPath:"#/properties/relatedReceiptIds/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}}}else {const err43 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}validate105.errors = vErrors;return errors === 0;}validate105.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};function validate104(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){/*# sourceURL="https://paperclip.dev/schemas/prp/v1/terminal.schema.json" */;let vErrors = null;let errors = 0;const evaluated0 = validate104.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.turnTerminalState === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "turnTerminalState"},message:"must have required property '"+"turnTerminalState"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.runTerminalState === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "runTerminalState"},message:"must have required property '"+"runTerminalState"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.reportedWorkDisposition === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reportedWorkDisposition"},message:"must have required property '"+"reportedWorkDisposition"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.schema !== undefined){if("paperclip.prp.terminal.v1" !== data.schema){const err4 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.terminal.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}}if(data.turnTerminalState !== undefined){let data1 = data.turnTerminalState;if(!((((data1 === "completed") || (data1 === "failed")) || (data1 === "interrupted")) || (data1 === "cancelled"))){const err5 = {instancePath:instancePath+"/turnTerminalState",schemaPath:"#/properties/turnTerminalState/enum",keyword:"enum",params:{allowedValues: schema194.properties.turnTerminalState.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}if(data.runTerminalState !== undefined){let data2 = data.runTerminalState;if(!(((data2 === "succeeded") || (data2 === "failed")) || (data2 === "cancelled"))){const err6 = {instancePath:instancePath+"/runTerminalState",schemaPath:"#/properties/runTerminalState/enum",keyword:"enum",params:{allowedValues: schema194.properties.runTerminalState.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.reportedWorkDisposition !== undefined){let data3 = data.reportedWorkDisposition;if(!((((data3 === "done") || (data3 === "blocked")) || (data3 === "needs_review")) || (data3 === "yielded"))){const err7 = {instancePath:instancePath+"/reportedWorkDisposition",schemaPath:"#/properties/reportedWorkDisposition/enum",keyword:"enum",params:{allowedValues: schema194.properties.reportedWorkDisposition.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.workAssessmentId !== undefined){let data4 = data.workAssessmentId;if(typeof data4 === "string"){if(func1(data4) < 1){const err8 = {instancePath:instancePath+"/workAssessmentId",schemaPath:"#/properties/workAssessmentId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}else {const err9 = {instancePath:instancePath+"/workAssessmentId",schemaPath:"#/properties/workAssessmentId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}if(data.statusDecisionId !== undefined){let data5 = data.statusDecisionId;if(typeof data5 === "string"){if(func1(data5) < 1){const err10 = {instancePath:instancePath+"/statusDecisionId",schemaPath:"#/properties/statusDecisionId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}else {const err11 = {instancePath:instancePath+"/statusDecisionId",schemaPath:"#/properties/statusDecisionId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.stopReason !== undefined){if(!(validate105(data.stopReason, {instancePath:instancePath+"/stopReason",parentData:data,parentDataProperty:"stopReason",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate105.errors : vErrors.concat(validate105.errors);errors = vErrors.length;}}}else {const err12 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}validate104.errors = vErrors;return errors === 0;}validate104.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const pattern69 = new RegExp("^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$", "u");const formats0 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function validate26(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){/*# sourceURL="https://paperclip.dev/schemas/prp/v1/event.schema.json" */;let vErrors = null;let errors = 0;const evaluated0 = validate26.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}const _errs2 = errors;let valid1 = true;const _errs3 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("plan.updated" !== data.eventType){const err0 = {};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}}}var _valid0 = _errs3 === errors;errors = _errs2;if(vErrors !== null){if(_errs2){vErrors.length = _errs2;}else {vErrors = null;}}if(_valid0){const _errs5 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate60(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate60.errors : vErrors.concat(validate60.errors);errors = vErrors.length;}}}var _valid0 = _errs5 === errors;valid1 = _valid0;if(valid1){var props0 = {};props0.payload = true;props0.eventType = true;}}if(!valid1){const err1 = {instancePath,schemaPath:"#/allOf/0/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}const _errs8 = errors;let valid4 = true;const _errs9 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data2 = data.eventType;if(!(((data2 === "tool.execution.started") || (data2 === "tool.execution.progressed")) || (data2 === "tool.execution.completed"))){const err2 = {};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}}}var _valid1 = _errs9 === errors;errors = _errs8;if(vErrors !== null){if(_errs8){vErrors.length = _errs8;}else {vErrors = null;}}if(_valid1){const _errs11 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate62(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate62.errors : vErrors.concat(validate62.errors);errors = vErrors.length;}}}var _valid1 = _errs11 === errors;valid4 = _valid1;if(valid4){var props1 = {};props1.payload = true;props1.eventType = true;}}if(!valid4){const err3 = {instancePath,schemaPath:"#/allOf/1/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(props0 !== true && props1 !== undefined){if(props1 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props1);}}const _errs14 = errors;let valid7 = true;const _errs15 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data4 = data.eventType;if(!(((data4 === "research.started") || (data4 === "research.progressed")) || (data4 === "research.completed"))){const err4 = {};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}}}var _valid2 = _errs15 === errors;errors = _errs14;if(vErrors !== null){if(_errs14){vErrors.length = _errs14;}else {vErrors = null;}}if(_valid2){const _errs17 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate64(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate64.errors : vErrors.concat(validate64.errors);errors = vErrors.length;}}}var _valid2 = _errs17 === errors;valid7 = _valid2;if(valid7){var props2 = {};props2.payload = true;props2.eventType = true;}}if(!valid7){const err5 = {instancePath,schemaPath:"#/allOf/2/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(props0 !== true && props2 !== undefined){if(props2 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props2);}}const _errs20 = errors;let valid10 = true;const _errs21 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data6 = data.eventType;if(!(((data6 === "delegation.started") || (data6 === "delegation.updated")) || (data6 === "delegation.completed"))){const err6 = {};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}}var _valid3 = _errs21 === errors;errors = _errs20;if(vErrors !== null){if(_errs20){vErrors.length = _errs20;}else {vErrors = null;}}if(_valid3){const _errs23 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate66(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate66.errors : vErrors.concat(validate66.errors);errors = vErrors.length;}}}var _valid3 = _errs23 === errors;valid10 = _valid3;if(valid10){var props3 = {};props3.payload = true;props3.eventType = true;}}if(!valid10){const err7 = {instancePath,schemaPath:"#/allOf/3/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(props0 !== true && props3 !== undefined){if(props3 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props3);}}const _errs26 = errors;let valid13 = true;const _errs27 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("model.route.changed" !== data.eventType){const err8 = {};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}}var _valid4 = _errs27 === errors;errors = _errs26;if(vErrors !== null){if(_errs26){vErrors.length = _errs26;}else {vErrors = null;}}if(_valid4){const _errs29 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate68(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate68.errors : vErrors.concat(validate68.errors);errors = vErrors.length;}}}var _valid4 = _errs29 === errors;valid13 = _valid4;if(valid13){var props4 = {};props4.payload = true;props4.eventType = true;}}if(!valid13){const err9 = {instancePath,schemaPath:"#/allOf/4/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(props0 !== true && props4 !== undefined){if(props4 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props4);}}const _errs32 = errors;let valid16 = true;const _errs33 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("model.verification.updated" !== data.eventType){const err10 = {};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}}var _valid5 = _errs33 === errors;errors = _errs32;if(vErrors !== null){if(_errs32){vErrors.length = _errs32;}else {vErrors = null;}}if(_valid5){const _errs35 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate70(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate70.errors : vErrors.concat(validate70.errors);errors = vErrors.length;}}}var _valid5 = _errs35 === errors;valid16 = _valid5;if(valid16){var props5 = {};props5.payload = true;props5.eventType = true;}}if(!valid16){const err11 = {instancePath,schemaPath:"#/allOf/5/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(props0 !== true && props5 !== undefined){if(props5 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props5);}}const _errs38 = errors;let valid19 = true;const _errs39 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("context.compacted" !== data.eventType){const err12 = {};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}}var _valid6 = _errs39 === errors;errors = _errs38;if(vErrors !== null){if(_errs38){vErrors.length = _errs38;}else {vErrors = null;}}if(_valid6){const _errs41 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate72(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate72.errors : vErrors.concat(validate72.errors);errors = vErrors.length;}}}var _valid6 = _errs41 === errors;valid19 = _valid6;if(valid19){var props6 = {};props6.payload = true;props6.eventType = true;}}if(!valid19){const err13 = {instancePath,schemaPath:"#/allOf/6/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(props0 !== true && props6 !== undefined){if(props6 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props6);}}const _errs44 = errors;let valid22 = true;const _errs45 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("artifact.viewed" !== data.eventType){const err14 = {};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}}var _valid7 = _errs45 === errors;errors = _errs44;if(vErrors !== null){if(_errs44){vErrors.length = _errs44;}else {vErrors = null;}}if(_valid7){const _errs47 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate74(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate74.errors : vErrors.concat(validate74.errors);errors = vErrors.length;}}}var _valid7 = _errs47 === errors;valid22 = _valid7;if(valid22){var props7 = {};props7.payload = true;props7.eventType = true;}}if(!valid22){const err15 = {instancePath,schemaPath:"#/allOf/7/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}if(props0 !== true && props7 !== undefined){if(props7 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props7);}}const _errs50 = errors;let valid25 = true;const _errs51 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("artifact.generated" !== data.eventType){const err16 = {};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}}var _valid8 = _errs51 === errors;errors = _errs50;if(vErrors !== null){if(_errs50){vErrors.length = _errs50;}else {vErrors = null;}}if(_valid8){const _errs53 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate76(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate76.errors : vErrors.concat(validate76.errors);errors = vErrors.length;}}}var _valid8 = _errs53 === errors;valid25 = _valid8;if(valid25){var props8 = {};props8.payload = true;props8.eventType = true;}}if(!valid25){const err17 = {instancePath,schemaPath:"#/allOf/8/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}if(props0 !== true && props8 !== undefined){if(props8 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props8);}}const _errs56 = errors;let valid28 = true;const _errs57 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("review.mode.changed" !== data.eventType){const err18 = {};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}}var _valid9 = _errs57 === errors;errors = _errs56;if(vErrors !== null){if(_errs56){vErrors.length = _errs56;}else {vErrors = null;}}if(_valid9){const _errs59 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate78(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate78.errors : vErrors.concat(validate78.errors);errors = vErrors.length;}}}var _valid9 = _errs59 === errors;valid28 = _valid9;if(valid28){var props9 = {};props9.payload = true;props9.eventType = true;}}if(!valid28){const err19 = {instancePath,schemaPath:"#/allOf/9/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}if(props0 !== true && props9 !== undefined){if(props9 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props9);}}const _errs62 = errors;let valid31 = true;const _errs63 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data20 = data.eventType;if(!((data20 === "hook.started") || (data20 === "hook.completed"))){const err20 = {};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}}var _valid10 = _errs63 === errors;errors = _errs62;if(vErrors !== null){if(_errs62){vErrors.length = _errs62;}else {vErrors = null;}}if(_valid10){const _errs65 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate80(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate80.errors : vErrors.concat(validate80.errors);errors = vErrors.length;}}}var _valid10 = _errs65 === errors;valid31 = _valid10;if(valid31){var props10 = {};props10.payload = true;props10.eventType = true;}}if(!valid31){const err21 = {instancePath,schemaPath:"#/allOf/10/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(props0 !== true && props10 !== undefined){if(props10 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props10);}}const _errs68 = errors;let valid34 = true;const _errs69 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("memory.citation.referenced" !== data.eventType){const err22 = {};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}}var _valid11 = _errs69 === errors;errors = _errs68;if(vErrors !== null){if(_errs68){vErrors.length = _errs68;}else {vErrors = null;}}if(_valid11){const _errs71 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate82(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate82.errors : vErrors.concat(validate82.errors);errors = vErrors.length;}}}var _valid11 = _errs71 === errors;valid34 = _valid11;if(valid34){var props11 = {};props11.payload = true;props11.eventType = true;}}if(!valid34){const err23 = {instancePath,schemaPath:"#/allOf/11/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}if(props0 !== true && props11 !== undefined){if(props11 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props11);}}const _errs74 = errors;let valid37 = true;const _errs75 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data24 = data.eventType;if(!((data24 === "safety.review.started") || (data24 === "safety.review.completed"))){const err24 = {};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}}var _valid12 = _errs75 === errors;errors = _errs74;if(vErrors !== null){if(_errs74){vErrors.length = _errs74;}else {vErrors = null;}}if(_valid12){const _errs77 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate84(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate84.errors : vErrors.concat(validate84.errors);errors = vErrors.length;}}}var _valid12 = _errs77 === errors;valid37 = _valid12;if(valid37){var props12 = {};props12.payload = true;props12.eventType = true;}}if(!valid37){const err25 = {instancePath,schemaPath:"#/allOf/12/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}if(props0 !== true && props12 !== undefined){if(props12 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props12);}}const _errs80 = errors;let valid40 = true;const _errs81 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("terminal.input.sent" !== data.eventType){const err26 = {};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}}}var _valid13 = _errs81 === errors;errors = _errs80;if(vErrors !== null){if(_errs80){vErrors.length = _errs80;}else {vErrors = null;}}if(_valid13){const _errs83 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate86(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate86.errors : vErrors.concat(validate86.errors);errors = vErrors.length;}}}var _valid13 = _errs83 === errors;valid40 = _valid13;if(valid40){var props13 = {};props13.payload = true;props13.eventType = true;}}if(!valid40){const err27 = {instancePath,schemaPath:"#/allOf/13/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if(props0 !== true && props13 !== undefined){if(props13 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props13);}}const _errs86 = errors;let valid43 = true;const _errs87 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data28 = data.eventType;if(!((data28 === "wait.started") || (data28 === "wait.completed"))){const err28 = {};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}}var _valid14 = _errs87 === errors;errors = _errs86;if(vErrors !== null){if(_errs86){vErrors.length = _errs86;}else {vErrors = null;}}if(_valid14){const _errs89 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate88(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate88.errors : vErrors.concat(validate88.errors);errors = vErrors.length;}}}var _valid14 = _errs89 === errors;valid43 = _valid14;if(valid43){var props14 = {};props14.payload = true;props14.eventType = true;}}if(!valid43){const err29 = {instancePath,schemaPath:"#/allOf/14/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}if(props0 !== true && props14 !== undefined){if(props14 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props14);}}const _errs92 = errors;let valid46 = true;const _errs93 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("provider.notice.recorded" !== data.eventType){const err30 = {};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}}}var _valid15 = _errs93 === errors;errors = _errs92;if(vErrors !== null){if(_errs92){vErrors.length = _errs92;}else {vErrors = null;}}if(_valid15){const _errs95 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate90(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate90.errors : vErrors.concat(validate90.errors);errors = vErrors.length;}}}var _valid15 = _errs95 === errors;valid46 = _valid15;if(valid46){var props15 = {};props15.payload = true;props15.eventType = true;}}if(!valid46){const err31 = {instancePath,schemaPath:"#/allOf/15/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}if(props0 !== true && props15 !== undefined){if(props15 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props15);}}const _errs98 = errors;let valid49 = true;const _errs99 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("workspace.file.referenced" !== data.eventType){const err32 = {};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}}}var _valid16 = _errs99 === errors;errors = _errs98;if(vErrors !== null){if(_errs98){vErrors.length = _errs98;}else {vErrors = null;}}if(_valid16){const _errs101 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data33 = data.payload;if(data33 && typeof data33 == "object" && !Array.isArray(data33)){if(data33.schema === undefined){const err33 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}if(data33.referenceId === undefined){const err34 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "referenceId"},message:"must have required property '"+"referenceId"+"'"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}if(data33.source === undefined){const err35 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "source"},message:"must have required property '"+"source"+"'"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}if(data33.path === undefined){const err36 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "path"},message:"must have required property '"+"path"+"'"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}if(data33.displayName === undefined){const err37 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "displayName"},message:"must have required property '"+"displayName"+"'"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}if(data33.mediaType === undefined){const err38 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "mediaType"},message:"must have required property '"+"mediaType"+"'"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}if(data33.presentation === undefined){const err39 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "presentation"},message:"must have required property '"+"presentation"+"'"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}if(data33.line === undefined){const err40 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "line"},message:"must have required property '"+"line"+"'"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}if(data33.preview === undefined){const err41 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "preview"},message:"must have required property '"+"preview"+"'"};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}if(data33.previewTruncated === undefined){const err42 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "previewTruncated"},message:"must have required property '"+"previewTruncated"+"'"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}if(data33.contentDigest === undefined){const err43 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "contentDigest"},message:"must have required property '"+"contentDigest"+"'"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}for(const key0 in data33){if(!(func58.call(schema171.properties, key0))){const err44 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err44];}else {vErrors.push(err44);}errors++;}}if(data33.schema !== undefined){if("paperclip.workspace.file_reference.v1" !== data33.schema){const err45 = {instancePath:instancePath+"/payload/schema",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.workspace.file_reference.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err45];}else {vErrors.push(err45);}errors++;}}if(data33.referenceId !== undefined){let data35 = data33.referenceId;if(typeof data35 === "string"){if(func1(data35) > 240){const err46 = {instancePath:instancePath+"/payload/referenceId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/referenceId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err46];}else {vErrors.push(err46);}errors++;}if(func1(data35) < 1){const err47 = {instancePath:instancePath+"/payload/referenceId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/referenceId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err47];}else {vErrors.push(err47);}errors++;}}else {const err48 = {instancePath:instancePath+"/payload/referenceId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/referenceId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err48];}else {vErrors.push(err48);}errors++;}}if(data33.source !== undefined){let data36 = data33.source;if(!((data36 === "harness_reported") || (data36 === "runner_verified"))){const err49 = {instancePath:instancePath+"/payload/source",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/source/enum",keyword:"enum",params:{allowedValues: schema171.properties.source.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err49];}else {vErrors.push(err49);}errors++;}}if(data33.path !== undefined){let data37 = data33.path;if(typeof data37 === "string"){if(func1(data37) > 1024){const err50 = {instancePath:instancePath+"/payload/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/path/maxLength",keyword:"maxLength",params:{limit: 1024},message:"must NOT have more than 1024 characters"};if(vErrors === null){vErrors = [err50];}else {vErrors.push(err50);}errors++;}if(func1(data37) < 1){const err51 = {instancePath:instancePath+"/payload/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/path/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err51];}else {vErrors.push(err51);}errors++;}if(!pattern69.test(data37)){const err52 = {instancePath:instancePath+"/payload/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/path/pattern",keyword:"pattern",params:{pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"},message:"must match pattern \""+"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"+"\""};if(vErrors === null){vErrors = [err52];}else {vErrors.push(err52);}errors++;}}else {const err53 = {instancePath:instancePath+"/payload/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/path/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err53];}else {vErrors.push(err53);}errors++;}}if(data33.displayName !== undefined){let data38 = data33.displayName;if(typeof data38 === "string"){if(func1(data38) > 255){const err54 = {instancePath:instancePath+"/payload/displayName",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/displayName/maxLength",keyword:"maxLength",params:{limit: 255},message:"must NOT have more than 255 characters"};if(vErrors === null){vErrors = [err54];}else {vErrors.push(err54);}errors++;}if(func1(data38) < 1){const err55 = {instancePath:instancePath+"/payload/displayName",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/displayName/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err55];}else {vErrors.push(err55);}errors++;}}else {const err56 = {instancePath:instancePath+"/payload/displayName",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/displayName/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err56];}else {vErrors.push(err56);}errors++;}}if(data33.mediaType !== undefined){let data39 = data33.mediaType;if((typeof data39 !== "string") && (data39 !== null)){const err57 = {instancePath:instancePath+"/payload/mediaType",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/mediaType/type",keyword:"type",params:{type: schema171.properties.mediaType.type},message:"must be string,null"};if(vErrors === null){vErrors = [err57];}else {vErrors.push(err57);}errors++;}if(typeof data39 === "string"){if(func1(data39) > 160){const err58 = {instancePath:instancePath+"/payload/mediaType",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/mediaType/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err58];}else {vErrors.push(err58);}errors++;}}}if(data33.presentation !== undefined){let data40 = data33.presentation;if(!((((data40 === "document") || (data40 === "code")) || (data40 === "image")) || (data40 === "generic"))){const err59 = {instancePath:instancePath+"/payload/presentation",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/presentation/enum",keyword:"enum",params:{allowedValues: schema171.properties.presentation.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err59];}else {vErrors.push(err59);}errors++;}}if(data33.line !== undefined){let data41 = data33.line;if((!(((typeof data41 == "number") && (!(data41 % 1) && !isNaN(data41))) && (isFinite(data41)))) && (data41 !== null)){const err60 = {instancePath:instancePath+"/payload/line",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/line/type",keyword:"type",params:{type: schema171.properties.line.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err60];}else {vErrors.push(err60);}errors++;}if((typeof data41 == "number") && (isFinite(data41))){if(data41 < 1 || isNaN(data41)){const err61 = {instancePath:instancePath+"/payload/line",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/line/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err61];}else {vErrors.push(err61);}errors++;}}}if(data33.preview !== undefined){let data42 = data33.preview;if((typeof data42 !== "string") && (data42 !== null)){const err62 = {instancePath:instancePath+"/payload/preview",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/preview/type",keyword:"type",params:{type: schema171.properties.preview.type},message:"must be string,null"};if(vErrors === null){vErrors = [err62];}else {vErrors.push(err62);}errors++;}if(typeof data42 === "string"){if(func1(data42) > 262144){const err63 = {instancePath:instancePath+"/payload/preview",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/preview/maxLength",keyword:"maxLength",params:{limit: 262144},message:"must NOT have more than 262144 characters"};if(vErrors === null){vErrors = [err63];}else {vErrors.push(err63);}errors++;}}}if(data33.previewTruncated !== undefined){if(typeof data33.previewTruncated !== "boolean"){const err64 = {instancePath:instancePath+"/payload/previewTruncated",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/previewTruncated/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err64];}else {vErrors.push(err64);}errors++;}}if(data33.contentDigest !== undefined){let data44 = data33.contentDigest;const _errs124 = errors;let valid54 = false;let passing0 = null;const _errs125 = errors;if(data44 !== null){const err65 = {instancePath:instancePath+"/payload/contentDigest",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/contentDigest/oneOf/0/type",keyword:"type",params:{type: "null"},message:"must be null"};if(vErrors === null){vErrors = [err65];}else {vErrors.push(err65);}errors++;}var _valid17 = _errs125 === errors;if(_valid17){valid54 = true;passing0 = 0;}const _errs127 = errors;if(typeof data44 === "string"){if(!pattern19.test(data44)){const err66 = {instancePath:instancePath+"/payload/contentDigest",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/contentDigest/oneOf/1/pattern",keyword:"pattern",params:{pattern: "^sha256:[a-f0-9]{64}$"},message:"must match pattern \""+"^sha256:[a-f0-9]{64}$"+"\""};if(vErrors === null){vErrors = [err66];}else {vErrors.push(err66);}errors++;}}else {const err67 = {instancePath:instancePath+"/payload/contentDigest",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/contentDigest/oneOf/1/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err67];}else {vErrors.push(err67);}errors++;}var _valid17 = _errs127 === errors;if(_valid17 && valid54){valid54 = false;passing0 = [passing0, 1];}else {if(_valid17){valid54 = true;passing0 = 1;}}if(!valid54){const err68 = {instancePath:instancePath+"/payload/contentDigest",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/contentDigest/oneOf",keyword:"oneOf",params:{passingSchemas: passing0},message:"must match exactly one schema in oneOf"};if(vErrors === null){vErrors = [err68];}else {vErrors.push(err68);}errors++;}else {errors = _errs124;if(vErrors !== null){if(_errs124){vErrors.length = _errs124;}else {vErrors = null;}}}}}else {const err69 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err69];}else {vErrors.push(err69);}errors++;}}}var _valid16 = _errs101 === errors;valid49 = _valid16;if(valid49){var props16 = {};props16.payload = true;props16.eventType = true;}}if(!valid49){const err70 = {instancePath,schemaPath:"#/allOf/16/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err70];}else {vErrors.push(err70);}errors++;}if(props0 !== true && props16 !== undefined){if(props16 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props16);}}const _errs130 = errors;let valid55 = true;const _errs131 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data45 = data.eventType;if(!((data45 === "workspace.change.updated") || (data45 === "workspace.diff.recorded"))){const err71 = {};if(vErrors === null){vErrors = [err71];}else {vErrors.push(err71);}errors++;}}}var _valid18 = _errs131 === errors;errors = _errs130;if(vErrors !== null){if(_errs130){vErrors.length = _errs130;}else {vErrors = null;}}if(_valid18){const _errs133 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data46 = data.payload;if(data46 && typeof data46 == "object" && !Array.isArray(data46)){if(data46.schema === undefined){const err72 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err72];}else {vErrors.push(err72);}errors++;}if(data46.changeSetId === undefined){const err73 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "changeSetId"},message:"must have required property '"+"changeSetId"+"'"};if(vErrors === null){vErrors = [err73];}else {vErrors.push(err73);}errors++;}if(data46.revision === undefined){const err74 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "revision"},message:"must have required property '"+"revision"+"'"};if(vErrors === null){vErrors = [err74];}else {vErrors.push(err74);}errors++;}if(data46.source === undefined){const err75 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "source"},message:"must have required property '"+"source"+"'"};if(vErrors === null){vErrors = [err75];}else {vErrors.push(err75);}errors++;}if(data46.complete === undefined){const err76 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "complete"},message:"must have required property '"+"complete"+"'"};if(vErrors === null){vErrors = [err76];}else {vErrors.push(err76);}errors++;}if(data46.files === undefined){const err77 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "files"},message:"must have required property '"+"files"+"'"};if(vErrors === null){vErrors = [err77];}else {vErrors.push(err77);}errors++;}if(data46.totals === undefined){const err78 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "totals"},message:"must have required property '"+"totals"+"'"};if(vErrors === null){vErrors = [err78];}else {vErrors.push(err78);}errors++;}if(data46.patchArtifactRef === undefined){const err79 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "patchArtifactRef"},message:"must have required property '"+"patchArtifactRef"+"'"};if(vErrors === null){vErrors = [err79];}else {vErrors.push(err79);}errors++;}for(const key1 in data46){if(!((((((((key1 === "schema") || (key1 === "changeSetId")) || (key1 === "revision")) || (key1 === "source")) || (key1 === "complete")) || (key1 === "files")) || (key1 === "totals")) || (key1 === "patchArtifactRef"))){const err80 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key1},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err80];}else {vErrors.push(err80);}errors++;}}if(data46.schema !== undefined){if("paperclip.workspace.diff.v1" !== data46.schema){const err81 = {instancePath:instancePath+"/payload/schema",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.workspace.diff.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err81];}else {vErrors.push(err81);}errors++;}}if(data46.changeSetId !== undefined){let data48 = data46.changeSetId;if(typeof data48 === "string"){if(func1(data48) > 200){const err82 = {instancePath:instancePath+"/payload/changeSetId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/changeSetId/maxLength",keyword:"maxLength",params:{limit: 200},message:"must NOT have more than 200 characters"};if(vErrors === null){vErrors = [err82];}else {vErrors.push(err82);}errors++;}if(func1(data48) < 1){const err83 = {instancePath:instancePath+"/payload/changeSetId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/changeSetId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err83];}else {vErrors.push(err83);}errors++;}}else {const err84 = {instancePath:instancePath+"/payload/changeSetId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/changeSetId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err84];}else {vErrors.push(err84);}errors++;}}if(data46.revision !== undefined){let data49 = data46.revision;if(!(((typeof data49 == "number") && (!(data49 % 1) && !isNaN(data49))) && (isFinite(data49)))){const err85 = {instancePath:instancePath+"/payload/revision",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/revision/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err85];}else {vErrors.push(err85);}errors++;}if((typeof data49 == "number") && (isFinite(data49))){if(data49 < 1 || isNaN(data49)){const err86 = {instancePath:instancePath+"/payload/revision",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/revision/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err86];}else {vErrors.push(err86);}errors++;}}}if(data46.source !== undefined){let data50 = data46.source;if(!((data50 === "harness_reported") || (data50 === "runner_verified"))){const err87 = {instancePath:instancePath+"/payload/source",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/source/enum",keyword:"enum",params:{allowedValues: schema172.properties.source.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err87];}else {vErrors.push(err87);}errors++;}}if(data46.complete !== undefined){if(typeof data46.complete !== "boolean"){const err88 = {instancePath:instancePath+"/payload/complete",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/complete/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err88];}else {vErrors.push(err88);}errors++;}}if(data46.files !== undefined){let data52 = data46.files;if(Array.isArray(data52)){if(data52.length > 2000){const err89 = {instancePath:instancePath+"/payload/files",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/maxItems",keyword:"maxItems",params:{limit: 2000},message:"must NOT have more than 2000 items"};if(vErrors === null){vErrors = [err89];}else {vErrors.push(err89);}errors++;}const len0 = data52.length;for(let i0=0; i0 1024){const err98 = {instancePath:instancePath+"/payload/files/" + i0+"/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/path/maxLength",keyword:"maxLength",params:{limit: 1024},message:"must NOT have more than 1024 characters"};if(vErrors === null){vErrors = [err98];}else {vErrors.push(err98);}errors++;}if(func1(data54) < 1){const err99 = {instancePath:instancePath+"/payload/files/" + i0+"/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/path/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err99];}else {vErrors.push(err99);}errors++;}if(!pattern69.test(data54)){const err100 = {instancePath:instancePath+"/payload/files/" + i0+"/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/path/pattern",keyword:"pattern",params:{pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"},message:"must match pattern \""+"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"+"\""};if(vErrors === null){vErrors = [err100];}else {vErrors.push(err100);}errors++;}}else {const err101 = {instancePath:instancePath+"/payload/files/" + i0+"/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/path/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err101];}else {vErrors.push(err101);}errors++;}}if(data53.operation !== undefined){let data55 = data53.operation;if(!(((((data55 === "create") || (data55 === "modify")) || (data55 === "delete")) || (data55 === "rename")) || (data55 === "mode_change"))){const err102 = {instancePath:instancePath+"/payload/files/" + i0+"/operation",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/operation/enum",keyword:"enum",params:{allowedValues: schema172.properties.files.items.properties.operation.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err102];}else {vErrors.push(err102);}errors++;}}if(data53.previousPath !== undefined){let data56 = data53.previousPath;const _errs155 = errors;let valid63 = false;let passing1 = null;const _errs156 = errors;if(data56 !== null){const err103 = {instancePath:instancePath+"/payload/files/" + i0+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/0/type",keyword:"type",params:{type: "null"},message:"must be null"};if(vErrors === null){vErrors = [err103];}else {vErrors.push(err103);}errors++;}var _valid19 = _errs156 === errors;if(_valid19){valid63 = true;passing1 = 0;}const _errs158 = errors;if(typeof data56 === "string"){if(func1(data56) > 1024){const err104 = {instancePath:instancePath+"/payload/files/" + i0+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/1/maxLength",keyword:"maxLength",params:{limit: 1024},message:"must NOT have more than 1024 characters"};if(vErrors === null){vErrors = [err104];}else {vErrors.push(err104);}errors++;}if(func1(data56) < 1){const err105 = {instancePath:instancePath+"/payload/files/" + i0+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/1/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err105];}else {vErrors.push(err105);}errors++;}if(!pattern69.test(data56)){const err106 = {instancePath:instancePath+"/payload/files/" + i0+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/1/pattern",keyword:"pattern",params:{pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"},message:"must match pattern \""+"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"+"\""};if(vErrors === null){vErrors = [err106];}else {vErrors.push(err106);}errors++;}}else {const err107 = {instancePath:instancePath+"/payload/files/" + i0+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/1/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err107];}else {vErrors.push(err107);}errors++;}var _valid19 = _errs158 === errors;if(_valid19 && valid63){valid63 = false;passing1 = [passing1, 1];}else {if(_valid19){valid63 = true;passing1 = 1;}}if(!valid63){const err108 = {instancePath:instancePath+"/payload/files/" + i0+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf",keyword:"oneOf",params:{passingSchemas: passing1},message:"must match exactly one schema in oneOf"};if(vErrors === null){vErrors = [err108];}else {vErrors.push(err108);}errors++;}else {errors = _errs155;if(vErrors !== null){if(_errs155){vErrors.length = _errs155;}else {vErrors = null;}}}}if(data53.additions !== undefined){let data57 = data53.additions;if((!(((typeof data57 == "number") && (!(data57 % 1) && !isNaN(data57))) && (isFinite(data57)))) && (data57 !== null)){const err109 = {instancePath:instancePath+"/payload/files/" + i0+"/additions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/additions/type",keyword:"type",params:{type: schema172.properties.files.items.properties.additions.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err109];}else {vErrors.push(err109);}errors++;}if((typeof data57 == "number") && (isFinite(data57))){if(data57 < 0 || isNaN(data57)){const err110 = {instancePath:instancePath+"/payload/files/" + i0+"/additions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/additions/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err110];}else {vErrors.push(err110);}errors++;}}}if(data53.deletions !== undefined){let data58 = data53.deletions;if((!(((typeof data58 == "number") && (!(data58 % 1) && !isNaN(data58))) && (isFinite(data58)))) && (data58 !== null)){const err111 = {instancePath:instancePath+"/payload/files/" + i0+"/deletions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/deletions/type",keyword:"type",params:{type: schema172.properties.files.items.properties.deletions.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err111];}else {vErrors.push(err111);}errors++;}if((typeof data58 == "number") && (isFinite(data58))){if(data58 < 0 || isNaN(data58)){const err112 = {instancePath:instancePath+"/payload/files/" + i0+"/deletions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/deletions/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err112];}else {vErrors.push(err112);}errors++;}}}if(data53.binary !== undefined){if(typeof data53.binary !== "boolean"){const err113 = {instancePath:instancePath+"/payload/files/" + i0+"/binary",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/binary/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err113];}else {vErrors.push(err113);}errors++;}}if(data53.diff !== undefined){let data60 = data53.diff;if((typeof data60 !== "string") && (data60 !== null)){const err114 = {instancePath:instancePath+"/payload/files/" + i0+"/diff",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/diff/type",keyword:"type",params:{type: schema172.properties.files.items.properties.diff.type},message:"must be string,null"};if(vErrors === null){vErrors = [err114];}else {vErrors.push(err114);}errors++;}if(typeof data60 === "string"){if(func1(data60) > 262144){const err115 = {instancePath:instancePath+"/payload/files/" + i0+"/diff",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/diff/maxLength",keyword:"maxLength",params:{limit: 262144},message:"must NOT have more than 262144 characters"};if(vErrors === null){vErrors = [err115];}else {vErrors.push(err115);}errors++;}}}}else {const err116 = {instancePath:instancePath+"/payload/files/" + i0,schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err116];}else {vErrors.push(err116);}errors++;}}}else {const err117 = {instancePath:instancePath+"/payload/files",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err117];}else {vErrors.push(err117);}errors++;}}if(data46.totals !== undefined){let data61 = data46.totals;if(data61 && typeof data61 == "object" && !Array.isArray(data61)){if(data61.files === undefined){const err118 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/required",keyword:"required",params:{missingProperty: "files"},message:"must have required property '"+"files"+"'"};if(vErrors === null){vErrors = [err118];}else {vErrors.push(err118);}errors++;}if(data61.additions === undefined){const err119 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/required",keyword:"required",params:{missingProperty: "additions"},message:"must have required property '"+"additions"+"'"};if(vErrors === null){vErrors = [err119];}else {vErrors.push(err119);}errors++;}if(data61.deletions === undefined){const err120 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/required",keyword:"required",params:{missingProperty: "deletions"},message:"must have required property '"+"deletions"+"'"};if(vErrors === null){vErrors = [err120];}else {vErrors.push(err120);}errors++;}for(const key3 in data61){if(!(((key3 === "files") || (key3 === "additions")) || (key3 === "deletions"))){const err121 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key3},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err121];}else {vErrors.push(err121);}errors++;}}if(data61.files !== undefined){let data62 = data61.files;if(!(((typeof data62 == "number") && (!(data62 % 1) && !isNaN(data62))) && (isFinite(data62)))){const err122 = {instancePath:instancePath+"/payload/totals/files",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/files/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err122];}else {vErrors.push(err122);}errors++;}if((typeof data62 == "number") && (isFinite(data62))){if(data62 < 0 || isNaN(data62)){const err123 = {instancePath:instancePath+"/payload/totals/files",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/files/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err123];}else {vErrors.push(err123);}errors++;}}}if(data61.additions !== undefined){let data63 = data61.additions;if((!(((typeof data63 == "number") && (!(data63 % 1) && !isNaN(data63))) && (isFinite(data63)))) && (data63 !== null)){const err124 = {instancePath:instancePath+"/payload/totals/additions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/additions/type",keyword:"type",params:{type: schema172.properties.totals.properties.additions.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err124];}else {vErrors.push(err124);}errors++;}if((typeof data63 == "number") && (isFinite(data63))){if(data63 < 0 || isNaN(data63)){const err125 = {instancePath:instancePath+"/payload/totals/additions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/additions/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err125];}else {vErrors.push(err125);}errors++;}}}if(data61.deletions !== undefined){let data64 = data61.deletions;if((!(((typeof data64 == "number") && (!(data64 % 1) && !isNaN(data64))) && (isFinite(data64)))) && (data64 !== null)){const err126 = {instancePath:instancePath+"/payload/totals/deletions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/deletions/type",keyword:"type",params:{type: schema172.properties.totals.properties.deletions.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err126];}else {vErrors.push(err126);}errors++;}if((typeof data64 == "number") && (isFinite(data64))){if(data64 < 0 || isNaN(data64)){const err127 = {instancePath:instancePath+"/payload/totals/deletions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/deletions/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err127];}else {vErrors.push(err127);}errors++;}}}}else {const err128 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err128];}else {vErrors.push(err128);}errors++;}}if(data46.patchArtifactRef !== undefined){let data65 = data46.patchArtifactRef;if((typeof data65 !== "string") && (data65 !== null)){const err129 = {instancePath:instancePath+"/payload/patchArtifactRef",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/patchArtifactRef/type",keyword:"type",params:{type: schema172.properties.patchArtifactRef.type},message:"must be string,null"};if(vErrors === null){vErrors = [err129];}else {vErrors.push(err129);}errors++;}if(typeof data65 === "string"){if(func1(data65) > 2048){const err130 = {instancePath:instancePath+"/payload/patchArtifactRef",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/patchArtifactRef/maxLength",keyword:"maxLength",params:{limit: 2048},message:"must NOT have more than 2048 characters"};if(vErrors === null){vErrors = [err130];}else {vErrors.push(err130);}errors++;}}}}else {const err131 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err131];}else {vErrors.push(err131);}errors++;}}}var _valid18 = _errs133 === errors;valid55 = _valid18;if(valid55){var props17 = {};props17.payload = true;props17.eventType = true;}}if(!valid55){const err132 = {instancePath,schemaPath:"#/allOf/17/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err132];}else {vErrors.push(err132);}errors++;}if(props0 !== true && props17 !== undefined){if(props17 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props17);}}const _errs180 = errors;let valid65 = true;const _errs181 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("workspace.diff.recorded" !== data.eventType){const err133 = {};if(vErrors === null){vErrors = [err133];}else {vErrors.push(err133);}errors++;}}}var _valid20 = _errs181 === errors;errors = _errs180;if(vErrors !== null){if(_errs180){vErrors.length = _errs180;}else {vErrors = null;}}if(_valid20){const _errs183 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data67 = data.payload;if(data67 && typeof data67 == "object" && !Array.isArray(data67)){if(data67.schema === undefined){const err134 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err134];}else {vErrors.push(err134);}errors++;}if(data67.changeSetId === undefined){const err135 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "changeSetId"},message:"must have required property '"+"changeSetId"+"'"};if(vErrors === null){vErrors = [err135];}else {vErrors.push(err135);}errors++;}if(data67.revision === undefined){const err136 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "revision"},message:"must have required property '"+"revision"+"'"};if(vErrors === null){vErrors = [err136];}else {vErrors.push(err136);}errors++;}if(data67.source === undefined){const err137 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "source"},message:"must have required property '"+"source"+"'"};if(vErrors === null){vErrors = [err137];}else {vErrors.push(err137);}errors++;}if(data67.complete === undefined){const err138 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "complete"},message:"must have required property '"+"complete"+"'"};if(vErrors === null){vErrors = [err138];}else {vErrors.push(err138);}errors++;}if(data67.files === undefined){const err139 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "files"},message:"must have required property '"+"files"+"'"};if(vErrors === null){vErrors = [err139];}else {vErrors.push(err139);}errors++;}if(data67.totals === undefined){const err140 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "totals"},message:"must have required property '"+"totals"+"'"};if(vErrors === null){vErrors = [err140];}else {vErrors.push(err140);}errors++;}if(data67.patchArtifactRef === undefined){const err141 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "patchArtifactRef"},message:"must have required property '"+"patchArtifactRef"+"'"};if(vErrors === null){vErrors = [err141];}else {vErrors.push(err141);}errors++;}for(const key4 in data67){if(!((((((((key4 === "schema") || (key4 === "changeSetId")) || (key4 === "revision")) || (key4 === "source")) || (key4 === "complete")) || (key4 === "files")) || (key4 === "totals")) || (key4 === "patchArtifactRef"))){const err142 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key4},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err142];}else {vErrors.push(err142);}errors++;}}if(data67.schema !== undefined){if("paperclip.workspace.diff.v1" !== data67.schema){const err143 = {instancePath:instancePath+"/payload/schema",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.workspace.diff.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err143];}else {vErrors.push(err143);}errors++;}}if(data67.changeSetId !== undefined){let data69 = data67.changeSetId;if(typeof data69 === "string"){if(func1(data69) > 200){const err144 = {instancePath:instancePath+"/payload/changeSetId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/changeSetId/maxLength",keyword:"maxLength",params:{limit: 200},message:"must NOT have more than 200 characters"};if(vErrors === null){vErrors = [err144];}else {vErrors.push(err144);}errors++;}if(func1(data69) < 1){const err145 = {instancePath:instancePath+"/payload/changeSetId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/changeSetId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err145];}else {vErrors.push(err145);}errors++;}}else {const err146 = {instancePath:instancePath+"/payload/changeSetId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/changeSetId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err146];}else {vErrors.push(err146);}errors++;}}if(data67.revision !== undefined){let data70 = data67.revision;if(!(((typeof data70 == "number") && (!(data70 % 1) && !isNaN(data70))) && (isFinite(data70)))){const err147 = {instancePath:instancePath+"/payload/revision",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/revision/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err147];}else {vErrors.push(err147);}errors++;}if((typeof data70 == "number") && (isFinite(data70))){if(data70 < 1 || isNaN(data70)){const err148 = {instancePath:instancePath+"/payload/revision",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/revision/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err148];}else {vErrors.push(err148);}errors++;}}}if(data67.source !== undefined){let data71 = data67.source;if(!((data71 === "harness_reported") || (data71 === "runner_verified"))){const err149 = {instancePath:instancePath+"/payload/source",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/source/enum",keyword:"enum",params:{allowedValues: schema172.properties.source.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err149];}else {vErrors.push(err149);}errors++;}}if(data67.complete !== undefined){if(typeof data67.complete !== "boolean"){const err150 = {instancePath:instancePath+"/payload/complete",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/complete/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err150];}else {vErrors.push(err150);}errors++;}}if(data67.files !== undefined){let data73 = data67.files;if(Array.isArray(data73)){if(data73.length > 2000){const err151 = {instancePath:instancePath+"/payload/files",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/maxItems",keyword:"maxItems",params:{limit: 2000},message:"must NOT have more than 2000 items"};if(vErrors === null){vErrors = [err151];}else {vErrors.push(err151);}errors++;}const len1 = data73.length;for(let i1=0; i1 1024){const err160 = {instancePath:instancePath+"/payload/files/" + i1+"/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/path/maxLength",keyword:"maxLength",params:{limit: 1024},message:"must NOT have more than 1024 characters"};if(vErrors === null){vErrors = [err160];}else {vErrors.push(err160);}errors++;}if(func1(data75) < 1){const err161 = {instancePath:instancePath+"/payload/files/" + i1+"/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/path/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err161];}else {vErrors.push(err161);}errors++;}if(!pattern69.test(data75)){const err162 = {instancePath:instancePath+"/payload/files/" + i1+"/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/path/pattern",keyword:"pattern",params:{pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"},message:"must match pattern \""+"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"+"\""};if(vErrors === null){vErrors = [err162];}else {vErrors.push(err162);}errors++;}}else {const err163 = {instancePath:instancePath+"/payload/files/" + i1+"/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/path/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err163];}else {vErrors.push(err163);}errors++;}}if(data74.operation !== undefined){let data76 = data74.operation;if(!(((((data76 === "create") || (data76 === "modify")) || (data76 === "delete")) || (data76 === "rename")) || (data76 === "mode_change"))){const err164 = {instancePath:instancePath+"/payload/files/" + i1+"/operation",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/operation/enum",keyword:"enum",params:{allowedValues: schema172.properties.files.items.properties.operation.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err164];}else {vErrors.push(err164);}errors++;}}if(data74.previousPath !== undefined){let data77 = data74.previousPath;const _errs206 = errors;let valid74 = false;let passing2 = null;const _errs207 = errors;if(data77 !== null){const err165 = {instancePath:instancePath+"/payload/files/" + i1+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/0/type",keyword:"type",params:{type: "null"},message:"must be null"};if(vErrors === null){vErrors = [err165];}else {vErrors.push(err165);}errors++;}var _valid21 = _errs207 === errors;if(_valid21){valid74 = true;passing2 = 0;}const _errs209 = errors;if(typeof data77 === "string"){if(func1(data77) > 1024){const err166 = {instancePath:instancePath+"/payload/files/" + i1+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/1/maxLength",keyword:"maxLength",params:{limit: 1024},message:"must NOT have more than 1024 characters"};if(vErrors === null){vErrors = [err166];}else {vErrors.push(err166);}errors++;}if(func1(data77) < 1){const err167 = {instancePath:instancePath+"/payload/files/" + i1+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/1/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err167];}else {vErrors.push(err167);}errors++;}if(!pattern69.test(data77)){const err168 = {instancePath:instancePath+"/payload/files/" + i1+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/1/pattern",keyword:"pattern",params:{pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"},message:"must match pattern \""+"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"+"\""};if(vErrors === null){vErrors = [err168];}else {vErrors.push(err168);}errors++;}}else {const err169 = {instancePath:instancePath+"/payload/files/" + i1+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/1/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err169];}else {vErrors.push(err169);}errors++;}var _valid21 = _errs209 === errors;if(_valid21 && valid74){valid74 = false;passing2 = [passing2, 1];}else {if(_valid21){valid74 = true;passing2 = 1;}}if(!valid74){const err170 = {instancePath:instancePath+"/payload/files/" + i1+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf",keyword:"oneOf",params:{passingSchemas: passing2},message:"must match exactly one schema in oneOf"};if(vErrors === null){vErrors = [err170];}else {vErrors.push(err170);}errors++;}else {errors = _errs206;if(vErrors !== null){if(_errs206){vErrors.length = _errs206;}else {vErrors = null;}}}}if(data74.additions !== undefined){let data78 = data74.additions;if((!(((typeof data78 == "number") && (!(data78 % 1) && !isNaN(data78))) && (isFinite(data78)))) && (data78 !== null)){const err171 = {instancePath:instancePath+"/payload/files/" + i1+"/additions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/additions/type",keyword:"type",params:{type: schema172.properties.files.items.properties.additions.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err171];}else {vErrors.push(err171);}errors++;}if((typeof data78 == "number") && (isFinite(data78))){if(data78 < 0 || isNaN(data78)){const err172 = {instancePath:instancePath+"/payload/files/" + i1+"/additions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/additions/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err172];}else {vErrors.push(err172);}errors++;}}}if(data74.deletions !== undefined){let data79 = data74.deletions;if((!(((typeof data79 == "number") && (!(data79 % 1) && !isNaN(data79))) && (isFinite(data79)))) && (data79 !== null)){const err173 = {instancePath:instancePath+"/payload/files/" + i1+"/deletions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/deletions/type",keyword:"type",params:{type: schema172.properties.files.items.properties.deletions.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err173];}else {vErrors.push(err173);}errors++;}if((typeof data79 == "number") && (isFinite(data79))){if(data79 < 0 || isNaN(data79)){const err174 = {instancePath:instancePath+"/payload/files/" + i1+"/deletions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/deletions/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err174];}else {vErrors.push(err174);}errors++;}}}if(data74.binary !== undefined){if(typeof data74.binary !== "boolean"){const err175 = {instancePath:instancePath+"/payload/files/" + i1+"/binary",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/binary/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err175];}else {vErrors.push(err175);}errors++;}}if(data74.diff !== undefined){let data81 = data74.diff;if((typeof data81 !== "string") && (data81 !== null)){const err176 = {instancePath:instancePath+"/payload/files/" + i1+"/diff",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/diff/type",keyword:"type",params:{type: schema172.properties.files.items.properties.diff.type},message:"must be string,null"};if(vErrors === null){vErrors = [err176];}else {vErrors.push(err176);}errors++;}if(typeof data81 === "string"){if(func1(data81) > 262144){const err177 = {instancePath:instancePath+"/payload/files/" + i1+"/diff",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/diff/maxLength",keyword:"maxLength",params:{limit: 262144},message:"must NOT have more than 262144 characters"};if(vErrors === null){vErrors = [err177];}else {vErrors.push(err177);}errors++;}}}}else {const err178 = {instancePath:instancePath+"/payload/files/" + i1,schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err178];}else {vErrors.push(err178);}errors++;}}}else {const err179 = {instancePath:instancePath+"/payload/files",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err179];}else {vErrors.push(err179);}errors++;}}if(data67.totals !== undefined){let data82 = data67.totals;if(data82 && typeof data82 == "object" && !Array.isArray(data82)){if(data82.files === undefined){const err180 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/required",keyword:"required",params:{missingProperty: "files"},message:"must have required property '"+"files"+"'"};if(vErrors === null){vErrors = [err180];}else {vErrors.push(err180);}errors++;}if(data82.additions === undefined){const err181 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/required",keyword:"required",params:{missingProperty: "additions"},message:"must have required property '"+"additions"+"'"};if(vErrors === null){vErrors = [err181];}else {vErrors.push(err181);}errors++;}if(data82.deletions === undefined){const err182 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/required",keyword:"required",params:{missingProperty: "deletions"},message:"must have required property '"+"deletions"+"'"};if(vErrors === null){vErrors = [err182];}else {vErrors.push(err182);}errors++;}for(const key6 in data82){if(!(((key6 === "files") || (key6 === "additions")) || (key6 === "deletions"))){const err183 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key6},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err183];}else {vErrors.push(err183);}errors++;}}if(data82.files !== undefined){let data83 = data82.files;if(!(((typeof data83 == "number") && (!(data83 % 1) && !isNaN(data83))) && (isFinite(data83)))){const err184 = {instancePath:instancePath+"/payload/totals/files",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/files/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err184];}else {vErrors.push(err184);}errors++;}if((typeof data83 == "number") && (isFinite(data83))){if(data83 < 0 || isNaN(data83)){const err185 = {instancePath:instancePath+"/payload/totals/files",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/files/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err185];}else {vErrors.push(err185);}errors++;}}}if(data82.additions !== undefined){let data84 = data82.additions;if((!(((typeof data84 == "number") && (!(data84 % 1) && !isNaN(data84))) && (isFinite(data84)))) && (data84 !== null)){const err186 = {instancePath:instancePath+"/payload/totals/additions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/additions/type",keyword:"type",params:{type: schema172.properties.totals.properties.additions.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err186];}else {vErrors.push(err186);}errors++;}if((typeof data84 == "number") && (isFinite(data84))){if(data84 < 0 || isNaN(data84)){const err187 = {instancePath:instancePath+"/payload/totals/additions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/additions/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err187];}else {vErrors.push(err187);}errors++;}}}if(data82.deletions !== undefined){let data85 = data82.deletions;if((!(((typeof data85 == "number") && (!(data85 % 1) && !isNaN(data85))) && (isFinite(data85)))) && (data85 !== null)){const err188 = {instancePath:instancePath+"/payload/totals/deletions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/deletions/type",keyword:"type",params:{type: schema172.properties.totals.properties.deletions.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err188];}else {vErrors.push(err188);}errors++;}if((typeof data85 == "number") && (isFinite(data85))){if(data85 < 0 || isNaN(data85)){const err189 = {instancePath:instancePath+"/payload/totals/deletions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/deletions/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err189];}else {vErrors.push(err189);}errors++;}}}}else {const err190 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err190];}else {vErrors.push(err190);}errors++;}}if(data67.patchArtifactRef !== undefined){let data86 = data67.patchArtifactRef;if((typeof data86 !== "string") && (data86 !== null)){const err191 = {instancePath:instancePath+"/payload/patchArtifactRef",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/patchArtifactRef/type",keyword:"type",params:{type: schema172.properties.patchArtifactRef.type},message:"must be string,null"};if(vErrors === null){vErrors = [err191];}else {vErrors.push(err191);}errors++;}if(typeof data86 === "string"){if(func1(data86) > 2048){const err192 = {instancePath:instancePath+"/payload/patchArtifactRef",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/patchArtifactRef/maxLength",keyword:"maxLength",params:{limit: 2048},message:"must NOT have more than 2048 characters"};if(vErrors === null){vErrors = [err192];}else {vErrors.push(err192);}errors++;}}}}else {const err193 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err193];}else {vErrors.push(err193);}errors++;}if(data67 && typeof data67 == "object" && !Array.isArray(data67)){if(data67.complete !== undefined){if(true !== data67.complete){const err194 = {instancePath:instancePath+"/payload/complete",schemaPath:"#/allOf/18/then/properties/payload/allOf/1/properties/complete/const",keyword:"const",params:{allowedValue: true},message:"must be equal to constant"};if(vErrors === null){vErrors = [err194];}else {vErrors.push(err194);}errors++;}}}else {const err195 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/18/then/properties/payload/allOf/1/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err195];}else {vErrors.push(err195);}errors++;}}}var _valid20 = _errs183 === errors;valid65 = _valid20;if(valid65){var props18 = {};props18.payload = true;props18.eventType = true;}}if(!valid65){const err196 = {instancePath,schemaPath:"#/allOf/18/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err196];}else {vErrors.push(err196);}errors++;}if(props0 !== true && props18 !== undefined){if(props18 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props18);}}const _errs234 = errors;let valid77 = true;const _errs235 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data88 = data.eventType;if(!((data88 === "semantic_tool.input") || (data88 === "mcp_app.tool_input"))){const err197 = {};if(vErrors === null){vErrors = [err197];}else {vErrors.push(err197);}errors++;}}}var _valid22 = _errs235 === errors;errors = _errs234;if(vErrors !== null){if(_errs234){vErrors.length = _errs234;}else {vErrors = null;}}if(_valid22){const _errs237 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data89 = data.payload;if(data89 && typeof data89 == "object" && !Array.isArray(data89)){if(data89.semantic_tool !== undefined){let data90 = data89.semantic_tool;if(!(validate92(data90, {instancePath:instancePath+"/payload/semantic_tool",parentData:data89,parentDataProperty:"semantic_tool",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate92.errors : vErrors.concat(validate92.errors);errors = vErrors.length;}if(data90 && typeof data90 == "object" && !Array.isArray(data90)){if(data90.phase !== undefined){if("input" !== data90.phase){const err198 = {instancePath:instancePath+"/payload/semantic_tool/phase",schemaPath:"#/allOf/19/then/properties/payload/properties/semantic_tool/allOf/1/properties/phase/const",keyword:"const",params:{allowedValue: "input"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err198];}else {vErrors.push(err198);}errors++;}}}else {const err199 = {instancePath:instancePath+"/payload/semantic_tool",schemaPath:"#/allOf/19/then/properties/payload/properties/semantic_tool/allOf/1/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err199];}else {vErrors.push(err199);}errors++;}}}else {const err200 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/19/then/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err200];}else {vErrors.push(err200);}errors++;}}}var _valid22 = _errs237 === errors;valid77 = _valid22;if(valid77){var props19 = {};props19.payload = true;props19.eventType = true;}}if(!valid77){const err201 = {instancePath,schemaPath:"#/allOf/19/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err201];}else {vErrors.push(err201);}errors++;}if(props0 !== true && props19 !== undefined){if(props19 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props19);}}const _errs247 = errors;let valid83 = true;const _errs248 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data92 = data.eventType;if(!((data92 === "semantic_tool.result") || (data92 === "mcp_app.tool_result"))){const err202 = {};if(vErrors === null){vErrors = [err202];}else {vErrors.push(err202);}errors++;}}}var _valid23 = _errs248 === errors;errors = _errs247;if(vErrors !== null){if(_errs247){vErrors.length = _errs247;}else {vErrors = null;}}if(_valid23){const _errs250 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data93 = data.payload;if(data93 && typeof data93 == "object" && !Array.isArray(data93)){if(data93.semantic_tool !== undefined){let data94 = data93.semantic_tool;if(!(validate92(data94, {instancePath:instancePath+"/payload/semantic_tool",parentData:data93,parentDataProperty:"semantic_tool",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate92.errors : vErrors.concat(validate92.errors);errors = vErrors.length;}if(data94 && typeof data94 == "object" && !Array.isArray(data94)){if(data94.phase !== undefined){if("result" !== data94.phase){const err203 = {instancePath:instancePath+"/payload/semantic_tool/phase",schemaPath:"#/allOf/20/then/properties/payload/properties/semantic_tool/allOf/1/properties/phase/const",keyword:"const",params:{allowedValue: "result"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err203];}else {vErrors.push(err203);}errors++;}}}else {const err204 = {instancePath:instancePath+"/payload/semantic_tool",schemaPath:"#/allOf/20/then/properties/payload/properties/semantic_tool/allOf/1/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err204];}else {vErrors.push(err204);}errors++;}}}else {const err205 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/20/then/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err205];}else {vErrors.push(err205);}errors++;}}}var _valid23 = _errs250 === errors;valid83 = _valid23;if(valid83){var props20 = {};props20.payload = true;props20.eventType = true;}}if(!valid83){const err206 = {instancePath,schemaPath:"#/allOf/20/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err206];}else {vErrors.push(err206);}errors++;}if(props0 !== true && props20 !== undefined){if(props20 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props20);}}const _errs260 = errors;let valid89 = true;const _errs261 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("semantic_tool.reconciled" !== data.eventType){const err207 = {};if(vErrors === null){vErrors = [err207];}else {vErrors.push(err207);}errors++;}}}var _valid24 = _errs261 === errors;errors = _errs260;if(vErrors !== null){if(_errs260){vErrors.length = _errs260;}else {vErrors = null;}}if(_valid24){const _errs263 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data97 = data.payload;if(data97 && typeof data97 == "object" && !Array.isArray(data97)){if(data97.semantic_tool !== undefined){let data98 = data97.semantic_tool;if(!(validate92(data98, {instancePath:instancePath+"/payload/semantic_tool",parentData:data97,parentDataProperty:"semantic_tool",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate92.errors : vErrors.concat(validate92.errors);errors = vErrors.length;}if(data98 && typeof data98 == "object" && !Array.isArray(data98)){if(data98.phase !== undefined){if("reconciled" !== data98.phase){const err208 = {instancePath:instancePath+"/payload/semantic_tool/phase",schemaPath:"#/allOf/21/then/properties/payload/properties/semantic_tool/allOf/1/properties/phase/const",keyword:"const",params:{allowedValue: "reconciled"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err208];}else {vErrors.push(err208);}errors++;}}}else {const err209 = {instancePath:instancePath+"/payload/semantic_tool",schemaPath:"#/allOf/21/then/properties/payload/properties/semantic_tool/allOf/1/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err209];}else {vErrors.push(err209);}errors++;}}}else {const err210 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/21/then/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err210];}else {vErrors.push(err210);}errors++;}}}var _valid24 = _errs263 === errors;valid89 = _valid24;if(valid89){var props21 = {};props21.payload = true;props21.eventType = true;}}if(!valid89){const err211 = {instancePath,schemaPath:"#/allOf/21/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err211];}else {vErrors.push(err211);}errors++;}if(props0 !== true && props21 !== undefined){if(props21 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props21);}}const _errs273 = errors;let valid95 = true;const _errs274 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("run.terminal" !== data.eventType){const err212 = {};if(vErrors === null){vErrors = [err212];}else {vErrors.push(err212);}errors++;}}}var _valid25 = _errs274 === errors;errors = _errs273;if(vErrors !== null){if(_errs273){vErrors.length = _errs273;}else {vErrors = null;}}if(_valid25){const _errs276 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate104(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate104.errors : vErrors.concat(validate104.errors);errors = vErrors.length;}}}var _valid25 = _errs276 === errors;valid95 = _valid25;if(valid95){var props22 = {};props22.payload = true;props22.eventType = true;}}if(!valid95){const err213 = {instancePath,schemaPath:"#/allOf/22/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err213];}else {vErrors.push(err213);}errors++;}if(props0 !== true && props22 !== undefined){if(props22 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props22);}}const _errs279 = errors;let valid98 = true;const _errs280 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("run.result.proposed" !== data.eventType){const err214 = {};if(vErrors === null){vErrors = [err214];}else {vErrors.push(err214);}errors++;}}}var _valid26 = _errs280 === errors;errors = _errs279;if(vErrors !== null){if(_errs279){vErrors.length = _errs279;}else {vErrors = null;}}if(_valid26){const _errs282 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data103 = data.payload;const _errs287 = errors;let valid103 = true;const _errs288 = errors;if(data103 && typeof data103 == "object" && !Array.isArray(data103)){if(data103.reportedWorkDisposition !== undefined){if("blocked" !== data103.reportedWorkDisposition){const err215 = {};if(vErrors === null){vErrors = [err215];}else {vErrors.push(err215);}errors++;}}}var _valid27 = _errs288 === errors;errors = _errs287;if(vErrors !== null){if(_errs287){vErrors.length = _errs287;}else {vErrors = null;}}if(_valid27){const _errs290 = errors;if(data103 && typeof data103 == "object" && !Array.isArray(data103)){if(data103.blocker === undefined){const err216 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/allOf/0/then/required",keyword:"required",params:{missingProperty: "blocker"},message:"must have required property '"+"blocker"+"'"};if(vErrors === null){vErrors = [err216];}else {vErrors.push(err216);}errors++;}}var _valid27 = _errs290 === errors;valid103 = _valid27;}if(!valid103){const err217 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/allOf/0/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err217];}else {vErrors.push(err217);}errors++;}const _errs292 = errors;let valid105 = true;const _errs293 = errors;if(data103 && typeof data103 == "object" && !Array.isArray(data103)){if(data103.reportedWorkDisposition !== undefined){if("yielded" !== data103.reportedWorkDisposition){const err218 = {};if(vErrors === null){vErrors = [err218];}else {vErrors.push(err218);}errors++;}}}var _valid28 = _errs293 === errors;errors = _errs292;if(vErrors !== null){if(_errs292){vErrors.length = _errs292;}else {vErrors = null;}}if(_valid28){const _errs295 = errors;if(data103 && typeof data103 == "object" && !Array.isArray(data103)){if(data103.continuation === undefined){const err219 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/allOf/1/then/required",keyword:"required",params:{missingProperty: "continuation"},message:"must have required property '"+"continuation"+"'"};if(vErrors === null){vErrors = [err219];}else {vErrors.push(err219);}errors++;}}var _valid28 = _errs295 === errors;valid105 = _valid28;}if(!valid105){const err220 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/allOf/1/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err220];}else {vErrors.push(err220);}errors++;}if(data103 && typeof data103 == "object" && !Array.isArray(data103)){if(data103.schema === undefined){const err221 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err221];}else {vErrors.push(err221);}errors++;}if(data103.reportedWorkDisposition === undefined){const err222 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/required",keyword:"required",params:{missingProperty: "reportedWorkDisposition"},message:"must have required property '"+"reportedWorkDisposition"+"'"};if(vErrors === null){vErrors = [err222];}else {vErrors.push(err222);}errors++;}if(data103.summary === undefined){const err223 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/required",keyword:"required",params:{missingProperty: "summary"},message:"must have required property '"+"summary"+"'"};if(vErrors === null){vErrors = [err223];}else {vErrors.push(err223);}errors++;}if(data103.completionClaim === undefined){const err224 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/required",keyword:"required",params:{missingProperty: "completionClaim"},message:"must have required property '"+"completionClaim"+"'"};if(vErrors === null){vErrors = [err224];}else {vErrors.push(err224);}errors++;}if(data103.evidence === undefined){const err225 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/required",keyword:"required",params:{missingProperty: "evidence"},message:"must have required property '"+"evidence"+"'"};if(vErrors === null){vErrors = [err225];}else {vErrors.push(err225);}errors++;}if(data103.verification === undefined){const err226 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/required",keyword:"required",params:{missingProperty: "verification"},message:"must have required property '"+"verification"+"'"};if(vErrors === null){vErrors = [err226];}else {vErrors.push(err226);}errors++;}if(data103.attentionRequests === undefined){const err227 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/required",keyword:"required",params:{missingProperty: "attentionRequests"},message:"must have required property '"+"attentionRequests"+"'"};if(vErrors === null){vErrors = [err227];}else {vErrors.push(err227);}errors++;}if(data103.artifacts === undefined){const err228 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/required",keyword:"required",params:{missingProperty: "artifacts"},message:"must have required property '"+"artifacts"+"'"};if(vErrors === null){vErrors = [err228];}else {vErrors.push(err228);}errors++;}if(data103.schema !== undefined){if("paperclip.run_result.v1" !== data103.schema){const err229 = {instancePath:instancePath+"/payload/schema",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.run_result.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err229];}else {vErrors.push(err229);}errors++;}}if(data103.reportedWorkDisposition !== undefined){let data107 = data103.reportedWorkDisposition;if(!((((data107 === "done") || (data107 === "blocked")) || (data107 === "needs_review")) || (data107 === "yielded"))){const err230 = {instancePath:instancePath+"/payload/reportedWorkDisposition",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/reportedWorkDisposition/enum",keyword:"enum",params:{allowedValues: schema199.properties.reportedWorkDisposition.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err230];}else {vErrors.push(err230);}errors++;}}if(data103.summary !== undefined){let data108 = data103.summary;if(typeof data108 === "string"){if(func1(data108) > 12000){const err231 = {instancePath:instancePath+"/payload/summary",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/summary/maxLength",keyword:"maxLength",params:{limit: 12000},message:"must NOT have more than 12000 characters"};if(vErrors === null){vErrors = [err231];}else {vErrors.push(err231);}errors++;}if(func1(data108) < 1){const err232 = {instancePath:instancePath+"/payload/summary",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/summary/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err232];}else {vErrors.push(err232);}errors++;}}else {const err233 = {instancePath:instancePath+"/payload/summary",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/summary/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err233];}else {vErrors.push(err233);}errors++;}}if(data103.completionClaim !== undefined){let data109 = data103.completionClaim;if(data109 && typeof data109 == "object" && !Array.isArray(data109)){if(data109.contractRevision === undefined){const err234 = {instancePath:instancePath+"/payload/completionClaim",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/required",keyword:"required",params:{missingProperty: "contractRevision"},message:"must have required property '"+"contractRevision"+"'"};if(vErrors === null){vErrors = [err234];}else {vErrors.push(err234);}errors++;}if(data109.objectiveSatisfied === undefined){const err235 = {instancePath:instancePath+"/payload/completionClaim",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/required",keyword:"required",params:{missingProperty: "objectiveSatisfied"},message:"must have required property '"+"objectiveSatisfied"+"'"};if(vErrors === null){vErrors = [err235];}else {vErrors.push(err235);}errors++;}if(data109.criteria === undefined){const err236 = {instancePath:instancePath+"/payload/completionClaim",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/required",keyword:"required",params:{missingProperty: "criteria"},message:"must have required property '"+"criteria"+"'"};if(vErrors === null){vErrors = [err236];}else {vErrors.push(err236);}errors++;}if(data109.remainingWork === undefined){const err237 = {instancePath:instancePath+"/payload/completionClaim",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/required",keyword:"required",params:{missingProperty: "remainingWork"},message:"must have required property '"+"remainingWork"+"'"};if(vErrors === null){vErrors = [err237];}else {vErrors.push(err237);}errors++;}if(data109.contractRevision !== undefined){let data110 = data109.contractRevision;if(typeof data110 === "string"){if(func1(data110) < 1){const err238 = {instancePath:instancePath+"/payload/completionClaim/contractRevision",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/properties/contractRevision/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err238];}else {vErrors.push(err238);}errors++;}}else {const err239 = {instancePath:instancePath+"/payload/completionClaim/contractRevision",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/properties/contractRevision/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err239];}else {vErrors.push(err239);}errors++;}}if(data109.objectiveSatisfied !== undefined){if(typeof data109.objectiveSatisfied !== "boolean"){const err240 = {instancePath:instancePath+"/payload/completionClaim/objectiveSatisfied",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/properties/objectiveSatisfied/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err240];}else {vErrors.push(err240);}errors++;}}if(data109.criteria !== undefined){let data112 = data109.criteria;if(Array.isArray(data112)){const len2 = data112.length;for(let i2=0; i2 160){const err329 = {instancePath:instancePath+"/sourceEventId",schemaPath:"#/properties/sourceEventId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err329];}else {vErrors.push(err329);}errors++;}if(func1(data155) < 1){const err330 = {instancePath:instancePath+"/sourceEventId",schemaPath:"#/properties/sourceEventId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err330];}else {vErrors.push(err330);}errors++;}}else {const err331 = {instancePath:instancePath+"/sourceEventId",schemaPath:"#/properties/sourceEventId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err331];}else {vErrors.push(err331);}errors++;}}if(data.sourceSeq !== undefined){let data156 = data.sourceSeq;if(!(((typeof data156 == "number") && (!(data156 % 1) && !isNaN(data156))) && (isFinite(data156)))){const err332 = {instancePath:instancePath+"/sourceSeq",schemaPath:"#/properties/sourceSeq/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err332];}else {vErrors.push(err332);}errors++;}if((typeof data156 == "number") && (isFinite(data156))){if(data156 > 9007199254740991 || isNaN(data156)){const err333 = {instancePath:instancePath+"/sourceSeq",schemaPath:"#/properties/sourceSeq/maximum",keyword:"maximum",params:{comparison: "<=", limit: 9007199254740991},message:"must be <= 9007199254740991"};if(vErrors === null){vErrors = [err333];}else {vErrors.push(err333);}errors++;}if(data156 < 1 || isNaN(data156)){const err334 = {instancePath:instancePath+"/sourceSeq",schemaPath:"#/properties/sourceSeq/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err334];}else {vErrors.push(err334);}errors++;}}}if(data.sourceInstanceId !== undefined){let data157 = data.sourceInstanceId;if(typeof data157 === "string"){if(func1(data157) > 160){const err335 = {instancePath:instancePath+"/sourceInstanceId",schemaPath:"#/properties/sourceInstanceId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err335];}else {vErrors.push(err335);}errors++;}if(func1(data157) < 1){const err336 = {instancePath:instancePath+"/sourceInstanceId",schemaPath:"#/properties/sourceInstanceId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err336];}else {vErrors.push(err336);}errors++;}}else {const err337 = {instancePath:instancePath+"/sourceInstanceId",schemaPath:"#/properties/sourceInstanceId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err337];}else {vErrors.push(err337);}errors++;}}if(data.sourceKind !== undefined){let data158 = data.sourceKind;if(!((data158 === "runner") || (data158 === "control_plane"))){const err338 = {instancePath:instancePath+"/sourceKind",schemaPath:"#/properties/sourceKind/enum",keyword:"enum",params:{allowedValues: schema47.properties.sourceKind.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err338];}else {vErrors.push(err338);}errors++;}}if(data.runId !== undefined){let data159 = data.runId;if(typeof data159 === "string"){if(func1(data159) > 160){const err339 = {instancePath:instancePath+"/runId",schemaPath:"#/properties/runId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err339];}else {vErrors.push(err339);}errors++;}if(func1(data159) < 1){const err340 = {instancePath:instancePath+"/runId",schemaPath:"#/properties/runId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err340];}else {vErrors.push(err340);}errors++;}}else {const err341 = {instancePath:instancePath+"/runId",schemaPath:"#/properties/runId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err341];}else {vErrors.push(err341);}errors++;}}if(data.normalizedSessionId !== undefined){let data160 = data.normalizedSessionId;if(typeof data160 === "string"){if(func1(data160) > 160){const err342 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/properties/normalizedSessionId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err342];}else {vErrors.push(err342);}errors++;}if(func1(data160) < 1){const err343 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/properties/normalizedSessionId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err343];}else {vErrors.push(err343);}errors++;}}else {const err344 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/properties/normalizedSessionId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err344];}else {vErrors.push(err344);}errors++;}}if(data.turnId !== undefined){let data161 = data.turnId;if(typeof data161 === "string"){if(func1(data161) > 240){const err345 = {instancePath:instancePath+"/turnId",schemaPath:"#/properties/turnId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err345];}else {vErrors.push(err345);}errors++;}if(func1(data161) < 1){const err346 = {instancePath:instancePath+"/turnId",schemaPath:"#/properties/turnId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err346];}else {vErrors.push(err346);}errors++;}}else {const err347 = {instancePath:instancePath+"/turnId",schemaPath:"#/properties/turnId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err347];}else {vErrors.push(err347);}errors++;}}if(data.itemId !== undefined){let data162 = data.itemId;if(typeof data162 === "string"){if(func1(data162) > 240){const err348 = {instancePath:instancePath+"/itemId",schemaPath:"#/properties/itemId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err348];}else {vErrors.push(err348);}errors++;}if(func1(data162) < 1){const err349 = {instancePath:instancePath+"/itemId",schemaPath:"#/properties/itemId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err349];}else {vErrors.push(err349);}errors++;}}else {const err350 = {instancePath:instancePath+"/itemId",schemaPath:"#/properties/itemId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err350];}else {vErrors.push(err350);}errors++;}}if(data.eventType !== undefined){let data163 = data.eventType;if(!(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((data163 === "runner.connected") || (data163 === "runner.reconnected")) || (data163 === "runner.reconciled")) || (data163 === "runner.disconnected")) || (data163 === "runner.draining")) || (data163 === "runner.backpressure")) || (data163 === "runner.suspending")) || (data163 === "runner.suspended")) || (data163 === "runner.stopped")) || (data163 === "runner.diagnostic")) || (data163 === "runtime.phase.changed")) || (data163 === "sandbox.metric")) || (data163 === "workspace.ready")) || (data163 === "workspace.change.updated")) || (data163 === "workspace.diff.recorded")) || (data163 === "workspace.file.referenced")) || (data163 === "harness.starting")) || (data163 === "harness.ready")) || (data163 === "harness.exited")) || (data163 === "harness.diagnostic")) || (data163 === "plan.updated")) || (data163 === "tool.execution.started")) || (data163 === "tool.execution.progressed")) || (data163 === "tool.execution.completed")) || (data163 === "research.started")) || (data163 === "research.progressed")) || (data163 === "research.completed")) || (data163 === "delegation.started")) || (data163 === "delegation.updated")) || (data163 === "delegation.completed")) || (data163 === "model.route.changed")) || (data163 === "model.verification.updated")) || (data163 === "context.compacted")) || (data163 === "artifact.viewed")) || (data163 === "artifact.generated")) || (data163 === "review.mode.changed")) || (data163 === "hook.started")) || (data163 === "hook.completed")) || (data163 === "memory.citation.referenced")) || (data163 === "safety.review.started")) || (data163 === "safety.review.completed")) || (data163 === "terminal.input.sent")) || (data163 === "wait.started")) || (data163 === "wait.completed")) || (data163 === "provider.notice.recorded")) || (data163 === "session.starting")) || (data163 === "session.started")) || (data163 === "session.resuming")) || (data163 === "session.resumed")) || (data163 === "session.reconciled")) || (data163 === "session.updated")) || (data163 === "session.closed")) || (data163 === "session.failed")) || (data163 === "turn.submitted")) || (data163 === "turn.accepted")) || (data163 === "turn.started")) || (data163 === "turn.completed")) || (data163 === "turn.failed")) || (data163 === "turn.interrupted")) || (data163 === "turn.cancelled")) || (data163 === "item.started")) || (data163 === "item.delta")) || (data163 === "item.completed")) || (data163 === "item.failed")) || (data163 === "usage.reported")) || (data163 === "semantic_tool.input")) || (data163 === "semantic_tool.result")) || (data163 === "semantic_tool.reconciled")) || (data163 === "mcp_app.discovered")) || (data163 === "mcp_app.resource.resolved")) || (data163 === "mcp_app.initializing")) || (data163 === "mcp_app.ready")) || (data163 === "mcp_app.tool_input")) || (data163 === "mcp_app.tool_result")) || (data163 === "mcp_app.action.requested")) || (data163 === "mcp_app.action.resolved")) || (data163 === "mcp_app.host_context.changed")) || (data163 === "mcp_app.failed")) || (data163 === "mcp_app.teardown")) || (data163 === "runtime_request.created")) || (data163 === "runtime_request.resolved")) || (data163 === "runtime_request.expired")) || (data163 === "runtime_request.cancelled")) || (data163 === "interaction.request.proposed")) || (data163 === "interaction.request.materialized")) || (data163 === "interaction.request.rejected")) || (data163 === "interaction.response.progressed")) || (data163 === "interaction.response.resolved")) || (data163 === "interaction.response.delivered")) || (data163 === "run.attached")) || (data163 === "run.detached")) || (data163 === "run.result.proposed")) || (data163 === "run.result.accepted")) || (data163 === "run.result.rejected")) || (data163 === "attention.request.proposed")) || (data163 === "attention.request.routed")) || (data163 === "attention.request.resolved")) || (data163 === "attention.request.expired")) || (data163 === "attention.request.superseded")) || (data163 === "work.assessment.recorded")) || (data163 === "issue.status.decision.recorded")) || (data163 === "issue.status.decision.applied")) || (data163 === "issue.status.decision.rejected")) || (data163 === "issue.status.decision.superseded")) || (data163 === "run.terminal"))){const err351 = {instancePath:instancePath+"/eventType",schemaPath:"#/properties/eventType/enum",keyword:"enum",params:{allowedValues: schema47.properties.eventType.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err351];}else {vErrors.push(err351);}errors++;}}if(data.schemaVersion !== undefined){if(1 !== data.schemaVersion){const err352 = {instancePath:instancePath+"/schemaVersion",schemaPath:"#/properties/schemaVersion/const",keyword:"const",params:{allowedValue: 1},message:"must be equal to constant"};if(vErrors === null){vErrors = [err352];}else {vErrors.push(err352);}errors++;}}if(data.priority !== undefined){let data165 = data.priority;if(!(((data165 === 0) || (data165 === 1)) || (data165 === 2))){const err353 = {instancePath:instancePath+"/priority",schemaPath:"#/properties/priority/enum",keyword:"enum",params:{allowedValues: schema47.properties.priority.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err353];}else {vErrors.push(err353);}errors++;}}if(data.emittedAt !== undefined){let data166 = data.emittedAt;if(typeof data166 === "string"){if(!(formats0.test(data166))){const err354 = {instancePath:instancePath+"/emittedAt",schemaPath:"#/properties/emittedAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err354];}else {vErrors.push(err354);}errors++;}}else {const err355 = {instancePath:instancePath+"/emittedAt",schemaPath:"#/properties/emittedAt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err355];}else {vErrors.push(err355);}errors++;}}if(data.observedAt !== undefined){let data167 = data.observedAt;if(typeof data167 === "string"){if(!(formats0.test(data167))){const err356 = {instancePath:instancePath+"/observedAt",schemaPath:"#/properties/observedAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err356];}else {vErrors.push(err356);}errors++;}}else {const err357 = {instancePath:instancePath+"/observedAt",schemaPath:"#/properties/observedAt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err357];}else {vErrors.push(err357);}errors++;}}if(data.payload !== undefined){let data168 = data.payload;if(data168 && typeof data168 == "object" && !Array.isArray(data168)){if(data168.channel !== undefined){let data169 = data168.channel;if(!(((((data169 === "progress") || (data169 === "final")) || (data169 === "summary")) || (data169 === "detail")) || (data169 === "unknown"))){const err358 = {instancePath:instancePath+"/payload/channel",schemaPath:"#/properties/payload/properties/channel/enum",keyword:"enum",params:{allowedValues: schema47.properties.payload.properties.channel.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err358];}else {vErrors.push(err358);}errors++;}}if(data168.providerPhase !== undefined){if(typeof data168.providerPhase !== "string"){const err359 = {instancePath:instancePath+"/payload/providerPhase",schemaPath:"#/properties/payload/properties/providerPhase/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err359];}else {vErrors.push(err359);}errors++;}}if(data168.providerMethod !== undefined){if(typeof data168.providerMethod !== "string"){const err360 = {instancePath:instancePath+"/payload/providerMethod",schemaPath:"#/properties/payload/properties/providerMethod/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err360];}else {vErrors.push(err360);}errors++;}}}else {const err361 = {instancePath:instancePath+"/payload",schemaPath:"#/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err361];}else {vErrors.push(err361);}errors++;}}if(data.debug !== undefined){let data172 = data.debug;if(data172 && typeof data172 == "object" && !Array.isArray(data172)){}else {const err362 = {instancePath:instancePath+"/debug",schemaPath:"#/properties/debug/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err362];}else {vErrors.push(err362);}errors++;}}}else {const err363 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err363];}else {vErrors.push(err363);}errors++;}validate26.errors = vErrors;return errors === 0;}validate26.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema200 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v2/event.schema.json","title":"PRP v2 native event","type":"object","required":["schema","sourceEventId","sourceSeq","sourceInstanceId","sourceKind","runId","eventType","schemaVersion","priority","emittedAt","payload"],"properties":{"schema":{"const":"paperclip.prp.event.v2"},"sourceEventId":{"type":"string","minLength":1,"maxLength":160},"sourceSeq":{"type":"integer","minimum":1,"maximum":9007199254740991},"sourceInstanceId":{"type":"string","minLength":1,"maxLength":160},"sourceKind":{"enum":["runner","control_plane"]},"runId":{"type":"string","minLength":1,"maxLength":160},"normalizedSessionId":{"type":"string","minLength":1,"maxLength":160},"turnId":{"type":"string","minLength":1,"maxLength":160},"itemId":{"type":"string","minLength":1,"maxLength":160},"eventType":{"enum":["runner.connected","runner.reconnected","runner.reconciled","runner.disconnected","runner.draining","runner.suspending","runner.suspended","runner.stopped","runner.diagnostic","runtime.phase.changed","sandbox.metric","workspace.ready","workspace.change.updated","workspace.diff.recorded","workspace.file.referenced","harness.starting","harness.ready","harness.exited","harness.diagnostic","plan.updated","tool.execution.started","tool.execution.progressed","tool.execution.completed","research.started","research.progressed","research.completed","delegation.started","delegation.updated","delegation.completed","model.route.changed","model.verification.updated","context.compacted","artifact.viewed","artifact.generated","review.mode.changed","hook.started","hook.completed","memory.citation.referenced","safety.review.started","safety.review.completed","terminal.input.sent","wait.started","wait.completed","provider.notice.recorded","session.starting","session.started","session.resuming","session.resumed","session.reconciled","session.updated","session.closed","session.failed","session.capabilities.updated","session.goal.snapshot","session.goal.updated","session.goal.cleared","turn.submitted","turn.accepted","turn.started","turn.completed","turn.failed","turn.interrupted","turn.cancelled","item.started","item.delta","item.completed","item.failed","usage.reported","semantic_tool.input","semantic_tool.result","mcp_app.discovered","mcp_app.resource.resolved","mcp_app.initializing","mcp_app.ready","mcp_app.tool_input","mcp_app.tool_result","mcp_app.action.requested","mcp_app.action.resolved","mcp_app.host_context.changed","mcp_app.failed","mcp_app.teardown","runtime_request.created","runtime_request.resolved","runtime_request.expired","runtime_request.cancelled","interaction.request.proposed","interaction.request.materialized","interaction.request.rejected","interaction.response.progressed","interaction.response.resolved","interaction.response.delivered","run.attached","run.detached","run.result.proposed","run.result.accepted","run.result.rejected","attention.request.proposed","attention.request.routed","attention.request.resolved","attention.request.expired","attention.request.superseded","work.assessment.recorded","issue.status.decision.recorded","issue.status.decision.applied","issue.status.decision.rejected","issue.status.decision.superseded","run.terminal"]},"schemaVersion":{"const":2},"priority":{"enum":[0,1,2]},"emittedAt":{"type":"string","format":"date-time"},"observedAt":{"type":"string","format":"date-time"},"payload":{"type":"object","additionalProperties":true},"debug":{"type":"object","additionalProperties":true}},"allOf":[{"if":{"properties":{"eventType":{"enum":["session.goal.snapshot","session.goal.updated"]}}},"then":{"properties":{"payload":{"type":"object","required":["goal"],"properties":{"goal":{"oneOf":[{"$ref":"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot"},{"type":"null"}]}},"additionalProperties":true}}}},{"if":{"properties":{"eventType":{"const":"session.capabilities.updated"}}},"then":{"properties":{"payload":{"type":"object","required":["sessionGoals"],"properties":{"sessionGoals":{"$ref":"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability"}},"additionalProperties":true}}}}],"additionalProperties":true};const schema201 = {"type":"object","required":["objective","status","tokenBudget","tokensUsed","elapsedSeconds","iterations","lastReason","createdAt","updatedAt","completedAt","workingNow"],"properties":{"objective":{"type":"string","minLength":1,"maxLength":4000},"status":{"enum":["active","paused","blocked","limited","usage_limited","budget_limited","complete"]},"tokenBudget":{"type":["integer","null"],"minimum":1},"tokensUsed":{"type":["integer","null"],"minimum":0},"elapsedSeconds":{"type":["number","null"],"minimum":0},"iterations":{"type":["integer","null"],"minimum":0},"lastReason":{"type":["string","null"],"maxLength":4000},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"},"completedAt":{"type":["string","null"],"format":"date-time"},"workingNow":{"type":"boolean"}},"additionalProperties":true};function validate109(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){/*# sourceURL="https://paperclip.dev/schemas/prp/v2/event.schema.json" */;let vErrors = null;let errors = 0;const evaluated0 = validate109.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}const _errs2 = errors;let valid1 = true;const _errs3 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data0 = data.eventType;if(!((data0 === "session.goal.snapshot") || (data0 === "session.goal.updated"))){const err0 = {};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}}}var _valid0 = _errs3 === errors;errors = _errs2;if(vErrors !== null){if(_errs2){vErrors.length = _errs2;}else {vErrors = null;}}if(_valid0){const _errs5 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data1 = data.payload;if(data1 && typeof data1 == "object" && !Array.isArray(data1)){if(data1.goal === undefined){const err1 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/0/then/properties/payload/required",keyword:"required",params:{missingProperty: "goal"},message:"must have required property '"+"goal"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data1.goal !== undefined){let data2 = data1.goal;const _errs10 = errors;let valid5 = false;let passing0 = null;const _errs11 = errors;if(data2 && typeof data2 == "object" && !Array.isArray(data2)){if(data2.objective === undefined){const err2 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "objective"},message:"must have required property '"+"objective"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data2.status === undefined){const err3 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data2.tokenBudget === undefined){const err4 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "tokenBudget"},message:"must have required property '"+"tokenBudget"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data2.tokensUsed === undefined){const err5 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "tokensUsed"},message:"must have required property '"+"tokensUsed"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data2.elapsedSeconds === undefined){const err6 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "elapsedSeconds"},message:"must have required property '"+"elapsedSeconds"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(data2.iterations === undefined){const err7 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "iterations"},message:"must have required property '"+"iterations"+"'"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(data2.lastReason === undefined){const err8 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "lastReason"},message:"must have required property '"+"lastReason"+"'"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(data2.createdAt === undefined){const err9 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "createdAt"},message:"must have required property '"+"createdAt"+"'"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(data2.updatedAt === undefined){const err10 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "updatedAt"},message:"must have required property '"+"updatedAt"+"'"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(data2.completedAt === undefined){const err11 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "completedAt"},message:"must have required property '"+"completedAt"+"'"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(data2.workingNow === undefined){const err12 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "workingNow"},message:"must have required property '"+"workingNow"+"'"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(data2.objective !== undefined){let data3 = data2.objective;if(typeof data3 === "string"){if(func1(data3) > 4000){const err13 = {instancePath:instancePath+"/payload/goal/objective",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/objective/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(func1(data3) < 1){const err14 = {instancePath:instancePath+"/payload/goal/objective",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/objective/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}else {const err15 = {instancePath:instancePath+"/payload/goal/objective",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/objective/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}if(data2.status !== undefined){let data4 = data2.status;if(!(((((((data4 === "active") || (data4 === "paused")) || (data4 === "blocked")) || (data4 === "limited")) || (data4 === "usage_limited")) || (data4 === "budget_limited")) || (data4 === "complete"))){const err16 = {instancePath:instancePath+"/payload/goal/status",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/status/enum",keyword:"enum",params:{allowedValues: schema201.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}if(data2.tokenBudget !== undefined){let data5 = data2.tokenBudget;if((!(((typeof data5 == "number") && (!(data5 % 1) && !isNaN(data5))) && (isFinite(data5)))) && (data5 !== null)){const err17 = {instancePath:instancePath+"/payload/goal/tokenBudget",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/tokenBudget/type",keyword:"type",params:{type: schema201.properties.tokenBudget.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}if((typeof data5 == "number") && (isFinite(data5))){if(data5 < 1 || isNaN(data5)){const err18 = {instancePath:instancePath+"/payload/goal/tokenBudget",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/tokenBudget/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}}if(data2.tokensUsed !== undefined){let data6 = data2.tokensUsed;if((!(((typeof data6 == "number") && (!(data6 % 1) && !isNaN(data6))) && (isFinite(data6)))) && (data6 !== null)){const err19 = {instancePath:instancePath+"/payload/goal/tokensUsed",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/tokensUsed/type",keyword:"type",params:{type: schema201.properties.tokensUsed.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}if((typeof data6 == "number") && (isFinite(data6))){if(data6 < 0 || isNaN(data6)){const err20 = {instancePath:instancePath+"/payload/goal/tokensUsed",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/tokensUsed/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}}if(data2.elapsedSeconds !== undefined){let data7 = data2.elapsedSeconds;if((!((typeof data7 == "number") && (isFinite(data7)))) && (data7 !== null)){const err21 = {instancePath:instancePath+"/payload/goal/elapsedSeconds",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/elapsedSeconds/type",keyword:"type",params:{type: schema201.properties.elapsedSeconds.type},message:"must be number,null"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if((typeof data7 == "number") && (isFinite(data7))){if(data7 < 0 || isNaN(data7)){const err22 = {instancePath:instancePath+"/payload/goal/elapsedSeconds",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/elapsedSeconds/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}}if(data2.iterations !== undefined){let data8 = data2.iterations;if((!(((typeof data8 == "number") && (!(data8 % 1) && !isNaN(data8))) && (isFinite(data8)))) && (data8 !== null)){const err23 = {instancePath:instancePath+"/payload/goal/iterations",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/iterations/type",keyword:"type",params:{type: schema201.properties.iterations.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}if((typeof data8 == "number") && (isFinite(data8))){if(data8 < 0 || isNaN(data8)){const err24 = {instancePath:instancePath+"/payload/goal/iterations",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/iterations/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}}if(data2.lastReason !== undefined){let data9 = data2.lastReason;if((typeof data9 !== "string") && (data9 !== null)){const err25 = {instancePath:instancePath+"/payload/goal/lastReason",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/lastReason/type",keyword:"type",params:{type: schema201.properties.lastReason.type},message:"must be string,null"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}if(typeof data9 === "string"){if(func1(data9) > 4000){const err26 = {instancePath:instancePath+"/payload/goal/lastReason",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/lastReason/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}}}if(data2.createdAt !== undefined){let data10 = data2.createdAt;if((typeof data10 !== "string") && (data10 !== null)){const err27 = {instancePath:instancePath+"/payload/goal/createdAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/createdAt/type",keyword:"type",params:{type: schema201.properties.createdAt.type},message:"must be string,null"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if(typeof data10 === "string"){if(!(formats0.test(data10))){const err28 = {instancePath:instancePath+"/payload/goal/createdAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/createdAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}}if(data2.updatedAt !== undefined){let data11 = data2.updatedAt;if((typeof data11 !== "string") && (data11 !== null)){const err29 = {instancePath:instancePath+"/payload/goal/updatedAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/updatedAt/type",keyword:"type",params:{type: schema201.properties.updatedAt.type},message:"must be string,null"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}if(typeof data11 === "string"){if(!(formats0.test(data11))){const err30 = {instancePath:instancePath+"/payload/goal/updatedAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/updatedAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}}}if(data2.completedAt !== undefined){let data12 = data2.completedAt;if((typeof data12 !== "string") && (data12 !== null)){const err31 = {instancePath:instancePath+"/payload/goal/completedAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/completedAt/type",keyword:"type",params:{type: schema201.properties.completedAt.type},message:"must be string,null"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}if(typeof data12 === "string"){if(!(formats0.test(data12))){const err32 = {instancePath:instancePath+"/payload/goal/completedAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/completedAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}}}if(data2.workingNow !== undefined){if(typeof data2.workingNow !== "boolean"){const err33 = {instancePath:instancePath+"/payload/goal/workingNow",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/workingNow/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}}}else {const err34 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}var _valid1 = _errs11 === errors;if(_valid1){valid5 = true;passing0 = 0;}const _errs36 = errors;if(data2 !== null){const err35 = {instancePath:instancePath+"/payload/goal",schemaPath:"#/allOf/0/then/properties/payload/properties/goal/oneOf/1/type",keyword:"type",params:{type: "null"},message:"must be null"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}var _valid1 = _errs36 === errors;if(_valid1 && valid5){valid5 = false;passing0 = [passing0, 1];}else {if(_valid1){valid5 = true;passing0 = 1;}}if(!valid5){const err36 = {instancePath:instancePath+"/payload/goal",schemaPath:"#/allOf/0/then/properties/payload/properties/goal/oneOf",keyword:"oneOf",params:{passingSchemas: passing0},message:"must match exactly one schema in oneOf"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}else {errors = _errs10;if(vErrors !== null){if(_errs10){vErrors.length = _errs10;}else {vErrors = null;}}}}}else {const err37 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/0/then/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}}}var _valid0 = _errs5 === errors;valid1 = _valid0;if(valid1){var props1 = {};props1.payload = true;props1.eventType = true;}}if(!valid1){const err38 = {instancePath,schemaPath:"#/allOf/0/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}const _errs39 = errors;let valid8 = true;const _errs40 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("session.capabilities.updated" !== data.eventType){const err39 = {};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}}}var _valid2 = _errs40 === errors;errors = _errs39;if(vErrors !== null){if(_errs39){vErrors.length = _errs39;}else {vErrors = null;}}if(_valid2){const _errs42 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data15 = data.payload;if(data15 && typeof data15 == "object" && !Array.isArray(data15)){if(data15.sessionGoals === undefined){const err40 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/1/then/properties/payload/required",keyword:"required",params:{missingProperty: "sessionGoals"},message:"must have required property '"+"sessionGoals"+"'"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}if(data15.sessionGoals !== undefined){let data16 = data15.sessionGoals;if(data16 && typeof data16 == "object" && !Array.isArray(data16)){if(data16.availability === undefined){const err41 = {instancePath:instancePath+"/payload/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "availability"},message:"must have required property '"+"availability"+"'"};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}if(data16.actions === undefined){const err42 = {instancePath:instancePath+"/payload/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "actions"},message:"must have required property '"+"actions"+"'"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}if(data16.autonomousUpdates === undefined){const err43 = {instancePath:instancePath+"/payload/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "autonomousUpdates"},message:"must have required property '"+"autonomousUpdates"+"'"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}if(data16.persistentAcrossResume === undefined){const err44 = {instancePath:instancePath+"/payload/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "persistentAcrossResume"},message:"must have required property '"+"persistentAcrossResume"+"'"};if(vErrors === null){vErrors = [err44];}else {vErrors.push(err44);}errors++;}if(data16.maxObjectiveChars === undefined){const err45 = {instancePath:instancePath+"/payload/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "maxObjectiveChars"},message:"must have required property '"+"maxObjectiveChars"+"'"};if(vErrors === null){vErrors = [err45];}else {vErrors.push(err45);}errors++;}if(data16.tokenBudgetControl === undefined){const err46 = {instancePath:instancePath+"/payload/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "tokenBudgetControl"},message:"must have required property '"+"tokenBudgetControl"+"'"};if(vErrors === null){vErrors = [err46];}else {vErrors.push(err46);}errors++;}if(data16.usageReporting === undefined){const err47 = {instancePath:instancePath+"/payload/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "usageReporting"},message:"must have required property '"+"usageReporting"+"'"};if(vErrors === null){vErrors = [err47];}else {vErrors.push(err47);}errors++;}if(data16.availability !== undefined){let data17 = data16.availability;if(!(((data17 === "available") || (data17 === "unsupported")) || (data17 === "policy_disabled"))){const err48 = {instancePath:instancePath+"/payload/sessionGoals/availability",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/availability/enum",keyword:"enum",params:{allowedValues: schema44.properties.availability.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err48];}else {vErrors.push(err48);}errors++;}}if(data16.actions !== undefined){let data18 = data16.actions;if(Array.isArray(data18)){const len0 = data18.length;for(let i0=0; i0 1){outer0:for(;i1--;){for(j0 = i1; j0--;){if(func0(data18[i1], data18[j0])){const err50 = {instancePath:instancePath+"/payload/sessionGoals/actions",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/actions/uniqueItems",keyword:"uniqueItems",params:{i: i1, j: j0},message:"must NOT have duplicate items (items ## "+j0+" and "+i1+" are identical)"};if(vErrors === null){vErrors = [err50];}else {vErrors.push(err50);}errors++;break outer0;}}}}}else {const err51 = {instancePath:instancePath+"/payload/sessionGoals/actions",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/actions/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err51];}else {vErrors.push(err51);}errors++;}}if(data16.autonomousUpdates !== undefined){if(typeof data16.autonomousUpdates !== "boolean"){const err52 = {instancePath:instancePath+"/payload/sessionGoals/autonomousUpdates",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/autonomousUpdates/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err52];}else {vErrors.push(err52);}errors++;}}if(data16.persistentAcrossResume !== undefined){if(typeof data16.persistentAcrossResume !== "boolean"){const err53 = {instancePath:instancePath+"/payload/sessionGoals/persistentAcrossResume",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/persistentAcrossResume/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err53];}else {vErrors.push(err53);}errors++;}}if(data16.maxObjectiveChars !== undefined){let data22 = data16.maxObjectiveChars;if(!(((typeof data22 == "number") && (!(data22 % 1) && !isNaN(data22))) && (isFinite(data22)))){const err54 = {instancePath:instancePath+"/payload/sessionGoals/maxObjectiveChars",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/maxObjectiveChars/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err54];}else {vErrors.push(err54);}errors++;}if((typeof data22 == "number") && (isFinite(data22))){if(data22 > 4000 || isNaN(data22)){const err55 = {instancePath:instancePath+"/payload/sessionGoals/maxObjectiveChars",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/maxObjectiveChars/maximum",keyword:"maximum",params:{comparison: "<=", limit: 4000},message:"must be <= 4000"};if(vErrors === null){vErrors = [err55];}else {vErrors.push(err55);}errors++;}if(data22 < 1 || isNaN(data22)){const err56 = {instancePath:instancePath+"/payload/sessionGoals/maxObjectiveChars",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/maxObjectiveChars/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err56];}else {vErrors.push(err56);}errors++;}}}if(data16.tokenBudgetControl !== undefined){if(typeof data16.tokenBudgetControl !== "boolean"){const err57 = {instancePath:instancePath+"/payload/sessionGoals/tokenBudgetControl",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/tokenBudgetControl/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err57];}else {vErrors.push(err57);}errors++;}}if(data16.usageReporting !== undefined){if(typeof data16.usageReporting !== "boolean"){const err58 = {instancePath:instancePath+"/payload/sessionGoals/usageReporting",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/usageReporting/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err58];}else {vErrors.push(err58);}errors++;}}if(data16.reasonCode !== undefined){let data25 = data16.reasonCode;if(typeof data25 === "string"){if(func1(data25) > 160){const err59 = {instancePath:instancePath+"/payload/sessionGoals/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reasonCode/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err59];}else {vErrors.push(err59);}errors++;}if(func1(data25) < 1){const err60 = {instancePath:instancePath+"/payload/sessionGoals/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reasonCode/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err60];}else {vErrors.push(err60);}errors++;}}else {const err61 = {instancePath:instancePath+"/payload/sessionGoals/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reasonCode/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err61];}else {vErrors.push(err61);}errors++;}}if(data16.reason !== undefined){let data26 = data16.reason;if(typeof data26 === "string"){if(func1(data26) > 1000){const err62 = {instancePath:instancePath+"/payload/sessionGoals/reason",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reason/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err62];}else {vErrors.push(err62);}errors++;}if(func1(data26) < 1){const err63 = {instancePath:instancePath+"/payload/sessionGoals/reason",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reason/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err63];}else {vErrors.push(err63);}errors++;}}else {const err64 = {instancePath:instancePath+"/payload/sessionGoals/reason",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reason/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err64];}else {vErrors.push(err64);}errors++;}}}else {const err65 = {instancePath:instancePath+"/payload/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err65];}else {vErrors.push(err65);}errors++;}}}else {const err66 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/1/then/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err66];}else {vErrors.push(err66);}errors++;}}}var _valid2 = _errs42 === errors;valid8 = _valid2;if(valid8){var props2 = {};props2.payload = true;props2.eventType = true;}}if(!valid8){const err67 = {instancePath,schemaPath:"#/allOf/1/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err67];}else {vErrors.push(err67);}errors++;}if(props1 !== true && props2 !== undefined){if(props2 === true){props1 = true;}else {props1 = props1 || {};Object.assign(props1, props2);}}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err68 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err68];}else {vErrors.push(err68);}errors++;}if(data.sourceEventId === undefined){const err69 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sourceEventId"},message:"must have required property '"+"sourceEventId"+"'"};if(vErrors === null){vErrors = [err69];}else {vErrors.push(err69);}errors++;}if(data.sourceSeq === undefined){const err70 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sourceSeq"},message:"must have required property '"+"sourceSeq"+"'"};if(vErrors === null){vErrors = [err70];}else {vErrors.push(err70);}errors++;}if(data.sourceInstanceId === undefined){const err71 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sourceInstanceId"},message:"must have required property '"+"sourceInstanceId"+"'"};if(vErrors === null){vErrors = [err71];}else {vErrors.push(err71);}errors++;}if(data.sourceKind === undefined){const err72 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sourceKind"},message:"must have required property '"+"sourceKind"+"'"};if(vErrors === null){vErrors = [err72];}else {vErrors.push(err72);}errors++;}if(data.runId === undefined){const err73 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "runId"},message:"must have required property '"+"runId"+"'"};if(vErrors === null){vErrors = [err73];}else {vErrors.push(err73);}errors++;}if(data.eventType === undefined){const err74 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "eventType"},message:"must have required property '"+"eventType"+"'"};if(vErrors === null){vErrors = [err74];}else {vErrors.push(err74);}errors++;}if(data.schemaVersion === undefined){const err75 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schemaVersion"},message:"must have required property '"+"schemaVersion"+"'"};if(vErrors === null){vErrors = [err75];}else {vErrors.push(err75);}errors++;}if(data.priority === undefined){const err76 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "priority"},message:"must have required property '"+"priority"+"'"};if(vErrors === null){vErrors = [err76];}else {vErrors.push(err76);}errors++;}if(data.emittedAt === undefined){const err77 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "emittedAt"},message:"must have required property '"+"emittedAt"+"'"};if(vErrors === null){vErrors = [err77];}else {vErrors.push(err77);}errors++;}if(data.payload === undefined){const err78 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "payload"},message:"must have required property '"+"payload"+"'"};if(vErrors === null){vErrors = [err78];}else {vErrors.push(err78);}errors++;}if(data.schema !== undefined){if("paperclip.prp.event.v2" !== data.schema){const err79 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.event.v2"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err79];}else {vErrors.push(err79);}errors++;}}if(data.sourceEventId !== undefined){let data28 = data.sourceEventId;if(typeof data28 === "string"){if(func1(data28) > 160){const err80 = {instancePath:instancePath+"/sourceEventId",schemaPath:"#/properties/sourceEventId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err80];}else {vErrors.push(err80);}errors++;}if(func1(data28) < 1){const err81 = {instancePath:instancePath+"/sourceEventId",schemaPath:"#/properties/sourceEventId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err81];}else {vErrors.push(err81);}errors++;}}else {const err82 = {instancePath:instancePath+"/sourceEventId",schemaPath:"#/properties/sourceEventId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err82];}else {vErrors.push(err82);}errors++;}}if(data.sourceSeq !== undefined){let data29 = data.sourceSeq;if(!(((typeof data29 == "number") && (!(data29 % 1) && !isNaN(data29))) && (isFinite(data29)))){const err83 = {instancePath:instancePath+"/sourceSeq",schemaPath:"#/properties/sourceSeq/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err83];}else {vErrors.push(err83);}errors++;}if((typeof data29 == "number") && (isFinite(data29))){if(data29 > 9007199254740991 || isNaN(data29)){const err84 = {instancePath:instancePath+"/sourceSeq",schemaPath:"#/properties/sourceSeq/maximum",keyword:"maximum",params:{comparison: "<=", limit: 9007199254740991},message:"must be <= 9007199254740991"};if(vErrors === null){vErrors = [err84];}else {vErrors.push(err84);}errors++;}if(data29 < 1 || isNaN(data29)){const err85 = {instancePath:instancePath+"/sourceSeq",schemaPath:"#/properties/sourceSeq/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err85];}else {vErrors.push(err85);}errors++;}}}if(data.sourceInstanceId !== undefined){let data30 = data.sourceInstanceId;if(typeof data30 === "string"){if(func1(data30) > 160){const err86 = {instancePath:instancePath+"/sourceInstanceId",schemaPath:"#/properties/sourceInstanceId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err86];}else {vErrors.push(err86);}errors++;}if(func1(data30) < 1){const err87 = {instancePath:instancePath+"/sourceInstanceId",schemaPath:"#/properties/sourceInstanceId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err87];}else {vErrors.push(err87);}errors++;}}else {const err88 = {instancePath:instancePath+"/sourceInstanceId",schemaPath:"#/properties/sourceInstanceId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err88];}else {vErrors.push(err88);}errors++;}}if(data.sourceKind !== undefined){let data31 = data.sourceKind;if(!((data31 === "runner") || (data31 === "control_plane"))){const err89 = {instancePath:instancePath+"/sourceKind",schemaPath:"#/properties/sourceKind/enum",keyword:"enum",params:{allowedValues: schema200.properties.sourceKind.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err89];}else {vErrors.push(err89);}errors++;}}if(data.runId !== undefined){let data32 = data.runId;if(typeof data32 === "string"){if(func1(data32) > 160){const err90 = {instancePath:instancePath+"/runId",schemaPath:"#/properties/runId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err90];}else {vErrors.push(err90);}errors++;}if(func1(data32) < 1){const err91 = {instancePath:instancePath+"/runId",schemaPath:"#/properties/runId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err91];}else {vErrors.push(err91);}errors++;}}else {const err92 = {instancePath:instancePath+"/runId",schemaPath:"#/properties/runId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err92];}else {vErrors.push(err92);}errors++;}}if(data.normalizedSessionId !== undefined){let data33 = data.normalizedSessionId;if(typeof data33 === "string"){if(func1(data33) > 160){const err93 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/properties/normalizedSessionId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err93];}else {vErrors.push(err93);}errors++;}if(func1(data33) < 1){const err94 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/properties/normalizedSessionId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err94];}else {vErrors.push(err94);}errors++;}}else {const err95 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/properties/normalizedSessionId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err95];}else {vErrors.push(err95);}errors++;}}if(data.turnId !== undefined){let data34 = data.turnId;if(typeof data34 === "string"){if(func1(data34) > 160){const err96 = {instancePath:instancePath+"/turnId",schemaPath:"#/properties/turnId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err96];}else {vErrors.push(err96);}errors++;}if(func1(data34) < 1){const err97 = {instancePath:instancePath+"/turnId",schemaPath:"#/properties/turnId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err97];}else {vErrors.push(err97);}errors++;}}else {const err98 = {instancePath:instancePath+"/turnId",schemaPath:"#/properties/turnId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err98];}else {vErrors.push(err98);}errors++;}}if(data.itemId !== undefined){let data35 = data.itemId;if(typeof data35 === "string"){if(func1(data35) > 160){const err99 = {instancePath:instancePath+"/itemId",schemaPath:"#/properties/itemId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err99];}else {vErrors.push(err99);}errors++;}if(func1(data35) < 1){const err100 = {instancePath:instancePath+"/itemId",schemaPath:"#/properties/itemId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err100];}else {vErrors.push(err100);}errors++;}}else {const err101 = {instancePath:instancePath+"/itemId",schemaPath:"#/properties/itemId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err101];}else {vErrors.push(err101);}errors++;}}if(data.eventType !== undefined){let data36 = data.eventType;if(!(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((data36 === "runner.connected") || (data36 === "runner.reconnected")) || (data36 === "runner.reconciled")) || (data36 === "runner.disconnected")) || (data36 === "runner.draining")) || (data36 === "runner.suspending")) || (data36 === "runner.suspended")) || (data36 === "runner.stopped")) || (data36 === "runner.diagnostic")) || (data36 === "runtime.phase.changed")) || (data36 === "sandbox.metric")) || (data36 === "workspace.ready")) || (data36 === "workspace.change.updated")) || (data36 === "workspace.diff.recorded")) || (data36 === "workspace.file.referenced")) || (data36 === "harness.starting")) || (data36 === "harness.ready")) || (data36 === "harness.exited")) || (data36 === "harness.diagnostic")) || (data36 === "plan.updated")) || (data36 === "tool.execution.started")) || (data36 === "tool.execution.progressed")) || (data36 === "tool.execution.completed")) || (data36 === "research.started")) || (data36 === "research.progressed")) || (data36 === "research.completed")) || (data36 === "delegation.started")) || (data36 === "delegation.updated")) || (data36 === "delegation.completed")) || (data36 === "model.route.changed")) || (data36 === "model.verification.updated")) || (data36 === "context.compacted")) || (data36 === "artifact.viewed")) || (data36 === "artifact.generated")) || (data36 === "review.mode.changed")) || (data36 === "hook.started")) || (data36 === "hook.completed")) || (data36 === "memory.citation.referenced")) || (data36 === "safety.review.started")) || (data36 === "safety.review.completed")) || (data36 === "terminal.input.sent")) || (data36 === "wait.started")) || (data36 === "wait.completed")) || (data36 === "provider.notice.recorded")) || (data36 === "session.starting")) || (data36 === "session.started")) || (data36 === "session.resuming")) || (data36 === "session.resumed")) || (data36 === "session.reconciled")) || (data36 === "session.updated")) || (data36 === "session.closed")) || (data36 === "session.failed")) || (data36 === "session.capabilities.updated")) || (data36 === "session.goal.snapshot")) || (data36 === "session.goal.updated")) || (data36 === "session.goal.cleared")) || (data36 === "turn.submitted")) || (data36 === "turn.accepted")) || (data36 === "turn.started")) || (data36 === "turn.completed")) || (data36 === "turn.failed")) || (data36 === "turn.interrupted")) || (data36 === "turn.cancelled")) || (data36 === "item.started")) || (data36 === "item.delta")) || (data36 === "item.completed")) || (data36 === "item.failed")) || (data36 === "usage.reported")) || (data36 === "semantic_tool.input")) || (data36 === "semantic_tool.result")) || (data36 === "mcp_app.discovered")) || (data36 === "mcp_app.resource.resolved")) || (data36 === "mcp_app.initializing")) || (data36 === "mcp_app.ready")) || (data36 === "mcp_app.tool_input")) || (data36 === "mcp_app.tool_result")) || (data36 === "mcp_app.action.requested")) || (data36 === "mcp_app.action.resolved")) || (data36 === "mcp_app.host_context.changed")) || (data36 === "mcp_app.failed")) || (data36 === "mcp_app.teardown")) || (data36 === "runtime_request.created")) || (data36 === "runtime_request.resolved")) || (data36 === "runtime_request.expired")) || (data36 === "runtime_request.cancelled")) || (data36 === "interaction.request.proposed")) || (data36 === "interaction.request.materialized")) || (data36 === "interaction.request.rejected")) || (data36 === "interaction.response.progressed")) || (data36 === "interaction.response.resolved")) || (data36 === "interaction.response.delivered")) || (data36 === "run.attached")) || (data36 === "run.detached")) || (data36 === "run.result.proposed")) || (data36 === "run.result.accepted")) || (data36 === "run.result.rejected")) || (data36 === "attention.request.proposed")) || (data36 === "attention.request.routed")) || (data36 === "attention.request.resolved")) || (data36 === "attention.request.expired")) || (data36 === "attention.request.superseded")) || (data36 === "work.assessment.recorded")) || (data36 === "issue.status.decision.recorded")) || (data36 === "issue.status.decision.applied")) || (data36 === "issue.status.decision.rejected")) || (data36 === "issue.status.decision.superseded")) || (data36 === "run.terminal"))){const err102 = {instancePath:instancePath+"/eventType",schemaPath:"#/properties/eventType/enum",keyword:"enum",params:{allowedValues: schema200.properties.eventType.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err102];}else {vErrors.push(err102);}errors++;}}if(data.schemaVersion !== undefined){if(2 !== data.schemaVersion){const err103 = {instancePath:instancePath+"/schemaVersion",schemaPath:"#/properties/schemaVersion/const",keyword:"const",params:{allowedValue: 2},message:"must be equal to constant"};if(vErrors === null){vErrors = [err103];}else {vErrors.push(err103);}errors++;}}if(data.priority !== undefined){let data38 = data.priority;if(!(((data38 === 0) || (data38 === 1)) || (data38 === 2))){const err104 = {instancePath:instancePath+"/priority",schemaPath:"#/properties/priority/enum",keyword:"enum",params:{allowedValues: schema200.properties.priority.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err104];}else {vErrors.push(err104);}errors++;}}if(data.emittedAt !== undefined){let data39 = data.emittedAt;if(typeof data39 === "string"){if(!(formats0.test(data39))){const err105 = {instancePath:instancePath+"/emittedAt",schemaPath:"#/properties/emittedAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err105];}else {vErrors.push(err105);}errors++;}}else {const err106 = {instancePath:instancePath+"/emittedAt",schemaPath:"#/properties/emittedAt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err106];}else {vErrors.push(err106);}errors++;}}if(data.observedAt !== undefined){let data40 = data.observedAt;if(typeof data40 === "string"){if(!(formats0.test(data40))){const err107 = {instancePath:instancePath+"/observedAt",schemaPath:"#/properties/observedAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err107];}else {vErrors.push(err107);}errors++;}}else {const err108 = {instancePath:instancePath+"/observedAt",schemaPath:"#/properties/observedAt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err108];}else {vErrors.push(err108);}errors++;}}if(data.payload !== undefined){let data41 = data.payload;if(data41 && typeof data41 == "object" && !Array.isArray(data41)){}else {const err109 = {instancePath:instancePath+"/payload",schemaPath:"#/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err109];}else {vErrors.push(err109);}errors++;}}if(data.debug !== undefined){let data42 = data.debug;if(data42 && typeof data42 == "object" && !Array.isArray(data42)){}else {const err110 = {instancePath:instancePath+"/debug",schemaPath:"#/properties/debug/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err110];}else {vErrors.push(err110);}errors++;}}}else {const err111 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err111];}else {vErrors.push(err111);}errors++;}validate109.errors = vErrors;return errors === 0;}validate109.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema203 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/request.schema.json","title":"PRP request","oneOf":[{"type":"object","required":["schema","requestKind","requestId","type","status","prompt","input"],"properties":{"schema":{"const":"paperclip.runtime_request.v2"},"requestKind":{"const":"runtime"},"requestId":{"type":"string","minLength":1,"maxLength":160},"type":{"const":"input"},"status":{"enum":["pending","resolved","expired","cancelled"]},"prompt":{"type":"string","minLength":1,"maxLength":4000},"input":{"$ref":"https://paperclip.dev/schemas/prp/v1/question-set.schema.json"},"origin":{"type":"object","required":["adapter"],"properties":{"adapter":{"type":"string","minLength":1,"maxLength":160},"provider":{"type":"string","minLength":1,"maxLength":160},"method":{"type":"string","minLength":1,"maxLength":500}},"additionalProperties":false},"turnId":{"type":"string","minLength":1,"maxLength":240},"itemId":{"type":"string","minLength":1,"maxLength":240}},"additionalProperties":false},{"type":"object","required":["schema","requestKind","requestId","type","status","prompt"],"properties":{"schema":{"const":"paperclip.runtime_request.v1"},"requestKind":{"const":"runtime"},"requestId":{"type":"string","minLength":1,"maxLength":160},"type":{"enum":["permission","input"]},"status":{"enum":["pending","resolved","expired","cancelled"]},"prompt":{"type":"string","minLength":1,"maxLength":4000},"choices":{"type":"array","items":{"type":"object","required":["key","label"],"properties":{"key":{"type":"string","minLength":1},"label":{"type":"string","minLength":1}},"additionalProperties":true}}},"additionalProperties":true},{"type":"object","required":["schema","requestKind","requestId","kind","blocking","payload"],"properties":{"schema":{"const":"paperclip.interaction_request.v1"},"requestKind":{"const":"issue_thread"},"requestId":{"type":"string","minLength":1,"maxLength":160},"kind":{"enum":["request_confirmation","request_checkbox_confirmation","request_item_verdicts","ask_user_questions","suggest_tasks"]},"blocking":{"type":"boolean"},"payload":{"type":"object","additionalProperties":true}},"additionalProperties":true}]};const schema204 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/question-set.schema.json","title":"Paperclip question set","type":"object","required":["schema","questions"],"properties":{"schema":{"const":"paperclip.question_set.v1"},"title":{"type":"string","maxLength":1000},"description":{"type":"string","maxLength":4000},"submitLabel":{"type":"string","maxLength":200},"questions":{"type":"array","minItems":1,"maxItems":64,"items":{"$ref":"#/$defs/question"}}},"$defs":{"option":{"type":"object","required":["id","label"],"properties":{"id":{"type":"string","minLength":1,"maxLength":160},"label":{"type":"string","minLength":1,"maxLength":1000},"description":{"type":"string","maxLength":4000},"recommended":{"type":"boolean"}},"additionalProperties":false},"customAnswer":{"type":"object","required":["enabled"],"properties":{"enabled":{"const":true},"label":{"type":"string","maxLength":1000},"placeholder":{"type":"string","maxLength":1000}},"additionalProperties":false},"textValidation":{"type":"object","properties":{"minLength":{"type":"integer","minimum":0,"maximum":100000},"maxLength":{"type":"integer","minimum":0,"maximum":100000},"pattern":{"type":"string","maxLength":1000},"inputType":{"enum":["text","number","integer"]},"minimum":{"type":"number"},"maximum":{"type":"number"}},"additionalProperties":false},"question":{"type":"object","required":["id","prompt","required","answerMode"],"properties":{"id":{"type":"string","minLength":1,"maxLength":160},"header":{"type":"string","maxLength":1000},"prompt":{"type":"string","minLength":1,"maxLength":4000},"helpText":{"type":"string","maxLength":4000},"required":{"type":"boolean"},"answerMode":{"enum":["single_select","multi_select","text"]},"options":{"type":"array","maxItems":128,"items":{"$ref":"#/$defs/option"}},"customAnswer":{"$ref":"#/$defs/customAnswer"},"textValidation":{"$ref":"#/$defs/textValidation"}},"allOf":[{"if":{"properties":{"answerMode":{"const":"text"}},"required":["answerMode"]},"then":{"properties":{"options":{"type":"array","maxItems":0}},"not":{"required":["customAnswer"]}}},{"if":{"properties":{"answerMode":{"enum":["single_select","multi_select"]}},"required":["answerMode"]},"then":{"required":["options"],"properties":{"options":{"type":"array","minItems":1}}}}],"additionalProperties":false}},"additionalProperties":false};const schema205 = {"type":"object","required":["id","prompt","required","answerMode"],"properties":{"id":{"type":"string","minLength":1,"maxLength":160},"header":{"type":"string","maxLength":1000},"prompt":{"type":"string","minLength":1,"maxLength":4000},"helpText":{"type":"string","maxLength":4000},"required":{"type":"boolean"},"answerMode":{"enum":["single_select","multi_select","text"]},"options":{"type":"array","maxItems":128,"items":{"$ref":"#/$defs/option"}},"customAnswer":{"$ref":"#/$defs/customAnswer"},"textValidation":{"$ref":"#/$defs/textValidation"}},"allOf":[{"if":{"properties":{"answerMode":{"const":"text"}},"required":["answerMode"]},"then":{"properties":{"options":{"type":"array","maxItems":0}},"not":{"required":["customAnswer"]}}},{"if":{"properties":{"answerMode":{"enum":["single_select","multi_select"]}},"required":["answerMode"]},"then":{"required":["options"],"properties":{"options":{"type":"array","minItems":1}}}}],"additionalProperties":false};const schema206 = {"type":"object","required":["id","label"],"properties":{"id":{"type":"string","minLength":1,"maxLength":160},"label":{"type":"string","minLength":1,"maxLength":1000},"description":{"type":"string","maxLength":4000},"recommended":{"type":"boolean"}},"additionalProperties":false};const schema207 = {"type":"object","required":["enabled"],"properties":{"enabled":{"const":true},"label":{"type":"string","maxLength":1000},"placeholder":{"type":"string","maxLength":1000}},"additionalProperties":false};const schema208 = {"type":"object","properties":{"minLength":{"type":"integer","minimum":0,"maximum":100000},"maxLength":{"type":"integer","minimum":0,"maximum":100000},"pattern":{"type":"string","maxLength":1000},"inputType":{"enum":["text","number","integer"]},"minimum":{"type":"number"},"maximum":{"type":"number"}},"additionalProperties":false};function validate113(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate113.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}const _errs2 = errors;let valid1 = true;const _errs3 = errors;if(data && typeof data == "object" && !Array.isArray(data)){let missing0;if((data.answerMode === undefined) && (missing0 = "answerMode")){const err0 = {};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}else {if(data.answerMode !== undefined){if("text" !== data.answerMode){const err1 = {};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}}}}var _valid0 = _errs3 === errors;errors = _errs2;if(vErrors !== null){if(_errs2){vErrors.length = _errs2;}else {vErrors = null;}}if(_valid0){const _errs5 = errors;const _errs6 = errors;const _errs7 = errors;if(data && typeof data == "object" && !Array.isArray(data)){let missing1;if((data.customAnswer === undefined) && (missing1 = "customAnswer")){const err2 = {};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}}var valid3 = _errs7 === errors;if(valid3){const err3 = {instancePath,schemaPath:"#/allOf/0/then/not",keyword:"not",params:{},message:"must NOT be valid"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}else {errors = _errs6;if(vErrors !== null){if(_errs6){vErrors.length = _errs6;}else {vErrors = null;}}}if(data && typeof data == "object" && !Array.isArray(data)){if(data.options !== undefined){let data1 = data.options;if(Array.isArray(data1)){if(data1.length > 0){const err4 = {instancePath:instancePath+"/options",schemaPath:"#/allOf/0/then/properties/options/maxItems",keyword:"maxItems",params:{limit: 0},message:"must NOT have more than 0 items"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}}else {const err5 = {instancePath:instancePath+"/options",schemaPath:"#/allOf/0/then/properties/options/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}}var _valid0 = _errs5 === errors;valid1 = _valid0;if(valid1){var props0 = {};props0.options = true;props0.answerMode = true;}}if(!valid1){const err6 = {instancePath,schemaPath:"#/allOf/0/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}const _errs11 = errors;let valid5 = true;const _errs12 = errors;if(data && typeof data == "object" && !Array.isArray(data)){let missing2;if((data.answerMode === undefined) && (missing2 = "answerMode")){const err7 = {};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}else {if(data.answerMode !== undefined){let data2 = data.answerMode;if(!((data2 === "single_select") || (data2 === "multi_select"))){const err8 = {};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}}}var _valid1 = _errs12 === errors;errors = _errs11;if(vErrors !== null){if(_errs11){vErrors.length = _errs11;}else {vErrors = null;}}if(_valid1){const _errs14 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.options === undefined){const err9 = {instancePath,schemaPath:"#/allOf/1/then/required",keyword:"required",params:{missingProperty: "options"},message:"must have required property '"+"options"+"'"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(data.options !== undefined){let data3 = data.options;if(Array.isArray(data3)){if(data3.length < 1){const err10 = {instancePath:instancePath+"/options",schemaPath:"#/allOf/1/then/properties/options/minItems",keyword:"minItems",params:{limit: 1},message:"must NOT have fewer than 1 items"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}else {const err11 = {instancePath:instancePath+"/options",schemaPath:"#/allOf/1/then/properties/options/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}}var _valid1 = _errs14 === errors;valid5 = _valid1;if(valid5){var props1 = {};props1.options = true;props1.answerMode = true;}}if(!valid5){const err12 = {instancePath,schemaPath:"#/allOf/1/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(props0 !== true && props1 !== undefined){if(props1 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props1);}}if(data && typeof data == "object" && !Array.isArray(data)){if(data.id === undefined){const err13 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "id"},message:"must have required property '"+"id"+"'"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(data.prompt === undefined){const err14 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "prompt"},message:"must have required property '"+"prompt"+"'"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(data.required === undefined){const err15 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "required"},message:"must have required property '"+"required"+"'"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}if(data.answerMode === undefined){const err16 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "answerMode"},message:"must have required property '"+"answerMode"+"'"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}for(const key0 in data){if(!(func58.call(schema205.properties, key0))){const err17 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}if(data.id !== undefined){let data4 = data.id;if(typeof data4 === "string"){if(func1(data4) > 160){const err18 = {instancePath:instancePath+"/id",schemaPath:"#/properties/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}if(func1(data4) < 1){const err19 = {instancePath:instancePath+"/id",schemaPath:"#/properties/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}else {const err20 = {instancePath:instancePath+"/id",schemaPath:"#/properties/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}if(data.header !== undefined){let data5 = data.header;if(typeof data5 === "string"){if(func1(data5) > 1000){const err21 = {instancePath:instancePath+"/header",schemaPath:"#/properties/header/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}}else {const err22 = {instancePath:instancePath+"/header",schemaPath:"#/properties/header/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}if(data.prompt !== undefined){let data6 = data.prompt;if(typeof data6 === "string"){if(func1(data6) > 4000){const err23 = {instancePath:instancePath+"/prompt",schemaPath:"#/properties/prompt/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}if(func1(data6) < 1){const err24 = {instancePath:instancePath+"/prompt",schemaPath:"#/properties/prompt/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}else {const err25 = {instancePath:instancePath+"/prompt",schemaPath:"#/properties/prompt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}if(data.helpText !== undefined){let data7 = data.helpText;if(typeof data7 === "string"){if(func1(data7) > 4000){const err26 = {instancePath:instancePath+"/helpText",schemaPath:"#/properties/helpText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}}else {const err27 = {instancePath:instancePath+"/helpText",schemaPath:"#/properties/helpText/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}}if(data.required !== undefined){if(typeof data.required !== "boolean"){const err28 = {instancePath:instancePath+"/required",schemaPath:"#/properties/required/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}if(data.answerMode !== undefined){let data9 = data.answerMode;if(!(((data9 === "single_select") || (data9 === "multi_select")) || (data9 === "text"))){const err29 = {instancePath:instancePath+"/answerMode",schemaPath:"#/properties/answerMode/enum",keyword:"enum",params:{allowedValues: schema205.properties.answerMode.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}}if(data.options !== undefined){let data10 = data.options;if(Array.isArray(data10)){if(data10.length > 128){const err30 = {instancePath:instancePath+"/options",schemaPath:"#/properties/options/maxItems",keyword:"maxItems",params:{limit: 128},message:"must NOT have more than 128 items"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}const len0 = data10.length;for(let i0=0; i0 160){const err34 = {instancePath:instancePath+"/options/" + i0+"/id",schemaPath:"#/$defs/option/properties/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}if(func1(data12) < 1){const err35 = {instancePath:instancePath+"/options/" + i0+"/id",schemaPath:"#/$defs/option/properties/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}}else {const err36 = {instancePath:instancePath+"/options/" + i0+"/id",schemaPath:"#/$defs/option/properties/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}}if(data11.label !== undefined){let data13 = data11.label;if(typeof data13 === "string"){if(func1(data13) > 1000){const err37 = {instancePath:instancePath+"/options/" + i0+"/label",schemaPath:"#/$defs/option/properties/label/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}if(func1(data13) < 1){const err38 = {instancePath:instancePath+"/options/" + i0+"/label",schemaPath:"#/$defs/option/properties/label/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}}else {const err39 = {instancePath:instancePath+"/options/" + i0+"/label",schemaPath:"#/$defs/option/properties/label/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}}if(data11.description !== undefined){let data14 = data11.description;if(typeof data14 === "string"){if(func1(data14) > 4000){const err40 = {instancePath:instancePath+"/options/" + i0+"/description",schemaPath:"#/$defs/option/properties/description/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}}else {const err41 = {instancePath:instancePath+"/options/" + i0+"/description",schemaPath:"#/$defs/option/properties/description/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}}if(data11.recommended !== undefined){if(typeof data11.recommended !== "boolean"){const err42 = {instancePath:instancePath+"/options/" + i0+"/recommended",schemaPath:"#/$defs/option/properties/recommended/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}}}else {const err43 = {instancePath:instancePath+"/options/" + i0,schemaPath:"#/$defs/option/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}}}else {const err44 = {instancePath:instancePath+"/options",schemaPath:"#/properties/options/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err44];}else {vErrors.push(err44);}errors++;}}if(data.customAnswer !== undefined){let data16 = data.customAnswer;if(data16 && typeof data16 == "object" && !Array.isArray(data16)){if(data16.enabled === undefined){const err45 = {instancePath:instancePath+"/customAnswer",schemaPath:"#/$defs/customAnswer/required",keyword:"required",params:{missingProperty: "enabled"},message:"must have required property '"+"enabled"+"'"};if(vErrors === null){vErrors = [err45];}else {vErrors.push(err45);}errors++;}for(const key2 in data16){if(!(((key2 === "enabled") || (key2 === "label")) || (key2 === "placeholder"))){const err46 = {instancePath:instancePath+"/customAnswer",schemaPath:"#/$defs/customAnswer/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key2},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err46];}else {vErrors.push(err46);}errors++;}}if(data16.enabled !== undefined){if(true !== data16.enabled){const err47 = {instancePath:instancePath+"/customAnswer/enabled",schemaPath:"#/$defs/customAnswer/properties/enabled/const",keyword:"const",params:{allowedValue: true},message:"must be equal to constant"};if(vErrors === null){vErrors = [err47];}else {vErrors.push(err47);}errors++;}}if(data16.label !== undefined){let data18 = data16.label;if(typeof data18 === "string"){if(func1(data18) > 1000){const err48 = {instancePath:instancePath+"/customAnswer/label",schemaPath:"#/$defs/customAnswer/properties/label/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err48];}else {vErrors.push(err48);}errors++;}}else {const err49 = {instancePath:instancePath+"/customAnswer/label",schemaPath:"#/$defs/customAnswer/properties/label/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err49];}else {vErrors.push(err49);}errors++;}}if(data16.placeholder !== undefined){let data19 = data16.placeholder;if(typeof data19 === "string"){if(func1(data19) > 1000){const err50 = {instancePath:instancePath+"/customAnswer/placeholder",schemaPath:"#/$defs/customAnswer/properties/placeholder/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err50];}else {vErrors.push(err50);}errors++;}}else {const err51 = {instancePath:instancePath+"/customAnswer/placeholder",schemaPath:"#/$defs/customAnswer/properties/placeholder/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err51];}else {vErrors.push(err51);}errors++;}}}else {const err52 = {instancePath:instancePath+"/customAnswer",schemaPath:"#/$defs/customAnswer/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err52];}else {vErrors.push(err52);}errors++;}}if(data.textValidation !== undefined){let data20 = data.textValidation;if(data20 && typeof data20 == "object" && !Array.isArray(data20)){for(const key3 in data20){if(!((((((key3 === "minLength") || (key3 === "maxLength")) || (key3 === "pattern")) || (key3 === "inputType")) || (key3 === "minimum")) || (key3 === "maximum"))){const err53 = {instancePath:instancePath+"/textValidation",schemaPath:"#/$defs/textValidation/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key3},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err53];}else {vErrors.push(err53);}errors++;}}if(data20.minLength !== undefined){let data21 = data20.minLength;if(!(((typeof data21 == "number") && (!(data21 % 1) && !isNaN(data21))) && (isFinite(data21)))){const err54 = {instancePath:instancePath+"/textValidation/minLength",schemaPath:"#/$defs/textValidation/properties/minLength/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err54];}else {vErrors.push(err54);}errors++;}if((typeof data21 == "number") && (isFinite(data21))){if(data21 > 100000 || isNaN(data21)){const err55 = {instancePath:instancePath+"/textValidation/minLength",schemaPath:"#/$defs/textValidation/properties/minLength/maximum",keyword:"maximum",params:{comparison: "<=", limit: 100000},message:"must be <= 100000"};if(vErrors === null){vErrors = [err55];}else {vErrors.push(err55);}errors++;}if(data21 < 0 || isNaN(data21)){const err56 = {instancePath:instancePath+"/textValidation/minLength",schemaPath:"#/$defs/textValidation/properties/minLength/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err56];}else {vErrors.push(err56);}errors++;}}}if(data20.maxLength !== undefined){let data22 = data20.maxLength;if(!(((typeof data22 == "number") && (!(data22 % 1) && !isNaN(data22))) && (isFinite(data22)))){const err57 = {instancePath:instancePath+"/textValidation/maxLength",schemaPath:"#/$defs/textValidation/properties/maxLength/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err57];}else {vErrors.push(err57);}errors++;}if((typeof data22 == "number") && (isFinite(data22))){if(data22 > 100000 || isNaN(data22)){const err58 = {instancePath:instancePath+"/textValidation/maxLength",schemaPath:"#/$defs/textValidation/properties/maxLength/maximum",keyword:"maximum",params:{comparison: "<=", limit: 100000},message:"must be <= 100000"};if(vErrors === null){vErrors = [err58];}else {vErrors.push(err58);}errors++;}if(data22 < 0 || isNaN(data22)){const err59 = {instancePath:instancePath+"/textValidation/maxLength",schemaPath:"#/$defs/textValidation/properties/maxLength/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err59];}else {vErrors.push(err59);}errors++;}}}if(data20.pattern !== undefined){let data23 = data20.pattern;if(typeof data23 === "string"){if(func1(data23) > 1000){const err60 = {instancePath:instancePath+"/textValidation/pattern",schemaPath:"#/$defs/textValidation/properties/pattern/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err60];}else {vErrors.push(err60);}errors++;}}else {const err61 = {instancePath:instancePath+"/textValidation/pattern",schemaPath:"#/$defs/textValidation/properties/pattern/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err61];}else {vErrors.push(err61);}errors++;}}if(data20.inputType !== undefined){let data24 = data20.inputType;if(!(((data24 === "text") || (data24 === "number")) || (data24 === "integer"))){const err62 = {instancePath:instancePath+"/textValidation/inputType",schemaPath:"#/$defs/textValidation/properties/inputType/enum",keyword:"enum",params:{allowedValues: schema208.properties.inputType.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err62];}else {vErrors.push(err62);}errors++;}}if(data20.minimum !== undefined){let data25 = data20.minimum;if(!((typeof data25 == "number") && (isFinite(data25)))){const err63 = {instancePath:instancePath+"/textValidation/minimum",schemaPath:"#/$defs/textValidation/properties/minimum/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err63];}else {vErrors.push(err63);}errors++;}}if(data20.maximum !== undefined){let data26 = data20.maximum;if(!((typeof data26 == "number") && (isFinite(data26)))){const err64 = {instancePath:instancePath+"/textValidation/maximum",schemaPath:"#/$defs/textValidation/properties/maximum/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err64];}else {vErrors.push(err64);}errors++;}}}else {const err65 = {instancePath:instancePath+"/textValidation",schemaPath:"#/$defs/textValidation/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err65];}else {vErrors.push(err65);}errors++;}}}else {const err66 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err66];}else {vErrors.push(err66);}errors++;}validate113.errors = vErrors;return errors === 0;}validate113.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};function validate112(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){/*# sourceURL="https://paperclip.dev/schemas/prp/v1/question-set.schema.json" */;let vErrors = null;let errors = 0;const evaluated0 = validate112.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.questions === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "questions"},message:"must have required property '"+"questions"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}for(const key0 in data){if(!(((((key0 === "schema") || (key0 === "title")) || (key0 === "description")) || (key0 === "submitLabel")) || (key0 === "questions"))){const err2 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}}if(data.schema !== undefined){if("paperclip.question_set.v1" !== data.schema){const err3 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.question_set.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}}if(data.title !== undefined){let data1 = data.title;if(typeof data1 === "string"){if(func1(data1) > 1000){const err4 = {instancePath:instancePath+"/title",schemaPath:"#/properties/title/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}}else {const err5 = {instancePath:instancePath+"/title",schemaPath:"#/properties/title/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}if(data.description !== undefined){let data2 = data.description;if(typeof data2 === "string"){if(func1(data2) > 4000){const err6 = {instancePath:instancePath+"/description",schemaPath:"#/properties/description/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}else {const err7 = {instancePath:instancePath+"/description",schemaPath:"#/properties/description/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.submitLabel !== undefined){let data3 = data.submitLabel;if(typeof data3 === "string"){if(func1(data3) > 200){const err8 = {instancePath:instancePath+"/submitLabel",schemaPath:"#/properties/submitLabel/maxLength",keyword:"maxLength",params:{limit: 200},message:"must NOT have more than 200 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}else {const err9 = {instancePath:instancePath+"/submitLabel",schemaPath:"#/properties/submitLabel/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}if(data.questions !== undefined){let data4 = data.questions;if(Array.isArray(data4)){if(data4.length > 64){const err10 = {instancePath:instancePath+"/questions",schemaPath:"#/properties/questions/maxItems",keyword:"maxItems",params:{limit: 64},message:"must NOT have more than 64 items"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(data4.length < 1){const err11 = {instancePath:instancePath+"/questions",schemaPath:"#/properties/questions/minItems",keyword:"minItems",params:{limit: 1},message:"must NOT have fewer than 1 items"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}const len0 = data4.length;for(let i0=0; i0 160){const err10 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/0/properties/requestId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(func1(data2) < 1){const err11 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/0/properties/requestId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}else {const err12 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/0/properties/requestId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.type !== undefined){if("input" !== data.type){const err13 = {instancePath:instancePath+"/type",schemaPath:"#/oneOf/0/properties/type/const",keyword:"const",params:{allowedValue: "input"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}if(data.status !== undefined){let data4 = data.status;if(!((((data4 === "pending") || (data4 === "resolved")) || (data4 === "expired")) || (data4 === "cancelled"))){const err14 = {instancePath:instancePath+"/status",schemaPath:"#/oneOf/0/properties/status/enum",keyword:"enum",params:{allowedValues: schema203.oneOf[0].properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}if(data.prompt !== undefined){let data5 = data.prompt;if(typeof data5 === "string"){if(func1(data5) > 4000){const err15 = {instancePath:instancePath+"/prompt",schemaPath:"#/oneOf/0/properties/prompt/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}if(func1(data5) < 1){const err16 = {instancePath:instancePath+"/prompt",schemaPath:"#/oneOf/0/properties/prompt/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}else {const err17 = {instancePath:instancePath+"/prompt",schemaPath:"#/oneOf/0/properties/prompt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}if(data.input !== undefined){if(!(validate112(data.input, {instancePath:instancePath+"/input",parentData:data,parentDataProperty:"input",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate112.errors : vErrors.concat(validate112.errors);errors = vErrors.length;}}if(data.origin !== undefined){let data7 = data.origin;if(data7 && typeof data7 == "object" && !Array.isArray(data7)){if(data7.adapter === undefined){const err18 = {instancePath:instancePath+"/origin",schemaPath:"#/oneOf/0/properties/origin/required",keyword:"required",params:{missingProperty: "adapter"},message:"must have required property '"+"adapter"+"'"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}for(const key1 in data7){if(!(((key1 === "adapter") || (key1 === "provider")) || (key1 === "method"))){const err19 = {instancePath:instancePath+"/origin",schemaPath:"#/oneOf/0/properties/origin/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key1},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}if(data7.adapter !== undefined){let data8 = data7.adapter;if(typeof data8 === "string"){if(func1(data8) > 160){const err20 = {instancePath:instancePath+"/origin/adapter",schemaPath:"#/oneOf/0/properties/origin/properties/adapter/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}if(func1(data8) < 1){const err21 = {instancePath:instancePath+"/origin/adapter",schemaPath:"#/oneOf/0/properties/origin/properties/adapter/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}}else {const err22 = {instancePath:instancePath+"/origin/adapter",schemaPath:"#/oneOf/0/properties/origin/properties/adapter/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}if(data7.provider !== undefined){let data9 = data7.provider;if(typeof data9 === "string"){if(func1(data9) > 160){const err23 = {instancePath:instancePath+"/origin/provider",schemaPath:"#/oneOf/0/properties/origin/properties/provider/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}if(func1(data9) < 1){const err24 = {instancePath:instancePath+"/origin/provider",schemaPath:"#/oneOf/0/properties/origin/properties/provider/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}else {const err25 = {instancePath:instancePath+"/origin/provider",schemaPath:"#/oneOf/0/properties/origin/properties/provider/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}if(data7.method !== undefined){let data10 = data7.method;if(typeof data10 === "string"){if(func1(data10) > 500){const err26 = {instancePath:instancePath+"/origin/method",schemaPath:"#/oneOf/0/properties/origin/properties/method/maxLength",keyword:"maxLength",params:{limit: 500},message:"must NOT have more than 500 characters"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}if(func1(data10) < 1){const err27 = {instancePath:instancePath+"/origin/method",schemaPath:"#/oneOf/0/properties/origin/properties/method/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}}else {const err28 = {instancePath:instancePath+"/origin/method",schemaPath:"#/oneOf/0/properties/origin/properties/method/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}}else {const err29 = {instancePath:instancePath+"/origin",schemaPath:"#/oneOf/0/properties/origin/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}}if(data.turnId !== undefined){let data11 = data.turnId;if(typeof data11 === "string"){if(func1(data11) > 240){const err30 = {instancePath:instancePath+"/turnId",schemaPath:"#/oneOf/0/properties/turnId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}if(func1(data11) < 1){const err31 = {instancePath:instancePath+"/turnId",schemaPath:"#/oneOf/0/properties/turnId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}}else {const err32 = {instancePath:instancePath+"/turnId",schemaPath:"#/oneOf/0/properties/turnId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}}if(data.itemId !== undefined){let data12 = data.itemId;if(typeof data12 === "string"){if(func1(data12) > 240){const err33 = {instancePath:instancePath+"/itemId",schemaPath:"#/oneOf/0/properties/itemId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}if(func1(data12) < 1){const err34 = {instancePath:instancePath+"/itemId",schemaPath:"#/oneOf/0/properties/itemId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}}else {const err35 = {instancePath:instancePath+"/itemId",schemaPath:"#/oneOf/0/properties/itemId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}}}else {const err36 = {instancePath,schemaPath:"#/oneOf/0/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}var _valid0 = _errs1 === errors;if(_valid0){valid0 = true;passing0 = 0;var props0 = true;}const _errs26 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err37 = {instancePath,schemaPath:"#/oneOf/1/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}if(data.requestKind === undefined){const err38 = {instancePath,schemaPath:"#/oneOf/1/required",keyword:"required",params:{missingProperty: "requestKind"},message:"must have required property '"+"requestKind"+"'"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}if(data.requestId === undefined){const err39 = {instancePath,schemaPath:"#/oneOf/1/required",keyword:"required",params:{missingProperty: "requestId"},message:"must have required property '"+"requestId"+"'"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}if(data.type === undefined){const err40 = {instancePath,schemaPath:"#/oneOf/1/required",keyword:"required",params:{missingProperty: "type"},message:"must have required property '"+"type"+"'"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}if(data.status === undefined){const err41 = {instancePath,schemaPath:"#/oneOf/1/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}if(data.prompt === undefined){const err42 = {instancePath,schemaPath:"#/oneOf/1/required",keyword:"required",params:{missingProperty: "prompt"},message:"must have required property '"+"prompt"+"'"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}if(data.schema !== undefined){if("paperclip.runtime_request.v1" !== data.schema){const err43 = {instancePath:instancePath+"/schema",schemaPath:"#/oneOf/1/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.runtime_request.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}}if(data.requestKind !== undefined){if("runtime" !== data.requestKind){const err44 = {instancePath:instancePath+"/requestKind",schemaPath:"#/oneOf/1/properties/requestKind/const",keyword:"const",params:{allowedValue: "runtime"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err44];}else {vErrors.push(err44);}errors++;}}if(data.requestId !== undefined){let data15 = data.requestId;if(typeof data15 === "string"){if(func1(data15) > 160){const err45 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/1/properties/requestId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err45];}else {vErrors.push(err45);}errors++;}if(func1(data15) < 1){const err46 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/1/properties/requestId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err46];}else {vErrors.push(err46);}errors++;}}else {const err47 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/1/properties/requestId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err47];}else {vErrors.push(err47);}errors++;}}if(data.type !== undefined){let data16 = data.type;if(!((data16 === "permission") || (data16 === "input"))){const err48 = {instancePath:instancePath+"/type",schemaPath:"#/oneOf/1/properties/type/enum",keyword:"enum",params:{allowedValues: schema203.oneOf[1].properties.type.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err48];}else {vErrors.push(err48);}errors++;}}if(data.status !== undefined){let data17 = data.status;if(!((((data17 === "pending") || (data17 === "resolved")) || (data17 === "expired")) || (data17 === "cancelled"))){const err49 = {instancePath:instancePath+"/status",schemaPath:"#/oneOf/1/properties/status/enum",keyword:"enum",params:{allowedValues: schema203.oneOf[1].properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err49];}else {vErrors.push(err49);}errors++;}}if(data.prompt !== undefined){let data18 = data.prompt;if(typeof data18 === "string"){if(func1(data18) > 4000){const err50 = {instancePath:instancePath+"/prompt",schemaPath:"#/oneOf/1/properties/prompt/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err50];}else {vErrors.push(err50);}errors++;}if(func1(data18) < 1){const err51 = {instancePath:instancePath+"/prompt",schemaPath:"#/oneOf/1/properties/prompt/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err51];}else {vErrors.push(err51);}errors++;}}else {const err52 = {instancePath:instancePath+"/prompt",schemaPath:"#/oneOf/1/properties/prompt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err52];}else {vErrors.push(err52);}errors++;}}if(data.choices !== undefined){let data19 = data.choices;if(Array.isArray(data19)){const len0 = data19.length;for(let i0=0; i0 160){const err70 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/2/properties/requestId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err70];}else {vErrors.push(err70);}errors++;}if(func1(data25) < 1){const err71 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/2/properties/requestId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err71];}else {vErrors.push(err71);}errors++;}}else {const err72 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/2/properties/requestId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err72];}else {vErrors.push(err72);}errors++;}}if(data.kind !== undefined){let data26 = data.kind;if(!(((((data26 === "request_confirmation") || (data26 === "request_checkbox_confirmation")) || (data26 === "request_item_verdicts")) || (data26 === "ask_user_questions")) || (data26 === "suggest_tasks"))){const err73 = {instancePath:instancePath+"/kind",schemaPath:"#/oneOf/2/properties/kind/enum",keyword:"enum",params:{allowedValues: schema203.oneOf[2].properties.kind.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err73];}else {vErrors.push(err73);}errors++;}}if(data.blocking !== undefined){if(typeof data.blocking !== "boolean"){const err74 = {instancePath:instancePath+"/blocking",schemaPath:"#/oneOf/2/properties/blocking/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err74];}else {vErrors.push(err74);}errors++;}}if(data.payload !== undefined){let data28 = data.payload;if(data28 && typeof data28 == "object" && !Array.isArray(data28)){}else {const err75 = {instancePath:instancePath+"/payload",schemaPath:"#/oneOf/2/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err75];}else {vErrors.push(err75);}errors++;}}}else {const err76 = {instancePath,schemaPath:"#/oneOf/2/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err76];}else {vErrors.push(err76);}errors++;}var _valid0 = _errs46 === errors;if(_valid0 && valid0){valid0 = false;passing0 = [passing0, 2];}else {if(_valid0){valid0 = true;passing0 = 2;if(props0 !== true){props0 = true;}}}}if(!valid0){const err77 = {instancePath,schemaPath:"#/oneOf",keyword:"oneOf",params:{passingSchemas: passing0},message:"must match exactly one schema in oneOf"};if(vErrors === null){vErrors = [err77];}else {vErrors.push(err77);}errors++;}else {errors = _errs0;if(vErrors !== null){if(_errs0){vErrors.length = _errs0;}else {vErrors = null;}}}validate111.errors = vErrors;evaluated0.props = props0;return errors === 0;}validate111.evaluated = {"dynamicProps":true,"dynamicItems":false};const pattern12 = new RegExp("^[a-z][a-z0-9_.-]*$", "u");function validate20(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){/*# sourceURL="https://paperclip.dev/schemas/prp/v1/fixture.schema.json" */;let vErrors = null;let errors = 0;const evaluated0 = validate20.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.fixtureVersion === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "fixtureVersion"},message:"must have required property '"+"fixtureVersion"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.protocolVersion === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "protocolVersion"},message:"must have required property '"+"protocolVersion"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.name === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "name"},message:"must have required property '"+"name"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.description === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "description"},message:"must have required property '"+"description"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.identity === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "identity"},message:"must have required property '"+"identity"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.capabilities === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "capabilities"},message:"must have required property '"+"capabilities"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(data.commands === undefined){const err7 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "commands"},message:"must have required property '"+"commands"+"'"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(data.events === undefined){const err8 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "events"},message:"must have required property '"+"events"+"'"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(data.result === undefined){const err9 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "result"},message:"must have required property '"+"result"+"'"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(data.schema !== undefined){if("paperclip.prp.fixture.v1" !== data.schema){const err10 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.fixture.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.fixtureVersion !== undefined){if(1 !== data.fixtureVersion){const err11 = {instancePath:instancePath+"/fixtureVersion",schemaPath:"#/properties/fixtureVersion/const",keyword:"const",params:{allowedValue: 1},message:"must be equal to constant"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.protocolVersion !== undefined){let data2 = data.protocolVersion;if(!((data2 === 1) || (data2 === 2))){const err12 = {instancePath:instancePath+"/protocolVersion",schemaPath:"#/properties/protocolVersion/enum",keyword:"enum",params:{allowedValues: schema31.properties.protocolVersion.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.name !== undefined){let data3 = data.name;if(typeof data3 === "string"){if(func1(data3) > 120){const err13 = {instancePath:instancePath+"/name",schemaPath:"#/properties/name/maxLength",keyword:"maxLength",params:{limit: 120},message:"must NOT have more than 120 characters"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(func1(data3) < 1){const err14 = {instancePath:instancePath+"/name",schemaPath:"#/properties/name/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}else {const err15 = {instancePath:instancePath+"/name",schemaPath:"#/properties/name/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}if(data.description !== undefined){let data4 = data.description;if(typeof data4 === "string"){if(func1(data4) > 1000){const err16 = {instancePath:instancePath+"/description",schemaPath:"#/properties/description/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}if(func1(data4) < 1){const err17 = {instancePath:instancePath+"/description",schemaPath:"#/properties/description/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}else {const err18 = {instancePath:instancePath+"/description",schemaPath:"#/properties/description/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}if(data.identity !== undefined){if(!(validate21(data.identity, {instancePath:instancePath+"/identity",parentData:data,parentDataProperty:"identity",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate21.errors : vErrors.concat(validate21.errors);errors = vErrors.length;}}if(data.capabilities !== undefined){let data6 = data.capabilities;const _errs11 = errors;let valid1 = false;let passing0 = null;const _errs12 = errors;if(data6 && typeof data6 == "object" && !Array.isArray(data6)){if(data6.schema === undefined){const err19 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}if(data6.sessionReusePolicy === undefined){const err20 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "sessionReusePolicy"},message:"must have required property '"+"sessionReusePolicy"+"'"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}if(data6.driver === undefined){const err21 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "driver"},message:"must have required property '"+"driver"+"'"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(data6.steer === undefined){const err22 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "steer"},message:"must have required property '"+"steer"+"'"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}if(data6.interrupt === undefined){const err23 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "interrupt"},message:"must have required property '"+"interrupt"+"'"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}if(data6.resume === undefined){const err24 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "resume"},message:"must have required property '"+"resume"+"'"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}if(data6.runtimeRequests === undefined){const err25 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "runtimeRequests"},message:"must have required property '"+"runtimeRequests"+"'"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}if(data6.structuredResult === undefined){const err26 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "structuredResult"},message:"must have required property '"+"structuredResult"+"'"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}if(data6.typedEvents === undefined){const err27 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "typedEvents"},message:"must have required property '"+"typedEvents"+"'"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if(data6.schema !== undefined){if("paperclip.prp.capabilities.v1" !== data6.schema){const err28 = {instancePath:instancePath+"/capabilities/schema",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.capabilities.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}if(data6.sessionReusePolicy !== undefined){let data8 = data6.sessionReusePolicy;if(!(((data8 === "new_per_run") || (data8 === "reuse_per_issue")) || (data8 === "reuse_per_workspace"))){const err29 = {instancePath:instancePath+"/capabilities/sessionReusePolicy",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/sessionReusePolicy/enum",keyword:"enum",params:{allowedValues: schema41.properties.sessionReusePolicy.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}}if(data6.driver !== undefined){let data9 = data6.driver;if(data9 && typeof data9 == "object" && !Array.isArray(data9)){if(data9.kind === undefined){const err30 = {instancePath:instancePath+"/capabilities/driver",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/required",keyword:"required",params:{missingProperty: "kind"},message:"must have required property '"+"kind"+"'"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}if(data9.version === undefined){const err31 = {instancePath:instancePath+"/capabilities/driver",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/required",keyword:"required",params:{missingProperty: "version"},message:"must have required property '"+"version"+"'"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}if(data9.kind !== undefined){let data10 = data9.kind;if(typeof data10 === "string"){if(func1(data10) > 80){const err32 = {instancePath:instancePath+"/capabilities/driver/kind",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/properties/kind/maxLength",keyword:"maxLength",params:{limit: 80},message:"must NOT have more than 80 characters"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}if(func1(data10) < 1){const err33 = {instancePath:instancePath+"/capabilities/driver/kind",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/properties/kind/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}}else {const err34 = {instancePath:instancePath+"/capabilities/driver/kind",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/properties/kind/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}}if(data9.version !== undefined){let data11 = data9.version;if(typeof data11 === "string"){if(func1(data11) > 80){const err35 = {instancePath:instancePath+"/capabilities/driver/version",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/properties/version/maxLength",keyword:"maxLength",params:{limit: 80},message:"must NOT have more than 80 characters"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}if(func1(data11) < 1){const err36 = {instancePath:instancePath+"/capabilities/driver/version",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/properties/version/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}}else {const err37 = {instancePath:instancePath+"/capabilities/driver/version",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/properties/version/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}}}else {const err38 = {instancePath:instancePath+"/capabilities/driver",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}}if(data6.steer !== undefined){if(typeof data6.steer !== "boolean"){const err39 = {instancePath:instancePath+"/capabilities/steer",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/steer/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}}if(data6.interrupt !== undefined){if(typeof data6.interrupt !== "boolean"){const err40 = {instancePath:instancePath+"/capabilities/interrupt",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/interrupt/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}}if(data6.resume !== undefined){if(typeof data6.resume !== "boolean"){const err41 = {instancePath:instancePath+"/capabilities/resume",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/resume/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}}if(data6.runtimeRequests !== undefined){if(typeof data6.runtimeRequests !== "boolean"){const err42 = {instancePath:instancePath+"/capabilities/runtimeRequests",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/runtimeRequests/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}}if(data6.structuredResult !== undefined){if(typeof data6.structuredResult !== "boolean"){const err43 = {instancePath:instancePath+"/capabilities/structuredResult",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/structuredResult/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}}if(data6.typedEvents !== undefined){if(typeof data6.typedEvents !== "boolean"){const err44 = {instancePath:instancePath+"/capabilities/typedEvents",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEvents/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err44];}else {vErrors.push(err44);}errors++;}}if(data6.typedEventFamilies !== undefined){let data18 = data6.typedEventFamilies;if(Array.isArray(data18)){if(data18.length > 64){const err45 = {instancePath:instancePath+"/capabilities/typedEventFamilies",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/maxItems",keyword:"maxItems",params:{limit: 64},message:"must NOT have more than 64 items"};if(vErrors === null){vErrors = [err45];}else {vErrors.push(err45);}errors++;}const len0 = data18.length;for(let i0=0; i0 160){const err51 = {instancePath:instancePath+"/capabilities/typedEventFamilies/" + i0+"/family",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/items/properties/family/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err51];}else {vErrors.push(err51);}errors++;}if(func1(data20) < 1){const err52 = {instancePath:instancePath+"/capabilities/typedEventFamilies/" + i0+"/family",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/items/properties/family/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err52];}else {vErrors.push(err52);}errors++;}if(!pattern12.test(data20)){const err53 = {instancePath:instancePath+"/capabilities/typedEventFamilies/" + i0+"/family",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/items/properties/family/pattern",keyword:"pattern",params:{pattern: "^[a-z][a-z0-9_.-]*$"},message:"must match pattern \""+"^[a-z][a-z0-9_.-]*$"+"\""};if(vErrors === null){vErrors = [err53];}else {vErrors.push(err53);}errors++;}}else {const err54 = {instancePath:instancePath+"/capabilities/typedEventFamilies/" + i0+"/family",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/items/properties/family/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err54];}else {vErrors.push(err54);}errors++;}}if(data19.version !== undefined){if(1 !== data19.version){const err55 = {instancePath:instancePath+"/capabilities/typedEventFamilies/" + i0+"/version",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/items/properties/version/const",keyword:"const",params:{allowedValue: 1},message:"must be equal to constant"};if(vErrors === null){vErrors = [err55];}else {vErrors.push(err55);}errors++;}}if(data19.availability !== undefined){let data22 = data19.availability;if(!(((data22 === "available") || (data22 === "unsupported")) || (data22 === "policy_disabled"))){const err56 = {instancePath:instancePath+"/capabilities/typedEventFamilies/" + i0+"/availability",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/items/properties/availability/enum",keyword:"enum",params:{allowedValues: schema41.properties.typedEventFamilies.items.properties.availability.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err56];}else {vErrors.push(err56);}errors++;}}if(data19.detailLevel !== undefined){let data23 = data19.detailLevel;if(!((data23 === "summary") || (data23 === "structured"))){const err57 = {instancePath:instancePath+"/capabilities/typedEventFamilies/" + i0+"/detailLevel",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/items/properties/detailLevel/enum",keyword:"enum",params:{allowedValues: schema41.properties.typedEventFamilies.items.properties.detailLevel.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err57];}else {vErrors.push(err57);}errors++;}}}else {const err58 = {instancePath:instancePath+"/capabilities/typedEventFamilies/" + i0,schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/items/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err58];}else {vErrors.push(err58);}errors++;}}}else {const err59 = {instancePath:instancePath+"/capabilities/typedEventFamilies",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err59];}else {vErrors.push(err59);}errors++;}}if(data6.semanticTools !== undefined){let data24 = data6.semanticTools;if(data24 && typeof data24 == "object" && !Array.isArray(data24)){if(data24.schema === undefined){const err60 = {instancePath:instancePath+"/capabilities/semanticTools",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err60];}else {vErrors.push(err60);}errors++;}if(data24.schemaVersion === undefined){const err61 = {instancePath:instancePath+"/capabilities/semanticTools",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/required",keyword:"required",params:{missingProperty: "schemaVersion"},message:"must have required property '"+"schemaVersion"+"'"};if(vErrors === null){vErrors = [err61];}else {vErrors.push(err61);}errors++;}if(data24.operations === undefined){const err62 = {instancePath:instancePath+"/capabilities/semanticTools",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/required",keyword:"required",params:{missingProperty: "operations"},message:"must have required property '"+"operations"+"'"};if(vErrors === null){vErrors = [err62];}else {vErrors.push(err62);}errors++;}if(data24.schema !== undefined){if("paperclip.prp.semantic_tools.v1" !== data24.schema){const err63 = {instancePath:instancePath+"/capabilities/semanticTools/schema",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.semantic_tools.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err63];}else {vErrors.push(err63);}errors++;}}if(data24.schemaVersion !== undefined){if(1 !== data24.schemaVersion){const err64 = {instancePath:instancePath+"/capabilities/semanticTools/schemaVersion",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/schemaVersion/const",keyword:"const",params:{allowedValue: 1},message:"must be equal to constant"};if(vErrors === null){vErrors = [err64];}else {vErrors.push(err64);}errors++;}}if(data24.operations !== undefined){let data27 = data24.operations;if(Array.isArray(data27)){const len1 = data27.length;for(let i1=0; i1 160){const err69 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/operationId",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/operationId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err69];}else {vErrors.push(err69);}errors++;}if(func1(data29) < 1){const err70 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/operationId",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/operationId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err70];}else {vErrors.push(err70);}errors++;}if(!pattern4.test(data29)){const err71 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/operationId",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/operationId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err71];}else {vErrors.push(err71);}errors++;}}else {const err72 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/operationId",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/operationId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err72];}else {vErrors.push(err72);}errors++;}}if(data28.version !== undefined){if(1 !== data28.version){const err73 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/version",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/version/const",keyword:"const",params:{allowedValue: 1},message:"must be equal to constant"};if(vErrors === null){vErrors = [err73];}else {vErrors.push(err73);}errors++;}}if(data28.availability !== undefined){let data31 = data28.availability;if(!(((data31 === "available") || (data31 === "denied")) || (data31 === "unsupported"))){const err74 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/availability",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/availability/enum",keyword:"enum",params:{allowedValues: schema41.properties.semanticTools.properties.operations.items.properties.availability.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err74];}else {vErrors.push(err74);}errors++;}}if(data28.redactionDisposition !== undefined){if("digest_only" !== data28.redactionDisposition){const err75 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/redactionDisposition",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/redactionDisposition/const",keyword:"const",params:{allowedValue: "digest_only"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err75];}else {vErrors.push(err75);}errors++;}}if(data28.requiredClaims !== undefined){let data33 = data28.requiredClaims;if(Array.isArray(data33)){const len2 = data33.length;for(let i2=0; i2 160){const err76 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/requiredClaims/" + i2,schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/requiredClaims/items/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err76];}else {vErrors.push(err76);}errors++;}if(func1(data34) < 1){const err77 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/requiredClaims/" + i2,schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/requiredClaims/items/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err77];}else {vErrors.push(err77);}errors++;}}else {const err78 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/requiredClaims/" + i2,schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/requiredClaims/items/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err78];}else {vErrors.push(err78);}errors++;}}let i3 = data33.length;let j0;if(i3 > 1){const indices0 = {};for(;i3--;){let item0 = data33[i3];if(typeof item0 !== "string"){continue;}if(typeof indices0[item0] == "number"){j0 = indices0[item0];const err79 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/requiredClaims",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/requiredClaims/uniqueItems",keyword:"uniqueItems",params:{i: i3, j: j0},message:"must NOT have duplicate items (items ## "+j0+" and "+i3+" are identical)"};if(vErrors === null){vErrors = [err79];}else {vErrors.push(err79);}errors++;break;}indices0[item0] = i3;}}}else {const err80 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/requiredClaims",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/requiredClaims/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err80];}else {vErrors.push(err80);}errors++;}}if(data28.reasonCode !== undefined){let data35 = data28.reasonCode;if(typeof data35 === "string"){if(func1(data35) > 160){const err81 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/reasonCode/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err81];}else {vErrors.push(err81);}errors++;}if(func1(data35) < 1){const err82 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/reasonCode/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err82];}else {vErrors.push(err82);}errors++;}if(!pattern14.test(data35)){const err83 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/reasonCode/pattern",keyword:"pattern",params:{pattern: "^[a-z][a-z0-9_.:-]*$"},message:"must match pattern \""+"^[a-z][a-z0-9_.:-]*$"+"\""};if(vErrors === null){vErrors = [err83];}else {vErrors.push(err83);}errors++;}}else {const err84 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/reasonCode/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err84];}else {vErrors.push(err84);}errors++;}}}else {const err85 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1,schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err85];}else {vErrors.push(err85);}errors++;}}}else {const err86 = {instancePath:instancePath+"/capabilities/semanticTools/operations",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err86];}else {vErrors.push(err86);}errors++;}}}else {const err87 = {instancePath:instancePath+"/capabilities/semanticTools",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err87];}else {vErrors.push(err87);}errors++;}}if(data6.unsupported !== undefined){let data36 = data6.unsupported;if(Array.isArray(data36)){const len3 = data36.length;for(let i4=0; i4 1){const indices1 = {};for(;i5--;){let item1 = data36[i5];if(typeof item1 !== "string"){continue;}if(typeof indices1[item1] == "number"){j1 = indices1[item1];const err90 = {instancePath:instancePath+"/capabilities/unsupported",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/unsupported/uniqueItems",keyword:"uniqueItems",params:{i: i5, j: j1},message:"must NOT have duplicate items (items ## "+j1+" and "+i5+" are identical)"};if(vErrors === null){vErrors = [err90];}else {vErrors.push(err90);}errors++;break;}indices1[item1] = i5;}}}else {const err91 = {instancePath:instancePath+"/capabilities/unsupported",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/unsupported/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err91];}else {vErrors.push(err91);}errors++;}}}else {const err92 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err92];}else {vErrors.push(err92);}errors++;}var _valid0 = _errs12 === errors;if(_valid0){valid1 = true;passing0 = 0;var props0 = true;}const _errs72 = errors;if(!(validate23(data6, {instancePath:instancePath+"/capabilities",parentData:data,parentDataProperty:"capabilities",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate23.errors : vErrors.concat(validate23.errors);errors = vErrors.length;}var _valid0 = _errs72 === errors;if(_valid0 && valid1){valid1 = false;passing0 = [passing0, 1];}else {if(_valid0){valid1 = true;passing0 = 1;if(props0 !== true){props0 = true;}}}if(!valid1){const err93 = {instancePath:instancePath+"/capabilities",schemaPath:"#/properties/capabilities/oneOf",keyword:"oneOf",params:{passingSchemas: passing0},message:"must match exactly one schema in oneOf"};if(vErrors === null){vErrors = [err93];}else {vErrors.push(err93);}errors++;}else {errors = _errs11;if(vErrors !== null){if(_errs11){vErrors.length = _errs11;}else {vErrors = null;}}}}if(data.commands !== undefined){let data38 = data.commands;if(Array.isArray(data38)){const len4 = data38.length;for(let i6=0; i6 160){const err101 = {instancePath:instancePath+"/commands/" + i6+"/commandId",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/commandId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err101];}else {vErrors.push(err101);}errors++;}if(func1(data41) < 1){const err102 = {instancePath:instancePath+"/commands/" + i6+"/commandId",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/commandId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err102];}else {vErrors.push(err102);}errors++;}}else {const err103 = {instancePath:instancePath+"/commands/" + i6+"/commandId",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/commandId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err103];}else {vErrors.push(err103);}errors++;}}if(data39.controllerSeq !== undefined){let data42 = data39.controllerSeq;if(!(((typeof data42 == "number") && (!(data42 % 1) && !isNaN(data42))) && (isFinite(data42)))){const err104 = {instancePath:instancePath+"/commands/" + i6+"/controllerSeq",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/controllerSeq/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err104];}else {vErrors.push(err104);}errors++;}if((typeof data42 == "number") && (isFinite(data42))){if(data42 < 1 || isNaN(data42)){const err105 = {instancePath:instancePath+"/commands/" + i6+"/controllerSeq",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/controllerSeq/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err105];}else {vErrors.push(err105);}errors++;}}}if(data39.type !== undefined){let data43 = data39.type;if(!((((((((((((((((((data43 === "run.prepare") || (data43 === "run.attach")) || (data43 === "session.open")) || (data43 === "turn.start")) || (data43 === "turn.steer")) || (data43 === "turn.interrupt")) || (data43 === "turn.stop")) || (data43 === "request.resolve")) || (data43 === "interaction.receipt")) || (data43 === "semantic_tool.result")) || (data43 === "session.snapshot")) || (data43 === "session.close")) || (data43 === "session.budget.increase")) || (data43 === "session.destroy")) || (data43 === "run.cancel")) || (data43 === "runner.drain")) || (data43 === "runner.suspend")) || (data43 === "runner.shutdown"))){const err106 = {instancePath:instancePath+"/commands/" + i6+"/type",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/type/enum",keyword:"enum",params:{allowedValues: schema45.properties.type.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err106];}else {vErrors.push(err106);}errors++;}}if(data39.issuedAt !== undefined){let data44 = data39.issuedAt;if(typeof data44 === "string"){if(!(formats0.test(data44))){const err107 = {instancePath:instancePath+"/commands/" + i6+"/issuedAt",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/issuedAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err107];}else {vErrors.push(err107);}errors++;}}else {const err108 = {instancePath:instancePath+"/commands/" + i6+"/issuedAt",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/issuedAt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err108];}else {vErrors.push(err108);}errors++;}}if(data39.deadlineAt !== undefined){let data45 = data39.deadlineAt;if(typeof data45 === "string"){if(!(formats0.test(data45))){const err109 = {instancePath:instancePath+"/commands/" + i6+"/deadlineAt",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/deadlineAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err109];}else {vErrors.push(err109);}errors++;}}else {const err110 = {instancePath:instancePath+"/commands/" + i6+"/deadlineAt",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/deadlineAt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err110];}else {vErrors.push(err110);}errors++;}}if(data39.precondition !== undefined){let data46 = data39.precondition;if(data46 && typeof data46 == "object" && !Array.isArray(data46)){if(data46.runnerState !== undefined){let data47 = data46.runnerState;if(Array.isArray(data47)){const len5 = data47.length;for(let i7=0; i7 160){const err122 = {instancePath:instancePath+"/commands/" + i6+"/payload/requestId",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/requestId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err122];}else {vErrors.push(err122);}errors++;}if(func1(data57) < 1){const err123 = {instancePath:instancePath+"/commands/" + i6+"/payload/requestId",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/requestId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err123];}else {vErrors.push(err123);}errors++;}}else {const err124 = {instancePath:instancePath+"/commands/" + i6+"/payload/requestId",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/requestId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err124];}else {vErrors.push(err124);}errors++;}}if(data56.objective !== undefined){let data58 = data56.objective;if(typeof data58 === "string"){if(func1(data58) > 4000){const err125 = {instancePath:instancePath+"/commands/" + i6+"/payload/objective",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/objective/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err125];}else {vErrors.push(err125);}errors++;}if(func1(data58) < 1){const err126 = {instancePath:instancePath+"/commands/" + i6+"/payload/objective",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/objective/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err126];}else {vErrors.push(err126);}errors++;}}else {const err127 = {instancePath:instancePath+"/commands/" + i6+"/payload/objective",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/objective/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err127];}else {vErrors.push(err127);}errors++;}}if(data56.status !== undefined){let data59 = data56.status;if(!((data59 === "active") || (data59 === "paused"))){const err128 = {instancePath:instancePath+"/commands/" + i6+"/payload/status",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/status/enum",keyword:"enum",params:{allowedValues: schema46.allOf[0].then.properties.payload.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err128];}else {vErrors.push(err128);}errors++;}}if(data56.tokenBudget !== undefined){let data60 = data56.tokenBudget;if((!(((typeof data60 == "number") && (!(data60 % 1) && !isNaN(data60))) && (isFinite(data60)))) && (data60 !== null)){const err129 = {instancePath:instancePath+"/commands/" + i6+"/payload/tokenBudget",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/tokenBudget/type",keyword:"type",params:{type: schema46.allOf[0].then.properties.payload.properties.tokenBudget.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err129];}else {vErrors.push(err129);}errors++;}if((typeof data60 == "number") && (isFinite(data60))){if(data60 < 1 || isNaN(data60)){const err130 = {instancePath:instancePath+"/commands/" + i6+"/payload/tokenBudget",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/tokenBudget/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err130];}else {vErrors.push(err130);}errors++;}}}}else {const err131 = {instancePath:instancePath+"/commands/" + i6+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err131];}else {vErrors.push(err131);}errors++;}}}var _valid2 = _errs118 === errors;valid32 = _valid2;if(valid32){var props2 = {};props2.payload = true;props2.type = true;}}if(!valid32){const err132 = {instancePath:instancePath+"/commands/" + i6,schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err132];}else {vErrors.push(err132);}errors++;}if(data39 && typeof data39 == "object" && !Array.isArray(data39)){if(data39.schema === undefined){const err133 = {instancePath:instancePath+"/commands/" + i6,schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err133];}else {vErrors.push(err133);}errors++;}if(data39.commandId === undefined){const err134 = {instancePath:instancePath+"/commands/" + i6,schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/required",keyword:"required",params:{missingProperty: "commandId"},message:"must have required property '"+"commandId"+"'"};if(vErrors === null){vErrors = [err134];}else {vErrors.push(err134);}errors++;}if(data39.controllerSeq === undefined){const err135 = {instancePath:instancePath+"/commands/" + i6,schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/required",keyword:"required",params:{missingProperty: "controllerSeq"},message:"must have required property '"+"controllerSeq"+"'"};if(vErrors === null){vErrors = [err135];}else {vErrors.push(err135);}errors++;}if(data39.type === undefined){const err136 = {instancePath:instancePath+"/commands/" + i6,schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/required",keyword:"required",params:{missingProperty: "type"},message:"must have required property '"+"type"+"'"};if(vErrors === null){vErrors = [err136];}else {vErrors.push(err136);}errors++;}if(data39.issuedAt === undefined){const err137 = {instancePath:instancePath+"/commands/" + i6,schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/required",keyword:"required",params:{missingProperty: "issuedAt"},message:"must have required property '"+"issuedAt"+"'"};if(vErrors === null){vErrors = [err137];}else {vErrors.push(err137);}errors++;}if(data39.payload === undefined){const err138 = {instancePath:instancePath+"/commands/" + i6,schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/required",keyword:"required",params:{missingProperty: "payload"},message:"must have required property '"+"payload"+"'"};if(vErrors === null){vErrors = [err138];}else {vErrors.push(err138);}errors++;}if(data39.schema !== undefined){if("paperclip.prp.command.v2" !== data39.schema){const err139 = {instancePath:instancePath+"/commands/" + i6+"/schema",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.command.v2"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err139];}else {vErrors.push(err139);}errors++;}}if(data39.commandId !== undefined){let data62 = data39.commandId;if(typeof data62 === "string"){if(func1(data62) > 160){const err140 = {instancePath:instancePath+"/commands/" + i6+"/commandId",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/commandId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err140];}else {vErrors.push(err140);}errors++;}if(func1(data62) < 1){const err141 = {instancePath:instancePath+"/commands/" + i6+"/commandId",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/commandId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err141];}else {vErrors.push(err141);}errors++;}}else {const err142 = {instancePath:instancePath+"/commands/" + i6+"/commandId",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/commandId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err142];}else {vErrors.push(err142);}errors++;}}if(data39.controllerSeq !== undefined){let data63 = data39.controllerSeq;if(!(((typeof data63 == "number") && (!(data63 % 1) && !isNaN(data63))) && (isFinite(data63)))){const err143 = {instancePath:instancePath+"/commands/" + i6+"/controllerSeq",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/controllerSeq/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err143];}else {vErrors.push(err143);}errors++;}if((typeof data63 == "number") && (isFinite(data63))){if(data63 < 1 || isNaN(data63)){const err144 = {instancePath:instancePath+"/commands/" + i6+"/controllerSeq",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/controllerSeq/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err144];}else {vErrors.push(err144);}errors++;}}}if(data39.type !== undefined){let data64 = data39.type;if(!(((((((((((((((((((((data64 === "run.prepare") || (data64 === "run.attach")) || (data64 === "session.open")) || (data64 === "turn.start")) || (data64 === "turn.steer")) || (data64 === "turn.interrupt")) || (data64 === "turn.stop")) || (data64 === "request.resolve")) || (data64 === "interaction.receipt")) || (data64 === "semantic_tool.result")) || (data64 === "session.snapshot")) || (data64 === "session.close")) || (data64 === "session.budget.increase")) || (data64 === "session.destroy")) || (data64 === "run.cancel")) || (data64 === "runner.drain")) || (data64 === "runner.suspend")) || (data64 === "runner.shutdown")) || (data64 === "session.goal.get")) || (data64 === "session.goal.set")) || (data64 === "session.goal.clear"))){const err145 = {instancePath:instancePath+"/commands/" + i6+"/type",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/type/enum",keyword:"enum",params:{allowedValues: schema46.properties.type.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err145];}else {vErrors.push(err145);}errors++;}}if(data39.issuedAt !== undefined){let data65 = data39.issuedAt;if(typeof data65 === "string"){if(!(formats0.test(data65))){const err146 = {instancePath:instancePath+"/commands/" + i6+"/issuedAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/issuedAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err146];}else {vErrors.push(err146);}errors++;}}else {const err147 = {instancePath:instancePath+"/commands/" + i6+"/issuedAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/issuedAt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err147];}else {vErrors.push(err147);}errors++;}}if(data39.deadlineAt !== undefined){let data66 = data39.deadlineAt;if(typeof data66 === "string"){if(!(formats0.test(data66))){const err148 = {instancePath:instancePath+"/commands/" + i6+"/deadlineAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/deadlineAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err148];}else {vErrors.push(err148);}errors++;}}else {const err149 = {instancePath:instancePath+"/commands/" + i6+"/deadlineAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/deadlineAt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err149];}else {vErrors.push(err149);}errors++;}}if(data39.precondition !== undefined){let data67 = data39.precondition;if(data67 && typeof data67 == "object" && !Array.isArray(data67)){if(data67.runnerState !== undefined){let data68 = data67.runnerState;if(Array.isArray(data68)){const len8 = data68.length;for(let i10=0; i10 12000){const err182 = {instancePath:instancePath+"/result/summary",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/summary/maxLength",keyword:"maxLength",params:{limit: 12000},message:"must NOT have more than 12000 characters"};if(vErrors === null){vErrors = [err182];}else {vErrors.push(err182);}errors++;}if(func1(data85) < 1){const err183 = {instancePath:instancePath+"/result/summary",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/summary/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err183];}else {vErrors.push(err183);}errors++;}}else {const err184 = {instancePath:instancePath+"/result/summary",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/summary/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err184];}else {vErrors.push(err184);}errors++;}}if(data80.completionClaim !== undefined){let data86 = data80.completionClaim;if(data86 && typeof data86 == "object" && !Array.isArray(data86)){if(data86.contractRevision === undefined){const err185 = {instancePath:instancePath+"/result/completionClaim",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/required",keyword:"required",params:{missingProperty: "contractRevision"},message:"must have required property '"+"contractRevision"+"'"};if(vErrors === null){vErrors = [err185];}else {vErrors.push(err185);}errors++;}if(data86.objectiveSatisfied === undefined){const err186 = {instancePath:instancePath+"/result/completionClaim",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/required",keyword:"required",params:{missingProperty: "objectiveSatisfied"},message:"must have required property '"+"objectiveSatisfied"+"'"};if(vErrors === null){vErrors = [err186];}else {vErrors.push(err186);}errors++;}if(data86.criteria === undefined){const err187 = {instancePath:instancePath+"/result/completionClaim",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/required",keyword:"required",params:{missingProperty: "criteria"},message:"must have required property '"+"criteria"+"'"};if(vErrors === null){vErrors = [err187];}else {vErrors.push(err187);}errors++;}if(data86.remainingWork === undefined){const err188 = {instancePath:instancePath+"/result/completionClaim",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/required",keyword:"required",params:{missingProperty: "remainingWork"},message:"must have required property '"+"remainingWork"+"'"};if(vErrors === null){vErrors = [err188];}else {vErrors.push(err188);}errors++;}if(data86.contractRevision !== undefined){let data87 = data86.contractRevision;if(typeof data87 === "string"){if(func1(data87) < 1){const err189 = {instancePath:instancePath+"/result/completionClaim/contractRevision",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/properties/contractRevision/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err189];}else {vErrors.push(err189);}errors++;}}else {const err190 = {instancePath:instancePath+"/result/completionClaim/contractRevision",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/properties/contractRevision/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err190];}else {vErrors.push(err190);}errors++;}}if(data86.objectiveSatisfied !== undefined){if(typeof data86.objectiveSatisfied !== "boolean"){const err191 = {instancePath:instancePath+"/result/completionClaim/objectiveSatisfied",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/properties/objectiveSatisfied/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err191];}else {vErrors.push(err191);}errors++;}}if(data86.criteria !== undefined){let data89 = data86.criteria;if(Array.isArray(data89)){const len13 = data89.length;for(let i15=0; i15 12000){const err16 = {instancePath:instancePath+"/summary",schemaPath:"#/properties/summary/maxLength",keyword:"maxLength",params:{limit: 12000},message:"must NOT have more than 12000 characters"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}if(func1(data4) < 1){const err17 = {instancePath:instancePath+"/summary",schemaPath:"#/properties/summary/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}else {const err18 = {instancePath:instancePath+"/summary",schemaPath:"#/properties/summary/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}if(data.completionClaim !== undefined){let data5 = data.completionClaim;if(data5 && typeof data5 == "object" && !Array.isArray(data5)){if(data5.contractRevision === undefined){const err19 = {instancePath:instancePath+"/completionClaim",schemaPath:"#/properties/completionClaim/required",keyword:"required",params:{missingProperty: "contractRevision"},message:"must have required property '"+"contractRevision"+"'"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}if(data5.objectiveSatisfied === undefined){const err20 = {instancePath:instancePath+"/completionClaim",schemaPath:"#/properties/completionClaim/required",keyword:"required",params:{missingProperty: "objectiveSatisfied"},message:"must have required property '"+"objectiveSatisfied"+"'"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}if(data5.criteria === undefined){const err21 = {instancePath:instancePath+"/completionClaim",schemaPath:"#/properties/completionClaim/required",keyword:"required",params:{missingProperty: "criteria"},message:"must have required property '"+"criteria"+"'"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(data5.remainingWork === undefined){const err22 = {instancePath:instancePath+"/completionClaim",schemaPath:"#/properties/completionClaim/required",keyword:"required",params:{missingProperty: "remainingWork"},message:"must have required property '"+"remainingWork"+"'"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}if(data5.contractRevision !== undefined){let data6 = data5.contractRevision;if(typeof data6 === "string"){if(func1(data6) < 1){const err23 = {instancePath:instancePath+"/completionClaim/contractRevision",schemaPath:"#/properties/completionClaim/properties/contractRevision/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}else {const err24 = {instancePath:instancePath+"/completionClaim/contractRevision",schemaPath:"#/properties/completionClaim/properties/contractRevision/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}if(data5.objectiveSatisfied !== undefined){if(typeof data5.objectiveSatisfied !== "boolean"){const err25 = {instancePath:instancePath+"/completionClaim/objectiveSatisfied",schemaPath:"#/properties/completionClaim/properties/objectiveSatisfied/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}if(data5.criteria !== undefined){let data8 = data5.criteria;if(Array.isArray(data8)){const len0 = data8.length;for(let i0=0; i0 160){const err9 = {instancePath:instancePath+"/companyId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(func1(data1) < 1){const err10 = {instancePath:instancePath+"/companyId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(!pattern4.test(data1)){const err11 = {instancePath:instancePath+"/companyId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}else {const err12 = {instancePath:instancePath+"/companyId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.issueId !== undefined){let data2 = data.issueId;if(typeof data2 === "string"){if(func1(data2) > 160){const err13 = {instancePath:instancePath+"/issueId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(func1(data2) < 1){const err14 = {instancePath:instancePath+"/issueId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(!pattern4.test(data2)){const err15 = {instancePath:instancePath+"/issueId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}else {const err16 = {instancePath:instancePath+"/issueId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}if(data.runId !== undefined){let data3 = data.runId;if(typeof data3 === "string"){if(func1(data3) > 160){const err17 = {instancePath:instancePath+"/runId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}if(func1(data3) < 1){const err18 = {instancePath:instancePath+"/runId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}if(!pattern4.test(data3)){const err19 = {instancePath:instancePath+"/runId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}else {const err20 = {instancePath:instancePath+"/runId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}if(data.environmentLeaseId !== undefined){let data4 = data.environmentLeaseId;if(typeof data4 === "string"){if(func1(data4) > 160){const err21 = {instancePath:instancePath+"/environmentLeaseId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(func1(data4) < 1){const err22 = {instancePath:instancePath+"/environmentLeaseId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}if(!pattern4.test(data4)){const err23 = {instancePath:instancePath+"/environmentLeaseId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}else {const err24 = {instancePath:instancePath+"/environmentLeaseId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}if(data.runnerInstanceId !== undefined){let data5 = data.runnerInstanceId;if(typeof data5 === "string"){if(func1(data5) > 160){const err25 = {instancePath:instancePath+"/runnerInstanceId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}if(func1(data5) < 1){const err26 = {instancePath:instancePath+"/runnerInstanceId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}if(!pattern4.test(data5)){const err27 = {instancePath:instancePath+"/runnerInstanceId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}}else {const err28 = {instancePath:instancePath+"/runnerInstanceId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}if(data.normalizedSessionId !== undefined){let data6 = data.normalizedSessionId;if(typeof data6 === "string"){if(func1(data6) > 160){const err29 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}if(func1(data6) < 1){const err30 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}if(!pattern4.test(data6)){const err31 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}}else {const err32 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}}if(data.driverSessionId !== undefined){let data7 = data.driverSessionId;if(typeof data7 === "string"){if(func1(data7) > 160){const err33 = {instancePath:instancePath+"/driverSessionId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}if(func1(data7) < 1){const err34 = {instancePath:instancePath+"/driverSessionId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}if(!pattern4.test(data7)){const err35 = {instancePath:instancePath+"/driverSessionId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}}else {const err36 = {instancePath:instancePath+"/driverSessionId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}}if(data.providerSessionId !== undefined){let data8 = data.providerSessionId;if(typeof data8 === "string"){if(func1(data8) > 160){const err37 = {instancePath:instancePath+"/providerSessionId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}if(func1(data8) < 1){const err38 = {instancePath:instancePath+"/providerSessionId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}if(!pattern4.test(data8)){const err39 = {instancePath:instancePath+"/providerSessionId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}}else {const err40 = {instancePath:instancePath+"/providerSessionId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}}}else {const err41 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}validate21.errors = vErrors;return errors === 0;}validate21.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema42 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v2/capabilities.schema.json","title":"PRP v2 negotiated capabilities","type":"object","required":["schema","sessionReusePolicy","driver","steer","interrupt","resume","runtimeRequests","structuredResult","typedEvents","sessionGoals"],"properties":{"schema":{"const":"paperclip.prp.capabilities.v2"},"sessionReusePolicy":{"enum":["new_per_run","reuse_per_issue","reuse_per_workspace"]},"driver":{"type":"object","required":["kind","version"],"properties":{"kind":{"type":"string","minLength":1,"maxLength":80},"version":{"type":"string","minLength":1,"maxLength":80}},"additionalProperties":true},"steer":{"type":"boolean"},"interrupt":{"type":"boolean"},"resume":{"type":"boolean"},"runtimeRequests":{"type":"boolean"},"structuredResult":{"type":"boolean"},"typedEvents":{"type":"boolean"},"sessionGoals":{"$ref":"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability"},"unsupported":{"type":"array","items":{"type":"string","minLength":1},"uniqueItems":true}},"additionalProperties":true};const schema44 = {"type":"object","required":["availability","actions","autonomousUpdates","persistentAcrossResume","maxObjectiveChars","tokenBudgetControl","usageReporting"],"properties":{"availability":{"enum":["available","unsupported","policy_disabled"]},"actions":{"type":"array","items":{"enum":["set","pause","resume","clear"]},"uniqueItems":true},"autonomousUpdates":{"type":"boolean"},"persistentAcrossResume":{"type":"boolean"},"maxObjectiveChars":{"type":"integer","minimum":1,"maximum":4000},"tokenBudgetControl":{"type":"boolean"},"usageReporting":{"type":"boolean"},"reasonCode":{"type":"string","minLength":1,"maxLength":160},"reason":{"type":"string","minLength":1,"maxLength":1000}},"additionalProperties":true};const func0 = typeof equalModule === "function" ? equalModule : equalModule.default;function validate23(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){/*# sourceURL="https://paperclip.dev/schemas/prp/v2/capabilities.schema.json" */;let vErrors = null;let errors = 0;const evaluated0 = validate23.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.sessionReusePolicy === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sessionReusePolicy"},message:"must have required property '"+"sessionReusePolicy"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.driver === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "driver"},message:"must have required property '"+"driver"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.steer === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "steer"},message:"must have required property '"+"steer"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.interrupt === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "interrupt"},message:"must have required property '"+"interrupt"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.resume === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "resume"},message:"must have required property '"+"resume"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.runtimeRequests === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "runtimeRequests"},message:"must have required property '"+"runtimeRequests"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(data.structuredResult === undefined){const err7 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "structuredResult"},message:"must have required property '"+"structuredResult"+"'"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(data.typedEvents === undefined){const err8 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "typedEvents"},message:"must have required property '"+"typedEvents"+"'"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(data.sessionGoals === undefined){const err9 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sessionGoals"},message:"must have required property '"+"sessionGoals"+"'"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(data.schema !== undefined){if("paperclip.prp.capabilities.v2" !== data.schema){const err10 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.capabilities.v2"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.sessionReusePolicy !== undefined){let data1 = data.sessionReusePolicy;if(!(((data1 === "new_per_run") || (data1 === "reuse_per_issue")) || (data1 === "reuse_per_workspace"))){const err11 = {instancePath:instancePath+"/sessionReusePolicy",schemaPath:"#/properties/sessionReusePolicy/enum",keyword:"enum",params:{allowedValues: schema42.properties.sessionReusePolicy.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.driver !== undefined){let data2 = data.driver;if(data2 && typeof data2 == "object" && !Array.isArray(data2)){if(data2.kind === undefined){const err12 = {instancePath:instancePath+"/driver",schemaPath:"#/properties/driver/required",keyword:"required",params:{missingProperty: "kind"},message:"must have required property '"+"kind"+"'"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(data2.version === undefined){const err13 = {instancePath:instancePath+"/driver",schemaPath:"#/properties/driver/required",keyword:"required",params:{missingProperty: "version"},message:"must have required property '"+"version"+"'"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(data2.kind !== undefined){let data3 = data2.kind;if(typeof data3 === "string"){if(func1(data3) > 80){const err14 = {instancePath:instancePath+"/driver/kind",schemaPath:"#/properties/driver/properties/kind/maxLength",keyword:"maxLength",params:{limit: 80},message:"must NOT have more than 80 characters"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(func1(data3) < 1){const err15 = {instancePath:instancePath+"/driver/kind",schemaPath:"#/properties/driver/properties/kind/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}else {const err16 = {instancePath:instancePath+"/driver/kind",schemaPath:"#/properties/driver/properties/kind/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}if(data2.version !== undefined){let data4 = data2.version;if(typeof data4 === "string"){if(func1(data4) > 80){const err17 = {instancePath:instancePath+"/driver/version",schemaPath:"#/properties/driver/properties/version/maxLength",keyword:"maxLength",params:{limit: 80},message:"must NOT have more than 80 characters"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}if(func1(data4) < 1){const err18 = {instancePath:instancePath+"/driver/version",schemaPath:"#/properties/driver/properties/version/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}else {const err19 = {instancePath:instancePath+"/driver/version",schemaPath:"#/properties/driver/properties/version/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}}else {const err20 = {instancePath:instancePath+"/driver",schemaPath:"#/properties/driver/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}if(data.steer !== undefined){if(typeof data.steer !== "boolean"){const err21 = {instancePath:instancePath+"/steer",schemaPath:"#/properties/steer/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}}if(data.interrupt !== undefined){if(typeof data.interrupt !== "boolean"){const err22 = {instancePath:instancePath+"/interrupt",schemaPath:"#/properties/interrupt/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}if(data.resume !== undefined){if(typeof data.resume !== "boolean"){const err23 = {instancePath:instancePath+"/resume",schemaPath:"#/properties/resume/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}if(data.runtimeRequests !== undefined){if(typeof data.runtimeRequests !== "boolean"){const err24 = {instancePath:instancePath+"/runtimeRequests",schemaPath:"#/properties/runtimeRequests/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}if(data.structuredResult !== undefined){if(typeof data.structuredResult !== "boolean"){const err25 = {instancePath:instancePath+"/structuredResult",schemaPath:"#/properties/structuredResult/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}if(data.typedEvents !== undefined){if(typeof data.typedEvents !== "boolean"){const err26 = {instancePath:instancePath+"/typedEvents",schemaPath:"#/properties/typedEvents/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}}if(data.sessionGoals !== undefined){let data11 = data.sessionGoals;if(data11 && typeof data11 == "object" && !Array.isArray(data11)){if(data11.availability === undefined){const err27 = {instancePath:instancePath+"/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "availability"},message:"must have required property '"+"availability"+"'"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if(data11.actions === undefined){const err28 = {instancePath:instancePath+"/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "actions"},message:"must have required property '"+"actions"+"'"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}if(data11.autonomousUpdates === undefined){const err29 = {instancePath:instancePath+"/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "autonomousUpdates"},message:"must have required property '"+"autonomousUpdates"+"'"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}if(data11.persistentAcrossResume === undefined){const err30 = {instancePath:instancePath+"/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "persistentAcrossResume"},message:"must have required property '"+"persistentAcrossResume"+"'"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}if(data11.maxObjectiveChars === undefined){const err31 = {instancePath:instancePath+"/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "maxObjectiveChars"},message:"must have required property '"+"maxObjectiveChars"+"'"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}if(data11.tokenBudgetControl === undefined){const err32 = {instancePath:instancePath+"/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "tokenBudgetControl"},message:"must have required property '"+"tokenBudgetControl"+"'"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}if(data11.usageReporting === undefined){const err33 = {instancePath:instancePath+"/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "usageReporting"},message:"must have required property '"+"usageReporting"+"'"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}if(data11.availability !== undefined){let data12 = data11.availability;if(!(((data12 === "available") || (data12 === "unsupported")) || (data12 === "policy_disabled"))){const err34 = {instancePath:instancePath+"/sessionGoals/availability",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/availability/enum",keyword:"enum",params:{allowedValues: schema44.properties.availability.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}}if(data11.actions !== undefined){let data13 = data11.actions;if(Array.isArray(data13)){const len0 = data13.length;for(let i0=0; i0 1){outer0:for(;i1--;){for(j0 = i1; j0--;){if(func0(data13[i1], data13[j0])){const err36 = {instancePath:instancePath+"/sessionGoals/actions",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/actions/uniqueItems",keyword:"uniqueItems",params:{i: i1, j: j0},message:"must NOT have duplicate items (items ## "+j0+" and "+i1+" are identical)"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;break outer0;}}}}}else {const err37 = {instancePath:instancePath+"/sessionGoals/actions",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/actions/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}}if(data11.autonomousUpdates !== undefined){if(typeof data11.autonomousUpdates !== "boolean"){const err38 = {instancePath:instancePath+"/sessionGoals/autonomousUpdates",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/autonomousUpdates/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}}if(data11.persistentAcrossResume !== undefined){if(typeof data11.persistentAcrossResume !== "boolean"){const err39 = {instancePath:instancePath+"/sessionGoals/persistentAcrossResume",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/persistentAcrossResume/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}}if(data11.maxObjectiveChars !== undefined){let data17 = data11.maxObjectiveChars;if(!(((typeof data17 == "number") && (!(data17 % 1) && !isNaN(data17))) && (isFinite(data17)))){const err40 = {instancePath:instancePath+"/sessionGoals/maxObjectiveChars",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/maxObjectiveChars/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}if((typeof data17 == "number") && (isFinite(data17))){if(data17 > 4000 || isNaN(data17)){const err41 = {instancePath:instancePath+"/sessionGoals/maxObjectiveChars",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/maxObjectiveChars/maximum",keyword:"maximum",params:{comparison: "<=", limit: 4000},message:"must be <= 4000"};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}if(data17 < 1 || isNaN(data17)){const err42 = {instancePath:instancePath+"/sessionGoals/maxObjectiveChars",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/maxObjectiveChars/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}}}if(data11.tokenBudgetControl !== undefined){if(typeof data11.tokenBudgetControl !== "boolean"){const err43 = {instancePath:instancePath+"/sessionGoals/tokenBudgetControl",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/tokenBudgetControl/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}}if(data11.usageReporting !== undefined){if(typeof data11.usageReporting !== "boolean"){const err44 = {instancePath:instancePath+"/sessionGoals/usageReporting",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/usageReporting/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err44];}else {vErrors.push(err44);}errors++;}}if(data11.reasonCode !== undefined){let data20 = data11.reasonCode;if(typeof data20 === "string"){if(func1(data20) > 160){const err45 = {instancePath:instancePath+"/sessionGoals/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reasonCode/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err45];}else {vErrors.push(err45);}errors++;}if(func1(data20) < 1){const err46 = {instancePath:instancePath+"/sessionGoals/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reasonCode/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err46];}else {vErrors.push(err46);}errors++;}}else {const err47 = {instancePath:instancePath+"/sessionGoals/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reasonCode/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err47];}else {vErrors.push(err47);}errors++;}}if(data11.reason !== undefined){let data21 = data11.reason;if(typeof data21 === "string"){if(func1(data21) > 1000){const err48 = {instancePath:instancePath+"/sessionGoals/reason",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reason/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err48];}else {vErrors.push(err48);}errors++;}if(func1(data21) < 1){const err49 = {instancePath:instancePath+"/sessionGoals/reason",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reason/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err49];}else {vErrors.push(err49);}errors++;}}else {const err50 = {instancePath:instancePath+"/sessionGoals/reason",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reason/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err50];}else {vErrors.push(err50);}errors++;}}}else {const err51 = {instancePath:instancePath+"/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err51];}else {vErrors.push(err51);}errors++;}}if(data.unsupported !== undefined){let data22 = data.unsupported;if(Array.isArray(data22)){const len1 = data22.length;for(let i2=0; i2 1){const indices0 = {};for(;i3--;){let item0 = data22[i3];if(typeof item0 !== "string"){continue;}if(typeof indices0[item0] == "number"){j1 = indices0[item0];const err54 = {instancePath:instancePath+"/unsupported",schemaPath:"#/properties/unsupported/uniqueItems",keyword:"uniqueItems",params:{i: i3, j: j1},message:"must NOT have duplicate items (items ## "+j1+" and "+i3+" are identical)"};if(vErrors === null){vErrors = [err54];}else {vErrors.push(err54);}errors++;break;}indices0[item0] = i3;}}}else {const err55 = {instancePath:instancePath+"/unsupported",schemaPath:"#/properties/unsupported/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err55];}else {vErrors.push(err55);}errors++;}}}else {const err56 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err56];}else {vErrors.push(err56);}errors++;}validate23.errors = vErrors;return errors === 0;}validate23.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema47 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/event.schema.json","title":"PRP native event","type":"object","required":["schema","sourceEventId","sourceSeq","sourceInstanceId","sourceKind","runId","eventType","schemaVersion","priority","emittedAt","payload"],"properties":{"schema":{"const":"paperclip.prp.event.v1"},"sourceEventId":{"type":"string","minLength":1,"maxLength":160},"sourceEpoch":{"type":"string","pattern":"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},"sourceSeq":{"type":"integer","minimum":1,"maximum":9007199254740991},"sourceInstanceId":{"type":"string","minLength":1,"maxLength":160},"sourceKind":{"enum":["runner","control_plane"]},"runId":{"type":"string","minLength":1,"maxLength":160},"normalizedSessionId":{"type":"string","minLength":1,"maxLength":160},"turnId":{"type":"string","minLength":1,"maxLength":240},"itemId":{"type":"string","minLength":1,"maxLength":240},"eventType":{"enum":["runner.connected","runner.reconnected","runner.reconciled","runner.disconnected","runner.draining","runner.backpressure","runner.suspending","runner.suspended","runner.stopped","runner.diagnostic","runtime.phase.changed","sandbox.metric","workspace.ready","workspace.change.updated","workspace.diff.recorded","workspace.file.referenced","harness.starting","harness.ready","harness.exited","harness.diagnostic","plan.updated","tool.execution.started","tool.execution.progressed","tool.execution.completed","research.started","research.progressed","research.completed","delegation.started","delegation.updated","delegation.completed","model.route.changed","model.verification.updated","context.compacted","artifact.viewed","artifact.generated","review.mode.changed","hook.started","hook.completed","memory.citation.referenced","safety.review.started","safety.review.completed","terminal.input.sent","wait.started","wait.completed","provider.notice.recorded","session.starting","session.started","session.resuming","session.resumed","session.reconciled","session.updated","session.closed","session.failed","turn.submitted","turn.accepted","turn.started","turn.completed","turn.failed","turn.interrupted","turn.cancelled","output.body.chunk","item.started","item.delta","item.completed","item.failed","usage.reported","semantic_tool.input","semantic_tool.result","semantic_tool.reconciled","mcp_app.discovered","mcp_app.resource.resolved","mcp_app.initializing","mcp_app.ready","mcp_app.tool_input","mcp_app.tool_result","mcp_app.action.requested","mcp_app.action.resolved","mcp_app.host_context.changed","mcp_app.failed","mcp_app.teardown","runtime_request.created","runtime_request.resolved","runtime_request.expired","runtime_request.cancelled","interaction.request.proposed","interaction.request.materialized","interaction.request.rejected","interaction.response.progressed","interaction.response.resolved","interaction.response.delivered","run.attached","run.detached","run.result.proposed","run.result.accepted","run.result.rejected","attention.request.proposed","attention.request.routed","attention.request.resolved","attention.request.expired","attention.request.superseded","work.assessment.recorded","issue.status.decision.recorded","issue.status.decision.applied","issue.status.decision.rejected","issue.status.decision.superseded","run.terminal"]},"schemaVersion":{"const":1},"priority":{"enum":[0,1,2]},"emittedAt":{"type":"string","format":"date-time"},"observedAt":{"type":"string","format":"date-time"},"payload":{"type":"object","properties":{"channel":{"enum":["progress","final","summary","detail","unknown"]},"providerPhase":{"type":"string"},"providerMethod":{"type":"string"}},"additionalProperties":true},"debug":{"type":"object","additionalProperties":true},"sourceEpochTransition":{"type":"object","additionalProperties":false,"required":["schema","runId","transitionId","fromEpoch","nextEpoch","finalOrdinal"],"properties":{"schema":{"const":"paperclip.prp.event-epoch.v1"},"runId":{"type":"string","minLength":1,"maxLength":240},"transitionId":{"type":"string","pattern":"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},"fromEpoch":{"anyOf":[{"type":"string","pattern":"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},{"type":"null"}]},"nextEpoch":{"type":"string","pattern":"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},"finalOrdinal":{"type":"integer","minimum":1,"maximum":9007199254740991}}}},"allOf":[{"if":{"properties":{"eventType":{"const":"plan.updated"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/plan"}}}},{"if":{"properties":{"eventType":{"enum":["tool.execution.started","tool.execution.progressed","tool.execution.completed"]}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/toolExecution"}}}},{"if":{"properties":{"eventType":{"enum":["research.started","research.progressed","research.completed"]}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/research"}}}},{"if":{"properties":{"eventType":{"enum":["delegation.started","delegation.updated","delegation.completed"]}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/delegation"}}}},{"if":{"properties":{"eventType":{"const":"model.route.changed"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/modelRoute"}}}},{"if":{"properties":{"eventType":{"const":"model.verification.updated"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/modelVerification"}}}},{"if":{"properties":{"eventType":{"const":"context.compacted"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/contextCompacted"}}}},{"if":{"properties":{"eventType":{"const":"artifact.viewed"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/artifactViewed"}}}},{"if":{"properties":{"eventType":{"const":"artifact.generated"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/artifactGenerated"}}}},{"if":{"properties":{"eventType":{"const":"review.mode.changed"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/reviewMode"}}}},{"if":{"properties":{"eventType":{"enum":["hook.started","hook.completed"]}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/hook"}}}},{"if":{"properties":{"eventType":{"const":"memory.citation.referenced"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/memoryCitation"}}}},{"if":{"properties":{"eventType":{"enum":["safety.review.started","safety.review.completed"]}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/safetyReview"}}}},{"if":{"properties":{"eventType":{"const":"terminal.input.sent"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/terminalInput"}}}},{"if":{"properties":{"eventType":{"enum":["wait.started","wait.completed"]}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/wait"}}}},{"if":{"properties":{"eventType":{"const":"provider.notice.recorded"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/providerNotice"}}}},{"if":{"properties":{"eventType":{"const":"workspace.file.referenced"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json"}}}},{"if":{"properties":{"eventType":{"enum":["workspace.change.updated","workspace.diff.recorded"]}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json"}}}},{"if":{"properties":{"eventType":{"const":"workspace.diff.recorded"}}},"then":{"properties":{"payload":{"allOf":[{"$ref":"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json"},{"type":"object","properties":{"complete":{"const":true}}}]}}}},{"if":{"properties":{"eventType":{"enum":["semantic_tool.input","mcp_app.tool_input"]}}},"then":{"properties":{"payload":{"type":"object","properties":{"semantic_tool":{"allOf":[{"$ref":"https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json"},{"type":"object","properties":{"phase":{"const":"input"}}}]}},"additionalProperties":true}}}},{"if":{"properties":{"eventType":{"enum":["semantic_tool.result","mcp_app.tool_result"]}}},"then":{"properties":{"payload":{"type":"object","properties":{"semantic_tool":{"allOf":[{"$ref":"https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json"},{"type":"object","properties":{"phase":{"const":"result"}}}]}},"additionalProperties":true}}}},{"if":{"properties":{"eventType":{"const":"semantic_tool.reconciled"}}},"then":{"properties":{"payload":{"type":"object","properties":{"semantic_tool":{"allOf":[{"$ref":"https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json"},{"type":"object","properties":{"phase":{"const":"reconciled"}}}]}},"additionalProperties":true}}}},{"if":{"properties":{"eventType":{"const":"run.terminal"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/terminal.schema.json"}}}},{"if":{"properties":{"eventType":{"const":"run.result.proposed"}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/result.schema.json"}}}},{"if":{"properties":{"eventType":{"const":"output.body.chunk"}}},"then":{"properties":{"payload":{"type":"object","required":["schema","body","offset","sha256","text"],"properties":{"schema":{"const":"paperclip.output.body.chunk.v1"},"body":{"type":"object","required":["schema","bodyId","sha256","byteLength","mediaType"],"properties":{"schema":{"const":"paperclip.output.body.v1"},"bodyId":{"type":"string","pattern":"^[a-f0-9]{64}$"},"sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"},"byteLength":{"type":"string","pattern":"^[1-9][0-9]{0,6}$"},"mediaType":{"const":"text/plain; charset=utf-8"}},"additionalProperties":false},"offset":{"type":"string","pattern":"^(0|[1-9][0-9]{0,6})$"},"sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"},"text":{"type":"string","minLength":1,"maxLength":32766}},"additionalProperties":false}}}}],"additionalProperties":true};const schema173 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json","title":"Paperclip workspace file reference","type":"object","required":["schema","referenceId","source","path","displayName","mediaType","presentation","line","preview","previewTruncated","contentDigest"],"properties":{"schema":{"const":"paperclip.workspace.file_reference.v1"},"referenceId":{"type":"string","minLength":1,"maxLength":240},"source":{"enum":["harness_reported","runner_verified"]},"path":{"type":"string","minLength":1,"maxLength":1024,"pattern":"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"},"displayName":{"type":"string","minLength":1,"maxLength":255},"mediaType":{"type":["string","null"],"maxLength":160},"presentation":{"enum":["document","code","image","generic"]},"line":{"type":["integer","null"],"minimum":1},"preview":{"type":["string","null"],"maxLength":262144},"previewTruncated":{"type":"boolean"},"contentDigest":{"oneOf":[{"type":"null"},{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"}]}},"additionalProperties":false};const schema174 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json","title":"Paperclip workspace diff","type":"object","required":["schema","changeSetId","revision","source","complete","files","totals","patchArtifactRef"],"properties":{"schema":{"const":"paperclip.workspace.diff.v1"},"changeSetId":{"type":"string","minLength":1,"maxLength":200},"revision":{"type":"integer","minimum":1},"source":{"enum":["harness_reported","runner_verified"]},"complete":{"type":"boolean"},"files":{"type":"array","maxItems":2000,"items":{"type":"object","required":["path","operation","previousPath","additions","deletions","binary","diff"],"properties":{"path":{"type":"string","minLength":1,"maxLength":1024,"pattern":"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"},"operation":{"enum":["create","modify","delete","rename","mode_change"]},"previousPath":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":1024,"pattern":"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"}]},"additions":{"type":["integer","null"],"minimum":0},"deletions":{"type":["integer","null"],"minimum":0},"binary":{"type":"boolean"},"diff":{"type":["string","null"],"maxLength":262144}},"additionalProperties":false}},"totals":{"type":"object","required":["files","additions","deletions"],"properties":{"files":{"type":"integer","minimum":0},"additions":{"type":["integer","null"],"minimum":0},"deletions":{"type":["integer","null"],"minimum":0}},"additionalProperties":false},"patchArtifactRef":{"type":["string","null"],"maxLength":2048}},"additionalProperties":false};const schema49 = {"type":"object","description":"A complete provider-authored within-turn checklist snapshot. Consumers replace the prior snapshot with the same planId in PRP sourceSeq order; this is not Paperclip's durable Plan document.","required":["schema","planId","revision","steps","complete","syncStatus"],"properties":{"schema":{"const":"paperclip.plan.updated.v1"},"planId":{"$ref":"#/$defs/id"},"revision":{"type":"integer","minimum":1},"explanation":{"$ref":"#/$defs/nullableShortText"},"steps":{"type":"array","maxItems":256,"items":{"type":"object","required":["stepId","body","status"],"properties":{"stepId":{"$ref":"#/$defs/id"},"body":{"$ref":"#/$defs/shortText"},"status":{"enum":["pending","in_progress","completed","blocked"]}},"additionalProperties":false}},"complete":{"type":"boolean"},"syncStatus":{"description":"Legacy compatibility field. Within-turn checklist snapshots use not_applicable.","enum":["streaming","pending","synchronized","conflict","not_applicable"]},"documentRevision":{"description":"Legacy compatibility field. Within-turn checklist snapshots use null.","type":["integer","null"],"minimum":1}},"additionalProperties":false};const schema50 = {"type":"string","minLength":1,"maxLength":160,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]*$"};const schema51 = {"type":["string","null"],"maxLength":4000};const schema53 = {"type":"string","maxLength":4000};function validate60(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate60.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.planId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "planId"},message:"must have required property '"+"planId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.revision === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "revision"},message:"must have required property '"+"revision"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.steps === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "steps"},message:"must have required property '"+"steps"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.complete === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "complete"},message:"must have required property '"+"complete"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.syncStatus === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "syncStatus"},message:"must have required property '"+"syncStatus"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}for(const key0 in data){if(!((((((((key0 === "schema") || (key0 === "planId")) || (key0 === "revision")) || (key0 === "explanation")) || (key0 === "steps")) || (key0 === "complete")) || (key0 === "syncStatus")) || (key0 === "documentRevision"))){const err6 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.schema !== undefined){if("paperclip.plan.updated.v1" !== data.schema){const err7 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.plan.updated.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.planId !== undefined){let data1 = data.planId;if(typeof data1 === "string"){if(func1(data1) > 160){const err8 = {instancePath:instancePath+"/planId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(func1(data1) < 1){const err9 = {instancePath:instancePath+"/planId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(!pattern4.test(data1)){const err10 = {instancePath:instancePath+"/planId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}else {const err11 = {instancePath:instancePath+"/planId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.revision !== undefined){let data2 = data.revision;if(!(((typeof data2 == "number") && (!(data2 % 1) && !isNaN(data2))) && (isFinite(data2)))){const err12 = {instancePath:instancePath+"/revision",schemaPath:"#/properties/revision/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if((typeof data2 == "number") && (isFinite(data2))){if(data2 < 1 || isNaN(data2)){const err13 = {instancePath:instancePath+"/revision",schemaPath:"#/properties/revision/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}}if(data.explanation !== undefined){let data3 = data.explanation;if((typeof data3 !== "string") && (data3 !== null)){const err14 = {instancePath:instancePath+"/explanation",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(typeof data3 === "string"){if(func1(data3) > 4000){const err15 = {instancePath:instancePath+"/explanation",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}}if(data.steps !== undefined){let data4 = data.steps;if(Array.isArray(data4)){if(data4.length > 256){const err16 = {instancePath:instancePath+"/steps",schemaPath:"#/properties/steps/maxItems",keyword:"maxItems",params:{limit: 256},message:"must NOT have more than 256 items"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}const len0 = data4.length;for(let i0=0; i0 160){const err21 = {instancePath:instancePath+"/steps/" + i0+"/stepId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(func1(data6) < 1){const err22 = {instancePath:instancePath+"/steps/" + i0+"/stepId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}if(!pattern4.test(data6)){const err23 = {instancePath:instancePath+"/steps/" + i0+"/stepId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}else {const err24 = {instancePath:instancePath+"/steps/" + i0+"/stepId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}if(data5.body !== undefined){let data7 = data5.body;if(typeof data7 === "string"){if(func1(data7) > 4000){const err25 = {instancePath:instancePath+"/steps/" + i0+"/body",schemaPath:"#/$defs/shortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}else {const err26 = {instancePath:instancePath+"/steps/" + i0+"/body",schemaPath:"#/$defs/shortText/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}}if(data5.status !== undefined){let data8 = data5.status;if(!((((data8 === "pending") || (data8 === "in_progress")) || (data8 === "completed")) || (data8 === "blocked"))){const err27 = {instancePath:instancePath+"/steps/" + i0+"/status",schemaPath:"#/properties/steps/items/properties/status/enum",keyword:"enum",params:{allowedValues: schema49.properties.steps.items.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}}}else {const err28 = {instancePath:instancePath+"/steps/" + i0,schemaPath:"#/properties/steps/items/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}}else {const err29 = {instancePath:instancePath+"/steps",schemaPath:"#/properties/steps/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}}if(data.complete !== undefined){if(typeof data.complete !== "boolean"){const err30 = {instancePath:instancePath+"/complete",schemaPath:"#/properties/complete/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}}if(data.syncStatus !== undefined){let data10 = data.syncStatus;if(!(((((data10 === "streaming") || (data10 === "pending")) || (data10 === "synchronized")) || (data10 === "conflict")) || (data10 === "not_applicable"))){const err31 = {instancePath:instancePath+"/syncStatus",schemaPath:"#/properties/syncStatus/enum",keyword:"enum",params:{allowedValues: schema49.properties.syncStatus.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}}if(data.documentRevision !== undefined){let data11 = data.documentRevision;if((!(((typeof data11 == "number") && (!(data11 % 1) && !isNaN(data11))) && (isFinite(data11)))) && (data11 !== null)){const err32 = {instancePath:instancePath+"/documentRevision",schemaPath:"#/properties/documentRevision/type",keyword:"type",params:{type: schema49.properties.documentRevision.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}if((typeof data11 == "number") && (isFinite(data11))){if(data11 < 1 || isNaN(data11)){const err33 = {instancePath:instancePath+"/documentRevision",schemaPath:"#/properties/documentRevision/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}}}}else {const err34 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}validate60.errors = vErrors;return errors === 0;}validate60.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema54 = {"type":"object","required":["schema","executionId","transport","operation","status","output","outputBytes","outputTruncated","outputDigest"],"properties":{"schema":{"const":"paperclip.tool.execution.v1"},"executionId":{"$ref":"#/$defs/id"},"transport":{"enum":["process","mcp","dynamic","builtin"]},"operation":{"enum":["read","search","list","execute","edit","unknown"]},"name":{"$ref":"#/$defs/nullableShortText"},"target":{"$ref":"#/$defs/safePath"},"namespace":{"type":["string","null"],"maxLength":240},"readOnly":{"type":["boolean","null"]},"inputUpdated":{"type":"boolean"},"status":{"enum":["running","completed","failed","cancelled","interrupted"]},"durationMs":{"type":["integer","null"],"minimum":0},"exitCode":{"type":["integer","null"]},"progress":{"$ref":"#/$defs/nullableShortText"},"output":{"type":["string","null"],"maxLength":65536},"outputBytes":{"type":"integer","minimum":0},"outputTruncated":{"type":"boolean"},"outputDigest":{"type":["string","null"],"pattern":"^sha256:[a-f0-9]{64}$"},"outputBody":{"$ref":"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json"}},"additionalProperties":false};const schema57 = {"type":["string","null"],"maxLength":4096,"pattern":"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$"};const schema59 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json","title":"Immutable output body reference","type":"object","required":["schema","bodyId","sha256","byteLength","mediaType"],"properties":{"schema":{"const":"paperclip.output.body.v1"},"bodyId":{"type":"string","pattern":"^[a-f0-9]{64}$"},"sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"},"byteLength":{"type":"string","pattern":"^[1-9][0-9]{0,6}$"},"mediaType":{"const":"text/plain; charset=utf-8"}},"additionalProperties":false};const func58 = Object.prototype.hasOwnProperty;const pattern20 = new RegExp("^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$", "u");const pattern21 = new RegExp("^sha256:[a-f0-9]{64}$", "u");const pattern22 = new RegExp("^[a-f0-9]{64}$", "u");const pattern24 = new RegExp("^[1-9][0-9]{0,6}$", "u");function validate62(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate62.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.executionId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "executionId"},message:"must have required property '"+"executionId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.transport === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "transport"},message:"must have required property '"+"transport"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.operation === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "operation"},message:"must have required property '"+"operation"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.status === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.output === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "output"},message:"must have required property '"+"output"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.outputBytes === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "outputBytes"},message:"must have required property '"+"outputBytes"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(data.outputTruncated === undefined){const err7 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "outputTruncated"},message:"must have required property '"+"outputTruncated"+"'"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(data.outputDigest === undefined){const err8 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "outputDigest"},message:"must have required property '"+"outputDigest"+"'"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}for(const key0 in data){if(!(func58.call(schema54.properties, key0))){const err9 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}if(data.schema !== undefined){if("paperclip.tool.execution.v1" !== data.schema){const err10 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.tool.execution.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.executionId !== undefined){let data1 = data.executionId;if(typeof data1 === "string"){if(func1(data1) > 160){const err11 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(func1(data1) < 1){const err12 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(!pattern4.test(data1)){const err13 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}else {const err14 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}if(data.transport !== undefined){let data2 = data.transport;if(!((((data2 === "process") || (data2 === "mcp")) || (data2 === "dynamic")) || (data2 === "builtin"))){const err15 = {instancePath:instancePath+"/transport",schemaPath:"#/properties/transport/enum",keyword:"enum",params:{allowedValues: schema54.properties.transport.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}if(data.operation !== undefined){let data3 = data.operation;if(!((((((data3 === "read") || (data3 === "search")) || (data3 === "list")) || (data3 === "execute")) || (data3 === "edit")) || (data3 === "unknown"))){const err16 = {instancePath:instancePath+"/operation",schemaPath:"#/properties/operation/enum",keyword:"enum",params:{allowedValues: schema54.properties.operation.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}if(data.name !== undefined){let data4 = data.name;if((typeof data4 !== "string") && (data4 !== null)){const err17 = {instancePath:instancePath+"/name",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}if(typeof data4 === "string"){if(func1(data4) > 4000){const err18 = {instancePath:instancePath+"/name",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}}if(data.target !== undefined){let data5 = data.target;if((typeof data5 !== "string") && (data5 !== null)){const err19 = {instancePath:instancePath+"/target",schemaPath:"#/$defs/safePath/type",keyword:"type",params:{type: schema57.type},message:"must be string,null"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}if(typeof data5 === "string"){if(func1(data5) > 4096){const err20 = {instancePath:instancePath+"/target",schemaPath:"#/$defs/safePath/maxLength",keyword:"maxLength",params:{limit: 4096},message:"must NOT have more than 4096 characters"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}if(!pattern20.test(data5)){const err21 = {instancePath:instancePath+"/target",schemaPath:"#/$defs/safePath/pattern",keyword:"pattern",params:{pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$"},message:"must match pattern \""+"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$"+"\""};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}}}if(data.namespace !== undefined){let data6 = data.namespace;if((typeof data6 !== "string") && (data6 !== null)){const err22 = {instancePath:instancePath+"/namespace",schemaPath:"#/properties/namespace/type",keyword:"type",params:{type: schema54.properties.namespace.type},message:"must be string,null"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}if(typeof data6 === "string"){if(func1(data6) > 240){const err23 = {instancePath:instancePath+"/namespace",schemaPath:"#/properties/namespace/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}}if(data.readOnly !== undefined){let data7 = data.readOnly;if((typeof data7 !== "boolean") && (data7 !== null)){const err24 = {instancePath:instancePath+"/readOnly",schemaPath:"#/properties/readOnly/type",keyword:"type",params:{type: schema54.properties.readOnly.type},message:"must be boolean,null"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}if(data.inputUpdated !== undefined){if(typeof data.inputUpdated !== "boolean"){const err25 = {instancePath:instancePath+"/inputUpdated",schemaPath:"#/properties/inputUpdated/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}if(data.status !== undefined){let data9 = data.status;if(!(((((data9 === "running") || (data9 === "completed")) || (data9 === "failed")) || (data9 === "cancelled")) || (data9 === "interrupted"))){const err26 = {instancePath:instancePath+"/status",schemaPath:"#/properties/status/enum",keyword:"enum",params:{allowedValues: schema54.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}}if(data.durationMs !== undefined){let data10 = data.durationMs;if((!(((typeof data10 == "number") && (!(data10 % 1) && !isNaN(data10))) && (isFinite(data10)))) && (data10 !== null)){const err27 = {instancePath:instancePath+"/durationMs",schemaPath:"#/properties/durationMs/type",keyword:"type",params:{type: schema54.properties.durationMs.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if((typeof data10 == "number") && (isFinite(data10))){if(data10 < 0 || isNaN(data10)){const err28 = {instancePath:instancePath+"/durationMs",schemaPath:"#/properties/durationMs/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}}if(data.exitCode !== undefined){let data11 = data.exitCode;if((!(((typeof data11 == "number") && (!(data11 % 1) && !isNaN(data11))) && (isFinite(data11)))) && (data11 !== null)){const err29 = {instancePath:instancePath+"/exitCode",schemaPath:"#/properties/exitCode/type",keyword:"type",params:{type: schema54.properties.exitCode.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}}if(data.progress !== undefined){let data12 = data.progress;if((typeof data12 !== "string") && (data12 !== null)){const err30 = {instancePath:instancePath+"/progress",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}if(typeof data12 === "string"){if(func1(data12) > 4000){const err31 = {instancePath:instancePath+"/progress",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}}}if(data.output !== undefined){let data13 = data.output;if((typeof data13 !== "string") && (data13 !== null)){const err32 = {instancePath:instancePath+"/output",schemaPath:"#/properties/output/type",keyword:"type",params:{type: schema54.properties.output.type},message:"must be string,null"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}if(typeof data13 === "string"){if(func1(data13) > 65536){const err33 = {instancePath:instancePath+"/output",schemaPath:"#/properties/output/maxLength",keyword:"maxLength",params:{limit: 65536},message:"must NOT have more than 65536 characters"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}}}if(data.outputBytes !== undefined){let data14 = data.outputBytes;if(!(((typeof data14 == "number") && (!(data14 % 1) && !isNaN(data14))) && (isFinite(data14)))){const err34 = {instancePath:instancePath+"/outputBytes",schemaPath:"#/properties/outputBytes/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}if((typeof data14 == "number") && (isFinite(data14))){if(data14 < 0 || isNaN(data14)){const err35 = {instancePath:instancePath+"/outputBytes",schemaPath:"#/properties/outputBytes/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}}}if(data.outputTruncated !== undefined){if(typeof data.outputTruncated !== "boolean"){const err36 = {instancePath:instancePath+"/outputTruncated",schemaPath:"#/properties/outputTruncated/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}}if(data.outputDigest !== undefined){let data16 = data.outputDigest;if((typeof data16 !== "string") && (data16 !== null)){const err37 = {instancePath:instancePath+"/outputDigest",schemaPath:"#/properties/outputDigest/type",keyword:"type",params:{type: schema54.properties.outputDigest.type},message:"must be string,null"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}if(typeof data16 === "string"){if(!pattern21.test(data16)){const err38 = {instancePath:instancePath+"/outputDigest",schemaPath:"#/properties/outputDigest/pattern",keyword:"pattern",params:{pattern: "^sha256:[a-f0-9]{64}$"},message:"must match pattern \""+"^sha256:[a-f0-9]{64}$"+"\""};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}}}if(data.outputBody !== undefined){let data17 = data.outputBody;if(data17 && typeof data17 == "object" && !Array.isArray(data17)){if(data17.schema === undefined){const err39 = {instancePath:instancePath+"/outputBody",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}if(data17.bodyId === undefined){const err40 = {instancePath:instancePath+"/outputBody",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/required",keyword:"required",params:{missingProperty: "bodyId"},message:"must have required property '"+"bodyId"+"'"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}if(data17.sha256 === undefined){const err41 = {instancePath:instancePath+"/outputBody",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/required",keyword:"required",params:{missingProperty: "sha256"},message:"must have required property '"+"sha256"+"'"};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}if(data17.byteLength === undefined){const err42 = {instancePath:instancePath+"/outputBody",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/required",keyword:"required",params:{missingProperty: "byteLength"},message:"must have required property '"+"byteLength"+"'"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}if(data17.mediaType === undefined){const err43 = {instancePath:instancePath+"/outputBody",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/required",keyword:"required",params:{missingProperty: "mediaType"},message:"must have required property '"+"mediaType"+"'"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}for(const key1 in data17){if(!(((((key1 === "schema") || (key1 === "bodyId")) || (key1 === "sha256")) || (key1 === "byteLength")) || (key1 === "mediaType"))){const err44 = {instancePath:instancePath+"/outputBody",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key1},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err44];}else {vErrors.push(err44);}errors++;}}if(data17.schema !== undefined){if("paperclip.output.body.v1" !== data17.schema){const err45 = {instancePath:instancePath+"/outputBody/schema",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.output.body.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err45];}else {vErrors.push(err45);}errors++;}}if(data17.bodyId !== undefined){let data19 = data17.bodyId;if(typeof data19 === "string"){if(!pattern22.test(data19)){const err46 = {instancePath:instancePath+"/outputBody/bodyId",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/properties/bodyId/pattern",keyword:"pattern",params:{pattern: "^[a-f0-9]{64}$"},message:"must match pattern \""+"^[a-f0-9]{64}$"+"\""};if(vErrors === null){vErrors = [err46];}else {vErrors.push(err46);}errors++;}}else {const err47 = {instancePath:instancePath+"/outputBody/bodyId",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/properties/bodyId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err47];}else {vErrors.push(err47);}errors++;}}if(data17.sha256 !== undefined){let data20 = data17.sha256;if(typeof data20 === "string"){if(!pattern22.test(data20)){const err48 = {instancePath:instancePath+"/outputBody/sha256",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/properties/sha256/pattern",keyword:"pattern",params:{pattern: "^[a-f0-9]{64}$"},message:"must match pattern \""+"^[a-f0-9]{64}$"+"\""};if(vErrors === null){vErrors = [err48];}else {vErrors.push(err48);}errors++;}}else {const err49 = {instancePath:instancePath+"/outputBody/sha256",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/properties/sha256/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err49];}else {vErrors.push(err49);}errors++;}}if(data17.byteLength !== undefined){let data21 = data17.byteLength;if(typeof data21 === "string"){if(!pattern24.test(data21)){const err50 = {instancePath:instancePath+"/outputBody/byteLength",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/properties/byteLength/pattern",keyword:"pattern",params:{pattern: "^[1-9][0-9]{0,6}$"},message:"must match pattern \""+"^[1-9][0-9]{0,6}$"+"\""};if(vErrors === null){vErrors = [err50];}else {vErrors.push(err50);}errors++;}}else {const err51 = {instancePath:instancePath+"/outputBody/byteLength",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/properties/byteLength/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err51];}else {vErrors.push(err51);}errors++;}}if(data17.mediaType !== undefined){if("text/plain; charset=utf-8" !== data17.mediaType){const err52 = {instancePath:instancePath+"/outputBody/mediaType",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/properties/mediaType/const",keyword:"const",params:{allowedValue: "text/plain; charset=utf-8"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err52];}else {vErrors.push(err52);}errors++;}}}else {const err53 = {instancePath:instancePath+"/outputBody",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err53];}else {vErrors.push(err53);}errors++;}}}else {const err54 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err54];}else {vErrors.push(err54);}errors++;}validate62.errors = vErrors;return errors === 0;}validate62.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema60 = {"type":"object","required":["schema","researchId","action","status","sources"],"properties":{"schema":{"const":"paperclip.research.v1"},"researchId":{"$ref":"#/$defs/id"},"action":{"enum":["search","open_page","find_in_page","other"]},"status":{"enum":["running","completed","failed","cancelled"]},"query":{"$ref":"#/$defs/nullableShortText"},"url":{"$ref":"#/$defs/safeUrl"},"pattern":{"$ref":"#/$defs/nullableShortText"},"sources":{"type":"array","maxItems":64,"items":{"type":"object","required":["sourceId","title","url","snippet"],"properties":{"sourceId":{"$ref":"#/$defs/id"},"title":{"$ref":"#/$defs/shortText"},"url":{"$ref":"#/$defs/safeUrl"},"snippet":{"$ref":"#/$defs/nullableShortText"}},"additionalProperties":false}}},"additionalProperties":false};const schema63 = {"type":["string","null"],"maxLength":8192,"pattern":"^https?://"};const pattern26 = new RegExp("^https?://", "u");function validate64(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate64.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.researchId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "researchId"},message:"must have required property '"+"researchId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.action === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "action"},message:"must have required property '"+"action"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.status === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.sources === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sources"},message:"must have required property '"+"sources"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}for(const key0 in data){if(!((((((((key0 === "schema") || (key0 === "researchId")) || (key0 === "action")) || (key0 === "status")) || (key0 === "query")) || (key0 === "url")) || (key0 === "pattern")) || (key0 === "sources"))){const err5 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}if(data.schema !== undefined){if("paperclip.research.v1" !== data.schema){const err6 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.research.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.researchId !== undefined){let data1 = data.researchId;if(typeof data1 === "string"){if(func1(data1) > 160){const err7 = {instancePath:instancePath+"/researchId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(func1(data1) < 1){const err8 = {instancePath:instancePath+"/researchId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(!pattern4.test(data1)){const err9 = {instancePath:instancePath+"/researchId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}else {const err10 = {instancePath:instancePath+"/researchId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.action !== undefined){let data2 = data.action;if(!((((data2 === "search") || (data2 === "open_page")) || (data2 === "find_in_page")) || (data2 === "other"))){const err11 = {instancePath:instancePath+"/action",schemaPath:"#/properties/action/enum",keyword:"enum",params:{allowedValues: schema60.properties.action.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.status !== undefined){let data3 = data.status;if(!((((data3 === "running") || (data3 === "completed")) || (data3 === "failed")) || (data3 === "cancelled"))){const err12 = {instancePath:instancePath+"/status",schemaPath:"#/properties/status/enum",keyword:"enum",params:{allowedValues: schema60.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.query !== undefined){let data4 = data.query;if((typeof data4 !== "string") && (data4 !== null)){const err13 = {instancePath:instancePath+"/query",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(typeof data4 === "string"){if(func1(data4) > 4000){const err14 = {instancePath:instancePath+"/query",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}}if(data.url !== undefined){let data5 = data.url;if((typeof data5 !== "string") && (data5 !== null)){const err15 = {instancePath:instancePath+"/url",schemaPath:"#/$defs/safeUrl/type",keyword:"type",params:{type: schema63.type},message:"must be string,null"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}if(typeof data5 === "string"){if(func1(data5) > 8192){const err16 = {instancePath:instancePath+"/url",schemaPath:"#/$defs/safeUrl/maxLength",keyword:"maxLength",params:{limit: 8192},message:"must NOT have more than 8192 characters"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}if(!pattern26.test(data5)){const err17 = {instancePath:instancePath+"/url",schemaPath:"#/$defs/safeUrl/pattern",keyword:"pattern",params:{pattern: "^https?://"},message:"must match pattern \""+"^https?://"+"\""};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}}if(data.pattern !== undefined){let data6 = data.pattern;if((typeof data6 !== "string") && (data6 !== null)){const err18 = {instancePath:instancePath+"/pattern",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}if(typeof data6 === "string"){if(func1(data6) > 4000){const err19 = {instancePath:instancePath+"/pattern",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}}if(data.sources !== undefined){let data7 = data.sources;if(Array.isArray(data7)){if(data7.length > 64){const err20 = {instancePath:instancePath+"/sources",schemaPath:"#/properties/sources/maxItems",keyword:"maxItems",params:{limit: 64},message:"must NOT have more than 64 items"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}const len0 = data7.length;for(let i0=0; i0 160){const err26 = {instancePath:instancePath+"/sources/" + i0+"/sourceId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}if(func1(data9) < 1){const err27 = {instancePath:instancePath+"/sources/" + i0+"/sourceId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if(!pattern4.test(data9)){const err28 = {instancePath:instancePath+"/sources/" + i0+"/sourceId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}else {const err29 = {instancePath:instancePath+"/sources/" + i0+"/sourceId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}}if(data8.title !== undefined){let data10 = data8.title;if(typeof data10 === "string"){if(func1(data10) > 4000){const err30 = {instancePath:instancePath+"/sources/" + i0+"/title",schemaPath:"#/$defs/shortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}}else {const err31 = {instancePath:instancePath+"/sources/" + i0+"/title",schemaPath:"#/$defs/shortText/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}}if(data8.url !== undefined){let data11 = data8.url;if((typeof data11 !== "string") && (data11 !== null)){const err32 = {instancePath:instancePath+"/sources/" + i0+"/url",schemaPath:"#/$defs/safeUrl/type",keyword:"type",params:{type: schema63.type},message:"must be string,null"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}if(typeof data11 === "string"){if(func1(data11) > 8192){const err33 = {instancePath:instancePath+"/sources/" + i0+"/url",schemaPath:"#/$defs/safeUrl/maxLength",keyword:"maxLength",params:{limit: 8192},message:"must NOT have more than 8192 characters"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}if(!pattern26.test(data11)){const err34 = {instancePath:instancePath+"/sources/" + i0+"/url",schemaPath:"#/$defs/safeUrl/pattern",keyword:"pattern",params:{pattern: "^https?://"},message:"must match pattern \""+"^https?://"+"\""};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}}}if(data8.snippet !== undefined){let data12 = data8.snippet;if((typeof data12 !== "string") && (data12 !== null)){const err35 = {instancePath:instancePath+"/sources/" + i0+"/snippet",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}if(typeof data12 === "string"){if(func1(data12) > 4000){const err36 = {instancePath:instancePath+"/sources/" + i0+"/snippet",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}}}}else {const err37 = {instancePath:instancePath+"/sources/" + i0,schemaPath:"#/properties/sources/items/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}}}else {const err38 = {instancePath:instancePath+"/sources",schemaPath:"#/properties/sources/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}}}else {const err39 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}validate64.errors = vErrors;return errors === 0;}validate64.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema69 = {"type":"object","required":["schema","delegationId","action","status","children"],"properties":{"schema":{"const":"paperclip.delegation.v1"},"delegationId":{"$ref":"#/$defs/id"},"action":{"enum":["spawn","message","resume","wait","close"]},"status":{"enum":["running","waiting","completed","failed","interrupted","closed"]},"children":{"type":"array","maxItems":64,"items":{"type":"object","required":["childId","role","model","status","summary","activitySummary"],"properties":{"childId":{"$ref":"#/$defs/id"},"role":{"type":["string","null"],"maxLength":160},"model":{"type":["string","null"],"maxLength":240},"status":{"enum":["running","waiting","completed","failed","interrupted","closed"]},"summary":{"$ref":"#/$defs/nullableShortText"},"activitySummary":{"$ref":"#/$defs/nullableShortText"}},"additionalProperties":false}}},"additionalProperties":false};function validate66(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate66.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.delegationId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "delegationId"},message:"must have required property '"+"delegationId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.action === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "action"},message:"must have required property '"+"action"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.status === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.children === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "children"},message:"must have required property '"+"children"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}for(const key0 in data){if(!(((((key0 === "schema") || (key0 === "delegationId")) || (key0 === "action")) || (key0 === "status")) || (key0 === "children"))){const err5 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}if(data.schema !== undefined){if("paperclip.delegation.v1" !== data.schema){const err6 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.delegation.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.delegationId !== undefined){let data1 = data.delegationId;if(typeof data1 === "string"){if(func1(data1) > 160){const err7 = {instancePath:instancePath+"/delegationId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(func1(data1) < 1){const err8 = {instancePath:instancePath+"/delegationId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(!pattern4.test(data1)){const err9 = {instancePath:instancePath+"/delegationId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}else {const err10 = {instancePath:instancePath+"/delegationId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.action !== undefined){let data2 = data.action;if(!(((((data2 === "spawn") || (data2 === "message")) || (data2 === "resume")) || (data2 === "wait")) || (data2 === "close"))){const err11 = {instancePath:instancePath+"/action",schemaPath:"#/properties/action/enum",keyword:"enum",params:{allowedValues: schema69.properties.action.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.status !== undefined){let data3 = data.status;if(!((((((data3 === "running") || (data3 === "waiting")) || (data3 === "completed")) || (data3 === "failed")) || (data3 === "interrupted")) || (data3 === "closed"))){const err12 = {instancePath:instancePath+"/status",schemaPath:"#/properties/status/enum",keyword:"enum",params:{allowedValues: schema69.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.children !== undefined){let data4 = data.children;if(Array.isArray(data4)){if(data4.length > 64){const err13 = {instancePath:instancePath+"/children",schemaPath:"#/properties/children/maxItems",keyword:"maxItems",params:{limit: 64},message:"must NOT have more than 64 items"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}const len0 = data4.length;for(let i0=0; i0 160){const err21 = {instancePath:instancePath+"/children/" + i0+"/childId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(func1(data6) < 1){const err22 = {instancePath:instancePath+"/children/" + i0+"/childId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}if(!pattern4.test(data6)){const err23 = {instancePath:instancePath+"/children/" + i0+"/childId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}else {const err24 = {instancePath:instancePath+"/children/" + i0+"/childId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}if(data5.role !== undefined){let data7 = data5.role;if((typeof data7 !== "string") && (data7 !== null)){const err25 = {instancePath:instancePath+"/children/" + i0+"/role",schemaPath:"#/properties/children/items/properties/role/type",keyword:"type",params:{type: schema69.properties.children.items.properties.role.type},message:"must be string,null"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}if(typeof data7 === "string"){if(func1(data7) > 160){const err26 = {instancePath:instancePath+"/children/" + i0+"/role",schemaPath:"#/properties/children/items/properties/role/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}}}if(data5.model !== undefined){let data8 = data5.model;if((typeof data8 !== "string") && (data8 !== null)){const err27 = {instancePath:instancePath+"/children/" + i0+"/model",schemaPath:"#/properties/children/items/properties/model/type",keyword:"type",params:{type: schema69.properties.children.items.properties.model.type},message:"must be string,null"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if(typeof data8 === "string"){if(func1(data8) > 240){const err28 = {instancePath:instancePath+"/children/" + i0+"/model",schemaPath:"#/properties/children/items/properties/model/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}}if(data5.status !== undefined){let data9 = data5.status;if(!((((((data9 === "running") || (data9 === "waiting")) || (data9 === "completed")) || (data9 === "failed")) || (data9 === "interrupted")) || (data9 === "closed"))){const err29 = {instancePath:instancePath+"/children/" + i0+"/status",schemaPath:"#/properties/children/items/properties/status/enum",keyword:"enum",params:{allowedValues: schema69.properties.children.items.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}}if(data5.summary !== undefined){let data10 = data5.summary;if((typeof data10 !== "string") && (data10 !== null)){const err30 = {instancePath:instancePath+"/children/" + i0+"/summary",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}if(typeof data10 === "string"){if(func1(data10) > 4000){const err31 = {instancePath:instancePath+"/children/" + i0+"/summary",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}}}if(data5.activitySummary !== undefined){let data11 = data5.activitySummary;if((typeof data11 !== "string") && (data11 !== null)){const err32 = {instancePath:instancePath+"/children/" + i0+"/activitySummary",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}if(typeof data11 === "string"){if(func1(data11) > 4000){const err33 = {instancePath:instancePath+"/children/" + i0+"/activitySummary",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}}}}else {const err34 = {instancePath:instancePath+"/children/" + i0,schemaPath:"#/properties/children/items/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}}}else {const err35 = {instancePath:instancePath+"/children",schemaPath:"#/properties/children/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}}}else {const err36 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}validate66.errors = vErrors;return errors === 0;}validate66.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema74 = {"type":"object","required":["schema","routeId","provider","requestedModel","fromModel","effectiveModel","reason"],"properties":{"schema":{"const":"paperclip.model.route_changed.v1"},"routeId":{"$ref":"#/$defs/id"},"provider":{"type":"string","minLength":1,"maxLength":160},"requestedModel":{"type":"string","minLength":1,"maxLength":240},"fromModel":{"type":["string","null"],"maxLength":240},"effectiveModel":{"type":"string","minLength":1,"maxLength":240},"reason":{"$ref":"#/$defs/shortText"}},"additionalProperties":false};function validate68(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate68.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.routeId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "routeId"},message:"must have required property '"+"routeId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.provider === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "provider"},message:"must have required property '"+"provider"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.requestedModel === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "requestedModel"},message:"must have required property '"+"requestedModel"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.fromModel === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "fromModel"},message:"must have required property '"+"fromModel"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.effectiveModel === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "effectiveModel"},message:"must have required property '"+"effectiveModel"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.reason === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reason"},message:"must have required property '"+"reason"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}for(const key0 in data){if(!(((((((key0 === "schema") || (key0 === "routeId")) || (key0 === "provider")) || (key0 === "requestedModel")) || (key0 === "fromModel")) || (key0 === "effectiveModel")) || (key0 === "reason"))){const err7 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.schema !== undefined){if("paperclip.model.route_changed.v1" !== data.schema){const err8 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.model.route_changed.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}if(data.routeId !== undefined){let data1 = data.routeId;if(typeof data1 === "string"){if(func1(data1) > 160){const err9 = {instancePath:instancePath+"/routeId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(func1(data1) < 1){const err10 = {instancePath:instancePath+"/routeId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(!pattern4.test(data1)){const err11 = {instancePath:instancePath+"/routeId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}else {const err12 = {instancePath:instancePath+"/routeId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.provider !== undefined){let data2 = data.provider;if(typeof data2 === "string"){if(func1(data2) > 160){const err13 = {instancePath:instancePath+"/provider",schemaPath:"#/properties/provider/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(func1(data2) < 1){const err14 = {instancePath:instancePath+"/provider",schemaPath:"#/properties/provider/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}else {const err15 = {instancePath:instancePath+"/provider",schemaPath:"#/properties/provider/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}if(data.requestedModel !== undefined){let data3 = data.requestedModel;if(typeof data3 === "string"){if(func1(data3) > 240){const err16 = {instancePath:instancePath+"/requestedModel",schemaPath:"#/properties/requestedModel/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}if(func1(data3) < 1){const err17 = {instancePath:instancePath+"/requestedModel",schemaPath:"#/properties/requestedModel/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}else {const err18 = {instancePath:instancePath+"/requestedModel",schemaPath:"#/properties/requestedModel/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}if(data.fromModel !== undefined){let data4 = data.fromModel;if((typeof data4 !== "string") && (data4 !== null)){const err19 = {instancePath:instancePath+"/fromModel",schemaPath:"#/properties/fromModel/type",keyword:"type",params:{type: schema74.properties.fromModel.type},message:"must be string,null"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}if(typeof data4 === "string"){if(func1(data4) > 240){const err20 = {instancePath:instancePath+"/fromModel",schemaPath:"#/properties/fromModel/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}}if(data.effectiveModel !== undefined){let data5 = data.effectiveModel;if(typeof data5 === "string"){if(func1(data5) > 240){const err21 = {instancePath:instancePath+"/effectiveModel",schemaPath:"#/properties/effectiveModel/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(func1(data5) < 1){const err22 = {instancePath:instancePath+"/effectiveModel",schemaPath:"#/properties/effectiveModel/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}else {const err23 = {instancePath:instancePath+"/effectiveModel",schemaPath:"#/properties/effectiveModel/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}if(data.reason !== undefined){let data6 = data.reason;if(typeof data6 === "string"){if(func1(data6) > 4000){const err24 = {instancePath:instancePath+"/reason",schemaPath:"#/$defs/shortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}else {const err25 = {instancePath:instancePath+"/reason",schemaPath:"#/$defs/shortText/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}}else {const err26 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}validate68.errors = vErrors;return errors === 0;}validate68.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema77 = {"type":"object","required":["schema","verificationId","status","classes","buffering","summary"],"properties":{"schema":{"const":"paperclip.model.verification.v1"},"verificationId":{"$ref":"#/$defs/id"},"status":{"enum":["running","completed","failed"]},"classes":{"type":"array","maxItems":32,"items":{"type":"string","maxLength":160}},"buffering":{"type":"boolean"},"summary":{"$ref":"#/$defs/nullableShortText"}},"additionalProperties":false};function validate70(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate70.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.verificationId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "verificationId"},message:"must have required property '"+"verificationId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.status === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.classes === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "classes"},message:"must have required property '"+"classes"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.buffering === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "buffering"},message:"must have required property '"+"buffering"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.summary === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "summary"},message:"must have required property '"+"summary"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}for(const key0 in data){if(!((((((key0 === "schema") || (key0 === "verificationId")) || (key0 === "status")) || (key0 === "classes")) || (key0 === "buffering")) || (key0 === "summary"))){const err6 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.schema !== undefined){if("paperclip.model.verification.v1" !== data.schema){const err7 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.model.verification.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.verificationId !== undefined){let data1 = data.verificationId;if(typeof data1 === "string"){if(func1(data1) > 160){const err8 = {instancePath:instancePath+"/verificationId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(func1(data1) < 1){const err9 = {instancePath:instancePath+"/verificationId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(!pattern4.test(data1)){const err10 = {instancePath:instancePath+"/verificationId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}else {const err11 = {instancePath:instancePath+"/verificationId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.status !== undefined){let data2 = data.status;if(!(((data2 === "running") || (data2 === "completed")) || (data2 === "failed"))){const err12 = {instancePath:instancePath+"/status",schemaPath:"#/properties/status/enum",keyword:"enum",params:{allowedValues: schema77.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.classes !== undefined){let data3 = data.classes;if(Array.isArray(data3)){if(data3.length > 32){const err13 = {instancePath:instancePath+"/classes",schemaPath:"#/properties/classes/maxItems",keyword:"maxItems",params:{limit: 32},message:"must NOT have more than 32 items"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}const len0 = data3.length;for(let i0=0; i0 160){const err14 = {instancePath:instancePath+"/classes/" + i0,schemaPath:"#/properties/classes/items/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}else {const err15 = {instancePath:instancePath+"/classes/" + i0,schemaPath:"#/properties/classes/items/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}}else {const err16 = {instancePath:instancePath+"/classes",schemaPath:"#/properties/classes/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}if(data.buffering !== undefined){if(typeof data.buffering !== "boolean"){const err17 = {instancePath:instancePath+"/buffering",schemaPath:"#/properties/buffering/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}if(data.summary !== undefined){let data6 = data.summary;if((typeof data6 !== "string") && (data6 !== null)){const err18 = {instancePath:instancePath+"/summary",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}if(typeof data6 === "string"){if(func1(data6) > 4000){const err19 = {instancePath:instancePath+"/summary",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}}}else {const err20 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}validate70.errors = vErrors;return errors === 0;}validate70.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema80 = {"type":"object","required":["schema","compactionId","reason","preTokens","postTokens","sameSession"],"properties":{"schema":{"const":"paperclip.context.compacted.v1"},"compactionId":{"$ref":"#/$defs/id"},"reason":{"enum":["context_window","manual","provider","unknown"]},"preTokens":{"type":["integer","null"],"minimum":0},"postTokens":{"type":["integer","null"],"minimum":0},"sameSession":{"type":"boolean"}},"additionalProperties":false};function validate72(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate72.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.compactionId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "compactionId"},message:"must have required property '"+"compactionId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.reason === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reason"},message:"must have required property '"+"reason"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.preTokens === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "preTokens"},message:"must have required property '"+"preTokens"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.postTokens === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "postTokens"},message:"must have required property '"+"postTokens"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.sameSession === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sameSession"},message:"must have required property '"+"sameSession"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}for(const key0 in data){if(!((((((key0 === "schema") || (key0 === "compactionId")) || (key0 === "reason")) || (key0 === "preTokens")) || (key0 === "postTokens")) || (key0 === "sameSession"))){const err6 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.schema !== undefined){if("paperclip.context.compacted.v1" !== data.schema){const err7 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.context.compacted.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.compactionId !== undefined){let data1 = data.compactionId;if(typeof data1 === "string"){if(func1(data1) > 160){const err8 = {instancePath:instancePath+"/compactionId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(func1(data1) < 1){const err9 = {instancePath:instancePath+"/compactionId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(!pattern4.test(data1)){const err10 = {instancePath:instancePath+"/compactionId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}else {const err11 = {instancePath:instancePath+"/compactionId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.reason !== undefined){let data2 = data.reason;if(!((((data2 === "context_window") || (data2 === "manual")) || (data2 === "provider")) || (data2 === "unknown"))){const err12 = {instancePath:instancePath+"/reason",schemaPath:"#/properties/reason/enum",keyword:"enum",params:{allowedValues: schema80.properties.reason.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.preTokens !== undefined){let data3 = data.preTokens;if((!(((typeof data3 == "number") && (!(data3 % 1) && !isNaN(data3))) && (isFinite(data3)))) && (data3 !== null)){const err13 = {instancePath:instancePath+"/preTokens",schemaPath:"#/properties/preTokens/type",keyword:"type",params:{type: schema80.properties.preTokens.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if((typeof data3 == "number") && (isFinite(data3))){if(data3 < 0 || isNaN(data3)){const err14 = {instancePath:instancePath+"/preTokens",schemaPath:"#/properties/preTokens/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}}if(data.postTokens !== undefined){let data4 = data.postTokens;if((!(((typeof data4 == "number") && (!(data4 % 1) && !isNaN(data4))) && (isFinite(data4)))) && (data4 !== null)){const err15 = {instancePath:instancePath+"/postTokens",schemaPath:"#/properties/postTokens/type",keyword:"type",params:{type: schema80.properties.postTokens.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}if((typeof data4 == "number") && (isFinite(data4))){if(data4 < 0 || isNaN(data4)){const err16 = {instancePath:instancePath+"/postTokens",schemaPath:"#/properties/postTokens/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}}if(data.sameSession !== undefined){if(typeof data.sameSession !== "boolean"){const err17 = {instancePath:instancePath+"/sameSession",schemaPath:"#/properties/sameSession/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}}else {const err18 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}validate72.errors = vErrors;return errors === 0;}validate72.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema82 = {"type":"object","required":["schema","artifactId","reference","mediaType","title"],"properties":{"schema":{"const":"paperclip.artifact.viewed.v1"},"artifactId":{"$ref":"#/$defs/id"},"reference":{"$ref":"#/$defs/safePath"},"mediaType":{"type":["string","null"],"maxLength":160},"title":{"$ref":"#/$defs/nullableShortText"}},"additionalProperties":false};function validate74(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate74.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.artifactId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "artifactId"},message:"must have required property '"+"artifactId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.reference === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reference"},message:"must have required property '"+"reference"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.mediaType === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "mediaType"},message:"must have required property '"+"mediaType"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.title === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "title"},message:"must have required property '"+"title"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}for(const key0 in data){if(!(((((key0 === "schema") || (key0 === "artifactId")) || (key0 === "reference")) || (key0 === "mediaType")) || (key0 === "title"))){const err5 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}if(data.schema !== undefined){if("paperclip.artifact.viewed.v1" !== data.schema){const err6 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.artifact.viewed.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.artifactId !== undefined){let data1 = data.artifactId;if(typeof data1 === "string"){if(func1(data1) > 160){const err7 = {instancePath:instancePath+"/artifactId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(func1(data1) < 1){const err8 = {instancePath:instancePath+"/artifactId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(!pattern4.test(data1)){const err9 = {instancePath:instancePath+"/artifactId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}else {const err10 = {instancePath:instancePath+"/artifactId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.reference !== undefined){let data2 = data.reference;if((typeof data2 !== "string") && (data2 !== null)){const err11 = {instancePath:instancePath+"/reference",schemaPath:"#/$defs/safePath/type",keyword:"type",params:{type: schema57.type},message:"must be string,null"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(typeof data2 === "string"){if(func1(data2) > 4096){const err12 = {instancePath:instancePath+"/reference",schemaPath:"#/$defs/safePath/maxLength",keyword:"maxLength",params:{limit: 4096},message:"must NOT have more than 4096 characters"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(!pattern20.test(data2)){const err13 = {instancePath:instancePath+"/reference",schemaPath:"#/$defs/safePath/pattern",keyword:"pattern",params:{pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$"},message:"must match pattern \""+"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$"+"\""};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}}if(data.mediaType !== undefined){let data3 = data.mediaType;if((typeof data3 !== "string") && (data3 !== null)){const err14 = {instancePath:instancePath+"/mediaType",schemaPath:"#/properties/mediaType/type",keyword:"type",params:{type: schema82.properties.mediaType.type},message:"must be string,null"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(typeof data3 === "string"){if(func1(data3) > 160){const err15 = {instancePath:instancePath+"/mediaType",schemaPath:"#/properties/mediaType/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}}if(data.title !== undefined){let data4 = data.title;if((typeof data4 !== "string") && (data4 !== null)){const err16 = {instancePath:instancePath+"/title",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}if(typeof data4 === "string"){if(func1(data4) > 4000){const err17 = {instancePath:instancePath+"/title",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}}}else {const err18 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}validate74.errors = vErrors;return errors === 0;}validate74.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema86 = {"type":"object","required":["schema","artifactId","status","reference","mediaType","registered","failure"],"properties":{"schema":{"const":"paperclip.artifact.generated.v1"},"artifactId":{"$ref":"#/$defs/id"},"status":{"enum":["running","completed","failed"]},"reference":{"$ref":"#/$defs/safePath"},"mediaType":{"type":["string","null"],"maxLength":160},"registered":{"type":"boolean"},"transparentBackground":{"type":["boolean","null"]},"failure":{"$ref":"#/$defs/nullableShortText"}},"additionalProperties":false};function validate76(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate76.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.artifactId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "artifactId"},message:"must have required property '"+"artifactId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.status === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.reference === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reference"},message:"must have required property '"+"reference"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.mediaType === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "mediaType"},message:"must have required property '"+"mediaType"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.registered === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "registered"},message:"must have required property '"+"registered"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.failure === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "failure"},message:"must have required property '"+"failure"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}for(const key0 in data){if(!((((((((key0 === "schema") || (key0 === "artifactId")) || (key0 === "status")) || (key0 === "reference")) || (key0 === "mediaType")) || (key0 === "registered")) || (key0 === "transparentBackground")) || (key0 === "failure"))){const err7 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.schema !== undefined){if("paperclip.artifact.generated.v1" !== data.schema){const err8 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.artifact.generated.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}if(data.artifactId !== undefined){let data1 = data.artifactId;if(typeof data1 === "string"){if(func1(data1) > 160){const err9 = {instancePath:instancePath+"/artifactId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(func1(data1) < 1){const err10 = {instancePath:instancePath+"/artifactId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(!pattern4.test(data1)){const err11 = {instancePath:instancePath+"/artifactId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}else {const err12 = {instancePath:instancePath+"/artifactId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.status !== undefined){let data2 = data.status;if(!(((data2 === "running") || (data2 === "completed")) || (data2 === "failed"))){const err13 = {instancePath:instancePath+"/status",schemaPath:"#/properties/status/enum",keyword:"enum",params:{allowedValues: schema86.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}if(data.reference !== undefined){let data3 = data.reference;if((typeof data3 !== "string") && (data3 !== null)){const err14 = {instancePath:instancePath+"/reference",schemaPath:"#/$defs/safePath/type",keyword:"type",params:{type: schema57.type},message:"must be string,null"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(typeof data3 === "string"){if(func1(data3) > 4096){const err15 = {instancePath:instancePath+"/reference",schemaPath:"#/$defs/safePath/maxLength",keyword:"maxLength",params:{limit: 4096},message:"must NOT have more than 4096 characters"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}if(!pattern20.test(data3)){const err16 = {instancePath:instancePath+"/reference",schemaPath:"#/$defs/safePath/pattern",keyword:"pattern",params:{pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$"},message:"must match pattern \""+"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$"+"\""};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}}if(data.mediaType !== undefined){let data4 = data.mediaType;if((typeof data4 !== "string") && (data4 !== null)){const err17 = {instancePath:instancePath+"/mediaType",schemaPath:"#/properties/mediaType/type",keyword:"type",params:{type: schema86.properties.mediaType.type},message:"must be string,null"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}if(typeof data4 === "string"){if(func1(data4) > 160){const err18 = {instancePath:instancePath+"/mediaType",schemaPath:"#/properties/mediaType/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}}if(data.registered !== undefined){if(typeof data.registered !== "boolean"){const err19 = {instancePath:instancePath+"/registered",schemaPath:"#/properties/registered/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}if(data.transparentBackground !== undefined){let data6 = data.transparentBackground;if((typeof data6 !== "boolean") && (data6 !== null)){const err20 = {instancePath:instancePath+"/transparentBackground",schemaPath:"#/properties/transparentBackground/type",keyword:"type",params:{type: schema86.properties.transparentBackground.type},message:"must be boolean,null"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}if(data.failure !== undefined){let data7 = data.failure;if((typeof data7 !== "string") && (data7 !== null)){const err21 = {instancePath:instancePath+"/failure",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(typeof data7 === "string"){if(func1(data7) > 4000){const err22 = {instancePath:instancePath+"/failure",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}}}else {const err23 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}validate76.errors = vErrors;return errors === 0;}validate76.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema90 = {"type":"object","required":["schema","reviewId","state","scope"],"properties":{"schema":{"const":"paperclip.review.mode_changed.v1"},"reviewId":{"$ref":"#/$defs/id"},"state":{"enum":["entered","exited"]},"scope":{"$ref":"#/$defs/nullableShortText"}},"additionalProperties":false};function validate78(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate78.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.reviewId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reviewId"},message:"must have required property '"+"reviewId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.state === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "state"},message:"must have required property '"+"state"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.scope === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "scope"},message:"must have required property '"+"scope"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}for(const key0 in data){if(!((((key0 === "schema") || (key0 === "reviewId")) || (key0 === "state")) || (key0 === "scope"))){const err4 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}}if(data.schema !== undefined){if("paperclip.review.mode_changed.v1" !== data.schema){const err5 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.review.mode_changed.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}if(data.reviewId !== undefined){let data1 = data.reviewId;if(typeof data1 === "string"){if(func1(data1) > 160){const err6 = {instancePath:instancePath+"/reviewId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(func1(data1) < 1){const err7 = {instancePath:instancePath+"/reviewId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(!pattern4.test(data1)){const err8 = {instancePath:instancePath+"/reviewId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}else {const err9 = {instancePath:instancePath+"/reviewId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}if(data.state !== undefined){let data2 = data.state;if(!((data2 === "entered") || (data2 === "exited"))){const err10 = {instancePath:instancePath+"/state",schemaPath:"#/properties/state/enum",keyword:"enum",params:{allowedValues: schema90.properties.state.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.scope !== undefined){let data3 = data.scope;if((typeof data3 !== "string") && (data3 !== null)){const err11 = {instancePath:instancePath+"/scope",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(typeof data3 === "string"){if(func1(data3) > 4000){const err12 = {instancePath:instancePath+"/scope",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}}}else {const err13 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}validate78.errors = vErrors;return errors === 0;}validate78.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema93 = {"type":"object","required":["schema","hookId","event","scope","status","blocking","durationMs","summary"],"properties":{"schema":{"const":"paperclip.hook.v1"},"hookId":{"$ref":"#/$defs/id"},"event":{"type":"string","minLength":1,"maxLength":160},"scope":{"type":"string","minLength":1,"maxLength":160},"status":{"enum":["running","completed","failed","cancelled"]},"blocking":{"type":"boolean"},"durationMs":{"type":["integer","null"],"minimum":0},"summary":{"$ref":"#/$defs/nullableShortText"}},"additionalProperties":false};function validate80(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate80.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.hookId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "hookId"},message:"must have required property '"+"hookId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.event === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "event"},message:"must have required property '"+"event"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.scope === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "scope"},message:"must have required property '"+"scope"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.status === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.blocking === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "blocking"},message:"must have required property '"+"blocking"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.durationMs === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "durationMs"},message:"must have required property '"+"durationMs"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(data.summary === undefined){const err7 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "summary"},message:"must have required property '"+"summary"+"'"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}for(const key0 in data){if(!((((((((key0 === "schema") || (key0 === "hookId")) || (key0 === "event")) || (key0 === "scope")) || (key0 === "status")) || (key0 === "blocking")) || (key0 === "durationMs")) || (key0 === "summary"))){const err8 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}if(data.schema !== undefined){if("paperclip.hook.v1" !== data.schema){const err9 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.hook.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}if(data.hookId !== undefined){let data1 = data.hookId;if(typeof data1 === "string"){if(func1(data1) > 160){const err10 = {instancePath:instancePath+"/hookId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(func1(data1) < 1){const err11 = {instancePath:instancePath+"/hookId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(!pattern4.test(data1)){const err12 = {instancePath:instancePath+"/hookId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}else {const err13 = {instancePath:instancePath+"/hookId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}if(data.event !== undefined){let data2 = data.event;if(typeof data2 === "string"){if(func1(data2) > 160){const err14 = {instancePath:instancePath+"/event",schemaPath:"#/properties/event/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(func1(data2) < 1){const err15 = {instancePath:instancePath+"/event",schemaPath:"#/properties/event/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}else {const err16 = {instancePath:instancePath+"/event",schemaPath:"#/properties/event/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}if(data.scope !== undefined){let data3 = data.scope;if(typeof data3 === "string"){if(func1(data3) > 160){const err17 = {instancePath:instancePath+"/scope",schemaPath:"#/properties/scope/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}if(func1(data3) < 1){const err18 = {instancePath:instancePath+"/scope",schemaPath:"#/properties/scope/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}else {const err19 = {instancePath:instancePath+"/scope",schemaPath:"#/properties/scope/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}if(data.status !== undefined){let data4 = data.status;if(!((((data4 === "running") || (data4 === "completed")) || (data4 === "failed")) || (data4 === "cancelled"))){const err20 = {instancePath:instancePath+"/status",schemaPath:"#/properties/status/enum",keyword:"enum",params:{allowedValues: schema93.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}if(data.blocking !== undefined){if(typeof data.blocking !== "boolean"){const err21 = {instancePath:instancePath+"/blocking",schemaPath:"#/properties/blocking/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}}if(data.durationMs !== undefined){let data6 = data.durationMs;if((!(((typeof data6 == "number") && (!(data6 % 1) && !isNaN(data6))) && (isFinite(data6)))) && (data6 !== null)){const err22 = {instancePath:instancePath+"/durationMs",schemaPath:"#/properties/durationMs/type",keyword:"type",params:{type: schema93.properties.durationMs.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}if((typeof data6 == "number") && (isFinite(data6))){if(data6 < 0 || isNaN(data6)){const err23 = {instancePath:instancePath+"/durationMs",schemaPath:"#/properties/durationMs/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}}if(data.summary !== undefined){let data7 = data.summary;if((typeof data7 !== "string") && (data7 !== null)){const err24 = {instancePath:instancePath+"/summary",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}if(typeof data7 === "string"){if(func1(data7) > 4000){const err25 = {instancePath:instancePath+"/summary",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}}}else {const err26 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}validate80.errors = vErrors;return errors === 0;}validate80.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema96 = {"type":"object","required":["schema","citationId","messageItemId","label","available","reference"],"properties":{"schema":{"const":"paperclip.memory.citation.v1"},"citationId":{"$ref":"#/$defs/id"},"messageItemId":{"$ref":"#/$defs/id"},"label":{"$ref":"#/$defs/shortText"},"available":{"type":"boolean"},"reference":{"type":["string","null"],"maxLength":4096}},"additionalProperties":false};function validate82(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate82.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.citationId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "citationId"},message:"must have required property '"+"citationId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.messageItemId === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "messageItemId"},message:"must have required property '"+"messageItemId"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.label === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "label"},message:"must have required property '"+"label"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.available === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "available"},message:"must have required property '"+"available"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.reference === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reference"},message:"must have required property '"+"reference"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}for(const key0 in data){if(!((((((key0 === "schema") || (key0 === "citationId")) || (key0 === "messageItemId")) || (key0 === "label")) || (key0 === "available")) || (key0 === "reference"))){const err6 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.schema !== undefined){if("paperclip.memory.citation.v1" !== data.schema){const err7 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.memory.citation.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.citationId !== undefined){let data1 = data.citationId;if(typeof data1 === "string"){if(func1(data1) > 160){const err8 = {instancePath:instancePath+"/citationId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(func1(data1) < 1){const err9 = {instancePath:instancePath+"/citationId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(!pattern4.test(data1)){const err10 = {instancePath:instancePath+"/citationId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}else {const err11 = {instancePath:instancePath+"/citationId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.messageItemId !== undefined){let data2 = data.messageItemId;if(typeof data2 === "string"){if(func1(data2) > 160){const err12 = {instancePath:instancePath+"/messageItemId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(func1(data2) < 1){const err13 = {instancePath:instancePath+"/messageItemId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(!pattern4.test(data2)){const err14 = {instancePath:instancePath+"/messageItemId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}else {const err15 = {instancePath:instancePath+"/messageItemId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}if(data.label !== undefined){let data3 = data.label;if(typeof data3 === "string"){if(func1(data3) > 4000){const err16 = {instancePath:instancePath+"/label",schemaPath:"#/$defs/shortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}else {const err17 = {instancePath:instancePath+"/label",schemaPath:"#/$defs/shortText/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}if(data.available !== undefined){if(typeof data.available !== "boolean"){const err18 = {instancePath:instancePath+"/available",schemaPath:"#/properties/available/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}if(data.reference !== undefined){let data5 = data.reference;if((typeof data5 !== "string") && (data5 !== null)){const err19 = {instancePath:instancePath+"/reference",schemaPath:"#/properties/reference/type",keyword:"type",params:{type: schema96.properties.reference.type},message:"must be string,null"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}if(typeof data5 === "string"){if(func1(data5) > 4096){const err20 = {instancePath:instancePath+"/reference",schemaPath:"#/properties/reference/maxLength",keyword:"maxLength",params:{limit: 4096},message:"must NOT have more than 4096 characters"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}}}else {const err21 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}validate82.errors = vErrors;return errors === 0;}validate82.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema100 = {"type":"object","required":["schema","reviewId","targetExecutionId","status","decision","summary"],"properties":{"schema":{"const":"paperclip.safety.review.v1"},"reviewId":{"$ref":"#/$defs/id"},"targetExecutionId":{"anyOf":[{"$ref":"#/$defs/id"},{"type":"null"}]},"status":{"enum":["running","completed","failed"]},"decision":{"enum":["pending","allowed","denied","user_required","unknown"]},"summary":{"$ref":"#/$defs/nullableShortText"}},"additionalProperties":false};function validate84(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate84.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.reviewId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reviewId"},message:"must have required property '"+"reviewId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.targetExecutionId === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "targetExecutionId"},message:"must have required property '"+"targetExecutionId"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.status === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.decision === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "decision"},message:"must have required property '"+"decision"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.summary === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "summary"},message:"must have required property '"+"summary"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}for(const key0 in data){if(!((((((key0 === "schema") || (key0 === "reviewId")) || (key0 === "targetExecutionId")) || (key0 === "status")) || (key0 === "decision")) || (key0 === "summary"))){const err6 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.schema !== undefined){if("paperclip.safety.review.v1" !== data.schema){const err7 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.safety.review.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.reviewId !== undefined){let data1 = data.reviewId;if(typeof data1 === "string"){if(func1(data1) > 160){const err8 = {instancePath:instancePath+"/reviewId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(func1(data1) < 1){const err9 = {instancePath:instancePath+"/reviewId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(!pattern4.test(data1)){const err10 = {instancePath:instancePath+"/reviewId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}else {const err11 = {instancePath:instancePath+"/reviewId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.targetExecutionId !== undefined){let data2 = data.targetExecutionId;const _errs7 = errors;let valid2 = false;const _errs8 = errors;if(typeof data2 === "string"){if(func1(data2) > 160){const err12 = {instancePath:instancePath+"/targetExecutionId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(func1(data2) < 1){const err13 = {instancePath:instancePath+"/targetExecutionId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(!pattern4.test(data2)){const err14 = {instancePath:instancePath+"/targetExecutionId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}else {const err15 = {instancePath:instancePath+"/targetExecutionId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}var _valid0 = _errs8 === errors;valid2 = valid2 || _valid0;const _errs11 = errors;if(data2 !== null){const err16 = {instancePath:instancePath+"/targetExecutionId",schemaPath:"#/properties/targetExecutionId/anyOf/1/type",keyword:"type",params:{type: "null"},message:"must be null"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}var _valid0 = _errs11 === errors;valid2 = valid2 || _valid0;if(!valid2){const err17 = {instancePath:instancePath+"/targetExecutionId",schemaPath:"#/properties/targetExecutionId/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}else {errors = _errs7;if(vErrors !== null){if(_errs7){vErrors.length = _errs7;}else {vErrors = null;}}}}if(data.status !== undefined){let data3 = data.status;if(!(((data3 === "running") || (data3 === "completed")) || (data3 === "failed"))){const err18 = {instancePath:instancePath+"/status",schemaPath:"#/properties/status/enum",keyword:"enum",params:{allowedValues: schema100.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}if(data.decision !== undefined){let data4 = data.decision;if(!(((((data4 === "pending") || (data4 === "allowed")) || (data4 === "denied")) || (data4 === "user_required")) || (data4 === "unknown"))){const err19 = {instancePath:instancePath+"/decision",schemaPath:"#/properties/decision/enum",keyword:"enum",params:{allowedValues: schema100.properties.decision.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}if(data.summary !== undefined){let data5 = data.summary;if((typeof data5 !== "string") && (data5 !== null)){const err20 = {instancePath:instancePath+"/summary",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}if(typeof data5 === "string"){if(func1(data5) > 4000){const err21 = {instancePath:instancePath+"/summary",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}}}}else {const err22 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}validate84.errors = vErrors;return errors === 0;}validate84.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema104 = {"type":"object","required":["schema","executionId","origin","inputClass","byteCount"],"properties":{"schema":{"const":"paperclip.terminal.input_sent.v1"},"executionId":{"$ref":"#/$defs/id"},"origin":{"enum":["agent","user","system"]},"inputClass":{"enum":["text","control","eof"]},"byteCount":{"type":"integer","minimum":0}},"additionalProperties":false};function validate86(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate86.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.executionId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "executionId"},message:"must have required property '"+"executionId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.origin === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "origin"},message:"must have required property '"+"origin"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.inputClass === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "inputClass"},message:"must have required property '"+"inputClass"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.byteCount === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "byteCount"},message:"must have required property '"+"byteCount"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}for(const key0 in data){if(!(((((key0 === "schema") || (key0 === "executionId")) || (key0 === "origin")) || (key0 === "inputClass")) || (key0 === "byteCount"))){const err5 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}if(data.schema !== undefined){if("paperclip.terminal.input_sent.v1" !== data.schema){const err6 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.terminal.input_sent.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.executionId !== undefined){let data1 = data.executionId;if(typeof data1 === "string"){if(func1(data1) > 160){const err7 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(func1(data1) < 1){const err8 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(!pattern4.test(data1)){const err9 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}else {const err10 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.origin !== undefined){let data2 = data.origin;if(!(((data2 === "agent") || (data2 === "user")) || (data2 === "system"))){const err11 = {instancePath:instancePath+"/origin",schemaPath:"#/properties/origin/enum",keyword:"enum",params:{allowedValues: schema104.properties.origin.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.inputClass !== undefined){let data3 = data.inputClass;if(!(((data3 === "text") || (data3 === "control")) || (data3 === "eof"))){const err12 = {instancePath:instancePath+"/inputClass",schemaPath:"#/properties/inputClass/enum",keyword:"enum",params:{allowedValues: schema104.properties.inputClass.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.byteCount !== undefined){let data4 = data.byteCount;if(!(((typeof data4 == "number") && (!(data4 % 1) && !isNaN(data4))) && (isFinite(data4)))){const err13 = {instancePath:instancePath+"/byteCount",schemaPath:"#/properties/byteCount/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if((typeof data4 == "number") && (isFinite(data4))){if(data4 < 0 || isNaN(data4)){const err14 = {instancePath:instancePath+"/byteCount",schemaPath:"#/properties/byteCount/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}}}else {const err15 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}validate86.errors = vErrors;return errors === 0;}validate86.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema106 = {"type":"object","required":["schema","waitId","reason","status","plannedDurationMs","elapsedDurationMs"],"properties":{"schema":{"const":"paperclip.wait.v1"},"waitId":{"$ref":"#/$defs/id"},"reason":{"enum":["timer","provider","rate_limit","unknown"]},"status":{"enum":["running","completed","interrupted","cancelled"]},"plannedDurationMs":{"type":["integer","null"],"minimum":0},"elapsedDurationMs":{"type":["integer","null"],"minimum":0}},"additionalProperties":false};function validate88(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate88.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.waitId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "waitId"},message:"must have required property '"+"waitId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.reason === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reason"},message:"must have required property '"+"reason"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.status === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.plannedDurationMs === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "plannedDurationMs"},message:"must have required property '"+"plannedDurationMs"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.elapsedDurationMs === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "elapsedDurationMs"},message:"must have required property '"+"elapsedDurationMs"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}for(const key0 in data){if(!((((((key0 === "schema") || (key0 === "waitId")) || (key0 === "reason")) || (key0 === "status")) || (key0 === "plannedDurationMs")) || (key0 === "elapsedDurationMs"))){const err6 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.schema !== undefined){if("paperclip.wait.v1" !== data.schema){const err7 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.wait.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.waitId !== undefined){let data1 = data.waitId;if(typeof data1 === "string"){if(func1(data1) > 160){const err8 = {instancePath:instancePath+"/waitId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(func1(data1) < 1){const err9 = {instancePath:instancePath+"/waitId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(!pattern4.test(data1)){const err10 = {instancePath:instancePath+"/waitId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}else {const err11 = {instancePath:instancePath+"/waitId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.reason !== undefined){let data2 = data.reason;if(!((((data2 === "timer") || (data2 === "provider")) || (data2 === "rate_limit")) || (data2 === "unknown"))){const err12 = {instancePath:instancePath+"/reason",schemaPath:"#/properties/reason/enum",keyword:"enum",params:{allowedValues: schema106.properties.reason.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.status !== undefined){let data3 = data.status;if(!((((data3 === "running") || (data3 === "completed")) || (data3 === "interrupted")) || (data3 === "cancelled"))){const err13 = {instancePath:instancePath+"/status",schemaPath:"#/properties/status/enum",keyword:"enum",params:{allowedValues: schema106.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}if(data.plannedDurationMs !== undefined){let data4 = data.plannedDurationMs;if((!(((typeof data4 == "number") && (!(data4 % 1) && !isNaN(data4))) && (isFinite(data4)))) && (data4 !== null)){const err14 = {instancePath:instancePath+"/plannedDurationMs",schemaPath:"#/properties/plannedDurationMs/type",keyword:"type",params:{type: schema106.properties.plannedDurationMs.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if((typeof data4 == "number") && (isFinite(data4))){if(data4 < 0 || isNaN(data4)){const err15 = {instancePath:instancePath+"/plannedDurationMs",schemaPath:"#/properties/plannedDurationMs/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}}if(data.elapsedDurationMs !== undefined){let data5 = data.elapsedDurationMs;if((!(((typeof data5 == "number") && (!(data5 % 1) && !isNaN(data5))) && (isFinite(data5)))) && (data5 !== null)){const err16 = {instancePath:instancePath+"/elapsedDurationMs",schemaPath:"#/properties/elapsedDurationMs/type",keyword:"type",params:{type: schema106.properties.elapsedDurationMs.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}if((typeof data5 == "number") && (isFinite(data5))){if(data5 < 0 || isNaN(data5)){const err17 = {instancePath:instancePath+"/elapsedDurationMs",schemaPath:"#/properties/elapsedDurationMs/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}}}else {const err18 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}validate88.errors = vErrors;return errors === 0;}validate88.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema108 = {"type":"object","required":["schema","noticeId","severity","category","scope","recoverable","userActionable","summary"],"properties":{"schema":{"const":"paperclip.provider.notice.v1"},"noticeId":{"$ref":"#/$defs/id"},"severity":{"enum":["info","warning","error"]},"category":{"type":"string","minLength":1,"maxLength":160},"scope":{"enum":["turn","session","environment","account"]},"recoverable":{"type":"boolean"},"userActionable":{"type":"boolean"},"summary":{"$ref":"#/$defs/shortText"}},"additionalProperties":false};function validate90(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate90.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.noticeId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "noticeId"},message:"must have required property '"+"noticeId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.severity === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "severity"},message:"must have required property '"+"severity"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.category === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "category"},message:"must have required property '"+"category"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.scope === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "scope"},message:"must have required property '"+"scope"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.recoverable === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "recoverable"},message:"must have required property '"+"recoverable"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.userActionable === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "userActionable"},message:"must have required property '"+"userActionable"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(data.summary === undefined){const err7 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "summary"},message:"must have required property '"+"summary"+"'"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}for(const key0 in data){if(!((((((((key0 === "schema") || (key0 === "noticeId")) || (key0 === "severity")) || (key0 === "category")) || (key0 === "scope")) || (key0 === "recoverable")) || (key0 === "userActionable")) || (key0 === "summary"))){const err8 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}if(data.schema !== undefined){if("paperclip.provider.notice.v1" !== data.schema){const err9 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.provider.notice.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}if(data.noticeId !== undefined){let data1 = data.noticeId;if(typeof data1 === "string"){if(func1(data1) > 160){const err10 = {instancePath:instancePath+"/noticeId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(func1(data1) < 1){const err11 = {instancePath:instancePath+"/noticeId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(!pattern4.test(data1)){const err12 = {instancePath:instancePath+"/noticeId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}else {const err13 = {instancePath:instancePath+"/noticeId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}if(data.severity !== undefined){let data2 = data.severity;if(!(((data2 === "info") || (data2 === "warning")) || (data2 === "error"))){const err14 = {instancePath:instancePath+"/severity",schemaPath:"#/properties/severity/enum",keyword:"enum",params:{allowedValues: schema108.properties.severity.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}if(data.category !== undefined){let data3 = data.category;if(typeof data3 === "string"){if(func1(data3) > 160){const err15 = {instancePath:instancePath+"/category",schemaPath:"#/properties/category/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}if(func1(data3) < 1){const err16 = {instancePath:instancePath+"/category",schemaPath:"#/properties/category/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}else {const err17 = {instancePath:instancePath+"/category",schemaPath:"#/properties/category/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}if(data.scope !== undefined){let data4 = data.scope;if(!((((data4 === "turn") || (data4 === "session")) || (data4 === "environment")) || (data4 === "account"))){const err18 = {instancePath:instancePath+"/scope",schemaPath:"#/properties/scope/enum",keyword:"enum",params:{allowedValues: schema108.properties.scope.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}if(data.recoverable !== undefined){if(typeof data.recoverable !== "boolean"){const err19 = {instancePath:instancePath+"/recoverable",schemaPath:"#/properties/recoverable/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}if(data.userActionable !== undefined){if(typeof data.userActionable !== "boolean"){const err20 = {instancePath:instancePath+"/userActionable",schemaPath:"#/properties/userActionable/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}if(data.summary !== undefined){let data7 = data.summary;if(typeof data7 === "string"){if(func1(data7) > 4000){const err21 = {instancePath:instancePath+"/summary",schemaPath:"#/$defs/shortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}}else {const err22 = {instancePath:instancePath+"/summary",schemaPath:"#/$defs/shortText/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}}else {const err23 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}validate90.errors = vErrors;return errors === 0;}validate90.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema176 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json","title":"PRP provider-neutral semantic tool envelope","type":"object","required":["schema","schemaVersion","phase","operationId","callId","correlation","idempotencyKey","content"],"properties":{"schema":{"const":"paperclip.prp.semantic_tool.v1"},"schemaVersion":{"const":1},"phase":{"enum":["input","result","reconciled"]},"operationId":{"$ref":"#/$defs/stableId"},"callId":{"$ref":"#/$defs/stableId"},"correlation":{"type":"object","required":["runId","normalizedSessionId","turnId","itemId"],"properties":{"runId":{"$ref":"#/$defs/stableId"},"normalizedSessionId":{"$ref":"#/$defs/stableId"},"turnId":{"$ref":"#/$defs/stableId"},"itemId":{"$ref":"#/$defs/stableId"},"requestId":{"$ref":"#/$defs/stableId"}},"additionalProperties":true},"idempotencyKey":{"type":["string","null"],"minLength":1,"maxLength":240},"content":{"$ref":"#/$defs/safeContent"},"outcome":{"enum":["succeeded","denied","conflict","duplicate","unavailable","failed"]},"code":{"$ref":"#/$defs/stableCode"},"retryable":{"type":"boolean"},"authorizationBoundary":{"enum":["company","actor","active_task","grant","governed_action","lock","revision"]},"operationReceiptId":{"$ref":"#/$defs/stableId"},"auditReceiptId":{"$ref":"#/$defs/stableId"},"currentRevision":{"oneOf":[{"type":"integer","minimum":0},{"$ref":"#/$defs/stableId"}]},"duplicateOfReceiptId":{"$ref":"#/$defs/stableId"},"artifactRefs":{"type":"array","items":{"allOf":[{"$ref":"#/$defs/safeReference"},{"type":"object","properties":{"kind":{"enum":["artifact","work_product"]}}}]},"uniqueItems":true},"targets":{"type":"array","items":{"$ref":"#/$defs/immutableTarget"},"uniqueItems":true},"causalRefs":{"type":"array","items":{"$ref":"#/$defs/safeReference"},"uniqueItems":true}},"allOf":[{"if":{"properties":{"phase":{"enum":["result","reconciled"]}},"required":["phase"]},"then":{"required":["outcome","code","retryable","authorizationBoundary","operationReceiptId"]}}],"additionalProperties":true,"$defs":{"stableId":{"type":"string","minLength":1,"maxLength":240,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]*$"},"stableCode":{"type":"string","minLength":1,"maxLength":160,"pattern":"^[a-z][a-z0-9_.:-]*$"},"safeContent":{"type":"object","required":["digest","redactionDisposition","references"],"properties":{"digest":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"redactionDisposition":{"enum":["digest_only","allowlisted_references","redacted"]},"references":{"type":"array","items":{"$ref":"#/$defs/safeReference"},"uniqueItems":true}},"additionalProperties":true},"safeReference":{"type":"object","required":["kind","id"],"properties":{"kind":{"enum":["task","document_revision","interaction","approval","decision","artifact","work_product","wake","monitor","audit","operation"]},"id":{"$ref":"#/$defs/stableId"}},"additionalProperties":true},"immutableTarget":{"type":"object","required":["kind","id","immutable"],"properties":{"kind":{"enum":["document_revision","interaction","approval","decision"]},"id":{"$ref":"#/$defs/stableId"},"immutable":{"const":true},"revisionId":{"$ref":"#/$defs/stableId"},"decisionId":{"$ref":"#/$defs/stableId"}},"additionalProperties":true}}};const schema177 = {"type":"string","minLength":1,"maxLength":240,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]*$"};const schema187 = {"type":"string","minLength":1,"maxLength":160,"pattern":"^[a-z][a-z0-9_.:-]*$"};const pattern14 = new RegExp("^[a-z][a-z0-9_.:-]*$", "u");const schema184 = {"type":"object","required":["digest","redactionDisposition","references"],"properties":{"digest":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"redactionDisposition":{"enum":["digest_only","allowlisted_references","redacted"]},"references":{"type":"array","items":{"$ref":"#/$defs/safeReference"},"uniqueItems":true}},"additionalProperties":true};const schema185 = {"type":"object","required":["kind","id"],"properties":{"kind":{"enum":["task","document_revision","interaction","approval","decision","artifact","work_product","wake","monitor","audit","operation"]},"id":{"$ref":"#/$defs/stableId"}},"additionalProperties":true};function validate94(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate94.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.kind === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "kind"},message:"must have required property '"+"kind"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.id === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "id"},message:"must have required property '"+"id"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.kind !== undefined){let data0 = data.kind;if(!(((((((((((data0 === "task") || (data0 === "document_revision")) || (data0 === "interaction")) || (data0 === "approval")) || (data0 === "decision")) || (data0 === "artifact")) || (data0 === "work_product")) || (data0 === "wake")) || (data0 === "monitor")) || (data0 === "audit")) || (data0 === "operation"))){const err2 = {instancePath:instancePath+"/kind",schemaPath:"#/properties/kind/enum",keyword:"enum",params:{allowedValues: schema185.properties.kind.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}}if(data.id !== undefined){let data1 = data.id;if(typeof data1 === "string"){if(func1(data1) > 240){const err3 = {instancePath:instancePath+"/id",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(func1(data1) < 1){const err4 = {instancePath:instancePath+"/id",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(!pattern4.test(data1)){const err5 = {instancePath:instancePath+"/id",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}else {const err6 = {instancePath:instancePath+"/id",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}}else {const err7 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}validate94.errors = vErrors;return errors === 0;}validate94.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};function validate93(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate93.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.digest === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "digest"},message:"must have required property '"+"digest"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.redactionDisposition === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "redactionDisposition"},message:"must have required property '"+"redactionDisposition"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.references === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "references"},message:"must have required property '"+"references"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.digest !== undefined){let data0 = data.digest;if(typeof data0 === "string"){if(!pattern21.test(data0)){const err3 = {instancePath:instancePath+"/digest",schemaPath:"#/properties/digest/pattern",keyword:"pattern",params:{pattern: "^sha256:[a-f0-9]{64}$"},message:"must match pattern \""+"^sha256:[a-f0-9]{64}$"+"\""};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}}else {const err4 = {instancePath:instancePath+"/digest",schemaPath:"#/properties/digest/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}}if(data.redactionDisposition !== undefined){let data1 = data.redactionDisposition;if(!(((data1 === "digest_only") || (data1 === "allowlisted_references")) || (data1 === "redacted"))){const err5 = {instancePath:instancePath+"/redactionDisposition",schemaPath:"#/properties/redactionDisposition/enum",keyword:"enum",params:{allowedValues: schema184.properties.redactionDisposition.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}if(data.references !== undefined){let data2 = data.references;if(Array.isArray(data2)){const len0 = data2.length;for(let i0=0; i0 1){outer0:for(;i1--;){for(j0 = i1; j0--;){if(func0(data2[i1], data2[j0])){const err6 = {instancePath:instancePath+"/references",schemaPath:"#/properties/references/uniqueItems",keyword:"uniqueItems",params:{i: i1, j: j0},message:"must NOT have duplicate items (items ## "+j0+" and "+i1+" are identical)"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;break outer0;}}}}}else {const err7 = {instancePath:instancePath+"/references",schemaPath:"#/properties/references/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}}else {const err8 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}validate93.errors = vErrors;return errors === 0;}validate93.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema192 = {"type":"object","required":["kind","id","immutable"],"properties":{"kind":{"enum":["document_revision","interaction","approval","decision"]},"id":{"$ref":"#/$defs/stableId"},"immutable":{"const":true},"revisionId":{"$ref":"#/$defs/stableId"},"decisionId":{"$ref":"#/$defs/stableId"}},"additionalProperties":true};function validate98(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate98.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.kind === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "kind"},message:"must have required property '"+"kind"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.id === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "id"},message:"must have required property '"+"id"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.immutable === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "immutable"},message:"must have required property '"+"immutable"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.kind !== undefined){let data0 = data.kind;if(!((((data0 === "document_revision") || (data0 === "interaction")) || (data0 === "approval")) || (data0 === "decision"))){const err3 = {instancePath:instancePath+"/kind",schemaPath:"#/properties/kind/enum",keyword:"enum",params:{allowedValues: schema192.properties.kind.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}}if(data.id !== undefined){let data1 = data.id;if(typeof data1 === "string"){if(func1(data1) > 240){const err4 = {instancePath:instancePath+"/id",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(func1(data1) < 1){const err5 = {instancePath:instancePath+"/id",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(!pattern4.test(data1)){const err6 = {instancePath:instancePath+"/id",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}else {const err7 = {instancePath:instancePath+"/id",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.immutable !== undefined){if(true !== data.immutable){const err8 = {instancePath:instancePath+"/immutable",schemaPath:"#/properties/immutable/const",keyword:"const",params:{allowedValue: true},message:"must be equal to constant"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}if(data.revisionId !== undefined){let data3 = data.revisionId;if(typeof data3 === "string"){if(func1(data3) > 240){const err9 = {instancePath:instancePath+"/revisionId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(func1(data3) < 1){const err10 = {instancePath:instancePath+"/revisionId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(!pattern4.test(data3)){const err11 = {instancePath:instancePath+"/revisionId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}else {const err12 = {instancePath:instancePath+"/revisionId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.decisionId !== undefined){let data4 = data.decisionId;if(typeof data4 === "string"){if(func1(data4) > 240){const err13 = {instancePath:instancePath+"/decisionId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(func1(data4) < 1){const err14 = {instancePath:instancePath+"/decisionId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(!pattern4.test(data4)){const err15 = {instancePath:instancePath+"/decisionId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}else {const err16 = {instancePath:instancePath+"/decisionId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}}else {const err17 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}validate98.errors = vErrors;return errors === 0;}validate98.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};function validate92(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){/*# sourceURL="https://paperclip.dev/schemas/prp/v1/semantic-tool.schema.json" */;let vErrors = null;let errors = 0;const evaluated0 = validate92.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}const _errs2 = errors;let valid1 = true;const _errs3 = errors;if(data && typeof data == "object" && !Array.isArray(data)){let missing0;if((data.phase === undefined) && (missing0 = "phase")){const err0 = {};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}else {if(data.phase !== undefined){let data0 = data.phase;if(!((data0 === "result") || (data0 === "reconciled"))){const err1 = {};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}}}}var _valid0 = _errs3 === errors;errors = _errs2;if(vErrors !== null){if(_errs2){vErrors.length = _errs2;}else {vErrors = null;}}if(_valid0){const _errs5 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.outcome === undefined){const err2 = {instancePath,schemaPath:"#/allOf/0/then/required",keyword:"required",params:{missingProperty: "outcome"},message:"must have required property '"+"outcome"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.code === undefined){const err3 = {instancePath,schemaPath:"#/allOf/0/then/required",keyword:"required",params:{missingProperty: "code"},message:"must have required property '"+"code"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.retryable === undefined){const err4 = {instancePath,schemaPath:"#/allOf/0/then/required",keyword:"required",params:{missingProperty: "retryable"},message:"must have required property '"+"retryable"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.authorizationBoundary === undefined){const err5 = {instancePath,schemaPath:"#/allOf/0/then/required",keyword:"required",params:{missingProperty: "authorizationBoundary"},message:"must have required property '"+"authorizationBoundary"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.operationReceiptId === undefined){const err6 = {instancePath,schemaPath:"#/allOf/0/then/required",keyword:"required",params:{missingProperty: "operationReceiptId"},message:"must have required property '"+"operationReceiptId"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}var _valid0 = _errs5 === errors;valid1 = _valid0;}if(!valid1){const err7 = {instancePath,schemaPath:"#/allOf/0/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err8 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(data.schemaVersion === undefined){const err9 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schemaVersion"},message:"must have required property '"+"schemaVersion"+"'"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(data.phase === undefined){const err10 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "phase"},message:"must have required property '"+"phase"+"'"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(data.operationId === undefined){const err11 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "operationId"},message:"must have required property '"+"operationId"+"'"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(data.callId === undefined){const err12 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "callId"},message:"must have required property '"+"callId"+"'"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(data.correlation === undefined){const err13 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "correlation"},message:"must have required property '"+"correlation"+"'"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(data.idempotencyKey === undefined){const err14 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "idempotencyKey"},message:"must have required property '"+"idempotencyKey"+"'"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(data.content === undefined){const err15 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "content"},message:"must have required property '"+"content"+"'"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}if(data.schema !== undefined){if("paperclip.prp.semantic_tool.v1" !== data.schema){const err16 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.semantic_tool.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}if(data.schemaVersion !== undefined){if(1 !== data.schemaVersion){const err17 = {instancePath:instancePath+"/schemaVersion",schemaPath:"#/properties/schemaVersion/const",keyword:"const",params:{allowedValue: 1},message:"must be equal to constant"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}if(data.phase !== undefined){let data3 = data.phase;if(!(((data3 === "input") || (data3 === "result")) || (data3 === "reconciled"))){const err18 = {instancePath:instancePath+"/phase",schemaPath:"#/properties/phase/enum",keyword:"enum",params:{allowedValues: schema176.properties.phase.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}if(data.operationId !== undefined){let data4 = data.operationId;if(typeof data4 === "string"){if(func1(data4) > 240){const err19 = {instancePath:instancePath+"/operationId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}if(func1(data4) < 1){const err20 = {instancePath:instancePath+"/operationId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}if(!pattern4.test(data4)){const err21 = {instancePath:instancePath+"/operationId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}}else {const err22 = {instancePath:instancePath+"/operationId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}if(data.callId !== undefined){let data5 = data.callId;if(typeof data5 === "string"){if(func1(data5) > 240){const err23 = {instancePath:instancePath+"/callId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}if(func1(data5) < 1){const err24 = {instancePath:instancePath+"/callId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}if(!pattern4.test(data5)){const err25 = {instancePath:instancePath+"/callId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}else {const err26 = {instancePath:instancePath+"/callId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}}if(data.correlation !== undefined){let data6 = data.correlation;if(data6 && typeof data6 == "object" && !Array.isArray(data6)){if(data6.runId === undefined){const err27 = {instancePath:instancePath+"/correlation",schemaPath:"#/properties/correlation/required",keyword:"required",params:{missingProperty: "runId"},message:"must have required property '"+"runId"+"'"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if(data6.normalizedSessionId === undefined){const err28 = {instancePath:instancePath+"/correlation",schemaPath:"#/properties/correlation/required",keyword:"required",params:{missingProperty: "normalizedSessionId"},message:"must have required property '"+"normalizedSessionId"+"'"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}if(data6.turnId === undefined){const err29 = {instancePath:instancePath+"/correlation",schemaPath:"#/properties/correlation/required",keyword:"required",params:{missingProperty: "turnId"},message:"must have required property '"+"turnId"+"'"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}if(data6.itemId === undefined){const err30 = {instancePath:instancePath+"/correlation",schemaPath:"#/properties/correlation/required",keyword:"required",params:{missingProperty: "itemId"},message:"must have required property '"+"itemId"+"'"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}if(data6.runId !== undefined){let data7 = data6.runId;if(typeof data7 === "string"){if(func1(data7) > 240){const err31 = {instancePath:instancePath+"/correlation/runId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}if(func1(data7) < 1){const err32 = {instancePath:instancePath+"/correlation/runId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}if(!pattern4.test(data7)){const err33 = {instancePath:instancePath+"/correlation/runId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}}else {const err34 = {instancePath:instancePath+"/correlation/runId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}}if(data6.normalizedSessionId !== undefined){let data8 = data6.normalizedSessionId;if(typeof data8 === "string"){if(func1(data8) > 240){const err35 = {instancePath:instancePath+"/correlation/normalizedSessionId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}if(func1(data8) < 1){const err36 = {instancePath:instancePath+"/correlation/normalizedSessionId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}if(!pattern4.test(data8)){const err37 = {instancePath:instancePath+"/correlation/normalizedSessionId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}}else {const err38 = {instancePath:instancePath+"/correlation/normalizedSessionId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}}if(data6.turnId !== undefined){let data9 = data6.turnId;if(typeof data9 === "string"){if(func1(data9) > 240){const err39 = {instancePath:instancePath+"/correlation/turnId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}if(func1(data9) < 1){const err40 = {instancePath:instancePath+"/correlation/turnId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}if(!pattern4.test(data9)){const err41 = {instancePath:instancePath+"/correlation/turnId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}}else {const err42 = {instancePath:instancePath+"/correlation/turnId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}}if(data6.itemId !== undefined){let data10 = data6.itemId;if(typeof data10 === "string"){if(func1(data10) > 240){const err43 = {instancePath:instancePath+"/correlation/itemId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}if(func1(data10) < 1){const err44 = {instancePath:instancePath+"/correlation/itemId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err44];}else {vErrors.push(err44);}errors++;}if(!pattern4.test(data10)){const err45 = {instancePath:instancePath+"/correlation/itemId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err45];}else {vErrors.push(err45);}errors++;}}else {const err46 = {instancePath:instancePath+"/correlation/itemId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err46];}else {vErrors.push(err46);}errors++;}}if(data6.requestId !== undefined){let data11 = data6.requestId;if(typeof data11 === "string"){if(func1(data11) > 240){const err47 = {instancePath:instancePath+"/correlation/requestId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err47];}else {vErrors.push(err47);}errors++;}if(func1(data11) < 1){const err48 = {instancePath:instancePath+"/correlation/requestId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err48];}else {vErrors.push(err48);}errors++;}if(!pattern4.test(data11)){const err49 = {instancePath:instancePath+"/correlation/requestId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err49];}else {vErrors.push(err49);}errors++;}}else {const err50 = {instancePath:instancePath+"/correlation/requestId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err50];}else {vErrors.push(err50);}errors++;}}}else {const err51 = {instancePath:instancePath+"/correlation",schemaPath:"#/properties/correlation/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err51];}else {vErrors.push(err51);}errors++;}}if(data.idempotencyKey !== undefined){let data12 = data.idempotencyKey;if((typeof data12 !== "string") && (data12 !== null)){const err52 = {instancePath:instancePath+"/idempotencyKey",schemaPath:"#/properties/idempotencyKey/type",keyword:"type",params:{type: schema176.properties.idempotencyKey.type},message:"must be string,null"};if(vErrors === null){vErrors = [err52];}else {vErrors.push(err52);}errors++;}if(typeof data12 === "string"){if(func1(data12) > 240){const err53 = {instancePath:instancePath+"/idempotencyKey",schemaPath:"#/properties/idempotencyKey/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err53];}else {vErrors.push(err53);}errors++;}if(func1(data12) < 1){const err54 = {instancePath:instancePath+"/idempotencyKey",schemaPath:"#/properties/idempotencyKey/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err54];}else {vErrors.push(err54);}errors++;}}}if(data.content !== undefined){if(!(validate93(data.content, {instancePath:instancePath+"/content",parentData:data,parentDataProperty:"content",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate93.errors : vErrors.concat(validate93.errors);errors = vErrors.length;}}if(data.outcome !== undefined){let data14 = data.outcome;if(!((((((data14 === "succeeded") || (data14 === "denied")) || (data14 === "conflict")) || (data14 === "duplicate")) || (data14 === "unavailable")) || (data14 === "failed"))){const err55 = {instancePath:instancePath+"/outcome",schemaPath:"#/properties/outcome/enum",keyword:"enum",params:{allowedValues: schema176.properties.outcome.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err55];}else {vErrors.push(err55);}errors++;}}if(data.code !== undefined){let data15 = data.code;if(typeof data15 === "string"){if(func1(data15) > 160){const err56 = {instancePath:instancePath+"/code",schemaPath:"#/$defs/stableCode/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err56];}else {vErrors.push(err56);}errors++;}if(func1(data15) < 1){const err57 = {instancePath:instancePath+"/code",schemaPath:"#/$defs/stableCode/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err57];}else {vErrors.push(err57);}errors++;}if(!pattern14.test(data15)){const err58 = {instancePath:instancePath+"/code",schemaPath:"#/$defs/stableCode/pattern",keyword:"pattern",params:{pattern: "^[a-z][a-z0-9_.:-]*$"},message:"must match pattern \""+"^[a-z][a-z0-9_.:-]*$"+"\""};if(vErrors === null){vErrors = [err58];}else {vErrors.push(err58);}errors++;}}else {const err59 = {instancePath:instancePath+"/code",schemaPath:"#/$defs/stableCode/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err59];}else {vErrors.push(err59);}errors++;}}if(data.retryable !== undefined){if(typeof data.retryable !== "boolean"){const err60 = {instancePath:instancePath+"/retryable",schemaPath:"#/properties/retryable/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err60];}else {vErrors.push(err60);}errors++;}}if(data.authorizationBoundary !== undefined){let data17 = data.authorizationBoundary;if(!(((((((data17 === "company") || (data17 === "actor")) || (data17 === "active_task")) || (data17 === "grant")) || (data17 === "governed_action")) || (data17 === "lock")) || (data17 === "revision"))){const err61 = {instancePath:instancePath+"/authorizationBoundary",schemaPath:"#/properties/authorizationBoundary/enum",keyword:"enum",params:{allowedValues: schema176.properties.authorizationBoundary.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err61];}else {vErrors.push(err61);}errors++;}}if(data.operationReceiptId !== undefined){let data18 = data.operationReceiptId;if(typeof data18 === "string"){if(func1(data18) > 240){const err62 = {instancePath:instancePath+"/operationReceiptId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err62];}else {vErrors.push(err62);}errors++;}if(func1(data18) < 1){const err63 = {instancePath:instancePath+"/operationReceiptId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err63];}else {vErrors.push(err63);}errors++;}if(!pattern4.test(data18)){const err64 = {instancePath:instancePath+"/operationReceiptId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err64];}else {vErrors.push(err64);}errors++;}}else {const err65 = {instancePath:instancePath+"/operationReceiptId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err65];}else {vErrors.push(err65);}errors++;}}if(data.auditReceiptId !== undefined){let data19 = data.auditReceiptId;if(typeof data19 === "string"){if(func1(data19) > 240){const err66 = {instancePath:instancePath+"/auditReceiptId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err66];}else {vErrors.push(err66);}errors++;}if(func1(data19) < 1){const err67 = {instancePath:instancePath+"/auditReceiptId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err67];}else {vErrors.push(err67);}errors++;}if(!pattern4.test(data19)){const err68 = {instancePath:instancePath+"/auditReceiptId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err68];}else {vErrors.push(err68);}errors++;}}else {const err69 = {instancePath:instancePath+"/auditReceiptId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err69];}else {vErrors.push(err69);}errors++;}}if(data.currentRevision !== undefined){let data20 = data.currentRevision;const _errs51 = errors;let valid15 = false;let passing0 = null;const _errs52 = errors;if(!(((typeof data20 == "number") && (!(data20 % 1) && !isNaN(data20))) && (isFinite(data20)))){const err70 = {instancePath:instancePath+"/currentRevision",schemaPath:"#/properties/currentRevision/oneOf/0/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err70];}else {vErrors.push(err70);}errors++;}if((typeof data20 == "number") && (isFinite(data20))){if(data20 < 0 || isNaN(data20)){const err71 = {instancePath:instancePath+"/currentRevision",schemaPath:"#/properties/currentRevision/oneOf/0/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err71];}else {vErrors.push(err71);}errors++;}}var _valid1 = _errs52 === errors;if(_valid1){valid15 = true;passing0 = 0;}const _errs54 = errors;if(typeof data20 === "string"){if(func1(data20) > 240){const err72 = {instancePath:instancePath+"/currentRevision",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err72];}else {vErrors.push(err72);}errors++;}if(func1(data20) < 1){const err73 = {instancePath:instancePath+"/currentRevision",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err73];}else {vErrors.push(err73);}errors++;}if(!pattern4.test(data20)){const err74 = {instancePath:instancePath+"/currentRevision",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err74];}else {vErrors.push(err74);}errors++;}}else {const err75 = {instancePath:instancePath+"/currentRevision",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err75];}else {vErrors.push(err75);}errors++;}var _valid1 = _errs54 === errors;if(_valid1 && valid15){valid15 = false;passing0 = [passing0, 1];}else {if(_valid1){valid15 = true;passing0 = 1;}}if(!valid15){const err76 = {instancePath:instancePath+"/currentRevision",schemaPath:"#/properties/currentRevision/oneOf",keyword:"oneOf",params:{passingSchemas: passing0},message:"must match exactly one schema in oneOf"};if(vErrors === null){vErrors = [err76];}else {vErrors.push(err76);}errors++;}else {errors = _errs51;if(vErrors !== null){if(_errs51){vErrors.length = _errs51;}else {vErrors = null;}}}}if(data.duplicateOfReceiptId !== undefined){let data21 = data.duplicateOfReceiptId;if(typeof data21 === "string"){if(func1(data21) > 240){const err77 = {instancePath:instancePath+"/duplicateOfReceiptId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err77];}else {vErrors.push(err77);}errors++;}if(func1(data21) < 1){const err78 = {instancePath:instancePath+"/duplicateOfReceiptId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err78];}else {vErrors.push(err78);}errors++;}if(!pattern4.test(data21)){const err79 = {instancePath:instancePath+"/duplicateOfReceiptId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err79];}else {vErrors.push(err79);}errors++;}}else {const err80 = {instancePath:instancePath+"/duplicateOfReceiptId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err80];}else {vErrors.push(err80);}errors++;}}if(data.artifactRefs !== undefined){let data22 = data.artifactRefs;if(Array.isArray(data22)){const len0 = data22.length;for(let i0=0; i0 1){outer0:for(;i1--;){for(j0 = i1; j0--;){if(func0(data22[i1], data22[j0])){const err83 = {instancePath:instancePath+"/artifactRefs",schemaPath:"#/properties/artifactRefs/uniqueItems",keyword:"uniqueItems",params:{i: i1, j: j0},message:"must NOT have duplicate items (items ## "+j0+" and "+i1+" are identical)"};if(vErrors === null){vErrors = [err83];}else {vErrors.push(err83);}errors++;break outer0;}}}}}else {const err84 = {instancePath:instancePath+"/artifactRefs",schemaPath:"#/properties/artifactRefs/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err84];}else {vErrors.push(err84);}errors++;}}if(data.targets !== undefined){let data25 = data.targets;if(Array.isArray(data25)){const len1 = data25.length;for(let i2=0; i2 1){outer1:for(;i3--;){for(j1 = i3; j1--;){if(func0(data25[i3], data25[j1])){const err85 = {instancePath:instancePath+"/targets",schemaPath:"#/properties/targets/uniqueItems",keyword:"uniqueItems",params:{i: i3, j: j1},message:"must NOT have duplicate items (items ## "+j1+" and "+i3+" are identical)"};if(vErrors === null){vErrors = [err85];}else {vErrors.push(err85);}errors++;break outer1;}}}}}else {const err86 = {instancePath:instancePath+"/targets",schemaPath:"#/properties/targets/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err86];}else {vErrors.push(err86);}errors++;}}if(data.causalRefs !== undefined){let data27 = data.causalRefs;if(Array.isArray(data27)){const len2 = data27.length;for(let i4=0; i4 1){outer2:for(;i5--;){for(j2 = i5; j2--;){if(func0(data27[i5], data27[j2])){const err87 = {instancePath:instancePath+"/causalRefs",schemaPath:"#/properties/causalRefs/uniqueItems",keyword:"uniqueItems",params:{i: i5, j: j2},message:"must NOT have duplicate items (items ## "+j2+" and "+i5+" are identical)"};if(vErrors === null){vErrors = [err87];}else {vErrors.push(err87);}errors++;break outer2;}}}}}else {const err88 = {instancePath:instancePath+"/causalRefs",schemaPath:"#/properties/causalRefs/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err88];}else {vErrors.push(err88);}errors++;}}}else {const err89 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err89];}else {vErrors.push(err89);}errors++;}validate92.errors = vErrors;return errors === 0;}validate92.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema196 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/terminal.schema.json","title":"PRP run terminal state","type":"object","required":["schema","turnTerminalState","runTerminalState","reportedWorkDisposition"],"properties":{"schema":{"const":"paperclip.prp.terminal.v1"},"turnTerminalState":{"enum":["completed","failed","interrupted","cancelled"]},"runTerminalState":{"enum":["succeeded","failed","cancelled"]},"reportedWorkDisposition":{"enum":["done","blocked","needs_review","yielded"]},"workAssessmentId":{"type":"string","minLength":1},"statusDecisionId":{"type":"string","minLength":1},"stopReason":{"$ref":"https://paperclip.dev/schemas/prp/v1/stop-reason.schema.json"}},"additionalProperties":true};const schema197 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/stop-reason.schema.json","title":"PRP terminal stop reason receipt","type":"object","required":["schema","schemaVersion","receiptId","kind","code","retryable","decisionId","limitClass","aggregate"],"properties":{"schema":{"const":"paperclip.prp.stop_reason.v1"},"schemaVersion":{"const":1},"receiptId":{"$ref":"#/$defs/stableId"},"kind":{"enum":["budget","cost"]},"code":{"type":"string","minLength":1,"maxLength":160,"pattern":"^[a-z][a-z0-9_.:-]*$"},"retryable":{"type":"boolean"},"decisionId":{"$ref":"#/$defs/stableId"},"limitClass":{"enum":["company_monthly","actor_monthly","project_monthly","run_cost","provider_quota"]},"aggregate":{"type":"object","required":["unit","observed","limit","window"],"properties":{"unit":{"enum":["cents","usd_micros","tokens"]},"observed":{"type":"integer","minimum":0},"limit":{"type":"integer","minimum":0},"window":{"enum":["run","monthly_utc","provider_window"]}},"additionalProperties":true},"relatedReceiptIds":{"type":"array","items":{"$ref":"#/$defs/stableId"},"uniqueItems":true}},"additionalProperties":true,"$defs":{"stableId":{"type":"string","minLength":1,"maxLength":240,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]*$"}}};const schema198 = {"type":"string","minLength":1,"maxLength":240,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]*$"};function validate105(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){/*# sourceURL="https://paperclip.dev/schemas/prp/v1/stop-reason.schema.json" */;let vErrors = null;let errors = 0;const evaluated0 = validate105.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.schemaVersion === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schemaVersion"},message:"must have required property '"+"schemaVersion"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.receiptId === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "receiptId"},message:"must have required property '"+"receiptId"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.kind === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "kind"},message:"must have required property '"+"kind"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.code === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "code"},message:"must have required property '"+"code"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.retryable === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "retryable"},message:"must have required property '"+"retryable"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.decisionId === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "decisionId"},message:"must have required property '"+"decisionId"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(data.limitClass === undefined){const err7 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "limitClass"},message:"must have required property '"+"limitClass"+"'"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(data.aggregate === undefined){const err8 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "aggregate"},message:"must have required property '"+"aggregate"+"'"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(data.schema !== undefined){if("paperclip.prp.stop_reason.v1" !== data.schema){const err9 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.stop_reason.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}if(data.schemaVersion !== undefined){if(1 !== data.schemaVersion){const err10 = {instancePath:instancePath+"/schemaVersion",schemaPath:"#/properties/schemaVersion/const",keyword:"const",params:{allowedValue: 1},message:"must be equal to constant"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.receiptId !== undefined){let data2 = data.receiptId;if(typeof data2 === "string"){if(func1(data2) > 240){const err11 = {instancePath:instancePath+"/receiptId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(func1(data2) < 1){const err12 = {instancePath:instancePath+"/receiptId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(!pattern4.test(data2)){const err13 = {instancePath:instancePath+"/receiptId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}else {const err14 = {instancePath:instancePath+"/receiptId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}if(data.kind !== undefined){let data3 = data.kind;if(!((data3 === "budget") || (data3 === "cost"))){const err15 = {instancePath:instancePath+"/kind",schemaPath:"#/properties/kind/enum",keyword:"enum",params:{allowedValues: schema197.properties.kind.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}if(data.code !== undefined){let data4 = data.code;if(typeof data4 === "string"){if(func1(data4) > 160){const err16 = {instancePath:instancePath+"/code",schemaPath:"#/properties/code/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}if(func1(data4) < 1){const err17 = {instancePath:instancePath+"/code",schemaPath:"#/properties/code/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}if(!pattern14.test(data4)){const err18 = {instancePath:instancePath+"/code",schemaPath:"#/properties/code/pattern",keyword:"pattern",params:{pattern: "^[a-z][a-z0-9_.:-]*$"},message:"must match pattern \""+"^[a-z][a-z0-9_.:-]*$"+"\""};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}else {const err19 = {instancePath:instancePath+"/code",schemaPath:"#/properties/code/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}if(data.retryable !== undefined){if(typeof data.retryable !== "boolean"){const err20 = {instancePath:instancePath+"/retryable",schemaPath:"#/properties/retryable/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}if(data.decisionId !== undefined){let data6 = data.decisionId;if(typeof data6 === "string"){if(func1(data6) > 240){const err21 = {instancePath:instancePath+"/decisionId",schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(func1(data6) < 1){const err22 = {instancePath:instancePath+"/decisionId",schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}if(!pattern4.test(data6)){const err23 = {instancePath:instancePath+"/decisionId",schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}else {const err24 = {instancePath:instancePath+"/decisionId",schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}if(data.limitClass !== undefined){let data7 = data.limitClass;if(!(((((data7 === "company_monthly") || (data7 === "actor_monthly")) || (data7 === "project_monthly")) || (data7 === "run_cost")) || (data7 === "provider_quota"))){const err25 = {instancePath:instancePath+"/limitClass",schemaPath:"#/properties/limitClass/enum",keyword:"enum",params:{allowedValues: schema197.properties.limitClass.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}if(data.aggregate !== undefined){let data8 = data.aggregate;if(data8 && typeof data8 == "object" && !Array.isArray(data8)){if(data8.unit === undefined){const err26 = {instancePath:instancePath+"/aggregate",schemaPath:"#/properties/aggregate/required",keyword:"required",params:{missingProperty: "unit"},message:"must have required property '"+"unit"+"'"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}if(data8.observed === undefined){const err27 = {instancePath:instancePath+"/aggregate",schemaPath:"#/properties/aggregate/required",keyword:"required",params:{missingProperty: "observed"},message:"must have required property '"+"observed"+"'"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if(data8.limit === undefined){const err28 = {instancePath:instancePath+"/aggregate",schemaPath:"#/properties/aggregate/required",keyword:"required",params:{missingProperty: "limit"},message:"must have required property '"+"limit"+"'"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}if(data8.window === undefined){const err29 = {instancePath:instancePath+"/aggregate",schemaPath:"#/properties/aggregate/required",keyword:"required",params:{missingProperty: "window"},message:"must have required property '"+"window"+"'"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}if(data8.unit !== undefined){let data9 = data8.unit;if(!(((data9 === "cents") || (data9 === "usd_micros")) || (data9 === "tokens"))){const err30 = {instancePath:instancePath+"/aggregate/unit",schemaPath:"#/properties/aggregate/properties/unit/enum",keyword:"enum",params:{allowedValues: schema197.properties.aggregate.properties.unit.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}}if(data8.observed !== undefined){let data10 = data8.observed;if(!(((typeof data10 == "number") && (!(data10 % 1) && !isNaN(data10))) && (isFinite(data10)))){const err31 = {instancePath:instancePath+"/aggregate/observed",schemaPath:"#/properties/aggregate/properties/observed/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}if((typeof data10 == "number") && (isFinite(data10))){if(data10 < 0 || isNaN(data10)){const err32 = {instancePath:instancePath+"/aggregate/observed",schemaPath:"#/properties/aggregate/properties/observed/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}}}if(data8.limit !== undefined){let data11 = data8.limit;if(!(((typeof data11 == "number") && (!(data11 % 1) && !isNaN(data11))) && (isFinite(data11)))){const err33 = {instancePath:instancePath+"/aggregate/limit",schemaPath:"#/properties/aggregate/properties/limit/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}if((typeof data11 == "number") && (isFinite(data11))){if(data11 < 0 || isNaN(data11)){const err34 = {instancePath:instancePath+"/aggregate/limit",schemaPath:"#/properties/aggregate/properties/limit/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}}}if(data8.window !== undefined){let data12 = data8.window;if(!(((data12 === "run") || (data12 === "monthly_utc")) || (data12 === "provider_window"))){const err35 = {instancePath:instancePath+"/aggregate/window",schemaPath:"#/properties/aggregate/properties/window/enum",keyword:"enum",params:{allowedValues: schema197.properties.aggregate.properties.window.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}}}else {const err36 = {instancePath:instancePath+"/aggregate",schemaPath:"#/properties/aggregate/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}}if(data.relatedReceiptIds !== undefined){let data13 = data.relatedReceiptIds;if(Array.isArray(data13)){const len0 = data13.length;for(let i0=0; i0 240){const err37 = {instancePath:instancePath+"/relatedReceiptIds/" + i0,schemaPath:"#/$defs/stableId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}if(func1(data14) < 1){const err38 = {instancePath:instancePath+"/relatedReceiptIds/" + i0,schemaPath:"#/$defs/stableId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}if(!pattern4.test(data14)){const err39 = {instancePath:instancePath+"/relatedReceiptIds/" + i0,schemaPath:"#/$defs/stableId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}}else {const err40 = {instancePath:instancePath+"/relatedReceiptIds/" + i0,schemaPath:"#/$defs/stableId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}}let i1 = data13.length;let j0;if(i1 > 1){outer0:for(;i1--;){for(j0 = i1; j0--;){if(func0(data13[i1], data13[j0])){const err41 = {instancePath:instancePath+"/relatedReceiptIds",schemaPath:"#/properties/relatedReceiptIds/uniqueItems",keyword:"uniqueItems",params:{i: i1, j: j0},message:"must NOT have duplicate items (items ## "+j0+" and "+i1+" are identical)"};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;break outer0;}}}}}else {const err42 = {instancePath:instancePath+"/relatedReceiptIds",schemaPath:"#/properties/relatedReceiptIds/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}}}else {const err43 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}validate105.errors = vErrors;return errors === 0;}validate105.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};function validate104(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){/*# sourceURL="https://paperclip.dev/schemas/prp/v1/terminal.schema.json" */;let vErrors = null;let errors = 0;const evaluated0 = validate104.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.turnTerminalState === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "turnTerminalState"},message:"must have required property '"+"turnTerminalState"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.runTerminalState === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "runTerminalState"},message:"must have required property '"+"runTerminalState"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.reportedWorkDisposition === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "reportedWorkDisposition"},message:"must have required property '"+"reportedWorkDisposition"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.schema !== undefined){if("paperclip.prp.terminal.v1" !== data.schema){const err4 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.terminal.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}}if(data.turnTerminalState !== undefined){let data1 = data.turnTerminalState;if(!((((data1 === "completed") || (data1 === "failed")) || (data1 === "interrupted")) || (data1 === "cancelled"))){const err5 = {instancePath:instancePath+"/turnTerminalState",schemaPath:"#/properties/turnTerminalState/enum",keyword:"enum",params:{allowedValues: schema196.properties.turnTerminalState.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}if(data.runTerminalState !== undefined){let data2 = data.runTerminalState;if(!(((data2 === "succeeded") || (data2 === "failed")) || (data2 === "cancelled"))){const err6 = {instancePath:instancePath+"/runTerminalState",schemaPath:"#/properties/runTerminalState/enum",keyword:"enum",params:{allowedValues: schema196.properties.runTerminalState.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}if(data.reportedWorkDisposition !== undefined){let data3 = data.reportedWorkDisposition;if(!((((data3 === "done") || (data3 === "blocked")) || (data3 === "needs_review")) || (data3 === "yielded"))){const err7 = {instancePath:instancePath+"/reportedWorkDisposition",schemaPath:"#/properties/reportedWorkDisposition/enum",keyword:"enum",params:{allowedValues: schema196.properties.reportedWorkDisposition.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.workAssessmentId !== undefined){let data4 = data.workAssessmentId;if(typeof data4 === "string"){if(func1(data4) < 1){const err8 = {instancePath:instancePath+"/workAssessmentId",schemaPath:"#/properties/workAssessmentId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}else {const err9 = {instancePath:instancePath+"/workAssessmentId",schemaPath:"#/properties/workAssessmentId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}if(data.statusDecisionId !== undefined){let data5 = data.statusDecisionId;if(typeof data5 === "string"){if(func1(data5) < 1){const err10 = {instancePath:instancePath+"/statusDecisionId",schemaPath:"#/properties/statusDecisionId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}else {const err11 = {instancePath:instancePath+"/statusDecisionId",schemaPath:"#/properties/statusDecisionId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.stopReason !== undefined){if(!(validate105(data.stopReason, {instancePath:instancePath+"/stopReason",parentData:data,parentDataProperty:"stopReason",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate105.errors : vErrors.concat(validate105.errors);errors = vErrors.length;}}}else {const err12 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}validate104.errors = vErrors;return errors === 0;}validate104.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const pattern77 = new RegExp("^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$", "u");const pattern107 = new RegExp("^(0|[1-9][0-9]{0,6})$", "u");const pattern15 = new RegExp("^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$", "u");const formats0 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function validate26(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){/*# sourceURL="https://paperclip.dev/schemas/prp/v1/event.schema.json" */;let vErrors = null;let errors = 0;const evaluated0 = validate26.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}const _errs2 = errors;let valid1 = true;const _errs3 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("plan.updated" !== data.eventType){const err0 = {};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}}}var _valid0 = _errs3 === errors;errors = _errs2;if(vErrors !== null){if(_errs2){vErrors.length = _errs2;}else {vErrors = null;}}if(_valid0){const _errs5 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate60(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate60.errors : vErrors.concat(validate60.errors);errors = vErrors.length;}}}var _valid0 = _errs5 === errors;valid1 = _valid0;if(valid1){var props0 = {};props0.payload = true;props0.eventType = true;}}if(!valid1){const err1 = {instancePath,schemaPath:"#/allOf/0/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}const _errs8 = errors;let valid4 = true;const _errs9 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data2 = data.eventType;if(!(((data2 === "tool.execution.started") || (data2 === "tool.execution.progressed")) || (data2 === "tool.execution.completed"))){const err2 = {};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}}}var _valid1 = _errs9 === errors;errors = _errs8;if(vErrors !== null){if(_errs8){vErrors.length = _errs8;}else {vErrors = null;}}if(_valid1){const _errs11 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate62(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate62.errors : vErrors.concat(validate62.errors);errors = vErrors.length;}}}var _valid1 = _errs11 === errors;valid4 = _valid1;if(valid4){var props1 = {};props1.payload = true;props1.eventType = true;}}if(!valid4){const err3 = {instancePath,schemaPath:"#/allOf/1/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(props0 !== true && props1 !== undefined){if(props1 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props1);}}const _errs14 = errors;let valid7 = true;const _errs15 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data4 = data.eventType;if(!(((data4 === "research.started") || (data4 === "research.progressed")) || (data4 === "research.completed"))){const err4 = {};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}}}var _valid2 = _errs15 === errors;errors = _errs14;if(vErrors !== null){if(_errs14){vErrors.length = _errs14;}else {vErrors = null;}}if(_valid2){const _errs17 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate64(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate64.errors : vErrors.concat(validate64.errors);errors = vErrors.length;}}}var _valid2 = _errs17 === errors;valid7 = _valid2;if(valid7){var props2 = {};props2.payload = true;props2.eventType = true;}}if(!valid7){const err5 = {instancePath,schemaPath:"#/allOf/2/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(props0 !== true && props2 !== undefined){if(props2 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props2);}}const _errs20 = errors;let valid10 = true;const _errs21 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data6 = data.eventType;if(!(((data6 === "delegation.started") || (data6 === "delegation.updated")) || (data6 === "delegation.completed"))){const err6 = {};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}}var _valid3 = _errs21 === errors;errors = _errs20;if(vErrors !== null){if(_errs20){vErrors.length = _errs20;}else {vErrors = null;}}if(_valid3){const _errs23 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate66(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate66.errors : vErrors.concat(validate66.errors);errors = vErrors.length;}}}var _valid3 = _errs23 === errors;valid10 = _valid3;if(valid10){var props3 = {};props3.payload = true;props3.eventType = true;}}if(!valid10){const err7 = {instancePath,schemaPath:"#/allOf/3/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(props0 !== true && props3 !== undefined){if(props3 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props3);}}const _errs26 = errors;let valid13 = true;const _errs27 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("model.route.changed" !== data.eventType){const err8 = {};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}}var _valid4 = _errs27 === errors;errors = _errs26;if(vErrors !== null){if(_errs26){vErrors.length = _errs26;}else {vErrors = null;}}if(_valid4){const _errs29 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate68(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate68.errors : vErrors.concat(validate68.errors);errors = vErrors.length;}}}var _valid4 = _errs29 === errors;valid13 = _valid4;if(valid13){var props4 = {};props4.payload = true;props4.eventType = true;}}if(!valid13){const err9 = {instancePath,schemaPath:"#/allOf/4/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(props0 !== true && props4 !== undefined){if(props4 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props4);}}const _errs32 = errors;let valid16 = true;const _errs33 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("model.verification.updated" !== data.eventType){const err10 = {};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}}var _valid5 = _errs33 === errors;errors = _errs32;if(vErrors !== null){if(_errs32){vErrors.length = _errs32;}else {vErrors = null;}}if(_valid5){const _errs35 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate70(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate70.errors : vErrors.concat(validate70.errors);errors = vErrors.length;}}}var _valid5 = _errs35 === errors;valid16 = _valid5;if(valid16){var props5 = {};props5.payload = true;props5.eventType = true;}}if(!valid16){const err11 = {instancePath,schemaPath:"#/allOf/5/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(props0 !== true && props5 !== undefined){if(props5 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props5);}}const _errs38 = errors;let valid19 = true;const _errs39 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("context.compacted" !== data.eventType){const err12 = {};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}}var _valid6 = _errs39 === errors;errors = _errs38;if(vErrors !== null){if(_errs38){vErrors.length = _errs38;}else {vErrors = null;}}if(_valid6){const _errs41 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate72(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate72.errors : vErrors.concat(validate72.errors);errors = vErrors.length;}}}var _valid6 = _errs41 === errors;valid19 = _valid6;if(valid19){var props6 = {};props6.payload = true;props6.eventType = true;}}if(!valid19){const err13 = {instancePath,schemaPath:"#/allOf/6/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(props0 !== true && props6 !== undefined){if(props6 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props6);}}const _errs44 = errors;let valid22 = true;const _errs45 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("artifact.viewed" !== data.eventType){const err14 = {};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}}var _valid7 = _errs45 === errors;errors = _errs44;if(vErrors !== null){if(_errs44){vErrors.length = _errs44;}else {vErrors = null;}}if(_valid7){const _errs47 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate74(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate74.errors : vErrors.concat(validate74.errors);errors = vErrors.length;}}}var _valid7 = _errs47 === errors;valid22 = _valid7;if(valid22){var props7 = {};props7.payload = true;props7.eventType = true;}}if(!valid22){const err15 = {instancePath,schemaPath:"#/allOf/7/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}if(props0 !== true && props7 !== undefined){if(props7 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props7);}}const _errs50 = errors;let valid25 = true;const _errs51 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("artifact.generated" !== data.eventType){const err16 = {};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}}var _valid8 = _errs51 === errors;errors = _errs50;if(vErrors !== null){if(_errs50){vErrors.length = _errs50;}else {vErrors = null;}}if(_valid8){const _errs53 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate76(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate76.errors : vErrors.concat(validate76.errors);errors = vErrors.length;}}}var _valid8 = _errs53 === errors;valid25 = _valid8;if(valid25){var props8 = {};props8.payload = true;props8.eventType = true;}}if(!valid25){const err17 = {instancePath,schemaPath:"#/allOf/8/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}if(props0 !== true && props8 !== undefined){if(props8 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props8);}}const _errs56 = errors;let valid28 = true;const _errs57 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("review.mode.changed" !== data.eventType){const err18 = {};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}}var _valid9 = _errs57 === errors;errors = _errs56;if(vErrors !== null){if(_errs56){vErrors.length = _errs56;}else {vErrors = null;}}if(_valid9){const _errs59 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate78(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate78.errors : vErrors.concat(validate78.errors);errors = vErrors.length;}}}var _valid9 = _errs59 === errors;valid28 = _valid9;if(valid28){var props9 = {};props9.payload = true;props9.eventType = true;}}if(!valid28){const err19 = {instancePath,schemaPath:"#/allOf/9/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}if(props0 !== true && props9 !== undefined){if(props9 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props9);}}const _errs62 = errors;let valid31 = true;const _errs63 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data20 = data.eventType;if(!((data20 === "hook.started") || (data20 === "hook.completed"))){const err20 = {};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}}var _valid10 = _errs63 === errors;errors = _errs62;if(vErrors !== null){if(_errs62){vErrors.length = _errs62;}else {vErrors = null;}}if(_valid10){const _errs65 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate80(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate80.errors : vErrors.concat(validate80.errors);errors = vErrors.length;}}}var _valid10 = _errs65 === errors;valid31 = _valid10;if(valid31){var props10 = {};props10.payload = true;props10.eventType = true;}}if(!valid31){const err21 = {instancePath,schemaPath:"#/allOf/10/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(props0 !== true && props10 !== undefined){if(props10 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props10);}}const _errs68 = errors;let valid34 = true;const _errs69 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("memory.citation.referenced" !== data.eventType){const err22 = {};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}}var _valid11 = _errs69 === errors;errors = _errs68;if(vErrors !== null){if(_errs68){vErrors.length = _errs68;}else {vErrors = null;}}if(_valid11){const _errs71 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate82(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate82.errors : vErrors.concat(validate82.errors);errors = vErrors.length;}}}var _valid11 = _errs71 === errors;valid34 = _valid11;if(valid34){var props11 = {};props11.payload = true;props11.eventType = true;}}if(!valid34){const err23 = {instancePath,schemaPath:"#/allOf/11/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}if(props0 !== true && props11 !== undefined){if(props11 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props11);}}const _errs74 = errors;let valid37 = true;const _errs75 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data24 = data.eventType;if(!((data24 === "safety.review.started") || (data24 === "safety.review.completed"))){const err24 = {};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}}var _valid12 = _errs75 === errors;errors = _errs74;if(vErrors !== null){if(_errs74){vErrors.length = _errs74;}else {vErrors = null;}}if(_valid12){const _errs77 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate84(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate84.errors : vErrors.concat(validate84.errors);errors = vErrors.length;}}}var _valid12 = _errs77 === errors;valid37 = _valid12;if(valid37){var props12 = {};props12.payload = true;props12.eventType = true;}}if(!valid37){const err25 = {instancePath,schemaPath:"#/allOf/12/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}if(props0 !== true && props12 !== undefined){if(props12 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props12);}}const _errs80 = errors;let valid40 = true;const _errs81 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("terminal.input.sent" !== data.eventType){const err26 = {};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}}}var _valid13 = _errs81 === errors;errors = _errs80;if(vErrors !== null){if(_errs80){vErrors.length = _errs80;}else {vErrors = null;}}if(_valid13){const _errs83 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate86(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate86.errors : vErrors.concat(validate86.errors);errors = vErrors.length;}}}var _valid13 = _errs83 === errors;valid40 = _valid13;if(valid40){var props13 = {};props13.payload = true;props13.eventType = true;}}if(!valid40){const err27 = {instancePath,schemaPath:"#/allOf/13/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if(props0 !== true && props13 !== undefined){if(props13 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props13);}}const _errs86 = errors;let valid43 = true;const _errs87 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data28 = data.eventType;if(!((data28 === "wait.started") || (data28 === "wait.completed"))){const err28 = {};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}}var _valid14 = _errs87 === errors;errors = _errs86;if(vErrors !== null){if(_errs86){vErrors.length = _errs86;}else {vErrors = null;}}if(_valid14){const _errs89 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate88(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate88.errors : vErrors.concat(validate88.errors);errors = vErrors.length;}}}var _valid14 = _errs89 === errors;valid43 = _valid14;if(valid43){var props14 = {};props14.payload = true;props14.eventType = true;}}if(!valid43){const err29 = {instancePath,schemaPath:"#/allOf/14/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}if(props0 !== true && props14 !== undefined){if(props14 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props14);}}const _errs92 = errors;let valid46 = true;const _errs93 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("provider.notice.recorded" !== data.eventType){const err30 = {};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}}}var _valid15 = _errs93 === errors;errors = _errs92;if(vErrors !== null){if(_errs92){vErrors.length = _errs92;}else {vErrors = null;}}if(_valid15){const _errs95 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate90(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate90.errors : vErrors.concat(validate90.errors);errors = vErrors.length;}}}var _valid15 = _errs95 === errors;valid46 = _valid15;if(valid46){var props15 = {};props15.payload = true;props15.eventType = true;}}if(!valid46){const err31 = {instancePath,schemaPath:"#/allOf/15/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}if(props0 !== true && props15 !== undefined){if(props15 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props15);}}const _errs98 = errors;let valid49 = true;const _errs99 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("workspace.file.referenced" !== data.eventType){const err32 = {};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}}}var _valid16 = _errs99 === errors;errors = _errs98;if(vErrors !== null){if(_errs98){vErrors.length = _errs98;}else {vErrors = null;}}if(_valid16){const _errs101 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data33 = data.payload;if(data33 && typeof data33 == "object" && !Array.isArray(data33)){if(data33.schema === undefined){const err33 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}if(data33.referenceId === undefined){const err34 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "referenceId"},message:"must have required property '"+"referenceId"+"'"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}if(data33.source === undefined){const err35 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "source"},message:"must have required property '"+"source"+"'"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}if(data33.path === undefined){const err36 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "path"},message:"must have required property '"+"path"+"'"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}if(data33.displayName === undefined){const err37 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "displayName"},message:"must have required property '"+"displayName"+"'"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}if(data33.mediaType === undefined){const err38 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "mediaType"},message:"must have required property '"+"mediaType"+"'"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}if(data33.presentation === undefined){const err39 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "presentation"},message:"must have required property '"+"presentation"+"'"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}if(data33.line === undefined){const err40 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "line"},message:"must have required property '"+"line"+"'"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}if(data33.preview === undefined){const err41 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "preview"},message:"must have required property '"+"preview"+"'"};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}if(data33.previewTruncated === undefined){const err42 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "previewTruncated"},message:"must have required property '"+"previewTruncated"+"'"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}if(data33.contentDigest === undefined){const err43 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/required",keyword:"required",params:{missingProperty: "contentDigest"},message:"must have required property '"+"contentDigest"+"'"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}for(const key0 in data33){if(!(func58.call(schema173.properties, key0))){const err44 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err44];}else {vErrors.push(err44);}errors++;}}if(data33.schema !== undefined){if("paperclip.workspace.file_reference.v1" !== data33.schema){const err45 = {instancePath:instancePath+"/payload/schema",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.workspace.file_reference.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err45];}else {vErrors.push(err45);}errors++;}}if(data33.referenceId !== undefined){let data35 = data33.referenceId;if(typeof data35 === "string"){if(func1(data35) > 240){const err46 = {instancePath:instancePath+"/payload/referenceId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/referenceId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err46];}else {vErrors.push(err46);}errors++;}if(func1(data35) < 1){const err47 = {instancePath:instancePath+"/payload/referenceId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/referenceId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err47];}else {vErrors.push(err47);}errors++;}}else {const err48 = {instancePath:instancePath+"/payload/referenceId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/referenceId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err48];}else {vErrors.push(err48);}errors++;}}if(data33.source !== undefined){let data36 = data33.source;if(!((data36 === "harness_reported") || (data36 === "runner_verified"))){const err49 = {instancePath:instancePath+"/payload/source",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/source/enum",keyword:"enum",params:{allowedValues: schema173.properties.source.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err49];}else {vErrors.push(err49);}errors++;}}if(data33.path !== undefined){let data37 = data33.path;if(typeof data37 === "string"){if(func1(data37) > 1024){const err50 = {instancePath:instancePath+"/payload/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/path/maxLength",keyword:"maxLength",params:{limit: 1024},message:"must NOT have more than 1024 characters"};if(vErrors === null){vErrors = [err50];}else {vErrors.push(err50);}errors++;}if(func1(data37) < 1){const err51 = {instancePath:instancePath+"/payload/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/path/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err51];}else {vErrors.push(err51);}errors++;}if(!pattern77.test(data37)){const err52 = {instancePath:instancePath+"/payload/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/path/pattern",keyword:"pattern",params:{pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"},message:"must match pattern \""+"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"+"\""};if(vErrors === null){vErrors = [err52];}else {vErrors.push(err52);}errors++;}}else {const err53 = {instancePath:instancePath+"/payload/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/path/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err53];}else {vErrors.push(err53);}errors++;}}if(data33.displayName !== undefined){let data38 = data33.displayName;if(typeof data38 === "string"){if(func1(data38) > 255){const err54 = {instancePath:instancePath+"/payload/displayName",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/displayName/maxLength",keyword:"maxLength",params:{limit: 255},message:"must NOT have more than 255 characters"};if(vErrors === null){vErrors = [err54];}else {vErrors.push(err54);}errors++;}if(func1(data38) < 1){const err55 = {instancePath:instancePath+"/payload/displayName",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/displayName/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err55];}else {vErrors.push(err55);}errors++;}}else {const err56 = {instancePath:instancePath+"/payload/displayName",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/displayName/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err56];}else {vErrors.push(err56);}errors++;}}if(data33.mediaType !== undefined){let data39 = data33.mediaType;if((typeof data39 !== "string") && (data39 !== null)){const err57 = {instancePath:instancePath+"/payload/mediaType",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/mediaType/type",keyword:"type",params:{type: schema173.properties.mediaType.type},message:"must be string,null"};if(vErrors === null){vErrors = [err57];}else {vErrors.push(err57);}errors++;}if(typeof data39 === "string"){if(func1(data39) > 160){const err58 = {instancePath:instancePath+"/payload/mediaType",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/mediaType/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err58];}else {vErrors.push(err58);}errors++;}}}if(data33.presentation !== undefined){let data40 = data33.presentation;if(!((((data40 === "document") || (data40 === "code")) || (data40 === "image")) || (data40 === "generic"))){const err59 = {instancePath:instancePath+"/payload/presentation",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/presentation/enum",keyword:"enum",params:{allowedValues: schema173.properties.presentation.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err59];}else {vErrors.push(err59);}errors++;}}if(data33.line !== undefined){let data41 = data33.line;if((!(((typeof data41 == "number") && (!(data41 % 1) && !isNaN(data41))) && (isFinite(data41)))) && (data41 !== null)){const err60 = {instancePath:instancePath+"/payload/line",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/line/type",keyword:"type",params:{type: schema173.properties.line.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err60];}else {vErrors.push(err60);}errors++;}if((typeof data41 == "number") && (isFinite(data41))){if(data41 < 1 || isNaN(data41)){const err61 = {instancePath:instancePath+"/payload/line",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/line/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err61];}else {vErrors.push(err61);}errors++;}}}if(data33.preview !== undefined){let data42 = data33.preview;if((typeof data42 !== "string") && (data42 !== null)){const err62 = {instancePath:instancePath+"/payload/preview",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/preview/type",keyword:"type",params:{type: schema173.properties.preview.type},message:"must be string,null"};if(vErrors === null){vErrors = [err62];}else {vErrors.push(err62);}errors++;}if(typeof data42 === "string"){if(func1(data42) > 262144){const err63 = {instancePath:instancePath+"/payload/preview",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/preview/maxLength",keyword:"maxLength",params:{limit: 262144},message:"must NOT have more than 262144 characters"};if(vErrors === null){vErrors = [err63];}else {vErrors.push(err63);}errors++;}}}if(data33.previewTruncated !== undefined){if(typeof data33.previewTruncated !== "boolean"){const err64 = {instancePath:instancePath+"/payload/previewTruncated",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/previewTruncated/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err64];}else {vErrors.push(err64);}errors++;}}if(data33.contentDigest !== undefined){let data44 = data33.contentDigest;const _errs124 = errors;let valid54 = false;let passing0 = null;const _errs125 = errors;if(data44 !== null){const err65 = {instancePath:instancePath+"/payload/contentDigest",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/contentDigest/oneOf/0/type",keyword:"type",params:{type: "null"},message:"must be null"};if(vErrors === null){vErrors = [err65];}else {vErrors.push(err65);}errors++;}var _valid17 = _errs125 === errors;if(_valid17){valid54 = true;passing0 = 0;}const _errs127 = errors;if(typeof data44 === "string"){if(!pattern21.test(data44)){const err66 = {instancePath:instancePath+"/payload/contentDigest",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/contentDigest/oneOf/1/pattern",keyword:"pattern",params:{pattern: "^sha256:[a-f0-9]{64}$"},message:"must match pattern \""+"^sha256:[a-f0-9]{64}$"+"\""};if(vErrors === null){vErrors = [err66];}else {vErrors.push(err66);}errors++;}}else {const err67 = {instancePath:instancePath+"/payload/contentDigest",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/contentDigest/oneOf/1/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err67];}else {vErrors.push(err67);}errors++;}var _valid17 = _errs127 === errors;if(_valid17 && valid54){valid54 = false;passing0 = [passing0, 1];}else {if(_valid17){valid54 = true;passing0 = 1;}}if(!valid54){const err68 = {instancePath:instancePath+"/payload/contentDigest",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/properties/contentDigest/oneOf",keyword:"oneOf",params:{passingSchemas: passing0},message:"must match exactly one schema in oneOf"};if(vErrors === null){vErrors = [err68];}else {vErrors.push(err68);}errors++;}else {errors = _errs124;if(vErrors !== null){if(_errs124){vErrors.length = _errs124;}else {vErrors = null;}}}}}else {const err69 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-file-reference.schema.json/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err69];}else {vErrors.push(err69);}errors++;}}}var _valid16 = _errs101 === errors;valid49 = _valid16;if(valid49){var props16 = {};props16.payload = true;props16.eventType = true;}}if(!valid49){const err70 = {instancePath,schemaPath:"#/allOf/16/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err70];}else {vErrors.push(err70);}errors++;}if(props0 !== true && props16 !== undefined){if(props16 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props16);}}const _errs130 = errors;let valid55 = true;const _errs131 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data45 = data.eventType;if(!((data45 === "workspace.change.updated") || (data45 === "workspace.diff.recorded"))){const err71 = {};if(vErrors === null){vErrors = [err71];}else {vErrors.push(err71);}errors++;}}}var _valid18 = _errs131 === errors;errors = _errs130;if(vErrors !== null){if(_errs130){vErrors.length = _errs130;}else {vErrors = null;}}if(_valid18){const _errs133 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data46 = data.payload;if(data46 && typeof data46 == "object" && !Array.isArray(data46)){if(data46.schema === undefined){const err72 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err72];}else {vErrors.push(err72);}errors++;}if(data46.changeSetId === undefined){const err73 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "changeSetId"},message:"must have required property '"+"changeSetId"+"'"};if(vErrors === null){vErrors = [err73];}else {vErrors.push(err73);}errors++;}if(data46.revision === undefined){const err74 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "revision"},message:"must have required property '"+"revision"+"'"};if(vErrors === null){vErrors = [err74];}else {vErrors.push(err74);}errors++;}if(data46.source === undefined){const err75 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "source"},message:"must have required property '"+"source"+"'"};if(vErrors === null){vErrors = [err75];}else {vErrors.push(err75);}errors++;}if(data46.complete === undefined){const err76 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "complete"},message:"must have required property '"+"complete"+"'"};if(vErrors === null){vErrors = [err76];}else {vErrors.push(err76);}errors++;}if(data46.files === undefined){const err77 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "files"},message:"must have required property '"+"files"+"'"};if(vErrors === null){vErrors = [err77];}else {vErrors.push(err77);}errors++;}if(data46.totals === undefined){const err78 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "totals"},message:"must have required property '"+"totals"+"'"};if(vErrors === null){vErrors = [err78];}else {vErrors.push(err78);}errors++;}if(data46.patchArtifactRef === undefined){const err79 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "patchArtifactRef"},message:"must have required property '"+"patchArtifactRef"+"'"};if(vErrors === null){vErrors = [err79];}else {vErrors.push(err79);}errors++;}for(const key1 in data46){if(!((((((((key1 === "schema") || (key1 === "changeSetId")) || (key1 === "revision")) || (key1 === "source")) || (key1 === "complete")) || (key1 === "files")) || (key1 === "totals")) || (key1 === "patchArtifactRef"))){const err80 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key1},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err80];}else {vErrors.push(err80);}errors++;}}if(data46.schema !== undefined){if("paperclip.workspace.diff.v1" !== data46.schema){const err81 = {instancePath:instancePath+"/payload/schema",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.workspace.diff.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err81];}else {vErrors.push(err81);}errors++;}}if(data46.changeSetId !== undefined){let data48 = data46.changeSetId;if(typeof data48 === "string"){if(func1(data48) > 200){const err82 = {instancePath:instancePath+"/payload/changeSetId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/changeSetId/maxLength",keyword:"maxLength",params:{limit: 200},message:"must NOT have more than 200 characters"};if(vErrors === null){vErrors = [err82];}else {vErrors.push(err82);}errors++;}if(func1(data48) < 1){const err83 = {instancePath:instancePath+"/payload/changeSetId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/changeSetId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err83];}else {vErrors.push(err83);}errors++;}}else {const err84 = {instancePath:instancePath+"/payload/changeSetId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/changeSetId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err84];}else {vErrors.push(err84);}errors++;}}if(data46.revision !== undefined){let data49 = data46.revision;if(!(((typeof data49 == "number") && (!(data49 % 1) && !isNaN(data49))) && (isFinite(data49)))){const err85 = {instancePath:instancePath+"/payload/revision",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/revision/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err85];}else {vErrors.push(err85);}errors++;}if((typeof data49 == "number") && (isFinite(data49))){if(data49 < 1 || isNaN(data49)){const err86 = {instancePath:instancePath+"/payload/revision",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/revision/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err86];}else {vErrors.push(err86);}errors++;}}}if(data46.source !== undefined){let data50 = data46.source;if(!((data50 === "harness_reported") || (data50 === "runner_verified"))){const err87 = {instancePath:instancePath+"/payload/source",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/source/enum",keyword:"enum",params:{allowedValues: schema174.properties.source.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err87];}else {vErrors.push(err87);}errors++;}}if(data46.complete !== undefined){if(typeof data46.complete !== "boolean"){const err88 = {instancePath:instancePath+"/payload/complete",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/complete/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err88];}else {vErrors.push(err88);}errors++;}}if(data46.files !== undefined){let data52 = data46.files;if(Array.isArray(data52)){if(data52.length > 2000){const err89 = {instancePath:instancePath+"/payload/files",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/maxItems",keyword:"maxItems",params:{limit: 2000},message:"must NOT have more than 2000 items"};if(vErrors === null){vErrors = [err89];}else {vErrors.push(err89);}errors++;}const len0 = data52.length;for(let i0=0; i0 1024){const err98 = {instancePath:instancePath+"/payload/files/" + i0+"/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/path/maxLength",keyword:"maxLength",params:{limit: 1024},message:"must NOT have more than 1024 characters"};if(vErrors === null){vErrors = [err98];}else {vErrors.push(err98);}errors++;}if(func1(data54) < 1){const err99 = {instancePath:instancePath+"/payload/files/" + i0+"/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/path/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err99];}else {vErrors.push(err99);}errors++;}if(!pattern77.test(data54)){const err100 = {instancePath:instancePath+"/payload/files/" + i0+"/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/path/pattern",keyword:"pattern",params:{pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"},message:"must match pattern \""+"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"+"\""};if(vErrors === null){vErrors = [err100];}else {vErrors.push(err100);}errors++;}}else {const err101 = {instancePath:instancePath+"/payload/files/" + i0+"/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/path/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err101];}else {vErrors.push(err101);}errors++;}}if(data53.operation !== undefined){let data55 = data53.operation;if(!(((((data55 === "create") || (data55 === "modify")) || (data55 === "delete")) || (data55 === "rename")) || (data55 === "mode_change"))){const err102 = {instancePath:instancePath+"/payload/files/" + i0+"/operation",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/operation/enum",keyword:"enum",params:{allowedValues: schema174.properties.files.items.properties.operation.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err102];}else {vErrors.push(err102);}errors++;}}if(data53.previousPath !== undefined){let data56 = data53.previousPath;const _errs155 = errors;let valid63 = false;let passing1 = null;const _errs156 = errors;if(data56 !== null){const err103 = {instancePath:instancePath+"/payload/files/" + i0+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/0/type",keyword:"type",params:{type: "null"},message:"must be null"};if(vErrors === null){vErrors = [err103];}else {vErrors.push(err103);}errors++;}var _valid19 = _errs156 === errors;if(_valid19){valid63 = true;passing1 = 0;}const _errs158 = errors;if(typeof data56 === "string"){if(func1(data56) > 1024){const err104 = {instancePath:instancePath+"/payload/files/" + i0+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/1/maxLength",keyword:"maxLength",params:{limit: 1024},message:"must NOT have more than 1024 characters"};if(vErrors === null){vErrors = [err104];}else {vErrors.push(err104);}errors++;}if(func1(data56) < 1){const err105 = {instancePath:instancePath+"/payload/files/" + i0+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/1/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err105];}else {vErrors.push(err105);}errors++;}if(!pattern77.test(data56)){const err106 = {instancePath:instancePath+"/payload/files/" + i0+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/1/pattern",keyword:"pattern",params:{pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"},message:"must match pattern \""+"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"+"\""};if(vErrors === null){vErrors = [err106];}else {vErrors.push(err106);}errors++;}}else {const err107 = {instancePath:instancePath+"/payload/files/" + i0+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/1/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err107];}else {vErrors.push(err107);}errors++;}var _valid19 = _errs158 === errors;if(_valid19 && valid63){valid63 = false;passing1 = [passing1, 1];}else {if(_valid19){valid63 = true;passing1 = 1;}}if(!valid63){const err108 = {instancePath:instancePath+"/payload/files/" + i0+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf",keyword:"oneOf",params:{passingSchemas: passing1},message:"must match exactly one schema in oneOf"};if(vErrors === null){vErrors = [err108];}else {vErrors.push(err108);}errors++;}else {errors = _errs155;if(vErrors !== null){if(_errs155){vErrors.length = _errs155;}else {vErrors = null;}}}}if(data53.additions !== undefined){let data57 = data53.additions;if((!(((typeof data57 == "number") && (!(data57 % 1) && !isNaN(data57))) && (isFinite(data57)))) && (data57 !== null)){const err109 = {instancePath:instancePath+"/payload/files/" + i0+"/additions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/additions/type",keyword:"type",params:{type: schema174.properties.files.items.properties.additions.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err109];}else {vErrors.push(err109);}errors++;}if((typeof data57 == "number") && (isFinite(data57))){if(data57 < 0 || isNaN(data57)){const err110 = {instancePath:instancePath+"/payload/files/" + i0+"/additions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/additions/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err110];}else {vErrors.push(err110);}errors++;}}}if(data53.deletions !== undefined){let data58 = data53.deletions;if((!(((typeof data58 == "number") && (!(data58 % 1) && !isNaN(data58))) && (isFinite(data58)))) && (data58 !== null)){const err111 = {instancePath:instancePath+"/payload/files/" + i0+"/deletions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/deletions/type",keyword:"type",params:{type: schema174.properties.files.items.properties.deletions.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err111];}else {vErrors.push(err111);}errors++;}if((typeof data58 == "number") && (isFinite(data58))){if(data58 < 0 || isNaN(data58)){const err112 = {instancePath:instancePath+"/payload/files/" + i0+"/deletions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/deletions/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err112];}else {vErrors.push(err112);}errors++;}}}if(data53.binary !== undefined){if(typeof data53.binary !== "boolean"){const err113 = {instancePath:instancePath+"/payload/files/" + i0+"/binary",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/binary/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err113];}else {vErrors.push(err113);}errors++;}}if(data53.diff !== undefined){let data60 = data53.diff;if((typeof data60 !== "string") && (data60 !== null)){const err114 = {instancePath:instancePath+"/payload/files/" + i0+"/diff",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/diff/type",keyword:"type",params:{type: schema174.properties.files.items.properties.diff.type},message:"must be string,null"};if(vErrors === null){vErrors = [err114];}else {vErrors.push(err114);}errors++;}if(typeof data60 === "string"){if(func1(data60) > 262144){const err115 = {instancePath:instancePath+"/payload/files/" + i0+"/diff",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/diff/maxLength",keyword:"maxLength",params:{limit: 262144},message:"must NOT have more than 262144 characters"};if(vErrors === null){vErrors = [err115];}else {vErrors.push(err115);}errors++;}}}}else {const err116 = {instancePath:instancePath+"/payload/files/" + i0,schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err116];}else {vErrors.push(err116);}errors++;}}}else {const err117 = {instancePath:instancePath+"/payload/files",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err117];}else {vErrors.push(err117);}errors++;}}if(data46.totals !== undefined){let data61 = data46.totals;if(data61 && typeof data61 == "object" && !Array.isArray(data61)){if(data61.files === undefined){const err118 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/required",keyword:"required",params:{missingProperty: "files"},message:"must have required property '"+"files"+"'"};if(vErrors === null){vErrors = [err118];}else {vErrors.push(err118);}errors++;}if(data61.additions === undefined){const err119 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/required",keyword:"required",params:{missingProperty: "additions"},message:"must have required property '"+"additions"+"'"};if(vErrors === null){vErrors = [err119];}else {vErrors.push(err119);}errors++;}if(data61.deletions === undefined){const err120 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/required",keyword:"required",params:{missingProperty: "deletions"},message:"must have required property '"+"deletions"+"'"};if(vErrors === null){vErrors = [err120];}else {vErrors.push(err120);}errors++;}for(const key3 in data61){if(!(((key3 === "files") || (key3 === "additions")) || (key3 === "deletions"))){const err121 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key3},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err121];}else {vErrors.push(err121);}errors++;}}if(data61.files !== undefined){let data62 = data61.files;if(!(((typeof data62 == "number") && (!(data62 % 1) && !isNaN(data62))) && (isFinite(data62)))){const err122 = {instancePath:instancePath+"/payload/totals/files",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/files/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err122];}else {vErrors.push(err122);}errors++;}if((typeof data62 == "number") && (isFinite(data62))){if(data62 < 0 || isNaN(data62)){const err123 = {instancePath:instancePath+"/payload/totals/files",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/files/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err123];}else {vErrors.push(err123);}errors++;}}}if(data61.additions !== undefined){let data63 = data61.additions;if((!(((typeof data63 == "number") && (!(data63 % 1) && !isNaN(data63))) && (isFinite(data63)))) && (data63 !== null)){const err124 = {instancePath:instancePath+"/payload/totals/additions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/additions/type",keyword:"type",params:{type: schema174.properties.totals.properties.additions.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err124];}else {vErrors.push(err124);}errors++;}if((typeof data63 == "number") && (isFinite(data63))){if(data63 < 0 || isNaN(data63)){const err125 = {instancePath:instancePath+"/payload/totals/additions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/additions/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err125];}else {vErrors.push(err125);}errors++;}}}if(data61.deletions !== undefined){let data64 = data61.deletions;if((!(((typeof data64 == "number") && (!(data64 % 1) && !isNaN(data64))) && (isFinite(data64)))) && (data64 !== null)){const err126 = {instancePath:instancePath+"/payload/totals/deletions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/deletions/type",keyword:"type",params:{type: schema174.properties.totals.properties.deletions.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err126];}else {vErrors.push(err126);}errors++;}if((typeof data64 == "number") && (isFinite(data64))){if(data64 < 0 || isNaN(data64)){const err127 = {instancePath:instancePath+"/payload/totals/deletions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/deletions/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err127];}else {vErrors.push(err127);}errors++;}}}}else {const err128 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err128];}else {vErrors.push(err128);}errors++;}}if(data46.patchArtifactRef !== undefined){let data65 = data46.patchArtifactRef;if((typeof data65 !== "string") && (data65 !== null)){const err129 = {instancePath:instancePath+"/payload/patchArtifactRef",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/patchArtifactRef/type",keyword:"type",params:{type: schema174.properties.patchArtifactRef.type},message:"must be string,null"};if(vErrors === null){vErrors = [err129];}else {vErrors.push(err129);}errors++;}if(typeof data65 === "string"){if(func1(data65) > 2048){const err130 = {instancePath:instancePath+"/payload/patchArtifactRef",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/patchArtifactRef/maxLength",keyword:"maxLength",params:{limit: 2048},message:"must NOT have more than 2048 characters"};if(vErrors === null){vErrors = [err130];}else {vErrors.push(err130);}errors++;}}}}else {const err131 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err131];}else {vErrors.push(err131);}errors++;}}}var _valid18 = _errs133 === errors;valid55 = _valid18;if(valid55){var props17 = {};props17.payload = true;props17.eventType = true;}}if(!valid55){const err132 = {instancePath,schemaPath:"#/allOf/17/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err132];}else {vErrors.push(err132);}errors++;}if(props0 !== true && props17 !== undefined){if(props17 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props17);}}const _errs180 = errors;let valid65 = true;const _errs181 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("workspace.diff.recorded" !== data.eventType){const err133 = {};if(vErrors === null){vErrors = [err133];}else {vErrors.push(err133);}errors++;}}}var _valid20 = _errs181 === errors;errors = _errs180;if(vErrors !== null){if(_errs180){vErrors.length = _errs180;}else {vErrors = null;}}if(_valid20){const _errs183 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data67 = data.payload;if(data67 && typeof data67 == "object" && !Array.isArray(data67)){if(data67.schema === undefined){const err134 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err134];}else {vErrors.push(err134);}errors++;}if(data67.changeSetId === undefined){const err135 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "changeSetId"},message:"must have required property '"+"changeSetId"+"'"};if(vErrors === null){vErrors = [err135];}else {vErrors.push(err135);}errors++;}if(data67.revision === undefined){const err136 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "revision"},message:"must have required property '"+"revision"+"'"};if(vErrors === null){vErrors = [err136];}else {vErrors.push(err136);}errors++;}if(data67.source === undefined){const err137 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "source"},message:"must have required property '"+"source"+"'"};if(vErrors === null){vErrors = [err137];}else {vErrors.push(err137);}errors++;}if(data67.complete === undefined){const err138 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "complete"},message:"must have required property '"+"complete"+"'"};if(vErrors === null){vErrors = [err138];}else {vErrors.push(err138);}errors++;}if(data67.files === undefined){const err139 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "files"},message:"must have required property '"+"files"+"'"};if(vErrors === null){vErrors = [err139];}else {vErrors.push(err139);}errors++;}if(data67.totals === undefined){const err140 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "totals"},message:"must have required property '"+"totals"+"'"};if(vErrors === null){vErrors = [err140];}else {vErrors.push(err140);}errors++;}if(data67.patchArtifactRef === undefined){const err141 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/required",keyword:"required",params:{missingProperty: "patchArtifactRef"},message:"must have required property '"+"patchArtifactRef"+"'"};if(vErrors === null){vErrors = [err141];}else {vErrors.push(err141);}errors++;}for(const key4 in data67){if(!((((((((key4 === "schema") || (key4 === "changeSetId")) || (key4 === "revision")) || (key4 === "source")) || (key4 === "complete")) || (key4 === "files")) || (key4 === "totals")) || (key4 === "patchArtifactRef"))){const err142 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key4},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err142];}else {vErrors.push(err142);}errors++;}}if(data67.schema !== undefined){if("paperclip.workspace.diff.v1" !== data67.schema){const err143 = {instancePath:instancePath+"/payload/schema",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.workspace.diff.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err143];}else {vErrors.push(err143);}errors++;}}if(data67.changeSetId !== undefined){let data69 = data67.changeSetId;if(typeof data69 === "string"){if(func1(data69) > 200){const err144 = {instancePath:instancePath+"/payload/changeSetId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/changeSetId/maxLength",keyword:"maxLength",params:{limit: 200},message:"must NOT have more than 200 characters"};if(vErrors === null){vErrors = [err144];}else {vErrors.push(err144);}errors++;}if(func1(data69) < 1){const err145 = {instancePath:instancePath+"/payload/changeSetId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/changeSetId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err145];}else {vErrors.push(err145);}errors++;}}else {const err146 = {instancePath:instancePath+"/payload/changeSetId",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/changeSetId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err146];}else {vErrors.push(err146);}errors++;}}if(data67.revision !== undefined){let data70 = data67.revision;if(!(((typeof data70 == "number") && (!(data70 % 1) && !isNaN(data70))) && (isFinite(data70)))){const err147 = {instancePath:instancePath+"/payload/revision",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/revision/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err147];}else {vErrors.push(err147);}errors++;}if((typeof data70 == "number") && (isFinite(data70))){if(data70 < 1 || isNaN(data70)){const err148 = {instancePath:instancePath+"/payload/revision",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/revision/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err148];}else {vErrors.push(err148);}errors++;}}}if(data67.source !== undefined){let data71 = data67.source;if(!((data71 === "harness_reported") || (data71 === "runner_verified"))){const err149 = {instancePath:instancePath+"/payload/source",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/source/enum",keyword:"enum",params:{allowedValues: schema174.properties.source.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err149];}else {vErrors.push(err149);}errors++;}}if(data67.complete !== undefined){if(typeof data67.complete !== "boolean"){const err150 = {instancePath:instancePath+"/payload/complete",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/complete/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err150];}else {vErrors.push(err150);}errors++;}}if(data67.files !== undefined){let data73 = data67.files;if(Array.isArray(data73)){if(data73.length > 2000){const err151 = {instancePath:instancePath+"/payload/files",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/maxItems",keyword:"maxItems",params:{limit: 2000},message:"must NOT have more than 2000 items"};if(vErrors === null){vErrors = [err151];}else {vErrors.push(err151);}errors++;}const len1 = data73.length;for(let i1=0; i1 1024){const err160 = {instancePath:instancePath+"/payload/files/" + i1+"/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/path/maxLength",keyword:"maxLength",params:{limit: 1024},message:"must NOT have more than 1024 characters"};if(vErrors === null){vErrors = [err160];}else {vErrors.push(err160);}errors++;}if(func1(data75) < 1){const err161 = {instancePath:instancePath+"/payload/files/" + i1+"/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/path/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err161];}else {vErrors.push(err161);}errors++;}if(!pattern77.test(data75)){const err162 = {instancePath:instancePath+"/payload/files/" + i1+"/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/path/pattern",keyword:"pattern",params:{pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"},message:"must match pattern \""+"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"+"\""};if(vErrors === null){vErrors = [err162];}else {vErrors.push(err162);}errors++;}}else {const err163 = {instancePath:instancePath+"/payload/files/" + i1+"/path",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/path/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err163];}else {vErrors.push(err163);}errors++;}}if(data74.operation !== undefined){let data76 = data74.operation;if(!(((((data76 === "create") || (data76 === "modify")) || (data76 === "delete")) || (data76 === "rename")) || (data76 === "mode_change"))){const err164 = {instancePath:instancePath+"/payload/files/" + i1+"/operation",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/operation/enum",keyword:"enum",params:{allowedValues: schema174.properties.files.items.properties.operation.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err164];}else {vErrors.push(err164);}errors++;}}if(data74.previousPath !== undefined){let data77 = data74.previousPath;const _errs206 = errors;let valid74 = false;let passing2 = null;const _errs207 = errors;if(data77 !== null){const err165 = {instancePath:instancePath+"/payload/files/" + i1+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/0/type",keyword:"type",params:{type: "null"},message:"must be null"};if(vErrors === null){vErrors = [err165];}else {vErrors.push(err165);}errors++;}var _valid21 = _errs207 === errors;if(_valid21){valid74 = true;passing2 = 0;}const _errs209 = errors;if(typeof data77 === "string"){if(func1(data77) > 1024){const err166 = {instancePath:instancePath+"/payload/files/" + i1+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/1/maxLength",keyword:"maxLength",params:{limit: 1024},message:"must NOT have more than 1024 characters"};if(vErrors === null){vErrors = [err166];}else {vErrors.push(err166);}errors++;}if(func1(data77) < 1){const err167 = {instancePath:instancePath+"/payload/files/" + i1+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/1/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err167];}else {vErrors.push(err167);}errors++;}if(!pattern77.test(data77)){const err168 = {instancePath:instancePath+"/payload/files/" + i1+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/1/pattern",keyword:"pattern",params:{pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"},message:"must match pattern \""+"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"+"\""};if(vErrors === null){vErrors = [err168];}else {vErrors.push(err168);}errors++;}}else {const err169 = {instancePath:instancePath+"/payload/files/" + i1+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf/1/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err169];}else {vErrors.push(err169);}errors++;}var _valid21 = _errs209 === errors;if(_valid21 && valid74){valid74 = false;passing2 = [passing2, 1];}else {if(_valid21){valid74 = true;passing2 = 1;}}if(!valid74){const err170 = {instancePath:instancePath+"/payload/files/" + i1+"/previousPath",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/previousPath/oneOf",keyword:"oneOf",params:{passingSchemas: passing2},message:"must match exactly one schema in oneOf"};if(vErrors === null){vErrors = [err170];}else {vErrors.push(err170);}errors++;}else {errors = _errs206;if(vErrors !== null){if(_errs206){vErrors.length = _errs206;}else {vErrors = null;}}}}if(data74.additions !== undefined){let data78 = data74.additions;if((!(((typeof data78 == "number") && (!(data78 % 1) && !isNaN(data78))) && (isFinite(data78)))) && (data78 !== null)){const err171 = {instancePath:instancePath+"/payload/files/" + i1+"/additions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/additions/type",keyword:"type",params:{type: schema174.properties.files.items.properties.additions.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err171];}else {vErrors.push(err171);}errors++;}if((typeof data78 == "number") && (isFinite(data78))){if(data78 < 0 || isNaN(data78)){const err172 = {instancePath:instancePath+"/payload/files/" + i1+"/additions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/additions/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err172];}else {vErrors.push(err172);}errors++;}}}if(data74.deletions !== undefined){let data79 = data74.deletions;if((!(((typeof data79 == "number") && (!(data79 % 1) && !isNaN(data79))) && (isFinite(data79)))) && (data79 !== null)){const err173 = {instancePath:instancePath+"/payload/files/" + i1+"/deletions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/deletions/type",keyword:"type",params:{type: schema174.properties.files.items.properties.deletions.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err173];}else {vErrors.push(err173);}errors++;}if((typeof data79 == "number") && (isFinite(data79))){if(data79 < 0 || isNaN(data79)){const err174 = {instancePath:instancePath+"/payload/files/" + i1+"/deletions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/deletions/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err174];}else {vErrors.push(err174);}errors++;}}}if(data74.binary !== undefined){if(typeof data74.binary !== "boolean"){const err175 = {instancePath:instancePath+"/payload/files/" + i1+"/binary",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/binary/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err175];}else {vErrors.push(err175);}errors++;}}if(data74.diff !== undefined){let data81 = data74.diff;if((typeof data81 !== "string") && (data81 !== null)){const err176 = {instancePath:instancePath+"/payload/files/" + i1+"/diff",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/diff/type",keyword:"type",params:{type: schema174.properties.files.items.properties.diff.type},message:"must be string,null"};if(vErrors === null){vErrors = [err176];}else {vErrors.push(err176);}errors++;}if(typeof data81 === "string"){if(func1(data81) > 262144){const err177 = {instancePath:instancePath+"/payload/files/" + i1+"/diff",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/properties/diff/maxLength",keyword:"maxLength",params:{limit: 262144},message:"must NOT have more than 262144 characters"};if(vErrors === null){vErrors = [err177];}else {vErrors.push(err177);}errors++;}}}}else {const err178 = {instancePath:instancePath+"/payload/files/" + i1,schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/items/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err178];}else {vErrors.push(err178);}errors++;}}}else {const err179 = {instancePath:instancePath+"/payload/files",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/files/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err179];}else {vErrors.push(err179);}errors++;}}if(data67.totals !== undefined){let data82 = data67.totals;if(data82 && typeof data82 == "object" && !Array.isArray(data82)){if(data82.files === undefined){const err180 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/required",keyword:"required",params:{missingProperty: "files"},message:"must have required property '"+"files"+"'"};if(vErrors === null){vErrors = [err180];}else {vErrors.push(err180);}errors++;}if(data82.additions === undefined){const err181 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/required",keyword:"required",params:{missingProperty: "additions"},message:"must have required property '"+"additions"+"'"};if(vErrors === null){vErrors = [err181];}else {vErrors.push(err181);}errors++;}if(data82.deletions === undefined){const err182 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/required",keyword:"required",params:{missingProperty: "deletions"},message:"must have required property '"+"deletions"+"'"};if(vErrors === null){vErrors = [err182];}else {vErrors.push(err182);}errors++;}for(const key6 in data82){if(!(((key6 === "files") || (key6 === "additions")) || (key6 === "deletions"))){const err183 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key6},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err183];}else {vErrors.push(err183);}errors++;}}if(data82.files !== undefined){let data83 = data82.files;if(!(((typeof data83 == "number") && (!(data83 % 1) && !isNaN(data83))) && (isFinite(data83)))){const err184 = {instancePath:instancePath+"/payload/totals/files",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/files/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err184];}else {vErrors.push(err184);}errors++;}if((typeof data83 == "number") && (isFinite(data83))){if(data83 < 0 || isNaN(data83)){const err185 = {instancePath:instancePath+"/payload/totals/files",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/files/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err185];}else {vErrors.push(err185);}errors++;}}}if(data82.additions !== undefined){let data84 = data82.additions;if((!(((typeof data84 == "number") && (!(data84 % 1) && !isNaN(data84))) && (isFinite(data84)))) && (data84 !== null)){const err186 = {instancePath:instancePath+"/payload/totals/additions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/additions/type",keyword:"type",params:{type: schema174.properties.totals.properties.additions.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err186];}else {vErrors.push(err186);}errors++;}if((typeof data84 == "number") && (isFinite(data84))){if(data84 < 0 || isNaN(data84)){const err187 = {instancePath:instancePath+"/payload/totals/additions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/additions/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err187];}else {vErrors.push(err187);}errors++;}}}if(data82.deletions !== undefined){let data85 = data82.deletions;if((!(((typeof data85 == "number") && (!(data85 % 1) && !isNaN(data85))) && (isFinite(data85)))) && (data85 !== null)){const err188 = {instancePath:instancePath+"/payload/totals/deletions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/deletions/type",keyword:"type",params:{type: schema174.properties.totals.properties.deletions.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err188];}else {vErrors.push(err188);}errors++;}if((typeof data85 == "number") && (isFinite(data85))){if(data85 < 0 || isNaN(data85)){const err189 = {instancePath:instancePath+"/payload/totals/deletions",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/properties/deletions/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err189];}else {vErrors.push(err189);}errors++;}}}}else {const err190 = {instancePath:instancePath+"/payload/totals",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/totals/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err190];}else {vErrors.push(err190);}errors++;}}if(data67.patchArtifactRef !== undefined){let data86 = data67.patchArtifactRef;if((typeof data86 !== "string") && (data86 !== null)){const err191 = {instancePath:instancePath+"/payload/patchArtifactRef",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/patchArtifactRef/type",keyword:"type",params:{type: schema174.properties.patchArtifactRef.type},message:"must be string,null"};if(vErrors === null){vErrors = [err191];}else {vErrors.push(err191);}errors++;}if(typeof data86 === "string"){if(func1(data86) > 2048){const err192 = {instancePath:instancePath+"/payload/patchArtifactRef",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/properties/patchArtifactRef/maxLength",keyword:"maxLength",params:{limit: 2048},message:"must NOT have more than 2048 characters"};if(vErrors === null){vErrors = [err192];}else {vErrors.push(err192);}errors++;}}}}else {const err193 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/workspace-diff.schema.json/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err193];}else {vErrors.push(err193);}errors++;}if(data67 && typeof data67 == "object" && !Array.isArray(data67)){if(data67.complete !== undefined){if(true !== data67.complete){const err194 = {instancePath:instancePath+"/payload/complete",schemaPath:"#/allOf/18/then/properties/payload/allOf/1/properties/complete/const",keyword:"const",params:{allowedValue: true},message:"must be equal to constant"};if(vErrors === null){vErrors = [err194];}else {vErrors.push(err194);}errors++;}}}else {const err195 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/18/then/properties/payload/allOf/1/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err195];}else {vErrors.push(err195);}errors++;}}}var _valid20 = _errs183 === errors;valid65 = _valid20;if(valid65){var props18 = {};props18.payload = true;props18.eventType = true;}}if(!valid65){const err196 = {instancePath,schemaPath:"#/allOf/18/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err196];}else {vErrors.push(err196);}errors++;}if(props0 !== true && props18 !== undefined){if(props18 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props18);}}const _errs234 = errors;let valid77 = true;const _errs235 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data88 = data.eventType;if(!((data88 === "semantic_tool.input") || (data88 === "mcp_app.tool_input"))){const err197 = {};if(vErrors === null){vErrors = [err197];}else {vErrors.push(err197);}errors++;}}}var _valid22 = _errs235 === errors;errors = _errs234;if(vErrors !== null){if(_errs234){vErrors.length = _errs234;}else {vErrors = null;}}if(_valid22){const _errs237 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data89 = data.payload;if(data89 && typeof data89 == "object" && !Array.isArray(data89)){if(data89.semantic_tool !== undefined){let data90 = data89.semantic_tool;if(!(validate92(data90, {instancePath:instancePath+"/payload/semantic_tool",parentData:data89,parentDataProperty:"semantic_tool",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate92.errors : vErrors.concat(validate92.errors);errors = vErrors.length;}if(data90 && typeof data90 == "object" && !Array.isArray(data90)){if(data90.phase !== undefined){if("input" !== data90.phase){const err198 = {instancePath:instancePath+"/payload/semantic_tool/phase",schemaPath:"#/allOf/19/then/properties/payload/properties/semantic_tool/allOf/1/properties/phase/const",keyword:"const",params:{allowedValue: "input"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err198];}else {vErrors.push(err198);}errors++;}}}else {const err199 = {instancePath:instancePath+"/payload/semantic_tool",schemaPath:"#/allOf/19/then/properties/payload/properties/semantic_tool/allOf/1/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err199];}else {vErrors.push(err199);}errors++;}}}else {const err200 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/19/then/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err200];}else {vErrors.push(err200);}errors++;}}}var _valid22 = _errs237 === errors;valid77 = _valid22;if(valid77){var props19 = {};props19.payload = true;props19.eventType = true;}}if(!valid77){const err201 = {instancePath,schemaPath:"#/allOf/19/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err201];}else {vErrors.push(err201);}errors++;}if(props0 !== true && props19 !== undefined){if(props19 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props19);}}const _errs247 = errors;let valid83 = true;const _errs248 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data92 = data.eventType;if(!((data92 === "semantic_tool.result") || (data92 === "mcp_app.tool_result"))){const err202 = {};if(vErrors === null){vErrors = [err202];}else {vErrors.push(err202);}errors++;}}}var _valid23 = _errs248 === errors;errors = _errs247;if(vErrors !== null){if(_errs247){vErrors.length = _errs247;}else {vErrors = null;}}if(_valid23){const _errs250 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data93 = data.payload;if(data93 && typeof data93 == "object" && !Array.isArray(data93)){if(data93.semantic_tool !== undefined){let data94 = data93.semantic_tool;if(!(validate92(data94, {instancePath:instancePath+"/payload/semantic_tool",parentData:data93,parentDataProperty:"semantic_tool",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate92.errors : vErrors.concat(validate92.errors);errors = vErrors.length;}if(data94 && typeof data94 == "object" && !Array.isArray(data94)){if(data94.phase !== undefined){if("result" !== data94.phase){const err203 = {instancePath:instancePath+"/payload/semantic_tool/phase",schemaPath:"#/allOf/20/then/properties/payload/properties/semantic_tool/allOf/1/properties/phase/const",keyword:"const",params:{allowedValue: "result"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err203];}else {vErrors.push(err203);}errors++;}}}else {const err204 = {instancePath:instancePath+"/payload/semantic_tool",schemaPath:"#/allOf/20/then/properties/payload/properties/semantic_tool/allOf/1/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err204];}else {vErrors.push(err204);}errors++;}}}else {const err205 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/20/then/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err205];}else {vErrors.push(err205);}errors++;}}}var _valid23 = _errs250 === errors;valid83 = _valid23;if(valid83){var props20 = {};props20.payload = true;props20.eventType = true;}}if(!valid83){const err206 = {instancePath,schemaPath:"#/allOf/20/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err206];}else {vErrors.push(err206);}errors++;}if(props0 !== true && props20 !== undefined){if(props20 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props20);}}const _errs260 = errors;let valid89 = true;const _errs261 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("semantic_tool.reconciled" !== data.eventType){const err207 = {};if(vErrors === null){vErrors = [err207];}else {vErrors.push(err207);}errors++;}}}var _valid24 = _errs261 === errors;errors = _errs260;if(vErrors !== null){if(_errs260){vErrors.length = _errs260;}else {vErrors = null;}}if(_valid24){const _errs263 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data97 = data.payload;if(data97 && typeof data97 == "object" && !Array.isArray(data97)){if(data97.semantic_tool !== undefined){let data98 = data97.semantic_tool;if(!(validate92(data98, {instancePath:instancePath+"/payload/semantic_tool",parentData:data97,parentDataProperty:"semantic_tool",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate92.errors : vErrors.concat(validate92.errors);errors = vErrors.length;}if(data98 && typeof data98 == "object" && !Array.isArray(data98)){if(data98.phase !== undefined){if("reconciled" !== data98.phase){const err208 = {instancePath:instancePath+"/payload/semantic_tool/phase",schemaPath:"#/allOf/21/then/properties/payload/properties/semantic_tool/allOf/1/properties/phase/const",keyword:"const",params:{allowedValue: "reconciled"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err208];}else {vErrors.push(err208);}errors++;}}}else {const err209 = {instancePath:instancePath+"/payload/semantic_tool",schemaPath:"#/allOf/21/then/properties/payload/properties/semantic_tool/allOf/1/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err209];}else {vErrors.push(err209);}errors++;}}}else {const err210 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/21/then/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err210];}else {vErrors.push(err210);}errors++;}}}var _valid24 = _errs263 === errors;valid89 = _valid24;if(valid89){var props21 = {};props21.payload = true;props21.eventType = true;}}if(!valid89){const err211 = {instancePath,schemaPath:"#/allOf/21/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err211];}else {vErrors.push(err211);}errors++;}if(props0 !== true && props21 !== undefined){if(props21 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props21);}}const _errs273 = errors;let valid95 = true;const _errs274 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("run.terminal" !== data.eventType){const err212 = {};if(vErrors === null){vErrors = [err212];}else {vErrors.push(err212);}errors++;}}}var _valid25 = _errs274 === errors;errors = _errs273;if(vErrors !== null){if(_errs273){vErrors.length = _errs273;}else {vErrors = null;}}if(_valid25){const _errs276 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate104(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate104.errors : vErrors.concat(validate104.errors);errors = vErrors.length;}}}var _valid25 = _errs276 === errors;valid95 = _valid25;if(valid95){var props22 = {};props22.payload = true;props22.eventType = true;}}if(!valid95){const err213 = {instancePath,schemaPath:"#/allOf/22/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err213];}else {vErrors.push(err213);}errors++;}if(props0 !== true && props22 !== undefined){if(props22 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props22);}}const _errs279 = errors;let valid98 = true;const _errs280 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("run.result.proposed" !== data.eventType){const err214 = {};if(vErrors === null){vErrors = [err214];}else {vErrors.push(err214);}errors++;}}}var _valid26 = _errs280 === errors;errors = _errs279;if(vErrors !== null){if(_errs279){vErrors.length = _errs279;}else {vErrors = null;}}if(_valid26){const _errs282 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data103 = data.payload;const _errs287 = errors;let valid103 = true;const _errs288 = errors;if(data103 && typeof data103 == "object" && !Array.isArray(data103)){if(data103.reportedWorkDisposition !== undefined){if("blocked" !== data103.reportedWorkDisposition){const err215 = {};if(vErrors === null){vErrors = [err215];}else {vErrors.push(err215);}errors++;}}}var _valid27 = _errs288 === errors;errors = _errs287;if(vErrors !== null){if(_errs287){vErrors.length = _errs287;}else {vErrors = null;}}if(_valid27){const _errs290 = errors;if(data103 && typeof data103 == "object" && !Array.isArray(data103)){if(data103.blocker === undefined){const err216 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/allOf/0/then/required",keyword:"required",params:{missingProperty: "blocker"},message:"must have required property '"+"blocker"+"'"};if(vErrors === null){vErrors = [err216];}else {vErrors.push(err216);}errors++;}}var _valid27 = _errs290 === errors;valid103 = _valid27;}if(!valid103){const err217 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/allOf/0/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err217];}else {vErrors.push(err217);}errors++;}const _errs292 = errors;let valid105 = true;const _errs293 = errors;if(data103 && typeof data103 == "object" && !Array.isArray(data103)){if(data103.reportedWorkDisposition !== undefined){if("yielded" !== data103.reportedWorkDisposition){const err218 = {};if(vErrors === null){vErrors = [err218];}else {vErrors.push(err218);}errors++;}}}var _valid28 = _errs293 === errors;errors = _errs292;if(vErrors !== null){if(_errs292){vErrors.length = _errs292;}else {vErrors = null;}}if(_valid28){const _errs295 = errors;if(data103 && typeof data103 == "object" && !Array.isArray(data103)){if(data103.continuation === undefined){const err219 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/allOf/1/then/required",keyword:"required",params:{missingProperty: "continuation"},message:"must have required property '"+"continuation"+"'"};if(vErrors === null){vErrors = [err219];}else {vErrors.push(err219);}errors++;}}var _valid28 = _errs295 === errors;valid105 = _valid28;}if(!valid105){const err220 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/allOf/1/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err220];}else {vErrors.push(err220);}errors++;}if(data103 && typeof data103 == "object" && !Array.isArray(data103)){if(data103.schema === undefined){const err221 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err221];}else {vErrors.push(err221);}errors++;}if(data103.reportedWorkDisposition === undefined){const err222 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/required",keyword:"required",params:{missingProperty: "reportedWorkDisposition"},message:"must have required property '"+"reportedWorkDisposition"+"'"};if(vErrors === null){vErrors = [err222];}else {vErrors.push(err222);}errors++;}if(data103.summary === undefined){const err223 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/required",keyword:"required",params:{missingProperty: "summary"},message:"must have required property '"+"summary"+"'"};if(vErrors === null){vErrors = [err223];}else {vErrors.push(err223);}errors++;}if(data103.completionClaim === undefined){const err224 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/required",keyword:"required",params:{missingProperty: "completionClaim"},message:"must have required property '"+"completionClaim"+"'"};if(vErrors === null){vErrors = [err224];}else {vErrors.push(err224);}errors++;}if(data103.evidence === undefined){const err225 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/required",keyword:"required",params:{missingProperty: "evidence"},message:"must have required property '"+"evidence"+"'"};if(vErrors === null){vErrors = [err225];}else {vErrors.push(err225);}errors++;}if(data103.verification === undefined){const err226 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/required",keyword:"required",params:{missingProperty: "verification"},message:"must have required property '"+"verification"+"'"};if(vErrors === null){vErrors = [err226];}else {vErrors.push(err226);}errors++;}if(data103.attentionRequests === undefined){const err227 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/required",keyword:"required",params:{missingProperty: "attentionRequests"},message:"must have required property '"+"attentionRequests"+"'"};if(vErrors === null){vErrors = [err227];}else {vErrors.push(err227);}errors++;}if(data103.artifacts === undefined){const err228 = {instancePath:instancePath+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/required",keyword:"required",params:{missingProperty: "artifacts"},message:"must have required property '"+"artifacts"+"'"};if(vErrors === null){vErrors = [err228];}else {vErrors.push(err228);}errors++;}if(data103.schema !== undefined){if("paperclip.run_result.v1" !== data103.schema){const err229 = {instancePath:instancePath+"/payload/schema",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.run_result.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err229];}else {vErrors.push(err229);}errors++;}}if(data103.reportedWorkDisposition !== undefined){let data107 = data103.reportedWorkDisposition;if(!((((data107 === "done") || (data107 === "blocked")) || (data107 === "needs_review")) || (data107 === "yielded"))){const err230 = {instancePath:instancePath+"/payload/reportedWorkDisposition",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/reportedWorkDisposition/enum",keyword:"enum",params:{allowedValues: schema201.properties.reportedWorkDisposition.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err230];}else {vErrors.push(err230);}errors++;}}if(data103.summary !== undefined){let data108 = data103.summary;if(typeof data108 === "string"){if(func1(data108) > 12000){const err231 = {instancePath:instancePath+"/payload/summary",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/summary/maxLength",keyword:"maxLength",params:{limit: 12000},message:"must NOT have more than 12000 characters"};if(vErrors === null){vErrors = [err231];}else {vErrors.push(err231);}errors++;}if(func1(data108) < 1){const err232 = {instancePath:instancePath+"/payload/summary",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/summary/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err232];}else {vErrors.push(err232);}errors++;}}else {const err233 = {instancePath:instancePath+"/payload/summary",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/summary/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err233];}else {vErrors.push(err233);}errors++;}}if(data103.completionClaim !== undefined){let data109 = data103.completionClaim;if(data109 && typeof data109 == "object" && !Array.isArray(data109)){if(data109.contractRevision === undefined){const err234 = {instancePath:instancePath+"/payload/completionClaim",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/required",keyword:"required",params:{missingProperty: "contractRevision"},message:"must have required property '"+"contractRevision"+"'"};if(vErrors === null){vErrors = [err234];}else {vErrors.push(err234);}errors++;}if(data109.objectiveSatisfied === undefined){const err235 = {instancePath:instancePath+"/payload/completionClaim",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/required",keyword:"required",params:{missingProperty: "objectiveSatisfied"},message:"must have required property '"+"objectiveSatisfied"+"'"};if(vErrors === null){vErrors = [err235];}else {vErrors.push(err235);}errors++;}if(data109.criteria === undefined){const err236 = {instancePath:instancePath+"/payload/completionClaim",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/required",keyword:"required",params:{missingProperty: "criteria"},message:"must have required property '"+"criteria"+"'"};if(vErrors === null){vErrors = [err236];}else {vErrors.push(err236);}errors++;}if(data109.remainingWork === undefined){const err237 = {instancePath:instancePath+"/payload/completionClaim",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/required",keyword:"required",params:{missingProperty: "remainingWork"},message:"must have required property '"+"remainingWork"+"'"};if(vErrors === null){vErrors = [err237];}else {vErrors.push(err237);}errors++;}if(data109.contractRevision !== undefined){let data110 = data109.contractRevision;if(typeof data110 === "string"){if(func1(data110) < 1){const err238 = {instancePath:instancePath+"/payload/completionClaim/contractRevision",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/properties/contractRevision/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err238];}else {vErrors.push(err238);}errors++;}}else {const err239 = {instancePath:instancePath+"/payload/completionClaim/contractRevision",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/properties/contractRevision/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err239];}else {vErrors.push(err239);}errors++;}}if(data109.objectiveSatisfied !== undefined){if(typeof data109.objectiveSatisfied !== "boolean"){const err240 = {instancePath:instancePath+"/payload/completionClaim/objectiveSatisfied",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/properties/objectiveSatisfied/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err240];}else {vErrors.push(err240);}errors++;}}if(data109.criteria !== undefined){let data112 = data109.criteria;if(Array.isArray(data112)){const len2 = data112.length;for(let i2=0; i2 32766){const err344 = {instancePath:instancePath+"/payload/text",schemaPath:"#/allOf/24/then/properties/payload/properties/text/maxLength",keyword:"maxLength",params:{limit: 32766},message:"must NOT have more than 32766 characters"};if(vErrors === null){vErrors = [err344];}else {vErrors.push(err344);}errors++;}if(func1(data165) < 1){const err345 = {instancePath:instancePath+"/payload/text",schemaPath:"#/allOf/24/then/properties/payload/properties/text/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err345];}else {vErrors.push(err345);}errors++;}}else {const err346 = {instancePath:instancePath+"/payload/text",schemaPath:"#/allOf/24/then/properties/payload/properties/text/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err346];}else {vErrors.push(err346);}errors++;}}}else {const err347 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/24/then/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err347];}else {vErrors.push(err347);}errors++;}}}var _valid29 = _errs397 === errors;valid131 = _valid29;if(valid131){var props24 = {};props24.payload = true;props24.eventType = true;}}if(!valid131){const err348 = {instancePath,schemaPath:"#/allOf/24/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err348];}else {vErrors.push(err348);}errors++;}if(props0 !== true && props24 !== undefined){if(props24 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props24);}}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err349 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err349];}else {vErrors.push(err349);}errors++;}if(data.sourceEventId === undefined){const err350 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sourceEventId"},message:"must have required property '"+"sourceEventId"+"'"};if(vErrors === null){vErrors = [err350];}else {vErrors.push(err350);}errors++;}if(data.sourceSeq === undefined){const err351 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sourceSeq"},message:"must have required property '"+"sourceSeq"+"'"};if(vErrors === null){vErrors = [err351];}else {vErrors.push(err351);}errors++;}if(data.sourceInstanceId === undefined){const err352 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sourceInstanceId"},message:"must have required property '"+"sourceInstanceId"+"'"};if(vErrors === null){vErrors = [err352];}else {vErrors.push(err352);}errors++;}if(data.sourceKind === undefined){const err353 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sourceKind"},message:"must have required property '"+"sourceKind"+"'"};if(vErrors === null){vErrors = [err353];}else {vErrors.push(err353);}errors++;}if(data.runId === undefined){const err354 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "runId"},message:"must have required property '"+"runId"+"'"};if(vErrors === null){vErrors = [err354];}else {vErrors.push(err354);}errors++;}if(data.eventType === undefined){const err355 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "eventType"},message:"must have required property '"+"eventType"+"'"};if(vErrors === null){vErrors = [err355];}else {vErrors.push(err355);}errors++;}if(data.schemaVersion === undefined){const err356 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schemaVersion"},message:"must have required property '"+"schemaVersion"+"'"};if(vErrors === null){vErrors = [err356];}else {vErrors.push(err356);}errors++;}if(data.priority === undefined){const err357 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "priority"},message:"must have required property '"+"priority"+"'"};if(vErrors === null){vErrors = [err357];}else {vErrors.push(err357);}errors++;}if(data.emittedAt === undefined){const err358 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "emittedAt"},message:"must have required property '"+"emittedAt"+"'"};if(vErrors === null){vErrors = [err358];}else {vErrors.push(err358);}errors++;}if(data.payload === undefined){const err359 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "payload"},message:"must have required property '"+"payload"+"'"};if(vErrors === null){vErrors = [err359];}else {vErrors.push(err359);}errors++;}if(data.schema !== undefined){if("paperclip.prp.event.v1" !== data.schema){const err360 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.event.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err360];}else {vErrors.push(err360);}errors++;}}if(data.sourceEventId !== undefined){let data167 = data.sourceEventId;if(typeof data167 === "string"){if(func1(data167) > 160){const err361 = {instancePath:instancePath+"/sourceEventId",schemaPath:"#/properties/sourceEventId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err361];}else {vErrors.push(err361);}errors++;}if(func1(data167) < 1){const err362 = {instancePath:instancePath+"/sourceEventId",schemaPath:"#/properties/sourceEventId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err362];}else {vErrors.push(err362);}errors++;}}else {const err363 = {instancePath:instancePath+"/sourceEventId",schemaPath:"#/properties/sourceEventId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err363];}else {vErrors.push(err363);}errors++;}}if(data.sourceEpoch !== undefined){let data168 = data.sourceEpoch;if(typeof data168 === "string"){if(!pattern15.test(data168)){const err364 = {instancePath:instancePath+"/sourceEpoch",schemaPath:"#/properties/sourceEpoch/pattern",keyword:"pattern",params:{pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},message:"must match pattern \""+"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"+"\""};if(vErrors === null){vErrors = [err364];}else {vErrors.push(err364);}errors++;}}else {const err365 = {instancePath:instancePath+"/sourceEpoch",schemaPath:"#/properties/sourceEpoch/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err365];}else {vErrors.push(err365);}errors++;}}if(data.sourceSeq !== undefined){let data169 = data.sourceSeq;if(!(((typeof data169 == "number") && (!(data169 % 1) && !isNaN(data169))) && (isFinite(data169)))){const err366 = {instancePath:instancePath+"/sourceSeq",schemaPath:"#/properties/sourceSeq/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err366];}else {vErrors.push(err366);}errors++;}if((typeof data169 == "number") && (isFinite(data169))){if(data169 > 9007199254740991 || isNaN(data169)){const err367 = {instancePath:instancePath+"/sourceSeq",schemaPath:"#/properties/sourceSeq/maximum",keyword:"maximum",params:{comparison: "<=", limit: 9007199254740991},message:"must be <= 9007199254740991"};if(vErrors === null){vErrors = [err367];}else {vErrors.push(err367);}errors++;}if(data169 < 1 || isNaN(data169)){const err368 = {instancePath:instancePath+"/sourceSeq",schemaPath:"#/properties/sourceSeq/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err368];}else {vErrors.push(err368);}errors++;}}}if(data.sourceInstanceId !== undefined){let data170 = data.sourceInstanceId;if(typeof data170 === "string"){if(func1(data170) > 160){const err369 = {instancePath:instancePath+"/sourceInstanceId",schemaPath:"#/properties/sourceInstanceId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err369];}else {vErrors.push(err369);}errors++;}if(func1(data170) < 1){const err370 = {instancePath:instancePath+"/sourceInstanceId",schemaPath:"#/properties/sourceInstanceId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err370];}else {vErrors.push(err370);}errors++;}}else {const err371 = {instancePath:instancePath+"/sourceInstanceId",schemaPath:"#/properties/sourceInstanceId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err371];}else {vErrors.push(err371);}errors++;}}if(data.sourceKind !== undefined){let data171 = data.sourceKind;if(!((data171 === "runner") || (data171 === "control_plane"))){const err372 = {instancePath:instancePath+"/sourceKind",schemaPath:"#/properties/sourceKind/enum",keyword:"enum",params:{allowedValues: schema47.properties.sourceKind.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err372];}else {vErrors.push(err372);}errors++;}}if(data.runId !== undefined){let data172 = data.runId;if(typeof data172 === "string"){if(func1(data172) > 160){const err373 = {instancePath:instancePath+"/runId",schemaPath:"#/properties/runId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err373];}else {vErrors.push(err373);}errors++;}if(func1(data172) < 1){const err374 = {instancePath:instancePath+"/runId",schemaPath:"#/properties/runId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err374];}else {vErrors.push(err374);}errors++;}}else {const err375 = {instancePath:instancePath+"/runId",schemaPath:"#/properties/runId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err375];}else {vErrors.push(err375);}errors++;}}if(data.normalizedSessionId !== undefined){let data173 = data.normalizedSessionId;if(typeof data173 === "string"){if(func1(data173) > 160){const err376 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/properties/normalizedSessionId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err376];}else {vErrors.push(err376);}errors++;}if(func1(data173) < 1){const err377 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/properties/normalizedSessionId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err377];}else {vErrors.push(err377);}errors++;}}else {const err378 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/properties/normalizedSessionId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err378];}else {vErrors.push(err378);}errors++;}}if(data.turnId !== undefined){let data174 = data.turnId;if(typeof data174 === "string"){if(func1(data174) > 240){const err379 = {instancePath:instancePath+"/turnId",schemaPath:"#/properties/turnId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err379];}else {vErrors.push(err379);}errors++;}if(func1(data174) < 1){const err380 = {instancePath:instancePath+"/turnId",schemaPath:"#/properties/turnId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err380];}else {vErrors.push(err380);}errors++;}}else {const err381 = {instancePath:instancePath+"/turnId",schemaPath:"#/properties/turnId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err381];}else {vErrors.push(err381);}errors++;}}if(data.itemId !== undefined){let data175 = data.itemId;if(typeof data175 === "string"){if(func1(data175) > 240){const err382 = {instancePath:instancePath+"/itemId",schemaPath:"#/properties/itemId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err382];}else {vErrors.push(err382);}errors++;}if(func1(data175) < 1){const err383 = {instancePath:instancePath+"/itemId",schemaPath:"#/properties/itemId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err383];}else {vErrors.push(err383);}errors++;}}else {const err384 = {instancePath:instancePath+"/itemId",schemaPath:"#/properties/itemId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err384];}else {vErrors.push(err384);}errors++;}}if(data.eventType !== undefined){let data176 = data.eventType;if(!((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((data176 === "runner.connected") || (data176 === "runner.reconnected")) || (data176 === "runner.reconciled")) || (data176 === "runner.disconnected")) || (data176 === "runner.draining")) || (data176 === "runner.backpressure")) || (data176 === "runner.suspending")) || (data176 === "runner.suspended")) || (data176 === "runner.stopped")) || (data176 === "runner.diagnostic")) || (data176 === "runtime.phase.changed")) || (data176 === "sandbox.metric")) || (data176 === "workspace.ready")) || (data176 === "workspace.change.updated")) || (data176 === "workspace.diff.recorded")) || (data176 === "workspace.file.referenced")) || (data176 === "harness.starting")) || (data176 === "harness.ready")) || (data176 === "harness.exited")) || (data176 === "harness.diagnostic")) || (data176 === "plan.updated")) || (data176 === "tool.execution.started")) || (data176 === "tool.execution.progressed")) || (data176 === "tool.execution.completed")) || (data176 === "research.started")) || (data176 === "research.progressed")) || (data176 === "research.completed")) || (data176 === "delegation.started")) || (data176 === "delegation.updated")) || (data176 === "delegation.completed")) || (data176 === "model.route.changed")) || (data176 === "model.verification.updated")) || (data176 === "context.compacted")) || (data176 === "artifact.viewed")) || (data176 === "artifact.generated")) || (data176 === "review.mode.changed")) || (data176 === "hook.started")) || (data176 === "hook.completed")) || (data176 === "memory.citation.referenced")) || (data176 === "safety.review.started")) || (data176 === "safety.review.completed")) || (data176 === "terminal.input.sent")) || (data176 === "wait.started")) || (data176 === "wait.completed")) || (data176 === "provider.notice.recorded")) || (data176 === "session.starting")) || (data176 === "session.started")) || (data176 === "session.resuming")) || (data176 === "session.resumed")) || (data176 === "session.reconciled")) || (data176 === "session.updated")) || (data176 === "session.closed")) || (data176 === "session.failed")) || (data176 === "turn.submitted")) || (data176 === "turn.accepted")) || (data176 === "turn.started")) || (data176 === "turn.completed")) || (data176 === "turn.failed")) || (data176 === "turn.interrupted")) || (data176 === "turn.cancelled")) || (data176 === "output.body.chunk")) || (data176 === "item.started")) || (data176 === "item.delta")) || (data176 === "item.completed")) || (data176 === "item.failed")) || (data176 === "usage.reported")) || (data176 === "semantic_tool.input")) || (data176 === "semantic_tool.result")) || (data176 === "semantic_tool.reconciled")) || (data176 === "mcp_app.discovered")) || (data176 === "mcp_app.resource.resolved")) || (data176 === "mcp_app.initializing")) || (data176 === "mcp_app.ready")) || (data176 === "mcp_app.tool_input")) || (data176 === "mcp_app.tool_result")) || (data176 === "mcp_app.action.requested")) || (data176 === "mcp_app.action.resolved")) || (data176 === "mcp_app.host_context.changed")) || (data176 === "mcp_app.failed")) || (data176 === "mcp_app.teardown")) || (data176 === "runtime_request.created")) || (data176 === "runtime_request.resolved")) || (data176 === "runtime_request.expired")) || (data176 === "runtime_request.cancelled")) || (data176 === "interaction.request.proposed")) || (data176 === "interaction.request.materialized")) || (data176 === "interaction.request.rejected")) || (data176 === "interaction.response.progressed")) || (data176 === "interaction.response.resolved")) || (data176 === "interaction.response.delivered")) || (data176 === "run.attached")) || (data176 === "run.detached")) || (data176 === "run.result.proposed")) || (data176 === "run.result.accepted")) || (data176 === "run.result.rejected")) || (data176 === "attention.request.proposed")) || (data176 === "attention.request.routed")) || (data176 === "attention.request.resolved")) || (data176 === "attention.request.expired")) || (data176 === "attention.request.superseded")) || (data176 === "work.assessment.recorded")) || (data176 === "issue.status.decision.recorded")) || (data176 === "issue.status.decision.applied")) || (data176 === "issue.status.decision.rejected")) || (data176 === "issue.status.decision.superseded")) || (data176 === "run.terminal"))){const err385 = {instancePath:instancePath+"/eventType",schemaPath:"#/properties/eventType/enum",keyword:"enum",params:{allowedValues: schema47.properties.eventType.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err385];}else {vErrors.push(err385);}errors++;}}if(data.schemaVersion !== undefined){if(1 !== data.schemaVersion){const err386 = {instancePath:instancePath+"/schemaVersion",schemaPath:"#/properties/schemaVersion/const",keyword:"const",params:{allowedValue: 1},message:"must be equal to constant"};if(vErrors === null){vErrors = [err386];}else {vErrors.push(err386);}errors++;}}if(data.priority !== undefined){let data178 = data.priority;if(!(((data178 === 0) || (data178 === 1)) || (data178 === 2))){const err387 = {instancePath:instancePath+"/priority",schemaPath:"#/properties/priority/enum",keyword:"enum",params:{allowedValues: schema47.properties.priority.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err387];}else {vErrors.push(err387);}errors++;}}if(data.emittedAt !== undefined){let data179 = data.emittedAt;if(typeof data179 === "string"){if(!(formats0.test(data179))){const err388 = {instancePath:instancePath+"/emittedAt",schemaPath:"#/properties/emittedAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err388];}else {vErrors.push(err388);}errors++;}}else {const err389 = {instancePath:instancePath+"/emittedAt",schemaPath:"#/properties/emittedAt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err389];}else {vErrors.push(err389);}errors++;}}if(data.observedAt !== undefined){let data180 = data.observedAt;if(typeof data180 === "string"){if(!(formats0.test(data180))){const err390 = {instancePath:instancePath+"/observedAt",schemaPath:"#/properties/observedAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err390];}else {vErrors.push(err390);}errors++;}}else {const err391 = {instancePath:instancePath+"/observedAt",schemaPath:"#/properties/observedAt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err391];}else {vErrors.push(err391);}errors++;}}if(data.payload !== undefined){let data181 = data.payload;if(data181 && typeof data181 == "object" && !Array.isArray(data181)){if(data181.channel !== undefined){let data182 = data181.channel;if(!(((((data182 === "progress") || (data182 === "final")) || (data182 === "summary")) || (data182 === "detail")) || (data182 === "unknown"))){const err392 = {instancePath:instancePath+"/payload/channel",schemaPath:"#/properties/payload/properties/channel/enum",keyword:"enum",params:{allowedValues: schema47.properties.payload.properties.channel.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err392];}else {vErrors.push(err392);}errors++;}}if(data181.providerPhase !== undefined){if(typeof data181.providerPhase !== "string"){const err393 = {instancePath:instancePath+"/payload/providerPhase",schemaPath:"#/properties/payload/properties/providerPhase/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err393];}else {vErrors.push(err393);}errors++;}}if(data181.providerMethod !== undefined){if(typeof data181.providerMethod !== "string"){const err394 = {instancePath:instancePath+"/payload/providerMethod",schemaPath:"#/properties/payload/properties/providerMethod/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err394];}else {vErrors.push(err394);}errors++;}}}else {const err395 = {instancePath:instancePath+"/payload",schemaPath:"#/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err395];}else {vErrors.push(err395);}errors++;}}if(data.debug !== undefined){let data185 = data.debug;if(data185 && typeof data185 == "object" && !Array.isArray(data185)){}else {const err396 = {instancePath:instancePath+"/debug",schemaPath:"#/properties/debug/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err396];}else {vErrors.push(err396);}errors++;}}if(data.sourceEpochTransition !== undefined){let data186 = data.sourceEpochTransition;if(data186 && typeof data186 == "object" && !Array.isArray(data186)){if(data186.schema === undefined){const err397 = {instancePath:instancePath+"/sourceEpochTransition",schemaPath:"#/properties/sourceEpochTransition/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err397];}else {vErrors.push(err397);}errors++;}if(data186.runId === undefined){const err398 = {instancePath:instancePath+"/sourceEpochTransition",schemaPath:"#/properties/sourceEpochTransition/required",keyword:"required",params:{missingProperty: "runId"},message:"must have required property '"+"runId"+"'"};if(vErrors === null){vErrors = [err398];}else {vErrors.push(err398);}errors++;}if(data186.transitionId === undefined){const err399 = {instancePath:instancePath+"/sourceEpochTransition",schemaPath:"#/properties/sourceEpochTransition/required",keyword:"required",params:{missingProperty: "transitionId"},message:"must have required property '"+"transitionId"+"'"};if(vErrors === null){vErrors = [err399];}else {vErrors.push(err399);}errors++;}if(data186.fromEpoch === undefined){const err400 = {instancePath:instancePath+"/sourceEpochTransition",schemaPath:"#/properties/sourceEpochTransition/required",keyword:"required",params:{missingProperty: "fromEpoch"},message:"must have required property '"+"fromEpoch"+"'"};if(vErrors === null){vErrors = [err400];}else {vErrors.push(err400);}errors++;}if(data186.nextEpoch === undefined){const err401 = {instancePath:instancePath+"/sourceEpochTransition",schemaPath:"#/properties/sourceEpochTransition/required",keyword:"required",params:{missingProperty: "nextEpoch"},message:"must have required property '"+"nextEpoch"+"'"};if(vErrors === null){vErrors = [err401];}else {vErrors.push(err401);}errors++;}if(data186.finalOrdinal === undefined){const err402 = {instancePath:instancePath+"/sourceEpochTransition",schemaPath:"#/properties/sourceEpochTransition/required",keyword:"required",params:{missingProperty: "finalOrdinal"},message:"must have required property '"+"finalOrdinal"+"'"};if(vErrors === null){vErrors = [err402];}else {vErrors.push(err402);}errors++;}for(const key9 in data186){if(!((((((key9 === "schema") || (key9 === "runId")) || (key9 === "transitionId")) || (key9 === "fromEpoch")) || (key9 === "nextEpoch")) || (key9 === "finalOrdinal"))){const err403 = {instancePath:instancePath+"/sourceEpochTransition",schemaPath:"#/properties/sourceEpochTransition/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key9},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err403];}else {vErrors.push(err403);}errors++;}}if(data186.schema !== undefined){if("paperclip.prp.event-epoch.v1" !== data186.schema){const err404 = {instancePath:instancePath+"/sourceEpochTransition/schema",schemaPath:"#/properties/sourceEpochTransition/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.event-epoch.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err404];}else {vErrors.push(err404);}errors++;}}if(data186.runId !== undefined){let data188 = data186.runId;if(typeof data188 === "string"){if(func1(data188) > 240){const err405 = {instancePath:instancePath+"/sourceEpochTransition/runId",schemaPath:"#/properties/sourceEpochTransition/properties/runId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err405];}else {vErrors.push(err405);}errors++;}if(func1(data188) < 1){const err406 = {instancePath:instancePath+"/sourceEpochTransition/runId",schemaPath:"#/properties/sourceEpochTransition/properties/runId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err406];}else {vErrors.push(err406);}errors++;}}else {const err407 = {instancePath:instancePath+"/sourceEpochTransition/runId",schemaPath:"#/properties/sourceEpochTransition/properties/runId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err407];}else {vErrors.push(err407);}errors++;}}if(data186.transitionId !== undefined){let data189 = data186.transitionId;if(typeof data189 === "string"){if(!pattern15.test(data189)){const err408 = {instancePath:instancePath+"/sourceEpochTransition/transitionId",schemaPath:"#/properties/sourceEpochTransition/properties/transitionId/pattern",keyword:"pattern",params:{pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},message:"must match pattern \""+"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"+"\""};if(vErrors === null){vErrors = [err408];}else {vErrors.push(err408);}errors++;}}else {const err409 = {instancePath:instancePath+"/sourceEpochTransition/transitionId",schemaPath:"#/properties/sourceEpochTransition/properties/transitionId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err409];}else {vErrors.push(err409);}errors++;}}if(data186.fromEpoch !== undefined){let data190 = data186.fromEpoch;const _errs465 = errors;let valid139 = false;const _errs466 = errors;if(typeof data190 === "string"){if(!pattern15.test(data190)){const err410 = {instancePath:instancePath+"/sourceEpochTransition/fromEpoch",schemaPath:"#/properties/sourceEpochTransition/properties/fromEpoch/anyOf/0/pattern",keyword:"pattern",params:{pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},message:"must match pattern \""+"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"+"\""};if(vErrors === null){vErrors = [err410];}else {vErrors.push(err410);}errors++;}}else {const err411 = {instancePath:instancePath+"/sourceEpochTransition/fromEpoch",schemaPath:"#/properties/sourceEpochTransition/properties/fromEpoch/anyOf/0/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err411];}else {vErrors.push(err411);}errors++;}var _valid30 = _errs466 === errors;valid139 = valid139 || _valid30;const _errs468 = errors;if(data190 !== null){const err412 = {instancePath:instancePath+"/sourceEpochTransition/fromEpoch",schemaPath:"#/properties/sourceEpochTransition/properties/fromEpoch/anyOf/1/type",keyword:"type",params:{type: "null"},message:"must be null"};if(vErrors === null){vErrors = [err412];}else {vErrors.push(err412);}errors++;}var _valid30 = _errs468 === errors;valid139 = valid139 || _valid30;if(!valid139){const err413 = {instancePath:instancePath+"/sourceEpochTransition/fromEpoch",schemaPath:"#/properties/sourceEpochTransition/properties/fromEpoch/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};if(vErrors === null){vErrors = [err413];}else {vErrors.push(err413);}errors++;}else {errors = _errs465;if(vErrors !== null){if(_errs465){vErrors.length = _errs465;}else {vErrors = null;}}}}if(data186.nextEpoch !== undefined){let data191 = data186.nextEpoch;if(typeof data191 === "string"){if(!pattern15.test(data191)){const err414 = {instancePath:instancePath+"/sourceEpochTransition/nextEpoch",schemaPath:"#/properties/sourceEpochTransition/properties/nextEpoch/pattern",keyword:"pattern",params:{pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},message:"must match pattern \""+"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"+"\""};if(vErrors === null){vErrors = [err414];}else {vErrors.push(err414);}errors++;}}else {const err415 = {instancePath:instancePath+"/sourceEpochTransition/nextEpoch",schemaPath:"#/properties/sourceEpochTransition/properties/nextEpoch/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err415];}else {vErrors.push(err415);}errors++;}}if(data186.finalOrdinal !== undefined){let data192 = data186.finalOrdinal;if(!(((typeof data192 == "number") && (!(data192 % 1) && !isNaN(data192))) && (isFinite(data192)))){const err416 = {instancePath:instancePath+"/sourceEpochTransition/finalOrdinal",schemaPath:"#/properties/sourceEpochTransition/properties/finalOrdinal/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err416];}else {vErrors.push(err416);}errors++;}if((typeof data192 == "number") && (isFinite(data192))){if(data192 > 9007199254740991 || isNaN(data192)){const err417 = {instancePath:instancePath+"/sourceEpochTransition/finalOrdinal",schemaPath:"#/properties/sourceEpochTransition/properties/finalOrdinal/maximum",keyword:"maximum",params:{comparison: "<=", limit: 9007199254740991},message:"must be <= 9007199254740991"};if(vErrors === null){vErrors = [err417];}else {vErrors.push(err417);}errors++;}if(data192 < 1 || isNaN(data192)){const err418 = {instancePath:instancePath+"/sourceEpochTransition/finalOrdinal",schemaPath:"#/properties/sourceEpochTransition/properties/finalOrdinal/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err418];}else {vErrors.push(err418);}errors++;}}}}else {const err419 = {instancePath:instancePath+"/sourceEpochTransition",schemaPath:"#/properties/sourceEpochTransition/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err419];}else {vErrors.push(err419);}errors++;}}}else {const err420 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err420];}else {vErrors.push(err420);}errors++;}validate26.errors = vErrors;return errors === 0;}validate26.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema202 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v2/event.schema.json","title":"PRP v2 native event","type":"object","required":["schema","sourceEventId","sourceSeq","sourceInstanceId","sourceKind","runId","eventType","schemaVersion","priority","emittedAt","payload"],"properties":{"schema":{"const":"paperclip.prp.event.v2"},"sourceEventId":{"type":"string","minLength":1,"maxLength":160},"sourceEpoch":{"type":"string","pattern":"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},"sourceSeq":{"type":"integer","minimum":1,"maximum":9007199254740991},"sourceInstanceId":{"type":"string","minLength":1,"maxLength":160},"sourceKind":{"enum":["runner","control_plane"]},"runId":{"type":"string","minLength":1,"maxLength":160},"normalizedSessionId":{"type":"string","minLength":1,"maxLength":160},"turnId":{"type":"string","minLength":1,"maxLength":160},"itemId":{"type":"string","minLength":1,"maxLength":160},"eventType":{"enum":["runner.connected","runner.reconnected","runner.reconciled","runner.disconnected","runner.draining","runner.suspending","runner.suspended","runner.stopped","runner.diagnostic","runtime.phase.changed","sandbox.metric","workspace.ready","workspace.change.updated","workspace.diff.recorded","workspace.file.referenced","harness.starting","harness.ready","harness.exited","harness.diagnostic","plan.updated","tool.execution.started","tool.execution.progressed","tool.execution.completed","research.started","research.progressed","research.completed","delegation.started","delegation.updated","delegation.completed","model.route.changed","model.verification.updated","context.compacted","artifact.viewed","artifact.generated","review.mode.changed","hook.started","hook.completed","memory.citation.referenced","safety.review.started","safety.review.completed","terminal.input.sent","wait.started","wait.completed","provider.notice.recorded","session.starting","session.started","session.resuming","session.resumed","session.reconciled","session.updated","session.closed","session.failed","session.capabilities.updated","session.goal.snapshot","session.goal.updated","session.goal.cleared","turn.submitted","turn.accepted","turn.started","turn.completed","turn.failed","turn.interrupted","turn.cancelled","output.body.chunk","item.started","item.delta","item.completed","item.failed","usage.reported","semantic_tool.input","semantic_tool.result","mcp_app.discovered","mcp_app.resource.resolved","mcp_app.initializing","mcp_app.ready","mcp_app.tool_input","mcp_app.tool_result","mcp_app.action.requested","mcp_app.action.resolved","mcp_app.host_context.changed","mcp_app.failed","mcp_app.teardown","runtime_request.created","runtime_request.resolved","runtime_request.expired","runtime_request.cancelled","interaction.request.proposed","interaction.request.materialized","interaction.request.rejected","interaction.response.progressed","interaction.response.resolved","interaction.response.delivered","run.attached","run.detached","run.result.proposed","run.result.accepted","run.result.rejected","attention.request.proposed","attention.request.routed","attention.request.resolved","attention.request.expired","attention.request.superseded","work.assessment.recorded","issue.status.decision.recorded","issue.status.decision.applied","issue.status.decision.rejected","issue.status.decision.superseded","run.terminal"]},"schemaVersion":{"const":2},"priority":{"enum":[0,1,2]},"emittedAt":{"type":"string","format":"date-time"},"observedAt":{"type":"string","format":"date-time"},"payload":{"type":"object","additionalProperties":true},"debug":{"type":"object","additionalProperties":true},"sourceEpochTransition":{"type":"object","additionalProperties":false,"required":["schema","runId","transitionId","fromEpoch","nextEpoch","finalOrdinal"],"properties":{"schema":{"const":"paperclip.prp.event-epoch.v1"},"runId":{"type":"string","minLength":1,"maxLength":240},"transitionId":{"type":"string","pattern":"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},"fromEpoch":{"anyOf":[{"type":"string","pattern":"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},{"type":"null"}]},"nextEpoch":{"type":"string","pattern":"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},"finalOrdinal":{"type":"integer","minimum":1,"maximum":9007199254740991}}}},"allOf":[{"if":{"properties":{"eventType":{"enum":["tool.execution.started","tool.execution.progressed","tool.execution.completed"]}}},"then":{"properties":{"payload":{"$ref":"https://paperclip.dev/schemas/prp/v1/provider-event.schema.json#/$defs/toolExecution"}}}},{"if":{"properties":{"eventType":{"enum":["session.goal.snapshot","session.goal.updated"]}}},"then":{"properties":{"payload":{"type":"object","required":["goal"],"properties":{"goal":{"oneOf":[{"$ref":"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot"},{"type":"null"}]}},"additionalProperties":true}}}},{"if":{"properties":{"eventType":{"const":"session.capabilities.updated"}}},"then":{"properties":{"payload":{"type":"object","required":["sessionGoals"],"properties":{"sessionGoals":{"$ref":"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability"}},"additionalProperties":true}}}},{"if":{"properties":{"eventType":{"const":"output.body.chunk"}}},"then":{"properties":{"payload":{"type":"object","required":["schema","body","offset","sha256","text"],"properties":{"schema":{"const":"paperclip.output.body.chunk.v1"},"body":{"type":"object","required":["schema","bodyId","sha256","byteLength","mediaType"],"properties":{"schema":{"const":"paperclip.output.body.v1"},"bodyId":{"type":"string","pattern":"^[a-f0-9]{64}$"},"sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"},"byteLength":{"type":"string","pattern":"^[1-9][0-9]{0,6}$"},"mediaType":{"const":"text/plain; charset=utf-8"}},"additionalProperties":false},"offset":{"type":"string","pattern":"^(0|[1-9][0-9]{0,6})$"},"sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"},"text":{"type":"string","minLength":1,"maxLength":32766}},"additionalProperties":false}}}}],"additionalProperties":true};const schema209 = {"type":"object","required":["objective","status","tokenBudget","tokensUsed","elapsedSeconds","iterations","lastReason","createdAt","updatedAt","completedAt","workingNow"],"properties":{"objective":{"type":"string","minLength":1,"maxLength":4000},"status":{"enum":["active","paused","blocked","limited","usage_limited","budget_limited","complete"]},"tokenBudget":{"type":["integer","null"],"minimum":1},"tokensUsed":{"type":["integer","null"],"minimum":0},"elapsedSeconds":{"type":["number","null"],"minimum":0},"iterations":{"type":["integer","null"],"minimum":0},"lastReason":{"type":["string","null"],"maxLength":4000},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"},"completedAt":{"type":["string","null"],"format":"date-time"},"workingNow":{"type":"boolean"}},"additionalProperties":true};function validate110(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate110.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.executionId === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "executionId"},message:"must have required property '"+"executionId"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.transport === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "transport"},message:"must have required property '"+"transport"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.operation === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "operation"},message:"must have required property '"+"operation"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.status === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.output === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "output"},message:"must have required property '"+"output"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.outputBytes === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "outputBytes"},message:"must have required property '"+"outputBytes"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(data.outputTruncated === undefined){const err7 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "outputTruncated"},message:"must have required property '"+"outputTruncated"+"'"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(data.outputDigest === undefined){const err8 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "outputDigest"},message:"must have required property '"+"outputDigest"+"'"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}for(const key0 in data){if(!(func58.call(schema54.properties, key0))){const err9 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}if(data.schema !== undefined){if("paperclip.tool.execution.v1" !== data.schema){const err10 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.tool.execution.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.executionId !== undefined){let data1 = data.executionId;if(typeof data1 === "string"){if(func1(data1) > 160){const err11 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(func1(data1) < 1){const err12 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(!pattern4.test(data1)){const err13 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}else {const err14 = {instancePath:instancePath+"/executionId",schemaPath:"#/$defs/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}if(data.transport !== undefined){let data2 = data.transport;if(!((((data2 === "process") || (data2 === "mcp")) || (data2 === "dynamic")) || (data2 === "builtin"))){const err15 = {instancePath:instancePath+"/transport",schemaPath:"#/properties/transport/enum",keyword:"enum",params:{allowedValues: schema54.properties.transport.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}if(data.operation !== undefined){let data3 = data.operation;if(!((((((data3 === "read") || (data3 === "search")) || (data3 === "list")) || (data3 === "execute")) || (data3 === "edit")) || (data3 === "unknown"))){const err16 = {instancePath:instancePath+"/operation",schemaPath:"#/properties/operation/enum",keyword:"enum",params:{allowedValues: schema54.properties.operation.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}if(data.name !== undefined){let data4 = data.name;if((typeof data4 !== "string") && (data4 !== null)){const err17 = {instancePath:instancePath+"/name",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}if(typeof data4 === "string"){if(func1(data4) > 4000){const err18 = {instancePath:instancePath+"/name",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}}if(data.target !== undefined){let data5 = data.target;if((typeof data5 !== "string") && (data5 !== null)){const err19 = {instancePath:instancePath+"/target",schemaPath:"#/$defs/safePath/type",keyword:"type",params:{type: schema57.type},message:"must be string,null"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}if(typeof data5 === "string"){if(func1(data5) > 4096){const err20 = {instancePath:instancePath+"/target",schemaPath:"#/$defs/safePath/maxLength",keyword:"maxLength",params:{limit: 4096},message:"must NOT have more than 4096 characters"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}if(!pattern20.test(data5)){const err21 = {instancePath:instancePath+"/target",schemaPath:"#/$defs/safePath/pattern",keyword:"pattern",params:{pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$"},message:"must match pattern \""+"^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$"+"\""};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}}}if(data.namespace !== undefined){let data6 = data.namespace;if((typeof data6 !== "string") && (data6 !== null)){const err22 = {instancePath:instancePath+"/namespace",schemaPath:"#/properties/namespace/type",keyword:"type",params:{type: schema54.properties.namespace.type},message:"must be string,null"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}if(typeof data6 === "string"){if(func1(data6) > 240){const err23 = {instancePath:instancePath+"/namespace",schemaPath:"#/properties/namespace/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}}if(data.readOnly !== undefined){let data7 = data.readOnly;if((typeof data7 !== "boolean") && (data7 !== null)){const err24 = {instancePath:instancePath+"/readOnly",schemaPath:"#/properties/readOnly/type",keyword:"type",params:{type: schema54.properties.readOnly.type},message:"must be boolean,null"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}if(data.inputUpdated !== undefined){if(typeof data.inputUpdated !== "boolean"){const err25 = {instancePath:instancePath+"/inputUpdated",schemaPath:"#/properties/inputUpdated/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}if(data.status !== undefined){let data9 = data.status;if(!(((((data9 === "running") || (data9 === "completed")) || (data9 === "failed")) || (data9 === "cancelled")) || (data9 === "interrupted"))){const err26 = {instancePath:instancePath+"/status",schemaPath:"#/properties/status/enum",keyword:"enum",params:{allowedValues: schema54.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}}if(data.durationMs !== undefined){let data10 = data.durationMs;if((!(((typeof data10 == "number") && (!(data10 % 1) && !isNaN(data10))) && (isFinite(data10)))) && (data10 !== null)){const err27 = {instancePath:instancePath+"/durationMs",schemaPath:"#/properties/durationMs/type",keyword:"type",params:{type: schema54.properties.durationMs.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if((typeof data10 == "number") && (isFinite(data10))){if(data10 < 0 || isNaN(data10)){const err28 = {instancePath:instancePath+"/durationMs",schemaPath:"#/properties/durationMs/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}}if(data.exitCode !== undefined){let data11 = data.exitCode;if((!(((typeof data11 == "number") && (!(data11 % 1) && !isNaN(data11))) && (isFinite(data11)))) && (data11 !== null)){const err29 = {instancePath:instancePath+"/exitCode",schemaPath:"#/properties/exitCode/type",keyword:"type",params:{type: schema54.properties.exitCode.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}}if(data.progress !== undefined){let data12 = data.progress;if((typeof data12 !== "string") && (data12 !== null)){const err30 = {instancePath:instancePath+"/progress",schemaPath:"#/$defs/nullableShortText/type",keyword:"type",params:{type: schema51.type},message:"must be string,null"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}if(typeof data12 === "string"){if(func1(data12) > 4000){const err31 = {instancePath:instancePath+"/progress",schemaPath:"#/$defs/nullableShortText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}}}if(data.output !== undefined){let data13 = data.output;if((typeof data13 !== "string") && (data13 !== null)){const err32 = {instancePath:instancePath+"/output",schemaPath:"#/properties/output/type",keyword:"type",params:{type: schema54.properties.output.type},message:"must be string,null"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}if(typeof data13 === "string"){if(func1(data13) > 65536){const err33 = {instancePath:instancePath+"/output",schemaPath:"#/properties/output/maxLength",keyword:"maxLength",params:{limit: 65536},message:"must NOT have more than 65536 characters"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}}}if(data.outputBytes !== undefined){let data14 = data.outputBytes;if(!(((typeof data14 == "number") && (!(data14 % 1) && !isNaN(data14))) && (isFinite(data14)))){const err34 = {instancePath:instancePath+"/outputBytes",schemaPath:"#/properties/outputBytes/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}if((typeof data14 == "number") && (isFinite(data14))){if(data14 < 0 || isNaN(data14)){const err35 = {instancePath:instancePath+"/outputBytes",schemaPath:"#/properties/outputBytes/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}}}if(data.outputTruncated !== undefined){if(typeof data.outputTruncated !== "boolean"){const err36 = {instancePath:instancePath+"/outputTruncated",schemaPath:"#/properties/outputTruncated/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}}if(data.outputDigest !== undefined){let data16 = data.outputDigest;if((typeof data16 !== "string") && (data16 !== null)){const err37 = {instancePath:instancePath+"/outputDigest",schemaPath:"#/properties/outputDigest/type",keyword:"type",params:{type: schema54.properties.outputDigest.type},message:"must be string,null"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}if(typeof data16 === "string"){if(!pattern21.test(data16)){const err38 = {instancePath:instancePath+"/outputDigest",schemaPath:"#/properties/outputDigest/pattern",keyword:"pattern",params:{pattern: "^sha256:[a-f0-9]{64}$"},message:"must match pattern \""+"^sha256:[a-f0-9]{64}$"+"\""};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}}}if(data.outputBody !== undefined){let data17 = data.outputBody;if(data17 && typeof data17 == "object" && !Array.isArray(data17)){if(data17.schema === undefined){const err39 = {instancePath:instancePath+"/outputBody",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}if(data17.bodyId === undefined){const err40 = {instancePath:instancePath+"/outputBody",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/required",keyword:"required",params:{missingProperty: "bodyId"},message:"must have required property '"+"bodyId"+"'"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}if(data17.sha256 === undefined){const err41 = {instancePath:instancePath+"/outputBody",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/required",keyword:"required",params:{missingProperty: "sha256"},message:"must have required property '"+"sha256"+"'"};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}if(data17.byteLength === undefined){const err42 = {instancePath:instancePath+"/outputBody",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/required",keyword:"required",params:{missingProperty: "byteLength"},message:"must have required property '"+"byteLength"+"'"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}if(data17.mediaType === undefined){const err43 = {instancePath:instancePath+"/outputBody",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/required",keyword:"required",params:{missingProperty: "mediaType"},message:"must have required property '"+"mediaType"+"'"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}for(const key1 in data17){if(!(((((key1 === "schema") || (key1 === "bodyId")) || (key1 === "sha256")) || (key1 === "byteLength")) || (key1 === "mediaType"))){const err44 = {instancePath:instancePath+"/outputBody",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key1},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err44];}else {vErrors.push(err44);}errors++;}}if(data17.schema !== undefined){if("paperclip.output.body.v1" !== data17.schema){const err45 = {instancePath:instancePath+"/outputBody/schema",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.output.body.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err45];}else {vErrors.push(err45);}errors++;}}if(data17.bodyId !== undefined){let data19 = data17.bodyId;if(typeof data19 === "string"){if(!pattern22.test(data19)){const err46 = {instancePath:instancePath+"/outputBody/bodyId",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/properties/bodyId/pattern",keyword:"pattern",params:{pattern: "^[a-f0-9]{64}$"},message:"must match pattern \""+"^[a-f0-9]{64}$"+"\""};if(vErrors === null){vErrors = [err46];}else {vErrors.push(err46);}errors++;}}else {const err47 = {instancePath:instancePath+"/outputBody/bodyId",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/properties/bodyId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err47];}else {vErrors.push(err47);}errors++;}}if(data17.sha256 !== undefined){let data20 = data17.sha256;if(typeof data20 === "string"){if(!pattern22.test(data20)){const err48 = {instancePath:instancePath+"/outputBody/sha256",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/properties/sha256/pattern",keyword:"pattern",params:{pattern: "^[a-f0-9]{64}$"},message:"must match pattern \""+"^[a-f0-9]{64}$"+"\""};if(vErrors === null){vErrors = [err48];}else {vErrors.push(err48);}errors++;}}else {const err49 = {instancePath:instancePath+"/outputBody/sha256",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/properties/sha256/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err49];}else {vErrors.push(err49);}errors++;}}if(data17.byteLength !== undefined){let data21 = data17.byteLength;if(typeof data21 === "string"){if(!pattern24.test(data21)){const err50 = {instancePath:instancePath+"/outputBody/byteLength",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/properties/byteLength/pattern",keyword:"pattern",params:{pattern: "^[1-9][0-9]{0,6}$"},message:"must match pattern \""+"^[1-9][0-9]{0,6}$"+"\""};if(vErrors === null){vErrors = [err50];}else {vErrors.push(err50);}errors++;}}else {const err51 = {instancePath:instancePath+"/outputBody/byteLength",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/properties/byteLength/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err51];}else {vErrors.push(err51);}errors++;}}if(data17.mediaType !== undefined){if("text/plain; charset=utf-8" !== data17.mediaType){const err52 = {instancePath:instancePath+"/outputBody/mediaType",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/properties/mediaType/const",keyword:"const",params:{allowedValue: "text/plain; charset=utf-8"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err52];}else {vErrors.push(err52);}errors++;}}}else {const err53 = {instancePath:instancePath+"/outputBody",schemaPath:"https://paperclip.dev/schemas/prp/v1/output-body-reference.schema.json/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err53];}else {vErrors.push(err53);}errors++;}}}else {const err54 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err54];}else {vErrors.push(err54);}errors++;}validate110.errors = vErrors;return errors === 0;}validate110.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};function validate109(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){/*# sourceURL="https://paperclip.dev/schemas/prp/v2/event.schema.json" */;let vErrors = null;let errors = 0;const evaluated0 = validate109.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}const _errs2 = errors;let valid1 = true;const _errs3 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data0 = data.eventType;if(!(((data0 === "tool.execution.started") || (data0 === "tool.execution.progressed")) || (data0 === "tool.execution.completed"))){const err0 = {};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}}}var _valid0 = _errs3 === errors;errors = _errs2;if(vErrors !== null){if(_errs2){vErrors.length = _errs2;}else {vErrors = null;}}if(_valid0){const _errs5 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){if(!(validate110(data.payload, {instancePath:instancePath+"/payload",parentData:data,parentDataProperty:"payload",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate110.errors : vErrors.concat(validate110.errors);errors = vErrors.length;}}}var _valid0 = _errs5 === errors;valid1 = _valid0;if(valid1){var props0 = {};props0.payload = true;props0.eventType = true;}}if(!valid1){const err1 = {instancePath,schemaPath:"#/allOf/0/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}const _errs8 = errors;let valid4 = true;const _errs9 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){let data2 = data.eventType;if(!((data2 === "session.goal.snapshot") || (data2 === "session.goal.updated"))){const err2 = {};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}}}var _valid1 = _errs9 === errors;errors = _errs8;if(vErrors !== null){if(_errs8){vErrors.length = _errs8;}else {vErrors = null;}}if(_valid1){const _errs11 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data3 = data.payload;if(data3 && typeof data3 == "object" && !Array.isArray(data3)){if(data3.goal === undefined){const err3 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/1/then/properties/payload/required",keyword:"required",params:{missingProperty: "goal"},message:"must have required property '"+"goal"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data3.goal !== undefined){let data4 = data3.goal;const _errs16 = errors;let valid8 = false;let passing0 = null;const _errs17 = errors;if(data4 && typeof data4 == "object" && !Array.isArray(data4)){if(data4.objective === undefined){const err4 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "objective"},message:"must have required property '"+"objective"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data4.status === undefined){const err5 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data4.tokenBudget === undefined){const err6 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "tokenBudget"},message:"must have required property '"+"tokenBudget"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(data4.tokensUsed === undefined){const err7 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "tokensUsed"},message:"must have required property '"+"tokensUsed"+"'"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(data4.elapsedSeconds === undefined){const err8 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "elapsedSeconds"},message:"must have required property '"+"elapsedSeconds"+"'"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(data4.iterations === undefined){const err9 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "iterations"},message:"must have required property '"+"iterations"+"'"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(data4.lastReason === undefined){const err10 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "lastReason"},message:"must have required property '"+"lastReason"+"'"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(data4.createdAt === undefined){const err11 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "createdAt"},message:"must have required property '"+"createdAt"+"'"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}if(data4.updatedAt === undefined){const err12 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "updatedAt"},message:"must have required property '"+"updatedAt"+"'"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(data4.completedAt === undefined){const err13 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "completedAt"},message:"must have required property '"+"completedAt"+"'"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(data4.workingNow === undefined){const err14 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/required",keyword:"required",params:{missingProperty: "workingNow"},message:"must have required property '"+"workingNow"+"'"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(data4.objective !== undefined){let data5 = data4.objective;if(typeof data5 === "string"){if(func1(data5) > 4000){const err15 = {instancePath:instancePath+"/payload/goal/objective",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/objective/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}if(func1(data5) < 1){const err16 = {instancePath:instancePath+"/payload/goal/objective",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/objective/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}else {const err17 = {instancePath:instancePath+"/payload/goal/objective",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/objective/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}if(data4.status !== undefined){let data6 = data4.status;if(!(((((((data6 === "active") || (data6 === "paused")) || (data6 === "blocked")) || (data6 === "limited")) || (data6 === "usage_limited")) || (data6 === "budget_limited")) || (data6 === "complete"))){const err18 = {instancePath:instancePath+"/payload/goal/status",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/status/enum",keyword:"enum",params:{allowedValues: schema209.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}if(data4.tokenBudget !== undefined){let data7 = data4.tokenBudget;if((!(((typeof data7 == "number") && (!(data7 % 1) && !isNaN(data7))) && (isFinite(data7)))) && (data7 !== null)){const err19 = {instancePath:instancePath+"/payload/goal/tokenBudget",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/tokenBudget/type",keyword:"type",params:{type: schema209.properties.tokenBudget.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}if((typeof data7 == "number") && (isFinite(data7))){if(data7 < 1 || isNaN(data7)){const err20 = {instancePath:instancePath+"/payload/goal/tokenBudget",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/tokenBudget/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}}if(data4.tokensUsed !== undefined){let data8 = data4.tokensUsed;if((!(((typeof data8 == "number") && (!(data8 % 1) && !isNaN(data8))) && (isFinite(data8)))) && (data8 !== null)){const err21 = {instancePath:instancePath+"/payload/goal/tokensUsed",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/tokensUsed/type",keyword:"type",params:{type: schema209.properties.tokensUsed.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if((typeof data8 == "number") && (isFinite(data8))){if(data8 < 0 || isNaN(data8)){const err22 = {instancePath:instancePath+"/payload/goal/tokensUsed",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/tokensUsed/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}}if(data4.elapsedSeconds !== undefined){let data9 = data4.elapsedSeconds;if((!((typeof data9 == "number") && (isFinite(data9)))) && (data9 !== null)){const err23 = {instancePath:instancePath+"/payload/goal/elapsedSeconds",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/elapsedSeconds/type",keyword:"type",params:{type: schema209.properties.elapsedSeconds.type},message:"must be number,null"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}if((typeof data9 == "number") && (isFinite(data9))){if(data9 < 0 || isNaN(data9)){const err24 = {instancePath:instancePath+"/payload/goal/elapsedSeconds",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/elapsedSeconds/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}}if(data4.iterations !== undefined){let data10 = data4.iterations;if((!(((typeof data10 == "number") && (!(data10 % 1) && !isNaN(data10))) && (isFinite(data10)))) && (data10 !== null)){const err25 = {instancePath:instancePath+"/payload/goal/iterations",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/iterations/type",keyword:"type",params:{type: schema209.properties.iterations.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}if((typeof data10 == "number") && (isFinite(data10))){if(data10 < 0 || isNaN(data10)){const err26 = {instancePath:instancePath+"/payload/goal/iterations",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/iterations/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}}}if(data4.lastReason !== undefined){let data11 = data4.lastReason;if((typeof data11 !== "string") && (data11 !== null)){const err27 = {instancePath:instancePath+"/payload/goal/lastReason",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/lastReason/type",keyword:"type",params:{type: schema209.properties.lastReason.type},message:"must be string,null"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if(typeof data11 === "string"){if(func1(data11) > 4000){const err28 = {instancePath:instancePath+"/payload/goal/lastReason",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/lastReason/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}}if(data4.createdAt !== undefined){let data12 = data4.createdAt;if((typeof data12 !== "string") && (data12 !== null)){const err29 = {instancePath:instancePath+"/payload/goal/createdAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/createdAt/type",keyword:"type",params:{type: schema209.properties.createdAt.type},message:"must be string,null"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}if(typeof data12 === "string"){if(!(formats0.test(data12))){const err30 = {instancePath:instancePath+"/payload/goal/createdAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/createdAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}}}if(data4.updatedAt !== undefined){let data13 = data4.updatedAt;if((typeof data13 !== "string") && (data13 !== null)){const err31 = {instancePath:instancePath+"/payload/goal/updatedAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/updatedAt/type",keyword:"type",params:{type: schema209.properties.updatedAt.type},message:"must be string,null"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}if(typeof data13 === "string"){if(!(formats0.test(data13))){const err32 = {instancePath:instancePath+"/payload/goal/updatedAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/updatedAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}}}if(data4.completedAt !== undefined){let data14 = data4.completedAt;if((typeof data14 !== "string") && (data14 !== null)){const err33 = {instancePath:instancePath+"/payload/goal/completedAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/completedAt/type",keyword:"type",params:{type: schema209.properties.completedAt.type},message:"must be string,null"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}if(typeof data14 === "string"){if(!(formats0.test(data14))){const err34 = {instancePath:instancePath+"/payload/goal/completedAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/completedAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}}}if(data4.workingNow !== undefined){if(typeof data4.workingNow !== "boolean"){const err35 = {instancePath:instancePath+"/payload/goal/workingNow",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/properties/workingNow/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}}}else {const err36 = {instancePath:instancePath+"/payload/goal",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/snapshot/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}var _valid2 = _errs17 === errors;if(_valid2){valid8 = true;passing0 = 0;}const _errs42 = errors;if(data4 !== null){const err37 = {instancePath:instancePath+"/payload/goal",schemaPath:"#/allOf/1/then/properties/payload/properties/goal/oneOf/1/type",keyword:"type",params:{type: "null"},message:"must be null"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}var _valid2 = _errs42 === errors;if(_valid2 && valid8){valid8 = false;passing0 = [passing0, 1];}else {if(_valid2){valid8 = true;passing0 = 1;}}if(!valid8){const err38 = {instancePath:instancePath+"/payload/goal",schemaPath:"#/allOf/1/then/properties/payload/properties/goal/oneOf",keyword:"oneOf",params:{passingSchemas: passing0},message:"must match exactly one schema in oneOf"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}else {errors = _errs16;if(vErrors !== null){if(_errs16){vErrors.length = _errs16;}else {vErrors = null;}}}}}else {const err39 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/1/then/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}}}var _valid1 = _errs11 === errors;valid4 = _valid1;if(valid4){var props2 = {};props2.payload = true;props2.eventType = true;}}if(!valid4){const err40 = {instancePath,schemaPath:"#/allOf/1/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}if(props0 !== true && props2 !== undefined){if(props2 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props2);}}const _errs45 = errors;let valid11 = true;const _errs46 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("session.capabilities.updated" !== data.eventType){const err41 = {};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}}}var _valid3 = _errs46 === errors;errors = _errs45;if(vErrors !== null){if(_errs45){vErrors.length = _errs45;}else {vErrors = null;}}if(_valid3){const _errs48 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data17 = data.payload;if(data17 && typeof data17 == "object" && !Array.isArray(data17)){if(data17.sessionGoals === undefined){const err42 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/2/then/properties/payload/required",keyword:"required",params:{missingProperty: "sessionGoals"},message:"must have required property '"+"sessionGoals"+"'"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}if(data17.sessionGoals !== undefined){let data18 = data17.sessionGoals;if(data18 && typeof data18 == "object" && !Array.isArray(data18)){if(data18.availability === undefined){const err43 = {instancePath:instancePath+"/payload/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "availability"},message:"must have required property '"+"availability"+"'"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}if(data18.actions === undefined){const err44 = {instancePath:instancePath+"/payload/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "actions"},message:"must have required property '"+"actions"+"'"};if(vErrors === null){vErrors = [err44];}else {vErrors.push(err44);}errors++;}if(data18.autonomousUpdates === undefined){const err45 = {instancePath:instancePath+"/payload/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "autonomousUpdates"},message:"must have required property '"+"autonomousUpdates"+"'"};if(vErrors === null){vErrors = [err45];}else {vErrors.push(err45);}errors++;}if(data18.persistentAcrossResume === undefined){const err46 = {instancePath:instancePath+"/payload/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "persistentAcrossResume"},message:"must have required property '"+"persistentAcrossResume"+"'"};if(vErrors === null){vErrors = [err46];}else {vErrors.push(err46);}errors++;}if(data18.maxObjectiveChars === undefined){const err47 = {instancePath:instancePath+"/payload/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "maxObjectiveChars"},message:"must have required property '"+"maxObjectiveChars"+"'"};if(vErrors === null){vErrors = [err47];}else {vErrors.push(err47);}errors++;}if(data18.tokenBudgetControl === undefined){const err48 = {instancePath:instancePath+"/payload/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "tokenBudgetControl"},message:"must have required property '"+"tokenBudgetControl"+"'"};if(vErrors === null){vErrors = [err48];}else {vErrors.push(err48);}errors++;}if(data18.usageReporting === undefined){const err49 = {instancePath:instancePath+"/payload/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/required",keyword:"required",params:{missingProperty: "usageReporting"},message:"must have required property '"+"usageReporting"+"'"};if(vErrors === null){vErrors = [err49];}else {vErrors.push(err49);}errors++;}if(data18.availability !== undefined){let data19 = data18.availability;if(!(((data19 === "available") || (data19 === "unsupported")) || (data19 === "policy_disabled"))){const err50 = {instancePath:instancePath+"/payload/sessionGoals/availability",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/availability/enum",keyword:"enum",params:{allowedValues: schema44.properties.availability.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err50];}else {vErrors.push(err50);}errors++;}}if(data18.actions !== undefined){let data20 = data18.actions;if(Array.isArray(data20)){const len0 = data20.length;for(let i0=0; i0 1){outer0:for(;i1--;){for(j0 = i1; j0--;){if(func0(data20[i1], data20[j0])){const err52 = {instancePath:instancePath+"/payload/sessionGoals/actions",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/actions/uniqueItems",keyword:"uniqueItems",params:{i: i1, j: j0},message:"must NOT have duplicate items (items ## "+j0+" and "+i1+" are identical)"};if(vErrors === null){vErrors = [err52];}else {vErrors.push(err52);}errors++;break outer0;}}}}}else {const err53 = {instancePath:instancePath+"/payload/sessionGoals/actions",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/actions/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err53];}else {vErrors.push(err53);}errors++;}}if(data18.autonomousUpdates !== undefined){if(typeof data18.autonomousUpdates !== "boolean"){const err54 = {instancePath:instancePath+"/payload/sessionGoals/autonomousUpdates",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/autonomousUpdates/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err54];}else {vErrors.push(err54);}errors++;}}if(data18.persistentAcrossResume !== undefined){if(typeof data18.persistentAcrossResume !== "boolean"){const err55 = {instancePath:instancePath+"/payload/sessionGoals/persistentAcrossResume",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/persistentAcrossResume/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err55];}else {vErrors.push(err55);}errors++;}}if(data18.maxObjectiveChars !== undefined){let data24 = data18.maxObjectiveChars;if(!(((typeof data24 == "number") && (!(data24 % 1) && !isNaN(data24))) && (isFinite(data24)))){const err56 = {instancePath:instancePath+"/payload/sessionGoals/maxObjectiveChars",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/maxObjectiveChars/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err56];}else {vErrors.push(err56);}errors++;}if((typeof data24 == "number") && (isFinite(data24))){if(data24 > 4000 || isNaN(data24)){const err57 = {instancePath:instancePath+"/payload/sessionGoals/maxObjectiveChars",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/maxObjectiveChars/maximum",keyword:"maximum",params:{comparison: "<=", limit: 4000},message:"must be <= 4000"};if(vErrors === null){vErrors = [err57];}else {vErrors.push(err57);}errors++;}if(data24 < 1 || isNaN(data24)){const err58 = {instancePath:instancePath+"/payload/sessionGoals/maxObjectiveChars",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/maxObjectiveChars/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err58];}else {vErrors.push(err58);}errors++;}}}if(data18.tokenBudgetControl !== undefined){if(typeof data18.tokenBudgetControl !== "boolean"){const err59 = {instancePath:instancePath+"/payload/sessionGoals/tokenBudgetControl",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/tokenBudgetControl/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err59];}else {vErrors.push(err59);}errors++;}}if(data18.usageReporting !== undefined){if(typeof data18.usageReporting !== "boolean"){const err60 = {instancePath:instancePath+"/payload/sessionGoals/usageReporting",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/usageReporting/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err60];}else {vErrors.push(err60);}errors++;}}if(data18.reasonCode !== undefined){let data27 = data18.reasonCode;if(typeof data27 === "string"){if(func1(data27) > 160){const err61 = {instancePath:instancePath+"/payload/sessionGoals/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reasonCode/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err61];}else {vErrors.push(err61);}errors++;}if(func1(data27) < 1){const err62 = {instancePath:instancePath+"/payload/sessionGoals/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reasonCode/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err62];}else {vErrors.push(err62);}errors++;}}else {const err63 = {instancePath:instancePath+"/payload/sessionGoals/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reasonCode/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err63];}else {vErrors.push(err63);}errors++;}}if(data18.reason !== undefined){let data28 = data18.reason;if(typeof data28 === "string"){if(func1(data28) > 1000){const err64 = {instancePath:instancePath+"/payload/sessionGoals/reason",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reason/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err64];}else {vErrors.push(err64);}errors++;}if(func1(data28) < 1){const err65 = {instancePath:instancePath+"/payload/sessionGoals/reason",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reason/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err65];}else {vErrors.push(err65);}errors++;}}else {const err66 = {instancePath:instancePath+"/payload/sessionGoals/reason",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/properties/reason/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err66];}else {vErrors.push(err66);}errors++;}}}else {const err67 = {instancePath:instancePath+"/payload/sessionGoals",schemaPath:"https://paperclip.dev/schemas/prp/v2/session-goal.schema.json#/$defs/capability/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err67];}else {vErrors.push(err67);}errors++;}}}else {const err68 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/2/then/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err68];}else {vErrors.push(err68);}errors++;}}}var _valid3 = _errs48 === errors;valid11 = _valid3;if(valid11){var props3 = {};props3.payload = true;props3.eventType = true;}}if(!valid11){const err69 = {instancePath,schemaPath:"#/allOf/2/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err69];}else {vErrors.push(err69);}errors++;}if(props0 !== true && props3 !== undefined){if(props3 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props3);}}const _errs75 = errors;let valid20 = true;const _errs76 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.eventType !== undefined){if("output.body.chunk" !== data.eventType){const err70 = {};if(vErrors === null){vErrors = [err70];}else {vErrors.push(err70);}errors++;}}}var _valid4 = _errs76 === errors;errors = _errs75;if(vErrors !== null){if(_errs75){vErrors.length = _errs75;}else {vErrors = null;}}if(_valid4){const _errs78 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.payload !== undefined){let data30 = data.payload;if(data30 && typeof data30 == "object" && !Array.isArray(data30)){if(data30.schema === undefined){const err71 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/3/then/properties/payload/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err71];}else {vErrors.push(err71);}errors++;}if(data30.body === undefined){const err72 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/3/then/properties/payload/required",keyword:"required",params:{missingProperty: "body"},message:"must have required property '"+"body"+"'"};if(vErrors === null){vErrors = [err72];}else {vErrors.push(err72);}errors++;}if(data30.offset === undefined){const err73 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/3/then/properties/payload/required",keyword:"required",params:{missingProperty: "offset"},message:"must have required property '"+"offset"+"'"};if(vErrors === null){vErrors = [err73];}else {vErrors.push(err73);}errors++;}if(data30.sha256 === undefined){const err74 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/3/then/properties/payload/required",keyword:"required",params:{missingProperty: "sha256"},message:"must have required property '"+"sha256"+"'"};if(vErrors === null){vErrors = [err74];}else {vErrors.push(err74);}errors++;}if(data30.text === undefined){const err75 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/3/then/properties/payload/required",keyword:"required",params:{missingProperty: "text"},message:"must have required property '"+"text"+"'"};if(vErrors === null){vErrors = [err75];}else {vErrors.push(err75);}errors++;}for(const key0 in data30){if(!(((((key0 === "schema") || (key0 === "body")) || (key0 === "offset")) || (key0 === "sha256")) || (key0 === "text"))){const err76 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/3/then/properties/payload/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err76];}else {vErrors.push(err76);}errors++;}}if(data30.schema !== undefined){if("paperclip.output.body.chunk.v1" !== data30.schema){const err77 = {instancePath:instancePath+"/payload/schema",schemaPath:"#/allOf/3/then/properties/payload/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.output.body.chunk.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err77];}else {vErrors.push(err77);}errors++;}}if(data30.body !== undefined){let data32 = data30.body;if(data32 && typeof data32 == "object" && !Array.isArray(data32)){if(data32.schema === undefined){const err78 = {instancePath:instancePath+"/payload/body",schemaPath:"#/allOf/3/then/properties/payload/properties/body/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err78];}else {vErrors.push(err78);}errors++;}if(data32.bodyId === undefined){const err79 = {instancePath:instancePath+"/payload/body",schemaPath:"#/allOf/3/then/properties/payload/properties/body/required",keyword:"required",params:{missingProperty: "bodyId"},message:"must have required property '"+"bodyId"+"'"};if(vErrors === null){vErrors = [err79];}else {vErrors.push(err79);}errors++;}if(data32.sha256 === undefined){const err80 = {instancePath:instancePath+"/payload/body",schemaPath:"#/allOf/3/then/properties/payload/properties/body/required",keyword:"required",params:{missingProperty: "sha256"},message:"must have required property '"+"sha256"+"'"};if(vErrors === null){vErrors = [err80];}else {vErrors.push(err80);}errors++;}if(data32.byteLength === undefined){const err81 = {instancePath:instancePath+"/payload/body",schemaPath:"#/allOf/3/then/properties/payload/properties/body/required",keyword:"required",params:{missingProperty: "byteLength"},message:"must have required property '"+"byteLength"+"'"};if(vErrors === null){vErrors = [err81];}else {vErrors.push(err81);}errors++;}if(data32.mediaType === undefined){const err82 = {instancePath:instancePath+"/payload/body",schemaPath:"#/allOf/3/then/properties/payload/properties/body/required",keyword:"required",params:{missingProperty: "mediaType"},message:"must have required property '"+"mediaType"+"'"};if(vErrors === null){vErrors = [err82];}else {vErrors.push(err82);}errors++;}for(const key1 in data32){if(!(((((key1 === "schema") || (key1 === "bodyId")) || (key1 === "sha256")) || (key1 === "byteLength")) || (key1 === "mediaType"))){const err83 = {instancePath:instancePath+"/payload/body",schemaPath:"#/allOf/3/then/properties/payload/properties/body/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key1},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err83];}else {vErrors.push(err83);}errors++;}}if(data32.schema !== undefined){if("paperclip.output.body.v1" !== data32.schema){const err84 = {instancePath:instancePath+"/payload/body/schema",schemaPath:"#/allOf/3/then/properties/payload/properties/body/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.output.body.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err84];}else {vErrors.push(err84);}errors++;}}if(data32.bodyId !== undefined){let data34 = data32.bodyId;if(typeof data34 === "string"){if(!pattern22.test(data34)){const err85 = {instancePath:instancePath+"/payload/body/bodyId",schemaPath:"#/allOf/3/then/properties/payload/properties/body/properties/bodyId/pattern",keyword:"pattern",params:{pattern: "^[a-f0-9]{64}$"},message:"must match pattern \""+"^[a-f0-9]{64}$"+"\""};if(vErrors === null){vErrors = [err85];}else {vErrors.push(err85);}errors++;}}else {const err86 = {instancePath:instancePath+"/payload/body/bodyId",schemaPath:"#/allOf/3/then/properties/payload/properties/body/properties/bodyId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err86];}else {vErrors.push(err86);}errors++;}}if(data32.sha256 !== undefined){let data35 = data32.sha256;if(typeof data35 === "string"){if(!pattern22.test(data35)){const err87 = {instancePath:instancePath+"/payload/body/sha256",schemaPath:"#/allOf/3/then/properties/payload/properties/body/properties/sha256/pattern",keyword:"pattern",params:{pattern: "^[a-f0-9]{64}$"},message:"must match pattern \""+"^[a-f0-9]{64}$"+"\""};if(vErrors === null){vErrors = [err87];}else {vErrors.push(err87);}errors++;}}else {const err88 = {instancePath:instancePath+"/payload/body/sha256",schemaPath:"#/allOf/3/then/properties/payload/properties/body/properties/sha256/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err88];}else {vErrors.push(err88);}errors++;}}if(data32.byteLength !== undefined){let data36 = data32.byteLength;if(typeof data36 === "string"){if(!pattern24.test(data36)){const err89 = {instancePath:instancePath+"/payload/body/byteLength",schemaPath:"#/allOf/3/then/properties/payload/properties/body/properties/byteLength/pattern",keyword:"pattern",params:{pattern: "^[1-9][0-9]{0,6}$"},message:"must match pattern \""+"^[1-9][0-9]{0,6}$"+"\""};if(vErrors === null){vErrors = [err89];}else {vErrors.push(err89);}errors++;}}else {const err90 = {instancePath:instancePath+"/payload/body/byteLength",schemaPath:"#/allOf/3/then/properties/payload/properties/body/properties/byteLength/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err90];}else {vErrors.push(err90);}errors++;}}if(data32.mediaType !== undefined){if("text/plain; charset=utf-8" !== data32.mediaType){const err91 = {instancePath:instancePath+"/payload/body/mediaType",schemaPath:"#/allOf/3/then/properties/payload/properties/body/properties/mediaType/const",keyword:"const",params:{allowedValue: "text/plain; charset=utf-8"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err91];}else {vErrors.push(err91);}errors++;}}}else {const err92 = {instancePath:instancePath+"/payload/body",schemaPath:"#/allOf/3/then/properties/payload/properties/body/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err92];}else {vErrors.push(err92);}errors++;}}if(data30.offset !== undefined){let data38 = data30.offset;if(typeof data38 === "string"){if(!pattern107.test(data38)){const err93 = {instancePath:instancePath+"/payload/offset",schemaPath:"#/allOf/3/then/properties/payload/properties/offset/pattern",keyword:"pattern",params:{pattern: "^(0|[1-9][0-9]{0,6})$"},message:"must match pattern \""+"^(0|[1-9][0-9]{0,6})$"+"\""};if(vErrors === null){vErrors = [err93];}else {vErrors.push(err93);}errors++;}}else {const err94 = {instancePath:instancePath+"/payload/offset",schemaPath:"#/allOf/3/then/properties/payload/properties/offset/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err94];}else {vErrors.push(err94);}errors++;}}if(data30.sha256 !== undefined){let data39 = data30.sha256;if(typeof data39 === "string"){if(!pattern22.test(data39)){const err95 = {instancePath:instancePath+"/payload/sha256",schemaPath:"#/allOf/3/then/properties/payload/properties/sha256/pattern",keyword:"pattern",params:{pattern: "^[a-f0-9]{64}$"},message:"must match pattern \""+"^[a-f0-9]{64}$"+"\""};if(vErrors === null){vErrors = [err95];}else {vErrors.push(err95);}errors++;}}else {const err96 = {instancePath:instancePath+"/payload/sha256",schemaPath:"#/allOf/3/then/properties/payload/properties/sha256/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err96];}else {vErrors.push(err96);}errors++;}}if(data30.text !== undefined){let data40 = data30.text;if(typeof data40 === "string"){if(func1(data40) > 32766){const err97 = {instancePath:instancePath+"/payload/text",schemaPath:"#/allOf/3/then/properties/payload/properties/text/maxLength",keyword:"maxLength",params:{limit: 32766},message:"must NOT have more than 32766 characters"};if(vErrors === null){vErrors = [err97];}else {vErrors.push(err97);}errors++;}if(func1(data40) < 1){const err98 = {instancePath:instancePath+"/payload/text",schemaPath:"#/allOf/3/then/properties/payload/properties/text/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err98];}else {vErrors.push(err98);}errors++;}}else {const err99 = {instancePath:instancePath+"/payload/text",schemaPath:"#/allOf/3/then/properties/payload/properties/text/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err99];}else {vErrors.push(err99);}errors++;}}}else {const err100 = {instancePath:instancePath+"/payload",schemaPath:"#/allOf/3/then/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err100];}else {vErrors.push(err100);}errors++;}}}var _valid4 = _errs78 === errors;valid20 = _valid4;if(valid20){var props4 = {};props4.payload = true;props4.eventType = true;}}if(!valid20){const err101 = {instancePath,schemaPath:"#/allOf/3/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err101];}else {vErrors.push(err101);}errors++;}if(props0 !== true && props4 !== undefined){if(props4 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props4);}}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err102 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err102];}else {vErrors.push(err102);}errors++;}if(data.sourceEventId === undefined){const err103 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sourceEventId"},message:"must have required property '"+"sourceEventId"+"'"};if(vErrors === null){vErrors = [err103];}else {vErrors.push(err103);}errors++;}if(data.sourceSeq === undefined){const err104 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sourceSeq"},message:"must have required property '"+"sourceSeq"+"'"};if(vErrors === null){vErrors = [err104];}else {vErrors.push(err104);}errors++;}if(data.sourceInstanceId === undefined){const err105 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sourceInstanceId"},message:"must have required property '"+"sourceInstanceId"+"'"};if(vErrors === null){vErrors = [err105];}else {vErrors.push(err105);}errors++;}if(data.sourceKind === undefined){const err106 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "sourceKind"},message:"must have required property '"+"sourceKind"+"'"};if(vErrors === null){vErrors = [err106];}else {vErrors.push(err106);}errors++;}if(data.runId === undefined){const err107 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "runId"},message:"must have required property '"+"runId"+"'"};if(vErrors === null){vErrors = [err107];}else {vErrors.push(err107);}errors++;}if(data.eventType === undefined){const err108 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "eventType"},message:"must have required property '"+"eventType"+"'"};if(vErrors === null){vErrors = [err108];}else {vErrors.push(err108);}errors++;}if(data.schemaVersion === undefined){const err109 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schemaVersion"},message:"must have required property '"+"schemaVersion"+"'"};if(vErrors === null){vErrors = [err109];}else {vErrors.push(err109);}errors++;}if(data.priority === undefined){const err110 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "priority"},message:"must have required property '"+"priority"+"'"};if(vErrors === null){vErrors = [err110];}else {vErrors.push(err110);}errors++;}if(data.emittedAt === undefined){const err111 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "emittedAt"},message:"must have required property '"+"emittedAt"+"'"};if(vErrors === null){vErrors = [err111];}else {vErrors.push(err111);}errors++;}if(data.payload === undefined){const err112 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "payload"},message:"must have required property '"+"payload"+"'"};if(vErrors === null){vErrors = [err112];}else {vErrors.push(err112);}errors++;}if(data.schema !== undefined){if("paperclip.prp.event.v2" !== data.schema){const err113 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.event.v2"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err113];}else {vErrors.push(err113);}errors++;}}if(data.sourceEventId !== undefined){let data42 = data.sourceEventId;if(typeof data42 === "string"){if(func1(data42) > 160){const err114 = {instancePath:instancePath+"/sourceEventId",schemaPath:"#/properties/sourceEventId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err114];}else {vErrors.push(err114);}errors++;}if(func1(data42) < 1){const err115 = {instancePath:instancePath+"/sourceEventId",schemaPath:"#/properties/sourceEventId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err115];}else {vErrors.push(err115);}errors++;}}else {const err116 = {instancePath:instancePath+"/sourceEventId",schemaPath:"#/properties/sourceEventId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err116];}else {vErrors.push(err116);}errors++;}}if(data.sourceEpoch !== undefined){let data43 = data.sourceEpoch;if(typeof data43 === "string"){if(!pattern15.test(data43)){const err117 = {instancePath:instancePath+"/sourceEpoch",schemaPath:"#/properties/sourceEpoch/pattern",keyword:"pattern",params:{pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},message:"must match pattern \""+"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"+"\""};if(vErrors === null){vErrors = [err117];}else {vErrors.push(err117);}errors++;}}else {const err118 = {instancePath:instancePath+"/sourceEpoch",schemaPath:"#/properties/sourceEpoch/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err118];}else {vErrors.push(err118);}errors++;}}if(data.sourceSeq !== undefined){let data44 = data.sourceSeq;if(!(((typeof data44 == "number") && (!(data44 % 1) && !isNaN(data44))) && (isFinite(data44)))){const err119 = {instancePath:instancePath+"/sourceSeq",schemaPath:"#/properties/sourceSeq/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err119];}else {vErrors.push(err119);}errors++;}if((typeof data44 == "number") && (isFinite(data44))){if(data44 > 9007199254740991 || isNaN(data44)){const err120 = {instancePath:instancePath+"/sourceSeq",schemaPath:"#/properties/sourceSeq/maximum",keyword:"maximum",params:{comparison: "<=", limit: 9007199254740991},message:"must be <= 9007199254740991"};if(vErrors === null){vErrors = [err120];}else {vErrors.push(err120);}errors++;}if(data44 < 1 || isNaN(data44)){const err121 = {instancePath:instancePath+"/sourceSeq",schemaPath:"#/properties/sourceSeq/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err121];}else {vErrors.push(err121);}errors++;}}}if(data.sourceInstanceId !== undefined){let data45 = data.sourceInstanceId;if(typeof data45 === "string"){if(func1(data45) > 160){const err122 = {instancePath:instancePath+"/sourceInstanceId",schemaPath:"#/properties/sourceInstanceId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err122];}else {vErrors.push(err122);}errors++;}if(func1(data45) < 1){const err123 = {instancePath:instancePath+"/sourceInstanceId",schemaPath:"#/properties/sourceInstanceId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err123];}else {vErrors.push(err123);}errors++;}}else {const err124 = {instancePath:instancePath+"/sourceInstanceId",schemaPath:"#/properties/sourceInstanceId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err124];}else {vErrors.push(err124);}errors++;}}if(data.sourceKind !== undefined){let data46 = data.sourceKind;if(!((data46 === "runner") || (data46 === "control_plane"))){const err125 = {instancePath:instancePath+"/sourceKind",schemaPath:"#/properties/sourceKind/enum",keyword:"enum",params:{allowedValues: schema202.properties.sourceKind.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err125];}else {vErrors.push(err125);}errors++;}}if(data.runId !== undefined){let data47 = data.runId;if(typeof data47 === "string"){if(func1(data47) > 160){const err126 = {instancePath:instancePath+"/runId",schemaPath:"#/properties/runId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err126];}else {vErrors.push(err126);}errors++;}if(func1(data47) < 1){const err127 = {instancePath:instancePath+"/runId",schemaPath:"#/properties/runId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err127];}else {vErrors.push(err127);}errors++;}}else {const err128 = {instancePath:instancePath+"/runId",schemaPath:"#/properties/runId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err128];}else {vErrors.push(err128);}errors++;}}if(data.normalizedSessionId !== undefined){let data48 = data.normalizedSessionId;if(typeof data48 === "string"){if(func1(data48) > 160){const err129 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/properties/normalizedSessionId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err129];}else {vErrors.push(err129);}errors++;}if(func1(data48) < 1){const err130 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/properties/normalizedSessionId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err130];}else {vErrors.push(err130);}errors++;}}else {const err131 = {instancePath:instancePath+"/normalizedSessionId",schemaPath:"#/properties/normalizedSessionId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err131];}else {vErrors.push(err131);}errors++;}}if(data.turnId !== undefined){let data49 = data.turnId;if(typeof data49 === "string"){if(func1(data49) > 160){const err132 = {instancePath:instancePath+"/turnId",schemaPath:"#/properties/turnId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err132];}else {vErrors.push(err132);}errors++;}if(func1(data49) < 1){const err133 = {instancePath:instancePath+"/turnId",schemaPath:"#/properties/turnId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err133];}else {vErrors.push(err133);}errors++;}}else {const err134 = {instancePath:instancePath+"/turnId",schemaPath:"#/properties/turnId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err134];}else {vErrors.push(err134);}errors++;}}if(data.itemId !== undefined){let data50 = data.itemId;if(typeof data50 === "string"){if(func1(data50) > 160){const err135 = {instancePath:instancePath+"/itemId",schemaPath:"#/properties/itemId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err135];}else {vErrors.push(err135);}errors++;}if(func1(data50) < 1){const err136 = {instancePath:instancePath+"/itemId",schemaPath:"#/properties/itemId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err136];}else {vErrors.push(err136);}errors++;}}else {const err137 = {instancePath:instancePath+"/itemId",schemaPath:"#/properties/itemId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err137];}else {vErrors.push(err137);}errors++;}}if(data.eventType !== undefined){let data51 = data.eventType;if(!((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((data51 === "runner.connected") || (data51 === "runner.reconnected")) || (data51 === "runner.reconciled")) || (data51 === "runner.disconnected")) || (data51 === "runner.draining")) || (data51 === "runner.suspending")) || (data51 === "runner.suspended")) || (data51 === "runner.stopped")) || (data51 === "runner.diagnostic")) || (data51 === "runtime.phase.changed")) || (data51 === "sandbox.metric")) || (data51 === "workspace.ready")) || (data51 === "workspace.change.updated")) || (data51 === "workspace.diff.recorded")) || (data51 === "workspace.file.referenced")) || (data51 === "harness.starting")) || (data51 === "harness.ready")) || (data51 === "harness.exited")) || (data51 === "harness.diagnostic")) || (data51 === "plan.updated")) || (data51 === "tool.execution.started")) || (data51 === "tool.execution.progressed")) || (data51 === "tool.execution.completed")) || (data51 === "research.started")) || (data51 === "research.progressed")) || (data51 === "research.completed")) || (data51 === "delegation.started")) || (data51 === "delegation.updated")) || (data51 === "delegation.completed")) || (data51 === "model.route.changed")) || (data51 === "model.verification.updated")) || (data51 === "context.compacted")) || (data51 === "artifact.viewed")) || (data51 === "artifact.generated")) || (data51 === "review.mode.changed")) || (data51 === "hook.started")) || (data51 === "hook.completed")) || (data51 === "memory.citation.referenced")) || (data51 === "safety.review.started")) || (data51 === "safety.review.completed")) || (data51 === "terminal.input.sent")) || (data51 === "wait.started")) || (data51 === "wait.completed")) || (data51 === "provider.notice.recorded")) || (data51 === "session.starting")) || (data51 === "session.started")) || (data51 === "session.resuming")) || (data51 === "session.resumed")) || (data51 === "session.reconciled")) || (data51 === "session.updated")) || (data51 === "session.closed")) || (data51 === "session.failed")) || (data51 === "session.capabilities.updated")) || (data51 === "session.goal.snapshot")) || (data51 === "session.goal.updated")) || (data51 === "session.goal.cleared")) || (data51 === "turn.submitted")) || (data51 === "turn.accepted")) || (data51 === "turn.started")) || (data51 === "turn.completed")) || (data51 === "turn.failed")) || (data51 === "turn.interrupted")) || (data51 === "turn.cancelled")) || (data51 === "output.body.chunk")) || (data51 === "item.started")) || (data51 === "item.delta")) || (data51 === "item.completed")) || (data51 === "item.failed")) || (data51 === "usage.reported")) || (data51 === "semantic_tool.input")) || (data51 === "semantic_tool.result")) || (data51 === "mcp_app.discovered")) || (data51 === "mcp_app.resource.resolved")) || (data51 === "mcp_app.initializing")) || (data51 === "mcp_app.ready")) || (data51 === "mcp_app.tool_input")) || (data51 === "mcp_app.tool_result")) || (data51 === "mcp_app.action.requested")) || (data51 === "mcp_app.action.resolved")) || (data51 === "mcp_app.host_context.changed")) || (data51 === "mcp_app.failed")) || (data51 === "mcp_app.teardown")) || (data51 === "runtime_request.created")) || (data51 === "runtime_request.resolved")) || (data51 === "runtime_request.expired")) || (data51 === "runtime_request.cancelled")) || (data51 === "interaction.request.proposed")) || (data51 === "interaction.request.materialized")) || (data51 === "interaction.request.rejected")) || (data51 === "interaction.response.progressed")) || (data51 === "interaction.response.resolved")) || (data51 === "interaction.response.delivered")) || (data51 === "run.attached")) || (data51 === "run.detached")) || (data51 === "run.result.proposed")) || (data51 === "run.result.accepted")) || (data51 === "run.result.rejected")) || (data51 === "attention.request.proposed")) || (data51 === "attention.request.routed")) || (data51 === "attention.request.resolved")) || (data51 === "attention.request.expired")) || (data51 === "attention.request.superseded")) || (data51 === "work.assessment.recorded")) || (data51 === "issue.status.decision.recorded")) || (data51 === "issue.status.decision.applied")) || (data51 === "issue.status.decision.rejected")) || (data51 === "issue.status.decision.superseded")) || (data51 === "run.terminal"))){const err138 = {instancePath:instancePath+"/eventType",schemaPath:"#/properties/eventType/enum",keyword:"enum",params:{allowedValues: schema202.properties.eventType.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err138];}else {vErrors.push(err138);}errors++;}}if(data.schemaVersion !== undefined){if(2 !== data.schemaVersion){const err139 = {instancePath:instancePath+"/schemaVersion",schemaPath:"#/properties/schemaVersion/const",keyword:"const",params:{allowedValue: 2},message:"must be equal to constant"};if(vErrors === null){vErrors = [err139];}else {vErrors.push(err139);}errors++;}}if(data.priority !== undefined){let data53 = data.priority;if(!(((data53 === 0) || (data53 === 1)) || (data53 === 2))){const err140 = {instancePath:instancePath+"/priority",schemaPath:"#/properties/priority/enum",keyword:"enum",params:{allowedValues: schema202.properties.priority.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err140];}else {vErrors.push(err140);}errors++;}}if(data.emittedAt !== undefined){let data54 = data.emittedAt;if(typeof data54 === "string"){if(!(formats0.test(data54))){const err141 = {instancePath:instancePath+"/emittedAt",schemaPath:"#/properties/emittedAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err141];}else {vErrors.push(err141);}errors++;}}else {const err142 = {instancePath:instancePath+"/emittedAt",schemaPath:"#/properties/emittedAt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err142];}else {vErrors.push(err142);}errors++;}}if(data.observedAt !== undefined){let data55 = data.observedAt;if(typeof data55 === "string"){if(!(formats0.test(data55))){const err143 = {instancePath:instancePath+"/observedAt",schemaPath:"#/properties/observedAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err143];}else {vErrors.push(err143);}errors++;}}else {const err144 = {instancePath:instancePath+"/observedAt",schemaPath:"#/properties/observedAt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err144];}else {vErrors.push(err144);}errors++;}}if(data.payload !== undefined){let data56 = data.payload;if(data56 && typeof data56 == "object" && !Array.isArray(data56)){}else {const err145 = {instancePath:instancePath+"/payload",schemaPath:"#/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err145];}else {vErrors.push(err145);}errors++;}}if(data.debug !== undefined){let data57 = data.debug;if(data57 && typeof data57 == "object" && !Array.isArray(data57)){}else {const err146 = {instancePath:instancePath+"/debug",schemaPath:"#/properties/debug/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err146];}else {vErrors.push(err146);}errors++;}}if(data.sourceEpochTransition !== undefined){let data58 = data.sourceEpochTransition;if(data58 && typeof data58 == "object" && !Array.isArray(data58)){if(data58.schema === undefined){const err147 = {instancePath:instancePath+"/sourceEpochTransition",schemaPath:"#/properties/sourceEpochTransition/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err147];}else {vErrors.push(err147);}errors++;}if(data58.runId === undefined){const err148 = {instancePath:instancePath+"/sourceEpochTransition",schemaPath:"#/properties/sourceEpochTransition/required",keyword:"required",params:{missingProperty: "runId"},message:"must have required property '"+"runId"+"'"};if(vErrors === null){vErrors = [err148];}else {vErrors.push(err148);}errors++;}if(data58.transitionId === undefined){const err149 = {instancePath:instancePath+"/sourceEpochTransition",schemaPath:"#/properties/sourceEpochTransition/required",keyword:"required",params:{missingProperty: "transitionId"},message:"must have required property '"+"transitionId"+"'"};if(vErrors === null){vErrors = [err149];}else {vErrors.push(err149);}errors++;}if(data58.fromEpoch === undefined){const err150 = {instancePath:instancePath+"/sourceEpochTransition",schemaPath:"#/properties/sourceEpochTransition/required",keyword:"required",params:{missingProperty: "fromEpoch"},message:"must have required property '"+"fromEpoch"+"'"};if(vErrors === null){vErrors = [err150];}else {vErrors.push(err150);}errors++;}if(data58.nextEpoch === undefined){const err151 = {instancePath:instancePath+"/sourceEpochTransition",schemaPath:"#/properties/sourceEpochTransition/required",keyword:"required",params:{missingProperty: "nextEpoch"},message:"must have required property '"+"nextEpoch"+"'"};if(vErrors === null){vErrors = [err151];}else {vErrors.push(err151);}errors++;}if(data58.finalOrdinal === undefined){const err152 = {instancePath:instancePath+"/sourceEpochTransition",schemaPath:"#/properties/sourceEpochTransition/required",keyword:"required",params:{missingProperty: "finalOrdinal"},message:"must have required property '"+"finalOrdinal"+"'"};if(vErrors === null){vErrors = [err152];}else {vErrors.push(err152);}errors++;}for(const key2 in data58){if(!((((((key2 === "schema") || (key2 === "runId")) || (key2 === "transitionId")) || (key2 === "fromEpoch")) || (key2 === "nextEpoch")) || (key2 === "finalOrdinal"))){const err153 = {instancePath:instancePath+"/sourceEpochTransition",schemaPath:"#/properties/sourceEpochTransition/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key2},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err153];}else {vErrors.push(err153);}errors++;}}if(data58.schema !== undefined){if("paperclip.prp.event-epoch.v1" !== data58.schema){const err154 = {instancePath:instancePath+"/sourceEpochTransition/schema",schemaPath:"#/properties/sourceEpochTransition/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.event-epoch.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err154];}else {vErrors.push(err154);}errors++;}}if(data58.runId !== undefined){let data60 = data58.runId;if(typeof data60 === "string"){if(func1(data60) > 240){const err155 = {instancePath:instancePath+"/sourceEpochTransition/runId",schemaPath:"#/properties/sourceEpochTransition/properties/runId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err155];}else {vErrors.push(err155);}errors++;}if(func1(data60) < 1){const err156 = {instancePath:instancePath+"/sourceEpochTransition/runId",schemaPath:"#/properties/sourceEpochTransition/properties/runId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err156];}else {vErrors.push(err156);}errors++;}}else {const err157 = {instancePath:instancePath+"/sourceEpochTransition/runId",schemaPath:"#/properties/sourceEpochTransition/properties/runId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err157];}else {vErrors.push(err157);}errors++;}}if(data58.transitionId !== undefined){let data61 = data58.transitionId;if(typeof data61 === "string"){if(!pattern15.test(data61)){const err158 = {instancePath:instancePath+"/sourceEpochTransition/transitionId",schemaPath:"#/properties/sourceEpochTransition/properties/transitionId/pattern",keyword:"pattern",params:{pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},message:"must match pattern \""+"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"+"\""};if(vErrors === null){vErrors = [err158];}else {vErrors.push(err158);}errors++;}}else {const err159 = {instancePath:instancePath+"/sourceEpochTransition/transitionId",schemaPath:"#/properties/sourceEpochTransition/properties/transitionId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err159];}else {vErrors.push(err159);}errors++;}}if(data58.fromEpoch !== undefined){let data62 = data58.fromEpoch;const _errs141 = errors;let valid27 = false;const _errs142 = errors;if(typeof data62 === "string"){if(!pattern15.test(data62)){const err160 = {instancePath:instancePath+"/sourceEpochTransition/fromEpoch",schemaPath:"#/properties/sourceEpochTransition/properties/fromEpoch/anyOf/0/pattern",keyword:"pattern",params:{pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},message:"must match pattern \""+"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"+"\""};if(vErrors === null){vErrors = [err160];}else {vErrors.push(err160);}errors++;}}else {const err161 = {instancePath:instancePath+"/sourceEpochTransition/fromEpoch",schemaPath:"#/properties/sourceEpochTransition/properties/fromEpoch/anyOf/0/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err161];}else {vErrors.push(err161);}errors++;}var _valid5 = _errs142 === errors;valid27 = valid27 || _valid5;const _errs144 = errors;if(data62 !== null){const err162 = {instancePath:instancePath+"/sourceEpochTransition/fromEpoch",schemaPath:"#/properties/sourceEpochTransition/properties/fromEpoch/anyOf/1/type",keyword:"type",params:{type: "null"},message:"must be null"};if(vErrors === null){vErrors = [err162];}else {vErrors.push(err162);}errors++;}var _valid5 = _errs144 === errors;valid27 = valid27 || _valid5;if(!valid27){const err163 = {instancePath:instancePath+"/sourceEpochTransition/fromEpoch",schemaPath:"#/properties/sourceEpochTransition/properties/fromEpoch/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};if(vErrors === null){vErrors = [err163];}else {vErrors.push(err163);}errors++;}else {errors = _errs141;if(vErrors !== null){if(_errs141){vErrors.length = _errs141;}else {vErrors = null;}}}}if(data58.nextEpoch !== undefined){let data63 = data58.nextEpoch;if(typeof data63 === "string"){if(!pattern15.test(data63)){const err164 = {instancePath:instancePath+"/sourceEpochTransition/nextEpoch",schemaPath:"#/properties/sourceEpochTransition/properties/nextEpoch/pattern",keyword:"pattern",params:{pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},message:"must match pattern \""+"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"+"\""};if(vErrors === null){vErrors = [err164];}else {vErrors.push(err164);}errors++;}}else {const err165 = {instancePath:instancePath+"/sourceEpochTransition/nextEpoch",schemaPath:"#/properties/sourceEpochTransition/properties/nextEpoch/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err165];}else {vErrors.push(err165);}errors++;}}if(data58.finalOrdinal !== undefined){let data64 = data58.finalOrdinal;if(!(((typeof data64 == "number") && (!(data64 % 1) && !isNaN(data64))) && (isFinite(data64)))){const err166 = {instancePath:instancePath+"/sourceEpochTransition/finalOrdinal",schemaPath:"#/properties/sourceEpochTransition/properties/finalOrdinal/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err166];}else {vErrors.push(err166);}errors++;}if((typeof data64 == "number") && (isFinite(data64))){if(data64 > 9007199254740991 || isNaN(data64)){const err167 = {instancePath:instancePath+"/sourceEpochTransition/finalOrdinal",schemaPath:"#/properties/sourceEpochTransition/properties/finalOrdinal/maximum",keyword:"maximum",params:{comparison: "<=", limit: 9007199254740991},message:"must be <= 9007199254740991"};if(vErrors === null){vErrors = [err167];}else {vErrors.push(err167);}errors++;}if(data64 < 1 || isNaN(data64)){const err168 = {instancePath:instancePath+"/sourceEpochTransition/finalOrdinal",schemaPath:"#/properties/sourceEpochTransition/properties/finalOrdinal/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err168];}else {vErrors.push(err168);}errors++;}}}}else {const err169 = {instancePath:instancePath+"/sourceEpochTransition",schemaPath:"#/properties/sourceEpochTransition/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err169];}else {vErrors.push(err169);}errors++;}}}else {const err170 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err170];}else {vErrors.push(err170);}errors++;}validate109.errors = vErrors;return errors === 0;}validate109.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};const schema211 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/request.schema.json","title":"PRP request","oneOf":[{"type":"object","required":["schema","requestKind","requestId","type","status","prompt","input"],"properties":{"schema":{"const":"paperclip.runtime_request.v2"},"requestKind":{"const":"runtime"},"requestId":{"type":"string","minLength":1,"maxLength":160},"type":{"const":"input"},"status":{"enum":["pending","resolved","expired","cancelled"]},"prompt":{"type":"string","minLength":1,"maxLength":4000},"input":{"$ref":"https://paperclip.dev/schemas/prp/v1/question-set.schema.json"},"origin":{"type":"object","required":["adapter"],"properties":{"adapter":{"type":"string","minLength":1,"maxLength":160},"provider":{"type":"string","minLength":1,"maxLength":160},"method":{"type":"string","minLength":1,"maxLength":500}},"additionalProperties":false},"turnId":{"type":"string","minLength":1,"maxLength":240},"itemId":{"type":"string","minLength":1,"maxLength":240}},"additionalProperties":false},{"type":"object","required":["schema","requestKind","requestId","type","status","prompt"],"properties":{"schema":{"const":"paperclip.runtime_request.v1"},"requestKind":{"const":"runtime"},"requestId":{"type":"string","minLength":1,"maxLength":160},"type":{"enum":["permission","input"]},"status":{"enum":["pending","resolved","expired","cancelled"]},"prompt":{"type":"string","minLength":1,"maxLength":4000},"choices":{"type":"array","items":{"type":"object","required":["key","label"],"properties":{"key":{"type":"string","minLength":1},"label":{"type":"string","minLength":1}},"additionalProperties":true}}},"additionalProperties":true},{"type":"object","required":["schema","requestKind","requestId","kind","blocking","payload"],"properties":{"schema":{"const":"paperclip.interaction_request.v1"},"requestKind":{"const":"issue_thread"},"requestId":{"type":"string","minLength":1,"maxLength":160},"kind":{"enum":["request_confirmation","request_checkbox_confirmation","request_item_verdicts","ask_user_questions","suggest_tasks"]},"blocking":{"type":"boolean"},"payload":{"type":"object","additionalProperties":true}},"additionalProperties":true}]};const schema212 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/question-set.schema.json","title":"Paperclip question set","type":"object","required":["schema","questions"],"properties":{"schema":{"const":"paperclip.question_set.v1"},"title":{"type":"string","maxLength":1000},"description":{"type":"string","maxLength":4000},"submitLabel":{"type":"string","maxLength":200},"questions":{"type":"array","minItems":1,"maxItems":64,"items":{"$ref":"#/$defs/question"}}},"$defs":{"option":{"type":"object","required":["id","label"],"properties":{"id":{"type":"string","minLength":1,"maxLength":160},"label":{"type":"string","minLength":1,"maxLength":1000},"description":{"type":"string","maxLength":4000},"recommended":{"type":"boolean"}},"additionalProperties":false},"customAnswer":{"type":"object","required":["enabled"],"properties":{"enabled":{"const":true},"label":{"type":"string","maxLength":1000},"placeholder":{"type":"string","maxLength":1000}},"additionalProperties":false},"textValidation":{"type":"object","properties":{"minLength":{"type":"integer","minimum":0,"maximum":100000},"maxLength":{"type":"integer","minimum":0,"maximum":100000},"pattern":{"type":"string","maxLength":1000},"inputType":{"enum":["text","number","integer"]},"minimum":{"type":"number"},"maximum":{"type":"number"}},"additionalProperties":false},"question":{"type":"object","required":["id","prompt","required","answerMode"],"properties":{"id":{"type":"string","minLength":1,"maxLength":160},"header":{"type":"string","maxLength":1000},"prompt":{"type":"string","minLength":1,"maxLength":4000},"helpText":{"type":"string","maxLength":4000},"required":{"type":"boolean"},"answerMode":{"enum":["single_select","multi_select","text"]},"options":{"type":"array","maxItems":128,"items":{"$ref":"#/$defs/option"}},"customAnswer":{"$ref":"#/$defs/customAnswer"},"textValidation":{"$ref":"#/$defs/textValidation"}},"allOf":[{"if":{"properties":{"answerMode":{"const":"text"}},"required":["answerMode"]},"then":{"properties":{"options":{"type":"array","maxItems":0}},"not":{"required":["customAnswer"]}}},{"if":{"properties":{"answerMode":{"enum":["single_select","multi_select"]}},"required":["answerMode"]},"then":{"required":["options"],"properties":{"options":{"type":"array","minItems":1}}}}],"additionalProperties":false}},"additionalProperties":false};const schema213 = {"type":"object","required":["id","prompt","required","answerMode"],"properties":{"id":{"type":"string","minLength":1,"maxLength":160},"header":{"type":"string","maxLength":1000},"prompt":{"type":"string","minLength":1,"maxLength":4000},"helpText":{"type":"string","maxLength":4000},"required":{"type":"boolean"},"answerMode":{"enum":["single_select","multi_select","text"]},"options":{"type":"array","maxItems":128,"items":{"$ref":"#/$defs/option"}},"customAnswer":{"$ref":"#/$defs/customAnswer"},"textValidation":{"$ref":"#/$defs/textValidation"}},"allOf":[{"if":{"properties":{"answerMode":{"const":"text"}},"required":["answerMode"]},"then":{"properties":{"options":{"type":"array","maxItems":0}},"not":{"required":["customAnswer"]}}},{"if":{"properties":{"answerMode":{"enum":["single_select","multi_select"]}},"required":["answerMode"]},"then":{"required":["options"],"properties":{"options":{"type":"array","minItems":1}}}}],"additionalProperties":false};const schema214 = {"type":"object","required":["id","label"],"properties":{"id":{"type":"string","minLength":1,"maxLength":160},"label":{"type":"string","minLength":1,"maxLength":1000},"description":{"type":"string","maxLength":4000},"recommended":{"type":"boolean"}},"additionalProperties":false};const schema215 = {"type":"object","required":["enabled"],"properties":{"enabled":{"const":true},"label":{"type":"string","maxLength":1000},"placeholder":{"type":"string","maxLength":1000}},"additionalProperties":false};const schema216 = {"type":"object","properties":{"minLength":{"type":"integer","minimum":0,"maximum":100000},"maxLength":{"type":"integer","minimum":0,"maximum":100000},"pattern":{"type":"string","maxLength":1000},"inputType":{"enum":["text","number","integer"]},"minimum":{"type":"number"},"maximum":{"type":"number"}},"additionalProperties":false};function validate115(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){let vErrors = null;let errors = 0;const evaluated0 = validate115.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}const _errs2 = errors;let valid1 = true;const _errs3 = errors;if(data && typeof data == "object" && !Array.isArray(data)){let missing0;if((data.answerMode === undefined) && (missing0 = "answerMode")){const err0 = {};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}else {if(data.answerMode !== undefined){if("text" !== data.answerMode){const err1 = {};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}}}}var _valid0 = _errs3 === errors;errors = _errs2;if(vErrors !== null){if(_errs2){vErrors.length = _errs2;}else {vErrors = null;}}if(_valid0){const _errs5 = errors;const _errs6 = errors;const _errs7 = errors;if(data && typeof data == "object" && !Array.isArray(data)){let missing1;if((data.customAnswer === undefined) && (missing1 = "customAnswer")){const err2 = {};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}}var valid3 = _errs7 === errors;if(valid3){const err3 = {instancePath,schemaPath:"#/allOf/0/then/not",keyword:"not",params:{},message:"must NOT be valid"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}else {errors = _errs6;if(vErrors !== null){if(_errs6){vErrors.length = _errs6;}else {vErrors = null;}}}if(data && typeof data == "object" && !Array.isArray(data)){if(data.options !== undefined){let data1 = data.options;if(Array.isArray(data1)){if(data1.length > 0){const err4 = {instancePath:instancePath+"/options",schemaPath:"#/allOf/0/then/properties/options/maxItems",keyword:"maxItems",params:{limit: 0},message:"must NOT have more than 0 items"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}}else {const err5 = {instancePath:instancePath+"/options",schemaPath:"#/allOf/0/then/properties/options/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}}var _valid0 = _errs5 === errors;valid1 = _valid0;if(valid1){var props0 = {};props0.options = true;props0.answerMode = true;}}if(!valid1){const err6 = {instancePath,schemaPath:"#/allOf/0/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}const _errs11 = errors;let valid5 = true;const _errs12 = errors;if(data && typeof data == "object" && !Array.isArray(data)){let missing2;if((data.answerMode === undefined) && (missing2 = "answerMode")){const err7 = {};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}else {if(data.answerMode !== undefined){let data2 = data.answerMode;if(!((data2 === "single_select") || (data2 === "multi_select"))){const err8 = {};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}}}var _valid1 = _errs12 === errors;errors = _errs11;if(vErrors !== null){if(_errs11){vErrors.length = _errs11;}else {vErrors = null;}}if(_valid1){const _errs14 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.options === undefined){const err9 = {instancePath,schemaPath:"#/allOf/1/then/required",keyword:"required",params:{missingProperty: "options"},message:"must have required property '"+"options"+"'"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(data.options !== undefined){let data3 = data.options;if(Array.isArray(data3)){if(data3.length < 1){const err10 = {instancePath:instancePath+"/options",schemaPath:"#/allOf/1/then/properties/options/minItems",keyword:"minItems",params:{limit: 1},message:"must NOT have fewer than 1 items"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}else {const err11 = {instancePath:instancePath+"/options",schemaPath:"#/allOf/1/then/properties/options/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}}var _valid1 = _errs14 === errors;valid5 = _valid1;if(valid5){var props1 = {};props1.options = true;props1.answerMode = true;}}if(!valid5){const err12 = {instancePath,schemaPath:"#/allOf/1/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}if(props0 !== true && props1 !== undefined){if(props1 === true){props0 = true;}else {props0 = props0 || {};Object.assign(props0, props1);}}if(data && typeof data == "object" && !Array.isArray(data)){if(data.id === undefined){const err13 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "id"},message:"must have required property '"+"id"+"'"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(data.prompt === undefined){const err14 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "prompt"},message:"must have required property '"+"prompt"+"'"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}if(data.required === undefined){const err15 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "required"},message:"must have required property '"+"required"+"'"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}if(data.answerMode === undefined){const err16 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "answerMode"},message:"must have required property '"+"answerMode"+"'"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}for(const key0 in data){if(!(func58.call(schema213.properties, key0))){const err17 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}if(data.id !== undefined){let data4 = data.id;if(typeof data4 === "string"){if(func1(data4) > 160){const err18 = {instancePath:instancePath+"/id",schemaPath:"#/properties/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}if(func1(data4) < 1){const err19 = {instancePath:instancePath+"/id",schemaPath:"#/properties/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}else {const err20 = {instancePath:instancePath+"/id",schemaPath:"#/properties/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}}if(data.header !== undefined){let data5 = data.header;if(typeof data5 === "string"){if(func1(data5) > 1000){const err21 = {instancePath:instancePath+"/header",schemaPath:"#/properties/header/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}}else {const err22 = {instancePath:instancePath+"/header",schemaPath:"#/properties/header/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}if(data.prompt !== undefined){let data6 = data.prompt;if(typeof data6 === "string"){if(func1(data6) > 4000){const err23 = {instancePath:instancePath+"/prompt",schemaPath:"#/properties/prompt/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}if(func1(data6) < 1){const err24 = {instancePath:instancePath+"/prompt",schemaPath:"#/properties/prompt/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}else {const err25 = {instancePath:instancePath+"/prompt",schemaPath:"#/properties/prompt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}if(data.helpText !== undefined){let data7 = data.helpText;if(typeof data7 === "string"){if(func1(data7) > 4000){const err26 = {instancePath:instancePath+"/helpText",schemaPath:"#/properties/helpText/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}}else {const err27 = {instancePath:instancePath+"/helpText",schemaPath:"#/properties/helpText/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}}if(data.required !== undefined){if(typeof data.required !== "boolean"){const err28 = {instancePath:instancePath+"/required",schemaPath:"#/properties/required/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}if(data.answerMode !== undefined){let data9 = data.answerMode;if(!(((data9 === "single_select") || (data9 === "multi_select")) || (data9 === "text"))){const err29 = {instancePath:instancePath+"/answerMode",schemaPath:"#/properties/answerMode/enum",keyword:"enum",params:{allowedValues: schema213.properties.answerMode.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}}if(data.options !== undefined){let data10 = data.options;if(Array.isArray(data10)){if(data10.length > 128){const err30 = {instancePath:instancePath+"/options",schemaPath:"#/properties/options/maxItems",keyword:"maxItems",params:{limit: 128},message:"must NOT have more than 128 items"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}const len0 = data10.length;for(let i0=0; i0 160){const err34 = {instancePath:instancePath+"/options/" + i0+"/id",schemaPath:"#/$defs/option/properties/id/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}if(func1(data12) < 1){const err35 = {instancePath:instancePath+"/options/" + i0+"/id",schemaPath:"#/$defs/option/properties/id/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}}else {const err36 = {instancePath:instancePath+"/options/" + i0+"/id",schemaPath:"#/$defs/option/properties/id/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}}if(data11.label !== undefined){let data13 = data11.label;if(typeof data13 === "string"){if(func1(data13) > 1000){const err37 = {instancePath:instancePath+"/options/" + i0+"/label",schemaPath:"#/$defs/option/properties/label/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}if(func1(data13) < 1){const err38 = {instancePath:instancePath+"/options/" + i0+"/label",schemaPath:"#/$defs/option/properties/label/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}}else {const err39 = {instancePath:instancePath+"/options/" + i0+"/label",schemaPath:"#/$defs/option/properties/label/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}}if(data11.description !== undefined){let data14 = data11.description;if(typeof data14 === "string"){if(func1(data14) > 4000){const err40 = {instancePath:instancePath+"/options/" + i0+"/description",schemaPath:"#/$defs/option/properties/description/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}}else {const err41 = {instancePath:instancePath+"/options/" + i0+"/description",schemaPath:"#/$defs/option/properties/description/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}}if(data11.recommended !== undefined){if(typeof data11.recommended !== "boolean"){const err42 = {instancePath:instancePath+"/options/" + i0+"/recommended",schemaPath:"#/$defs/option/properties/recommended/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}}}else {const err43 = {instancePath:instancePath+"/options/" + i0,schemaPath:"#/$defs/option/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}}}else {const err44 = {instancePath:instancePath+"/options",schemaPath:"#/properties/options/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err44];}else {vErrors.push(err44);}errors++;}}if(data.customAnswer !== undefined){let data16 = data.customAnswer;if(data16 && typeof data16 == "object" && !Array.isArray(data16)){if(data16.enabled === undefined){const err45 = {instancePath:instancePath+"/customAnswer",schemaPath:"#/$defs/customAnswer/required",keyword:"required",params:{missingProperty: "enabled"},message:"must have required property '"+"enabled"+"'"};if(vErrors === null){vErrors = [err45];}else {vErrors.push(err45);}errors++;}for(const key2 in data16){if(!(((key2 === "enabled") || (key2 === "label")) || (key2 === "placeholder"))){const err46 = {instancePath:instancePath+"/customAnswer",schemaPath:"#/$defs/customAnswer/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key2},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err46];}else {vErrors.push(err46);}errors++;}}if(data16.enabled !== undefined){if(true !== data16.enabled){const err47 = {instancePath:instancePath+"/customAnswer/enabled",schemaPath:"#/$defs/customAnswer/properties/enabled/const",keyword:"const",params:{allowedValue: true},message:"must be equal to constant"};if(vErrors === null){vErrors = [err47];}else {vErrors.push(err47);}errors++;}}if(data16.label !== undefined){let data18 = data16.label;if(typeof data18 === "string"){if(func1(data18) > 1000){const err48 = {instancePath:instancePath+"/customAnswer/label",schemaPath:"#/$defs/customAnswer/properties/label/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err48];}else {vErrors.push(err48);}errors++;}}else {const err49 = {instancePath:instancePath+"/customAnswer/label",schemaPath:"#/$defs/customAnswer/properties/label/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err49];}else {vErrors.push(err49);}errors++;}}if(data16.placeholder !== undefined){let data19 = data16.placeholder;if(typeof data19 === "string"){if(func1(data19) > 1000){const err50 = {instancePath:instancePath+"/customAnswer/placeholder",schemaPath:"#/$defs/customAnswer/properties/placeholder/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err50];}else {vErrors.push(err50);}errors++;}}else {const err51 = {instancePath:instancePath+"/customAnswer/placeholder",schemaPath:"#/$defs/customAnswer/properties/placeholder/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err51];}else {vErrors.push(err51);}errors++;}}}else {const err52 = {instancePath:instancePath+"/customAnswer",schemaPath:"#/$defs/customAnswer/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err52];}else {vErrors.push(err52);}errors++;}}if(data.textValidation !== undefined){let data20 = data.textValidation;if(data20 && typeof data20 == "object" && !Array.isArray(data20)){for(const key3 in data20){if(!((((((key3 === "minLength") || (key3 === "maxLength")) || (key3 === "pattern")) || (key3 === "inputType")) || (key3 === "minimum")) || (key3 === "maximum"))){const err53 = {instancePath:instancePath+"/textValidation",schemaPath:"#/$defs/textValidation/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key3},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err53];}else {vErrors.push(err53);}errors++;}}if(data20.minLength !== undefined){let data21 = data20.minLength;if(!(((typeof data21 == "number") && (!(data21 % 1) && !isNaN(data21))) && (isFinite(data21)))){const err54 = {instancePath:instancePath+"/textValidation/minLength",schemaPath:"#/$defs/textValidation/properties/minLength/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err54];}else {vErrors.push(err54);}errors++;}if((typeof data21 == "number") && (isFinite(data21))){if(data21 > 100000 || isNaN(data21)){const err55 = {instancePath:instancePath+"/textValidation/minLength",schemaPath:"#/$defs/textValidation/properties/minLength/maximum",keyword:"maximum",params:{comparison: "<=", limit: 100000},message:"must be <= 100000"};if(vErrors === null){vErrors = [err55];}else {vErrors.push(err55);}errors++;}if(data21 < 0 || isNaN(data21)){const err56 = {instancePath:instancePath+"/textValidation/minLength",schemaPath:"#/$defs/textValidation/properties/minLength/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err56];}else {vErrors.push(err56);}errors++;}}}if(data20.maxLength !== undefined){let data22 = data20.maxLength;if(!(((typeof data22 == "number") && (!(data22 % 1) && !isNaN(data22))) && (isFinite(data22)))){const err57 = {instancePath:instancePath+"/textValidation/maxLength",schemaPath:"#/$defs/textValidation/properties/maxLength/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err57];}else {vErrors.push(err57);}errors++;}if((typeof data22 == "number") && (isFinite(data22))){if(data22 > 100000 || isNaN(data22)){const err58 = {instancePath:instancePath+"/textValidation/maxLength",schemaPath:"#/$defs/textValidation/properties/maxLength/maximum",keyword:"maximum",params:{comparison: "<=", limit: 100000},message:"must be <= 100000"};if(vErrors === null){vErrors = [err58];}else {vErrors.push(err58);}errors++;}if(data22 < 0 || isNaN(data22)){const err59 = {instancePath:instancePath+"/textValidation/maxLength",schemaPath:"#/$defs/textValidation/properties/maxLength/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err59];}else {vErrors.push(err59);}errors++;}}}if(data20.pattern !== undefined){let data23 = data20.pattern;if(typeof data23 === "string"){if(func1(data23) > 1000){const err60 = {instancePath:instancePath+"/textValidation/pattern",schemaPath:"#/$defs/textValidation/properties/pattern/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err60];}else {vErrors.push(err60);}errors++;}}else {const err61 = {instancePath:instancePath+"/textValidation/pattern",schemaPath:"#/$defs/textValidation/properties/pattern/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err61];}else {vErrors.push(err61);}errors++;}}if(data20.inputType !== undefined){let data24 = data20.inputType;if(!(((data24 === "text") || (data24 === "number")) || (data24 === "integer"))){const err62 = {instancePath:instancePath+"/textValidation/inputType",schemaPath:"#/$defs/textValidation/properties/inputType/enum",keyword:"enum",params:{allowedValues: schema216.properties.inputType.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err62];}else {vErrors.push(err62);}errors++;}}if(data20.minimum !== undefined){let data25 = data20.minimum;if(!((typeof data25 == "number") && (isFinite(data25)))){const err63 = {instancePath:instancePath+"/textValidation/minimum",schemaPath:"#/$defs/textValidation/properties/minimum/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err63];}else {vErrors.push(err63);}errors++;}}if(data20.maximum !== undefined){let data26 = data20.maximum;if(!((typeof data26 == "number") && (isFinite(data26)))){const err64 = {instancePath:instancePath+"/textValidation/maximum",schemaPath:"#/$defs/textValidation/properties/maximum/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err64];}else {vErrors.push(err64);}errors++;}}}else {const err65 = {instancePath:instancePath+"/textValidation",schemaPath:"#/$defs/textValidation/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err65];}else {vErrors.push(err65);}errors++;}}}else {const err66 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err66];}else {vErrors.push(err66);}errors++;}validate115.errors = vErrors;return errors === 0;}validate115.evaluated = {"props":true,"dynamicProps":false,"dynamicItems":false};function validate114(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){/*# sourceURL="https://paperclip.dev/schemas/prp/v1/question-set.schema.json" */;let vErrors = null;let errors = 0;const evaluated0 = validate114.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.questions === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "questions"},message:"must have required property '"+"questions"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}for(const key0 in data){if(!(((((key0 === "schema") || (key0 === "title")) || (key0 === "description")) || (key0 === "submitLabel")) || (key0 === "questions"))){const err2 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}}if(data.schema !== undefined){if("paperclip.question_set.v1" !== data.schema){const err3 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.question_set.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}}if(data.title !== undefined){let data1 = data.title;if(typeof data1 === "string"){if(func1(data1) > 1000){const err4 = {instancePath:instancePath+"/title",schemaPath:"#/properties/title/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}}else {const err5 = {instancePath:instancePath+"/title",schemaPath:"#/properties/title/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}if(data.description !== undefined){let data2 = data.description;if(typeof data2 === "string"){if(func1(data2) > 4000){const err6 = {instancePath:instancePath+"/description",schemaPath:"#/properties/description/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}}else {const err7 = {instancePath:instancePath+"/description",schemaPath:"#/properties/description/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}if(data.submitLabel !== undefined){let data3 = data.submitLabel;if(typeof data3 === "string"){if(func1(data3) > 200){const err8 = {instancePath:instancePath+"/submitLabel",schemaPath:"#/properties/submitLabel/maxLength",keyword:"maxLength",params:{limit: 200},message:"must NOT have more than 200 characters"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}else {const err9 = {instancePath:instancePath+"/submitLabel",schemaPath:"#/properties/submitLabel/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}if(data.questions !== undefined){let data4 = data.questions;if(Array.isArray(data4)){if(data4.length > 64){const err10 = {instancePath:instancePath+"/questions",schemaPath:"#/properties/questions/maxItems",keyword:"maxItems",params:{limit: 64},message:"must NOT have more than 64 items"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(data4.length < 1){const err11 = {instancePath:instancePath+"/questions",schemaPath:"#/properties/questions/minItems",keyword:"minItems",params:{limit: 1},message:"must NOT have fewer than 1 items"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}const len0 = data4.length;for(let i0=0; i0 160){const err10 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/0/properties/requestId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}if(func1(data2) < 1){const err11 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/0/properties/requestId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}else {const err12 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/0/properties/requestId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.type !== undefined){if("input" !== data.type){const err13 = {instancePath:instancePath+"/type",schemaPath:"#/oneOf/0/properties/type/const",keyword:"const",params:{allowedValue: "input"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}if(data.status !== undefined){let data4 = data.status;if(!((((data4 === "pending") || (data4 === "resolved")) || (data4 === "expired")) || (data4 === "cancelled"))){const err14 = {instancePath:instancePath+"/status",schemaPath:"#/oneOf/0/properties/status/enum",keyword:"enum",params:{allowedValues: schema211.oneOf[0].properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}if(data.prompt !== undefined){let data5 = data.prompt;if(typeof data5 === "string"){if(func1(data5) > 4000){const err15 = {instancePath:instancePath+"/prompt",schemaPath:"#/oneOf/0/properties/prompt/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}if(func1(data5) < 1){const err16 = {instancePath:instancePath+"/prompt",schemaPath:"#/oneOf/0/properties/prompt/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}}else {const err17 = {instancePath:instancePath+"/prompt",schemaPath:"#/oneOf/0/properties/prompt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}if(data.input !== undefined){if(!(validate114(data.input, {instancePath:instancePath+"/input",parentData:data,parentDataProperty:"input",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate114.errors : vErrors.concat(validate114.errors);errors = vErrors.length;}}if(data.origin !== undefined){let data7 = data.origin;if(data7 && typeof data7 == "object" && !Array.isArray(data7)){if(data7.adapter === undefined){const err18 = {instancePath:instancePath+"/origin",schemaPath:"#/oneOf/0/properties/origin/required",keyword:"required",params:{missingProperty: "adapter"},message:"must have required property '"+"adapter"+"'"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}for(const key1 in data7){if(!(((key1 === "adapter") || (key1 === "provider")) || (key1 === "method"))){const err19 = {instancePath:instancePath+"/origin",schemaPath:"#/oneOf/0/properties/origin/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key1},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}}if(data7.adapter !== undefined){let data8 = data7.adapter;if(typeof data8 === "string"){if(func1(data8) > 160){const err20 = {instancePath:instancePath+"/origin/adapter",schemaPath:"#/oneOf/0/properties/origin/properties/adapter/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}if(func1(data8) < 1){const err21 = {instancePath:instancePath+"/origin/adapter",schemaPath:"#/oneOf/0/properties/origin/properties/adapter/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}}else {const err22 = {instancePath:instancePath+"/origin/adapter",schemaPath:"#/oneOf/0/properties/origin/properties/adapter/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}}if(data7.provider !== undefined){let data9 = data7.provider;if(typeof data9 === "string"){if(func1(data9) > 160){const err23 = {instancePath:instancePath+"/origin/provider",schemaPath:"#/oneOf/0/properties/origin/properties/provider/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}if(func1(data9) < 1){const err24 = {instancePath:instancePath+"/origin/provider",schemaPath:"#/oneOf/0/properties/origin/properties/provider/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}else {const err25 = {instancePath:instancePath+"/origin/provider",schemaPath:"#/oneOf/0/properties/origin/properties/provider/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}if(data7.method !== undefined){let data10 = data7.method;if(typeof data10 === "string"){if(func1(data10) > 500){const err26 = {instancePath:instancePath+"/origin/method",schemaPath:"#/oneOf/0/properties/origin/properties/method/maxLength",keyword:"maxLength",params:{limit: 500},message:"must NOT have more than 500 characters"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}if(func1(data10) < 1){const err27 = {instancePath:instancePath+"/origin/method",schemaPath:"#/oneOf/0/properties/origin/properties/method/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}}else {const err28 = {instancePath:instancePath+"/origin/method",schemaPath:"#/oneOf/0/properties/origin/properties/method/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}}else {const err29 = {instancePath:instancePath+"/origin",schemaPath:"#/oneOf/0/properties/origin/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}}if(data.turnId !== undefined){let data11 = data.turnId;if(typeof data11 === "string"){if(func1(data11) > 240){const err30 = {instancePath:instancePath+"/turnId",schemaPath:"#/oneOf/0/properties/turnId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}if(func1(data11) < 1){const err31 = {instancePath:instancePath+"/turnId",schemaPath:"#/oneOf/0/properties/turnId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}}else {const err32 = {instancePath:instancePath+"/turnId",schemaPath:"#/oneOf/0/properties/turnId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}}if(data.itemId !== undefined){let data12 = data.itemId;if(typeof data12 === "string"){if(func1(data12) > 240){const err33 = {instancePath:instancePath+"/itemId",schemaPath:"#/oneOf/0/properties/itemId/maxLength",keyword:"maxLength",params:{limit: 240},message:"must NOT have more than 240 characters"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}if(func1(data12) < 1){const err34 = {instancePath:instancePath+"/itemId",schemaPath:"#/oneOf/0/properties/itemId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}}else {const err35 = {instancePath:instancePath+"/itemId",schemaPath:"#/oneOf/0/properties/itemId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}}}else {const err36 = {instancePath,schemaPath:"#/oneOf/0/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}var _valid0 = _errs1 === errors;if(_valid0){valid0 = true;passing0 = 0;var props0 = true;}const _errs26 = errors;if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err37 = {instancePath,schemaPath:"#/oneOf/1/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}if(data.requestKind === undefined){const err38 = {instancePath,schemaPath:"#/oneOf/1/required",keyword:"required",params:{missingProperty: "requestKind"},message:"must have required property '"+"requestKind"+"'"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}if(data.requestId === undefined){const err39 = {instancePath,schemaPath:"#/oneOf/1/required",keyword:"required",params:{missingProperty: "requestId"},message:"must have required property '"+"requestId"+"'"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}if(data.type === undefined){const err40 = {instancePath,schemaPath:"#/oneOf/1/required",keyword:"required",params:{missingProperty: "type"},message:"must have required property '"+"type"+"'"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}if(data.status === undefined){const err41 = {instancePath,schemaPath:"#/oneOf/1/required",keyword:"required",params:{missingProperty: "status"},message:"must have required property '"+"status"+"'"};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}if(data.prompt === undefined){const err42 = {instancePath,schemaPath:"#/oneOf/1/required",keyword:"required",params:{missingProperty: "prompt"},message:"must have required property '"+"prompt"+"'"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}if(data.schema !== undefined){if("paperclip.runtime_request.v1" !== data.schema){const err43 = {instancePath:instancePath+"/schema",schemaPath:"#/oneOf/1/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.runtime_request.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}}if(data.requestKind !== undefined){if("runtime" !== data.requestKind){const err44 = {instancePath:instancePath+"/requestKind",schemaPath:"#/oneOf/1/properties/requestKind/const",keyword:"const",params:{allowedValue: "runtime"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err44];}else {vErrors.push(err44);}errors++;}}if(data.requestId !== undefined){let data15 = data.requestId;if(typeof data15 === "string"){if(func1(data15) > 160){const err45 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/1/properties/requestId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err45];}else {vErrors.push(err45);}errors++;}if(func1(data15) < 1){const err46 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/1/properties/requestId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err46];}else {vErrors.push(err46);}errors++;}}else {const err47 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/1/properties/requestId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err47];}else {vErrors.push(err47);}errors++;}}if(data.type !== undefined){let data16 = data.type;if(!((data16 === "permission") || (data16 === "input"))){const err48 = {instancePath:instancePath+"/type",schemaPath:"#/oneOf/1/properties/type/enum",keyword:"enum",params:{allowedValues: schema211.oneOf[1].properties.type.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err48];}else {vErrors.push(err48);}errors++;}}if(data.status !== undefined){let data17 = data.status;if(!((((data17 === "pending") || (data17 === "resolved")) || (data17 === "expired")) || (data17 === "cancelled"))){const err49 = {instancePath:instancePath+"/status",schemaPath:"#/oneOf/1/properties/status/enum",keyword:"enum",params:{allowedValues: schema211.oneOf[1].properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err49];}else {vErrors.push(err49);}errors++;}}if(data.prompt !== undefined){let data18 = data.prompt;if(typeof data18 === "string"){if(func1(data18) > 4000){const err50 = {instancePath:instancePath+"/prompt",schemaPath:"#/oneOf/1/properties/prompt/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err50];}else {vErrors.push(err50);}errors++;}if(func1(data18) < 1){const err51 = {instancePath:instancePath+"/prompt",schemaPath:"#/oneOf/1/properties/prompt/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err51];}else {vErrors.push(err51);}errors++;}}else {const err52 = {instancePath:instancePath+"/prompt",schemaPath:"#/oneOf/1/properties/prompt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err52];}else {vErrors.push(err52);}errors++;}}if(data.choices !== undefined){let data19 = data.choices;if(Array.isArray(data19)){const len0 = data19.length;for(let i0=0; i0 160){const err70 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/2/properties/requestId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err70];}else {vErrors.push(err70);}errors++;}if(func1(data25) < 1){const err71 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/2/properties/requestId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err71];}else {vErrors.push(err71);}errors++;}}else {const err72 = {instancePath:instancePath+"/requestId",schemaPath:"#/oneOf/2/properties/requestId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err72];}else {vErrors.push(err72);}errors++;}}if(data.kind !== undefined){let data26 = data.kind;if(!(((((data26 === "request_confirmation") || (data26 === "request_checkbox_confirmation")) || (data26 === "request_item_verdicts")) || (data26 === "ask_user_questions")) || (data26 === "suggest_tasks"))){const err73 = {instancePath:instancePath+"/kind",schemaPath:"#/oneOf/2/properties/kind/enum",keyword:"enum",params:{allowedValues: schema211.oneOf[2].properties.kind.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err73];}else {vErrors.push(err73);}errors++;}}if(data.blocking !== undefined){if(typeof data.blocking !== "boolean"){const err74 = {instancePath:instancePath+"/blocking",schemaPath:"#/oneOf/2/properties/blocking/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err74];}else {vErrors.push(err74);}errors++;}}if(data.payload !== undefined){let data28 = data.payload;if(data28 && typeof data28 == "object" && !Array.isArray(data28)){}else {const err75 = {instancePath:instancePath+"/payload",schemaPath:"#/oneOf/2/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err75];}else {vErrors.push(err75);}errors++;}}}else {const err76 = {instancePath,schemaPath:"#/oneOf/2/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err76];}else {vErrors.push(err76);}errors++;}var _valid0 = _errs46 === errors;if(_valid0 && valid0){valid0 = false;passing0 = [passing0, 2];}else {if(_valid0){valid0 = true;passing0 = 2;if(props0 !== true){props0 = true;}}}}if(!valid0){const err77 = {instancePath,schemaPath:"#/oneOf",keyword:"oneOf",params:{passingSchemas: passing0},message:"must match exactly one schema in oneOf"};if(vErrors === null){vErrors = [err77];}else {vErrors.push(err77);}errors++;}else {errors = _errs0;if(vErrors !== null){if(_errs0){vErrors.length = _errs0;}else {vErrors = null;}}}validate113.errors = vErrors;evaluated0.props = props0;return errors === 0;}validate113.evaluated = {"dynamicProps":true,"dynamicItems":false};const pattern12 = new RegExp("^[a-z][a-z0-9_.-]*$", "u");function validate20(data, {instancePath="", parentData, parentDataProperty, rootData=data, dynamicAnchors={}}={}){/*# sourceURL="https://paperclip.dev/schemas/prp/v1/fixture.schema.json" */;let vErrors = null;let errors = 0;const evaluated0 = validate20.evaluated;if(evaluated0.dynamicProps){evaluated0.props = undefined;}if(evaluated0.dynamicItems){evaluated0.items = undefined;}if(data && typeof data == "object" && !Array.isArray(data)){if(data.schema === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(data.fixtureVersion === undefined){const err1 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "fixtureVersion"},message:"must have required property '"+"fixtureVersion"+"'"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}if(data.protocolVersion === undefined){const err2 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "protocolVersion"},message:"must have required property '"+"protocolVersion"+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}if(data.name === undefined){const err3 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "name"},message:"must have required property '"+"name"+"'"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}if(data.description === undefined){const err4 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "description"},message:"must have required property '"+"description"+"'"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}if(data.identity === undefined){const err5 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "identity"},message:"must have required property '"+"identity"+"'"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}if(data.capabilities === undefined){const err6 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "capabilities"},message:"must have required property '"+"capabilities"+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}if(data.commands === undefined){const err7 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "commands"},message:"must have required property '"+"commands"+"'"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}if(data.events === undefined){const err8 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "events"},message:"must have required property '"+"events"+"'"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}if(data.result === undefined){const err9 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "result"},message:"must have required property '"+"result"+"'"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}if(data.schema !== undefined){if("paperclip.prp.fixture.v1" !== data.schema){const err10 = {instancePath:instancePath+"/schema",schemaPath:"#/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.fixture.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}}if(data.fixtureVersion !== undefined){if(1 !== data.fixtureVersion){const err11 = {instancePath:instancePath+"/fixtureVersion",schemaPath:"#/properties/fixtureVersion/const",keyword:"const",params:{allowedValue: 1},message:"must be equal to constant"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;}}if(data.protocolVersion !== undefined){let data2 = data.protocolVersion;if(!((data2 === 1) || (data2 === 2))){const err12 = {instancePath:instancePath+"/protocolVersion",schemaPath:"#/properties/protocolVersion/enum",keyword:"enum",params:{allowedValues: schema31.properties.protocolVersion.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}if(data.name !== undefined){let data3 = data.name;if(typeof data3 === "string"){if(func1(data3) > 120){const err13 = {instancePath:instancePath+"/name",schemaPath:"#/properties/name/maxLength",keyword:"maxLength",params:{limit: 120},message:"must NOT have more than 120 characters"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}if(func1(data3) < 1){const err14 = {instancePath:instancePath+"/name",schemaPath:"#/properties/name/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;}}else {const err15 = {instancePath:instancePath+"/name",schemaPath:"#/properties/name/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err15];}else {vErrors.push(err15);}errors++;}}if(data.description !== undefined){let data4 = data.description;if(typeof data4 === "string"){if(func1(data4) > 1000){const err16 = {instancePath:instancePath+"/description",schemaPath:"#/properties/description/maxLength",keyword:"maxLength",params:{limit: 1000},message:"must NOT have more than 1000 characters"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}if(func1(data4) < 1){const err17 = {instancePath:instancePath+"/description",schemaPath:"#/properties/description/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}else {const err18 = {instancePath:instancePath+"/description",schemaPath:"#/properties/description/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}if(data.identity !== undefined){if(!(validate21(data.identity, {instancePath:instancePath+"/identity",parentData:data,parentDataProperty:"identity",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate21.errors : vErrors.concat(validate21.errors);errors = vErrors.length;}}if(data.capabilities !== undefined){let data6 = data.capabilities;const _errs11 = errors;let valid1 = false;let passing0 = null;const _errs12 = errors;if(data6 && typeof data6 == "object" && !Array.isArray(data6)){if(data6.schema === undefined){const err19 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}if(data6.sessionReusePolicy === undefined){const err20 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "sessionReusePolicy"},message:"must have required property '"+"sessionReusePolicy"+"'"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}if(data6.driver === undefined){const err21 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "driver"},message:"must have required property '"+"driver"+"'"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(data6.steer === undefined){const err22 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "steer"},message:"must have required property '"+"steer"+"'"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}if(data6.interrupt === undefined){const err23 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "interrupt"},message:"must have required property '"+"interrupt"+"'"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}if(data6.resume === undefined){const err24 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "resume"},message:"must have required property '"+"resume"+"'"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}if(data6.runtimeRequests === undefined){const err25 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "runtimeRequests"},message:"must have required property '"+"runtimeRequests"+"'"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}if(data6.structuredResult === undefined){const err26 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "structuredResult"},message:"must have required property '"+"structuredResult"+"'"};if(vErrors === null){vErrors = [err26];}else {vErrors.push(err26);}errors++;}if(data6.typedEvents === undefined){const err27 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/required",keyword:"required",params:{missingProperty: "typedEvents"},message:"must have required property '"+"typedEvents"+"'"};if(vErrors === null){vErrors = [err27];}else {vErrors.push(err27);}errors++;}if(data6.schema !== undefined){if("paperclip.prp.capabilities.v1" !== data6.schema){const err28 = {instancePath:instancePath+"/capabilities/schema",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.capabilities.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err28];}else {vErrors.push(err28);}errors++;}}if(data6.sessionReusePolicy !== undefined){let data8 = data6.sessionReusePolicy;if(!(((data8 === "new_per_run") || (data8 === "reuse_per_issue")) || (data8 === "reuse_per_workspace"))){const err29 = {instancePath:instancePath+"/capabilities/sessionReusePolicy",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/sessionReusePolicy/enum",keyword:"enum",params:{allowedValues: schema41.properties.sessionReusePolicy.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err29];}else {vErrors.push(err29);}errors++;}}if(data6.driver !== undefined){let data9 = data6.driver;if(data9 && typeof data9 == "object" && !Array.isArray(data9)){if(data9.kind === undefined){const err30 = {instancePath:instancePath+"/capabilities/driver",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/required",keyword:"required",params:{missingProperty: "kind"},message:"must have required property '"+"kind"+"'"};if(vErrors === null){vErrors = [err30];}else {vErrors.push(err30);}errors++;}if(data9.version === undefined){const err31 = {instancePath:instancePath+"/capabilities/driver",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/required",keyword:"required",params:{missingProperty: "version"},message:"must have required property '"+"version"+"'"};if(vErrors === null){vErrors = [err31];}else {vErrors.push(err31);}errors++;}if(data9.kind !== undefined){let data10 = data9.kind;if(typeof data10 === "string"){if(func1(data10) > 80){const err32 = {instancePath:instancePath+"/capabilities/driver/kind",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/properties/kind/maxLength",keyword:"maxLength",params:{limit: 80},message:"must NOT have more than 80 characters"};if(vErrors === null){vErrors = [err32];}else {vErrors.push(err32);}errors++;}if(func1(data10) < 1){const err33 = {instancePath:instancePath+"/capabilities/driver/kind",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/properties/kind/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err33];}else {vErrors.push(err33);}errors++;}}else {const err34 = {instancePath:instancePath+"/capabilities/driver/kind",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/properties/kind/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err34];}else {vErrors.push(err34);}errors++;}}if(data9.version !== undefined){let data11 = data9.version;if(typeof data11 === "string"){if(func1(data11) > 80){const err35 = {instancePath:instancePath+"/capabilities/driver/version",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/properties/version/maxLength",keyword:"maxLength",params:{limit: 80},message:"must NOT have more than 80 characters"};if(vErrors === null){vErrors = [err35];}else {vErrors.push(err35);}errors++;}if(func1(data11) < 1){const err36 = {instancePath:instancePath+"/capabilities/driver/version",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/properties/version/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err36];}else {vErrors.push(err36);}errors++;}}else {const err37 = {instancePath:instancePath+"/capabilities/driver/version",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/properties/version/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err37];}else {vErrors.push(err37);}errors++;}}}else {const err38 = {instancePath:instancePath+"/capabilities/driver",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/driver/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err38];}else {vErrors.push(err38);}errors++;}}if(data6.steer !== undefined){if(typeof data6.steer !== "boolean"){const err39 = {instancePath:instancePath+"/capabilities/steer",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/steer/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err39];}else {vErrors.push(err39);}errors++;}}if(data6.interrupt !== undefined){if(typeof data6.interrupt !== "boolean"){const err40 = {instancePath:instancePath+"/capabilities/interrupt",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/interrupt/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err40];}else {vErrors.push(err40);}errors++;}}if(data6.resume !== undefined){if(typeof data6.resume !== "boolean"){const err41 = {instancePath:instancePath+"/capabilities/resume",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/resume/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err41];}else {vErrors.push(err41);}errors++;}}if(data6.runtimeRequests !== undefined){if(typeof data6.runtimeRequests !== "boolean"){const err42 = {instancePath:instancePath+"/capabilities/runtimeRequests",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/runtimeRequests/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err42];}else {vErrors.push(err42);}errors++;}}if(data6.structuredResult !== undefined){if(typeof data6.structuredResult !== "boolean"){const err43 = {instancePath:instancePath+"/capabilities/structuredResult",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/structuredResult/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err43];}else {vErrors.push(err43);}errors++;}}if(data6.typedEvents !== undefined){if(typeof data6.typedEvents !== "boolean"){const err44 = {instancePath:instancePath+"/capabilities/typedEvents",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEvents/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err44];}else {vErrors.push(err44);}errors++;}}if(data6.typedEventFamilies !== undefined){let data18 = data6.typedEventFamilies;if(Array.isArray(data18)){if(data18.length > 64){const err45 = {instancePath:instancePath+"/capabilities/typedEventFamilies",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/maxItems",keyword:"maxItems",params:{limit: 64},message:"must NOT have more than 64 items"};if(vErrors === null){vErrors = [err45];}else {vErrors.push(err45);}errors++;}const len0 = data18.length;for(let i0=0; i0 160){const err51 = {instancePath:instancePath+"/capabilities/typedEventFamilies/" + i0+"/family",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/items/properties/family/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err51];}else {vErrors.push(err51);}errors++;}if(func1(data20) < 1){const err52 = {instancePath:instancePath+"/capabilities/typedEventFamilies/" + i0+"/family",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/items/properties/family/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err52];}else {vErrors.push(err52);}errors++;}if(!pattern12.test(data20)){const err53 = {instancePath:instancePath+"/capabilities/typedEventFamilies/" + i0+"/family",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/items/properties/family/pattern",keyword:"pattern",params:{pattern: "^[a-z][a-z0-9_.-]*$"},message:"must match pattern \""+"^[a-z][a-z0-9_.-]*$"+"\""};if(vErrors === null){vErrors = [err53];}else {vErrors.push(err53);}errors++;}}else {const err54 = {instancePath:instancePath+"/capabilities/typedEventFamilies/" + i0+"/family",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/items/properties/family/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err54];}else {vErrors.push(err54);}errors++;}}if(data19.version !== undefined){if(1 !== data19.version){const err55 = {instancePath:instancePath+"/capabilities/typedEventFamilies/" + i0+"/version",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/items/properties/version/const",keyword:"const",params:{allowedValue: 1},message:"must be equal to constant"};if(vErrors === null){vErrors = [err55];}else {vErrors.push(err55);}errors++;}}if(data19.availability !== undefined){let data22 = data19.availability;if(!(((data22 === "available") || (data22 === "unsupported")) || (data22 === "policy_disabled"))){const err56 = {instancePath:instancePath+"/capabilities/typedEventFamilies/" + i0+"/availability",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/items/properties/availability/enum",keyword:"enum",params:{allowedValues: schema41.properties.typedEventFamilies.items.properties.availability.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err56];}else {vErrors.push(err56);}errors++;}}if(data19.detailLevel !== undefined){let data23 = data19.detailLevel;if(!((data23 === "summary") || (data23 === "structured"))){const err57 = {instancePath:instancePath+"/capabilities/typedEventFamilies/" + i0+"/detailLevel",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/items/properties/detailLevel/enum",keyword:"enum",params:{allowedValues: schema41.properties.typedEventFamilies.items.properties.detailLevel.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err57];}else {vErrors.push(err57);}errors++;}}}else {const err58 = {instancePath:instancePath+"/capabilities/typedEventFamilies/" + i0,schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/items/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err58];}else {vErrors.push(err58);}errors++;}}}else {const err59 = {instancePath:instancePath+"/capabilities/typedEventFamilies",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/typedEventFamilies/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err59];}else {vErrors.push(err59);}errors++;}}if(data6.semanticTools !== undefined){let data24 = data6.semanticTools;if(data24 && typeof data24 == "object" && !Array.isArray(data24)){if(data24.schema === undefined){const err60 = {instancePath:instancePath+"/capabilities/semanticTools",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err60];}else {vErrors.push(err60);}errors++;}if(data24.schemaVersion === undefined){const err61 = {instancePath:instancePath+"/capabilities/semanticTools",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/required",keyword:"required",params:{missingProperty: "schemaVersion"},message:"must have required property '"+"schemaVersion"+"'"};if(vErrors === null){vErrors = [err61];}else {vErrors.push(err61);}errors++;}if(data24.operations === undefined){const err62 = {instancePath:instancePath+"/capabilities/semanticTools",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/required",keyword:"required",params:{missingProperty: "operations"},message:"must have required property '"+"operations"+"'"};if(vErrors === null){vErrors = [err62];}else {vErrors.push(err62);}errors++;}if(data24.schema !== undefined){if("paperclip.prp.semantic_tools.v1" !== data24.schema){const err63 = {instancePath:instancePath+"/capabilities/semanticTools/schema",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.semantic_tools.v1"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err63];}else {vErrors.push(err63);}errors++;}}if(data24.schemaVersion !== undefined){if(1 !== data24.schemaVersion){const err64 = {instancePath:instancePath+"/capabilities/semanticTools/schemaVersion",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/schemaVersion/const",keyword:"const",params:{allowedValue: 1},message:"must be equal to constant"};if(vErrors === null){vErrors = [err64];}else {vErrors.push(err64);}errors++;}}if(data24.operations !== undefined){let data27 = data24.operations;if(Array.isArray(data27)){const len1 = data27.length;for(let i1=0; i1 160){const err69 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/operationId",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/operationId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err69];}else {vErrors.push(err69);}errors++;}if(func1(data29) < 1){const err70 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/operationId",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/operationId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err70];}else {vErrors.push(err70);}errors++;}if(!pattern4.test(data29)){const err71 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/operationId",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/operationId/pattern",keyword:"pattern",params:{pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]*$"},message:"must match pattern \""+"^[A-Za-z0-9][A-Za-z0-9._:-]*$"+"\""};if(vErrors === null){vErrors = [err71];}else {vErrors.push(err71);}errors++;}}else {const err72 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/operationId",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/operationId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err72];}else {vErrors.push(err72);}errors++;}}if(data28.version !== undefined){if(1 !== data28.version){const err73 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/version",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/version/const",keyword:"const",params:{allowedValue: 1},message:"must be equal to constant"};if(vErrors === null){vErrors = [err73];}else {vErrors.push(err73);}errors++;}}if(data28.availability !== undefined){let data31 = data28.availability;if(!(((data31 === "available") || (data31 === "denied")) || (data31 === "unsupported"))){const err74 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/availability",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/availability/enum",keyword:"enum",params:{allowedValues: schema41.properties.semanticTools.properties.operations.items.properties.availability.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err74];}else {vErrors.push(err74);}errors++;}}if(data28.redactionDisposition !== undefined){if("digest_only" !== data28.redactionDisposition){const err75 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/redactionDisposition",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/redactionDisposition/const",keyword:"const",params:{allowedValue: "digest_only"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err75];}else {vErrors.push(err75);}errors++;}}if(data28.requiredClaims !== undefined){let data33 = data28.requiredClaims;if(Array.isArray(data33)){const len2 = data33.length;for(let i2=0; i2 160){const err76 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/requiredClaims/" + i2,schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/requiredClaims/items/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err76];}else {vErrors.push(err76);}errors++;}if(func1(data34) < 1){const err77 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/requiredClaims/" + i2,schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/requiredClaims/items/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err77];}else {vErrors.push(err77);}errors++;}}else {const err78 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/requiredClaims/" + i2,schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/requiredClaims/items/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err78];}else {vErrors.push(err78);}errors++;}}let i3 = data33.length;let j0;if(i3 > 1){const indices0 = {};for(;i3--;){let item0 = data33[i3];if(typeof item0 !== "string"){continue;}if(typeof indices0[item0] == "number"){j0 = indices0[item0];const err79 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/requiredClaims",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/requiredClaims/uniqueItems",keyword:"uniqueItems",params:{i: i3, j: j0},message:"must NOT have duplicate items (items ## "+j0+" and "+i3+" are identical)"};if(vErrors === null){vErrors = [err79];}else {vErrors.push(err79);}errors++;break;}indices0[item0] = i3;}}}else {const err80 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/requiredClaims",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/requiredClaims/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err80];}else {vErrors.push(err80);}errors++;}}if(data28.reasonCode !== undefined){let data35 = data28.reasonCode;if(typeof data35 === "string"){if(func1(data35) > 160){const err81 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/reasonCode/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err81];}else {vErrors.push(err81);}errors++;}if(func1(data35) < 1){const err82 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/reasonCode/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err82];}else {vErrors.push(err82);}errors++;}if(!pattern14.test(data35)){const err83 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/reasonCode/pattern",keyword:"pattern",params:{pattern: "^[a-z][a-z0-9_.:-]*$"},message:"must match pattern \""+"^[a-z][a-z0-9_.:-]*$"+"\""};if(vErrors === null){vErrors = [err83];}else {vErrors.push(err83);}errors++;}}else {const err84 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1+"/reasonCode",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/properties/reasonCode/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err84];}else {vErrors.push(err84);}errors++;}}}else {const err85 = {instancePath:instancePath+"/capabilities/semanticTools/operations/" + i1,schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/items/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err85];}else {vErrors.push(err85);}errors++;}}}else {const err86 = {instancePath:instancePath+"/capabilities/semanticTools/operations",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/properties/operations/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err86];}else {vErrors.push(err86);}errors++;}}}else {const err87 = {instancePath:instancePath+"/capabilities/semanticTools",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/semanticTools/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err87];}else {vErrors.push(err87);}errors++;}}if(data6.unsupported !== undefined){let data36 = data6.unsupported;if(Array.isArray(data36)){const len3 = data36.length;for(let i4=0; i4 1){const indices1 = {};for(;i5--;){let item1 = data36[i5];if(typeof item1 !== "string"){continue;}if(typeof indices1[item1] == "number"){j1 = indices1[item1];const err90 = {instancePath:instancePath+"/capabilities/unsupported",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/unsupported/uniqueItems",keyword:"uniqueItems",params:{i: i5, j: j1},message:"must NOT have duplicate items (items ## "+j1+" and "+i5+" are identical)"};if(vErrors === null){vErrors = [err90];}else {vErrors.push(err90);}errors++;break;}indices1[item1] = i5;}}}else {const err91 = {instancePath:instancePath+"/capabilities/unsupported",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/properties/unsupported/type",keyword:"type",params:{type: "array"},message:"must be array"};if(vErrors === null){vErrors = [err91];}else {vErrors.push(err91);}errors++;}}}else {const err92 = {instancePath:instancePath+"/capabilities",schemaPath:"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err92];}else {vErrors.push(err92);}errors++;}var _valid0 = _errs12 === errors;if(_valid0){valid1 = true;passing0 = 0;var props0 = true;}const _errs72 = errors;if(!(validate23(data6, {instancePath:instancePath+"/capabilities",parentData:data,parentDataProperty:"capabilities",rootData,dynamicAnchors}))){vErrors = vErrors === null ? validate23.errors : vErrors.concat(validate23.errors);errors = vErrors.length;}var _valid0 = _errs72 === errors;if(_valid0 && valid1){valid1 = false;passing0 = [passing0, 1];}else {if(_valid0){valid1 = true;passing0 = 1;if(props0 !== true){props0 = true;}}}if(!valid1){const err93 = {instancePath:instancePath+"/capabilities",schemaPath:"#/properties/capabilities/oneOf",keyword:"oneOf",params:{passingSchemas: passing0},message:"must match exactly one schema in oneOf"};if(vErrors === null){vErrors = [err93];}else {vErrors.push(err93);}errors++;}else {errors = _errs11;if(vErrors !== null){if(_errs11){vErrors.length = _errs11;}else {vErrors = null;}}}}if(data.commands !== undefined){let data38 = data.commands;if(Array.isArray(data38)){const len4 = data38.length;for(let i6=0; i6 160){const err101 = {instancePath:instancePath+"/commands/" + i6+"/commandId",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/commandId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err101];}else {vErrors.push(err101);}errors++;}if(func1(data41) < 1){const err102 = {instancePath:instancePath+"/commands/" + i6+"/commandId",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/commandId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err102];}else {vErrors.push(err102);}errors++;}}else {const err103 = {instancePath:instancePath+"/commands/" + i6+"/commandId",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/commandId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err103];}else {vErrors.push(err103);}errors++;}}if(data39.controllerSeq !== undefined){let data42 = data39.controllerSeq;if(!(((typeof data42 == "number") && (!(data42 % 1) && !isNaN(data42))) && (isFinite(data42)))){const err104 = {instancePath:instancePath+"/commands/" + i6+"/controllerSeq",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/controllerSeq/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err104];}else {vErrors.push(err104);}errors++;}if((typeof data42 == "number") && (isFinite(data42))){if(data42 < 1 || isNaN(data42)){const err105 = {instancePath:instancePath+"/commands/" + i6+"/controllerSeq",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/controllerSeq/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err105];}else {vErrors.push(err105);}errors++;}}}if(data39.controllerEpoch !== undefined){let data43 = data39.controllerEpoch;if(typeof data43 === "string"){if(!pattern15.test(data43)){const err106 = {instancePath:instancePath+"/commands/" + i6+"/controllerEpoch",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/controllerEpoch/pattern",keyword:"pattern",params:{pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},message:"must match pattern \""+"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"+"\""};if(vErrors === null){vErrors = [err106];}else {vErrors.push(err106);}errors++;}}else {const err107 = {instancePath:instancePath+"/commands/" + i6+"/controllerEpoch",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/controllerEpoch/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err107];}else {vErrors.push(err107);}errors++;}}if(data39.type !== undefined){let data44 = data39.type;if(!((((((((((((((((((data44 === "run.prepare") || (data44 === "run.attach")) || (data44 === "session.open")) || (data44 === "turn.start")) || (data44 === "turn.steer")) || (data44 === "turn.interrupt")) || (data44 === "turn.stop")) || (data44 === "request.resolve")) || (data44 === "interaction.receipt")) || (data44 === "semantic_tool.result")) || (data44 === "session.snapshot")) || (data44 === "session.close")) || (data44 === "session.budget.increase")) || (data44 === "session.destroy")) || (data44 === "run.cancel")) || (data44 === "runner.drain")) || (data44 === "runner.suspend")) || (data44 === "runner.shutdown"))){const err108 = {instancePath:instancePath+"/commands/" + i6+"/type",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/type/enum",keyword:"enum",params:{allowedValues: schema45.properties.type.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err108];}else {vErrors.push(err108);}errors++;}}if(data39.issuedAt !== undefined){let data45 = data39.issuedAt;if(typeof data45 === "string"){if(!(formats0.test(data45))){const err109 = {instancePath:instancePath+"/commands/" + i6+"/issuedAt",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/issuedAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err109];}else {vErrors.push(err109);}errors++;}}else {const err110 = {instancePath:instancePath+"/commands/" + i6+"/issuedAt",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/issuedAt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err110];}else {vErrors.push(err110);}errors++;}}if(data39.deadlineAt !== undefined){let data46 = data39.deadlineAt;if(typeof data46 === "string"){if(!(formats0.test(data46))){const err111 = {instancePath:instancePath+"/commands/" + i6+"/deadlineAt",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/deadlineAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err111];}else {vErrors.push(err111);}errors++;}}else {const err112 = {instancePath:instancePath+"/commands/" + i6+"/deadlineAt",schemaPath:"https://paperclip.dev/schemas/prp/v1/command.schema.json/properties/deadlineAt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err112];}else {vErrors.push(err112);}errors++;}}if(data39.precondition !== undefined){let data47 = data39.precondition;if(data47 && typeof data47 == "object" && !Array.isArray(data47)){if(data47.runnerState !== undefined){let data48 = data47.runnerState;if(Array.isArray(data48)){const len5 = data48.length;for(let i7=0; i7 160){const err124 = {instancePath:instancePath+"/commands/" + i6+"/payload/requestId",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/requestId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err124];}else {vErrors.push(err124);}errors++;}if(func1(data58) < 1){const err125 = {instancePath:instancePath+"/commands/" + i6+"/payload/requestId",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/requestId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err125];}else {vErrors.push(err125);}errors++;}}else {const err126 = {instancePath:instancePath+"/commands/" + i6+"/payload/requestId",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/requestId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err126];}else {vErrors.push(err126);}errors++;}}if(data57.objective !== undefined){let data59 = data57.objective;if(typeof data59 === "string"){if(func1(data59) > 4000){const err127 = {instancePath:instancePath+"/commands/" + i6+"/payload/objective",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/objective/maxLength",keyword:"maxLength",params:{limit: 4000},message:"must NOT have more than 4000 characters"};if(vErrors === null){vErrors = [err127];}else {vErrors.push(err127);}errors++;}if(func1(data59) < 1){const err128 = {instancePath:instancePath+"/commands/" + i6+"/payload/objective",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/objective/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err128];}else {vErrors.push(err128);}errors++;}}else {const err129 = {instancePath:instancePath+"/commands/" + i6+"/payload/objective",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/objective/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err129];}else {vErrors.push(err129);}errors++;}}if(data57.status !== undefined){let data60 = data57.status;if(!((data60 === "active") || (data60 === "paused"))){const err130 = {instancePath:instancePath+"/commands/" + i6+"/payload/status",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/status/enum",keyword:"enum",params:{allowedValues: schema46.allOf[0].then.properties.payload.properties.status.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err130];}else {vErrors.push(err130);}errors++;}}if(data57.tokenBudget !== undefined){let data61 = data57.tokenBudget;if((!(((typeof data61 == "number") && (!(data61 % 1) && !isNaN(data61))) && (isFinite(data61)))) && (data61 !== null)){const err131 = {instancePath:instancePath+"/commands/" + i6+"/payload/tokenBudget",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/tokenBudget/type",keyword:"type",params:{type: schema46.allOf[0].then.properties.payload.properties.tokenBudget.type},message:"must be integer,null"};if(vErrors === null){vErrors = [err131];}else {vErrors.push(err131);}errors++;}if((typeof data61 == "number") && (isFinite(data61))){if(data61 < 1 || isNaN(data61)){const err132 = {instancePath:instancePath+"/commands/" + i6+"/payload/tokenBudget",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/properties/tokenBudget/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err132];}else {vErrors.push(err132);}errors++;}}}}else {const err133 = {instancePath:instancePath+"/commands/" + i6+"/payload",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/then/properties/payload/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err133];}else {vErrors.push(err133);}errors++;}}}var _valid2 = _errs120 === errors;valid32 = _valid2;if(valid32){var props2 = {};props2.payload = true;props2.type = true;}}if(!valid32){const err134 = {instancePath:instancePath+"/commands/" + i6,schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/allOf/0/if",keyword:"if",params:{failingKeyword: "then"},message:"must match \"then\" schema"};if(vErrors === null){vErrors = [err134];}else {vErrors.push(err134);}errors++;}if(data39 && typeof data39 == "object" && !Array.isArray(data39)){if(data39.schema === undefined){const err135 = {instancePath:instancePath+"/commands/" + i6,schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/required",keyword:"required",params:{missingProperty: "schema"},message:"must have required property '"+"schema"+"'"};if(vErrors === null){vErrors = [err135];}else {vErrors.push(err135);}errors++;}if(data39.commandId === undefined){const err136 = {instancePath:instancePath+"/commands/" + i6,schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/required",keyword:"required",params:{missingProperty: "commandId"},message:"must have required property '"+"commandId"+"'"};if(vErrors === null){vErrors = [err136];}else {vErrors.push(err136);}errors++;}if(data39.controllerSeq === undefined){const err137 = {instancePath:instancePath+"/commands/" + i6,schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/required",keyword:"required",params:{missingProperty: "controllerSeq"},message:"must have required property '"+"controllerSeq"+"'"};if(vErrors === null){vErrors = [err137];}else {vErrors.push(err137);}errors++;}if(data39.type === undefined){const err138 = {instancePath:instancePath+"/commands/" + i6,schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/required",keyword:"required",params:{missingProperty: "type"},message:"must have required property '"+"type"+"'"};if(vErrors === null){vErrors = [err138];}else {vErrors.push(err138);}errors++;}if(data39.issuedAt === undefined){const err139 = {instancePath:instancePath+"/commands/" + i6,schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/required",keyword:"required",params:{missingProperty: "issuedAt"},message:"must have required property '"+"issuedAt"+"'"};if(vErrors === null){vErrors = [err139];}else {vErrors.push(err139);}errors++;}if(data39.payload === undefined){const err140 = {instancePath:instancePath+"/commands/" + i6,schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/required",keyword:"required",params:{missingProperty: "payload"},message:"must have required property '"+"payload"+"'"};if(vErrors === null){vErrors = [err140];}else {vErrors.push(err140);}errors++;}if(data39.schema !== undefined){if("paperclip.prp.command.v2" !== data39.schema){const err141 = {instancePath:instancePath+"/commands/" + i6+"/schema",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/schema/const",keyword:"const",params:{allowedValue: "paperclip.prp.command.v2"},message:"must be equal to constant"};if(vErrors === null){vErrors = [err141];}else {vErrors.push(err141);}errors++;}}if(data39.commandId !== undefined){let data63 = data39.commandId;if(typeof data63 === "string"){if(func1(data63) > 160){const err142 = {instancePath:instancePath+"/commands/" + i6+"/commandId",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/commandId/maxLength",keyword:"maxLength",params:{limit: 160},message:"must NOT have more than 160 characters"};if(vErrors === null){vErrors = [err142];}else {vErrors.push(err142);}errors++;}if(func1(data63) < 1){const err143 = {instancePath:instancePath+"/commands/" + i6+"/commandId",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/commandId/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err143];}else {vErrors.push(err143);}errors++;}}else {const err144 = {instancePath:instancePath+"/commands/" + i6+"/commandId",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/commandId/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err144];}else {vErrors.push(err144);}errors++;}}if(data39.controllerSeq !== undefined){let data64 = data39.controllerSeq;if(!(((typeof data64 == "number") && (!(data64 % 1) && !isNaN(data64))) && (isFinite(data64)))){const err145 = {instancePath:instancePath+"/commands/" + i6+"/controllerSeq",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/controllerSeq/type",keyword:"type",params:{type: "integer"},message:"must be integer"};if(vErrors === null){vErrors = [err145];}else {vErrors.push(err145);}errors++;}if((typeof data64 == "number") && (isFinite(data64))){if(data64 < 1 || isNaN(data64)){const err146 = {instancePath:instancePath+"/commands/" + i6+"/controllerSeq",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/controllerSeq/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err146];}else {vErrors.push(err146);}errors++;}}}if(data39.controllerEpoch !== undefined){let data65 = data39.controllerEpoch;if(typeof data65 === "string"){if(!pattern15.test(data65)){const err147 = {instancePath:instancePath+"/commands/" + i6+"/controllerEpoch",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/controllerEpoch/pattern",keyword:"pattern",params:{pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"},message:"must match pattern \""+"^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"+"\""};if(vErrors === null){vErrors = [err147];}else {vErrors.push(err147);}errors++;}}else {const err148 = {instancePath:instancePath+"/commands/" + i6+"/controllerEpoch",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/controllerEpoch/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err148];}else {vErrors.push(err148);}errors++;}}if(data39.type !== undefined){let data66 = data39.type;if(!(((((((((((((((((((((data66 === "run.prepare") || (data66 === "run.attach")) || (data66 === "session.open")) || (data66 === "turn.start")) || (data66 === "turn.steer")) || (data66 === "turn.interrupt")) || (data66 === "turn.stop")) || (data66 === "request.resolve")) || (data66 === "interaction.receipt")) || (data66 === "semantic_tool.result")) || (data66 === "session.snapshot")) || (data66 === "session.close")) || (data66 === "session.budget.increase")) || (data66 === "session.destroy")) || (data66 === "run.cancel")) || (data66 === "runner.drain")) || (data66 === "runner.suspend")) || (data66 === "runner.shutdown")) || (data66 === "session.goal.get")) || (data66 === "session.goal.set")) || (data66 === "session.goal.clear"))){const err149 = {instancePath:instancePath+"/commands/" + i6+"/type",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/type/enum",keyword:"enum",params:{allowedValues: schema46.properties.type.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err149];}else {vErrors.push(err149);}errors++;}}if(data39.issuedAt !== undefined){let data67 = data39.issuedAt;if(typeof data67 === "string"){if(!(formats0.test(data67))){const err150 = {instancePath:instancePath+"/commands/" + i6+"/issuedAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/issuedAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err150];}else {vErrors.push(err150);}errors++;}}else {const err151 = {instancePath:instancePath+"/commands/" + i6+"/issuedAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/issuedAt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err151];}else {vErrors.push(err151);}errors++;}}if(data39.deadlineAt !== undefined){let data68 = data39.deadlineAt;if(typeof data68 === "string"){if(!(formats0.test(data68))){const err152 = {instancePath:instancePath+"/commands/" + i6+"/deadlineAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/deadlineAt/format",keyword:"format",params:{format: "date-time"},message:"must match format \""+"date-time"+"\""};if(vErrors === null){vErrors = [err152];}else {vErrors.push(err152);}errors++;}}else {const err153 = {instancePath:instancePath+"/commands/" + i6+"/deadlineAt",schemaPath:"https://paperclip.dev/schemas/prp/v2/command.schema.json/properties/deadlineAt/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err153];}else {vErrors.push(err153);}errors++;}}if(data39.precondition !== undefined){let data69 = data39.precondition;if(data69 && typeof data69 == "object" && !Array.isArray(data69)){if(data69.runnerState !== undefined){let data70 = data69.runnerState;if(Array.isArray(data70)){const len8 = data70.length;for(let i10=0; i10 12000){const err186 = {instancePath:instancePath+"/result/summary",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/summary/maxLength",keyword:"maxLength",params:{limit: 12000},message:"must NOT have more than 12000 characters"};if(vErrors === null){vErrors = [err186];}else {vErrors.push(err186);}errors++;}if(func1(data87) < 1){const err187 = {instancePath:instancePath+"/result/summary",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/summary/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err187];}else {vErrors.push(err187);}errors++;}}else {const err188 = {instancePath:instancePath+"/result/summary",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/summary/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err188];}else {vErrors.push(err188);}errors++;}}if(data82.completionClaim !== undefined){let data88 = data82.completionClaim;if(data88 && typeof data88 == "object" && !Array.isArray(data88)){if(data88.contractRevision === undefined){const err189 = {instancePath:instancePath+"/result/completionClaim",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/required",keyword:"required",params:{missingProperty: "contractRevision"},message:"must have required property '"+"contractRevision"+"'"};if(vErrors === null){vErrors = [err189];}else {vErrors.push(err189);}errors++;}if(data88.objectiveSatisfied === undefined){const err190 = {instancePath:instancePath+"/result/completionClaim",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/required",keyword:"required",params:{missingProperty: "objectiveSatisfied"},message:"must have required property '"+"objectiveSatisfied"+"'"};if(vErrors === null){vErrors = [err190];}else {vErrors.push(err190);}errors++;}if(data88.criteria === undefined){const err191 = {instancePath:instancePath+"/result/completionClaim",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/required",keyword:"required",params:{missingProperty: "criteria"},message:"must have required property '"+"criteria"+"'"};if(vErrors === null){vErrors = [err191];}else {vErrors.push(err191);}errors++;}if(data88.remainingWork === undefined){const err192 = {instancePath:instancePath+"/result/completionClaim",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/required",keyword:"required",params:{missingProperty: "remainingWork"},message:"must have required property '"+"remainingWork"+"'"};if(vErrors === null){vErrors = [err192];}else {vErrors.push(err192);}errors++;}if(data88.contractRevision !== undefined){let data89 = data88.contractRevision;if(typeof data89 === "string"){if(func1(data89) < 1){const err193 = {instancePath:instancePath+"/result/completionClaim/contractRevision",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/properties/contractRevision/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err193];}else {vErrors.push(err193);}errors++;}}else {const err194 = {instancePath:instancePath+"/result/completionClaim/contractRevision",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/properties/contractRevision/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err194];}else {vErrors.push(err194);}errors++;}}if(data88.objectiveSatisfied !== undefined){if(typeof data88.objectiveSatisfied !== "boolean"){const err195 = {instancePath:instancePath+"/result/completionClaim/objectiveSatisfied",schemaPath:"https://paperclip.dev/schemas/prp/v1/result.schema.json/properties/completionClaim/properties/objectiveSatisfied/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err195];}else {vErrors.push(err195);}errors++;}}if(data88.criteria !== undefined){let data91 = data88.criteria;if(Array.isArray(data91)){const len13 = data91.length;for(let i15=0; i15 12000){const err16 = {instancePath:instancePath+"/summary",schemaPath:"#/properties/summary/maxLength",keyword:"maxLength",params:{limit: 12000},message:"must NOT have more than 12000 characters"};if(vErrors === null){vErrors = [err16];}else {vErrors.push(err16);}errors++;}if(func1(data4) < 1){const err17 = {instancePath:instancePath+"/summary",schemaPath:"#/properties/summary/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err17];}else {vErrors.push(err17);}errors++;}}else {const err18 = {instancePath:instancePath+"/summary",schemaPath:"#/properties/summary/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err18];}else {vErrors.push(err18);}errors++;}}if(data.completionClaim !== undefined){let data5 = data.completionClaim;if(data5 && typeof data5 == "object" && !Array.isArray(data5)){if(data5.contractRevision === undefined){const err19 = {instancePath:instancePath+"/completionClaim",schemaPath:"#/properties/completionClaim/required",keyword:"required",params:{missingProperty: "contractRevision"},message:"must have required property '"+"contractRevision"+"'"};if(vErrors === null){vErrors = [err19];}else {vErrors.push(err19);}errors++;}if(data5.objectiveSatisfied === undefined){const err20 = {instancePath:instancePath+"/completionClaim",schemaPath:"#/properties/completionClaim/required",keyword:"required",params:{missingProperty: "objectiveSatisfied"},message:"must have required property '"+"objectiveSatisfied"+"'"};if(vErrors === null){vErrors = [err20];}else {vErrors.push(err20);}errors++;}if(data5.criteria === undefined){const err21 = {instancePath:instancePath+"/completionClaim",schemaPath:"#/properties/completionClaim/required",keyword:"required",params:{missingProperty: "criteria"},message:"must have required property '"+"criteria"+"'"};if(vErrors === null){vErrors = [err21];}else {vErrors.push(err21);}errors++;}if(data5.remainingWork === undefined){const err22 = {instancePath:instancePath+"/completionClaim",schemaPath:"#/properties/completionClaim/required",keyword:"required",params:{missingProperty: "remainingWork"},message:"must have required property '"+"remainingWork"+"'"};if(vErrors === null){vErrors = [err22];}else {vErrors.push(err22);}errors++;}if(data5.contractRevision !== undefined){let data6 = data5.contractRevision;if(typeof data6 === "string"){if(func1(data6) < 1){const err23 = {instancePath:instancePath+"/completionClaim/contractRevision",schemaPath:"#/properties/completionClaim/properties/contractRevision/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err23];}else {vErrors.push(err23);}errors++;}}else {const err24 = {instancePath:instancePath+"/completionClaim/contractRevision",schemaPath:"#/properties/completionClaim/properties/contractRevision/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err24];}else {vErrors.push(err24);}errors++;}}if(data5.objectiveSatisfied !== undefined){if(typeof data5.objectiveSatisfied !== "boolean"){const err25 = {instancePath:instancePath+"/completionClaim/objectiveSatisfied",schemaPath:"#/properties/completionClaim/properties/objectiveSatisfied/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err25];}else {vErrors.push(err25);}errors++;}}if(data5.criteria !== undefined){let data8 = data5.criteria;if(Array.isArray(data8)){const len0 = data8.length;for(let i0=0; i0 { const schemas = await loadSchemaCatalog(resolve(protocolRoot, "schemas")); - assert.equal(schemas.length, 25); + assert.equal(schemas.length, 26); assert.doesNotThrow(() => compileProtocolValidators(schemas)); });