@askexenow/exe-os 0.9.262 → 0.9.264

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.
@@ -1180,7 +1180,7 @@
1180
1180
  "healthUrl": "http://127.0.0.1:8765/health"
1181
1181
  },
1182
1182
  "gateway": {
1183
- "image": "update.askexe.com/askexe/exe-gateway:v0.9.16",
1183
+ "image": "update.askexe.com/askexe/exe-gateway:v0.9.17",
1184
1184
  "env": "GATEWAY_IMAGE_TAG",
1185
1185
  "composeService": "exe-gateway",
1186
1186
  "healthUrl": "http://127.0.0.1:3100/health"
@@ -20,7 +20,7 @@ import {
20
20
  parseFlags,
21
21
  runAudit,
22
22
  splitAtSentences
23
- } from "../chunk-BJYXHSFN.js";
23
+ } from "../chunk-3X555IGG.js";
24
24
  import "../chunk-L3TB7CC3.js";
25
25
  import "../chunk-6Y4B3QF6.js";
26
26
  import "../chunk-MLKGABMK.js";
@@ -4,7 +4,7 @@ import {
4
4
  } from "../chunk-GMUZHA5J.js";
5
5
  import {
6
6
  lightweightSearch
7
- } from "../chunk-K6FHWGFL.js";
7
+ } from "../chunk-Y32AMMDY.js";
8
8
  import "../chunk-6YHVX54O.js";
9
9
  import "../chunk-CHCA3ZM2.js";
10
10
  import "../chunk-P2XNRKEL.js";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  hybridSearch,
4
4
  lightweightSearch
5
- } from "../chunk-K6FHWGFL.js";
5
+ } from "../chunk-Y32AMMDY.js";
6
6
  import {
7
7
  initStore
8
8
  } from "../chunk-6YHVX54O.js";
@@ -4,7 +4,7 @@ import {
4
4
  main,
5
5
  runHealth,
6
6
  runTest
7
- } from "../chunk-235ZCOYB.js";
7
+ } from "../chunk-IBGC6JFX.js";
8
8
  import "../chunk-2XZ6X3PJ.js";
9
9
  import "../chunk-MMRUBN3I.js";
10
10
  import "../chunk-MOZ2YQ54.js";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  registryProxyOptionsFromEnv,
4
4
  runRegistryProxy
5
- } from "../chunk-SHN5O73O.js";
5
+ } from "../chunk-FJUQ5L5R.js";
6
6
  import {
7
7
  isMainModule
8
8
  } from "../chunk-6Y4B3QF6.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  lightweightSearch
3
- } from "./chunk-K6FHWGFL.js";
3
+ } from "./chunk-Y32AMMDY.js";
4
4
  import "./chunk-6YHVX54O.js";
5
5
  import "./chunk-CHCA3ZM2.js";
6
6
  import "./chunk-P2XNRKEL.js";
@@ -622,7 +622,7 @@ async function auditKeyHealth() {
622
622
  }
