@octane-xplat/mcp 0.1.0 → 0.16.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.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +342 -1
  3. package/dist/_virtual/_rolldown/runtime.js +27 -0
  4. package/dist/actions/bridge.js +1 -0
  5. package/dist/context.js +0 -0
  6. package/dist/contracts/actions.js +1 -0
  7. package/dist/contracts/boundaries.js +1 -0
  8. package/dist/contracts/capabilities.js +1 -0
  9. package/dist/contracts/code.js +1 -0
  10. package/dist/contracts/errors.js +8 -0
  11. package/dist/contracts/evidence.js +8 -0
  12. package/dist/contracts/identity.js +8 -0
  13. package/dist/contracts/index.js +1 -0
  14. package/dist/contracts/inspection.js +1 -0
  15. package/dist/contracts/policy.js +8 -0
  16. package/dist/contracts/sessions.js +1 -0
  17. package/dist/contracts/status.js +1 -0
  18. package/dist/discovery.js +8 -0
  19. package/dist/docs.js +63 -0
  20. package/dist/inspection/service.js +3 -0
  21. package/dist/inspection/tools.js +43 -0
  22. package/dist/runtime/logs.js +1 -0
  23. package/dist/runtime/process.js +1 -0
  24. package/dist/runtime/service.js +1 -0
  25. package/dist/runtime/targets.js +1 -0
  26. package/dist/runtime/tools.js +119 -0
  27. package/dist/server.js +324 -0
  28. package/dist/status.js +8 -0
  29. package/dist/xplat-mcp.js +68 -0
  30. package/package.json +43 -4
  31. package/src/actions/bridge.ts +2 -0
  32. package/src/context.ts +108 -0
  33. package/src/contracts/actions.ts +3 -0
  34. package/src/contracts/boundaries.ts +3 -0
  35. package/src/contracts/capabilities.ts +3 -0
  36. package/src/contracts/code.ts +3 -0
  37. package/src/contracts/errors.ts +3 -0
  38. package/src/contracts/evidence.ts +3 -0
  39. package/src/contracts/identity.ts +3 -0
  40. package/src/contracts/index.ts +3 -0
  41. package/src/contracts/inspection.ts +3 -0
  42. package/src/contracts/policy.ts +3 -0
  43. package/src/contracts/sessions.ts +3 -0
  44. package/src/contracts/status.ts +3 -0
  45. package/src/discovery.ts +2 -0
  46. package/src/docs.ts +89 -0
  47. package/src/inspection/service.ts +5 -0
  48. package/src/inspection/tools.ts +61 -0
  49. package/src/runtime/logs.ts +2 -0
  50. package/src/runtime/process.ts +2 -0
  51. package/src/runtime/service.ts +2 -0
  52. package/src/runtime/targets.ts +2 -0
  53. package/src/runtime/tools.ts +136 -0
  54. package/src/server.ts +580 -0
  55. package/src/status.ts +2 -0
  56. package/src/xplat-mcp.ts +96 -0
  57. package/types/contracts/actions.d.ts +209 -0
  58. package/types/contracts/boundaries.d.ts +6 -0
  59. package/types/contracts/capabilities.d.ts +106 -0
  60. package/types/contracts/code.d.ts +134 -0
  61. package/types/contracts/errors.d.ts +75 -0
  62. package/types/contracts/evidence.d.ts +115 -0
  63. package/types/contracts/identity.d.ts +127 -0
  64. package/types/contracts/index.d.ts +11 -0
  65. package/types/contracts/inspection.d.ts +45 -0
  66. package/types/contracts/policy.d.ts +86 -0
  67. package/types/contracts/sessions.d.ts +160 -0
  68. package/types/contracts/status.d.ts +40 -0
