Add deterministic capability control plane (#12355)

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Semantic operations need a control plane that owns authorization and
state changes
> - Conformance tests need this authority without a live server or
database
> - The existing in-memory port covers transport facts but not
capability-governed domains
> - This pull request adds a deterministic capability-aware control
plane for tests
> - It keeps the implementation behind the package testing entry point
> - The benefit is repeatable authorization and mutation tests before
production wiring lands

## Linked Issues or Issue Description

**Subsystem affected**

packages/paperclip-runner testing and semantic capability contracts

**Problem or motivation**

Later semantic action slices need deterministic company, actor, task,
governance, workspace, secret, budget, routine, and audit state. A live
Paperclip server would make those tests slow and environment-dependent.

**Proposed solution**

Add serializable capability fixture types and an in-memory control-plane
adapter. Enforce company scope, claims, roles, idempotency, redaction,
faults, and audit records. Export the adapter only from the testing
entry point.

**Alternatives considered**

The tests could mock each action separately. That would duplicate
authorization rules and would not verify state transitions across
multiple actions.

**Roadmap alignment**

This supports the existing experimental Paperclip Runner rollout. It
does not enable production semantic operations or change app execution.

## What Changed

- Add deterministic capability fixture types and seed state.
- Add an in-memory capability control-plane adapter.
- Enforce run, company, actor, claim, role, and idempotency boundaries.
- Model governed task, document, interaction, workspace, secret,
routine, and audit changes.
- Add deterministic tests and testing-entry-point exports.

## Verification

- `pnpm --filter @paperclipai/paperclip-runner test:typescript`
- `pnpm --filter @paperclipai/paperclip-runner typecheck:typescript`
- `pnpm -r typecheck`
- `pnpm build`
- The branch changes 5 files relative to its declared base.

## Risks

The main risk is a permissive mock that hides a production authorization
error. The adapter fails closed for missing claims, wrong roles,
cross-company access, duplicate mutations, invalid state, and restricted
secret access. Tests cover each fixture domain and serialized restore
behavior.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

OpenAI Codex, `gpt-5`, with agentic reasoning, tool use, and code
execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either linked an existing public issue or described the
issue in-PR
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
This commit is contained in:
Dotta 2026-08-29 22:54:30 -05:00 committed by GitHub
parent 773d357d8a
commit 300b7f4b7e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 4256 additions and 0 deletions

View File

@ -0,0 +1,558 @@
import type {
CompleteControlPlaneRunInput,
ControlPlanePort,
OpenControlPlaneRunInput,
} from "../contracts/control-plane-port.js";
import type { NativeRunEvent, NativeRunResult } from "../contracts/types.js";
import type { PersistedNativeSession } from "../contracts/native-session-backend.js";
import type { PrpEvent } from "../protocol/replay-contract.js";
export type CapabilityJsonPrimitive = string | number | boolean | null;
export type CapabilityJsonValue =
| CapabilityJsonPrimitive
| CapabilityJsonValue[]
| { [key: string]: CapabilityJsonValue };
export type CapabilityTaskStatus =
| "backlog"
| "todo"
| "in_progress"
| "in_review"
| "done"
| "blocked"
| "cancelled";
export interface CapabilityFixtureCompany {
id: string;
name: string;
issuePrefix: string;
status: "active" | "paused";
budgetId: string;
}
export interface CapabilityFixtureActor {
id: string;
companyId: string;
name: string;
role: string;
status: "active" | "paused";
budgetId: string;
capabilityGrants: string[];
}
export interface CapabilityFixtureTask {
id: string;
companyId: string;
identifier: string;
title: string;
description: string | null;
status: CapabilityTaskStatus;
priority: "critical" | "high" | "medium" | "low";
workMode: "standard" | "ask" | "planning" | "skill_test";
parentId: string | null;
assigneeActorId: string | null;
checkoutRunId: string | null;
executionRunId: string | null;
startedAt: string | null;
completedAt: string | null;
}
export interface CapabilityFixtureComment {
id: string;
taskId: string;
authorActorId: string | null;
body: string;
createdAt: string;
}
export interface CapabilityFixtureDocumentRevision {
id: string;
documentId: string;
revision: number;
body: string;
changeSummary: string | null;
createdAt: string;
}
export interface CapabilityFixtureDocument {
id: string;
taskId: string;
key: string;
title: string;
format: "markdown";
latestRevisionId: string;
revisions: CapabilityFixtureDocumentRevision[];
}
export type CapabilityInteractionKind =
| "confirmation"
| "checkbox"
| "questions"
| "suggest_tasks"
| "item_verdicts";
export interface CapabilityFixtureInteraction {
id: string;
taskId: string;
kind: CapabilityInteractionKind;
status: "pending" | "answered" | "accepted" | "rejected" | "expired";
title: string;
prompt: string;
payload: CapabilityJsonValue;
targetRevisionId: string | null;
continuationPolicy: "none" | "wake_assignee" | "wake_assignee_on_accept";
result: CapabilityJsonValue | null;
createdAt: string;
resolvedAt: string | null;
}
export interface CapabilityFixtureApprovalComment {
id: string;
authorActorId: string | null;
body: string;
createdAt: string;
}
export interface CapabilityFixtureApproval {
id: string;
companyId: string;
taskIds: string[];
type: string;
status: "pending" | "approved" | "rejected" | "cancelled";
requestedByActorId: string | null;
payload: CapabilityJsonValue;
decisionNote: string | null;
comments: CapabilityFixtureApprovalComment[];
createdAt: string;
decidedAt: string | null;
}
export interface CapabilityFixtureArtifact {
id: string;
taskId: string;
filename: string;
contentType: string;
byteSize: number;
sha256: string;
contentRef: string;
createdAt: string;
}
export interface CapabilityFixtureWorkProduct {
id: string;
taskId: string;
type: "artifact" | "document" | "pull_request" | "preview_url" | "runtime_service" | "commit" | "branch";
title: string;
metadata: CapabilityJsonValue;
artifactId: string | null;
createdAt: string;
}
export interface CapabilityFixtureBlocker {
id: string;
taskId: string;
blockedByTaskId: string;
createdAt: string;
}
export interface CapabilityFixtureWorkspaceService {
id: string;
taskId: string;
name: string;
status: "stopped" | "starting" | "running" | "failed";
url: string | null;
lastTransitionAt: string;
}
export interface CapabilityFixtureBudget {
id: string;
scope: "company" | "actor";
scopeId: string;
limitCents: number;
spentCents: number;
hardStop: boolean;
}
export type CapabilityWakeReason =
| "manual"
| "issue_commented"
| "interaction_resolved"
| "approval_resolved"
| "blockers_resolved"
| "scheduled_retry"
| "resume";
export interface CapabilityWakeContext {
reason: CapabilityWakeReason;
payload: CapabilityJsonValue;
}
export interface CapabilityScheduledWake {
id: string;
actorId: string;
taskId: string;
reason: CapabilityWakeReason;
payload: CapabilityJsonValue;
dueAt: string;
status: "scheduled" | "delivered" | "cancelled";
createdAt: string;
}
export interface CapabilityFixtureRun {
id: string;
companyId: string;
actorId: string;
taskId: string;
sessionId: string;
backendKind: OpenControlPlaneRunInput["backendKind"];
sourceInstanceId: string;
status: "running" | "succeeded" | "failed" | "cancelled" | "budget_stopped";
attempt: number;
openedAt: string;
finishedAt: string | null;
wake: CapabilityWakeContext;
capabilities: string[];
events: Array<NativeRunEvent | PrpEvent>;
result: NativeRunResult | CompleteControlPlaneRunInput | null;
sessionCheckpoint: PersistedNativeSession | null;
}
export interface CapabilityAuditRecord {
id: string;
at: string;
runId: string | null;
actorId: string | null;
action: string;
entityType: string;
entityId: string;
details: CapabilityJsonValue;
}
export interface CapabilityDecisionRecord {
id: string;
at: string;
runId: string | null;
operation: string;
outcome: "allowed" | "denied" | "duplicate" | "reconciled" | "faulted";
reason: string;
stateRevision: number;
entityRefs: string[];
}
export interface CapabilityFaultRule {
id: string;
operation: "open_run" | "append_event" | "apply_command" | "complete_run";
commandKind?: CapabilitySemanticCommand["kind"];
effect: "retryable_error" | "lost_ack";
remaining: number;
code: string;
}
export interface ScenarioChatdempotencyRecord {
scope: string;
key: string;
inputCanonical: string;
result: CapabilityCommandResult;
}
export interface CapabilityFixtureState {
schema: "paperclip.capability.mock-state.v1";
revision: number;
clock: { epochMs: number; tick: number };
counters: Record<string, number>;
lifecycle: "stopped" | "running";
activeRunId: string | null;
company: CapabilityFixtureCompany;
actors: CapabilityFixtureActor[];
tasks: CapabilityFixtureTask[];
/** Known task ids outside the fixture company, without leaking foreign data. */
outOfScopeTaskIds: string[];
comments: CapabilityFixtureComment[];
documents: CapabilityFixtureDocument[];
interactions: CapabilityFixtureInteraction[];
approvals: CapabilityFixtureApproval[];
artifacts: CapabilityFixtureArtifact[];
workProducts: CapabilityFixtureWorkProduct[];
blockers: CapabilityFixtureBlocker[];
workspaceServices: CapabilityFixtureWorkspaceService[];
budgets: CapabilityFixtureBudget[];
runs: CapabilityFixtureRun[];
wakes: CapabilityScheduledWake[];
audit: CapabilityAuditRecord[];
decisions: CapabilityDecisionRecord[];
idempotency: ScenarioChatdempotencyRecord[];
faults: CapabilityFaultRule[];
}
export interface CapabilityRunContext {
schema: "paperclip.capability.run-context.v1";
company: Pick<CapabilityFixtureCompany, "id" | "name" | "issuePrefix" | "status">;
actor: Pick<CapabilityFixtureActor, "id" | "name" | "role" | "status" | "capabilityGrants">;
activeTask: CapabilityFixtureTask;
ancestors: CapabilityFixtureTask[];
wake: CapabilityWakeContext;
capabilities: string[];
budget: { limitCents: number; spentCents: number; remainingCents: number };
interactionResults: Array<Pick<CapabilityFixtureInteraction, "id" | "kind" | "status" | "result">>;
}
export interface CapabilityOpenFixtureRunInput extends OpenControlPlaneRunInput {
wake?: CapabilityWakeContext;
capabilities?: string[];
resume?: boolean;
}
interface CapabilityBaseCommand {
taskId?: string;
}
export type CapabilitySemanticCommand =
| (CapabilityBaseCommand & { kind: "report_progress"; body: string })
| (CapabilityBaseCommand & {
kind: "write_document";
key: string;
title: string;
body: string;
baseRevisionId: string | null;
changeSummary?: string | null;
})
| (CapabilityBaseCommand & {
kind: "request_human_input";
interactionKind: CapabilityInteractionKind;
title: string;
prompt: string;
payload?: CapabilityJsonValue;
targetRevisionId?: string | null;
continuationPolicy: CapabilityFixtureInteraction["continuationPolicy"];
})
| (CapabilityBaseCommand & {
kind: "resolve_human_input";
interactionId: string;
outcome: "answered" | "accepted" | "rejected" | "expired";
result: CapabilityJsonValue;
})
| (CapabilityBaseCommand & {
kind: "register_deliverable";
filename: string;
contentType: string;
byteSize: number;
sha256: string;
contentRef: string;
title: string;
})
| (CapabilityBaseCommand & { kind: "set_dependencies"; blockedByTaskIds: string[] })
| (CapabilityBaseCommand & {
kind: "create_task";
title: string;
description?: string | null;
assigneeActorId?: string | null;
priority?: CapabilityFixtureTask["priority"];
blockedByTaskIds?: string[];
})
| (CapabilityBaseCommand & {
kind: "request_approval";
approvalType: string;
payload: CapabilityJsonValue;
})
| (CapabilityBaseCommand & {
kind: "decide_approval";
approvalId: string;
decision: "approved" | "rejected" | "cancelled";
note: string;
})
| (CapabilityBaseCommand & {
kind: "comment_on_approval";
approvalId: string;
body: string;
})
| (CapabilityBaseCommand & {
kind: "control_workspace_service";
serviceId: string;
action: "start" | "stop" | "fail";
url?: string | null;
})
| (CapabilityBaseCommand & { kind: "record_budget_usage"; cents: number })
| (CapabilityBaseCommand & { kind: "finish_task"; summary: string })
| (CapabilityBaseCommand & {
kind: "block_task";
reason: string;
blockedByTaskIds?: string[];
})
| (CapabilityBaseCommand & { kind: "request_review"; summary: string })
| (CapabilityBaseCommand & { kind: "release_task"; reason: string })
| (CapabilityBaseCommand & {
kind: "schedule_wake";
reason: CapabilityWakeReason;
payload?: CapabilityJsonValue;
delayTicks: number;
});
export interface CapabilityCommandEnvelope {
runId: string;
idempotencyKey: string;
expectedRevision?: number;
command: CapabilitySemanticCommand;
}
export interface CapabilityCommandResult {
commandId: string;
commandKind: CapabilitySemanticCommand["kind"];
disposition: "applied" | "duplicate";
stateRevision: number;
entityRefs: string[];
scheduledWakeIds: string[];
}
export interface CapabilityCommandErrorResult {
code: string;
message: string;
retryable: boolean;
}
export type CapabilityCommandOutcome =
| { ok: true; result: CapabilityCommandResult }
| {
ok: false;
commandKind: CapabilitySemanticCommand["kind"];
stateRevision: number;
error: CapabilityCommandErrorResult;
};
/**
* Claims enforced by the mock command boundary itself. The semantic catalog
* may hide ungranted operations earlier, but direct adapter consumers cannot
* bypass these checks.
*/
export const CAPABILITY_COMMAND_REQUIRED_CLAIMS = {
report_progress: [],
write_document: [],
request_human_input: [],
resolve_human_input: ["control_plane:interactions"],
register_deliverable: [],
set_dependencies: ["dependencies:write"],
create_task: ["delegation:tasks:create"],
request_approval: ["governance:approvals:request"],
decide_approval: ["governance:approvals:decide"],
comment_on_approval: ["governance:approvals:comment"],
control_workspace_service: ["workspace:control"],
record_budget_usage: ["control_plane:budget"],
finish_task: [],
block_task: [],
request_review: [],
release_task: ["control_plane:release"],
schedule_wake: ["control_plane:wakes"],
} as const satisfies Record<CapabilitySemanticCommand["kind"], readonly string[]>;
export interface CapabilityMockControlPlanePort extends ControlPlanePort {
start(): Promise<void>;
stop(): Promise<void>;
openFixtureRun(input: CapabilityOpenFixtureRunInput): Promise<CapabilityRunContext>;
context(runId: string): CapabilityRunContext;
applyCommand(envelope: CapabilityCommandEnvelope): Promise<CapabilityCommandResult>;
tryApplyCommand(envelope: CapabilityCommandEnvelope): Promise<CapabilityCommandOutcome>;
snapshot(): Readonly<CapabilityFixtureState>;
decisionRecords(): readonly CapabilityDecisionRecord[];
serialize(): string;
}
export interface CapabilityFixtureSeed {
epochMs?: number;
company?: Partial<CapabilityFixtureCompany>;
actors?: CapabilityFixtureActor[];
tasks?: CapabilityFixtureTask[];
outOfScopeTaskIds?: string[];
comments?: CapabilityFixtureComment[];
documents?: CapabilityFixtureDocument[];
interactions?: CapabilityFixtureInteraction[];
approvals?: CapabilityFixtureApproval[];
artifacts?: CapabilityFixtureArtifact[];
workProducts?: CapabilityFixtureWorkProduct[];
blockers?: CapabilityFixtureBlocker[];
workspaceServices?: CapabilityFixtureWorkspaceService[];
budgets?: CapabilityFixtureBudget[];
faults?: CapabilityFaultRule[];
}
export function createCapabilityFixtureState(seed: CapabilityFixtureSeed = {}): CapabilityFixtureState {
const company: CapabilityFixtureCompany = {
id: "company-1",
name: "Mock Paperclip Company",
issuePrefix: "MCK",
status: "active",
budgetId: "budget-company-1",
...seed.company,
};
const actors = seed.actors ?? [
{
id: "actor-1",
companyId: company.id,
name: "Mock Engineer",
role: "engineer",
status: "active",
budgetId: "budget-actor-1",
capabilityGrants: [],
},
];
const tasks = seed.tasks ?? [
{
id: "task-1",
companyId: company.id,
identifier: `${company.issuePrefix}-1`,
title: "Deterministic fixture task",
description: null,
status: "todo",
priority: "medium",
workMode: "standard",
parentId: null,
assigneeActorId: actors[0]?.id ?? null,
checkoutRunId: null,
executionRunId: null,
startedAt: null,
completedAt: null,
},
];
const budgets = seed.budgets ?? [
{
id: company.budgetId,
scope: "company",
scopeId: company.id,
limitCents: 10_000,
spentCents: 0,
hardStop: true,
},
...actors.map((actor) => ({
id: actor.budgetId,
scope: "actor" as const,
scopeId: actor.id,
limitCents: 1_000,
spentCents: 0,
hardStop: true,
})),
];
return {
schema: "paperclip.capability.mock-state.v1",
revision: 0,
clock: { epochMs: seed.epochMs ?? Date.UTC(2026, 7, 9), tick: 0 },
counters: {},
lifecycle: "stopped",
activeRunId: null,
company,
actors: structuredClone(actors),
tasks: structuredClone(tasks),
outOfScopeTaskIds: structuredClone(seed.outOfScopeTaskIds ?? []),
comments: structuredClone(seed.comments ?? []),
documents: structuredClone(seed.documents ?? []),
interactions: structuredClone(seed.interactions ?? []),
approvals: structuredClone(seed.approvals ?? []),
artifacts: structuredClone(seed.artifacts ?? []),
workProducts: structuredClone(seed.workProducts ?? []),
blockers: structuredClone(seed.blockers ?? []),
workspaceServices: structuredClone(seed.workspaceServices ?? []),
budgets: structuredClone(budgets),
runs: [],
wakes: [],
audit: [],
decisions: [],
idempotency: [],
faults: structuredClone(seed.faults ?? []),
};
}

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,8 @@ export * from "./conformance/harness-driver.js";
export * from "./conformance/semantic-conformance.js";
export * from "./mock-core/deterministic-harness-driver.js";
export * from "./mock-core/mock-control-plane-adapter.js";
export * from "./mock-core/capability-control-plane-types.js";
export * from "./mock-core/capability-mock-control-plane-adapter.js";
export * from "./protocol/conformance-fixture.js";
export * from "./protocol/replay-loader.js";
export * from "./tracer/conformance-runner.js";

View File

@ -0,0 +1,210 @@
import type {
CapabilityCommandResult,
CapabilityInteractionKind,
CapabilityJsonValue,
CapabilitySemanticCommand,
} from "../mock-core/capability-control-plane-types.js";
export type { CapabilityJsonValue } from "../mock-core/capability-control-plane-types.js";
export type CapabilityToolDisposition = "always_agent_tool" | "optional_agent_tool";
export type CapabilityOptionalCatalogGroup =
| "discovery"
| "delegation_dependencies"
| "governance"
| "cases"
| "workspace_runtime"
| "routines"
| "company_skills"
| "secrets"
| "portability_admin"
| "test_escape_hatch";
export type CapabilitySideEffectClass =
| "read"
| "task_write"
| "company_write"
| "governance"
| "workspace_control"
| "secret_read"
| "admin"
| "test_escape_hatch";
export type ScenarioChatdempotencyBehavior = "none" | "required" | "recommended";
export type CapabilityToolTaskMode = "standard" | "ask" | "planning" | "skill_test";
export interface CapabilityJsonSchema {
$schema?: string;
type?: "object" | "array" | "string" | "number" | "integer" | "boolean" | "null";
title?: string;
description?: string;
properties?: Record<string, CapabilityJsonSchema>;
required?: string[];
additionalProperties?: boolean | CapabilityJsonSchema;
items?: CapabilityJsonSchema;
enum?: CapabilityJsonValue[];
minLength?: number;
minimum?: number;
oneOf?: CapabilityJsonSchema[];
}
export interface CapabilityRedactionRule {
path: string;
replacement: "[REDACTED]" | "[SECRET_VALUE]";
appliesTo: ReadonlyArray<"input" | "output" | "error" | "authorization_record">;
}
export type CapabilityMockCommandMapping =
| { kind: "context_read"; projection: string }
| { kind: "snapshot_read"; projection: string }
| { kind: "semantic_command"; commandKind: CapabilitySemanticCommand["kind"] }
| { kind: "operation_result" }
| { kind: "mock_extension"; extension: string };
export interface CapabilitySemanticToolDescriptor {
operationId: string;
version: 1;
title: string;
description: string;
inputSchema: CapabilityJsonSchema;
outputSchema: CapabilityJsonSchema;
disposition: CapabilityToolDisposition;
optionalGroup: CapabilityOptionalCatalogGroup | null;
requiredClaims: string[];
allowedRoles?: string[];
taskModes: CapabilityToolTaskMode[];
sideEffectClass: CapabilitySideEffectClass;
idempotency: ScenarioChatdempotencyBehavior;
redaction: CapabilityRedactionRule[];
mockCommandMapping: CapabilityMockCommandMapping;
}
export interface CapabilityScenarioToolPolicy {
deniedOperationIds?: string[];
deniedClaims?: string[];
allowedInteractionKinds?: CapabilityInteractionKind[];
allowSecretValueAccess?: boolean;
allowGenericEscapeHatch?: boolean;
genericEscapeHatchAllowlist?: Array<{ method: string; path: string }>;
}
export interface CapabilityToolAuthorizationContext {
runId: string;
actor: {
id: string;
role: string;
capabilityGrants: string[];
};
task: {
id: string;
assigneeActorId: string | null;
workMode: CapabilityToolTaskMode;
};
scenarioGrants: string[];
policy?: CapabilityScenarioToolPolicy;
}
export type CapabilityAuthorizationOutcome = "exposed" | "allowed" | "denied" | "absent";
export interface CapabilityAuthorizationRecord {
schema: "paperclip.capability.authorization-record.v1";
sequence: number;
runId: string;
actorId: string;
taskId: string;
operationId: string;
phase: "exposure" | "invocation";
outcome: CapabilityAuthorizationOutcome;
consideredClaims: string[];
grantedClaims: string[];
missingClaims: string[];
reason: string;
redactions: Array<{ path: string; replacement: string }>;
resultingStateChange: {
beforeRevision: number | null;
afterRevision: number | null;
entityRefs: string[];
} | null;
}
export interface CapabilityVisibleToolSet {
schema: "paperclip.capability.visible-tools.v1";
tools: CapabilitySemanticToolDescriptor[];
authorizationRecords: CapabilityAuthorizationRecord[];
}
export interface CapabilityPolicyDenial {
schema: "paperclip.capability.tool-result.v1";
ok: false;
error: {
code: "policy_denied" | "operation_absent" | "input_invalid" | "operation_unsupported";
message: string;
operationId: string;
reason: string;
};
authorization: CapabilityAuthorizationRecord;
}
export interface CapabilityToolSuccess {
schema: "paperclip.capability.tool-result.v1";
ok: true;
operationId: string;
operationResultId: string;
value: CapabilityJsonValue;
commandResult: CapabilityCommandResult | null;
authorization: CapabilityAuthorizationRecord;
}
export type CapabilityToolInvocationResult = CapabilityToolSuccess | CapabilityPolicyDenial;
/**
* A result intended only for the provider/model tool-response channel.
*
* The distinct schema prevents this value from being assigned to an observable
* tool-result sink. Callers receive it only by explicitly opening a
* {@link CapabilityModelToolDelivery}; the delivery capsule itself serializes to
* its redacted observable result.
*/
export interface CapabilityModelToolSuccess {
schema: "paperclip.capability.model-tool-result.v1";
ok: true;
operationId: string;
operationResultId: string;
value: CapabilityJsonValue;
commandResult: CapabilityCommandResult | null;
authorization: CapabilityAuthorizationRecord;
}
export type CapabilityModelToolInvocationResult = CapabilityModelToolSuccess | CapabilityPolicyDenial;
export interface CapabilityModelToolDelivery {
/** Safe for traces, snapshots, persistence, errors, and browser payloads. */
readonly observableResult: CapabilityToolInvocationResult;
/** Explicitly opens the narrowly scoped provider/model delivery value. */
readModelResult(): CapabilityModelToolInvocationResult;
/** Accidental JSON serialization is always the observable redacted form. */
toJSON(): CapabilityToolInvocationResult;
}
export interface CapabilityToolInvocation {
operationId: string;
input: CapabilityJsonValue;
idempotencyKey?: string;
}
export const CAPABILITY_CONTROL_PLANE_OWNED_OPERATION_IDS = [
"checkout_task",
"release_task",
"select_work",
"route_wake",
"enforce_budget",
"append_audit_record",
"persist_run",
"replay_run",
"schedule_blocker_wake",
"reconcile_run",
] as const;