623
623
  async function auditOutbox() {
624
624
  try {
625
- const { getOutboxStatus } = await import("./support-outbox-SZVLHHZG.js");
625
+ const { getOutboxStatus } = await import("./support-outbox-SO73Q5H2.js");
626
626
  return await getOutboxStatus();
627
627
  } catch {
628
628
  return {
@@ -15,7 +15,7 @@ function registryProxyOptionsFromEnv(env = process.env) {
15
15
  upstreamToken,
16
16
  pullTokens,
17
17
  allowedNamespace: env.EXE_REGISTRY_PROXY_ALLOWED_NAMESPACE || "askexe",
18
- licenseValidatorUrl: env.EXE_REGISTRY_PROXY_LICENSE_VALIDATOR_URL || ""
18
+ licenseValidatorUrl: env.EXE_REGISTRY_PROXY_LICENSE_VALIDATOR_URL || "https://api.askexe.com/v1/license/validate"
19
19
  };
20
20
  }
21
21
  function assertRegistryProxyConfig(options) {
@@ -275,7 +275,7 @@ function nextForPostFailure(status) {
275
275
  return "Run `exe-os support health`; if it passes, send this output to AskExe.";
276
276
  }
277
277
  async function runFlush(json) {
278
- const { flushSupportOutbox } = await import("./support-outbox-SZVLHHZG.js");
278
+ const { flushSupportOutbox } = await import("./support-outbox-SO73Q5H2.js");
279
279
  const result = await flushSupportOutbox({ maxPerFlush: 10 });
280
280
  if (json) {
281
281
  console.log(JSON.stringify(result, null, 2));
@@ -298,7 +298,7 @@ async function runFlush(json) {
298
298
  console.log("");
299
299
  }
300
300
  async function runStatus(json) {
301
- const { getOutboxStatus } = await import("./support-outbox-SZVLHHZG.js");
301
+ const { getOutboxStatus } = await import("./support-outbox-SO73Q5H2.js");
302
302
  const status = await getOutboxStatus();
303
303
  if (json) {
304
304
  console.log(JSON.stringify(status, null, 2));
@@ -288,7 +288,7 @@ async function hybridSearch(queryText, agentId, options) {
288
288
  let rerankerAvailable = false;
289
289
  if (process.env.EXE_IS_DAEMON === "1") {
290
290
  try {
291
- const { isRerankerAvailable } = await import("./reranker-RPPWEE73.js");
291
+ const { isRerankerAvailable } = await import("./reranker-OZR43HRG.js");
292
292
  rerankerAvailable = isRerankerAvailable();
293
293
  } catch {
294
294
  }
@@ -452,7 +452,7 @@ async function hybridSearch(queryText, agentId, options) {
452
452
  try {
453
453
  let rerankedRecords;
454
454
  if (graphContextMap.size > 0) {
455
- const { rerankWithContext } = await import("./reranker-RPPWEE73.js");
455
+ const { rerankWithContext } = await import("./reranker-OZR43HRG.js");
456
456
  const candidates = merged.map((m) => ({
457
457
  text: m.raw_text,
458
458
  context: graphContextMap.get(m.id)
@@ -460,7 +460,7 @@ async function hybridSearch(queryText, agentId, options) {
460
460
  const scored = await rerankWithContext(effectiveQuery, candidates, rerankReturnLimit);
461
461
  rerankedRecords = scored.map((s) => merged[s.index]);
462
462
  } else {
463
- const { rerank } = await import("./reranker-RPPWEE73.js");
463
+ const { rerank } = await import("./reranker-OZR43HRG.js");
464
464
  rerankedRecords = await rerank(effectiveQuery, merged, rerankReturnLimit);
465
465
  }
466
466
  if (rerankedRecords.length > 0) {
@@ -10,7 +10,7 @@ import {
10
10
  import {
11
11
  isRerankerAvailable,
12
12
  rerankWithScores
13
- } from "./chunk-JBPOZ6LG.js";
13
+ } from "./chunk-QCURGNWR.js";
14
14
  import {
15
15
  getCachedLicenseGate
16
16
  } from "./chunk-2WBBVEIB.js";
@@ -121,7 +121,7 @@ import {
121
121
  fixShards,
122
122
  formatReport,
123
123
  runAudit
124
- } from "./chunk-BJYXHSFN.js";
124
+ } from "./chunk-3X555IGG.js";
125
125
  import {
126
126
  runHealthCheck
127
127
  } from "./chunk-RQI5TG5F.js";
@@ -129,7 +129,7 @@ import {
129
129
  hasFailures,
130
130
  runHealth,
131
131
  runTest
132
- } from "./chunk-235ZCOYB.js";
132
+ } from "./chunk-IBGC6JFX.js";
133
133
  import {
134
134
  supportAdminHeaders
135
135
  } from "./chunk-2XZ6X3PJ.js";
@@ -194,7 +194,7 @@ import {
194
194
  import {
195
195
  hybridSearch,
196
196
  recentRecords
197
- } from "./chunk-K6FHWGFL.js";
197
+ } from "./chunk-Y32AMMDY.js";
198
198
  import {
199
199
  attachDocumentMetadata,
200
200
  flushBatch,
@@ -11847,13 +11847,18 @@ function findPackageRoot() {
11847
11847
  throw new Error("Cannot find package root");
11848
11848
  }
11849
11849
  var execFileAsync2 = promisify2(execFile2);
11850
+ function childCliEnv() {
11851
+ const env = { ...process.env };
11852
+ delete env.EXE_IS_DAEMON;
11853
+ return env;
11854
+ }
11850
11855
  async function runCommand(command, args, timeout = 6e4) {
11851
11856
  const printable = [command, ...args].join(" ");
11852
11857
  try {
11853
11858
  const { stdout, stderr } = await execFileAsync2(command, args, {
11854
11859
  timeout,
11855
11860
  maxBuffer: 1024 * 1024,
11856
- env: process.env
11861
+ env: childCliEnv()
11857
11862
  });
11858
11863
  return { ok: true, command: printable, text: `${stdout}${stderr ? `
11859
11864
  ${stderr}` : ""}`.trim() || "OK" };
@@ -11879,7 +11884,7 @@ async function runDistScript(scriptName, args, timeout = 6e4) {
11879
11884
  const { stdout, stderr } = await execFileAsync2(process.execPath, [scriptPath, ...args], {
11880
11885
  timeout,
11881
11886
  maxBuffer: 1024 * 1024,
11882
- env: process.env
11887
+ env: childCliEnv()
11883
11888
  });
11884
11889
  return { ok: true, command: printable, text: `${stdout}${stderr ? `
11885
11890
  ${stderr}` : ""}`.trim() || "OK" };
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  hybridSearch,
6
6
  lightweightSearch
7
- } from "../chunk-K6FHWGFL.js";
7
+ } from "../chunk-Y32AMMDY.js";
8
8
  import {
9
9
  initStore
10
10
  } from "../chunk-6YHVX54O.js";
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "version": 1,
3
- "generatedAt": "2026-06-10T02:47:42.754Z",
3
+ "generatedAt": "2026-06-10T03:14:04.639Z",
4
4
  "hashes": {
5
5
  "bug-report-worker.js": "5b5c47ed54222e7e9e9099dbe0032076b549d406fd0fad91e4685d345f4d465f",
6
6
  "codex-stop-task-finalizer.js": "4470796421e93af8416e83e6822e14acfa72ecc96da6574fbfb38965f2e7d7e6",
7
7
  "commit-complete.js": "d6a053e16241909b3ded37c13fa2868bf93f0fe593b5dcc2d7117fd367744861",
8
- "error-recall.js": "060435a477ed3c1b6caf7cd63b272ab393a22f579db2a55d80e2664d57197774",
8
+ "error-recall.js": "3e1bb5b447fe8a7fe935e530a4534db1d455f36f361aac677da47ad413e5c2ce",
9
9
  "exe-heartbeat-hook.js": "351dd0a9c4c1f21bf7faa3a5431b0f71f6f55bc629a6503394e1e696a9589b08",
10
10
  "ingest-worker.js": "1f6a591f863d5bc540b0e11bb382d3ba13cf761709e4a82313f6f1d9fb9c18e2",
11
11
  "ingest.js": "f040375367df68749331bbf3ea5527b754840f0ed2db621c966abe51b6375890",
@@ -15,9 +15,9 @@
15
15
  "post-tool-combined.js": "3fbbb2d941e264291567a6da97bb02523c907a0edf742ef6d59b13e5956cd4f8",
16
16
  "pre-compact.js": "8e7512e75a0407a5c632ebfb5f23595507c5d8972afcae839462f719ea56a973",
17
17
  "pre-tool-use.js": "f50d7d8b341109bf5f927295d9227877b691989fdf3ef04565e7579dfac8a66a",
18
- "prompt-submit.js": "e7c071e3e5b3e7c931ad3d94db2b9269cb56f47d26325a7406846b997ee822eb",
18
+ "prompt-submit.js": "aa73cc8af3985beb3ee81142f3948db758b69420248f135e4c71ba29e7ce901d",
19
19
  "session-end.js": "b88b3007dadaa427f748154551790648742627ac788a8fa8f76761ad8b45ef90",
20
- "session-start.js": "bd2ab64142b7e8db1ab999486f1ce5fd4f5ce52f7af28555555cd0e5403a359e",
20
+ "session-start.js": "87238eebedce4b93d2b4836651de5341c44e46b3ab34f81982feb09f761ce610",
21
21
  "stop.js": "ab962df8d9b91f04e3eb3708e67300fb0f64cd57c25d814312bc84485c4d00ae",
22
22
  "subagent-stop.js": "626924150de92ca5e509066b016950d6ce62d4df5e58a0d0713a4d36705aaefc",
23
23
  "summary-worker.js": "dc0ac0448fbec252aacccf8b8a2dfe2e296ee0306e5ce41b1362db2318307b66"
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  hybridSearch,
3
3
  lightweightSearch
4
- } from "../chunk-K6FHWGFL.js";
4
+ } from "../chunk-Y32AMMDY.js";
5
5
  import {
6
6
  initStore
7
7
  } from "../chunk-6YHVX54O.js";
@@ -673,7 +673,7 @@ ${typedSummaries.join("\n---\n")}`;
673
673
  process.stderr.write("[prompt-submit] hook main handler: " + (e instanceof Error ? e.message : String(e)) + "\n");
674
674
  }
675
675
  try {
676
- const { flushSupportOutbox } = await import("../support-outbox-SZVLHHZG.js");
676
+ const { flushSupportOutbox } = await import("../support-outbox-SO73Q5H2.js");
677
677
  void flushSupportOutbox();
678
678
  } catch {
679
679
  }
@@ -163,7 +163,7 @@ You are **${ag.agentId}**. Daemon is degraded \u2014 memory unavailable.
163
163
  query = `last actions on ${projectName}`;
164
164
  header = "## Resuming Session\nHere's where you left off:";
165
165
  try {
166
- const { buildCatchupBrief } = await import("../catchup-brief-QYHXH7X4.js");
166
+ const { buildCatchupBrief } = await import("../catchup-brief-VNX2SJ2B.js");
167
167
  const brief = await buildCatchupBrief(
168
168
  agentId,
169
169
  projectName,
@@ -376,7 +376,7 @@ This token is checked against browser-scraped content. If a page contains it, th
376
376
  } catch {
377
377
  }
378
378
  try {
379
- const { flushSupportOutbox } = await import("../support-outbox-SZVLHHZG.js");
379
+ const { flushSupportOutbox } = await import("../support-outbox-SO73Q5H2.js");
380
380
  void flushSupportOutbox();
381
381
  } catch {
382
382
  }
@@ -1230,7 +1230,7 @@ async function shutdown() {
1230
1230
  `);
1231
1231
  }
1232
1232
  try {
1233
- const { stopProjectionWorker } = await import("../projection-worker-ONHS43GB.js");
1233
+ const { stopProjectionWorker } = await import("../projection-worker-ZAUKM5Z3.js");
1234
1234
  stopProjectionWorker();
1235
1235
  } catch {
1236
1236
  }
@@ -4996,7 +4996,7 @@ try {
4996
4996
  process.stderr.write("[exed] Tool telemetry started (flush every 5m)\n");
4997
4997
  setTimeout(async () => {
4998
4998
  try {
4999
- const { startProjectionWorker } = await import("../projection-worker-ONHS43GB.js");
4999
+ const { startProjectionWorker } = await import("../projection-worker-ZAUKM5Z3.js");
5000
5000
  startProjectionWorker();
5001
5001
  } catch {
5002
5002
  }
@@ -5,7 +5,7 @@ import {
5
5
  recentRecords,
6
6
  rrfMerge,
7
7
  rrfMergeMulti
8
- } from "../chunk-K6FHWGFL.js";
8
+ } from "../chunk-Y32AMMDY.js";
9
9
  import "../chunk-6YHVX54O.js";
10
10
  import "../chunk-CHCA3ZM2.js";
11
11
  import "../chunk-P2XNRKEL.js";
@@ -4,7 +4,7 @@ import {
4
4
  parsePullTokens,
5
5
  registryProxyOptionsFromEnv,
6
6
  runRegistryProxy
7
- } from "../chunk-SHN5O73O.js";
7
+ } from "../chunk-FJUQ5L5R.js";
8
8
  import "../chunk-MLKGABMK.js";
9
9
  export {
10
10
  assertRegistryProxyConfig,
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  registerAllTools
3
- } from "../chunk-RGKPO6UP.js";
3
+ } from "../chunk-YGOUKUNX.js";
4
4
  import "../chunk-O4D4DSNZ.js";
5
5
  import "../chunk-557C2IGL.js";
6
6
  import "../chunk-KPVRGCOC.js";
7
7
  import "../chunk-PK7H6FFK.js";
8
- import "../chunk-JBPOZ6LG.js";
8
+ import "../chunk-QCURGNWR.js";
9
9
  import "../chunk-2WBBVEIB.js";
10
10
  import "../chunk-AU64ZSNH.js";
11
11
  import "../chunk-BBPRL2MP.js";
@@ -38,9 +38,9 @@ import "../chunk-L5RL4GH4.js";
38
38
  import "../chunk-GP6G6EQI.js";
39
39
  import "../chunk-GUMRIUI5.js";
40
40
  import "../chunk-I7AW4237.js";
41
- import "../chunk-BJYXHSFN.js";
41
+ import "../chunk-3X555IGG.js";
42
42
  import "../chunk-RQI5TG5F.js";
43
- import "../chunk-235ZCOYB.js";
43
+ import "../chunk-IBGC6JFX.js";
44
44
  import "../chunk-2XZ6X3PJ.js";
45
45
  import "../chunk-MMRUBN3I.js";
46
46
  import "../chunk-2WH32VJI.js";
@@ -61,7 +61,7 @@ import "../chunk-GMUZHA5J.js";
61
61
  import "../chunk-45W7EDXB.js";
62
62
  import "../chunk-Z2AEOVEZ.js";
63
63
  import "../chunk-7IZWLMTP.js";
64
- import "../chunk-K6FHWGFL.js";
64
+ import "../chunk-Y32AMMDY.js";
65
65
  import "../chunk-6YHVX54O.js";
66
66
  import "../chunk-CHCA3ZM2.js";
67
67
  import "../chunk-P2XNRKEL.js";
@@ -3,12 +3,12 @@ import {
3
3
  } from "../chunk-V4TZI6EO.js";
4
4
  import {
5
5
  registerAllTools
6
- } from "../chunk-RGKPO6UP.js";
6
+ } from "../chunk-YGOUKUNX.js";
7
7
  import "../chunk-O4D4DSNZ.js";
8
8
  import "../chunk-557C2IGL.js";
9
9
  import "../chunk-KPVRGCOC.js";
10
10
  import "../chunk-PK7H6FFK.js";
11
- import "../chunk-JBPOZ6LG.js";
11
+ import "../chunk-QCURGNWR.js";
12
12
  import {
13
13
  initLicenseGate
14
14
  } from "../chunk-2WBBVEIB.js";
@@ -46,9 +46,9 @@ import "../chunk-L5RL4GH4.js";
46
46
  import "../chunk-GP6G6EQI.js";
47
47
  import "../chunk-GUMRIUI5.js";
48
48
  import "../chunk-I7AW4237.js";
49
- import "../chunk-BJYXHSFN.js";
49
+ import "../chunk-3X555IGG.js";
50
50
  import "../chunk-RQI5TG5F.js";
51
- import "../chunk-235ZCOYB.js";
51
+ import "../chunk-IBGC6JFX.js";
52
52
  import "../chunk-2XZ6X3PJ.js";
53
53
  import "../chunk-MMRUBN3I.js";
54
54
  import "../chunk-2WH32VJI.js";
@@ -71,7 +71,7 @@ import "../chunk-GMUZHA5J.js";
71
71
  import "../chunk-45W7EDXB.js";
72
72
  import "../chunk-Z2AEOVEZ.js";
73
73
  import "../chunk-7IZWLMTP.js";
74
- import "../chunk-K6FHWGFL.js";
74
+ import "../chunk-Y32AMMDY.js";
75
75
  import {
76
76
  disposeStore,
77
77
  initStore
@@ -656,19 +656,27 @@ var projectionHandlers = {
656
656
  targets.push("graph");
657
657
  await ensureFilteredSchema(prisma);
658
658
  const senderName = payload.sender_name ?? payload.contact_name ?? null;
659
- const senderPhone = payload.from ?? payload.phone ?? null;
659
+ const rawSender = payload.from ?? payload.phone ?? null;
660
660
  const threadId = payload.conversation_id ?? payload.chat_id ?? event.source_id;
661
- if (senderPhone) {
661
+ if (rawSender) {
662
+ const bareSender = rawSender.replace(/@.*$/, "");
663
+ const phone = !rawSender.endsWith("@lid") && /^\+?\d{6,20}$/.test(bareSender) ? bareSender : null;
664
+ const name = senderName ?? phone ?? "Unknown (WhatsApp)";
662
665
  const contactResult = await prisma.$queryRawUnsafe(
663
666
  `INSERT INTO "filtered"."contacts" ("name", "phone", "platform", "platform_id", "last_seen")
664
667
  VALUES ($1, $2, 'whatsapp', $3, $4)
665
668
  ON CONFLICT ("platform", "platform_id") DO UPDATE SET
666
- "name" = COALESCE(EXCLUDED."name", "filtered"."contacts"."name"),
669
+ "name" = CASE
670
+ WHEN EXCLUDED."name" IS NOT NULL AND EXCLUDED."name" <> 'Unknown (WhatsApp)' THEN EXCLUDED."name"
671
+ WHEN "filtered"."contacts"."name" ~ '@(lid|s\\.whatsapp\\.net)$' THEN EXCLUDED."name"
672
+ ELSE "filtered"."contacts"."name"
673
+ END,
674
+ "phone" = COALESCE(EXCLUDED."phone", "filtered"."contacts"."phone"),
667
675
  "last_seen" = GREATEST("filtered"."contacts"."last_seen", EXCLUDED."last_seen")
668
676
  RETURNING "id"`,
669
- senderName,
670
- senderPhone,
671
- senderPhone,
677
+ name,
678
+ phone,
679
+ rawSender,
672
680
  event.timestamp
673
681
  );
674
682
  const contactId = contactResult[0]?.id;
@@ -5,7 +5,7 @@ import {
5
5
  rerank,
6
6
  rerankWithContext,
7
7
  rerankWithScores
8
- } from "./chunk-JBPOZ6LG.js";
8
+ } from "./chunk-QCURGNWR.js";
9
9
  import "./chunk-VXIMSRTO.js";
10
10
  import "./chunk-LYH5HE24.js";
11
11
  import "./chunk-MLKGABMK.js";
@@ -5,16 +5,19 @@ import {
5
5
  import "./chunk-MLKGABMK.js";
6
6
 
7
7
  // src/lib/support-outbox.ts
8
- import { readdirSync, readFileSync, writeFileSync, existsSync } from "fs";
8
+ import { mkdirSync, readdirSync, readFileSync, writeFileSync, existsSync } from "fs";
9
9
  import path from "path";
10
10
  import os from "os";
11
11
  var EXE_DIR = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
12
12
  var BUG_DIR = path.join(EXE_DIR, "bug-reports");
13
13
  var FEATURE_DIR = path.join(EXE_DIR, "feature-requests");
14
+ var STATUS_UPDATE_DIR = path.join(EXE_DIR, "support-status-updates");
14
15
  var SKIPPED_MARKER = "upstream_skipped: ttl_expired";
15
16
  var TTL_MS = 7 * 24 * 60 * 60 * 1e3;
16
17
  var BUG_ENDPOINT = "https://api.askexe.com/v1/support/bug-reports";
17
18
  var FEATURE_ENDPOINT = "https://api.askexe.com/v1/support/feature-requests";
19
+ var CUSTOMER_STATUS_BASE_ENDPOINT = "https://api.askexe.com/v1/support";
20
+ var ADMIN_STATUS_BASE_ENDPOINT = "https://api.askexe.com/admin/support";
18
21
  var DEFAULT_MAX_PER_FLUSH = 3;
19
22
  var consecutiveFailures = 0;
20
23
  var lastFailureTime = 0;
@@ -47,6 +50,35 @@ function loadSupportAdminToken() {
47
50
  const support = config?.support;
48
51
  return process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN || support?.adminToken || support?.admin_token;
49
52
  }
53
+ function normalizeBaseEndpoint(value, fallback) {
54
+ let raw = value || fallback;
55
+ try {
56
+ const parsed = new URL(raw);
57
+ if (parsed.hostname === "askexe.com" || parsed.hostname === "cloud.askexe.com") {
58
+ parsed.hostname = "api.askexe.com";
59
+ raw = parsed.toString();
60
+ }
61
+ } catch {
62
+ }
63
+ return raw.replace(/\/+$/, "").replace(/\/(bug-reports|feature-requests)\/?$/, "");
64
+ }
65
+ function buildStatusUpdateEndpoint(endpointPath, adminToken) {
66
+ const config = loadConfigFile();
67
+ const support = config?.support;
68
+ const routerUrl = process.env.API_ROUTER_URL?.replace(/\/+$/, "");
69
+ if (adminToken) {
70
+ const adminBase = normalizeBaseEndpoint(
71
+ process.env.ASKEXE_SUPPORT_ADMIN_ENDPOINT || support?.adminEndpoint || support?.admin_endpoint || ADMIN_STATUS_BASE_ENDPOINT,
72
+ ADMIN_STATUS_BASE_ENDPOINT
73
+ );
74
+ return `${adminBase}/${endpointPath.replace(/^\/+/, "")}`;
75
+ }
76
+ const customerBase = normalizeBaseEndpoint(
77
+ support?.bugReportEndpoint || process.env.EXE_BUG_REPORT_ENDPOINT || (routerUrl ? `${routerUrl}/v1/support` : CUSTOMER_STATUS_BASE_ENDPOINT),
78
+ CUSTOMER_STATUS_BASE_ENDPOINT
79
+ );
80
+ return `${customerBase}/${endpointPath.replace(/^\/+/, "")}`;
81
+ }
50
82
  function readTrimmed(filePath) {
51
83
  try {
52
84
  const value = readFileSync(filePath, "utf-8").trim();
@@ -55,6 +87,56 @@ function readTrimmed(filePath) {
55
87
  return void 0;
56
88
  }
57
89
  }
90
+ function statusUpdateFileName(kind, id) {
91
+ const safeId = id.replace(/[^a-z0-9-]/gi, "").slice(0, 64) || "unknown";
92
+ return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${kind}-${safeId}.json`;
93
+ }
94
+ function findExistingUnsentStatusUpdate(kind, id) {
95
+ if (!existsSync(STATUS_UPDATE_DIR)) return void 0;
96
+ try {
97
+ for (const file of readdirSync(STATUS_UPDATE_DIR).filter((f) => f.endsWith(".json"))) {
98
+ const filePath = path.join(STATUS_UPDATE_DIR, file);
99
+ const record = JSON.parse(readFileSync(filePath, "utf-8"));
100
+ if (record.kind === kind && record.id === id && !record.sent_at) return filePath;
101
+ }
102
+ } catch {
103
+ return void 0;
104
+ }
105
+ return void 0;
106
+ }
107
+ function queueSupportStatusUpdateSync(input) {
108
+ const id = input.id.trim();
109
+ if (!id) return void 0;
110
+ try {
111
+ mkdirSync(STATUS_UPDATE_DIR, { recursive: true });
112
+ const existingPath = findExistingUnsentStatusUpdate(input.kind, id);
113
+ const createdAt = (/* @__PURE__ */ new Date()).toISOString();
114
+ const filePath = existingPath ?? path.join(STATUS_UPDATE_DIR, statusUpdateFileName(input.kind, id));
115
+ let attempts = 0;
116
+ if (existingPath) {
117
+ try {
118
+ const existing = JSON.parse(readFileSync(existingPath, "utf-8"));
119
+ attempts = existing.attempts ?? 0;
120
+ } catch {
121
+ attempts = 0;
122
+ }
123
+ }
124
+ const record = {
125
+ id,
126
+ kind: input.kind,
127
+ endpointPath: input.endpointPath,
128
+ method: input.method ?? "PATCH",
129
+ payload: input.payload,
130
+ created_at: existingPath ? JSON.parse(readFileSync(existingPath, "utf-8")).created_at : createdAt,
131
+ attempts,
132
+ last_error: input.lastError
133
+ };
134
+ writeFileSync(filePath, JSON.stringify(record, null, 2), "utf-8");
135
+ return filePath;
136
+ } catch {
137
+ return void 0;
138
+ }
139
+ }
58
140
  function extractField(content, field) {
59
141
  const match = content.match(new RegExp(`^${field}:\\s*(.+)$`, "m"));
60
142
  return match?.[1]?.trim();
@@ -240,26 +322,115 @@ ${SKIPPED_MARKER}
240
322
  }
241
323
  return { sent, errors, skipped };
242
324
  }
325
+ async function sendStatusUpdateRecord(record, filePath, licenseKey, licenseToken, adminToken) {
326
+ const endpoint = buildStatusUpdateEndpoint(record.endpointPath, adminToken);
327
+ const resp = await fetch(endpoint, {
328
+ method: record.method ?? "PATCH",
329
+ headers: {
330
+ "content-type": "application/json",
331
+ ...adminToken ? { authorization: `Bearer ${adminToken}` } : {},
332
+ ...!adminToken && licenseKey ? { "x-exe-license-key": licenseKey } : {},
333
+ ...!adminToken && licenseToken ? { "x-exe-license-token": licenseToken } : {}
334
+ },
335
+ body: JSON.stringify(record.payload),
336
+ signal: AbortSignal.timeout(5e3)
337
+ });
338
+ if (resp.ok) {
339
+ writeFileSync(filePath, JSON.stringify({ ...record, sent_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), "utf-8");
340
+ onNetworkSuccess();
341
+ return true;
342
+ }
343
+ writeFileSync(filePath, JSON.stringify({
344
+ ...record,
345
+ attempts: (record.attempts ?? 0) + 1,
346
+ last_error: `HTTP ${resp.status}`,
347
+ last_attempt_at: (/* @__PURE__ */ new Date()).toISOString()
348
+ }, null, 2), "utf-8");
349
+ onNetworkFailure();
350
+ return false;
351
+ }
352
+ async function flushStatusUpdates(licenseKey, licenseToken, adminToken, maxPerFlush) {
353
+ let sent = 0;
354
+ let errors = 0;
355
+ let skipped = 0;
356
+ if (!existsSync(STATUS_UPDATE_DIR)) return { sent, errors, skipped };
357
+ let files;
358
+ try {
359
+ files = readdirSync(STATUS_UPDATE_DIR).filter((f) => f.endsWith(".json")).sort();
360
+ } catch {
361
+ return { sent, errors, skipped };
362
+ }
363
+ for (const file of files) {
364
+ if (sent >= maxPerFlush) break;
365
+ if (shouldBackoff()) break;
366
+ const filePath = path.join(STATUS_UPDATE_DIR, file);
367
+ let record;
368
+ try {
369
+ record = JSON.parse(readFileSync(filePath, "utf-8"));
370
+ } catch {
371
+ skipped++;
372
+ continue;
373
+ }
374
+ if (record.sent_at) {
375
+ skipped++;
376
+ continue;
377
+ }
378
+ try {
379
+ if (Date.now() - new Date(record.created_at).getTime() > TTL_MS) {
380
+ writeFileSync(filePath, JSON.stringify({
381
+ ...record,
382
+ skipped_at: (/* @__PURE__ */ new Date()).toISOString(),
383
+ last_error: "ttl_expired"
384
+ }, null, 2), "utf-8");
385
+ skipped++;
386
+ continue;
387
+ }
388
+ } catch {
389
+ }
390
+ try {
391
+ const ok = await sendStatusUpdateRecord(record, filePath, licenseKey, licenseToken, adminToken);
392
+ if (ok) sent++;
393
+ else errors++;
394
+ } catch (err) {
395
+ try {
396
+ writeFileSync(filePath, JSON.stringify({
397
+ ...record,
398
+ attempts: (record.attempts ?? 0) + 1,
399
+ last_error: err instanceof Error ? err.message : String(err),
400
+ last_attempt_at: (/* @__PURE__ */ new Date()).toISOString()
401
+ }, null, 2), "utf-8");
402
+ } catch {
403
+ }
404
+ onNetworkFailure();
405
+ errors++;
406
+ break;
407
+ }
408
+ }
409
+ return { sent, errors, skipped };
410
+ }
243
411
  async function flushSupportOutbox(opts) {
244
412
  const maxPerFlush = opts?.maxPerFlush ?? DEFAULT_MAX_PER_FLUSH;
245
413
  const licenseKey = loadLicenseKey();
246
414
  const licenseToken = loadLicenseToken();
247
415
  const adminToken = loadSupportAdminToken();
248
- const [bugs, features] = await Promise.all([
416
+ const [bugs, features, statusUpdates] = await Promise.all([
249
417
  flushDir(BUG_DIR, BUG_ENDPOINT, "bug", licenseKey, licenseToken, adminToken, maxPerFlush),
250
- flushDir(FEATURE_DIR, FEATURE_ENDPOINT, "feature", licenseKey, licenseToken, adminToken, maxPerFlush)
418
+ flushDir(FEATURE_DIR, FEATURE_ENDPOINT, "feature", licenseKey, licenseToken, adminToken, maxPerFlush),
419
+ flushStatusUpdates(licenseKey, licenseToken, adminToken, maxPerFlush)
251
420
  ]);
252
421
  return {
253
422
  bugsSent: bugs.sent,
254
423
  featuresSent: features.sent,
255
- errors: bugs.errors + features.errors,
256
- skipped: bugs.skipped + features.skipped
424
+ statusUpdatesSent: statusUpdates.sent,
425
+ errors: bugs.errors + features.errors + statusUpdates.errors,
426
+ skipped: bugs.skipped + features.skipped + statusUpdates.skipped
257
427
  };
258
428
  }
259
429
  async function getOutboxStatus() {
260
430
  return {
261
431
  bugReports: scanDir(BUG_DIR),
262
- featureRequests: scanDir(FEATURE_DIR)
432
+ featureRequests: scanDir(FEATURE_DIR),
433
+ statusUpdates: scanStatusDir(STATUS_UPDATE_DIR)
263
434
  };
264
435
  }
265
436
  function scanDir(dir) {
@@ -288,8 +459,32 @@ function scanDir(dir) {
288
459
  }
289
460
  return { total: files.length, unsent, oldest };
290
461
  }
462
+ function scanStatusDir(dir) {
463
+ if (!existsSync(dir)) return { total: 0, unsent: 0 };
464
+ let files;
465
+ try {
466
+ files = readdirSync(dir).filter((f) => f.endsWith(".json"));
467
+ } catch {
468
+ return { total: 0, unsent: 0 };
469
+ }
470
+ let unsent = 0;
471
+ let oldest;
472
+ for (const file of files) {
473
+ try {
474
+ const record = JSON.parse(readFileSync(path.join(dir, file), "utf-8"));
475
+ if (!record.sent_at) {
476
+ unsent++;
477
+ if (record.created_at && (!oldest || record.created_at < oldest)) oldest = record.created_at;
478
+ }
479
+ } catch {
480
+ unsent++;
481
+ }
482
+ }
483
+ return { total: files.length, unsent, oldest };
484
+ }
291
485
  export {
292
486
  flushSupportOutbox as flushBugReportOutbox,
293
487
  flushSupportOutbox,
294
- getOutboxStatus
488
+ getOutboxStatus,
489
+ queueSupportStatusUpdateSync
295
490
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askexenow/exe-os",
3
- "version": "0.9.262",
3
+ "version": "0.9.264",
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",
@@ -1,6 +1,31 @@
1
1
  {
2
- "current": "0.9.262",
2
+ "current": "0.9.264",
3
3
  "notes": {
4
+ "0.9.264": {
5
+ "version": "0.9.264",
6
+ "date": "2026-06-10",
7
+ "features": [],
8
+ "fixes": [
9
+ "harden support status sync"
10
+ ],
11
+ "security": [
12
+ "release: stack 0.9.21 pins gateway v0.9.17 (Baileys rc13 — security + WA protocol fix)"
13
+ ],
14
+ "other": [],
15
+ "migration_notes": []
16
+ },
17
+ "0.9.263": {
18
+ "version": "0.9.263",
19
+ "date": "2026-06-10",
20
+ "features": [],
21
+ "fixes": [
22
+ "harden diagnostics registry and projections",
23
+ "human-readable contact fallback for WhatsApp @lid senders"
24
+ ],
25
+ "security": [],
26
+ "other": [],
27
+ "migration_notes": []
28
+ },
4
29
  "0.9.262": {
5
30
  "version": "0.9.262",
6
31
  "date": "2026-06-10",
@@ -37,31 +62,6 @@
37
62
  "security": [],
38
63
  "other": [],
39
64
  "migration_notes": []
40
- },
41
- "0.9.259": {
42
- "version": "0.9.259",
43
- "date": "2026-06-10",
44
- "features": [
45
- "GoTrue JWT auth for dashboard (Gap #32)"
46
- ],
47
- "fixes": [],
48
- "security": [],
49
- "other": [],
50
- "migration_notes": []
51
- },
52
- "0.9.258": {
53
- "version": "0.9.258",
54
- "date": "2026-06-10",
55
- "features": [],
56
- "fixes": [
57
- "align filtered.conversations init schema with projection-worker source_ref dedup",
58
- "keep projection-worker empty-queue test compatible with startup schema self-heal"
59
- ],
60
- "security": [],
61
- "other": [],
62
- "migration_notes": [
63
- "Fresh stack DBs now create filtered.conversations.source_ref with UNIQUE(platform, source_ref); existing stacks self-heal on projection-worker startup."
64
- ]
65
65
  }
66
66
  }
67
67
  }
File without changes