@inixiative/agent-session 0.6.8 → 0.7.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/package.json +6 -6
- package/src/claude-code-session.ts +56 -0
- package/src/harness-session.ts +31 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inixiative/agent-session",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Drive coding-agent CLIs as persistent, streaming, event-captured sessions over interchangeable transports, with primed decision sessions, subscription pools, routing, limit polling and continuity.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -36,18 +36,18 @@
|
|
|
36
36
|
],
|
|
37
37
|
"license": "MIT",
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@inixiative/json-rules": "^3.
|
|
39
|
+
"@inixiative/json-rules": "^3.16.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@biomejs/biome": "2.5.2",
|
|
43
|
-
"@inixiative/config": "^0.5.
|
|
44
|
-
"@types/bun": "1.4.
|
|
43
|
+
"@inixiative/config": "^0.5.166",
|
|
44
|
+
"@types/bun": "1.4.4",
|
|
45
45
|
"lefthook": "2.1.9",
|
|
46
46
|
"typescript": "6.0.3"
|
|
47
47
|
},
|
|
48
|
-
"packageManager": "bun@1.4.
|
|
48
|
+
"packageManager": "bun@1.4.4",
|
|
49
49
|
"engines": {
|
|
50
|
-
"bun": ">=1.4.
|
|
50
|
+
"bun": ">=1.4.4"
|
|
51
51
|
},
|
|
52
52
|
"repository": {
|
|
53
53
|
"type": "git",
|
|
@@ -59,6 +59,7 @@ import type {
|
|
|
59
59
|
SessionSendOptions,
|
|
60
60
|
SessionTokens,
|
|
61
61
|
SubagentLifecycle,
|
|
62
|
+
TaskLifecycle,
|
|
62
63
|
ToolDecision,
|
|
63
64
|
ToolGate,
|
|
64
65
|
ToolRequest,
|
|
@@ -385,6 +386,8 @@ export class ClaudeCodeSession implements HarnessSession {
|
|
|
385
386
|
private _seenBoundaries = new Set<string>();
|
|
386
387
|
/** Task ids whose task_started was a subagent (local_agent); later task frames carry no type. */
|
|
387
388
|
private _agentTasks = new Set<string>();
|
|
389
|
+
/** Task ids whose task_started was another task type (a background shell, a monitor): id → type. */
|
|
390
|
+
private _otherTasks = new Map<string, string>();
|
|
388
391
|
|
|
389
392
|
// -- Turn queue --
|
|
390
393
|
private _queue: QueuedTurn[] = [];
|
|
@@ -829,6 +832,11 @@ export class ClaudeCodeSession implements HarnessSession {
|
|
|
829
832
|
return turn.evidence.nativeOutcome === 'unknown' ? 'unacknowledged' : 'acknowledged';
|
|
830
833
|
}
|
|
831
834
|
|
|
835
|
+
/** Stop a background task via the CLI control protocol (`stop_task`); its end comes as lifecycle. */
|
|
836
|
+
async stopTask(taskId: string, opts?: { timeoutMs?: number }): Promise<void> {
|
|
837
|
+
await this._controlRequest({ subtype: 'stop_task', task_id: taskId }, opts?.timeoutMs ?? 5_000);
|
|
838
|
+
}
|
|
839
|
+
|
|
832
840
|
/** Account limits via the CLI control protocol (`get_usage`); no model turn. Requires a started session. */
|
|
833
841
|
async readLimits(opts?: { timeoutMs?: number }): Promise<LimitSnapshot | undefined> {
|
|
834
842
|
const response = await this._controlRequest(
|
|
@@ -1297,6 +1305,20 @@ export class ClaudeCodeSession implements HarnessSession {
|
|
|
1297
1305
|
? raw.parent_tool_use_id
|
|
1298
1306
|
: undefined;
|
|
1299
1307
|
const lifecycle = this._subagentLifecycle(raw);
|
|
1308
|
+
// A background task (a shell, a monitor) outlives its turn like a subagent: never an admission's.
|
|
1309
|
+
const task = lifecycle || parentToolUseId ? undefined : this._taskLifecycle(raw);
|
|
1310
|
+
if (task) {
|
|
1311
|
+
this._emit({
|
|
1312
|
+
kind: 'task',
|
|
1313
|
+
timestamp: Date.now(),
|
|
1314
|
+
task,
|
|
1315
|
+
raw,
|
|
1316
|
+
correlation: 'unknown',
|
|
1317
|
+
unattributedReason: 'task',
|
|
1318
|
+
nativeSessionId: typeof raw.session_id === 'string' ? raw.session_id : undefined,
|
|
1319
|
+
});
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1300
1322
|
if (parentToolUseId || lifecycle) {
|
|
1301
1323
|
const message = raw.message as Record<string, unknown> | undefined;
|
|
1302
1324
|
const events: SessionEvent[] = lifecycle
|
|
@@ -1669,6 +1691,40 @@ export class ClaudeCodeSession implements HarnessSession {
|
|
|
1669
1691
|
return claudeSubagentLifecycle(msg, phase, taskId);
|
|
1670
1692
|
}
|
|
1671
1693
|
|
|
1694
|
+
/**
|
|
1695
|
+
* A Claude Code `system` task frame for a task that is not a subagent (`task_type` other than
|
|
1696
|
+
* `local_agent`), as lifecycle. Later phases carry no task type: the session remembers it.
|
|
1697
|
+
*/
|
|
1698
|
+
private _taskLifecycle(msg: Record<string, unknown>): TaskLifecycle | undefined {
|
|
1699
|
+
if (msg.type !== 'system' || typeof msg.subtype !== 'string') return undefined;
|
|
1700
|
+
const phase = CLAUDE_TASK_PHASES[msg.subtype];
|
|
1701
|
+
const taskId = typeof msg.task_id === 'string' && msg.task_id ? msg.task_id : undefined;
|
|
1702
|
+
if (!phase || !taskId) return undefined;
|
|
1703
|
+
if (phase === 'started') {
|
|
1704
|
+
if (typeof msg.task_type !== 'string' || msg.task_type === 'local_agent') return undefined;
|
|
1705
|
+
this._otherTasks.set(taskId, msg.task_type);
|
|
1706
|
+
}
|
|
1707
|
+
const taskType = this._otherTasks.get(taskId);
|
|
1708
|
+
if (!taskType) return undefined;
|
|
1709
|
+
if (phase === 'ended') this._otherTasks.delete(taskId);
|
|
1710
|
+
const text = (value: unknown) => (typeof value === 'string' && value ? value : undefined);
|
|
1711
|
+
const patch =
|
|
1712
|
+
msg.patch && typeof msg.patch === 'object' && !Array.isArray(msg.patch)
|
|
1713
|
+
? (msg.patch as Record<string, unknown>)
|
|
1714
|
+
: undefined;
|
|
1715
|
+
const status = text(msg.status) ?? text(patch?.status);
|
|
1716
|
+
return {
|
|
1717
|
+
phase,
|
|
1718
|
+
taskId,
|
|
1719
|
+
taskType,
|
|
1720
|
+
...(text(msg.tool_use_id) ? { toolUseId: text(msg.tool_use_id) } : {}),
|
|
1721
|
+
...(text(msg.description) ? { description: text(msg.description) } : {}),
|
|
1722
|
+
...(status ? { status } : {}),
|
|
1723
|
+
...(text(msg.reason) ? { reason: text(msg.reason) } : {}),
|
|
1724
|
+
...(text(msg.summary) ? { summary: text(msg.summary) } : {}),
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1672
1728
|
private _classify(msg: Record<string, unknown>, parentToolUseId?: string): SessionEvent[] {
|
|
1673
1729
|
return this._classifyFrame(msg).map((event) =>
|
|
1674
1730
|
parentToolUseId ? { ...event, parentToolUseId } : event,
|
package/src/harness-session.ts
CHANGED
|
@@ -122,7 +122,8 @@ export type SessionEventKind =
|
|
|
122
122
|
| 'rate_limit'
|
|
123
123
|
| 'result'
|
|
124
124
|
| 'error'
|
|
125
|
-
| 'subagent'
|
|
125
|
+
| 'subagent'
|
|
126
|
+
| 'task';
|
|
126
127
|
|
|
127
128
|
/**
|
|
128
129
|
* A subagent's lifecycle as the native runtime reports it (kind `subagent`). Claude Code: its
|
|
@@ -154,6 +155,26 @@ export interface SubagentLifecycle {
|
|
|
154
155
|
};
|
|
155
156
|
}
|
|
156
157
|
|
|
158
|
+
/**
|
|
159
|
+
* A background task that is not a subagent (kind `task`): Claude Code's `system` task frames for a
|
|
160
|
+
* background shell (`local_bash`), a monitor or a workflow. Like a subagent it can outlive the turn
|
|
161
|
+
* that started it, so its events belong to no admission (`unattributedReason: "task"`).
|
|
162
|
+
*/
|
|
163
|
+
export interface TaskLifecycle {
|
|
164
|
+
readonly phase: 'started' | 'progress' | 'updated' | 'ended';
|
|
165
|
+
/** The native task id (the id the agent's own task tools name). */
|
|
166
|
+
readonly taskId: string;
|
|
167
|
+
/** The runtime's task type, e.g. `local_bash`. */
|
|
168
|
+
readonly taskType: string;
|
|
169
|
+
/** The tool call that started it, in the session's stream (its command is that call's input). */
|
|
170
|
+
readonly toolUseId?: string;
|
|
171
|
+
readonly description?: string;
|
|
172
|
+
/** The native status at the end (completed, failed, stopped, killed). */
|
|
173
|
+
readonly status?: string;
|
|
174
|
+
readonly reason?: string;
|
|
175
|
+
readonly summary?: string;
|
|
176
|
+
}
|
|
177
|
+
|
|
157
178
|
/** Input excludes cache reads/writes when those disjoint counters are supplied.
|
|
158
179
|
* Thinking is a subset of output; TTL counters are subsets of cacheWrite.
|
|
159
180
|
* Missing optional counters mean the runtime did not report them.
|
|
@@ -191,7 +212,8 @@ export interface SessionEvent extends SessionIdentity {
|
|
|
191
212
|
| 'unmatched-tool'
|
|
192
213
|
| 'malformed-tool'
|
|
193
214
|
| 'account-status'
|
|
194
|
-
| 'subagent'
|
|
215
|
+
| 'subagent'
|
|
216
|
+
| 'task';
|
|
195
217
|
/** Local transport observation, not a native terminal acknowledgment. */
|
|
196
218
|
readonly transportOutcome?: SessionAttempt['transportOutcome'];
|
|
197
219
|
readonly nativeOutcome?: NativeOutcome;
|
|
@@ -243,6 +265,8 @@ export interface SessionEvent extends SessionIdentity {
|
|
|
243
265
|
readonly parentToolUseId?: string;
|
|
244
266
|
/** A subagent's lifecycle (kind `subagent`). */
|
|
245
267
|
readonly subagent?: SubagentLifecycle;
|
|
268
|
+
/** A background task's lifecycle (kind `task`). */
|
|
269
|
+
readonly task?: TaskLifecycle;
|
|
246
270
|
}
|
|
247
271
|
|
|
248
272
|
/** Result of a single send() — the turn's content plus all classified events. */
|
|
@@ -398,6 +422,11 @@ export interface HarnessSession {
|
|
|
398
422
|
interruptNative?(opts?: { timeoutMs?: number }): Promise<NativeInterruptOutcome>;
|
|
399
423
|
/** Poll account usage limits without a model turn. Present where the transport declares `limits.poll`. */
|
|
400
424
|
readLimits?(opts?: { timeoutMs?: number }): Promise<LimitSnapshot | undefined>;
|
|
425
|
+
/**
|
|
426
|
+
* Stop one background task (a `task` lifecycle's `taskId`) through the runtime's own control
|
|
427
|
+
* channel. Its end arrives as the task's `ended` lifecycle. Claude Code: the `stop_task` request.
|
|
428
|
+
*/
|
|
429
|
+
stopTask?(taskId: string, opts?: { timeoutMs?: number }): Promise<void>;
|
|
401
430
|
/** One immutable admission snapshot, without copying unrelated session history. */
|
|
402
431
|
inspectAttempt?(admissionId: string): SessionAttempt | undefined;
|
|
403
432
|
readonly admissionProtocol?: 'prewrite-v1';
|