@askexenow/exe-os 0.9.111 → 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 +62 -12
- package/dist/bin/agentic-reflection-backfill.js +37 -2
- package/dist/bin/agentic-semantic-label.js +37 -2
- package/dist/bin/backfill-conversations.js +61 -11
- package/dist/bin/backfill-responses.js +62 -12
- package/dist/bin/backfill-vectors.js +37 -2
- package/dist/bin/bulk-sync-postgres.js +63 -13
- package/dist/bin/cleanup-stale-review-tasks.js +83 -16
- package/dist/bin/cli.js +312 -80
- package/dist/bin/exe-agent-config.js +7 -1
- package/dist/bin/exe-agent.js +29 -3
- package/dist/bin/exe-assign.js +62 -12
- package/dist/bin/exe-boot.js +500 -151
- package/dist/bin/exe-call.js +46 -5
- package/dist/bin/exe-cloud.js +101 -16
- package/dist/bin/exe-dispatch.js +827 -27
- package/dist/bin/exe-doctor.js +61 -11
- package/dist/bin/exe-export-behaviors.js +67 -14
- package/dist/bin/exe-forget.js +62 -12
- package/dist/bin/exe-gateway.js +147 -27
- package/dist/bin/exe-heartbeat.js +83 -16
- package/dist/bin/exe-kill.js +62 -12
- package/dist/bin/exe-launch-agent.js +83 -15
- package/dist/bin/exe-new-employee.js +176 -8
- package/dist/bin/exe-pending-messages.js +83 -16
- package/dist/bin/exe-pending-notifications.js +83 -16
- package/dist/bin/exe-pending-reviews.js +83 -16
- package/dist/bin/exe-rename.js +62 -12
- package/dist/bin/exe-review.js +62 -12
- package/dist/bin/exe-search.js +62 -12
- package/dist/bin/exe-session-cleanup.js +949 -149
- package/dist/bin/exe-settings.js +10 -4
- package/dist/bin/exe-start-codex.js +537 -248
- package/dist/bin/exe-start-opencode.js +547 -168
- package/dist/bin/exe-status.js +83 -16
- package/dist/bin/exe-support.js +1 -1
- package/dist/bin/exe-team.js +62 -12
- package/dist/bin/git-sweep.js +827 -27
- package/dist/bin/graph-backfill.js +62 -12
- package/dist/bin/graph-export.js +62 -12
- package/dist/bin/install.js +62 -4
- package/dist/bin/intercom-check.js +949 -149
- package/dist/bin/pre-publish.js +14 -2
- package/dist/bin/scan-tasks.js +827 -27
- package/dist/bin/setup.js +99 -14
- package/dist/bin/shard-migrate.js +62 -12
- package/dist/bin/stack-update.js +1 -1
- package/dist/bin/update.js +3 -3
- package/dist/gateway/index.js +586 -26
- package/dist/hooks/bug-report-worker.js +586 -26
- package/dist/hooks/codex-stop-task-finalizer.js +977 -143
- package/dist/hooks/commit-complete.js +827 -27
- package/dist/hooks/error-recall.js +62 -12
- package/dist/hooks/ingest.js +4579 -249
- package/dist/hooks/instructions-loaded.js +62 -12
- package/dist/hooks/notification.js +62 -12
- package/dist/hooks/post-compact.js +83 -16
- package/dist/hooks/post-tool-combined.js +83 -16
- package/dist/hooks/pre-compact.js +907 -107
- package/dist/hooks/pre-tool-use.js +98 -16
- package/dist/hooks/prompt-submit.js +596 -30
- package/dist/hooks/session-end.js +909 -112
- package/dist/hooks/session-start.js +112 -17
- package/dist/hooks/stop.js +82 -15
- package/dist/hooks/subagent-stop.js +83 -16
- package/dist/hooks/summary-worker.js +81 -8
- package/dist/index.js +595 -29
- package/dist/lib/agent-config.js +16 -1
- package/dist/lib/cloud-sync.js +45 -1
- package/dist/lib/consolidation.js +16 -1
- package/dist/lib/database.js +23 -0
- package/dist/lib/db.js +23 -0
- package/dist/lib/device-registry.js +23 -0
- package/dist/lib/employee-templates.js +30 -4
- package/dist/lib/employees.js +16 -1
- package/dist/lib/exe-daemon.js +482 -52
- package/dist/lib/hybrid-search.js +62 -12
- package/dist/lib/license.js +3 -3
- package/dist/lib/messaging.js +21 -4
- package/dist/lib/schedules.js +37 -2
- package/dist/lib/skill-learning.js +910 -41
- package/dist/lib/status-brief.js +14 -1
- package/dist/lib/store.js +62 -12
- package/dist/lib/tasks.js +843 -93
- package/dist/lib/tmux-routing.js +766 -16
- package/dist/mcp/server.js +238 -41
- package/dist/mcp/tools/create-task.js +525 -15
- 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 +840 -93
- package/dist/runtime/index.js +913 -107
- package/dist/tui/App.js +227 -58
- package/package.json +1 -1
|
@@ -423,6 +423,7 @@ __export(agent_config_exports, {
|
|
|
423
423
|
getAgentRuntime: () => getAgentRuntime,
|
|
424
424
|
loadAgentConfig: () => loadAgentConfig,
|
|
425
425
|
saveAgentConfig: () => saveAgentConfig,
|
|
426
|
+
setAgentMcps: () => setAgentMcps,
|
|
426
427
|
setAgentRuntime: () => setAgentRuntime
|
|
427
428
|
});
|
|
428
429
|
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
@@ -449,7 +450,7 @@ function getAgentRuntime(agentId) {
|
|
|
449
450
|
if (orgDefault) return orgDefault;
|
|
450
451
|
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
451
452
|
}
|
|
452
|
-
function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
|
|
453
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
453
454
|
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
454
455
|
if (!knownModels) {
|
|
455
456
|
return {
|
|
@@ -464,12 +465,26 @@ function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
|
|
|
464
465
|
};
|
|
465
466
|
}
|
|
466
467
|
const config = loadAgentConfig();
|
|
468
|
+
const existing = config[agentId];
|
|
467
469
|
const entry = { runtime, model };
|
|
468
470
|
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
471
|
+
if (mcps !== void 0) {
|
|
472
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
473
|
+
} else if (existing?.mcps) {
|
|
474
|
+
entry.mcps = existing.mcps;
|
|
475
|
+
}
|
|
469
476
|
config[agentId] = entry;
|
|
470
477
|
saveAgentConfig(config);
|
|
471
478
|
return { ok: true };
|
|
472
479
|
}
|
|
480
|
+
function setAgentMcps(agentId, mcps) {
|
|
481
|
+
const config = loadAgentConfig();
|
|
482
|
+
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
483
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
484
|
+
config[agentId] = existing;
|
|
485
|
+
saveAgentConfig(config);
|
|
486
|
+
return { ok: true };
|
|
487
|
+
}
|
|
473
488
|
function clearAgentRuntime(agentId) {
|
|
474
489
|
const config = loadAgentConfig();
|
|
475
490
|
delete config[agentId];
|
|
@@ -2436,6 +2451,13 @@ async function ensureSchema() {
|
|
|
2436
2451
|
} catch (e) {
|
|
2437
2452
|
logCatchDebug("migration", e);
|
|
2438
2453
|
}
|
|
2454
|
+
for (const col of ["created_by_agent TEXT", "created_by_device TEXT", "source_session_id TEXT"]) {
|
|
2455
|
+
try {
|
|
2456
|
+
await client.execute({ sql: `ALTER TABLE behaviors ADD COLUMN ${col}`, args: [] });
|
|
2457
|
+
} catch (e) {
|
|
2458
|
+
logCatchDebug("migration", e);
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2439
2461
|
try {
|
|
2440
2462
|
await client.execute({
|
|
2441
2463
|
sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
|
|
@@ -3652,6 +3674,22 @@ async function ensureSchema() {
|
|
|
3652
3674
|
} catch (e) {
|
|
3653
3675
|
logCatchDebug("migration", e);
|
|
3654
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
|
+
}
|
|
3655
3693
|
}
|
|
3656
3694
|
async function disposeDatabase() {
|
|
3657
3695
|
if (_walCheckpointTimer) {
|
|
@@ -4775,11 +4813,17 @@ var init_platform_procedures = __esm({
|
|
|
4775
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."
|
|
4776
4814
|
},
|
|
4777
4815
|
{
|
|
4778
|
-
title: "
|
|
4816
|
+
title: "Orchestration phase guidance \u2014 recommend, never trap",
|
|
4779
4817
|
domain: "workflow",
|
|
4780
4818
|
priority: "p1",
|
|
4781
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."
|
|
4782
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
|
+
},
|
|
4783
4827
|
{
|
|
4784
4828
|
title: "Single dispatch path \u2014 create_task only",
|
|
4785
4829
|
domain: "workflow",
|
|
@@ -4813,6 +4857,12 @@ var init_platform_procedures = __esm({
|
|
|
4813
4857
|
priority: "p0",
|
|
4814
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."
|
|
4815
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
|
+
},
|
|
4816
4866
|
{
|
|
4817
4867
|
title: "Customer patch triage \u2014 upstream bug vs customization",
|
|
4818
4868
|
domain: "support",
|
|
@@ -4964,7 +5014,7 @@ var init_platform_procedures = __esm({
|
|
|
4964
5014
|
title: "MCP tool dispatch \u2014 all tools use action parameter",
|
|
4965
5015
|
domain: "tool-use",
|
|
4966
5016
|
priority: "p0",
|
|
4967
|
-
content: 'exe-os MCP tools
|
|
5017
|
+
content: 'exe-os MCP tools use consolidated action-based dispatch by default (19 tools). Call domain tools with an action parameter: memory(action="recall"), task(action="create"), config(action="list_employees"), etc. Legacy mode (108 separate tools like recall_my_memory, create_task) is still available via EXE_MCP_TOOL_SURFACE=legacy but will be removed in a future version. If you see specific tool names, call them directly \u2014 both surfaces are identical. Consolidated is the default and recommended surface.'
|
|
4968
5018
|
},
|
|
4969
5019
|
{
|
|
4970
5020
|
title: "MCP tools \u2014 memory, decision, and search",
|
|
@@ -5098,10 +5148,24 @@ function stableId(memoryId, type, content) {
|
|
|
5098
5148
|
return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
|
|
5099
5149
|
}
|
|
5100
5150
|
function cleanText(text) {
|
|
5101
|
-
|
|
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;
|
|
5102
5157
|
}
|
|
5103
|
-
function
|
|
5104
|
-
|
|
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;
|
|
5105
5169
|
}
|
|
5106
5170
|
function inferCardType(sentence, toolName) {
|
|
5107
5171
|
const lower = sentence.toLowerCase();
|
|
@@ -5133,12 +5197,12 @@ function predicateFor(type) {
|
|
|
5133
5197
|
}
|
|
5134
5198
|
}
|
|
5135
5199
|
function extractMemoryCards(row) {
|
|
5136
|
-
const
|
|
5200
|
+
const segments = splitSegments(row.raw_text);
|
|
5137
5201
|
const cards = [];
|
|
5138
|
-
for (const sentence of
|
|
5202
|
+
for (const sentence of segments) {
|
|
5139
5203
|
const type = inferCardType(sentence, row.tool_name);
|
|
5140
5204
|
const subject = extractSubject(sentence, row.agent_id);
|
|
5141
|
-
const content = sentence.length >
|
|
5205
|
+
const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
|
|
5142
5206
|
cards.push({
|
|
5143
5207
|
id: stableId(row.id, type, content),
|
|
5144
5208
|
memory_id: row.id,
|
|
@@ -5234,13 +5298,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
|
|
|
5234
5298
|
last_accessed: String(row.timestamp)
|
|
5235
5299
|
}));
|
|
5236
5300
|
}
|
|
5237
|
-
var MAX_CARDS_PER_MEMORY,
|
|
5301
|
+
var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
|
|
5238
5302
|
var init_memory_cards = __esm({
|
|
5239
5303
|
"src/lib/memory-cards.ts"() {
|
|
5240
5304
|
"use strict";
|
|
5241
5305
|
init_database();
|
|
5242
|
-
MAX_CARDS_PER_MEMORY =
|
|
5243
|
-
|
|
5306
|
+
MAX_CARDS_PER_MEMORY = 8;
|
|
5307
|
+
MAX_SEGMENT_CHARS = 500;
|
|
5308
|
+
MIN_SEGMENT_CHARS = 20;
|
|
5244
5309
|
}
|
|
5245
5310
|
});
|
|
5246
5311
|
|
|
@@ -7552,7 +7617,7 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
7552
7617
|
try {
|
|
7553
7618
|
const client = getClient();
|
|
7554
7619
|
void client.execute({
|
|
7555
|
-
sql: `UPDATE memories SET last_accessed = ?, retrieval_count = COALESCE(retrieval_count, 0) + 1 WHERE id IN (${placeholders})`,
|
|
7620
|
+
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})`,
|
|
7556
7621
|
args: [now, ...ids]
|
|
7557
7622
|
}).catch(() => {
|
|
7558
7623
|
});
|
|
@@ -8468,7 +8533,7 @@ var init_license = __esm({
|
|
|
8468
8533
|
LICENSE_PATH = path15.join(EXE_AI_DIR, "license.key");
|
|
8469
8534
|
CACHE_PATH = path15.join(EXE_AI_DIR, "license-cache.json");
|
|
8470
8535
|
DEVICE_ID_PATH = path15.join(EXE_AI_DIR, "device-id");
|
|
8471
|
-
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com
|
|
8536
|
+
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
|
|
8472
8537
|
}
|
|
8473
8538
|
});
|
|
8474
8539
|
|
|
@@ -8521,6 +8586,18 @@ function extractRootExe(name) {
|
|
|
8521
8586
|
const parts = name.split("-").filter(Boolean);
|
|
8522
8587
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
8523
8588
|
}
|
|
8589
|
+
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
8590
|
+
if (!existsSync16(SESSION_CACHE)) {
|
|
8591
|
+
mkdirSync8(SESSION_CACHE, { recursive: true });
|
|
8592
|
+
}
|
|
8593
|
+
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
8594
|
+
const filePath = path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
8595
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
8596
|
+
parentExe: rootExe,
|
|
8597
|
+
dispatchedBy: dispatchedBy || rootExe,
|
|
8598
|
+
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8599
|
+
}));
|
|
8600
|
+
}
|
|
8524
8601
|
function getParentExe(sessionKey) {
|
|
8525
8602
|
try {
|
|
8526
8603
|
const data = JSON.parse(readFileSync11(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
@@ -8530,11 +8607,12 @@ function getParentExe(sessionKey) {
|
|
|
8530
8607
|
}
|
|
8531
8608
|
}
|
|
8532
8609
|
function resolveExeSession() {
|
|
8610
|
+
if (process.env.EXE_SESSION_NAME) {
|
|
8611
|
+
const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
8612
|
+
if (fromEnv) return fromEnv;
|
|
8613
|
+
}
|
|
8533
8614
|
const mySession = getMySession();
|
|
8534
8615
|
if (!mySession) {
|
|
8535
|
-
if (process.env.EXE_SESSION_NAME) {
|
|
8536
|
-
return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
8537
|
-
}
|
|
8538
8616
|
return null;
|
|
8539
8617
|
}
|
|
8540
8618
|
const fromSessionName = extractRootExe(mySession);
|
|
@@ -8549,6 +8627,10 @@ function resolveExeSession() {
|
|
|
8549
8627
|
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
8550
8628
|
`
|
|
8551
8629
|
);
|
|
8630
|
+
try {
|
|
8631
|
+
registerParentExe(key, fromSessionName);
|
|
8632
|
+
} catch {
|
|
8633
|
+
}
|
|
8552
8634
|
candidate = fromSessionName;
|
|
8553
8635
|
} else {
|
|
8554
8636
|
candidate = fromCache;
|
|
@@ -9396,6 +9478,19 @@ ${phase.label}. Focus: ${phase.focus}. This is guidance, not a blocker; the user
|
|
|
9396
9478
|
`;
|
|
9397
9479
|
} catch {
|
|
9398
9480
|
}
|
|
9481
|
+
if (canCoordinate2(agentId, agent.agentRole) || ["CTO", "COO"].includes(agent.agentRole ?? "")) {
|
|
9482
|
+
try {
|
|
9483
|
+
const archPath = path21.join(data.cwd || process.cwd(), "exe", "ARCHITECTURE.md");
|
|
9484
|
+
if (existsSync18(archPath)) {
|
|
9485
|
+
const archContent = readFileSync13(archPath, "utf-8").slice(0, 1500);
|
|
9486
|
+
additionalContext += `## Project Architecture
|
|
9487
|
+
${archContent}
|
|
9488
|
+
|
|
9489
|
+
`;
|
|
9490
|
+
}
|
|
9491
|
+
} catch {
|
|
9492
|
+
}
|
|
9493
|
+
}
|
|
9399
9494
|
if (memories.length > 0) {
|
|
9400
9495
|
const brief = memories.map(
|
|
9401
9496
|
(m) => `[${m.timestamp}] ${m.tool_name}: ${m.raw_text.slice(0, 200)}`
|
package/dist/hooks/stop.js
CHANGED
|
@@ -2358,6 +2358,13 @@ async function ensureSchema() {
|
|
|
2358
2358
|
} catch (e) {
|
|
2359
2359
|
logCatchDebug("migration", e);
|
|
2360
2360
|
}
|
|
2361
|
+
for (const col of ["created_by_agent TEXT", "created_by_device TEXT", "source_session_id TEXT"]) {
|
|
2362
|
+
try {
|
|
2363
|
+
await client.execute({ sql: `ALTER TABLE behaviors ADD COLUMN ${col}`, args: [] });
|
|
2364
|
+
} catch (e) {
|
|
2365
|
+
logCatchDebug("migration", e);
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2361
2368
|
try {
|
|
2362
2369
|
await client.execute({
|
|
2363
2370
|
sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
|
|
@@ -3574,6 +3581,22 @@ async function ensureSchema() {
|
|
|
3574
3581
|
} catch (e) {
|
|
3575
3582
|
logCatchDebug("migration", e);
|
|
3576
3583
|
}
|
|
3584
|
+
try {
|
|
3585
|
+
await client.execute({
|
|
3586
|
+
sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
|
|
3587
|
+
args: []
|
|
3588
|
+
});
|
|
3589
|
+
} catch (e) {
|
|
3590
|
+
logCatchDebug("migration", e);
|
|
3591
|
+
}
|
|
3592
|
+
try {
|
|
3593
|
+
await client.execute({
|
|
3594
|
+
sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
|
|
3595
|
+
args: []
|
|
3596
|
+
});
|
|
3597
|
+
} catch (e) {
|
|
3598
|
+
logCatchDebug("migration", e);
|
|
3599
|
+
}
|
|
3577
3600
|
}
|
|
3578
3601
|
async function disposeDatabase() {
|
|
3579
3602
|
if (_walCheckpointTimer) {
|
|
@@ -3640,7 +3663,7 @@ var init_license = __esm({
|
|
|
3640
3663
|
LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
|
|
3641
3664
|
CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
3642
3665
|
DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
|
|
3643
|
-
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com
|
|
3666
|
+
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
|
|
3644
3667
|
}
|
|
3645
3668
|
});
|
|
3646
3669
|
|
|
@@ -3693,6 +3716,18 @@ function extractRootExe(name) {
|
|
|
3693
3716
|
const parts = name.split("-").filter(Boolean);
|
|
3694
3717
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
3695
3718
|
}
|
|
3719
|
+
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
3720
|
+
if (!existsSync12(SESSION_CACHE)) {
|
|
3721
|
+
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
3722
|
+
}
|
|
3723
|
+
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
3724
|
+
const filePath = path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
3725
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
3726
|
+
parentExe: rootExe,
|
|
3727
|
+
dispatchedBy: dispatchedBy || rootExe,
|
|
3728
|
+
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3729
|
+
}));
|
|
3730
|
+
}
|
|
3696
3731
|
function getParentExe(sessionKey) {
|
|
3697
3732
|
try {
|
|
3698
3733
|
const data = JSON.parse(readFileSync10(path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
@@ -3702,11 +3737,12 @@ function getParentExe(sessionKey) {
|
|
|
3702
3737
|
}
|
|
3703
3738
|
}
|
|
3704
3739
|
function resolveExeSession() {
|
|
3740
|
+
if (process.env.EXE_SESSION_NAME) {
|
|
3741
|
+
const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
3742
|
+
if (fromEnv) return fromEnv;
|
|
3743
|
+
}
|
|
3705
3744
|
const mySession = getMySession();
|
|
3706
3745
|
if (!mySession) {
|
|
3707
|
-
if (process.env.EXE_SESSION_NAME) {
|
|
3708
|
-
return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
3709
|
-
}
|
|
3710
3746
|
return null;
|
|
3711
3747
|
}
|
|
3712
3748
|
const fromSessionName = extractRootExe(mySession);
|
|
@@ -3721,6 +3757,10 @@ function resolveExeSession() {
|
|
|
3721
3757
|
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
3722
3758
|
`
|
|
3723
3759
|
);
|
|
3760
|
+
try {
|
|
3761
|
+
registerParentExe(key, fromSessionName);
|
|
3762
|
+
} catch {
|
|
3763
|
+
}
|
|
3724
3764
|
candidate = fromSessionName;
|
|
3725
3765
|
} else {
|
|
3726
3766
|
candidate = fromCache;
|
|
@@ -4893,11 +4933,17 @@ var init_platform_procedures = __esm({
|
|
|
4893
4933
|
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."
|
|
4894
4934
|
},
|
|
4895
4935
|
{
|
|
4896
|
-
title: "
|
|
4936
|
+
title: "Orchestration phase guidance \u2014 recommend, never trap",
|
|
4897
4937
|
domain: "workflow",
|
|
4898
4938
|
priority: "p1",
|
|
4899
4939
|
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."
|
|
4900
4940
|
},
|
|
4941
|
+
{
|
|
4942
|
+
title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
|
|
4943
|
+
domain: "identity",
|
|
4944
|
+
priority: "p0",
|
|
4945
|
+
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."
|
|
4946
|
+
},
|
|
4901
4947
|
{
|
|
4902
4948
|
title: "Single dispatch path \u2014 create_task only",
|
|
4903
4949
|
domain: "workflow",
|
|
@@ -4931,6 +4977,12 @@ var init_platform_procedures = __esm({
|
|
|
4931
4977
|
priority: "p0",
|
|
4932
4978
|
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."
|
|
4933
4979
|
},
|
|
4980
|
+
{
|
|
4981
|
+
title: "Destructive operations \u2014 mandatory reviewer gate",
|
|
4982
|
+
domain: "security",
|
|
4983
|
+
priority: "p0",
|
|
4984
|
+
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."
|
|
4985
|
+
},
|
|
4934
4986
|
{
|
|
4935
4987
|
title: "Customer patch triage \u2014 upstream bug vs customization",
|
|
4936
4988
|
domain: "support",
|
|
@@ -5082,7 +5134,7 @@ var init_platform_procedures = __esm({
|
|
|
5082
5134
|
title: "MCP tool dispatch \u2014 all tools use action parameter",
|
|
5083
5135
|
domain: "tool-use",
|
|
5084
5136
|
priority: "p0",
|
|
5085
|
-
content: 'exe-os MCP tools
|
|
5137
|
+
content: 'exe-os MCP tools use consolidated action-based dispatch by default (19 tools). Call domain tools with an action parameter: memory(action="recall"), task(action="create"), config(action="list_employees"), etc. Legacy mode (108 separate tools like recall_my_memory, create_task) is still available via EXE_MCP_TOOL_SURFACE=legacy but will be removed in a future version. If you see specific tool names, call them directly \u2014 both surfaces are identical. Consolidated is the default and recommended surface.'
|
|
5086
5138
|
},
|
|
5087
5139
|
{
|
|
5088
5140
|
title: "MCP tools \u2014 memory, decision, and search",
|
|
@@ -5216,10 +5268,24 @@ function stableId(memoryId, type, content) {
|
|
|
5216
5268
|
return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
|
|
5217
5269
|
}
|
|
5218
5270
|
function cleanText(text) {
|
|
5219
|
-
|
|
5271
|
+
let cleaned = text.replace(
|
|
5272
|
+
/```(\w*)\n(.*?)(?:\n[\s\S]*?)```/g,
|
|
5273
|
+
(_m, lang, firstLine) => `[code${lang ? `:${lang}` : ""}] ${firstLine.trim()}`
|
|
5274
|
+
);
|
|
5275
|
+
cleaned = cleaned.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
5276
|
+
return cleaned;
|
|
5220
5277
|
}
|
|
5221
|
-
function
|
|
5222
|
-
|
|
5278
|
+
function splitSegments(text) {
|
|
5279
|
+
const cleaned = cleanText(text);
|
|
5280
|
+
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);
|
|
5281
|
+
if (segments.length === 0 && cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
5282
|
+
const lines = cleaned.split(/\n+/).map((l) => l.trim()).filter((l) => l.length >= MIN_SEGMENT_CHARS && l.length <= MAX_SEGMENT_CHARS);
|
|
5283
|
+
if (lines.length > 0) return lines;
|
|
5284
|
+
if (cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
5285
|
+
return [cleaned.slice(0, MAX_SEGMENT_CHARS)];
|
|
5286
|
+
}
|
|
5287
|
+
}
|
|
5288
|
+
return segments;
|
|
5223
5289
|
}
|
|
5224
5290
|
function inferCardType(sentence, toolName) {
|
|
5225
5291
|
const lower = sentence.toLowerCase();
|
|
@@ -5251,12 +5317,12 @@ function predicateFor(type) {
|
|
|
5251
5317
|
}
|
|
5252
5318
|
}
|
|
5253
5319
|
function extractMemoryCards(row) {
|
|
5254
|
-
const
|
|
5320
|
+
const segments = splitSegments(row.raw_text);
|
|
5255
5321
|
const cards = [];
|
|
5256
|
-
for (const sentence of
|
|
5322
|
+
for (const sentence of segments) {
|
|
5257
5323
|
const type = inferCardType(sentence, row.tool_name);
|
|
5258
5324
|
const subject = extractSubject(sentence, row.agent_id);
|
|
5259
|
-
const content = sentence.length >
|
|
5325
|
+
const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
|
|
5260
5326
|
cards.push({
|
|
5261
5327
|
id: stableId(row.id, type, content),
|
|
5262
5328
|
memory_id: row.id,
|
|
@@ -5352,13 +5418,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
|
|
|
5352
5418
|
last_accessed: String(row.timestamp)
|
|
5353
5419
|
}));
|
|
5354
5420
|
}
|
|
5355
|
-
var MAX_CARDS_PER_MEMORY,
|
|
5421
|
+
var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
|
|
5356
5422
|
var init_memory_cards = __esm({
|
|
5357
5423
|
"src/lib/memory-cards.ts"() {
|
|
5358
5424
|
"use strict";
|
|
5359
5425
|
init_database();
|
|
5360
|
-
MAX_CARDS_PER_MEMORY =
|
|
5361
|
-
|
|
5426
|
+
MAX_CARDS_PER_MEMORY = 8;
|
|
5427
|
+
MAX_SEGMENT_CHARS = 500;
|
|
5428
|
+
MIN_SEGMENT_CHARS = 20;
|
|
5362
5429
|
}
|
|
5363
5430
|
});
|
|
5364
5431
|
|
|
@@ -2330,6 +2330,13 @@ async function ensureSchema() {
|
|
|
2330
2330
|
} catch (e) {
|
|
2331
2331
|
logCatchDebug("migration", e);
|
|
2332
2332
|
}
|
|
2333
|
+
for (const col of ["created_by_agent TEXT", "created_by_device TEXT", "source_session_id TEXT"]) {
|
|
2334
|
+
try {
|
|
2335
|
+
await client.execute({ sql: `ALTER TABLE behaviors ADD COLUMN ${col}`, args: [] });
|
|
2336
|
+
} catch (e) {
|
|
2337
|
+
logCatchDebug("migration", e);
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2333
2340
|
try {
|
|
2334
2341
|
await client.execute({
|
|
2335
2342
|
sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
|
|
@@ -3546,6 +3553,22 @@ async function ensureSchema() {
|
|
|
3546
3553
|
} catch (e) {
|
|
3547
3554
|
logCatchDebug("migration", e);
|
|
3548
3555
|
}
|
|
3556
|
+
try {
|
|
3557
|
+
await client.execute({
|
|
3558
|
+
sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
|
|
3559
|
+
args: []
|
|
3560
|
+
});
|
|
3561
|
+
} catch (e) {
|
|
3562
|
+
logCatchDebug("migration", e);
|
|
3563
|
+
}
|
|
3564
|
+
try {
|
|
3565
|
+
await client.execute({
|
|
3566
|
+
sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
|
|
3567
|
+
args: []
|
|
3568
|
+
});
|
|
3569
|
+
} catch (e) {
|
|
3570
|
+
logCatchDebug("migration", e);
|
|
3571
|
+
}
|
|
3549
3572
|
}
|
|
3550
3573
|
async function disposeDatabase() {
|
|
3551
3574
|
if (_walCheckpointTimer) {
|
|
@@ -3612,7 +3635,7 @@ var init_license = __esm({
|
|
|
3612
3635
|
LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
|
|
3613
3636
|
CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
3614
3637
|
DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
|
|
3615
|
-
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com
|
|
3638
|
+
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
|
|
3616
3639
|
}
|
|
3617
3640
|
});
|
|
3618
3641
|
|
|
@@ -3665,6 +3688,18 @@ function extractRootExe(name) {
|
|
|
3665
3688
|
const parts = name.split("-").filter(Boolean);
|
|
3666
3689
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
3667
3690
|
}
|
|
3691
|
+
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
3692
|
+
if (!existsSync12(SESSION_CACHE)) {
|
|
3693
|
+
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
3694
|
+
}
|
|
3695
|
+
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
3696
|
+
const filePath = path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
3697
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
3698
|
+
parentExe: rootExe,
|
|
3699
|
+
dispatchedBy: dispatchedBy || rootExe,
|
|
3700
|
+
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3701
|
+
}));
|
|
3702
|
+
}
|
|
3668
3703
|
function getParentExe(sessionKey) {
|
|
3669
3704
|
try {
|
|
3670
3705
|
const data = JSON.parse(readFileSync10(path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
@@ -3674,11 +3709,12 @@ function getParentExe(sessionKey) {
|
|
|
3674
3709
|
}
|
|
3675
3710
|
}
|
|
3676
3711
|
function resolveExeSession() {
|
|
3712
|
+
if (process.env.EXE_SESSION_NAME) {
|
|
3713
|
+
const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
3714
|
+
if (fromEnv) return fromEnv;
|
|
3715
|
+
}
|
|
3677
3716
|
const mySession = getMySession();
|
|
3678
3717
|
if (!mySession) {
|
|
3679
|
-
if (process.env.EXE_SESSION_NAME) {
|
|
3680
|
-
return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
3681
|
-
}
|
|
3682
3718
|
return null;
|
|
3683
3719
|
}
|
|
3684
3720
|
const fromSessionName = extractRootExe(mySession);
|
|
@@ -3693,6 +3729,10 @@ function resolveExeSession() {
|
|
|
3693
3729
|
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
3694
3730
|
`
|
|
3695
3731
|
);
|
|
3732
|
+
try {
|
|
3733
|
+
registerParentExe(key, fromSessionName);
|
|
3734
|
+
} catch {
|
|
3735
|
+
}
|
|
3696
3736
|
candidate = fromSessionName;
|
|
3697
3737
|
} else {
|
|
3698
3738
|
candidate = fromCache;
|
|
@@ -4856,11 +4896,17 @@ var init_platform_procedures = __esm({
|
|
|
4856
4896
|
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."
|
|
4857
4897
|
},
|
|
4858
4898
|
{
|
|
4859
|
-
title: "
|
|
4899
|
+
title: "Orchestration phase guidance \u2014 recommend, never trap",
|
|
4860
4900
|
domain: "workflow",
|
|
4861
4901
|
priority: "p1",
|
|
4862
4902
|
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."
|
|
4863
4903
|
},
|
|
4904
|
+
{
|
|
4905
|
+
title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
|
|
4906
|
+
domain: "identity",
|
|
4907
|
+
priority: "p0",
|
|
4908
|
+
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."
|
|
4909
|
+
},
|
|
4864
4910
|
{
|
|
4865
4911
|
title: "Single dispatch path \u2014 create_task only",
|
|
4866
4912
|
domain: "workflow",
|
|
@@ -4894,6 +4940,12 @@ var init_platform_procedures = __esm({
|
|
|
4894
4940
|
priority: "p0",
|
|
4895
4941
|
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."
|
|
4896
4942
|
},
|
|
4943
|
+
{
|
|
4944
|
+
title: "Destructive operations \u2014 mandatory reviewer gate",
|
|
4945
|
+
domain: "security",
|
|
4946
|
+
priority: "p0",
|
|
4947
|
+
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."
|
|
4948
|
+
},
|
|
4897
4949
|
{
|
|
4898
4950
|
title: "Customer patch triage \u2014 upstream bug vs customization",
|
|
4899
4951
|
domain: "support",
|
|
@@ -5045,7 +5097,7 @@ var init_platform_procedures = __esm({
|
|
|
5045
5097
|
title: "MCP tool dispatch \u2014 all tools use action parameter",
|
|
5046
5098
|
domain: "tool-use",
|
|
5047
5099
|
priority: "p0",
|
|
5048
|
-
content: 'exe-os MCP tools
|
|
5100
|
+
content: 'exe-os MCP tools use consolidated action-based dispatch by default (19 tools). Call domain tools with an action parameter: memory(action="recall"), task(action="create"), config(action="list_employees"), etc. Legacy mode (108 separate tools like recall_my_memory, create_task) is still available via EXE_MCP_TOOL_SURFACE=legacy but will be removed in a future version. If you see specific tool names, call them directly \u2014 both surfaces are identical. Consolidated is the default and recommended surface.'
|
|
5049
5101
|
},
|
|
5050
5102
|
{
|
|
5051
5103
|
title: "MCP tools \u2014 memory, decision, and search",
|
|
@@ -5179,10 +5231,24 @@ function stableId(memoryId, type, content) {
|
|
|
5179
5231
|
return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
|
|
5180
5232
|
}
|
|
5181
5233
|
function cleanText(text) {
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5234
|
+
let cleaned = text.replace(
|
|
5235
|
+
/```(\w*)\n(.*?)(?:\n[\s\S]*?)```/g,
|
|
5236
|
+
(_m, lang, firstLine) => `[code${lang ? `:${lang}` : ""}] ${firstLine.trim()}`
|
|
5237
|
+
);
|
|
5238
|
+
cleaned = cleaned.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
5239
|
+
return cleaned;
|
|
5240
|
+
}
|
|
5241
|
+
function splitSegments(text) {
|
|
5242
|
+
const cleaned = cleanText(text);
|
|
5243
|
+
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);
|
|
5244
|
+
if (segments.length === 0 && cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
5245
|
+
const lines = cleaned.split(/\n+/).map((l) => l.trim()).filter((l) => l.length >= MIN_SEGMENT_CHARS && l.length <= MAX_SEGMENT_CHARS);
|
|
5246
|
+
if (lines.length > 0) return lines;
|
|
5247
|
+
if (cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
5248
|
+
return [cleaned.slice(0, MAX_SEGMENT_CHARS)];
|
|
5249
|
+
}
|
|
5250
|
+
}
|
|
5251
|
+
return segments;
|
|
5186
5252
|
}
|
|
5187
5253
|
function inferCardType(sentence, toolName) {
|
|
5188
5254
|
const lower = sentence.toLowerCase();
|
|
@@ -5214,12 +5280,12 @@ function predicateFor(type) {
|
|
|
5214
5280
|
}
|
|
5215
5281
|
}
|
|
5216
5282
|
function extractMemoryCards(row) {
|
|
5217
|
-
const
|
|
5283
|
+
const segments = splitSegments(row.raw_text);
|
|
5218
5284
|
const cards = [];
|
|
5219
|
-
for (const sentence of
|
|
5285
|
+
for (const sentence of segments) {
|
|
5220
5286
|
const type = inferCardType(sentence, row.tool_name);
|
|
5221
5287
|
const subject = extractSubject(sentence, row.agent_id);
|
|
5222
|
-
const content = sentence.length >
|
|
5288
|
+
const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
|
|
5223
5289
|
cards.push({
|
|
5224
5290
|
id: stableId(row.id, type, content),
|
|
5225
5291
|
memory_id: row.id,
|
|
@@ -5315,13 +5381,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
|
|
|
5315
5381
|
last_accessed: String(row.timestamp)
|
|
5316
5382
|
}));
|
|
5317
5383
|
}
|
|
5318
|
-
var MAX_CARDS_PER_MEMORY,
|
|
5384
|
+
var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
|
|
5319
5385
|
var init_memory_cards = __esm({
|
|
5320
5386
|
"src/lib/memory-cards.ts"() {
|
|
5321
5387
|
"use strict";
|
|
5322
5388
|
init_database();
|
|
5323
|
-
MAX_CARDS_PER_MEMORY =
|
|
5324
|
-
|
|
5389
|
+
MAX_CARDS_PER_MEMORY = 8;
|
|
5390
|
+
MAX_SEGMENT_CHARS = 500;
|
|
5391
|
+
MIN_SEGMENT_CHARS = 20;
|
|
5325
5392
|
}
|
|
5326
5393
|
});
|
|
5327
5394
|
|