@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.
@@ -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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"}