@askexenow/exe-os 0.9.111 → 0.9.112

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.
Files changed (79) hide show
  1. package/dist/bin/agentic-ontology-backfill.js +8 -1
  2. package/dist/bin/agentic-reflection-backfill.js +8 -1
  3. package/dist/bin/agentic-semantic-label.js +8 -1
  4. package/dist/bin/backfill-conversations.js +8 -1
  5. package/dist/bin/backfill-responses.js +8 -1
  6. package/dist/bin/backfill-vectors.js +8 -1
  7. package/dist/bin/bulk-sync-postgres.js +8 -1
  8. package/dist/bin/cleanup-stale-review-tasks.js +8 -1
  9. package/dist/bin/cli.js +19 -4
  10. package/dist/bin/exe-agent.js +1 -1
  11. package/dist/bin/exe-assign.js +8 -1
  12. package/dist/bin/exe-boot.js +19 -4
  13. package/dist/bin/exe-call.js +1 -1
  14. package/dist/bin/exe-cloud.js +8 -1
  15. package/dist/bin/exe-dispatch.js +460 -5
  16. package/dist/bin/exe-doctor.js +8 -1
  17. package/dist/bin/exe-export-behaviors.js +13 -3
  18. package/dist/bin/exe-forget.js +8 -1
  19. package/dist/bin/exe-gateway.js +19 -4
  20. package/dist/bin/exe-heartbeat.js +8 -1
  21. package/dist/bin/exe-kill.js +8 -1
  22. package/dist/bin/exe-launch-agent.js +13 -3
  23. package/dist/bin/exe-new-employee.js +1 -1
  24. package/dist/bin/exe-pending-messages.js +8 -1
  25. package/dist/bin/exe-pending-notifications.js +8 -1
  26. package/dist/bin/exe-pending-reviews.js +8 -1
  27. package/dist/bin/exe-rename.js +8 -1
  28. package/dist/bin/exe-review.js +8 -1
  29. package/dist/bin/exe-search.js +8 -1
  30. package/dist/bin/exe-session-cleanup.js +460 -5
  31. package/dist/bin/exe-start-codex.js +13 -3
  32. package/dist/bin/exe-start-opencode.js +13 -3
  33. package/dist/bin/exe-status.js +8 -1
  34. package/dist/bin/exe-team.js +8 -1
  35. package/dist/bin/git-sweep.js +460 -5
  36. package/dist/bin/graph-backfill.js +8 -1
  37. package/dist/bin/graph-export.js +8 -1
  38. package/dist/bin/intercom-check.js +460 -5
  39. package/dist/bin/pre-publish.js +1 -1
  40. package/dist/bin/scan-tasks.js +460 -5
  41. package/dist/bin/setup.js +8 -1
  42. package/dist/bin/shard-migrate.js +8 -1
  43. package/dist/gateway/index.js +460 -5
  44. package/dist/hooks/bug-report-worker.js +460 -5
  45. package/dist/hooks/codex-stop-task-finalizer.js +467 -5
  46. package/dist/hooks/commit-complete.js +460 -5
  47. package/dist/hooks/error-recall.js +8 -1
  48. package/dist/hooks/ingest.js +8 -1
  49. package/dist/hooks/instructions-loaded.js +8 -1
  50. package/dist/hooks/notification.js +8 -1
  51. package/dist/hooks/post-compact.js +8 -1
  52. package/dist/hooks/post-tool-combined.js +8 -1
  53. package/dist/hooks/pre-compact.js +460 -5
  54. package/dist/hooks/pre-tool-use.js +8 -1
  55. package/dist/hooks/prompt-submit.js +469 -8
  56. package/dist/hooks/session-end.js +460 -5
  57. package/dist/hooks/session-start.js +8 -1
  58. package/dist/hooks/stop.js +8 -1
  59. package/dist/hooks/subagent-stop.js +8 -1
  60. package/dist/hooks/summary-worker.js +8 -1
  61. package/dist/index.js +469 -8
  62. package/dist/lib/cloud-sync.js +7 -0
  63. package/dist/lib/database.js +7 -0
  64. package/dist/lib/db.js +7 -0
  65. package/dist/lib/device-registry.js +7 -0
  66. package/dist/lib/employee-templates.js +1 -1
  67. package/dist/lib/exe-daemon.js +216 -12
  68. package/dist/lib/hybrid-search.js +8 -1
  69. package/dist/lib/schedules.js +8 -1
  70. package/dist/lib/skill-learning.js +488 -7
  71. package/dist/lib/store.js +8 -1
  72. package/dist/lib/tasks.js +452 -4
  73. package/dist/lib/tmux-routing.js +452 -4
  74. package/dist/mcp/server.js +69 -11
  75. package/dist/mcp/tools/create-task.js +452 -4
  76. package/dist/mcp/tools/update-task.js +452 -4
  77. package/dist/runtime/index.js +469 -8
  78. package/dist/tui/App.js +19 -4
  79. package/package.json +1 -1
