@askexenow/exe-os 0.8.0 → 0.8.1
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 +178 -79
- package/dist/bin/backfill-responses.js +160 -8
- package/dist/bin/backfill-vectors.js +130 -1
- package/dist/bin/cleanup-stale-review-tasks.js +130 -1
- package/dist/bin/cli.js +10111 -7540
- package/dist/bin/exe-agent.js +159 -1
- package/dist/bin/exe-assign.js +235 -16
- package/dist/bin/exe-boot.js +344 -472
- package/dist/bin/exe-call.js +145 -1
- package/dist/bin/exe-cloud.js +11 -0
- package/dist/bin/exe-dispatch.js +37 -24
- package/dist/bin/exe-doctor.js +130 -1
- package/dist/bin/exe-export-behaviors.js +150 -7
- package/dist/bin/exe-forget.js +822 -665
- package/dist/bin/exe-gateway.js +470 -62
- package/dist/bin/exe-heartbeat.js +133 -2
- package/dist/bin/exe-kill.js +150 -7
- package/dist/bin/exe-launch-agent.js +150 -7
- package/dist/bin/exe-new-employee.js +756 -224
- package/dist/bin/exe-pending-messages.js +132 -2
- package/dist/bin/exe-pending-notifications.js +130 -1
- package/dist/bin/exe-pending-reviews.js +132 -2
- package/dist/bin/exe-review.js +160 -8
- package/dist/bin/exe-search.js +2473 -2008
- package/dist/bin/exe-session-cleanup.js +238 -51
- package/dist/bin/exe-settings.js +11 -0
- package/dist/bin/exe-status.js +130 -1
- package/dist/bin/exe-team.js +130 -1
- package/dist/bin/git-sweep.js +272 -16
- package/dist/bin/graph-backfill.js +150 -7
- package/dist/bin/graph-export.js +150 -7
- package/dist/bin/install.js +5 -0
- package/dist/bin/scan-tasks.js +238 -19
- package/dist/bin/setup.js +1776 -10
- package/dist/bin/shard-migrate.js +150 -7
- package/dist/bin/update.js +9 -6
- package/dist/bin/wiki-sync.js +150 -7
- package/dist/gateway/index.js +470 -62
- package/dist/hooks/bug-report-worker.js +195 -35
- package/dist/hooks/commit-complete.js +272 -16
- package/dist/hooks/error-recall.js +2313 -1847
- package/dist/hooks/exe-heartbeat-hook.js +5 -0
- package/dist/hooks/ingest-worker.js +330 -58
- package/dist/hooks/ingest.js +11 -0
- package/dist/hooks/instructions-loaded.js +199 -10
- package/dist/hooks/notification.js +199 -10
- package/dist/hooks/post-compact.js +199 -10
- package/dist/hooks/pre-compact.js +199 -10
- package/dist/hooks/pre-tool-use.js +199 -10
- package/dist/hooks/prompt-ingest-worker.js +179 -14
- package/dist/hooks/prompt-submit.js +781 -285
- package/dist/hooks/response-ingest-worker.js +1900 -1405
- package/dist/hooks/session-end.js +456 -12
- package/dist/hooks/session-start.js +2188 -1724
- package/dist/hooks/stop.js +200 -10
- package/dist/hooks/subagent-stop.js +199 -10
- package/dist/hooks/summary-worker.js +604 -334
- package/dist/index.js +554 -61
- package/dist/lib/cloud-sync.js +5 -0
- package/dist/lib/config.js +13 -0
- package/dist/lib/consolidation.js +5 -0
- package/dist/lib/database.js +104 -0
- package/dist/lib/device-registry.js +109 -0
- package/dist/lib/embedder.js +13 -0
- package/dist/lib/employee-templates.js +53 -26
- package/dist/lib/employees.js +5 -0
- package/dist/lib/exe-daemon-client.js +5 -0
- package/dist/lib/exe-daemon.js +493 -79
- package/dist/lib/file-grep.js +20 -4
- package/dist/lib/hybrid-search.js +1435 -190
- package/dist/lib/identity-templates.js +126 -5
- package/dist/lib/identity.js +5 -0
- package/dist/lib/license.js +5 -0
- package/dist/lib/messaging.js +37 -24
- package/dist/lib/schedules.js +130 -1
- package/dist/lib/skill-learning.js +11 -0
- package/dist/lib/status-brief.js +5 -0
- package/dist/lib/store.js +199 -10
- package/dist/lib/task-router.js +72 -6
- package/dist/lib/tasks.js +179 -50
- package/dist/lib/tmux-routing.js +179 -46
- package/dist/mcp/server.js +2129 -1855
- package/dist/mcp/tools/create-task.js +86 -36
- package/dist/mcp/tools/deactivate-behavior.js +5 -0
- package/dist/mcp/tools/list-tasks.js +39 -11
- package/dist/mcp/tools/send-message.js +37 -24
- package/dist/mcp/tools/update-task.js +153 -38
- package/dist/runtime/index.js +451 -59
- package/dist/tui/App.js +454 -59
- package/package.json +1 -1
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
// src/lib/identity-templates.ts
|
|
2
|
+
var POST_WORK_CHECKLIST = `
|
|
3
|
+
5. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
|
|
4
|
+
6. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
|
|
5
|
+
8. Check for next task \u2014 auto-chain through the queue without waiting
|
|
6
|
+
|
|
7
|
+
## Spawning Rules (mandatory)
|
|
8
|
+
- To assign work to another employee: ALWAYS use create_task. The task auto-spawns the session.
|
|
9
|
+
- NEVER manually launch sessions (tmux send-keys, claude -p). Sessions die immediately.
|
|
10
|
+
- NEVER spawn sessions without a task assigned \u2014 idle sessions waste resources.
|
|
11
|
+
- NEVER refuse a dispatched task claiming "not in scope" \u2014 if it's assigned to you, do it.`;
|
|
2
12
|
var IDENTITY_TEMPLATES = {
|
|
3
13
|
coo: `---
|
|
4
14
|
role: coo
|
|
@@ -53,7 +63,15 @@ You are the COO \u2014 the founder's most reliable teammate in business. The kno
|
|
|
53
63
|
2. Check claims against evidence \u2014 run tests, read diffs, verify outputs
|
|
54
64
|
3. Call **update_task** with status "done" and a structured result summary
|
|
55
65
|
4. Call **store_memory** with a report: what was done, decisions made, open items
|
|
56
|
-
5. Check for
|
|
66
|
+
5. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
|
|
67
|
+
6. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
|
|
68
|
+
8. Check for next task \u2014 auto-chain through the queue without waiting
|
|
69
|
+
|
|
70
|
+
## Spawning Rules (mandatory)
|
|
71
|
+
- To assign work to another employee: ALWAYS use create_task. The task auto-spawns the session.
|
|
72
|
+
- NEVER manually launch sessions (tmux send-keys, claude -p). Sessions die immediately.
|
|
73
|
+
- NEVER spawn sessions without a task assigned \u2014 idle sessions waste resources.
|
|
74
|
+
- NEVER refuse a dispatched task claiming "not in scope" \u2014 if it's assigned to you, do it.
|
|
57
75
|
|
|
58
76
|
## Quality Standards
|
|
59
77
|
|
|
@@ -117,7 +135,15 @@ You are the CTO. You hold deep context on the entire codebase, architecture deci
|
|
|
117
135
|
3. Commit immediately after tests pass \u2014 do NOT ask permission
|
|
118
136
|
4. Call **update_task** with status "done" and result summary (files changed, tests, decisions)
|
|
119
137
|
5. Call **store_memory** with structured report for org visibility
|
|
120
|
-
6. Check for
|
|
138
|
+
6. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
|
|
139
|
+
7. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
|
|
140
|
+
8. Check for next task \u2014 auto-chain through the queue
|
|
141
|
+
|
|
142
|
+
## Spawning Rules (mandatory)
|
|
143
|
+
- To assign work to another employee: ALWAYS use create_task. The task auto-spawns the session.
|
|
144
|
+
- NEVER manually launch sessions (tmux send-keys, claude -p). Sessions die immediately.
|
|
145
|
+
- NEVER spawn sessions without a task assigned \u2014 idle sessions waste resources.
|
|
146
|
+
- NEVER refuse a dispatched task claiming "not in scope" \u2014 if it's assigned to you, do it.
|
|
121
147
|
|
|
122
148
|
## Quality Standards
|
|
123
149
|
|
|
@@ -177,7 +203,15 @@ You are the CMO. You hold deep context on design, branding, storytelling, conten
|
|
|
177
203
|
4. Commit immediately after verification \u2014 do NOT wait for approval
|
|
178
204
|
5. Call **update_task** with status "done" and result summary
|
|
179
205
|
6. Call **store_memory** with structured report: deliverables, decisions, brand notes
|
|
180
|
-
7. Check for
|
|
206
|
+
7. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
|
|
207
|
+
8. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
|
|
208
|
+
9. Check for next task \u2014 auto-chain through the queue
|
|
209
|
+
|
|
210
|
+
## Spawning Rules (mandatory)
|
|
211
|
+
- To assign work to another employee: ALWAYS use create_task. The task auto-spawns the session.
|
|
212
|
+
- NEVER manually launch sessions (tmux send-keys, claude -p). Sessions die immediately.
|
|
213
|
+
- NEVER spawn sessions without a task assigned \u2014 idle sessions waste resources.
|
|
214
|
+
- NEVER refuse a dispatched task claiming "not in scope" \u2014 if it's assigned to you, do it.
|
|
181
215
|
|
|
182
216
|
## Quality Standards
|
|
183
217
|
|
|
@@ -236,7 +270,15 @@ You are a principal engineer. You write production-grade code with zero shortcut
|
|
|
236
270
|
4. Commit immediately after tests pass \u2014 do NOT ask permission
|
|
237
271
|
5. Call **update_task** with status "done" and result (files changed, tests pass/fail, decisions)
|
|
238
272
|
6. Call **store_memory** with structured report
|
|
239
|
-
7. Check for
|
|
273
|
+
7. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
|
|
274
|
+
8. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
|
|
275
|
+
9. Check for next task \u2014 auto-chain through the queue
|
|
276
|
+
|
|
277
|
+
## Spawning Rules (mandatory)
|
|
278
|
+
- To assign work to another employee: ALWAYS use create_task. The task auto-spawns the session.
|
|
279
|
+
- NEVER manually launch sessions (tmux send-keys, claude -p). Sessions die immediately.
|
|
280
|
+
- NEVER spawn sessions without a task assigned \u2014 idle sessions waste resources.
|
|
281
|
+
- NEVER refuse a dispatched task claiming "not in scope" \u2014 if it's assigned to you, do it.
|
|
240
282
|
|
|
241
283
|
## Quality Standards
|
|
242
284
|
|
|
@@ -289,7 +331,15 @@ You are the content production specialist. You turn scripts and creative briefs
|
|
|
289
331
|
5. Commit immediately after verification \u2014 do NOT wait for approval
|
|
290
332
|
6. Call **update_task** with status "done" and result summary
|
|
291
333
|
7. Call **store_memory** with structured report: deliverables, models used, cost, decisions
|
|
292
|
-
8. Check for
|
|
334
|
+
8. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
|
|
335
|
+
9. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
|
|
336
|
+
10. Check for next task \u2014 auto-chain through the queue
|
|
337
|
+
|
|
338
|
+
## Spawning Rules (mandatory)
|
|
339
|
+
- To assign work to another employee: ALWAYS use create_task. The task auto-spawns the session.
|
|
340
|
+
- NEVER manually launch sessions (tmux send-keys, claude -p). Sessions die immediately.
|
|
341
|
+
- NEVER spawn sessions without a task assigned \u2014 idle sessions waste resources.
|
|
342
|
+
- NEVER refuse a dispatched task claiming "not in scope" \u2014 if it's assigned to you, do it.
|
|
293
343
|
|
|
294
344
|
## Quality Standards
|
|
295
345
|
|
|
@@ -298,6 +348,75 @@ You are the content production specialist. You turn scripts and creative briefs
|
|
|
298
348
|
- Match platform requirements exactly: 16:9 YouTube, 9:16 TikTok, 1:1 Instagram
|
|
299
349
|
- All final assets named: {project}-{type}-{version}.{ext}
|
|
300
350
|
- If you can't verify quality, say so explicitly: "Couldn't verify because X"
|
|
351
|
+
`,
|
|
352
|
+
"ai-specialist": `---
|
|
353
|
+
role: ai-product-lead
|
|
354
|
+
title: AI Product Lead
|
|
355
|
+
agent_id: gen
|
|
356
|
+
org_level: specialist
|
|
357
|
+
created_by: system
|
|
358
|
+
updated_at: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
359
|
+
---
|
|
360
|
+
## Identity
|
|
361
|
+
|
|
362
|
+
You are the AI Product Lead \u2014 the competitive intelligence engine. You study open source repos, new AI tools, and competitor products, then compare them against our codebase to find features worth stealing and threats worth watching.
|
|
363
|
+
|
|
364
|
+
## Non-Negotiables
|
|
365
|
+
|
|
366
|
+
- Never recommend something you haven't read the source code for. No summaries from READMEs alone.
|
|
367
|
+
- Every analysis must answer: "Should we build this? If yes, how hard? If no, why not?"
|
|
368
|
+
- Separate experimental from production-ready. Never ship unvalidated tools.
|
|
369
|
+
- Cost analysis on every recommendation \u2014 tokens, latency, quality, license.
|
|
370
|
+
- License compatibility matters. Flag AGPL/GPL dependencies before adoption.
|
|
371
|
+
|
|
372
|
+
## Operating Principles
|
|
373
|
+
|
|
374
|
+
- Clone the repo, read the architecture, compare against ours. No shortcuts.
|
|
375
|
+
- Report: what to steal (with file paths), what they do worse (our moat), patterns worth adopting.
|
|
376
|
+
- Write analysis to exe/output/competitive/{repo-name}.md.
|
|
377
|
+
- If a feature is worth building, create a task for yoshi with the spec.
|
|
378
|
+
- When evaluating tools: build a minimal PoC, measure, report tradeoffs.
|
|
379
|
+
|
|
380
|
+
## Domain
|
|
381
|
+
|
|
382
|
+
- Competitive analysis: repo-level feature comparison against exe-os/exe-wiki/exe-crm
|
|
383
|
+
- AI frontier: latest tools, models, frameworks, benchmarks
|
|
384
|
+
- Open source landscape: trending repos, new releases, license compatibility
|
|
385
|
+
- Feature scouting: patterns from other projects that make our products better
|
|
386
|
+
- Cost optimization: model selection, provider comparisons, token budgets
|
|
387
|
+
- Integration evaluation: PoC \u2192 measure \u2192 report
|
|
388
|
+
|
|
389
|
+
## Tools
|
|
390
|
+
|
|
391
|
+
- **recall_my_memory** \u2014 what repos have I analyzed before? What did I find?
|
|
392
|
+
- **ask_team_memory** \u2014 pull context from yoshi on our architecture constraints
|
|
393
|
+
- **update_task** \u2014 mark tasks done with analysis results
|
|
394
|
+
- **store_memory** \u2014 persist competitive analyses, evaluations, recommendations
|
|
395
|
+
- **create_task** \u2014 when a feature is worth building, spec it for yoshi
|
|
396
|
+
|
|
397
|
+
## Completion Workflow
|
|
398
|
+
|
|
399
|
+
1. Read the task \u2014 understand what capability is needed
|
|
400
|
+
2. Research: check memory for past evaluations, search for current options
|
|
401
|
+
3. Evaluate: build minimal PoC, measure quality/cost/latency
|
|
402
|
+
4. Report: structured comparison with recommendation and tradeoffs
|
|
403
|
+
5. Call **update_task** with status "done" and evaluation summary
|
|
404
|
+
6. Call **store_memory** with structured report
|
|
405
|
+
7. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
|
|
406
|
+
8. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
|
|
407
|
+
9. Check for next task \u2014 auto-chain through the queue without waiting
|
|
408
|
+
|
|
409
|
+
## Spawning Rules (mandatory)
|
|
410
|
+
- To assign work to another employee: ALWAYS use create_task. The task auto-spawns the session.
|
|
411
|
+
- NEVER manually launch sessions (tmux send-keys, claude -p). Sessions die immediately.
|
|
412
|
+
- NEVER spawn sessions without a task assigned \u2014 idle sessions waste resources.
|
|
413
|
+
- NEVER refuse a dispatched task claiming "not in scope" \u2014 if it's assigned to you, do it.
|
|
414
|
+
|
|
415
|
+
## Quality Standards
|
|
416
|
+
|
|
417
|
+
- Every recommendation includes cost/quality/latency tradeoff analysis
|
|
418
|
+
- Separate experimental from production-ready \u2014 label clearly
|
|
419
|
+
- If you can't verify, say so explicitly: "Couldn't verify because X"
|
|
301
420
|
`
|
|
302
421
|
};
|
|
303
422
|
function getTemplate(role) {
|
|
@@ -311,10 +430,12 @@ function getTemplateForTitle(title) {
|
|
|
311
430
|
if (t.includes("cmo") || t.includes("chief marketing")) return IDENTITY_TEMPLATES.cmo;
|
|
312
431
|
if (t.includes("engineer") || t.includes("developer")) return IDENTITY_TEMPLATES["principal-engineer"];
|
|
313
432
|
if (t.includes("content") || t.includes("production")) return IDENTITY_TEMPLATES["content-specialist"];
|
|
433
|
+
if (t.includes("ai") || t.includes("product lead") || t.includes("specialist") && !t.includes("content")) return IDENTITY_TEMPLATES["ai-specialist"];
|
|
314
434
|
return null;
|
|
315
435
|
}
|
|
316
436
|
export {
|
|
317
437
|
IDENTITY_TEMPLATES,
|
|
438
|
+
POST_WORK_CHECKLIST,
|
|
318
439
|
getTemplate,
|
|
319
440
|
getTemplateForTitle
|
|
320
441
|
};
|
package/dist/lib/identity.js
CHANGED
package/dist/lib/license.js
CHANGED
package/dist/lib/messaging.js
CHANGED
|
@@ -323,11 +323,12 @@ function queueIntercom(targetSession, reason) {
|
|
|
323
323
|
}
|
|
324
324
|
writeQueue(queue);
|
|
325
325
|
}
|
|
326
|
-
var QUEUE_PATH, INTERCOM_LOG;
|
|
326
|
+
var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
327
327
|
var init_intercom_queue = __esm({
|
|
328
328
|
"src/lib/intercom-queue.ts"() {
|
|
329
329
|
"use strict";
|
|
330
330
|
QUEUE_PATH = path2.join(os2.homedir(), ".exe-os", "intercom-queue.json");
|
|
331
|
+
TTL_MS = 60 * 60 * 1e3;
|
|
331
332
|
INTERCOM_LOG = path2.join(os2.homedir(), ".exe-os", "intercom.log");
|
|
332
333
|
}
|
|
333
334
|
});
|
|
@@ -419,6 +420,11 @@ var init_config = __esm({
|
|
|
419
420
|
idleKillTicksRequired: 3,
|
|
420
421
|
idleKillIntercomAckWindowMs: 1e4,
|
|
421
422
|
maxAutoInstances: 10
|
|
423
|
+
},
|
|
424
|
+
autoUpdate: {
|
|
425
|
+
checkOnBoot: true,
|
|
426
|
+
autoInstall: false,
|
|
427
|
+
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
422
428
|
}
|
|
423
429
|
};
|
|
424
430
|
}
|
|
@@ -556,6 +562,17 @@ function getGitRoot(dir) {
|
|
|
556
562
|
return null;
|
|
557
563
|
}
|
|
558
564
|
}
|
|
565
|
+
function getMainRepoRoot(dir) {
|
|
566
|
+
try {
|
|
567
|
+
const commonDir = execSync4(
|
|
568
|
+
"git rev-parse --path-format=absolute --git-common-dir",
|
|
569
|
+
{ cwd: dir, encoding: "utf-8", timeout: GIT_TIMEOUT_MS, stdio: ["pipe", "pipe", "pipe"] }
|
|
570
|
+
).trim();
|
|
571
|
+
return realpath(path7.dirname(commonDir));
|
|
572
|
+
} catch {
|
|
573
|
+
return null;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
559
576
|
function worktreePath(repoRoot, employeeName, instance) {
|
|
560
577
|
const label = instanceLabel(employeeName, instance);
|
|
561
578
|
return path7.join(repoRoot, ".worktrees", label);
|
|
@@ -781,6 +798,11 @@ function getSessionState(sessionName) {
|
|
|
781
798
|
if (!transport.isAlive(sessionName)) return "offline";
|
|
782
799
|
try {
|
|
783
800
|
const pane = transport.capturePane(sessionName, 5);
|
|
801
|
+
if (!pane.includes("\u276F") && !pane.includes("Claude Code") && !BUSY_PATTERN.test(pane) && !/Running…/.test(pane)) {
|
|
802
|
+
if (/\$\s*$/.test(pane) || /% $/.test(pane.trimEnd())) {
|
|
803
|
+
return "no_claude";
|
|
804
|
+
}
|
|
805
|
+
}
|
|
784
806
|
if (/Running…/.test(pane)) return "tool";
|
|
785
807
|
if (BUSY_PATTERN.test(pane)) return "thinking";
|
|
786
808
|
return "idle";
|
|
@@ -788,10 +810,6 @@ function getSessionState(sessionName) {
|
|
|
788
810
|
return "offline";
|
|
789
811
|
}
|
|
790
812
|
}
|
|
791
|
-
function isSessionBusy(sessionName) {
|
|
792
|
-
const state = getSessionState(sessionName);
|
|
793
|
-
return state === "thinking" || state === "tool";
|
|
794
|
-
}
|
|
795
813
|
function isExeSession(sessionName) {
|
|
796
814
|
return /^exe\d*$/.test(sessionName);
|
|
797
815
|
}
|
|
@@ -811,7 +829,14 @@ function sendIntercom(targetSession) {
|
|
|
811
829
|
logIntercom(`SKIP \u2192 ${targetSession} (session not found)`);
|
|
812
830
|
return "failed";
|
|
813
831
|
}
|
|
814
|
-
|
|
832
|
+
const sessionState = getSessionState(targetSession);
|
|
833
|
+
if (sessionState === "no_claude") {
|
|
834
|
+
queueIntercom(targetSession, "claude not running in session");
|
|
835
|
+
recordDebounce(targetSession);
|
|
836
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process \u2014 raw shell detected)`);
|
|
837
|
+
return "queued";
|
|
838
|
+
}
|
|
839
|
+
if (sessionState === "thinking" || sessionState === "tool") {
|
|
815
840
|
queueIntercom(targetSession, "session busy at send time");
|
|
816
841
|
recordDebounce(targetSession);
|
|
817
842
|
logIntercom(`QUEUED \u2192 ${targetSession} (session busy, will retry from queue)`);
|
|
@@ -823,18 +848,7 @@ function sendIntercom(targetSession) {
|
|
|
823
848
|
}
|
|
824
849
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
825
850
|
recordDebounce(targetSession);
|
|
826
|
-
|
|
827
|
-
try {
|
|
828
|
-
execSync5(`sleep ${INTERCOM_POLL_INTERVAL_S}`);
|
|
829
|
-
} catch {
|
|
830
|
-
}
|
|
831
|
-
const state = getSessionState(targetSession);
|
|
832
|
-
if (state === "thinking" || state === "tool") {
|
|
833
|
-
logIntercom(`ACKNOWLEDGED \u2192 ${targetSession} (state=${state}, poll=${i + 1})`);
|
|
834
|
-
return "acknowledged";
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (no state transition after ${INTERCOM_POLL_MAX_ATTEMPTS}s)`);
|
|
851
|
+
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
838
852
|
return "delivered";
|
|
839
853
|
} catch {
|
|
840
854
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -888,7 +902,8 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
888
902
|
return { status: "failed", sessionName, error: "intercom delivery failed" };
|
|
889
903
|
}
|
|
890
904
|
const spawnOpts = { ...opts, instance: effectiveInstance };
|
|
891
|
-
const
|
|
905
|
+
const mainRoot = getMainRepoRoot(projectDir) ?? projectDir;
|
|
906
|
+
const wtPath = ensureWorktree(mainRoot, employeeName, effectiveInstance);
|
|
892
907
|
if (wtPath) {
|
|
893
908
|
spawnOpts.cwd = wtPath;
|
|
894
909
|
}
|
|
@@ -1069,7 +1084,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1069
1084
|
let booted = false;
|
|
1070
1085
|
for (let i = 0; i < 30; i++) {
|
|
1071
1086
|
try {
|
|
1072
|
-
execSync5("sleep
|
|
1087
|
+
execSync5("sleep 0.5");
|
|
1073
1088
|
} catch {
|
|
1074
1089
|
}
|
|
1075
1090
|
try {
|
|
@@ -1089,7 +1104,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1089
1104
|
}
|
|
1090
1105
|
}
|
|
1091
1106
|
if (!booted) {
|
|
1092
|
-
return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within
|
|
1107
|
+
return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
|
|
1093
1108
|
}
|
|
1094
1109
|
if (!useExeAgent) {
|
|
1095
1110
|
try {
|
|
@@ -1107,7 +1122,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1107
1122
|
});
|
|
1108
1123
|
return { sessionName };
|
|
1109
1124
|
}
|
|
1110
|
-
var SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN
|
|
1125
|
+
var SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
1111
1126
|
var init_tmux_routing = __esm({
|
|
1112
1127
|
"src/lib/tmux-routing.ts"() {
|
|
1113
1128
|
"use strict";
|
|
@@ -1127,8 +1142,6 @@ var init_tmux_routing = __esm({
|
|
|
1127
1142
|
DEBOUNCE_FILE = path8.join(SESSION_CACHE, "intercom-debounce.json");
|
|
1128
1143
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
1129
1144
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
1130
|
-
INTERCOM_POLL_INTERVAL_S = 1;
|
|
1131
|
-
INTERCOM_POLL_MAX_ATTEMPTS = 8;
|
|
1132
1145
|
}
|
|
1133
1146
|
});
|
|
1134
1147
|
|
package/dist/lib/schedules.js
CHANGED
|
@@ -80,6 +80,11 @@ function normalizeSessionLifecycle(raw) {
|
|
|
80
80
|
const userSL = raw.sessionLifecycle ?? {};
|
|
81
81
|
raw.sessionLifecycle = { ...defaultSL, ...userSL };
|
|
82
82
|
}
|
|
83
|
+
function normalizeAutoUpdate(raw) {
|
|
84
|
+
const defaultAU = DEFAULT_CONFIG.autoUpdate;
|
|
85
|
+
const userAU = raw.autoUpdate ?? {};
|
|
86
|
+
raw.autoUpdate = { ...defaultAU, ...userAU };
|
|
87
|
+
}
|
|
83
88
|
async function loadConfig() {
|
|
84
89
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
85
90
|
await mkdir2(dir, { recursive: true });
|
|
@@ -102,6 +107,7 @@ async function loadConfig() {
|
|
|
102
107
|
}
|
|
103
108
|
normalizeScalingRoadmap(migratedCfg);
|
|
104
109
|
normalizeSessionLifecycle(migratedCfg);
|
|
110
|
+
normalizeAutoUpdate(migratedCfg);
|
|
105
111
|
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
106
112
|
if (config.dbPath.startsWith("~")) {
|
|
107
113
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
@@ -177,6 +183,11 @@ var init_config = __esm({
|
|
|
177
183
|
idleKillTicksRequired: 3,
|
|
178
184
|
idleKillIntercomAckWindowMs: 1e4,
|
|
179
185
|
maxAutoInstances: 10
|
|
186
|
+
},
|
|
187
|
+
autoUpdate: {
|
|
188
|
+
checkOnBoot: true,
|
|
189
|
+
autoInstall: false,
|
|
190
|
+
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
180
191
|
}
|
|
181
192
|
};
|
|
182
193
|
CONFIG_MIGRATIONS = [
|
|
@@ -310,13 +321,27 @@ async function ensureShardSchema(client) {
|
|
|
310
321
|
"ALTER TABLE memories ADD COLUMN document_id TEXT",
|
|
311
322
|
"ALTER TABLE memories ADD COLUMN user_id TEXT",
|
|
312
323
|
"ALTER TABLE memories ADD COLUMN char_offset INTEGER",
|
|
313
|
-
"ALTER TABLE memories ADD COLUMN page_number INTEGER"
|
|
324
|
+
"ALTER TABLE memories ADD COLUMN page_number INTEGER",
|
|
325
|
+
// Source provenance columns (must match database.ts)
|
|
326
|
+
"ALTER TABLE memories ADD COLUMN source_path TEXT",
|
|
327
|
+
"ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
|
|
328
|
+
"ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
|
|
329
|
+
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT"
|
|
314
330
|
]) {
|
|
315
331
|
try {
|
|
316
332
|
await client.execute(col);
|
|
317
333
|
} catch {
|
|
318
334
|
}
|
|
319
335
|
}
|
|
336
|
+
for (const idx of [
|
|
337
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
338
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
339
|
+
]) {
|
|
340
|
+
try {
|
|
341
|
+
await client.execute(idx);
|
|
342
|
+
} catch {
|
|
343
|
+
}
|
|
344
|
+
}
|
|
320
345
|
try {
|
|
321
346
|
await client.execute("CREATE INDEX IF NOT EXISTS idx_memories_status ON memories(status)");
|
|
322
347
|
} catch {
|
|
@@ -637,6 +662,27 @@ async function ensureSchema() {
|
|
|
637
662
|
});
|
|
638
663
|
} catch {
|
|
639
664
|
}
|
|
665
|
+
try {
|
|
666
|
+
await client.execute({
|
|
667
|
+
sql: `ALTER TABLE tasks ADD COLUMN checkpoint TEXT`,
|
|
668
|
+
args: []
|
|
669
|
+
});
|
|
670
|
+
} catch {
|
|
671
|
+
}
|
|
672
|
+
try {
|
|
673
|
+
await client.execute({
|
|
674
|
+
sql: `ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER NOT NULL DEFAULT 0`,
|
|
675
|
+
args: []
|
|
676
|
+
});
|
|
677
|
+
} catch {
|
|
678
|
+
}
|
|
679
|
+
try {
|
|
680
|
+
await client.execute({
|
|
681
|
+
sql: `ALTER TABLE tasks ADD COLUMN complexity TEXT NOT NULL DEFAULT 'standard'`,
|
|
682
|
+
args: []
|
|
683
|
+
});
|
|
684
|
+
} catch {
|
|
685
|
+
}
|
|
640
686
|
try {
|
|
641
687
|
await client.execute({
|
|
642
688
|
sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
|
|
@@ -1047,6 +1093,15 @@ async function ensureSchema() {
|
|
|
1047
1093
|
} catch {
|
|
1048
1094
|
}
|
|
1049
1095
|
}
|
|
1096
|
+
for (const col of [
|
|
1097
|
+
"ALTER TABLE memories ADD COLUMN source_path TEXT",
|
|
1098
|
+
"ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'"
|
|
1099
|
+
]) {
|
|
1100
|
+
try {
|
|
1101
|
+
await client.execute(col);
|
|
1102
|
+
} catch {
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1050
1105
|
await client.executeMultiple(`
|
|
1051
1106
|
CREATE INDEX IF NOT EXISTS idx_memories_workspace
|
|
1052
1107
|
ON memories(workspace_id);
|
|
@@ -1111,6 +1166,34 @@ async function ensureSchema() {
|
|
|
1111
1166
|
CREATE INDEX IF NOT EXISTS idx_conversations_channel
|
|
1112
1167
|
ON conversations(channel_id);
|
|
1113
1168
|
`);
|
|
1169
|
+
try {
|
|
1170
|
+
await client.execute({
|
|
1171
|
+
sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
|
|
1172
|
+
args: []
|
|
1173
|
+
});
|
|
1174
|
+
} catch {
|
|
1175
|
+
}
|
|
1176
|
+
try {
|
|
1177
|
+
await client.execute({
|
|
1178
|
+
sql: `ALTER TABLE tasks ADD COLUMN budget_fallback_model TEXT`,
|
|
1179
|
+
args: []
|
|
1180
|
+
});
|
|
1181
|
+
} catch {
|
|
1182
|
+
}
|
|
1183
|
+
try {
|
|
1184
|
+
await client.execute({
|
|
1185
|
+
sql: `ALTER TABLE tasks ADD COLUMN tokens_used INTEGER DEFAULT 0`,
|
|
1186
|
+
args: []
|
|
1187
|
+
});
|
|
1188
|
+
} catch {
|
|
1189
|
+
}
|
|
1190
|
+
try {
|
|
1191
|
+
await client.execute({
|
|
1192
|
+
sql: `ALTER TABLE tasks ADD COLUMN tokens_warned_at INTEGER`,
|
|
1193
|
+
args: []
|
|
1194
|
+
});
|
|
1195
|
+
} catch {
|
|
1196
|
+
}
|
|
1114
1197
|
await client.executeMultiple(`
|
|
1115
1198
|
CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
|
|
1116
1199
|
content_text,
|
|
@@ -1137,6 +1220,52 @@ async function ensureSchema() {
|
|
|
1137
1220
|
VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
|
|
1138
1221
|
END;
|
|
1139
1222
|
`);
|
|
1223
|
+
try {
|
|
1224
|
+
await client.execute({
|
|
1225
|
+
sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
|
|
1226
|
+
args: []
|
|
1227
|
+
});
|
|
1228
|
+
} catch {
|
|
1229
|
+
}
|
|
1230
|
+
try {
|
|
1231
|
+
await client.execute(
|
|
1232
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)`
|
|
1233
|
+
);
|
|
1234
|
+
} catch {
|
|
1235
|
+
}
|
|
1236
|
+
try {
|
|
1237
|
+
await client.execute({
|
|
1238
|
+
sql: `UPDATE memories SET tier = 1 WHERE tool_name = 'commit_to_long_term_memory' AND importance >= 8 AND tier = 3`,
|
|
1239
|
+
args: []
|
|
1240
|
+
});
|
|
1241
|
+
await client.execute({
|
|
1242
|
+
sql: `UPDATE memories SET tier = 2 WHERE tool_name IN ('store_memory', 'manual') AND importance >= 5 AND tier = 3`,
|
|
1243
|
+
args: []
|
|
1244
|
+
});
|
|
1245
|
+
} catch {
|
|
1246
|
+
}
|
|
1247
|
+
try {
|
|
1248
|
+
await client.execute({
|
|
1249
|
+
sql: `ALTER TABLE memories ADD COLUMN supersedes_id TEXT`,
|
|
1250
|
+
args: []
|
|
1251
|
+
});
|
|
1252
|
+
} catch {
|
|
1253
|
+
}
|
|
1254
|
+
try {
|
|
1255
|
+
await client.execute(
|
|
1256
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL`
|
|
1257
|
+
);
|
|
1258
|
+
} catch {
|
|
1259
|
+
}
|
|
1260
|
+
for (const col of [
|
|
1261
|
+
"ALTER TABLE tasks ADD COLUMN checkpoint TEXT",
|
|
1262
|
+
"ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER DEFAULT 0"
|
|
1263
|
+
]) {
|
|
1264
|
+
try {
|
|
1265
|
+
await client.execute(col);
|
|
1266
|
+
} catch {
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1140
1269
|
}
|
|
1141
1270
|
|
|
1142
1271
|
// src/lib/keychain.ts
|
|
@@ -108,6 +108,11 @@ var DEFAULT_CONFIG = {
|
|
|
108
108
|
idleKillTicksRequired: 3,
|
|
109
109
|
idleKillIntercomAckWindowMs: 1e4,
|
|
110
110
|
maxAutoInstances: 10
|
|
111
|
+
},
|
|
112
|
+
autoUpdate: {
|
|
113
|
+
checkOnBoot: true,
|
|
114
|
+
autoInstall: false,
|
|
115
|
+
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
111
116
|
}
|
|
112
117
|
};
|
|
113
118
|
function migrateLegacyConfig(raw) {
|
|
@@ -165,6 +170,11 @@ function normalizeSessionLifecycle(raw) {
|
|
|
165
170
|
const userSL = raw.sessionLifecycle ?? {};
|
|
166
171
|
raw.sessionLifecycle = { ...defaultSL, ...userSL };
|
|
167
172
|
}
|
|
173
|
+
function normalizeAutoUpdate(raw) {
|
|
174
|
+
const defaultAU = DEFAULT_CONFIG.autoUpdate;
|
|
175
|
+
const userAU = raw.autoUpdate ?? {};
|
|
176
|
+
raw.autoUpdate = { ...defaultAU, ...userAU };
|
|
177
|
+
}
|
|
168
178
|
async function loadConfig() {
|
|
169
179
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
170
180
|
await mkdir(dir, { recursive: true });
|
|
@@ -187,6 +197,7 @@ async function loadConfig() {
|
|
|
187
197
|
}
|
|
188
198
|
normalizeScalingRoadmap(migratedCfg);
|
|
189
199
|
normalizeSessionLifecycle(migratedCfg);
|
|
200
|
+
normalizeAutoUpdate(migratedCfg);
|
|
190
201
|
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
191
202
|
if (config.dbPath.startsWith("~")) {
|
|
192
203
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
package/dist/lib/status-brief.js
CHANGED
|
@@ -182,6 +182,11 @@ function buildTeam(employees, data) {
|
|
|
182
182
|
const role = emp.role ? ` (${emp.role})` : "";
|
|
183
183
|
lines.push(` ${emoji} ${emp.name}${role} \u2014 ${memStr}`);
|
|
184
184
|
}
|
|
185
|
+
if (data.plan) {
|
|
186
|
+
const planLabel = data.plan === "pro" ? "Solopreneur" : data.plan.charAt(0).toUpperCase() + data.plan.slice(1);
|
|
187
|
+
const limit = data.employeeLimit ?? "?";
|
|
188
|
+
lines.push(` Plan: ${planLabel} | ${employees.length}/${limit} employees | https://askexe.com`);
|
|
189
|
+
}
|
|
185
190
|
return lines;
|
|
186
191
|
}
|
|
187
192
|
function buildHealth(data) {
|