@askexenow/exe-os 0.9.112 → 0.9.114
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 +78 -23
- package/dist/bin/agentic-reflection-backfill.js +53 -13
- package/dist/bin/agentic-semantic-label.js +53 -13
- package/dist/bin/backfill-conversations.js +77 -22
- package/dist/bin/backfill-responses.js +78 -23
- package/dist/bin/backfill-vectors.js +53 -13
- package/dist/bin/bulk-sync-postgres.js +78 -23
- package/dist/bin/cleanup-stale-review-tasks.js +98 -26
- package/dist/bin/cli.js +388 -97
- package/dist/bin/exe-agent-config.js +7 -1
- package/dist/bin/exe-agent.js +55 -2
- package/dist/bin/exe-assign.js +78 -23
- package/dist/bin/exe-boot.js +524 -161
- package/dist/bin/exe-call.js +53 -4
- package/dist/bin/exe-cloud.js +127 -26
- package/dist/bin/exe-dispatch.js +402 -39
- package/dist/bin/exe-doctor.js +76 -21
- package/dist/bin/exe-export-behaviors.js +77 -22
- package/dist/bin/exe-forget.js +77 -22
- package/dist/bin/exe-gateway.js +161 -38
- package/dist/bin/exe-heartbeat.js +98 -26
- package/dist/bin/exe-kill.js +77 -22
- package/dist/bin/exe-launch-agent.js +173 -29
- package/dist/bin/exe-new-employee.js +183 -7
- package/dist/bin/exe-pending-messages.js +98 -26
- package/dist/bin/exe-pending-notifications.js +98 -26
- package/dist/bin/exe-pending-reviews.js +98 -26
- package/dist/bin/exe-rename.js +77 -22
- package/dist/bin/exe-review.js +77 -22
- package/dist/bin/exe-search.js +77 -22
- package/dist/bin/exe-session-cleanup.js +523 -160
- package/dist/bin/exe-settings.js +10 -4
- package/dist/bin/exe-start-codex.js +554 -255
- package/dist/bin/exe-start-opencode.js +564 -175
- package/dist/bin/exe-status.js +98 -26
- package/dist/bin/exe-support.js +1 -1
- package/dist/bin/exe-team.js +77 -22
- package/dist/bin/git-sweep.js +402 -39
- package/dist/bin/graph-backfill.js +78 -23
- package/dist/bin/graph-export.js +77 -22
- package/dist/bin/install.js +70 -4
- package/dist/bin/intercom-check.js +523 -160
- package/dist/bin/pre-publish.js +13 -1
- package/dist/bin/scan-tasks.js +402 -39
- package/dist/bin/setup.js +151 -24
- package/dist/bin/shard-migrate.js +78 -23
- package/dist/bin/stack-update.js +1 -1
- package/dist/bin/update.js +3 -3
- package/dist/gateway/index.js +161 -38
- package/dist/hooks/bug-report-worker.js +161 -38
- package/dist/hooks/codex-stop-task-finalizer.js +542 -150
- package/dist/hooks/commit-complete.js +402 -39
- package/dist/hooks/error-recall.js +77 -22
- package/dist/hooks/ingest.js +4592 -251
- package/dist/hooks/instructions-loaded.js +77 -22
- package/dist/hooks/notification.js +77 -22
- package/dist/hooks/post-compact.js +98 -26
- package/dist/hooks/post-tool-combined.js +98 -26
- package/dist/hooks/pre-compact.js +482 -119
- package/dist/hooks/pre-tool-use.js +148 -26
- package/dist/hooks/prompt-submit.js +162 -39
- package/dist/hooks/session-end.js +484 -124
- package/dist/hooks/session-start.js +135 -27
- package/dist/hooks/stop.js +97 -25
- package/dist/hooks/subagent-stop.js +98 -26
- package/dist/hooks/summary-worker.js +107 -18
- package/dist/index.js +188 -38
- package/dist/lib/agent-config.js +24 -1
- package/dist/lib/cloud-sync.js +72 -12
- package/dist/lib/consolidation.js +25 -2
- 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 +24 -1
- package/dist/lib/exe-daemon.js +441 -58
- package/dist/lib/hybrid-search.js +77 -22
- package/dist/lib/keychain.js +24 -12
- package/dist/lib/license.js +3 -3
- package/dist/lib/messaging.js +21 -4
- package/dist/lib/schedules.js +53 -13
- package/dist/lib/skill-learning.js +466 -70
- package/dist/lib/status-brief.js +14 -1
- package/dist/lib/store.js +78 -23
- package/dist/lib/tasks.js +403 -95
- package/dist/lib/tmux-routing.js +326 -18
- package/dist/mcp/server.js +213 -45
- package/dist/mcp/tools/create-task.js +85 -17
- 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 +400 -95
- package/dist/runtime/index.js +506 -116
- package/dist/tui/App.js +268 -69
- package/package.json +1 -1
package/dist/bin/exe-call.js
CHANGED
|
@@ -290,11 +290,17 @@ var init_platform_procedures = __esm({
|
|
|
290
290
|
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."
|
|
291
291
|
},
|
|
292
292
|
{
|
|
293
|
-
title: "
|
|
293
|
+
title: "Orchestration phase guidance \u2014 recommend, never trap",
|
|
294
294
|
domain: "workflow",
|
|
295
295
|
priority: "p1",
|
|
296
296
|
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."
|
|
297
297
|
},
|
|
298
|
+
{
|
|
299
|
+
title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
|
|
300
|
+
domain: "identity",
|
|
301
|
+
priority: "p0",
|
|
302
|
+
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."
|
|
303
|
+
},
|
|
298
304
|
{
|
|
299
305
|
title: "Single dispatch path \u2014 create_task only",
|
|
300
306
|
domain: "workflow",
|
|
@@ -328,6 +334,12 @@ var init_platform_procedures = __esm({
|
|
|
328
334
|
priority: "p0",
|
|
329
335
|
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."
|
|
330
336
|
},
|
|
337
|
+
{
|
|
338
|
+
title: "Destructive operations \u2014 mandatory reviewer gate",
|
|
339
|
+
domain: "security",
|
|
340
|
+
priority: "p0",
|
|
341
|
+
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."
|
|
342
|
+
},
|
|
331
343
|
{
|
|
332
344
|
title: "Customer patch triage \u2014 upstream bug vs customization",
|
|
333
345
|
domain: "support",
|
|
@@ -573,9 +585,23 @@ __export(employee_templates_exports, {
|
|
|
573
585
|
function getSessionPrompt(storedPrompt) {
|
|
574
586
|
const markerIndex = storedPrompt.indexOf(PROCEDURES_MARKER);
|
|
575
587
|
const withoutProcedures = markerIndex >= 0 ? storedPrompt.slice(0, markerIndex).trimEnd() : storedPrompt;
|
|
588
|
+
let titlePrefix = "";
|
|
589
|
+
const frontmatterMatch = withoutProcedures.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
590
|
+
if (frontmatterMatch) {
|
|
591
|
+
const titleMatch = frontmatterMatch[1].match(/^title:\s*(.+)$/m);
|
|
592
|
+
const roleMatch = frontmatterMatch[1].match(/^role:\s*(.+)$/m);
|
|
593
|
+
if (titleMatch) {
|
|
594
|
+
const title = titleMatch[1].trim();
|
|
595
|
+
const role = roleMatch ? roleMatch[1].trim() : "";
|
|
596
|
+
if (title && role && title.toLowerCase() !== role.toLowerCase()) {
|
|
597
|
+
titlePrefix = `## Your Identity
|
|
598
|
+
You are **${title}** (specialist). `;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
576
602
|
const rolePrompt = withoutProcedures.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").replace(/<!--[\s\S]*?-->/g, "").trimStart();
|
|
577
603
|
const globalBlock = getGlobalProceduresBlock();
|
|
578
|
-
return `${globalBlock}${rolePrompt}
|
|
604
|
+
return `${globalBlock}${titlePrefix}${rolePrompt}
|
|
579
605
|
${BASE_OPERATING_PROCEDURES}`;
|
|
580
606
|
}
|
|
581
607
|
function buildCustomEmployeePrompt(name, role) {
|
|
@@ -594,7 +620,7 @@ function personalizePrompt(prompt, templateName, actualName) {
|
|
|
594
620
|
return prompt.replace(new RegExp(`\\bYou are ${escaped}\\b`, "g"), `You are ${actualName}`);
|
|
595
621
|
}
|
|
596
622
|
function renderClientCOOTemplate(vars) {
|
|
597
|
-
const resolved = { ...vars, title: vars.title || "Chief
|
|
623
|
+
const resolved = { ...vars, title: vars.title || "Chief of Staff" };
|
|
598
624
|
for (const key of CLIENT_COO_PLACEHOLDERS) {
|
|
599
625
|
const value = resolved[key];
|
|
600
626
|
if (typeof value !== "string" || value.length === 0) {
|
|
@@ -1237,7 +1263,9 @@ __export(agent_config_exports, {
|
|
|
1237
1263
|
clearAgentRuntime: () => clearAgentRuntime,
|
|
1238
1264
|
getAgentRuntime: () => getAgentRuntime,
|
|
1239
1265
|
loadAgentConfig: () => loadAgentConfig,
|
|
1266
|
+
normalizeCcModelName: () => normalizeCcModelName,
|
|
1240
1267
|
saveAgentConfig: () => saveAgentConfig,
|
|
1268
|
+
setAgentMcps: () => setAgentMcps,
|
|
1241
1269
|
setAgentRuntime: () => setAgentRuntime
|
|
1242
1270
|
});
|
|
1243
1271
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync5 } from "fs";
|
|
@@ -1264,7 +1292,14 @@ function getAgentRuntime(agentId) {
|
|
|
1264
1292
|
if (orgDefault) return orgDefault;
|
|
1265
1293
|
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
1266
1294
|
}
|
|
1267
|
-
function
|
|
1295
|
+
function normalizeCcModelName(model) {
|
|
1296
|
+
let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
1297
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
1298
|
+
ccModel += "[1m]";
|
|
1299
|
+
}
|
|
1300
|
+
return ccModel;
|
|
1301
|
+
}
|
|
1302
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
1268
1303
|
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
1269
1304
|
if (!knownModels) {
|
|
1270
1305
|
return {
|
|
@@ -1279,12 +1314,26 @@ function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
|
|
|
1279
1314
|
};
|
|
1280
1315
|
}
|
|
1281
1316
|
const config = loadAgentConfig();
|
|
1317
|
+
const existing = config[agentId];
|
|
1282
1318
|
const entry = { runtime, model };
|
|
1283
1319
|
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
1320
|
+
if (mcps !== void 0) {
|
|
1321
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
1322
|
+
} else if (existing?.mcps) {
|
|
1323
|
+
entry.mcps = existing.mcps;
|
|
1324
|
+
}
|
|
1284
1325
|
config[agentId] = entry;
|
|
1285
1326
|
saveAgentConfig(config);
|
|
1286
1327
|
return { ok: true };
|
|
1287
1328
|
}
|
|
1329
|
+
function setAgentMcps(agentId, mcps) {
|
|
1330
|
+
const config = loadAgentConfig();
|
|
1331
|
+
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
1332
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
1333
|
+
config[agentId] = existing;
|
|
1334
|
+
saveAgentConfig(config);
|
|
1335
|
+
return { ok: true };
|
|
1336
|
+
}
|
|
1288
1337
|
function clearAgentRuntime(agentId) {
|
|
1289
1338
|
const config = loadAgentConfig();
|
|
1290
1339
|
delete config[agentId];
|
package/dist/bin/exe-cloud.js
CHANGED
|
@@ -25,7 +25,7 @@ __export(keychain_exports, {
|
|
|
25
25
|
importMnemonic: () => importMnemonic,
|
|
26
26
|
setMasterKey: () => setMasterKey
|
|
27
27
|
});
|
|
28
|
-
import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
|
|
28
|
+
import { readFile, writeFile, unlink, mkdir, chmod, rename, copyFile } from "fs/promises";
|
|
29
29
|
import { existsSync, statSync } from "fs";
|
|
30
30
|
import { execSync } from "child_process";
|
|
31
31
|
import path from "path";
|
|
@@ -60,12 +60,14 @@ function linuxSecretAvailable() {
|
|
|
60
60
|
function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
61
61
|
if (process.platform !== "linux") return false;
|
|
62
62
|
try {
|
|
63
|
-
const uid = typeof os.userInfo().uid === "number" ? os.userInfo().uid : -1;
|
|
64
63
|
const st = statSync(keyPath);
|
|
65
64
|
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
65
|
+
const uid = typeof os.userInfo().uid === "number" ? os.userInfo().uid : -1;
|
|
66
66
|
if (uid === 0) return true;
|
|
67
67
|
const exeOsDir = process.env.EXE_OS_DIR;
|
|
68
|
-
|
|
68
|
+
if (exeOsDir && path.resolve(keyPath).startsWith(path.resolve(exeOsDir) + path.sep)) return true;
|
|
69
|
+
if (!linuxSecretAvailable()) return true;
|
|
70
|
+
return false;
|
|
69
71
|
} catch {
|
|
70
72
|
return false;
|
|
71
73
|
}
|
|
@@ -215,15 +217,25 @@ async function writeMachineBoundFileFallback(b64) {
|
|
|
215
217
|
await mkdir(dir, { recursive: true });
|
|
216
218
|
const keyPath = getKeyPath();
|
|
217
219
|
const machineKey = deriveMachineKey();
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
220
|
+
const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
|
|
221
|
+
const result = machineKey ? "encrypted" : "plaintext";
|
|
222
|
+
const tmpPath = keyPath + ".tmp";
|
|
223
|
+
try {
|
|
224
|
+
if (existsSync(keyPath)) {
|
|
225
|
+
await copyFile(keyPath, keyPath + ".bak").catch(() => {
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
await writeFile(tmpPath, content, "utf-8");
|
|
229
|
+
await chmod(tmpPath, 384);
|
|
230
|
+
await rename(tmpPath, keyPath);
|
|
231
|
+
} catch (err) {
|
|
232
|
+
try {
|
|
233
|
+
await unlink(tmpPath);
|
|
234
|
+
} catch {
|
|
235
|
+
}
|
|
236
|
+
throw err;
|
|
223
237
|
}
|
|
224
|
-
|
|
225
|
-
await chmod(keyPath, 384);
|
|
226
|
-
return "plaintext";
|
|
238
|
+
return result;
|
|
227
239
|
}
|
|
228
240
|
async function getMasterKey() {
|
|
229
241
|
let nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
@@ -3772,6 +3784,22 @@ async function ensureSchema() {
|
|
|
3772
3784
|
} catch (e) {
|
|
3773
3785
|
logCatchDebug("migration", e);
|
|
3774
3786
|
}
|
|
3787
|
+
try {
|
|
3788
|
+
await client.execute({
|
|
3789
|
+
sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
|
|
3790
|
+
args: []
|
|
3791
|
+
});
|
|
3792
|
+
} catch (e) {
|
|
3793
|
+
logCatchDebug("migration", e);
|
|
3794
|
+
}
|
|
3795
|
+
try {
|
|
3796
|
+
await client.execute({
|
|
3797
|
+
sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
|
|
3798
|
+
args: []
|
|
3799
|
+
});
|
|
3800
|
+
} catch (e) {
|
|
3801
|
+
logCatchDebug("migration", e);
|
|
3802
|
+
}
|
|
3775
3803
|
}
|
|
3776
3804
|
async function disposeDatabase() {
|
|
3777
3805
|
if (_walCheckpointTimer) {
|
|
@@ -4269,7 +4297,7 @@ async function assertVpsLicense(opts) {
|
|
|
4269
4297
|
}
|
|
4270
4298
|
if (!transientFailure) {
|
|
4271
4299
|
throw new Error(
|
|
4272
|
-
"License validation failed: unknown backend state. Restore network connectivity to https://askexe.com
|
|
4300
|
+
"License validation failed: unknown backend state. Restore network connectivity to https://cloud.askexe.com and retry."
|
|
4273
4301
|
);
|
|
4274
4302
|
}
|
|
4275
4303
|
const fresh = await getCachedLicense();
|
|
@@ -4306,7 +4334,7 @@ async function assertVpsLicense(opts) {
|
|
|
4306
4334
|
} catch {
|
|
4307
4335
|
}
|
|
4308
4336
|
throw new Error(
|
|
4309
|
-
`License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com
|
|
4337
|
+
`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.`
|
|
4310
4338
|
);
|
|
4311
4339
|
}
|
|
4312
4340
|
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
@@ -4338,7 +4366,7 @@ var init_license = __esm({
|
|
|
4338
4366
|
LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
|
|
4339
4367
|
CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
|
|
4340
4368
|
DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
|
|
4341
|
-
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com
|
|
4369
|
+
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
|
|
4342
4370
|
RETRY_DELAY_MS = 500;
|
|
4343
4371
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
4344
4372
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
@@ -4745,6 +4773,7 @@ __export(cloud_sync_exports, {
|
|
|
4745
4773
|
markCloudReuploadRequired: () => markCloudReuploadRequired,
|
|
4746
4774
|
mergeConfig: () => mergeConfig,
|
|
4747
4775
|
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
4776
|
+
migrateEndpoint: () => migrateEndpoint,
|
|
4748
4777
|
pushToPostgres: () => pushToPostgres,
|
|
4749
4778
|
recordRosterDeletion: () => recordRosterDeletion
|
|
4750
4779
|
});
|
|
@@ -4915,6 +4944,15 @@ async function fetchWithRetry(url, init) {
|
|
|
4915
4944
|
}
|
|
4916
4945
|
throw lastError;
|
|
4917
4946
|
}
|
|
4947
|
+
function migrateEndpoint(endpoint) {
|
|
4948
|
+
if (endpoint === "https://askexe.com/cloud" || endpoint === "https://askexe.com/cloud/") {
|
|
4949
|
+
process.stderr.write(
|
|
4950
|
+
"[cloud-sync] Auto-migrating endpoint from askexe.com/cloud to cloud.askexe.com (bypasses Cloudflare WAF for datacenter IPs)\n"
|
|
4951
|
+
);
|
|
4952
|
+
return "https://cloud.askexe.com";
|
|
4953
|
+
}
|
|
4954
|
+
return endpoint;
|
|
4955
|
+
}
|
|
4918
4956
|
function assertSecureEndpoint(endpoint) {
|
|
4919
4957
|
if (endpoint.startsWith("https://")) return;
|
|
4920
4958
|
if (endpoint.startsWith("http://")) {
|
|
@@ -5052,6 +5090,7 @@ async function markCloudReuploadRequired(client = getClient()) {
|
|
|
5052
5090
|
await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '1')");
|
|
5053
5091
|
}
|
|
5054
5092
|
async function cloudSync(config) {
|
|
5093
|
+
config = { ...config, endpoint: migrateEndpoint(config.endpoint) };
|
|
5055
5094
|
if (!isSyncCryptoInitialized()) {
|
|
5056
5095
|
try {
|
|
5057
5096
|
const { getMasterKey: getMasterKey2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
|
|
@@ -5141,6 +5180,27 @@ async function cloudSync(config) {
|
|
|
5141
5180
|
if (stmts.length > 0) await client.batch(stmts, "write");
|
|
5142
5181
|
pulled = pullResult.records.length;
|
|
5143
5182
|
} else {
|
|
5183
|
+
try {
|
|
5184
|
+
const incomingIds = pullResult.records.map((r) => sqlSafe(r.id));
|
|
5185
|
+
if (incomingIds.length > 0) {
|
|
5186
|
+
const ph = incomingIds.map(() => "?").join(",");
|
|
5187
|
+
const existing = await client.execute({
|
|
5188
|
+
sql: `SELECT id, version, timestamp FROM memories WHERE id IN (${ph})`,
|
|
5189
|
+
args: incomingIds
|
|
5190
|
+
});
|
|
5191
|
+
const localMap = new Map(existing.rows.map((r) => [String(r.id), r]));
|
|
5192
|
+
for (const rec of pullResult.records) {
|
|
5193
|
+
const local = localMap.get(String(rec.id));
|
|
5194
|
+
if (local && Number(local.version) > 0 && Number(local.version) !== Number(rec.version ?? 0)) {
|
|
5195
|
+
process.stderr.write(
|
|
5196
|
+
`[cloud-sync] CONFLICT: memory ${String(rec.id).slice(0, 8)} \u2014 local v${local.version} vs remote v${rec.version ?? 0}. Remote wins (LWW).
|
|
5197
|
+
`
|
|
5198
|
+
);
|
|
5199
|
+
}
|
|
5200
|
+
}
|
|
5201
|
+
}
|
|
5202
|
+
} catch {
|
|
5203
|
+
}
|
|
5144
5204
|
const stmts = pullResult.records.map((rec) => ({
|
|
5145
5205
|
sql: `INSERT OR REPLACE INTO memories
|
|
5146
5206
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
@@ -6898,11 +6958,17 @@ var init_platform_procedures = __esm({
|
|
|
6898
6958
|
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."
|
|
6899
6959
|
},
|
|
6900
6960
|
{
|
|
6901
|
-
title: "
|
|
6961
|
+
title: "Orchestration phase guidance \u2014 recommend, never trap",
|
|
6902
6962
|
domain: "workflow",
|
|
6903
6963
|
priority: "p1",
|
|
6904
6964
|
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."
|
|
6905
6965
|
},
|
|
6966
|
+
{
|
|
6967
|
+
title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
|
|
6968
|
+
domain: "identity",
|
|
6969
|
+
priority: "p0",
|
|
6970
|
+
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."
|
|
6971
|
+
},
|
|
6906
6972
|
{
|
|
6907
6973
|
title: "Single dispatch path \u2014 create_task only",
|
|
6908
6974
|
domain: "workflow",
|
|
@@ -6936,6 +7002,12 @@ var init_platform_procedures = __esm({
|
|
|
6936
7002
|
priority: "p0",
|
|
6937
7003
|
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."
|
|
6938
7004
|
},
|
|
7005
|
+
{
|
|
7006
|
+
title: "Destructive operations \u2014 mandatory reviewer gate",
|
|
7007
|
+
domain: "security",
|
|
7008
|
+
priority: "p0",
|
|
7009
|
+
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."
|
|
7010
|
+
},
|
|
6939
7011
|
{
|
|
6940
7012
|
title: "Customer patch triage \u2014 upstream bug vs customization",
|
|
6941
7013
|
domain: "support",
|
|
@@ -7221,10 +7293,24 @@ function stableId(memoryId, type, content) {
|
|
|
7221
7293
|
return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
|
|
7222
7294
|
}
|
|
7223
7295
|
function cleanText(text) {
|
|
7224
|
-
|
|
7296
|
+
let cleaned = text.replace(
|
|
7297
|
+
/```(\w*)\n(.*?)(?:\n[\s\S]*?)```/g,
|
|
7298
|
+
(_m, lang, firstLine) => `[code${lang ? `:${lang}` : ""}] ${firstLine.trim()}`
|
|
7299
|
+
);
|
|
7300
|
+
cleaned = cleaned.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
7301
|
+
return cleaned;
|
|
7225
7302
|
}
|
|
7226
|
-
function
|
|
7227
|
-
|
|
7303
|
+
function splitSegments(text) {
|
|
7304
|
+
const cleaned = cleanText(text);
|
|
7305
|
+
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);
|
|
7306
|
+
if (segments.length === 0 && cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
7307
|
+
const lines = cleaned.split(/\n+/).map((l) => l.trim()).filter((l) => l.length >= MIN_SEGMENT_CHARS && l.length <= MAX_SEGMENT_CHARS);
|
|
7308
|
+
if (lines.length > 0) return lines;
|
|
7309
|
+
if (cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
7310
|
+
return [cleaned.slice(0, MAX_SEGMENT_CHARS)];
|
|
7311
|
+
}
|
|
7312
|
+
}
|
|
7313
|
+
return segments;
|
|
7228
7314
|
}
|
|
7229
7315
|
function inferCardType(sentence, toolName) {
|
|
7230
7316
|
const lower = sentence.toLowerCase();
|
|
@@ -7256,12 +7342,12 @@ function predicateFor(type) {
|
|
|
7256
7342
|
}
|
|
7257
7343
|
}
|
|
7258
7344
|
function extractMemoryCards(row) {
|
|
7259
|
-
const
|
|
7345
|
+
const segments = splitSegments(row.raw_text);
|
|
7260
7346
|
const cards = [];
|
|
7261
|
-
for (const sentence of
|
|
7347
|
+
for (const sentence of segments) {
|
|
7262
7348
|
const type = inferCardType(sentence, row.tool_name);
|
|
7263
7349
|
const subject = extractSubject(sentence, row.agent_id);
|
|
7264
|
-
const content = sentence.length >
|
|
7350
|
+
const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
|
|
7265
7351
|
cards.push({
|
|
7266
7352
|
id: stableId(row.id, type, content),
|
|
7267
7353
|
memory_id: row.id,
|
|
@@ -7357,13 +7443,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
|
|
|
7357
7443
|
last_accessed: String(row.timestamp)
|
|
7358
7444
|
}));
|
|
7359
7445
|
}
|
|
7360
|
-
var MAX_CARDS_PER_MEMORY,
|
|
7446
|
+
var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
|
|
7361
7447
|
var init_memory_cards = __esm({
|
|
7362
7448
|
"src/lib/memory-cards.ts"() {
|
|
7363
7449
|
"use strict";
|
|
7364
7450
|
init_database();
|
|
7365
|
-
MAX_CARDS_PER_MEMORY =
|
|
7366
|
-
|
|
7451
|
+
MAX_CARDS_PER_MEMORY = 8;
|
|
7452
|
+
MAX_SEGMENT_CHARS = 500;
|
|
7453
|
+
MIN_SEGMENT_CHARS = 20;
|
|
7367
7454
|
}
|
|
7368
7455
|
});
|
|
7369
7456
|
|
|
@@ -8486,7 +8573,7 @@ async function setupMode() {
|
|
|
8486
8573
|
rl.close();
|
|
8487
8574
|
return;
|
|
8488
8575
|
}
|
|
8489
|
-
const endpoint = await ask(rl, " Endpoint [https://askexe.com
|
|
8576
|
+
const endpoint = await ask(rl, " Endpoint [https://cloud.askexe.com]: ") || "https://cloud.askexe.com";
|
|
8490
8577
|
console.log(" Validating...");
|
|
8491
8578
|
try {
|
|
8492
8579
|
assertSecureEndpoint(endpoint);
|
|
@@ -8552,6 +8639,14 @@ async function setupMode() {
|
|
|
8552
8639
|
}
|
|
8553
8640
|
console.log("");
|
|
8554
8641
|
}
|
|
8642
|
+
try {
|
|
8643
|
+
const keyInfo = await getKeyStorageInfo();
|
|
8644
|
+
if (keyInfo.kind !== "missing") {
|
|
8645
|
+
console.log(` \u2713 Encryption key: ${keyInfo.note}`);
|
|
8646
|
+
}
|
|
8647
|
+
} catch {
|
|
8648
|
+
}
|
|
8649
|
+
console.log("");
|
|
8555
8650
|
console.log(BAR);
|
|
8556
8651
|
console.log(latestConfig.cloud?.apiKey ? " Setup complete. Cloud sync is connected." : " Setup complete. Cloud sync is not configured yet.");
|
|
8557
8652
|
console.log(BAR);
|
|
@@ -8643,7 +8738,13 @@ async function statusMode() {
|
|
|
8643
8738
|
console.log("");
|
|
8644
8739
|
if (key) {
|
|
8645
8740
|
const fingerprint = key.toString("hex").slice(0, 8);
|
|
8646
|
-
|
|
8741
|
+
let storageNote = "";
|
|
8742
|
+
try {
|
|
8743
|
+
const keyInfo = await getKeyStorageInfo();
|
|
8744
|
+
if (keyInfo.kind !== "missing") storageNote = ` [${keyInfo.note}]`;
|
|
8745
|
+
} catch {
|
|
8746
|
+
}
|
|
8747
|
+
console.log(` Encryption key: \u2713 present (${fingerprint}...)${storageNote}`);
|
|
8647
8748
|
} else {
|
|
8648
8749
|
console.log(" Encryption key: \u2717 not found \u2014 run /exe-setup");
|
|
8649
8750
|
}
|