@@ -2595,6 +2595,13 @@ async function ensureSchema() {
2595
2595
  } catch (e) {
2596
2596
  logCatchDebug("migration", e);
2597
2597
  }
2598
+ for (const col of ["created_by_agent TEXT", "created_by_device TEXT", "source_session_id TEXT"]) {
2599
+ try {
2600
+ await client.execute({ sql: `ALTER TABLE behaviors ADD COLUMN ${col}`, args: [] });
2601
+ } catch (e) {
2602
+ logCatchDebug("migration", e);
2603
+ }
2604
+ }
2598
2605
  try {
2599
2606
  await client.execute({
2600
2607
  sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
@@ -3862,6 +3869,23 @@ var init_database = __esm({
3862
3869
  });
3863
3870
 
3864
3871
  // src/lib/license.ts
3872
+ var license_exports = {};
3873
+ __export(license_exports, {
3874
+ LICENSE_PUBLIC_KEY_PEM: () => LICENSE_PUBLIC_KEY_PEM,
3875
+ PLAN_LIMITS: () => PLAN_LIMITS,
3876
+ assertVpsLicense: () => assertVpsLicense,
3877
+ checkLicense: () => checkLicense,
3878
+ getCachedLicense: () => getCachedLicense,
3879
+ isFeatureAllowed: () => isFeatureAllowed,
3880
+ loadDeviceId: () => loadDeviceId,
3881
+ loadLicense: () => loadLicense,
3882
+ mirrorLicenseKey: () => mirrorLicenseKey,
3883
+ readCachedLicenseToken: () => readCachedLicenseToken,
3884
+ saveLicense: () => saveLicense,
3885
+ startLicenseRevalidation: () => startLicenseRevalidation,
3886
+ stopLicenseRevalidation: () => stopLicenseRevalidation,
3887
+ validateLicense: () => validateLicense
3888
+ });
3865
3889
  import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync10, mkdirSync as mkdirSync5 } from "fs";
3866
3890
  import { randomUUID as randomUUID3 } from "crypto";
3867
3891
  import { createRequire as createRequire2 } from "module";
@@ -3869,7 +3893,411 @@ import { pathToFileURL as pathToFileURL2 } from "url";
3869
3893
  import os8 from "os";
3870
3894
  import path10 from "path";
3871
3895
  import { jwtVerify, importSPKI } from "jose";
3872
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, PLAN_LIMITS;
3896
+ async function fetchRetry(url, init) {
3897
+ try {
3898
+ return await fetch(url, init);
3899
+ } catch {
3900
+ await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
3901
+ return fetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
3902
+ }
3903
+ }
3904
+ function loadDeviceId() {
3905
+ const deviceJsonPath = path10.join(EXE_AI_DIR, "device.json");
3906
+ try {
3907
+ if (existsSync10(deviceJsonPath)) {
3908
+ const data = JSON.parse(readFileSync8(deviceJsonPath, "utf8"));
3909
+ if (data.deviceId) return data.deviceId;
3910
+ }
3911
+ } catch {
3912
+ }
3913
+ try {
3914
+ if (existsSync10(DEVICE_ID_PATH)) {
3915
+ const id2 = readFileSync8(DEVICE_ID_PATH, "utf8").trim();
3916
+ if (id2) return id2;
3917
+ }
3918
+ } catch {
3919
+ }
3920
+ const id = randomUUID3();
3921
+ mkdirSync5(EXE_AI_DIR, { recursive: true });
3922
+ writeFileSync6(DEVICE_ID_PATH, id, "utf8");
3923
+ return id;
3924
+ }
3925
+ function loadLicense() {
3926
+ try {
3927
+ if (!existsSync10(LICENSE_PATH)) return null;
3928
+ return readFileSync8(LICENSE_PATH, "utf8").trim();
3929
+ } catch {
3930
+ return null;
3931
+ }
3932
+ }
3933
+ function saveLicense(apiKey) {
3934
+ mkdirSync5(EXE_AI_DIR, { recursive: true });
3935
+ writeFileSync6(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
3936
+ }
3937
+ async function verifyLicenseJwt(token) {
3938
+ try {
3939
+ const key = await importSPKI(LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG);
3940
+ const { payload } = await jwtVerify(token, key, {
3941
+ algorithms: [LICENSE_JWT_ALG]
3942
+ });
3943
+ const plan = payload.plan ?? "free";
3944
+ const email = payload.sub ?? "";
3945
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
3946
+ return {
3947
+ valid: true,
3948
+ plan,
3949
+ email,
3950
+ expiresAt: payload.exp ? new Date(payload.exp * 1e3).toISOString() : null,
3951
+ deviceLimit: limits.devices,
3952
+ employeeLimit: limits.employees,
3953
+ memoryLimit: limits.memories
3954
+ };
3955
+ } catch {
3956
+ return null;
3957
+ }
3958
+ }
3959
+ async function getCachedLicense() {
3960
+ try {
3961
+ if (!existsSync10(CACHE_PATH)) return null;
3962
+ const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
3963
+ if (!raw.token || typeof raw.token !== "string") return null;
3964
+ return await verifyLicenseJwt(raw.token);
3965
+ } catch {
3966
+ return null;
3967
+ }
3968
+ }
3969
+ function readCachedLicenseToken() {
3970
+ try {
3971
+ if (!existsSync10(CACHE_PATH)) return null;
3972
+ const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
3973
+ return typeof raw.token === "string" ? raw.token : null;
3974
+ } catch {
3975
+ return null;
3976
+ }
3977
+ }
3978
+ function getRawCachedPlan() {
3979
+ try {
3980
+ const token = readCachedLicenseToken();
3981
+ if (!token) return null;
3982
+ const parts = token.split(".");
3983
+ if (parts.length !== 3) return null;
3984
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
3985
+ const plan = payload.plan ?? "free";
3986
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
3987
+ process.stderr.write(
3988
+ `[license] WARN: using unverified cached plan (API unreachable, JWT expired). Plan: ${plan}
3989
+ `
3990
+ );
3991
+ return {
3992
+ valid: true,
3993
+ plan,
3994
+ email: payload.sub ?? "",
3995
+ expiresAt: payload.exp ? new Date(payload.exp * 1e3).toISOString() : null,
3996
+ deviceLimit: limits.devices,
3997
+ employeeLimit: limits.employees,
3998
+ memoryLimit: limits.memories
3999
+ };
4000
+ } catch {
4001
+ return null;
4002
+ }
4003
+ }
4004
+ function cacheResponse(token) {
4005
+ try {
4006
+ writeFileSync6(CACHE_PATH, JSON.stringify({ token }), "utf8");
4007
+ } catch {
4008
+ }
4009
+ }
4010
+ function loadPrismaForLicense() {
4011
+ if (_prismaFailed) return null;
4012
+ const dbUrl = process.env.DATABASE_URL;
4013
+ if (!dbUrl) {
4014
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path10.join(os8.homedir(), "exe-db");
4015
+ if (!existsSync10(path10.join(exeDbRoot, "package.json"))) {
4016
+ _prismaFailed = true;
4017
+ return null;
4018
+ }
4019
+ }
4020
+ if (!_prismaPromise) {
4021
+ _prismaPromise = (async () => {
4022
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
4023
+ if (explicitPath) {
4024
+ const mod2 = await import(pathToFileURL2(explicitPath).href);
4025
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
4026
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
4027
+ return new Ctor2();
4028
+ }
4029
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path10.join(os8.homedir(), "exe-db");
4030
+ const req = createRequire2(path10.join(exeDbRoot, "package.json"));
4031
+ const entry = req.resolve("@prisma/client");
4032
+ const mod = await import(pathToFileURL2(entry).href);
4033
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
4034
+ if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
4035
+ return new Ctor();
4036
+ })().catch((err) => {
4037
+ _prismaFailed = true;
4038
+ _prismaPromise = null;
4039
+ throw err;
4040
+ });
4041
+ }
4042
+ return _prismaPromise;
4043
+ }
4044
+ async function validateViaPostgres(apiKey) {
4045
+ const loader = loadPrismaForLicense();
4046
+ if (!loader) return null;
4047
+ try {
4048
+ const prisma = await loader;
4049
+ const rows = await prisma.$queryRawUnsafe(
4050
+ `SELECT plan, email, status, device_limit, employee_limit, memory_limit, expires_at
4051
+ FROM billing.licenses WHERE key = $1 LIMIT 1`,
4052
+ apiKey
4053
+ );
4054
+ if (!rows || rows.length === 0) return null;
4055
+ const row = rows[0];
4056
+ if (row.status !== "active") return null;
4057
+ if (row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date()) return null;
4058
+ const plan = row.plan;
4059
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
4060
+ return {
4061
+ valid: true,
4062
+ plan,
4063
+ email: row.email,
4064
+ expiresAt: row.expires_at ? new Date(row.expires_at).toISOString() : null,
4065
+ deviceLimit: row.device_limit ?? limits.devices,
4066
+ employeeLimit: row.employee_limit ?? limits.employees,
4067
+ memoryLimit: row.memory_limit ?? limits.memories
4068
+ };
4069
+ } catch {
4070
+ return null;
4071
+ }
4072
+ }
4073
+ async function validateViaCFWorker(apiKey, deviceId) {
4074
+ try {
4075
+ const res = await fetchRetry(`${API_BASE}/auth/activate`, {
4076
+ method: "POST",
4077
+ headers: { "Content-Type": "application/json" },
4078
+ body: JSON.stringify({ apiKey, deviceId }),
4079
+ signal: AbortSignal.timeout(1e4)
4080
+ });
4081
+ if (!res.ok) return null;
4082
+ const data = await res.json();
4083
+ if (data.error === "device_limit_exceeded") return null;
4084
+ if (!data.valid) return null;
4085
+ if (data.token) {
4086
+ cacheResponse(data.token);
4087
+ const verified = await verifyLicenseJwt(data.token);
4088
+ if (verified) return verified;
4089
+ }
4090
+ const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
4091
+ return {
4092
+ valid: data.valid,
4093
+ plan: data.plan,
4094
+ email: data.email,
4095
+ expiresAt: data.expiresAt,
4096
+ deviceLimit: limits.devices,
4097
+ employeeLimit: limits.employees,
4098
+ memoryLimit: limits.memories
4099
+ };
4100
+ } catch {
4101
+ return null;
4102
+ }
4103
+ }
4104
+ async function validateLicense(apiKey, deviceId) {
4105
+ const did = deviceId ?? loadDeviceId();
4106
+ const pgResult = await validateViaPostgres(apiKey);
4107
+ if (pgResult) {
4108
+ try {
4109
+ writeFileSync6(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
4110
+ } catch {
4111
+ }
4112
+ return pgResult;
4113
+ }
4114
+ const cfResult = await validateViaCFWorker(apiKey, did);
4115
+ if (cfResult) return cfResult;
4116
+ const cached = await getCachedLicense();
4117
+ if (cached) return cached;
4118
+ try {
4119
+ if (existsSync10(CACHE_PATH)) {
4120
+ const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
4121
+ if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
4122
+ return raw.pgLicense;
4123
+ }
4124
+ }
4125
+ } catch {
4126
+ }
4127
+ const rawFallback = getRawCachedPlan();
4128
+ if (rawFallback) return rawFallback;
4129
+ return { ...FREE_LICENSE, valid: false };
4130
+ }
4131
+ function getCacheAgeMs() {
4132
+ try {
4133
+ const { statSync: statSync5 } = __require("fs");
4134
+ const s = statSync5(CACHE_PATH);
4135
+ return Date.now() - s.mtimeMs;
4136
+ } catch {
4137
+ return Infinity;
4138
+ }
4139
+ }
4140
+ async function checkLicense() {
4141
+ let key = loadLicense();
4142
+ if (!key) {
4143
+ try {
4144
+ const configPath = path10.join(EXE_AI_DIR, "config.json");
4145
+ if (existsSync10(configPath)) {
4146
+ const raw = JSON.parse(readFileSync8(configPath, "utf8"));
4147
+ const cloud = raw.cloud;
4148
+ if (cloud?.apiKey) {
4149
+ key = cloud.apiKey;
4150
+ saveLicense(key);
4151
+ }
4152
+ }
4153
+ } catch {
4154
+ }
4155
+ }
4156
+ if (!key) return FREE_LICENSE;
4157
+ const cached = await getCachedLicense();
4158
+ if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
4159
+ const deviceId = loadDeviceId();
4160
+ return validateLicense(key, deviceId);
4161
+ }
4162
+ function isFeatureAllowed(license, feature) {
4163
+ switch (feature) {
4164
+ case "cloud_sync":
4165
+ case "external_agents":
4166
+ case "wiki":
4167
+ return license.plan !== "free";
4168
+ case "unlimited_employees":
4169
+ return license.plan === "team" || license.plan === "agency" || license.plan === "enterprise";
4170
+ }
4171
+ }
4172
+ function mirrorLicenseKey(apiKey) {
4173
+ const trimmed = apiKey.trim();
4174
+ if (!trimmed) return;
4175
+ saveLicense(trimmed);
4176
+ }
4177
+ async function assertVpsLicense(opts) {
4178
+ const env = opts?.env ?? process.env;
4179
+ const inProduction = env.NODE_ENV === "production";
4180
+ if (!opts?.force && !inProduction) {
4181
+ return { ...FREE_LICENSE, plan: "free" };
4182
+ }
4183
+ const envKey = env.EXE_LICENSE_KEY?.trim();
4184
+ if (envKey) {
4185
+ saveLicense(envKey);
4186
+ }
4187
+ const apiKey = envKey ?? loadLicense();
4188
+ if (!apiKey) {
4189
+ throw new Error(
4190
+ "License required: set EXE_LICENSE_KEY env var with your exe_sk_* key. Purchase at https://askexe.com. This VPS image refuses to boot without a valid license."
4191
+ );
4192
+ }
4193
+ const deviceId = loadDeviceId();
4194
+ let backendResponse = null;
4195
+ let explicitRejection = false;
4196
+ let transientFailure = false;
4197
+ try {
4198
+ const res = await fetchRetry(`${API_BASE}/auth/activate`, {
4199
+ method: "POST",
4200
+ headers: { "Content-Type": "application/json" },
4201
+ body: JSON.stringify({ apiKey, deviceId }),
4202
+ signal: AbortSignal.timeout(1e4)
4203
+ });
4204
+ if (res.ok) {
4205
+ backendResponse = await res.json();
4206
+ if (!backendResponse.valid) explicitRejection = true;
4207
+ } else if (res.status === 401 || res.status === 403) {
4208
+ explicitRejection = true;
4209
+ } else {
4210
+ transientFailure = true;
4211
+ }
4212
+ } catch {
4213
+ transientFailure = true;
4214
+ }
4215
+ if (backendResponse?.valid) {
4216
+ if (backendResponse.token) {
4217
+ cacheResponse(backendResponse.token);
4218
+ const verified = await verifyLicenseJwt(backendResponse.token);
4219
+ if (verified) return verified;
4220
+ }
4221
+ const limits = PLAN_LIMITS[backendResponse.plan] ?? PLAN_LIMITS.free;
4222
+ return {
4223
+ valid: true,
4224
+ plan: backendResponse.plan,
4225
+ email: backendResponse.email,
4226
+ expiresAt: backendResponse.expiresAt,
4227
+ deviceLimit: limits.devices,
4228
+ employeeLimit: limits.employees,
4229
+ memoryLimit: limits.memories
4230
+ };
4231
+ }
4232
+ if (explicitRejection) {
4233
+ throw new Error(
4234
+ `License invalid or expired. Renew at https://askexe.com, then restart. Backend rejected key ending in ...${apiKey.slice(-4)}.`
4235
+ );
4236
+ }
4237
+ if (!transientFailure) {
4238
+ throw new Error(
4239
+ "License validation failed: unknown backend state. Restore network connectivity to https://askexe.com/cloud and retry."
4240
+ );
4241
+ }
4242
+ const fresh = await getCachedLicense();
4243
+ if (fresh && fresh.valid) return fresh;
4244
+ const graceDays = opts?.offlineGraceDays ?? 7;
4245
+ const graceMs = graceDays * 24 * 60 * 60 * 1e3;
4246
+ try {
4247
+ const token = readCachedLicenseToken();
4248
+ if (token) {
4249
+ const payloadB64 = token.split(".")[1];
4250
+ if (payloadB64) {
4251
+ const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString());
4252
+ const expMs = (payload.exp ?? 0) * 1e3;
4253
+ if (Date.now() < expMs + graceMs) {
4254
+ const key = await importSPKI(LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG);
4255
+ const { payload: verified } = await jwtVerify(token, key, {
4256
+ algorithms: [LICENSE_JWT_ALG],
4257
+ clockTolerance: graceDays * 24 * 60 * 60
4258
+ });
4259
+ const plan = verified.plan ?? "free";
4260
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
4261
+ return {
4262
+ valid: true,
4263
+ plan,
4264
+ email: verified.sub ?? "",
4265
+ expiresAt: verified.exp ? new Date(verified.exp * 1e3).toISOString() : null,
4266
+ deviceLimit: limits.devices,
4267
+ employeeLimit: limits.employees,
4268
+ memoryLimit: limits.memories
4269
+ };
4270
+ }
4271
+ }
4272
+ }
4273
+ } catch {
4274
+ }
4275
+ throw new Error(
4276
+ `License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com/cloud and retry. This VPS image refuses to boot after the offline grace window.`
4277
+ );
4278
+ }
4279
+ function startLicenseRevalidation(intervalMs = 36e5) {
4280
+ if (_revalTimer) return;
4281
+ _revalTimer = setInterval(async () => {
4282
+ try {
4283
+ const license = await checkLicense();
4284
+ if (!license.valid) {
4285
+ process.stderr.write("[exe-os] License expired or invalid \u2014 features may be restricted\n");
4286
+ }
4287
+ } catch {
4288
+ }
4289
+ }, intervalMs);
4290
+ if (_revalTimer && typeof _revalTimer === "object" && "unref" in _revalTimer) {
4291
+ _revalTimer.unref();
4292
+ }
4293
+ }
4294
+ function stopLicenseRevalidation() {
4295
+ if (_revalTimer) {
4296
+ clearInterval(_revalTimer);
4297
+ _revalTimer = null;
4298
+ }
4299
+ }
4300
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, _prismaPromise, _prismaFailed, CACHE_MAX_AGE_MS, _revalTimer;
3873
4301
  var init_license = __esm({
3874
4302
  "src/lib/license.ts"() {
3875
4303
  "use strict";
@@ -3878,6 +4306,12 @@ var init_license = __esm({
3878
4306
  CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
3879
4307
  DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
3880
4308
  API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
4309
+ RETRY_DELAY_MS = 500;
4310
+ LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
4311
+ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
4312
+ 4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
4313
+ -----END PUBLIC KEY-----`;
4314
+ LICENSE_JWT_ALG = "ES256";
3881
4315
  PLAN_LIMITS = {
3882
4316
  free: { devices: 1, employees: 1, memories: 5e3 },
3883
4317
  pro: { devices: 3, employees: 5, memories: 1e5 },
@@ -3885,6 +4319,19 @@ var init_license = __esm({
3885
4319
  agency: { devices: 50, employees: 100, memories: 1e7 },
3886
4320
  enterprise: { devices: -1, employees: -1, memories: -1 }
3887
4321
  };
4322
+ FREE_LICENSE = {
4323
+ valid: true,
4324
+ plan: "free",
4325
+ email: "",
4326
+ expiresAt: null,
4327
+ deviceLimit: 1,
4328
+ employeeLimit: 1,
4329
+ memoryLimit: 5e3
4330
+ };
4331
+ _prismaPromise = null;
4332
+ _prismaFailed = false;
4333
+ CACHE_MAX_AGE_MS = 36e5;
4334
+ _revalTimer = null;
3888
4335
  }
3889
4336
  });
3890
4337
 
@@ -5499,17 +5946,25 @@ async function storeBehavior(opts) {
5499
5946
  vector = new Float32Array(vec);
5500
5947
  } catch {
5501
5948
  }
5949
+ let createdByDevice = null;
5950
+ try {
5951
+ const { loadDeviceId: loadDeviceId2 } = await Promise.resolve().then(() => (init_license(), license_exports));
5952
+ createdByDevice = loadDeviceId2() ?? null;
5953
+ } catch {
5954
+ }
5955
+ const createdByAgent = process.env.AGENT_ID ?? null;
5956
+ const sourceSessionId = process.env.CLAUDE_SESSION_ID ?? process.env.SESSION_ID ?? null;
5502
5957
  await client.execute({
5503
- sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector)
5504
- VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`,
5505
- args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null]
5958
+ sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector, created_by_agent, created_by_device, source_session_id)
5959
+ VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?)`,
5960
+ args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null, createdByAgent, createdByDevice, sourceSessionId]
5506
5961
  });
5507
5962
  return id;
5508
5963
  }
5509
5964
  async function listBehaviors(agentId, projectName, limit = 30) {
5510
5965
  const client = getClient();
5511
5966
  const result = await client.execute({
5512
- sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector
5967
+ sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector, created_by_agent, created_by_device, source_session_id
5513
5968
  FROM behaviors
5514
5969
  WHERE agent_id = ? AND active = 1
5515
5970
  AND (project_name IS NULL OR project_name = ?)
@@ -5538,7 +5993,10 @@ async function listBehaviors(agentId, projectName, limit = 30) {
5538
5993
  active: Number(r.active),
5539
5994
  created_at: String(r.created_at),
5540
5995
  updated_at: String(r.updated_at),
5541
- vector: r.vector ? Array.from(new Float32Array(r.vector)) : null
5996
+ vector: r.vector ? Array.from(new Float32Array(r.vector)) : null,
5997
+ created_by_agent: r.created_by_agent ? String(r.created_by_agent) : null,
5998
+ created_by_device: r.created_by_device ? String(r.created_by_device) : null,
5999
+ source_session_id: r.source_session_id ? String(r.source_session_id) : null
5542
6000
  }));
5543
6001
  }
5544
6002
  async function listBehaviorsByDomain(agentId, domain) {
@@ -5559,7 +6017,10 @@ async function listBehaviorsByDomain(agentId, domain) {
5559
6017
  active: Number(r.active),
5560
6018
  created_at: String(r.created_at),
5561
6019
  updated_at: String(r.updated_at),
5562
- vector: r.vector ? Array.from(new Float32Array(r.vector)) : null
6020
+ vector: r.vector ? Array.from(new Float32Array(r.vector)) : null,
6021
+ created_by_agent: r.created_by_agent ? String(r.created_by_agent) : null,
6022
+ created_by_device: r.created_by_device ? String(r.created_by_device) : null,
6023
+ source_session_id: r.source_session_id ? String(r.source_session_id) : null
5563
6024
  }));
5564
6025
  }
5565
6026
  async function deactivateBehavior(id) {
@@ -8431,7 +8892,7 @@ var init_platform_procedures = __esm({
8431
8892
  title: "MCP tool dispatch \u2014 all tools use action parameter",
8432
8893
  domain: "tool-use",
8433
8894
  priority: "p0",
8434
- content: 'exe-os MCP tools come in two surfaces depending on EXE_MCP_TOOL_SURFACE config. Consolidated (19 tools): action-based dispatch \u2014 memory(action="recall"), task(action="create"), etc. Legacy (108 tools): one tool per operation \u2014 recall_my_memory, create_task, etc. Both surfaces have identical functionality. Use whichever tool names are available in your session. If you see domain tools (memory, task, config, etc.), use the action parameter. If you see specific tools (recall_my_memory, create_task, etc.), call them directly.'
8895
+ 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.'
8435
8896
  },
8436
8897
  {
8437
8898
  title: "MCP tools \u2014 memory, decision, and search",
package/dist/tui/App.js CHANGED
@@ -2847,6 +2847,13 @@ async function ensureSchema() {
2847
2847
  } catch (e) {
2848
2848
  logCatchDebug("migration", e);
2849
2849
  }
2850
+ for (const col of ["created_by_agent TEXT", "created_by_device TEXT", "source_session_id TEXT"]) {
2851
+ try {
2852
+ await client.execute({ sql: `ALTER TABLE behaviors ADD COLUMN ${col}`, args: [] });
2853
+ } catch (e) {
2854
+ logCatchDebug("migration", e);
2855
+ }
2856
+ }
2850
2857
  try {
2851
2858
  await client.execute({
2852
2859
  sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
@@ -6161,10 +6168,18 @@ async function storeBehavior(opts) {
6161
6168
  vector = new Float32Array(vec);
6162
6169
  } catch {
6163
6170
  }
6171
+ let createdByDevice = null;
6172
+ try {
6173
+ const { loadDeviceId: loadDeviceId2 } = await Promise.resolve().then(() => (init_license(), license_exports));
6174
+ createdByDevice = loadDeviceId2() ?? null;
6175
+ } catch {
6176
+ }
6177
+ const createdByAgent = process.env.AGENT_ID ?? null;
6178
+ const sourceSessionId = process.env.CLAUDE_SESSION_ID ?? process.env.SESSION_ID ?? null;
6164
6179
  await client.execute({
6165
- sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector)
6166
- VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`,
6167
- args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null]
6180
+ sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector, created_by_agent, created_by_device, source_session_id)
6181
+ VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?)`,
6182
+ args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null, createdByAgent, createdByDevice, sourceSessionId]
6168
6183
  });
6169
6184
  return id;
6170
6185
  }
@@ -9353,7 +9368,7 @@ var init_platform_procedures = __esm({
9353
9368
  title: "MCP tool dispatch \u2014 all tools use action parameter",
9354
9369
  domain: "tool-use",
9355
9370
  priority: "p0",
9356
- content: 'exe-os MCP tools come in two surfaces depending on EXE_MCP_TOOL_SURFACE config. Consolidated (19 tools): action-based dispatch \u2014 memory(action="recall"), task(action="create"), etc. Legacy (108 tools): one tool per operation \u2014 recall_my_memory, create_task, etc. Both surfaces have identical functionality. Use whichever tool names are available in your session. If you see domain tools (memory, task, config, etc.), use the action parameter. If you see specific tools (recall_my_memory, create_task, etc.), call them directly.'
9371
+ 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.'
9357
9372
  },
9358
9373
  {
9359
9374
  title: "MCP tools \u2014 memory, decision, and search",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askexenow/exe-os",
3
- "version": "0.9.111",
3
+ "version": "0.9.112",
4
4
  "description": "AI employee operating system — persistent memory, task management, and multi-agent coordination for Claude Code.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",