@controlvector/cv-agent 1.9.2 → 1.10.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.
@@ -0,0 +1,66 @@
1
+ /**
2
+ * EventQueue: persistent, ordered, at-least-once delivery of task events to CV-Hub.
3
+ *
4
+ * Background: cv-agent previously posted `output` / `output_final` events with
5
+ * fire-and-forget `.catch(() => {})`. Any transient network blip or server
6
+ * rejection silently dropped output, leaving the planner blind to what the
7
+ * executor actually produced.
8
+ *
9
+ * This queue:
10
+ * - enqueues events in memory, drains them serially via the provided poster
11
+ * - retries failures with bounded exponential backoff (1s, 2s, 4s, ...)
12
+ * - spills pending events to disk every flush so a crash doesn't lose them
13
+ * - reloads the on-disk spill on construction (next-run pickup)
14
+ * - exposes flush() + close() so callers can drain on shutdown
15
+ *
16
+ * Events are tagged with a monotonic sequence_number assigned by the queue —
17
+ * the server uses it to reconstruct ordering deterministically even under
18
+ * same-millisecond bursts.
19
+ */
20
+ export type QueuedEvent = {
21
+ event_type: string;
22
+ content: Record<string, unknown> | string;
23
+ needs_response?: boolean;
24
+ sequence_number: number;
25
+ };
26
+ export type EventPoster = (event: QueuedEvent) => Promise<void>;
27
+ type Options = {
28
+ spillPath: string;
29
+ poster: EventPoster;
30
+ maxRetries?: number;
31
+ baseDelayMs?: number;
32
+ onError?: (err: Error, event: QueuedEvent, attempt: number) => void;
33
+ };
34
+ export declare class EventQueue {
35
+ private buffer;
36
+ private sequence;
37
+ private drainPromise;
38
+ private closed;
39
+ private spillDirty;
40
+ private readonly spillPath;
41
+ private readonly poster;
42
+ private readonly maxRetries;
43
+ private readonly baseDelayMs;
44
+ private readonly onError?;
45
+ constructor(opts: Options);
46
+ /** Load any events that were spilled to disk by a previous run. */
47
+ loadSpill(): Promise<void>;
48
+ /** Assign the next sequence number. Callers that want to stamp manually can read this. */
49
+ nextSequence(): number;
50
+ /** Enqueue an event. Returns the assigned sequence_number. */
51
+ enqueue(event: Omit<QueuedEvent, 'sequence_number'> & {
52
+ sequence_number?: number;
53
+ }): number;
54
+ /** Drain the buffer, retrying transient failures. Serial — concurrent calls share the same promise. */
55
+ private drain;
56
+ private drainInternal;
57
+ /** Flush: drain remaining events, then persist anything still pending to disk. */
58
+ flush(): Promise<void>;
59
+ /** Mark the queue closed and flush. Safe to call multiple times. */
60
+ close(): Promise<void>;
61
+ /** Number of events still pending delivery. */
62
+ size(): number;
63
+ private writeSpill;
64
+ }
65
+ export {};
66
+ //# sourceMappingURL=event-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-queue.d.ts","sourceRoot":"","sources":["../../src/utils/event-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;IAC1C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEhE,KAAK,OAAO,GAAG;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrE,CAAC;AAEF,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAA4D;gBAEzE,IAAI,EAAE,OAAO;IAQzB,mEAAmE;IAC7D,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBhC,0FAA0F;IAC1F,YAAY,IAAI,MAAM;IAItB,8DAA8D;IAC9D,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,GAAG;QAAE,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IAa3F,uGAAuG;IACvG,OAAO,CAAC,KAAK;YAQC,aAAa;IAiC3B,kFAAkF;IAC5E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B,oEAAoE;IAC9D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,+CAA+C;IAC/C,IAAI,IAAI,MAAM;YAIA,UAAU;CAczB"}
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ /**
3
+ * EventQueue: persistent, ordered, at-least-once delivery of task events to CV-Hub.
4
+ *
5
+ * Background: cv-agent previously posted `output` / `output_final` events with
6
+ * fire-and-forget `.catch(() => {})`. Any transient network blip or server
7
+ * rejection silently dropped output, leaving the planner blind to what the
8
+ * executor actually produced.
9
+ *
10
+ * This queue:
11
+ * - enqueues events in memory, drains them serially via the provided poster
12
+ * - retries failures with bounded exponential backoff (1s, 2s, 4s, ...)
13
+ * - spills pending events to disk every flush so a crash doesn't lose them
14
+ * - reloads the on-disk spill on construction (next-run pickup)
15
+ * - exposes flush() + close() so callers can drain on shutdown
16
+ *
17
+ * Events are tagged with a monotonic sequence_number assigned by the queue —
18
+ * the server uses it to reconstruct ordering deterministically even under
19
+ * same-millisecond bursts.
20
+ */
21
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ var desc = Object.getOwnPropertyDescriptor(m, k);
24
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
25
+ desc = { enumerable: true, get: function() { return m[k]; } };
26
+ }
27
+ Object.defineProperty(o, k2, desc);
28
+ }) : (function(o, m, k, k2) {
29
+ if (k2 === undefined) k2 = k;
30
+ o[k2] = m[k];
31
+ }));
32
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
33
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
34
+ }) : function(o, v) {
35
+ o["default"] = v;
36
+ });
37
+ var __importStar = (this && this.__importStar) || (function () {
38
+ var ownKeys = function(o) {
39
+ ownKeys = Object.getOwnPropertyNames || function (o) {
40
+ var ar = [];
41
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
42
+ return ar;
43
+ };
44
+ return ownKeys(o);
45
+ };
46
+ return function (mod) {
47
+ if (mod && mod.__esModule) return mod;
48
+ var result = {};
49
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
50
+ __setModuleDefault(result, mod);
51
+ return result;
52
+ };
53
+ })();
54
+ Object.defineProperty(exports, "__esModule", { value: true });
55
+ exports.EventQueue = void 0;
56
+ const node_fs_1 = require("node:fs");
57
+ const path = __importStar(require("node:path"));
58
+ class EventQueue {
59
+ buffer = [];
60
+ sequence = 0;
61
+ drainPromise = null;
62
+ closed = false;
63
+ spillDirty = false;
64
+ spillPath;
65
+ poster;
66
+ maxRetries;
67
+ baseDelayMs;
68
+ onError;
69
+ constructor(opts) {
70
+ this.spillPath = opts.spillPath;
71
+ this.poster = opts.poster;
72
+ this.maxRetries = opts.maxRetries ?? 5;
73
+ this.baseDelayMs = opts.baseDelayMs ?? 1000;
74
+ this.onError = opts.onError;
75
+ }
76
+ /** Load any events that were spilled to disk by a previous run. */
77
+ async loadSpill() {
78
+ try {
79
+ const raw = await node_fs_1.promises.readFile(this.spillPath, 'utf8');
80
+ const lines = raw.split('\n').filter((l) => l.trim().length > 0);
81
+ for (const line of lines) {
82
+ const ev = JSON.parse(line);
83
+ this.buffer.push(ev);
84
+ if (ev.sequence_number >= this.sequence) {
85
+ this.sequence = ev.sequence_number + 1;
86
+ }
87
+ }
88
+ }
89
+ catch (err) {
90
+ if (err.code !== 'ENOENT')
91
+ throw err;
92
+ }
93
+ }
94
+ /** Assign the next sequence number. Callers that want to stamp manually can read this. */
95
+ nextSequence() {
96
+ return this.sequence++;
97
+ }
98
+ /** Enqueue an event. Returns the assigned sequence_number. */
99
+ enqueue(event) {
100
+ if (this.closed) {
101
+ throw new Error('EventQueue is closed');
102
+ }
103
+ const seq = event.sequence_number ?? this.nextSequence();
104
+ if (seq >= this.sequence)
105
+ this.sequence = seq + 1;
106
+ this.buffer.push({ ...event, sequence_number: seq });
107
+ this.spillDirty = true;
108
+ // Drain in the background — don't block the caller.
109
+ this.drain().catch(() => { });
110
+ return seq;
111
+ }
112
+ /** Drain the buffer, retrying transient failures. Serial — concurrent calls share the same promise. */
113
+ drain() {
114
+ if (this.drainPromise)
115
+ return this.drainPromise;
116
+ this.drainPromise = this.drainInternal().finally(() => {
117
+ this.drainPromise = null;
118
+ });
119
+ return this.drainPromise;
120
+ }
121
+ async drainInternal() {
122
+ try {
123
+ while (this.buffer.length > 0) {
124
+ const ev = this.buffer[0];
125
+ let delivered = false;
126
+ for (let attempt = 0; attempt < this.maxRetries; attempt++) {
127
+ try {
128
+ await this.poster(ev);
129
+ delivered = true;
130
+ break;
131
+ }
132
+ catch (err) {
133
+ this.onError?.(err instanceof Error ? err : new Error(String(err)), ev, attempt);
134
+ if (attempt < this.maxRetries - 1) {
135
+ await sleep(this.baseDelayMs * Math.pow(2, attempt));
136
+ }
137
+ }
138
+ }
139
+ if (delivered) {
140
+ this.buffer.shift();
141
+ this.spillDirty = true;
142
+ }
143
+ else {
144
+ // Out of retries — leave on buffer so flush() can persist it,
145
+ // and stop draining to avoid hot-looping on a dead server.
146
+ break;
147
+ }
148
+ }
149
+ }
150
+ finally {
151
+ if (this.spillDirty) {
152
+ await this.writeSpill().catch(() => { });
153
+ }
154
+ }
155
+ }
156
+ /** Flush: drain remaining events, then persist anything still pending to disk. */
157
+ async flush() {
158
+ await this.drain();
159
+ await this.writeSpill();
160
+ }
161
+ /** Mark the queue closed and flush. Safe to call multiple times. */
162
+ async close() {
163
+ if (this.closed)
164
+ return;
165
+ this.closed = true;
166
+ await this.flush();
167
+ }
168
+ /** Number of events still pending delivery. */
169
+ size() {
170
+ return this.buffer.length;
171
+ }
172
+ async writeSpill() {
173
+ try {
174
+ if (this.buffer.length === 0) {
175
+ await node_fs_1.promises.rm(this.spillPath, { force: true });
176
+ }
177
+ else {
178
+ await node_fs_1.promises.mkdir(path.dirname(this.spillPath), { recursive: true });
179
+ const body = this.buffer.map((e) => JSON.stringify(e)).join('\n') + '\n';
180
+ await node_fs_1.promises.writeFile(this.spillPath, body, 'utf8');
181
+ }
182
+ this.spillDirty = false;
183
+ }
184
+ catch {
185
+ // Spill errors are non-fatal — we still have events in memory.
186
+ }
187
+ }
188
+ }
189
+ exports.EventQueue = EventQueue;
190
+ function sleep(ms) {
191
+ return new Promise((r) => setTimeout(r, ms));
192
+ }
193
+ //# sourceMappingURL=event-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-queue.js","sourceRoot":"","sources":["../../src/utils/event-queue.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@controlvector/cv-agent",
3
- "version": "1.9.2",
3
+ "version": "1.10.0",
4
4
  "description": "Standalone agent daemon for CV-Hub — bridges Claude Code with CV-Hub task dispatch",
5
5
  "bin": {
6
6
  "cva": "./dist/bundle.cjs"