@octane-xplat/mcp 0.16.0 → 0.17.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 +21 -6
- package/dist/runtime/tools.js +12 -26
- package/dist/server.js +13 -7
- package/package.json +2 -2
- package/src/context.ts +6 -3
- package/src/runtime/tools.ts +15 -23
- package/src/server.ts +14 -9
- package/types/contracts/actions.d.ts +14 -1
- package/types/contracts/sessions.d.ts +9 -2
package/README.md
CHANGED
|
@@ -61,12 +61,12 @@ Options: `--workspace` / `XPLAT_MCP_WORKSPACE` (default cwd), `--app` /
|
|
|
61
61
|
| `xplat_select_app` | Explicitly bind an app by id/path/name. Ambiguous or unknown selections fail with the candidate list — the server never picks silently. |
|
|
62
62
|
| `xplat_capability_matrix` | Per-platform capability entries: build, launch, logs, inspection, screenshot, interaction, and the rest are separate rows with status, detail, and alternatives. |
|
|
63
63
|
| `xplat_permissions` | Grant states per scope plus the action-time check result and recovery path. |
|
|
64
|
-
| `xplat_sessions` |
|
|
64
|
+
| `xplat_sessions` | Retained sessions and launch progress; optional `sessionId` selects exact status, including failures and ended sessions. |
|
|
65
65
|
| `xplat_targets` | Launchable targets per platform (simulators, devices, host targets) with availability, missing-toolchain reasons, and busy/ownership state. |
|
|
66
66
|
| `xplat_build` | Build the bound app for one platform as a job — same commands `xplat build` runs. `requestKey` dedupes retries. |
|
|
67
|
-
| `xplat_launch` |
|
|
67
|
+
| `xplat_launch` | Start an owned session and return its durable ID immediately; poll `xplat_sessions` for readiness, end with `xplat_session_end`. |
|
|
68
68
|
| `xplat_attach` | Bind a compatible already-running dev session (user-owned, preserved). |
|
|
69
|
-
| `
|
|
69
|
+
| `xplat_diagnostics` | Retained session discovery; log pages under `logs`, native summaries under `reports`, explicit source `coverage`, and original report reads. Build/action jobs retain their own logs. |
|
|
70
70
|
| `xplat_dev_actions` | The bound app's live development-action registry: instance identity, revision, declarations (contracts, effects, availability). Empty vs disconnected vs no-bridge are distinct states. |
|
|
71
71
|
| `xplat_dev_action_invoke` | Invoke an action against an exact app instance + registry revision. Pre-validates input, availability, grants and queue bounds; returns a job (`requestKey` dedupes retries). |
|
|
72
72
|
| `xplat_dev_action_status` | Invocation status/result — same job data restricted to dev-action jobs, with instance/request identity. |
|
|
@@ -102,7 +102,7 @@ snapshot availability, so readiness alone is not a successful retrieval.
|
|
|
102
102
|
"name": "xplat_docs_read",
|
|
103
103
|
"arguments": {
|
|
104
104
|
"package": "@octane-xplat/ui",
|
|
105
|
-
"path": "docs/
|
|
105
|
+
"path": "docs/testing/test-identifiers.md",
|
|
106
106
|
"limit": 2000
|
|
107
107
|
}
|
|
108
108
|
}
|
|
@@ -197,8 +197,8 @@ cancel, ownership-safe cleanup, bounded logs, and capability-matrix entries.
|
|
|
197
197
|
|
|
198
198
|
Readiness is probe/marker-confirmed (`ready` session state) — HTTP 200 for
|
|
199
199
|
web, the host-start marker for macOS, `Successfully synced application` /
|
|
200
|
-
first `CONSOLE` line for `ns run`. A timeout or pre-ready exit
|
|
201
|
-
and
|
|
200
|
+
first `CONSOLE` line for `ns run`. A timeout or pre-ready exit marks the session `failed`
|
|
201
|
+
and releases its owned resources. Chromium is located via
|
|
202
202
|
`XPLAT_MCP_CHROMIUM`, the Playwright browser cache, or system installs;
|
|
203
203
|
without it the web session still reports the server URL with an explicit
|
|
204
204
|
`browser.launched: false`.
|
|
@@ -207,6 +207,21 @@ Physical iOS devices, Windows attach detection, and non-simulator iOS log
|
|
|
207
207
|
taps are reported limitations, not silent gaps — the capability matrix
|
|
208
208
|
carries the detail and an alternative.
|
|
209
209
|
|
|
210
|
+
Launch accepts an optional exact target ID or platform name. Platform or omitted
|
|
211
|
+
selection requires exactly one eligible target; ambiguity returns candidates.
|
|
212
|
+
Use the same session ID for status, diagnostics, and cleanup:
|
|
213
|
+
|
|
214
|
+
```js
|
|
215
|
+
const launch = await client.callTool({name: "xplat_launch", arguments: {target: "ios"}});
|
|
216
|
+
const {sessionId} = launch.structuredContent.data.session;
|
|
217
|
+
await client.callTool({name: "xplat_sessions", arguments: {sessionId}});
|
|
218
|
+
await client.callTool({name: "xplat_diagnostics", arguments: {sessionId}});
|
|
219
|
+
await client.callTool({name: "xplat_session_end", arguments: {sessionId}});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Ending a session
|
|
223
|
+
also cancels startup when it has not reached readiness.
|
|
224
|
+
|
|
210
225
|
## Inspect and interact with Chromium
|
|
211
226
|
|
|
212
227
|
This development package adds `xplat_inspect`, `xplat_interact`, and
|
package/dist/runtime/tools.js
CHANGED
|
@@ -17,7 +17,7 @@ function registerRuntimeTools(server, context, respond) {
|
|
|
17
17
|
});
|
|
18
18
|
});
|
|
19
19
|
server.registerTool("xplat_build", {
|
|
20
|
-
description: "Build the bound app for one platform as a background job (the same commands `xplat build` runs). Returns a job id — poll xplat_job_status and read output with
|
|
20
|
+
description: "Build the bound app for one platform as a background job (the same commands `xplat build` runs). Returns a job id — poll xplat_job_status and read output with xplat_diagnostics. Repeating with the same requestKey returns the live job instead of duplicating it.",
|
|
21
21
|
inputSchema: {
|
|
22
22
|
platform: z.enum(identity_exports.PLATFORMS),
|
|
23
23
|
requestKey: z.string().optional().describe("Idempotence key for retries")
|
|
@@ -36,17 +36,15 @@ function registerRuntimeTools(server, context, respond) {
|
|
|
36
36
|
});
|
|
37
37
|
});
|
|
38
38
|
server.registerTool("xplat_launch", {
|
|
39
|
-
description: "Start an owned development session:
|
|
39
|
+
description: "Start an owned development session: return one durable session id immediately. Read launch progress and readiness through xplat_sessions; diagnostics use the same session id, including after shutdown.",
|
|
40
40
|
inputSchema: {
|
|
41
|
-
|
|
42
|
-
targetId: z.string().optional().describe("Target from xplat_targets; required when several are available"),
|
|
41
|
+
target: z.string().optional().describe("Exact discovered target id or platform name; omit only when one eligible target exists"),
|
|
43
42
|
requestKey: z.string().optional().describe("Idempotence key for retries"),
|
|
44
43
|
browserMode: z.enum(["headless", "headed"]).optional().describe("Web only. Headed mode enables scoped OS keyboard input on qualified hosts; reserve desktop input before using it.")
|
|
45
44
|
}
|
|
46
|
-
}, async ({
|
|
45
|
+
}, async ({ target, requestKey, browserMode }) => {
|
|
47
46
|
const result = await runtime.launch({
|
|
48
|
-
|
|
49
|
-
targetId,
|
|
47
|
+
target,
|
|
50
48
|
requestKey,
|
|
51
49
|
browserMode
|
|
52
50
|
});
|
|
@@ -77,12 +75,14 @@ function registerRuntimeTools(server, context, respond) {
|
|
|
77
75
|
data: result
|
|
78
76
|
});
|
|
79
77
|
});
|
|
80
|
-
server.registerTool("
|
|
81
|
-
description: "
|
|
78
|
+
server.registerTool("xplat_diagnostics", {
|
|
79
|
+
description: "List retained sessions or read logs and existing native report summaries. Log cursors do not page reports; reportId with sessionId reads a retained original. Session selectors include retained logs and native reports; standalone build jobs have log captures only.",
|
|
82
80
|
inputSchema: {
|
|
81
|
+
reportId: z.string().optional(),
|
|
82
|
+
platform: z.enum(identity_exports.PLATFORMS).optional(),
|
|
83
83
|
sessionId: z.string().optional(),
|
|
84
84
|
jobId: z.string().optional(),
|
|
85
|
-
cursor: z.number().
|
|
85
|
+
cursor: z.number().int().nonnegative().optional(),
|
|
86
86
|
limit: z.number().int().positive().max(5e3).optional(),
|
|
87
87
|
stream: z.enum([
|
|
88
88
|
"stdout",
|
|
@@ -90,22 +90,8 @@ function registerRuntimeTools(server, context, respond) {
|
|
|
90
90
|
"lifecycle"
|
|
91
91
|
]).optional()
|
|
92
92
|
}
|
|
93
|
-
}, async (
|
|
94
|
-
|
|
95
|
-
ok: false,
|
|
96
|
-
error: {
|
|
97
|
-
code: "invalid_selection",
|
|
98
|
-
message: "Pass a sessionId or a jobId.",
|
|
99
|
-
recovery: { action: "Use xplat_sessions or a build/launch job id." }
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
const result = runtime.readLogs({
|
|
103
|
-
sessionId,
|
|
104
|
-
jobId,
|
|
105
|
-
cursor,
|
|
106
|
-
limit,
|
|
107
|
-
stream
|
|
108
|
-
});
|
|
93
|
+
}, async (args) => {
|
|
94
|
+
const result = await runtime.readDiagnostics(args);
|
|
109
95
|
return result.error ? respond({
|
|
110
96
|
ok: false,
|
|
111
97
|
error: result.error
|
package/dist/server.js
CHANGED
|
@@ -15,7 +15,7 @@ import { dirname, join } from "node:path";
|
|
|
15
15
|
import { fileURLToPath } from "node:url";
|
|
16
16
|
//#region src/server.ts
|
|
17
17
|
var SERVER_VERSION = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), "../package.json"), "utf8")).version;
|
|
18
|
-
var INSTRUCTIONS = "MCP server for Octane Xplat apps. Call xplat_project_status first — it reports detected apps, framework versions, guidance, prerequisites, and capabilities. If several apps are detected, ask the developer which to use and bind it with xplat_select_app; never guess. With a dev-mode app running, xplat_dev_actions lists the app-declared action registry and xplat_dev_action_invoke runs one against an exact instance and registry revision. For runtime work, check xplat_capability_matrix then xplat_targets; xplat_launch
|
|
18
|
+
var INSTRUCTIONS = "MCP server for Octane Xplat apps. Call xplat_project_status first — it reports detected apps, framework versions, guidance, prerequisites, and capabilities. If several apps are detected, ask the developer which to use and bind it with xplat_select_app; never guess. With a dev-mode app running, xplat_dev_actions lists the app-declared action registry and xplat_dev_action_invoke runs one against an exact instance and registry revision. For runtime work, check xplat_capability_matrix then xplat_targets; xplat_launch returns a durable session identity and launch progress and xplat_attach binds a session you already run. Results always say what was actually checked; a successful build is not proof of behavior.";
|
|
19
19
|
var respond = (outcome) => ({
|
|
20
20
|
structuredContent: outcome,
|
|
21
21
|
isError: outcome.ok === false,
|
|
@@ -129,9 +129,12 @@ function createXplatMcpServer(context) {
|
|
|
129
129
|
});
|
|
130
130
|
});
|
|
131
131
|
server.registerTool("xplat_sessions", {
|
|
132
|
-
description: "List
|
|
133
|
-
inputSchema: {}
|
|
134
|
-
}, async () =>
|
|
132
|
+
description: "List retained sessions or read exact sessionId status, including launch progress, failures and ended sessions. Attached user-owned apps are preserved on end.",
|
|
133
|
+
inputSchema: { sessionId: z.string().optional() }
|
|
134
|
+
}, async ({ sessionId }) => {
|
|
135
|
+
const result = sessionId ? context.runtime.sessionStatus(sessionId) : context.runtime.listSessions();
|
|
136
|
+
return result.error ? fail(result.error) : ok(result);
|
|
137
|
+
});
|
|
135
138
|
server.registerTool("xplat_job_status", {
|
|
136
139
|
description: "Bounded status query for a job started by a runtime tool.",
|
|
137
140
|
inputSchema: { jobId: z.string() }
|
|
@@ -150,9 +153,12 @@ function createXplatMcpServer(context) {
|
|
|
150
153
|
description: "End a session with ownership-safe cleanup: owned sessions release their resources; attached user-owned sessions are detached and preserved.",
|
|
151
154
|
inputSchema: { sessionId: z.string() }
|
|
152
155
|
}, async ({ sessionId }) => {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
+
try {
|
|
157
|
+
const result = await context.runtime.endSession(sessionId);
|
|
158
|
+
return result.error ? fail(result.error) : ok({ result });
|
|
159
|
+
} catch (error) {
|
|
160
|
+
return fail((0, errors_exports.toContractError)(error));
|
|
161
|
+
}
|
|
156
162
|
});
|
|
157
163
|
server.registerTool("xplat_dev_actions", {
|
|
158
164
|
description: "List the bound app's declared development actions — the live registry announced by a running dev app: stable ids, titles, input/result contracts, effects, availability with reasons, and the registry revision. Never executes an action. An empty registry is reported distinctly from a disconnected app.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octane-xplat/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "MCP server for Octane Xplat consumer apps — project identity, capability discovery, permission and evidence contracts.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@modelcontextprotocol/sdk": "1.31.0",
|
|
37
|
-
"@octane-xplat/cli": "0.
|
|
37
|
+
"@octane-xplat/cli": "0.17.0",
|
|
38
38
|
"yaml": "2.9.1",
|
|
39
39
|
"zod": "4.6.5"
|
|
40
40
|
},
|
package/src/context.ts
CHANGED
|
@@ -30,13 +30,16 @@ export interface RuntimeService {
|
|
|
30
30
|
listTargets(args: { platform?: Platform }): Promise<any>
|
|
31
31
|
build(args: { platform: Platform; requestKey?: string }): Promise<any>
|
|
32
32
|
launch(args: {
|
|
33
|
-
|
|
34
|
-
targetId?: string
|
|
33
|
+
target?: string
|
|
35
34
|
requestKey?: string
|
|
36
35
|
browserMode?: 'headless' | 'headed'
|
|
37
36
|
}): Promise<any>
|
|
37
|
+
listSessions(): any
|
|
38
|
+
sessionStatus(sessionId: string): any
|
|
38
39
|
attach(args: { platform: Platform; targetId?: string }): Promise<any>
|
|
39
|
-
|
|
40
|
+
readDiagnostics(args: {
|
|
41
|
+
reportId?: string
|
|
42
|
+
platform?: Platform
|
|
40
43
|
sessionId?: string
|
|
41
44
|
jobId?: string
|
|
42
45
|
cursor?: number
|
package/src/runtime/tools.ts
CHANGED
|
@@ -39,7 +39,7 @@ export function registerRuntimeTools(
|
|
|
39
39
|
'xplat_build',
|
|
40
40
|
{
|
|
41
41
|
description:
|
|
42
|
-
'Build the bound app for one platform as a background job (the same commands `xplat build` runs). Returns a job id — poll xplat_job_status and read output with
|
|
42
|
+
'Build the bound app for one platform as a background job (the same commands `xplat build` runs). Returns a job id — poll xplat_job_status and read output with xplat_diagnostics. Repeating with the same requestKey returns the live job instead of duplicating it.',
|
|
43
43
|
inputSchema: {
|
|
44
44
|
platform: z.enum(PLATFORMS as Platform[]),
|
|
45
45
|
requestKey: z.string().optional().describe('Idempotence key for retries'),
|
|
@@ -57,13 +57,14 @@ export function registerRuntimeTools(
|
|
|
57
57
|
'xplat_launch',
|
|
58
58
|
{
|
|
59
59
|
description:
|
|
60
|
-
'Start an owned development session:
|
|
60
|
+
'Start an owned development session: return one durable session id immediately. Read launch progress and readiness through xplat_sessions; diagnostics use the same session id, including after shutdown.',
|
|
61
61
|
inputSchema: {
|
|
62
|
-
|
|
63
|
-
targetId: z
|
|
62
|
+
target: z
|
|
64
63
|
.string()
|
|
65
64
|
.optional()
|
|
66
|
-
.describe(
|
|
65
|
+
.describe(
|
|
66
|
+
'Exact discovered target id or platform name; omit only when one eligible target exists',
|
|
67
|
+
),
|
|
67
68
|
requestKey: z.string().optional().describe('Idempotence key for retries'),
|
|
68
69
|
browserMode: z
|
|
69
70
|
.enum(['headless', 'headed'])
|
|
@@ -73,8 +74,8 @@ export function registerRuntimeTools(
|
|
|
73
74
|
),
|
|
74
75
|
},
|
|
75
76
|
},
|
|
76
|
-
async ({
|
|
77
|
-
const result = await runtime.launch({
|
|
77
|
+
async ({ target, requestKey, browserMode }) => {
|
|
78
|
+
const result = await runtime.launch({ target, requestKey, browserMode })
|
|
78
79
|
return result.error
|
|
79
80
|
? respond({ ok: false, error: result.error })
|
|
80
81
|
: respond({ ok: true, data: result })
|
|
@@ -103,31 +104,22 @@ export function registerRuntimeTools(
|
|
|
103
104
|
)
|
|
104
105
|
|
|
105
106
|
server.registerTool(
|
|
106
|
-
'
|
|
107
|
+
'xplat_diagnostics',
|
|
107
108
|
{
|
|
108
109
|
description:
|
|
109
|
-
'
|
|
110
|
+
'List retained sessions or read logs and existing native report summaries. Log cursors do not page reports; reportId with sessionId reads a retained original. Session selectors include retained logs and native reports; standalone build jobs have log captures only.',
|
|
110
111
|
inputSchema: {
|
|
112
|
+
reportId: z.string().optional(),
|
|
113
|
+
platform: z.enum(PLATFORMS as Platform[]).optional(),
|
|
111
114
|
sessionId: z.string().optional(),
|
|
112
115
|
jobId: z.string().optional(),
|
|
113
|
-
cursor: z.number().
|
|
116
|
+
cursor: z.number().int().nonnegative().optional(),
|
|
114
117
|
limit: z.number().int().positive().max(5000).optional(),
|
|
115
118
|
stream: z.enum(['stdout', 'stderr', 'lifecycle']).optional(),
|
|
116
119
|
},
|
|
117
120
|
},
|
|
118
|
-
async (
|
|
119
|
-
|
|
120
|
-
return respond({
|
|
121
|
-
ok: false,
|
|
122
|
-
error: {
|
|
123
|
-
code: 'invalid_selection',
|
|
124
|
-
message: 'Pass a sessionId or a jobId.',
|
|
125
|
-
recovery: { action: 'Use xplat_sessions or a build/launch job id.' },
|
|
126
|
-
},
|
|
127
|
-
})
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const result = runtime.readLogs({ sessionId, jobId, cursor, limit, stream })
|
|
121
|
+
async (args) => {
|
|
122
|
+
const result = await runtime.readDiagnostics(args)
|
|
131
123
|
return result.error
|
|
132
124
|
? respond({ ok: false, error: result.error })
|
|
133
125
|
: respond({ ok: true, data: result })
|
package/src/server.ts
CHANGED
|
@@ -31,7 +31,7 @@ const INSTRUCTIONS =
|
|
|
31
31
|
'MCP server for Octane Xplat apps. Call xplat_project_status first — it reports detected apps, framework versions, guidance, prerequisites, and capabilities. ' +
|
|
32
32
|
'If several apps are detected, ask the developer which to use and bind it with xplat_select_app; never guess. ' +
|
|
33
33
|
'With a dev-mode app running, xplat_dev_actions lists the app-declared action registry and xplat_dev_action_invoke runs one against an exact instance and registry revision. ' +
|
|
34
|
-
'For runtime work, check xplat_capability_matrix then xplat_targets; xplat_launch
|
|
34
|
+
'For runtime work, check xplat_capability_matrix then xplat_targets; xplat_launch returns a durable session identity and launch progress and xplat_attach binds a session you already run. ' +
|
|
35
35
|
'Results always say what was actually checked; a successful build is not proof of behavior.'
|
|
36
36
|
|
|
37
37
|
const respond: Respond = (outcome) => ({
|
|
@@ -216,10 +216,15 @@ export function createXplatMcpServer(context: WorkspaceContext) {
|
|
|
216
216
|
'xplat_sessions',
|
|
217
217
|
{
|
|
218
218
|
description:
|
|
219
|
-
'List
|
|
220
|
-
inputSchema: {},
|
|
219
|
+
'List retained sessions or read exact sessionId status, including launch progress, failures and ended sessions. Attached user-owned apps are preserved on end.',
|
|
220
|
+
inputSchema: { sessionId: z.string().optional() },
|
|
221
|
+
},
|
|
222
|
+
async ({ sessionId }) => {
|
|
223
|
+
const result = sessionId
|
|
224
|
+
? context.runtime.sessionStatus(sessionId)
|
|
225
|
+
: context.runtime.listSessions()
|
|
226
|
+
return result.error ? fail(result.error) : ok(result)
|
|
221
227
|
},
|
|
222
|
-
async () => ok({ sessions: context.sessions.list() }),
|
|
223
228
|
)
|
|
224
229
|
|
|
225
230
|
server.registerTool(
|
|
@@ -261,12 +266,12 @@ export function createXplatMcpServer(context: WorkspaceContext) {
|
|
|
261
266
|
inputSchema: { sessionId: z.string() },
|
|
262
267
|
},
|
|
263
268
|
async ({ sessionId }) => {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
return fail(
|
|
269
|
+
try {
|
|
270
|
+
const result = await context.runtime.endSession(sessionId)
|
|
271
|
+
return result.error ? fail(result.error) : ok({ result })
|
|
272
|
+
} catch (error) {
|
|
273
|
+
return fail(toContractError(error))
|
|
267
274
|
}
|
|
268
|
-
|
|
269
|
-
return ok({ result: await context.runtime.endSession(sessionId) })
|
|
270
275
|
},
|
|
271
276
|
)
|
|
272
277
|
|
|
@@ -163,13 +163,26 @@ export type DevActionInvocationStatus =
|
|
|
163
163
|
/** The app may or may not have run the action; reported, never silently retried. */
|
|
164
164
|
| 'unknown'
|
|
165
165
|
|
|
166
|
+
/** One handler's failure as structured data — which owner threw, and the error it threw. */
|
|
167
|
+
export interface DevActionFailure {
|
|
168
|
+
/** 0-based position, in attach order, of the failing handler among the action's handlers at dispatch. */
|
|
169
|
+
handler: number
|
|
170
|
+
/** Optional owner label (e.g. component name) supplied at attach time. */
|
|
171
|
+
label?: string
|
|
172
|
+
name: string
|
|
173
|
+
message: string
|
|
174
|
+
stack?: string
|
|
175
|
+
/** Stack with frames resolved to original source positions by the dev server; present only when at least one frame resolved (web today). */
|
|
176
|
+
sourceStack?: string
|
|
177
|
+
}
|
|
178
|
+
|
|
166
179
|
export interface DevActionOutcome {
|
|
167
180
|
requestId: string
|
|
168
181
|
status: DevActionInvocationStatus
|
|
169
182
|
/** Structured result validated against the declared result contract. */
|
|
170
183
|
result?: Record<string, unknown>
|
|
171
184
|
/** Machine-readable failure detail; mapped to ContractError codes server-side. */
|
|
172
|
-
error?: { code: string; message: string }
|
|
185
|
+
error?: { code: string; message: string; failures?: DevActionFailure[] }
|
|
173
186
|
/** What the app knows changed before a failure/cancellation; absent = unknown. */
|
|
174
187
|
partialEffects?: string[]
|
|
175
188
|
completedAt: string
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* preserves it). Restarting always establishes a new session identity; tools
|
|
8
8
|
* refuse operations against stopped or disconnected sessions.
|
|
9
9
|
*
|
|
10
|
-
* Jobs are bounded units of work (build,
|
|
10
|
+
* Jobs are bounded units of work (build, dev-action, …) whose status
|
|
11
11
|
* is queryable without holding a tool call open. Repeating a request with the
|
|
12
12
|
* same `requestKey` returns the live job instead of duplicating it.
|
|
13
13
|
* Cancellation is cooperative: `cancelling` and `cancelled` are distinct
|
|
@@ -25,6 +25,8 @@ export const JOB_TERMINAL_STATES: readonly JobState[]
|
|
|
25
25
|
export type SessionState =
|
|
26
26
|
/** Bound but not yet ready for observation/interaction. */
|
|
27
27
|
| 'starting'
|
|
28
|
+
/** Launch failed; retained diagnostics remain readable. */
|
|
29
|
+
| 'failed'
|
|
28
30
|
/** Ready — inspection, logs, and authorized interactions may proceed. */
|
|
29
31
|
| 'ready'
|
|
30
32
|
/** Lost contact; last-known data is historical evidence, never current state. */
|
|
@@ -57,6 +59,7 @@ export interface SessionBinding {
|
|
|
57
59
|
processKind: 'fresh' | 'warm' | null
|
|
58
60
|
buildIdentity?: BuildIdentity
|
|
59
61
|
detail?: string
|
|
62
|
+
launch?: { phase?: string; error?: ContractError; [key: string]: unknown }
|
|
60
63
|
}
|
|
61
64
|
|
|
62
65
|
export interface EndSessionResult {
|
|
@@ -78,7 +81,7 @@ export interface JobProgress {
|
|
|
78
81
|
|
|
79
82
|
export interface Job {
|
|
80
83
|
jobId: string
|
|
81
|
-
/** e.g. `build`, `
|
|
84
|
+
/** e.g. `build`, `dev-action`. */
|
|
82
85
|
kind: string
|
|
83
86
|
sessionId?: string
|
|
84
87
|
/** Idempotence key supplied by the caller or adapter. */
|
|
@@ -113,6 +116,7 @@ export interface StartJobResult {
|
|
|
113
116
|
export interface SessionRegistry {
|
|
114
117
|
/** Start a server-owned session in `starting` state. `cleanup` runs on end/shutdown and must only release resources this session created. */
|
|
115
118
|
bindOwned(input: {
|
|
119
|
+
sessionId?: string
|
|
116
120
|
appId: string
|
|
117
121
|
platform: Platform
|
|
118
122
|
targetId: string
|
|
@@ -133,6 +137,9 @@ export interface SessionRegistry {
|
|
|
133
137
|
requireUsable(
|
|
134
138
|
sessionId: string,
|
|
135
139
|
): { usable: true; session: SessionBinding } | { usable: false; error: ContractError }
|
|
140
|
+
watch(fn: (session: SessionBinding) => void): () => void
|
|
141
|
+
setLaunch(sessionId: string, progress: Record<string, unknown>): void
|
|
142
|
+
markFailed(sessionId: string, error: ContractError): void
|
|
136
143
|
markReady(
|
|
137
144
|
sessionId: string,
|
|
138
145
|
meta?: { processKind?: 'fresh' | 'warm'; buildIdentity?: BuildIdentity },
|