@@ -0,0 +1,209 @@
1
+ /**
2
+ * App-declared development-action contract (design: plans/octane-xplat-mcp.md,
3
+ * "App-declared development actions").
4
+ *
5
+ * The app owns action semantics; the framework owns discovery and invocation.
6
+ * A dev-mode app announces a registry snapshot (identity + revision + typed
7
+ * declarations) over a platform bridge; this server lists the live registry
8
+ * and invokes actions through that same bridge. The declaration is the single
9
+ * source of truth: handler, runtime-validatable input/result contracts, and
10
+ * metadata all travel in one announcement — there are no MCP-side per-action
11
+ * definitions.
12
+ *
13
+ * Wire invariants:
14
+ * - Every registry carries `appInstanceId` — each running app realm (tab,
15
+ * window, device process) is a distinct invocation destination. Calls are
16
+ * never silently shared or redirected.
17
+ * - `revision` increments on every registry change (add/change/remove, owner
18
+ * disposal, hot reload). Invocation presents the revision it read; a stale
19
+ * revision is rejected with `stale_identity` before anything runs.
20
+ * - Announcements are heartbeats: a stopped heartbeat marks the instance
21
+ * `disconnected` — a live app with zero actions still announces, so an
22
+ * empty registry is always distinguishable from a disconnected runtime.
23
+ */
24
+
25
+ import type { ContractError } from './errors'
26
+ import type { Platform } from './identity'
27
+
28
+ export const DEV_ACTIONS_PROTOCOL = 'xplat-dev-actions'
29
+ export const DEV_ACTIONS_PROTOCOL_VERSION = 1
30
+ /** Heartbeat interval the app-side bridge uses; instances quieter than this ×3 are stale. */
31
+ export const DEV_ACTIONS_ANNOUNCE_INTERVAL_MS: number
32
+ export const DEV_ACTIONS_STALE_AFTER_MS: number
33
+ /** Bounded pending-invocation queue per instance; saturation returns `target_busy`. */
34
+ export const DEV_ACTIONS_QUEUE_LIMIT: number
35
+ /** Cap on overlapping invocations per instance (declared `overlap` actions). */
36
+ export const DEV_ACTIONS_OVERLAP_LIMIT: number
37
+ /** Default and maximum execution timeouts. */
38
+ export const DEV_ACTIONS_DEFAULT_TIMEOUT_MS: number
39
+ export const DEV_ACTIONS_MAX_TIMEOUT_MS: number
40
+
41
+ // ---------- declaration contract ----------
42
+
43
+ /** Runtime-validatable field subset. `fields`/`items` allow one level of structure. */
44
+ export interface DevActionField {
45
+ type: 'string' | 'number' | 'integer' | 'boolean' | 'object' | 'array'
46
+ description?: string
47
+ /** Applied when the caller omits the field. */
48
+ default?: unknown
49
+ /** Closed value set. */
50
+ enum?: readonly unknown[]
51
+ minLength?: number
52
+ maxLength?: number
53
+ /** Regular expression source (must compile). */
54
+ pattern?: string
55
+ minimum?: number
56
+ maximum?: number
57
+ minItems?: number
58
+ maxItems?: number
59
+ /** For `object`: named nested fields. Defaults apply to top-level fields only. */
60
+ fields?: Record<string, DevActionField>
61
+ /** For `array`: element contract. */
62
+ items?: DevActionField
63
+ }
64
+
65
+ /** Named typed fields. `{}` (or omitted) means the action takes no arguments. */
66
+ export interface DevActionFields {
67
+ fields?: Record<string, DevActionField>
68
+ }
69
+
70
+ /**
71
+ * Disclosed effect categories — the agent reads these to decide whether an
72
+ * action fits the existing authorization. `externalEffects` maps to the
73
+ * `external-effects` permission scope and is checked at invocation time.
74
+ */
75
+ export interface DevActionEffects {
76
+ /** Plain-language summary of what the action changes. */
77
+ summary: string
78
+ auth?: boolean
79
+ navigation?: boolean
80
+ appState?: boolean
81
+ testData?: boolean
82
+ /** Effects outside the app itself — external test services, destructive resets. */
83
+ externalEffects?: boolean
84
+ }
85
+
86
+ export type DevActionConcurrency =
87
+ /** Default: actions for one app instance run one at a time through a bounded queue. */
88
+ | 'serialized'
89
+ /** The app declares this action safe to overlap with other executions. */
90
+ | 'overlap'
91
+
92
+ export interface DevActionExecution {
93
+ concurrency?: DevActionConcurrency
94
+ /** Cooperative cancellation supported by the handler. */
95
+ cancellable?: boolean
96
+ /** Safe to retry (idempotent). Non-retryable actions never auto-rerun. */
97
+ retryable?: boolean
98
+ /** Per-action execution deadline; bounded by DEV_ACTIONS_MAX_TIMEOUT_MS. */
99
+ timeoutMs?: number
100
+ }
101
+
102
+ export interface DevActionDeclaration {
103
+ /** Stable machine identity, e.g. `auth.login-as-test-admin`. */
104
+ id: string
105
+ title: string
106
+ /** When to use it and what successful completion means. */
107
+ description: string
108
+ input?: DevActionFields
109
+ result?: DevActionFields
110
+ effects: DevActionEffects
111
+ availability?: {
112
+ /** Platforms this action applies to; absent = all. */
113
+ platforms?: Platform[]
114
+ /** Readiness conditions outside platform selection (free text). */
115
+ readyWhen?: string
116
+ }
117
+ execution?: DevActionExecution
118
+ }
119
+
120
+ /** Declared action plus its live availability, as announced by the app. */
121
+ export interface DevActionListing extends DevActionDeclaration {
122
+ availability?: {
123
+ platforms?: Platform[]
124
+ readyWhen?: string
125
+ /** Live readiness verdict from the app. */
126
+ ready: boolean
127
+ /** Why it is unavailable right now. */
128
+ reason?: string
129
+ }
130
+ }
131
+
132
+ // ---------- registry announcement ----------
133
+
134
+ export interface DevActionRegistryAnnouncement {
135
+ protocol: typeof DEV_ACTIONS_PROTOCOL
136
+ version: number
137
+ /** Unique per app realm — survives hot reload, never crosses processes. */
138
+ appInstanceId: string
139
+ /** App-supplied label for the developer, e.g. package name or window title. */
140
+ appLabel?: string
141
+ platform: Platform
142
+ revision: number
143
+ announcedAt: string
144
+ actions: DevActionListing[]
145
+ }
146
+
147
+ // ---------- invocation ----------
148
+
149
+ export interface DevActionInvocationRequest {
150
+ /** Stable request identity — retries with the same requestId must not re-run the effect. */
151
+ requestId: string
152
+ appInstanceId: string
153
+ actionId: string
154
+ /** The registry revision the caller discovered; rejected when stale. */
155
+ registryRevision: number
156
+ input: Record<string, unknown>
157
+ }
158
+
159
+ export type DevActionInvocationStatus =
160
+ | 'succeeded'
161
+ | 'failed'
162
+ | 'cancelled'
163
+ /** The app may or may not have run the action; reported, never silently retried. */
164
+ | 'unknown'
165
+
166
+ export interface DevActionOutcome {
167
+ requestId: string
168
+ status: DevActionInvocationStatus
169
+ /** Structured result validated against the declared result contract. */
170
+ result?: Record<string, unknown>
171
+ /** Machine-readable failure detail; mapped to ContractError codes server-side. */
172
+ error?: { code: string; message: string }
173
+ /** What the app knows changed before a failure/cancellation; absent = unknown. */
174
+ partialEffects?: string[]
175
+ completedAt: string
176
+ }
177
+
178
+ /** What `validateActionInput` returns. */
179
+ export type DevActionInputResult =
180
+ | { ok: true; value: Record<string, unknown> }
181
+ | { ok: false; errors: string[] }
182
+
183
+ // ---------- validators (shared with the app-side implementation) ----------
184
+
185
+ /** Declaration sanity: id shape, required metadata, compilable field patterns. */
186
+ export declare function validateActionDeclaration(action: unknown): string[]
187
+
188
+ /** Type-check + apply defaults + reject undeclared fields. */
189
+ export declare function validateActionInput(
190
+ contract: DevActionFields | undefined,
191
+ input: unknown,
192
+ ): DevActionInputResult
193
+
194
+ /** Result-contract validation. An invalid output fails explicitly — it does not undo a ran action. */
195
+ export declare function validateActionOutput(
196
+ contract: DevActionFields | undefined,
197
+ output: unknown,
198
+ ): DevActionInputResult
199
+
200
+ /** Structural check on an announcement payload. */
201
+ export declare function isDevActionAnnouncement(
202
+ value: unknown,
203
+ ): value is DevActionRegistryAnnouncement
204
+
205
+ /** Live availability for an announced action on a given platform. */
206
+ export declare function actionAvailability(
207
+ action: DevActionListing,
208
+ platform: Platform,
209
+ ): { ready: boolean; reason?: string }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Product boundaries — what this server deliberately does not do, surfaced in
3
+ * the project-status report so agents don't probe for excluded features.
4
+ */
5
+
6
+ export const PRODUCT_BOUNDARIES: readonly string[]
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Explicit capability matrix — per platform and target, each capability is
3
+ * reported separately. Unsupported or unqualified capabilities stay visible
4
+ * with an explanation and a usable alternative, never hidden or faked.
5
+ */
6
+
7
+ import type { AppRef, Platform, RuntimePrerequisite } from './identity'
8
+ import type { PermissionScope } from './policy'
9
+
10
+ /** The externally observable capability surface. */
11
+ export const CAPABILITY_IDS: readonly CapabilityId[]
12
+
13
+ export type CapabilityId =
14
+ /** Detected project identity, versions, guidance, prerequisites (package A). */
15
+ | 'project_status'
16
+ /** Project-scoped guidance retrieval. */
17
+ | 'guidance'
18
+ /** Version-bound documentation search/read (package B). */
19
+ | 'docs'
20
+ /** Exact/semantic code discovery (package C). */
21
+ | 'code_search'
22
+ /** Device/target enumeration with availability and ownership (package D). */
23
+ | 'target_discovery'
24
+ /** Authorized build for a development session (package D). */
25
+ | 'build'
26
+ /** Launch a built app on a target (package D). */
27
+ | 'launch'
28
+ /** Attach to an existing compatible development session (package D). */
29
+ | 'attach'
30
+ /** Bounded, cursor-paginated logs with gap indicators (package D). */
31
+ | 'logs'
32
+ /** Job status/cancellation without holding a tool call open (package A+D). */
33
+ | 'job_control'
34
+ /** Qualified host-state inspection (package E). */
35
+ | 'ui_inspection'
36
+ /** Screenshot capture — always permission-gated at action time (package E). */
37
+ | 'screenshot'
38
+ /** Scoped interactions with an explicit mechanism (package E). */
39
+ | 'interaction'
40
+ /** App-declared development actions: discovery + invocation (package G). */
41
+ | 'dev_actions'
42
+ /** Ownership-safe session end (package A+D). */
43
+ | 'session_end'
44
+ /** Session-scoped evidence with retain/delete (package A). */
45
+ | 'evidence'
46
+
47
+ export type CapabilityStatus =
48
+ /** Usable now on this platform/target. */
49
+ | 'supported'
50
+ /** Works within stated coverage limits; `detail` names them. */
51
+ | 'partial'
52
+ /** Exists, but every use is checked against a permission grant at action time. */
53
+ | 'permission_gated'
54
+ /** Achievable on this platform but a provider or prerequisite is absent. */
55
+ | 'unavailable'
56
+ /** Not possible on this platform; `detail` says why. */
57
+ | 'unsupported'
58
+ /** Not yet qualified; never presented as ready. */
59
+ | 'unknown'
60
+
61
+ /** A usable path when a capability is not supported here. */
62
+ export interface CapabilityAlternative {
63
+ description: string
64
+ /** A concrete command the developer can run, when one exists. */
65
+ command?: string
66
+ }
67
+
68
+ export interface CapabilityEntry {
69
+ capability: CapabilityId
70
+ status: CapabilityStatus
71
+ /** Why the capability is limited, missing, or gated — or its coverage bounds. */
72
+ detail?: string
73
+ /** The grant checked at action time (screenshots: 'visual-capture'). */
74
+ requiresPermission?: PermissionScope
75
+ prerequisites?: RuntimePrerequisite[]
76
+ alternative?: CapabilityAlternative
77
+ }
78
+
79
+ export interface PlatformCapabilityMatrix {
80
+ platform: Platform
81
+ /** Whether the bound app declares this platform. */
82
+ declared: boolean
83
+ capabilities: CapabilityEntry[]
84
+ }
85
+
86
+ /**
87
+ * A platform adapter supplied by a runtime package (D/E/G). Providers return
88
+ * entries only for capabilities they implement; everything else keeps the
89
+ * honest default from `defaultCapabilityEntry`.
90
+ */
91
+ export interface CapabilityProvider {
92
+ platform: Platform
93
+ describe(app: AppRef): CapabilityEntry[] | Promise<CapabilityEntry[]>
94
+ }
95
+
96
+ export declare function defaultCapabilityEntry(
97
+ capability: CapabilityId,
98
+ platform: Platform,
99
+ ): CapabilityEntry
100
+
101
+ export interface CapabilityRegistry {
102
+ register(provider: CapabilityProvider): void
103
+ matrixFor(app: AppRef | null, platforms: Platform[]): Promise<PlatformCapabilityMatrix[]>
104
+ }
105
+
106
+ export declare function createCapabilityRegistry(): CapabilityRegistry
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Code retrieval contract (work item C) — the provider seam the workspace
3
+ * context's `code` slot and the `xplat_code_search` / `xplat_code_read` tools
4
+ * consume.
5
+ *
6
+ * Rules carried by the design:
7
+ * - Exact search reads current source files directly — it stays usable while
8
+ * the semantic index is absent, preparing, stale, or failed.
9
+ * - Semantic search is optional local acceleration (no remote embedding
10
+ * without explicit opt-in). Similarity rank is relevance, never evidence of
11
+ * correctness or platform support.
12
+ * - Results are leads: every hit carries a ref, provenance, and a freshness
13
+ * marker so the agent can open current source before citing an API.
14
+ * - Secrets, private configuration, generated output, and paths outside the
15
+ * permitted corpus scope are never served.
16
+ */
17
+
18
+ import type { AppRef, Platform, SubsystemState, WorkspaceKind } from './identity'
19
+
20
+ /** `app` = the bound application's own source; `framework` = its installed @octane-xplat package sources. */
21
+ export type CodeCorpus = 'app' | 'framework'
22
+
23
+ export type CodeSearchMode = 'auto' | 'exact' | 'semantic'
24
+
25
+ /** Per-hit freshness: `current` means revalidated or read live just now. */
26
+ export interface CodeFreshness {
27
+ state: 'current' | 'stale'
28
+ /** Content hash at serve time — a later read with a different hash drifted. */
29
+ contentHash?: string
30
+ mtimeMs?: number
31
+ }
32
+
33
+ export interface CodeHitProvenance {
34
+ source: 'app-source' | 'framework-source'
35
+ scope: 'release' | 'project'
36
+ package?: string | null
37
+ requestedVersion?: string | null
38
+ servedVersion?: string | null
39
+ match:
40
+ | 'exact'
41
+ | 'verified-compatible'
42
+ | 'outside-installed-binding'
43
+ | 'unavailable'
44
+ | 'tag-conflict'
45
+ offline?: boolean
46
+ }
47
+
48
+ export interface CodeHit {
49
+ /** `xplat-code://<corpus>/<key>` — open it with xplat_code_read. */
50
+ ref: string
51
+ corpus: CodeCorpus
52
+ package?: string
53
+ /** Corpus-relative path (app: workspace-relative; framework: package source root). */
54
+ path: string
55
+ line: number
56
+ endLine: number
57
+ excerpt: string
58
+ matchType: 'exact' | 'semantic'
59
+ score?: number
60
+ freshness: CodeFreshness
61
+ provenance: CodeHitProvenance
62
+ /** Index revision that produced a semantic hit. */
63
+ indexRevision?: number
64
+ provisional?: boolean
65
+ }
66
+
67
+ export interface SemanticIndexStatus {
68
+ state: SubsystemState
69
+ /** e.g. 'local-tfidf' — remote embedding is never used without opt-in. */
70
+ backend?: string
71
+ detail?: string
72
+ files?: number
73
+ chunks?: number
74
+ truncated?: boolean
75
+ revision?: number
76
+ /** Hits dropped because they failed revalidation against current source. */
77
+ droppedStale?: number
78
+ }
79
+
80
+ export interface CodeSearchArgs {
81
+ app: AppRef | null
82
+ workspaceKind?: WorkspaceKind
83
+ query: string
84
+ mode?: CodeSearchMode
85
+ corpus?: CodeCorpus | 'all'
86
+ platform?: Platform
87
+ package?: string
88
+ limit?: number
89
+ }
90
+
91
+ export interface CodeSearchResult {
92
+ status?: CodeResultStatus
93
+ mode: CodeSearchMode
94
+ results: CodeHit[]
95
+ semantic: SemanticIndexStatus
96
+ coverage: unknown
97
+ /** Working-tree freshness marker (HEAD + uncommitted count) when git is available. */
98
+ tree?: string
99
+ }
100
+
101
+ export interface CodeReadArgs {
102
+ app: AppRef | null
103
+ workspaceKind?: WorkspaceKind
104
+ ref?: string
105
+ corpus?: CodeCorpus
106
+ package?: string
107
+ path?: string
108
+ offset?: number
109
+ limit?: number
110
+ }
111
+
112
+ export type CodeResultStatus =
113
+ | 'selection-required'
114
+ | 'invalid-selection'
115
+ | 'invalid-ref'
116
+ | 'out-of-scope'
117
+ | 'excluded'
118
+ | 'unavailable'
119
+
120
+ export interface CodeErrorResult {
121
+ status: CodeResultStatus
122
+ reason?: string
123
+ detail?: string
124
+ corpus?: string
125
+ path?: string
126
+ }
127
+
128
+ /** The provider contract injected as `options.code` on the workspace context. */
129
+ export interface CodeProvider {
130
+ /** Fills the project-status `codeIndex` slot. */
131
+ status(app: AppRef | null): unknown
132
+ search(args: CodeSearchArgs): CodeSearchResult | CodeErrorResult | Promise<unknown>
133
+ read(args: CodeReadArgs): unknown | Promise<unknown>
134
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Shared error vocabulary for the Octane Xplat MCP server.
3
+ *
4
+ * Every tool failure is one of these codes — agents must be able to tell an
5
+ * invalid selection from a missing prerequisite, a busy target, a stale
6
+ * identity, a disconnected runtime, or a real execution failure. Each error
7
+ * names the smallest corrective action through `recovery`.
8
+ */
9
+
10
+ export const CONTRACT_ERROR_CODES: readonly ContractErrorCode[]
11
+
12
+ export type ContractErrorCode =
13
+ /** The named app, target, session, job, or option does not exist or does not apply. */
14
+ | 'invalid_selection'
15
+ /** Equally plausible choices would change the result — the developer must pick. Never resolved silently. */
16
+ | 'ambiguous_selection'
17
+ /** A required permission grant is absent, expired, or revoked. */
18
+ | 'permission_denied'
19
+ /** The platform/target does not support this capability; `alternative` may offer a usable path. */
20
+ | 'unsupported_capability'
21
+ /** A toolchain, device, or data prerequisite is missing; `recovery` names the smallest fix. */
22
+ | 'prerequisite_missing'
23
+ /** The target is owned by another session/agent, or a bounded queue is saturated. */
24
+ | 'target_busy'
25
+ /** The session, registry revision, or build identity changed since the caller bound it. */
26
+ | 'stale_identity'
27
+ /** The runtime disconnected; last-known state is historical evidence, never current state. */
28
+ | 'disconnected_runtime'
29
+ /** The operation ran and failed. */
30
+ | 'execution_failure'
31
+ /** The named resource (job, session, evidence record) does not exist. */
32
+ | 'not_found'
33
+ /** Cancellation won the race or was requested before/while running. */
34
+ | 'cancelled'
35
+ /** A bounded wait or execution timeout expired. */
36
+ | 'timeout'
37
+ /** An optional subsystem (docs index, code index, embeddings) is not provided or offline. */
38
+ | 'unavailable'
39
+
40
+ /** The smallest corrective step a developer or agent can take. */
41
+ export interface Recovery {
42
+ /** Imperative description, e.g. "Select an app with xplat_select_app". */
43
+ action: string
44
+ /** Optional concrete detail: a command, path, or env var. */
45
+ detail?: string
46
+ }
47
+
48
+ export interface ContractError {
49
+ code: ContractErrorCode
50
+ message: string
51
+ recovery?: Recovery
52
+ /** Whether retrying the same call could succeed without other changes. */
53
+ retryable?: boolean
54
+ /** Structured context, e.g. `candidates` for ambiguous_selection. */
55
+ context?: Record<string, unknown>
56
+ }
57
+
58
+ /** Success/failure envelope used by every MCP tool result's structuredContent. */
59
+ export type ToolOutcome<T> = { ok: true; data: T } | { ok: false; error: ContractError }
60
+
61
+ export declare function contractError(
62
+ code: ContractErrorCode,
63
+ message: string,
64
+ options?: { recovery?: Recovery; retryable?: boolean; context?: Record<string, unknown> },
65
+ ): ContractError
66
+
67
+ export declare function ok<T>(data: T): ToolOutcome<T>
68
+ export declare function fail(error: ContractError): ToolOutcome<never>
69
+ export declare function isContractError(value: unknown): value is ContractError
70
+
71
+ /** Normalize any thrown value into a ContractError (default code: execution_failure). */
72
+ export declare function toContractError(
73
+ value: unknown,
74
+ fallbackCode?: ContractErrorCode,
75
+ ): ContractError
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Evidence and retention contract.
3
+ *
4
+ * Evidence is session-scoped by default: when the MCP session ends, its
5
+ * artifacts are deleted. The developer may explicitly retain selected
6
+ * artifacts with a stated expiration or until manual deletion, and may delete
7
+ * retained evidence sooner. Deleting server artifacts cannot erase an
8
+ * external agent-client transcript that recorded them — that note travels
9
+ * with every retain/delete result (`TRANSCRIPT_LIMITATION_NOTE`).
10
+ *
11
+ * Every observation carries provenance: app, platform, target, session,
12
+ * build/source identity, capture time, and — for interactions — the
13
+ * mechanism. `handler-dispatch` and `dom-event` evidence must never be
14
+ * presented as OS-input verification (`isOsInputEvidence`).
15
+ */
16
+
17
+ import type { BuildIdentity } from './sessions'
18
+ import type { Platform } from './identity'
19
+ import type { ContractError } from './errors'
20
+
21
+ export const EVIDENCE_KINDS: readonly EvidenceKind[]
22
+ export const INTERACTION_MECHANISMS: readonly InteractionMechanism[]
23
+ /** Included in retain/delete results: deletion here never touches the client transcript. */
24
+ export const TRANSCRIPT_LIMITATION_NOTE: string
25
+
26
+ export type EvidenceKind =
27
+ | 'log'
28
+ | 'screenshot'
29
+ | 'inspection'
30
+ | 'interaction'
31
+ | 'measurement'
32
+ | 'assertion'
33
+ | 'job'
34
+ | 'note'
35
+
36
+ export type InteractionMechanism =
37
+ /** App handler invoked directly — proves handler behavior only. */
38
+ | 'handler-dispatch'
39
+ /** Synthesized DOM/platform events — proves event-path behavior, not OS hit-testing. */
40
+ | 'dom-event'
41
+ /** Real OS input path — can support OS-level claims. */
42
+ | 'os-input'
43
+ /** Not an interaction record. */
44
+ | 'none'
45
+
46
+ export interface EvidenceProvenance {
47
+ appId?: string
48
+ platform?: Platform
49
+ targetId?: string
50
+ sessionId?: string
51
+ buildIdentity?: BuildIdentity
52
+ /** ISO capture time. */
53
+ capturedAt: string
54
+ mechanism?: InteractionMechanism
55
+ }
56
+
57
+ export type EvidenceRetention =
58
+ /** Deleted when the MCP session ends (the default). */
59
+ | { scope: 'session' }
60
+ /** Kept until `expiresAt` (null = until manual deletion). */
61
+ | { scope: 'retained'; expiresAt: string | null }
62
+
63
+ export interface EvidenceRecord {
64
+ id: string
65
+ kind: EvidenceKind
66
+ summary: string
67
+ provenance: EvidenceProvenance
68
+ /** File path under the evidence root, for binary/large artifacts. */
69
+ artifactPath?: string
70
+ /** Small structured payload kept in memory. */
71
+ data?: unknown
72
+ retention: EvidenceRetention
73
+ createdAt: string
74
+ }
75
+
76
+ export interface RetainResult {
77
+ record: EvidenceRecord
78
+ transcriptNote: string
79
+ }
80
+
81
+ export interface DeleteResult {
82
+ deleted: boolean
83
+ transcriptNote: string
84
+ }
85
+
86
+ export interface EvidenceStore {
87
+ record(input: {
88
+ kind: EvidenceKind
89
+ summary: string
90
+ provenance: Omit<EvidenceProvenance, 'capturedAt'> & { capturedAt?: string }
91
+ data?: unknown
92
+ /** Write this content as the artifact file (utf8). */
93
+ artifact?: string | Uint8Array
94
+ /** Suggested artifact file name; sanitized. */
95
+ artifactName?: string
96
+ }): EvidenceRecord
97
+ list(filter?: { sessionId?: string; kind?: EvidenceKind }): EvidenceRecord[]
98
+ /** Move a record out of session scope. `expiresAt` ISO string or null for manual deletion. */
99
+ retain(
100
+ id: string,
101
+ options?: { expiresAt?: string | null },
102
+ ): RetainResult | { error: ContractError }
103
+ delete(id: string): DeleteResult | { error: ContractError }
104
+ /** Delete retained records past their expiration. */
105
+ sweepExpired(now?: Date): number
106
+ /** Session end: delete every session-scoped record and its artifact file. */
107
+ closeSession(): { deleted: number }
108
+ /** Absolute path of the evidence root for this server session. */
109
+ readonly root: string
110
+ }
111
+
112
+ /** True only for evidence captured through the real OS input path. */
113
+ export declare function isOsInputEvidence(record: EvidenceRecord): boolean
114
+
115
+ export declare function createEvidenceStore(options: { root: string }): EvidenceStore