@claxedo/agent-runtime-contract 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -9
- package/dist/agent-runtime-event.d.ts +975 -0
- package/dist/availability.d.ts +3 -4
- package/dist/background-work.d.ts +10 -0
- package/dist/claims.d.ts +2 -0
- package/dist/connections.d.ts +66 -1
- package/dist/content.d.ts +72 -15
- package/dist/credential-broker-errors.d.ts +4 -0
- package/dist/custom-provider-headers.d.ts +12 -0
- package/dist/d1-bookmarks.d.ts +39 -0
- package/dist/daemon-ownership-snapshot.d.ts +40 -0
- package/dist/diagnostics.d.ts +24 -0
- package/dist/elicitation-pattern-worker.d.ts +6 -0
- package/dist/elicitation-pattern-worker.mjs +17 -0
- package/dist/elicitation-validation.d.ts +23 -0
- package/dist/elicitation.d.ts +51 -0
- package/dist/errors.d.ts +8 -1
- package/dist/event-cursor.d.ts +19 -0
- package/dist/events.d.ts +146 -216
- package/dist/files.d.ts +2 -0
- package/dist/first-turn-error.d.ts +12 -0
- package/dist/goals.d.ts +28 -0
- package/dist/harness-permission-modes.d.ts +76 -0
- package/dist/harness-table.d.ts +66 -4
- package/dist/harnesses.d.ts +19 -1
- package/dist/ids.d.ts +4 -0
- package/dist/index.d.ts +46 -1
- package/dist/index.mjs +2814 -95
- package/dist/mcp-app.d.ts +24 -0
- package/dist/message-page.d.ts +101 -0
- package/dist/permissions.d.ts +47 -0
- package/dist/pi-providers.d.ts +36 -0
- package/dist/prompt-title.d.ts +2 -0
- package/dist/provider-projection.d.ts +166 -0
- package/dist/raw-harness-event.d.ts +7 -0
- package/dist/recovery/actions.d.ts +20 -0
- package/dist/recovery/budgets.d.ts +16 -0
- package/dist/recovery/facts.d.ts +37 -0
- package/dist/recovery/operations.d.ts +66 -0
- package/dist/recovery/outcomes.d.ts +48 -0
- package/dist/recovery/payload-line.d.ts +19 -0
- package/dist/recovery/postconditions.d.ts +18 -0
- package/dist/recovery/requests.d.ts +44 -0
- package/dist/recovery/targets.d.ts +55 -0
- package/dist/recovery/validation.d.ts +9 -0
- package/dist/runtime-content.d.ts +34 -0
- package/dist/session-handoff.d.ts +4 -0
- package/dist/session-titles.d.ts +14 -0
- package/dist/sessions.d.ts +183 -5
- package/dist/stream-heartbeat.d.ts +11 -0
- package/dist/subagents.d.ts +92 -0
- package/dist/tool-header.d.ts +13 -0
- package/dist/tool-names.d.ts +5 -0
- package/dist/transcript-notice.d.ts +28 -0
- package/dist/turn-account.d.ts +26 -0
- package/dist/turn-error-classes.d.ts +10 -0
- package/dist/turn-fold.d.ts +111 -0
- package/dist/turn-fold.mjs +414 -0
- package/dist/turn-message-ids.d.ts +16 -0
- package/dist/turn-outline.d.ts +37 -0
- package/dist/turn-page.d.ts +58 -0
- package/dist/usage-streams.d.ts +19 -0
- package/dist/vendor-body.d.ts +31 -0
- package/dist/vendor-providers.d.ts +11 -0
- package/package.json +17 -3
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare const MCP_APP_MIME = "text/html;profile=mcp-app";
|
|
2
|
+
export declare const MCP_APP_MAX_BYTES: number;
|
|
3
|
+
export declare const MCP_APP_DOMAIN_LIMIT = 32;
|
|
4
|
+
export declare const MCP_APP_DOMAIN_MAX_LENGTH = 256;
|
|
5
|
+
export declare const MCP_APP_CSP_KEYS: readonly ["connectDomains", "resourceDomains", "frameDomains", "baseUriDomains"];
|
|
6
|
+
export type McpAppCsp = Partial<Record<(typeof MCP_APP_CSP_KEYS)[number], readonly string[]>>;
|
|
7
|
+
export type McpAppReference = {
|
|
8
|
+
readonly resourceUri: string;
|
|
9
|
+
readonly csp?: McpAppCsp;
|
|
10
|
+
readonly prefersBorder?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export type ToolMcpFacts = {
|
|
13
|
+
readonly server: string;
|
|
14
|
+
readonly tool: string;
|
|
15
|
+
readonly resultMeta?: Record<string, unknown>;
|
|
16
|
+
readonly structuredContent?: unknown;
|
|
17
|
+
readonly app?: McpAppReference;
|
|
18
|
+
};
|
|
19
|
+
export declare function mcpAppCsp(value: unknown): McpAppCsp | undefined;
|
|
20
|
+
export type McpToolAudience = "model" | "app";
|
|
21
|
+
export declare function mcpToolVisibleTo(toolMeta: unknown, audience: McpToolAudience): boolean;
|
|
22
|
+
export declare function mcpAppResourceUri(toolMeta: unknown): string | undefined;
|
|
23
|
+
export declare function toolMcpFacts(metadata: Record<string, unknown> | undefined): ToolMcpFacts | undefined;
|
|
24
|
+
export declare function toolMcpHeader(metadata: Record<string, unknown> | undefined): Record<string, unknown>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { AgentMessage } from "./content";
|
|
2
|
+
import type { AgentTurnOutcome } from "./sessions";
|
|
3
|
+
/** The most messages a numeric page may ask for; every producer rejects a larger `limit`. */
|
|
4
|
+
export declare const AGENT_MESSAGE_PAGE_LIMIT = 500;
|
|
5
|
+
/**
|
|
6
|
+
* An authoritative transcript-window request. Cursors are opaque to every
|
|
7
|
+
* consumer.
|
|
8
|
+
*
|
|
9
|
+
* Semantic views are intentionally distinct from numeric pages. `latest-turn`
|
|
10
|
+
* returns the complete latest turn, or with `before` the complete turn that
|
|
11
|
+
* ends at that cursor; its cursor points at the turn's user message, so a
|
|
12
|
+
* reader walks back one whole turn per read. `latest-surface` returns at most
|
|
13
|
+
* its owning user and final message; its cursor points at the final message so
|
|
14
|
+
* ordinary paging restores every omitted intermediate without a second cursor
|
|
15
|
+
* protocol. The surface is a first-paint projection: the two envelopes whole,
|
|
16
|
+
* and their text parts only, whole. It is what a folded turn draws — the
|
|
17
|
+
* prompt, the fold row and the answer — so the answer is never the thing a
|
|
18
|
+
* budget trims.
|
|
19
|
+
*/
|
|
20
|
+
export type AgentMessagePageInput = {
|
|
21
|
+
view: "latest-turn";
|
|
22
|
+
limit?: never;
|
|
23
|
+
before?: string;
|
|
24
|
+
} | {
|
|
25
|
+
view: "latest-surface";
|
|
26
|
+
limit?: never;
|
|
27
|
+
before?: never;
|
|
28
|
+
} | {
|
|
29
|
+
view?: never;
|
|
30
|
+
limit: number;
|
|
31
|
+
before?: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* A coverage request for one named turn, answered by whoever owns its journal.
|
|
35
|
+
*
|
|
36
|
+
* Either of the turn's two message ids names it: a caller that admitted the
|
|
37
|
+
* turn holds the user message id, while the journal keys the turn on the
|
|
38
|
+
* assistant message id, and neither side can derive the other.
|
|
39
|
+
*/
|
|
40
|
+
type AgentTurnCoverageInput = {
|
|
41
|
+
turnId: string;
|
|
42
|
+
};
|
|
43
|
+
/** What a `GET /session/:id/message` read asks for; the shapes are exclusive. */
|
|
44
|
+
export type AgentMessageReadInput = AgentMessagePageInput | AgentTurnCoverageInput;
|
|
45
|
+
/**
|
|
46
|
+
* How much of one turn a producer can account for.
|
|
47
|
+
*
|
|
48
|
+
* `complete` is a claim that nothing more will be added to this turn and that
|
|
49
|
+
* every message of it is in `messages`; only a producer holding the turn's
|
|
50
|
+
* journal can make it. `partial` names a turn whose extent is known and whose
|
|
51
|
+
* transcript is not all here yet. `unavailable` is the answer of a producer
|
|
52
|
+
* that cannot establish either, and it always carries a reason.
|
|
53
|
+
*/
|
|
54
|
+
export type AgentTurnCoverage = "complete" | "partial" | "unavailable";
|
|
55
|
+
/** One turn, and how much of it the answering producer can account for. */
|
|
56
|
+
export type AgentTurnCoveragePage = {
|
|
57
|
+
/** The turn that was asked for, never the turn the producer found instead. */
|
|
58
|
+
turnId: string;
|
|
59
|
+
coverage: AgentTurnCoverage;
|
|
60
|
+
/** Why the coverage is not `complete`, from the producer that decided it. */
|
|
61
|
+
reason?: string;
|
|
62
|
+
/** Present only when the journal records the turn finished. */
|
|
63
|
+
terminal?: AgentTurnOutcome;
|
|
64
|
+
/** The last journal sequence the answering projection applied, not the journal's head. */
|
|
65
|
+
committedSequence: number;
|
|
66
|
+
messages: AgentMessage[];
|
|
67
|
+
};
|
|
68
|
+
/** One chronological transcript page and the cursor for the next older page. */
|
|
69
|
+
export type AgentMessagePage = {
|
|
70
|
+
/** Committed event-log position represented by a runtime-owned projection. */
|
|
71
|
+
maxEventOrdinal?: number;
|
|
72
|
+
messages: AgentMessage[];
|
|
73
|
+
nextCursor?: string;
|
|
74
|
+
};
|
|
75
|
+
/** The message shape the latest-surface projection reads. */
|
|
76
|
+
export type LatestSurfaceMessage = {
|
|
77
|
+
info: Record<string, unknown>;
|
|
78
|
+
parts: unknown[];
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Apply the producer-independent `latest-surface` projection: every text part
|
|
82
|
+
* of the message, in canonical order, and nothing else.
|
|
83
|
+
*/
|
|
84
|
+
export declare function projectLatestSurfaceMessage<TMessage extends LatestSurfaceMessage>(message: TMessage): TMessage;
|
|
85
|
+
export declare function projectLatestSurfaceMessages<TMessage extends LatestSurfaceMessage>(messages: readonly TMessage[]): TMessage[];
|
|
86
|
+
/** An authoritative message-page producer rejected the request. */
|
|
87
|
+
export declare class AgentMessagePageError extends Error {
|
|
88
|
+
readonly status: number;
|
|
89
|
+
constructor(status: number, message: string);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The `before` cursor a message-page producer hands out: its own versioned
|
|
93
|
+
* prefix, then the session and the ordinal the next page ends before. A
|
|
94
|
+
* producer that changes the payload changes its prefix, so a cursor minted
|
|
95
|
+
* before is refused by version rather than misread.
|
|
96
|
+
*/
|
|
97
|
+
export declare function encodeMessagePageCursor(prefix: string, sessionId: string, ordinal: number): string;
|
|
98
|
+
export declare function decodeMessagePageCursor(prefix: string, sessionId: string, input: string): number;
|
|
99
|
+
/** The `limit`/`before`/`view` query of a message page read; every producer refuses a malformed one with a 400. */
|
|
100
|
+
export declare function parseMessagePageQuery(limit: string | undefined, before: string | undefined, view?: string): AgentMessagePageInput | undefined;
|
|
101
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type PermissionDecision = "allow_once" | "allow_always" | "deny" | "reject_always";
|
|
2
|
+
/**
|
|
3
|
+
* The three rungs every harness's permission surface is mapped onto.
|
|
4
|
+
*
|
|
5
|
+
* A LADDER, not a taxonomy: the rungs are ordered by how much runs without
|
|
6
|
+
* asking, and that ordering is the only thing shared across harnesses. What each
|
|
7
|
+
* rung concretely does is the harness's business and differs wildly — `auto` is
|
|
8
|
+
* an OS sandbox on codex and a model classifier on claude and cursor.
|
|
9
|
+
*
|
|
10
|
+
* `level` is therefore a HINT for choosing a default, never a promise about
|
|
11
|
+
* behaviour. Anything user-facing must show `AgentPermissionMode.name` — the
|
|
12
|
+
* harness's own word for it — because that is the only label guaranteed to
|
|
13
|
+
* describe what actually happens.
|
|
14
|
+
*/
|
|
15
|
+
export type AutoLevel = "ask" | "auto" | "full";
|
|
16
|
+
/** One selectable permission mode, in the harness's own vocabulary. */
|
|
17
|
+
export type AgentPermissionMode = {
|
|
18
|
+
id: string;
|
|
19
|
+
/** The harness's own name. Rendered as-is; never paraphrased. */
|
|
20
|
+
name: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Which rung this is, when it maps to one at all. Absent means the harness
|
|
24
|
+
* offers it but it does not correspond to a rung — still selectable, just not
|
|
25
|
+
* a candidate for the default.
|
|
26
|
+
*/
|
|
27
|
+
level?: AutoLevel;
|
|
28
|
+
};
|
|
29
|
+
export type AgentPermissionModeState = {
|
|
30
|
+
modes: AgentPermissionMode[];
|
|
31
|
+
/**
|
|
32
|
+
* Read back from the harness, never assumed from the last write. An ACP agent
|
|
33
|
+
* may clamp this to a different mode when its available set changes.
|
|
34
|
+
*/
|
|
35
|
+
currentModeId?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Set when this harness has no mode surface. Distinct from `modes: []`, which
|
|
38
|
+
* means it has one and reported nothing — greying out for the right reason
|
|
39
|
+
* requires telling those apart.
|
|
40
|
+
*/
|
|
41
|
+
unsupported?: string;
|
|
42
|
+
/**
|
|
43
|
+
* When the harness applies a change: acknowledged live control, next turn,
|
|
44
|
+
* or next agent session.
|
|
45
|
+
*/
|
|
46
|
+
appliesFrom: "immediate" | "next-turn" | "next-session";
|
|
47
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { PromptModel } from "./sessions";
|
|
2
|
+
/**
|
|
3
|
+
* The providers Pi can be launched on, and the stored rows each one can be
|
|
4
|
+
* launched from.
|
|
5
|
+
*
|
|
6
|
+
* Here rather than beside either consumer, because two of them ask the same
|
|
7
|
+
* question and a disagreement between them is invisible: the credential
|
|
8
|
+
* catalog decides whether Settings calls a provider connected, and the harness
|
|
9
|
+
* decides whether it writes that provider an overlay. Held apart, a provider
|
|
10
|
+
* reads as connected and then has nothing to bind at the turn.
|
|
11
|
+
*
|
|
12
|
+
* `openai-codex` is Pi's own name for the ChatGPT-plan endpoint. It is a
|
|
13
|
+
* different destination from its `openai` provider rather than the same one
|
|
14
|
+
* signed in differently, which is why the plan and the key never appear in
|
|
15
|
+
* each other's lists.
|
|
16
|
+
*/
|
|
17
|
+
export declare const PI_LAUNCH_PROVIDERS: readonly ["openai-codex", "anthropic", "openai", "openrouter", "google", "groq", "xai"];
|
|
18
|
+
export type PiLaunchProvider = (typeof PI_LAUNCH_PROVIDERS)[number];
|
|
19
|
+
/**
|
|
20
|
+
* The model a Pi draft starts on for each provider: the vendor's newest
|
|
21
|
+
* general model. Pi's catalog carries no release dates and lists ids
|
|
22
|
+
* alphabetically, so its first row is the oldest-looking name, not the newest.
|
|
23
|
+
*/
|
|
24
|
+
export declare const PI_DEFAULT_MODELS: Readonly<Partial<Record<PiLaunchProvider, string>>>;
|
|
25
|
+
export declare const PI_PROVIDER_LABELS: Readonly<Record<PiLaunchProvider, string>>;
|
|
26
|
+
/**
|
|
27
|
+
* The vendor and the vendor's own model id a model selection runs. Pi keys a
|
|
28
|
+
* model as `<vendor>/<model>` under its own provider `pi`; every other
|
|
29
|
+
* harness's selection already names its vendor.
|
|
30
|
+
*/
|
|
31
|
+
export declare function vendorModel(model: PromptModel): PromptModel;
|
|
32
|
+
export declare function isPiLaunchProvider(providerID: string): providerID is PiLaunchProvider;
|
|
33
|
+
/** The stored provider ids a Pi provider can be launched on, best first. */
|
|
34
|
+
export declare function piCredentialProviderIDs(providerID: string): readonly string[];
|
|
35
|
+
/** The Pi provider that spends a stored row of this provider id, where one does. */
|
|
36
|
+
export declare function piProviderSpending(credentialProviderId: string): PiLaunchProvider | undefined;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import type { ConnectionSecretAuthority } from "./connections";
|
|
2
|
+
/**
|
|
3
|
+
* What a harness receives in place of a credential.
|
|
4
|
+
*
|
|
5
|
+
* A bound row carries one binding's broker path, a signed capability for that
|
|
6
|
+
* binding alone, and the mode naming the header the harness must carry it in.
|
|
7
|
+
* The credential's own bytes stay with the authority that minted the binding
|
|
8
|
+
* and never reach this process.
|
|
9
|
+
*
|
|
10
|
+
* An unavailable row says the operator selected an account for this provider
|
|
11
|
+
* that cannot be bound. Without it a withdrawn account reaches a harness as an
|
|
12
|
+
* absent projection, and the turn runs on whatever login the machine holds —
|
|
13
|
+
* under an identity the operator did not choose.
|
|
14
|
+
*/
|
|
15
|
+
export type ProviderBinding = {
|
|
16
|
+
baseUrl: string;
|
|
17
|
+
placeholder: string;
|
|
18
|
+
authMode: "api-key" | "bearer";
|
|
19
|
+
/**
|
|
20
|
+
* Absent when the placeholder has no lifetime of its own: a sandbox provider
|
|
21
|
+
* that substitutes on egress holds the value until the authority withdraws
|
|
22
|
+
* it, so there is no moment at which the harness must stop using it.
|
|
23
|
+
*/
|
|
24
|
+
expiresAt?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Where the vendor's API root sits under `baseUrl`. A client that appends the
|
|
27
|
+
* whole vendor path itself (Claude Code, the Cursor SDK) is configured with
|
|
28
|
+
* `baseUrl`; one configured with an API root (Codex, Pi, the OpenCode engine)
|
|
29
|
+
* appends this. Absent from an authority that does not model vendor paths,
|
|
30
|
+
* which means `baseUrl` is already the root.
|
|
31
|
+
*/
|
|
32
|
+
apiPath?: string;
|
|
33
|
+
/** The stored credential the placeholder spends, by its non-secret metadata, so a turn can say which account it ran on. */
|
|
34
|
+
account?: BindingAccount;
|
|
35
|
+
};
|
|
36
|
+
/** `revision` names the stored value handed over, so a refusal of it is told apart from a refusal of an older one. */
|
|
37
|
+
export type BindingAccount = {
|
|
38
|
+
credentialId: string;
|
|
39
|
+
providerId: string;
|
|
40
|
+
label?: string;
|
|
41
|
+
revision?: number;
|
|
42
|
+
};
|
|
43
|
+
export type ProviderUnavailable = {
|
|
44
|
+
unavailable: true;
|
|
45
|
+
reason: string;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The refusal an account carries in a sandbox whose provider cannot keep the
|
|
49
|
+
* secret out of it: no harness running inside that sandbox may be handed it.
|
|
50
|
+
* A harness that calls the vendor from outside the sandbox still can.
|
|
51
|
+
*/
|
|
52
|
+
export declare const HARNESS_NEEDS_BROKERING = "harness_needs_brokering";
|
|
53
|
+
/** The refusal a provider carries for a person who chose the organization's account for it while the organization has none. */
|
|
54
|
+
export declare const ORG_ACCOUNT_UNAVAILABLE = "org_account_unavailable";
|
|
55
|
+
/**
|
|
56
|
+
* Whether a row is the refusal rather than a binding.
|
|
57
|
+
*
|
|
58
|
+
* Takes any object rather than a `ProviderProjection`, because the same
|
|
59
|
+
* refusal travels alongside binding shapes this module does not own — the
|
|
60
|
+
* engine's provider overlay carries `baseURL`/`apiKey` — and a second copy of
|
|
61
|
+
* the predicate beside each of them is how two of them came to disagree.
|
|
62
|
+
*/
|
|
63
|
+
export declare function isProviderUnavailable(row: object): row is ProviderUnavailable;
|
|
64
|
+
export type ProviderProjection = ProviderBinding | ProviderUnavailable;
|
|
65
|
+
/**
|
|
66
|
+
* What an authority puts on the wire, before the runtime resolves it.
|
|
67
|
+
*
|
|
68
|
+
* An authority that mints the placeholder itself sends it. One whose sandbox
|
|
69
|
+
* provider issues the placeholder — the provider substitutes the value of an
|
|
70
|
+
* env var it filled, and only the sandbox can read it — names that env var
|
|
71
|
+
* instead, and `providerProjection` reads it off the runtime's own environment.
|
|
72
|
+
* Exactly one of the two is a projection; both or neither is not.
|
|
73
|
+
*/
|
|
74
|
+
export type ProviderBindingSource = Omit<ProviderBinding, "placeholder"> & ({
|
|
75
|
+
placeholder: string;
|
|
76
|
+
placeholderEnv?: undefined;
|
|
77
|
+
} | {
|
|
78
|
+
placeholderEnv: string;
|
|
79
|
+
placeholder?: undefined;
|
|
80
|
+
});
|
|
81
|
+
export type ProviderProjectionSource = ProviderBindingSource | ProviderUnavailable;
|
|
82
|
+
/**
|
|
83
|
+
* A credential handed to a harness that calls the vendor itself, in process,
|
|
84
|
+
* with no broker between: the subscription providers that read the account
|
|
85
|
+
* from the token, and a Durable Object host that has no egress broker.
|
|
86
|
+
*/
|
|
87
|
+
export type ProviderDirect = {
|
|
88
|
+
delivery: "direct";
|
|
89
|
+
baseUrl: string;
|
|
90
|
+
apiPath?: string;
|
|
91
|
+
secret: string;
|
|
92
|
+
authKind: "api-key" | "subscription";
|
|
93
|
+
expiresAt?: number;
|
|
94
|
+
account?: BindingAccount;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* A fresh copy of a direct credential, asked of the authority that handed it
|
|
98
|
+
* over, under the proof of the turn that needs it; nothing when that authority
|
|
99
|
+
* has no newer one to give or no turn is running.
|
|
100
|
+
*/
|
|
101
|
+
export type DirectCredentialRefresh = (input: {
|
|
102
|
+
authority: ConnectionSecretAuthority;
|
|
103
|
+
credentialProviderId: string;
|
|
104
|
+
rejectedExpiresAt?: number;
|
|
105
|
+
}) => Promise<ProviderDirect | undefined>;
|
|
106
|
+
export declare function isProviderDirect(row: object): row is ProviderDirect;
|
|
107
|
+
export type CredentialSnapshot<T = ProviderProjectionSource> = {
|
|
108
|
+
machineOwnerUserId: string;
|
|
109
|
+
/**
|
|
110
|
+
* The account each person spends, by user id and provider: their own, or the
|
|
111
|
+
* org's account for a provider they chose it for. Nothing else is spent.
|
|
112
|
+
*/
|
|
113
|
+
accounts: Record<string, Record<string, T>>;
|
|
114
|
+
direct?: Record<string, Record<string, ProviderDirect>>;
|
|
115
|
+
};
|
|
116
|
+
/** The environment a `placeholderEnv` row is resolved against. */
|
|
117
|
+
export type PlaceholderEnvironment = Record<string, string | undefined>;
|
|
118
|
+
export declare function providerProjection(input: unknown, env?: PlaceholderEnvironment): ProviderProjection | undefined;
|
|
119
|
+
export declare function providerDirect(input: unknown): ProviderDirect | undefined;
|
|
120
|
+
/**
|
|
121
|
+
* What a row this validator cannot read does to the whole record.
|
|
122
|
+
*
|
|
123
|
+
* `reject` is for a map that crossed a process boundary: a producer that sent a
|
|
124
|
+
* row this runtime cannot read has said nothing trustworthy about the rest, so
|
|
125
|
+
* the snapshot is refused whole and the one already applied stays. Only a
|
|
126
|
+
* process projecting its own authority passes `unavailable`, where refusing
|
|
127
|
+
* everything would disable every working account over one malformed row; that
|
|
128
|
+
* row alone becomes an unavailable projection, which disables its provider and
|
|
129
|
+
* refuses a turn on it rather than letting the harness fall back to a login the
|
|
130
|
+
* operator did not choose.
|
|
131
|
+
*/
|
|
132
|
+
type ProviderProjectionRowPolicy = "reject" | "unavailable";
|
|
133
|
+
export declare function credentialSnapshot(input: unknown, env: PlaceholderEnvironment): CredentialSnapshot<ProviderProjection> | undefined;
|
|
134
|
+
export declare function providerProjectionRecord(input: unknown, env: PlaceholderEnvironment, options: {
|
|
135
|
+
onInvalid: ProviderProjectionRowPolicy;
|
|
136
|
+
}): Record<string, ProviderProjection> | undefined;
|
|
137
|
+
/**
|
|
138
|
+
* Whether a held projection is due for replacement. `all` is the caller saying
|
|
139
|
+
* the process lost track of time — a laptop resumed from sleep reads a clock
|
|
140
|
+
* later than any tick the timer saw — so nothing that expires may be trusted.
|
|
141
|
+
*/
|
|
142
|
+
export declare function projectionRenewalDue(input: {
|
|
143
|
+
at: number;
|
|
144
|
+
all?: boolean;
|
|
145
|
+
}, renewAt: number | undefined): boolean;
|
|
146
|
+
/**
|
|
147
|
+
* When the earliest placeholder or direct credential in this map has to be
|
|
148
|
+
* replaced: half of its own lifetime before it expires, so a turn that starts
|
|
149
|
+
* just before renewal still finishes on a valid one.
|
|
150
|
+
*
|
|
151
|
+
* Read from `expiresAt` rather than from a fixed interval, because the lifetime
|
|
152
|
+
* belongs to the authority that minted the placeholder and can be shorter than
|
|
153
|
+
* any interval chosen here. A map carrying no row that expires never needs
|
|
154
|
+
* renewing.
|
|
155
|
+
*/
|
|
156
|
+
export declare function projectionRenewalDueAt(auth: Record<string, ProviderProjectionSource | ProviderProjection | ProviderDirect>, appliedAt: number): number | undefined;
|
|
157
|
+
/**
|
|
158
|
+
* The earliest renewal any account row of a snapshot needs, across every
|
|
159
|
+
* person, and none of its direct rows: for an authority whose direct
|
|
160
|
+
* credentials are renewed by the harness mid-turn and whose placeholders are
|
|
161
|
+
* its own to re-deliver.
|
|
162
|
+
*/
|
|
163
|
+
export declare function accountsRenewalDueAt(auth: Pick<CredentialSnapshot, "accounts" | "direct">, appliedAt: number): number | undefined;
|
|
164
|
+
/** The earliest renewal any row of a snapshot needs, across every person's accounts and direct credentials. */
|
|
165
|
+
export declare function credentialSnapshotRenewalDueAt(auth: CredentialSnapshot<ProviderProjection>, appliedAt: number): number | undefined;
|
|
166
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type RecoveryTargetScope, type RecoveryTarget } from "./targets";
|
|
2
|
+
export declare const RECOVERY_ACTIONS: readonly ["inspect", "cancel_turn", "reconcile_session", "retire_harness", "drain_daemon", "stop_daemon", "release_drain"];
|
|
3
|
+
export type RecoveryAction = (typeof RECOVERY_ACTIONS)[number];
|
|
4
|
+
export declare const RECOVERY_MUTATING_ACTIONS: readonly ["cancel_turn", "reconcile_session", "retire_harness", "drain_daemon", "stop_daemon", "release_drain"];
|
|
5
|
+
export type RecoveryMutatingAction = (typeof RECOVERY_MUTATING_ACTIONS)[number];
|
|
6
|
+
/**
|
|
7
|
+
* `read_operation` reads an existing operation by id, so it is not itself a
|
|
8
|
+
* `RecoveryAction`: it has no target, no scope and no receipt of its own.
|
|
9
|
+
*/
|
|
10
|
+
export declare const RECOVERY_READ_ACTIONS: readonly ["inspect", "read_operation"];
|
|
11
|
+
export type RecoveryReadAction = (typeof RECOVERY_READ_ACTIONS)[number];
|
|
12
|
+
export declare function isMutatingRecoveryAction(action: RecoveryAction): action is RecoveryMutatingAction;
|
|
13
|
+
/**
|
|
14
|
+
* Which target an action may name. `reconcile_session` accepts a turn so a
|
|
15
|
+
* caller can narrow reconciliation to one turn of a session; every other
|
|
16
|
+
* action has exactly the owner its postcondition is written against.
|
|
17
|
+
*/
|
|
18
|
+
export declare const RECOVERY_ACTION_SCOPES: Readonly<Record<RecoveryAction, readonly RecoveryTargetScope[]>>;
|
|
19
|
+
/** Incoming requests and published operations must obey the same action-to-owner constraint. */
|
|
20
|
+
export declare function scopedTarget(action: RecoveryAction, input: unknown): RecoveryTarget;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type RecoveryBudgets = {
|
|
2
|
+
ackMs: number;
|
|
3
|
+
providerQueryMs: number;
|
|
4
|
+
gracefulCancelMs: number;
|
|
5
|
+
termGraceMs: number;
|
|
6
|
+
killVerifyMs: number;
|
|
7
|
+
reconcileMs: number;
|
|
8
|
+
drainMs: number;
|
|
9
|
+
};
|
|
10
|
+
export declare const DEFAULT_RECOVERY_BUDGETS: Readonly<RecoveryBudgets>;
|
|
11
|
+
/**
|
|
12
|
+
* The deadline a child phase may run to. The parent caps it, so a sequence of
|
|
13
|
+
* child phases cannot multiply the deadline the caller was promised. An already
|
|
14
|
+
* expired parent yields `now`, which every phase treats as no time left.
|
|
15
|
+
*/
|
|
16
|
+
export declare function capChildBudget(parentDeadlineAt: number, childMs: number, now: number): number;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The owner's identity on the wire and across a restart. The in-process object
|
|
3
|
+
* generation is a different value and is never sent: it cannot survive the
|
|
4
|
+
* process that minted it, so a stale caller could not be told apart from a
|
|
5
|
+
* replacement one.
|
|
6
|
+
*/
|
|
7
|
+
export type RecoveryGeneration = string;
|
|
8
|
+
export declare const EXECUTION_FACTS: readonly ["running", "terminal", "unknown"];
|
|
9
|
+
export declare const CLEANUP_FACTS: readonly ["owned", "verified_clear", "unknown"];
|
|
10
|
+
export declare const PERSISTENCE_FACTS: readonly ["committed", "pending", "unavailable"];
|
|
11
|
+
export type ExecutionFact = (typeof EXECUTION_FACTS)[number];
|
|
12
|
+
export type CleanupFact = (typeof CLEANUP_FACTS)[number];
|
|
13
|
+
export type PersistenceFact = (typeof PERSISTENCE_FACTS)[number];
|
|
14
|
+
/** `observedAt` is epoch milliseconds; the wire carries no Date. */
|
|
15
|
+
export type RecoveryFactEvidence<V extends string> = {
|
|
16
|
+
value: V;
|
|
17
|
+
source: string;
|
|
18
|
+
observedAt: number;
|
|
19
|
+
generation: RecoveryGeneration;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Execution, cleanup and persistence are three fields rather than one status
|
|
23
|
+
* because they are three independent events: a provider can confirm
|
|
24
|
+
* cancellation while a turn-owned child survives, and an exited process group
|
|
25
|
+
* can still fail to finalize in SQLite. A single boolean outcome can let a
|
|
26
|
+
* timed-out stop read as success and admit conflicting work.
|
|
27
|
+
*
|
|
28
|
+
* Session availability is deliberately absent. `AgentRuntimeStatus`
|
|
29
|
+
* and `ExecutionAvailability` remain the only session-status authority; a
|
|
30
|
+
* recovery operation's state describes the command, not the transcript.
|
|
31
|
+
*/
|
|
32
|
+
export type RecoveryFacts = {
|
|
33
|
+
execution: RecoveryFactEvidence<ExecutionFact>;
|
|
34
|
+
cleanup: RecoveryFactEvidence<CleanupFact>;
|
|
35
|
+
persistence: RecoveryFactEvidence<PersistenceFact>;
|
|
36
|
+
};
|
|
37
|
+
export declare function parseRecoveryFacts(input: unknown): RecoveryFacts;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type RecoveryAction } from "./actions";
|
|
2
|
+
import { type RecoveryFacts } from "./facts";
|
|
3
|
+
import { type RecoveryTarget } from "./targets";
|
|
4
|
+
export declare const RECOVERY_OPERATION_STATES: readonly ["accepted", "running", "succeeded", "failed", "needs_action"];
|
|
5
|
+
export type RecoveryOperationState = (typeof RECOVERY_OPERATION_STATES)[number];
|
|
6
|
+
export declare const RECOVERY_PHASES: readonly ["ack", "provider_query", "graceful_cancel", "term_grace", "kill_verify", "reconcile", "drain"];
|
|
7
|
+
export type RecoveryPhase = (typeof RECOVERY_PHASES)[number];
|
|
8
|
+
/** A volatile receipt was never durably recorded and never survives a restart. */
|
|
9
|
+
export type RecoveryReceipt = "durable" | "volatile";
|
|
10
|
+
export type RecoveryNextAction = {
|
|
11
|
+
action: RecoveryAction;
|
|
12
|
+
scopePreviewRequired: boolean;
|
|
13
|
+
reason: string;
|
|
14
|
+
};
|
|
15
|
+
export declare const RECOVERY_ERROR_CODES: readonly ["provider_unreachable", "cancellation_unsupported", "cancellation_timeout", "signal_denied", "exit_unverified", "ownership_unverified", "persistence_unavailable", "projection_failed", "generation_retired", "authority_lost", "deadline_exceeded", "owner_unavailable", "internal_error"];
|
|
16
|
+
export type RecoveryErrorCode = (typeof RECOVERY_ERROR_CODES)[number];
|
|
17
|
+
/**
|
|
18
|
+
* `message` is the whole public payload by construction: there is no free-form
|
|
19
|
+
* details field, so a credential or a raw tool argument has nowhere to ride
|
|
20
|
+
* out to a caller.
|
|
21
|
+
*/
|
|
22
|
+
export type RecoveryError = {
|
|
23
|
+
code: RecoveryErrorCode;
|
|
24
|
+
/** The owner that reported it, by its own name. */
|
|
25
|
+
origin: string;
|
|
26
|
+
target: RecoveryTarget;
|
|
27
|
+
stage: RecoveryPhase;
|
|
28
|
+
executionMayContinue: boolean;
|
|
29
|
+
message: string;
|
|
30
|
+
at: number;
|
|
31
|
+
};
|
|
32
|
+
export type RecoveryOperation = {
|
|
33
|
+
operationId: string;
|
|
34
|
+
requestId: string;
|
|
35
|
+
target: RecoveryTarget;
|
|
36
|
+
action: RecoveryAction;
|
|
37
|
+
scopeRevision: string;
|
|
38
|
+
attempt: number;
|
|
39
|
+
state: RecoveryOperationState;
|
|
40
|
+
phase: RecoveryPhase;
|
|
41
|
+
phaseDeadlineAt: number;
|
|
42
|
+
facts: RecoveryFacts;
|
|
43
|
+
initiatingError?: RecoveryError;
|
|
44
|
+
cleanupErrors: RecoveryError[];
|
|
45
|
+
nextActions: RecoveryNextAction[];
|
|
46
|
+
receipt: RecoveryReceipt;
|
|
47
|
+
linkedOperationId?: string;
|
|
48
|
+
createdAt: number;
|
|
49
|
+
updatedAt: number;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* How long a settled recovery operation stays listed and stored. Ten reconcile
|
|
53
|
+
* budgets: long enough that a caller which lost its connection can still read
|
|
54
|
+
* its own receipt, short enough that the list is current work. An operation
|
|
55
|
+
* whose facts still show cleanup owned or unknown, or persistence pending, is
|
|
56
|
+
* exempt at every layer: it is the only record of an undischarged obligation.
|
|
57
|
+
*/
|
|
58
|
+
export declare const RECOVERY_OPERATION_RETENTION_MS: number;
|
|
59
|
+
/**
|
|
60
|
+
* Applies the latest evidence to an attempt. A terminal attempt keeps its
|
|
61
|
+
* state: later evidence corrects the facts, but an attempt that timed out is
|
|
62
|
+
* not rewritten into a success.
|
|
63
|
+
*/
|
|
64
|
+
export declare function finalizeRecoveryOperation(operation: RecoveryOperation, facts: RecoveryFacts): RecoveryOperation;
|
|
65
|
+
export declare function parseRecoveryError(input: unknown): RecoveryError;
|
|
66
|
+
export declare function parseRecoveryOperation(input: unknown): RecoveryOperation;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type RecoveryOperation } from "./operations";
|
|
2
|
+
import { type RecoveryTarget } from "./targets";
|
|
3
|
+
/** The sessions and resources an escalation would interrupt, named individually. */
|
|
4
|
+
export type RecoveryScopePreview = {
|
|
5
|
+
sessions: string[];
|
|
6
|
+
resources: string[];
|
|
7
|
+
summary: string;
|
|
8
|
+
};
|
|
9
|
+
export type RecoveryRefusal = {
|
|
10
|
+
kind: "generation_conflict";
|
|
11
|
+
message: string;
|
|
12
|
+
current?: RecoveryTarget;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "intent_conflict";
|
|
15
|
+
message: string;
|
|
16
|
+
requestId: string;
|
|
17
|
+
} | {
|
|
18
|
+
kind: "receipt_expired";
|
|
19
|
+
message: string;
|
|
20
|
+
requestId: string;
|
|
21
|
+
} | {
|
|
22
|
+
kind: "scope_changed";
|
|
23
|
+
message: string;
|
|
24
|
+
scopeRevision: string;
|
|
25
|
+
preview: RecoveryScopePreview;
|
|
26
|
+
} | {
|
|
27
|
+
kind: "unauthorized";
|
|
28
|
+
message: string;
|
|
29
|
+
} | {
|
|
30
|
+
kind: "unavailable";
|
|
31
|
+
message: string;
|
|
32
|
+
} | {
|
|
33
|
+
kind: "version_update_required";
|
|
34
|
+
message: string;
|
|
35
|
+
contractVersion: number;
|
|
36
|
+
};
|
|
37
|
+
export type RecoveryOutcome = {
|
|
38
|
+
kind: "operation";
|
|
39
|
+
operation: RecoveryOperation;
|
|
40
|
+
} | {
|
|
41
|
+
kind: "refused";
|
|
42
|
+
refusal: RecoveryRefusal;
|
|
43
|
+
};
|
|
44
|
+
export declare function parseRecoveryRefusal(input: unknown): RecoveryRefusal;
|
|
45
|
+
export declare function serializeRecoveryOutcome(outcome: RecoveryOutcome): string;
|
|
46
|
+
/** Accepts the HTTP body as text or an already decoded IPC value. */
|
|
47
|
+
export declare function parseRecoveryOutcome(input: unknown): RecoveryOutcome;
|
|
48
|
+
export declare function isRecoveryOutcome(value: unknown): value is RecoveryOutcome;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marks the line carrying a recovery tool's JSON payload.
|
|
3
|
+
*
|
|
4
|
+
* A tool answers in two parts — a sentence a person reads, then the payload a
|
|
5
|
+
* renderer decodes — and they arrive joined into one string. Without a marker a
|
|
6
|
+
* reader has to guess where the prose stops, and the first guess (the first
|
|
7
|
+
* line opening a brace) silently lost the payload as soon as the prose above it
|
|
8
|
+
* changed shape.
|
|
9
|
+
*/
|
|
10
|
+
export declare const RECOVERY_PAYLOAD_PREFIX = "recovery-payload: ";
|
|
11
|
+
/**
|
|
12
|
+
* The payload a recovery tool marked, or nothing when the text carries none.
|
|
13
|
+
*
|
|
14
|
+
* The last marked line wins. An owner's prose precedes the payload and may
|
|
15
|
+
* quote a marker back — a refusal message repeating what a caller sent, for
|
|
16
|
+
* instance — and the tool writes its own payload last, so reading from the end
|
|
17
|
+
* cannot be talked into decoding someone's quotation instead.
|
|
18
|
+
*/
|
|
19
|
+
export declare function readRecoveryPayloadLine(text: string): string | undefined;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type RecoveryAction } from "./actions";
|
|
2
|
+
import { type RecoveryFacts } from "./facts";
|
|
3
|
+
import { type RecoveryOutcome } from "./outcomes";
|
|
4
|
+
export declare function recoveryPostconditionHolds(action: RecoveryAction, facts: RecoveryFacts): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Whether the turn is over and the record of it is durable: the postcondition a
|
|
7
|
+
* dependent mutation waits on, and the only honest reading of "it stopped".
|
|
8
|
+
*
|
|
9
|
+
* The operation's state is not that test. `cancel_turn` additionally requires
|
|
10
|
+
* `cleanup: verified_clear` to reach `succeeded`, which an adapter reports only
|
|
11
|
+
* when it can prove nothing the turn started survives; a harness that cannot
|
|
12
|
+
* prove it leaves cleanup `unknown`, so a working cancellation closes as
|
|
13
|
+
* `needs_action`. Reading that as "did not stop" marks every healthy Stop a
|
|
14
|
+
* failure and blocks the revert, undo and handoff that need only the turn to be
|
|
15
|
+
* over and written down. Cleanup stays a separate fact rather than a weaker
|
|
16
|
+
* form of this one.
|
|
17
|
+
*/
|
|
18
|
+
export declare function turnStopped(outcome: RecoveryOutcome): boolean;
|