@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.
- package/LICENSE +21 -0
- package/README.md +342 -1
- package/dist/_virtual/_rolldown/runtime.js +27 -0
- package/dist/actions/bridge.js +1 -0
- package/dist/context.js +0 -0
- package/dist/contracts/actions.js +1 -0
- package/dist/contracts/boundaries.js +1 -0
- package/dist/contracts/capabilities.js +1 -0
- package/dist/contracts/code.js +1 -0
- package/dist/contracts/errors.js +8 -0
- package/dist/contracts/evidence.js +8 -0
- package/dist/contracts/identity.js +8 -0
- package/dist/contracts/index.js +1 -0
- package/dist/contracts/inspection.js +1 -0
- package/dist/contracts/policy.js +8 -0
- package/dist/contracts/sessions.js +1 -0
- package/dist/contracts/status.js +1 -0
- package/dist/discovery.js +8 -0
- package/dist/docs.js +63 -0
- package/dist/inspection/service.js +3 -0
- package/dist/inspection/tools.js +43 -0
- package/dist/runtime/logs.js +1 -0
- package/dist/runtime/process.js +1 -0
- package/dist/runtime/service.js +1 -0
- package/dist/runtime/targets.js +1 -0
- package/dist/runtime/tools.js +119 -0
- package/dist/server.js +324 -0
- package/dist/status.js +8 -0
- package/dist/xplat-mcp.js +68 -0
- package/package.json +43 -4
- package/src/actions/bridge.ts +2 -0
- package/src/context.ts +108 -0
- package/src/contracts/actions.ts +3 -0
- package/src/contracts/boundaries.ts +3 -0
- package/src/contracts/capabilities.ts +3 -0
- package/src/contracts/code.ts +3 -0
- package/src/contracts/errors.ts +3 -0
- package/src/contracts/evidence.ts +3 -0
- package/src/contracts/identity.ts +3 -0
- package/src/contracts/index.ts +3 -0
- package/src/contracts/inspection.ts +3 -0
- package/src/contracts/policy.ts +3 -0
- package/src/contracts/sessions.ts +3 -0
- package/src/contracts/status.ts +3 -0
- package/src/discovery.ts +2 -0
- package/src/docs.ts +89 -0
- package/src/inspection/service.ts +5 -0
- package/src/inspection/tools.ts +61 -0
- package/src/runtime/logs.ts +2 -0
- package/src/runtime/process.ts +2 -0
- package/src/runtime/service.ts +2 -0
- package/src/runtime/targets.ts +2 -0
- package/src/runtime/tools.ts +136 -0
- package/src/server.ts +580 -0
- package/src/status.ts +2 -0
- package/src/xplat-mcp.ts +96 -0
- package/types/contracts/actions.d.ts +209 -0
- package/types/contracts/boundaries.d.ts +6 -0
- package/types/contracts/capabilities.d.ts +106 -0
- package/types/contracts/code.d.ts +134 -0
- package/types/contracts/errors.d.ts +75 -0
- package/types/contracts/evidence.d.ts +115 -0
- package/types/contracts/identity.d.ts +127 -0
- package/types/contracts/index.d.ts +11 -0
- package/types/contracts/inspection.d.ts +45 -0
- package/types/contracts/policy.d.ts +86 -0
- package/types/contracts/sessions.d.ts +160 -0
- 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,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
|