omnius 1.0.41 → 1.0.43

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/dist/index.js CHANGED
@@ -16271,8 +16271,8 @@ function tokenizeSkillQuery(value2) {
16271
16271
  return out;
16272
16272
  }
16273
16273
  function compactSkillLine(value2, max = 180) {
16274
- const clean3 = value2.replace(/\s+/g, " ").trim();
16275
- return clean3.length > max ? `${clean3.slice(0, Math.max(0, max - 3)).trimEnd()}...` : clean3;
16274
+ const clean5 = value2.replace(/\s+/g, " ").trim();
16275
+ return clean5.length > max ? `${clean5.slice(0, Math.max(0, max - 3)).trimEnd()}...` : clean5;
16276
16276
  }
16277
16277
  function skillHaystack(skill) {
16278
16278
  return [
@@ -34756,7 +34756,7 @@ var init_key = __esm({
34756
34756
  * @param {string | Uint8Array} s
34757
34757
  * @param {boolean} [clean]
34758
34758
  */
34759
- constructor(s2, clean3) {
34759
+ constructor(s2, clean5) {
34760
34760
  if (typeof s2 === "string") {
34761
34761
  this._buf = fromString2(s2);
34762
34762
  } else if (s2 instanceof Uint8Array) {
@@ -34764,10 +34764,10 @@ var init_key = __esm({
34764
34764
  } else {
34765
34765
  throw new Error("Invalid key, should be String of Uint8Array");
34766
34766
  }
34767
- if (clean3 == null) {
34768
- clean3 = true;
34767
+ if (clean5 == null) {
34768
+ clean5 = true;
34769
34769
  }
34770
- if (clean3) {
34770
+ if (clean5) {
34771
34771
  this.clean();
34772
34772
  }
34773
34773
  if (this._buf.byteLength === 0 || this._buf[0] !== pathSep) {
@@ -85036,10 +85036,10 @@ var require_abort = __commonJS({
85036
85036
  "../node_modules/asynckit/lib/abort.js"(exports, module) {
85037
85037
  module.exports = abort;
85038
85038
  function abort(state) {
85039
- Object.keys(state.jobs).forEach(clean3.bind(state));
85039
+ Object.keys(state.jobs).forEach(clean5.bind(state));
85040
85040
  state.jobs = {};
85041
85041
  }
85042
- function clean3(key) {
85042
+ function clean5(key) {
85043
85043
  if (typeof this.jobs[key] == "function") {
85044
85044
  this.jobs[key]();
85045
85045
  }
@@ -91562,7 +91562,7 @@ var require_auto = __commonJS({
91562
91562
  // ../node_modules/acme-client/src/client.js
91563
91563
  var require_client = __commonJS({
91564
91564
  "../node_modules/acme-client/src/client.js"(exports, module) {
91565
- var { createHash: createHash26 } = __require("crypto");
91565
+ var { createHash: createHash28 } = __require("crypto");
91566
91566
  var { getPemBodyAsB64u } = require_crypto();
91567
91567
  var { log: log22 } = require_logger();
91568
91568
  var HttpClient = require_http();
@@ -91873,14 +91873,14 @@ var require_client = __commonJS({
91873
91873
  */
91874
91874
  async getChallengeKeyAuthorization(challenge) {
91875
91875
  const jwk = this.http.getJwk();
91876
- const keysum = createHash26("sha256").update(JSON.stringify(jwk));
91876
+ const keysum = createHash28("sha256").update(JSON.stringify(jwk));
91877
91877
  const thumbprint = keysum.digest("base64url");
91878
91878
  const result = `${challenge.token}.${thumbprint}`;
91879
91879
  if (challenge.type === "http-01") {
91880
91880
  return result;
91881
91881
  }
91882
91882
  if (challenge.type === "dns-01") {
91883
- return createHash26("sha256").update(result).digest("base64url");
91883
+ return createHash28("sha256").update(result).digest("base64url");
91884
91884
  }
91885
91885
  if (challenge.type === "tls-alpn-01") {
91886
91886
  return result;
@@ -234560,7 +234560,7 @@ var require_websocket2 = __commonJS({
234560
234560
  var http6 = __require("http");
234561
234561
  var net5 = __require("net");
234562
234562
  var tls2 = __require("tls");
234563
- var { randomBytes: randomBytes26, createHash: createHash26 } = __require("crypto");
234563
+ var { randomBytes: randomBytes26, createHash: createHash28 } = __require("crypto");
234564
234564
  var { Duplex: Duplex3, Readable } = __require("stream");
234565
234565
  var { URL: URL3 } = __require("url");
234566
234566
  var PerMessageDeflate3 = require_permessage_deflate2();
@@ -235220,7 +235220,7 @@ var require_websocket2 = __commonJS({
235220
235220
  abortHandshake(websocket, socket, "Invalid Upgrade header");
235221
235221
  return;
235222
235222
  }
235223
- const digest3 = createHash26("sha1").update(key + GUID).digest("base64");
235223
+ const digest3 = createHash28("sha1").update(key + GUID).digest("base64");
235224
235224
  if (res.headers["sec-websocket-accept"] !== digest3) {
235225
235225
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
235226
235226
  return;
@@ -235587,7 +235587,7 @@ var require_websocket_server = __commonJS({
235587
235587
  var EventEmitter13 = __require("events");
235588
235588
  var http6 = __require("http");
235589
235589
  var { Duplex: Duplex3 } = __require("stream");
235590
- var { createHash: createHash26 } = __require("crypto");
235590
+ var { createHash: createHash28 } = __require("crypto");
235591
235591
  var extension3 = require_extension2();
235592
235592
  var PerMessageDeflate3 = require_permessage_deflate2();
235593
235593
  var subprotocol3 = require_subprotocol();
@@ -235888,7 +235888,7 @@ var require_websocket_server = __commonJS({
235888
235888
  );
235889
235889
  }
235890
235890
  if (this._state > RUNNING) return abortHandshake(socket, 503);
235891
- const digest3 = createHash26("sha1").update(key + GUID).digest("base64");
235891
+ const digest3 = createHash28("sha1").update(key + GUID).digest("base64");
235892
235892
  const headers = [
235893
235893
  "HTTP/1.1 101 Switching Protocols",
235894
235894
  "Upgrade: websocket",
@@ -245608,8 +245608,8 @@ var require_pattern = __commonJS({
245608
245608
  }
245609
245609
  exports.endsWithSlashGlobStar = endsWithSlashGlobStar;
245610
245610
  function isAffectDepthOfReadingPattern(pattern) {
245611
- const basename29 = path11.basename(pattern);
245612
- return endsWithSlashGlobStar(pattern) || isStaticPattern(basename29);
245611
+ const basename30 = path11.basename(pattern);
245612
+ return endsWithSlashGlobStar(pattern) || isStaticPattern(basename30);
245613
245613
  }
245614
245614
  exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern;
245615
245615
  function expandPatternsWithBraceExpansion(patterns) {
@@ -248695,13 +248695,13 @@ Justification: ${justification || "(none provided)"}`,
248695
248695
  }
248696
248696
  const snapshot = JSON.stringify(this.selfState, null, 2);
248697
248697
  try {
248698
- const { createHash: createHash26 } = await import("node:crypto");
248698
+ const { createHash: createHash28 } = await import("node:crypto");
248699
248699
  const snapshotDir = join30(this.cwd, ".omnius", "identity", "snapshots");
248700
248700
  await mkdir6(snapshotDir, { recursive: true });
248701
248701
  const version4 = this.selfState.version;
248702
248702
  const snapshotPath = join30(snapshotDir, `v${version4}.json`);
248703
248703
  await writeFile11(snapshotPath, snapshot, "utf8");
248704
- const hash = createHash26("sha256").update(snapshot).digest("hex");
248704
+ const hash = createHash28("sha256").update(snapshot).digest("hex");
248705
248705
  await writeFile11(join30(this.cwd, ".omnius", "identity", "latest-hash.txt"), hash, "utf8");
248706
248706
  let ipfsCid = "";
248707
248707
  try {
@@ -248834,8 +248834,8 @@ New: ${newNarrative.slice(0, 200)}...`,
248834
248834
  }
248835
248835
  // ── Helpers ──────────────────────────────────────────────────────────────
248836
248836
  createDefaultState() {
248837
- const { createHash: createHash26 } = __require("node:crypto");
248838
- const machineId = createHash26("sha256").update(this.cwd).digest("hex").slice(0, 12);
248837
+ const { createHash: createHash28 } = __require("node:crypto");
248838
+ const machineId = createHash28("sha256").update(this.cwd).digest("hex").slice(0, 12);
248839
248839
  return {
248840
248840
  self_id: `omnius-${machineId}`,
248841
248841
  version: 1,
@@ -248917,9 +248917,9 @@ New: ${newNarrative.slice(0, 200)}...`,
248917
248917
  let cid;
248918
248918
  if (this.selfState.version > prevVersion) {
248919
248919
  try {
248920
- const { createHash: createHash26 } = await import("node:crypto");
248920
+ const { createHash: createHash28 } = await import("node:crypto");
248921
248921
  const stateJson = JSON.stringify(this.selfState);
248922
- const hash = createHash26("sha256").update(stateJson).digest("hex").slice(0, 32);
248922
+ const hash = createHash28("sha256").update(stateJson).digest("hex").slice(0, 32);
248923
248923
  const cidsPath = join30(this.cwd, ".omnius", "identity", "cids.json");
248924
248924
  const cidsData = { latest: "", hash, version: this.selfState.version };
248925
248925
  try {
@@ -251005,22 +251005,22 @@ async function runProcess2(command, args, options2) {
251005
251005
  return;
251006
251006
  const parts = raw.split(/[\r\n]+/).filter((part) => part.trim());
251007
251007
  for (const part of parts.length > 0 ? parts : [raw]) {
251008
- const clean3 = cleanProgressText(part);
251009
- if (!clean3)
251008
+ const clean5 = cleanProgressText(part);
251009
+ if (!clean5)
251010
251010
  continue;
251011
251011
  const now = Date.now();
251012
- const structured = parseStructuredProgress(clean3);
251013
- if (clean3 === lastProgress && now - lastProgressAt < 750)
251012
+ const structured = parseStructuredProgress(clean5);
251013
+ if (clean5 === lastProgress && now - lastProgressAt < 750)
251014
251014
  continue;
251015
- lastProgress = clean3;
251015
+ lastProgress = clean5;
251016
251016
  lastProgressAt = now;
251017
251017
  if (structured) {
251018
251018
  options2.onProgress({ ...structured, elapsedMs: now - startedAt2 });
251019
251019
  } else {
251020
251020
  options2.onProgress({
251021
251021
  stage: stream === "stderr" ? "download" : "process",
251022
- message: clean3.length > 220 ? clean3.slice(0, 217) + "..." : clean3,
251023
- percent: parsePercent(clean3),
251022
+ message: clean5.length > 220 ? clean5.slice(0, 217) + "..." : clean5,
251023
+ percent: parsePercent(clean5),
251024
251024
  elapsedMs: now - startedAt2
251025
251025
  });
251026
251026
  }
@@ -251063,10 +251063,10 @@ async function runProcess2(command, args, options2) {
251063
251063
  });
251064
251064
  }
251065
251065
  function trimProcessText(text, max = 1800) {
251066
- const clean3 = text.trim();
251067
- if (clean3.length <= max)
251068
- return clean3;
251069
- return clean3.slice(0, max - 20) + "\n... (truncated)";
251066
+ const clean5 = text.trim();
251067
+ if (clean5.length <= max)
251068
+ return clean5;
251069
+ return clean5.slice(0, max - 20) + "\n... (truncated)";
251070
251070
  }
251071
251071
  function formatDiffusersFailure(stderrOrStdout) {
251072
251072
  const text = trimProcessText(stderrOrStdout);
@@ -252632,13 +252632,13 @@ async function runProcess3(command, args, options2) {
252632
252632
  return;
252633
252633
  const parts = raw.split(/[\r\n]+/).filter((part) => part.trim());
252634
252634
  for (const part of parts.length > 0 ? parts : [raw]) {
252635
- const clean3 = cleanProgressText2(part);
252636
- if (!clean3)
252635
+ const clean5 = cleanProgressText2(part);
252636
+ if (!clean5)
252637
252637
  continue;
252638
- const jsonStart = clean3.indexOf("{");
252638
+ const jsonStart = clean5.indexOf("{");
252639
252639
  if (jsonStart >= 0) {
252640
252640
  try {
252641
- const payload = JSON.parse(clean3.slice(jsonStart));
252641
+ const payload = JSON.parse(clean5.slice(jsonStart));
252642
252642
  if (payload.omnius_progress && payload.stage && payload.message) {
252643
252643
  const rawTotalBytes = typeof payload.totalBytes === "number" ? payload.totalBytes : typeof payload.total_bytes === "number" ? payload.total_bytes : void 0;
252644
252644
  const rawDownloadedBytes = typeof payload.downloadedBytes === "number" ? payload.downloadedBytes : typeof payload.downloaded_bytes === "number" ? payload.downloaded_bytes : void 0;
@@ -252662,10 +252662,10 @@ async function runProcess3(command, args, options2) {
252662
252662
  }
252663
252663
  }
252664
252664
  const now = Date.now();
252665
- if (clean3 !== lastProgress || now - lastProgressAt > 1500) {
252666
- lastProgress = clean3;
252665
+ if (clean5 !== lastProgress || now - lastProgressAt > 1500) {
252666
+ lastProgress = clean5;
252667
252667
  lastProgressAt = now;
252668
- options2.onProgress({ stage: "process", message: clean3.slice(0, 180), elapsedMs: now - startedAt2 });
252668
+ options2.onProgress({ stage: "process", message: clean5.slice(0, 180), elapsedMs: now - startedAt2 });
252669
252669
  }
252670
252670
  }
252671
252671
  };
@@ -252725,10 +252725,10 @@ async function runProcess3(command, args, options2) {
252725
252725
  });
252726
252726
  }
252727
252727
  function trimProcessText2(text, max = 1800) {
252728
- const clean3 = text.trim();
252729
- if (clean3.length <= max)
252730
- return clean3;
252731
- return clean3.slice(0, max - 20) + "\n... (truncated)";
252728
+ const clean5 = text.trim();
252729
+ if (clean5.length <= max)
252730
+ return clean5;
252731
+ return clean5.slice(0, max - 20) + "\n... (truncated)";
252732
252732
  }
252733
252733
  async function pythonCanImport2(command, code8, repoRoot, env2) {
252734
252734
  return (await pythonImportResult(command, code8, repoRoot, env2)).code === 0;
@@ -265551,7 +265551,7 @@ var require_typescript = __commonJS({
265551
265551
  commandLineOptionOfCustomType: () => commandLineOptionOfCustomType,
265552
265552
  commentPragmas: () => commentPragmas,
265553
265553
  commonOptionsWithBuild: () => commonOptionsWithBuild,
265554
- compact: () => compact,
265554
+ compact: () => compact2,
265555
265555
  compareBooleans: () => compareBooleans,
265556
265556
  compareComparableValues: () => compareComparableValues,
265557
265557
  compareDataObjects: () => compareDataObjects,
@@ -268066,7 +268066,7 @@ var require_typescript = __commonJS({
268066
268066
  }
268067
268067
  return true;
268068
268068
  }
268069
- function compact(array) {
268069
+ function compact2(array) {
268070
268070
  let result;
268071
268071
  if (array !== void 0) {
268072
268072
  for (let i2 = 0; i2 < array.length; i2++) {
@@ -317409,8 +317409,8 @@ ${lanes.join("\n")}
317409
317409
  }
317410
317410
  }
317411
317411
  function isUseStrictPrologueDirective(node) {
317412
- const nodeText2 = getSourceTextOfNodeFromSourceFile(file, node.expression);
317413
- return nodeText2 === '"use strict"' || nodeText2 === "'use strict'";
317412
+ const nodeText22 = getSourceTextOfNodeFromSourceFile(file, node.expression);
317413
+ return nodeText22 === '"use strict"' || nodeText22 === "'use strict'";
317414
317414
  }
317415
317415
  function bindWorker(node) {
317416
317416
  switch (node.kind) {
@@ -350504,20 +350504,20 @@ ${lanes.join("\n")}
350504
350504
  return isNullableType(type) ? getNonNullableType(type) : type;
350505
350505
  }
350506
350506
  function reportObjectPossiblyNullOrUndefinedError(node, facts) {
350507
- const nodeText2 = isEntityNameExpression(node) ? entityNameToString(node) : void 0;
350507
+ const nodeText22 = isEntityNameExpression(node) ? entityNameToString(node) : void 0;
350508
350508
  if (node.kind === 106) {
350509
350509
  error2(node, Diagnostics.The_value_0_cannot_be_used_here, "null");
350510
350510
  return;
350511
350511
  }
350512
- if (nodeText2 !== void 0 && nodeText2.length < 100) {
350513
- if (isIdentifier(node) && nodeText2 === "undefined") {
350512
+ if (nodeText22 !== void 0 && nodeText22.length < 100) {
350513
+ if (isIdentifier(node) && nodeText22 === "undefined") {
350514
350514
  error2(node, Diagnostics.The_value_0_cannot_be_used_here, "undefined");
350515
350515
  return;
350516
350516
  }
350517
350517
  error2(
350518
350518
  node,
350519
350519
  facts & 16777216 ? facts & 33554432 ? Diagnostics._0_is_possibly_null_or_undefined : Diagnostics._0_is_possibly_undefined : Diagnostics._0_is_possibly_null,
350520
- nodeText2
350520
+ nodeText22
350521
350521
  );
350522
350522
  } else {
350523
350523
  error2(
@@ -350535,9 +350535,9 @@ ${lanes.join("\n")}
350535
350535
  function checkNonNullTypeWithReporter(type, node, reportError) {
350536
350536
  if (strictNullChecks && type.flags & 2) {
350537
350537
  if (isEntityNameExpression(node)) {
350538
- const nodeText2 = entityNameToString(node);
350539
- if (nodeText2.length < 100) {
350540
- error2(node, Diagnostics._0_is_of_type_unknown, nodeText2);
350538
+ const nodeText22 = entityNameToString(node);
350539
+ if (nodeText22.length < 100) {
350540
+ error2(node, Diagnostics._0_is_of_type_unknown, nodeText22);
350541
350541
  return errorType;
350542
350542
  }
350543
350543
  }
@@ -350563,13 +350563,13 @@ ${lanes.join("\n")}
350563
350563
  const nonNullType = checkNonNullType(type, node);
350564
350564
  if (nonNullType.flags & 16) {
350565
350565
  if (isEntityNameExpression(node)) {
350566
- const nodeText2 = entityNameToString(node);
350567
- if (isIdentifier(node) && nodeText2 === "undefined") {
350568
- error2(node, Diagnostics.The_value_0_cannot_be_used_here, nodeText2);
350566
+ const nodeText22 = entityNameToString(node);
350567
+ if (isIdentifier(node) && nodeText22 === "undefined") {
350568
+ error2(node, Diagnostics.The_value_0_cannot_be_used_here, nodeText22);
350569
350569
  return nonNullType;
350570
350570
  }
350571
- if (nodeText2.length < 100) {
350572
- error2(node, Diagnostics._0_is_possibly_undefined, nodeText2);
350571
+ if (nodeText22.length < 100) {
350572
+ error2(node, Diagnostics._0_is_possibly_undefined, nodeText22);
350573
350573
  return nonNullType;
350574
350574
  }
350575
350575
  }
@@ -374137,7 +374137,7 @@ ${lanes.join("\n")}
374137
374137
  node.operatorToken,
374138
374138
  visitNode(node.right, visitor, isExpression)
374139
374139
  );
374140
- const expr = some(pendingExpressions) ? factory2.inlineExpressions(compact([...pendingExpressions, node])) : node;
374140
+ const expr = some(pendingExpressions) ? factory2.inlineExpressions(compact2([...pendingExpressions, node])) : node;
374141
374141
  pendingExpressions = savedPendingExpressions;
374142
374142
  return expr;
374143
374143
  }
@@ -394406,7 +394406,7 @@ ${lanes.join("\n")}
394406
394406
  let parameterProperties;
394407
394407
  if (ctor) {
394408
394408
  const oldDiag2 = getSymbolAccessibilityDiagnostic;
394409
- parameterProperties = compact(flatMap(ctor.parameters, (param) => {
394409
+ parameterProperties = compact2(flatMap(ctor.parameters, (param) => {
394410
394410
  if (!hasSyntacticModifier(
394411
394411
  param,
394412
394412
  31
@@ -402392,9 +402392,9 @@ ${lanes.join("\n")}
402392
402392
  /*ignoreCase*/
402393
402393
  false
402394
402394
  )) {
402395
- const basename29 = getBaseFileName(a2.fileName);
402396
- if (basename29 === "lib.d.ts" || basename29 === "lib.es6.d.ts") return 0;
402397
- const name10 = removeSuffix(removePrefix(basename29, "lib."), ".d.ts");
402395
+ const basename30 = getBaseFileName(a2.fileName);
402396
+ if (basename30 === "lib.d.ts" || basename30 === "lib.es6.d.ts") return 0;
402397
+ const name10 = removeSuffix(removePrefix(basename30, "lib."), ".d.ts");
402398
402398
  const index = libs.indexOf(name10);
402399
402399
  if (index !== -1) return index + 1;
402400
402400
  }
@@ -411161,7 +411161,7 @@ ${lanes.join("\n")}
411161
411161
  startWatching(state, buildOrder);
411162
411162
  return isCircularBuildOrder(buildOrder) ? 4 : !buildOrder.some((p2) => state.diagnostics.has(toResolvedConfigFilePath(state, p2))) ? 0 : successfulProjects ? 2 : 1;
411163
411163
  }
411164
- function clean3(state, project, onlyReferences) {
411164
+ function clean5(state, project, onlyReferences) {
411165
411165
  mark("SolutionBuilder::beforeClean");
411166
411166
  const result = cleanWorker(state, project, onlyReferences);
411167
411167
  mark("SolutionBuilder::afterClean");
@@ -411450,7 +411450,7 @@ ${lanes.join("\n")}
411450
411450
  const state = createSolutionBuilderState(watch, hostOrHostWithWatch, rootNames, options2, baseWatchOptions);
411451
411451
  return {
411452
411452
  build: (project, cancellationToken, writeFile23, getCustomTransformers) => build(state, project, cancellationToken, writeFile23, getCustomTransformers),
411453
- clean: (project) => clean3(state, project),
411453
+ clean: (project) => clean5(state, project),
411454
411454
  buildReferences: (project, cancellationToken, writeFile23, getCustomTransformers) => build(
411455
411455
  state,
411456
411456
  project,
@@ -411460,7 +411460,7 @@ ${lanes.join("\n")}
411460
411460
  /*onlyReferences*/
411461
411461
  true
411462
411462
  ),
411463
- cleanReferences: (project) => clean3(
411463
+ cleanReferences: (project) => clean5(
411464
411464
  state,
411465
411465
  project,
411466
411466
  /*onlyReferences*/
@@ -421352,7 +421352,7 @@ interface Symbol {
421352
421352
  parent = void 0;
421353
421353
  emptyChildItemArray = [];
421354
421354
  }
421355
- function nodeText(node) {
421355
+ function nodeText2(node) {
421356
421356
  return cleanText(node.getText(curSourceFile));
421357
421357
  }
421358
421358
  function navigationBarNodeKind(n2) {
@@ -421667,7 +421667,7 @@ interface Symbol {
421667
421667
  const nameToItems = /* @__PURE__ */ new Map();
421668
421668
  filterMutate(children2, (child, index) => {
421669
421669
  const declName = child.name || getNameOfDeclaration(child.node);
421670
- const name10 = declName && nodeText(declName);
421670
+ const name10 = declName && nodeText2(declName);
421671
421671
  if (!name10) {
421672
421672
  return true;
421673
421673
  }
@@ -421893,7 +421893,7 @@ interface Symbol {
421893
421893
  return cleanText(getModuleName(node));
421894
421894
  }
421895
421895
  if (name10) {
421896
- const text = isIdentifier(name10) ? name10.text : isElementAccessExpression(name10) ? `[${nodeText(name10.argumentExpression)}]` : nodeText(name10);
421896
+ const text = isIdentifier(name10) ? name10.text : isElementAccessExpression(name10) ? `[${nodeText2(name10.argumentExpression)}]` : nodeText2(name10);
421897
421897
  if (text.length > 0) {
421898
421898
  return cleanText(text);
421899
421899
  }
@@ -422056,9 +422056,9 @@ interface Symbol {
422056
422056
  } else if (isVariableDeclaration(parent2)) {
422057
422057
  return cleanText(declarationNameToString(parent2.name));
422058
422058
  } else if (isBinaryExpression(parent2) && parent2.operatorToken.kind === 64) {
422059
- return nodeText(parent2.left).replace(whiteSpaceRegex, "");
422059
+ return nodeText2(parent2.left).replace(whiteSpaceRegex, "");
422060
422060
  } else if (isPropertyAssignment(parent2)) {
422061
- return nodeText(parent2.name);
422061
+ return nodeText2(parent2.name);
422062
422062
  } else if (getSyntacticModifierFlags(node) & 2048) {
422063
422063
  return "default";
422064
422064
  } else if (isClassLike(node)) {
@@ -431487,7 +431487,7 @@ ${newComment.split("\n").map((c9) => ` * ${c9}`).join("\n")}
431487
431487
  }
431488
431488
  const checker = context2.program.getTypeChecker();
431489
431489
  const trackChanges = (cb) => ts_textChanges_exports.ChangeTracker.with(context2, cb);
431490
- return compact([
431490
+ return compact2([
431491
431491
  getDeclarationSiteFix(context2, expression, errorCode, checker, trackChanges),
431492
431492
  getUseSiteFix(context2, expression, errorCode, checker, trackChanges)
431493
431493
  ]);
@@ -455078,7 +455078,7 @@ ${content}
455078
455078
  const lastToken = last2(children2);
455079
455079
  const separateLastToken = separateTrailingSemicolon && lastToken.kind === 27;
455080
455080
  const rightChildren = children2.slice(splitTokenIndex + 1, separateLastToken ? children2.length - 1 : void 0);
455081
- const result = compact([
455081
+ const result = compact2([
455082
455082
  leftChildren.length ? createSyntaxList2(leftChildren) : void 0,
455083
455083
  splitToken,
455084
455084
  rightChildren.length ? createSyntaxList2(rightChildren) : void 0
@@ -461802,7 +461802,7 @@ ${options2.prefix}` : "\n" : options2.prefix
461802
461802
  commandLineOptionOfCustomType: () => commandLineOptionOfCustomType,
461803
461803
  commentPragmas: () => commentPragmas,
461804
461804
  commonOptionsWithBuild: () => commonOptionsWithBuild,
461805
- compact: () => compact,
461805
+ compact: () => compact2,
461806
461806
  compareBooleans: () => compareBooleans,
461807
461807
  compareComparableValues: () => compareComparableValues,
461808
461808
  compareDataObjects: () => compareDataObjects,
@@ -466320,8 +466320,8 @@ ${options2.prefix}` : "\n" : options2.prefix
466320
466320
  }
466321
466321
  };
466322
466322
  for (const file of files) {
466323
- const basename29 = getBaseFileName(file);
466324
- if (basename29 === "package.json" || basename29 === "bower.json") {
466323
+ const basename30 = getBaseFileName(file);
466324
+ if (basename30 === "package.json" || basename30 === "bower.json") {
466325
466325
  createProjectWatcher(
466326
466326
  file,
466327
466327
  "FileWatcher"
@@ -470005,8 +470005,8 @@ All files are: ${JSON.stringify(names)}`,
470005
470005
  var _a;
470006
470006
  const fileOrDirectoryPath = removeIgnoredPath(this.toPath(fileOrDirectory));
470007
470007
  if (!fileOrDirectoryPath) return;
470008
- const basename29 = getBaseFileName(fileOrDirectoryPath);
470009
- if (((_a = result.affectedModuleSpecifierCacheProjects) == null ? void 0 : _a.size) && (basename29 === "package.json" || basename29 === "node_modules")) {
470008
+ const basename30 = getBaseFileName(fileOrDirectoryPath);
470009
+ if (((_a = result.affectedModuleSpecifierCacheProjects) == null ? void 0 : _a.size) && (basename30 === "package.json" || basename30 === "node_modules")) {
470010
470010
  result.affectedModuleSpecifierCacheProjects.forEach((project) => {
470011
470011
  var _a2;
470012
470012
  (_a2 = project.getModuleSpecifierCache()) == null ? void 0 : _a2.clear();
@@ -478909,7 +478909,7 @@ var require_path_browserify = __commonJS({
478909
478909
  if (hasRoot && end === 1) return "//";
478910
478910
  return path11.slice(0, end);
478911
478911
  },
478912
- basename: function basename29(path11, ext) {
478912
+ basename: function basename30(path11, ext) {
478913
478913
  if (ext !== void 0 && typeof ext !== "string") throw new TypeError('"ext" argument must be a string');
478914
478914
  assertPath(path11);
478915
478915
  var start2 = 0;
@@ -512614,6 +512614,11 @@ var init_visual_memory = __esm({
512614
512614
  type: "number",
512615
512615
  description: "Recognition confidence threshold 0-1 (default: 0.5 for faces, 0.3 for objects)."
512616
512616
  },
512617
+ format: {
512618
+ type: "string",
512619
+ enum: ["text", "json"],
512620
+ description: "Return format for identify/recognize results. Use json when another tool or service needs machine-readable matches."
512621
+ },
512617
512622
  labels: {
512618
512623
  type: "array",
512619
512624
  description: "Text labels to score against image (for CLIP 'recognize' or 'describe').",
@@ -512661,6 +512666,7 @@ var init_visual_memory = __esm({
512661
512666
  if (!image || !existsSync45(image)) {
512662
512667
  return { success: false, output: "", error: "Provide a valid image path.", durationMs: performance.now() - start2 };
512663
512668
  }
512669
+ const wantsJson = args["format"] === "json" || args["json"] === true;
512664
512670
  const result = await this.runVisionPython(`
512665
512671
  import json, sys
512666
512672
  from insightface.app import FaceAnalysis
@@ -512690,6 +512696,10 @@ print(json.dumps({"success": True, "faces": results, "total": len(results)}))
512690
512696
  `, 6e4);
512691
512697
  if (!result.success)
512692
512698
  return { success: false, output: "", error: result.error, durationMs: performance.now() - start2 };
512699
+ if (wantsJson) {
512700
+ const payload = JSON.stringify(result);
512701
+ return { success: true, output: payload, llmContent: payload, durationMs: performance.now() - start2 };
512702
+ }
512693
512703
  return { success: true, output: `Detected ${result.total} face(s):
512694
512704
  ${JSON.stringify(result.faces, null, 2)}`, durationMs: performance.now() - start2 };
512695
512705
  }
@@ -512771,6 +512781,7 @@ print(json.dumps({
512771
512781
  if (!image || !existsSync45(image))
512772
512782
  return { success: false, output: "", error: "Provide a valid image path.", durationMs: performance.now() - start2 };
512773
512783
  const threshold = args["threshold"] || 0.5;
512784
+ const wantsJson = args["format"] === "json" || args["json"] === true;
512774
512785
  const result = await this.runVisionPython(`
512775
512786
  import json, sys, os, numpy as np
512776
512787
  from insightface.app import FaceAnalysis
@@ -512838,6 +512849,15 @@ print(json.dumps({"success": True, "faces": results, "total_detected": len(faces
512838
512849
  `, 6e4);
512839
512850
  if (!result.success)
512840
512851
  return { success: false, output: "", error: result.error, durationMs: performance.now() - start2 };
512852
+ if (wantsJson) {
512853
+ const payload = JSON.stringify(result);
512854
+ return {
512855
+ success: true,
512856
+ output: payload,
512857
+ llmContent: payload,
512858
+ durationMs: performance.now() - start2
512859
+ };
512860
+ }
512841
512861
  const faces = result.faces || [];
512842
512862
  if (faces.length === 0) {
512843
512863
  return { success: true, output: "No faces detected in image.", durationMs: performance.now() - start2 };
@@ -512934,6 +512954,7 @@ print(json.dumps({
512934
512954
  return { success: false, output: "", error: "Provide a valid image path.", durationMs: performance.now() - start2 };
512935
512955
  const threshold = args["threshold"] || 0.3;
512936
512956
  const extraLabels = args["labels"] || [];
512957
+ const wantsJson = args["format"] === "json" || args["json"] === true;
512937
512958
  const result = await this.runVisionPython(`
512938
512959
  import json, sys, os, numpy as np
512939
512960
  import torch
@@ -513009,6 +513030,10 @@ print(json.dumps({
513009
513030
  `, 12e4);
513010
513031
  if (!result.success)
513011
513032
  return { success: false, output: "", error: result.error, durationMs: performance.now() - start2 };
513033
+ if (wantsJson) {
513034
+ const payload = JSON.stringify(result);
513035
+ return { success: true, output: payload, llmContent: payload, durationMs: performance.now() - start2 };
513036
+ }
513012
513037
  const matches = (result.matches || []).filter((m2) => m2.recognized);
513013
513038
  const lines = matches.map((m2) => ` ${m2.label}: ${(m2.blended_score * 100).toFixed(0)}% (image=${(m2.image_similarity * 100).toFixed(0)}%, text=${(m2.text_similarity * 100).toFixed(0)}%)`);
513014
513039
  let output = matches.length > 0 ? `Recognized ${result.recognized_count} object(s):
@@ -524884,6 +524909,9 @@ var init_temporalGraph = __esm({
524884
524909
  nodesByType(nodeType, limit = 100) {
524885
524910
  return this.db.prepare("SELECT * FROM kg_nodes WHERE node_type = ? ORDER BY mention_count DESC LIMIT ?").all(nodeType, limit).map((r2) => this.rowToNode(r2));
524886
524911
  }
524912
+ allNodes(limit = 1e3) {
524913
+ return this.db.prepare("SELECT * FROM kg_nodes ORDER BY mention_count DESC, last_seen DESC LIMIT ?").all(limit).map((r2) => this.rowToNode(r2));
524914
+ }
524887
524915
  /**
524888
524916
  * Find code-symbol entity nodes by the inner symbol name. Symbol nodes
524889
524917
  * stored by the cross-graph link layer use text `sym:{name}@{path}`;
@@ -524977,6 +525005,26 @@ var init_temporalGraph = __esm({
524977
525005
  edge: this.rowToEdge(r2)
524978
525006
  }));
524979
525007
  }
525008
+ currentEdgesForSourceEpisode(sourceEpisodeId, limit = 200) {
525009
+ return this.db.prepare(`
525010
+ SELECT * FROM kg_edges
525011
+ WHERE source_episode_id = ? AND valid_until IS NULL
525012
+ ORDER BY confidence DESC, valid_from DESC
525013
+ LIMIT ?
525014
+ `).all(sourceEpisodeId, limit).map((r2) => this.rowToEdge(r2));
525015
+ }
525016
+ currentEdgesForSourceEpisodes(sourceEpisodeIds, limit = 1e3) {
525017
+ const ids = [...new Set(sourceEpisodeIds.filter(Boolean))];
525018
+ if (ids.length === 0)
525019
+ return [];
525020
+ const placeholders = ids.map(() => "?").join(", ");
525021
+ return this.db.prepare(`
525022
+ SELECT * FROM kg_edges
525023
+ WHERE source_episode_id IN (${placeholders}) AND valid_until IS NULL
525024
+ ORDER BY confidence DESC, valid_from DESC
525025
+ LIMIT ?
525026
+ `).all(...ids, limit).map((r2) => this.rowToEdge(r2));
525027
+ }
524980
525028
  // ─── Statistics ──────────────────────────────────────────────────────────
524981
525029
  nodeCount() {
524982
525030
  return this.db.prepare("SELECT COUNT(*) as n FROM kg_nodes").get().n;
@@ -525118,6 +525166,7 @@ var init_multimodalIdentity = __esm({
525118
525166
  const sender = input.sender;
525119
525167
  const content = buildEpisodeContent(input);
525120
525168
  const metadata = {
525169
+ ...input.metadata || {},
525121
525170
  mmidVersion: 1,
525122
525171
  sourceSurface,
525123
525172
  scope,
@@ -525132,6 +525181,7 @@ var init_multimodalIdentity = __esm({
525132
525181
  embeddingSpaces: this.embeddingSpaces(input.embeddings)
525133
525182
  };
525134
525183
  const episodeId = this.episodeStore.insert({
525184
+ sessionId: input.sessionId,
525135
525185
  modality,
525136
525186
  content,
525137
525187
  metadata,
@@ -525205,6 +525255,7 @@ var init_multimodalIdentity = __esm({
525205
525255
  if (input.transcript && input.transcript.trim()) {
525206
525256
  const transcriptMeta = { ...metadata, sourceEpisodeId: episodeId, transcriptOf: episodeId };
525207
525257
  const transcriptEpisodeId = this.episodeStore.insert({
525258
+ sessionId: input.sessionId,
525208
525259
  modality: "text",
525209
525260
  content: input.transcript,
525210
525261
  metadata: transcriptMeta,
@@ -525380,6 +525431,158 @@ var init_gistCompressor = __esm({
525380
525431
  }
525381
525432
  });
525382
525433
 
525434
+ // packages/memory/dist/graphWalk.js
525435
+ import { createHash as createHash12 } from "node:crypto";
525436
+ function seededUnit(seed) {
525437
+ const digest3 = createHash12("sha256").update(seed || "graph-walk").digest();
525438
+ const value2 = digest3.readUInt32BE(0);
525439
+ return value2 / 4294967295;
525440
+ }
525441
+ function edgeKey(edge) {
525442
+ return String(edge.id ?? `${edge.srcId}:${edge.dstId}:${edge.relation}:${edge.sourceEpisodeId ?? ""}`);
525443
+ }
525444
+ function recencyScore(timestamp, now) {
525445
+ if (!timestamp || !Number.isFinite(timestamp))
525446
+ return 0;
525447
+ const ageMs = Math.max(0, now - timestamp);
525448
+ return Math.exp(-ageMs / (7 * 24 * 60 * 6e4));
525449
+ }
525450
+ function relationAllowed(edge, options2) {
525451
+ if (options2.allowedRelations?.length && !options2.allowedRelations.includes(edge.relation))
525452
+ return false;
525453
+ if (options2.deniedRelations?.includes(edge.relation))
525454
+ return false;
525455
+ return true;
525456
+ }
525457
+ function selectInnerGraphCandidates(graph, candidates, options2 = {}) {
525458
+ const minDegree = Math.max(1, Math.floor(options2.minDegree ?? 2));
525459
+ const candidateEpisodeIds = [...new Set(candidates.map((candidate) => candidate.episode.id).filter(Boolean))];
525460
+ const candidateScore = new Map(candidates.map((candidate) => [candidate.episode.id, candidate.score ?? 0]));
525461
+ const candidateTimestamp = new Map(candidates.map((candidate) => [candidate.episode.id, candidate.episode.timestamp]));
525462
+ const sourceEdges = graph.currentEdgesForSourceEpisodes(candidateEpisodeIds, Math.max(1e3, candidateEpisodeIds.length * 20));
525463
+ const byNode = /* @__PURE__ */ new Map();
525464
+ for (const edge of sourceEdges) {
525465
+ if (!relationAllowed(edge, options2))
525466
+ continue;
525467
+ const episodeId = edge.sourceEpisodeId;
525468
+ if (!episodeId || !candidateEpisodeIds.includes(episodeId))
525469
+ continue;
525470
+ for (const nodeId of [edge.srcId, edge.dstId]) {
525471
+ const node = graph.getNode(nodeId);
525472
+ if (!node)
525473
+ continue;
525474
+ const entry = byNode.get(nodeId) ?? { node, candidateEpisodeIds: /* @__PURE__ */ new Set(), edges: /* @__PURE__ */ new Map() };
525475
+ entry.candidateEpisodeIds.add(episodeId);
525476
+ entry.edges.set(edgeKey(edge), edge);
525477
+ byNode.set(nodeId, entry);
525478
+ }
525479
+ }
525480
+ const now = Date.now();
525481
+ const results = [];
525482
+ for (const entry of byNode.values()) {
525483
+ const allEdges = graph.currentEdges(entry.node.id).filter((edge) => relationAllowed(edge, options2));
525484
+ const degree = allEdges.length;
525485
+ if (degree < minDegree)
525486
+ continue;
525487
+ const relationDiversity = new Set(allEdges.map((edge) => edge.relation)).size;
525488
+ const sourceEpisodeIds = [...new Set(allEdges.map((edge) => edge.sourceEpisodeId).filter((id) => Boolean(id)))];
525489
+ const selectedEpisodeIds = [...entry.candidateEpisodeIds];
525490
+ const sourceStrength = sourceEpisodeIds.length;
525491
+ const candidateStrength = selectedEpisodeIds.reduce((sum, id) => sum + (candidateScore.get(id) ?? 0), 0);
525492
+ const recency = selectedEpisodeIds.reduce((sum, id) => sum + recencyScore(candidateTimestamp.get(id), now), 0);
525493
+ const confidence2 = allEdges.reduce((sum, edge) => sum + (Number.isFinite(edge.confidence) ? edge.confidence : 0), 0) / Math.max(1, allEdges.length);
525494
+ const score = degree * 1.5 + relationDiversity * 1.2 + sourceStrength * 1.4 + selectedEpisodeIds.length * 1.6 + candidateStrength + recency + confidence2 + Math.log1p(entry.node.mentionCount);
525495
+ results.push({
525496
+ node: entry.node,
525497
+ score,
525498
+ degree,
525499
+ relationDiversity,
525500
+ sourceEpisodeIds,
525501
+ candidateEpisodeIds: selectedEpisodeIds,
525502
+ edges: allEdges
525503
+ });
525504
+ }
525505
+ return results.sort((a2, b) => b.score - a2.score || b.degree - a2.degree || a2.node.text.localeCompare(b.node.text));
525506
+ }
525507
+ function chooseInnerGraphSeed(candidates, seed = "graph-walk", topCandidatePool = 16) {
525508
+ if (candidates.length === 0)
525509
+ return null;
525510
+ const pool3 = candidates.slice(0, Math.max(1, Math.min(topCandidatePool, candidates.length)));
525511
+ const index = Math.min(pool3.length - 1, Math.floor(seededUnit(seed) * pool3.length));
525512
+ return pool3[index] ?? null;
525513
+ }
525514
+ function walkGraphFromSeed(graph, seed, options2 = {}) {
525515
+ const maxDepth = Math.max(0, Math.floor(options2.maxDepth ?? 2));
525516
+ const maxVisitedNodes = Math.max(1, Math.floor(options2.maxVisitedNodes ?? 64));
525517
+ const maxTraversedEdges = Math.max(1, Math.floor(options2.maxTraversedEdges ?? 160));
525518
+ const maxSourceEpisodes = Math.max(1, Math.floor(options2.maxSourceEpisodes ?? 80));
525519
+ const visited = /* @__PURE__ */ new Map();
525520
+ const depthByNodeId = {};
525521
+ const traversed = /* @__PURE__ */ new Map();
525522
+ const sourceEpisodeIds = [];
525523
+ const seenSourceEpisodes = /* @__PURE__ */ new Set();
525524
+ const queue = [{ node: seed.node, depth: 0 }];
525525
+ visited.set(seed.node.id, seed.node);
525526
+ depthByNodeId[seed.node.id] = 0;
525527
+ for (const id of seed.sourceEpisodeIds) {
525528
+ if (seenSourceEpisodes.size >= maxSourceEpisodes)
525529
+ break;
525530
+ seenSourceEpisodes.add(id);
525531
+ sourceEpisodeIds.push(id);
525532
+ }
525533
+ while (queue.length > 0) {
525534
+ const item = queue.shift();
525535
+ if (item.depth >= maxDepth)
525536
+ continue;
525537
+ const neighbors2 = graph.neighbors(item.node.id).filter(({ edge }) => relationAllowed(edge, options2)).sort((a2, b) => b.edge.confidence - a2.edge.confidence || b.node.mentionCount - a2.node.mentionCount);
525538
+ for (const { node, edge } of neighbors2) {
525539
+ if (traversed.size >= maxTraversedEdges)
525540
+ break;
525541
+ traversed.set(edgeKey(edge), edge);
525542
+ if (edge.sourceEpisodeId && !seenSourceEpisodes.has(edge.sourceEpisodeId) && seenSourceEpisodes.size < maxSourceEpisodes) {
525543
+ seenSourceEpisodes.add(edge.sourceEpisodeId);
525544
+ sourceEpisodeIds.push(edge.sourceEpisodeId);
525545
+ }
525546
+ if (!visited.has(node.id) && visited.size < maxVisitedNodes) {
525547
+ visited.set(node.id, node);
525548
+ depthByNodeId[node.id] = item.depth + 1;
525549
+ queue.push({ node, depth: item.depth + 1 });
525550
+ }
525551
+ }
525552
+ }
525553
+ return {
525554
+ visitedNodes: [...visited.values()],
525555
+ traversedEdges: [...traversed.values()],
525556
+ sourceEpisodeIds,
525557
+ depthByNodeId
525558
+ };
525559
+ }
525560
+ function selectAndWalkGraphCandidate(graph, candidates, options2 = {}) {
525561
+ const eligibleCandidates = selectInnerGraphCandidates(graph, candidates, options2);
525562
+ const seed = chooseInnerGraphSeed(eligibleCandidates, options2.seed, options2.topCandidatePool);
525563
+ if (!seed) {
525564
+ return {
525565
+ seed: null,
525566
+ eligibleCandidates,
525567
+ visitedNodes: [],
525568
+ traversedEdges: [],
525569
+ sourceEpisodeIds: [],
525570
+ depthByNodeId: {},
525571
+ fallbackReason: candidates.length === 0 ? "no episode candidates" : "no eligible inner graph nodes"
525572
+ };
525573
+ }
525574
+ return {
525575
+ seed,
525576
+ eligibleCandidates,
525577
+ ...walkGraphFromSeed(graph, seed, options2)
525578
+ };
525579
+ }
525580
+ var init_graphWalk = __esm({
525581
+ "packages/memory/dist/graphWalk.js"() {
525582
+ "use strict";
525583
+ }
525584
+ });
525585
+
525383
525586
  // packages/memory/dist/embeddings.js
525384
525587
  async function generateEmbedding(text, config) {
525385
525588
  const cfg = { ...DEFAULT_CONFIG5, ...config };
@@ -528615,8 +528818,8 @@ function slowWaveReplay(db, options2 = {}) {
528615
528818
  try {
528616
528819
  const referenced = db.prepare(`SELECT 1 FROM kg_edges WHERE source_episode_id = ? LIMIT 1`).get(id);
528617
528820
  if (!referenced) {
528618
- const nodeText = `episode:${id}`;
528619
- const newNodeId = options2.graph.upsertNode({ text: nodeText, nodeType: "event" });
528821
+ const nodeText2 = `episode:${id}`;
528822
+ const newNodeId = options2.graph.upsertNode({ text: nodeText2, nodeType: "event" });
528620
528823
  if (newNodeId)
528621
528824
  integratedNodes.push(newNodeId);
528622
528825
  }
@@ -529362,7 +529565,7 @@ var init_memoryStageContext = __esm({
529362
529565
  });
529363
529566
 
529364
529567
  // packages/memory/dist/sessionGist.js
529365
- import { createHash as createHash12 } from "node:crypto";
529568
+ import { createHash as createHash13 } from "node:crypto";
529366
529569
  function inferDomain(input) {
529367
529570
  const blob = [
529368
529571
  input.goal,
@@ -529387,7 +529590,7 @@ function inferDomain(input) {
529387
529590
  return ranked[0][0];
529388
529591
  }
529389
529592
  function computeGoalHash(goal) {
529390
- return createHash12("sha256").update(goal.trim().toLowerCase()).digest("hex").slice(0, 16);
529593
+ return createHash13("sha256").update(goal.trim().toLowerCase()).digest("hex").slice(0, 16);
529391
529594
  }
529392
529595
  function clip(text, n2) {
529393
529596
  if (!text)
@@ -529598,12 +529801,12 @@ var init_toolOutcomes = __esm({
529598
529801
  });
529599
529802
 
529600
529803
  // packages/memory/dist/stagnationRecipes.js
529601
- import { createHash as createHash13 } from "node:crypto";
529804
+ import { createHash as createHash14 } from "node:crypto";
529602
529805
  function fingerprintSignature(fp) {
529603
529806
  const normClusters = (fp.errorClusters ?? []).map((s2) => (s2 || "").toLowerCase().replace(/[0-9]+/g, "N").replace(/\s+/g, " ").trim()).filter(Boolean).sort();
529604
529807
  const tool = (fp.stuckTool ?? "").toLowerCase().trim();
529605
529808
  const blob = `tool=${tool};clusters=${normClusters.join("|")}`;
529606
- return createHash13("sha256").update(blob).digest("hex").slice(0, 16);
529809
+ return createHash14("sha256").update(blob).digest("hex").slice(0, 16);
529607
529810
  }
529608
529811
  function crystallize(store2, input) {
529609
529812
  const sig = fingerprintSignature(input.fingerprint);
@@ -529660,7 +529863,7 @@ var init_stagnationRecipes = __esm({
529660
529863
  });
529661
529864
 
529662
529865
  // packages/memory/dist/codebaseMap.js
529663
- import { createHash as createHash14, randomUUID as randomUUID12 } from "node:crypto";
529866
+ import { createHash as createHash15, randomUUID as randomUUID12 } from "node:crypto";
529664
529867
  function freshNodeId() {
529665
529868
  return randomUUID12();
529666
529869
  }
@@ -529674,7 +529877,7 @@ var init_codebaseMap = __esm({
529674
529877
  touchCount = /* @__PURE__ */ new Map();
529675
529878
  constructor(db, repoRoot, commitSha) {
529676
529879
  this.db = db;
529677
- this.repoFp = createHash14("sha256").update(`${repoRoot}::${commitSha ?? "no-commit"}`).digest("hex").slice(0, 16);
529880
+ this.repoFp = createHash15("sha256").update(`${repoRoot}::${commitSha ?? "no-commit"}`).digest("hex").slice(0, 16);
529678
529881
  this.ensureSchema();
529679
529882
  }
529680
529883
  ensureSchema() {
@@ -529856,7 +530059,7 @@ var init_codebaseMap = __esm({
529856
530059
  }
529857
530060
  /** Stable composite id: `<kind>:<sha16(path)>` so insert ON CONFLICT works. */
529858
530061
  idFor(kind, path11) {
529859
- const h = createHash14("sha256").update(`${this.repoFp}:${kind}:${path11}`).digest("hex").slice(0, 24);
530062
+ const h = createHash15("sha256").update(`${this.repoFp}:${kind}:${path11}`).digest("hex").slice(0, 24);
529860
530063
  return `${kind}-${h}`;
529861
530064
  }
529862
530065
  };
@@ -530020,6 +530223,7 @@ __export(dist_exports2, {
530020
530223
  characteristicsForStage: () => characteristicsForStage,
530021
530224
  checkEmbeddingAvailable: () => checkEmbeddingAvailable,
530022
530225
  checkEmbeddingDrift: () => checkEmbeddingDrift,
530226
+ chooseInnerGraphSeed: () => chooseInnerGraphSeed,
530023
530227
  closeDb: () => closeDb,
530024
530228
  compressAndStore: () => compressAndStore,
530025
530229
  compressToGist: () => compressToGist,
@@ -530065,6 +530269,8 @@ __export(dist_exports2, {
530065
530269
  retrieveByPPR: () => retrieveByPPR,
530066
530270
  runConsolidationCycle: () => runConsolidationCycle,
530067
530271
  sanitizeEmotionalState: () => sanitizeEmotionalState,
530272
+ selectAndWalkGraphCandidate: () => selectAndWalkGraphCandidate,
530273
+ selectInnerGraphCandidates: () => selectInnerGraphCandidates,
530068
530274
  selfTrustFor: () => selfTrustFor,
530069
530275
  slowWaveReplay: () => slowWaveReplay,
530070
530276
  splanifoldCosine: () => cosine,
@@ -530077,6 +530283,7 @@ __export(dist_exports2, {
530077
530283
  suggestRegulationActions: () => suggestRegulationActions,
530078
530284
  thresholdsForStage: () => thresholdsForStage,
530079
530285
  tokenSimilarity: () => tokenSimilarity,
530286
+ walkGraphFromSeed: () => walkGraphFromSeed,
530080
530287
  wasHesitant: () => wasHesitant
530081
530288
  });
530082
530289
  var init_dist7 = __esm({
@@ -530096,6 +530303,7 @@ var init_dist7 = __esm({
530096
530303
  init_zettelkasten();
530097
530304
  init_gistCompressor();
530098
530305
  init_pprRetrieval();
530306
+ init_graphWalk();
530099
530307
  init_embeddings2();
530100
530308
  init_proceduralMemoryStore();
530101
530309
  init_splanifold();
@@ -532316,7 +532524,7 @@ import { existsSync as existsSync72, readFileSync as readFileSync56, statSync as
532316
532524
  import { execSync as execSync45 } from "node:child_process";
532317
532525
  import { homedir as homedir23, platform as platform2, arch as arch2, totalmem as totalmem2, freemem as freemem2, hostname as hostname3 } from "node:os";
532318
532526
  import { join as join86 } from "node:path";
532319
- import { createHash as createHash15 } from "node:crypto";
532527
+ import { createHash as createHash16 } from "node:crypto";
532320
532528
  function capturePreflightSnapshot(workingDir) {
532321
532529
  const warnings = [];
532322
532530
  const configFingerprints = {};
@@ -532483,7 +532691,7 @@ function expandPath(p2) {
532483
532691
  return p2;
532484
532692
  }
532485
532693
  function sha2563(s2) {
532486
- return createHash15("sha256").update(s2).digest("hex").slice(0, 16);
532694
+ return createHash16("sha256").update(s2).digest("hex").slice(0, 16);
532487
532695
  }
532488
532696
  function freeDiskBytes(path11 = "/tmp") {
532489
532697
  try {
@@ -538505,8 +538713,8 @@ If you're stuck, try a completely different approach. Do NOT repeat what failed
538505
538713
  if (process.env["OMNIUS_DISABLE_ADAPTIVE_RETRIEVAL"] !== "1") {
538506
538714
  const goalForSig = (this._taskState.goal || "").slice(0, 200);
538507
538715
  const recentTools = this._toolSequence.slice(-5).join("|");
538508
- const { createHash: createHash26 } = await import("node:crypto");
538509
- const sig = createHash26("sha256").update(`${goalForSig}::${recentTools}`).digest("hex").slice(0, 16);
538716
+ const { createHash: createHash28 } = await import("node:crypto");
538717
+ const sig = createHash28("sha256").update(`${goalForSig}::${recentTools}`).digest("hex").slice(0, 16);
538510
538718
  if (this._lastPprSig === sig && this._lastPprMemoryLines.length > 0) {
538511
538719
  compacted.push({
538512
538720
  role: "system",
@@ -551340,10 +551548,10 @@ transcribe-cli error: ${transcribeCliError}` : "";
551340
551548
  wordTimestamps: false
551341
551549
  });
551342
551550
  if (outputDir) {
551343
- const { basename: basename29 } = await import("node:path");
551551
+ const { basename: basename30 } = await import("node:path");
551344
551552
  const transcriptDir = join94(outputDir, ".omnius", "transcripts");
551345
551553
  mkdirSync44(transcriptDir, { recursive: true });
551346
- const outFile = join94(transcriptDir, `${basename29(filePath)}.txt`);
551554
+ const outFile = join94(transcriptDir, `${basename30(filePath)}.txt`);
551347
551555
  writeFileSync39(outFile, result.text, "utf-8");
551348
551556
  }
551349
551557
  return {
@@ -551359,10 +551567,10 @@ transcribe-cli error: ${transcribeCliError}` : "";
551359
551567
  const fb = await transcribeFileViaWhisper(filePath, this.config.model);
551360
551568
  if (fb) {
551361
551569
  if (outputDir) {
551362
- const { basename: basename29 } = await import("node:path");
551570
+ const { basename: basename30 } = await import("node:path");
551363
551571
  const transcriptDir = join94(outputDir, ".omnius", "transcripts");
551364
551572
  mkdirSync44(transcriptDir, { recursive: true });
551365
- const outFile = join94(transcriptDir, `${basename29(filePath)}.txt`);
551573
+ const outFile = join94(transcriptDir, `${basename30(filePath)}.txt`);
551366
551574
  writeFileSync39(outFile, fb.text, "utf-8");
551367
551575
  }
551368
551576
  return fb;
@@ -553610,7 +553818,7 @@ var require_websocket3 = __commonJS({
553610
553818
  var http6 = __require("http");
553611
553819
  var net5 = __require("net");
553612
553820
  var tls2 = __require("tls");
553613
- var { randomBytes: randomBytes26, createHash: createHash26 } = __require("crypto");
553821
+ var { randomBytes: randomBytes26, createHash: createHash28 } = __require("crypto");
553614
553822
  var { Duplex: Duplex3, Readable } = __require("stream");
553615
553823
  var { URL: URL3 } = __require("url");
553616
553824
  var PerMessageDeflate3 = require_permessage_deflate3();
@@ -554270,7 +554478,7 @@ var require_websocket3 = __commonJS({
554270
554478
  abortHandshake(websocket, socket, "Invalid Upgrade header");
554271
554479
  return;
554272
554480
  }
554273
- const digest3 = createHash26("sha1").update(key + GUID).digest("base64");
554481
+ const digest3 = createHash28("sha1").update(key + GUID).digest("base64");
554274
554482
  if (res.headers["sec-websocket-accept"] !== digest3) {
554275
554483
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
554276
554484
  return;
@@ -554637,7 +554845,7 @@ var require_websocket_server2 = __commonJS({
554637
554845
  var EventEmitter13 = __require("events");
554638
554846
  var http6 = __require("http");
554639
554847
  var { Duplex: Duplex3 } = __require("stream");
554640
- var { createHash: createHash26 } = __require("crypto");
554848
+ var { createHash: createHash28 } = __require("crypto");
554641
554849
  var extension3 = require_extension3();
554642
554850
  var PerMessageDeflate3 = require_permessage_deflate3();
554643
554851
  var subprotocol3 = require_subprotocol2();
@@ -554938,7 +555146,7 @@ var require_websocket_server2 = __commonJS({
554938
555146
  );
554939
555147
  }
554940
555148
  if (this._state > RUNNING) return abortHandshake(socket, 503);
554941
- const digest3 = createHash26("sha1").update(key + GUID).digest("base64");
555149
+ const digest3 = createHash28("sha1").update(key + GUID).digest("base64");
554942
555150
  const headers = [
554943
555151
  "HTTP/1.1 101 Switching Protocols",
554944
555152
  "Upgrade: websocket",
@@ -555312,7 +555520,11 @@ function buildTelegramBotCommands(opts = {}) {
555312
555520
  if (includeAuth) {
555313
555521
  add2("auth", "Authenticate this Telegram user as the bot admin", "auth");
555314
555522
  }
555523
+ add2("reflect", "Run scoped Telegram chat reflection", "reflect");
555524
+ add2("reflection", "Alias for scoped Telegram chat reflection", "reflect");
555525
+ add2("daydream", "Alias for scoped Telegram chat reflection", "reflect");
555315
555526
  for (const cmd of listCommandRegistry({ includePlanned: false })) {
555527
+ if (cmd.name === "dream") continue;
555316
555528
  add2(cmd.name, cmd.description || `Run /${cmd.name}`, cmd.name);
555317
555529
  if (commands.length >= maxCommands) break;
555318
555530
  }
@@ -558169,14 +558381,14 @@ var init_voice_session = __esm({
558169
558381
  });
558170
558382
 
558171
558383
  // packages/cli/src/tui/scoped-personality.ts
558172
- import { createHash as createHash16 } from "node:crypto";
558384
+ import { createHash as createHash17 } from "node:crypto";
558173
558385
  import { existsSync as existsSync79, mkdirSync as mkdirSync45, readFileSync as readFileSync62, writeFileSync as writeFileSync40 } from "node:fs";
558174
558386
  import { join as join95, resolve as resolve35 } from "node:path";
558175
558387
  function safeName(input) {
558176
558388
  return input.replace(/[^A-Za-z0-9_.-]/g, "-").slice(0, 80) || "default";
558177
558389
  }
558178
558390
  function scopeHash(scope) {
558179
- return createHash16("sha1").update(`${scope.kind}:${scope.id}`).digest("hex").slice(0, 16);
558391
+ return createHash17("sha1").update(`${scope.kind}:${scope.id}`).digest("hex").slice(0, 16);
558180
558392
  }
558181
558393
  function scopedPersonalityDir(repoRoot, kind) {
558182
558394
  return resolve35(repoRoot, ".omnius", "scoped-personality", kind);
@@ -558214,9 +558426,9 @@ function keywords(text) {
558214
558426
  return out;
558215
558427
  }
558216
558428
  function compactLine(text, limit = 220) {
558217
- const compact = text.replace(/\s+/g, " ").trim();
558218
- if (compact.length <= limit) return compact;
558219
- return `${compact.slice(0, Math.max(0, limit - 3)).trimEnd()}...`;
558429
+ const compact2 = text.replace(/\s+/g, " ").trim();
558430
+ if (compact2.length <= limit) return compact2;
558431
+ return `${compact2.slice(0, Math.max(0, limit - 3)).trimEnd()}...`;
558220
558432
  }
558221
558433
  function newDocument(scope) {
558222
558434
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -558321,8 +558533,8 @@ function updateScopedPersonality(scope, observation) {
558321
558533
  }
558322
558534
  const relationshipHints = observation.relationshipHints ?? [];
558323
558535
  for (const hint of relationshipHints) {
558324
- const clean3 = compactLine(hint, 180);
558325
- if (clean3 && !doc.relationshipModel.includes(clean3)) doc.relationshipModel.push(clean3);
558536
+ const clean5 = compactLine(hint, 180);
558537
+ if (clean5 && !doc.relationshipModel.includes(clean5)) doc.relationshipModel.push(clean5);
558326
558538
  }
558327
558539
  doc.relationshipModel = doc.relationshipModel.slice(-12);
558328
558540
  if (text) {
@@ -560017,7 +560229,7 @@ var init_types = __esm({
560017
560229
  });
560018
560230
 
560019
560231
  // packages/cli/src/tui/p2p/secret-vault.ts
560020
- import { createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv3, randomBytes as randomBytes19, scryptSync as scryptSync2, createHash as createHash17 } from "node:crypto";
560232
+ import { createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv3, randomBytes as randomBytes19, scryptSync as scryptSync2, createHash as createHash18 } from "node:crypto";
560021
560233
  import { readFileSync as readFileSync64, writeFileSync as writeFileSync42, existsSync as existsSync81, mkdirSync as mkdirSync47 } from "node:fs";
560022
560234
  import { dirname as dirname25 } from "node:path";
560023
560235
  var PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, CIPHER_ALGO, SALT_LEN, IV_LEN, KEY_LEN, SecretVault;
@@ -560262,7 +560474,7 @@ var init_secret_vault = __esm({
560262
560474
  /** Generate a deterministic fingerprint of vault contents (for sync verification) */
560263
560475
  fingerprint() {
560264
560476
  const names = Array.from(this.secrets.keys()).sort();
560265
- const hash = createHash17("sha256");
560477
+ const hash = createHash18("sha256");
560266
560478
  for (const name10 of names) {
560267
560479
  hash.update(name10 + ":");
560268
560480
  hash.update(this.secrets.get(name10).value);
@@ -560277,7 +560489,7 @@ var init_secret_vault = __esm({
560277
560489
  // packages/cli/src/tui/p2p/peer-mesh.ts
560278
560490
  import { EventEmitter as EventEmitter7 } from "node:events";
560279
560491
  import { createServer as createServer5 } from "node:http";
560280
- import { randomBytes as randomBytes20, createHash as createHash18, generateKeyPairSync } from "node:crypto";
560492
+ import { randomBytes as randomBytes20, createHash as createHash19, generateKeyPairSync } from "node:crypto";
560281
560493
  var PING_INTERVAL_MS, PEER_TIMEOUT_MS, GOSSIP_INTERVAL_MS, MAX_PEERS, PeerMesh;
560282
560494
  var init_peer_mesh = __esm({
560283
560495
  "packages/cli/src/tui/p2p/peer-mesh.ts"() {
@@ -560294,7 +560506,7 @@ var init_peer_mesh = __esm({
560294
560506
  const { publicKey: publicKey2, privateKey } = generateKeyPairSync("ed25519");
560295
560507
  this.publicKey = publicKey2.export({ type: "spki", format: "der" });
560296
560508
  this.privateKey = privateKey.export({ type: "pkcs8", format: "der" });
560297
- this.peerId = createHash18("sha256").update(this.publicKey).digest("base64url").slice(0, 22);
560509
+ this.peerId = createHash19("sha256").update(this.publicKey).digest("base64url").slice(0, 22);
560298
560510
  this.capabilities = options2.capabilities;
560299
560511
  this.displayName = options2.displayName;
560300
560512
  this._authKey = options2.authKey ?? randomBytes20(24).toString("base64url");
@@ -562017,7 +562229,7 @@ __export(omnius_directory_exports, {
562017
562229
  import { cpSync, existsSync as existsSync83, mkdirSync as mkdirSync48, readFileSync as readFileSync66, writeFileSync as writeFileSync43, readdirSync as readdirSync26, statSync as statSync29, unlinkSync as unlinkSync14, openSync as openSync2, closeSync as closeSync2, renameSync as renameSync3 } from "node:fs";
562018
562230
  import { join as join100, relative as relative9, basename as basename17, dirname as dirname28 } from "node:path";
562019
562231
  import { homedir as homedir27 } from "node:os";
562020
- import { createHash as createHash19 } from "node:crypto";
562232
+ import { createHash as createHash20 } from "node:crypto";
562021
562233
  function findGitRoot(startDir) {
562022
562234
  let dir = startDir;
562023
562235
  const visited = /* @__PURE__ */ new Set();
@@ -562398,7 +562610,7 @@ function buildHandoffPrompt(repoRoot) {
562398
562610
  return lines.join("\n");
562399
562611
  }
562400
562612
  function computeDedupeHash(task, savedAt) {
562401
- return createHash19("sha256").update(`${task}|${savedAt}`).digest("hex").slice(0, 16);
562613
+ return createHash20("sha256").update(`${task}|${savedAt}`).digest("hex").slice(0, 16);
562402
562614
  }
562403
562615
  function generateSessionId() {
562404
562616
  const timestamp = Date.now().toString(36);
@@ -562462,10 +562674,10 @@ function normalizeList(items, maxItems) {
562462
562674
  const seen = /* @__PURE__ */ new Set();
562463
562675
  const out = [];
562464
562676
  for (const item of items) {
562465
- const clean3 = normalizeSessionText(item, 300);
562466
- if (!clean3 || seen.has(clean3)) continue;
562467
- seen.add(clean3);
562468
- out.push(clean3);
562677
+ const clean5 = normalizeSessionText(item, 300);
562678
+ if (!clean5 || seen.has(clean5)) continue;
562679
+ seen.add(clean5);
562680
+ out.push(clean5);
562469
562681
  if (out.length >= maxItems) break;
562470
562682
  }
562471
562683
  return out.length > 0 ? out : void 0;
@@ -562753,8 +562965,8 @@ function getLastTaskSummary(repoRoot) {
562753
562965
  if (!ctx3 || ctx3.entries.length === 0) return null;
562754
562966
  const last2 = ctx3.entries[ctx3.entries.length - 1];
562755
562967
  const text = last2.summary || last2.task;
562756
- const clean3 = text.replace(/^\[.*?\]\s*/, "").replace(/\s+/g, " ").trim();
562757
- return clean3.length > 40 ? clean3.slice(0, 37) + "..." : clean3;
562968
+ const clean5 = text.replace(/^\[.*?\]\s*/, "").replace(/\s+/g, " ").trim();
562969
+ return clean5.length > 40 ? clean5.slice(0, 37) + "..." : clean5;
562758
562970
  }
562759
562971
  function saveSessionHistory(repoRoot, sessionId, contentLines, meta) {
562760
562972
  const sessDir = join100(repoRoot, OMNIUS_DIR, SESSIONS_DIR);
@@ -562835,9 +563047,9 @@ function deleteSession(repoRoot, sessionId) {
562835
563047
  }
562836
563048
  function generateSessionName(lines) {
562837
563049
  for (const line of lines.slice(0, 30)) {
562838
- const clean3 = line.trim();
562839
- if (clean3.length > 10 && !clean3.startsWith("i ") && !clean3.startsWith(" ")) {
562840
- const phrase = clean3.replace(/^[>❯▹]\s*/, "").slice(0, 40);
563050
+ const clean5 = line.trim();
563051
+ if (clean5.length > 10 && !clean5.startsWith("i ") && !clean5.startsWith(" ")) {
563052
+ const phrase = clean5.replace(/^[>❯▹]\s*/, "").slice(0, 40);
562841
563053
  return phrase.length > 37 ? phrase.slice(0, 37) + "..." : phrase;
562842
563054
  }
562843
563055
  }
@@ -564705,7 +564917,7 @@ function setTerminalTitle(task, version4) {
564705
564917
  const title = task ? `${task.slice(0, 60)} · ${ver}` : ver;
564706
564918
  process.stdout.write(`\x1B]2;${title}\x07`);
564707
564919
  }
564708
- var EXPERT_TOOL_BASELINES, CONTEXT_SWITCH_OVERHEAD, TURN_PLANNING_OVERHEAD, DEFAULT_TOOL_BASELINE, CODE_READ_CHARS_PER_SEC, PROSE_READ_CHARS_PER_SEC, MIN_CONTENT_FOR_READING, CODE_CONTENT_TOOLS, PROSE_CONTENT_TOOLS, HumanSpeedTracker, PANEL_BG_SEQ, CONTENT_BG_SEQ, BOX_FG, TEXT_PRIMARY, TEXT_DIM, BOX_TL, BOX_TR, BOX_BL, BOX_BR, BOX_H, BOX_V, _globalFooterLock, RESET2, CURSOR_BLINK_BLOCK, _isWindows, StatusBar;
564920
+ var EXPERT_TOOL_BASELINES, CONTEXT_SWITCH_OVERHEAD, TURN_PLANNING_OVERHEAD, DEFAULT_TOOL_BASELINE, CODE_READ_CHARS_PER_SEC, PROSE_READ_CHARS_PER_SEC, MIN_CONTENT_FOR_READING, CODE_CONTENT_TOOLS, PROSE_CONTENT_TOOLS, HumanSpeedTracker, PANEL_BG_SEQ, CONTENT_BG_SEQ, BOX_FG, TEXT_PRIMARY, TEXT_DIM, NO_SUB_AGENTS_HEADER_LABEL, BOX_TL, BOX_TR, BOX_BL, BOX_BR, BOX_H, BOX_V, _globalFooterLock, RESET2, CURSOR_BLINK_BLOCK, _isWindows, StatusBar;
564709
564921
  var init_status_bar = __esm({
564710
564922
  "packages/cli/src/tui/status-bar.ts"() {
564711
564923
  "use strict";
@@ -564877,6 +565089,7 @@ var init_status_bar = __esm({
564877
565089
  BOX_FG = tuiBoxFg();
564878
565090
  TEXT_PRIMARY = tuiTextPrimary() < 0 ? 252 : tuiTextPrimary();
564879
565091
  TEXT_DIM = tuiTextDim();
565092
+ NO_SUB_AGENTS_HEADER_LABEL = " no sub-agents ";
564880
565093
  BOX_TL = "╭";
564881
565094
  BOX_TR = "╮";
564882
565095
  BOX_BL = "╰";
@@ -565326,8 +565539,8 @@ var init_status_bar = __esm({
565326
565539
  }
565327
565540
  } else {
565328
565541
  sysItems.push({
565329
- render: () => `\x1B[38;5;${TEXT_DIM}m no sub-agents `,
565330
- w: 14
565542
+ render: () => `\x1B[38;5;${TEXT_DIM}m${NO_SUB_AGENTS_HEADER_LABEL}`,
565543
+ w: NO_SUB_AGENTS_HEADER_LABEL.length
565331
565544
  });
565332
565545
  }
565333
565546
  const sysSeparatorOffset = sysItems.reduce((sum, item) => sum + item.w, 0);
@@ -565410,7 +565623,10 @@ var init_status_bar = __esm({
565410
565623
  return identity3.separatorOffsets.map((offset) => chrome.contentStartCol + offset).filter((col) => col > 1 && col < termWidth);
565411
565624
  }
565412
565625
  if (panel.meta.kind === "system" && this._sysSeparatorOffset !== null) {
565413
- const col = chrome.contentStartCol + this._sysSeparatorOffset;
565626
+ const rendered = stripAnsi(panel.render(chrome.innerWidth));
565627
+ const renderedOffset = Array.from(rendered).indexOf("│");
565628
+ const offset = renderedOffset >= 0 ? renderedOffset : this._sysSeparatorOffset;
565629
+ const col = chrome.contentStartCol + offset;
565414
565630
  if (col > 1 && col < termWidth) return [col];
565415
565631
  }
565416
565632
  return [];
@@ -565491,7 +565707,7 @@ var init_status_bar = __esm({
565491
565707
  zones.push({ w: base3.length + 1, id: view.id, render: () => "" });
565492
565708
  }
565493
565709
  } else {
565494
- zones.push({ w: 14, render: () => "" });
565710
+ zones.push({ w: NO_SUB_AGENTS_HEADER_LABEL.length, render: () => "" });
565495
565711
  }
565496
565712
  zones.push({ w: 2, render: () => "" });
565497
565713
  const voiceLabel = this._voiceActive ? ` ${this._voiceModelId || "voice"} ` : " voice ";
@@ -567324,10 +567540,10 @@ ${CONTENT_BG_SEQ}`);
567324
567540
  if (this._telegramStatus.active) {
567325
567541
  const suffix = this._telegramStatus.activeSubAgents > 0 ? ` ${this._telegramStatus.activeSubAgents}` : "";
567326
567542
  const label = `✈ tg${suffix}`;
567327
- const compact = `✈${suffix}`;
567543
+ const compact2 = `✈${suffix}`;
567328
567544
  sections.push({
567329
567545
  expanded: `\x1B[38;5;45m${label}\x1B[0m`,
567330
- compact: `\x1B[38;5;45m${compact}\x1B[0m`,
567546
+ compact: `\x1B[38;5;45m${compact2}\x1B[0m`,
567331
567547
  expandedW: 2 + 3 + suffix.length,
567332
567548
  compactW: 2 + suffix.length,
567333
567549
  empty: false
@@ -576056,17 +576272,17 @@ function toggleFocus(state) {
576056
576272
  toggleFocus(state);
576057
576273
  return;
576058
576274
  }
576059
- const clean3 = raw.replace(STDIN_MOUSE_FOCUS_RE, "");
576060
- if (!clean3) return;
576061
- fn(Buffer.from(clean3));
576275
+ const clean5 = raw.replace(STDIN_MOUSE_FOCUS_RE, "");
576276
+ if (!clean5) return;
576277
+ fn(Buffer.from(clean5));
576062
576278
  } else if (typeof args[0] === "string") {
576063
576279
  if (args[0] === "") {
576064
576280
  toggleFocus(state);
576065
576281
  return;
576066
576282
  }
576067
- const clean3 = args[0].replace(STDIN_MOUSE_FOCUS_RE, "");
576068
- if (!clean3) return;
576069
- fn(clean3);
576283
+ const clean5 = args[0].replace(STDIN_MOUSE_FOCUS_RE, "");
576284
+ if (!clean5) return;
576285
+ fn(clean5);
576070
576286
  } else {
576071
576287
  fn(...args);
576072
576288
  }
@@ -577844,8 +578060,8 @@ function normalizeAscii(ascii2, width, height) {
577844
578060
  return lines.slice(0, height).map((line) => line.length > width ? line.slice(0, width) : line).join("\n").trimEnd();
577845
578061
  }
577846
578062
  function previewFailure(message2, width) {
577847
- const clean3 = message2.replace(/\s+/g, " ").trim();
577848
- const text = `[image-to-ascii preview failed: ${clean3 || "unknown error"}]`;
578063
+ const clean5 = message2.replace(/\s+/g, " ").trim();
578064
+ const text = `[image-to-ascii preview failed: ${clean5 || "unknown error"}]`;
577849
578065
  return text.length > width ? `${text.slice(0, Math.max(0, width - 1))}]` : text;
577850
578066
  }
577851
578067
  async function convertWithImageToAscii(imagePath, width, height, timeoutMs) {
@@ -580212,10 +580428,10 @@ except Exception as exc:
580212
580428
  }));
580213
580429
  }
580214
580430
  saveSupertonicProfile(name10) {
580215
- const clean3 = name10.trim().replace(/[^a-zA-Z0-9_.-]/g, "-");
580216
- if (!clean3) return;
580431
+ const clean5 = name10.trim().replace(/[^a-zA-Z0-9_.-]/g, "-");
580432
+ if (!clean5) return;
580217
580433
  const store2 = this.loadSupertonicStore();
580218
- store2.profiles[clean3] = { ...store2.active };
580434
+ store2.profiles[clean5] = { ...store2.active };
580219
580435
  this.saveSupertonicStore(store2);
580220
580436
  }
580221
580437
  loadSupertonicProfile(name10) {
@@ -588126,7 +588342,9 @@ async function showPlatformOnboardingMenu(ctx3, id) {
588126
588342
  } else if (result.key === "telegram-start") {
588127
588343
  const settings = ctx3.getTelegramSettings?.() ?? {};
588128
588344
  if (!settings.key) renderWarning("No Telegram bot token configured.");
588129
- else await ctx3.telegramStart?.(settings.key, settings.admin);
588345
+ else if (ctx3.isTelegramActive?.()) {
588346
+ renderWarning("Telegram bridge already active. Use /telegram stop before restarting.");
588347
+ } else await ctx3.telegramStart?.(settings.key, settings.admin);
588130
588348
  } else if (result.key === "telegram-stop") {
588131
588349
  ctx3.telegramStop?.();
588132
588350
  }
@@ -589926,14 +590144,14 @@ async function handleVoiceMenu(ctx3, save2, hasLocal) {
589926
590144
  if (!jsonDrop.confirmed || !jsonDrop.path) {
589927
590145
  continue;
589928
590146
  }
589929
- const { basename: basename29, join: pathJoin } = await import("node:path");
590147
+ const { basename: basename30, join: pathJoin } = await import("node:path");
589930
590148
  const {
589931
590149
  copyFileSync: copyFileSync5,
589932
590150
  mkdirSync: mkdirSync79,
589933
590151
  existsSync: exists2
589934
590152
  } = await import("node:fs");
589935
590153
  const { homedir: homedir48 } = await import("node:os");
589936
- const modelName = basename29(onnxDrop.path, ".onnx").replace(
590154
+ const modelName = basename30(onnxDrop.path, ".onnx").replace(
589937
590155
  /[^a-zA-Z0-9_-]/g,
589938
590156
  "-"
589939
590157
  );
@@ -590320,7 +590538,7 @@ async function handleVoiceList(ctx3, focusFilename) {
590320
590538
  copyFileSync: cpf,
590321
590539
  mkdirSync: mkd
590322
590540
  } = __require("node:fs");
590323
- const { basename: basename29, join: pjoin } = __require("node:path");
590541
+ const { basename: basename30, join: pjoin } = __require("node:path");
590324
590542
  if (!fe(src2)) {
590325
590543
  renderError(`File not found: ${src2}`);
590326
590544
  helpers.render();
@@ -590333,7 +590551,7 @@ async function handleVoiceList(ctx3, focusFilename) {
590333
590551
  "clone-refs"
590334
590552
  );
590335
590553
  mkd(refsDir, { recursive: true });
590336
- const destName = basename29(src2);
590554
+ const destName = basename30(src2);
590337
590555
  const dest = pjoin(refsDir, destName);
590338
590556
  cpf(src2, dest);
590339
590557
  renderInfo(`Imported "${destName}" → ${dest}`);
@@ -593244,9 +593462,9 @@ async function showExposeDashboard(gateway, rl, ctx3) {
593244
593462
  const recentEntries = [];
593245
593463
  for (let i2 = streamLog.length - 1; i2 >= 0 && recentEntries.length < streamAreaHeight - 1; i2--) {
593246
593464
  const entry = streamLog[i2];
593247
- const clean3 = entry.content.replace(/\n/g, "⏎").replace(/[\x00-\x1F\x7F]/g, "");
593248
- if (!clean3) continue;
593249
- const truncated = clean3.length > maxContentW ? clean3.slice(0, maxContentW - 1) + "…" : clean3;
593465
+ const clean5 = entry.content.replace(/\n/g, "⏎").replace(/[\x00-\x1F\x7F]/g, "");
593466
+ if (!clean5) continue;
593467
+ const truncated = clean5.length > maxContentW ? clean5.slice(0, maxContentW - 1) + "…" : clean5;
593250
593468
  recentEntries.unshift(` \x1B[38;5;245m${truncated}\x1B[0m`);
593251
593469
  }
593252
593470
  while (recentEntries.length < streamAreaHeight - 1)
@@ -593978,27 +594196,468 @@ var init_project_context = __esm({
593978
594196
  }
593979
594197
  });
593980
594198
 
593981
- // packages/cli/src/tui/identity-memory-tool.ts
593982
- import { mkdirSync as mkdirSync55, existsSync as existsSync98 } from "node:fs";
593983
- import { basename as basename20, extname as extname14, join as join112, resolve as resolve39 } from "node:path";
593984
- function memoryDbPaths(repoRoot) {
593985
- const dir = join112(repoRoot, ".omnius", "memory");
594199
+ // packages/cli/src/tui/memory-paths.ts
594200
+ import { mkdirSync as mkdirSync55 } from "node:fs";
594201
+ import { join as join112 } from "node:path";
594202
+ function omniusMemoryDbPaths(repoRoot) {
594203
+ const dir = join112(repoRoot, ".omnius");
593986
594204
  mkdirSync55(dir, { recursive: true });
593987
594205
  return {
594206
+ dir,
593988
594207
  episodes: join112(dir, "episodes.db"),
593989
594208
  knowledge: join112(dir, "knowledge.db")
593990
594209
  };
593991
594210
  }
594211
+ var init_memory_paths = __esm({
594212
+ "packages/cli/src/tui/memory-paths.ts"() {
594213
+ "use strict";
594214
+ }
594215
+ });
594216
+
594217
+ // packages/cli/src/tui/visual-identity-association.ts
594218
+ var visual_identity_association_exports = {};
594219
+ __export(visual_identity_association_exports, {
594220
+ associateVisualIdentityFromImage: () => associateVisualIdentityFromImage,
594221
+ formatVisualIdentityAssociationContext: () => formatVisualIdentityAssociationContext,
594222
+ stageVisualIdentityAssertion: () => stageVisualIdentityAssertion
594223
+ });
594224
+ import { basename as basename20 } from "node:path";
594225
+ function normalizePersonName(name10) {
594226
+ return name10.trim().toLowerCase().replace(/\s+/g, " ");
594227
+ }
593992
594228
  function personKey(name10) {
594229
+ return `person:${normalizePersonName(name10)}`;
594230
+ }
594231
+ function clamp016(value2, fallback = 0) {
594232
+ if (typeof value2 !== "number" || !Number.isFinite(value2)) return fallback;
594233
+ return Math.max(0, Math.min(1, value2));
594234
+ }
594235
+ function parseStructuredIdentifyResult(result) {
594236
+ const raw = String(result.llmContent || result.output || "").trim();
594237
+ if (!raw) return { matches: [], structured: false, raw };
594238
+ let parsed = null;
594239
+ try {
594240
+ parsed = JSON.parse(raw);
594241
+ } catch {
594242
+ return { matches: [], structured: false, raw };
594243
+ }
594244
+ const faces = Array.isArray(parsed.faces) ? parsed.faces : [];
594245
+ const matches = faces.filter((face) => face["identified"] === true && typeof face["name"] === "string" && String(face["name"]).trim()).map((face) => ({
594246
+ name: String(face["name"]).trim(),
594247
+ personId: typeof face["person_id"] === "string" ? face["person_id"] : void 0,
594248
+ confidence: clamp016(face["confidence"], 0),
594249
+ margin: typeof face["margin"] === "number" ? face["margin"] : void 0,
594250
+ bbox: Array.isArray(face["bbox"]) ? face["bbox"].map((n2) => Number(n2)).filter(Number.isFinite) : void 0
594251
+ }));
594252
+ return { matches, structured: true, raw };
594253
+ }
594254
+ function parseDetectedFaceCount(result) {
594255
+ const raw = String(result.llmContent || result.output || "").trim();
594256
+ if (!raw) return 0;
594257
+ try {
594258
+ const parsed = JSON.parse(raw);
594259
+ if (Array.isArray(parsed.faces)) return parsed.faces.length;
594260
+ if (typeof parsed.total === "number" && Number.isFinite(parsed.total)) return Math.max(0, Math.floor(parsed.total));
594261
+ } catch {
594262
+ }
594263
+ return 0;
594264
+ }
594265
+ function readMetadataObject(value2) {
594266
+ return value2 && typeof value2 === "object" ? value2 : {};
594267
+ }
594268
+ function sameScope(a2, b) {
594269
+ if (!b || !a2 || typeof a2 !== "object") return false;
594270
+ const raw = a2;
594271
+ return String(raw["kind"] ?? "") === String(b.kind) && String(raw["id"] ?? "") === String(b.id);
594272
+ }
594273
+ function episodeMatchesScope(ep, scope, sessionId) {
594274
+ if (!scope) return true;
594275
+ const metadata = readMetadataObject(ep.metadata);
594276
+ if (sameScope(metadata["scope"], scope)) return true;
594277
+ if (sessionId && ep.sessionId === sessionId) return true;
594278
+ const chatId = metadata["chat_id"] ?? metadata["chatId"];
594279
+ if ((scope.kind === "group" || scope.kind === "private") && chatId != null && String(chatId) === String(scope.id)) return true;
594280
+ const telegram = readMetadataObject(metadata["telegram"]);
594281
+ if ((scope.kind === "group" || scope.kind === "private") && telegram["chatId"] != null && String(telegram["chatId"]) === String(scope.id)) return true;
594282
+ return false;
594283
+ }
594284
+ function episodeSenderDisplay(ep) {
594285
+ const metadata = readMetadataObject(ep.metadata);
594286
+ const sender = readMetadataObject(metadata["sender"]);
594287
+ return String(sender["displayName"] || sender["username"] || sender["id"] || "").trim() || void 0;
594288
+ }
594289
+ function episodeMessageId(ep) {
594290
+ const metadata = readMetadataObject(ep.metadata);
594291
+ const message2 = readMetadataObject(metadata["message"]);
594292
+ return message2["id"];
594293
+ }
594294
+ function episodeSurface(ep) {
594295
+ const metadata = readMetadataObject(ep.metadata);
594296
+ return typeof metadata["sourceSurface"] === "string" ? metadata["sourceSurface"] : void 0;
594297
+ }
594298
+ function compactContent(value2, max = 220) {
594299
+ const clean5 = value2.replace(/\s+/g, " ").trim();
594300
+ return clean5.length > max ? `${clean5.slice(0, max - 1)}...` : clean5;
594301
+ }
594302
+ function pendingConsumedIds(store2, scope, sessionId) {
594303
+ const consumed = /* @__PURE__ */ new Set();
594304
+ for (const ep of store2.recent(250)) {
594305
+ if (!episodeMatchesScope(ep, scope, sessionId)) continue;
594306
+ const metadata = readMetadataObject(ep.metadata);
594307
+ const consumedMeta = readMetadataObject(metadata["identityPendingConsumed"]);
594308
+ const pendingId = String(consumedMeta["pendingId"] || "").trim();
594309
+ if (pendingId) consumed.add(pendingId);
594310
+ }
594311
+ return consumed;
594312
+ }
594313
+ function activePendingVisualIdentities(store2, scope, sessionId, limit = 3) {
594314
+ const now = Date.now();
594315
+ const consumed = pendingConsumedIds(store2, scope, sessionId);
594316
+ const pending = [];
594317
+ for (const ep of store2.recent(250)) {
594318
+ if (!episodeMatchesScope(ep, scope, sessionId)) continue;
594319
+ const metadata = readMetadataObject(ep.metadata);
594320
+ const meta = readMetadataObject(metadata["identityPending"]);
594321
+ if (String(meta["kind"] || "") !== "visual_identity") continue;
594322
+ const pendingId = String(meta["pendingId"] || "").trim();
594323
+ const name10 = String(meta["name"] || "").trim();
594324
+ if (!pendingId || !name10 || consumed.has(pendingId)) continue;
594325
+ const expiresAt = typeof meta["expiresAt"] === "number" ? meta["expiresAt"] : 0;
594326
+ if (expiresAt > 0 && expiresAt < now) continue;
594327
+ pending.push({
594328
+ pendingId,
594329
+ name: name10,
594330
+ relation: String(meta["relation"] || "depicts"),
594331
+ confidence: clamp016(meta["confidence"], 0.92),
594332
+ note: typeof meta["note"] === "string" ? meta["note"] : void 0,
594333
+ episodeId: ep.id,
594334
+ createdAt: ep.timestamp,
594335
+ expiresAt,
594336
+ sender: readMetadataObject(metadata["sender"]),
594337
+ message: readMetadataObject(metadata["message"])
594338
+ });
594339
+ if (pending.length >= limit) break;
594340
+ }
594341
+ return pending;
594342
+ }
594343
+ function recallPersonEpisodes(store2, graph, name10, scope, sessionId, excludedEpisodeIds, limit) {
594344
+ const person = graph.findNode(personKey(name10), "person");
594345
+ if (!person) return [];
594346
+ const edges = graph.currentEdges(person.id);
594347
+ const ids = /* @__PURE__ */ new Set();
594348
+ for (const edge of edges) {
594349
+ if (edge.sourceEpisodeId && !excludedEpisodeIds.has(edge.sourceEpisodeId)) ids.add(edge.sourceEpisodeId);
594350
+ }
594351
+ const episodes = [...ids].map((id) => store2.get(id)).filter((ep) => Boolean(ep)).filter((ep) => episodeMatchesScope(ep, scope, sessionId)).sort((a2, b) => b.timestamp - a2.timestamp).slice(0, limit);
594352
+ return episodes.map((ep) => ({
594353
+ id: ep.id,
594354
+ timestamp: ep.timestamp,
594355
+ modality: ep.modality,
594356
+ content: compactContent(ep.gist || ep.content),
594357
+ sourceSurface: episodeSurface(ep),
594358
+ messageId: episodeMessageId(ep),
594359
+ senderDisplay: episodeSenderDisplay(ep)
594360
+ }));
594361
+ }
594362
+ function formatTimestamp2(ms) {
594363
+ try {
594364
+ return new Date(ms).toISOString();
594365
+ } catch {
594366
+ return String(ms);
594367
+ }
594368
+ }
594369
+ function formatVisualIdentityAssociationContext(result) {
594370
+ if (result.matches.length === 0 && result.pendingApplied.length === 0 && result.pendingSkipped.length === 0 && result.unknownFaceCount === 0) {
594371
+ return "";
594372
+ }
594373
+ const lines = [];
594374
+ if (result.pendingApplied.length > 0) {
594375
+ lines.push("## Pending Visual Identity Applied");
594376
+ lines.push("A prior explicit user identity assertion was waiting for the next image in this same scope. It has now been linked to this image.");
594377
+ for (const pending of result.pendingApplied) {
594378
+ lines.push(`- ${pending.name} confidence=${pending.confidence.toFixed(2)} pending_id=${pending.pendingId}`);
594379
+ }
594380
+ }
594381
+ if (result.matches.length > 0) {
594382
+ lines.push("## Scoped Visual Identity Recall");
594383
+ lines.push("Prior enrolled visual-memory face match found for the current image. This is not a new identity assertion from the caption or prompt.");
594384
+ for (const match of result.matches) {
594385
+ const confidence2 = Number.isFinite(match.confidence) ? ` confidence=${match.confidence.toFixed(2)}` : "";
594386
+ const margin = typeof match.margin === "number" ? ` margin=${match.margin.toFixed(2)}` : "";
594387
+ lines.push(`- ${match.name}${confidence2}${margin}`);
594388
+ }
594389
+ if (result.recalledEpisodes.length) {
594390
+ lines.push("Scoped related memory:");
594391
+ for (const ep of result.recalledEpisodes) {
594392
+ const sender = ep.senderDisplay ? ` ${ep.senderDisplay}:` : "";
594393
+ const msg = ep.messageId != null ? ` message=${ep.messageId}` : "";
594394
+ lines.push(`- ${formatTimestamp2(ep.timestamp)}${msg}${sender} ${ep.content}`);
594395
+ }
594396
+ } else {
594397
+ lines.push("Scoped related memory: no prior same-scope episode found beyond the enrolled visual identity.");
594398
+ }
594399
+ }
594400
+ if (result.pendingSkipped.length > 0) {
594401
+ lines.push("## Pending Visual Identity Not Applied");
594402
+ for (const skipped of result.pendingSkipped) {
594403
+ lines.push(`- ${skipped.name}: ${skipped.reason}`);
594404
+ }
594405
+ }
594406
+ if (result.matches.length === 0 && result.pendingApplied.length === 0 && result.unknownFaceCount > 0) {
594407
+ lines.push("## Unknown Visual Identity Candidate");
594408
+ lines.push(`${result.unknownFaceCount} face(s) detected, but no prior enrolled identity matched in this scope.`);
594409
+ lines.push("If the user's task depends on who this is, ask who the person is and offer to remember them. Do not guess a real identity.");
594410
+ }
594411
+ lines.push("Use identity_memory only when the user explicitly names, stages, enrolls, asks to identify, or asks to recall identity evidence.");
594412
+ return lines.join("\n");
594413
+ }
594414
+ function stageVisualIdentityAssertion(options2) {
594415
+ const name10 = options2.name.trim();
594416
+ if (!name10) throw new Error("Name is required to stage a visual identity assertion.");
594417
+ const pendingId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
594418
+ const expiresAt = Date.now() + (typeof options2.expiresInMs === "number" && Number.isFinite(options2.expiresInMs) ? Math.max(6e4, Math.min(864e5, options2.expiresInMs)) : 72e5);
594419
+ const dbPaths = omniusMemoryDbPaths(options2.repoRoot);
594420
+ const graph = new TemporalGraph(dbPaths.knowledge);
594421
+ const store2 = new EpisodeStore(dbPaths.episodes, graph);
594422
+ try {
594423
+ const service = new MultimodalIdentityService({ episodeStore: store2, graph });
594424
+ const result = service.ingest({
594425
+ sourceSurface: options2.sourceSurface,
594426
+ sessionId: options2.sessionId,
594427
+ scope: options2.scope,
594428
+ sender: options2.sender,
594429
+ message: options2.message,
594430
+ replyTo: options2.replyTo,
594431
+ modality: "social",
594432
+ content: options2.note || `Pending visual identity assertion for next image: ${name10}`,
594433
+ labels: ["pending_visual_identity", name10],
594434
+ metadata: {
594435
+ identityPending: {
594436
+ kind: "visual_identity",
594437
+ pendingId,
594438
+ target: "next_visual_media",
594439
+ name: name10,
594440
+ relation: options2.relation || "depicts",
594441
+ confidence: clamp016(options2.confidence, 0.92),
594442
+ note: options2.note,
594443
+ createdAt: Date.now(),
594444
+ expiresAt
594445
+ }
594446
+ },
594447
+ identityAssertions: [{
594448
+ name: name10,
594449
+ relation: options2.relation || "named_as",
594450
+ confidence: clamp016(options2.confidence, 0.92),
594451
+ assertedBy: options2.sender,
594452
+ note: options2.note || "Explicit user-provided identity staged for the next visual media."
594453
+ }]
594454
+ });
594455
+ return { pendingId, episodeId: result.episodeId, name: name10, expiresAt };
594456
+ } finally {
594457
+ store2.close();
594458
+ graph.close();
594459
+ }
594460
+ }
594461
+ async function associateVisualIdentityFromImage(options2) {
594462
+ const startedAt2 = Date.now();
594463
+ const visual = options2.visualMemoryTool ?? new VisualMemoryTool();
594464
+ const identify2 = await visual.execute({
594465
+ action: "identify",
594466
+ image: options2.imagePath,
594467
+ threshold: typeof options2.threshold === "number" ? options2.threshold : 0.5,
594468
+ format: "json"
594469
+ });
594470
+ const parsed = parseStructuredIdentifyResult(identify2);
594471
+ if (!identify2.success) {
594472
+ return {
594473
+ matches: [],
594474
+ unknownFaceCount: 0,
594475
+ pendingApplied: [],
594476
+ pendingSkipped: [],
594477
+ recalledEpisodes: [],
594478
+ committedEpisodeIds: [],
594479
+ structured: parsed.structured,
594480
+ contextBlock: "",
594481
+ degradedReason: identify2.error || "visual_memory identify failed",
594482
+ rawIdentifyOutput: parsed.raw
594483
+ };
594484
+ }
594485
+ if (!parsed.structured) {
594486
+ return {
594487
+ matches: [],
594488
+ unknownFaceCount: 0,
594489
+ pendingApplied: [],
594490
+ pendingSkipped: [],
594491
+ recalledEpisodes: [],
594492
+ committedEpisodeIds: [],
594493
+ structured: false,
594494
+ contextBlock: "",
594495
+ degradedReason: "visual_memory identify did not return structured JSON",
594496
+ rawIdentifyOutput: parsed.raw
594497
+ };
594498
+ }
594499
+ const dbPaths = omniusMemoryDbPaths(options2.repoRoot);
594500
+ const graph = new TemporalGraph(dbPaths.knowledge);
594501
+ const store2 = new EpisodeStore(dbPaths.episodes, graph);
594502
+ const committedEpisodeIds = [];
594503
+ const pendingApplied = [];
594504
+ const pendingSkipped = [];
594505
+ let unknownFaceCount = 0;
594506
+ try {
594507
+ const shouldCommit = options2.commitMatches !== false;
594508
+ const service = new MultimodalIdentityService({ episodeStore: store2, graph });
594509
+ if (parsed.matches.length === 0) {
594510
+ try {
594511
+ const detect2 = await visual.execute({ action: "detect", image: options2.imagePath, format: "json" });
594512
+ if (detect2.success) unknownFaceCount = parseDetectedFaceCount(detect2);
594513
+ } catch {
594514
+ }
594515
+ }
594516
+ if (shouldCommit && parsed.matches.length > 0) {
594517
+ for (const match of parsed.matches) {
594518
+ const result2 = service.ingest({
594519
+ sourceSurface: options2.sourceSurface,
594520
+ sessionId: options2.sessionId,
594521
+ scope: options2.scope,
594522
+ sender: options2.sender,
594523
+ message: options2.message,
594524
+ replyTo: options2.replyTo,
594525
+ modality: "visual",
594526
+ content: `Prior enrolled visual identity match: ${match.name}`,
594527
+ extractedContent: options2.extractedContent,
594528
+ media: options2.media ?? { path: options2.imagePath, mediaType: "photo" },
594529
+ labels: ["visual_identity_match", match.name],
594530
+ metadata: {
594531
+ visualIdentityAssociation: {
594532
+ version: 1,
594533
+ imagePath: options2.imagePath,
594534
+ name: match.name,
594535
+ personId: match.personId,
594536
+ confidence: match.confidence,
594537
+ margin: match.margin,
594538
+ elapsedMs: Date.now() - startedAt2
594539
+ }
594540
+ },
594541
+ identityAssertions: [{
594542
+ name: match.name,
594543
+ relation: "same_person_candidate",
594544
+ confidence: match.confidence,
594545
+ assertedBy: { id: "visual_memory", displayName: "visual_memory", isBot: true },
594546
+ note: `Prior enrolled visual-memory face match for ${basename20(options2.imagePath)}`
594547
+ }]
594548
+ });
594549
+ if (result2.episodeId) committedEpisodeIds.push(result2.episodeId);
594550
+ }
594551
+ }
594552
+ if (shouldCommit && parsed.matches.length === 0) {
594553
+ const pending = activePendingVisualIdentities(store2, options2.scope, options2.sessionId);
594554
+ for (const item of pending) {
594555
+ const enrollment = await visual.execute({ action: "enroll", image: options2.imagePath, name: item.name });
594556
+ if (!enrollment.success) {
594557
+ pendingSkipped.push({
594558
+ pendingId: item.pendingId,
594559
+ name: item.name,
594560
+ reason: enrollment.error || enrollment.output || "face enrollment failed"
594561
+ });
594562
+ continue;
594563
+ }
594564
+ const result2 = service.ingest({
594565
+ sourceSurface: options2.sourceSurface,
594566
+ sessionId: options2.sessionId,
594567
+ scope: options2.scope,
594568
+ sender: options2.sender,
594569
+ message: options2.message,
594570
+ replyTo: options2.replyTo,
594571
+ modality: "visual",
594572
+ content: `Applied pending visual identity assertion: ${item.name}`,
594573
+ extractedContent: options2.extractedContent,
594574
+ media: options2.media ?? { path: options2.imagePath, mediaType: "photo" },
594575
+ labels: ["pending_visual_identity_applied", item.name],
594576
+ metadata: {
594577
+ identityPendingConsumed: {
594578
+ pendingId: item.pendingId,
594579
+ sourceEpisodeId: item.episodeId,
594580
+ name: item.name,
594581
+ imagePath: options2.imagePath,
594582
+ consumedAt: Date.now(),
594583
+ enrollmentOutput: enrollment.output
594584
+ }
594585
+ },
594586
+ identityAssertions: [{
594587
+ name: item.name,
594588
+ relation: "depicts",
594589
+ confidence: item.confidence,
594590
+ assertedBy: item.sender || options2.sender,
594591
+ note: item.note || `Applied explicit pending identity assertion to ${basename20(options2.imagePath)}`
594592
+ }]
594593
+ });
594594
+ if (result2.episodeId) committedEpisodeIds.push(result2.episodeId);
594595
+ pendingApplied.push({
594596
+ pendingId: item.pendingId,
594597
+ name: item.name,
594598
+ confidence: item.confidence,
594599
+ sourceEpisodeId: item.episodeId,
594600
+ committedEpisodeId: result2.episodeId,
594601
+ enrollmentOutput: enrollment.output
594602
+ });
594603
+ }
594604
+ }
594605
+ const excluded = new Set(committedEpisodeIds);
594606
+ const limit = typeof options2.limit === "number" && Number.isFinite(options2.limit) ? Math.max(1, Math.min(12, Math.floor(options2.limit))) : 5;
594607
+ const recalledEpisodes = [];
594608
+ const seen = /* @__PURE__ */ new Set();
594609
+ const namesForRecall = [
594610
+ ...parsed.matches.map((match) => match.name),
594611
+ ...pendingApplied.map((pending) => pending.name)
594612
+ ];
594613
+ for (const name10 of namesForRecall) {
594614
+ for (const ep of recallPersonEpisodes(store2, graph, name10, options2.scope, options2.sessionId, excluded, limit)) {
594615
+ if (seen.has(ep.id)) continue;
594616
+ seen.add(ep.id);
594617
+ recalledEpisodes.push(ep);
594618
+ }
594619
+ }
594620
+ const result = {
594621
+ matches: parsed.matches,
594622
+ unknownFaceCount,
594623
+ pendingApplied,
594624
+ pendingSkipped,
594625
+ recalledEpisodes: recalledEpisodes.slice(0, limit),
594626
+ committedEpisodeIds,
594627
+ structured: true,
594628
+ contextBlock: "",
594629
+ rawIdentifyOutput: parsed.raw
594630
+ };
594631
+ result.contextBlock = formatVisualIdentityAssociationContext(result);
594632
+ return result;
594633
+ } finally {
594634
+ store2.close();
594635
+ graph.close();
594636
+ }
594637
+ }
594638
+ var init_visual_identity_association = __esm({
594639
+ "packages/cli/src/tui/visual-identity-association.ts"() {
594640
+ "use strict";
594641
+ init_dist7();
594642
+ init_dist5();
594643
+ init_memory_paths();
594644
+ }
594645
+ });
594646
+
594647
+ // packages/cli/src/tui/identity-memory-tool.ts
594648
+ import { existsSync as existsSync98 } from "node:fs";
594649
+ import { basename as basename21, extname as extname14, resolve as resolve39 } from "node:path";
594650
+ function personKey2(name10) {
593993
594651
  return `person:${name10.trim().toLowerCase().replace(/\s+/g, " ")}`;
593994
594652
  }
593995
594653
  function coerceAction(value2) {
593996
594654
  const raw = String(value2 || "recall").trim().toLowerCase();
593997
594655
  if (raw === "assert" || raw === "remember" || raw === "name") return "assert_identity";
594656
+ if (raw === "stage" || raw === "pending" || raw === "next" || raw === "expect") return "stage_identity";
593998
594657
  if (raw === "enroll" || raw === "enrol" || raw === "face") return "enroll_face";
593999
594658
  if (raw === "who" || raw === "recognize" || raw === "recognise") return "identify";
594000
594659
  if (raw === "list") return "inspect";
594001
- if (raw === "assert_identity" || raw === "enroll_face" || raw === "identify" || raw === "recall" || raw === "inspect") {
594660
+ if (raw === "assert_identity" || raw === "stage_identity" || raw === "enroll_face" || raw === "identify" || raw === "recall" || raw === "inspect") {
594002
594661
  return raw;
594003
594662
  }
594004
594663
  return "recall";
@@ -594056,7 +594715,7 @@ async function resolveMediaFromArgs(args, opts) {
594056
594715
  path: path11,
594057
594716
  media,
594058
594717
  modality: inferModality(media),
594059
- label: basename20(path11)
594718
+ label: basename21(path11)
594060
594719
  };
594061
594720
  }
594062
594721
  function edgeDirection(edge, nodeId, otherText) {
@@ -594067,7 +594726,10 @@ function formatIdentityMemoryContext(surface) {
594067
594726
  return [
594068
594727
  "Identity memory contract:",
594069
594728
  "- Use identity_memory when a user explicitly asks you to remember, name, enroll, identify, or recall a person/voice/object identity from scoped media.",
594070
- "- Do not infer a real person's identity from an image on your own. Store identity only from an explicit user assertion or from a prior enrolled match.",
594729
+ "- Do not infer a real person's identity from an image on your own. Store identity only from an explicit user assertion or from a structured prior enrolled visual-memory match.",
594730
+ "- Image ingress may include a Scoped Visual Identity Recall block. Treat it as prior enrolled evidence scoped to the current chat/session, not as a new caption-derived assertion.",
594731
+ "- If the user gives a name before the image arrives, or says the next image/media will be someone, call identity_memory with action='stage_identity', name='<explicit name>'. The next same-scope image can then link and enroll that face.",
594732
+ "- If image ingress includes Unknown Visual Identity Candidate, ask who the person is only when identity matters to the user's task; if they provide a name, call identity_memory.",
594071
594733
  "- For Telegram replies, use media='reply' when the user is naming or asking about the replied-to media; use media='latest' for the latest media in the same chat.",
594072
594734
  "- For visual identity assertions such as 'this is Amy', call identity_memory with action='assert_identity', name='Amy', relation='depicts'. The tool stores graph evidence and attempts face enrollment.",
594073
594735
  "- For 'who is this?' on an image, call identity_memory with action='identify'.",
@@ -594080,6 +594742,8 @@ var init_identity_memory_tool = __esm({
594080
594742
  "use strict";
594081
594743
  init_dist7();
594082
594744
  init_dist5();
594745
+ init_memory_paths();
594746
+ init_visual_identity_association();
594083
594747
  IdentityMemoryTool = class {
594084
594748
  constructor(repoRoot, opts = {}) {
594085
594749
  this.repoRoot = repoRoot;
@@ -594088,14 +594752,14 @@ var init_identity_memory_tool = __esm({
594088
594752
  repoRoot;
594089
594753
  opts;
594090
594754
  name = "identity_memory";
594091
- description = "Scoped multimodal identity memory. Agentically records explicit identity assertions, enrolls visual face evidence, identifies enrolled faces, and recalls stored person evidence without regex shortcuts.";
594755
+ description = "Scoped multimodal identity memory. Agentically records explicit identity assertions, stages next-media identity assertions, enrolls visual face evidence, identifies enrolled faces, and recalls stored person evidence without regex shortcuts.";
594092
594756
  parameters = {
594093
594757
  type: "object",
594094
594758
  properties: {
594095
594759
  action: {
594096
594760
  type: "string",
594097
- enum: ["assert_identity", "enroll_face", "identify", "recall", "inspect"],
594098
- description: "assert_identity/enroll_face store explicit identity evidence; identify recognizes current media; recall/inspect read stored identity evidence."
594761
+ enum: ["assert_identity", "stage_identity", "enroll_face", "identify", "recall", "inspect"],
594762
+ description: "assert_identity/enroll_face store explicit current-media evidence; stage_identity stores an explicit name for the next visual media in this scope; identify recognizes current media; recall/inspect read stored identity evidence."
594099
594763
  },
594100
594764
  name: {
594101
594765
  type: "string",
@@ -594133,6 +594797,10 @@ var init_identity_memory_tool = __esm({
594133
594797
  limit: {
594134
594798
  type: "number",
594135
594799
  description: "Recall/inspect result limit. Default 12."
594800
+ },
594801
+ expires_in_minutes: {
594802
+ type: "number",
594803
+ description: "For stage_identity, how long the pending next-image assertion remains active. Default 120 minutes."
594136
594804
  }
594137
594805
  },
594138
594806
  required: ["action"]
@@ -594140,7 +594808,7 @@ var init_identity_memory_tool = __esm({
594140
594808
  async execute(args) {
594141
594809
  const startedAt2 = Date.now();
594142
594810
  const action = coerceAction(args["action"]);
594143
- const dbPaths = memoryDbPaths(this.repoRoot);
594811
+ const dbPaths = omniusMemoryDbPaths(this.repoRoot);
594144
594812
  const store2 = new EpisodeStore(dbPaths.episodes);
594145
594813
  const graph = new TemporalGraph(dbPaths.knowledge);
594146
594814
  try {
@@ -594150,6 +594818,9 @@ var init_identity_memory_tool = __esm({
594150
594818
  if (action === "recall" || action === "inspect") {
594151
594819
  return this.recall(args, startedAt2, graph);
594152
594820
  }
594821
+ if (action === "stage_identity") {
594822
+ return this.stageIdentity(args, startedAt2);
594823
+ }
594153
594824
  return await this.assertIdentity(action, args, startedAt2, store2, graph);
594154
594825
  } finally {
594155
594826
  store2.close();
@@ -594176,6 +594847,7 @@ var init_identity_memory_tool = __esm({
594176
594847
  const content = evidenceNote || this.opts.message?.text || (media?.caption ? `Identity assertion for ${name10}: ${media.caption}` : `Identity assertion for ${name10}`);
594177
594848
  const result = service.ingest({
594178
594849
  sourceSurface: this.opts.sourceSurface || "api",
594850
+ sessionId: this.opts.sessionId,
594179
594851
  scope: this.opts.scope,
594180
594852
  sender: this.opts.sender,
594181
594853
  message: this.opts.message,
@@ -594201,7 +594873,7 @@ var init_identity_memory_tool = __esm({
594201
594873
  } else if (shouldEnrollFace) {
594202
594874
  faceLine = "face enrollment: skipped because no resolved image path was available";
594203
594875
  }
594204
- const mediaLine = resolvedMedia ? `media: ${resolvedMedia.label || basename20(resolvedMedia.path)} (${resolvedMedia.path})` : "media: none; stored as scoped textual identity evidence only";
594876
+ const mediaLine = resolvedMedia ? `media: ${resolvedMedia.label || basename21(resolvedMedia.path)} (${resolvedMedia.path})` : "media: none; stored as scoped textual identity evidence only";
594205
594877
  const output = [
594206
594878
  `Stored identity evidence for ${name10}.`,
594207
594879
  `relation: ${relation}`,
@@ -594222,6 +594894,50 @@ var init_identity_memory_tool = __esm({
594222
594894
  durationMs: Date.now() - startedAt2
594223
594895
  };
594224
594896
  }
594897
+ stageIdentity(args, startedAt2) {
594898
+ const name10 = String(args["name"] || "").trim();
594899
+ if (!name10) {
594900
+ return {
594901
+ success: false,
594902
+ output: "",
594903
+ error: "identity_memory stage_identity requires name. Pass the explicit user-provided name for the next visual media.",
594904
+ durationMs: Date.now() - startedAt2
594905
+ };
594906
+ }
594907
+ const evidenceNote = String(args["evidence_note"] ?? args["note"] ?? this.opts.message?.text ?? "").trim();
594908
+ const expiresMinutes = typeof args["expires_in_minutes"] === "number" && Number.isFinite(args["expires_in_minutes"]) ? Math.max(1, Math.min(1440, args["expires_in_minutes"])) : 120;
594909
+ const result = stageVisualIdentityAssertion({
594910
+ repoRoot: this.repoRoot,
594911
+ sourceSurface: this.opts.sourceSurface || "api",
594912
+ sessionId: this.opts.sessionId,
594913
+ scope: this.opts.scope,
594914
+ sender: this.opts.sender,
594915
+ message: this.opts.message,
594916
+ replyTo: this.opts.replyTo,
594917
+ name: name10,
594918
+ relation: "depicts",
594919
+ confidence: coerceConfidence(args["confidence"]),
594920
+ note: evidenceNote || `Explicit user-provided name for the next visual media: ${name10}`,
594921
+ expiresInMs: expiresMinutes * 6e4
594922
+ });
594923
+ return {
594924
+ success: true,
594925
+ output: [
594926
+ `Staged visual identity for next same-scope image: ${result.name}.`,
594927
+ `pending_id: ${result.pendingId}`,
594928
+ `episode: ${result.episodeId}`,
594929
+ `expires_at: ${new Date(result.expiresAt).toISOString()}`
594930
+ ].join("\n"),
594931
+ llmContent: [
594932
+ `identity_memory staged next_visual_media name=${result.name}`,
594933
+ `pending_id=${result.pendingId}`,
594934
+ `episode_id=${result.episodeId}`,
594935
+ `expires_at=${new Date(result.expiresAt).toISOString()}`,
594936
+ "When the next image arrives in this same scope, image ingress can link and enroll it without guessing from text."
594937
+ ].join("\n"),
594938
+ durationMs: Date.now() - startedAt2
594939
+ };
594940
+ }
594225
594941
  async identify(args, startedAt2, graph) {
594226
594942
  const resolvedMedia = await resolveMediaFromArgs(args, this.opts);
594227
594943
  if (!resolvedMedia?.path) {
@@ -594240,23 +594956,40 @@ var init_identity_memory_tool = __esm({
594240
594956
  durationMs: Date.now() - startedAt2
594241
594957
  };
594242
594958
  }
594243
- const visual = this.opts.visualMemoryTool ?? new VisualMemoryTool();
594244
594959
  const threshold = typeof args["threshold"] === "number" ? args["threshold"] : 0.5;
594245
- const visualResult = await visual.execute({ action: "identify", image: resolvedMedia.path, threshold });
594960
+ const association = await associateVisualIdentityFromImage({
594961
+ repoRoot: this.repoRoot,
594962
+ imagePath: resolvedMedia.path,
594963
+ sourceSurface: this.opts.sourceSurface || "api",
594964
+ scope: this.opts.scope,
594965
+ sender: this.opts.sender,
594966
+ message: this.opts.message,
594967
+ replyTo: this.opts.replyTo,
594968
+ sessionId: this.opts.sessionId,
594969
+ media: resolvedMedia.media ?? { path: resolvedMedia.path, mediaType: inferMediaTypeFromPath(resolvedMedia.path) },
594970
+ extractedContent: resolvedMedia.extractedContent,
594971
+ threshold,
594972
+ visualMemoryTool: this.opts.visualMemoryTool,
594973
+ limit: typeof args["limit"] === "number" ? args["limit"] : 5
594974
+ });
594246
594975
  const known = graph.nodesByType("person", 50).filter((node) => node.text.startsWith("person:")).map((node) => node.text.slice("person:".length)).slice(0, 12);
594247
594976
  const knownLine = known.length ? `known identity nodes: ${known.join(", ")}` : "known identity nodes: none";
594977
+ const matchLine = association.matches.length ? `visual matches: ${association.matches.map((match) => `${match.name} (${match.confidence.toFixed(2)})`).join(", ")}` : "visual matches: none";
594978
+ const context2 = association.contextBlock || association.degradedReason || "No enrolled face matched this image.";
594248
594979
  return {
594249
- success: visualResult.success,
594980
+ success: !association.degradedReason,
594250
594981
  output: [
594251
- visualResult.output || visualResult.error || "No visual identification result.",
594982
+ matchLine,
594983
+ context2,
594252
594984
  knownLine
594253
594985
  ].filter(Boolean).join("\n"),
594254
594986
  llmContent: [
594255
- visualResult.llmContent || visualResult.output || visualResult.error || "No visual identification result.",
594987
+ matchLine,
594988
+ association.contextBlock || association.degradedReason || "No enrolled face matched this image.",
594256
594989
  knownLine,
594257
594990
  "If no face matched and the user supplied a name, call identity_memory action='assert_identity'."
594258
594991
  ].join("\n"),
594259
- error: visualResult.success ? void 0 : visualResult.error,
594992
+ error: association.degradedReason,
594260
594993
  durationMs: Date.now() - startedAt2
594261
594994
  };
594262
594995
  }
@@ -594272,7 +595005,7 @@ ${people.join("\n")}` : "No stored explicit identity nodes found.",
594272
595005
  durationMs: Date.now() - startedAt2
594273
595006
  };
594274
595007
  }
594275
- const node = graph.findNode(personKey(name10), "person");
595008
+ const node = graph.findNode(personKey2(name10), "person");
594276
595009
  if (!node) {
594277
595010
  return {
594278
595011
  success: true,
@@ -595081,7 +595814,7 @@ var init_banner = __esm({
595081
595814
 
595082
595815
  // packages/cli/src/tui/carousel-descriptors.ts
595083
595816
  import { existsSync as existsSync100, readFileSync as readFileSync80, writeFileSync as writeFileSync51, mkdirSync as mkdirSync57, readdirSync as readdirSync32 } from "node:fs";
595084
- import { join as join114, basename as basename21 } from "node:path";
595817
+ import { join as join114, basename as basename22 } from "node:path";
595085
595818
  function loadToolProfile(repoRoot) {
595086
595819
  const filePath = join114(repoRoot, OMNIUS_DIR, "context", TOOL_PROFILE_FILE);
595087
595820
  try {
@@ -595177,7 +595910,7 @@ function generateDescriptors(repoRoot) {
595177
595910
  extractFromSessions(repoRoot, tags);
595178
595911
  extractFromMemory(repoRoot, tags);
595179
595912
  extractFromToolProfile(profile, tags);
595180
- const repoName2 = basename21(repoRoot);
595913
+ const repoName2 = basename22(repoRoot);
595181
595914
  if (repoName2 && !tags.includes(repoName2)) {
595182
595915
  tags.push(repoName2);
595183
595916
  }
@@ -596197,7 +596930,7 @@ var init_promptLoader3 = __esm({
596197
596930
 
596198
596931
  // packages/cli/src/tui/dream-engine.ts
596199
596932
  import { mkdirSync as mkdirSync59, writeFileSync as writeFileSync52, readFileSync as readFileSync82, existsSync as existsSync102, readdirSync as readdirSync33 } from "node:fs";
596200
- import { join as join117, basename as basename22 } from "node:path";
596933
+ import { join as join117, basename as basename23 } from "node:path";
596201
596934
  import { execSync as execSync54 } from "node:child_process";
596202
596935
  function setDreamWriteContent(fn) {
596203
596936
  _dreamWriteContent = fn;
@@ -596410,7 +597143,7 @@ var init_dream_engine = __esm({
596410
597143
  const rawPath = String(args["path"] ?? "");
596411
597144
  const content = String(args["content"] ?? "");
596412
597145
  if (!rawPath) return { success: false, output: "", error: "path is required", durationMs: Date.now() - start2 };
596413
- const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".omnius/autoresearch") ? join117(this.autoresearchDir, basename22(rawPath)) : join117(this.autoresearchDir, rawPath);
597146
+ const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".omnius/autoresearch") ? join117(this.autoresearchDir, basename23(rawPath)) : join117(this.autoresearchDir, rawPath);
596414
597147
  if (!targetPath.startsWith(this.autoresearchDir)) {
596415
597148
  return { success: false, output: "", error: "Autoresearch mode: writes are confined to .omnius/autoresearch/", durationMs: Date.now() - start2 };
596416
597149
  }
@@ -596445,7 +597178,7 @@ var init_dream_engine = __esm({
596445
597178
  const rawPath = String(args["path"] ?? "");
596446
597179
  const oldStr = String(args["old_string"] ?? "");
596447
597180
  const newStr = String(args["new_string"] ?? "");
596448
- const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".omnius/autoresearch") ? join117(this.autoresearchDir, basename22(rawPath)) : join117(this.autoresearchDir, rawPath);
597181
+ const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".omnius/autoresearch") ? join117(this.autoresearchDir, basename23(rawPath)) : join117(this.autoresearchDir, rawPath);
596449
597182
  if (!targetPath.startsWith(this.autoresearchDir)) {
596450
597183
  return { success: false, output: "", error: "Autoresearch mode: edits are confined to .omnius/autoresearch/", durationMs: Date.now() - start2 };
596451
597184
  }
@@ -596498,7 +597231,7 @@ var init_dream_engine = __esm({
596498
597231
  const rawPath = String(args["path"] ?? "");
596499
597232
  const content = String(args["content"] ?? "");
596500
597233
  if (!rawPath) return { success: false, output: "", error: "path is required", durationMs: Date.now() - start2 };
596501
- const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".omnius/dreams") ? join117(this.dreamsDir, basename22(rawPath)) : join117(this.dreamsDir, rawPath);
597234
+ const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".omnius/dreams") ? join117(this.dreamsDir, basename23(rawPath)) : join117(this.dreamsDir, rawPath);
596502
597235
  if (!targetPath.startsWith(this.dreamsDir)) {
596503
597236
  return { success: false, output: "", error: "Dream mode: writes are confined to .omnius/dreams/", durationMs: Date.now() - start2 };
596504
597237
  }
@@ -596533,7 +597266,7 @@ var init_dream_engine = __esm({
596533
597266
  const rawPath = String(args["path"] ?? "");
596534
597267
  const oldStr = String(args["old_string"] ?? "");
596535
597268
  const newStr = String(args["new_string"] ?? "");
596536
- const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".omnius/dreams") ? join117(this.dreamsDir, basename22(rawPath)) : join117(this.dreamsDir, rawPath);
597269
+ const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".omnius/dreams") ? join117(this.dreamsDir, basename23(rawPath)) : join117(this.dreamsDir, rawPath);
596537
597270
  if (!targetPath.startsWith(this.dreamsDir)) {
596538
597271
  return { success: false, output: "", error: "Dream mode: edits are confined to .omnius/dreams/", durationMs: Date.now() - start2 };
596539
597272
  }
@@ -598130,7 +598863,7 @@ var init_bless_engine = __esm({
598130
598863
 
598131
598864
  // packages/cli/src/tui/dmn-engine.ts
598132
598865
  import { existsSync as existsSync103, readFileSync as readFileSync83, writeFileSync as writeFileSync53, mkdirSync as mkdirSync60, readdirSync as readdirSync34, unlinkSync as unlinkSync18 } from "node:fs";
598133
- import { join as join118, basename as basename23 } from "node:path";
598866
+ import { join as join118, basename as basename24 } from "node:path";
598134
598867
  function buildDMNGatherPrompt(recentTaskSummaries, dueReminders, attentionItems, memoryTopics, capabilities, competence, reflectionBuffer) {
598135
598868
  const competenceReport = competence.length > 0 ? competence.map((c9) => {
598136
598869
  const rate = c9.attempts > 0 ? Math.round(c9.successes / c9.attempts * 100) : 0;
@@ -598881,7 +599614,7 @@ OUTPUT: Call task_complete with JSON:
598881
599614
  try {
598882
599615
  const files = readdirSync34(dir).filter((f2) => f2.endsWith(".json"));
598883
599616
  for (const f2 of files) {
598884
- const topic = basename23(f2, ".json");
599617
+ const topic = basename24(f2, ".json");
598885
599618
  if (!topics.includes(topic)) topics.push(topic);
598886
599619
  }
598887
599620
  } catch {
@@ -598935,7 +599668,7 @@ OUTPUT: Call task_complete with JSON:
598935
599668
 
598936
599669
  // packages/cli/src/tui/snr-engine.ts
598937
599670
  import { existsSync as existsSync104, readdirSync as readdirSync35, readFileSync as readFileSync84 } from "node:fs";
598938
- import { join as join119, basename as basename24 } from "node:path";
599671
+ import { join as join119, basename as basename25 } from "node:path";
598939
599672
  function computeDPrime(signalScores, noiseScores) {
598940
599673
  if (signalScores.length === 0 || noiseScores.length === 0) return 0;
598941
599674
  const mean = (arr) => arr.reduce((s2, v) => s2 + v, 0) / arr.length;
@@ -599229,7 +599962,7 @@ Call task_complete with the JSON array when done.`,
599229
599962
  try {
599230
599963
  const files = readdirSync35(dir).filter((f2) => f2.endsWith(".json"));
599231
599964
  for (const f2 of files) {
599232
- const topic = basename24(f2, ".json");
599965
+ const topic = basename25(f2, ".json");
599233
599966
  if (topics.length > 0 && !topics.includes(topic)) continue;
599234
599967
  try {
599235
599968
  const data = JSON.parse(readFileSync84(join119(dir, f2), "utf-8"));
@@ -599861,7 +600594,7 @@ import {
599861
600594
  } from "node:fs";
599862
600595
  import { mkdir as mkdir17 } from "node:fs/promises";
599863
600596
  import {
599864
- basename as basename25,
600597
+ basename as basename26,
599865
600598
  dirname as dirname33,
599866
600599
  extname as extname15,
599867
600600
  isAbsolute as isAbsolute6,
@@ -600078,7 +600811,7 @@ function guardPath(root, rawPath) {
600078
600811
  error: `Path escapes the public creative workspace. Use a relative path under ${rootAbs}.`
600079
600812
  };
600080
600813
  }
600081
- if (basename25(abs) === MANIFEST_FILE) {
600814
+ if (basename26(abs) === MANIFEST_FILE) {
600082
600815
  return { ok: false, error: "The creative workspace manifest is internal and cannot be edited." };
600083
600816
  }
600084
600817
  return { ok: true, path: { abs, rel } };
@@ -600170,7 +600903,7 @@ function rememberCreated(root, absPath) {
600170
600903
  manifest.objects[rel] = {
600171
600904
  logicalRel: rel,
600172
600905
  storedRel,
600173
- originalName: basename25(guarded.path.abs),
600906
+ originalName: basename26(guarded.path.abs),
600174
600907
  prefixBytes: prefix.length,
600175
600908
  encrypted: true,
600176
600909
  key: key.toString("base64"),
@@ -600221,7 +600954,7 @@ function materializeTelegramCreativeArtifactForSend(root, rawPath) {
600221
600954
  }
600222
600955
  const stageDir = join120(rootAbs, SEND_DIR, `${Date.now()}-${randomBytes21(8).toString("hex")}`);
600223
600956
  mkdirSync61(stageDir, { recursive: true });
600224
- const staged = join120(stageDir, object.originalName || basename25(rel));
600957
+ const staged = join120(stageDir, object.originalName || basename26(rel));
600225
600958
  writeFileSync54(staged, payload);
600226
600959
  return {
600227
600960
  ok: true,
@@ -600437,7 +601170,7 @@ ${result.output}`,
600437
601170
  });
600438
601171
 
600439
601172
  // packages/cli/src/tui/stimulation.ts
600440
- function clamp016(value2) {
601173
+ function clamp017(value2) {
600441
601174
  return Math.max(0, Math.min(1, value2));
600442
601175
  }
600443
601176
  function cloneState(state) {
@@ -600493,7 +601226,7 @@ var init_stimulation = __esm({
600493
601226
  ...DEFAULT_STATE,
600494
601227
  ...state,
600495
601228
  phase: normalizePhase(state.phase) ?? DEFAULT_STATE.phase,
600496
- attention: clamp016(Number.isFinite(state.attention) ? Number(state.attention) : DEFAULT_STATE.attention),
601229
+ attention: clamp017(Number.isFinite(state.attention) ? Number(state.attention) : DEFAULT_STATE.attention),
600497
601230
  updatedAtMs: Number.isFinite(state.updatedAtMs) ? Number(state.updatedAtMs) : now,
600498
601231
  lastStimulusAtMs: Number.isFinite(state.lastStimulusAtMs) ? Number(state.lastStimulusAtMs) : now,
600499
601232
  messagesSinceAnalysis: Math.max(0, Math.floor(Number(state.messagesSinceAnalysis ?? 0))),
@@ -600543,11 +601276,11 @@ var init_stimulation = __esm({
600543
601276
  const state = this.stateFor(channelId, nowMs);
600544
601277
  this.applyTimeDecay(state, nowMs);
600545
601278
  if (Number.isFinite(decision.attentionScore)) {
600546
- state.attention = clamp016(Number(decision.attentionScore));
601279
+ state.attention = clamp017(Number(decision.attentionScore));
600547
601280
  } else if (Number.isFinite(decision.attentionDelta)) {
600548
- state.attention = clamp016(state.attention + Number(decision.attentionDelta));
601281
+ state.attention = clamp017(state.attention + Number(decision.attentionDelta));
600549
601282
  } else {
600550
- state.attention = clamp016(state.attention + (decision.shouldReply ? 0.22 : -0.1));
601283
+ state.attention = clamp017(state.attention + (decision.shouldReply ? 0.22 : -0.1));
600551
601284
  }
600552
601285
  if (decision.phase) {
600553
601286
  state.attention = Math.max(state.attention, PHASE_FLOORS[decision.phase]);
@@ -600603,7 +601336,7 @@ var init_stimulation = __esm({
600603
601336
  const elapsedMs2 = Math.max(0, nowMs - (state.updatedAtMs || nowMs));
600604
601337
  if (elapsedMs2 <= 0) return;
600605
601338
  const halfLives = elapsedMs2 / (12 * 6e4);
600606
- state.attention = clamp016(state.attention * Math.pow(0.5, halfLives));
601339
+ state.attention = clamp017(state.attention * Math.pow(0.5, halfLives));
600607
601340
  if (state.phase !== "engaged" || elapsedMs2 > 4 * 6e4) {
600608
601341
  state.phase = phaseFromAttention(state.attention);
600609
601342
  }
@@ -600615,7 +601348,7 @@ var init_stimulation = __esm({
600615
601348
  // packages/cli/src/tui/telegram-channel-dmn.ts
600616
601349
  import { existsSync as existsSync106, mkdirSync as mkdirSync62, readdirSync as readdirSync36, readFileSync as readFileSync86, writeFileSync as writeFileSync55 } from "node:fs";
600617
601350
  import { join as join121 } from "node:path";
600618
- import { createHash as createHash20 } from "node:crypto";
601351
+ import { createHash as createHash21 } from "node:crypto";
600619
601352
  function safeFilePart(value2) {
600620
601353
  return value2.replace(/[^A-Za-z0-9_.-]+/g, "_").slice(0, 80) || "telegram";
600621
601354
  }
@@ -600623,12 +601356,12 @@ function daydreamRoot(repoRoot) {
600623
601356
  return join121(repoRoot, ".omnius", "telegram-daydreams");
600624
601357
  }
600625
601358
  function sessionDir(repoRoot, sessionKey) {
600626
- const hash = createHash20("sha1").update(sessionKey).digest("hex").slice(0, 20);
601359
+ const hash = createHash21("sha1").update(sessionKey).digest("hex").slice(0, 20);
600627
601360
  return join121(daydreamRoot(repoRoot), safeFilePart(hash));
600628
601361
  }
600629
601362
  function compactLine2(value2, max = 220) {
600630
- const clean3 = value2.replace(/\s+/g, " ").trim();
600631
- return clean3.length > max ? `${clean3.slice(0, Math.max(0, max - 3)).trimEnd()}...` : clean3;
601363
+ const clean5 = value2.replace(/\s+/g, " ").trim();
601364
+ return clean5.length > max ? `${clean5.slice(0, Math.max(0, max - 3)).trimEnd()}...` : clean5;
600632
601365
  }
600633
601366
  function isoFromMs(value2) {
600634
601367
  return value2 ? new Date(value2).toISOString() : void 0;
@@ -600698,7 +601431,7 @@ function buildReplyOpportunities(input, openQuestions) {
600698
601431
  }
600699
601432
  return opportunities;
600700
601433
  }
600701
- function clamp017(value2) {
601434
+ function clamp018(value2) {
600702
601435
  if (!Number.isFinite(value2)) return 0;
600703
601436
  return Math.max(0, Math.min(1, value2));
600704
601437
  }
@@ -600709,7 +601442,7 @@ function pushStimulationSignal(signals, signal, source, weight) {
600709
601442
  const cleanSignal = compactLine2(signal, 120);
600710
601443
  const cleanSource = compactLine2(source, 180);
600711
601444
  if (!cleanSignal || signals.some((entry) => entry.signal === cleanSignal && entry.source === cleanSource)) return;
600712
- signals.push({ signal: cleanSignal, source: cleanSource, weight: clamp017(weight) });
601445
+ signals.push({ signal: cleanSignal, source: cleanSource, weight: clamp018(weight) });
600713
601446
  }
600714
601447
  function buildMetaAnalysisSignals(input) {
600715
601448
  const chatLabel = input.chatTitle || input.chatId;
@@ -600784,7 +601517,7 @@ function buildCuriosityThreads(input, openQuestions, stimulationSignals) {
600784
601517
  question: text.endsWith("?") || text.endsWith("?") ? text : `What should be learned or clarified from: ${text || entry.mediaSummary || "recent media"}?`,
600785
601518
  rationale: "Human curiosity, uncertainty, or multimodal content makes this a useful idle exploration target.",
600786
601519
  sourceMessages: messageId,
600787
- intensity: clamp017(0.5 + replyBoost + mediaBoost + questionBoost)
601520
+ intensity: clamp018(0.5 + replyBoost + mediaBoost + questionBoost)
600788
601521
  });
600789
601522
  }
600790
601523
  for (const question of openQuestions.slice(-4)) {
@@ -600804,7 +601537,7 @@ function buildCuriosityThreads(input, openQuestions, stimulationSignals) {
600804
601537
  question: `Is there a useful clarification or memory consolidation around ${strongest.source}?`,
600805
601538
  rationale: "Strongest stimulation signal can seed a low-intrusion reflection target.",
600806
601539
  sourceMessages: [],
600807
- intensity: clamp017(strongest.weight * 0.72)
601540
+ intensity: clamp018(strongest.weight * 0.72)
600808
601541
  });
600809
601542
  }
600810
601543
  return threads.sort((a2, b) => b.intensity - a2.intensity).slice(0, 8);
@@ -600864,8 +601597,8 @@ function participantForThread(input, thread) {
600864
601597
  return input.participants.find((participant) => participant.username === sourceMessage.username);
600865
601598
  }
600866
601599
  if (sourceMessage?.speaker) {
600867
- const clean3 = sourceMessage.speaker.replace(/^@/, "");
600868
- return input.participants.find((participant) => participant.username === clean3 || participant.firstName === sourceMessage.speaker);
601600
+ const clean5 = sourceMessage.speaker.replace(/^@/, "");
601601
+ return input.participants.find((participant) => participant.username === clean5 || participant.firstName === sourceMessage.speaker);
600869
601602
  }
600870
601603
  return topParticipants(input)[0];
600871
601604
  }
@@ -600878,7 +601611,7 @@ function buildOutreachPlans(input, curiosityThreads) {
600878
601611
  purpose: "Continue the public thread only when the live model judges that the group would benefit from a concise follow-up.",
600879
601612
  draftIntent: "Ask one concrete clarification, offer one useful synthesis, or stay silent if the room has moved on.",
600880
601613
  gate: "model_decision",
600881
- confidence: clamp017(thread.intensity * 0.86)
601614
+ confidence: clamp018(thread.intensity * 0.86)
600882
601615
  });
600883
601616
  const participant = participantForThread(input, thread);
600884
601617
  if (!participant) continue;
@@ -600890,7 +601623,7 @@ function buildOutreachPlans(input, curiosityThreads) {
600890
601623
  purpose: "Offer a one-to-one follow-up only if private contact is allowed and the issue is personal, unresolved, or better handled outside the group.",
600891
601624
  draftIntent: "Reference the public thread briefly, ask permission to continue privately, and do not reveal hidden meta-analysis.",
600892
601625
  gate: "admin_review",
600893
- confidence: clamp017(thread.intensity * 0.58)
601626
+ confidence: clamp018(thread.intensity * 0.58)
600894
601627
  });
600895
601628
  }
600896
601629
  return plans.slice(0, 8);
@@ -600941,7 +601674,48 @@ function buildMemoryProposals(input) {
600941
601674
  }
600942
601675
  return proposals.slice(0, 6);
600943
601676
  }
600944
- function buildTelegramChannelDaydream(input) {
601677
+ function corpusPacket(corpus) {
601678
+ return {
601679
+ query: corpus?.query,
601680
+ selectionSeed: corpus?.selectionSeed,
601681
+ stats: corpus?.stats,
601682
+ fallbacks: corpus?.fallbacks ?? [],
601683
+ episodeIds: corpus?.episodeIds ?? [],
601684
+ candidateEpisodeIds: corpus?.candidateEpisodeIds ?? [],
601685
+ selectedEpisodeIds: corpus?.selectedEpisodeIds ?? []
601686
+ };
601687
+ }
601688
+ function selectedSeedPacket(corpus) {
601689
+ const seed = corpus?.graphWalk.seed;
601690
+ if (!seed) return void 0;
601691
+ return {
601692
+ nodeId: seed.node.id,
601693
+ nodeText: seed.node.text,
601694
+ nodeType: seed.node.nodeType,
601695
+ degree: seed.degree,
601696
+ score: seed.score,
601697
+ sourceEpisodeIds: seed.sourceEpisodeIds
601698
+ };
601699
+ }
601700
+ function graphWalkPacket(corpus) {
601701
+ return {
601702
+ visitedNodes: corpus?.graphWalk.visitedNodes.map((node) => ({
601703
+ id: node.id,
601704
+ text: node.text,
601705
+ nodeType: node.nodeType,
601706
+ depth: corpus.graphWalk.depthByNodeId[node.id] ?? 0
601707
+ })) ?? [],
601708
+ traversedEdges: corpus?.graphWalk.traversedEdges.map((edge) => ({
601709
+ id: edge.id,
601710
+ relation: edge.relation,
601711
+ fact: edge.fact,
601712
+ sourceEpisodeId: edge.sourceEpisodeId,
601713
+ confidence: edge.confidence
601714
+ })) ?? [],
601715
+ sourceEpisodeIds: corpus?.graphWalk.sourceEpisodeIds ?? []
601716
+ };
601717
+ }
601718
+ function buildTelegramChannelDaydream(input, corpus, extraction, extractionCommit) {
600945
601719
  const firstTs = input.history.map((entry) => entry.ts).find((ts) => typeof ts === "number");
600946
601720
  const lastTs = [...input.history].reverse().map((entry) => entry.ts).find((ts) => typeof ts === "number");
600947
601721
  const metaAnalysisSignals = buildMetaAnalysisSignals(input);
@@ -600974,8 +601748,8 @@ function buildTelegramChannelDaydream(input) {
600974
601748
  ].filter((entry) => Boolean(entry));
600975
601749
  const seed = `${input.sessionKey}:${input.generatedAtMs}:${input.history.length}`;
600976
601750
  return {
600977
- version: 2,
600978
- id: createHash20("sha1").update(seed).digest("hex").slice(0, 16),
601751
+ version: 3,
601752
+ id: createHash21("sha1").update(seed).digest("hex").slice(0, 16),
600979
601753
  sessionKey: input.sessionKey,
600980
601754
  chatId: input.chatId,
600981
601755
  chatTitle: input.chatTitle,
@@ -600999,7 +601773,17 @@ function buildTelegramChannelDaydream(input) {
600999
601773
  memoryProposals,
601000
601774
  artifactProposals,
601001
601775
  stimulationContext: input.stimulationContext,
601002
- personaContext: input.personaContext
601776
+ personaContext: input.personaContext,
601777
+ corpus: corpusPacket(corpus),
601778
+ selectedSeed: selectedSeedPacket(corpus),
601779
+ graphWalk: graphWalkPacket(corpus),
601780
+ tagging: extraction?.tags ?? [],
601781
+ summation: extraction?.summaries ?? [],
601782
+ titling: extraction?.titles ?? [],
601783
+ extraction: extraction?.extractions ?? [],
601784
+ linking: extraction?.links ?? [],
601785
+ extractionFollowups: extraction?.followups ?? [],
601786
+ extractionCommit
601003
601787
  };
601004
601788
  }
601005
601789
  function formatTelegramChannelDaydreamMarkdown(artifact) {
@@ -601010,6 +601794,41 @@ function formatTelegramChannelDaydreamMarkdown(artifact) {
601010
601794
  `Generated: ${artifact.generatedAt}`,
601011
601795
  `Session: ${artifact.sessionKey}`,
601012
601796
  `Chat: ${artifact.chatTitle || artifact.chatId} (${artifact.chatType})`,
601797
+ "",
601798
+ "## Reflection Corpus",
601799
+ artifact.corpus.stats ? [
601800
+ `- retained messages: ${artifact.corpus.stats.retainedMessages}`,
601801
+ `- candidate episodes: ${artifact.corpus.stats.candidateEpisodes}`,
601802
+ `- selected episodes: ${artifact.corpus.stats.selectedEpisodes}`,
601803
+ `- vector search limit: ${artifact.corpus.stats.vectorSearchLimitUsed}`,
601804
+ `- graph walk: ${artifact.corpus.stats.graphNodesVisited} node(s), ${artifact.corpus.stats.graphEdgesTraversed} edge(s)`,
601805
+ artifact.corpus.fallbacks.length ? `- fallbacks: ${artifact.corpus.fallbacks.join("; ")}` : "- fallbacks: none"
601806
+ ].join("\n") : "- Not available",
601807
+ artifact.selectedSeed ? `
601808
+ Selected seed: ${artifact.selectedSeed.nodeText} (${artifact.selectedSeed.nodeType}, degree ${artifact.selectedSeed.degree})` : "",
601809
+ "",
601810
+ "## Tags",
601811
+ artifact.tagging.length ? artifact.tagging.map((item) => `- ${item.label} [${item.kind}] confidence=${item.confidence.toFixed(2)}${item.sourceMessageIds.length ? ` messages=${item.sourceMessageIds.join(", ")}` : ""}`).join("\n") : "- None",
601812
+ "",
601813
+ "## Summaries",
601814
+ artifact.summation.length ? artifact.summation.map((item) => `- ${item.title} [${item.scope}] confidence=${item.confidence.toFixed(2)}
601815
+ ${item.text}${item.sourceMessageIds.length ? `
601816
+ messages=${item.sourceMessageIds.join(", ")}` : ""}`).join("\n") : "- None",
601817
+ "",
601818
+ "## Titles",
601819
+ artifact.titling.length ? artifact.titling.map((item) => `- ${item.title} -> ${item.target} confidence=${item.confidence.toFixed(2)}`).join("\n") : "- None",
601820
+ "",
601821
+ "## Extractions",
601822
+ artifact.extraction.length ? artifact.extraction.map((item) => `- ${item.kind}: ${item.text} confidence=${item.confidence.toFixed(2)}${item.sourceMessageIds.length ? ` messages=${item.sourceMessageIds.join(", ")}` : ""}`).join("\n") : "- None",
601823
+ "",
601824
+ "## Links",
601825
+ artifact.linking.length ? artifact.linking.map((item) => `- ${item.srcNodeText} --${item.relation}--> ${item.dstNodeText} confidence=${item.confidence.toFixed(2)}
601826
+ ${item.fact}${item.sourceMessageIds.length ? `
601827
+ messages=${item.sourceMessageIds.join(", ")}` : ""}`).join("\n") : "- None",
601828
+ "",
601829
+ "## Extraction Followups",
601830
+ artifact.extractionFollowups.length ? artifact.extractionFollowups.map((item) => `- ${item.target}${item.replyToMessageId ? ` reply_to=${item.replyToMessageId}` : ""} confidence=${item.confidence.toFixed(2)}
601831
+ ${item.text || item.rationale}`).join("\n") : "- None",
601013
601832
  "",
601014
601833
  section("Meta-Analysis Signals", artifact.metaAnalysisSignals),
601015
601834
  "",
@@ -601099,6 +601918,18 @@ function formatTelegramChannelDaydreamContext(artifact) {
601099
601918
  "### Telegram Channel Daydream",
601100
601919
  "Private idle reflection and stimulation artifact. Use it as context only; do not mention it unless the user asks about internal memory.",
601101
601920
  `Generated: ${artifact.generatedAt}`,
601921
+ artifact.corpus?.stats ? `Corpus: ${artifact.corpus.stats.selectedEpisodes} selected episode(s), ${artifact.corpus.stats.graphNodesVisited} graph node(s), search limit ${artifact.corpus.stats.vectorSearchLimitUsed}` : "",
601922
+ artifact.selectedSeed ? `Selected seed: ${artifact.selectedSeed.nodeText} (degree ${artifact.selectedSeed.degree})` : "",
601923
+ artifact.tagging?.length ? `Tags:
601924
+ ${artifact.tagging.slice(0, 8).map((item) => `- ${item.label} [${item.kind}] (${item.confidence.toFixed(2)})`).join("\n")}` : "",
601925
+ artifact.summation?.length ? `Summaries:
601926
+ ${artifact.summation.slice(0, 4).map((item) => `- ${item.title}: ${item.text}`).join("\n")}` : "",
601927
+ artifact.extraction?.length ? `Extracted items:
601928
+ ${artifact.extraction.slice(0, 6).map((item) => `- ${item.kind}: ${item.text}${item.sourceMessageIds.length ? ` (messages ${item.sourceMessageIds.join(", ")})` : ""}`).join("\n")}` : "",
601929
+ artifact.linking?.length ? `Graph links:
601930
+ ${artifact.linking.slice(0, 5).map((item) => `- ${item.srcNodeText} --${item.relation}--> ${item.dstNodeText}`).join("\n")}` : "",
601931
+ artifact.extractionFollowups?.length ? `Extraction followups:
601932
+ ${artifact.extractionFollowups.slice(0, 4).map((item) => `- ${item.target}${item.replyToMessageId ? ` reply_to=${item.replyToMessageId}` : ""}: ${item.text || item.rationale}`).join("\n")}` : "",
601102
601933
  artifact.metaAnalysisSignals?.length ? `Meta-analysis signals:
601103
601934
  ${artifact.metaAnalysisSignals.slice(0, 4).map((line) => `- ${line}`).join("\n")}` : "",
601104
601935
  artifact.humanStimulationSignals?.length ? `Human stimulation signals:
@@ -601128,6 +601959,715 @@ var init_telegram_channel_dmn = __esm({
601128
601959
  }
601129
601960
  });
601130
601961
 
601962
+ // packages/cli/src/tui/telegram-reflection-corpus.ts
601963
+ import { createHash as createHash22 } from "node:crypto";
601964
+ function telegramReflectionMemoryDbPaths(repoRoot) {
601965
+ return omniusMemoryDbPaths(repoRoot);
601966
+ }
601967
+ function stableHash2(value2, length4 = 16) {
601968
+ return createHash22("sha1").update(value2).digest("hex").slice(0, length4);
601969
+ }
601970
+ function clean3(value2) {
601971
+ return String(value2 ?? "").replace(/\s+/g, " ").trim();
601972
+ }
601973
+ function compact(value2, max = 420) {
601974
+ const text = clean3(value2);
601975
+ return text.length > max ? `${text.slice(0, Math.max(0, max - 3)).trimEnd()}...` : text;
601976
+ }
601977
+ function senderLabel(entry) {
601978
+ if (entry.role === "assistant") return entry.speaker || "Assistant";
601979
+ if (entry.speaker) return entry.speaker;
601980
+ if (entry.username) return `@${entry.username}`;
601981
+ if (entry.firstName) return entry.firstName;
601982
+ if (entry.fromUserId) return `user:${entry.fromUserId}`;
601983
+ return "Telegram user";
601984
+ }
601985
+ function senderKey2(entry) {
601986
+ if (entry.role === "assistant") return entry.username || entry.speaker || "assistant";
601987
+ return String(entry.fromUserId || entry.username || entry.firstName || senderLabel(entry));
601988
+ }
601989
+ function scopeFor(entry, options2) {
601990
+ const chatType = entry.chatType || options2.chatType || "unknown";
601991
+ return {
601992
+ kind: `telegram-${chatType}`,
601993
+ id: String(entry.chatId ?? options2.chatId),
601994
+ title: entry.chatTitle || options2.chatTitle
601995
+ };
601996
+ }
601997
+ function senderFor(entry) {
601998
+ return {
601999
+ id: senderKey2(entry),
602000
+ username: entry.username,
602001
+ displayName: senderLabel(entry),
602002
+ isBot: entry.role === "assistant"
602003
+ };
602004
+ }
602005
+ function messageIdFor(entry, sessionKey) {
602006
+ if (typeof entry.messageId === "number" && Number.isFinite(entry.messageId)) return entry.messageId;
602007
+ return stableHash2(`${sessionKey}:${entry.role}:${entry.ts ?? ""}:${senderLabel(entry)}:${entry.text}`);
602008
+ }
602009
+ function messageRefFor(entry, sessionKey) {
602010
+ return {
602011
+ id: messageIdFor(entry, sessionKey),
602012
+ threadId: entry.messageThreadId,
602013
+ timestamp: entry.ts,
602014
+ text: entry.text
602015
+ };
602016
+ }
602017
+ function replyRefFor(entry) {
602018
+ const reply = entry.replyContext;
602019
+ const replyId = reply?.messageId ?? entry.replyToMessageId;
602020
+ if (replyId == null) return void 0;
602021
+ return {
602022
+ id: replyId,
602023
+ threadId: reply?.threadId,
602024
+ text: reply?.text || reply?.caption || reply?.mediaSummary,
602025
+ sender: reply?.sender ? {
602026
+ id: reply.sender.id == null ? void 0 : String(reply.sender.id),
602027
+ username: reply.sender.username,
602028
+ displayName: reply.sender.title || reply.sender.firstName || reply.sender.username,
602029
+ isBot: reply.sender.isBot
602030
+ } : void 0
602031
+ };
602032
+ }
602033
+ function contentFor(entry, sessionKey, options2) {
602034
+ const lines = [
602035
+ `Telegram ${entry.chatType || options2.chatType || "unknown"} ${entry.role} message`,
602036
+ `session: ${sessionKey}`,
602037
+ `chat: ${entry.chatTitle || options2.chatTitle || entry.chatId || options2.chatId}`,
602038
+ `message_id: ${messageIdFor(entry, sessionKey)}`,
602039
+ entry.messageThreadId != null ? `thread_id: ${entry.messageThreadId}` : "",
602040
+ entry.replyToMessageId != null ? `reply_to_message_id: ${entry.replyToMessageId}` : "",
602041
+ `speaker: ${senderLabel(entry)}`,
602042
+ entry.mode ? `mode: ${entry.mode}` : "",
602043
+ entry.mediaSummary ? `media: ${compact(entry.mediaSummary, 260)}` : "",
602044
+ "",
602045
+ entry.text
602046
+ ].filter((line) => line !== "");
602047
+ return lines.join("\n");
602048
+ }
602049
+ function metadataFor(entry, sessionKey, options2) {
602050
+ return {
602051
+ sourceSurface: "telegram",
602052
+ sessionKey,
602053
+ telegram: {
602054
+ role: entry.role,
602055
+ mode: entry.mode,
602056
+ chatId: String(entry.chatId ?? options2.chatId),
602057
+ chatType: entry.chatType || options2.chatType,
602058
+ chatTitle: entry.chatTitle || options2.chatTitle,
602059
+ messageId: entry.messageId,
602060
+ messageThreadId: entry.messageThreadId,
602061
+ replyToMessageId: entry.replyToMessageId,
602062
+ username: entry.username,
602063
+ firstName: entry.firstName,
602064
+ fromUserId: entry.fromUserId,
602065
+ speaker: senderLabel(entry),
602066
+ mediaSummary: entry.mediaSummary
602067
+ }
602068
+ };
602069
+ }
602070
+ function graphTopicNode(graph, label) {
602071
+ return graph.upsertNode({ text: `topic:${label}`, nodeType: "concept" });
602072
+ }
602073
+ function addTextEdges(graph, result, entry) {
602074
+ const messageId = result.nodeIds.message;
602075
+ const senderId = result.nodeIds.sender;
602076
+ if (messageId && senderId) {
602077
+ graph.addEdge({
602078
+ srcId: messageId,
602079
+ dstId: senderId,
602080
+ relation: "said_by",
602081
+ fact: compact(entry.text, 260),
602082
+ sourceEpisodeId: result.episodeId,
602083
+ modality: entry.role === "user" ? "social" : "text",
602084
+ confidence: entry.role === "assistant" ? 0.92 : 0.96
602085
+ });
602086
+ }
602087
+ if (messageId) {
602088
+ const channelTopic = graphTopicNode(graph, "telegram-reflection-corpus");
602089
+ graph.addEdge({
602090
+ srcId: messageId,
602091
+ dstId: channelTopic,
602092
+ relation: "related_to",
602093
+ fact: compact(entry.text || entry.mediaSummary || "telegram message", 220),
602094
+ sourceEpisodeId: result.episodeId,
602095
+ modality: entry.role === "user" ? "social" : "text",
602096
+ confidence: 0.72
602097
+ });
602098
+ }
602099
+ }
602100
+ function upsertTelegramReflectionMessage(repoRoot, sessionKey, entry, options2) {
602101
+ const paths = telegramReflectionMemoryDbPaths(repoRoot);
602102
+ const graph = new TemporalGraph(paths.knowledge);
602103
+ const store2 = new EpisodeStore(paths.episodes, graph);
602104
+ try {
602105
+ const before = store2.count(sessionKey);
602106
+ const service = new MultimodalIdentityService({ episodeStore: store2, graph });
602107
+ const content = contentFor(entry, sessionKey, options2);
602108
+ const result = service.ingest({
602109
+ sourceSurface: "telegram",
602110
+ sessionId: sessionKey,
602111
+ metadata: metadataFor(entry, sessionKey, options2),
602112
+ scope: scopeFor(entry, options2),
602113
+ sender: senderFor(entry),
602114
+ message: messageRefFor(entry, sessionKey),
602115
+ replyTo: replyRefFor(entry),
602116
+ modality: entry.role === "user" ? "social" : "text",
602117
+ content,
602118
+ labels: [entry.mode, entry.mediaSummary, senderLabel(entry)].filter((value2) => Boolean(value2))
602119
+ });
602120
+ addTextEdges(graph, result, entry);
602121
+ const after = store2.count(sessionKey);
602122
+ return { episodeId: result.episodeId, reused: after === before };
602123
+ } finally {
602124
+ store2.close();
602125
+ graph.close();
602126
+ }
602127
+ }
602128
+ function queryFor(options2) {
602129
+ if (options2.query?.trim()) return options2.query.trim();
602130
+ const recent = options2.history.slice(-12).map((entry) => compact(`${senderLabel(entry)}: ${entry.text || entry.mediaSummary || ""}`, 180));
602131
+ return [
602132
+ `Telegram ${options2.chatType} ${options2.chatTitle || options2.chatId} reflection`,
602133
+ ...recent
602134
+ ].filter(Boolean).join("\n");
602135
+ }
602136
+ async function fillMissingEmbeddings(store2, graph, episodes, embeddingConfig) {
602137
+ if (embeddingConfig === false) return { embedded: 0, unavailable: true, queryEmbedding: null };
602138
+ const missing = episodes.filter((episode) => !episode.embedding && episode.content.trim());
602139
+ if (missing.length === 0) return { embedded: 0, unavailable: false, queryEmbedding: null };
602140
+ const results = await generateEmbeddingBatch(missing.map((episode) => episode.content.slice(0, 4e3)), embeddingConfig);
602141
+ let embedded = 0;
602142
+ for (let i2 = 0; i2 < missing.length; i2++) {
602143
+ const result = results[i2];
602144
+ const episode = missing[i2];
602145
+ if (!result || !episode) continue;
602146
+ store2.setEmbedding(episode.id, result.vector);
602147
+ const refreshed = store2.get(episode.id);
602148
+ if (refreshed) linkEpisode(refreshed, store2, graph);
602149
+ embedded++;
602150
+ }
602151
+ return { embedded, unavailable: embedded === 0 && missing.length > 0, queryEmbedding: null };
602152
+ }
602153
+ async function queryEmbeddingFor(query, embeddingConfig) {
602154
+ if (embeddingConfig === false) return null;
602155
+ const result = await generateEmbedding(query.slice(0, 4e3), embeddingConfig);
602156
+ return result?.vector ?? null;
602157
+ }
602158
+ async function buildTelegramReflectionCorpus(options2) {
602159
+ const paths = telegramReflectionMemoryDbPaths(options2.repoRoot);
602160
+ const graph = new TemporalGraph(paths.knowledge);
602161
+ const store2 = new EpisodeStore(paths.episodes, graph);
602162
+ const episodeIds = [];
602163
+ let reusedEpisodes = 0;
602164
+ const fallbacks = [];
602165
+ try {
602166
+ const service = new MultimodalIdentityService({ episodeStore: store2, graph });
602167
+ for (const entry of options2.history) {
602168
+ const before = store2.count(options2.sessionKey);
602169
+ const content = contentFor(entry, options2.sessionKey, options2);
602170
+ const result = service.ingest({
602171
+ sourceSurface: "telegram",
602172
+ sessionId: options2.sessionKey,
602173
+ metadata: metadataFor(entry, options2.sessionKey, options2),
602174
+ scope: scopeFor(entry, options2),
602175
+ sender: senderFor(entry),
602176
+ message: messageRefFor(entry, options2.sessionKey),
602177
+ replyTo: replyRefFor(entry),
602178
+ modality: entry.role === "user" ? "social" : "text",
602179
+ content,
602180
+ labels: [entry.mode, entry.mediaSummary, senderLabel(entry)].filter((value2) => Boolean(value2))
602181
+ });
602182
+ addTextEdges(graph, result, entry);
602183
+ const after = store2.count(options2.sessionKey);
602184
+ if (after === before) reusedEpisodes++;
602185
+ if (result.episodeId) episodeIds.push(result.episodeId);
602186
+ }
602187
+ const scopedEpisodes = store2.recent(500, options2.sessionKey).filter((episode) => {
602188
+ const meta = episode.metadata;
602189
+ return meta?.sourceSurface === "telegram" || meta?.telegram && typeof meta.telegram === "object";
602190
+ });
602191
+ const embeddingFill = await fillMissingEmbeddings(store2, graph, scopedEpisodes, options2.embeddingConfig);
602192
+ const query = queryFor(options2);
602193
+ const queryEmbedding = await queryEmbeddingFor(query, options2.embeddingConfig);
602194
+ if (embeddingFill.unavailable || !queryEmbedding) fallbacks.push("embedding unavailable or incomplete; lexical scoped search included");
602195
+ let graphWalk = null;
602196
+ let selectedEpisodes = [];
602197
+ let vectorSearchLimitUsed = 0;
602198
+ let candidateEpisodes = [];
602199
+ const limits = options2.candidateLimits ?? [48, 96, 192, 384, 500];
602200
+ for (const limit of limits) {
602201
+ vectorSearchLimitUsed = limit;
602202
+ candidateEpisodes = store2.search(
602203
+ {
602204
+ sessionId: options2.sessionKey,
602205
+ query,
602206
+ limit,
602207
+ metadataFilter: { sourceSurface: "telegram" }
602208
+ },
602209
+ {
602210
+ queryEmbedding,
602211
+ lexicalWeight: 1,
602212
+ embeddingWeight: queryEmbedding ? 1.8 : 0
602213
+ }
602214
+ );
602215
+ graphWalk = selectAndWalkGraphCandidate(
602216
+ graph,
602217
+ candidateEpisodes.map((episode, index) => ({ episode, score: limit - index })),
602218
+ {
602219
+ seed: `${options2.sessionKey}:${options2.generatedAtMs}:${limit}`,
602220
+ maxDepth: 2,
602221
+ maxVisitedNodes: 72,
602222
+ maxTraversedEdges: 180,
602223
+ maxSourceEpisodes: 100
602224
+ }
602225
+ );
602226
+ if (graphWalk.seed) {
602227
+ selectedEpisodes = graphWalk.sourceEpisodeIds.map((id) => store2.get(id)).filter((episode) => Boolean(episode));
602228
+ if (selectedEpisodes.length < (options2.minWalkEpisodes ?? 8)) {
602229
+ const deeper = selectAndWalkGraphCandidate(
602230
+ graph,
602231
+ candidateEpisodes.map((episode, index) => ({ episode, score: limit - index })),
602232
+ {
602233
+ seed: `${options2.sessionKey}:${options2.generatedAtMs}:${limit}:deep`,
602234
+ maxDepth: 3,
602235
+ maxVisitedNodes: 96,
602236
+ maxTraversedEdges: 240,
602237
+ maxSourceEpisodes: 120
602238
+ }
602239
+ );
602240
+ if (deeper.seed && deeper.sourceEpisodeIds.length >= graphWalk.sourceEpisodeIds.length) graphWalk = deeper;
602241
+ }
602242
+ selectedEpisodes = graphWalk.sourceEpisodeIds.map((id) => store2.get(id)).filter((episode) => Boolean(episode));
602243
+ break;
602244
+ }
602245
+ }
602246
+ if (!graphWalk) {
602247
+ graphWalk = selectAndWalkGraphCandidate(graph, [], { seed: `${options2.sessionKey}:${options2.generatedAtMs}:empty` });
602248
+ }
602249
+ if (!graphWalk.seed && graphWalk.fallbackReason) fallbacks.push(graphWalk.fallbackReason);
602250
+ if (selectedEpisodes.length === 0) {
602251
+ selectedEpisodes = candidateEpisodes.slice(0, Math.min(24, candidateEpisodes.length));
602252
+ if (selectedEpisodes.length > 0) fallbacks.push("selected recent scoped episodes because graph walk produced no source episode anchors");
602253
+ }
602254
+ return {
602255
+ stats: {
602256
+ retainedMessages: options2.history.length,
602257
+ upsertedEpisodes: episodeIds.length,
602258
+ reusedEpisodes,
602259
+ embeddedEpisodes: embeddingFill.embedded,
602260
+ candidateEpisodes: candidateEpisodes.length,
602261
+ selectedEpisodes: selectedEpisodes.length,
602262
+ graphNodesVisited: graphWalk.visitedNodes.length,
602263
+ graphEdgesTraversed: graphWalk.traversedEdges.length,
602264
+ vectorSearchLimitUsed,
602265
+ embeddingUnavailable: embeddingFill.unavailable || !queryEmbedding
602266
+ },
602267
+ episodeIds: [...new Set(episodeIds)],
602268
+ candidateEpisodeIds: candidateEpisodes.map((episode) => episode.id),
602269
+ selectedEpisodeIds: selectedEpisodes.map((episode) => episode.id),
602270
+ selectedEpisodes,
602271
+ graphWalk,
602272
+ selectionSeed: `${options2.sessionKey}:${options2.generatedAtMs}:${vectorSearchLimitUsed}`,
602273
+ query,
602274
+ fallbacks
602275
+ };
602276
+ } finally {
602277
+ store2.close();
602278
+ graph.close();
602279
+ }
602280
+ }
602281
+ var init_telegram_reflection_corpus = __esm({
602282
+ "packages/cli/src/tui/telegram-reflection-corpus.ts"() {
602283
+ "use strict";
602284
+ init_dist7();
602285
+ init_memory_paths();
602286
+ }
602287
+ });
602288
+
602289
+ // packages/cli/src/tui/telegram-reflection-extraction.ts
602290
+ function clean4(value2, max = 2e3) {
602291
+ const text = String(value2 ?? "").replace(/\s+/g, " ").trim();
602292
+ return text.length > max ? `${text.slice(0, Math.max(0, max - 3)).trimEnd()}...` : text;
602293
+ }
602294
+ function clampConfidence(value2, fallback = 0.5) {
602295
+ if (typeof value2 !== "number" || !Number.isFinite(value2)) return fallback;
602296
+ return Math.max(0, Math.min(1, value2));
602297
+ }
602298
+ function numberArray(value2) {
602299
+ if (!Array.isArray(value2)) return [];
602300
+ return [...new Set(value2.map((item) => Number(item)).filter((item) => Number.isFinite(item)).map((item) => Math.trunc(item)))];
602301
+ }
602302
+ function stringArray(value2) {
602303
+ if (!Array.isArray(value2)) return [];
602304
+ return [...new Set(value2.map((item) => clean4(item, 180)).filter(Boolean))];
602305
+ }
602306
+ function episodeLine(episode) {
602307
+ const meta = episode.metadata;
602308
+ const telegram = meta?.telegram;
602309
+ const speaker = clean4(telegram?.speaker || telegram?.username || "unknown", 80);
602310
+ const messageId = telegram?.messageId == null ? "unknown" : String(telegram.messageId);
602311
+ const replyTo = telegram?.replyToMessageId == null ? "" : ` reply_to=${telegram.replyToMessageId}`;
602312
+ return [
602313
+ `episode_id=${episode.id}`,
602314
+ `message_id=${messageId}${replyTo}`,
602315
+ `speaker=${speaker}`,
602316
+ `modality=${episode.modality}`,
602317
+ `content=${clean4(episode.content, 700)}`
602318
+ ].join(" | ");
602319
+ }
602320
+ function buildTelegramReflectionExtractionPrompt(options2) {
602321
+ const corpus = options2.corpus;
602322
+ const walkNodes = corpus.graphWalk.visitedNodes.slice(0, 40).map((node) => `node_id=${node.id} type=${node.nodeType} mentions=${node.mentionCount} text=${clean4(node.text, 220)}`).join("\n");
602323
+ const walkEdges = corpus.graphWalk.traversedEdges.slice(0, 60).map((edge) => `edge_id=${edge.id} relation=${edge.relation} confidence=${edge.confidence.toFixed(2)} src=${edge.srcId} dst=${edge.dstId} source_episode=${edge.sourceEpisodeId || "none"} fact=${clean4(edge.fact, 220)}`).join("\n");
602324
+ const episodes = corpus.selectedEpisodes.slice(0, 36).map(episodeLine).join("\n");
602325
+ return [
602326
+ "You are extracting a reusable Telegram reflection artifact from a scoped chat corpus.",
602327
+ "Return strict JSON only. Do not include Markdown, prose, or comments.",
602328
+ "",
602329
+ "Required top-level JSON keys:",
602330
+ "artifact_title, tags, summaries, titles, extractions, links, followups",
602331
+ "",
602332
+ "Rules:",
602333
+ "- Use only the scoped Telegram corpus, graph nodes, graph edges, and source anchors below.",
602334
+ "- Preserve message_id and episode_id anchors on every item when possible.",
602335
+ "- Do not infer identity from a face, voice, or name unless the corpus explicitly says it.",
602336
+ "- Private DM followups may be proposed but must not be framed as already sent.",
602337
+ "- same_group followups must be concise, low-intrusion, and anchored to a source message id.",
602338
+ "- If a category has no evidence, return an empty array for that category.",
602339
+ "",
602340
+ "Schema:",
602341
+ JSON.stringify({
602342
+ artifact_title: "short title",
602343
+ tags: [{ label: "tag", kind: "topic|person|preference|task|media|tone", confidence: 0, source_message_ids: [1], target_episode_ids: ["episode-id"], target_node_ids: ["node-id"] }],
602344
+ summaries: [{ title: "summary title", scope: "channel|thread|participant|message_window", text: "summary text", confidence: 0, source_message_ids: [1], target_episode_ids: ["episode-id"] }],
602345
+ titles: [{ title: "usable title", target: "artifact|thread|memory_card|graph_node", confidence: 0, source_message_ids: [1], target_episode_ids: ["episode-id"] }],
602346
+ extractions: [{ kind: "fact|preference|open_question|decision|identity_candidate|media_reference|followup_need", text: "extracted item", confidence: 0, source_message_ids: [1], target_episode_ids: ["episode-id"], target_node_ids: ["node-id"] }],
602347
+ links: [{ relation: "related_to|contains|authored_by|replied_to|said_by|depicts|named_as|same_person_candidate", src_node_text: "node text", dst_node_text: "node text", confidence: 0, fact: "why linked", source_message_ids: [1], target_episode_ids: ["episode-id"] }],
602348
+ followups: [{ target: "same_group|private_dm|none", text: "candidate visible reply", reply_to_message_id: 1, rationale: "why", confidence: 0 }]
602349
+ }),
602350
+ "",
602351
+ `Chat: ${options2.chatTitle || options2.chatId} (${options2.chatType})`,
602352
+ `Session: ${options2.sessionKey}`,
602353
+ `Corpus query: ${clean4(corpus.query, 1e3)}`,
602354
+ `Corpus stats: ${JSON.stringify(corpus.stats)}`,
602355
+ `Selected seed: ${corpus.graphWalk.seed ? `${corpus.graphWalk.seed.node.text} degree=${corpus.graphWalk.seed.degree}` : "none"}`,
602356
+ corpus.fallbacks.length ? `Fallbacks: ${corpus.fallbacks.join("; ")}` : "Fallbacks: none",
602357
+ "",
602358
+ "Graph nodes:",
602359
+ walkNodes || "none",
602360
+ "",
602361
+ "Graph edges:",
602362
+ walkEdges || "none",
602363
+ "",
602364
+ "Source episodes:",
602365
+ episodes || "none"
602366
+ ].join("\n");
602367
+ }
602368
+ function parseJsonObject(raw) {
602369
+ const text = raw.trim();
602370
+ if (!text) return null;
602371
+ const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i)?.[1]?.trim();
602372
+ const candidate = fenced || text.slice(text.indexOf("{"), text.lastIndexOf("}") + 1);
602373
+ if (!candidate || !candidate.startsWith("{")) return null;
602374
+ try {
602375
+ const parsed = JSON.parse(candidate);
602376
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
602377
+ } catch {
602378
+ return null;
602379
+ }
602380
+ }
602381
+ function parseTelegramReflectionExtraction(raw) {
602382
+ const parsed = parseJsonObject(raw);
602383
+ if (!parsed) return null;
602384
+ const tags = Array.isArray(parsed.tags) ? parsed.tags.map((item) => {
602385
+ const obj = item;
602386
+ return {
602387
+ label: clean4(obj.label, 80),
602388
+ kind: clean4(obj.kind, 40) || "topic",
602389
+ confidence: clampConfidence(obj.confidence),
602390
+ sourceMessageIds: numberArray(obj.source_message_ids ?? obj.sourceMessageIds),
602391
+ targetEpisodeIds: stringArray(obj.target_episode_ids ?? obj.targetEpisodeIds),
602392
+ targetNodeIds: stringArray(obj.target_node_ids ?? obj.targetNodeIds)
602393
+ };
602394
+ }).filter((item) => item.label) : [];
602395
+ const summaries = Array.isArray(parsed.summaries) ? parsed.summaries.map((item) => {
602396
+ const obj = item;
602397
+ return {
602398
+ title: clean4(obj.title, 120),
602399
+ scope: clean4(obj.scope, 60) || "message_window",
602400
+ text: clean4(obj.text, 1200),
602401
+ confidence: clampConfidence(obj.confidence),
602402
+ sourceMessageIds: numberArray(obj.source_message_ids ?? obj.sourceMessageIds),
602403
+ targetEpisodeIds: stringArray(obj.target_episode_ids ?? obj.targetEpisodeIds)
602404
+ };
602405
+ }).filter((item) => item.title && item.text) : [];
602406
+ const titles = Array.isArray(parsed.titles) ? parsed.titles.map((item) => {
602407
+ const obj = item;
602408
+ return {
602409
+ title: clean4(obj.title, 120),
602410
+ target: clean4(obj.target, 60) || "artifact",
602411
+ confidence: clampConfidence(obj.confidence),
602412
+ sourceMessageIds: numberArray(obj.source_message_ids ?? obj.sourceMessageIds),
602413
+ targetEpisodeIds: stringArray(obj.target_episode_ids ?? obj.targetEpisodeIds)
602414
+ };
602415
+ }).filter((item) => item.title) : [];
602416
+ const extractions = Array.isArray(parsed.extractions) ? parsed.extractions.map((item) => {
602417
+ const obj = item;
602418
+ return {
602419
+ kind: clean4(obj.kind, 60) || "fact",
602420
+ text: clean4(obj.text, 1e3),
602421
+ confidence: clampConfidence(obj.confidence),
602422
+ sourceMessageIds: numberArray(obj.source_message_ids ?? obj.sourceMessageIds),
602423
+ targetEpisodeIds: stringArray(obj.target_episode_ids ?? obj.targetEpisodeIds),
602424
+ targetNodeIds: stringArray(obj.target_node_ids ?? obj.targetNodeIds)
602425
+ };
602426
+ }).filter((item) => item.text) : [];
602427
+ const links2 = Array.isArray(parsed.links) ? parsed.links.map((item) => {
602428
+ const obj = item;
602429
+ return {
602430
+ relation: clean4(obj.relation, 60) || "related_to",
602431
+ srcNodeText: clean4(obj.src_node_text ?? obj.srcNodeText, 180),
602432
+ dstNodeText: clean4(obj.dst_node_text ?? obj.dstNodeText, 180),
602433
+ confidence: clampConfidence(obj.confidence),
602434
+ fact: clean4(obj.fact, 500),
602435
+ sourceMessageIds: numberArray(obj.source_message_ids ?? obj.sourceMessageIds),
602436
+ targetEpisodeIds: stringArray(obj.target_episode_ids ?? obj.targetEpisodeIds)
602437
+ };
602438
+ }).filter((item) => item.srcNodeText && item.dstNodeText) : [];
602439
+ const followups = Array.isArray(parsed.followups) ? parsed.followups.map((item) => {
602440
+ const obj = item;
602441
+ const target = clean4(obj.target, 40);
602442
+ const replyTo = Number(obj.reply_to_message_id ?? obj.replyToMessageId);
602443
+ const normalizedTarget = target === "private_dm" ? "private_dm" : target === "same_group" ? "same_group" : "none";
602444
+ return {
602445
+ target: normalizedTarget,
602446
+ text: clean4(obj.text, 700),
602447
+ replyToMessageId: Number.isFinite(replyTo) ? Math.trunc(replyTo) : null,
602448
+ rationale: clean4(obj.rationale, 400),
602449
+ confidence: clampConfidence(obj.confidence)
602450
+ };
602451
+ }) : [];
602452
+ return {
602453
+ artifactTitle: clean4(parsed.artifact_title ?? parsed.artifactTitle, 140) || "Telegram reflection",
602454
+ tags,
602455
+ summaries,
602456
+ titles,
602457
+ extractions,
602458
+ links: links2,
602459
+ followups,
602460
+ raw
602461
+ };
602462
+ }
602463
+ async function runTelegramReflectionExtraction(options2) {
602464
+ const prompt = buildTelegramReflectionExtractionPrompt(options2);
602465
+ const result = await options2.backend.chatCompletion({
602466
+ messages: [
602467
+ { role: "system", content: "You produce strict JSON for Telegram reflection memory extraction." },
602468
+ { role: "user", content: prompt }
602469
+ ],
602470
+ tools: [],
602471
+ temperature: 0.2,
602472
+ maxTokens: 1800,
602473
+ timeoutMs: options2.timeoutMs,
602474
+ think: false
602475
+ });
602476
+ return parseTelegramReflectionExtraction(result.choices[0]?.message?.content ?? "");
602477
+ }
602478
+ function memoryDbPaths(repoRoot) {
602479
+ return omniusMemoryDbPaths(repoRoot);
602480
+ }
602481
+ function allowedRelation(value2) {
602482
+ const relations = [
602483
+ "caused_by",
602484
+ "discovered_during",
602485
+ "fixed_by",
602486
+ "produced_by",
602487
+ "co_occurred_with",
602488
+ "requires",
602489
+ "is_instance_of",
602490
+ "related_to",
602491
+ "contains",
602492
+ "modified_by",
602493
+ "said_by",
602494
+ "appears_in",
602495
+ "alias_of",
602496
+ "authored_by",
602497
+ "uploaded_by",
602498
+ "replied_to",
602499
+ "depicts",
602500
+ "named_as",
602501
+ "face_match",
602502
+ "voice_sample_of",
602503
+ "speaker_candidate",
602504
+ "same_person_candidate"
602505
+ ];
602506
+ return relations.includes(value2) ? value2 : "related_to";
602507
+ }
602508
+ function nodeText(prefix, value2) {
602509
+ return `${prefix}:${clean4(value2, 180).toLowerCase()}`;
602510
+ }
602511
+ function firstSourceEpisode(itemEpisodeIds, fallback) {
602512
+ return itemEpisodeIds[0] || fallback;
602513
+ }
602514
+ function commitTelegramReflectionExtraction(repoRoot, sessionKey, corpus, extraction) {
602515
+ const paths = memoryDbPaths(repoRoot);
602516
+ const graph = new TemporalGraph(paths.knowledge);
602517
+ const store2 = new EpisodeStore(paths.episodes, graph);
602518
+ try {
602519
+ const reflectionContent = [
602520
+ `Telegram reflection extraction: ${extraction.artifactTitle}`,
602521
+ `session: ${sessionKey}`,
602522
+ `seed: ${corpus.graphWalk.seed?.node.text || "none"}`,
602523
+ `tags: ${extraction.tags.map((tag) => tag.label).join(", ") || "none"}`,
602524
+ "",
602525
+ ...extraction.summaries.map((summary) => `${summary.title}: ${summary.text}`),
602526
+ ...extraction.extractions.map((item) => `${item.kind}: ${item.text}`)
602527
+ ].join("\n");
602528
+ const reflectionEpisodeId = store2.insert({
602529
+ sessionId: sessionKey,
602530
+ modality: "reflection",
602531
+ toolName: "telegram_reflection_extraction",
602532
+ content: reflectionContent,
602533
+ metadata: {
602534
+ sourceSurface: "telegram_reflection",
602535
+ sessionKey,
602536
+ corpusStats: corpus.stats,
602537
+ selectedEpisodeIds: corpus.selectedEpisodeIds,
602538
+ extraction: {
602539
+ artifactTitle: extraction.artifactTitle,
602540
+ tags: extraction.tags,
602541
+ summaries: extraction.summaries,
602542
+ titles: extraction.titles,
602543
+ extractions: extraction.extractions,
602544
+ links: extraction.links,
602545
+ followups: extraction.followups
602546
+ }
602547
+ },
602548
+ importance: 8,
602549
+ decayClass: "procedural"
602550
+ });
602551
+ const artifactNode = graph.upsertNode({ text: nodeText("telegram_reflection", extraction.artifactTitle), nodeType: "concept" });
602552
+ let graphNodes = 1;
602553
+ let graphEdges = 0;
602554
+ const summaryEpisodeIds = [];
602555
+ const seedNodeId = corpus.graphWalk.seed?.node.id;
602556
+ if (seedNodeId) {
602557
+ graph.addEdge({
602558
+ srcId: artifactNode,
602559
+ dstId: seedNodeId,
602560
+ relation: "related_to",
602561
+ fact: `Reflection seed for ${extraction.artifactTitle}`,
602562
+ sourceEpisodeId: reflectionEpisodeId,
602563
+ modality: "reflection",
602564
+ confidence: 0.9
602565
+ });
602566
+ graphEdges++;
602567
+ }
602568
+ for (const tag of extraction.tags) {
602569
+ const tagNode = graph.upsertNode({ text: nodeText("tag", tag.label), nodeType: "concept" });
602570
+ graphNodes++;
602571
+ graph.addEdge({
602572
+ srcId: artifactNode,
602573
+ dstId: tagNode,
602574
+ relation: "contains",
602575
+ fact: `${tag.kind}: ${tag.label}`,
602576
+ sourceEpisodeId: firstSourceEpisode(tag.targetEpisodeIds, reflectionEpisodeId),
602577
+ modality: "reflection",
602578
+ confidence: tag.confidence
602579
+ });
602580
+ graphEdges++;
602581
+ }
602582
+ for (const summary of extraction.summaries) {
602583
+ const episodeId = store2.insert({
602584
+ sessionId: sessionKey,
602585
+ modality: "gist",
602586
+ toolName: "telegram_reflection_summary",
602587
+ content: `${summary.title}
602588
+ ${summary.text}`,
602589
+ metadata: {
602590
+ sourceSurface: "telegram_reflection",
602591
+ sessionKey,
602592
+ scope: summary.scope,
602593
+ sourceMessageIds: summary.sourceMessageIds,
602594
+ targetEpisodeIds: summary.targetEpisodeIds
602595
+ },
602596
+ importance: Math.max(6, summary.confidence * 10),
602597
+ decayClass: "procedural",
602598
+ sourceEpisodeId: reflectionEpisodeId
602599
+ });
602600
+ summaryEpisodeIds.push(episodeId);
602601
+ const summaryNode = graph.upsertNode({ text: nodeText("summary", summary.title), nodeType: "concept" });
602602
+ graphNodes++;
602603
+ graph.addEdge({
602604
+ srcId: artifactNode,
602605
+ dstId: summaryNode,
602606
+ relation: "contains",
602607
+ fact: summary.text,
602608
+ sourceEpisodeId: episodeId,
602609
+ modality: "reflection",
602610
+ confidence: summary.confidence
602611
+ });
602612
+ graphEdges++;
602613
+ }
602614
+ for (const title of extraction.titles) {
602615
+ const titleNode = graph.upsertNode({ text: nodeText("title", title.title), nodeType: "concept" });
602616
+ graphNodes++;
602617
+ graph.addEdge({
602618
+ srcId: artifactNode,
602619
+ dstId: titleNode,
602620
+ relation: "contains",
602621
+ fact: `${title.target}: ${title.title}`,
602622
+ sourceEpisodeId: firstSourceEpisode(title.targetEpisodeIds, reflectionEpisodeId),
602623
+ modality: "reflection",
602624
+ confidence: title.confidence
602625
+ });
602626
+ graphEdges++;
602627
+ }
602628
+ for (const item of extraction.extractions) {
602629
+ const itemNode = graph.upsertNode({ text: nodeText(item.kind, item.text), nodeType: item.kind === "open_question" ? "event" : "concept" });
602630
+ graphNodes++;
602631
+ graph.addEdge({
602632
+ srcId: artifactNode,
602633
+ dstId: itemNode,
602634
+ relation: "contains",
602635
+ fact: item.text,
602636
+ sourceEpisodeId: firstSourceEpisode(item.targetEpisodeIds, reflectionEpisodeId),
602637
+ modality: "reflection",
602638
+ confidence: item.confidence
602639
+ });
602640
+ graphEdges++;
602641
+ }
602642
+ for (const link of extraction.links) {
602643
+ const src2 = graph.upsertNode({ text: clean4(link.srcNodeText, 180), nodeType: "concept" });
602644
+ const dst = graph.upsertNode({ text: clean4(link.dstNodeText, 180), nodeType: "concept" });
602645
+ graphNodes += 2;
602646
+ graph.addEdge({
602647
+ srcId: src2,
602648
+ dstId: dst,
602649
+ relation: allowedRelation(link.relation),
602650
+ fact: link.fact,
602651
+ sourceEpisodeId: firstSourceEpisode(link.targetEpisodeIds, reflectionEpisodeId),
602652
+ modality: "reflection",
602653
+ confidence: link.confidence
602654
+ });
602655
+ graphEdges++;
602656
+ }
602657
+ return { reflectionEpisodeId, summaryEpisodeIds, graphNodes, graphEdges };
602658
+ } finally {
602659
+ store2.close();
602660
+ graph.close();
602661
+ }
602662
+ }
602663
+ var init_telegram_reflection_extraction = __esm({
602664
+ "packages/cli/src/tui/telegram-reflection-extraction.ts"() {
602665
+ "use strict";
602666
+ init_dist7();
602667
+ init_memory_paths();
602668
+ }
602669
+ });
602670
+
601131
602671
  // packages/cli/src/tui/vision-ingress.ts
601132
602672
  var vision_ingress_exports = {};
601133
602673
  __export(vision_ingress_exports, {
@@ -601283,9 +602823,9 @@ var init_vision_ingress = __esm({
601283
602823
 
601284
602824
  // packages/cli/src/tui/telegram-bridge.ts
601285
602825
  import { mkdirSync as mkdirSync63, existsSync as existsSync108, unlinkSync as unlinkSync21, readdirSync as readdirSync37, statSync as statSync36, statfsSync as statfsSync3, readFileSync as readFileSync88, writeFileSync as writeFileSync57 } from "node:fs";
601286
- import { join as join123, resolve as resolve41, basename as basename26, relative as relative13, isAbsolute as isAbsolute7, extname as extname16 } from "node:path";
602826
+ import { join as join123, resolve as resolve41, basename as basename27, relative as relative13, isAbsolute as isAbsolute7, extname as extname16 } from "node:path";
601287
602827
  import { writeFile as writeFileAsync } from "node:fs/promises";
601288
- import { createHash as createHash21, randomInt } from "node:crypto";
602828
+ import { createHash as createHash23, randomInt } from "node:crypto";
601289
602829
  function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
601290
602830
  const cleaned = stripTelegramHiddenThinking(text).replace(/```(?:json)?/gi, "").replace(/```/g, "").trim();
601291
602831
  const jsonText = cleaned.startsWith("{") ? cleaned : cleaned.match(/\{[\s\S]*\}/)?.[0] ?? "";
@@ -601321,6 +602861,29 @@ function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
601321
602861
  return null;
601322
602862
  }
601323
602863
  }
602864
+ function parseTelegramReflectionFollowupDecision(text) {
602865
+ const cleaned = stripTelegramHiddenThinking(text).replace(/```(?:json)?/gi, "").replace(/```/g, "").trim();
602866
+ const jsonText = cleaned.startsWith("{") ? cleaned : cleaned.match(/\{[\s\S]*\}/)?.[0] ?? "";
602867
+ if (!jsonText) return null;
602868
+ try {
602869
+ const parsed = JSON.parse(jsonText);
602870
+ const shouldSendRaw = parsed["should_send"] ?? parsed["shouldSend"];
602871
+ const shouldSend = shouldSendRaw === true;
602872
+ const confidenceRaw = Number(parsed["confidence"]);
602873
+ const confidence2 = Number.isFinite(confidenceRaw) ? Math.max(0, Math.min(1, confidenceRaw)) : 0;
602874
+ const replyRaw = Number(parsed["reply_to_message_id"] ?? parsed["replyToMessageId"]);
602875
+ const replyToMessageId = Number.isFinite(replyRaw) && replyRaw > 0 ? Math.trunc(replyRaw) : void 0;
602876
+ return {
602877
+ shouldSend,
602878
+ text: String(parsed["text"] ?? "").trim().slice(0, 900),
602879
+ reason: String(parsed["reason"] ?? (shouldSend ? "model selected a scoped follow-up" : "model chose silence")).trim().slice(0, 240),
602880
+ confidence: confidence2,
602881
+ replyToMessageId
602882
+ };
602883
+ } catch {
602884
+ return null;
602885
+ }
602886
+ }
601324
602887
  function convertMarkdownToTelegramHTML(md) {
601325
602888
  let html = md;
601326
602889
  html = html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -601365,8 +602928,8 @@ function stripTelegramHiddenThinking(text) {
601365
602928
  return withoutClosedThink.replace(/<think>[\s\S]*$/gi, "");
601366
602929
  }
601367
602930
  function sanitizeTelegramProgressText(text, maxLength) {
601368
- const compact = stripTelegramHiddenThinking(text).replace(/\s+/g, " ").trim();
601369
- return compact.length > maxLength ? compact.slice(0, Math.max(0, maxLength - 3)) + "..." : compact;
602931
+ const compact2 = stripTelegramHiddenThinking(text).replace(/\s+/g, " ").trim();
602932
+ return compact2.length > maxLength ? compact2.slice(0, Math.max(0, maxLength - 3)) + "..." : compact2;
601370
602933
  }
601371
602934
  function isCodebaseMemoryStatus(text) {
601372
602935
  return /^\s*\[CODEBASE MEMORY\]/i.test(stripTelegramHiddenThinking(text));
@@ -601408,40 +602971,40 @@ function isTelegramNoReplySentinel(text) {
601408
602971
  return lower === "no_reply" || lower.startsWith("no_reply");
601409
602972
  }
601410
602973
  function isTelegramInternalStatusText(text) {
601411
- const compact = compactTelegramVisibleText(text);
601412
- if (!compact) return false;
601413
- const lower = compact.toLowerCase();
601414
- if (isTelegramNoReplySentinel(compact)) return true;
602974
+ const compact2 = compactTelegramVisibleText(text);
602975
+ if (!compact2) return false;
602976
+ const lower = compact2.toLowerCase();
602977
+ if (isTelegramNoReplySentinel(compact2)) return true;
601415
602978
  if (lower === "complete" || lower === "completed") return true;
601416
- if (/^memory stage:/i.test(compact)) return true;
601417
- if (/^\[ppr[-_\s]?skip\]/i.test(compact)) return true;
601418
- if (/^(casual|ambient|group)\b.{0,180}\b(skipping|skipped|not directed|no action needed|no reply)\b/i.test(compact)) return true;
601419
- if (/^no further action needed\b/i.test(compact)) return true;
601420
- if (/^no action needed\b.{0,120}\b(task|complete|completed|done)\b/i.test(compact)) return true;
601421
- if (/^(there'?s|there is) no active task\b/i.test(compact)) return true;
601422
- if (/^everything'?s (done|complete|completed|wrapped up)\b/i.test(compact)) return true;
601423
- if (/\balready (been )?(provided|answered|handled|delivered) above\b/i.test(compact)) return true;
601424
- if (/\b(no remaining work|nothing left to do|task is complete|task has been completed)\b/i.test(compact)) return true;
602979
+ if (/^memory stage:/i.test(compact2)) return true;
602980
+ if (/^\[ppr[-_\s]?skip\]/i.test(compact2)) return true;
602981
+ if (/^(casual|ambient|group)\b.{0,180}\b(skipping|skipped|not directed|no action needed|no reply)\b/i.test(compact2)) return true;
602982
+ if (/^no further action needed\b/i.test(compact2)) return true;
602983
+ if (/^no action needed\b.{0,120}\b(task|complete|completed|done)\b/i.test(compact2)) return true;
602984
+ if (/^(there'?s|there is) no active task\b/i.test(compact2)) return true;
602985
+ if (/^everything'?s (done|complete|completed|wrapped up)\b/i.test(compact2)) return true;
602986
+ if (/\balready (been )?(provided|answered|handled|delivered) above\b/i.test(compact2)) return true;
602987
+ if (/\b(no remaining work|nothing left to do|task is complete|task has been completed)\b/i.test(compact2)) return true;
601425
602988
  return false;
601426
602989
  }
601427
602990
  function cleanTelegramVisibleReply(text, options2 = {}) {
601428
- const clean3 = stripTelegramHiddenThinking(text).trim();
601429
- if (!clean3) return "";
601430
- if (options2.suppressPotentialNoReplyPrefix && isTelegramPotentialNoReplyPrefix(clean3)) return "";
601431
- if (isTelegramInternalStatusText(clean3)) return "";
601432
- return dedupeTelegramVisibleReply(clean3);
602991
+ const clean5 = stripTelegramHiddenThinking(text).trim();
602992
+ if (!clean5) return "";
602993
+ if (options2.suppressPotentialNoReplyPrefix && isTelegramPotentialNoReplyPrefix(clean5)) return "";
602994
+ if (isTelegramInternalStatusText(clean5)) return "";
602995
+ return dedupeTelegramVisibleReply(clean5);
601433
602996
  }
601434
602997
  function dedupeTelegramVisibleReply(text) {
601435
602998
  const paragraphs = text.split(/\n{2,}/);
601436
602999
  const seenParagraphs = /* @__PURE__ */ new Set();
601437
603000
  const collapsedParagraphs = [];
601438
603001
  for (const paragraph of paragraphs) {
601439
- const clean3 = paragraph.trim();
601440
- if (!clean3) continue;
601441
- const key = compactTelegramVisibleText(clean3).toLowerCase();
603002
+ const clean5 = paragraph.trim();
603003
+ if (!clean5) continue;
603004
+ const key = compactTelegramVisibleText(clean5).toLowerCase();
601442
603005
  if (seenParagraphs.has(key)) continue;
601443
603006
  seenParagraphs.add(key);
601444
- collapsedParagraphs.push(clean3);
603007
+ collapsedParagraphs.push(clean5);
601445
603008
  }
601446
603009
  const paragraphCollapsed = collapsedParagraphs.join("\n\n");
601447
603010
  const sentenceLike = paragraphCollapsed.match(/[^.!?]+[.!?]+|[^.!?]+$/g);
@@ -601471,9 +603034,9 @@ function truncateTelegramContext(text, maxLength) {
601471
603034
  [Telegram context truncated; use tools for full detail.]`;
601472
603035
  }
601473
603036
  function truncateTelegramContextLine(text, maxLength = TELEGRAM_CONTEXT_LINE_LIMIT) {
601474
- const compact = stripTelegramHiddenThinking(text).replace(/\s+/g, " ").trim();
601475
- if (compact.length <= maxLength) return compact;
601476
- return `${compact.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;
603037
+ const compact2 = stripTelegramHiddenThinking(text).replace(/\s+/g, " ").trim();
603038
+ if (compact2.length <= maxLength) return compact2;
603039
+ return `${compact2.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;
601477
603040
  }
601478
603041
  function telegramSpeakerLabel(msg) {
601479
603042
  if (msg.username && msg.username !== "unknown") return `@${msg.username}`;
@@ -601620,7 +603183,7 @@ function buildTelegramRuntimeContext(now = /* @__PURE__ */ new Date(), repoRoot)
601620
603183
  ].filter(Boolean).join("\n");
601621
603184
  }
601622
603185
  function telegramSessionIdFromKey(sessionKey) {
601623
- return `telegram-${createHash21("sha1").update(sessionKey).digest("hex").slice(0, 16)}`;
603186
+ return `telegram-${createHash23("sha1").update(sessionKey).digest("hex").slice(0, 16)}`;
601624
603187
  }
601625
603188
  function selectTelegramFinalResponse(args) {
601626
603189
  const committedVisibleReply = cleanTelegramVisibleReply(args.visibleReplyText || "");
@@ -601695,10 +603258,17 @@ function telegramSyntheticHelpSignatures() {
601695
603258
  { signature: "/help", description: "Show Telegram command help" },
601696
603259
  { signature: "/start", description: "Show Telegram bridge status and authentication instructions" },
601697
603260
  { signature: "/auth <code>", description: "Authenticate this Telegram user as bot admin using the TUI code" },
601698
- { signature: "/call", description: "Get the active voice call link when a call session is running" }
603261
+ { signature: "/call", description: "Get the active voice call link when a call session is running" },
603262
+ { signature: "/reflect", description: "Run scoped Telegram chat reflection over retained chat history" },
603263
+ { signature: "/reflect status", description: "Show the latest scoped Telegram reflection artifact" },
603264
+ { signature: "/reflect now", description: "Run reflection and let the model decide whether to post a public follow-up" },
603265
+ { signature: "/reflect auto on|off", description: "Enable or disable model-gated idle follow-ups for this chat" },
603266
+ { signature: "/reflection", description: "Alias for /reflect" },
603267
+ { signature: "/daydream", description: "Alias for /reflect in Telegram chats" }
601699
603268
  ];
601700
603269
  }
601701
603270
  function telegramHelpCommandAllowed(cmd, scope) {
603271
+ if (cmd.name === "dream") return false;
601702
603272
  if (scope === "admin") return cmd.implementationStatus === "implemented";
601703
603273
  if (TELEGRAM_PUBLIC_HELP_COMMANDS.has(cmd.name)) return true;
601704
603274
  return cmd.surfaces.agentTool && !cmd.safety.secretBearing && !cmd.safety.destructive && !cmd.safety.profileGated;
@@ -601729,6 +603299,65 @@ function buildTelegramHelpHTML(scope, maxPublicCommands = 24) {
601729
603299
  }
601730
603300
  return lines.join("\n");
601731
603301
  }
603302
+ function formatTelegramReflectionSummaryHTML(artifact, opts = { autoFollowup: false }) {
603303
+ const topThreads = artifact.curiosityThreads.slice(0, 3);
603304
+ const topReplies = artifact.replyOpportunities.slice(0, 3);
603305
+ const groupPlans = artifact.outreachPlans.filter((plan) => plan.target === "same_group").slice(0, 2);
603306
+ const topTags = (artifact.tagging ?? []).slice(0, 6);
603307
+ const topSummaries = (artifact.summation ?? []).slice(0, 3);
603308
+ const topExtractions = (artifact.extraction ?? []).slice(0, 4);
603309
+ const topLinks = (artifact.linking ?? []).slice(0, 4);
603310
+ const lines = [
603311
+ `<b>Telegram reflection</b>`,
603312
+ `Scope: <code>${escapeTelegramHTML(artifact.chatTitle || artifact.chatId)}</code> (${escapeTelegramHTML(artifact.chatType)})`,
603313
+ `Window: ${artifact.messageWindow.totalRetained} retained messages`,
603314
+ artifact.corpus?.stats ? `Corpus: ${artifact.corpus.stats.selectedEpisodes} selected episodes, ${artifact.corpus.stats.graphNodesVisited} graph nodes, ${artifact.corpus.stats.graphEdgesTraversed} graph edges` : "",
603315
+ artifact.selectedSeed ? `Seed: <code>${escapeTelegramHTML(artifact.selectedSeed.nodeText)}</code>` : "",
603316
+ `Auto follow-up: <b>${opts.autoFollowup ? "on" : "off"}</b>`,
603317
+ opts.sentFollowup ? `Follow-up: <b>sent</b>` : opts.followupReason ? `Follow-up: ${escapeTelegramHTML(opts.followupReason)}` : "",
603318
+ "",
603319
+ topTags.length ? `<b>Tags</b>` : "",
603320
+ ...topTags.map(
603321
+ (tag) => `- ${escapeTelegramHTML(tag.label)} (${escapeTelegramHTML(tag.kind)}, ${Math.round(tag.confidence * 100)}%)`
603322
+ ),
603323
+ topSummaries.length ? "" : "",
603324
+ topSummaries.length ? `<b>Summaries</b>` : "",
603325
+ ...topSummaries.map(
603326
+ (summary) => `- ${escapeTelegramHTML(summary.title)}: ${escapeTelegramHTML(summary.text)}`
603327
+ ),
603328
+ topExtractions.length ? "" : "",
603329
+ topExtractions.length ? `<b>Extractions</b>` : "",
603330
+ ...topExtractions.map(
603331
+ (item) => `- ${escapeTelegramHTML(item.kind)}: ${escapeTelegramHTML(item.text)}`
603332
+ ),
603333
+ topLinks.length ? "" : "",
603334
+ topLinks.length ? `<b>Links</b>` : "",
603335
+ ...topLinks.map(
603336
+ (link) => `- ${escapeTelegramHTML(link.srcNodeText)} -> ${escapeTelegramHTML(link.dstNodeText)} (${escapeTelegramHTML(link.relation)})`
603337
+ ),
603338
+ artifact.corpus?.fallbacks?.length ? "" : "",
603339
+ artifact.corpus?.fallbacks?.length ? `<b>Fallbacks</b>` : "",
603340
+ ...(artifact.corpus?.fallbacks ?? []).slice(0, 3).map((fallback) => `- ${escapeTelegramHTML(fallback)}`),
603341
+ "",
603342
+ topThreads.length ? `<b>Curiosity threads</b>` : "",
603343
+ ...topThreads.map(
603344
+ (thread) => `- ${escapeTelegramHTML(thread.question)} (${Math.round(thread.intensity * 100)}%)`
603345
+ ),
603346
+ topReplies.length ? "" : "",
603347
+ topReplies.length ? `<b>Reply opportunities</b>` : "",
603348
+ ...topReplies.map(
603349
+ (item) => `- ${escapeTelegramHTML(item.trigger)} (${Math.round(item.confidence * 100)}%)`
603350
+ ),
603351
+ groupPlans.length ? "" : "",
603352
+ groupPlans.length ? `<b>Same-group outreach plans</b>` : "",
603353
+ ...groupPlans.map(
603354
+ (plan) => `- ${escapeTelegramHTML(plan.trigger)} (${Math.round(plan.confidence * 100)}%, ${escapeTelegramHTML(plan.gate)})`
603355
+ ),
603356
+ "",
603357
+ "Saved as scoped Telegram context for future routing. It reflects chat history only, not the workspace filesystem."
603358
+ ].filter((line) => line !== "");
603359
+ return lines.join("\n");
603360
+ }
601732
603361
  function splitTelegramHTMLMessage(html, maxLength = 3600) {
601733
603362
  const chunks = [];
601734
603363
  let current = "";
@@ -602084,6 +603713,16 @@ function telegramImageMime(media) {
602084
603713
  if (ext === ".tif" || ext === ".tiff") return "image/tiff";
602085
603714
  return "image/jpeg";
602086
603715
  }
603716
+ function appendMediaContextBlock(description, block) {
603717
+ const clean5 = block.trim();
603718
+ if (!clean5) return description;
603719
+ if (description.endsWith("]")) return `${description.slice(0, -1)}
603720
+
603721
+ ${clean5}]`;
603722
+ return `${description}
603723
+
603724
+ ${clean5}`;
603725
+ }
602087
603726
  function telegramCachedMediaIsImage(entry) {
602088
603727
  if (entry.mediaType === "photo") return true;
602089
603728
  if (entry.mimeType?.toLowerCase().startsWith("image/")) return true;
@@ -602312,7 +603951,7 @@ function renderTelegramSubAgentError(username, error) {
602312
603951
  process.stdout.write(` ${c3.dim("⎿")} ${c3.red("✘")} @${username}: ${c3.dim(preview)}
602313
603952
  `);
602314
603953
  }
602315
- var TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, TELEGRAM_PUBLIC_SOUL_PROFILE, TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT, TELEGRAM_PUBLIC_MEMORY_SCOPE_CONTRACT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, ADMIN_CHAT_PROFILE_PROMPT, TELEGRAM_ACTION_RESPONSE_CONTRACT, TELEGRAM_CHAT_HISTORY_LIMIT, TELEGRAM_CONTEXT_RECENT_DEFAULT, TELEGRAM_CONTEXT_LINE_LIMIT, TELEGRAM_CONTEXT_SAMPLE_LIMIT, TELEGRAM_MEMORY_CARD_LIMIT, TELEGRAM_MEMORY_NOTE_LIMIT, TELEGRAM_MEMORY_STOPWORDS, TELEGRAM_PUBLIC_HELP_COMMANDS, TELEGRAM_REMINDER_SLASH_COMMANDS, TELEGRAM_IMAGE_EXTENSIONS, MEDIA_CACHE_TTL_MS, TELEGRAM_CHANNEL_DMN_SWEEP_MS, TELEGRAM_CHANNEL_DMN_IDLE_AFTER_MS, TELEGRAM_CHANNEL_DMN_MIN_INTERVAL_MS, TELEGRAM_CHANNEL_DMN_MIN_MESSAGES, TelegramBridge;
603954
+ var TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, TELEGRAM_PUBLIC_SOUL_PROFILE, TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT, TELEGRAM_PUBLIC_MEMORY_SCOPE_CONTRACT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, ADMIN_CHAT_PROFILE_PROMPT, TELEGRAM_ACTION_RESPONSE_CONTRACT, TELEGRAM_CHAT_HISTORY_LIMIT, TELEGRAM_CONTEXT_RECENT_DEFAULT, TELEGRAM_CONTEXT_LINE_LIMIT, TELEGRAM_CONTEXT_SAMPLE_LIMIT, TELEGRAM_MEMORY_CARD_LIMIT, TELEGRAM_MEMORY_NOTE_LIMIT, TELEGRAM_MEMORY_STOPWORDS, TELEGRAM_PUBLIC_HELP_COMMANDS, TELEGRAM_REMINDER_SLASH_COMMANDS, TELEGRAM_REFLECTION_SLASH_COMMANDS, TELEGRAM_IMAGE_EXTENSIONS, MEDIA_CACHE_TTL_MS, TELEGRAM_CHANNEL_DMN_SWEEP_MS, TELEGRAM_CHANNEL_DMN_IDLE_AFTER_MS, TELEGRAM_CHANNEL_DMN_MIN_INTERVAL_MS, TELEGRAM_CHANNEL_DMN_MIN_MESSAGES, TelegramBridge;
602316
603955
  var init_telegram_bridge = __esm({
602317
603956
  "packages/cli/src/tui/telegram-bridge.ts"() {
602318
603957
  "use strict";
@@ -602328,7 +603967,10 @@ var init_telegram_bridge = __esm({
602328
603967
  init_omnius_directory();
602329
603968
  init_stimulation();
602330
603969
  init_identity_memory_tool();
603970
+ init_visual_identity_association();
602331
603971
  init_telegram_channel_dmn();
603972
+ init_telegram_reflection_corpus();
603973
+ init_telegram_reflection_extraction();
602332
603974
  TELEGRAM_SAFETY_PROMPT = `
602333
603975
  CRITICAL SAFETY NOTICE — PUBLIC TELEGRAM CHANNEL
602334
603976
 
@@ -602512,6 +604154,7 @@ Telegram response contract:
602512
604154
  ]);
602513
604155
  TELEGRAM_PUBLIC_HELP_COMMANDS = /* @__PURE__ */ new Set(["help", "start", "auth", "call"]);
602514
604156
  TELEGRAM_REMINDER_SLASH_COMMANDS = /* @__PURE__ */ new Set(["remind", "reminder", "reminders"]);
604157
+ TELEGRAM_REFLECTION_SLASH_COMMANDS = /* @__PURE__ */ new Set(["reflect", "reflection", "daydream", "dream"]);
602515
604158
  TELEGRAM_IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".tiff", ".tif", ".svg"]);
602516
604159
  MEDIA_CACHE_TTL_MS = 30 * 60 * 1e3;
602517
604160
  TELEGRAM_CHANNEL_DMN_SWEEP_MS = 2 * 60 * 1e3;
@@ -602614,6 +604257,8 @@ Telegram response contract:
602614
604257
  channelDmnRunning = /* @__PURE__ */ new Set();
602615
604258
  /** Daydream artifact already used to force a live-router check. */
602616
604259
  channelDmnPromptedArtifactAt = /* @__PURE__ */ new Map();
604260
+ /** Per-chat reflection settings for model-gated idle follow-ups. */
604261
+ channelReflectionState = /* @__PURE__ */ new Map();
602617
604262
  /** Set admin user ID filter */
602618
604263
  setAdmin(userId) {
602619
604264
  this.adminUserId = userId;
@@ -602698,7 +604343,7 @@ Telegram response contract:
602698
604343
  return !!this.adminAuthChallenge && this.adminAuthChallenge.expiresAtMs > Date.now();
602699
604344
  }
602700
604345
  hashAdminAuthCode(code8) {
602701
- return createHash21("sha256").update(`omnius-telegram-admin:${code8.trim()}`).digest("hex");
604346
+ return createHash23("sha256").update(`omnius-telegram-admin:${code8.trim()}`).digest("hex");
602702
604347
  }
602703
604348
  viewIdForMessage(msg) {
602704
604349
  return `telegram-${this.sessionKeyForMessage(msg).replace(/[^A-Za-z0-9_-]/g, "-")}`;
@@ -602769,6 +604414,69 @@ Telegram response contract:
602769
604414
  result.success ? result.output || "Reminder updated." : result.error || `/${cmd} failed`
602770
604415
  );
602771
604416
  }
604417
+ async handleTelegramReflectionSlash(msg, commandText) {
604418
+ if (!this.repoRoot) {
604419
+ await this.replyToTelegramMessage(msg, "Telegram reflection storage is not available yet.");
604420
+ return;
604421
+ }
604422
+ const sessionKey = this.sessionKeyForMessage(msg);
604423
+ this.ensureTelegramConversationLoaded(sessionKey);
604424
+ const first2 = commandText.trim().split(/\s+/)[0] ?? "";
604425
+ const commandName = first2.slice(1).split("@")[0]?.toLowerCase() || "reflect";
604426
+ const arg = commandText.trim().slice(first2.length).trim();
604427
+ const lower = arg.toLowerCase();
604428
+ const state = this.reflectionStateForSession(sessionKey);
604429
+ if (commandName === "dream" && (lower === "deep" || lower === "lucid" || lower === "consolidate")) {
604430
+ await this.replyToTelegramMessage(
604431
+ msg,
604432
+ "Telegram /dream is scoped to chat reflection and does not start filesystem dream mode. Use /reflect, /reflect now, or run /dream in the TUI terminal."
604433
+ );
604434
+ return;
604435
+ }
604436
+ if (lower === "auto on" || lower === "on") {
604437
+ state.autoFollowup = true;
604438
+ this.saveTelegramConversationState(sessionKey);
604439
+ await this.replyToTelegramMessage(msg, "Scoped Telegram reflection auto follow-up is on for this chat. Idle cycles remain model-gated and public-only.");
604440
+ return;
604441
+ }
604442
+ if (lower === "auto off" || lower === "off" || lower === "stop") {
604443
+ state.autoFollowup = false;
604444
+ this.saveTelegramConversationState(sessionKey);
604445
+ await this.replyToTelegramMessage(msg, "Scoped Telegram reflection auto follow-up is off for this chat.");
604446
+ return;
604447
+ }
604448
+ if (lower === "auto" || lower === "status" || lower === "show") {
604449
+ const latest = this.latestTelegramChannelDaydream(sessionKey);
604450
+ const html2 = latest ? formatTelegramReflectionSummaryHTML(latest, { autoFollowup: state.autoFollowup }) : `<b>Telegram reflection</b>
604451
+ No scoped reflection artifact exists yet for this chat. Use <code>/reflect</code> to build one from retained Telegram history.`;
604452
+ await this.replyToTelegramMessage(msg, html2, {
604453
+ html: true,
604454
+ replyToMessageId: msg.chatType !== "private" ? msg.messageId : void 0
604455
+ });
604456
+ return;
604457
+ }
604458
+ const artifact = await this.runTelegramChannelDmnForSession(sessionKey, "telegram-command", Date.now(), true);
604459
+ if (!artifact) {
604460
+ await this.replyToTelegramMessage(
604461
+ msg,
604462
+ "No retained Telegram chat history is available for scoped reflection yet. Send a few messages in this chat first, then run /reflect again."
604463
+ );
604464
+ return;
604465
+ }
604466
+ let followup;
604467
+ if (lower === "now" || lower === "reply" || lower === "send") {
604468
+ followup = await this.maybeSendTelegramReflectionFollowup(sessionKey, artifact, "telegram-command");
604469
+ }
604470
+ const html = formatTelegramReflectionSummaryHTML(artifact, {
604471
+ autoFollowup: state.autoFollowup,
604472
+ sentFollowup: followup?.sent,
604473
+ followupReason: followup?.reason
604474
+ });
604475
+ await this.replyToTelegramMessage(msg, html, {
604476
+ html: true,
604477
+ replyToMessageId: msg.chatType !== "private" ? msg.messageId : void 0
604478
+ });
604479
+ }
602772
604480
  /** Write to the scrollable TUI waterfall area (respects status bar scroll region) */
602773
604481
  tuiWrite(fn) {
602774
604482
  if (this.writeContent) {
@@ -602887,9 +604595,9 @@ Telegram response contract:
602887
604595
  return reply;
602888
604596
  }
602889
604597
  quoteTelegramContextBlock(text, maxLength = 1800) {
602890
- const clean3 = stripTelegramHiddenThinking(text).trim();
602891
- const clipped = clean3.length > maxLength ? `${clean3.slice(0, Math.max(0, maxLength - 60)).trimEnd()}
602892
- [reply context truncated]` : clean3;
604598
+ const clean5 = stripTelegramHiddenThinking(text).trim();
604599
+ const clipped = clean5.length > maxLength ? `${clean5.slice(0, Math.max(0, maxLength - 60)).trimEnd()}
604600
+ [reply context truncated]` : clean5;
602893
604601
  return clipped.split(/\r?\n/).map((line) => `> ${line}`).join("\n");
602894
604602
  }
602895
604603
  buildTelegramCurrentReplyContext(sessionKey, msg) {
@@ -602995,6 +604703,7 @@ ${mediaContext}` : ""
602995
604703
  const modality = media.type === "audio" || media.type === "voice" ? "audio" : telegramMediaIsImage(media) ? "visual" : "text";
602996
604704
  const payload = {
602997
604705
  sourceSurface: "telegram",
604706
+ sessionId: this.sessionKeyForMessage(msg),
602998
604707
  scope: this.telegramMemoryScope(msg),
602999
604708
  sender,
603000
604709
  message: {
@@ -603036,7 +604745,7 @@ ${mediaContext}` : ""
603036
604745
  return payload;
603037
604746
  }
603038
604747
  telegramConversationPath(sessionKey) {
603039
- const safe = createHash21("sha1").update(sessionKey).digest("hex").slice(0, 20);
604748
+ const safe = createHash23("sha1").update(sessionKey).digest("hex").slice(0, 20);
603040
604749
  return join123(this.telegramConversationDir, `${safe}.json`);
603041
604750
  }
603042
604751
  telegramPersonalityScope(sessionKey, msg) {
@@ -603076,6 +604785,13 @@ ${mediaContext}` : ""
603076
604785
  if (parsed.stimulation) {
603077
604786
  this.stimulation.setState(sessionKey, parsed.stimulation);
603078
604787
  }
604788
+ if (parsed.reflection) {
604789
+ this.channelReflectionState.set(sessionKey, {
604790
+ autoFollowup: parsed.reflection.autoFollowup === true,
604791
+ lastFollowupAt: typeof parsed.reflection.lastFollowupAt === "number" ? parsed.reflection.lastFollowupAt : void 0,
604792
+ lastFollowupArtifactAt: typeof parsed.reflection.lastFollowupArtifactAt === "string" ? parsed.reflection.lastFollowupArtifactAt : void 0
604793
+ });
604794
+ }
603079
604795
  } catch {
603080
604796
  }
603081
604797
  }
@@ -603110,7 +604826,8 @@ ${mediaContext}` : ""
603110
604826
  history: this.chatHistory.get(sessionKey) ?? [],
603111
604827
  participants,
603112
604828
  memoryCards: this.chatMemoryCards.get(sessionKey) ?? [],
603113
- stimulation: this.stimulation.getState(sessionKey)
604829
+ stimulation: this.stimulation.getState(sessionKey),
604830
+ reflection: this.channelReflectionState.get(sessionKey) ?? { autoFollowup: false }
603114
604831
  };
603115
604832
  writeFileSync57(this.telegramConversationPath(sessionKey), JSON.stringify(payload, null, 2) + "\n", "utf8");
603116
604833
  } catch {
@@ -603120,6 +604837,21 @@ ${mediaContext}` : ""
603120
604837
  if (!this.repoRoot) return null;
603121
604838
  return latestTelegramChannelDaydream(this.repoRoot, sessionKey);
603122
604839
  }
604840
+ reflectionStateForSession(sessionKey) {
604841
+ const existing = this.channelReflectionState.get(sessionKey);
604842
+ if (existing) return existing;
604843
+ const created = { autoFollowup: false };
604844
+ this.channelReflectionState.set(sessionKey, created);
604845
+ return created;
604846
+ }
604847
+ telegramReflectionEmbeddingConfig() {
604848
+ if (!this.agentConfig || this.agentConfig.backendType !== "ollama") return false;
604849
+ return {
604850
+ baseUrl: this.agentConfig.backendUrl,
604851
+ model: process.env["OMNIUS_EMBEDDING_MODEL"] || "nomic-embed-text",
604852
+ timeoutMs: Math.min(Math.max(this.agentConfig.timeoutMs ?? 3e4, 5e3), 3e4)
604853
+ };
604854
+ }
603123
604855
  buildTelegramChannelDaydreamInput(sessionKey, nowMs = Date.now()) {
603124
604856
  const history = this.chatHistory.get(sessionKey) ?? [];
603125
604857
  if (history.length === 0) return null;
@@ -603153,11 +604885,18 @@ ${mediaContext}` : ""
603153
604885
  role: entry.role,
603154
604886
  speaker: entry.speaker,
603155
604887
  username: entry.username,
604888
+ firstName: entry.firstName,
604889
+ fromUserId: entry.fromUserId,
603156
604890
  text: entry.text,
603157
604891
  ts: entry.ts,
603158
604892
  mode: entry.mode,
604893
+ chatId: entry.chatId,
604894
+ chatType: entry.chatType,
604895
+ chatTitle: entry.chatTitle,
603159
604896
  messageId: entry.messageId,
604897
+ messageThreadId: entry.messageThreadId,
603160
604898
  replyToMessageId: entry.replyToMessageId,
604899
+ replyContext: entry.replyContext,
603161
604900
  mediaSummary: entry.mediaSummary
603162
604901
  })),
603163
604902
  participants,
@@ -603172,7 +604911,7 @@ ${mediaContext}` : ""
603172
604911
  if (this.subAgents.has(sessionKey)) return null;
603173
604912
  const input = this.buildTelegramChannelDaydreamInput(sessionKey, nowMs);
603174
604913
  if (!input) return null;
603175
- if (input.chatType === "private") return null;
604914
+ if (input.chatType === "private" && !force) return null;
603176
604915
  const lastMessageAt = input.history.map((entry) => entry.ts).filter((ts) => typeof ts === "number").sort((a2, b) => b - a2)[0] ?? nowMs;
603177
604916
  const lastRunAt = this.channelDmnLastRunAt.get(sessionKey) ?? 0;
603178
604917
  if (!force) {
@@ -603182,9 +604921,51 @@ ${mediaContext}` : ""
603182
604921
  }
603183
604922
  this.channelDmnRunning.add(sessionKey);
603184
604923
  try {
603185
- const artifact = buildTelegramChannelDaydream(input);
604924
+ const corpus = await buildTelegramReflectionCorpus({
604925
+ repoRoot: this.repoRoot,
604926
+ sessionKey,
604927
+ chatId: input.chatId,
604928
+ chatType: input.chatType,
604929
+ chatTitle: input.chatTitle,
604930
+ generatedAtMs: nowMs,
604931
+ history: input.history,
604932
+ embeddingConfig: this.telegramReflectionEmbeddingConfig()
604933
+ });
604934
+ let extraction = null;
604935
+ let extractionCommit;
604936
+ if (this.agentConfig) {
604937
+ try {
604938
+ const backend = new OllamaAgenticBackend(
604939
+ this.agentConfig.backendUrl,
604940
+ this.agentConfig.model,
604941
+ this.agentConfig.apiKey
604942
+ );
604943
+ extraction = await runTelegramReflectionExtraction({
604944
+ backend: {
604945
+ chatCompletion: (args) => backend.chatCompletion(args)
604946
+ },
604947
+ corpus,
604948
+ sessionKey,
604949
+ chatId: input.chatId,
604950
+ chatType: input.chatType,
604951
+ chatTitle: input.chatTitle,
604952
+ timeoutMs: Math.min(Math.max(this.agentConfig.timeoutMs ?? 3e4, 5e3), 3e4)
604953
+ });
604954
+ if (extraction) {
604955
+ extractionCommit = commitTelegramReflectionExtraction(this.repoRoot, sessionKey, corpus, extraction);
604956
+ } else {
604957
+ corpus.fallbacks.push("structured extraction unavailable: model returned no valid JSON");
604958
+ }
604959
+ } catch (err) {
604960
+ corpus.fallbacks.push(`structured extraction unavailable: ${err instanceof Error ? err.message : String(err)}`);
604961
+ }
604962
+ } else {
604963
+ corpus.fallbacks.push("structured extraction unavailable: backend unavailable");
604964
+ }
604965
+ const artifact = buildTelegramChannelDaydream(input, corpus, extraction, extractionCommit);
603186
604966
  writeTelegramChannelDaydream(this.repoRoot, artifact);
603187
604967
  this.channelDmnLastRunAt.set(sessionKey, nowMs);
604968
+ this.channelDmnPromptedArtifactAt.delete(sessionKey);
603188
604969
  void reason;
603189
604970
  return artifact;
603190
604971
  } finally {
@@ -603196,7 +604977,123 @@ ${mediaContext}` : ""
603196
604977
  this.ensureAllTelegramConversationsLoaded();
603197
604978
  const now = Date.now();
603198
604979
  for (const sessionKey of this.chatHistory.keys()) {
603199
- await this.runTelegramChannelDmnForSession(sessionKey, reason, now).catch(() => null);
604980
+ const artifact = await this.runTelegramChannelDmnForSession(sessionKey, reason, now).catch(() => null);
604981
+ if (!artifact) continue;
604982
+ const reflection = this.reflectionStateForSession(sessionKey);
604983
+ if (reflection.autoFollowup) {
604984
+ await this.maybeSendTelegramReflectionFollowup(sessionKey, artifact, reason).catch(() => null);
604985
+ }
604986
+ }
604987
+ }
604988
+ telegramChatIdFromArtifact(artifact) {
604989
+ const numeric = Number(artifact.chatId);
604990
+ return Number.isFinite(numeric) && String(Math.trunc(numeric)) === artifact.chatId ? Math.trunc(numeric) : artifact.chatId;
604991
+ }
604992
+ async maybeSendTelegramReflectionFollowup(sessionKey, artifact, reason = "reflection") {
604993
+ if (!this.agentConfig) return { sent: false, reason: "backend unavailable" };
604994
+ if (artifact.chatType === "private") return { sent: false, reason: "private chat reflection stored without autonomous outreach" };
604995
+ const state = this.reflectionStateForSession(sessionKey);
604996
+ if (state.lastFollowupArtifactAt === artifact.generatedAt) {
604997
+ return { sent: false, reason: "follow-up already evaluated for this artifact" };
604998
+ }
604999
+ const now = Date.now();
605000
+ if (state.lastFollowupAt && now - state.lastFollowupAt < 60 * 6e4) {
605001
+ return { sent: false, reason: "rate limit held public follow-up" };
605002
+ }
605003
+ const candidateMessageIds = Array.from(new Set([
605004
+ ...artifact.curiosityThreads.flatMap((thread) => thread.sourceMessages ?? []),
605005
+ ...artifact.memoryProposals.flatMap((proposal) => proposal.sourceMessages ?? []),
605006
+ ...(artifact.tagging ?? []).flatMap((item) => item.sourceMessageIds ?? []),
605007
+ ...(artifact.summation ?? []).flatMap((item) => item.sourceMessageIds ?? []),
605008
+ ...(artifact.titling ?? []).flatMap((item) => item.sourceMessageIds ?? []),
605009
+ ...(artifact.extraction ?? []).flatMap((item) => item.sourceMessageIds ?? []),
605010
+ ...(artifact.linking ?? []).flatMap((item) => item.sourceMessageIds ?? []),
605011
+ ...(artifact.extractionFollowups ?? []).map((item) => item.replyToMessageId).filter((id) => typeof id === "number")
605012
+ ].filter((id) => typeof id === "number" && Number.isFinite(id))));
605013
+ const context2 = this.buildTelegramConversationContextStream(sessionKey, {
605014
+ chatId: this.telegramChatIdFromArtifact(artifact),
605015
+ chatType: artifact.chatType,
605016
+ chatTitle: artifact.chatTitle,
605017
+ text: `[internal Telegram reflection cycle: ${reason}]`,
605018
+ username: this.state.botUsername || "omnius",
605019
+ firstName: "Omnius",
605020
+ fromUserId: 0,
605021
+ messageId: 0
605022
+ }, 40);
605023
+ const prompt = [
605024
+ "You are deciding whether an idle Telegram reflection cycle should post one public follow-up in the same group.",
605025
+ 'Return JSON only: {"should_send":boolean,"text":"public message text","reply_to_message_id":number|null,"confidence":0.0-1.0,"reason":"short reason"}.',
605026
+ "",
605027
+ "Hard constraints:",
605028
+ "- Use only the Telegram chat context and reflection artifact below.",
605029
+ "- Do not inspect or reference the workspace filesystem.",
605030
+ "- Default should_send=false unless a concise public follow-up is timely, low-intrusion, and clearly useful.",
605031
+ "- Do not reveal hidden meta-analysis, local paths, internal tools, or that a private artifact exists.",
605032
+ "- If sending, write one short message under 700 characters.",
605033
+ "- Prefer replying to one of the candidate message IDs when the follow-up is anchored to old content.",
605034
+ "- Private DM outreach is admin-review only and must not be sent by this autonomous public follow-up path.",
605035
+ "",
605036
+ `Candidate reply message IDs: ${candidateMessageIds.length ? candidateMessageIds.join(", ") : "none"}`,
605037
+ "",
605038
+ formatTelegramChannelDaydreamContext(artifact),
605039
+ "",
605040
+ context2
605041
+ ].join("\n");
605042
+ try {
605043
+ const backend = new OllamaAgenticBackend(
605044
+ this.agentConfig.backendUrl,
605045
+ this.agentConfig.model,
605046
+ this.agentConfig.apiKey
605047
+ );
605048
+ const result = await backend.chatCompletion({
605049
+ messages: [
605050
+ { role: "system", content: "You are a Telegram public-follow-up discretion model. Output strict JSON only." },
605051
+ { role: "user", content: prompt }
605052
+ ],
605053
+ tools: [],
605054
+ temperature: 0.2,
605055
+ maxTokens: 300,
605056
+ timeoutMs: Math.min(Math.max(this.agentConfig.timeoutMs ?? 3e4, 5e3), 2e4),
605057
+ think: false
605058
+ });
605059
+ const decision = parseTelegramReflectionFollowupDecision(result.choices[0]?.message?.content ?? "");
605060
+ state.lastFollowupArtifactAt = artifact.generatedAt;
605061
+ if (!decision) {
605062
+ this.saveTelegramConversationState(sessionKey);
605063
+ return { sent: false, reason: "model returned no usable follow-up decision" };
605064
+ }
605065
+ if (!decision.shouldSend || !decision.text || decision.confidence < 0.66) {
605066
+ this.saveTelegramConversationState(sessionKey);
605067
+ return { sent: false, reason: decision.reason || "model chose silence" };
605068
+ }
605069
+ const replyTo = decision.replyToMessageId && candidateMessageIds.includes(decision.replyToMessageId) ? decision.replyToMessageId : void 0;
605070
+ const chatId = this.telegramChatIdFromArtifact(artifact);
605071
+ const sentMessageId = await this.sendMessageHTML(
605072
+ chatId,
605073
+ convertMarkdownToTelegramHTML(decision.text),
605074
+ replyTo
605075
+ );
605076
+ this.recordTelegramAssistantMessage({
605077
+ chatId,
605078
+ chatType: artifact.chatType,
605079
+ chatTitle: artifact.chatTitle,
605080
+ text: decision.text,
605081
+ username: this.state.botUsername || "omnius",
605082
+ firstName: "Omnius",
605083
+ fromUserId: 0,
605084
+ messageId: sentMessageId ?? 0
605085
+ }, decision.text, "chat", {
605086
+ messageId: sentMessageId,
605087
+ replyToMessageId: replyTo
605088
+ });
605089
+ state.lastFollowupAt = now;
605090
+ this.saveTelegramConversationState(sessionKey);
605091
+ this.tuiWrite(() => renderTelegramSubAgentEvent("reflection", `sent scoped Telegram follow-up (${decision.confidence.toFixed(2)}): ${decision.reason}`));
605092
+ return { sent: true, reason: decision.reason };
605093
+ } catch (err) {
605094
+ state.lastFollowupArtifactAt = artifact.generatedAt;
605095
+ this.saveTelegramConversationState(sessionKey);
605096
+ return { sent: false, reason: err instanceof Error ? err.message : String(err) };
603200
605097
  }
603201
605098
  }
603202
605099
  formatLatestTelegramChannelDaydreamContext(sessionKey) {
@@ -603220,6 +605117,22 @@ ${mediaContext}` : ""
603220
605117
  return "";
603221
605118
  }
603222
605119
  }
605120
+ upsertTelegramReflectionHistoryEntry(sessionKey, entry) {
605121
+ if (!this.repoRoot || entry.chatId === void 0) return;
605122
+ try {
605123
+ upsertTelegramReflectionMessage(
605124
+ this.repoRoot,
605125
+ sessionKey,
605126
+ entry,
605127
+ {
605128
+ chatId: String(entry.chatId),
605129
+ chatType: entry.chatType || "unknown",
605130
+ chatTitle: entry.chatTitle
605131
+ }
605132
+ );
605133
+ } catch {
605134
+ }
605135
+ }
603223
605136
  recordTelegramUserMessage(msg, mode, textOverride) {
603224
605137
  const sessionKey = this.sessionKeyForMessage(msg);
603225
605138
  const mediaSummary = summarizeTelegramMessageAttachments(msg);
@@ -603242,6 +605155,7 @@ ${mediaContext}` : ""
603242
605155
  mediaSummary
603243
605156
  };
603244
605157
  this.recordChatHistory(sessionKey, entry);
605158
+ this.upsertTelegramReflectionHistoryEntry(sessionKey, entry);
603245
605159
  this.updateTelegramParticipantProfile(sessionKey, msg, text);
603246
605160
  this.updateTelegramMemoryCards(sessionKey, entry);
603247
605161
  try {
@@ -603261,12 +605175,12 @@ ${mediaContext}` : ""
603261
605175
  this.saveTelegramConversationState(sessionKey);
603262
605176
  }
603263
605177
  recordTelegramAssistantMessage(msg, text, mode, options2 = {}) {
603264
- const clean3 = stripTelegramHiddenThinking(text).trim();
603265
- if (!clean3) return;
605178
+ const clean5 = stripTelegramHiddenThinking(text).trim();
605179
+ if (!clean5) return;
603266
605180
  const sessionKey = this.sessionKeyForMessage(msg);
603267
605181
  const entry = {
603268
605182
  role: "assistant",
603269
- text: clean3,
605183
+ text: clean5,
603270
605184
  mode,
603271
605185
  chatId: msg.chatId,
603272
605186
  speaker: this.state.botUsername ? `@${this.state.botUsername}` : "Assistant",
@@ -603277,16 +605191,17 @@ ${mediaContext}` : ""
603277
605191
  chatTitle: msg.chatTitle
603278
605192
  };
603279
605193
  this.recordChatHistory(sessionKey, entry);
605194
+ this.upsertTelegramReflectionHistoryEntry(sessionKey, entry);
603280
605195
  this.stimulation.recordAgentOutput(sessionKey);
603281
605196
  this.updateTelegramMemoryCards(sessionKey, entry);
603282
605197
  try {
603283
605198
  updateScopedPersonality(this.telegramPersonalityScope(sessionKey, msg), {
603284
605199
  speaker: entry.speaker || "Assistant",
603285
- text: clean3,
605200
+ text: clean5,
603286
605201
  role: "assistant",
603287
605202
  mode,
603288
605203
  ts: entry.ts,
603289
- toneTags: inferTelegramToneTags(clean3)
605204
+ toneTags: inferTelegramToneTags(clean5)
603290
605205
  });
603291
605206
  } catch {
603292
605207
  }
@@ -603354,7 +605269,7 @@ ${mediaContext}` : ""
603354
605269
  }
603355
605270
  const matchingEntry = mediaEntries.find((entry) => {
603356
605271
  if (resolve41(entry.localPath) === resolve41(raw)) return true;
603357
- if (basename26(entry.localPath) === raw) return true;
605272
+ if (basename27(entry.localPath) === raw) return true;
603358
605273
  if (entry.fileUniqueId === raw || entry.fileId === raw) return true;
603359
605274
  if (entry.messageId && String(entry.messageId) === raw) return true;
603360
605275
  return false;
@@ -603390,7 +605305,7 @@ ${mediaContext}` : ""
603390
605305
  }
603391
605306
  return entries.find((entry2) => {
603392
605307
  if (resolve41(entry2.localPath) === resolve41(ref)) return true;
603393
- if (basename26(entry2.localPath) === ref) return true;
605308
+ if (basename27(entry2.localPath) === ref) return true;
603394
605309
  if (entry2.fileUniqueId === ref || entry2.fileId === ref) return true;
603395
605310
  if (entry2.messageId && String(entry2.messageId) === ref) return true;
603396
605311
  return false;
@@ -603418,7 +605333,7 @@ ${mediaContext}` : ""
603418
605333
  caption: entry.caption
603419
605334
  },
603420
605335
  modality,
603421
- label: `Telegram message_id ${entry.messageId || "unknown"} ${basename26(entry.localPath)}`,
605336
+ label: `Telegram message_id ${entry.messageId || "unknown"} ${basename27(entry.localPath)}`,
603422
605337
  extractedContent: entry.extractedContent
603423
605338
  };
603424
605339
  }
@@ -603493,7 +605408,7 @@ ${mediaContext}` : ""
603493
605408
  const titleTags = tags.slice(0, 4);
603494
605409
  const title = titleTags.length > 0 ? `${speaker} / ${titleTags.join(" ")}` : `${speaker} / conversation`;
603495
605410
  const card = {
603496
- id: createHash21("sha1").update(`${sessionKey}:${now}:${speaker}:${text}`).digest("hex").slice(0, 12),
605411
+ id: createHash23("sha1").update(`${sessionKey}:${now}:${speaker}:${text}`).digest("hex").slice(0, 12),
603497
605412
  title,
603498
605413
  notes: [],
603499
605414
  tags: [],
@@ -603624,7 +605539,7 @@ ${cardLines.join("\n")}`);
603624
605539
  const caption = entry.caption ? ` caption:${truncateTelegramContextLine(entry.caption, 120)}` : "";
603625
605540
  const extracted = entry.extractedContent ? `
603626
605541
  ${truncateTelegramContextLine(entry.extractedContent.replace(/\s+/g, " "), 220)}` : "";
603627
- return `- message_id ${entry.messageId}${replyMark}: ${kind}; path ${entry.localPath}; file ${basename26(entry.localPath)}${caption}${extracted}`;
605542
+ return `- message_id ${entry.messageId}${replyMark}: ${kind}; path ${entry.localPath}; file ${basename27(entry.localPath)}${caption}${extracted}`;
603628
605543
  });
603629
605544
  sections.push([
603630
605545
  "### Recent Chat Media",
@@ -603965,7 +605880,7 @@ ${list}` : "No shared group target is currently known for this sender. Ask in th
603965
605880
  }
603966
605881
  telegramRunnerStateDir(sessionKey) {
603967
605882
  if (!this.repoRoot) return void 0;
603968
- const safe = createHash21("sha1").update(sessionKey).digest("hex").slice(0, 20);
605883
+ const safe = createHash23("sha1").update(sessionKey).digest("hex").slice(0, 20);
603969
605884
  return join123(this.repoRoot, ".omnius", "telegram-runner-state", safe);
603970
605885
  }
603971
605886
  buildTelegramAdminOverviewContext(currentSessionKey) {
@@ -604283,11 +606198,12 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
604283
606198
  const chunks = splitTelegramMessageText(html, 3900);
604284
606199
  if (chunks.length === 0) return null;
604285
606200
  const replyToMessageId = msg.chatType !== "private" ? msg.messageId : void 0;
606201
+ const suppressMedia = this.deliveredArtifactMediaSuppressorForMessage(msg);
604286
606202
  if (liveMessageId && !msg.guestQueryId) {
604287
606203
  const edited = await this.editLiveMessage(msg.chatId, liveMessageId, chunks[0]);
604288
606204
  if (edited) {
604289
606205
  for (const chunk of chunks.slice(1)) {
604290
- await this.sendMessageHTML(msg.chatId, chunk);
606206
+ await this.sendMessageHTML(msg.chatId, chunk, void 0, { suppressMedia });
604291
606207
  }
604292
606208
  return liveMessageId;
604293
606209
  }
@@ -604298,7 +606214,12 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
604298
606214
  }
604299
606215
  let firstMessageId = null;
604300
606216
  for (let idx = 0; idx < chunks.length; idx++) {
604301
- const messageId = await this.sendMessageHTML(msg.chatId, chunks[idx], idx === 0 ? replyToMessageId : void 0);
606217
+ const messageId = await this.sendMessageHTML(
606218
+ msg.chatId,
606219
+ chunks[idx],
606220
+ idx === 0 ? replyToMessageId : void 0,
606221
+ { suppressMedia }
606222
+ );
604302
606223
  if (firstMessageId === null) firstMessageId = messageId;
604303
606224
  }
604304
606225
  return firstMessageId;
@@ -604325,6 +606246,11 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
604325
606246
  await this.replyWithTelegramHelp(msg, isAdmin);
604326
606247
  return;
604327
606248
  }
606249
+ const telegramSlash = this.telegramSlashName(normalizedCommandText);
606250
+ if (msg.text.trim().startsWith("/") && TELEGRAM_REFLECTION_SLASH_COMMANDS.has(telegramSlash)) {
606251
+ await this.handleTelegramReflectionSlash(msg, normalizedCommandText);
606252
+ return;
606253
+ }
604328
606254
  if (!this.agentConfig || !this.repoRoot) {
604329
606255
  this.onMessage(msg);
604330
606256
  return;
@@ -604332,7 +606258,6 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
604332
606258
  const toolContext = this.resolveToolContext(msg, isAdmin);
604333
606259
  const isAdminDM = toolContext === "telegram-admin-dm";
604334
606260
  const sessionKey = this.sessionKeyForMessage(msg);
604335
- const telegramSlash = this.telegramSlashName(normalizedCommandText);
604336
606261
  if (msg.text.trim().startsWith("/") && TELEGRAM_REMINDER_SLASH_COMMANDS.has(telegramSlash)) {
604337
606262
  await this.handleTelegramReminderSlash(msg, normalizedCommandText, toolContext);
604338
606263
  return;
@@ -604494,6 +606419,7 @@ Join: ${newUrl}`);
604494
606419
  creativeWorkspaceRoot: this.creativeWorkspaceRootForMessage(msg, toolContext),
604495
606420
  generatedArtifacts: [],
604496
606421
  deliveredArtifacts: [],
606422
+ deliveredFileSends: /* @__PURE__ */ new Set(),
604497
606423
  surfacedToolCallFingerprints: /* @__PURE__ */ new Set()
604498
606424
  };
604499
606425
  this.subAgents.set(sessionKey, subAgent);
@@ -604606,6 +606532,7 @@ Join: ${newUrl}`);
604606
606532
  creativeWorkspaceRoot: this.creativeWorkspaceRootForMessage(msg, toolContext),
604607
606533
  generatedArtifacts: [],
604608
606534
  deliveredArtifacts: [],
606535
+ deliveredFileSends: /* @__PURE__ */ new Set(),
604609
606536
  surfacedToolCallFingerprints: /* @__PURE__ */ new Set()
604610
606537
  };
604611
606538
  this.subAgents.set(sessionKey, subAgent);
@@ -604897,12 +606824,12 @@ ${conversationStream}`
604897
606824
  }
604898
606825
  retainTelegramVisibleReplyDraft(subAgent, draft, streamToolNames = subAgent.currentStreamToolNames) {
604899
606826
  if (subAgent.visibleReplyText) return;
604900
- const clean3 = cleanTelegramVisibleReply(draft);
604901
- if (!clean3) return;
606827
+ const clean5 = cleanTelegramVisibleReply(draft);
606828
+ if (!clean5) return;
604902
606829
  const toolNames = [...streamToolNames];
604903
606830
  const hasNonCompletionTool = toolNames.some((name10) => name10 !== "task_complete");
604904
606831
  if (hasNonCompletionTool) return;
604905
- subAgent.visibleReplyText = clean3;
606832
+ subAgent.visibleReplyText = clean5;
604906
606833
  }
604907
606834
  retainTelegramVisibleReplyFromCompletedStream(subAgent) {
604908
606835
  this.retainTelegramVisibleReplyDraft(
@@ -605685,6 +607612,7 @@ Scoped workspace: ${scopedRoot}`,
605685
607612
  return new IdentityMemoryTool(repoRoot, {
605686
607613
  sourceSurface: "telegram",
605687
607614
  scope,
607615
+ sessionId: currentMsg ? this.sessionKeyForMessage(currentMsg) : chatId !== void 0 ? `chat:${String(chatId)}` : void 0,
605688
607616
  sender: currentMsg ? this.telegramMemorySenderFromMessage(currentMsg) : void 0,
605689
607617
  message: message2,
605690
607618
  replyTo: currentMsg ? this.telegramMemoryReplyRef(currentMsg) : void 0,
@@ -605720,7 +607648,7 @@ Scoped workspace: ${scopedRoot}`,
605720
607648
  `${index + 1}. message_id ${entry.messageId || "unknown"}`,
605721
607649
  currentMsg?.replyToMessageId === entry.messageId ? "replied-to" : "",
605722
607650
  telegramCachedMediaIsImage(entry) ? "image" : telegramCachedMediaIsPdf(entry) ? "pdf" : telegramCachedMediaIsAudio(entry) ? "audio" : telegramCachedMediaIsVideo(entry) ? "video" : entry.mediaType,
605723
- `file=${basename26(entry.localPath)}`,
607651
+ `file=${basename27(entry.localPath)}`,
605724
607652
  `path=${entry.localPath}`,
605725
607653
  entry.caption ? `caption=${truncateTelegramContextLine(entry.caption, 140)}` : ""
605726
607654
  ].filter(Boolean);
@@ -605823,17 +607751,37 @@ Scoped workspace: ${scopedRoot}`,
605823
607751
  const kind = normalizeTelegramSendKind(args["kind"], file.path);
605824
607752
  const caption = typeof args["caption"] === "string" ? args["caption"].trim().slice(0, 1024) : void 0;
605825
607753
  const replyTo = Number(args["reply_to_message_id"]);
607754
+ const replyToMessageId = Number.isFinite(replyTo) && replyTo > 0 ? Math.floor(replyTo) : void 0;
607755
+ const ledgerPath = file.logicalPath ?? file.path;
607756
+ const sendFingerprint = bridge.telegramFileSendFingerprint(
607757
+ target.chatId,
607758
+ ledgerPath,
607759
+ kind,
607760
+ caption,
607761
+ replyToMessageId
607762
+ );
607763
+ if (bridge.telegramFileSendAlreadyDeliveredForMessage(currentMsg, sendFingerprint)) {
607764
+ return {
607765
+ success: true,
607766
+ output: `Telegram file already sent in this turn: ${basename27(file.path)} as ${kind} to ${String(target.chatId)}`,
607767
+ llmContent: `Already sent ${basename27(file.path)} to Telegram as ${kind}; do not send it again.`,
607768
+ durationMs: performance.now() - start2,
607769
+ mutated: false,
607770
+ mutatedFiles: []
607771
+ };
607772
+ }
605826
607773
  try {
605827
607774
  const messageId = await bridge.sendTelegramFileToChat(target.chatId, file.path, {
605828
607775
  kind,
605829
607776
  caption: caption || void 0,
605830
- replyToMessageId: Number.isFinite(replyTo) && replyTo > 0 ? Math.floor(replyTo) : void 0
607777
+ replyToMessageId
605831
607778
  });
605832
- bridge.rememberTelegramDeliveredArtifactForMessage(currentMsg, file.logicalPath ?? file.path);
607779
+ bridge.rememberTelegramFileSendForMessage(currentMsg, sendFingerprint);
607780
+ bridge.rememberTelegramDeliveredArtifactForMessage(currentMsg, ledgerPath);
605833
607781
  return {
605834
607782
  success: true,
605835
- output: `Sent Telegram file: ${basename26(file.path)} as ${kind} to ${String(target.chatId)}${messageId ? ` (message_id ${messageId})` : ""}`,
605836
- llmContent: `Sent ${basename26(file.path)} to Telegram as ${kind}.`,
607783
+ output: `Sent Telegram file: ${basename27(file.path)} as ${kind} to ${String(target.chatId)}${messageId ? ` (message_id ${messageId})` : ""}`,
607784
+ llmContent: `Sent ${basename27(file.path)} to Telegram as ${kind}.`,
605837
607785
  durationMs: performance.now() - start2,
605838
607786
  mutated: false,
605839
607787
  mutatedFiles: []
@@ -605927,6 +607875,27 @@ ${knownList}` : "Private-user telegram_send_file target must be this DM or a kno
605927
607875
  if (!subAgent) return;
605928
607876
  this.rememberTelegramDeliveredArtifact(subAgent, path11);
605929
607877
  }
607878
+ telegramFileSendFingerprint(chatId, path11, kind, caption, replyToMessageId) {
607879
+ return stableTelegramValueKey({
607880
+ chatId: String(chatId),
607881
+ path: resolve41(path11),
607882
+ kind,
607883
+ caption: caption ?? "",
607884
+ replyToMessageId: replyToMessageId ?? null
607885
+ });
607886
+ }
607887
+ telegramFileSendAlreadyDeliveredForMessage(msg, fingerprint) {
607888
+ if (!msg) return false;
607889
+ const subAgent = this.subAgents.get(this.sessionKeyForMessage(msg));
607890
+ return Boolean(subAgent?.deliveredFileSends?.has(fingerprint));
607891
+ }
607892
+ rememberTelegramFileSendForMessage(msg, fingerprint) {
607893
+ if (!msg) return;
607894
+ const subAgent = this.subAgents.get(this.sessionKeyForMessage(msg));
607895
+ if (!subAgent) return;
607896
+ subAgent.deliveredFileSends ??= /* @__PURE__ */ new Set();
607897
+ subAgent.deliveredFileSends.add(fingerprint);
607898
+ }
605930
607899
  /** Check if a message is from the admin user (uses fromUserId, NOT chatId) */
605931
607900
  isAdminUser(msg) {
605932
607901
  if (!this.adminUserId) return false;
@@ -606039,15 +608008,43 @@ ${visionContext}]`;
606039
608008
  } else {
606040
608009
  description = `[${sourceLabel}image received and saved to ${localPath}${caption ? ` — caption: "${caption}"` : ""}. You can use image_read, ocr, or vision tools to analyze it.]`;
606041
608010
  }
608011
+ const ingestPayload = this.telegramMemoryIngestPayload(msg, media, localPath, source, cacheEntry.extractedContent);
608012
+ let visualIdentityContext = "";
608013
+ let ingestReachedDaemon = false;
606042
608014
  try {
606043
- await fetch("http://127.0.0.1:11435/v1/memory/ingest", {
608015
+ const ingestResponse = await fetch("http://127.0.0.1:11435/v1/memory/ingest", {
606044
608016
  method: "POST",
606045
608017
  headers: { "Content-Type": "application/json" },
606046
- body: JSON.stringify(this.telegramMemoryIngestPayload(msg, media, localPath, source, cacheEntry.extractedContent)),
608018
+ body: JSON.stringify(ingestPayload),
606047
608019
  signal: AbortSignal.timeout(2e3)
606048
608020
  });
608021
+ ingestReachedDaemon = ingestResponse.ok;
608022
+ if (ingestResponse.ok) {
608023
+ const ingested = await ingestResponse.json().catch(() => null);
608024
+ const block = ingested?.visualIdentity?.contextBlock;
608025
+ if (typeof block === "string" && block.trim()) visualIdentityContext = block.trim();
608026
+ }
606049
608027
  } catch {
606050
608028
  }
608029
+ if (!ingestReachedDaemon && this.repoRoot) {
608030
+ try {
608031
+ const association = await associateVisualIdentityFromImage({
608032
+ repoRoot: this.repoRoot,
608033
+ imagePath: localPath,
608034
+ sourceSurface: "telegram",
608035
+ scope: ingestPayload["scope"],
608036
+ sender: ingestPayload["sender"],
608037
+ message: ingestPayload["message"],
608038
+ replyTo: ingestPayload["replyTo"],
608039
+ sessionId: typeof ingestPayload["sessionId"] === "string" ? ingestPayload["sessionId"] : this.sessionKeyForMessage(msg),
608040
+ media: ingestPayload["media"],
608041
+ extractedContent: cacheEntry.extractedContent
608042
+ });
608043
+ if (association.contextBlock) visualIdentityContext = association.contextBlock;
608044
+ } catch {
608045
+ }
608046
+ }
608047
+ description = appendMediaContextBlock(description, visualIdentityContext);
606051
608048
  } else if (type === "audio" || type === "voice") {
606052
608049
  let transcription = null;
606053
608050
  try {
@@ -606115,23 +608112,25 @@ ${text}`.trim());
606115
608112
  }
606116
608113
  // ── Message sending ───────────────────────────────────────────────────
606117
608114
  /** Send a response back to a Telegram chat (Markdown → HTML conversion) */
606118
- async sendMessage(chatId, text, replyToMessageId) {
608115
+ async sendMessage(chatId, text, replyToMessageId, options2 = {}) {
606119
608116
  const extracted = extractMediaReferences(text);
608117
+ const mediaRefs = this.filterTelegramMediaReferences(extracted.media, options2);
606120
608118
  const html = convertMarkdownToTelegramHTML(extracted.text);
606121
- const msgId = extracted.text.trim() ? await this.sendMessageHTML(chatId, html, replyToMessageId) : null;
606122
- for (const media of extracted.media) {
608119
+ const msgId = extracted.text.trim() ? await this.sendMessageHTML(chatId, html, replyToMessageId, options2) : null;
608120
+ for (const media of mediaRefs) {
606123
608121
  await this.sendMediaReference(chatId, media, { replyToMessageId }).catch(() => null);
606124
608122
  }
606125
608123
  return msgId;
606126
608124
  }
606127
608125
  /** Send an HTML-formatted message to a Telegram chat */
606128
- async sendMessageHTML(chatId, html, replyToMessageId) {
608126
+ async sendMessageHTML(chatId, html, replyToMessageId, options2 = {}) {
606129
608127
  const extracted = extractMediaReferences(html);
608128
+ const mediaRefs = this.filterTelegramMediaReferences(extracted.media, options2);
606130
608129
  const sendHtml = extracted.text || (extracted.media.length > 0 ? "" : html);
606131
608130
  let sentId = null;
606132
608131
  if (!sendHtml.trim()) {
606133
- for (let idx = 0; idx < extracted.media.length; idx++) {
606134
- const media = extracted.media[idx];
608132
+ for (let idx = 0; idx < mediaRefs.length; idx++) {
608133
+ const media = mediaRefs[idx];
606135
608134
  const mediaId = await this.sendMediaReference(
606136
608135
  chatId,
606137
608136
  media,
@@ -606170,11 +608169,24 @@ ${text}`.trim());
606170
608169
  }
606171
608170
  }
606172
608171
  }
606173
- for (const media of extracted.media) {
608172
+ for (const media of mediaRefs) {
606174
608173
  await this.sendMediaReference(chatId, media).catch(() => null);
606175
608174
  }
606176
608175
  return sentId;
606177
608176
  }
608177
+ filterTelegramMediaReferences(media, options2) {
608178
+ const suppress = options2.suppressMedia;
608179
+ if (!suppress) return media;
608180
+ return media.filter((ref) => !suppress(ref));
608181
+ }
608182
+ deliveredArtifactMediaSuppressorForMessage(msg) {
608183
+ const subAgent = this.subAgents.get(this.sessionKeyForMessage(msg));
608184
+ const delivered = new Set(
608185
+ (subAgent?.deliveredArtifacts ?? []).map((path11) => resolve41(path11))
608186
+ );
608187
+ if (delivered.size === 0) return void 0;
608188
+ return (media) => media.source === "file" && delivered.has(resolve41(media.value));
608189
+ }
606178
608190
  async replyToTelegramMessage(msg, text, options2 = {}) {
606179
608191
  if (msg.guestQueryId) {
606180
608192
  return this.answerGuestQuery(msg.guestQueryId, text, {
@@ -606221,7 +608233,7 @@ ${text}`.trim());
606221
608233
  if (!existsSync108(media.value)) throw new Error(`File does not exist: ${media.value}`);
606222
608234
  const buffer2 = readFileSync88(media.value);
606223
608235
  const boundary = `----omnius-media-${Date.now()}-${Math.random().toString(36).slice(2)}`;
606224
- const filename = basename26(media.value);
608236
+ const filename = basename27(media.value);
606225
608237
  const contentType = mimeForPath(media.value, media.kind);
606226
608238
  const parts = [];
606227
608239
  const addField = (name10, value2) => {
@@ -606326,7 +608338,7 @@ Content-Type: ${contentType}\r
606326
608338
  continue;
606327
608339
  }
606328
608340
  const buffer2 = readFileSync88(pathOrFileId);
606329
- const filename = basename26(pathOrFileId);
608341
+ const filename = basename27(pathOrFileId);
606330
608342
  parts.push(Buffer.from(`--${boundary}\r
606331
608343
  `));
606332
608344
  parts.push(Buffer.from(
@@ -606356,11 +608368,11 @@ Content-Type: ${mimeForPath(pathOrFileId, field === "photo" ? "image" : "video")
606356
608368
  * Telegram accepts @botusername in chat_id for the supported bot-to-bot subset.
606357
608369
  */
606358
608370
  async sendMessageToBot(username, text) {
606359
- const clean3 = username.trim().replace(/^@/, "");
606360
- if (!/^[A-Za-z0-9_]{5,32}$/.test(clean3)) {
608371
+ const clean5 = username.trim().replace(/^@/, "");
608372
+ if (!/^[A-Za-z0-9_]{5,32}$/.test(clean5)) {
606361
608373
  throw new Error("Expected a Telegram bot username (5-32 chars, letters/numbers/underscore).");
606362
608374
  }
606363
- return this.sendMessage(`@${clean3}`, text);
608375
+ return this.sendMessage(`@${clean5}`, text);
606364
608376
  }
606365
608377
  async setMyCommands(commands) {
606366
608378
  const apiCommands = commands.map((cmd) => ({
@@ -607390,7 +609402,7 @@ __export(projects_exports, {
607390
609402
  });
607391
609403
  import { readFileSync as readFileSync90, writeFileSync as writeFileSync59, mkdirSync as mkdirSync65, existsSync as existsSync110, statSync as statSync37, renameSync as renameSync6 } from "node:fs";
607392
609404
  import { homedir as homedir37 } from "node:os";
607393
- import { basename as basename27, join as join125, resolve as resolve42 } from "node:path";
609405
+ import { basename as basename28, join as join125, resolve as resolve42 } from "node:path";
607394
609406
  import { randomUUID as randomUUID14 } from "node:crypto";
607395
609407
  function readAll2() {
607396
609408
  try {
@@ -607438,7 +609450,7 @@ function registerProject(root, pid) {
607438
609450
  } else {
607439
609451
  entry = {
607440
609452
  root: canonical,
607441
- name: basename27(canonical) || canonical,
609453
+ name: basename28(canonical) || canonical,
607442
609454
  firstSeen: now,
607443
609455
  lastSeen: now,
607444
609456
  pid: pid ?? null,
@@ -608164,24 +610176,24 @@ function stripMappedPrefix(ip) {
608164
610176
  }
608165
610177
  function isLoopbackIP(ip) {
608166
610178
  if (!ip) return false;
608167
- const clean3 = stripMappedPrefix(ip);
608168
- if (clean3 === "::1") return true;
608169
- if (/^127\./.test(clean3)) return true;
610179
+ const clean5 = stripMappedPrefix(ip);
610180
+ if (clean5 === "::1") return true;
610181
+ if (/^127\./.test(clean5)) return true;
608170
610182
  return false;
608171
610183
  }
608172
610184
  function isPrivateIP(ip) {
608173
610185
  if (!ip) return false;
608174
- const clean3 = stripMappedPrefix(ip);
608175
- if (/^10\./.test(clean3)) return true;
608176
- if (/^192\.168\./.test(clean3)) return true;
608177
- const m2 = /^172\.(\d{1,3})\./.exec(clean3);
610186
+ const clean5 = stripMappedPrefix(ip);
610187
+ if (/^10\./.test(clean5)) return true;
610188
+ if (/^192\.168\./.test(clean5)) return true;
610189
+ const m2 = /^172\.(\d{1,3})\./.exec(clean5);
608178
610190
  if (m2) {
608179
610191
  const second3 = parseInt(m2[1], 10);
608180
610192
  if (second3 >= 16 && second3 <= 31) return true;
608181
610193
  }
608182
- if (/^169\.254\./.test(clean3)) return true;
608183
- if (/^f[cd][0-9a-f]{2}:/i.test(clean3)) return true;
608184
- if (/^fe[89ab][0-9a-f]:/i.test(clean3)) return true;
610194
+ if (/^169\.254\./.test(clean5)) return true;
610195
+ if (/^f[cd][0-9a-f]{2}:/i.test(clean5)) return true;
610196
+ if (/^fe[89ab][0-9a-f]:/i.test(clean5)) return true;
608185
610197
  return false;
608186
610198
  }
608187
610199
  function isAllowedIP(ip, mode) {
@@ -608207,14 +610219,14 @@ var init_access_policy = __esm({
608207
610219
  });
608208
610220
 
608209
610221
  // packages/cli/src/api/project-preferences.ts
608210
- import { createHash as createHash22 } from "node:crypto";
610222
+ import { createHash as createHash24 } from "node:crypto";
608211
610223
  import { existsSync as existsSync111, mkdirSync as mkdirSync66, readFileSync as readFileSync91, renameSync as renameSync7, writeFileSync as writeFileSync60, unlinkSync as unlinkSync23 } from "node:fs";
608212
610224
  import { homedir as homedir38 } from "node:os";
608213
610225
  import { join as join126, resolve as resolve43 } from "node:path";
608214
610226
  import { randomUUID as randomUUID15 } from "node:crypto";
608215
610227
  function projectKey(root) {
608216
610228
  const canonical = resolve43(root);
608217
- return createHash22("sha256").update(canonical).digest("hex").slice(0, 16);
610229
+ return createHash24("sha256").update(canonical).digest("hex").slice(0, 16);
608218
610230
  }
608219
610231
  function projectDir(root) {
608220
610232
  return join126(PROJECTS_DIR, projectKey(root));
@@ -608306,7 +610318,7 @@ __export(voicechat_exports, {
608306
610318
  VoiceChatSession: () => VoiceChatSession
608307
610319
  });
608308
610320
  import { EventEmitter as EventEmitter11 } from "node:events";
608309
- function clamp018(x) {
610321
+ function clamp019(x) {
608310
610322
  return x < 0 ? 0 : x > 1 ? 1 : x;
608311
610323
  }
608312
610324
  function alnumRatio(s2) {
@@ -608345,9 +610357,9 @@ function computeSignalFromText(text, confidence2) {
608345
610357
  else score = 0.15;
608346
610358
  score -= repeatingCharPenalty(t2) * 0.4;
608347
610359
  if (typeof confidence2 === "number" && !Number.isNaN(confidence2)) {
608348
- score = 0.7 * score + 0.3 * clamp018(confidence2);
610360
+ score = 0.7 * score + 0.3 * clamp019(confidence2);
608349
610361
  }
608350
- return clamp018(score);
610362
+ return clamp019(score);
608351
610363
  }
608352
610364
  function truncateForLog(s2, n2) {
608353
610365
  return s2.length <= n2 ? s2 : s2.slice(0, n2 - 1) + "…";
@@ -608617,7 +610629,7 @@ Rules:
608617
610629
  }, MAX_SEGMENT_MS);
608618
610630
  }
608619
610631
  this.captureBuffer = text;
608620
- this.lastSignalScore = typeof snr === "number" && !Number.isNaN(snr) ? clamp018(snr) : computeSignalFromText(text, confidence2);
610632
+ this.lastSignalScore = typeof snr === "number" && !Number.isNaN(snr) ? clamp019(snr) : computeSignalFromText(text, confidence2);
608621
610633
  this.emit("snr", { score: this.lastSignalScore });
608622
610634
  this.onPartialTranscript(text);
608623
610635
  if (this.silenceTimer) clearTimeout(this.silenceTimer);
@@ -609392,7 +611404,7 @@ var init_disk_task_output = __esm({
609392
611404
  });
609393
611405
 
609394
611406
  // packages/cli/src/api/http.ts
609395
- import { createHash as createHash23 } from "node:crypto";
611407
+ import { createHash as createHash25 } from "node:crypto";
609396
611408
  function problemDetails(opts) {
609397
611409
  const p2 = {
609398
611410
  type: opts.type ?? "about:blank",
@@ -609455,7 +611467,7 @@ function paginated(items, page2, total) {
609455
611467
  }
609456
611468
  function computeEtag(payload) {
609457
611469
  const json = typeof payload === "string" ? payload : JSON.stringify(payload);
609458
- const hash = createHash23("sha1").update(json).digest("hex").slice(0, 16);
611470
+ const hash = createHash25("sha1").update(json).digest("hex").slice(0, 16);
609459
611471
  return `W/"${hash}"`;
609460
611472
  }
609461
611473
  function checkNotModified(req2, res, etag) {
@@ -616604,13 +618616,20 @@ async function sendMessage() {
616604
618616
  showStreamingIndicator(msgDiv, 'thinking');
616605
618617
 
616606
618618
  try {
618619
+ const attachmentContextBlock = _attachments.length > 0
618620
+ ? await uploadChatAttachments(chatSessionId)
618621
+ : '';
618622
+
616607
618623
  // Prepend context files as a FILES block so the agent can read them
616608
618624
  // without having to guess paths. The user picked these via right-click
616609
618625
  // "Add to context" on the workspace tree.
616610
618626
  let messageWithContext = sendText;
618627
+ if (attachmentContextBlock) {
618628
+ messageWithContext = attachmentContextBlock + '\\n\\n' + messageWithContext;
618629
+ }
616611
618630
  if (contextFiles.length > 0) {
616612
618631
  const filesBlock = 'FILES IN CONTEXT:\\n' + contextFiles.map(p => ' - ' + p).join('\\n') + '\\n\\n';
616613
- messageWithContext = filesBlock + sendText;
618632
+ messageWithContext = filesBlock + messageWithContext;
616614
618633
  }
616615
618634
 
616616
618635
  // FRESH-SESSION: pull the one-shot fresh flag the newChatSession()
@@ -621863,6 +623882,51 @@ function _removeAttachment(i) {
621863
623882
  _renderAttachChips();
621864
623883
  }
621865
623884
 
623885
+ function _fileToBase64(file) {
623886
+ return new Promise((resolve, reject) => {
623887
+ const reader = new FileReader();
623888
+ reader.onload = () => {
623889
+ const raw = String(reader.result || '');
623890
+ const comma = raw.indexOf(',');
623891
+ resolve(comma >= 0 ? raw.slice(comma + 1) : raw);
623892
+ };
623893
+ reader.onerror = () => reject(reader.error || new Error('file_read_failed'));
623894
+ reader.readAsDataURL(file);
623895
+ });
623896
+ }
623897
+
623898
+ async function uploadChatAttachments(sessionId) {
623899
+ if (!_attachments.length) return '';
623900
+ const blocks = [];
623901
+ for (const attachment of _attachments) {
623902
+ try {
623903
+ const base64 = await _fileToBase64(attachment.file);
623904
+ const response = await fetch('/v1/chat/attachments', {
623905
+ method: 'POST',
623906
+ headers: headers(),
623907
+ body: JSON.stringify({
623908
+ session_id: sessionId || '',
623909
+ filename: attachment.name,
623910
+ mimeType: attachment.file && attachment.file.type ? attachment.file.type : '',
623911
+ base64,
623912
+ }),
623913
+ });
623914
+ if (!response.ok) {
623915
+ blocks.push('GUI attachment failed: ' + attachment.name);
623916
+ continue;
623917
+ }
623918
+ const data = await response.json();
623919
+ blocks.push(data.contextBlock || ('GUI attachment saved: ' + (data.path || attachment.name)));
623920
+ } catch (err) {
623921
+ blocks.push('GUI attachment failed: ' + attachment.name + ' (' + ((err && err.message) ? err.message : String(err)) + ')');
623922
+ }
623923
+ }
623924
+ _attachments = [];
623925
+ _renderAttachChips();
623926
+ if (!blocks.length) return '';
623927
+ return 'GUI ATTACHMENTS:\\n' + blocks.map((block) => String(block).split('\\n').map((line) => ' ' + line).join('\\n')).join('\\n\\n');
623928
+ }
623929
+
621866
623930
  // Drag & drop on the input row
621867
623931
  (function _wireDropZone() {
621868
623932
  const row = document.getElementById('input-row');
@@ -623458,7 +625522,7 @@ __export(embedding_workers_exports, {
623458
625522
  startEmbeddingWorkers: () => startEmbeddingWorkers,
623459
625523
  stopEmbeddingWorkers: () => stopEmbeddingWorkers
623460
625524
  });
623461
- import { basename as basename28, join as join136 } from "node:path";
625525
+ import { basename as basename29, join as join136 } from "node:path";
623462
625526
  function startEmbeddingWorkers(opts) {
623463
625527
  if (_running) return;
623464
625528
  _running = true;
@@ -623524,7 +625588,7 @@ async function runEmbeddingTask(modality, episodeId, taskId, opts) {
623524
625588
  try {
623525
625589
  if (!_aligner) {
623526
625590
  const stateRoot = process.env.OMNIUS_DIR || process.cwd();
623527
- const omniusDir = basename28(stateRoot) === ".omnius" ? stateRoot : join136(stateRoot, ".omnius");
625591
+ const omniusDir = basename29(stateRoot) === ".omnius" ? stateRoot : join136(stateRoot, ".omnius");
623528
625592
  const memDir = join136(omniusDir, "memory");
623529
625593
  _aligner = new EmbeddingAligner(
623530
625594
  `${modality}-${emb.length}`,
@@ -623645,7 +625709,7 @@ import { homedir as homedir45 } from "node:os";
623645
625709
  import { spawn as spawn29, execSync as execSync57 } from "node:child_process";
623646
625710
  import { mkdirSync as mkdirSync74, writeFileSync as writeFileSync66, readFileSync as readFileSync99, readdirSync as readdirSync42, existsSync as existsSync122, watch as fsWatch3, renameSync as renameSync8, unlinkSync as unlinkSync25 } from "node:fs";
623647
625711
  import { randomBytes as randomBytes24, randomUUID as randomUUID16 } from "node:crypto";
623648
- import { createHash as createHash25 } from "node:crypto";
625712
+ import { createHash as createHash27 } from "node:crypto";
623649
625713
  function memoryDbPaths3(baseDir = process.cwd()) {
623650
625714
  const dir = join137(baseDir, ".omnius");
623651
625715
  return {
@@ -629518,7 +631582,7 @@ function listScheduledTasks() {
629518
631582
  const schedule = String(t2?.schedule || t2?.cron || t2?.when || "");
629519
631583
  const enabled2 = typeof t2?.enabled === "boolean" ? t2.enabled : true;
629520
631584
  const realId = typeof t2?.id === "string" && t2.id ? t2.id : null;
629521
- const fallbackId = createHash25("sha1").update(`${file}#${i2}`).digest("hex").slice(0, 16);
631585
+ const fallbackId = createHash27("sha1").update(`${file}#${i2}`).digest("hex").slice(0, 16);
629522
631586
  const uid = realId || fallbackId;
629523
631587
  const key = `${uid}`;
629524
631588
  if (seen.has(key)) return;
@@ -629635,8 +631699,8 @@ function deleteScheduledById(id) {
629635
631699
  if (id) candidates.push(id);
629636
631700
  if (typeof entry?.id === "string" && entry.id && !candidates.includes(entry.id)) candidates.push(entry.id);
629637
631701
  try {
629638
- const { createHash: createHash26 } = require4("node:crypto");
629639
- const fallback = createHash26("sha1").update(`${target.file}#${target.index}`).digest("hex").slice(0, 16);
631702
+ const { createHash: createHash28 } = require4("node:crypto");
631703
+ const fallback = createHash28("sha1").update(`${target.file}#${target.index}`).digest("hex").slice(0, 16);
629640
631704
  if (!candidates.includes(fallback)) candidates.push(fallback);
629641
631705
  } catch {
629642
631706
  }
@@ -630391,6 +632455,10 @@ function startApiServer(options2 = {}) {
630391
632455
  handleMemoryIngest(req2, res, ollamaUrl);
630392
632456
  return;
630393
632457
  }
632458
+ if (req2.method === "POST" && url.pathname === "/v1/chat/attachments") {
632459
+ handleChatAttachmentUpload(req2, res);
632460
+ return;
632461
+ }
630394
632462
  if (req2.method === "GET" && url.pathname === "/v1/memory/entities") {
630395
632463
  handleEntitiesList(req2, res);
630396
632464
  return;
@@ -630948,6 +633016,126 @@ async function handleAudioEmbed(req2, res) {
630948
633016
  jsonResponse(res, 400, { error: "bad_request", message: err instanceof Error ? err.message : String(err) });
630949
633017
  }
630950
633018
  }
633019
+ async function handleChatAttachmentUpload(req2, res) {
633020
+ try {
633021
+ const body = await parseJsonBody(req2);
633022
+ if (!body || typeof body !== "object") {
633023
+ jsonResponse(res, 400, { error: "bad_request" });
633024
+ return;
633025
+ }
633026
+ const b = body;
633027
+ const filename = typeof b.filename === "string" && b.filename.trim() ? b.filename.trim() : `attachment-${Date.now()}.bin`;
633028
+ const base642 = typeof b.base64 === "string" ? b.base64 : "";
633029
+ if (!base642) {
633030
+ jsonResponse(res, 400, { error: "missing_base64" });
633031
+ return;
633032
+ }
633033
+ const safeName2 = filename.replace(/[^a-zA-Z0-9._-]/g, "-").slice(0, 180) || `attachment-${Date.now()}.bin`;
633034
+ const dir = join137(process.cwd(), ".omnius", "gui-attachments");
633035
+ mkdirSync74(dir, { recursive: true });
633036
+ const localPath = join137(dir, `${Date.now()}-${randomUUID16().slice(0, 8)}-${safeName2}`);
633037
+ writeFileSync66(localPath, Buffer.from(base642, "base64"));
633038
+ const mimeType = typeof b.mimeType === "string" ? b.mimeType : typeof b.mime_type === "string" ? b.mime_type : "";
633039
+ const isImage = mimeType.toLowerCase().startsWith("image/") || /\.(png|jpe?g|gif|webp|bmp|tiff?)$/i.test(safeName2);
633040
+ const sessionId = typeof b.sessionId === "string" ? b.sessionId : typeof b.session_id === "string" ? b.session_id : void 0;
633041
+ const scope = typeof b.scope === "object" && b.scope ? b.scope : { kind: "gui", id: sessionId || "web", title: "GUI session" };
633042
+ const sender = typeof b.sender === "object" && b.sender ? b.sender : { id: "gui-user", displayName: "GUI user" };
633043
+ const message2 = {
633044
+ id: typeof b.message_id === "string" || typeof b.message_id === "number" ? b.message_id : `attachment:${randomUUID16()}`,
633045
+ timestamp: Date.now(),
633046
+ text: typeof b.caption === "string" && b.caption.trim() ? b.caption.trim() : `GUI attachment: ${safeName2}`
633047
+ };
633048
+ const media = {
633049
+ path: localPath,
633050
+ mediaType: isImage ? "photo" : "document",
633051
+ mimeType: mimeType || void 0,
633052
+ caption: typeof b.caption === "string" ? b.caption : void 0
633053
+ };
633054
+ const modality = isImage ? "visual" : "text";
633055
+ const { EpisodeStore: EpisodeStore3, TemporalGraph: TemporalGraph3, MultimodalIdentityService: MultimodalIdentityService2 } = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
633056
+ const dbPaths = memoryDbPaths3(process.cwd());
633057
+ const graph = new TemporalGraph3(dbPaths.knowledge);
633058
+ const store2 = new EpisodeStore3(dbPaths.episodes, graph);
633059
+ let ingestId = "";
633060
+ try {
633061
+ const embeddings = {};
633062
+ if (isImage) {
633063
+ try {
633064
+ const provider = globalThis.__omniusVisionProvider;
633065
+ const emb = provider ? await provider({ path: localPath }) : null;
633066
+ if (emb) embeddings.imageClip = emb;
633067
+ } catch {
633068
+ }
633069
+ }
633070
+ const service = new MultimodalIdentityService2({ episodeStore: store2, graph });
633071
+ ingestId = service.ingest({
633072
+ sourceSurface: "gui",
633073
+ sessionId,
633074
+ scope,
633075
+ sender,
633076
+ message: message2,
633077
+ modality,
633078
+ content: `GUI attachment: ${safeName2}`,
633079
+ media,
633080
+ labels: [safeName2, typeof b.caption === "string" ? b.caption : ""].filter(Boolean),
633081
+ embeddings
633082
+ }).episodeId;
633083
+ } finally {
633084
+ store2.close();
633085
+ graph.close();
633086
+ }
633087
+ let visualIdentity = void 0;
633088
+ let contextBlock = [
633089
+ `GUI attachment saved: ${safeName2}`,
633090
+ `path: ${localPath}`,
633091
+ mimeType ? `mime_type: ${mimeType}` : ""
633092
+ ].filter(Boolean).join("\n");
633093
+ if (isImage) {
633094
+ try {
633095
+ const { associateVisualIdentityFromImage: associateVisualIdentityFromImage2 } = await Promise.resolve().then(() => (init_visual_identity_association(), visual_identity_association_exports));
633096
+ const association = await associateVisualIdentityFromImage2({
633097
+ repoRoot: process.cwd(),
633098
+ imagePath: localPath,
633099
+ sourceSurface: "gui",
633100
+ scope,
633101
+ sender,
633102
+ message: message2,
633103
+ sessionId,
633104
+ media
633105
+ });
633106
+ visualIdentity = {
633107
+ matches: association.matches,
633108
+ recalledEpisodes: association.recalledEpisodes,
633109
+ committedEpisodeIds: association.committedEpisodeIds,
633110
+ contextBlock: association.contextBlock,
633111
+ degradedReason: association.degradedReason
633112
+ };
633113
+ if (association.contextBlock) contextBlock += `
633114
+
633115
+ ${association.contextBlock}`;
633116
+ } catch (err) {
633117
+ visualIdentity = {
633118
+ matches: [],
633119
+ recalledEpisodes: [],
633120
+ committedEpisodeIds: [],
633121
+ contextBlock: "",
633122
+ degradedReason: err instanceof Error ? err.message : String(err)
633123
+ };
633124
+ }
633125
+ }
633126
+ jsonResponse(res, 200, {
633127
+ id: ingestId,
633128
+ path: localPath,
633129
+ filename: safeName2,
633130
+ mimeType,
633131
+ modality,
633132
+ contextBlock,
633133
+ visualIdentity
633134
+ });
633135
+ } catch (err) {
633136
+ jsonResponse(res, 400, { error: "bad_request", message: err instanceof Error ? err.message : String(err) });
633137
+ }
633138
+ }
630951
633139
  async function handleMemoryIngest(req2, res, ollamaUrl) {
630952
633140
  try {
630953
633141
  const body = await parseJsonBody(req2);
@@ -630971,6 +633159,7 @@ async function handleMemoryIngest(req2, res, ollamaUrl) {
630971
633159
  };
630972
633160
  const media = Object.values(rawMedia).some((value2) => value2 != null && String(value2).trim() !== "") ? rawMedia : void 0;
630973
633161
  const sourceSurface = String(b.sourceSurface || b.source_surface || "api");
633162
+ const sessionId = typeof b.sessionId === "string" ? b.sessionId : typeof b.session_id === "string" ? b.session_id : void 0;
630974
633163
  const scope = typeof b.scope === "object" && b.scope ? b.scope : {
630975
633164
  kind: b.scope_kind || (b.chat_type === "private" ? "private" : b.chat_id ? "group" : "global"),
630976
633165
  id: String(b.scope_id || b.chat_id || sourceSurface),
@@ -631026,6 +633215,7 @@ async function handleMemoryIngest(req2, res, ollamaUrl) {
631026
633215
  const service = new MultimodalIdentityService2({ episodeStore: epStore, graph: kg });
631027
633216
  const result = service.ingest({
631028
633217
  sourceSurface,
633218
+ sessionId,
631029
633219
  scope,
631030
633220
  sender,
631031
633221
  message: message2,
@@ -631039,7 +633229,42 @@ async function handleMemoryIngest(req2, res, ollamaUrl) {
631039
633229
  identityAssertions,
631040
633230
  embeddings
631041
633231
  });
631042
- jsonResponse(res, 200, { id: result.episodeId, ...result });
633232
+ let visualIdentity = void 0;
633233
+ if (modality === "visual" && mediaPath) {
633234
+ try {
633235
+ const { associateVisualIdentityFromImage: associateVisualIdentityFromImage2 } = await Promise.resolve().then(() => (init_visual_identity_association(), visual_identity_association_exports));
633236
+ const association = await associateVisualIdentityFromImage2({
633237
+ repoRoot: process.cwd(),
633238
+ imagePath: mediaPath,
633239
+ sourceSurface,
633240
+ scope,
633241
+ sender,
633242
+ message: message2,
633243
+ replyTo,
633244
+ sessionId,
633245
+ media,
633246
+ extractedContent: b.extracted_content || b.extractedContent,
633247
+ threshold: typeof b.threshold === "number" ? b.threshold : 0.5,
633248
+ limit: typeof b.limit === "number" ? b.limit : 5
633249
+ });
633250
+ visualIdentity = {
633251
+ matches: association.matches,
633252
+ recalledEpisodes: association.recalledEpisodes,
633253
+ committedEpisodeIds: association.committedEpisodeIds,
633254
+ contextBlock: association.contextBlock,
633255
+ degradedReason: association.degradedReason
633256
+ };
633257
+ } catch (err) {
633258
+ visualIdentity = {
633259
+ matches: [],
633260
+ recalledEpisodes: [],
633261
+ committedEpisodeIds: [],
633262
+ contextBlock: "",
633263
+ degradedReason: err instanceof Error ? err.message : String(err)
633264
+ };
633265
+ }
633266
+ }
633267
+ jsonResponse(res, 200, { id: result.episodeId, ...result, visualIdentity });
631043
633268
  } catch (err) {
631044
633269
  jsonResponse(res, 400, { error: "bad_request", message: err instanceof Error ? err.message : String(err) });
631045
633270
  }
@@ -631690,6 +633915,7 @@ function buildSubAgentTools(repoRoot, config) {
631690
633915
  id: process.env["OMNIUS_SESSION_ID"] || "terminal",
631691
633916
  title: "TUI session"
631692
633917
  },
633918
+ sessionId: process.env["OMNIUS_SESSION_ID"] || "terminal",
631693
633919
  sender: { id: "local-user", displayName: "User" }
631694
633920
  }),
631695
633921
  new VideoUnderstandTool(repoRoot),
@@ -631842,6 +634068,7 @@ function buildTools(repoRoot, config, contextWindowSize, modelTier) {
631842
634068
  id: process.env["OMNIUS_SESSION_ID"] || "terminal",
631843
634069
  title: "TUI session"
631844
634070
  },
634071
+ sessionId: process.env["OMNIUS_SESSION_ID"] || "terminal",
631845
634072
  sender: { id: "local-user", displayName: "User" }
631846
634073
  }),
631847
634074
  new AsrListenTool(),
@@ -636637,8 +638864,28 @@ Log: ${nexusLogPath}`)
636637
638864
  visionContext = formatImageContextPrefix2(ingressResult);
636638
638865
  } catch {
636639
638866
  }
638867
+ let visualIdentityContext = "";
638868
+ try {
638869
+ const association = await associateVisualIdentityFromImage({
638870
+ repoRoot,
638871
+ imagePath: pasted.path,
638872
+ sourceSurface: "tui",
638873
+ scope: {
638874
+ kind: "terminal",
638875
+ id: process.env["OMNIUS_SESSION_ID"] || "terminal",
638876
+ title: "TUI session"
638877
+ },
638878
+ sender: { id: "local-user", displayName: "User" },
638879
+ message: { timestamp: Date.now(), text: "User pasted a clipboard image." },
638880
+ sessionId: process.env["OMNIUS_SESSION_ID"] || "terminal",
638881
+ media: { path: pasted.path, mediaType: "photo", mimeType: pasted.mime },
638882
+ extractedContent: visionContext
638883
+ });
638884
+ visualIdentityContext = association.contextBlock;
638885
+ } catch {
638886
+ }
636640
638887
  const asciiContext = await asciiContextPromise;
636641
- const imageContext = [visionContext, asciiContext].filter(Boolean).join("\n\n");
638888
+ const imageContext = [visionContext, visualIdentityContext, asciiContext].filter(Boolean).join("\n\n");
636642
638889
  if (activeTask) {
636643
638890
  activeTask.runner.injectImage(
636644
638891
  pasted.buffer.toString("base64"),
@@ -636966,6 +639213,17 @@ The user pasted a clipboard image saved at ${relPath}. Use the OCR, vision analy
636966
639213
  },
636967
639214
  // Telegram bridge
636968
639215
  async telegramStart(token, adminId) {
639216
+ if (telegramBridge?.isActive) {
639217
+ writeContent(
639218
+ () => renderWarning("Telegram bridge already active. Use /telegram stop before restarting.")
639219
+ );
639220
+ showPrompt();
639221
+ return;
639222
+ }
639223
+ if (telegramBridge) {
639224
+ telegramBridge.stop();
639225
+ telegramBridge = null;
639226
+ }
636969
639227
  telegramBridge = new TelegramBridge(
636970
639228
  token,
636971
639229
  (msg) => {
@@ -637094,9 +639352,9 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
637094
639352
  return `Skill invoked: ${result.name}`;
637095
639353
  }
637096
639354
  const raw = captured.join("");
637097
- const clean3 = raw.replace(/\x1B\[[0-9;]*[A-Za-z]/g, "").replace(/\x1B\][^\x07]*\x07/g, "").replace(/\x1B[()][A-Z0-9]/g, "").replace(/\x1B\[?\??[0-9;]*[a-zA-Z]/g, "").replace(/\x1B/g, "").replace(/[─━│┃┌┐└┘├┤┬┴┼╔╗╚╝╠╣╦╩╬⎿⎾▕▏⏐]/g, "").replace(/\n{3,}/g, "\n\n").trim();
637098
- if (!clean3) return null;
637099
- return clean3.length > 3900 ? clean3.slice(0, 3900) + "\n..." : clean3;
639355
+ const clean5 = raw.replace(/\x1B\[[0-9;]*[A-Za-z]/g, "").replace(/\x1B\][^\x07]*\x07/g, "").replace(/\x1B[()][A-Z0-9]/g, "").replace(/\x1B\[?\??[0-9;]*[a-zA-Z]/g, "").replace(/\x1B/g, "").replace(/[─━│┃┌┐└┘├┤┬┴┼╔╗╚╝╠╣╦╩╬⎿⎾▕▏⏐]/g, "").replace(/\n{3,}/g, "\n\n").trim();
639356
+ if (!clean5) return null;
639357
+ return clean5.length > 3900 ? clean5.slice(0, 3900) + "\n..." : clean5;
637100
639358
  } catch (err) {
637101
639359
  process.stdout.write = origWrite;
637102
639360
  throw err;
@@ -638806,12 +641064,32 @@ Execute this skill now. Follow the behavioral guidance above.`;
638806
641064
  "Shared image",
638807
641065
  writeContent
638808
641066
  );
641067
+ let visualIdentityContext = "";
641068
+ try {
641069
+ const association = await associateVisualIdentityFromImage({
641070
+ repoRoot,
641071
+ imagePath: imgPath,
641072
+ sourceSurface: "tui",
641073
+ scope: {
641074
+ kind: "terminal",
641075
+ id: process.env["OMNIUS_SESSION_ID"] || "terminal",
641076
+ title: "TUI session"
641077
+ },
641078
+ sender: { id: "local-user", displayName: "User" },
641079
+ message: { timestamp: Date.now(), text: `User shared image: ${cleanPath}` },
641080
+ sessionId: process.env["OMNIUS_SESSION_ID"] || "terminal",
641081
+ media: { path: imgPath, mediaType: "photo", mimeType: mime }
641082
+ });
641083
+ visualIdentityContext = association.contextBlock;
641084
+ } catch {
641085
+ }
641086
+ const combinedImageContext = [asciiContext, visualIdentityContext].filter(Boolean).join("\n\n");
638809
641087
  activeTask.runner.injectImage(
638810
641088
  base642,
638811
641089
  mime,
638812
- asciiContext ? `User shared image: ${cleanPath}
641090
+ combinedImageContext ? `User shared image: ${cleanPath}
638813
641091
 
638814
- ${asciiContext}` : `User shared image: ${cleanPath}`
641092
+ ${combinedImageContext}` : `User shared image: ${cleanPath}`
638815
641093
  );
638816
641094
  writeContent(() => renderUserInterrupt(`[Image: ${cleanPath}]`));
638817
641095
  } catch {
@@ -638960,15 +641238,38 @@ ${result.text}`;
638960
641238
  }
638961
641239
  let fullInput = input;
638962
641240
  if (isImage && fullInput === input) {
641241
+ const imgPath = resolve46(repoRoot, cleanPath);
638963
641242
  const asciiContext = await renderAsciiPreviewForImage(
638964
- resolve46(repoRoot, cleanPath),
641243
+ imgPath,
638965
641244
  cleanPath,
638966
641245
  "Image context",
638967
641246
  writeContent
638968
641247
  );
638969
- fullInput = asciiContext ? `The user has provided an image file: ${cleanPath}.
641248
+ let visualIdentityContext = "";
641249
+ try {
641250
+ const ext = extname17(cleanPath).toLowerCase();
641251
+ const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
641252
+ const association = await associateVisualIdentityFromImage({
641253
+ repoRoot,
641254
+ imagePath: imgPath,
641255
+ sourceSurface: "tui",
641256
+ scope: {
641257
+ kind: "terminal",
641258
+ id: process.env["OMNIUS_SESSION_ID"] || "terminal",
641259
+ title: "TUI session"
641260
+ },
641261
+ sender: { id: "local-user", displayName: "User" },
641262
+ message: { timestamp: Date.now(), text: `User provided image file: ${cleanPath}` },
641263
+ sessionId: process.env["OMNIUS_SESSION_ID"] || "terminal",
641264
+ media: { path: imgPath, mediaType: "photo", mimeType: mime }
641265
+ });
641266
+ visualIdentityContext = association.contextBlock;
641267
+ } catch {
641268
+ }
641269
+ const imageContext = [asciiContext, visualIdentityContext].filter(Boolean).join("\n\n");
641270
+ fullInput = imageContext ? `The user has provided an image file: ${cleanPath}.
638970
641271
 
638971
- ${asciiContext}
641272
+ ${imageContext}
638972
641273
 
638973
641274
  Read it with image_read or vision if more detail is needed. Describe what you see and extract any text with OCR if relevant.` : `The user has provided an image file: ${cleanPath}. Read it with image_read and describe what you see. Extract any text with OCR if relevant.`;
638974
641275
  }
@@ -639168,13 +641469,13 @@ NEW TASK: ${fullInput}`;
639168
641469
  writeContent(() => renderError(errMsg));
639169
641470
  if (failureStore) {
639170
641471
  try {
639171
- const { createHash: createHash26 } = await import("node:crypto");
641472
+ const { createHash: createHash28 } = await import("node:crypto");
639172
641473
  failureStore.insert({
639173
641474
  taskId: "",
639174
641475
  sessionId: `${Date.now()}`,
639175
641476
  repoRoot,
639176
641477
  failureType: "runtime-error",
639177
- fingerprint: createHash26("sha256").update(errMsg.slice(0, 200)).digest("hex").slice(0, 16),
641478
+ fingerprint: createHash28("sha256").update(errMsg.slice(0, 200)).digest("hex").slice(0, 16),
639178
641479
  filePath: null,
639179
641480
  errorMessage: errMsg.slice(0, 500),
639180
641481
  context: null,
@@ -639922,6 +642223,7 @@ var init_interactive = __esm({
639922
642223
  init_model_picker();
639923
642224
  init_project_context();
639924
642225
  init_identity_memory_tool();
642226
+ init_visual_identity_association();
639925
642227
  init_dist7();
639926
642228
  init_omnius_directory();
639927
642229
  init_render();