@lucascouts/claude-agent-acp-plus 0.23.6 → 0.25.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 +54 -0
- package/dist/acp-agent.d.ts +85 -2
- package/dist/acp-agent.d.ts.map +1 -1
- package/dist/acp-agent.js +286 -31
- package/dist/classifier-escalation.d.ts +158 -0
- package/dist/classifier-escalation.d.ts.map +1 -0
- package/dist/classifier-escalation.js +562 -0
- package/dist/permissions/options/shared.d.ts +8 -0
- package/dist/permissions/options/shared.d.ts.map +1 -1
- package/dist/permissions/options/shared.js +18 -0
- package/dist/permissions/presentation.d.ts +1 -0
- package/dist/permissions/presentation.d.ts.map +1 -1
- package/dist/permissions/presentation.js +1 -1
- package/dist/permissions/response.d.ts +6 -0
- package/dist/permissions/response.d.ts.map +1 -1
- package/dist/permissions/response.js +19 -0
- package/dist/task-feed.d.ts +195 -0
- package/dist/task-feed.d.ts.map +1 -0
- package/dist/task-feed.js +530 -0
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { applyClaudePermissionSelection, parseClaudePermissionSelection } from "./effects.js";
|
|
2
|
+
import { PERMISSION_OPTION_ID } from "./options/shared.js";
|
|
2
3
|
/** Decode, validate, and interpret an ACP response exactly once. */
|
|
3
4
|
export function decodeClaudePermissionResponse(response, toolName, input, toolUseID, offeredOptions, durableChangeSet) {
|
|
4
5
|
const selection = parseClaudePermissionSelection(response, toolName);
|
|
@@ -17,3 +18,21 @@ export function decodeClaudePermissionResponse(response, toolName, input, toolUs
|
|
|
17
18
|
...(selection.contextResetMode ? { contextResetMode: selection.contextResetMode } : {}),
|
|
18
19
|
};
|
|
19
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Map the answer to a classifier-denial request. Only the three ids that
|
|
23
|
+
* request offers are accepted; anything else is an error, never an approval.
|
|
24
|
+
*/
|
|
25
|
+
export function decodeClassifierEscalationResponse(response) {
|
|
26
|
+
if (response.outcome.outcome === "cancelled")
|
|
27
|
+
return "cancelled";
|
|
28
|
+
switch (response.outcome.optionId) {
|
|
29
|
+
case PERMISSION_OPTION_ID.classifierAllowOnce:
|
|
30
|
+
return "once";
|
|
31
|
+
case PERMISSION_OPTION_ID.classifierAllowSession:
|
|
32
|
+
return "session";
|
|
33
|
+
case PERMISSION_OPTION_ID.reject:
|
|
34
|
+
return "reject";
|
|
35
|
+
default:
|
|
36
|
+
throw new Error(`Classifier escalation option was not offered: ${response.outcome.optionId}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent's task feed: background shells, subagents, monitors and workflows,
|
|
3
|
+
* folded from the SDK's task messages into the `_claude/tasks` snapshot.
|
|
4
|
+
*
|
|
5
|
+
* The SDK reports tasks as a stream of bookends (`task_started`,
|
|
6
|
+
* `task_progress`, `task_updated`, `task_notification`) plus a level
|
|
7
|
+
* (`background_tasks_changed`). Zeo needs one table it can render, so
|
|
8
|
+
* `TaskFeed` keeps that table per session and renders it as a snapshot. The
|
|
9
|
+
* snapshot is a LEVEL, not a stream: every publish carries every live task,
|
|
10
|
+
* so a lost update is repaired by the next one, and the client replaces its
|
|
11
|
+
* list instead of merging.
|
|
12
|
+
*
|
|
13
|
+
* An ended task appears in exactly one successful snapshot with its final
|
|
14
|
+
* status, then drops out. `snapshot()` marks nothing; the publisher calls
|
|
15
|
+
* `markPublished()` only after the send carrying the endings succeeded, so a
|
|
16
|
+
* failed send never loses an ending.
|
|
17
|
+
*
|
|
18
|
+
* `TaskFeedPublisher` decides WHEN to publish: at most once per second during
|
|
19
|
+
* progress, at once on an ending, and once more after a burst so the latest
|
|
20
|
+
* values are not lost.
|
|
21
|
+
*
|
|
22
|
+
* A foreground shell task (`local_bash`, not backgrounded) is held back for
|
|
23
|
+
* `FOREGROUND_SHELL_HOLD_MS`: Claude Code registers every shell command as a
|
|
24
|
+
* task, so only one still running after the hold, or moved to the background,
|
|
25
|
+
* is published. One that ends while held is dropped without a trace. The feed
|
|
26
|
+
* stays clock-driven: the publisher's timer calls `promoteDue()`.
|
|
27
|
+
*
|
|
28
|
+
* Neither class ever copies a command line (`SDKTaskStartedMessage.prompt`)
|
|
29
|
+
* into an entry or a log line.
|
|
30
|
+
*/
|
|
31
|
+
import type { SDKTaskNotificationMessage, SDKTaskProgressMessage, SDKTaskStartedMessage, SDKTaskUpdatedMessage } from "@anthropic-ai/claude-agent-sdk";
|
|
32
|
+
/** The `session_info_update._meta` key the snapshot is published under. */
|
|
33
|
+
export declare const TASKS_META_KEY = "_claude/tasks";
|
|
34
|
+
export type TaskStatus = "running" | "completed" | "failed" | "stopped" | "interrupted";
|
|
35
|
+
export type TaskUsage = {
|
|
36
|
+
tokens: number;
|
|
37
|
+
toolUses: number;
|
|
38
|
+
durationMs: number;
|
|
39
|
+
};
|
|
40
|
+
/** One row of the snapshot — design.md's Data Models, field for field. */
|
|
41
|
+
export type TaskEntry = {
|
|
42
|
+
id: string;
|
|
43
|
+
toolCallId: string | null;
|
|
44
|
+
type: string;
|
|
45
|
+
description: string;
|
|
46
|
+
background: boolean;
|
|
47
|
+
depth: number;
|
|
48
|
+
status: TaskStatus;
|
|
49
|
+
startedAt: number;
|
|
50
|
+
endedAt: number | null;
|
|
51
|
+
usage: TaskUsage | null;
|
|
52
|
+
lastTool: string | null;
|
|
53
|
+
summary: string | null;
|
|
54
|
+
};
|
|
55
|
+
export type TasksSnapshot = {
|
|
56
|
+
tasks: TaskEntry[];
|
|
57
|
+
};
|
|
58
|
+
/** What a message did to the table; the publisher decides timing from it. */
|
|
59
|
+
export type TaskChange = "none" | "progress" | "terminal" | "held";
|
|
60
|
+
/** How long a foreground shell task is held back before it is published. */
|
|
61
|
+
export declare const FOREGROUND_SHELL_HOLD_MS = 5000;
|
|
62
|
+
/** How long a running background task may be missing from the level before
|
|
63
|
+
* it is ended as `interrupted`. The level and the bookends race (the level
|
|
64
|
+
* may arrive first), so a real ending gets this long to land. */
|
|
65
|
+
export declare const RECONCILE_GRACE_MS = 2000;
|
|
66
|
+
/** Minimum gap between two progress publishes of one session. */
|
|
67
|
+
export declare const PUBLISH_INTERVAL_MS = 1000;
|
|
68
|
+
export type TaskFeedLog = {
|
|
69
|
+
/** Diagnostics nobody acts on (an unknown task id). Omitted = dropped. */
|
|
70
|
+
debug?: (message: string) => void;
|
|
71
|
+
/** Degraded but self-recovered (a task ended by reconciliation). */
|
|
72
|
+
warn?: (message: string) => void;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* The one place the SDK's ending vocabulary becomes the wire's. The same stop
|
|
76
|
+
* arrives as `task_updated.patch.status = "killed"` and as
|
|
77
|
+
* `task_notification.status = "stopped"`; both are `stopped`. Returns null for
|
|
78
|
+
* a non-terminal status (`pending`, `running`, `paused`).
|
|
79
|
+
*/
|
|
80
|
+
export declare function normaliseStatus(status: string | undefined, reason?: string): Exclude<TaskStatus, "running"> | null;
|
|
81
|
+
export declare class TaskFeed {
|
|
82
|
+
private readonly clock;
|
|
83
|
+
private readonly log;
|
|
84
|
+
private readonly sessionId;
|
|
85
|
+
private readonly tasks;
|
|
86
|
+
/** Ended tasks whose final snapshot was sent successfully: never shown again. */
|
|
87
|
+
private readonly published;
|
|
88
|
+
/** Ambient task ids: never shown, and their later messages are not news. */
|
|
89
|
+
private readonly ambient;
|
|
90
|
+
/** task id → its current life; bumped on every start. */
|
|
91
|
+
private readonly lives;
|
|
92
|
+
/** task id → the life the latest snapshot carried as ended. */
|
|
93
|
+
private readonly snapshotLives;
|
|
94
|
+
/** task id → time after which a missing bookend means `interrupted`. */
|
|
95
|
+
private readonly deadlines;
|
|
96
|
+
/** Foreground shell tasks not published yet: see `promoteDue`. */
|
|
97
|
+
private readonly held;
|
|
98
|
+
/** Shell task ids labelled by their tool call: a patch never relabels them. */
|
|
99
|
+
private readonly toolDescribed;
|
|
100
|
+
constructor(clock: () => number, log?: TaskFeedLog, sessionId?: string);
|
|
101
|
+
/**
|
|
102
|
+
* `toolDescription` is the spawning tool call's own `description` input. A
|
|
103
|
+
* shell task carries it when it is non-empty; otherwise the SDK's, which
|
|
104
|
+
* for a shell is the command text. Other task types keep the SDK's.
|
|
105
|
+
*/
|
|
106
|
+
onStarted(msg: SDKTaskStartedMessage, toolDescription?: string): TaskChange;
|
|
107
|
+
/** Publishes every held shell task whose hold has run out. */
|
|
108
|
+
promoteDue(): TaskChange;
|
|
109
|
+
/** When the earliest held shell task is due for promotion; null if none is held. */
|
|
110
|
+
nextHoldDue(): number | null;
|
|
111
|
+
onProgress(msg: SDKTaskProgressMessage): TaskChange;
|
|
112
|
+
onUpdated(msg: SDKTaskUpdatedMessage): TaskChange;
|
|
113
|
+
onNotification(msg: SDKTaskNotificationMessage): TaskChange;
|
|
114
|
+
/**
|
|
115
|
+
* The SDK's level: every live background task. It never adds or ends a task
|
|
116
|
+
* by itself — it may precede the `task_started` that names a task, or the
|
|
117
|
+
* bookend that ends one. A running background task the level omits gets a
|
|
118
|
+
* reconcile deadline; a level that lists it again clears it.
|
|
119
|
+
*/
|
|
120
|
+
onLevel(level: {
|
|
121
|
+
task_id: string;
|
|
122
|
+
}[]): TaskChange;
|
|
123
|
+
nextReconcileAt(): number | null;
|
|
124
|
+
/** Ends as `interrupted` every task whose deadline passed with no bookend. */
|
|
125
|
+
reconcile(now: number): TaskChange;
|
|
126
|
+
/** The CLI process restarted: whatever was running did not survive it. A
|
|
127
|
+
* held shell task was never shown, so it is dropped rather than ended. */
|
|
128
|
+
onProcessRestart(): TaskChange;
|
|
129
|
+
/** True when the feed holds `taskId` as running — the stop request's gate. */
|
|
130
|
+
isRunning(taskId: string): boolean;
|
|
131
|
+
/** Running tasks plus ended tasks not yet published, oldest start first.
|
|
132
|
+
* Marks nothing: see `markPublished`. */
|
|
133
|
+
snapshot(): {
|
|
134
|
+
snapshot: TasksSnapshot;
|
|
135
|
+
endedIds: string[];
|
|
136
|
+
};
|
|
137
|
+
/** Called after the send carrying these endings succeeded. Their entries are
|
|
138
|
+
* dropped; the ids are remembered so a late bookend cannot resurrect them. */
|
|
139
|
+
markPublished(endedIds: string[]): void;
|
|
140
|
+
private running;
|
|
141
|
+
/** Puts a running entry into the table. A known id starting again is a new
|
|
142
|
+
* life of the same task — a resumed subagent is re-registered under its
|
|
143
|
+
* agent id. It replaces the ended entry, published or not: the running
|
|
144
|
+
* state supersedes an ending the client has not seen yet, and a published
|
|
145
|
+
* id must be shown again. */
|
|
146
|
+
private admit;
|
|
147
|
+
/** Applies a `task_updated` patch's label and placement; a label taken from
|
|
148
|
+
* the tool call is never replaced. */
|
|
149
|
+
private patch;
|
|
150
|
+
private end;
|
|
151
|
+
}
|
|
152
|
+
export type PublisherTimers = {
|
|
153
|
+
now: () => number;
|
|
154
|
+
setTimeout: (fn: () => void, ms: number) => unknown;
|
|
155
|
+
clearTimeout: (handle: any) => void;
|
|
156
|
+
};
|
|
157
|
+
export declare class TaskFeedPublisher {
|
|
158
|
+
private readonly feed;
|
|
159
|
+
private readonly send;
|
|
160
|
+
private readonly timers;
|
|
161
|
+
private readonly onSendError;
|
|
162
|
+
private lastSendAt;
|
|
163
|
+
/** One send at a time: a snapshot taken while another is in flight would
|
|
164
|
+
* carry that one's endings again, and both would succeed. */
|
|
165
|
+
private inFlight;
|
|
166
|
+
/** A publish requested while a send was in flight; `"terminal"` skips the
|
|
167
|
+
* throttle when it runs. */
|
|
168
|
+
private queued;
|
|
169
|
+
/** The trailing publish or the retry after a failed send — never both. */
|
|
170
|
+
private pending;
|
|
171
|
+
private reconcileTimer;
|
|
172
|
+
private reconcileAt;
|
|
173
|
+
/** Promotes held foreground shells: armed at the feed's `nextHoldDue()`. */
|
|
174
|
+
private holdTimer;
|
|
175
|
+
private holdAt;
|
|
176
|
+
private closed;
|
|
177
|
+
/** Set by `dispose({ flush: true })`: a publish queued behind an in-flight
|
|
178
|
+
* send still goes out once — a retirement's `interrupted` endings. */
|
|
179
|
+
private flushOnSettle;
|
|
180
|
+
constructor(feed: TaskFeed, send: (snapshot: TasksSnapshot) => Promise<void>, timers?: PublisherTimers, onSendError?: (error: unknown) => void);
|
|
181
|
+
notify(change: TaskChange): void;
|
|
182
|
+
dispose(options?: {
|
|
183
|
+
flush?: boolean;
|
|
184
|
+
}): void;
|
|
185
|
+
private publishNow;
|
|
186
|
+
private settle;
|
|
187
|
+
private arm;
|
|
188
|
+
private cancelPending;
|
|
189
|
+
/** One timer for every held shell, at the earliest due time. A held shell
|
|
190
|
+
* that ended early leaves the timer armed: it fires, promotes nothing, and
|
|
191
|
+
* re-arms for the next one still held. */
|
|
192
|
+
private armHold;
|
|
193
|
+
private armReconcile;
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=task-feed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-feed.d.ts","sourceRoot":"","sources":["../src/task-feed.ts"],"names":[],"mappings":"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"}
|