@inixiative/agent-session 0.6.5 → 0.6.7
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 +45 -0
- package/package.json +2 -2
- package/src/claude-code-session.ts +211 -4
- package/src/claude-in-session.ts +1369 -0
- package/src/codex-session.ts +17 -3
- package/src/harness-session.ts +37 -0
- package/src/index.ts +10 -0
- package/src/transport.ts +9 -0
package/src/codex-session.ts
CHANGED
|
@@ -23,9 +23,11 @@ import type {
|
|
|
23
23
|
SessionArtifact,
|
|
24
24
|
SessionEvent,
|
|
25
25
|
SessionEventHandler,
|
|
26
|
+
SessionImage,
|
|
26
27
|
SessionResult,
|
|
27
28
|
ToolGate,
|
|
28
29
|
} from './harness-session';
|
|
30
|
+
import { sendableImages } from './harness-session';
|
|
29
31
|
|
|
30
32
|
// Re-export types so importers can stay on one path (mirrors claude-code-session).
|
|
31
33
|
export type {
|
|
@@ -153,6 +155,7 @@ interface QueuedTurn {
|
|
|
153
155
|
onAdmission?: import('./harness-session').SessionSendOptions['onAdmission'];
|
|
154
156
|
evidence: TurnState;
|
|
155
157
|
message: string;
|
|
158
|
+
images: readonly SessionImage[];
|
|
156
159
|
timeout: number;
|
|
157
160
|
resolve: (result: SessionResult) => void;
|
|
158
161
|
reject: (error: Error) => void;
|
|
@@ -455,6 +458,7 @@ export class CodexAppServerSession implements HarnessSession {
|
|
|
455
458
|
if (!this._alive) throw new Error('Session ended');
|
|
456
459
|
|
|
457
460
|
const timeout = opts?.timeout ?? this._defaultTimeout;
|
|
461
|
+
const images = sendableImages(this.transport, opts?.images);
|
|
458
462
|
|
|
459
463
|
let transformed = message;
|
|
460
464
|
for (const hook of this._beforeSendHooks) {
|
|
@@ -473,6 +477,7 @@ export class CodexAppServerSession implements HarnessSession {
|
|
|
473
477
|
const turn: QueuedTurn = {
|
|
474
478
|
evidence,
|
|
475
479
|
message: transformed,
|
|
480
|
+
images,
|
|
476
481
|
timeout,
|
|
477
482
|
resolve,
|
|
478
483
|
reject,
|
|
@@ -539,7 +544,7 @@ export class CodexAppServerSession implements HarnessSession {
|
|
|
539
544
|
this._resultText = '';
|
|
540
545
|
|
|
541
546
|
// Resolves once the native terminal and the turn/start RPC have both settled.
|
|
542
|
-
this._sendTurn(turn.message)
|
|
547
|
+
this._sendTurn(turn.message, turn.images)
|
|
543
548
|
.then((tokens) => {
|
|
544
549
|
if (this._inflight !== turn) return;
|
|
545
550
|
turn.evidence.rpcSettled = true;
|
|
@@ -1209,7 +1214,10 @@ export class CodexAppServerSession implements HarnessSession {
|
|
|
1209
1214
|
this._rpcNotify('initialized');
|
|
1210
1215
|
}
|
|
1211
1216
|
|
|
1212
|
-
private async _sendTurn(
|
|
1217
|
+
private async _sendTurn(
|
|
1218
|
+
message: string,
|
|
1219
|
+
images: readonly SessionImage[],
|
|
1220
|
+
): Promise<{ input: number; output: number } | undefined> {
|
|
1213
1221
|
const a = this._inflight!.evidence;
|
|
1214
1222
|
if (!this._threadReady) {
|
|
1215
1223
|
const binding = this._externalSessionId;
|
|
@@ -1344,7 +1352,13 @@ export class CodexAppServerSession implements HarnessSession {
|
|
|
1344
1352
|
});
|
|
1345
1353
|
await this._rpcRequest('turn/start', {
|
|
1346
1354
|
threadId: this._externalSessionId,
|
|
1347
|
-
input: [
|
|
1355
|
+
input: [
|
|
1356
|
+
{ type: 'text', text: message },
|
|
1357
|
+
...images.map((image) => ({
|
|
1358
|
+
type: 'image',
|
|
1359
|
+
url: `data:${image.mediaType};base64,${image.data}`,
|
|
1360
|
+
})),
|
|
1361
|
+
],
|
|
1348
1362
|
...(this._effort ? { effort: this._effort } : {}),
|
|
1349
1363
|
});
|
|
1350
1364
|
await completed; // Native terminal and RPC acknowledgment are both required.
|
package/src/harness-session.ts
CHANGED
|
@@ -266,10 +266,47 @@ export type SessionEventHandler = (event: SessionEvent) => void;
|
|
|
266
266
|
*/
|
|
267
267
|
export type BeforeSendHook = (message: string) => string | Promise<string>;
|
|
268
268
|
|
|
269
|
+
export const SESSION_IMAGE_MEDIA_TYPES = [
|
|
270
|
+
'image/png',
|
|
271
|
+
'image/jpeg',
|
|
272
|
+
'image/gif',
|
|
273
|
+
'image/webp',
|
|
274
|
+
] as const;
|
|
275
|
+
export type SessionImageMediaType = (typeof SESSION_IMAGE_MEDIA_TYPES)[number];
|
|
276
|
+
|
|
277
|
+
/** An image sent with a turn's text, where the transport declares `capabilities.images`. */
|
|
278
|
+
export interface SessionImage {
|
|
279
|
+
readonly mediaType: SessionImageMediaType;
|
|
280
|
+
/** The image bytes, base64 (no `data:` prefix). */
|
|
281
|
+
readonly data: string;
|
|
282
|
+
}
|
|
283
|
+
|
|
269
284
|
export interface SessionSendOptions {
|
|
270
285
|
timeout?: number;
|
|
271
286
|
/** Required ownership registration, not an observer. Rejection prevents the native write. */
|
|
272
287
|
onAdmission?: (attempt: SessionAttempt) => void | Promise<void>;
|
|
288
|
+
/**
|
|
289
|
+
* Images sent with the message, in order, as part of the same user turn. Refused (before
|
|
290
|
+
* anything is written) by a transport without `capabilities.images` and for any other media type.
|
|
291
|
+
*/
|
|
292
|
+
images?: readonly SessionImage[];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** Images a send may write: a copy of each, or a refusal naming the first one that is not. */
|
|
296
|
+
export function sendableImages(
|
|
297
|
+
transport: TransportDescriptor | undefined,
|
|
298
|
+
images: readonly SessionImage[] | undefined,
|
|
299
|
+
): SessionImage[] {
|
|
300
|
+
if (!images?.length) return [];
|
|
301
|
+
if (!transport?.capabilities.images)
|
|
302
|
+
throw new Error(`Transport "${transport?.kind ?? 'unknown'}" does not accept image input`);
|
|
303
|
+
return images.map((image, index) => {
|
|
304
|
+
if (!SESSION_IMAGE_MEDIA_TYPES.includes(image?.mediaType))
|
|
305
|
+
throw new Error(`Image ${index + 1}: unsupported media type ${String(image?.mediaType)}`);
|
|
306
|
+
if (typeof image.data !== 'string' || !image.data || /[^A-Za-z0-9+/=]/.test(image.data))
|
|
307
|
+
throw new Error(`Image ${index + 1}: data must be non-empty base64`);
|
|
308
|
+
return { mediaType: image.mediaType, data: image.data };
|
|
309
|
+
});
|
|
273
310
|
}
|
|
274
311
|
|
|
275
312
|
// ---------------------------------------------------------------------------
|
package/src/index.ts
CHANGED
|
@@ -13,9 +13,19 @@ export {
|
|
|
13
13
|
// Claude Code adapter (persistent stream-json session, subscription auth).
|
|
14
14
|
export {
|
|
15
15
|
CLAUDE_TEXT_ONLY_ARGS,
|
|
16
|
+
type ClaudeAgentDefinition,
|
|
16
17
|
ClaudeCodeSession,
|
|
17
18
|
type ClaudeCodeSessionConfig,
|
|
19
|
+
type ClaudeSessionExtension,
|
|
20
|
+
type ClaudeSessionHost,
|
|
18
21
|
} from './claude-code-session';
|
|
22
|
+
export {
|
|
23
|
+
type ClaudeInSessionConfig,
|
|
24
|
+
ClaudeInSessionDecisions,
|
|
25
|
+
IN_SESSION_AGENTS,
|
|
26
|
+
IN_SESSION_SERVER,
|
|
27
|
+
type InSessionSetup,
|
|
28
|
+
} from './claude-in-session';
|
|
19
29
|
export { type ClaudePrimedConfig, ClaudePrimedSessions } from './claude-primed';
|
|
20
30
|
export { type ClaudeSubagentConfig, ClaudeSubagentSessions } from './claude-subagents';
|
|
21
31
|
export { parseClaudeUsage } from './claude-usage';
|
package/src/transport.ts
CHANGED
|
@@ -36,6 +36,8 @@ export interface TransportCapabilities {
|
|
|
36
36
|
readonly limits: { readonly stream: boolean; readonly poll: boolean };
|
|
37
37
|
/** A tool-free, text-only session can be enforced (decisions). */
|
|
38
38
|
readonly textOnly: boolean;
|
|
39
|
+
/** send() takes images beside the text (`SessionSendOptions.images`): png, jpeg, gif, webp. */
|
|
40
|
+
readonly images: boolean;
|
|
39
41
|
/** Primed decision host on this transport and how it returns to its primed state each cycle (see primed.ts). */
|
|
40
42
|
readonly primed: 'fork' | 'none';
|
|
41
43
|
readonly billing: 'subscription' | 'api';
|
|
@@ -70,6 +72,7 @@ export const TRANSPORTS: Readonly<Record<TransportKind, TransportDescriptor>> =
|
|
|
70
72
|
usage: true,
|
|
71
73
|
limits: { stream: true, poll: true },
|
|
72
74
|
textOnly: true,
|
|
75
|
+
images: true,
|
|
73
76
|
primed: 'fork',
|
|
74
77
|
billing: 'subscription',
|
|
75
78
|
},
|
|
@@ -80,6 +83,7 @@ export const TRANSPORTS: Readonly<Record<TransportKind, TransportDescriptor>> =
|
|
|
80
83
|
"interruptNative() and readLimits() use the CLI's stdio control protocol (`interrupt`, `get_usage`).",
|
|
81
84
|
'toolGate: true launches with `--permission-prompt-tool stdio` and registers a PreToolUse callback over the control protocol: onToolRequest() gates see every call (`pre-use`, any mode, bypass included; only a deny counts) and the asks the permission mode raises (`permission`, final).',
|
|
82
85
|
'Rollback (`--resume-session-at`) exists natively but is not wired here.',
|
|
86
|
+
'Images ride in the same stream-json user message as base64 `image` content blocks, before the text.',
|
|
83
87
|
],
|
|
84
88
|
},
|
|
85
89
|
'codex-app-server': {
|
|
@@ -97,6 +101,7 @@ export const TRANSPORTS: Readonly<Record<TransportKind, TransportDescriptor>> =
|
|
|
97
101
|
usage: true,
|
|
98
102
|
limits: { stream: true, poll: true },
|
|
99
103
|
textOnly: false,
|
|
104
|
+
images: true,
|
|
100
105
|
primed: 'fork',
|
|
101
106
|
billing: 'subscription',
|
|
102
107
|
},
|
|
@@ -107,6 +112,7 @@ export const TRANSPORTS: Readonly<Record<TransportKind, TransportDescriptor>> =
|
|
|
107
112
|
'appServer.approvalPolicy/sandbox (default never / danger-full-access; `codexPermissions(mode)`) set the thread policy; command and file-change approvals for the owned turn go to onToolRequest() gates (`permission`) and are answered accept/decline. Other server requests are refused (-32601).',
|
|
108
113
|
'Primed decisions (CodexPrimedSessions) host every key as a thread of one process per account and fork the primed thread per cycle.',
|
|
109
114
|
'thread/rollback is refused by codex-cli 0.155 for both ephemeral and paginated threads; session fork() is not wired.',
|
|
115
|
+
'Images go to turn/start as `image` inputs with a `data:` URL, after the text.',
|
|
110
116
|
],
|
|
111
117
|
},
|
|
112
118
|
'claude-agent-sdk': {
|
|
@@ -124,6 +130,7 @@ export const TRANSPORTS: Readonly<Record<TransportKind, TransportDescriptor>> =
|
|
|
124
130
|
usage: true,
|
|
125
131
|
limits: { stream: true, poll: false },
|
|
126
132
|
textOnly: true,
|
|
133
|
+
images: true,
|
|
127
134
|
primed: 'none',
|
|
128
135
|
billing: 'subscription',
|
|
129
136
|
},
|
|
@@ -147,6 +154,7 @@ export const TRANSPORTS: Readonly<Record<TransportKind, TransportDescriptor>> =
|
|
|
147
154
|
usage: false,
|
|
148
155
|
limits: none,
|
|
149
156
|
textOnly: false,
|
|
157
|
+
images: false,
|
|
150
158
|
primed: 'none',
|
|
151
159
|
billing: 'subscription',
|
|
152
160
|
},
|
|
@@ -169,6 +177,7 @@ export const TRANSPORTS: Readonly<Record<TransportKind, TransportDescriptor>> =
|
|
|
169
177
|
usage: false,
|
|
170
178
|
limits: none,
|
|
171
179
|
textOnly: false,
|
|
180
|
+
images: false,
|
|
172
181
|
primed: 'none',
|
|
173
182
|
billing: 'api',
|
|
174
183
|
},
|