@cohortapp/agent-sdk 2.11.8 → 2.11.9
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 +1 -1
- package/scripts/daemon/agent-daemon.mjs +78 -37
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cohortapp/agent-sdk",
|
|
3
|
-
"version": "2.11.
|
|
3
|
+
"version": "2.11.9",
|
|
4
4
|
"description": "Cohort Agent SDK — autonomous AI colleague runtime. Deploy senior AI colleagues on dedicated Mac minis, wired to the Cohort operating surface.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -125,6 +125,9 @@ import { policyFor } from "../../lib/execution/surface-policy.mjs";
|
|
|
125
125
|
// ---------------------------------------------------------------------------
|
|
126
126
|
|
|
127
127
|
const POLL_INTERVAL = parseInt(process.env.DAEMON_POLL_INTERVAL || "60000", 10); // 60s (up from 30s to avoid Slack rate limits)
|
|
128
|
+
// Fast reactive scan of the LOCAL cohort inbox only (no API, no rate limit) so an
|
|
129
|
+
// org-push-fetched DM is answered in seconds, not on the 60s poll. See pollCohortFast.
|
|
130
|
+
const COHORT_FAST_INTERVAL = parseInt(process.env.DAEMON_COHORT_POLL_INTERVAL || "2000", 10); // 2s
|
|
128
131
|
// Backlog sweep cadence — bumped 2min → 10min in 1.10. The dispatcher
|
|
129
132
|
// applies a per-item post-completion cooldown (4h success, 30min failure)
|
|
130
133
|
// so we no longer need a tight sweep loop. Operators with high-throughput
|
|
@@ -207,44 +210,8 @@ async function poll() {
|
|
|
207
210
|
}
|
|
208
211
|
|
|
209
212
|
let totalItems = 0;
|
|
210
|
-
|
|
211
213
|
for (const svc of services) {
|
|
212
|
-
|
|
213
|
-
const result = await svc.fn();
|
|
214
|
-
// Dedup by raw_ref first: the same Slack message arrives via
|
|
215
|
-
// multiple paths (channel scan, thread scan, events-server JSON)
|
|
216
|
-
// with different item IDs. Using raw_ref as the lock key ensures
|
|
217
|
-
// a single message is only processed once regardless of path.
|
|
218
|
-
const seenRefs = new Set();
|
|
219
|
-
const newItems = result.items.filter((item) => {
|
|
220
|
-
// Skip the agent's own messages — defensive check in case intake filters miss them
|
|
221
|
-
const sender = (item.sender || "").toLowerCase();
|
|
222
|
-
const me = loadAgent();
|
|
223
|
-
const myFirst = (me.firstName || "").toLowerCase();
|
|
224
|
-
const myFull = (me.fullName || "").toLowerCase().replace(/\s+/g, "-");
|
|
225
|
-
if (myFirst && (sender === myFirst || sender === myFull)) return false;
|
|
226
|
-
|
|
227
|
-
const lockKey = item.raw_ref || item.id || `${svc.name}-${Date.now()}`;
|
|
228
|
-
if (seenRefs.has(lockKey)) return false;
|
|
229
|
-
seenRefs.add(lockKey);
|
|
230
|
-
const lock = acquireLock(lockKey, {
|
|
231
|
-
sender: item.sender || "unknown",
|
|
232
|
-
channel: item.channel || item.channel_id || "unknown",
|
|
233
|
-
});
|
|
234
|
-
return lock.acquired;
|
|
235
|
-
});
|
|
236
|
-
totalItems += newItems.length;
|
|
237
|
-
|
|
238
|
-
for (const item of newItems) {
|
|
239
|
-
await processItem(item, svc.name);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
if (result.errors.length > 0) {
|
|
243
|
-
console.warn(`[daemon] ${svc.name} errors:`, result.errors.slice(0, 3));
|
|
244
|
-
}
|
|
245
|
-
} catch (err) {
|
|
246
|
-
console.error(`[daemon] ${svc.name} poll failed:`, err.message);
|
|
247
|
-
}
|
|
214
|
+
totalItems += await pollService(svc);
|
|
248
215
|
}
|
|
249
216
|
|
|
250
217
|
recordPoll(totalItems);
|
|
@@ -254,6 +221,74 @@ async function poll() {
|
|
|
254
221
|
|
|
255
222
|
}
|
|
256
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Scan + process ONE service's inbox. Factored out of poll() so the reactive
|
|
226
|
+
* cohort path can run on its own fast interval (pollCohortFast) without waiting
|
|
227
|
+
* on the 60s full poll. Returns the count of new items processed. Never throws.
|
|
228
|
+
*/
|
|
229
|
+
async function pollService(svc) {
|
|
230
|
+
try {
|
|
231
|
+
const result = await svc.fn();
|
|
232
|
+
// Dedup by raw_ref first: the same Slack message arrives via multiple paths
|
|
233
|
+
// (channel scan, thread scan, events-server JSON) with different item IDs.
|
|
234
|
+
// Using raw_ref as the lock key ensures a single message is only processed
|
|
235
|
+
// once regardless of path.
|
|
236
|
+
const seenRefs = new Set();
|
|
237
|
+
const newItems = result.items.filter((item) => {
|
|
238
|
+
// Skip the agent's own messages — defensive check in case intake filters miss them
|
|
239
|
+
const sender = (item.sender || "").toLowerCase();
|
|
240
|
+
const me = loadAgent();
|
|
241
|
+
const myFirst = (me.firstName || "").toLowerCase();
|
|
242
|
+
const myFull = (me.fullName || "").toLowerCase().replace(/\s+/g, "-");
|
|
243
|
+
if (myFirst && (sender === myFirst || sender === myFull)) return false;
|
|
244
|
+
|
|
245
|
+
const lockKey = item.raw_ref || item.id || `${svc.name}-${Date.now()}`;
|
|
246
|
+
if (seenRefs.has(lockKey)) return false;
|
|
247
|
+
seenRefs.add(lockKey);
|
|
248
|
+
const lock = acquireLock(lockKey, {
|
|
249
|
+
sender: item.sender || "unknown",
|
|
250
|
+
channel: item.channel || item.channel_id || "unknown",
|
|
251
|
+
});
|
|
252
|
+
return lock.acquired;
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
for (const item of newItems) {
|
|
256
|
+
await processItem(item, svc.name);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (result.errors.length > 0) {
|
|
260
|
+
console.warn(`[daemon] ${svc.name} errors:`, result.errors.slice(0, 3));
|
|
261
|
+
}
|
|
262
|
+
return newItems.length;
|
|
263
|
+
} catch (err) {
|
|
264
|
+
console.error(`[daemon] ${svc.name} poll failed:`, err.message);
|
|
265
|
+
return 0;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ── Fast reactive cohort scan ────────────────────────────────────────────────
|
|
270
|
+
// The org-push wake pulls org DMs/@mentions into state/inbox/cohort/ within ~2s,
|
|
271
|
+
// but the full poll() runs only every POLL_INTERVAL (60s, kept slow because it
|
|
272
|
+
// also hits the RATE-LIMITED Slack API) — so a DM sat up to 60s BETWEEN being
|
|
273
|
+
// fetched and being answered (measured: ~24-44s reactive round-trips that were
|
|
274
|
+
// almost entirely this gap, not the model). Cohort items are local YAML with no
|
|
275
|
+
// API call, so we drain that ONE directory on a short interval. This is the
|
|
276
|
+
// single biggest reactive-latency win. Overlap-guarded; the item lock dedups
|
|
277
|
+
// against the 60s poll's own cohort scan (kept as a safety net).
|
|
278
|
+
let _cohortPoller = null;
|
|
279
|
+
let _cohortFastRunning = false;
|
|
280
|
+
async function pollCohortFast() {
|
|
281
|
+
if (_cohortFastRunning) return 0;
|
|
282
|
+
if (!_existsSync(join(AGENT_REPO_DIR, "config/org.yaml"))) return 0;
|
|
283
|
+
if (!_cohortPoller) _cohortPoller = makeInboxScanPoller("cohort", { agentRoot: AGENT_REPO_DIR });
|
|
284
|
+
_cohortFastRunning = true;
|
|
285
|
+
try {
|
|
286
|
+
return await pollService({ name: "cohort", fn: _cohortPoller });
|
|
287
|
+
} finally {
|
|
288
|
+
_cohortFastRunning = false;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
257
292
|
// ---------------------------------------------------------------------------
|
|
258
293
|
// Process a single item
|
|
259
294
|
// ---------------------------------------------------------------------------
|
|
@@ -2143,6 +2178,12 @@ async function main() {
|
|
|
2143
2178
|
}
|
|
2144
2179
|
}, POLL_INTERVAL);
|
|
2145
2180
|
|
|
2181
|
+
// Fast reactive cohort scan — drains org-push-fetched DMs in ~2s instead of
|
|
2182
|
+
// waiting up to 60s for the full poll. Local YAML only; no API/rate-limit cost.
|
|
2183
|
+
setInterval(() => {
|
|
2184
|
+
pollCohortFast().catch((err) => console.error("[daemon] cohort fast-poll error:", err.message));
|
|
2185
|
+
}, COHORT_FAST_INTERVAL);
|
|
2186
|
+
|
|
2146
2187
|
// Backlog sweep
|
|
2147
2188
|
setInterval(async () => {
|
|
2148
2189
|
try {
|