@askexenow/exe-os 0.9.16 → 0.9.18
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 +1242 -909
- package/dist/bin/backfill-responses.js +1245 -912
- package/dist/bin/backfill-vectors.js +1244 -906
- package/dist/bin/cleanup-stale-review-tasks.js +1870 -417
- package/dist/bin/cli.js +217 -107
- package/dist/bin/exe-agent-config.js +2 -2
- package/dist/bin/exe-agent.js +62 -0
- package/dist/bin/exe-assign.js +346 -10
- package/dist/bin/exe-boot.js +387 -32
- package/dist/bin/exe-call.js +72 -2
- package/dist/bin/exe-cloud.js +8 -0
- package/dist/bin/exe-dispatch.js +1821 -225
- package/dist/bin/exe-doctor.js +720 -52
- package/dist/bin/exe-export-behaviors.js +1429 -148
- package/dist/bin/exe-forget.js +1408 -34
- package/dist/bin/exe-gateway.js +1629 -1295
- package/dist/bin/exe-heartbeat.js +1899 -448
- package/dist/bin/exe-kill.js +1624 -346
- package/dist/bin/exe-launch-agent.js +726 -90
- package/dist/bin/exe-link.js +27 -8
- package/dist/bin/exe-new-employee.js +75 -9
- package/dist/bin/exe-pending-messages.js +2769 -1316
- package/dist/bin/exe-pending-notifications.js +2829 -1376
- package/dist/bin/exe-pending-reviews.js +2847 -1392
- package/dist/bin/exe-rename.js +89 -8
- package/dist/bin/exe-review.js +1494 -312
- package/dist/bin/exe-search.js +1608 -1300
- package/dist/bin/exe-session-cleanup.js +194 -91
- package/dist/bin/exe-settings.js +10 -2
- package/dist/bin/exe-start-codex.js +769 -120
- package/dist/bin/exe-start-opencode.js +763 -108
- package/dist/bin/exe-status.js +1887 -434
- package/dist/bin/exe-team.js +1782 -324
- package/dist/bin/git-sweep.js +408 -33
- package/dist/bin/graph-backfill.js +681 -27
- package/dist/bin/graph-export.js +1419 -141
- package/dist/bin/install.js +4 -8
- package/dist/bin/intercom-check.js +8641 -0
- package/dist/bin/scan-tasks.js +553 -38
- package/dist/bin/setup.js +82 -10
- package/dist/bin/shard-migrate.js +682 -28
- package/dist/gateway/index.js +1629 -1295
- package/dist/hooks/bug-report-worker.js +1136 -183
- package/dist/hooks/codex-stop-task-finalizer.js +1060 -107
- package/dist/hooks/commit-complete.js +408 -33
- package/dist/hooks/error-recall.js +1608 -1300
- package/dist/hooks/ingest-worker.js +250 -7966
- package/dist/hooks/ingest.js +707 -119
- package/dist/hooks/instructions-loaded.js +383 -20
- package/dist/hooks/notification.js +383 -20
- package/dist/hooks/post-compact.js +384 -21
- package/dist/hooks/{response-ingest-worker.js → post-tool-combined.js} +4157 -1716
- package/dist/hooks/pre-compact.js +486 -45
- package/dist/hooks/pre-tool-use.js +385 -22
- package/dist/hooks/prompt-submit.js +291 -96
- package/dist/hooks/session-end.js +490 -48
- package/dist/hooks/session-start.js +236 -22
- package/dist/hooks/stop.js +192 -50
- package/dist/hooks/subagent-stop.js +95 -18
- package/dist/hooks/summary-worker.js +205 -361
- package/dist/index.js +221 -105
- package/dist/lib/agent-config.js +2 -2
- package/dist/lib/cloud-sync.js +27 -8
- package/dist/lib/consolidation.js +437 -41
- package/dist/lib/database.js +20 -8
- package/dist/lib/db-daemon-client.js +2 -2
- package/dist/lib/db.js +20 -8
- package/dist/lib/device-registry.js +27 -8
- package/dist/lib/employee-templates.js +62 -0
- package/dist/lib/employees.js +2 -2
- package/dist/lib/exe-daemon.js +6703 -6259
- package/dist/lib/hybrid-search.js +1608 -1300
- package/dist/lib/identity.js +1 -1
- package/dist/lib/messaging.js +11 -3
- package/dist/lib/reminders.js +1 -1
- package/dist/lib/schedules.js +718 -26
- package/dist/lib/session-registry.js +11 -0
- package/dist/lib/skill-learning.js +639 -9
- package/dist/lib/store.js +676 -27
- package/dist/lib/tasks.js +665 -27
- package/dist/lib/tmux-routing.js +732 -94
- package/dist/lib/token-spend.js +1 -1
- package/dist/mcp/server.js +856 -383
- package/dist/mcp/tools/complete-reminder.js +1 -1
- package/dist/mcp/tools/create-reminder.js +1 -1
- package/dist/mcp/tools/create-task.js +616 -46
- package/dist/mcp/tools/deactivate-behavior.js +9 -1
- package/dist/mcp/tools/list-reminders.js +1 -1
- package/dist/mcp/tools/list-tasks.js +10 -2
- package/dist/mcp/tools/send-message.js +11 -3
- package/dist/mcp/tools/update-task.js +677 -39
- package/dist/runtime/index.js +425 -37
- package/dist/tui/App.js +365 -34
- package/package.json +5 -2
- package/src/commands/exe/intercom.md +6 -17
- package/dist/bin/wiki-sync.js +0 -2991
- package/dist/hooks/prompt-ingest-worker.js +0 -3979
|
@@ -409,7 +409,7 @@ var init_agent_config = __esm({
|
|
|
409
409
|
init_secure_files();
|
|
410
410
|
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
411
411
|
KNOWN_RUNTIMES = {
|
|
412
|
-
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
412
|
+
claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
413
413
|
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
414
414
|
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
415
415
|
};
|
|
@@ -419,7 +419,7 @@ var init_agent_config = __esm({
|
|
|
419
419
|
opencode: "OpenCode (open source)"
|
|
420
420
|
};
|
|
421
421
|
DEFAULT_MODELS = {
|
|
422
|
-
claude: "claude-opus-4",
|
|
422
|
+
claude: "claude-opus-4.6",
|
|
423
423
|
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
424
424
|
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
425
425
|
};
|
|
@@ -703,13 +703,22 @@ var init_database_adapter = __esm({
|
|
|
703
703
|
}
|
|
704
704
|
});
|
|
705
705
|
|
|
706
|
+
// src/types/memory.ts
|
|
707
|
+
var EMBEDDING_DIM;
|
|
708
|
+
var init_memory = __esm({
|
|
709
|
+
"src/types/memory.ts"() {
|
|
710
|
+
"use strict";
|
|
711
|
+
EMBEDDING_DIM = 1024;
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
|
|
706
715
|
// src/lib/database.ts
|
|
707
716
|
import { createClient } from "@libsql/client";
|
|
708
717
|
function getClient() {
|
|
709
718
|
if (!_adapterClient) {
|
|
710
719
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
711
720
|
}
|
|
712
|
-
if (process.env.DATABASE_URL) {
|
|
721
|
+
if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
|
|
713
722
|
return _adapterClient;
|
|
714
723
|
}
|
|
715
724
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
@@ -727,6 +736,7 @@ var init_database = __esm({
|
|
|
727
736
|
init_db_retry();
|
|
728
737
|
init_employees();
|
|
729
738
|
init_database_adapter();
|
|
739
|
+
init_memory();
|
|
730
740
|
_resilientClient = null;
|
|
731
741
|
_daemonClient = null;
|
|
732
742
|
_adapterClient = null;
|
|
@@ -2252,7 +2262,7 @@ function isSessionBusy(sessionName) {
|
|
|
2252
2262
|
function isExeSession(sessionName) {
|
|
2253
2263
|
const matchesBaseWithInstance = (baseName) => sessionName === baseName || sessionName.startsWith(baseName) && /^\d+$/.test(sessionName.slice(baseName.length));
|
|
2254
2264
|
const coordinatorName = getCoordinatorName();
|
|
2255
|
-
return matchesBaseWithInstance(coordinatorName)
|
|
2265
|
+
return matchesBaseWithInstance(coordinatorName);
|
|
2256
2266
|
}
|
|
2257
2267
|
function sendIntercom(targetSession) {
|
|
2258
2268
|
const transport = getTransport();
|
|
@@ -3081,15 +3091,20 @@ async function createTaskCore(input) {
|
|
|
3081
3091
|
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
3082
3092
|
const resolved = resolveExeSession2();
|
|
3083
3093
|
if (resolved && input.projectName) {
|
|
3084
|
-
const
|
|
3085
|
-
|
|
3086
|
-
if (sessionProject && sessionProject !== input.projectName) {
|
|
3087
|
-
scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
|
|
3088
|
-
process.stderr.write(`[create_task] ${scopeMismatchWarning}
|
|
3089
|
-
`);
|
|
3090
|
-
earlySessionScope = null;
|
|
3091
|
-
} else {
|
|
3094
|
+
const isCoordinatorSession = !resolved.includes("-");
|
|
3095
|
+
if (isCoordinatorSession) {
|
|
3092
3096
|
earlySessionScope = resolved;
|
|
3097
|
+
} else {
|
|
3098
|
+
const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
|
|
3099
|
+
const sessionProject = getSessionProject2(resolved);
|
|
3100
|
+
if (sessionProject && sessionProject !== input.projectName) {
|
|
3101
|
+
scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
|
|
3102
|
+
process.stderr.write(`[create_task] ${scopeMismatchWarning}
|
|
3103
|
+
`);
|
|
3104
|
+
earlySessionScope = null;
|
|
3105
|
+
} else {
|
|
3106
|
+
earlySessionScope = resolved;
|
|
3107
|
+
}
|
|
3093
3108
|
}
|
|
3094
3109
|
} else {
|
|
3095
3110
|
earlySessionScope = resolved;
|
|
@@ -3666,6 +3681,10 @@ async function dispatchTaskToEmployee(input) {
|
|
|
3666
3681
|
if (transport.isAlive(sessionName)) {
|
|
3667
3682
|
const result = sendIntercom(sessionName);
|
|
3668
3683
|
const dispatched = result === "acknowledged" || result === "debounced" || result === "queued" ? "verified" : result === "delivered" ? "sent_unverified" : "session_dead";
|
|
3684
|
+
process.stderr.write(
|
|
3685
|
+
`[dispatch-audit] intercom \u2192 ${sessionName} | task="${input.title}" [${input.priority}] | result=${dispatched} (sendIntercom=${result})
|
|
3686
|
+
`
|
|
3687
|
+
);
|
|
3669
3688
|
return { dispatched, session: sessionName, crossProject };
|
|
3670
3689
|
} else {
|
|
3671
3690
|
const projectDir = input.projectDir ?? process.cwd();
|
|
@@ -3674,11 +3693,15 @@ async function dispatchTaskToEmployee(input) {
|
|
|
3674
3693
|
});
|
|
3675
3694
|
if (result.status === "failed") {
|
|
3676
3695
|
process.stderr.write(
|
|
3677
|
-
`[dispatch]
|
|
3696
|
+
`[dispatch-audit] SPAWN FAILED \u2192 ${input.assignedTo} | task="${input.title}" [${input.priority}] | error=${result.error}
|
|
3678
3697
|
`
|
|
3679
3698
|
);
|
|
3680
3699
|
return { dispatched: "session_missing" };
|
|
3681
3700
|
}
|
|
3701
|
+
process.stderr.write(
|
|
3702
|
+
`[dispatch-audit] SPAWNED \u2192 ${result.sessionName} | task="${input.title}" [${input.priority}]
|
|
3703
|
+
`
|
|
3704
|
+
);
|
|
3682
3705
|
return { dispatched: "spawned", session: result.sessionName, crossProject };
|
|
3683
3706
|
}
|
|
3684
3707
|
} catch {
|
|
@@ -3688,7 +3711,13 @@ async function dispatchTaskToEmployee(input) {
|
|
|
3688
3711
|
function notifyTaskDone() {
|
|
3689
3712
|
try {
|
|
3690
3713
|
const key = getSessionKey();
|
|
3691
|
-
if (key && !process.env.VITEST)
|
|
3714
|
+
if (key && !process.env.VITEST) {
|
|
3715
|
+
notifyParentExe(key);
|
|
3716
|
+
process.stderr.write(
|
|
3717
|
+
`[dispatch-audit] notifyTaskDone \u2192 parent coordinator (session=${key})
|
|
3718
|
+
`
|
|
3719
|
+
);
|
|
3720
|
+
}
|
|
3692
3721
|
} catch {
|
|
3693
3722
|
}
|
|
3694
3723
|
}
|
|
@@ -3709,16 +3738,557 @@ var init_tasks_notify = __esm({
|
|
|
3709
3738
|
}
|
|
3710
3739
|
});
|
|
3711
3740
|
|
|
3712
|
-
// src/lib/
|
|
3741
|
+
// src/lib/daemon-auth.ts
|
|
3713
3742
|
import crypto4 from "crypto";
|
|
3743
|
+
import path15 from "path";
|
|
3744
|
+
import { existsSync as existsSync13, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
|
|
3745
|
+
function normalizeToken(token) {
|
|
3746
|
+
if (!token) return null;
|
|
3747
|
+
const trimmed = token.trim();
|
|
3748
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
3749
|
+
}
|
|
3750
|
+
function readDaemonToken() {
|
|
3751
|
+
try {
|
|
3752
|
+
if (!existsSync13(DAEMON_TOKEN_PATH)) return null;
|
|
3753
|
+
return normalizeToken(readFileSync11(DAEMON_TOKEN_PATH, "utf8"));
|
|
3754
|
+
} catch {
|
|
3755
|
+
return null;
|
|
3756
|
+
}
|
|
3757
|
+
}
|
|
3758
|
+
function ensureDaemonToken(seed) {
|
|
3759
|
+
const existing = readDaemonToken();
|
|
3760
|
+
if (existing) return existing;
|
|
3761
|
+
const token = normalizeToken(seed) ?? crypto4.randomBytes(32).toString("hex");
|
|
3762
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
3763
|
+
writeFileSync7(DAEMON_TOKEN_PATH, `${token}
|
|
3764
|
+
`, "utf8");
|
|
3765
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
3766
|
+
return token;
|
|
3767
|
+
}
|
|
3768
|
+
var DAEMON_TOKEN_PATH;
|
|
3769
|
+
var init_daemon_auth = __esm({
|
|
3770
|
+
"src/lib/daemon-auth.ts"() {
|
|
3771
|
+
"use strict";
|
|
3772
|
+
init_config();
|
|
3773
|
+
init_secure_files();
|
|
3774
|
+
DAEMON_TOKEN_PATH = path15.join(EXE_AI_DIR, "exed.token");
|
|
3775
|
+
}
|
|
3776
|
+
});
|
|
3777
|
+
|
|
3778
|
+
// src/lib/exe-daemon-client.ts
|
|
3779
|
+
import net from "net";
|
|
3780
|
+
import os10 from "os";
|
|
3781
|
+
import { spawn } from "child_process";
|
|
3782
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
3783
|
+
import { existsSync as existsSync14, unlinkSync as unlinkSync5, readFileSync as readFileSync12, openSync, closeSync, statSync } from "fs";
|
|
3784
|
+
import path16 from "path";
|
|
3785
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3786
|
+
function handleData(chunk) {
|
|
3787
|
+
_buffer += chunk.toString();
|
|
3788
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
3789
|
+
_buffer = "";
|
|
3790
|
+
return;
|
|
3791
|
+
}
|
|
3792
|
+
let newlineIdx;
|
|
3793
|
+
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
3794
|
+
const line = _buffer.slice(0, newlineIdx).trim();
|
|
3795
|
+
_buffer = _buffer.slice(newlineIdx + 1);
|
|
3796
|
+
if (!line) continue;
|
|
3797
|
+
try {
|
|
3798
|
+
const response = JSON.parse(line);
|
|
3799
|
+
const id = response.id;
|
|
3800
|
+
if (!id) continue;
|
|
3801
|
+
const entry = _pending.get(id);
|
|
3802
|
+
if (entry) {
|
|
3803
|
+
clearTimeout(entry.timer);
|
|
3804
|
+
_pending.delete(id);
|
|
3805
|
+
entry.resolve(response);
|
|
3806
|
+
}
|
|
3807
|
+
} catch {
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
function cleanupStaleFiles() {
|
|
3812
|
+
if (existsSync14(PID_PATH)) {
|
|
3813
|
+
try {
|
|
3814
|
+
const pid = parseInt(readFileSync12(PID_PATH, "utf8").trim(), 10);
|
|
3815
|
+
if (pid > 0) {
|
|
3816
|
+
try {
|
|
3817
|
+
process.kill(pid, 0);
|
|
3818
|
+
return;
|
|
3819
|
+
} catch {
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
} catch {
|
|
3823
|
+
}
|
|
3824
|
+
try {
|
|
3825
|
+
unlinkSync5(PID_PATH);
|
|
3826
|
+
} catch {
|
|
3827
|
+
}
|
|
3828
|
+
try {
|
|
3829
|
+
unlinkSync5(SOCKET_PATH);
|
|
3830
|
+
} catch {
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
3834
|
+
function findPackageRoot() {
|
|
3835
|
+
let dir = path16.dirname(fileURLToPath2(import.meta.url));
|
|
3836
|
+
const { root } = path16.parse(dir);
|
|
3837
|
+
while (dir !== root) {
|
|
3838
|
+
if (existsSync14(path16.join(dir, "package.json"))) return dir;
|
|
3839
|
+
dir = path16.dirname(dir);
|
|
3840
|
+
}
|
|
3841
|
+
return null;
|
|
3842
|
+
}
|
|
3843
|
+
function getAvailableMemoryGB() {
|
|
3844
|
+
if (process.platform === "darwin") {
|
|
3845
|
+
try {
|
|
3846
|
+
const { execSync: execSync8 } = __require("child_process");
|
|
3847
|
+
const vmstat = execSync8("vm_stat", { encoding: "utf8" });
|
|
3848
|
+
const pageSize = 16384;
|
|
3849
|
+
const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
|
|
3850
|
+
const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
|
|
3851
|
+
const free = vmstat.match(/Pages free:\s+(\d+)/);
|
|
3852
|
+
const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
|
|
3853
|
+
const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
|
|
3854
|
+
const freePages = free ? parseInt(free[1], 10) : 0;
|
|
3855
|
+
const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
|
|
3856
|
+
const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
|
|
3857
|
+
return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
|
|
3858
|
+
} catch {
|
|
3859
|
+
return os10.freemem() / (1024 * 1024 * 1024);
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3862
|
+
return os10.freemem() / (1024 * 1024 * 1024);
|
|
3863
|
+
}
|
|
3864
|
+
function spawnDaemon() {
|
|
3865
|
+
const freeGB = getAvailableMemoryGB();
|
|
3866
|
+
const totalGB = os10.totalmem() / (1024 * 1024 * 1024);
|
|
3867
|
+
if (totalGB <= 8) {
|
|
3868
|
+
process.stderr.write(
|
|
3869
|
+
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
3870
|
+
`
|
|
3871
|
+
);
|
|
3872
|
+
return;
|
|
3873
|
+
}
|
|
3874
|
+
if (totalGB <= 16 && freeGB < 2) {
|
|
3875
|
+
process.stderr.write(
|
|
3876
|
+
`[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
|
|
3877
|
+
`
|
|
3878
|
+
);
|
|
3879
|
+
return;
|
|
3880
|
+
}
|
|
3881
|
+
const pkgRoot = findPackageRoot();
|
|
3882
|
+
if (!pkgRoot) {
|
|
3883
|
+
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
3884
|
+
return;
|
|
3885
|
+
}
|
|
3886
|
+
const daemonPath = path16.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
3887
|
+
if (!existsSync14(daemonPath)) {
|
|
3888
|
+
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
3889
|
+
`);
|
|
3890
|
+
return;
|
|
3891
|
+
}
|
|
3892
|
+
const resolvedPath = daemonPath;
|
|
3893
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
3894
|
+
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
3895
|
+
`);
|
|
3896
|
+
const logPath = path16.join(path16.dirname(SOCKET_PATH), "exed.log");
|
|
3897
|
+
let stderrFd = "ignore";
|
|
3898
|
+
try {
|
|
3899
|
+
stderrFd = openSync(logPath, "a");
|
|
3900
|
+
} catch {
|
|
3901
|
+
}
|
|
3902
|
+
const heapCapMB = totalGB <= 8 ? 256 : 512;
|
|
3903
|
+
const nodeArgs = [`--max-old-space-size=${heapCapMB}`, resolvedPath];
|
|
3904
|
+
const child = spawn(process.execPath, nodeArgs, {
|
|
3905
|
+
detached: true,
|
|
3906
|
+
stdio: ["ignore", "ignore", stderrFd],
|
|
3907
|
+
env: {
|
|
3908
|
+
...process.env,
|
|
3909
|
+
TMUX: void 0,
|
|
3910
|
+
// Daemon is global — must not inherit session scope
|
|
3911
|
+
TMUX_PANE: void 0,
|
|
3912
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
3913
|
+
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
3914
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
3915
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
3916
|
+
}
|
|
3917
|
+
});
|
|
3918
|
+
child.unref();
|
|
3919
|
+
if (typeof stderrFd === "number") {
|
|
3920
|
+
try {
|
|
3921
|
+
closeSync(stderrFd);
|
|
3922
|
+
} catch {
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
}
|
|
3926
|
+
function acquireSpawnLock2() {
|
|
3927
|
+
try {
|
|
3928
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
3929
|
+
closeSync(fd);
|
|
3930
|
+
return true;
|
|
3931
|
+
} catch {
|
|
3932
|
+
try {
|
|
3933
|
+
const stat = statSync(SPAWN_LOCK_PATH);
|
|
3934
|
+
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
3935
|
+
try {
|
|
3936
|
+
unlinkSync5(SPAWN_LOCK_PATH);
|
|
3937
|
+
} catch {
|
|
3938
|
+
}
|
|
3939
|
+
try {
|
|
3940
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
3941
|
+
closeSync(fd);
|
|
3942
|
+
return true;
|
|
3943
|
+
} catch {
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
} catch {
|
|
3947
|
+
}
|
|
3948
|
+
return false;
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
function releaseSpawnLock2() {
|
|
3952
|
+
try {
|
|
3953
|
+
unlinkSync5(SPAWN_LOCK_PATH);
|
|
3954
|
+
} catch {
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
function connectToSocket() {
|
|
3958
|
+
return new Promise((resolve) => {
|
|
3959
|
+
if (_socket && _connected) {
|
|
3960
|
+
resolve(true);
|
|
3961
|
+
return;
|
|
3962
|
+
}
|
|
3963
|
+
const socket = net.createConnection({ path: SOCKET_PATH });
|
|
3964
|
+
const connectTimeout = setTimeout(() => {
|
|
3965
|
+
socket.destroy();
|
|
3966
|
+
resolve(false);
|
|
3967
|
+
}, 2e3);
|
|
3968
|
+
socket.on("connect", () => {
|
|
3969
|
+
clearTimeout(connectTimeout);
|
|
3970
|
+
_socket = socket;
|
|
3971
|
+
_connected = true;
|
|
3972
|
+
_buffer = "";
|
|
3973
|
+
socket.on("data", handleData);
|
|
3974
|
+
socket.on("close", () => {
|
|
3975
|
+
_connected = false;
|
|
3976
|
+
_socket = null;
|
|
3977
|
+
for (const [id, entry] of _pending) {
|
|
3978
|
+
clearTimeout(entry.timer);
|
|
3979
|
+
_pending.delete(id);
|
|
3980
|
+
entry.resolve({ error: "Connection closed" });
|
|
3981
|
+
}
|
|
3982
|
+
});
|
|
3983
|
+
socket.on("error", () => {
|
|
3984
|
+
_connected = false;
|
|
3985
|
+
_socket = null;
|
|
3986
|
+
});
|
|
3987
|
+
resolve(true);
|
|
3988
|
+
});
|
|
3989
|
+
socket.on("error", () => {
|
|
3990
|
+
clearTimeout(connectTimeout);
|
|
3991
|
+
resolve(false);
|
|
3992
|
+
});
|
|
3993
|
+
});
|
|
3994
|
+
}
|
|
3995
|
+
async function connectEmbedDaemon() {
|
|
3996
|
+
if (_socket && _connected) return true;
|
|
3997
|
+
if (await connectToSocket()) return true;
|
|
3998
|
+
if (acquireSpawnLock2()) {
|
|
3999
|
+
try {
|
|
4000
|
+
cleanupStaleFiles();
|
|
4001
|
+
spawnDaemon();
|
|
4002
|
+
} finally {
|
|
4003
|
+
releaseSpawnLock2();
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4006
|
+
const start = Date.now();
|
|
4007
|
+
let delay = 100;
|
|
4008
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
4009
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
4010
|
+
if (await connectToSocket()) return true;
|
|
4011
|
+
delay = Math.min(delay * 2, 3e3);
|
|
4012
|
+
}
|
|
4013
|
+
return false;
|
|
4014
|
+
}
|
|
4015
|
+
function sendRequest(texts, priority) {
|
|
4016
|
+
return sendDaemonRequest({ texts, priority });
|
|
4017
|
+
}
|
|
4018
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
4019
|
+
return new Promise((resolve) => {
|
|
4020
|
+
if (!_socket || !_connected) {
|
|
4021
|
+
resolve({ error: "Not connected" });
|
|
4022
|
+
return;
|
|
4023
|
+
}
|
|
4024
|
+
const id = randomUUID2();
|
|
4025
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
4026
|
+
const timer = setTimeout(() => {
|
|
4027
|
+
_pending.delete(id);
|
|
4028
|
+
resolve({ error: "Request timeout" });
|
|
4029
|
+
}, timeoutMs);
|
|
4030
|
+
_pending.set(id, { resolve, timer });
|
|
4031
|
+
try {
|
|
4032
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
4033
|
+
} catch {
|
|
4034
|
+
clearTimeout(timer);
|
|
4035
|
+
_pending.delete(id);
|
|
4036
|
+
resolve({ error: "Write failed" });
|
|
4037
|
+
}
|
|
4038
|
+
});
|
|
4039
|
+
}
|
|
4040
|
+
async function pingDaemon() {
|
|
4041
|
+
if (!_socket || !_connected) return null;
|
|
4042
|
+
const response = await sendDaemonRequest({ type: "health" }, 5e3);
|
|
4043
|
+
if (response.health) {
|
|
4044
|
+
return response.health;
|
|
4045
|
+
}
|
|
4046
|
+
return null;
|
|
4047
|
+
}
|
|
4048
|
+
function killAndRespawnDaemon() {
|
|
4049
|
+
if (!acquireSpawnLock2()) {
|
|
4050
|
+
process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
|
|
4051
|
+
if (_socket) {
|
|
4052
|
+
_socket.destroy();
|
|
4053
|
+
_socket = null;
|
|
4054
|
+
}
|
|
4055
|
+
_connected = false;
|
|
4056
|
+
_buffer = "";
|
|
4057
|
+
return;
|
|
4058
|
+
}
|
|
4059
|
+
try {
|
|
4060
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
4061
|
+
if (existsSync14(PID_PATH)) {
|
|
4062
|
+
try {
|
|
4063
|
+
const pid = parseInt(readFileSync12(PID_PATH, "utf8").trim(), 10);
|
|
4064
|
+
if (pid > 0) {
|
|
4065
|
+
try {
|
|
4066
|
+
process.kill(pid, "SIGKILL");
|
|
4067
|
+
} catch {
|
|
4068
|
+
}
|
|
4069
|
+
}
|
|
4070
|
+
} catch {
|
|
4071
|
+
}
|
|
4072
|
+
}
|
|
4073
|
+
if (_socket) {
|
|
4074
|
+
_socket.destroy();
|
|
4075
|
+
_socket = null;
|
|
4076
|
+
}
|
|
4077
|
+
_connected = false;
|
|
4078
|
+
_buffer = "";
|
|
4079
|
+
try {
|
|
4080
|
+
unlinkSync5(PID_PATH);
|
|
4081
|
+
} catch {
|
|
4082
|
+
}
|
|
4083
|
+
try {
|
|
4084
|
+
unlinkSync5(SOCKET_PATH);
|
|
4085
|
+
} catch {
|
|
4086
|
+
}
|
|
4087
|
+
spawnDaemon();
|
|
4088
|
+
} finally {
|
|
4089
|
+
releaseSpawnLock2();
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
function isDaemonTooYoung() {
|
|
4093
|
+
try {
|
|
4094
|
+
const stat = statSync(PID_PATH);
|
|
4095
|
+
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
4096
|
+
} catch {
|
|
4097
|
+
return false;
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4100
|
+
async function retryThenRestart(doRequest, label) {
|
|
4101
|
+
const result = await doRequest();
|
|
4102
|
+
if (!result.error) {
|
|
4103
|
+
_consecutiveFailures = 0;
|
|
4104
|
+
return result;
|
|
4105
|
+
}
|
|
4106
|
+
_consecutiveFailures++;
|
|
4107
|
+
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
4108
|
+
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
4109
|
+
process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
4110
|
+
`);
|
|
4111
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
4112
|
+
if (!_connected) {
|
|
4113
|
+
if (!await connectToSocket()) continue;
|
|
4114
|
+
}
|
|
4115
|
+
const retry = await doRequest();
|
|
4116
|
+
if (!retry.error) {
|
|
4117
|
+
_consecutiveFailures = 0;
|
|
4118
|
+
return retry;
|
|
4119
|
+
}
|
|
4120
|
+
_consecutiveFailures++;
|
|
4121
|
+
}
|
|
4122
|
+
if (isDaemonTooYoung()) {
|
|
4123
|
+
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
4124
|
+
`);
|
|
4125
|
+
return { error: result.error };
|
|
4126
|
+
}
|
|
4127
|
+
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
4128
|
+
`);
|
|
4129
|
+
killAndRespawnDaemon();
|
|
4130
|
+
const start = Date.now();
|
|
4131
|
+
let delay = 200;
|
|
4132
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
4133
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
4134
|
+
if (await connectToSocket()) break;
|
|
4135
|
+
delay = Math.min(delay * 2, 3e3);
|
|
4136
|
+
}
|
|
4137
|
+
if (!_connected) return { error: "Daemon restart failed" };
|
|
4138
|
+
const final = await doRequest();
|
|
4139
|
+
if (!final.error) _consecutiveFailures = 0;
|
|
4140
|
+
return final;
|
|
4141
|
+
}
|
|
4142
|
+
async function embedViaClient(text, priority = "high") {
|
|
4143
|
+
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
4144
|
+
_requestCount++;
|
|
4145
|
+
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
4146
|
+
const health = await pingDaemon();
|
|
4147
|
+
if (!health && !isDaemonTooYoung()) {
|
|
4148
|
+
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
4149
|
+
`);
|
|
4150
|
+
killAndRespawnDaemon();
|
|
4151
|
+
const start = Date.now();
|
|
4152
|
+
let d = 200;
|
|
4153
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
4154
|
+
await new Promise((r) => setTimeout(r, d));
|
|
4155
|
+
if (await connectToSocket()) break;
|
|
4156
|
+
d = Math.min(d * 2, 3e3);
|
|
4157
|
+
}
|
|
4158
|
+
if (!_connected) return null;
|
|
4159
|
+
}
|
|
4160
|
+
}
|
|
4161
|
+
const result = await retryThenRestart(
|
|
4162
|
+
() => sendRequest([text], priority),
|
|
4163
|
+
"Embed"
|
|
4164
|
+
);
|
|
4165
|
+
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
4166
|
+
}
|
|
4167
|
+
function disconnectClient() {
|
|
4168
|
+
if (_socket) {
|
|
4169
|
+
_socket.destroy();
|
|
4170
|
+
_socket = null;
|
|
4171
|
+
}
|
|
4172
|
+
_connected = false;
|
|
4173
|
+
_buffer = "";
|
|
4174
|
+
for (const [id, entry] of _pending) {
|
|
4175
|
+
clearTimeout(entry.timer);
|
|
4176
|
+
_pending.delete(id);
|
|
4177
|
+
entry.resolve({ error: "Client disconnected" });
|
|
4178
|
+
}
|
|
4179
|
+
}
|
|
4180
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
|
|
4181
|
+
var init_exe_daemon_client = __esm({
|
|
4182
|
+
"src/lib/exe-daemon-client.ts"() {
|
|
4183
|
+
"use strict";
|
|
4184
|
+
init_config();
|
|
4185
|
+
init_daemon_auth();
|
|
4186
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path16.join(EXE_AI_DIR, "exed.sock");
|
|
4187
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path16.join(EXE_AI_DIR, "exed.pid");
|
|
4188
|
+
SPAWN_LOCK_PATH = path16.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
4189
|
+
SPAWN_LOCK_STALE_MS = 3e4;
|
|
4190
|
+
CONNECT_TIMEOUT_MS = 15e3;
|
|
4191
|
+
REQUEST_TIMEOUT_MS = 3e4;
|
|
4192
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
4193
|
+
_socket = null;
|
|
4194
|
+
_connected = false;
|
|
4195
|
+
_buffer = "";
|
|
4196
|
+
_requestCount = 0;
|
|
4197
|
+
_consecutiveFailures = 0;
|
|
4198
|
+
HEALTH_CHECK_INTERVAL = 100;
|
|
4199
|
+
MAX_RETRIES_BEFORE_RESTART = 3;
|
|
4200
|
+
RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
4201
|
+
MIN_DAEMON_AGE_MS = 3e4;
|
|
4202
|
+
_pending = /* @__PURE__ */ new Map();
|
|
4203
|
+
MAX_BUFFER = 1e7;
|
|
4204
|
+
}
|
|
4205
|
+
});
|
|
4206
|
+
|
|
4207
|
+
// src/lib/embedder.ts
|
|
4208
|
+
var embedder_exports = {};
|
|
4209
|
+
__export(embedder_exports, {
|
|
4210
|
+
disposeEmbedder: () => disposeEmbedder,
|
|
4211
|
+
embed: () => embed,
|
|
4212
|
+
embedDirect: () => embedDirect,
|
|
4213
|
+
getEmbedder: () => getEmbedder
|
|
4214
|
+
});
|
|
4215
|
+
async function getEmbedder() {
|
|
4216
|
+
const ok = await connectEmbedDaemon();
|
|
4217
|
+
if (!ok) {
|
|
4218
|
+
throw new Error(
|
|
4219
|
+
"Could not connect to embedding daemon. Ensure the model is installed (run /exe-setup)."
|
|
4220
|
+
);
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
async function embed(text) {
|
|
4224
|
+
const priority = process.env.EXE_EMBED_PRIORITY === "low" ? "low" : "high";
|
|
4225
|
+
const vector = await embedViaClient(text, priority);
|
|
4226
|
+
if (!vector) {
|
|
4227
|
+
throw new Error(
|
|
4228
|
+
"Embedding failed: daemon unavailable. Run /exe-setup to verify model installation."
|
|
4229
|
+
);
|
|
4230
|
+
}
|
|
4231
|
+
if (vector.length !== EMBEDDING_DIM) {
|
|
4232
|
+
throw new Error(
|
|
4233
|
+
`Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}. Ensure the correct Jina v5-small Q4_K_M GGUF model is installed.`
|
|
4234
|
+
);
|
|
4235
|
+
}
|
|
4236
|
+
return vector;
|
|
4237
|
+
}
|
|
4238
|
+
async function disposeEmbedder() {
|
|
4239
|
+
disconnectClient();
|
|
4240
|
+
}
|
|
4241
|
+
async function embedDirect(text) {
|
|
4242
|
+
const llamaCpp = await import("node-llama-cpp");
|
|
4243
|
+
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
4244
|
+
const { existsSync: existsSync16 } = await import("fs");
|
|
4245
|
+
const path20 = await import("path");
|
|
4246
|
+
const modelPath = path20.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
4247
|
+
if (!existsSync16(modelPath)) {
|
|
4248
|
+
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
4249
|
+
}
|
|
4250
|
+
const llama = await llamaCpp.getLlama();
|
|
4251
|
+
const model = await llama.loadModel({ modelPath });
|
|
4252
|
+
const context = await model.createEmbeddingContext();
|
|
4253
|
+
try {
|
|
4254
|
+
const embedding = await context.getEmbeddingFor(text);
|
|
4255
|
+
const vector = Array.from(embedding.vector);
|
|
4256
|
+
if (vector.length !== EMBEDDING_DIM) {
|
|
4257
|
+
throw new Error(
|
|
4258
|
+
`Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}.`
|
|
4259
|
+
);
|
|
4260
|
+
}
|
|
4261
|
+
return vector;
|
|
4262
|
+
} finally {
|
|
4263
|
+
await context.dispose();
|
|
4264
|
+
await model.dispose();
|
|
4265
|
+
}
|
|
4266
|
+
}
|
|
4267
|
+
var init_embedder = __esm({
|
|
4268
|
+
"src/lib/embedder.ts"() {
|
|
4269
|
+
"use strict";
|
|
4270
|
+
init_memory();
|
|
4271
|
+
init_exe_daemon_client();
|
|
4272
|
+
}
|
|
4273
|
+
});
|
|
4274
|
+
|
|
4275
|
+
// src/lib/behaviors.ts
|
|
4276
|
+
import crypto5 from "crypto";
|
|
3714
4277
|
async function storeBehavior(opts) {
|
|
3715
4278
|
const client = getClient();
|
|
3716
|
-
const id =
|
|
4279
|
+
const id = crypto5.randomUUID();
|
|
3717
4280
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4281
|
+
let vector = null;
|
|
4282
|
+
try {
|
|
4283
|
+
const { embed: embed2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
|
|
4284
|
+
const vec = await embed2(opts.content);
|
|
4285
|
+
vector = new Float32Array(vec);
|
|
4286
|
+
} catch {
|
|
4287
|
+
}
|
|
3718
4288
|
await client.execute({
|
|
3719
|
-
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
3720
|
-
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)`,
|
|
3721
|
-
args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now]
|
|
4289
|
+
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector)
|
|
4290
|
+
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`,
|
|
4291
|
+
args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null]
|
|
3722
4292
|
});
|
|
3723
4293
|
return id;
|
|
3724
4294
|
}
|
|
@@ -3742,7 +4312,7 @@ __export(skill_learning_exports, {
|
|
|
3742
4312
|
storeTrajectory: () => storeTrajectory,
|
|
3743
4313
|
sweepTrajectories: () => sweepTrajectories
|
|
3744
4314
|
});
|
|
3745
|
-
import
|
|
4315
|
+
import crypto6 from "crypto";
|
|
3746
4316
|
async function extractTrajectory(taskId, agentId) {
|
|
3747
4317
|
const client = getClient();
|
|
3748
4318
|
const result = await client.execute({
|
|
@@ -3771,11 +4341,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
3771
4341
|
return signature;
|
|
3772
4342
|
}
|
|
3773
4343
|
function hashSignature(signature) {
|
|
3774
|
-
return
|
|
4344
|
+
return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
3775
4345
|
}
|
|
3776
4346
|
async function storeTrajectory(opts) {
|
|
3777
4347
|
const client = getClient();
|
|
3778
|
-
const id =
|
|
4348
|
+
const id = crypto6.randomUUID();
|
|
3779
4349
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3780
4350
|
const signatureHash = hashSignature(opts.signature);
|
|
3781
4351
|
await client.execute({
|
|
@@ -4040,8 +4610,8 @@ __export(tasks_exports, {
|
|
|
4040
4610
|
updateTaskStatus: () => updateTaskStatus,
|
|
4041
4611
|
writeCheckpoint: () => writeCheckpoint
|
|
4042
4612
|
});
|
|
4043
|
-
import
|
|
4044
|
-
import { writeFileSync as
|
|
4613
|
+
import path17 from "path";
|
|
4614
|
+
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, unlinkSync as unlinkSync6 } from "fs";
|
|
4045
4615
|
async function createTask(input) {
|
|
4046
4616
|
const result = await createTaskCore(input);
|
|
4047
4617
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -4060,14 +4630,14 @@ async function updateTask(input) {
|
|
|
4060
4630
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
4061
4631
|
try {
|
|
4062
4632
|
const agent = String(row.assigned_to);
|
|
4063
|
-
const cacheDir =
|
|
4064
|
-
const cachePath =
|
|
4633
|
+
const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
|
|
4634
|
+
const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
|
|
4065
4635
|
if (input.status === "in_progress") {
|
|
4066
4636
|
mkdirSync6(cacheDir, { recursive: true });
|
|
4067
|
-
|
|
4637
|
+
writeFileSync8(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
4068
4638
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
|
|
4069
4639
|
try {
|
|
4070
|
-
|
|
4640
|
+
unlinkSync6(cachePath);
|
|
4071
4641
|
} catch {
|
|
4072
4642
|
}
|
|
4073
4643
|
}
|
|
@@ -4230,17 +4800,17 @@ __export(identity_exports, {
|
|
|
4230
4800
|
listIdentities: () => listIdentities,
|
|
4231
4801
|
updateIdentity: () => updateIdentity
|
|
4232
4802
|
});
|
|
4233
|
-
import { existsSync as
|
|
4803
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
|
|
4234
4804
|
import { readdirSync as readdirSync5 } from "fs";
|
|
4235
|
-
import
|
|
4805
|
+
import path19 from "path";
|
|
4236
4806
|
import { createHash } from "crypto";
|
|
4237
4807
|
function ensureDir2() {
|
|
4238
|
-
if (!
|
|
4808
|
+
if (!existsSync15(IDENTITY_DIR2)) {
|
|
4239
4809
|
mkdirSync8(IDENTITY_DIR2, { recursive: true });
|
|
4240
4810
|
}
|
|
4241
4811
|
}
|
|
4242
4812
|
function identityPath(agentId) {
|
|
4243
|
-
return
|
|
4813
|
+
return path19.join(IDENTITY_DIR2, `${agentId}.md`);
|
|
4244
4814
|
}
|
|
4245
4815
|
function parseFrontmatter(raw) {
|
|
4246
4816
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -4281,8 +4851,8 @@ function contentHash(content) {
|
|
|
4281
4851
|
}
|
|
4282
4852
|
function getIdentity(agentId) {
|
|
4283
4853
|
const filePath = identityPath(agentId);
|
|
4284
|
-
if (!
|
|
4285
|
-
const raw =
|
|
4854
|
+
if (!existsSync15(filePath)) return null;
|
|
4855
|
+
const raw = readFileSync14(filePath, "utf-8");
|
|
4286
4856
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
4287
4857
|
return {
|
|
4288
4858
|
agentId,
|
|
@@ -4296,7 +4866,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
4296
4866
|
ensureDir2();
|
|
4297
4867
|
const filePath = identityPath(agentId);
|
|
4298
4868
|
const hash = contentHash(content);
|
|
4299
|
-
|
|
4869
|
+
writeFileSync10(filePath, content, "utf-8");
|
|
4300
4870
|
try {
|
|
4301
4871
|
const client = getClient();
|
|
4302
4872
|
await client.execute({
|
|
@@ -4352,7 +4922,7 @@ var init_identity = __esm({
|
|
|
4352
4922
|
"use strict";
|
|
4353
4923
|
init_config();
|
|
4354
4924
|
init_database();
|
|
4355
|
-
IDENTITY_DIR2 =
|
|
4925
|
+
IDENTITY_DIR2 = path19.join(EXE_AI_DIR, "identity");
|
|
4356
4926
|
}
|
|
4357
4927
|
});
|
|
4358
4928
|
|
|
@@ -4905,10 +5475,10 @@ import { z } from "zod";
|
|
|
4905
5475
|
init_config();
|
|
4906
5476
|
init_session_key();
|
|
4907
5477
|
init_employees();
|
|
4908
|
-
import { readFileSync as
|
|
5478
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, mkdirSync as mkdirSync7, unlinkSync as unlinkSync7, readdirSync as readdirSync4 } from "fs";
|
|
4909
5479
|
import { execSync as execSync7 } from "child_process";
|
|
4910
|
-
import
|
|
4911
|
-
var CACHE_DIR =
|
|
5480
|
+
import path18 from "path";
|
|
5481
|
+
var CACHE_DIR = path18.join(EXE_AI_DIR, "session-cache");
|
|
4912
5482
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
4913
5483
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
4914
5484
|
if (candidate === baseName) return true;
|
|
@@ -4953,19 +5523,19 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
4953
5523
|
return null;
|
|
4954
5524
|
}
|
|
4955
5525
|
function getMarkerPath() {
|
|
4956
|
-
return
|
|
5526
|
+
return path18.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
4957
5527
|
}
|
|
4958
5528
|
function getActiveAgent() {
|
|
4959
5529
|
try {
|
|
4960
5530
|
const markerPath = getMarkerPath();
|
|
4961
|
-
const raw =
|
|
5531
|
+
const raw = readFileSync13(markerPath, "utf8");
|
|
4962
5532
|
const data = JSON.parse(raw);
|
|
4963
5533
|
if (data.agentId) {
|
|
4964
5534
|
if (data.startedAt) {
|
|
4965
5535
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
4966
5536
|
if (age > STALE_MS) {
|
|
4967
5537
|
try {
|
|
4968
|
-
|
|
5538
|
+
unlinkSync7(markerPath);
|
|
4969
5539
|
} catch {
|
|
4970
5540
|
}
|
|
4971
5541
|
} else {
|
|
@@ -5057,10 +5627,10 @@ function registerCreateTask(server) {
|
|
|
5057
5627
|
skipDispatch: true
|
|
5058
5628
|
});
|
|
5059
5629
|
try {
|
|
5060
|
-
const { existsSync:
|
|
5630
|
+
const { existsSync: existsSync16, mkdirSync: mkdirSync9, writeFileSync: writeFileSync11 } = await import("fs");
|
|
5061
5631
|
const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
5062
5632
|
const idPath = identityPath2(assigned_to);
|
|
5063
|
-
if (!
|
|
5633
|
+
if (!existsSync16(idPath)) {
|
|
5064
5634
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
5065
5635
|
const employees = await loadEmployees2();
|
|
5066
5636
|
const emp = employees.find((e) => e.name === assigned_to);
|
|
@@ -5069,8 +5639,8 @@ function registerCreateTask(server) {
|
|
|
5069
5639
|
const template = getTemplateForTitle2(emp.role);
|
|
5070
5640
|
if (template) {
|
|
5071
5641
|
const dir = (await import("path")).dirname(idPath);
|
|
5072
|
-
if (!
|
|
5073
|
-
|
|
5642
|
+
if (!existsSync16(dir)) mkdirSync9(dir, { recursive: true });
|
|
5643
|
+
writeFileSync11(idPath, template.replace(/^agent_id: \w+/m, `agent_id: ${assigned_to}`), "utf-8");
|
|
5074
5644
|
}
|
|
5075
5645
|
}
|
|
5076
5646
|
}
|