@canonmsg/codex-plugin 0.19.2 → 0.21.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 +2 -2
- package/dist/adapter.d.ts +0 -1
- package/dist/app-server-adapter.d.ts +0 -10
- package/dist/app-server-adapter.js +4 -53
- package/dist/app-server-approval.d.ts +1 -1
- package/dist/app-server-approval.js +1 -1
- package/dist/cli-entry.d.ts +2 -2
- package/dist/cli-entry.js +1 -1
- package/dist/control-channel.d.ts +46 -26
- package/dist/control-channel.js +50 -37
- package/dist/host-lifecycle.d.ts +4 -0
- package/dist/host-lifecycle.js +3 -0
- package/dist/host.d.ts +1 -1
- package/dist/host.js +930 -920
- package/dist/inbound-policy.d.ts +22 -0
- package/dist/inbound-policy.js +46 -0
- package/dist/register.js +25 -73
- package/dist/session-store.d.ts +1 -1
- package/dist/session-store.js +1 -1
- package/dist/startup-recovery.d.ts +46 -0
- package/dist/startup-recovery.js +59 -0
- package/dist/turn-activity.d.ts +8 -23
- package/dist/turn-activity.js +40 -24
- package/package.json +7 -10
- package/dist/bridge-bin.d.ts +0 -14
- package/dist/bridge-bin.js +0 -27
package/README.md
CHANGED
|
@@ -108,7 +108,7 @@ ps aux | rg canon-codex
|
|
|
108
108
|
If you installed the package only inside this repo and not globally, run the built host directly:
|
|
109
109
|
|
|
110
110
|
```bash
|
|
111
|
-
node
|
|
111
|
+
node packages/codex-plugin/dist/host.js --cwd /path/to/project --full-auto
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
If `canon-codex` starts but cannot find the `codex` binary, either fix your `PATH` or launch with an explicit binary path:
|
|
@@ -149,7 +149,7 @@ CANON_AGENT=frontend canon-codex --cwd ~/projects/frontend
|
|
|
149
149
|
## Development
|
|
150
150
|
|
|
151
151
|
```bash
|
|
152
|
-
cd
|
|
152
|
+
cd packages/codex-plugin
|
|
153
153
|
npm install
|
|
154
154
|
npm run build
|
|
155
155
|
```
|
package/dist/adapter.d.ts
CHANGED
|
@@ -39,7 +39,6 @@ export declare class CodexAppServerAdapter {
|
|
|
39
39
|
private skillsCache;
|
|
40
40
|
private messageTextByItem;
|
|
41
41
|
private planText;
|
|
42
|
-
private traceTurnEpochMs;
|
|
43
42
|
constructor(opts: {
|
|
44
43
|
cwd: string;
|
|
45
44
|
threadId?: string | null;
|
|
@@ -85,18 +84,9 @@ export declare class CodexAppServerAdapter {
|
|
|
85
84
|
private handleLine;
|
|
86
85
|
private handleServerRequest;
|
|
87
86
|
private handleNotification;
|
|
88
|
-
/**
|
|
89
|
-
* The turn's final message is the JOIN of every agentMessage item, in
|
|
90
|
-
* insertion order — not just the last item. The streamed turn trail folds
|
|
91
|
-
* all items (one text segment per item), so the final text must match the
|
|
92
|
-
* streamed/folded text or the app renders the turn twice.
|
|
93
|
-
*/
|
|
94
|
-
private joinedAgentMessageText;
|
|
95
87
|
private isCurrentThreadNotification;
|
|
96
88
|
private resolveCurrentTurn;
|
|
97
89
|
private clearActiveTurn;
|
|
98
90
|
private sendRequest;
|
|
99
91
|
private write;
|
|
100
|
-
private trace;
|
|
101
|
-
private traceLine;
|
|
102
92
|
}
|
|
@@ -31,7 +31,6 @@ export class CodexAppServerAdapter {
|
|
|
31
31
|
skillsCache = null;
|
|
32
32
|
messageTextByItem = new Map();
|
|
33
33
|
planText = '';
|
|
34
|
-
traceTurnEpochMs = null;
|
|
35
34
|
constructor(opts) {
|
|
36
35
|
this.cwd = opts.cwd;
|
|
37
36
|
this.threadId = opts.threadId ?? null;
|
|
@@ -118,8 +117,6 @@ export class CodexAppServerAdapter {
|
|
|
118
117
|
this.interrupted = false;
|
|
119
118
|
this.messageTextByItem.clear();
|
|
120
119
|
this.planText = '';
|
|
121
|
-
this.traceTurnEpochMs = Date.now();
|
|
122
|
-
this.trace('turn/run begin');
|
|
123
120
|
try {
|
|
124
121
|
if (this.threadId && this.loadedThreadId !== this.threadId) {
|
|
125
122
|
const resumed = await this.sendRequest('thread/resume', {
|
|
@@ -161,11 +158,9 @@ export class CodexAppServerAdapter {
|
|
|
161
158
|
this.currentTurnReject = reject;
|
|
162
159
|
});
|
|
163
160
|
turnPromise.catch(() => { });
|
|
164
|
-
const turnInput = await this.buildTurnInput(prompt, imagePaths);
|
|
165
|
-
this.trace('turn/start sent');
|
|
166
161
|
const turnStarted = await this.sendRequest('turn/start', {
|
|
167
162
|
threadId: this.threadId,
|
|
168
|
-
input:
|
|
163
|
+
input: await this.buildTurnInput(prompt, imagePaths),
|
|
169
164
|
...(this.model ? { model: this.model } : {}),
|
|
170
165
|
...this.sandboxPolicyPayload(_extraAddDirs),
|
|
171
166
|
collaborationMode: {
|
|
@@ -177,7 +172,6 @@ export class CodexAppServerAdapter {
|
|
|
177
172
|
},
|
|
178
173
|
},
|
|
179
174
|
});
|
|
180
|
-
this.trace('turn/start ack');
|
|
181
175
|
const turn = turnStarted.turn;
|
|
182
176
|
if (this.currentTurnResolve) {
|
|
183
177
|
this.currentTurnId = readString(turn, 'id') ?? null;
|
|
@@ -328,7 +322,6 @@ export class CodexAppServerAdapter {
|
|
|
328
322
|
const message = parseJson(line);
|
|
329
323
|
if (!message)
|
|
330
324
|
return;
|
|
331
|
-
this.traceLine(line, message);
|
|
332
325
|
if ('id' in message && ('result' in message || 'error' in message) && !('method' in message)) {
|
|
333
326
|
const id = Number(message.id);
|
|
334
327
|
const pending = this.pending.get(id);
|
|
@@ -400,9 +393,9 @@ export class CodexAppServerAdapter {
|
|
|
400
393
|
const delta = readRawString(params, 'delta') ?? '';
|
|
401
394
|
const next = `${this.messageTextByItem.get(itemId) ?? ''}${delta}`;
|
|
402
395
|
this.messageTextByItem.set(itemId, next);
|
|
403
|
-
this.currentFinalMessage =
|
|
396
|
+
this.currentFinalMessage = next.trim() ? next : this.currentFinalMessage;
|
|
404
397
|
if (next.trim())
|
|
405
|
-
this.currentOnEvent?.({ type: 'message', text: next,
|
|
398
|
+
this.currentOnEvent?.({ type: 'message', text: next, itemId });
|
|
406
399
|
return;
|
|
407
400
|
}
|
|
408
401
|
if (method === 'turn/plan/updated') {
|
|
@@ -439,8 +432,7 @@ export class CodexAppServerAdapter {
|
|
|
439
432
|
const itemId = readString(item, 'id');
|
|
440
433
|
const text = readString(item, 'text');
|
|
441
434
|
if (text) {
|
|
442
|
-
this.
|
|
443
|
-
this.currentFinalMessage = this.joinedAgentMessageText() ?? text;
|
|
435
|
+
this.currentFinalMessage = text;
|
|
444
436
|
this.currentOnEvent?.({
|
|
445
437
|
type: 'message',
|
|
446
438
|
text,
|
|
@@ -474,7 +466,6 @@ export class CodexAppServerAdapter {
|
|
|
474
466
|
return;
|
|
475
467
|
}
|
|
476
468
|
if (method === 'turn/completed') {
|
|
477
|
-
this.trace('turn/completed notification');
|
|
478
469
|
const turn = params.turn;
|
|
479
470
|
const status = turn?.status;
|
|
480
471
|
if (isRecord(status) && status.type === 'failed') {
|
|
@@ -495,18 +486,6 @@ export class CodexAppServerAdapter {
|
|
|
495
486
|
this.currentErrorText = stringifyPreview(params);
|
|
496
487
|
}
|
|
497
488
|
}
|
|
498
|
-
/**
|
|
499
|
-
* The turn's final message is the JOIN of every agentMessage item, in
|
|
500
|
-
* insertion order — not just the last item. The streamed turn trail folds
|
|
501
|
-
* all items (one text segment per item), so the final text must match the
|
|
502
|
-
* streamed/folded text or the app renders the turn twice.
|
|
503
|
-
*/
|
|
504
|
-
joinedAgentMessageText() {
|
|
505
|
-
const joined = [...this.messageTextByItem.values()]
|
|
506
|
-
.filter((text) => text.trim())
|
|
507
|
-
.join('\n\n');
|
|
508
|
-
return joined.trim() ? joined : null;
|
|
509
|
-
}
|
|
510
489
|
isCurrentThreadNotification(params) {
|
|
511
490
|
const threadId = readNotificationThreadId(params);
|
|
512
491
|
if (threadId && this.threadId && threadId !== this.threadId)
|
|
@@ -538,7 +517,6 @@ export class CodexAppServerAdapter {
|
|
|
538
517
|
this.currentErrorText = null;
|
|
539
518
|
this.messageTextByItem.clear();
|
|
540
519
|
this.planText = '';
|
|
541
|
-
this.traceTurnEpochMs = null;
|
|
542
520
|
}
|
|
543
521
|
sendRequest(method, params) {
|
|
544
522
|
const id = this.requestSeq++;
|
|
@@ -558,33 +536,6 @@ export class CodexAppServerAdapter {
|
|
|
558
536
|
throw new Error('Codex app-server is not running');
|
|
559
537
|
this.child.stdin.write(`${JSON.stringify(message)}\n`);
|
|
560
538
|
}
|
|
561
|
-
trace(message) {
|
|
562
|
-
if (!isCodexTraceEnabled())
|
|
563
|
-
return;
|
|
564
|
-
const elapsedMs = this.traceTurnEpochMs === null ? 0 : Date.now() - this.traceTurnEpochMs;
|
|
565
|
-
console.error(`[canon-codex-trace] +${elapsedMs}ms ${message}`);
|
|
566
|
-
}
|
|
567
|
-
traceLine(line, message) {
|
|
568
|
-
if (!isCodexTraceEnabled())
|
|
569
|
-
return;
|
|
570
|
-
const method = typeof message.method === 'string' ? message.method : null;
|
|
571
|
-
if (!method) {
|
|
572
|
-
const id = 'id' in message ? ` id=${String(message.id)}` : '';
|
|
573
|
-
this.trace(`line bytes=${line.length} response${id}`);
|
|
574
|
-
return;
|
|
575
|
-
}
|
|
576
|
-
if (method === 'item/agentMessage/delta') {
|
|
577
|
-
const params = isRecord(message.params) ? message.params : {};
|
|
578
|
-
const itemId = readString(params, 'itemId') ?? 'agent-message';
|
|
579
|
-
const delta = readRawString(params, 'delta') ?? '';
|
|
580
|
-
this.trace(`line bytes=${line.length} method=${method} itemId=${itemId} deltaLen=${delta.length}`);
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
|
-
this.trace(`line bytes=${line.length} method=${method}`);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
function isCodexTraceEnabled() {
|
|
587
|
-
return process.env.CANON_CODEX_TRACE_EVENTS === '1';
|
|
588
539
|
}
|
|
589
540
|
function parseJson(line) {
|
|
590
541
|
try {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalResult, ApprovalRisk, CanonUnifiedDiff } from '@canonmsg/core
|
|
1
|
+
import type { ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalResult, ApprovalRisk, CanonUnifiedDiff } from '@canonmsg/core';
|
|
2
2
|
/**
|
|
3
3
|
* Mapping for Codex app-server native approval requests.
|
|
4
4
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { sanitizeUnifiedDiffForSharing, truncateUnifiedDiff } from '@canonmsg/core
|
|
1
|
+
import { sanitizeUnifiedDiffForSharing, truncateUnifiedDiff } from '@canonmsg/core';
|
|
2
2
|
function isRecord(value) {
|
|
3
3
|
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
4
4
|
}
|
package/dist/cli-entry.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { handleCliMetadataRequest, isDirectExecution, readCliPackageVersion, runCli, } from '@canonmsg/core
|
|
2
|
-
export type { CliMetadata, RunCliMetadataOptions, } from '@canonmsg/core
|
|
1
|
+
export { handleCliMetadataRequest, isDirectExecution, readCliPackageVersion, runCli, } from '@canonmsg/core';
|
|
2
|
+
export type { CliMetadata, RunCliMetadataOptions, } from '@canonmsg/core';
|
package/dist/cli-entry.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { handleCliMetadataRequest, isDirectExecution, readCliPackageVersion, runCli, } from '@canonmsg/core
|
|
1
|
+
export { handleCliMetadataRequest, isDirectExecution, readCliPackageVersion, runCli, } from '@canonmsg/core';
|
|
@@ -1,32 +1,52 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Codex host control
|
|
2
|
+
* Codex host RTDB `/control/{conversationId}/{agentId}` channel wiring.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* and pushes `onSessionControl` / `onControlSignal` / `onControlPrimitive`
|
|
8
|
-
* notifications instead. This module keeps the codex-shaped handler wiring:
|
|
9
|
-
* session + signal ride the shared agent-host router; the primitive channel
|
|
10
|
-
* (context.compact) subscribes alongside them.
|
|
4
|
+
* Configures the shared `ControlChannelPoller` from `@canonmsg/core` to the
|
|
5
|
+
* codex host characterization profile (the "codex host profile" describe
|
|
6
|
+
* block in core's control-poller.test.ts is the contract):
|
|
11
7
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
8
|
+
* - Keys: `session` + `signal`, plus `primitive` when a primitive handler is
|
|
9
|
+
* configured; read sequentially per conversation, conversations polled one
|
|
10
|
+
* at a time.
|
|
11
|
+
* - Cadence: immediate first cycle, then active/idle delays + jitter with
|
|
12
|
+
* the activity probe sampled BEFORE the cycle runs.
|
|
13
|
+
* - Session controls are always consumed once newer (default consume), even
|
|
14
|
+
* when no live session applied them; signals are NOT consumed when the
|
|
15
|
+
* handler throws (consumeOnError stays false).
|
|
16
|
+
* - Dedupe is primed eagerly via `poller.baseline([conversationId])` at
|
|
17
|
+
* session creation — the second legacy poll site.
|
|
16
18
|
*/
|
|
17
|
-
import type
|
|
18
|
-
|
|
19
|
+
import { ControlChannelPoller, type ControlChannelRTDB, type ControlHandlerResult, type ControlPollerError, type ControlPrimitiveEvent, type ControlSessionEvent, type ControlSignalEvent } from '@canonmsg/core';
|
|
20
|
+
export declare const CONTROL_POLL_MS = 2000;
|
|
21
|
+
export declare const IDLE_CONTROL_POLL_MS = 10000;
|
|
22
|
+
export declare const CONTROL_POLL_JITTER_MS = 1000;
|
|
19
23
|
export interface CodexControlChannelInput {
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
/** Scoped RTDB handle from `initRTDBAuth` — never the module-global default. */
|
|
25
|
+
rtdb: ControlChannelRTDB;
|
|
26
|
+
agentId: string;
|
|
27
|
+
/** Live session conversation ids, snapshotted at the start of every cycle. */
|
|
28
|
+
conversationIds: () => Iterable<string>;
|
|
29
|
+
/** Sampled before each cycle to choose the active vs idle delay. */
|
|
30
|
+
hasActiveWork: () => boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Applies a newer `/session` control node. The node is always consumed
|
|
33
|
+
* after the handler resolves; a thrown error leaves it in place (with
|
|
34
|
+
* dedupe already advanced, so it is never retried).
|
|
35
|
+
*/
|
|
36
|
+
onSessionControl: (event: ControlSessionEvent) => Promise<ControlHandlerResult> | ControlHandlerResult;
|
|
37
|
+
/**
|
|
38
|
+
* Handles a newer `/signal` node. Return `{ consume: false }` to leave the
|
|
39
|
+
* node in place; thrown errors also leave it (consumeOnError stays false).
|
|
40
|
+
*/
|
|
41
|
+
onSignal: (event: ControlSignalEvent) => Promise<ControlHandlerResult> | ControlHandlerResult;
|
|
42
|
+
/**
|
|
43
|
+
* Handles a newer `/primitive` node. Primitive commands are consumed by
|
|
44
|
+
* default after the handler resolves or throws, matching core's command
|
|
45
|
+
* semantics so one failed command cannot replay forever.
|
|
46
|
+
*/
|
|
47
|
+
onPrimitive?: (event: ControlPrimitiveEvent) => Promise<ControlHandlerResult> | ControlHandlerResult;
|
|
48
|
+
onError?: (error: ControlPollerError) => void;
|
|
49
|
+
/** Injectable jitter source (tests). */
|
|
50
|
+
random?: () => number;
|
|
30
51
|
}
|
|
31
|
-
|
|
32
|
-
export declare function attachCodexControlNotifications(input: CodexControlChannelInput): () => void;
|
|
52
|
+
export declare function createCodexControlPoller(input: CodexControlChannelInput): ControlChannelPoller;
|
package/dist/control-channel.js
CHANGED
|
@@ -1,43 +1,56 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Codex host control
|
|
2
|
+
* Codex host RTDB `/control/{conversationId}/{agentId}` channel wiring.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* and pushes `onSessionControl` / `onControlSignal` / `onControlPrimitive`
|
|
8
|
-
* notifications instead. This module keeps the codex-shaped handler wiring:
|
|
9
|
-
* session + signal ride the shared agent-host router; the primitive channel
|
|
10
|
-
* (context.compact) subscribes alongside them.
|
|
4
|
+
* Configures the shared `ControlChannelPoller` from `@canonmsg/core` to the
|
|
5
|
+
* codex host characterization profile (the "codex host profile" describe
|
|
6
|
+
* block in core's control-poller.test.ts is the contract):
|
|
11
7
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
8
|
+
* - Keys: `session` + `signal`, plus `primitive` when a primitive handler is
|
|
9
|
+
* configured; read sequentially per conversation, conversations polled one
|
|
10
|
+
* at a time.
|
|
11
|
+
* - Cadence: immediate first cycle, then active/idle delays + jitter with
|
|
12
|
+
* the activity probe sampled BEFORE the cycle runs.
|
|
13
|
+
* - Session controls are always consumed once newer (default consume), even
|
|
14
|
+
* when no live session applied them; signals are NOT consumed when the
|
|
15
|
+
* handler throws (consumeOnError stays false).
|
|
16
|
+
* - Dedupe is primed eagerly via `poller.baseline([conversationId])` at
|
|
17
|
+
* session creation — the second legacy poll site.
|
|
16
18
|
*/
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
export
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
import { ControlChannelPoller, } from '@canonmsg/core';
|
|
20
|
+
export const CONTROL_POLL_MS = 2_000;
|
|
21
|
+
export const IDLE_CONTROL_POLL_MS = 10_000;
|
|
22
|
+
export const CONTROL_POLL_JITTER_MS = 1_000;
|
|
23
|
+
export function createCodexControlPoller(input) {
|
|
24
|
+
return new ControlChannelPoller({
|
|
25
|
+
rtdb: input.rtdb,
|
|
26
|
+
agentId: input.agentId,
|
|
27
|
+
conversationIds: input.conversationIds,
|
|
28
|
+
cadence: {
|
|
29
|
+
kind: 'adaptive',
|
|
30
|
+
activeMs: CONTROL_POLL_MS,
|
|
31
|
+
idleMs: IDLE_CONTROL_POLL_MS,
|
|
32
|
+
jitterMs: CONTROL_POLL_JITTER_MS,
|
|
33
|
+
hasActiveWork: input.hasActiveWork,
|
|
34
|
+
activitySample: 'cycle-start',
|
|
35
|
+
},
|
|
36
|
+
pollOnStart: true,
|
|
37
|
+
conversationConcurrency: 'sequential',
|
|
38
|
+
handlers: {
|
|
39
|
+
session: {
|
|
40
|
+
handle: input.onSessionControl,
|
|
41
|
+
},
|
|
42
|
+
signal: {
|
|
43
|
+
handle: input.onSignal,
|
|
44
|
+
},
|
|
45
|
+
...(input.onPrimitive
|
|
46
|
+
? {
|
|
47
|
+
primitive: {
|
|
48
|
+
handle: input.onPrimitive,
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
: {}),
|
|
52
|
+
},
|
|
53
|
+
...(input.onError ? { onError: input.onError } : {}),
|
|
54
|
+
...(input.random ? { random: input.random } : {}),
|
|
23
55
|
});
|
|
24
|
-
const detachShared = attachHostControlNotifications(input.source, { logPrefix: '[canon-codex]' }, {
|
|
25
|
-
onSessionControl: input.onSessionControl,
|
|
26
|
-
onControlSignal: input.onSignal,
|
|
27
|
-
onError: (error, kind, conversationId) => report(error, kind, conversationId),
|
|
28
|
-
});
|
|
29
|
-
let detachPrimitive = null;
|
|
30
|
-
if (input.onPrimitive) {
|
|
31
|
-
const onPrimitive = input.onPrimitive;
|
|
32
|
-
detachPrimitive = input.source.onNotification('onControlPrimitive', (params) => {
|
|
33
|
-
const event = params;
|
|
34
|
-
void Promise.resolve(onPrimitive(event)).catch((error) => {
|
|
35
|
-
report(error, 'primitive', event?.conversationId ?? null);
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
return () => {
|
|
40
|
-
detachShared();
|
|
41
|
-
detachPrimitive?.();
|
|
42
|
-
};
|
|
43
56
|
}
|
package/dist/host.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { type CanonRuntimeCommandDescriptor, type CanonRuntimeDescriptor, type CanonRuntimePresentationPolicy, type ExecutionEnvironmentMode, type WorkspaceOption, type CanonWorkspaceRootMetadata } from '@canonmsg/core
|
|
2
|
+
import { type CanonRuntimeCommandDescriptor, type CanonRuntimeDescriptor, type CanonRuntimePresentationPolicy, type ExecutionEnvironmentMode, type WorkspaceOption, type CanonWorkspaceRootMetadata } from '@canonmsg/core';
|
|
3
3
|
import { type CodexSkillMetadata } from './app-server-adapter.js';
|
|
4
4
|
interface HostSessionState {
|
|
5
5
|
lastError?: string;
|