@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
|
@@ -3553,6 +3553,22 @@ async function ensureSchema() {
|
|
|
3553
3553
|
} catch (e) {
|
|
3554
3554
|
logCatchDebug("migration", e);
|
|
3555
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
|
+
}
|
|
3556
3572
|
}
|
|
3557
3573
|
async function disposeDatabase() {
|
|
3558
3574
|
if (_walCheckpointTimer) {
|
|
@@ -3619,7 +3635,7 @@ var init_license = __esm({
|
|
|
3619
3635
|
LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
|
|
3620
3636
|
CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
3621
3637
|
DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
|
|
3622
|
-
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com
|
|
3638
|
+
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
|
|
3623
3639
|
}
|
|
3624
3640
|
});
|
|
3625
3641
|
|
|
@@ -3672,6 +3688,18 @@ function extractRootExe(name) {
|
|
|
3672
3688
|
const parts = name.split("-").filter(Boolean);
|
|
3673
3689
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
3674
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
|
+
}
|
|
3675
3703
|
function getParentExe(sessionKey) {
|
|
3676
3704
|
try {
|
|
3677
3705
|
const data = JSON.parse(readFileSync10(path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
@@ -3681,11 +3709,12 @@ function getParentExe(sessionKey) {
|
|
|
3681
3709
|
}
|
|
3682
3710
|
}
|
|
3683
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
|
+
}
|
|
3684
3716
|
const mySession = getMySession();
|
|
3685
3717
|
if (!mySession) {
|
|
3686
|
-
if (process.env.EXE_SESSION_NAME) {
|
|
3687
|
-
return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
3688
|
-
}
|
|
3689
3718
|
return null;
|
|
3690
3719
|
}
|
|
3691
3720
|
const fromSessionName = extractRootExe(mySession);
|
|
@@ -3700,6 +3729,10 @@ function resolveExeSession() {
|
|
|
3700
3729
|
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
3701
3730
|
`
|
|
3702
3731
|
);
|
|
3732
|
+
try {
|
|
3733
|
+
registerParentExe(key, fromSessionName);
|
|
3734
|
+
} catch {
|
|
3735
|
+
}
|
|
3703
3736
|
candidate = fromSessionName;
|
|
3704
3737
|
} else {
|
|
3705
3738
|
candidate = fromCache;
|
|
@@ -3791,7 +3824,7 @@ var init_task_scope = __esm({
|
|
|
3791
3824
|
});
|
|
3792
3825
|
|
|
3793
3826
|
// src/lib/keychain.ts
|
|
3794
|
-
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3827
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
|
|
3795
3828
|
import { existsSync as existsSync13, statSync as statSync3 } from "fs";
|
|
3796
3829
|
import { execSync as execSync6 } from "child_process";
|
|
3797
3830
|
import path14 from "path";
|
|
@@ -3826,12 +3859,14 @@ function linuxSecretAvailable() {
|
|
|
3826
3859
|
function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
3827
3860
|
if (process.platform !== "linux") return false;
|
|
3828
3861
|
try {
|
|
3829
|
-
const uid = typeof os10.userInfo().uid === "number" ? os10.userInfo().uid : -1;
|
|
3830
3862
|
const st = statSync3(keyPath);
|
|
3831
3863
|
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
3864
|
+
const uid = typeof os10.userInfo().uid === "number" ? os10.userInfo().uid : -1;
|
|
3832
3865
|
if (uid === 0) return true;
|
|
3833
3866
|
const exeOsDir = process.env.EXE_OS_DIR;
|
|
3834
|
-
|
|
3867
|
+
if (exeOsDir && path14.resolve(keyPath).startsWith(path14.resolve(exeOsDir) + path14.sep)) return true;
|
|
3868
|
+
if (!linuxSecretAvailable()) return true;
|
|
3869
|
+
return false;
|
|
3835
3870
|
} catch {
|
|
3836
3871
|
return false;
|
|
3837
3872
|
}
|
|
@@ -3981,15 +4016,25 @@ async function writeMachineBoundFileFallback(b64) {
|
|
|
3981
4016
|
await mkdir3(dir, { recursive: true });
|
|
3982
4017
|
const keyPath = getKeyPath();
|
|
3983
4018
|
const machineKey = deriveMachineKey();
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
4019
|
+
const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
|
|
4020
|
+
const result = machineKey ? "encrypted" : "plaintext";
|
|
4021
|
+
const tmpPath = keyPath + ".tmp";
|
|
4022
|
+
try {
|
|
4023
|
+
if (existsSync13(keyPath)) {
|
|
4024
|
+
await copyFile(keyPath, keyPath + ".bak").catch(() => {
|
|
4025
|
+
});
|
|
4026
|
+
}
|
|
4027
|
+
await writeFile3(tmpPath, content, "utf-8");
|
|
4028
|
+
await chmod2(tmpPath, 384);
|
|
4029
|
+
await rename(tmpPath, keyPath);
|
|
4030
|
+
} catch (err) {
|
|
4031
|
+
try {
|
|
4032
|
+
await unlink(tmpPath);
|
|
4033
|
+
} catch {
|
|
4034
|
+
}
|
|
4035
|
+
throw err;
|
|
3989
4036
|
}
|
|
3990
|
-
|
|
3991
|
-
await chmod2(keyPath, 384);
|
|
3992
|
-
return "plaintext";
|
|
4037
|
+
return result;
|
|
3993
4038
|
}
|
|
3994
4039
|
async function getMasterKey() {
|
|
3995
4040
|
let nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
@@ -4863,11 +4908,17 @@ var init_platform_procedures = __esm({
|
|
|
4863
4908
|
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."
|
|
4864
4909
|
},
|
|
4865
4910
|
{
|
|
4866
|
-
title: "
|
|
4911
|
+
title: "Orchestration phase guidance \u2014 recommend, never trap",
|
|
4867
4912
|
domain: "workflow",
|
|
4868
4913
|
priority: "p1",
|
|
4869
4914
|
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."
|
|
4870
4915
|
},
|
|
4916
|
+
{
|
|
4917
|
+
title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
|
|
4918
|
+
domain: "identity",
|
|
4919
|
+
priority: "p0",
|
|
4920
|
+
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."
|
|
4921
|
+
},
|
|
4871
4922
|
{
|
|
4872
4923
|
title: "Single dispatch path \u2014 create_task only",
|
|
4873
4924
|
domain: "workflow",
|
|
@@ -4901,6 +4952,12 @@ var init_platform_procedures = __esm({
|
|
|
4901
4952
|
priority: "p0",
|
|
4902
4953
|
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."
|
|
4903
4954
|
},
|
|
4955
|
+
{
|
|
4956
|
+
title: "Destructive operations \u2014 mandatory reviewer gate",
|
|
4957
|
+
domain: "security",
|
|
4958
|
+
priority: "p0",
|
|
4959
|
+
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."
|
|
4960
|
+
},
|
|
4904
4961
|
{
|
|
4905
4962
|
title: "Customer patch triage \u2014 upstream bug vs customization",
|
|
4906
4963
|
domain: "support",
|
|
@@ -5186,10 +5243,24 @@ function stableId(memoryId, type, content) {
|
|
|
5186
5243
|
return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
|
|
5187
5244
|
}
|
|
5188
5245
|
function cleanText(text) {
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5246
|
+
let cleaned = text.replace(
|
|
5247
|
+
/```(\w*)\n(.*?)(?:\n[\s\S]*?)```/g,
|
|
5248
|
+
(_m, lang, firstLine) => `[code${lang ? `:${lang}` : ""}] ${firstLine.trim()}`
|
|
5249
|
+
);
|
|
5250
|
+
cleaned = cleaned.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
5251
|
+
return cleaned;
|
|
5252
|
+
}
|
|
5253
|
+
function splitSegments(text) {
|
|
5254
|
+
const cleaned = cleanText(text);
|
|
5255
|
+
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);
|
|
5256
|
+
if (segments.length === 0 && cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
5257
|
+
const lines = cleaned.split(/\n+/).map((l) => l.trim()).filter((l) => l.length >= MIN_SEGMENT_CHARS && l.length <= MAX_SEGMENT_CHARS);
|
|
5258
|
+
if (lines.length > 0) return lines;
|
|
5259
|
+
if (cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
5260
|
+
return [cleaned.slice(0, MAX_SEGMENT_CHARS)];
|
|
5261
|
+
}
|
|
5262
|
+
}
|
|
5263
|
+
return segments;
|
|
5193
5264
|
}
|
|
5194
5265
|
function inferCardType(sentence, toolName) {
|
|
5195
5266
|
const lower = sentence.toLowerCase();
|
|
@@ -5221,12 +5292,12 @@ function predicateFor(type) {
|
|
|
5221
5292
|
}
|
|
5222
5293
|
}
|
|
5223
5294
|
function extractMemoryCards(row) {
|
|
5224
|
-
const
|
|
5295
|
+
const segments = splitSegments(row.raw_text);
|
|
5225
5296
|
const cards = [];
|
|
5226
|
-
for (const sentence of
|
|
5297
|
+
for (const sentence of segments) {
|
|
5227
5298
|
const type = inferCardType(sentence, row.tool_name);
|
|
5228
5299
|
const subject = extractSubject(sentence, row.agent_id);
|
|
5229
|
-
const content = sentence.length >
|
|
5300
|
+
const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
|
|
5230
5301
|
cards.push({
|
|
5231
5302
|
id: stableId(row.id, type, content),
|
|
5232
5303
|
memory_id: row.id,
|
|
@@ -5322,13 +5393,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
|
|
|
5322
5393
|
last_accessed: String(row.timestamp)
|
|
5323
5394
|
}));
|
|
5324
5395
|
}
|
|
5325
|
-
var MAX_CARDS_PER_MEMORY,
|
|
5396
|
+
var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
|
|
5326
5397
|
var init_memory_cards = __esm({
|
|
5327
5398
|
"src/lib/memory-cards.ts"() {
|
|
5328
5399
|
"use strict";
|
|
5329
5400
|
init_database();
|
|
5330
|
-
MAX_CARDS_PER_MEMORY =
|
|
5331
|
-
|
|
5401
|
+
MAX_CARDS_PER_MEMORY = 8;
|
|
5402
|
+
MAX_SEGMENT_CHARS = 500;
|
|
5403
|
+
MIN_SEGMENT_CHARS = 20;
|
|
5332
5404
|
}
|
|
5333
5405
|
});
|
|
5334
5406
|
|
|
@@ -3320,6 +3320,22 @@ async function ensureSchema() {
|
|
|
3320
3320
|
} catch (e) {
|
|
3321
3321
|
logCatchDebug("migration", e);
|
|
3322
3322
|
}
|
|
3323
|
+
try {
|
|
3324
|
+
await client.execute({
|
|
3325
|
+
sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
|
|
3326
|
+
args: []
|
|
3327
|
+
});
|
|
3328
|
+
} catch (e) {
|
|
3329
|
+
logCatchDebug("migration", e);
|
|
3330
|
+
}
|
|
3331
|
+
try {
|
|
3332
|
+
await client.execute({
|
|
3333
|
+
sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
|
|
3334
|
+
args: []
|
|
3335
|
+
});
|
|
3336
|
+
} catch (e) {
|
|
3337
|
+
logCatchDebug("migration", e);
|
|
3338
|
+
}
|
|
3323
3339
|
}
|
|
3324
3340
|
async function disposeDatabase() {
|
|
3325
3341
|
if (_walCheckpointTimer) {
|
|
@@ -3380,7 +3396,7 @@ __export(keychain_exports, {
|
|
|
3380
3396
|
importMnemonic: () => importMnemonic,
|
|
3381
3397
|
setMasterKey: () => setMasterKey
|
|
3382
3398
|
});
|
|
3383
|
-
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3399
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
|
|
3384
3400
|
import { existsSync as existsSync7, statSync as statSync3 } from "fs";
|
|
3385
3401
|
import { execSync as execSync3 } from "child_process";
|
|
3386
3402
|
import path6 from "path";
|
|
@@ -3415,12 +3431,14 @@ function linuxSecretAvailable() {
|
|
|
3415
3431
|
function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
3416
3432
|
if (process.platform !== "linux") return false;
|
|
3417
3433
|
try {
|
|
3418
|
-
const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
|
|
3419
3434
|
const st = statSync3(keyPath);
|
|
3420
3435
|
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
3436
|
+
const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
|
|
3421
3437
|
if (uid === 0) return true;
|
|
3422
3438
|
const exeOsDir = process.env.EXE_OS_DIR;
|
|
3423
|
-
|
|
3439
|
+
if (exeOsDir && path6.resolve(keyPath).startsWith(path6.resolve(exeOsDir) + path6.sep)) return true;
|
|
3440
|
+
if (!linuxSecretAvailable()) return true;
|
|
3441
|
+
return false;
|
|
3424
3442
|
} catch {
|
|
3425
3443
|
return false;
|
|
3426
3444
|
}
|
|
@@ -3570,15 +3588,25 @@ async function writeMachineBoundFileFallback(b64) {
|
|
|
3570
3588
|
await mkdir3(dir, { recursive: true });
|
|
3571
3589
|
const keyPath = getKeyPath();
|
|
3572
3590
|
const machineKey = deriveMachineKey();
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3591
|
+
const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
|
|
3592
|
+
const result = machineKey ? "encrypted" : "plaintext";
|
|
3593
|
+
const tmpPath = keyPath + ".tmp";
|
|
3594
|
+
try {
|
|
3595
|
+
if (existsSync7(keyPath)) {
|
|
3596
|
+
await copyFile(keyPath, keyPath + ".bak").catch(() => {
|
|
3597
|
+
});
|
|
3598
|
+
}
|
|
3599
|
+
await writeFile3(tmpPath, content, "utf-8");
|
|
3600
|
+
await chmod2(tmpPath, 384);
|
|
3601
|
+
await rename(tmpPath, keyPath);
|
|
3602
|
+
} catch (err) {
|
|
3603
|
+
try {
|
|
3604
|
+
await unlink(tmpPath);
|
|
3605
|
+
} catch {
|
|
3606
|
+
}
|
|
3607
|
+
throw err;
|
|
3578
3608
|
}
|
|
3579
|
-
|
|
3580
|
-
await chmod2(keyPath, 384);
|
|
3581
|
-
return "plaintext";
|
|
3609
|
+
return result;
|
|
3582
3610
|
}
|
|
3583
3611
|
async function getMasterKey() {
|
|
3584
3612
|
let nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
@@ -4437,11 +4465,17 @@ var init_platform_procedures = __esm({
|
|
|
4437
4465
|
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."
|
|
4438
4466
|
},
|
|
4439
4467
|
{
|
|
4440
|
-
title: "
|
|
4468
|
+
title: "Orchestration phase guidance \u2014 recommend, never trap",
|
|
4441
4469
|
domain: "workflow",
|
|
4442
4470
|
priority: "p1",
|
|
4443
4471
|
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."
|
|
4444
4472
|
},
|
|
4473
|
+
{
|
|
4474
|
+
title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
|
|
4475
|
+
domain: "identity",
|
|
4476
|
+
priority: "p0",
|
|
4477
|
+
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."
|
|
4478
|
+
},
|
|
4445
4479
|
{
|
|
4446
4480
|
title: "Single dispatch path \u2014 create_task only",
|
|
4447
4481
|
domain: "workflow",
|
|
@@ -4475,6 +4509,12 @@ var init_platform_procedures = __esm({
|
|
|
4475
4509
|
priority: "p0",
|
|
4476
4510
|
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."
|
|
4477
4511
|
},
|
|
4512
|
+
{
|
|
4513
|
+
title: "Destructive operations \u2014 mandatory reviewer gate",
|
|
4514
|
+
domain: "security",
|
|
4515
|
+
priority: "p0",
|
|
4516
|
+
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."
|
|
4517
|
+
},
|
|
4478
4518
|
{
|
|
4479
4519
|
title: "Customer patch triage \u2014 upstream bug vs customization",
|
|
4480
4520
|
domain: "support",
|
|
@@ -5400,7 +5440,7 @@ async function assertVpsLicense(opts) {
|
|
|
5400
5440
|
}
|
|
5401
5441
|
if (!transientFailure) {
|
|
5402
5442
|
throw new Error(
|
|
5403
|
-
"License validation failed: unknown backend state. Restore network connectivity to https://askexe.com
|
|
5443
|
+
"License validation failed: unknown backend state. Restore network connectivity to https://cloud.askexe.com and retry."
|
|
5404
5444
|
);
|
|
5405
5445
|
}
|
|
5406
5446
|
const fresh = await getCachedLicense();
|
|
@@ -5437,7 +5477,7 @@ async function assertVpsLicense(opts) {
|
|
|
5437
5477
|
} catch {
|
|
5438
5478
|
}
|
|
5439
5479
|
throw new Error(
|
|
5440
|
-
`License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com
|
|
5480
|
+
`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.`
|
|
5441
5481
|
);
|
|
5442
5482
|
}
|
|
5443
5483
|
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
@@ -5469,7 +5509,7 @@ var init_license = __esm({
|
|
|
5469
5509
|
LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
|
|
5470
5510
|
CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
5471
5511
|
DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
|
|
5472
|
-
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com
|
|
5512
|
+
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
|
|
5473
5513
|
RETRY_DELAY_MS = 500;
|
|
5474
5514
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
5475
5515
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
@@ -5658,6 +5698,18 @@ function extractRootExe(name) {
|
|
|
5658
5698
|
const parts = name.split("-").filter(Boolean);
|
|
5659
5699
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
5660
5700
|
}
|
|
5701
|
+
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
5702
|
+
if (!existsSync14(SESSION_CACHE)) {
|
|
5703
|
+
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
5704
|
+
}
|
|
5705
|
+
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
5706
|
+
const filePath = path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
5707
|
+
writeFileSync6(filePath, JSON.stringify({
|
|
5708
|
+
parentExe: rootExe,
|
|
5709
|
+
dispatchedBy: dispatchedBy || rootExe,
|
|
5710
|
+
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5711
|
+
}));
|
|
5712
|
+
}
|
|
5661
5713
|
function getParentExe(sessionKey) {
|
|
5662
5714
|
try {
|
|
5663
5715
|
const data = JSON.parse(readFileSync9(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
@@ -5667,11 +5719,12 @@ function getParentExe(sessionKey) {
|
|
|
5667
5719
|
}
|
|
5668
5720
|
}
|
|
5669
5721
|
function resolveExeSession() {
|
|
5722
|
+
if (process.env.EXE_SESSION_NAME) {
|
|
5723
|
+
const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
5724
|
+
if (fromEnv) return fromEnv;
|
|
5725
|
+
}
|
|
5670
5726
|
const mySession = getMySession();
|
|
5671
5727
|
if (!mySession) {
|
|
5672
|
-
if (process.env.EXE_SESSION_NAME) {
|
|
5673
|
-
return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
5674
|
-
}
|
|
5675
5728
|
return null;
|
|
5676
5729
|
}
|
|
5677
5730
|
const fromSessionName = extractRootExe(mySession);
|
|
@@ -5686,6 +5739,10 @@ function resolveExeSession() {
|
|
|
5686
5739
|
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
5687
5740
|
`
|
|
5688
5741
|
);
|
|
5742
|
+
try {
|
|
5743
|
+
registerParentExe(key, fromSessionName);
|
|
5744
|
+
} catch {
|
|
5745
|
+
}
|
|
5689
5746
|
candidate = fromSessionName;
|
|
5690
5747
|
} else {
|
|
5691
5748
|
candidate = fromCache;
|
|
@@ -6455,6 +6512,7 @@ __export(cloud_sync_exports, {
|
|
|
6455
6512
|
markCloudReuploadRequired: () => markCloudReuploadRequired,
|
|
6456
6513
|
mergeConfig: () => mergeConfig,
|
|
6457
6514
|
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
6515
|
+
migrateEndpoint: () => migrateEndpoint,
|
|
6458
6516
|
pushToPostgres: () => pushToPostgres,
|
|
6459
6517
|
recordRosterDeletion: () => recordRosterDeletion
|
|
6460
6518
|
});
|
|
@@ -6625,6 +6683,15 @@ async function fetchWithRetry(url, init) {
|
|
|
6625
6683
|
}
|
|
6626
6684
|
throw lastError;
|
|
6627
6685
|
}
|
|
6686
|
+
function migrateEndpoint(endpoint) {
|
|
6687
|
+
if (endpoint === "https://askexe.com/cloud" || endpoint === "https://askexe.com/cloud/") {
|
|
6688
|
+
process.stderr.write(
|
|
6689
|
+
"[cloud-sync] Auto-migrating endpoint from askexe.com/cloud to cloud.askexe.com (bypasses Cloudflare WAF for datacenter IPs)\n"
|
|
6690
|
+
);
|
|
6691
|
+
return "https://cloud.askexe.com";
|
|
6692
|
+
}
|
|
6693
|
+
return endpoint;
|
|
6694
|
+
}
|
|
6628
6695
|
function assertSecureEndpoint(endpoint) {
|
|
6629
6696
|
if (endpoint.startsWith("https://")) return;
|
|
6630
6697
|
if (endpoint.startsWith("http://")) {
|
|
@@ -6762,6 +6829,7 @@ async function markCloudReuploadRequired(client = getClient()) {
|
|
|
6762
6829
|
await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '1')");
|
|
6763
6830
|
}
|
|
6764
6831
|
async function cloudSync(config) {
|
|
6832
|
+
config = { ...config, endpoint: migrateEndpoint(config.endpoint) };
|
|
6765
6833
|
if (!isSyncCryptoInitialized()) {
|
|
6766
6834
|
try {
|
|
6767
6835
|
const { getMasterKey: getMasterKey2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
|
|
@@ -6851,6 +6919,27 @@ async function cloudSync(config) {
|
|
|
6851
6919
|
if (stmts.length > 0) await client.batch(stmts, "write");
|
|
6852
6920
|
pulled = pullResult.records.length;
|
|
6853
6921
|
} else {
|
|
6922
|
+
try {
|
|
6923
|
+
const incomingIds = pullResult.records.map((r) => sqlSafe(r.id));
|
|
6924
|
+
if (incomingIds.length > 0) {
|
|
6925
|
+
const ph = incomingIds.map(() => "?").join(",");
|
|
6926
|
+
const existing = await client.execute({
|
|
6927
|
+
sql: `SELECT id, version, timestamp FROM memories WHERE id IN (${ph})`,
|
|
6928
|
+
args: incomingIds
|
|
6929
|
+
});
|
|
6930
|
+
const localMap = new Map(existing.rows.map((r) => [String(r.id), r]));
|
|
6931
|
+
for (const rec of pullResult.records) {
|
|
6932
|
+
const local = localMap.get(String(rec.id));
|
|
6933
|
+
if (local && Number(local.version) > 0 && Number(local.version) !== Number(rec.version ?? 0)) {
|
|
6934
|
+
process.stderr.write(
|
|
6935
|
+
`[cloud-sync] CONFLICT: memory ${String(rec.id).slice(0, 8)} \u2014 local v${local.version} vs remote v${rec.version ?? 0}. Remote wins (LWW).
|
|
6936
|
+
`
|
|
6937
|
+
);
|
|
6938
|
+
}
|
|
6939
|
+
}
|
|
6940
|
+
}
|
|
6941
|
+
} catch {
|
|
6942
|
+
}
|
|
6854
6943
|
const stmts = pullResult.records.map((rec) => ({
|
|
6855
6944
|
sql: `INSERT OR REPLACE INTO memories
|
|
6856
6945
|
(id, agent_id, agent_role, session_id, timestamp,
|