@askexenow/exe-os 0.8.83 → 0.8.86
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/dist/bin/backfill-conversations.js +746 -595
- package/dist/bin/backfill-responses.js +745 -594
- package/dist/bin/backfill-vectors.js +312 -226
- package/dist/bin/cleanup-stale-review-tasks.js +154 -21
- package/dist/bin/cli.js +14678 -12676
- package/dist/bin/exe-agent-config.js +242 -0
- package/dist/bin/exe-agent.js +100 -91
- package/dist/bin/exe-assign.js +1003 -854
- package/dist/bin/exe-boot.js +1420 -485
- package/dist/bin/exe-call.js +10 -0
- package/dist/bin/exe-cloud.js +29 -6
- package/dist/bin/exe-dispatch.js +572 -271
- package/dist/bin/exe-doctor.js +403 -6
- package/dist/bin/exe-export-behaviors.js +175 -72
- package/dist/bin/exe-forget.js +102 -3
- package/dist/bin/exe-gateway.js +796 -292
- package/dist/bin/exe-healthcheck.js +134 -1
- package/dist/bin/exe-heartbeat.js +172 -36
- package/dist/bin/exe-kill.js +175 -72
- package/dist/bin/exe-launch-agent.js +189 -76
- package/dist/bin/exe-link.js +927 -82
- package/dist/bin/exe-new-employee.js +60 -8
- package/dist/bin/exe-pending-messages.js +151 -19
- package/dist/bin/exe-pending-notifications.js +97 -2
- package/dist/bin/exe-pending-reviews.js +155 -22
- package/dist/bin/exe-rename.js +564 -23
- package/dist/bin/exe-review.js +231 -73
- package/dist/bin/exe-search.js +995 -228
- package/dist/bin/exe-session-cleanup.js +4930 -1664
- package/dist/bin/exe-settings.js +20 -5
- package/dist/bin/exe-start-codex.js +2598 -0
- package/dist/bin/exe-start.sh +15 -3
- package/dist/bin/exe-status.js +154 -21
- package/dist/bin/exe-team.js +97 -2
- package/dist/bin/git-sweep.js +1180 -363
- package/dist/bin/graph-backfill.js +175 -72
- package/dist/bin/graph-export.js +175 -72
- package/dist/bin/install.js +60 -7
- package/dist/bin/list-providers.js +1 -0
- package/dist/bin/scan-tasks.js +1185 -367
- package/dist/bin/setup.js +914 -270
- package/dist/bin/shard-migrate.js +175 -72
- package/dist/bin/update.js +1 -0
- package/dist/bin/wiki-sync.js +175 -72
- package/dist/gateway/index.js +792 -285
- package/dist/hooks/bug-report-worker.js +445 -135
- package/dist/hooks/commit-complete.js +1178 -361
- package/dist/hooks/error-recall.js +994 -228
- package/dist/hooks/ingest-worker.js +1799 -1234
- package/dist/hooks/ingest.js +3 -0
- package/dist/hooks/instructions-loaded.js +707 -97
- package/dist/hooks/notification.js +699 -89
- package/dist/hooks/post-compact.js +757 -109
- package/dist/hooks/pre-compact.js +1061 -244
- package/dist/hooks/pre-tool-use.js +787 -130
- package/dist/hooks/prompt-ingest-worker.js +242 -101
- package/dist/hooks/prompt-submit.js +1121 -299
- package/dist/hooks/response-ingest-worker.js +242 -101
- package/dist/hooks/session-end.js +4063 -397
- package/dist/hooks/session-start.js +1071 -254
- package/dist/hooks/stop.js +768 -120
- package/dist/hooks/subagent-stop.js +757 -109
- package/dist/hooks/summary-worker.js +1706 -1011
- package/dist/index.js +1821 -1098
- package/dist/lib/agent-config.js +167 -0
- package/dist/lib/cloud-sync.js +932 -88
- package/dist/lib/consolidation.js +2 -1
- package/dist/lib/database.js +642 -87
- package/dist/lib/db-daemon-client.js +503 -0
- package/dist/lib/device-registry.js +547 -7
- package/dist/lib/embedder.js +14 -28
- package/dist/lib/employee-templates.js +84 -74
- package/dist/lib/employees.js +9 -0
- package/dist/lib/exe-daemon-client.js +16 -29
- package/dist/lib/exe-daemon.js +2733 -1575
- package/dist/lib/hybrid-search.js +995 -228
- package/dist/lib/identity.js +87 -67
- package/dist/lib/keychain.js +9 -1
- package/dist/lib/messaging.js +103 -40
- package/dist/lib/reminders.js +91 -74
- package/dist/lib/runtime-table.js +16 -0
- package/dist/lib/schedules.js +96 -2
- package/dist/lib/session-wrappers.js +22 -0
- package/dist/lib/skill-learning.js +103 -85
- package/dist/lib/store.js +234 -73
- package/dist/lib/tasks.js +348 -134
- package/dist/lib/tmux-routing.js +422 -208
- package/dist/lib/token-spend.js +273 -0
- package/dist/lib/ws-client.js +11 -0
- package/dist/mcp/server.js +5742 -696
- package/dist/mcp/tools/complete-reminder.js +94 -77
- package/dist/mcp/tools/create-reminder.js +94 -77
- package/dist/mcp/tools/create-task.js +375 -152
- package/dist/mcp/tools/deactivate-behavior.js +95 -77
- package/dist/mcp/tools/list-reminders.js +94 -77
- package/dist/mcp/tools/list-tasks.js +99 -31
- package/dist/mcp/tools/send-message.js +108 -45
- package/dist/mcp/tools/update-task.js +162 -77
- package/dist/runtime/index.js +1075 -258
- package/dist/tui/App.js +1333 -506
- package/package.json +6 -1
- package/src/commands/exe/agent-config.md +27 -0
- package/src/commands/exe/cc-doctor.md +10 -0
|
@@ -262,15 +262,22 @@ function getClient() {
|
|
|
262
262
|
if (!_resilientClient) {
|
|
263
263
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
264
264
|
}
|
|
265
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
266
|
+
return _resilientClient;
|
|
267
|
+
}
|
|
268
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
269
|
+
return _daemonClient;
|
|
270
|
+
}
|
|
265
271
|
return _resilientClient;
|
|
266
272
|
}
|
|
267
|
-
var _resilientClient;
|
|
273
|
+
var _resilientClient, _daemonClient;
|
|
268
274
|
var init_database = __esm({
|
|
269
275
|
"src/lib/database.ts"() {
|
|
270
276
|
"use strict";
|
|
271
277
|
init_db_retry();
|
|
272
278
|
init_employees();
|
|
273
279
|
_resilientClient = null;
|
|
280
|
+
_daemonClient = null;
|
|
274
281
|
}
|
|
275
282
|
});
|
|
276
283
|
|
|
@@ -563,18 +570,54 @@ var init_provider_table = __esm({
|
|
|
563
570
|
}
|
|
564
571
|
});
|
|
565
572
|
|
|
566
|
-
// src/lib/
|
|
567
|
-
|
|
573
|
+
// src/lib/runtime-table.ts
|
|
574
|
+
var RUNTIME_TABLE;
|
|
575
|
+
var init_runtime_table = __esm({
|
|
576
|
+
"src/lib/runtime-table.ts"() {
|
|
577
|
+
"use strict";
|
|
578
|
+
RUNTIME_TABLE = {
|
|
579
|
+
codex: {
|
|
580
|
+
binary: "codex",
|
|
581
|
+
launchMode: "exec",
|
|
582
|
+
autoApproveFlag: "--full-auto",
|
|
583
|
+
inlineFlag: "--no-alt-screen",
|
|
584
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
585
|
+
defaultModel: "gpt-5.4"
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
// src/lib/agent-config.ts
|
|
592
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync } from "fs";
|
|
568
593
|
import path5 from "path";
|
|
594
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
595
|
+
var init_agent_config = __esm({
|
|
596
|
+
"src/lib/agent-config.ts"() {
|
|
597
|
+
"use strict";
|
|
598
|
+
init_config();
|
|
599
|
+
init_runtime_table();
|
|
600
|
+
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
601
|
+
DEFAULT_MODELS = {
|
|
602
|
+
claude: "claude-opus-4",
|
|
603
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
604
|
+
opencode: "minimax-m2.7"
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
// src/lib/intercom-queue.ts
|
|
610
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
611
|
+
import path6 from "path";
|
|
569
612
|
import os5 from "os";
|
|
570
613
|
function ensureDir() {
|
|
571
|
-
const dir =
|
|
572
|
-
if (!
|
|
614
|
+
const dir = path6.dirname(QUEUE_PATH);
|
|
615
|
+
if (!existsSync5(dir)) mkdirSync2(dir, { recursive: true });
|
|
573
616
|
}
|
|
574
617
|
function readQueue() {
|
|
575
618
|
try {
|
|
576
|
-
if (!
|
|
577
|
-
return JSON.parse(
|
|
619
|
+
if (!existsSync5(QUEUE_PATH)) return [];
|
|
620
|
+
return JSON.parse(readFileSync5(QUEUE_PATH, "utf8"));
|
|
578
621
|
} catch {
|
|
579
622
|
return [];
|
|
580
623
|
}
|
|
@@ -582,7 +625,7 @@ function readQueue() {
|
|
|
582
625
|
function writeQueue(queue) {
|
|
583
626
|
ensureDir();
|
|
584
627
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
585
|
-
|
|
628
|
+
writeFileSync3(tmp, JSON.stringify(queue, null, 2));
|
|
586
629
|
renameSync3(tmp, QUEUE_PATH);
|
|
587
630
|
}
|
|
588
631
|
function queueIntercom(targetSession, reason) {
|
|
@@ -606,31 +649,31 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
|
606
649
|
var init_intercom_queue = __esm({
|
|
607
650
|
"src/lib/intercom-queue.ts"() {
|
|
608
651
|
"use strict";
|
|
609
|
-
QUEUE_PATH =
|
|
652
|
+
QUEUE_PATH = path6.join(os5.homedir(), ".exe-os", "intercom-queue.json");
|
|
610
653
|
TTL_MS = 60 * 60 * 1e3;
|
|
611
|
-
INTERCOM_LOG =
|
|
654
|
+
INTERCOM_LOG = path6.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
612
655
|
}
|
|
613
656
|
});
|
|
614
657
|
|
|
615
658
|
// src/lib/license.ts
|
|
616
|
-
import { readFileSync as
|
|
659
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
617
660
|
import { randomUUID } from "crypto";
|
|
618
|
-
import
|
|
661
|
+
import path7 from "path";
|
|
619
662
|
import { jwtVerify, importSPKI } from "jose";
|
|
620
663
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
|
|
621
664
|
var init_license = __esm({
|
|
622
665
|
"src/lib/license.ts"() {
|
|
623
666
|
"use strict";
|
|
624
667
|
init_config();
|
|
625
|
-
LICENSE_PATH =
|
|
626
|
-
CACHE_PATH =
|
|
627
|
-
DEVICE_ID_PATH =
|
|
668
|
+
LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
|
|
669
|
+
CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
|
|
670
|
+
DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
|
|
628
671
|
}
|
|
629
672
|
});
|
|
630
673
|
|
|
631
674
|
// src/lib/plan-limits.ts
|
|
632
|
-
import { readFileSync as
|
|
633
|
-
import
|
|
675
|
+
import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
|
|
676
|
+
import path8 from "path";
|
|
634
677
|
var CACHE_PATH2;
|
|
635
678
|
var init_plan_limits = __esm({
|
|
636
679
|
"src/lib/plan-limits.ts"() {
|
|
@@ -639,13 +682,13 @@ var init_plan_limits = __esm({
|
|
|
639
682
|
init_employees();
|
|
640
683
|
init_license();
|
|
641
684
|
init_config();
|
|
642
|
-
CACHE_PATH2 =
|
|
685
|
+
CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
643
686
|
}
|
|
644
687
|
});
|
|
645
688
|
|
|
646
689
|
// src/lib/tmux-routing.ts
|
|
647
|
-
import { readFileSync as
|
|
648
|
-
import
|
|
690
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync8, appendFileSync } from "fs";
|
|
691
|
+
import path9 from "path";
|
|
649
692
|
import os6 from "os";
|
|
650
693
|
import { fileURLToPath } from "url";
|
|
651
694
|
function getMySession() {
|
|
@@ -659,7 +702,7 @@ function extractRootExe(name) {
|
|
|
659
702
|
}
|
|
660
703
|
function getParentExe(sessionKey) {
|
|
661
704
|
try {
|
|
662
|
-
const data = JSON.parse(
|
|
705
|
+
const data = JSON.parse(readFileSync8(path9.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
663
706
|
return data.parentExe || null;
|
|
664
707
|
} catch {
|
|
665
708
|
return null;
|
|
@@ -667,8 +710,8 @@ function getParentExe(sessionKey) {
|
|
|
667
710
|
}
|
|
668
711
|
function getDispatchedBy(sessionKey) {
|
|
669
712
|
try {
|
|
670
|
-
const data = JSON.parse(
|
|
671
|
-
|
|
713
|
+
const data = JSON.parse(readFileSync8(
|
|
714
|
+
path9.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
672
715
|
"utf8"
|
|
673
716
|
));
|
|
674
717
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -691,32 +734,50 @@ function resolveExeSession() {
|
|
|
691
734
|
}
|
|
692
735
|
function readDebounceState() {
|
|
693
736
|
try {
|
|
694
|
-
if (!
|
|
695
|
-
|
|
737
|
+
if (!existsSync8(DEBOUNCE_FILE)) return {};
|
|
738
|
+
const raw = JSON.parse(readFileSync8(DEBOUNCE_FILE, "utf8"));
|
|
739
|
+
const state = {};
|
|
740
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
741
|
+
if (typeof val === "number") {
|
|
742
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
743
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
744
|
+
state[key] = val;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return state;
|
|
696
748
|
} catch {
|
|
697
749
|
return {};
|
|
698
750
|
}
|
|
699
751
|
}
|
|
700
752
|
function writeDebounceState(state) {
|
|
701
753
|
try {
|
|
702
|
-
if (!
|
|
703
|
-
|
|
754
|
+
if (!existsSync8(SESSION_CACHE)) mkdirSync4(SESSION_CACHE, { recursive: true });
|
|
755
|
+
writeFileSync5(DEBOUNCE_FILE, JSON.stringify(state));
|
|
704
756
|
} catch {
|
|
705
757
|
}
|
|
706
758
|
}
|
|
707
759
|
function isDebounced(targetSession) {
|
|
708
760
|
const state = readDebounceState();
|
|
709
|
-
const
|
|
710
|
-
|
|
761
|
+
const entry = state[targetSession];
|
|
762
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
763
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
764
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
765
|
+
state[targetSession].pending++;
|
|
766
|
+
writeDebounceState(state);
|
|
767
|
+
return true;
|
|
768
|
+
}
|
|
769
|
+
return false;
|
|
711
770
|
}
|
|
712
771
|
function recordDebounce(targetSession) {
|
|
713
772
|
const state = readDebounceState();
|
|
714
|
-
state[targetSession]
|
|
773
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
774
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
715
775
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
716
776
|
for (const key of Object.keys(state)) {
|
|
717
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
777
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
718
778
|
}
|
|
719
779
|
writeDebounceState(state);
|
|
780
|
+
return batched;
|
|
720
781
|
}
|
|
721
782
|
function logIntercom(msg) {
|
|
722
783
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -757,7 +818,7 @@ function sendIntercom(targetSession) {
|
|
|
757
818
|
return "skipped_exe";
|
|
758
819
|
}
|
|
759
820
|
if (isDebounced(targetSession)) {
|
|
760
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
821
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
761
822
|
return "debounced";
|
|
762
823
|
}
|
|
763
824
|
try {
|
|
@@ -769,14 +830,14 @@ function sendIntercom(targetSession) {
|
|
|
769
830
|
const sessionState = getSessionState(targetSession);
|
|
770
831
|
if (sessionState === "no_claude") {
|
|
771
832
|
queueIntercom(targetSession, "claude not running in session");
|
|
772
|
-
recordDebounce(targetSession);
|
|
773
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
833
|
+
const batched2 = recordDebounce(targetSession);
|
|
834
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
774
835
|
return "queued";
|
|
775
836
|
}
|
|
776
837
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
777
838
|
queueIntercom(targetSession, "session busy at send time");
|
|
778
|
-
recordDebounce(targetSession);
|
|
779
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
839
|
+
const batched2 = recordDebounce(targetSession);
|
|
840
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
780
841
|
return "queued";
|
|
781
842
|
}
|
|
782
843
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -784,8 +845,8 @@ function sendIntercom(targetSession) {
|
|
|
784
845
|
transport.sendKeys(targetSession, "q");
|
|
785
846
|
}
|
|
786
847
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
787
|
-
recordDebounce(targetSession);
|
|
788
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
848
|
+
const batched = recordDebounce(targetSession);
|
|
849
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
789
850
|
return "delivered";
|
|
790
851
|
} catch {
|
|
791
852
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -824,14 +885,16 @@ var init_tmux_routing = __esm({
|
|
|
824
885
|
init_cc_agent_support();
|
|
825
886
|
init_mcp_prefix();
|
|
826
887
|
init_provider_table();
|
|
888
|
+
init_agent_config();
|
|
889
|
+
init_runtime_table();
|
|
827
890
|
init_intercom_queue();
|
|
828
891
|
init_plan_limits();
|
|
829
892
|
init_employees();
|
|
830
|
-
SPAWN_LOCK_DIR =
|
|
831
|
-
SESSION_CACHE =
|
|
893
|
+
SPAWN_LOCK_DIR = path9.join(os6.homedir(), ".exe-os", "spawn-locks");
|
|
894
|
+
SESSION_CACHE = path9.join(os6.homedir(), ".exe-os", "session-cache");
|
|
832
895
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
833
|
-
INTERCOM_LOG2 =
|
|
834
|
-
DEBOUNCE_FILE =
|
|
896
|
+
INTERCOM_LOG2 = path9.join(os6.homedir(), ".exe-os", "intercom.log");
|
|
897
|
+
DEBOUNCE_FILE = path9.join(SESSION_CACHE, "intercom-debounce.json");
|
|
835
898
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
836
899
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
837
900
|
}
|
|
@@ -863,10 +926,11 @@ var init_task_scope = __esm({
|
|
|
863
926
|
|
|
864
927
|
// src/lib/tasks-crud.ts
|
|
865
928
|
import crypto2 from "crypto";
|
|
866
|
-
import
|
|
929
|
+
import path10 from "path";
|
|
930
|
+
import os7 from "os";
|
|
867
931
|
import { execSync as execSync4 } from "child_process";
|
|
868
932
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
869
|
-
import { existsSync as
|
|
933
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
|
|
870
934
|
async function writeCheckpoint(input) {
|
|
871
935
|
const client = getClient();
|
|
872
936
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -897,6 +961,17 @@ async function writeCheckpoint(input) {
|
|
|
897
961
|
const checkpointCount = Number(countResult.rows[0]?.checkpoint_count ?? 1);
|
|
898
962
|
return { checkpointCount };
|
|
899
963
|
}
|
|
964
|
+
function buildKeywordIndex() {
|
|
965
|
+
const idx = /* @__PURE__ */ new Map();
|
|
966
|
+
for (const [role, keywords] of Object.entries(LANE_KEYWORDS)) {
|
|
967
|
+
for (const kw of keywords) {
|
|
968
|
+
const existing = idx.get(kw) ?? [];
|
|
969
|
+
existing.push(role);
|
|
970
|
+
idx.set(kw, existing);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
return idx;
|
|
974
|
+
}
|
|
900
975
|
async function resolveTask(client, identifier, scopeSession) {
|
|
901
976
|
const scope = sessionScopeFilter(scopeSession);
|
|
902
977
|
let result = await client.execute({
|
|
@@ -1065,7 +1140,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
1065
1140
|
return { row, taskFile, now, taskId };
|
|
1066
1141
|
}
|
|
1067
1142
|
}
|
|
1068
|
-
if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || input.callerAgentId
|
|
1143
|
+
if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || isCoordinatorName(input.callerAgentId))) {
|
|
1069
1144
|
process.stderr.write(
|
|
1070
1145
|
`[tasks] Assigner override: ${input.callerAgentId} reclaiming ${taskId}
|
|
1071
1146
|
`
|
|
@@ -1118,20 +1193,30 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
1118
1193
|
}
|
|
1119
1194
|
return { row, taskFile, now, taskId };
|
|
1120
1195
|
}
|
|
1121
|
-
var DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
|
|
1196
|
+
var LANE_KEYWORDS, KEYWORD_INDEX, DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
|
|
1122
1197
|
var init_tasks_crud = __esm({
|
|
1123
1198
|
"src/lib/tasks-crud.ts"() {
|
|
1124
1199
|
"use strict";
|
|
1125
1200
|
init_database();
|
|
1126
1201
|
init_task_scope();
|
|
1202
|
+
init_employees();
|
|
1203
|
+
LANE_KEYWORDS = {
|
|
1204
|
+
CMO: ["sales", "script", "pitch", "offer", "copy", "objection", "brand", "content", "seo", "marketing", "newsletter", "carousel", "social", "campaign"],
|
|
1205
|
+
CTO: ["spec", "architecture", "migration", "schema", "database", "design doc", "adr", "security audit", "tech stack"],
|
|
1206
|
+
"Principal Engineer": ["implement", "build", "fix", "commit", "refactor", "bug", "feature", "wire", "integration"],
|
|
1207
|
+
"Staff Code Reviewer": ["critique", "verdict", "review", "audit", "code quality"],
|
|
1208
|
+
"Content Production Specialist": ["render", "video", "image", "b-roll", "remotion", "animation", "thumbnail"],
|
|
1209
|
+
"AI Product Lead": ["competitive", "analysis", "benchmark", "compare", "scout", "evaluate", "poc"]
|
|
1210
|
+
};
|
|
1211
|
+
KEYWORD_INDEX = buildKeywordIndex();
|
|
1127
1212
|
DELEGATION_KEYWORDS = /parallel|delegate|wave|worktree|multi-instance/i;
|
|
1128
1213
|
TASK_ALREADY_CLAIMED_PREFIX = "TASK_ALREADY_CLAIMED";
|
|
1129
1214
|
}
|
|
1130
1215
|
});
|
|
1131
1216
|
|
|
1132
1217
|
// src/lib/tasks-review.ts
|
|
1133
|
-
import
|
|
1134
|
-
import { existsSync as
|
|
1218
|
+
import path11 from "path";
|
|
1219
|
+
import { existsSync as existsSync10, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
|
|
1135
1220
|
async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
1136
1221
|
if (String(row.assigned_by) !== "system" || !taskFile.includes("review-")) return;
|
|
1137
1222
|
try {
|
|
@@ -1156,14 +1241,14 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
1156
1241
|
if (parts.length >= 3 && parts[0] === "review") {
|
|
1157
1242
|
const agent = parts[1];
|
|
1158
1243
|
const slug = parts.slice(2).join("-");
|
|
1159
|
-
const
|
|
1244
|
+
const legacyTaskFile = `exe/${agent}/${slug}.md`;
|
|
1160
1245
|
const result = await client.execute({
|
|
1161
|
-
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE task_file = ? AND status = 'needs_review'",
|
|
1162
|
-
args: [now,
|
|
1246
|
+
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE (task_file = ? OR task_file LIKE ?) AND status = 'needs_review'",
|
|
1247
|
+
args: [now, legacyTaskFile, `tasks/%/${agent}/${slug}.md`]
|
|
1163
1248
|
});
|
|
1164
1249
|
if (result.rowsAffected > 0) {
|
|
1165
1250
|
process.stderr.write(
|
|
1166
|
-
`[review-cleanup] Cascaded original task to done
|
|
1251
|
+
`[review-cleanup] Cascaded original task to done: ${agent}/${slug}.md
|
|
1167
1252
|
`
|
|
1168
1253
|
);
|
|
1169
1254
|
}
|
|
@@ -1176,11 +1261,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
1176
1261
|
);
|
|
1177
1262
|
}
|
|
1178
1263
|
try {
|
|
1179
|
-
const cacheDir =
|
|
1180
|
-
if (
|
|
1264
|
+
const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
|
|
1265
|
+
if (existsSync10(cacheDir)) {
|
|
1181
1266
|
for (const f of readdirSync2(cacheDir)) {
|
|
1182
1267
|
if (f.startsWith("review-notified-")) {
|
|
1183
|
-
unlinkSync3(
|
|
1268
|
+
unlinkSync3(path11.join(cacheDir, f));
|
|
1184
1269
|
}
|
|
1185
1270
|
}
|
|
1186
1271
|
}
|
|
@@ -1201,7 +1286,7 @@ var init_tasks_review = __esm({
|
|
|
1201
1286
|
});
|
|
1202
1287
|
|
|
1203
1288
|
// src/lib/tasks-chain.ts
|
|
1204
|
-
import
|
|
1289
|
+
import path12 from "path";
|
|
1205
1290
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
1206
1291
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
1207
1292
|
const client = getClient();
|
|
@@ -1218,7 +1303,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
1218
1303
|
});
|
|
1219
1304
|
for (const ur of unblockedRows.rows) {
|
|
1220
1305
|
try {
|
|
1221
|
-
const ubFile =
|
|
1306
|
+
const ubFile = path12.join(baseDir, String(ur.task_file));
|
|
1222
1307
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
1223
1308
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
1224
1309
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -1621,17 +1706,17 @@ var init_skill_learning = __esm({
|
|
|
1621
1706
|
});
|
|
1622
1707
|
|
|
1623
1708
|
// src/lib/tasks.ts
|
|
1624
|
-
import
|
|
1625
|
-
import { writeFileSync as
|
|
1709
|
+
import path13 from "path";
|
|
1710
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "fs";
|
|
1626
1711
|
async function updateTask(input) {
|
|
1627
1712
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
1628
1713
|
try {
|
|
1629
1714
|
const agent = String(row.assigned_to);
|
|
1630
|
-
const cacheDir =
|
|
1631
|
-
const cachePath =
|
|
1715
|
+
const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
|
|
1716
|
+
const cachePath = path13.join(cacheDir, `current-task-${agent}.json`);
|
|
1632
1717
|
if (input.status === "in_progress") {
|
|
1633
|
-
|
|
1634
|
-
|
|
1718
|
+
mkdirSync5(cacheDir, { recursive: true });
|
|
1719
|
+
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
1635
1720
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
1636
1721
|
try {
|
|
1637
1722
|
unlinkSync4(cachePath);
|
|
@@ -1691,7 +1776,7 @@ async function updateTask(input) {
|
|
|
1691
1776
|
}
|
|
1692
1777
|
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
1693
1778
|
if (isTerminal) {
|
|
1694
|
-
const isCoordinator =
|
|
1779
|
+
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
1695
1780
|
if (!isCoordinator) {
|
|
1696
1781
|
notifyTaskDone();
|
|
1697
1782
|
}
|
|
@@ -1716,7 +1801,7 @@ async function updateTask(input) {
|
|
|
1716
1801
|
}
|
|
1717
1802
|
}
|
|
1718
1803
|
}
|
|
1719
|
-
if (input.status === "done" &&
|
|
1804
|
+
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
1720
1805
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
1721
1806
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
1722
1807
|
taskId,
|
|
@@ -1732,7 +1817,7 @@ async function updateTask(input) {
|
|
|
1732
1817
|
});
|
|
1733
1818
|
}
|
|
1734
1819
|
let nextTask;
|
|
1735
|
-
if (isTerminal &&
|
|
1820
|
+
if (isTerminal && !isCoordinatorName(String(row.assigned_to))) {
|
|
1736
1821
|
try {
|
|
1737
1822
|
nextTask = await findNextTask(String(row.assigned_to));
|
|
1738
1823
|
} catch {
|
|
@@ -1791,9 +1876,9 @@ __export(active_agent_exports, {
|
|
|
1791
1876
|
resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
|
|
1792
1877
|
writeActiveAgent: () => writeActiveAgent
|
|
1793
1878
|
});
|
|
1794
|
-
import { readFileSync as
|
|
1879
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5, readdirSync as readdirSync3 } from "fs";
|
|
1795
1880
|
import { execSync as execSync5 } from "child_process";
|
|
1796
|
-
import
|
|
1881
|
+
import path14 from "path";
|
|
1797
1882
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
1798
1883
|
if (candidate === baseName) return true;
|
|
1799
1884
|
if (!candidate.startsWith(baseName)) return false;
|
|
@@ -1837,12 +1922,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
1837
1922
|
return null;
|
|
1838
1923
|
}
|
|
1839
1924
|
function getMarkerPath() {
|
|
1840
|
-
return
|
|
1925
|
+
return path14.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
1841
1926
|
}
|
|
1842
1927
|
function writeActiveAgent(agentId, agentRole) {
|
|
1843
1928
|
try {
|
|
1844
|
-
|
|
1845
|
-
|
|
1929
|
+
mkdirSync6(CACHE_DIR, { recursive: true });
|
|
1930
|
+
writeFileSync7(
|
|
1846
1931
|
getMarkerPath(),
|
|
1847
1932
|
JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
1848
1933
|
);
|
|
@@ -1858,7 +1943,7 @@ function clearActiveAgent() {
|
|
|
1858
1943
|
function getActiveAgent() {
|
|
1859
1944
|
try {
|
|
1860
1945
|
const markerPath = getMarkerPath();
|
|
1861
|
-
const raw =
|
|
1946
|
+
const raw = readFileSync10(markerPath, "utf8");
|
|
1862
1947
|
const data = JSON.parse(raw);
|
|
1863
1948
|
if (data.agentId) {
|
|
1864
1949
|
if (data.startedAt) {
|
|
@@ -1906,14 +1991,14 @@ function getAllActiveAgents() {
|
|
|
1906
1991
|
const key = file.slice("active-agent-".length, -".json".length);
|
|
1907
1992
|
if (key === "undefined") continue;
|
|
1908
1993
|
try {
|
|
1909
|
-
const raw =
|
|
1994
|
+
const raw = readFileSync10(path14.join(CACHE_DIR, file), "utf8");
|
|
1910
1995
|
const data = JSON.parse(raw);
|
|
1911
1996
|
if (!data.agentId) continue;
|
|
1912
1997
|
if (data.startedAt) {
|
|
1913
1998
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
1914
1999
|
if (age > STALE_MS) {
|
|
1915
2000
|
try {
|
|
1916
|
-
unlinkSync5(
|
|
2001
|
+
unlinkSync5(path14.join(CACHE_DIR, file));
|
|
1917
2002
|
} catch {
|
|
1918
2003
|
}
|
|
1919
2004
|
continue;
|
|
@@ -1936,11 +2021,11 @@ function getAllActiveAgents() {
|
|
|
1936
2021
|
function cleanupSessionMarkers() {
|
|
1937
2022
|
const key = getSessionKey();
|
|
1938
2023
|
try {
|
|
1939
|
-
unlinkSync5(
|
|
2024
|
+
unlinkSync5(path14.join(CACHE_DIR, `active-agent-${key}.json`));
|
|
1940
2025
|
} catch {
|
|
1941
2026
|
}
|
|
1942
2027
|
try {
|
|
1943
|
-
unlinkSync5(
|
|
2028
|
+
unlinkSync5(path14.join(CACHE_DIR, "active-agent-undefined.json"));
|
|
1944
2029
|
} catch {
|
|
1945
2030
|
}
|
|
1946
2031
|
}
|
|
@@ -1951,7 +2036,7 @@ var init_active_agent = __esm({
|
|
|
1951
2036
|
init_config();
|
|
1952
2037
|
init_session_key2();
|
|
1953
2038
|
init_employees();
|
|
1954
|
-
CACHE_DIR =
|
|
2039
|
+
CACHE_DIR = path14.join(EXE_AI_DIR, "session-cache");
|
|
1955
2040
|
STALE_MS = 24 * 60 * 60 * 1e3;
|
|
1956
2041
|
}
|
|
1957
2042
|
});
|