@askexenow/exe-os 0.9.112 → 0.9.113
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 +9 -7
- package/dist/bin/agentic-ontology-backfill.js +54 -11
- package/dist/bin/agentic-reflection-backfill.js +29 -1
- package/dist/bin/agentic-semantic-label.js +29 -1
- package/dist/bin/backfill-conversations.js +53 -10
- package/dist/bin/backfill-responses.js +54 -11
- package/dist/bin/backfill-vectors.js +29 -1
- package/dist/bin/bulk-sync-postgres.js +55 -12
- package/dist/bin/cleanup-stale-review-tasks.js +75 -15
- package/dist/bin/cli.js +293 -76
- package/dist/bin/exe-agent-config.js +7 -1
- package/dist/bin/exe-agent.js +28 -2
- package/dist/bin/exe-assign.js +54 -11
- package/dist/bin/exe-boot.js +481 -147
- package/dist/bin/exe-call.js +45 -4
- package/dist/bin/exe-cloud.js +93 -15
- package/dist/bin/exe-dispatch.js +369 -24
- package/dist/bin/exe-doctor.js +53 -10
- package/dist/bin/exe-export-behaviors.js +54 -11
- package/dist/bin/exe-forget.js +54 -11
- package/dist/bin/exe-gateway.js +128 -23
- package/dist/bin/exe-heartbeat.js +75 -15
- package/dist/bin/exe-kill.js +54 -11
- package/dist/bin/exe-launch-agent.js +70 -12
- package/dist/bin/exe-new-employee.js +175 -7
- package/dist/bin/exe-pending-messages.js +75 -15
- package/dist/bin/exe-pending-notifications.js +75 -15
- package/dist/bin/exe-pending-reviews.js +75 -15
- package/dist/bin/exe-rename.js +54 -11
- package/dist/bin/exe-review.js +54 -11
- package/dist/bin/exe-search.js +54 -11
- package/dist/bin/exe-session-cleanup.js +491 -146
- package/dist/bin/exe-settings.js +10 -4
- package/dist/bin/exe-start-codex.js +524 -245
- package/dist/bin/exe-start-opencode.js +534 -165
- package/dist/bin/exe-status.js +75 -15
- package/dist/bin/exe-support.js +1 -1
- package/dist/bin/exe-team.js +54 -11
- package/dist/bin/git-sweep.js +369 -24
- package/dist/bin/graph-backfill.js +54 -11
- package/dist/bin/graph-export.js +54 -11
- package/dist/bin/install.js +62 -4
- package/dist/bin/intercom-check.js +491 -146
- package/dist/bin/pre-publish.js +13 -1
- package/dist/bin/scan-tasks.js +369 -24
- package/dist/bin/setup.js +91 -13
- package/dist/bin/shard-migrate.js +54 -11
- package/dist/bin/stack-update.js +1 -1
- package/dist/bin/update.js +3 -3
- package/dist/gateway/index.js +128 -23
- package/dist/hooks/bug-report-worker.js +128 -23
- package/dist/hooks/codex-stop-task-finalizer.js +512 -140
- package/dist/hooks/commit-complete.js +369 -24
- package/dist/hooks/error-recall.js +54 -11
- package/dist/hooks/ingest.js +4575 -252
- package/dist/hooks/instructions-loaded.js +54 -11
- package/dist/hooks/notification.js +54 -11
- package/dist/hooks/post-compact.js +75 -15
- package/dist/hooks/post-tool-combined.js +75 -15
- package/dist/hooks/pre-compact.js +449 -104
- package/dist/hooks/pre-tool-use.js +90 -15
- package/dist/hooks/prompt-submit.js +129 -24
- package/dist/hooks/session-end.js +451 -109
- package/dist/hooks/session-start.js +104 -16
- package/dist/hooks/stop.js +74 -14
- package/dist/hooks/subagent-stop.js +75 -15
- package/dist/hooks/summary-worker.js +73 -7
- package/dist/index.js +128 -23
- package/dist/lib/agent-config.js +16 -1
- package/dist/lib/cloud-sync.js +38 -1
- package/dist/lib/consolidation.js +16 -1
- package/dist/lib/database.js +16 -0
- package/dist/lib/db.js +16 -0
- package/dist/lib/device-registry.js +16 -0
- package/dist/lib/employee-templates.js +29 -3
- package/dist/lib/employees.js +16 -1
- package/dist/lib/exe-daemon.js +268 -42
- package/dist/lib/hybrid-search.js +54 -11
- package/dist/lib/license.js +3 -3
- package/dist/lib/messaging.js +21 -4
- package/dist/lib/schedules.js +29 -1
- package/dist/lib/skill-learning.js +458 -70
- package/dist/lib/status-brief.js +14 -1
- package/dist/lib/store.js +54 -11
- package/dist/lib/tasks.js +393 -91
- package/dist/lib/tmux-routing.js +316 -14
- package/dist/mcp/server.js +169 -30
- package/dist/mcp/tools/create-task.js +75 -13
- package/dist/mcp/tools/deactivate-behavior.js +33 -24
- package/dist/mcp/tools/list-tasks.js +21 -4
- package/dist/mcp/tools/send-message.js +21 -4
- package/dist/mcp/tools/update-task.js +390 -91
- package/dist/runtime/index.js +446 -101
- package/dist/tui/App.js +208 -54
- package/package.json +1 -1
|
@@ -371,6 +371,7 @@ __export(agent_config_exports, {
|
|
|
371
371
|
getAgentRuntime: () => getAgentRuntime,
|
|
372
372
|
loadAgentConfig: () => loadAgentConfig,
|
|
373
373
|
saveAgentConfig: () => saveAgentConfig,
|
|
374
|
+
setAgentMcps: () => setAgentMcps,
|
|
374
375
|
setAgentRuntime: () => setAgentRuntime
|
|
375
376
|
});
|
|
376
377
|
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
@@ -397,7 +398,7 @@ function getAgentRuntime(agentId) {
|
|
|
397
398
|
if (orgDefault) return orgDefault;
|
|
398
399
|
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
399
400
|
}
|
|
400
|
-
function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
|
|
401
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
401
402
|
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
402
403
|
if (!knownModels) {
|
|
403
404
|
return {
|
|
@@ -412,12 +413,26 @@ function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
|
|
|
412
413
|
};
|
|
413
414
|
}
|
|
414
415
|
const config = loadAgentConfig();
|
|
416
|
+
const existing = config[agentId];
|
|
415
417
|
const entry = { runtime, model };
|
|
416
418
|
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
419
|
+
if (mcps !== void 0) {
|
|
420
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
421
|
+
} else if (existing?.mcps) {
|
|
422
|
+
entry.mcps = existing.mcps;
|
|
423
|
+
}
|
|
417
424
|
config[agentId] = entry;
|
|
418
425
|
saveAgentConfig(config);
|
|
419
426
|
return { ok: true };
|
|
420
427
|
}
|
|
428
|
+
function setAgentMcps(agentId, mcps) {
|
|
429
|
+
const config = loadAgentConfig();
|
|
430
|
+
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
431
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
432
|
+
config[agentId] = existing;
|
|
433
|
+
saveAgentConfig(config);
|
|
434
|
+
return { ok: true };
|
|
435
|
+
}
|
|
421
436
|
function clearAgentRuntime(agentId) {
|
|
422
437
|
const config = loadAgentConfig();
|
|
423
438
|
delete config[agentId];
|
|
@@ -3833,6 +3848,22 @@ async function ensureSchema() {
|
|
|
3833
3848
|
} catch (e) {
|
|
3834
3849
|
logCatchDebug("migration", e);
|
|
3835
3850
|
}
|
|
3851
|
+
try {
|
|
3852
|
+
await client.execute({
|
|
3853
|
+
sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
|
|
3854
|
+
args: []
|
|
3855
|
+
});
|
|
3856
|
+
} catch (e) {
|
|
3857
|
+
logCatchDebug("migration", e);
|
|
3858
|
+
}
|
|
3859
|
+
try {
|
|
3860
|
+
await client.execute({
|
|
3861
|
+
sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
|
|
3862
|
+
args: []
|
|
3863
|
+
});
|
|
3864
|
+
} catch (e) {
|
|
3865
|
+
logCatchDebug("migration", e);
|
|
3866
|
+
}
|
|
3836
3867
|
}
|
|
3837
3868
|
async function disposeDatabase() {
|
|
3838
3869
|
if (_walCheckpointTimer) {
|
|
@@ -3899,7 +3930,7 @@ var init_license = __esm({
|
|
|
3899
3930
|
LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
|
|
3900
3931
|
CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
3901
3932
|
DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
|
|
3902
|
-
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com
|
|
3933
|
+
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
|
|
3903
3934
|
}
|
|
3904
3935
|
});
|
|
3905
3936
|
|
|
@@ -3952,6 +3983,18 @@ function extractRootExe(name) {
|
|
|
3952
3983
|
const parts = name.split("-").filter(Boolean);
|
|
3953
3984
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
3954
3985
|
}
|
|
3986
|
+
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
3987
|
+
if (!existsSync12(SESSION_CACHE)) {
|
|
3988
|
+
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
3989
|
+
}
|
|
3990
|
+
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
3991
|
+
const filePath = path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
3992
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
3993
|
+
parentExe: rootExe,
|
|
3994
|
+
dispatchedBy: dispatchedBy || rootExe,
|
|
3995
|
+
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3996
|
+
}));
|
|
3997
|
+
}
|
|
3955
3998
|
function getParentExe(sessionKey) {
|
|
3956
3999
|
try {
|
|
3957
4000
|
const data = JSON.parse(readFileSync10(path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
@@ -3961,11 +4004,12 @@ function getParentExe(sessionKey) {
|
|
|
3961
4004
|
}
|
|
3962
4005
|
}
|
|
3963
4006
|
function resolveExeSession() {
|
|
4007
|
+
if (process.env.EXE_SESSION_NAME) {
|
|
4008
|
+
const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
4009
|
+
if (fromEnv) return fromEnv;
|
|
4010
|
+
}
|
|
3964
4011
|
const mySession = getMySession();
|
|
3965
4012
|
if (!mySession) {
|
|
3966
|
-
if (process.env.EXE_SESSION_NAME) {
|
|
3967
|
-
return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
3968
|
-
}
|
|
3969
4013
|
return null;
|
|
3970
4014
|
}
|
|
3971
4015
|
const fromSessionName = extractRootExe(mySession);
|
|
@@ -3980,6 +4024,10 @@ function resolveExeSession() {
|
|
|
3980
4024
|
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
3981
4025
|
`
|
|
3982
4026
|
);
|
|
4027
|
+
try {
|
|
4028
|
+
registerParentExe(key, fromSessionName);
|
|
4029
|
+
} catch {
|
|
4030
|
+
}
|
|
3983
4031
|
candidate = fromSessionName;
|
|
3984
4032
|
} else {
|
|
3985
4033
|
candidate = fromCache;
|
|
@@ -5327,11 +5375,17 @@ var init_platform_procedures = __esm({
|
|
|
5327
5375
|
content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
|
|
5328
5376
|
},
|
|
5329
5377
|
{
|
|
5330
|
-
title: "
|
|
5378
|
+
title: "Orchestration phase guidance \u2014 recommend, never trap",
|
|
5331
5379
|
domain: "workflow",
|
|
5332
5380
|
priority: "p1",
|
|
5333
5381
|
content: "New customers start best in Phase 1: founder \u2194 coordinator/Chief of Staff, building company context. Suggest Phase 2 executives when domain work repeats; suggest Phase 3 parallel execution only when review/permission gates are ready. This is guidance, not a blocker: users may jump phases anytime. Never overwrite their phase, role titles, identities, or custom org design."
|
|
5334
5382
|
},
|
|
5383
|
+
{
|
|
5384
|
+
title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
|
|
5385
|
+
domain: "identity",
|
|
5386
|
+
priority: "p0",
|
|
5387
|
+
content: "These procedures reference 'COO' as a shorthand for the coordinator role. This is an INTERNAL routing slot used by exe-os code (chain-of-command checks, dispatch logic, session detection). It is NOT your display title. Your actual title comes from your identity file's `title:` field \u2014 that is what you use externally: introductions, sign-offs, team comms, and any user-facing text. If your identity says `title: AI Chief of Staff`, you are the AI Chief of Staff. The routing slot stays `role: coo` for code compatibility \u2014 never rename it, but also never introduce yourself as 'COO' unless your identity file explicitly says so. The founder chose your title; respect it."
|
|
5388
|
+
},
|
|
5335
5389
|
{
|
|
5336
5390
|
title: "Single dispatch path \u2014 create_task only",
|
|
5337
5391
|
domain: "workflow",
|
|
@@ -5365,6 +5419,12 @@ var init_platform_procedures = __esm({
|
|
|
5365
5419
|
priority: "p0",
|
|
5366
5420
|
content: "NEVER: (1) Access the database directly \u2014 it's SQLCipher encrypted, always fails. Use MCP tools only. (2) Manually spawn tmux sessions \u2014 create_task handles it. (3) Run git checkout main \u2014 agents work in worktrees. (4) Modify another agent's in-progress task. (5) Push to remote \u2014 the COO reviews and pushes. (6) Skip update_task(done) \u2014 it's the ONLY way your work gets reviewed. (7) Run git init."
|
|
5367
5421
|
},
|
|
5422
|
+
{
|
|
5423
|
+
title: "Destructive operations \u2014 mandatory reviewer gate",
|
|
5424
|
+
domain: "security",
|
|
5425
|
+
priority: "p0",
|
|
5426
|
+
content: "Before ANY destructive operation (delete, remove, overwrite, drop, reset, force-push, truncate), you MUST: (1) Have your full task spec accessible \u2014 if you cannot read it, STOP and report to your reviewer. Never improvise destructive actions. (2) Confirm with your reviewer (assigned_by or COO) before executing. (3) If the task spec explicitly authorizes the operation, proceed \u2014 but log it. Violation = immediate task failure. This applies to ALL agents regardless of role."
|
|
5427
|
+
},
|
|
5368
5428
|
{
|
|
5369
5429
|
title: "Customer patch triage \u2014 upstream bug vs customization",
|
|
5370
5430
|
domain: "support",
|
|
@@ -5650,10 +5710,24 @@ function stableId(memoryId, type, content) {
|
|
|
5650
5710
|
return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
|
|
5651
5711
|
}
|
|
5652
5712
|
function cleanText(text) {
|
|
5653
|
-
|
|
5713
|
+
let cleaned = text.replace(
|
|
5714
|
+
/```(\w*)\n(.*?)(?:\n[\s\S]*?)```/g,
|
|
5715
|
+
(_m, lang, firstLine) => `[code${lang ? `:${lang}` : ""}] ${firstLine.trim()}`
|
|
5716
|
+
);
|
|
5717
|
+
cleaned = cleaned.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
5718
|
+
return cleaned;
|
|
5654
5719
|
}
|
|
5655
|
-
function
|
|
5656
|
-
|
|
5720
|
+
function splitSegments(text) {
|
|
5721
|
+
const cleaned = cleanText(text);
|
|
5722
|
+
const segments = cleaned.split(/(?<=[.!?:;])\s+|\n{2,}|(?<=\))\s+(?=[A-Z])|\s*[|│]\s*/).map((s) => s.trim()).filter((s) => s.length >= MIN_SEGMENT_CHARS && s.length <= MAX_SEGMENT_CHARS);
|
|
5723
|
+
if (segments.length === 0 && cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
5724
|
+
const lines = cleaned.split(/\n+/).map((l) => l.trim()).filter((l) => l.length >= MIN_SEGMENT_CHARS && l.length <= MAX_SEGMENT_CHARS);
|
|
5725
|
+
if (lines.length > 0) return lines;
|
|
5726
|
+
if (cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
5727
|
+
return [cleaned.slice(0, MAX_SEGMENT_CHARS)];
|
|
5728
|
+
}
|
|
5729
|
+
}
|
|
5730
|
+
return segments;
|
|
5657
5731
|
}
|
|
5658
5732
|
function inferCardType(sentence, toolName) {
|
|
5659
5733
|
const lower = sentence.toLowerCase();
|
|
@@ -5685,12 +5759,12 @@ function predicateFor(type) {
|
|
|
5685
5759
|
}
|
|
5686
5760
|
}
|
|
5687
5761
|
function extractMemoryCards(row) {
|
|
5688
|
-
const
|
|
5762
|
+
const segments = splitSegments(row.raw_text);
|
|
5689
5763
|
const cards = [];
|
|
5690
|
-
for (const sentence of
|
|
5764
|
+
for (const sentence of segments) {
|
|
5691
5765
|
const type = inferCardType(sentence, row.tool_name);
|
|
5692
5766
|
const subject = extractSubject(sentence, row.agent_id);
|
|
5693
|
-
const content = sentence.length >
|
|
5767
|
+
const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
|
|
5694
5768
|
cards.push({
|
|
5695
5769
|
id: stableId(row.id, type, content),
|
|
5696
5770
|
memory_id: row.id,
|
|
@@ -5786,13 +5860,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
|
|
|
5786
5860
|
last_accessed: String(row.timestamp)
|
|
5787
5861
|
}));
|
|
5788
5862
|
}
|
|
5789
|
-
var MAX_CARDS_PER_MEMORY,
|
|
5863
|
+
var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
|
|
5790
5864
|
var init_memory_cards = __esm({
|
|
5791
5865
|
"src/lib/memory-cards.ts"() {
|
|
5792
5866
|
"use strict";
|
|
5793
5867
|
init_database();
|
|
5794
|
-
MAX_CARDS_PER_MEMORY =
|
|
5795
|
-
|
|
5868
|
+
MAX_CARDS_PER_MEMORY = 8;
|
|
5869
|
+
MAX_SEGMENT_CHARS = 500;
|
|
5870
|
+
MIN_SEGMENT_CHARS = 20;
|
|
5796
5871
|
}
|
|
5797
5872
|
});
|
|
5798
5873
|
|
|
@@ -432,6 +432,7 @@ __export(agent_config_exports, {
|
|
|
432
432
|
getAgentRuntime: () => getAgentRuntime,
|
|
433
433
|
loadAgentConfig: () => loadAgentConfig,
|
|
434
434
|
saveAgentConfig: () => saveAgentConfig,
|
|
435
|
+
setAgentMcps: () => setAgentMcps,
|
|
435
436
|
setAgentRuntime: () => setAgentRuntime
|
|
436
437
|
});
|
|
437
438
|
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
@@ -458,7 +459,7 @@ function getAgentRuntime(agentId) {
|
|
|
458
459
|
if (orgDefault) return orgDefault;
|
|
459
460
|
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
460
461
|
}
|
|
461
|
-
function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
|
|
462
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
462
463
|
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
463
464
|
if (!knownModels) {
|
|
464
465
|
return {
|
|
@@ -473,12 +474,26 @@ function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
|
|
|
473
474
|
};
|
|
474
475
|
}
|
|
475
476
|
const config = loadAgentConfig();
|
|
477
|
+
const existing = config[agentId];
|
|
476
478
|
const entry = { runtime, model };
|
|
477
479
|
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
480
|
+
if (mcps !== void 0) {
|
|
481
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
482
|
+
} else if (existing?.mcps) {
|
|
483
|
+
entry.mcps = existing.mcps;
|
|
484
|
+
}
|
|
478
485
|
config[agentId] = entry;
|
|
479
486
|
saveAgentConfig(config);
|
|
480
487
|
return { ok: true };
|
|
481
488
|
}
|
|
489
|
+
function setAgentMcps(agentId, mcps) {
|
|
490
|
+
const config = loadAgentConfig();
|
|
491
|
+
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
492
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
493
|
+
config[agentId] = existing;
|
|
494
|
+
saveAgentConfig(config);
|
|
495
|
+
return { ok: true };
|
|
496
|
+
}
|
|
482
497
|
function clearAgentRuntime(agentId) {
|
|
483
498
|
const config = loadAgentConfig();
|
|
484
499
|
delete config[agentId];
|
|
@@ -3659,6 +3674,22 @@ async function ensureSchema() {
|
|
|
3659
3674
|
} catch (e) {
|
|
3660
3675
|
logCatchDebug("migration", e);
|
|
3661
3676
|
}
|
|
3677
|
+
try {
|
|
3678
|
+
await client.execute({
|
|
3679
|
+
sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
|
|
3680
|
+
args: []
|
|
3681
|
+
});
|
|
3682
|
+
} catch (e) {
|
|
3683
|
+
logCatchDebug("migration", e);
|
|
3684
|
+
}
|
|
3685
|
+
try {
|
|
3686
|
+
await client.execute({
|
|
3687
|
+
sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
|
|
3688
|
+
args: []
|
|
3689
|
+
});
|
|
3690
|
+
} catch (e) {
|
|
3691
|
+
logCatchDebug("migration", e);
|
|
3692
|
+
}
|
|
3662
3693
|
}
|
|
3663
3694
|
async function disposeDatabase() {
|
|
3664
3695
|
if (_walCheckpointTimer) {
|
|
@@ -4782,11 +4813,17 @@ var init_platform_procedures = __esm({
|
|
|
4782
4813
|
content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
|
|
4783
4814
|
},
|
|
4784
4815
|
{
|
|
4785
|
-
title: "
|
|
4816
|
+
title: "Orchestration phase guidance \u2014 recommend, never trap",
|
|
4786
4817
|
domain: "workflow",
|
|
4787
4818
|
priority: "p1",
|
|
4788
4819
|
content: "New customers start best in Phase 1: founder \u2194 coordinator/Chief of Staff, building company context. Suggest Phase 2 executives when domain work repeats; suggest Phase 3 parallel execution only when review/permission gates are ready. This is guidance, not a blocker: users may jump phases anytime. Never overwrite their phase, role titles, identities, or custom org design."
|
|
4789
4820
|
},
|
|
4821
|
+
{
|
|
4822
|
+
title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
|
|
4823
|
+
domain: "identity",
|
|
4824
|
+
priority: "p0",
|
|
4825
|
+
content: "These procedures reference 'COO' as a shorthand for the coordinator role. This is an INTERNAL routing slot used by exe-os code (chain-of-command checks, dispatch logic, session detection). It is NOT your display title. Your actual title comes from your identity file's `title:` field \u2014 that is what you use externally: introductions, sign-offs, team comms, and any user-facing text. If your identity says `title: AI Chief of Staff`, you are the AI Chief of Staff. The routing slot stays `role: coo` for code compatibility \u2014 never rename it, but also never introduce yourself as 'COO' unless your identity file explicitly says so. The founder chose your title; respect it."
|
|
4826
|
+
},
|
|
4790
4827
|
{
|
|
4791
4828
|
title: "Single dispatch path \u2014 create_task only",
|
|
4792
4829
|
domain: "workflow",
|
|
@@ -4820,6 +4857,12 @@ var init_platform_procedures = __esm({
|
|
|
4820
4857
|
priority: "p0",
|
|
4821
4858
|
content: "NEVER: (1) Access the database directly \u2014 it's SQLCipher encrypted, always fails. Use MCP tools only. (2) Manually spawn tmux sessions \u2014 create_task handles it. (3) Run git checkout main \u2014 agents work in worktrees. (4) Modify another agent's in-progress task. (5) Push to remote \u2014 the COO reviews and pushes. (6) Skip update_task(done) \u2014 it's the ONLY way your work gets reviewed. (7) Run git init."
|
|
4822
4859
|
},
|
|
4860
|
+
{
|
|
4861
|
+
title: "Destructive operations \u2014 mandatory reviewer gate",
|
|
4862
|
+
domain: "security",
|
|
4863
|
+
priority: "p0",
|
|
4864
|
+
content: "Before ANY destructive operation (delete, remove, overwrite, drop, reset, force-push, truncate), you MUST: (1) Have your full task spec accessible \u2014 if you cannot read it, STOP and report to your reviewer. Never improvise destructive actions. (2) Confirm with your reviewer (assigned_by or COO) before executing. (3) If the task spec explicitly authorizes the operation, proceed \u2014 but log it. Violation = immediate task failure. This applies to ALL agents regardless of role."
|
|
4865
|
+
},
|
|
4823
4866
|
{
|
|
4824
4867
|
title: "Customer patch triage \u2014 upstream bug vs customization",
|
|
4825
4868
|
domain: "support",
|
|
@@ -5105,10 +5148,24 @@ function stableId(memoryId, type, content) {
|
|
|
5105
5148
|
return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
|
|
5106
5149
|
}
|
|
5107
5150
|
function cleanText(text) {
|
|
5108
|
-
|
|
5151
|
+
let cleaned = text.replace(
|
|
5152
|
+
/```(\w*)\n(.*?)(?:\n[\s\S]*?)```/g,
|
|
5153
|
+
(_m, lang, firstLine) => `[code${lang ? `:${lang}` : ""}] ${firstLine.trim()}`
|
|
5154
|
+
);
|
|
5155
|
+
cleaned = cleaned.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
5156
|
+
return cleaned;
|
|
5109
5157
|
}
|
|
5110
|
-
function
|
|
5111
|
-
|
|
5158
|
+
function splitSegments(text) {
|
|
5159
|
+
const cleaned = cleanText(text);
|
|
5160
|
+
const segments = cleaned.split(/(?<=[.!?:;])\s+|\n{2,}|(?<=\))\s+(?=[A-Z])|\s*[|│]\s*/).map((s) => s.trim()).filter((s) => s.length >= MIN_SEGMENT_CHARS && s.length <= MAX_SEGMENT_CHARS);
|
|
5161
|
+
if (segments.length === 0 && cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
5162
|
+
const lines = cleaned.split(/\n+/).map((l) => l.trim()).filter((l) => l.length >= MIN_SEGMENT_CHARS && l.length <= MAX_SEGMENT_CHARS);
|
|
5163
|
+
if (lines.length > 0) return lines;
|
|
5164
|
+
if (cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
5165
|
+
return [cleaned.slice(0, MAX_SEGMENT_CHARS)];
|
|
5166
|
+
}
|
|
5167
|
+
}
|
|
5168
|
+
return segments;
|
|
5112
5169
|
}
|
|
5113
5170
|
function inferCardType(sentence, toolName) {
|
|
5114
5171
|
const lower = sentence.toLowerCase();
|
|
@@ -5140,12 +5197,12 @@ function predicateFor(type) {
|
|
|
5140
5197
|
}
|
|
5141
5198
|
}
|
|
5142
5199
|
function extractMemoryCards(row) {
|
|
5143
|
-
const
|
|
5200
|
+
const segments = splitSegments(row.raw_text);
|
|
5144
5201
|
const cards = [];
|
|
5145
|
-
for (const sentence of
|
|
5202
|
+
for (const sentence of segments) {
|
|
5146
5203
|
const type = inferCardType(sentence, row.tool_name);
|
|
5147
5204
|
const subject = extractSubject(sentence, row.agent_id);
|
|
5148
|
-
const content = sentence.length >
|
|
5205
|
+
const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
|
|
5149
5206
|
cards.push({
|
|
5150
5207
|
id: stableId(row.id, type, content),
|
|
5151
5208
|
memory_id: row.id,
|
|
@@ -5241,13 +5298,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
|
|
|
5241
5298
|
last_accessed: String(row.timestamp)
|
|
5242
5299
|
}));
|
|
5243
5300
|
}
|
|
5244
|
-
var MAX_CARDS_PER_MEMORY,
|
|
5301
|
+
var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
|
|
5245
5302
|
var init_memory_cards = __esm({
|
|
5246
5303
|
"src/lib/memory-cards.ts"() {
|
|
5247
5304
|
"use strict";
|
|
5248
5305
|
init_database();
|
|
5249
|
-
MAX_CARDS_PER_MEMORY =
|
|
5250
|
-
|
|
5306
|
+
MAX_CARDS_PER_MEMORY = 8;
|
|
5307
|
+
MAX_SEGMENT_CHARS = 500;
|
|
5308
|
+
MIN_SEGMENT_CHARS = 20;
|
|
5251
5309
|
}
|
|
5252
5310
|
});
|
|
5253
5311
|
|
|
@@ -8151,7 +8209,7 @@ async function assertVpsLicense(opts) {
|
|
|
8151
8209
|
}
|
|
8152
8210
|
if (!transientFailure) {
|
|
8153
8211
|
throw new Error(
|
|
8154
|
-
"License validation failed: unknown backend state. Restore network connectivity to https://askexe.com
|
|
8212
|
+
"License validation failed: unknown backend state. Restore network connectivity to https://cloud.askexe.com and retry."
|
|
8155
8213
|
);
|
|
8156
8214
|
}
|
|
8157
8215
|
const fresh = await getCachedLicense();
|
|
@@ -8188,7 +8246,7 @@ async function assertVpsLicense(opts) {
|
|
|
8188
8246
|
} catch {
|
|
8189
8247
|
}
|
|
8190
8248
|
throw new Error(
|
|
8191
|
-
`License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com
|
|
8249
|
+
`License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://cloud.askexe.com and retry. This VPS image refuses to boot after the offline grace window.`
|
|
8192
8250
|
);
|
|
8193
8251
|
}
|
|
8194
8252
|
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
@@ -8220,7 +8278,7 @@ var init_license = __esm({
|
|
|
8220
8278
|
LICENSE_PATH = path16.join(EXE_AI_DIR, "license.key");
|
|
8221
8279
|
CACHE_PATH = path16.join(EXE_AI_DIR, "license-cache.json");
|
|
8222
8280
|
DEVICE_ID_PATH = path16.join(EXE_AI_DIR, "device-id");
|
|
8223
|
-
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com
|
|
8281
|
+
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
|
|
8224
8282
|
RETRY_DELAY_MS = 500;
|
|
8225
8283
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
8226
8284
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
@@ -8661,6 +8719,19 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
8661
8719
|
args: [identifier, ...scope.args]
|
|
8662
8720
|
});
|
|
8663
8721
|
if (result.rows.length === 1) return result.rows[0];
|
|
8722
|
+
if (/^[a-f0-9]{7,12}$/i.test(identifier)) {
|
|
8723
|
+
result = await client.execute({
|
|
8724
|
+
sql: `SELECT * FROM tasks WHERE id LIKE ?`,
|
|
8725
|
+
args: [`${identifier}%`]
|
|
8726
|
+
});
|
|
8727
|
+
if (result.rows.length === 1) return result.rows[0];
|
|
8728
|
+
if (result.rows.length > 1) {
|
|
8729
|
+
const matches = result.rows.map((r) => `${String(r.id)} "${String(r.title)}" (${String(r.status)})`).join(", ");
|
|
8730
|
+
throw new Error(
|
|
8731
|
+
`Multiple tasks match short-ID "${identifier}": ${matches}. Use a longer prefix to disambiguate.`
|
|
8732
|
+
);
|
|
8733
|
+
}
|
|
8734
|
+
}
|
|
8664
8735
|
result = await client.execute({
|
|
8665
8736
|
sql: `SELECT * FROM tasks WHERE task_file LIKE ?${scope.sql}`,
|
|
8666
8737
|
args: [`%${identifier}%`, ...scope.args]
|
|
@@ -9515,12 +9586,13 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
9515
9586
|
WHERE blocked_by = ? AND status = 'blocked'`,
|
|
9516
9587
|
args: [now, taskId]
|
|
9517
9588
|
});
|
|
9518
|
-
if (
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
9522
|
-
|
|
9523
|
-
|
|
9589
|
+
if (unblocked.rowsAffected === 0) return;
|
|
9590
|
+
const ubScope = sessionScopeFilter();
|
|
9591
|
+
const unblockedRows = await client.execute({
|
|
9592
|
+
sql: `SELECT id, title, assigned_to, priority, task_file FROM tasks WHERE blocked_by IS NULL AND updated_at = ?${ubScope.sql}`,
|
|
9593
|
+
args: [now, ...ubScope.args]
|
|
9594
|
+
});
|
|
9595
|
+
if (baseDir) {
|
|
9524
9596
|
for (const ur of unblockedRows.rows) {
|
|
9525
9597
|
try {
|
|
9526
9598
|
const ubFile = path22.join(baseDir, String(ur.task_file));
|
|
@@ -9532,6 +9604,19 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
9532
9604
|
}
|
|
9533
9605
|
}
|
|
9534
9606
|
}
|
|
9607
|
+
if (unblockedRows.rows.length > 0 && !process.env.VITEST) {
|
|
9608
|
+
try {
|
|
9609
|
+
const { queueIntercom: queueIntercom2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
9610
|
+
const dispatched = /* @__PURE__ */ new Set();
|
|
9611
|
+
for (const ur of unblockedRows.rows) {
|
|
9612
|
+
const assignee = String(ur.assigned_to);
|
|
9613
|
+
if (dispatched.has(assignee)) continue;
|
|
9614
|
+
dispatched.add(assignee);
|
|
9615
|
+
queueIntercom2(`${assignee}`, `unblocked: "${String(ur.title)}" is now ready`);
|
|
9616
|
+
}
|
|
9617
|
+
} catch {
|
|
9618
|
+
}
|
|
9619
|
+
}
|
|
9535
9620
|
}
|
|
9536
9621
|
async function findNextTask(assignedTo) {
|
|
9537
9622
|
const client = getClient();
|
|
@@ -9680,6 +9765,15 @@ __export(behaviors_exports, {
|
|
|
9680
9765
|
});
|
|
9681
9766
|
import crypto6 from "crypto";
|
|
9682
9767
|
async function storeBehavior(opts) {
|
|
9768
|
+
try {
|
|
9769
|
+
const { loadEmployeesSync: loadEmployeesSync2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
9770
|
+
const roster = loadEmployeesSync2();
|
|
9771
|
+
if (roster.length > 0 && !roster.some((e) => e.name === opts.agentId)) {
|
|
9772
|
+
throw new Error(`Agent "${opts.agentId}" not found in roster. Cannot store behavior for unregistered agent.`);
|
|
9773
|
+
}
|
|
9774
|
+
} catch (e) {
|
|
9775
|
+
if (e instanceof Error && e.message.includes("not found in roster")) throw e;
|
|
9776
|
+
}
|
|
9683
9777
|
const client = getClient();
|
|
9684
9778
|
const id = crypto6.randomUUID();
|
|
9685
9779
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -10203,6 +10297,12 @@ async function updateTask(input2) {
|
|
|
10203
10297
|
}
|
|
10204
10298
|
}
|
|
10205
10299
|
}
|
|
10300
|
+
if (input2.status === "cancelled") {
|
|
10301
|
+
try {
|
|
10302
|
+
await cascadeUnblock(taskId, input2.baseDir, now);
|
|
10303
|
+
} catch {
|
|
10304
|
+
}
|
|
10305
|
+
}
|
|
10206
10306
|
if ((input2.status === "done" || input2.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
10207
10307
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
10208
10308
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
@@ -10734,11 +10834,12 @@ function getDispatchedBy(sessionKey) {
|
|
|
10734
10834
|
}
|
|
10735
10835
|
}
|
|
10736
10836
|
function resolveExeSession() {
|
|
10837
|
+
if (process.env.EXE_SESSION_NAME) {
|
|
10838
|
+
const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
10839
|
+
if (fromEnv) return fromEnv;
|
|
10840
|
+
}
|
|
10737
10841
|
const mySession = getMySession();
|
|
10738
10842
|
if (!mySession) {
|
|
10739
|
-
if (process.env.EXE_SESSION_NAME) {
|
|
10740
|
-
return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
10741
|
-
}
|
|
10742
10843
|
return null;
|
|
10743
10844
|
}
|
|
10744
10845
|
const fromSessionName = extractRootExe(mySession);
|
|
@@ -10753,6 +10854,10 @@ function resolveExeSession() {
|
|
|
10753
10854
|
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
10754
10855
|
`
|
|
10755
10856
|
);
|
|
10857
|
+
try {
|
|
10858
|
+
registerParentExe(key, fromSessionName);
|
|
10859
|
+
} catch {
|
|
10860
|
+
}
|
|
10756
10861
|
candidate = fromSessionName;
|
|
10757
10862
|
} else {
|
|
10758
10863
|
candidate = fromCache;
|
|
@@ -12214,7 +12319,7 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
12214
12319
|
try {
|
|
12215
12320
|
const client = getClient();
|
|
12216
12321
|
void client.execute({
|
|
12217
|
-
sql: `UPDATE memories SET last_accessed = ?, retrieval_count = COALESCE(retrieval_count, 0) + 1 WHERE id IN (${placeholders})`,
|
|
12322
|
+
sql: `UPDATE memories SET last_accessed = ?, retrieval_count = COALESCE(retrieval_count, 0) + 1, strength = MIN(1.0, COALESCE(strength, 1.0) + 0.1) WHERE id IN (${placeholders})`,
|
|
12218
12323
|
args: [now, ...ids]
|
|
12219
12324
|
}).catch(() => {
|
|
12220
12325
|
});
|