omnius 1.0.40 → 1.0.42

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
  }
@@ -565410,7 +565622,7 @@ var init_status_bar = __esm({
565410
565622
  return identity3.separatorOffsets.map((offset) => chrome.contentStartCol + offset).filter((col) => col > 1 && col < termWidth);
565411
565623
  }
565412
565624
  if (panel.meta.kind === "system" && this._sysSeparatorOffset !== null) {
565413
- const col = chrome.contentStartCol + this._sysSeparatorOffset + 1;
565625
+ const col = chrome.contentStartCol + this._sysSeparatorOffset;
565414
565626
  if (col > 1 && col < termWidth) return [col];
565415
565627
  }
565416
565628
  return [];
@@ -567324,10 +567536,10 @@ ${CONTENT_BG_SEQ}`);
567324
567536
  if (this._telegramStatus.active) {
567325
567537
  const suffix = this._telegramStatus.activeSubAgents > 0 ? ` ${this._telegramStatus.activeSubAgents}` : "";
567326
567538
  const label = `✈ tg${suffix}`;
567327
- const compact = `✈${suffix}`;
567539
+ const compact2 = `✈${suffix}`;
567328
567540
  sections.push({
567329
567541
  expanded: `\x1B[38;5;45m${label}\x1B[0m`,
567330
- compact: `\x1B[38;5;45m${compact}\x1B[0m`,
567542
+ compact: `\x1B[38;5;45m${compact2}\x1B[0m`,
567331
567543
  expandedW: 2 + 3 + suffix.length,
567332
567544
  compactW: 2 + suffix.length,
567333
567545
  empty: false
@@ -576056,17 +576268,17 @@ function toggleFocus(state) {
576056
576268
  toggleFocus(state);
576057
576269
  return;
576058
576270
  }
576059
- const clean3 = raw.replace(STDIN_MOUSE_FOCUS_RE, "");
576060
- if (!clean3) return;
576061
- fn(Buffer.from(clean3));
576271
+ const clean5 = raw.replace(STDIN_MOUSE_FOCUS_RE, "");
576272
+ if (!clean5) return;
576273
+ fn(Buffer.from(clean5));
576062
576274
  } else if (typeof args[0] === "string") {
576063
576275
  if (args[0] === "") {
576064
576276
  toggleFocus(state);
576065
576277
  return;
576066
576278
  }
576067
- const clean3 = args[0].replace(STDIN_MOUSE_FOCUS_RE, "");
576068
- if (!clean3) return;
576069
- fn(clean3);
576279
+ const clean5 = args[0].replace(STDIN_MOUSE_FOCUS_RE, "");
576280
+ if (!clean5) return;
576281
+ fn(clean5);
576070
576282
  } else {
576071
576283
  fn(...args);
576072
576284
  }
@@ -577844,8 +578056,8 @@ function normalizeAscii(ascii2, width, height) {
577844
578056
  return lines.slice(0, height).map((line) => line.length > width ? line.slice(0, width) : line).join("\n").trimEnd();
577845
578057
  }
577846
578058
  function previewFailure(message2, width) {
577847
- const clean3 = message2.replace(/\s+/g, " ").trim();
577848
- const text = `[image-to-ascii preview failed: ${clean3 || "unknown error"}]`;
578059
+ const clean5 = message2.replace(/\s+/g, " ").trim();
578060
+ const text = `[image-to-ascii preview failed: ${clean5 || "unknown error"}]`;
577849
578061
  return text.length > width ? `${text.slice(0, Math.max(0, width - 1))}]` : text;
577850
578062
  }
577851
578063
  async function convertWithImageToAscii(imagePath, width, height, timeoutMs) {
@@ -580212,10 +580424,10 @@ except Exception as exc:
580212
580424
  }));
580213
580425
  }
580214
580426
  saveSupertonicProfile(name10) {
580215
- const clean3 = name10.trim().replace(/[^a-zA-Z0-9_.-]/g, "-");
580216
- if (!clean3) return;
580427
+ const clean5 = name10.trim().replace(/[^a-zA-Z0-9_.-]/g, "-");
580428
+ if (!clean5) return;
580217
580429
  const store2 = this.loadSupertonicStore();
580218
- store2.profiles[clean3] = { ...store2.active };
580430
+ store2.profiles[clean5] = { ...store2.active };
580219
580431
  this.saveSupertonicStore(store2);
580220
580432
  }
580221
580433
  loadSupertonicProfile(name10) {
@@ -589926,14 +590138,14 @@ async function handleVoiceMenu(ctx3, save2, hasLocal) {
589926
590138
  if (!jsonDrop.confirmed || !jsonDrop.path) {
589927
590139
  continue;
589928
590140
  }
589929
- const { basename: basename29, join: pathJoin } = await import("node:path");
590141
+ const { basename: basename30, join: pathJoin } = await import("node:path");
589930
590142
  const {
589931
590143
  copyFileSync: copyFileSync5,
589932
590144
  mkdirSync: mkdirSync79,
589933
590145
  existsSync: exists2
589934
590146
  } = await import("node:fs");
589935
590147
  const { homedir: homedir48 } = await import("node:os");
589936
- const modelName = basename29(onnxDrop.path, ".onnx").replace(
590148
+ const modelName = basename30(onnxDrop.path, ".onnx").replace(
589937
590149
  /[^a-zA-Z0-9_-]/g,
589938
590150
  "-"
589939
590151
  );
@@ -590320,7 +590532,7 @@ async function handleVoiceList(ctx3, focusFilename) {
590320
590532
  copyFileSync: cpf,
590321
590533
  mkdirSync: mkd
590322
590534
  } = __require("node:fs");
590323
- const { basename: basename29, join: pjoin } = __require("node:path");
590535
+ const { basename: basename30, join: pjoin } = __require("node:path");
590324
590536
  if (!fe(src2)) {
590325
590537
  renderError(`File not found: ${src2}`);
590326
590538
  helpers.render();
@@ -590333,7 +590545,7 @@ async function handleVoiceList(ctx3, focusFilename) {
590333
590545
  "clone-refs"
590334
590546
  );
590335
590547
  mkd(refsDir, { recursive: true });
590336
- const destName = basename29(src2);
590548
+ const destName = basename30(src2);
590337
590549
  const dest = pjoin(refsDir, destName);
590338
590550
  cpf(src2, dest);
590339
590551
  renderInfo(`Imported "${destName}" → ${dest}`);
@@ -593244,9 +593456,9 @@ async function showExposeDashboard(gateway, rl, ctx3) {
593244
593456
  const recentEntries = [];
593245
593457
  for (let i2 = streamLog.length - 1; i2 >= 0 && recentEntries.length < streamAreaHeight - 1; i2--) {
593246
593458
  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;
593459
+ const clean5 = entry.content.replace(/\n/g, "⏎").replace(/[\x00-\x1F\x7F]/g, "");
593460
+ if (!clean5) continue;
593461
+ const truncated = clean5.length > maxContentW ? clean5.slice(0, maxContentW - 1) + "…" : clean5;
593250
593462
  recentEntries.unshift(` \x1B[38;5;245m${truncated}\x1B[0m`);
593251
593463
  }
593252
593464
  while (recentEntries.length < streamAreaHeight - 1)
@@ -593978,27 +594190,468 @@ var init_project_context = __esm({
593978
594190
  }
593979
594191
  });
593980
594192
 
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");
594193
+ // packages/cli/src/tui/memory-paths.ts
594194
+ import { mkdirSync as mkdirSync55 } from "node:fs";
594195
+ import { join as join112 } from "node:path";
594196
+ function omniusMemoryDbPaths(repoRoot) {
594197
+ const dir = join112(repoRoot, ".omnius");
593986
594198
  mkdirSync55(dir, { recursive: true });
593987
594199
  return {
594200
+ dir,
593988
594201
  episodes: join112(dir, "episodes.db"),
593989
594202
  knowledge: join112(dir, "knowledge.db")
593990
594203
  };
593991
594204
  }
594205
+ var init_memory_paths = __esm({
594206
+ "packages/cli/src/tui/memory-paths.ts"() {
594207
+ "use strict";
594208
+ }
594209
+ });
594210
+
594211
+ // packages/cli/src/tui/visual-identity-association.ts
594212
+ var visual_identity_association_exports = {};
594213
+ __export(visual_identity_association_exports, {
594214
+ associateVisualIdentityFromImage: () => associateVisualIdentityFromImage,
594215
+ formatVisualIdentityAssociationContext: () => formatVisualIdentityAssociationContext,
594216
+ stageVisualIdentityAssertion: () => stageVisualIdentityAssertion
594217
+ });
594218
+ import { basename as basename20 } from "node:path";
594219
+ function normalizePersonName(name10) {
594220
+ return name10.trim().toLowerCase().replace(/\s+/g, " ");
594221
+ }
593992
594222
  function personKey(name10) {
594223
+ return `person:${normalizePersonName(name10)}`;
594224
+ }
594225
+ function clamp016(value2, fallback = 0) {
594226
+ if (typeof value2 !== "number" || !Number.isFinite(value2)) return fallback;
594227
+ return Math.max(0, Math.min(1, value2));
594228
+ }
594229
+ function parseStructuredIdentifyResult(result) {
594230
+ const raw = String(result.llmContent || result.output || "").trim();
594231
+ if (!raw) return { matches: [], structured: false, raw };
594232
+ let parsed = null;
594233
+ try {
594234
+ parsed = JSON.parse(raw);
594235
+ } catch {
594236
+ return { matches: [], structured: false, raw };
594237
+ }
594238
+ const faces = Array.isArray(parsed.faces) ? parsed.faces : [];
594239
+ const matches = faces.filter((face) => face["identified"] === true && typeof face["name"] === "string" && String(face["name"]).trim()).map((face) => ({
594240
+ name: String(face["name"]).trim(),
594241
+ personId: typeof face["person_id"] === "string" ? face["person_id"] : void 0,
594242
+ confidence: clamp016(face["confidence"], 0),
594243
+ margin: typeof face["margin"] === "number" ? face["margin"] : void 0,
594244
+ bbox: Array.isArray(face["bbox"]) ? face["bbox"].map((n2) => Number(n2)).filter(Number.isFinite) : void 0
594245
+ }));
594246
+ return { matches, structured: true, raw };
594247
+ }
594248
+ function parseDetectedFaceCount(result) {
594249
+ const raw = String(result.llmContent || result.output || "").trim();
594250
+ if (!raw) return 0;
594251
+ try {
594252
+ const parsed = JSON.parse(raw);
594253
+ if (Array.isArray(parsed.faces)) return parsed.faces.length;
594254
+ if (typeof parsed.total === "number" && Number.isFinite(parsed.total)) return Math.max(0, Math.floor(parsed.total));
594255
+ } catch {
594256
+ }
594257
+ return 0;
594258
+ }
594259
+ function readMetadataObject(value2) {
594260
+ return value2 && typeof value2 === "object" ? value2 : {};
594261
+ }
594262
+ function sameScope(a2, b) {
594263
+ if (!b || !a2 || typeof a2 !== "object") return false;
594264
+ const raw = a2;
594265
+ return String(raw["kind"] ?? "") === String(b.kind) && String(raw["id"] ?? "") === String(b.id);
594266
+ }
594267
+ function episodeMatchesScope(ep, scope, sessionId) {
594268
+ if (!scope) return true;
594269
+ const metadata = readMetadataObject(ep.metadata);
594270
+ if (sameScope(metadata["scope"], scope)) return true;
594271
+ if (sessionId && ep.sessionId === sessionId) return true;
594272
+ const chatId = metadata["chat_id"] ?? metadata["chatId"];
594273
+ if ((scope.kind === "group" || scope.kind === "private") && chatId != null && String(chatId) === String(scope.id)) return true;
594274
+ const telegram = readMetadataObject(metadata["telegram"]);
594275
+ if ((scope.kind === "group" || scope.kind === "private") && telegram["chatId"] != null && String(telegram["chatId"]) === String(scope.id)) return true;
594276
+ return false;
594277
+ }
594278
+ function episodeSenderDisplay(ep) {
594279
+ const metadata = readMetadataObject(ep.metadata);
594280
+ const sender = readMetadataObject(metadata["sender"]);
594281
+ return String(sender["displayName"] || sender["username"] || sender["id"] || "").trim() || void 0;
594282
+ }
594283
+ function episodeMessageId(ep) {
594284
+ const metadata = readMetadataObject(ep.metadata);
594285
+ const message2 = readMetadataObject(metadata["message"]);
594286
+ return message2["id"];
594287
+ }
594288
+ function episodeSurface(ep) {
594289
+ const metadata = readMetadataObject(ep.metadata);
594290
+ return typeof metadata["sourceSurface"] === "string" ? metadata["sourceSurface"] : void 0;
594291
+ }
594292
+ function compactContent(value2, max = 220) {
594293
+ const clean5 = value2.replace(/\s+/g, " ").trim();
594294
+ return clean5.length > max ? `${clean5.slice(0, max - 1)}...` : clean5;
594295
+ }
594296
+ function pendingConsumedIds(store2, scope, sessionId) {
594297
+ const consumed = /* @__PURE__ */ new Set();
594298
+ for (const ep of store2.recent(250)) {
594299
+ if (!episodeMatchesScope(ep, scope, sessionId)) continue;
594300
+ const metadata = readMetadataObject(ep.metadata);
594301
+ const consumedMeta = readMetadataObject(metadata["identityPendingConsumed"]);
594302
+ const pendingId = String(consumedMeta["pendingId"] || "").trim();
594303
+ if (pendingId) consumed.add(pendingId);
594304
+ }
594305
+ return consumed;
594306
+ }
594307
+ function activePendingVisualIdentities(store2, scope, sessionId, limit = 3) {
594308
+ const now = Date.now();
594309
+ const consumed = pendingConsumedIds(store2, scope, sessionId);
594310
+ const pending = [];
594311
+ for (const ep of store2.recent(250)) {
594312
+ if (!episodeMatchesScope(ep, scope, sessionId)) continue;
594313
+ const metadata = readMetadataObject(ep.metadata);
594314
+ const meta = readMetadataObject(metadata["identityPending"]);
594315
+ if (String(meta["kind"] || "") !== "visual_identity") continue;
594316
+ const pendingId = String(meta["pendingId"] || "").trim();
594317
+ const name10 = String(meta["name"] || "").trim();
594318
+ if (!pendingId || !name10 || consumed.has(pendingId)) continue;
594319
+ const expiresAt = typeof meta["expiresAt"] === "number" ? meta["expiresAt"] : 0;
594320
+ if (expiresAt > 0 && expiresAt < now) continue;
594321
+ pending.push({
594322
+ pendingId,
594323
+ name: name10,
594324
+ relation: String(meta["relation"] || "depicts"),
594325
+ confidence: clamp016(meta["confidence"], 0.92),
594326
+ note: typeof meta["note"] === "string" ? meta["note"] : void 0,
594327
+ episodeId: ep.id,
594328
+ createdAt: ep.timestamp,
594329
+ expiresAt,
594330
+ sender: readMetadataObject(metadata["sender"]),
594331
+ message: readMetadataObject(metadata["message"])
594332
+ });
594333
+ if (pending.length >= limit) break;
594334
+ }
594335
+ return pending;
594336
+ }
594337
+ function recallPersonEpisodes(store2, graph, name10, scope, sessionId, excludedEpisodeIds, limit) {
594338
+ const person = graph.findNode(personKey(name10), "person");
594339
+ if (!person) return [];
594340
+ const edges = graph.currentEdges(person.id);
594341
+ const ids = /* @__PURE__ */ new Set();
594342
+ for (const edge of edges) {
594343
+ if (edge.sourceEpisodeId && !excludedEpisodeIds.has(edge.sourceEpisodeId)) ids.add(edge.sourceEpisodeId);
594344
+ }
594345
+ 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);
594346
+ return episodes.map((ep) => ({
594347
+ id: ep.id,
594348
+ timestamp: ep.timestamp,
594349
+ modality: ep.modality,
594350
+ content: compactContent(ep.gist || ep.content),
594351
+ sourceSurface: episodeSurface(ep),
594352
+ messageId: episodeMessageId(ep),
594353
+ senderDisplay: episodeSenderDisplay(ep)
594354
+ }));
594355
+ }
594356
+ function formatTimestamp2(ms) {
594357
+ try {
594358
+ return new Date(ms).toISOString();
594359
+ } catch {
594360
+ return String(ms);
594361
+ }
594362
+ }
594363
+ function formatVisualIdentityAssociationContext(result) {
594364
+ if (result.matches.length === 0 && result.pendingApplied.length === 0 && result.pendingSkipped.length === 0 && result.unknownFaceCount === 0) {
594365
+ return "";
594366
+ }
594367
+ const lines = [];
594368
+ if (result.pendingApplied.length > 0) {
594369
+ lines.push("## Pending Visual Identity Applied");
594370
+ 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.");
594371
+ for (const pending of result.pendingApplied) {
594372
+ lines.push(`- ${pending.name} confidence=${pending.confidence.toFixed(2)} pending_id=${pending.pendingId}`);
594373
+ }
594374
+ }
594375
+ if (result.matches.length > 0) {
594376
+ lines.push("## Scoped Visual Identity Recall");
594377
+ 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.");
594378
+ for (const match of result.matches) {
594379
+ const confidence2 = Number.isFinite(match.confidence) ? ` confidence=${match.confidence.toFixed(2)}` : "";
594380
+ const margin = typeof match.margin === "number" ? ` margin=${match.margin.toFixed(2)}` : "";
594381
+ lines.push(`- ${match.name}${confidence2}${margin}`);
594382
+ }
594383
+ if (result.recalledEpisodes.length) {
594384
+ lines.push("Scoped related memory:");
594385
+ for (const ep of result.recalledEpisodes) {
594386
+ const sender = ep.senderDisplay ? ` ${ep.senderDisplay}:` : "";
594387
+ const msg = ep.messageId != null ? ` message=${ep.messageId}` : "";
594388
+ lines.push(`- ${formatTimestamp2(ep.timestamp)}${msg}${sender} ${ep.content}`);
594389
+ }
594390
+ } else {
594391
+ lines.push("Scoped related memory: no prior same-scope episode found beyond the enrolled visual identity.");
594392
+ }
594393
+ }
594394
+ if (result.pendingSkipped.length > 0) {
594395
+ lines.push("## Pending Visual Identity Not Applied");
594396
+ for (const skipped of result.pendingSkipped) {
594397
+ lines.push(`- ${skipped.name}: ${skipped.reason}`);
594398
+ }
594399
+ }
594400
+ if (result.matches.length === 0 && result.pendingApplied.length === 0 && result.unknownFaceCount > 0) {
594401
+ lines.push("## Unknown Visual Identity Candidate");
594402
+ lines.push(`${result.unknownFaceCount} face(s) detected, but no prior enrolled identity matched in this scope.`);
594403
+ 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.");
594404
+ }
594405
+ lines.push("Use identity_memory only when the user explicitly names, stages, enrolls, asks to identify, or asks to recall identity evidence.");
594406
+ return lines.join("\n");
594407
+ }
594408
+ function stageVisualIdentityAssertion(options2) {
594409
+ const name10 = options2.name.trim();
594410
+ if (!name10) throw new Error("Name is required to stage a visual identity assertion.");
594411
+ const pendingId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
594412
+ const expiresAt = Date.now() + (typeof options2.expiresInMs === "number" && Number.isFinite(options2.expiresInMs) ? Math.max(6e4, Math.min(864e5, options2.expiresInMs)) : 72e5);
594413
+ const dbPaths = omniusMemoryDbPaths(options2.repoRoot);
594414
+ const graph = new TemporalGraph(dbPaths.knowledge);
594415
+ const store2 = new EpisodeStore(dbPaths.episodes, graph);
594416
+ try {
594417
+ const service = new MultimodalIdentityService({ episodeStore: store2, graph });
594418
+ const result = service.ingest({
594419
+ sourceSurface: options2.sourceSurface,
594420
+ sessionId: options2.sessionId,
594421
+ scope: options2.scope,
594422
+ sender: options2.sender,
594423
+ message: options2.message,
594424
+ replyTo: options2.replyTo,
594425
+ modality: "social",
594426
+ content: options2.note || `Pending visual identity assertion for next image: ${name10}`,
594427
+ labels: ["pending_visual_identity", name10],
594428
+ metadata: {
594429
+ identityPending: {
594430
+ kind: "visual_identity",
594431
+ pendingId,
594432
+ target: "next_visual_media",
594433
+ name: name10,
594434
+ relation: options2.relation || "depicts",
594435
+ confidence: clamp016(options2.confidence, 0.92),
594436
+ note: options2.note,
594437
+ createdAt: Date.now(),
594438
+ expiresAt
594439
+ }
594440
+ },
594441
+ identityAssertions: [{
594442
+ name: name10,
594443
+ relation: options2.relation || "named_as",
594444
+ confidence: clamp016(options2.confidence, 0.92),
594445
+ assertedBy: options2.sender,
594446
+ note: options2.note || "Explicit user-provided identity staged for the next visual media."
594447
+ }]
594448
+ });
594449
+ return { pendingId, episodeId: result.episodeId, name: name10, expiresAt };
594450
+ } finally {
594451
+ store2.close();
594452
+ graph.close();
594453
+ }
594454
+ }
594455
+ async function associateVisualIdentityFromImage(options2) {
594456
+ const startedAt2 = Date.now();
594457
+ const visual = options2.visualMemoryTool ?? new VisualMemoryTool();
594458
+ const identify2 = await visual.execute({
594459
+ action: "identify",
594460
+ image: options2.imagePath,
594461
+ threshold: typeof options2.threshold === "number" ? options2.threshold : 0.5,
594462
+ format: "json"
594463
+ });
594464
+ const parsed = parseStructuredIdentifyResult(identify2);
594465
+ if (!identify2.success) {
594466
+ return {
594467
+ matches: [],
594468
+ unknownFaceCount: 0,
594469
+ pendingApplied: [],
594470
+ pendingSkipped: [],
594471
+ recalledEpisodes: [],
594472
+ committedEpisodeIds: [],
594473
+ structured: parsed.structured,
594474
+ contextBlock: "",
594475
+ degradedReason: identify2.error || "visual_memory identify failed",
594476
+ rawIdentifyOutput: parsed.raw
594477
+ };
594478
+ }
594479
+ if (!parsed.structured) {
594480
+ return {
594481
+ matches: [],
594482
+ unknownFaceCount: 0,
594483
+ pendingApplied: [],
594484
+ pendingSkipped: [],
594485
+ recalledEpisodes: [],
594486
+ committedEpisodeIds: [],
594487
+ structured: false,
594488
+ contextBlock: "",
594489
+ degradedReason: "visual_memory identify did not return structured JSON",
594490
+ rawIdentifyOutput: parsed.raw
594491
+ };
594492
+ }
594493
+ const dbPaths = omniusMemoryDbPaths(options2.repoRoot);
594494
+ const graph = new TemporalGraph(dbPaths.knowledge);
594495
+ const store2 = new EpisodeStore(dbPaths.episodes, graph);
594496
+ const committedEpisodeIds = [];
594497
+ const pendingApplied = [];
594498
+ const pendingSkipped = [];
594499
+ let unknownFaceCount = 0;
594500
+ try {
594501
+ const shouldCommit = options2.commitMatches !== false;
594502
+ const service = new MultimodalIdentityService({ episodeStore: store2, graph });
594503
+ if (parsed.matches.length === 0) {
594504
+ try {
594505
+ const detect2 = await visual.execute({ action: "detect", image: options2.imagePath, format: "json" });
594506
+ if (detect2.success) unknownFaceCount = parseDetectedFaceCount(detect2);
594507
+ } catch {
594508
+ }
594509
+ }
594510
+ if (shouldCommit && parsed.matches.length > 0) {
594511
+ for (const match of parsed.matches) {
594512
+ const result2 = service.ingest({
594513
+ sourceSurface: options2.sourceSurface,
594514
+ sessionId: options2.sessionId,
594515
+ scope: options2.scope,
594516
+ sender: options2.sender,
594517
+ message: options2.message,
594518
+ replyTo: options2.replyTo,
594519
+ modality: "visual",
594520
+ content: `Prior enrolled visual identity match: ${match.name}`,
594521
+ extractedContent: options2.extractedContent,
594522
+ media: options2.media ?? { path: options2.imagePath, mediaType: "photo" },
594523
+ labels: ["visual_identity_match", match.name],
594524
+ metadata: {
594525
+ visualIdentityAssociation: {
594526
+ version: 1,
594527
+ imagePath: options2.imagePath,
594528
+ name: match.name,
594529
+ personId: match.personId,
594530
+ confidence: match.confidence,
594531
+ margin: match.margin,
594532
+ elapsedMs: Date.now() - startedAt2
594533
+ }
594534
+ },
594535
+ identityAssertions: [{
594536
+ name: match.name,
594537
+ relation: "same_person_candidate",
594538
+ confidence: match.confidence,
594539
+ assertedBy: { id: "visual_memory", displayName: "visual_memory", isBot: true },
594540
+ note: `Prior enrolled visual-memory face match for ${basename20(options2.imagePath)}`
594541
+ }]
594542
+ });
594543
+ if (result2.episodeId) committedEpisodeIds.push(result2.episodeId);
594544
+ }
594545
+ }
594546
+ if (shouldCommit && parsed.matches.length === 0) {
594547
+ const pending = activePendingVisualIdentities(store2, options2.scope, options2.sessionId);
594548
+ for (const item of pending) {
594549
+ const enrollment = await visual.execute({ action: "enroll", image: options2.imagePath, name: item.name });
594550
+ if (!enrollment.success) {
594551
+ pendingSkipped.push({
594552
+ pendingId: item.pendingId,
594553
+ name: item.name,
594554
+ reason: enrollment.error || enrollment.output || "face enrollment failed"
594555
+ });
594556
+ continue;
594557
+ }
594558
+ const result2 = service.ingest({
594559
+ sourceSurface: options2.sourceSurface,
594560
+ sessionId: options2.sessionId,
594561
+ scope: options2.scope,
594562
+ sender: options2.sender,
594563
+ message: options2.message,
594564
+ replyTo: options2.replyTo,
594565
+ modality: "visual",
594566
+ content: `Applied pending visual identity assertion: ${item.name}`,
594567
+ extractedContent: options2.extractedContent,
594568
+ media: options2.media ?? { path: options2.imagePath, mediaType: "photo" },
594569
+ labels: ["pending_visual_identity_applied", item.name],
594570
+ metadata: {
594571
+ identityPendingConsumed: {
594572
+ pendingId: item.pendingId,
594573
+ sourceEpisodeId: item.episodeId,
594574
+ name: item.name,
594575
+ imagePath: options2.imagePath,
594576
+ consumedAt: Date.now(),
594577
+ enrollmentOutput: enrollment.output
594578
+ }
594579
+ },
594580
+ identityAssertions: [{
594581
+ name: item.name,
594582
+ relation: "depicts",
594583
+ confidence: item.confidence,
594584
+ assertedBy: item.sender || options2.sender,
594585
+ note: item.note || `Applied explicit pending identity assertion to ${basename20(options2.imagePath)}`
594586
+ }]
594587
+ });
594588
+ if (result2.episodeId) committedEpisodeIds.push(result2.episodeId);
594589
+ pendingApplied.push({
594590
+ pendingId: item.pendingId,
594591
+ name: item.name,
594592
+ confidence: item.confidence,
594593
+ sourceEpisodeId: item.episodeId,
594594
+ committedEpisodeId: result2.episodeId,
594595
+ enrollmentOutput: enrollment.output
594596
+ });
594597
+ }
594598
+ }
594599
+ const excluded = new Set(committedEpisodeIds);
594600
+ const limit = typeof options2.limit === "number" && Number.isFinite(options2.limit) ? Math.max(1, Math.min(12, Math.floor(options2.limit))) : 5;
594601
+ const recalledEpisodes = [];
594602
+ const seen = /* @__PURE__ */ new Set();
594603
+ const namesForRecall = [
594604
+ ...parsed.matches.map((match) => match.name),
594605
+ ...pendingApplied.map((pending) => pending.name)
594606
+ ];
594607
+ for (const name10 of namesForRecall) {
594608
+ for (const ep of recallPersonEpisodes(store2, graph, name10, options2.scope, options2.sessionId, excluded, limit)) {
594609
+ if (seen.has(ep.id)) continue;
594610
+ seen.add(ep.id);
594611
+ recalledEpisodes.push(ep);
594612
+ }
594613
+ }
594614
+ const result = {
594615
+ matches: parsed.matches,
594616
+ unknownFaceCount,
594617
+ pendingApplied,
594618
+ pendingSkipped,
594619
+ recalledEpisodes: recalledEpisodes.slice(0, limit),
594620
+ committedEpisodeIds,
594621
+ structured: true,
594622
+ contextBlock: "",
594623
+ rawIdentifyOutput: parsed.raw
594624
+ };
594625
+ result.contextBlock = formatVisualIdentityAssociationContext(result);
594626
+ return result;
594627
+ } finally {
594628
+ store2.close();
594629
+ graph.close();
594630
+ }
594631
+ }
594632
+ var init_visual_identity_association = __esm({
594633
+ "packages/cli/src/tui/visual-identity-association.ts"() {
594634
+ "use strict";
594635
+ init_dist7();
594636
+ init_dist5();
594637
+ init_memory_paths();
594638
+ }
594639
+ });
594640
+
594641
+ // packages/cli/src/tui/identity-memory-tool.ts
594642
+ import { existsSync as existsSync98 } from "node:fs";
594643
+ import { basename as basename21, extname as extname14, resolve as resolve39 } from "node:path";
594644
+ function personKey2(name10) {
593993
594645
  return `person:${name10.trim().toLowerCase().replace(/\s+/g, " ")}`;
593994
594646
  }
593995
594647
  function coerceAction(value2) {
593996
594648
  const raw = String(value2 || "recall").trim().toLowerCase();
593997
594649
  if (raw === "assert" || raw === "remember" || raw === "name") return "assert_identity";
594650
+ if (raw === "stage" || raw === "pending" || raw === "next" || raw === "expect") return "stage_identity";
593998
594651
  if (raw === "enroll" || raw === "enrol" || raw === "face") return "enroll_face";
593999
594652
  if (raw === "who" || raw === "recognize" || raw === "recognise") return "identify";
594000
594653
  if (raw === "list") return "inspect";
594001
- if (raw === "assert_identity" || raw === "enroll_face" || raw === "identify" || raw === "recall" || raw === "inspect") {
594654
+ if (raw === "assert_identity" || raw === "stage_identity" || raw === "enroll_face" || raw === "identify" || raw === "recall" || raw === "inspect") {
594002
594655
  return raw;
594003
594656
  }
594004
594657
  return "recall";
@@ -594056,7 +594709,7 @@ async function resolveMediaFromArgs(args, opts) {
594056
594709
  path: path11,
594057
594710
  media,
594058
594711
  modality: inferModality(media),
594059
- label: basename20(path11)
594712
+ label: basename21(path11)
594060
594713
  };
594061
594714
  }
594062
594715
  function edgeDirection(edge, nodeId, otherText) {
@@ -594067,7 +594720,10 @@ function formatIdentityMemoryContext(surface) {
594067
594720
  return [
594068
594721
  "Identity memory contract:",
594069
594722
  "- 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.",
594723
+ "- 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.",
594724
+ "- 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.",
594725
+ "- 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.",
594726
+ "- 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
594727
  "- 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
594728
  "- 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
594729
  "- For 'who is this?' on an image, call identity_memory with action='identify'.",
@@ -594080,6 +594736,8 @@ var init_identity_memory_tool = __esm({
594080
594736
  "use strict";
594081
594737
  init_dist7();
594082
594738
  init_dist5();
594739
+ init_memory_paths();
594740
+ init_visual_identity_association();
594083
594741
  IdentityMemoryTool = class {
594084
594742
  constructor(repoRoot, opts = {}) {
594085
594743
  this.repoRoot = repoRoot;
@@ -594088,14 +594746,14 @@ var init_identity_memory_tool = __esm({
594088
594746
  repoRoot;
594089
594747
  opts;
594090
594748
  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.";
594749
+ 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
594750
  parameters = {
594093
594751
  type: "object",
594094
594752
  properties: {
594095
594753
  action: {
594096
594754
  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."
594755
+ enum: ["assert_identity", "stage_identity", "enroll_face", "identify", "recall", "inspect"],
594756
+ 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
594757
  },
594100
594758
  name: {
594101
594759
  type: "string",
@@ -594133,6 +594791,10 @@ var init_identity_memory_tool = __esm({
594133
594791
  limit: {
594134
594792
  type: "number",
594135
594793
  description: "Recall/inspect result limit. Default 12."
594794
+ },
594795
+ expires_in_minutes: {
594796
+ type: "number",
594797
+ description: "For stage_identity, how long the pending next-image assertion remains active. Default 120 minutes."
594136
594798
  }
594137
594799
  },
594138
594800
  required: ["action"]
@@ -594140,7 +594802,7 @@ var init_identity_memory_tool = __esm({
594140
594802
  async execute(args) {
594141
594803
  const startedAt2 = Date.now();
594142
594804
  const action = coerceAction(args["action"]);
594143
- const dbPaths = memoryDbPaths(this.repoRoot);
594805
+ const dbPaths = omniusMemoryDbPaths(this.repoRoot);
594144
594806
  const store2 = new EpisodeStore(dbPaths.episodes);
594145
594807
  const graph = new TemporalGraph(dbPaths.knowledge);
594146
594808
  try {
@@ -594150,6 +594812,9 @@ var init_identity_memory_tool = __esm({
594150
594812
  if (action === "recall" || action === "inspect") {
594151
594813
  return this.recall(args, startedAt2, graph);
594152
594814
  }
594815
+ if (action === "stage_identity") {
594816
+ return this.stageIdentity(args, startedAt2);
594817
+ }
594153
594818
  return await this.assertIdentity(action, args, startedAt2, store2, graph);
594154
594819
  } finally {
594155
594820
  store2.close();
@@ -594176,6 +594841,7 @@ var init_identity_memory_tool = __esm({
594176
594841
  const content = evidenceNote || this.opts.message?.text || (media?.caption ? `Identity assertion for ${name10}: ${media.caption}` : `Identity assertion for ${name10}`);
594177
594842
  const result = service.ingest({
594178
594843
  sourceSurface: this.opts.sourceSurface || "api",
594844
+ sessionId: this.opts.sessionId,
594179
594845
  scope: this.opts.scope,
594180
594846
  sender: this.opts.sender,
594181
594847
  message: this.opts.message,
@@ -594201,7 +594867,7 @@ var init_identity_memory_tool = __esm({
594201
594867
  } else if (shouldEnrollFace) {
594202
594868
  faceLine = "face enrollment: skipped because no resolved image path was available";
594203
594869
  }
594204
- const mediaLine = resolvedMedia ? `media: ${resolvedMedia.label || basename20(resolvedMedia.path)} (${resolvedMedia.path})` : "media: none; stored as scoped textual identity evidence only";
594870
+ const mediaLine = resolvedMedia ? `media: ${resolvedMedia.label || basename21(resolvedMedia.path)} (${resolvedMedia.path})` : "media: none; stored as scoped textual identity evidence only";
594205
594871
  const output = [
594206
594872
  `Stored identity evidence for ${name10}.`,
594207
594873
  `relation: ${relation}`,
@@ -594222,6 +594888,50 @@ var init_identity_memory_tool = __esm({
594222
594888
  durationMs: Date.now() - startedAt2
594223
594889
  };
594224
594890
  }
594891
+ stageIdentity(args, startedAt2) {
594892
+ const name10 = String(args["name"] || "").trim();
594893
+ if (!name10) {
594894
+ return {
594895
+ success: false,
594896
+ output: "",
594897
+ error: "identity_memory stage_identity requires name. Pass the explicit user-provided name for the next visual media.",
594898
+ durationMs: Date.now() - startedAt2
594899
+ };
594900
+ }
594901
+ const evidenceNote = String(args["evidence_note"] ?? args["note"] ?? this.opts.message?.text ?? "").trim();
594902
+ 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;
594903
+ const result = stageVisualIdentityAssertion({
594904
+ repoRoot: this.repoRoot,
594905
+ sourceSurface: this.opts.sourceSurface || "api",
594906
+ sessionId: this.opts.sessionId,
594907
+ scope: this.opts.scope,
594908
+ sender: this.opts.sender,
594909
+ message: this.opts.message,
594910
+ replyTo: this.opts.replyTo,
594911
+ name: name10,
594912
+ relation: "depicts",
594913
+ confidence: coerceConfidence(args["confidence"]),
594914
+ note: evidenceNote || `Explicit user-provided name for the next visual media: ${name10}`,
594915
+ expiresInMs: expiresMinutes * 6e4
594916
+ });
594917
+ return {
594918
+ success: true,
594919
+ output: [
594920
+ `Staged visual identity for next same-scope image: ${result.name}.`,
594921
+ `pending_id: ${result.pendingId}`,
594922
+ `episode: ${result.episodeId}`,
594923
+ `expires_at: ${new Date(result.expiresAt).toISOString()}`
594924
+ ].join("\n"),
594925
+ llmContent: [
594926
+ `identity_memory staged next_visual_media name=${result.name}`,
594927
+ `pending_id=${result.pendingId}`,
594928
+ `episode_id=${result.episodeId}`,
594929
+ `expires_at=${new Date(result.expiresAt).toISOString()}`,
594930
+ "When the next image arrives in this same scope, image ingress can link and enroll it without guessing from text."
594931
+ ].join("\n"),
594932
+ durationMs: Date.now() - startedAt2
594933
+ };
594934
+ }
594225
594935
  async identify(args, startedAt2, graph) {
594226
594936
  const resolvedMedia = await resolveMediaFromArgs(args, this.opts);
594227
594937
  if (!resolvedMedia?.path) {
@@ -594240,23 +594950,40 @@ var init_identity_memory_tool = __esm({
594240
594950
  durationMs: Date.now() - startedAt2
594241
594951
  };
594242
594952
  }
594243
- const visual = this.opts.visualMemoryTool ?? new VisualMemoryTool();
594244
594953
  const threshold = typeof args["threshold"] === "number" ? args["threshold"] : 0.5;
594245
- const visualResult = await visual.execute({ action: "identify", image: resolvedMedia.path, threshold });
594954
+ const association = await associateVisualIdentityFromImage({
594955
+ repoRoot: this.repoRoot,
594956
+ imagePath: resolvedMedia.path,
594957
+ sourceSurface: this.opts.sourceSurface || "api",
594958
+ scope: this.opts.scope,
594959
+ sender: this.opts.sender,
594960
+ message: this.opts.message,
594961
+ replyTo: this.opts.replyTo,
594962
+ sessionId: this.opts.sessionId,
594963
+ media: resolvedMedia.media ?? { path: resolvedMedia.path, mediaType: inferMediaTypeFromPath(resolvedMedia.path) },
594964
+ extractedContent: resolvedMedia.extractedContent,
594965
+ threshold,
594966
+ visualMemoryTool: this.opts.visualMemoryTool,
594967
+ limit: typeof args["limit"] === "number" ? args["limit"] : 5
594968
+ });
594246
594969
  const known = graph.nodesByType("person", 50).filter((node) => node.text.startsWith("person:")).map((node) => node.text.slice("person:".length)).slice(0, 12);
594247
594970
  const knownLine = known.length ? `known identity nodes: ${known.join(", ")}` : "known identity nodes: none";
594971
+ const matchLine = association.matches.length ? `visual matches: ${association.matches.map((match) => `${match.name} (${match.confidence.toFixed(2)})`).join(", ")}` : "visual matches: none";
594972
+ const context2 = association.contextBlock || association.degradedReason || "No enrolled face matched this image.";
594248
594973
  return {
594249
- success: visualResult.success,
594974
+ success: !association.degradedReason,
594250
594975
  output: [
594251
- visualResult.output || visualResult.error || "No visual identification result.",
594976
+ matchLine,
594977
+ context2,
594252
594978
  knownLine
594253
594979
  ].filter(Boolean).join("\n"),
594254
594980
  llmContent: [
594255
- visualResult.llmContent || visualResult.output || visualResult.error || "No visual identification result.",
594981
+ matchLine,
594982
+ association.contextBlock || association.degradedReason || "No enrolled face matched this image.",
594256
594983
  knownLine,
594257
594984
  "If no face matched and the user supplied a name, call identity_memory action='assert_identity'."
594258
594985
  ].join("\n"),
594259
- error: visualResult.success ? void 0 : visualResult.error,
594986
+ error: association.degradedReason,
594260
594987
  durationMs: Date.now() - startedAt2
594261
594988
  };
594262
594989
  }
@@ -594272,7 +594999,7 @@ ${people.join("\n")}` : "No stored explicit identity nodes found.",
594272
594999
  durationMs: Date.now() - startedAt2
594273
595000
  };
594274
595001
  }
594275
- const node = graph.findNode(personKey(name10), "person");
595002
+ const node = graph.findNode(personKey2(name10), "person");
594276
595003
  if (!node) {
594277
595004
  return {
594278
595005
  success: true,
@@ -595081,7 +595808,7 @@ var init_banner = __esm({
595081
595808
 
595082
595809
  // packages/cli/src/tui/carousel-descriptors.ts
595083
595810
  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";
595811
+ import { join as join114, basename as basename22 } from "node:path";
595085
595812
  function loadToolProfile(repoRoot) {
595086
595813
  const filePath = join114(repoRoot, OMNIUS_DIR, "context", TOOL_PROFILE_FILE);
595087
595814
  try {
@@ -595177,7 +595904,7 @@ function generateDescriptors(repoRoot) {
595177
595904
  extractFromSessions(repoRoot, tags);
595178
595905
  extractFromMemory(repoRoot, tags);
595179
595906
  extractFromToolProfile(profile, tags);
595180
- const repoName2 = basename21(repoRoot);
595907
+ const repoName2 = basename22(repoRoot);
595181
595908
  if (repoName2 && !tags.includes(repoName2)) {
595182
595909
  tags.push(repoName2);
595183
595910
  }
@@ -596197,7 +596924,7 @@ var init_promptLoader3 = __esm({
596197
596924
 
596198
596925
  // packages/cli/src/tui/dream-engine.ts
596199
596926
  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";
596927
+ import { join as join117, basename as basename23 } from "node:path";
596201
596928
  import { execSync as execSync54 } from "node:child_process";
596202
596929
  function setDreamWriteContent(fn) {
596203
596930
  _dreamWriteContent = fn;
@@ -596410,7 +597137,7 @@ var init_dream_engine = __esm({
596410
597137
  const rawPath = String(args["path"] ?? "");
596411
597138
  const content = String(args["content"] ?? "");
596412
597139
  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);
597140
+ const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".omnius/autoresearch") ? join117(this.autoresearchDir, basename23(rawPath)) : join117(this.autoresearchDir, rawPath);
596414
597141
  if (!targetPath.startsWith(this.autoresearchDir)) {
596415
597142
  return { success: false, output: "", error: "Autoresearch mode: writes are confined to .omnius/autoresearch/", durationMs: Date.now() - start2 };
596416
597143
  }
@@ -596445,7 +597172,7 @@ var init_dream_engine = __esm({
596445
597172
  const rawPath = String(args["path"] ?? "");
596446
597173
  const oldStr = String(args["old_string"] ?? "");
596447
597174
  const newStr = String(args["new_string"] ?? "");
596448
- const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".omnius/autoresearch") ? join117(this.autoresearchDir, basename22(rawPath)) : join117(this.autoresearchDir, rawPath);
597175
+ const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".omnius/autoresearch") ? join117(this.autoresearchDir, basename23(rawPath)) : join117(this.autoresearchDir, rawPath);
596449
597176
  if (!targetPath.startsWith(this.autoresearchDir)) {
596450
597177
  return { success: false, output: "", error: "Autoresearch mode: edits are confined to .omnius/autoresearch/", durationMs: Date.now() - start2 };
596451
597178
  }
@@ -596498,7 +597225,7 @@ var init_dream_engine = __esm({
596498
597225
  const rawPath = String(args["path"] ?? "");
596499
597226
  const content = String(args["content"] ?? "");
596500
597227
  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);
597228
+ const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".omnius/dreams") ? join117(this.dreamsDir, basename23(rawPath)) : join117(this.dreamsDir, rawPath);
596502
597229
  if (!targetPath.startsWith(this.dreamsDir)) {
596503
597230
  return { success: false, output: "", error: "Dream mode: writes are confined to .omnius/dreams/", durationMs: Date.now() - start2 };
596504
597231
  }
@@ -596533,7 +597260,7 @@ var init_dream_engine = __esm({
596533
597260
  const rawPath = String(args["path"] ?? "");
596534
597261
  const oldStr = String(args["old_string"] ?? "");
596535
597262
  const newStr = String(args["new_string"] ?? "");
596536
- const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".omnius/dreams") ? join117(this.dreamsDir, basename22(rawPath)) : join117(this.dreamsDir, rawPath);
597263
+ const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".omnius/dreams") ? join117(this.dreamsDir, basename23(rawPath)) : join117(this.dreamsDir, rawPath);
596537
597264
  if (!targetPath.startsWith(this.dreamsDir)) {
596538
597265
  return { success: false, output: "", error: "Dream mode: edits are confined to .omnius/dreams/", durationMs: Date.now() - start2 };
596539
597266
  }
@@ -598130,7 +598857,7 @@ var init_bless_engine = __esm({
598130
598857
 
598131
598858
  // packages/cli/src/tui/dmn-engine.ts
598132
598859
  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";
598860
+ import { join as join118, basename as basename24 } from "node:path";
598134
598861
  function buildDMNGatherPrompt(recentTaskSummaries, dueReminders, attentionItems, memoryTopics, capabilities, competence, reflectionBuffer) {
598135
598862
  const competenceReport = competence.length > 0 ? competence.map((c9) => {
598136
598863
  const rate = c9.attempts > 0 ? Math.round(c9.successes / c9.attempts * 100) : 0;
@@ -598881,7 +599608,7 @@ OUTPUT: Call task_complete with JSON:
598881
599608
  try {
598882
599609
  const files = readdirSync34(dir).filter((f2) => f2.endsWith(".json"));
598883
599610
  for (const f2 of files) {
598884
- const topic = basename23(f2, ".json");
599611
+ const topic = basename24(f2, ".json");
598885
599612
  if (!topics.includes(topic)) topics.push(topic);
598886
599613
  }
598887
599614
  } catch {
@@ -598935,7 +599662,7 @@ OUTPUT: Call task_complete with JSON:
598935
599662
 
598936
599663
  // packages/cli/src/tui/snr-engine.ts
598937
599664
  import { existsSync as existsSync104, readdirSync as readdirSync35, readFileSync as readFileSync84 } from "node:fs";
598938
- import { join as join119, basename as basename24 } from "node:path";
599665
+ import { join as join119, basename as basename25 } from "node:path";
598939
599666
  function computeDPrime(signalScores, noiseScores) {
598940
599667
  if (signalScores.length === 0 || noiseScores.length === 0) return 0;
598941
599668
  const mean = (arr) => arr.reduce((s2, v) => s2 + v, 0) / arr.length;
@@ -599229,7 +599956,7 @@ Call task_complete with the JSON array when done.`,
599229
599956
  try {
599230
599957
  const files = readdirSync35(dir).filter((f2) => f2.endsWith(".json"));
599231
599958
  for (const f2 of files) {
599232
- const topic = basename24(f2, ".json");
599959
+ const topic = basename25(f2, ".json");
599233
599960
  if (topics.length > 0 && !topics.includes(topic)) continue;
599234
599961
  try {
599235
599962
  const data = JSON.parse(readFileSync84(join119(dir, f2), "utf-8"));
@@ -599861,7 +600588,7 @@ import {
599861
600588
  } from "node:fs";
599862
600589
  import { mkdir as mkdir17 } from "node:fs/promises";
599863
600590
  import {
599864
- basename as basename25,
600591
+ basename as basename26,
599865
600592
  dirname as dirname33,
599866
600593
  extname as extname15,
599867
600594
  isAbsolute as isAbsolute6,
@@ -600078,7 +600805,7 @@ function guardPath(root, rawPath) {
600078
600805
  error: `Path escapes the public creative workspace. Use a relative path under ${rootAbs}.`
600079
600806
  };
600080
600807
  }
600081
- if (basename25(abs) === MANIFEST_FILE) {
600808
+ if (basename26(abs) === MANIFEST_FILE) {
600082
600809
  return { ok: false, error: "The creative workspace manifest is internal and cannot be edited." };
600083
600810
  }
600084
600811
  return { ok: true, path: { abs, rel } };
@@ -600170,7 +600897,7 @@ function rememberCreated(root, absPath) {
600170
600897
  manifest.objects[rel] = {
600171
600898
  logicalRel: rel,
600172
600899
  storedRel,
600173
- originalName: basename25(guarded.path.abs),
600900
+ originalName: basename26(guarded.path.abs),
600174
600901
  prefixBytes: prefix.length,
600175
600902
  encrypted: true,
600176
600903
  key: key.toString("base64"),
@@ -600221,7 +600948,7 @@ function materializeTelegramCreativeArtifactForSend(root, rawPath) {
600221
600948
  }
600222
600949
  const stageDir = join120(rootAbs, SEND_DIR, `${Date.now()}-${randomBytes21(8).toString("hex")}`);
600223
600950
  mkdirSync61(stageDir, { recursive: true });
600224
- const staged = join120(stageDir, object.originalName || basename25(rel));
600951
+ const staged = join120(stageDir, object.originalName || basename26(rel));
600225
600952
  writeFileSync54(staged, payload);
600226
600953
  return {
600227
600954
  ok: true,
@@ -600437,7 +601164,7 @@ ${result.output}`,
600437
601164
  });
600438
601165
 
600439
601166
  // packages/cli/src/tui/stimulation.ts
600440
- function clamp016(value2) {
601167
+ function clamp017(value2) {
600441
601168
  return Math.max(0, Math.min(1, value2));
600442
601169
  }
600443
601170
  function cloneState(state) {
@@ -600493,7 +601220,7 @@ var init_stimulation = __esm({
600493
601220
  ...DEFAULT_STATE,
600494
601221
  ...state,
600495
601222
  phase: normalizePhase(state.phase) ?? DEFAULT_STATE.phase,
600496
- attention: clamp016(Number.isFinite(state.attention) ? Number(state.attention) : DEFAULT_STATE.attention),
601223
+ attention: clamp017(Number.isFinite(state.attention) ? Number(state.attention) : DEFAULT_STATE.attention),
600497
601224
  updatedAtMs: Number.isFinite(state.updatedAtMs) ? Number(state.updatedAtMs) : now,
600498
601225
  lastStimulusAtMs: Number.isFinite(state.lastStimulusAtMs) ? Number(state.lastStimulusAtMs) : now,
600499
601226
  messagesSinceAnalysis: Math.max(0, Math.floor(Number(state.messagesSinceAnalysis ?? 0))),
@@ -600543,11 +601270,11 @@ var init_stimulation = __esm({
600543
601270
  const state = this.stateFor(channelId, nowMs);
600544
601271
  this.applyTimeDecay(state, nowMs);
600545
601272
  if (Number.isFinite(decision.attentionScore)) {
600546
- state.attention = clamp016(Number(decision.attentionScore));
601273
+ state.attention = clamp017(Number(decision.attentionScore));
600547
601274
  } else if (Number.isFinite(decision.attentionDelta)) {
600548
- state.attention = clamp016(state.attention + Number(decision.attentionDelta));
601275
+ state.attention = clamp017(state.attention + Number(decision.attentionDelta));
600549
601276
  } else {
600550
- state.attention = clamp016(state.attention + (decision.shouldReply ? 0.22 : -0.1));
601277
+ state.attention = clamp017(state.attention + (decision.shouldReply ? 0.22 : -0.1));
600551
601278
  }
600552
601279
  if (decision.phase) {
600553
601280
  state.attention = Math.max(state.attention, PHASE_FLOORS[decision.phase]);
@@ -600603,7 +601330,7 @@ var init_stimulation = __esm({
600603
601330
  const elapsedMs2 = Math.max(0, nowMs - (state.updatedAtMs || nowMs));
600604
601331
  if (elapsedMs2 <= 0) return;
600605
601332
  const halfLives = elapsedMs2 / (12 * 6e4);
600606
- state.attention = clamp016(state.attention * Math.pow(0.5, halfLives));
601333
+ state.attention = clamp017(state.attention * Math.pow(0.5, halfLives));
600607
601334
  if (state.phase !== "engaged" || elapsedMs2 > 4 * 6e4) {
600608
601335
  state.phase = phaseFromAttention(state.attention);
600609
601336
  }
@@ -600615,7 +601342,7 @@ var init_stimulation = __esm({
600615
601342
  // packages/cli/src/tui/telegram-channel-dmn.ts
600616
601343
  import { existsSync as existsSync106, mkdirSync as mkdirSync62, readdirSync as readdirSync36, readFileSync as readFileSync86, writeFileSync as writeFileSync55 } from "node:fs";
600617
601344
  import { join as join121 } from "node:path";
600618
- import { createHash as createHash20 } from "node:crypto";
601345
+ import { createHash as createHash21 } from "node:crypto";
600619
601346
  function safeFilePart(value2) {
600620
601347
  return value2.replace(/[^A-Za-z0-9_.-]+/g, "_").slice(0, 80) || "telegram";
600621
601348
  }
@@ -600623,12 +601350,12 @@ function daydreamRoot(repoRoot) {
600623
601350
  return join121(repoRoot, ".omnius", "telegram-daydreams");
600624
601351
  }
600625
601352
  function sessionDir(repoRoot, sessionKey) {
600626
- const hash = createHash20("sha1").update(sessionKey).digest("hex").slice(0, 20);
601353
+ const hash = createHash21("sha1").update(sessionKey).digest("hex").slice(0, 20);
600627
601354
  return join121(daydreamRoot(repoRoot), safeFilePart(hash));
600628
601355
  }
600629
601356
  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;
601357
+ const clean5 = value2.replace(/\s+/g, " ").trim();
601358
+ return clean5.length > max ? `${clean5.slice(0, Math.max(0, max - 3)).trimEnd()}...` : clean5;
600632
601359
  }
600633
601360
  function isoFromMs(value2) {
600634
601361
  return value2 ? new Date(value2).toISOString() : void 0;
@@ -600698,7 +601425,7 @@ function buildReplyOpportunities(input, openQuestions) {
600698
601425
  }
600699
601426
  return opportunities;
600700
601427
  }
600701
- function clamp017(value2) {
601428
+ function clamp018(value2) {
600702
601429
  if (!Number.isFinite(value2)) return 0;
600703
601430
  return Math.max(0, Math.min(1, value2));
600704
601431
  }
@@ -600709,7 +601436,7 @@ function pushStimulationSignal(signals, signal, source, weight) {
600709
601436
  const cleanSignal = compactLine2(signal, 120);
600710
601437
  const cleanSource = compactLine2(source, 180);
600711
601438
  if (!cleanSignal || signals.some((entry) => entry.signal === cleanSignal && entry.source === cleanSource)) return;
600712
- signals.push({ signal: cleanSignal, source: cleanSource, weight: clamp017(weight) });
601439
+ signals.push({ signal: cleanSignal, source: cleanSource, weight: clamp018(weight) });
600713
601440
  }
600714
601441
  function buildMetaAnalysisSignals(input) {
600715
601442
  const chatLabel = input.chatTitle || input.chatId;
@@ -600784,7 +601511,7 @@ function buildCuriosityThreads(input, openQuestions, stimulationSignals) {
600784
601511
  question: text.endsWith("?") || text.endsWith("?") ? text : `What should be learned or clarified from: ${text || entry.mediaSummary || "recent media"}?`,
600785
601512
  rationale: "Human curiosity, uncertainty, or multimodal content makes this a useful idle exploration target.",
600786
601513
  sourceMessages: messageId,
600787
- intensity: clamp017(0.5 + replyBoost + mediaBoost + questionBoost)
601514
+ intensity: clamp018(0.5 + replyBoost + mediaBoost + questionBoost)
600788
601515
  });
600789
601516
  }
600790
601517
  for (const question of openQuestions.slice(-4)) {
@@ -600804,7 +601531,7 @@ function buildCuriosityThreads(input, openQuestions, stimulationSignals) {
600804
601531
  question: `Is there a useful clarification or memory consolidation around ${strongest.source}?`,
600805
601532
  rationale: "Strongest stimulation signal can seed a low-intrusion reflection target.",
600806
601533
  sourceMessages: [],
600807
- intensity: clamp017(strongest.weight * 0.72)
601534
+ intensity: clamp018(strongest.weight * 0.72)
600808
601535
  });
600809
601536
  }
600810
601537
  return threads.sort((a2, b) => b.intensity - a2.intensity).slice(0, 8);
@@ -600864,8 +601591,8 @@ function participantForThread(input, thread) {
600864
601591
  return input.participants.find((participant) => participant.username === sourceMessage.username);
600865
601592
  }
600866
601593
  if (sourceMessage?.speaker) {
600867
- const clean3 = sourceMessage.speaker.replace(/^@/, "");
600868
- return input.participants.find((participant) => participant.username === clean3 || participant.firstName === sourceMessage.speaker);
601594
+ const clean5 = sourceMessage.speaker.replace(/^@/, "");
601595
+ return input.participants.find((participant) => participant.username === clean5 || participant.firstName === sourceMessage.speaker);
600869
601596
  }
600870
601597
  return topParticipants(input)[0];
600871
601598
  }
@@ -600878,7 +601605,7 @@ function buildOutreachPlans(input, curiosityThreads) {
600878
601605
  purpose: "Continue the public thread only when the live model judges that the group would benefit from a concise follow-up.",
600879
601606
  draftIntent: "Ask one concrete clarification, offer one useful synthesis, or stay silent if the room has moved on.",
600880
601607
  gate: "model_decision",
600881
- confidence: clamp017(thread.intensity * 0.86)
601608
+ confidence: clamp018(thread.intensity * 0.86)
600882
601609
  });
600883
601610
  const participant = participantForThread(input, thread);
600884
601611
  if (!participant) continue;
@@ -600890,7 +601617,7 @@ function buildOutreachPlans(input, curiosityThreads) {
600890
601617
  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
601618
  draftIntent: "Reference the public thread briefly, ask permission to continue privately, and do not reveal hidden meta-analysis.",
600892
601619
  gate: "admin_review",
600893
- confidence: clamp017(thread.intensity * 0.58)
601620
+ confidence: clamp018(thread.intensity * 0.58)
600894
601621
  });
600895
601622
  }
600896
601623
  return plans.slice(0, 8);
@@ -600941,7 +601668,48 @@ function buildMemoryProposals(input) {
600941
601668
  }
600942
601669
  return proposals.slice(0, 6);
600943
601670
  }
600944
- function buildTelegramChannelDaydream(input) {
601671
+ function corpusPacket(corpus) {
601672
+ return {
601673
+ query: corpus?.query,
601674
+ selectionSeed: corpus?.selectionSeed,
601675
+ stats: corpus?.stats,
601676
+ fallbacks: corpus?.fallbacks ?? [],
601677
+ episodeIds: corpus?.episodeIds ?? [],
601678
+ candidateEpisodeIds: corpus?.candidateEpisodeIds ?? [],
601679
+ selectedEpisodeIds: corpus?.selectedEpisodeIds ?? []
601680
+ };
601681
+ }
601682
+ function selectedSeedPacket(corpus) {
601683
+ const seed = corpus?.graphWalk.seed;
601684
+ if (!seed) return void 0;
601685
+ return {
601686
+ nodeId: seed.node.id,
601687
+ nodeText: seed.node.text,
601688
+ nodeType: seed.node.nodeType,
601689
+ degree: seed.degree,
601690
+ score: seed.score,
601691
+ sourceEpisodeIds: seed.sourceEpisodeIds
601692
+ };
601693
+ }
601694
+ function graphWalkPacket(corpus) {
601695
+ return {
601696
+ visitedNodes: corpus?.graphWalk.visitedNodes.map((node) => ({
601697
+ id: node.id,
601698
+ text: node.text,
601699
+ nodeType: node.nodeType,
601700
+ depth: corpus.graphWalk.depthByNodeId[node.id] ?? 0
601701
+ })) ?? [],
601702
+ traversedEdges: corpus?.graphWalk.traversedEdges.map((edge) => ({
601703
+ id: edge.id,
601704
+ relation: edge.relation,
601705
+ fact: edge.fact,
601706
+ sourceEpisodeId: edge.sourceEpisodeId,
601707
+ confidence: edge.confidence
601708
+ })) ?? [],
601709
+ sourceEpisodeIds: corpus?.graphWalk.sourceEpisodeIds ?? []
601710
+ };
601711
+ }
601712
+ function buildTelegramChannelDaydream(input, corpus, extraction, extractionCommit) {
600945
601713
  const firstTs = input.history.map((entry) => entry.ts).find((ts) => typeof ts === "number");
600946
601714
  const lastTs = [...input.history].reverse().map((entry) => entry.ts).find((ts) => typeof ts === "number");
600947
601715
  const metaAnalysisSignals = buildMetaAnalysisSignals(input);
@@ -600974,8 +601742,8 @@ function buildTelegramChannelDaydream(input) {
600974
601742
  ].filter((entry) => Boolean(entry));
600975
601743
  const seed = `${input.sessionKey}:${input.generatedAtMs}:${input.history.length}`;
600976
601744
  return {
600977
- version: 2,
600978
- id: createHash20("sha1").update(seed).digest("hex").slice(0, 16),
601745
+ version: 3,
601746
+ id: createHash21("sha1").update(seed).digest("hex").slice(0, 16),
600979
601747
  sessionKey: input.sessionKey,
600980
601748
  chatId: input.chatId,
600981
601749
  chatTitle: input.chatTitle,
@@ -600999,7 +601767,17 @@ function buildTelegramChannelDaydream(input) {
600999
601767
  memoryProposals,
601000
601768
  artifactProposals,
601001
601769
  stimulationContext: input.stimulationContext,
601002
- personaContext: input.personaContext
601770
+ personaContext: input.personaContext,
601771
+ corpus: corpusPacket(corpus),
601772
+ selectedSeed: selectedSeedPacket(corpus),
601773
+ graphWalk: graphWalkPacket(corpus),
601774
+ tagging: extraction?.tags ?? [],
601775
+ summation: extraction?.summaries ?? [],
601776
+ titling: extraction?.titles ?? [],
601777
+ extraction: extraction?.extractions ?? [],
601778
+ linking: extraction?.links ?? [],
601779
+ extractionFollowups: extraction?.followups ?? [],
601780
+ extractionCommit
601003
601781
  };
601004
601782
  }
601005
601783
  function formatTelegramChannelDaydreamMarkdown(artifact) {
@@ -601010,6 +601788,41 @@ function formatTelegramChannelDaydreamMarkdown(artifact) {
601010
601788
  `Generated: ${artifact.generatedAt}`,
601011
601789
  `Session: ${artifact.sessionKey}`,
601012
601790
  `Chat: ${artifact.chatTitle || artifact.chatId} (${artifact.chatType})`,
601791
+ "",
601792
+ "## Reflection Corpus",
601793
+ artifact.corpus.stats ? [
601794
+ `- retained messages: ${artifact.corpus.stats.retainedMessages}`,
601795
+ `- candidate episodes: ${artifact.corpus.stats.candidateEpisodes}`,
601796
+ `- selected episodes: ${artifact.corpus.stats.selectedEpisodes}`,
601797
+ `- vector search limit: ${artifact.corpus.stats.vectorSearchLimitUsed}`,
601798
+ `- graph walk: ${artifact.corpus.stats.graphNodesVisited} node(s), ${artifact.corpus.stats.graphEdgesTraversed} edge(s)`,
601799
+ artifact.corpus.fallbacks.length ? `- fallbacks: ${artifact.corpus.fallbacks.join("; ")}` : "- fallbacks: none"
601800
+ ].join("\n") : "- Not available",
601801
+ artifact.selectedSeed ? `
601802
+ Selected seed: ${artifact.selectedSeed.nodeText} (${artifact.selectedSeed.nodeType}, degree ${artifact.selectedSeed.degree})` : "",
601803
+ "",
601804
+ "## Tags",
601805
+ 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",
601806
+ "",
601807
+ "## Summaries",
601808
+ artifact.summation.length ? artifact.summation.map((item) => `- ${item.title} [${item.scope}] confidence=${item.confidence.toFixed(2)}
601809
+ ${item.text}${item.sourceMessageIds.length ? `
601810
+ messages=${item.sourceMessageIds.join(", ")}` : ""}`).join("\n") : "- None",
601811
+ "",
601812
+ "## Titles",
601813
+ artifact.titling.length ? artifact.titling.map((item) => `- ${item.title} -> ${item.target} confidence=${item.confidence.toFixed(2)}`).join("\n") : "- None",
601814
+ "",
601815
+ "## Extractions",
601816
+ 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",
601817
+ "",
601818
+ "## Links",
601819
+ artifact.linking.length ? artifact.linking.map((item) => `- ${item.srcNodeText} --${item.relation}--> ${item.dstNodeText} confidence=${item.confidence.toFixed(2)}
601820
+ ${item.fact}${item.sourceMessageIds.length ? `
601821
+ messages=${item.sourceMessageIds.join(", ")}` : ""}`).join("\n") : "- None",
601822
+ "",
601823
+ "## Extraction Followups",
601824
+ artifact.extractionFollowups.length ? artifact.extractionFollowups.map((item) => `- ${item.target}${item.replyToMessageId ? ` reply_to=${item.replyToMessageId}` : ""} confidence=${item.confidence.toFixed(2)}
601825
+ ${item.text || item.rationale}`).join("\n") : "- None",
601013
601826
  "",
601014
601827
  section("Meta-Analysis Signals", artifact.metaAnalysisSignals),
601015
601828
  "",
@@ -601099,6 +601912,18 @@ function formatTelegramChannelDaydreamContext(artifact) {
601099
601912
  "### Telegram Channel Daydream",
601100
601913
  "Private idle reflection and stimulation artifact. Use it as context only; do not mention it unless the user asks about internal memory.",
601101
601914
  `Generated: ${artifact.generatedAt}`,
601915
+ artifact.corpus?.stats ? `Corpus: ${artifact.corpus.stats.selectedEpisodes} selected episode(s), ${artifact.corpus.stats.graphNodesVisited} graph node(s), search limit ${artifact.corpus.stats.vectorSearchLimitUsed}` : "",
601916
+ artifact.selectedSeed ? `Selected seed: ${artifact.selectedSeed.nodeText} (degree ${artifact.selectedSeed.degree})` : "",
601917
+ artifact.tagging?.length ? `Tags:
601918
+ ${artifact.tagging.slice(0, 8).map((item) => `- ${item.label} [${item.kind}] (${item.confidence.toFixed(2)})`).join("\n")}` : "",
601919
+ artifact.summation?.length ? `Summaries:
601920
+ ${artifact.summation.slice(0, 4).map((item) => `- ${item.title}: ${item.text}`).join("\n")}` : "",
601921
+ artifact.extraction?.length ? `Extracted items:
601922
+ ${artifact.extraction.slice(0, 6).map((item) => `- ${item.kind}: ${item.text}${item.sourceMessageIds.length ? ` (messages ${item.sourceMessageIds.join(", ")})` : ""}`).join("\n")}` : "",
601923
+ artifact.linking?.length ? `Graph links:
601924
+ ${artifact.linking.slice(0, 5).map((item) => `- ${item.srcNodeText} --${item.relation}--> ${item.dstNodeText}`).join("\n")}` : "",
601925
+ artifact.extractionFollowups?.length ? `Extraction followups:
601926
+ ${artifact.extractionFollowups.slice(0, 4).map((item) => `- ${item.target}${item.replyToMessageId ? ` reply_to=${item.replyToMessageId}` : ""}: ${item.text || item.rationale}`).join("\n")}` : "",
601102
601927
  artifact.metaAnalysisSignals?.length ? `Meta-analysis signals:
601103
601928
  ${artifact.metaAnalysisSignals.slice(0, 4).map((line) => `- ${line}`).join("\n")}` : "",
601104
601929
  artifact.humanStimulationSignals?.length ? `Human stimulation signals:
@@ -601128,6 +601953,715 @@ var init_telegram_channel_dmn = __esm({
601128
601953
  }
601129
601954
  });
601130
601955
 
601956
+ // packages/cli/src/tui/telegram-reflection-corpus.ts
601957
+ import { createHash as createHash22 } from "node:crypto";
601958
+ function telegramReflectionMemoryDbPaths(repoRoot) {
601959
+ return omniusMemoryDbPaths(repoRoot);
601960
+ }
601961
+ function stableHash2(value2, length4 = 16) {
601962
+ return createHash22("sha1").update(value2).digest("hex").slice(0, length4);
601963
+ }
601964
+ function clean3(value2) {
601965
+ return String(value2 ?? "").replace(/\s+/g, " ").trim();
601966
+ }
601967
+ function compact(value2, max = 420) {
601968
+ const text = clean3(value2);
601969
+ return text.length > max ? `${text.slice(0, Math.max(0, max - 3)).trimEnd()}...` : text;
601970
+ }
601971
+ function senderLabel(entry) {
601972
+ if (entry.role === "assistant") return entry.speaker || "Assistant";
601973
+ if (entry.speaker) return entry.speaker;
601974
+ if (entry.username) return `@${entry.username}`;
601975
+ if (entry.firstName) return entry.firstName;
601976
+ if (entry.fromUserId) return `user:${entry.fromUserId}`;
601977
+ return "Telegram user";
601978
+ }
601979
+ function senderKey2(entry) {
601980
+ if (entry.role === "assistant") return entry.username || entry.speaker || "assistant";
601981
+ return String(entry.fromUserId || entry.username || entry.firstName || senderLabel(entry));
601982
+ }
601983
+ function scopeFor(entry, options2) {
601984
+ const chatType = entry.chatType || options2.chatType || "unknown";
601985
+ return {
601986
+ kind: `telegram-${chatType}`,
601987
+ id: String(entry.chatId ?? options2.chatId),
601988
+ title: entry.chatTitle || options2.chatTitle
601989
+ };
601990
+ }
601991
+ function senderFor(entry) {
601992
+ return {
601993
+ id: senderKey2(entry),
601994
+ username: entry.username,
601995
+ displayName: senderLabel(entry),
601996
+ isBot: entry.role === "assistant"
601997
+ };
601998
+ }
601999
+ function messageIdFor(entry, sessionKey) {
602000
+ if (typeof entry.messageId === "number" && Number.isFinite(entry.messageId)) return entry.messageId;
602001
+ return stableHash2(`${sessionKey}:${entry.role}:${entry.ts ?? ""}:${senderLabel(entry)}:${entry.text}`);
602002
+ }
602003
+ function messageRefFor(entry, sessionKey) {
602004
+ return {
602005
+ id: messageIdFor(entry, sessionKey),
602006
+ threadId: entry.messageThreadId,
602007
+ timestamp: entry.ts,
602008
+ text: entry.text
602009
+ };
602010
+ }
602011
+ function replyRefFor(entry) {
602012
+ const reply = entry.replyContext;
602013
+ const replyId = reply?.messageId ?? entry.replyToMessageId;
602014
+ if (replyId == null) return void 0;
602015
+ return {
602016
+ id: replyId,
602017
+ threadId: reply?.threadId,
602018
+ text: reply?.text || reply?.caption || reply?.mediaSummary,
602019
+ sender: reply?.sender ? {
602020
+ id: reply.sender.id == null ? void 0 : String(reply.sender.id),
602021
+ username: reply.sender.username,
602022
+ displayName: reply.sender.title || reply.sender.firstName || reply.sender.username,
602023
+ isBot: reply.sender.isBot
602024
+ } : void 0
602025
+ };
602026
+ }
602027
+ function contentFor(entry, sessionKey, options2) {
602028
+ const lines = [
602029
+ `Telegram ${entry.chatType || options2.chatType || "unknown"} ${entry.role} message`,
602030
+ `session: ${sessionKey}`,
602031
+ `chat: ${entry.chatTitle || options2.chatTitle || entry.chatId || options2.chatId}`,
602032
+ `message_id: ${messageIdFor(entry, sessionKey)}`,
602033
+ entry.messageThreadId != null ? `thread_id: ${entry.messageThreadId}` : "",
602034
+ entry.replyToMessageId != null ? `reply_to_message_id: ${entry.replyToMessageId}` : "",
602035
+ `speaker: ${senderLabel(entry)}`,
602036
+ entry.mode ? `mode: ${entry.mode}` : "",
602037
+ entry.mediaSummary ? `media: ${compact(entry.mediaSummary, 260)}` : "",
602038
+ "",
602039
+ entry.text
602040
+ ].filter((line) => line !== "");
602041
+ return lines.join("\n");
602042
+ }
602043
+ function metadataFor(entry, sessionKey, options2) {
602044
+ return {
602045
+ sourceSurface: "telegram",
602046
+ sessionKey,
602047
+ telegram: {
602048
+ role: entry.role,
602049
+ mode: entry.mode,
602050
+ chatId: String(entry.chatId ?? options2.chatId),
602051
+ chatType: entry.chatType || options2.chatType,
602052
+ chatTitle: entry.chatTitle || options2.chatTitle,
602053
+ messageId: entry.messageId,
602054
+ messageThreadId: entry.messageThreadId,
602055
+ replyToMessageId: entry.replyToMessageId,
602056
+ username: entry.username,
602057
+ firstName: entry.firstName,
602058
+ fromUserId: entry.fromUserId,
602059
+ speaker: senderLabel(entry),
602060
+ mediaSummary: entry.mediaSummary
602061
+ }
602062
+ };
602063
+ }
602064
+ function graphTopicNode(graph, label) {
602065
+ return graph.upsertNode({ text: `topic:${label}`, nodeType: "concept" });
602066
+ }
602067
+ function addTextEdges(graph, result, entry) {
602068
+ const messageId = result.nodeIds.message;
602069
+ const senderId = result.nodeIds.sender;
602070
+ if (messageId && senderId) {
602071
+ graph.addEdge({
602072
+ srcId: messageId,
602073
+ dstId: senderId,
602074
+ relation: "said_by",
602075
+ fact: compact(entry.text, 260),
602076
+ sourceEpisodeId: result.episodeId,
602077
+ modality: entry.role === "user" ? "social" : "text",
602078
+ confidence: entry.role === "assistant" ? 0.92 : 0.96
602079
+ });
602080
+ }
602081
+ if (messageId) {
602082
+ const channelTopic = graphTopicNode(graph, "telegram-reflection-corpus");
602083
+ graph.addEdge({
602084
+ srcId: messageId,
602085
+ dstId: channelTopic,
602086
+ relation: "related_to",
602087
+ fact: compact(entry.text || entry.mediaSummary || "telegram message", 220),
602088
+ sourceEpisodeId: result.episodeId,
602089
+ modality: entry.role === "user" ? "social" : "text",
602090
+ confidence: 0.72
602091
+ });
602092
+ }
602093
+ }
602094
+ function upsertTelegramReflectionMessage(repoRoot, sessionKey, entry, options2) {
602095
+ const paths = telegramReflectionMemoryDbPaths(repoRoot);
602096
+ const graph = new TemporalGraph(paths.knowledge);
602097
+ const store2 = new EpisodeStore(paths.episodes, graph);
602098
+ try {
602099
+ const before = store2.count(sessionKey);
602100
+ const service = new MultimodalIdentityService({ episodeStore: store2, graph });
602101
+ const content = contentFor(entry, sessionKey, options2);
602102
+ const result = service.ingest({
602103
+ sourceSurface: "telegram",
602104
+ sessionId: sessionKey,
602105
+ metadata: metadataFor(entry, sessionKey, options2),
602106
+ scope: scopeFor(entry, options2),
602107
+ sender: senderFor(entry),
602108
+ message: messageRefFor(entry, sessionKey),
602109
+ replyTo: replyRefFor(entry),
602110
+ modality: entry.role === "user" ? "social" : "text",
602111
+ content,
602112
+ labels: [entry.mode, entry.mediaSummary, senderLabel(entry)].filter((value2) => Boolean(value2))
602113
+ });
602114
+ addTextEdges(graph, result, entry);
602115
+ const after = store2.count(sessionKey);
602116
+ return { episodeId: result.episodeId, reused: after === before };
602117
+ } finally {
602118
+ store2.close();
602119
+ graph.close();
602120
+ }
602121
+ }
602122
+ function queryFor(options2) {
602123
+ if (options2.query?.trim()) return options2.query.trim();
602124
+ const recent = options2.history.slice(-12).map((entry) => compact(`${senderLabel(entry)}: ${entry.text || entry.mediaSummary || ""}`, 180));
602125
+ return [
602126
+ `Telegram ${options2.chatType} ${options2.chatTitle || options2.chatId} reflection`,
602127
+ ...recent
602128
+ ].filter(Boolean).join("\n");
602129
+ }
602130
+ async function fillMissingEmbeddings(store2, graph, episodes, embeddingConfig) {
602131
+ if (embeddingConfig === false) return { embedded: 0, unavailable: true, queryEmbedding: null };
602132
+ const missing = episodes.filter((episode) => !episode.embedding && episode.content.trim());
602133
+ if (missing.length === 0) return { embedded: 0, unavailable: false, queryEmbedding: null };
602134
+ const results = await generateEmbeddingBatch(missing.map((episode) => episode.content.slice(0, 4e3)), embeddingConfig);
602135
+ let embedded = 0;
602136
+ for (let i2 = 0; i2 < missing.length; i2++) {
602137
+ const result = results[i2];
602138
+ const episode = missing[i2];
602139
+ if (!result || !episode) continue;
602140
+ store2.setEmbedding(episode.id, result.vector);
602141
+ const refreshed = store2.get(episode.id);
602142
+ if (refreshed) linkEpisode(refreshed, store2, graph);
602143
+ embedded++;
602144
+ }
602145
+ return { embedded, unavailable: embedded === 0 && missing.length > 0, queryEmbedding: null };
602146
+ }
602147
+ async function queryEmbeddingFor(query, embeddingConfig) {
602148
+ if (embeddingConfig === false) return null;
602149
+ const result = await generateEmbedding(query.slice(0, 4e3), embeddingConfig);
602150
+ return result?.vector ?? null;
602151
+ }
602152
+ async function buildTelegramReflectionCorpus(options2) {
602153
+ const paths = telegramReflectionMemoryDbPaths(options2.repoRoot);
602154
+ const graph = new TemporalGraph(paths.knowledge);
602155
+ const store2 = new EpisodeStore(paths.episodes, graph);
602156
+ const episodeIds = [];
602157
+ let reusedEpisodes = 0;
602158
+ const fallbacks = [];
602159
+ try {
602160
+ const service = new MultimodalIdentityService({ episodeStore: store2, graph });
602161
+ for (const entry of options2.history) {
602162
+ const before = store2.count(options2.sessionKey);
602163
+ const content = contentFor(entry, options2.sessionKey, options2);
602164
+ const result = service.ingest({
602165
+ sourceSurface: "telegram",
602166
+ sessionId: options2.sessionKey,
602167
+ metadata: metadataFor(entry, options2.sessionKey, options2),
602168
+ scope: scopeFor(entry, options2),
602169
+ sender: senderFor(entry),
602170
+ message: messageRefFor(entry, options2.sessionKey),
602171
+ replyTo: replyRefFor(entry),
602172
+ modality: entry.role === "user" ? "social" : "text",
602173
+ content,
602174
+ labels: [entry.mode, entry.mediaSummary, senderLabel(entry)].filter((value2) => Boolean(value2))
602175
+ });
602176
+ addTextEdges(graph, result, entry);
602177
+ const after = store2.count(options2.sessionKey);
602178
+ if (after === before) reusedEpisodes++;
602179
+ if (result.episodeId) episodeIds.push(result.episodeId);
602180
+ }
602181
+ const scopedEpisodes = store2.recent(500, options2.sessionKey).filter((episode) => {
602182
+ const meta = episode.metadata;
602183
+ return meta?.sourceSurface === "telegram" || meta?.telegram && typeof meta.telegram === "object";
602184
+ });
602185
+ const embeddingFill = await fillMissingEmbeddings(store2, graph, scopedEpisodes, options2.embeddingConfig);
602186
+ const query = queryFor(options2);
602187
+ const queryEmbedding = await queryEmbeddingFor(query, options2.embeddingConfig);
602188
+ if (embeddingFill.unavailable || !queryEmbedding) fallbacks.push("embedding unavailable or incomplete; lexical scoped search included");
602189
+ let graphWalk = null;
602190
+ let selectedEpisodes = [];
602191
+ let vectorSearchLimitUsed = 0;
602192
+ let candidateEpisodes = [];
602193
+ const limits = options2.candidateLimits ?? [48, 96, 192, 384, 500];
602194
+ for (const limit of limits) {
602195
+ vectorSearchLimitUsed = limit;
602196
+ candidateEpisodes = store2.search(
602197
+ {
602198
+ sessionId: options2.sessionKey,
602199
+ query,
602200
+ limit,
602201
+ metadataFilter: { sourceSurface: "telegram" }
602202
+ },
602203
+ {
602204
+ queryEmbedding,
602205
+ lexicalWeight: 1,
602206
+ embeddingWeight: queryEmbedding ? 1.8 : 0
602207
+ }
602208
+ );
602209
+ graphWalk = selectAndWalkGraphCandidate(
602210
+ graph,
602211
+ candidateEpisodes.map((episode, index) => ({ episode, score: limit - index })),
602212
+ {
602213
+ seed: `${options2.sessionKey}:${options2.generatedAtMs}:${limit}`,
602214
+ maxDepth: 2,
602215
+ maxVisitedNodes: 72,
602216
+ maxTraversedEdges: 180,
602217
+ maxSourceEpisodes: 100
602218
+ }
602219
+ );
602220
+ if (graphWalk.seed) {
602221
+ selectedEpisodes = graphWalk.sourceEpisodeIds.map((id) => store2.get(id)).filter((episode) => Boolean(episode));
602222
+ if (selectedEpisodes.length < (options2.minWalkEpisodes ?? 8)) {
602223
+ const deeper = selectAndWalkGraphCandidate(
602224
+ graph,
602225
+ candidateEpisodes.map((episode, index) => ({ episode, score: limit - index })),
602226
+ {
602227
+ seed: `${options2.sessionKey}:${options2.generatedAtMs}:${limit}:deep`,
602228
+ maxDepth: 3,
602229
+ maxVisitedNodes: 96,
602230
+ maxTraversedEdges: 240,
602231
+ maxSourceEpisodes: 120
602232
+ }
602233
+ );
602234
+ if (deeper.seed && deeper.sourceEpisodeIds.length >= graphWalk.sourceEpisodeIds.length) graphWalk = deeper;
602235
+ }
602236
+ selectedEpisodes = graphWalk.sourceEpisodeIds.map((id) => store2.get(id)).filter((episode) => Boolean(episode));
602237
+ break;
602238
+ }
602239
+ }
602240
+ if (!graphWalk) {
602241
+ graphWalk = selectAndWalkGraphCandidate(graph, [], { seed: `${options2.sessionKey}:${options2.generatedAtMs}:empty` });
602242
+ }
602243
+ if (!graphWalk.seed && graphWalk.fallbackReason) fallbacks.push(graphWalk.fallbackReason);
602244
+ if (selectedEpisodes.length === 0) {
602245
+ selectedEpisodes = candidateEpisodes.slice(0, Math.min(24, candidateEpisodes.length));
602246
+ if (selectedEpisodes.length > 0) fallbacks.push("selected recent scoped episodes because graph walk produced no source episode anchors");
602247
+ }
602248
+ return {
602249
+ stats: {
602250
+ retainedMessages: options2.history.length,
602251
+ upsertedEpisodes: episodeIds.length,
602252
+ reusedEpisodes,
602253
+ embeddedEpisodes: embeddingFill.embedded,
602254
+ candidateEpisodes: candidateEpisodes.length,
602255
+ selectedEpisodes: selectedEpisodes.length,
602256
+ graphNodesVisited: graphWalk.visitedNodes.length,
602257
+ graphEdgesTraversed: graphWalk.traversedEdges.length,
602258
+ vectorSearchLimitUsed,
602259
+ embeddingUnavailable: embeddingFill.unavailable || !queryEmbedding
602260
+ },
602261
+ episodeIds: [...new Set(episodeIds)],
602262
+ candidateEpisodeIds: candidateEpisodes.map((episode) => episode.id),
602263
+ selectedEpisodeIds: selectedEpisodes.map((episode) => episode.id),
602264
+ selectedEpisodes,
602265
+ graphWalk,
602266
+ selectionSeed: `${options2.sessionKey}:${options2.generatedAtMs}:${vectorSearchLimitUsed}`,
602267
+ query,
602268
+ fallbacks
602269
+ };
602270
+ } finally {
602271
+ store2.close();
602272
+ graph.close();
602273
+ }
602274
+ }
602275
+ var init_telegram_reflection_corpus = __esm({
602276
+ "packages/cli/src/tui/telegram-reflection-corpus.ts"() {
602277
+ "use strict";
602278
+ init_dist7();
602279
+ init_memory_paths();
602280
+ }
602281
+ });
602282
+
602283
+ // packages/cli/src/tui/telegram-reflection-extraction.ts
602284
+ function clean4(value2, max = 2e3) {
602285
+ const text = String(value2 ?? "").replace(/\s+/g, " ").trim();
602286
+ return text.length > max ? `${text.slice(0, Math.max(0, max - 3)).trimEnd()}...` : text;
602287
+ }
602288
+ function clampConfidence(value2, fallback = 0.5) {
602289
+ if (typeof value2 !== "number" || !Number.isFinite(value2)) return fallback;
602290
+ return Math.max(0, Math.min(1, value2));
602291
+ }
602292
+ function numberArray(value2) {
602293
+ if (!Array.isArray(value2)) return [];
602294
+ return [...new Set(value2.map((item) => Number(item)).filter((item) => Number.isFinite(item)).map((item) => Math.trunc(item)))];
602295
+ }
602296
+ function stringArray(value2) {
602297
+ if (!Array.isArray(value2)) return [];
602298
+ return [...new Set(value2.map((item) => clean4(item, 180)).filter(Boolean))];
602299
+ }
602300
+ function episodeLine(episode) {
602301
+ const meta = episode.metadata;
602302
+ const telegram = meta?.telegram;
602303
+ const speaker = clean4(telegram?.speaker || telegram?.username || "unknown", 80);
602304
+ const messageId = telegram?.messageId == null ? "unknown" : String(telegram.messageId);
602305
+ const replyTo = telegram?.replyToMessageId == null ? "" : ` reply_to=${telegram.replyToMessageId}`;
602306
+ return [
602307
+ `episode_id=${episode.id}`,
602308
+ `message_id=${messageId}${replyTo}`,
602309
+ `speaker=${speaker}`,
602310
+ `modality=${episode.modality}`,
602311
+ `content=${clean4(episode.content, 700)}`
602312
+ ].join(" | ");
602313
+ }
602314
+ function buildTelegramReflectionExtractionPrompt(options2) {
602315
+ const corpus = options2.corpus;
602316
+ 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");
602317
+ 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");
602318
+ const episodes = corpus.selectedEpisodes.slice(0, 36).map(episodeLine).join("\n");
602319
+ return [
602320
+ "You are extracting a reusable Telegram reflection artifact from a scoped chat corpus.",
602321
+ "Return strict JSON only. Do not include Markdown, prose, or comments.",
602322
+ "",
602323
+ "Required top-level JSON keys:",
602324
+ "artifact_title, tags, summaries, titles, extractions, links, followups",
602325
+ "",
602326
+ "Rules:",
602327
+ "- Use only the scoped Telegram corpus, graph nodes, graph edges, and source anchors below.",
602328
+ "- Preserve message_id and episode_id anchors on every item when possible.",
602329
+ "- Do not infer identity from a face, voice, or name unless the corpus explicitly says it.",
602330
+ "- Private DM followups may be proposed but must not be framed as already sent.",
602331
+ "- same_group followups must be concise, low-intrusion, and anchored to a source message id.",
602332
+ "- If a category has no evidence, return an empty array for that category.",
602333
+ "",
602334
+ "Schema:",
602335
+ JSON.stringify({
602336
+ artifact_title: "short title",
602337
+ 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"] }],
602338
+ summaries: [{ title: "summary title", scope: "channel|thread|participant|message_window", text: "summary text", confidence: 0, source_message_ids: [1], target_episode_ids: ["episode-id"] }],
602339
+ titles: [{ title: "usable title", target: "artifact|thread|memory_card|graph_node", confidence: 0, source_message_ids: [1], target_episode_ids: ["episode-id"] }],
602340
+ 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"] }],
602341
+ 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"] }],
602342
+ followups: [{ target: "same_group|private_dm|none", text: "candidate visible reply", reply_to_message_id: 1, rationale: "why", confidence: 0 }]
602343
+ }),
602344
+ "",
602345
+ `Chat: ${options2.chatTitle || options2.chatId} (${options2.chatType})`,
602346
+ `Session: ${options2.sessionKey}`,
602347
+ `Corpus query: ${clean4(corpus.query, 1e3)}`,
602348
+ `Corpus stats: ${JSON.stringify(corpus.stats)}`,
602349
+ `Selected seed: ${corpus.graphWalk.seed ? `${corpus.graphWalk.seed.node.text} degree=${corpus.graphWalk.seed.degree}` : "none"}`,
602350
+ corpus.fallbacks.length ? `Fallbacks: ${corpus.fallbacks.join("; ")}` : "Fallbacks: none",
602351
+ "",
602352
+ "Graph nodes:",
602353
+ walkNodes || "none",
602354
+ "",
602355
+ "Graph edges:",
602356
+ walkEdges || "none",
602357
+ "",
602358
+ "Source episodes:",
602359
+ episodes || "none"
602360
+ ].join("\n");
602361
+ }
602362
+ function parseJsonObject(raw) {
602363
+ const text = raw.trim();
602364
+ if (!text) return null;
602365
+ const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i)?.[1]?.trim();
602366
+ const candidate = fenced || text.slice(text.indexOf("{"), text.lastIndexOf("}") + 1);
602367
+ if (!candidate || !candidate.startsWith("{")) return null;
602368
+ try {
602369
+ const parsed = JSON.parse(candidate);
602370
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
602371
+ } catch {
602372
+ return null;
602373
+ }
602374
+ }
602375
+ function parseTelegramReflectionExtraction(raw) {
602376
+ const parsed = parseJsonObject(raw);
602377
+ if (!parsed) return null;
602378
+ const tags = Array.isArray(parsed.tags) ? parsed.tags.map((item) => {
602379
+ const obj = item;
602380
+ return {
602381
+ label: clean4(obj.label, 80),
602382
+ kind: clean4(obj.kind, 40) || "topic",
602383
+ confidence: clampConfidence(obj.confidence),
602384
+ sourceMessageIds: numberArray(obj.source_message_ids ?? obj.sourceMessageIds),
602385
+ targetEpisodeIds: stringArray(obj.target_episode_ids ?? obj.targetEpisodeIds),
602386
+ targetNodeIds: stringArray(obj.target_node_ids ?? obj.targetNodeIds)
602387
+ };
602388
+ }).filter((item) => item.label) : [];
602389
+ const summaries = Array.isArray(parsed.summaries) ? parsed.summaries.map((item) => {
602390
+ const obj = item;
602391
+ return {
602392
+ title: clean4(obj.title, 120),
602393
+ scope: clean4(obj.scope, 60) || "message_window",
602394
+ text: clean4(obj.text, 1200),
602395
+ confidence: clampConfidence(obj.confidence),
602396
+ sourceMessageIds: numberArray(obj.source_message_ids ?? obj.sourceMessageIds),
602397
+ targetEpisodeIds: stringArray(obj.target_episode_ids ?? obj.targetEpisodeIds)
602398
+ };
602399
+ }).filter((item) => item.title && item.text) : [];
602400
+ const titles = Array.isArray(parsed.titles) ? parsed.titles.map((item) => {
602401
+ const obj = item;
602402
+ return {
602403
+ title: clean4(obj.title, 120),
602404
+ target: clean4(obj.target, 60) || "artifact",
602405
+ confidence: clampConfidence(obj.confidence),
602406
+ sourceMessageIds: numberArray(obj.source_message_ids ?? obj.sourceMessageIds),
602407
+ targetEpisodeIds: stringArray(obj.target_episode_ids ?? obj.targetEpisodeIds)
602408
+ };
602409
+ }).filter((item) => item.title) : [];
602410
+ const extractions = Array.isArray(parsed.extractions) ? parsed.extractions.map((item) => {
602411
+ const obj = item;
602412
+ return {
602413
+ kind: clean4(obj.kind, 60) || "fact",
602414
+ text: clean4(obj.text, 1e3),
602415
+ confidence: clampConfidence(obj.confidence),
602416
+ sourceMessageIds: numberArray(obj.source_message_ids ?? obj.sourceMessageIds),
602417
+ targetEpisodeIds: stringArray(obj.target_episode_ids ?? obj.targetEpisodeIds),
602418
+ targetNodeIds: stringArray(obj.target_node_ids ?? obj.targetNodeIds)
602419
+ };
602420
+ }).filter((item) => item.text) : [];
602421
+ const links2 = Array.isArray(parsed.links) ? parsed.links.map((item) => {
602422
+ const obj = item;
602423
+ return {
602424
+ relation: clean4(obj.relation, 60) || "related_to",
602425
+ srcNodeText: clean4(obj.src_node_text ?? obj.srcNodeText, 180),
602426
+ dstNodeText: clean4(obj.dst_node_text ?? obj.dstNodeText, 180),
602427
+ confidence: clampConfidence(obj.confidence),
602428
+ fact: clean4(obj.fact, 500),
602429
+ sourceMessageIds: numberArray(obj.source_message_ids ?? obj.sourceMessageIds),
602430
+ targetEpisodeIds: stringArray(obj.target_episode_ids ?? obj.targetEpisodeIds)
602431
+ };
602432
+ }).filter((item) => item.srcNodeText && item.dstNodeText) : [];
602433
+ const followups = Array.isArray(parsed.followups) ? parsed.followups.map((item) => {
602434
+ const obj = item;
602435
+ const target = clean4(obj.target, 40);
602436
+ const replyTo = Number(obj.reply_to_message_id ?? obj.replyToMessageId);
602437
+ const normalizedTarget = target === "private_dm" ? "private_dm" : target === "same_group" ? "same_group" : "none";
602438
+ return {
602439
+ target: normalizedTarget,
602440
+ text: clean4(obj.text, 700),
602441
+ replyToMessageId: Number.isFinite(replyTo) ? Math.trunc(replyTo) : null,
602442
+ rationale: clean4(obj.rationale, 400),
602443
+ confidence: clampConfidence(obj.confidence)
602444
+ };
602445
+ }) : [];
602446
+ return {
602447
+ artifactTitle: clean4(parsed.artifact_title ?? parsed.artifactTitle, 140) || "Telegram reflection",
602448
+ tags,
602449
+ summaries,
602450
+ titles,
602451
+ extractions,
602452
+ links: links2,
602453
+ followups,
602454
+ raw
602455
+ };
602456
+ }
602457
+ async function runTelegramReflectionExtraction(options2) {
602458
+ const prompt = buildTelegramReflectionExtractionPrompt(options2);
602459
+ const result = await options2.backend.chatCompletion({
602460
+ messages: [
602461
+ { role: "system", content: "You produce strict JSON for Telegram reflection memory extraction." },
602462
+ { role: "user", content: prompt }
602463
+ ],
602464
+ tools: [],
602465
+ temperature: 0.2,
602466
+ maxTokens: 1800,
602467
+ timeoutMs: options2.timeoutMs,
602468
+ think: false
602469
+ });
602470
+ return parseTelegramReflectionExtraction(result.choices[0]?.message?.content ?? "");
602471
+ }
602472
+ function memoryDbPaths(repoRoot) {
602473
+ return omniusMemoryDbPaths(repoRoot);
602474
+ }
602475
+ function allowedRelation(value2) {
602476
+ const relations = [
602477
+ "caused_by",
602478
+ "discovered_during",
602479
+ "fixed_by",
602480
+ "produced_by",
602481
+ "co_occurred_with",
602482
+ "requires",
602483
+ "is_instance_of",
602484
+ "related_to",
602485
+ "contains",
602486
+ "modified_by",
602487
+ "said_by",
602488
+ "appears_in",
602489
+ "alias_of",
602490
+ "authored_by",
602491
+ "uploaded_by",
602492
+ "replied_to",
602493
+ "depicts",
602494
+ "named_as",
602495
+ "face_match",
602496
+ "voice_sample_of",
602497
+ "speaker_candidate",
602498
+ "same_person_candidate"
602499
+ ];
602500
+ return relations.includes(value2) ? value2 : "related_to";
602501
+ }
602502
+ function nodeText(prefix, value2) {
602503
+ return `${prefix}:${clean4(value2, 180).toLowerCase()}`;
602504
+ }
602505
+ function firstSourceEpisode(itemEpisodeIds, fallback) {
602506
+ return itemEpisodeIds[0] || fallback;
602507
+ }
602508
+ function commitTelegramReflectionExtraction(repoRoot, sessionKey, corpus, extraction) {
602509
+ const paths = memoryDbPaths(repoRoot);
602510
+ const graph = new TemporalGraph(paths.knowledge);
602511
+ const store2 = new EpisodeStore(paths.episodes, graph);
602512
+ try {
602513
+ const reflectionContent = [
602514
+ `Telegram reflection extraction: ${extraction.artifactTitle}`,
602515
+ `session: ${sessionKey}`,
602516
+ `seed: ${corpus.graphWalk.seed?.node.text || "none"}`,
602517
+ `tags: ${extraction.tags.map((tag) => tag.label).join(", ") || "none"}`,
602518
+ "",
602519
+ ...extraction.summaries.map((summary) => `${summary.title}: ${summary.text}`),
602520
+ ...extraction.extractions.map((item) => `${item.kind}: ${item.text}`)
602521
+ ].join("\n");
602522
+ const reflectionEpisodeId = store2.insert({
602523
+ sessionId: sessionKey,
602524
+ modality: "reflection",
602525
+ toolName: "telegram_reflection_extraction",
602526
+ content: reflectionContent,
602527
+ metadata: {
602528
+ sourceSurface: "telegram_reflection",
602529
+ sessionKey,
602530
+ corpusStats: corpus.stats,
602531
+ selectedEpisodeIds: corpus.selectedEpisodeIds,
602532
+ extraction: {
602533
+ artifactTitle: extraction.artifactTitle,
602534
+ tags: extraction.tags,
602535
+ summaries: extraction.summaries,
602536
+ titles: extraction.titles,
602537
+ extractions: extraction.extractions,
602538
+ links: extraction.links,
602539
+ followups: extraction.followups
602540
+ }
602541
+ },
602542
+ importance: 8,
602543
+ decayClass: "procedural"
602544
+ });
602545
+ const artifactNode = graph.upsertNode({ text: nodeText("telegram_reflection", extraction.artifactTitle), nodeType: "concept" });
602546
+ let graphNodes = 1;
602547
+ let graphEdges = 0;
602548
+ const summaryEpisodeIds = [];
602549
+ const seedNodeId = corpus.graphWalk.seed?.node.id;
602550
+ if (seedNodeId) {
602551
+ graph.addEdge({
602552
+ srcId: artifactNode,
602553
+ dstId: seedNodeId,
602554
+ relation: "related_to",
602555
+ fact: `Reflection seed for ${extraction.artifactTitle}`,
602556
+ sourceEpisodeId: reflectionEpisodeId,
602557
+ modality: "reflection",
602558
+ confidence: 0.9
602559
+ });
602560
+ graphEdges++;
602561
+ }
602562
+ for (const tag of extraction.tags) {
602563
+ const tagNode = graph.upsertNode({ text: nodeText("tag", tag.label), nodeType: "concept" });
602564
+ graphNodes++;
602565
+ graph.addEdge({
602566
+ srcId: artifactNode,
602567
+ dstId: tagNode,
602568
+ relation: "contains",
602569
+ fact: `${tag.kind}: ${tag.label}`,
602570
+ sourceEpisodeId: firstSourceEpisode(tag.targetEpisodeIds, reflectionEpisodeId),
602571
+ modality: "reflection",
602572
+ confidence: tag.confidence
602573
+ });
602574
+ graphEdges++;
602575
+ }
602576
+ for (const summary of extraction.summaries) {
602577
+ const episodeId = store2.insert({
602578
+ sessionId: sessionKey,
602579
+ modality: "gist",
602580
+ toolName: "telegram_reflection_summary",
602581
+ content: `${summary.title}
602582
+ ${summary.text}`,
602583
+ metadata: {
602584
+ sourceSurface: "telegram_reflection",
602585
+ sessionKey,
602586
+ scope: summary.scope,
602587
+ sourceMessageIds: summary.sourceMessageIds,
602588
+ targetEpisodeIds: summary.targetEpisodeIds
602589
+ },
602590
+ importance: Math.max(6, summary.confidence * 10),
602591
+ decayClass: "procedural",
602592
+ sourceEpisodeId: reflectionEpisodeId
602593
+ });
602594
+ summaryEpisodeIds.push(episodeId);
602595
+ const summaryNode = graph.upsertNode({ text: nodeText("summary", summary.title), nodeType: "concept" });
602596
+ graphNodes++;
602597
+ graph.addEdge({
602598
+ srcId: artifactNode,
602599
+ dstId: summaryNode,
602600
+ relation: "contains",
602601
+ fact: summary.text,
602602
+ sourceEpisodeId: episodeId,
602603
+ modality: "reflection",
602604
+ confidence: summary.confidence
602605
+ });
602606
+ graphEdges++;
602607
+ }
602608
+ for (const title of extraction.titles) {
602609
+ const titleNode = graph.upsertNode({ text: nodeText("title", title.title), nodeType: "concept" });
602610
+ graphNodes++;
602611
+ graph.addEdge({
602612
+ srcId: artifactNode,
602613
+ dstId: titleNode,
602614
+ relation: "contains",
602615
+ fact: `${title.target}: ${title.title}`,
602616
+ sourceEpisodeId: firstSourceEpisode(title.targetEpisodeIds, reflectionEpisodeId),
602617
+ modality: "reflection",
602618
+ confidence: title.confidence
602619
+ });
602620
+ graphEdges++;
602621
+ }
602622
+ for (const item of extraction.extractions) {
602623
+ const itemNode = graph.upsertNode({ text: nodeText(item.kind, item.text), nodeType: item.kind === "open_question" ? "event" : "concept" });
602624
+ graphNodes++;
602625
+ graph.addEdge({
602626
+ srcId: artifactNode,
602627
+ dstId: itemNode,
602628
+ relation: "contains",
602629
+ fact: item.text,
602630
+ sourceEpisodeId: firstSourceEpisode(item.targetEpisodeIds, reflectionEpisodeId),
602631
+ modality: "reflection",
602632
+ confidence: item.confidence
602633
+ });
602634
+ graphEdges++;
602635
+ }
602636
+ for (const link of extraction.links) {
602637
+ const src2 = graph.upsertNode({ text: clean4(link.srcNodeText, 180), nodeType: "concept" });
602638
+ const dst = graph.upsertNode({ text: clean4(link.dstNodeText, 180), nodeType: "concept" });
602639
+ graphNodes += 2;
602640
+ graph.addEdge({
602641
+ srcId: src2,
602642
+ dstId: dst,
602643
+ relation: allowedRelation(link.relation),
602644
+ fact: link.fact,
602645
+ sourceEpisodeId: firstSourceEpisode(link.targetEpisodeIds, reflectionEpisodeId),
602646
+ modality: "reflection",
602647
+ confidence: link.confidence
602648
+ });
602649
+ graphEdges++;
602650
+ }
602651
+ return { reflectionEpisodeId, summaryEpisodeIds, graphNodes, graphEdges };
602652
+ } finally {
602653
+ store2.close();
602654
+ graph.close();
602655
+ }
602656
+ }
602657
+ var init_telegram_reflection_extraction = __esm({
602658
+ "packages/cli/src/tui/telegram-reflection-extraction.ts"() {
602659
+ "use strict";
602660
+ init_dist7();
602661
+ init_memory_paths();
602662
+ }
602663
+ });
602664
+
601131
602665
  // packages/cli/src/tui/vision-ingress.ts
601132
602666
  var vision_ingress_exports = {};
601133
602667
  __export(vision_ingress_exports, {
@@ -601283,9 +602817,9 @@ var init_vision_ingress = __esm({
601283
602817
 
601284
602818
  // packages/cli/src/tui/telegram-bridge.ts
601285
602819
  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";
602820
+ import { join as join123, resolve as resolve41, basename as basename27, relative as relative13, isAbsolute as isAbsolute7, extname as extname16 } from "node:path";
601287
602821
  import { writeFile as writeFileAsync } from "node:fs/promises";
601288
- import { createHash as createHash21, randomInt } from "node:crypto";
602822
+ import { createHash as createHash23, randomInt } from "node:crypto";
601289
602823
  function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
601290
602824
  const cleaned = stripTelegramHiddenThinking(text).replace(/```(?:json)?/gi, "").replace(/```/g, "").trim();
601291
602825
  const jsonText = cleaned.startsWith("{") ? cleaned : cleaned.match(/\{[\s\S]*\}/)?.[0] ?? "";
@@ -601321,6 +602855,29 @@ function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
601321
602855
  return null;
601322
602856
  }
601323
602857
  }
602858
+ function parseTelegramReflectionFollowupDecision(text) {
602859
+ const cleaned = stripTelegramHiddenThinking(text).replace(/```(?:json)?/gi, "").replace(/```/g, "").trim();
602860
+ const jsonText = cleaned.startsWith("{") ? cleaned : cleaned.match(/\{[\s\S]*\}/)?.[0] ?? "";
602861
+ if (!jsonText) return null;
602862
+ try {
602863
+ const parsed = JSON.parse(jsonText);
602864
+ const shouldSendRaw = parsed["should_send"] ?? parsed["shouldSend"];
602865
+ const shouldSend = shouldSendRaw === true;
602866
+ const confidenceRaw = Number(parsed["confidence"]);
602867
+ const confidence2 = Number.isFinite(confidenceRaw) ? Math.max(0, Math.min(1, confidenceRaw)) : 0;
602868
+ const replyRaw = Number(parsed["reply_to_message_id"] ?? parsed["replyToMessageId"]);
602869
+ const replyToMessageId = Number.isFinite(replyRaw) && replyRaw > 0 ? Math.trunc(replyRaw) : void 0;
602870
+ return {
602871
+ shouldSend,
602872
+ text: String(parsed["text"] ?? "").trim().slice(0, 900),
602873
+ reason: String(parsed["reason"] ?? (shouldSend ? "model selected a scoped follow-up" : "model chose silence")).trim().slice(0, 240),
602874
+ confidence: confidence2,
602875
+ replyToMessageId
602876
+ };
602877
+ } catch {
602878
+ return null;
602879
+ }
602880
+ }
601324
602881
  function convertMarkdownToTelegramHTML(md) {
601325
602882
  let html = md;
601326
602883
  html = html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -601365,8 +602922,8 @@ function stripTelegramHiddenThinking(text) {
601365
602922
  return withoutClosedThink.replace(/<think>[\s\S]*$/gi, "");
601366
602923
  }
601367
602924
  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;
602925
+ const compact2 = stripTelegramHiddenThinking(text).replace(/\s+/g, " ").trim();
602926
+ return compact2.length > maxLength ? compact2.slice(0, Math.max(0, maxLength - 3)) + "..." : compact2;
601370
602927
  }
601371
602928
  function isCodebaseMemoryStatus(text) {
601372
602929
  return /^\s*\[CODEBASE MEMORY\]/i.test(stripTelegramHiddenThinking(text));
@@ -601408,40 +602965,40 @@ function isTelegramNoReplySentinel(text) {
601408
602965
  return lower === "no_reply" || lower.startsWith("no_reply");
601409
602966
  }
601410
602967
  function isTelegramInternalStatusText(text) {
601411
- const compact = compactTelegramVisibleText(text);
601412
- if (!compact) return false;
601413
- const lower = compact.toLowerCase();
601414
- if (isTelegramNoReplySentinel(compact)) return true;
602968
+ const compact2 = compactTelegramVisibleText(text);
602969
+ if (!compact2) return false;
602970
+ const lower = compact2.toLowerCase();
602971
+ if (isTelegramNoReplySentinel(compact2)) return true;
601415
602972
  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;
602973
+ if (/^memory stage:/i.test(compact2)) return true;
602974
+ if (/^\[ppr[-_\s]?skip\]/i.test(compact2)) return true;
602975
+ if (/^(casual|ambient|group)\b.{0,180}\b(skipping|skipped|not directed|no action needed|no reply)\b/i.test(compact2)) return true;
602976
+ if (/^no further action needed\b/i.test(compact2)) return true;
602977
+ if (/^no action needed\b.{0,120}\b(task|complete|completed|done)\b/i.test(compact2)) return true;
602978
+ if (/^(there'?s|there is) no active task\b/i.test(compact2)) return true;
602979
+ if (/^everything'?s (done|complete|completed|wrapped up)\b/i.test(compact2)) return true;
602980
+ if (/\balready (been )?(provided|answered|handled|delivered) above\b/i.test(compact2)) return true;
602981
+ if (/\b(no remaining work|nothing left to do|task is complete|task has been completed)\b/i.test(compact2)) return true;
601425
602982
  return false;
601426
602983
  }
601427
602984
  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);
602985
+ const clean5 = stripTelegramHiddenThinking(text).trim();
602986
+ if (!clean5) return "";
602987
+ if (options2.suppressPotentialNoReplyPrefix && isTelegramPotentialNoReplyPrefix(clean5)) return "";
602988
+ if (isTelegramInternalStatusText(clean5)) return "";
602989
+ return dedupeTelegramVisibleReply(clean5);
601433
602990
  }
601434
602991
  function dedupeTelegramVisibleReply(text) {
601435
602992
  const paragraphs = text.split(/\n{2,}/);
601436
602993
  const seenParagraphs = /* @__PURE__ */ new Set();
601437
602994
  const collapsedParagraphs = [];
601438
602995
  for (const paragraph of paragraphs) {
601439
- const clean3 = paragraph.trim();
601440
- if (!clean3) continue;
601441
- const key = compactTelegramVisibleText(clean3).toLowerCase();
602996
+ const clean5 = paragraph.trim();
602997
+ if (!clean5) continue;
602998
+ const key = compactTelegramVisibleText(clean5).toLowerCase();
601442
602999
  if (seenParagraphs.has(key)) continue;
601443
603000
  seenParagraphs.add(key);
601444
- collapsedParagraphs.push(clean3);
603001
+ collapsedParagraphs.push(clean5);
601445
603002
  }
601446
603003
  const paragraphCollapsed = collapsedParagraphs.join("\n\n");
601447
603004
  const sentenceLike = paragraphCollapsed.match(/[^.!?]+[.!?]+|[^.!?]+$/g);
@@ -601471,9 +603028,9 @@ function truncateTelegramContext(text, maxLength) {
601471
603028
  [Telegram context truncated; use tools for full detail.]`;
601472
603029
  }
601473
603030
  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()}...`;
603031
+ const compact2 = stripTelegramHiddenThinking(text).replace(/\s+/g, " ").trim();
603032
+ if (compact2.length <= maxLength) return compact2;
603033
+ return `${compact2.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;
601477
603034
  }
601478
603035
  function telegramSpeakerLabel(msg) {
601479
603036
  if (msg.username && msg.username !== "unknown") return `@${msg.username}`;
@@ -601620,7 +603177,7 @@ function buildTelegramRuntimeContext(now = /* @__PURE__ */ new Date(), repoRoot)
601620
603177
  ].filter(Boolean).join("\n");
601621
603178
  }
601622
603179
  function telegramSessionIdFromKey(sessionKey) {
601623
- return `telegram-${createHash21("sha1").update(sessionKey).digest("hex").slice(0, 16)}`;
603180
+ return `telegram-${createHash23("sha1").update(sessionKey).digest("hex").slice(0, 16)}`;
601624
603181
  }
601625
603182
  function selectTelegramFinalResponse(args) {
601626
603183
  const committedVisibleReply = cleanTelegramVisibleReply(args.visibleReplyText || "");
@@ -601695,10 +603252,17 @@ function telegramSyntheticHelpSignatures() {
601695
603252
  { signature: "/help", description: "Show Telegram command help" },
601696
603253
  { signature: "/start", description: "Show Telegram bridge status and authentication instructions" },
601697
603254
  { 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" }
603255
+ { signature: "/call", description: "Get the active voice call link when a call session is running" },
603256
+ { signature: "/reflect", description: "Run scoped Telegram chat reflection over retained chat history" },
603257
+ { signature: "/reflect status", description: "Show the latest scoped Telegram reflection artifact" },
603258
+ { signature: "/reflect now", description: "Run reflection and let the model decide whether to post a public follow-up" },
603259
+ { signature: "/reflect auto on|off", description: "Enable or disable model-gated idle follow-ups for this chat" },
603260
+ { signature: "/reflection", description: "Alias for /reflect" },
603261
+ { signature: "/daydream", description: "Alias for /reflect in Telegram chats" }
601699
603262
  ];
601700
603263
  }
601701
603264
  function telegramHelpCommandAllowed(cmd, scope) {
603265
+ if (cmd.name === "dream") return false;
601702
603266
  if (scope === "admin") return cmd.implementationStatus === "implemented";
601703
603267
  if (TELEGRAM_PUBLIC_HELP_COMMANDS.has(cmd.name)) return true;
601704
603268
  return cmd.surfaces.agentTool && !cmd.safety.secretBearing && !cmd.safety.destructive && !cmd.safety.profileGated;
@@ -601729,6 +603293,65 @@ function buildTelegramHelpHTML(scope, maxPublicCommands = 24) {
601729
603293
  }
601730
603294
  return lines.join("\n");
601731
603295
  }
603296
+ function formatTelegramReflectionSummaryHTML(artifact, opts = { autoFollowup: false }) {
603297
+ const topThreads = artifact.curiosityThreads.slice(0, 3);
603298
+ const topReplies = artifact.replyOpportunities.slice(0, 3);
603299
+ const groupPlans = artifact.outreachPlans.filter((plan) => plan.target === "same_group").slice(0, 2);
603300
+ const topTags = (artifact.tagging ?? []).slice(0, 6);
603301
+ const topSummaries = (artifact.summation ?? []).slice(0, 3);
603302
+ const topExtractions = (artifact.extraction ?? []).slice(0, 4);
603303
+ const topLinks = (artifact.linking ?? []).slice(0, 4);
603304
+ const lines = [
603305
+ `<b>Telegram reflection</b>`,
603306
+ `Scope: <code>${escapeTelegramHTML(artifact.chatTitle || artifact.chatId)}</code> (${escapeTelegramHTML(artifact.chatType)})`,
603307
+ `Window: ${artifact.messageWindow.totalRetained} retained messages`,
603308
+ artifact.corpus?.stats ? `Corpus: ${artifact.corpus.stats.selectedEpisodes} selected episodes, ${artifact.corpus.stats.graphNodesVisited} graph nodes, ${artifact.corpus.stats.graphEdgesTraversed} graph edges` : "",
603309
+ artifact.selectedSeed ? `Seed: <code>${escapeTelegramHTML(artifact.selectedSeed.nodeText)}</code>` : "",
603310
+ `Auto follow-up: <b>${opts.autoFollowup ? "on" : "off"}</b>`,
603311
+ opts.sentFollowup ? `Follow-up: <b>sent</b>` : opts.followupReason ? `Follow-up: ${escapeTelegramHTML(opts.followupReason)}` : "",
603312
+ "",
603313
+ topTags.length ? `<b>Tags</b>` : "",
603314
+ ...topTags.map(
603315
+ (tag) => `- ${escapeTelegramHTML(tag.label)} (${escapeTelegramHTML(tag.kind)}, ${Math.round(tag.confidence * 100)}%)`
603316
+ ),
603317
+ topSummaries.length ? "" : "",
603318
+ topSummaries.length ? `<b>Summaries</b>` : "",
603319
+ ...topSummaries.map(
603320
+ (summary) => `- ${escapeTelegramHTML(summary.title)}: ${escapeTelegramHTML(summary.text)}`
603321
+ ),
603322
+ topExtractions.length ? "" : "",
603323
+ topExtractions.length ? `<b>Extractions</b>` : "",
603324
+ ...topExtractions.map(
603325
+ (item) => `- ${escapeTelegramHTML(item.kind)}: ${escapeTelegramHTML(item.text)}`
603326
+ ),
603327
+ topLinks.length ? "" : "",
603328
+ topLinks.length ? `<b>Links</b>` : "",
603329
+ ...topLinks.map(
603330
+ (link) => `- ${escapeTelegramHTML(link.srcNodeText)} -> ${escapeTelegramHTML(link.dstNodeText)} (${escapeTelegramHTML(link.relation)})`
603331
+ ),
603332
+ artifact.corpus?.fallbacks?.length ? "" : "",
603333
+ artifact.corpus?.fallbacks?.length ? `<b>Fallbacks</b>` : "",
603334
+ ...(artifact.corpus?.fallbacks ?? []).slice(0, 3).map((fallback) => `- ${escapeTelegramHTML(fallback)}`),
603335
+ "",
603336
+ topThreads.length ? `<b>Curiosity threads</b>` : "",
603337
+ ...topThreads.map(
603338
+ (thread) => `- ${escapeTelegramHTML(thread.question)} (${Math.round(thread.intensity * 100)}%)`
603339
+ ),
603340
+ topReplies.length ? "" : "",
603341
+ topReplies.length ? `<b>Reply opportunities</b>` : "",
603342
+ ...topReplies.map(
603343
+ (item) => `- ${escapeTelegramHTML(item.trigger)} (${Math.round(item.confidence * 100)}%)`
603344
+ ),
603345
+ groupPlans.length ? "" : "",
603346
+ groupPlans.length ? `<b>Same-group outreach plans</b>` : "",
603347
+ ...groupPlans.map(
603348
+ (plan) => `- ${escapeTelegramHTML(plan.trigger)} (${Math.round(plan.confidence * 100)}%, ${escapeTelegramHTML(plan.gate)})`
603349
+ ),
603350
+ "",
603351
+ "Saved as scoped Telegram context for future routing. It reflects chat history only, not the workspace filesystem."
603352
+ ].filter((line) => line !== "");
603353
+ return lines.join("\n");
603354
+ }
601732
603355
  function splitTelegramHTMLMessage(html, maxLength = 3600) {
601733
603356
  const chunks = [];
601734
603357
  let current = "";
@@ -602084,6 +603707,16 @@ function telegramImageMime(media) {
602084
603707
  if (ext === ".tif" || ext === ".tiff") return "image/tiff";
602085
603708
  return "image/jpeg";
602086
603709
  }
603710
+ function appendMediaContextBlock(description, block) {
603711
+ const clean5 = block.trim();
603712
+ if (!clean5) return description;
603713
+ if (description.endsWith("]")) return `${description.slice(0, -1)}
603714
+
603715
+ ${clean5}]`;
603716
+ return `${description}
603717
+
603718
+ ${clean5}`;
603719
+ }
602087
603720
  function telegramCachedMediaIsImage(entry) {
602088
603721
  if (entry.mediaType === "photo") return true;
602089
603722
  if (entry.mimeType?.toLowerCase().startsWith("image/")) return true;
@@ -602312,7 +603945,7 @@ function renderTelegramSubAgentError(username, error) {
602312
603945
  process.stdout.write(` ${c3.dim("⎿")} ${c3.red("✘")} @${username}: ${c3.dim(preview)}
602313
603946
  `);
602314
603947
  }
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;
603948
+ 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
603949
  var init_telegram_bridge = __esm({
602317
603950
  "packages/cli/src/tui/telegram-bridge.ts"() {
602318
603951
  "use strict";
@@ -602328,7 +603961,10 @@ var init_telegram_bridge = __esm({
602328
603961
  init_omnius_directory();
602329
603962
  init_stimulation();
602330
603963
  init_identity_memory_tool();
603964
+ init_visual_identity_association();
602331
603965
  init_telegram_channel_dmn();
603966
+ init_telegram_reflection_corpus();
603967
+ init_telegram_reflection_extraction();
602332
603968
  TELEGRAM_SAFETY_PROMPT = `
602333
603969
  CRITICAL SAFETY NOTICE — PUBLIC TELEGRAM CHANNEL
602334
603970
 
@@ -602512,6 +604148,7 @@ Telegram response contract:
602512
604148
  ]);
602513
604149
  TELEGRAM_PUBLIC_HELP_COMMANDS = /* @__PURE__ */ new Set(["help", "start", "auth", "call"]);
602514
604150
  TELEGRAM_REMINDER_SLASH_COMMANDS = /* @__PURE__ */ new Set(["remind", "reminder", "reminders"]);
604151
+ TELEGRAM_REFLECTION_SLASH_COMMANDS = /* @__PURE__ */ new Set(["reflect", "reflection", "daydream", "dream"]);
602515
604152
  TELEGRAM_IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".tiff", ".tif", ".svg"]);
602516
604153
  MEDIA_CACHE_TTL_MS = 30 * 60 * 1e3;
602517
604154
  TELEGRAM_CHANNEL_DMN_SWEEP_MS = 2 * 60 * 1e3;
@@ -602614,6 +604251,8 @@ Telegram response contract:
602614
604251
  channelDmnRunning = /* @__PURE__ */ new Set();
602615
604252
  /** Daydream artifact already used to force a live-router check. */
602616
604253
  channelDmnPromptedArtifactAt = /* @__PURE__ */ new Map();
604254
+ /** Per-chat reflection settings for model-gated idle follow-ups. */
604255
+ channelReflectionState = /* @__PURE__ */ new Map();
602617
604256
  /** Set admin user ID filter */
602618
604257
  setAdmin(userId) {
602619
604258
  this.adminUserId = userId;
@@ -602698,7 +604337,7 @@ Telegram response contract:
602698
604337
  return !!this.adminAuthChallenge && this.adminAuthChallenge.expiresAtMs > Date.now();
602699
604338
  }
602700
604339
  hashAdminAuthCode(code8) {
602701
- return createHash21("sha256").update(`omnius-telegram-admin:${code8.trim()}`).digest("hex");
604340
+ return createHash23("sha256").update(`omnius-telegram-admin:${code8.trim()}`).digest("hex");
602702
604341
  }
602703
604342
  viewIdForMessage(msg) {
602704
604343
  return `telegram-${this.sessionKeyForMessage(msg).replace(/[^A-Za-z0-9_-]/g, "-")}`;
@@ -602769,6 +604408,69 @@ Telegram response contract:
602769
604408
  result.success ? result.output || "Reminder updated." : result.error || `/${cmd} failed`
602770
604409
  );
602771
604410
  }
604411
+ async handleTelegramReflectionSlash(msg, commandText) {
604412
+ if (!this.repoRoot) {
604413
+ await this.replyToTelegramMessage(msg, "Telegram reflection storage is not available yet.");
604414
+ return;
604415
+ }
604416
+ const sessionKey = this.sessionKeyForMessage(msg);
604417
+ this.ensureTelegramConversationLoaded(sessionKey);
604418
+ const first2 = commandText.trim().split(/\s+/)[0] ?? "";
604419
+ const commandName = first2.slice(1).split("@")[0]?.toLowerCase() || "reflect";
604420
+ const arg = commandText.trim().slice(first2.length).trim();
604421
+ const lower = arg.toLowerCase();
604422
+ const state = this.reflectionStateForSession(sessionKey);
604423
+ if (commandName === "dream" && (lower === "deep" || lower === "lucid" || lower === "consolidate")) {
604424
+ await this.replyToTelegramMessage(
604425
+ msg,
604426
+ "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."
604427
+ );
604428
+ return;
604429
+ }
604430
+ if (lower === "auto on" || lower === "on") {
604431
+ state.autoFollowup = true;
604432
+ this.saveTelegramConversationState(sessionKey);
604433
+ await this.replyToTelegramMessage(msg, "Scoped Telegram reflection auto follow-up is on for this chat. Idle cycles remain model-gated and public-only.");
604434
+ return;
604435
+ }
604436
+ if (lower === "auto off" || lower === "off" || lower === "stop") {
604437
+ state.autoFollowup = false;
604438
+ this.saveTelegramConversationState(sessionKey);
604439
+ await this.replyToTelegramMessage(msg, "Scoped Telegram reflection auto follow-up is off for this chat.");
604440
+ return;
604441
+ }
604442
+ if (lower === "auto" || lower === "status" || lower === "show") {
604443
+ const latest = this.latestTelegramChannelDaydream(sessionKey);
604444
+ const html2 = latest ? formatTelegramReflectionSummaryHTML(latest, { autoFollowup: state.autoFollowup }) : `<b>Telegram reflection</b>
604445
+ No scoped reflection artifact exists yet for this chat. Use <code>/reflect</code> to build one from retained Telegram history.`;
604446
+ await this.replyToTelegramMessage(msg, html2, {
604447
+ html: true,
604448
+ replyToMessageId: msg.chatType !== "private" ? msg.messageId : void 0
604449
+ });
604450
+ return;
604451
+ }
604452
+ const artifact = await this.runTelegramChannelDmnForSession(sessionKey, "telegram-command", Date.now(), true);
604453
+ if (!artifact) {
604454
+ await this.replyToTelegramMessage(
604455
+ msg,
604456
+ "No retained Telegram chat history is available for scoped reflection yet. Send a few messages in this chat first, then run /reflect again."
604457
+ );
604458
+ return;
604459
+ }
604460
+ let followup;
604461
+ if (lower === "now" || lower === "reply" || lower === "send") {
604462
+ followup = await this.maybeSendTelegramReflectionFollowup(sessionKey, artifact, "telegram-command");
604463
+ }
604464
+ const html = formatTelegramReflectionSummaryHTML(artifact, {
604465
+ autoFollowup: state.autoFollowup,
604466
+ sentFollowup: followup?.sent,
604467
+ followupReason: followup?.reason
604468
+ });
604469
+ await this.replyToTelegramMessage(msg, html, {
604470
+ html: true,
604471
+ replyToMessageId: msg.chatType !== "private" ? msg.messageId : void 0
604472
+ });
604473
+ }
602772
604474
  /** Write to the scrollable TUI waterfall area (respects status bar scroll region) */
602773
604475
  tuiWrite(fn) {
602774
604476
  if (this.writeContent) {
@@ -602887,9 +604589,9 @@ Telegram response contract:
602887
604589
  return reply;
602888
604590
  }
602889
604591
  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;
604592
+ const clean5 = stripTelegramHiddenThinking(text).trim();
604593
+ const clipped = clean5.length > maxLength ? `${clean5.slice(0, Math.max(0, maxLength - 60)).trimEnd()}
604594
+ [reply context truncated]` : clean5;
602893
604595
  return clipped.split(/\r?\n/).map((line) => `> ${line}`).join("\n");
602894
604596
  }
602895
604597
  buildTelegramCurrentReplyContext(sessionKey, msg) {
@@ -602995,6 +604697,7 @@ ${mediaContext}` : ""
602995
604697
  const modality = media.type === "audio" || media.type === "voice" ? "audio" : telegramMediaIsImage(media) ? "visual" : "text";
602996
604698
  const payload = {
602997
604699
  sourceSurface: "telegram",
604700
+ sessionId: this.sessionKeyForMessage(msg),
602998
604701
  scope: this.telegramMemoryScope(msg),
602999
604702
  sender,
603000
604703
  message: {
@@ -603036,7 +604739,7 @@ ${mediaContext}` : ""
603036
604739
  return payload;
603037
604740
  }
603038
604741
  telegramConversationPath(sessionKey) {
603039
- const safe = createHash21("sha1").update(sessionKey).digest("hex").slice(0, 20);
604742
+ const safe = createHash23("sha1").update(sessionKey).digest("hex").slice(0, 20);
603040
604743
  return join123(this.telegramConversationDir, `${safe}.json`);
603041
604744
  }
603042
604745
  telegramPersonalityScope(sessionKey, msg) {
@@ -603076,6 +604779,13 @@ ${mediaContext}` : ""
603076
604779
  if (parsed.stimulation) {
603077
604780
  this.stimulation.setState(sessionKey, parsed.stimulation);
603078
604781
  }
604782
+ if (parsed.reflection) {
604783
+ this.channelReflectionState.set(sessionKey, {
604784
+ autoFollowup: parsed.reflection.autoFollowup === true,
604785
+ lastFollowupAt: typeof parsed.reflection.lastFollowupAt === "number" ? parsed.reflection.lastFollowupAt : void 0,
604786
+ lastFollowupArtifactAt: typeof parsed.reflection.lastFollowupArtifactAt === "string" ? parsed.reflection.lastFollowupArtifactAt : void 0
604787
+ });
604788
+ }
603079
604789
  } catch {
603080
604790
  }
603081
604791
  }
@@ -603110,7 +604820,8 @@ ${mediaContext}` : ""
603110
604820
  history: this.chatHistory.get(sessionKey) ?? [],
603111
604821
  participants,
603112
604822
  memoryCards: this.chatMemoryCards.get(sessionKey) ?? [],
603113
- stimulation: this.stimulation.getState(sessionKey)
604823
+ stimulation: this.stimulation.getState(sessionKey),
604824
+ reflection: this.channelReflectionState.get(sessionKey) ?? { autoFollowup: false }
603114
604825
  };
603115
604826
  writeFileSync57(this.telegramConversationPath(sessionKey), JSON.stringify(payload, null, 2) + "\n", "utf8");
603116
604827
  } catch {
@@ -603120,6 +604831,21 @@ ${mediaContext}` : ""
603120
604831
  if (!this.repoRoot) return null;
603121
604832
  return latestTelegramChannelDaydream(this.repoRoot, sessionKey);
603122
604833
  }
604834
+ reflectionStateForSession(sessionKey) {
604835
+ const existing = this.channelReflectionState.get(sessionKey);
604836
+ if (existing) return existing;
604837
+ const created = { autoFollowup: false };
604838
+ this.channelReflectionState.set(sessionKey, created);
604839
+ return created;
604840
+ }
604841
+ telegramReflectionEmbeddingConfig() {
604842
+ if (!this.agentConfig || this.agentConfig.backendType !== "ollama") return false;
604843
+ return {
604844
+ baseUrl: this.agentConfig.backendUrl,
604845
+ model: process.env["OMNIUS_EMBEDDING_MODEL"] || "nomic-embed-text",
604846
+ timeoutMs: Math.min(Math.max(this.agentConfig.timeoutMs ?? 3e4, 5e3), 3e4)
604847
+ };
604848
+ }
603123
604849
  buildTelegramChannelDaydreamInput(sessionKey, nowMs = Date.now()) {
603124
604850
  const history = this.chatHistory.get(sessionKey) ?? [];
603125
604851
  if (history.length === 0) return null;
@@ -603153,11 +604879,18 @@ ${mediaContext}` : ""
603153
604879
  role: entry.role,
603154
604880
  speaker: entry.speaker,
603155
604881
  username: entry.username,
604882
+ firstName: entry.firstName,
604883
+ fromUserId: entry.fromUserId,
603156
604884
  text: entry.text,
603157
604885
  ts: entry.ts,
603158
604886
  mode: entry.mode,
604887
+ chatId: entry.chatId,
604888
+ chatType: entry.chatType,
604889
+ chatTitle: entry.chatTitle,
603159
604890
  messageId: entry.messageId,
604891
+ messageThreadId: entry.messageThreadId,
603160
604892
  replyToMessageId: entry.replyToMessageId,
604893
+ replyContext: entry.replyContext,
603161
604894
  mediaSummary: entry.mediaSummary
603162
604895
  })),
603163
604896
  participants,
@@ -603172,7 +604905,7 @@ ${mediaContext}` : ""
603172
604905
  if (this.subAgents.has(sessionKey)) return null;
603173
604906
  const input = this.buildTelegramChannelDaydreamInput(sessionKey, nowMs);
603174
604907
  if (!input) return null;
603175
- if (input.chatType === "private") return null;
604908
+ if (input.chatType === "private" && !force) return null;
603176
604909
  const lastMessageAt = input.history.map((entry) => entry.ts).filter((ts) => typeof ts === "number").sort((a2, b) => b - a2)[0] ?? nowMs;
603177
604910
  const lastRunAt = this.channelDmnLastRunAt.get(sessionKey) ?? 0;
603178
604911
  if (!force) {
@@ -603182,9 +604915,51 @@ ${mediaContext}` : ""
603182
604915
  }
603183
604916
  this.channelDmnRunning.add(sessionKey);
603184
604917
  try {
603185
- const artifact = buildTelegramChannelDaydream(input);
604918
+ const corpus = await buildTelegramReflectionCorpus({
604919
+ repoRoot: this.repoRoot,
604920
+ sessionKey,
604921
+ chatId: input.chatId,
604922
+ chatType: input.chatType,
604923
+ chatTitle: input.chatTitle,
604924
+ generatedAtMs: nowMs,
604925
+ history: input.history,
604926
+ embeddingConfig: this.telegramReflectionEmbeddingConfig()
604927
+ });
604928
+ let extraction = null;
604929
+ let extractionCommit;
604930
+ if (this.agentConfig) {
604931
+ try {
604932
+ const backend = new OllamaAgenticBackend(
604933
+ this.agentConfig.backendUrl,
604934
+ this.agentConfig.model,
604935
+ this.agentConfig.apiKey
604936
+ );
604937
+ extraction = await runTelegramReflectionExtraction({
604938
+ backend: {
604939
+ chatCompletion: (args) => backend.chatCompletion(args)
604940
+ },
604941
+ corpus,
604942
+ sessionKey,
604943
+ chatId: input.chatId,
604944
+ chatType: input.chatType,
604945
+ chatTitle: input.chatTitle,
604946
+ timeoutMs: Math.min(Math.max(this.agentConfig.timeoutMs ?? 3e4, 5e3), 3e4)
604947
+ });
604948
+ if (extraction) {
604949
+ extractionCommit = commitTelegramReflectionExtraction(this.repoRoot, sessionKey, corpus, extraction);
604950
+ } else {
604951
+ corpus.fallbacks.push("structured extraction unavailable: model returned no valid JSON");
604952
+ }
604953
+ } catch (err) {
604954
+ corpus.fallbacks.push(`structured extraction unavailable: ${err instanceof Error ? err.message : String(err)}`);
604955
+ }
604956
+ } else {
604957
+ corpus.fallbacks.push("structured extraction unavailable: backend unavailable");
604958
+ }
604959
+ const artifact = buildTelegramChannelDaydream(input, corpus, extraction, extractionCommit);
603186
604960
  writeTelegramChannelDaydream(this.repoRoot, artifact);
603187
604961
  this.channelDmnLastRunAt.set(sessionKey, nowMs);
604962
+ this.channelDmnPromptedArtifactAt.delete(sessionKey);
603188
604963
  void reason;
603189
604964
  return artifact;
603190
604965
  } finally {
@@ -603196,7 +604971,123 @@ ${mediaContext}` : ""
603196
604971
  this.ensureAllTelegramConversationsLoaded();
603197
604972
  const now = Date.now();
603198
604973
  for (const sessionKey of this.chatHistory.keys()) {
603199
- await this.runTelegramChannelDmnForSession(sessionKey, reason, now).catch(() => null);
604974
+ const artifact = await this.runTelegramChannelDmnForSession(sessionKey, reason, now).catch(() => null);
604975
+ if (!artifact) continue;
604976
+ const reflection = this.reflectionStateForSession(sessionKey);
604977
+ if (reflection.autoFollowup) {
604978
+ await this.maybeSendTelegramReflectionFollowup(sessionKey, artifact, reason).catch(() => null);
604979
+ }
604980
+ }
604981
+ }
604982
+ telegramChatIdFromArtifact(artifact) {
604983
+ const numeric = Number(artifact.chatId);
604984
+ return Number.isFinite(numeric) && String(Math.trunc(numeric)) === artifact.chatId ? Math.trunc(numeric) : artifact.chatId;
604985
+ }
604986
+ async maybeSendTelegramReflectionFollowup(sessionKey, artifact, reason = "reflection") {
604987
+ if (!this.agentConfig) return { sent: false, reason: "backend unavailable" };
604988
+ if (artifact.chatType === "private") return { sent: false, reason: "private chat reflection stored without autonomous outreach" };
604989
+ const state = this.reflectionStateForSession(sessionKey);
604990
+ if (state.lastFollowupArtifactAt === artifact.generatedAt) {
604991
+ return { sent: false, reason: "follow-up already evaluated for this artifact" };
604992
+ }
604993
+ const now = Date.now();
604994
+ if (state.lastFollowupAt && now - state.lastFollowupAt < 60 * 6e4) {
604995
+ return { sent: false, reason: "rate limit held public follow-up" };
604996
+ }
604997
+ const candidateMessageIds = Array.from(new Set([
604998
+ ...artifact.curiosityThreads.flatMap((thread) => thread.sourceMessages ?? []),
604999
+ ...artifact.memoryProposals.flatMap((proposal) => proposal.sourceMessages ?? []),
605000
+ ...(artifact.tagging ?? []).flatMap((item) => item.sourceMessageIds ?? []),
605001
+ ...(artifact.summation ?? []).flatMap((item) => item.sourceMessageIds ?? []),
605002
+ ...(artifact.titling ?? []).flatMap((item) => item.sourceMessageIds ?? []),
605003
+ ...(artifact.extraction ?? []).flatMap((item) => item.sourceMessageIds ?? []),
605004
+ ...(artifact.linking ?? []).flatMap((item) => item.sourceMessageIds ?? []),
605005
+ ...(artifact.extractionFollowups ?? []).map((item) => item.replyToMessageId).filter((id) => typeof id === "number")
605006
+ ].filter((id) => typeof id === "number" && Number.isFinite(id))));
605007
+ const context2 = this.buildTelegramConversationContextStream(sessionKey, {
605008
+ chatId: this.telegramChatIdFromArtifact(artifact),
605009
+ chatType: artifact.chatType,
605010
+ chatTitle: artifact.chatTitle,
605011
+ text: `[internal Telegram reflection cycle: ${reason}]`,
605012
+ username: this.state.botUsername || "omnius",
605013
+ firstName: "Omnius",
605014
+ fromUserId: 0,
605015
+ messageId: 0
605016
+ }, 40);
605017
+ const prompt = [
605018
+ "You are deciding whether an idle Telegram reflection cycle should post one public follow-up in the same group.",
605019
+ 'Return JSON only: {"should_send":boolean,"text":"public message text","reply_to_message_id":number|null,"confidence":0.0-1.0,"reason":"short reason"}.',
605020
+ "",
605021
+ "Hard constraints:",
605022
+ "- Use only the Telegram chat context and reflection artifact below.",
605023
+ "- Do not inspect or reference the workspace filesystem.",
605024
+ "- Default should_send=false unless a concise public follow-up is timely, low-intrusion, and clearly useful.",
605025
+ "- Do not reveal hidden meta-analysis, local paths, internal tools, or that a private artifact exists.",
605026
+ "- If sending, write one short message under 700 characters.",
605027
+ "- Prefer replying to one of the candidate message IDs when the follow-up is anchored to old content.",
605028
+ "- Private DM outreach is admin-review only and must not be sent by this autonomous public follow-up path.",
605029
+ "",
605030
+ `Candidate reply message IDs: ${candidateMessageIds.length ? candidateMessageIds.join(", ") : "none"}`,
605031
+ "",
605032
+ formatTelegramChannelDaydreamContext(artifact),
605033
+ "",
605034
+ context2
605035
+ ].join("\n");
605036
+ try {
605037
+ const backend = new OllamaAgenticBackend(
605038
+ this.agentConfig.backendUrl,
605039
+ this.agentConfig.model,
605040
+ this.agentConfig.apiKey
605041
+ );
605042
+ const result = await backend.chatCompletion({
605043
+ messages: [
605044
+ { role: "system", content: "You are a Telegram public-follow-up discretion model. Output strict JSON only." },
605045
+ { role: "user", content: prompt }
605046
+ ],
605047
+ tools: [],
605048
+ temperature: 0.2,
605049
+ maxTokens: 300,
605050
+ timeoutMs: Math.min(Math.max(this.agentConfig.timeoutMs ?? 3e4, 5e3), 2e4),
605051
+ think: false
605052
+ });
605053
+ const decision = parseTelegramReflectionFollowupDecision(result.choices[0]?.message?.content ?? "");
605054
+ state.lastFollowupArtifactAt = artifact.generatedAt;
605055
+ if (!decision) {
605056
+ this.saveTelegramConversationState(sessionKey);
605057
+ return { sent: false, reason: "model returned no usable follow-up decision" };
605058
+ }
605059
+ if (!decision.shouldSend || !decision.text || decision.confidence < 0.66) {
605060
+ this.saveTelegramConversationState(sessionKey);
605061
+ return { sent: false, reason: decision.reason || "model chose silence" };
605062
+ }
605063
+ const replyTo = decision.replyToMessageId && candidateMessageIds.includes(decision.replyToMessageId) ? decision.replyToMessageId : void 0;
605064
+ const chatId = this.telegramChatIdFromArtifact(artifact);
605065
+ const sentMessageId = await this.sendMessageHTML(
605066
+ chatId,
605067
+ convertMarkdownToTelegramHTML(decision.text),
605068
+ replyTo
605069
+ );
605070
+ this.recordTelegramAssistantMessage({
605071
+ chatId,
605072
+ chatType: artifact.chatType,
605073
+ chatTitle: artifact.chatTitle,
605074
+ text: decision.text,
605075
+ username: this.state.botUsername || "omnius",
605076
+ firstName: "Omnius",
605077
+ fromUserId: 0,
605078
+ messageId: sentMessageId ?? 0
605079
+ }, decision.text, "chat", {
605080
+ messageId: sentMessageId,
605081
+ replyToMessageId: replyTo
605082
+ });
605083
+ state.lastFollowupAt = now;
605084
+ this.saveTelegramConversationState(sessionKey);
605085
+ this.tuiWrite(() => renderTelegramSubAgentEvent("reflection", `sent scoped Telegram follow-up (${decision.confidence.toFixed(2)}): ${decision.reason}`));
605086
+ return { sent: true, reason: decision.reason };
605087
+ } catch (err) {
605088
+ state.lastFollowupArtifactAt = artifact.generatedAt;
605089
+ this.saveTelegramConversationState(sessionKey);
605090
+ return { sent: false, reason: err instanceof Error ? err.message : String(err) };
603200
605091
  }
603201
605092
  }
603202
605093
  formatLatestTelegramChannelDaydreamContext(sessionKey) {
@@ -603220,6 +605111,22 @@ ${mediaContext}` : ""
603220
605111
  return "";
603221
605112
  }
603222
605113
  }
605114
+ upsertTelegramReflectionHistoryEntry(sessionKey, entry) {
605115
+ if (!this.repoRoot || entry.chatId === void 0) return;
605116
+ try {
605117
+ upsertTelegramReflectionMessage(
605118
+ this.repoRoot,
605119
+ sessionKey,
605120
+ entry,
605121
+ {
605122
+ chatId: String(entry.chatId),
605123
+ chatType: entry.chatType || "unknown",
605124
+ chatTitle: entry.chatTitle
605125
+ }
605126
+ );
605127
+ } catch {
605128
+ }
605129
+ }
603223
605130
  recordTelegramUserMessage(msg, mode, textOverride) {
603224
605131
  const sessionKey = this.sessionKeyForMessage(msg);
603225
605132
  const mediaSummary = summarizeTelegramMessageAttachments(msg);
@@ -603242,6 +605149,7 @@ ${mediaContext}` : ""
603242
605149
  mediaSummary
603243
605150
  };
603244
605151
  this.recordChatHistory(sessionKey, entry);
605152
+ this.upsertTelegramReflectionHistoryEntry(sessionKey, entry);
603245
605153
  this.updateTelegramParticipantProfile(sessionKey, msg, text);
603246
605154
  this.updateTelegramMemoryCards(sessionKey, entry);
603247
605155
  try {
@@ -603261,12 +605169,12 @@ ${mediaContext}` : ""
603261
605169
  this.saveTelegramConversationState(sessionKey);
603262
605170
  }
603263
605171
  recordTelegramAssistantMessage(msg, text, mode, options2 = {}) {
603264
- const clean3 = stripTelegramHiddenThinking(text).trim();
603265
- if (!clean3) return;
605172
+ const clean5 = stripTelegramHiddenThinking(text).trim();
605173
+ if (!clean5) return;
603266
605174
  const sessionKey = this.sessionKeyForMessage(msg);
603267
605175
  const entry = {
603268
605176
  role: "assistant",
603269
- text: clean3,
605177
+ text: clean5,
603270
605178
  mode,
603271
605179
  chatId: msg.chatId,
603272
605180
  speaker: this.state.botUsername ? `@${this.state.botUsername}` : "Assistant",
@@ -603277,16 +605185,17 @@ ${mediaContext}` : ""
603277
605185
  chatTitle: msg.chatTitle
603278
605186
  };
603279
605187
  this.recordChatHistory(sessionKey, entry);
605188
+ this.upsertTelegramReflectionHistoryEntry(sessionKey, entry);
603280
605189
  this.stimulation.recordAgentOutput(sessionKey);
603281
605190
  this.updateTelegramMemoryCards(sessionKey, entry);
603282
605191
  try {
603283
605192
  updateScopedPersonality(this.telegramPersonalityScope(sessionKey, msg), {
603284
605193
  speaker: entry.speaker || "Assistant",
603285
- text: clean3,
605194
+ text: clean5,
603286
605195
  role: "assistant",
603287
605196
  mode,
603288
605197
  ts: entry.ts,
603289
- toneTags: inferTelegramToneTags(clean3)
605198
+ toneTags: inferTelegramToneTags(clean5)
603290
605199
  });
603291
605200
  } catch {
603292
605201
  }
@@ -603354,7 +605263,7 @@ ${mediaContext}` : ""
603354
605263
  }
603355
605264
  const matchingEntry = mediaEntries.find((entry) => {
603356
605265
  if (resolve41(entry.localPath) === resolve41(raw)) return true;
603357
- if (basename26(entry.localPath) === raw) return true;
605266
+ if (basename27(entry.localPath) === raw) return true;
603358
605267
  if (entry.fileUniqueId === raw || entry.fileId === raw) return true;
603359
605268
  if (entry.messageId && String(entry.messageId) === raw) return true;
603360
605269
  return false;
@@ -603390,7 +605299,7 @@ ${mediaContext}` : ""
603390
605299
  }
603391
605300
  return entries.find((entry2) => {
603392
605301
  if (resolve41(entry2.localPath) === resolve41(ref)) return true;
603393
- if (basename26(entry2.localPath) === ref) return true;
605302
+ if (basename27(entry2.localPath) === ref) return true;
603394
605303
  if (entry2.fileUniqueId === ref || entry2.fileId === ref) return true;
603395
605304
  if (entry2.messageId && String(entry2.messageId) === ref) return true;
603396
605305
  return false;
@@ -603418,7 +605327,7 @@ ${mediaContext}` : ""
603418
605327
  caption: entry.caption
603419
605328
  },
603420
605329
  modality,
603421
- label: `Telegram message_id ${entry.messageId || "unknown"} ${basename26(entry.localPath)}`,
605330
+ label: `Telegram message_id ${entry.messageId || "unknown"} ${basename27(entry.localPath)}`,
603422
605331
  extractedContent: entry.extractedContent
603423
605332
  };
603424
605333
  }
@@ -603493,7 +605402,7 @@ ${mediaContext}` : ""
603493
605402
  const titleTags = tags.slice(0, 4);
603494
605403
  const title = titleTags.length > 0 ? `${speaker} / ${titleTags.join(" ")}` : `${speaker} / conversation`;
603495
605404
  const card = {
603496
- id: createHash21("sha1").update(`${sessionKey}:${now}:${speaker}:${text}`).digest("hex").slice(0, 12),
605405
+ id: createHash23("sha1").update(`${sessionKey}:${now}:${speaker}:${text}`).digest("hex").slice(0, 12),
603497
605406
  title,
603498
605407
  notes: [],
603499
605408
  tags: [],
@@ -603624,7 +605533,7 @@ ${cardLines.join("\n")}`);
603624
605533
  const caption = entry.caption ? ` caption:${truncateTelegramContextLine(entry.caption, 120)}` : "";
603625
605534
  const extracted = entry.extractedContent ? `
603626
605535
  ${truncateTelegramContextLine(entry.extractedContent.replace(/\s+/g, " "), 220)}` : "";
603627
- return `- message_id ${entry.messageId}${replyMark}: ${kind}; path ${entry.localPath}; file ${basename26(entry.localPath)}${caption}${extracted}`;
605536
+ return `- message_id ${entry.messageId}${replyMark}: ${kind}; path ${entry.localPath}; file ${basename27(entry.localPath)}${caption}${extracted}`;
603628
605537
  });
603629
605538
  sections.push([
603630
605539
  "### Recent Chat Media",
@@ -603965,7 +605874,7 @@ ${list}` : "No shared group target is currently known for this sender. Ask in th
603965
605874
  }
603966
605875
  telegramRunnerStateDir(sessionKey) {
603967
605876
  if (!this.repoRoot) return void 0;
603968
- const safe = createHash21("sha1").update(sessionKey).digest("hex").slice(0, 20);
605877
+ const safe = createHash23("sha1").update(sessionKey).digest("hex").slice(0, 20);
603969
605878
  return join123(this.repoRoot, ".omnius", "telegram-runner-state", safe);
603970
605879
  }
603971
605880
  buildTelegramAdminOverviewContext(currentSessionKey) {
@@ -604325,6 +606234,11 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
604325
606234
  await this.replyWithTelegramHelp(msg, isAdmin);
604326
606235
  return;
604327
606236
  }
606237
+ const telegramSlash = this.telegramSlashName(normalizedCommandText);
606238
+ if (msg.text.trim().startsWith("/") && TELEGRAM_REFLECTION_SLASH_COMMANDS.has(telegramSlash)) {
606239
+ await this.handleTelegramReflectionSlash(msg, normalizedCommandText);
606240
+ return;
606241
+ }
604328
606242
  if (!this.agentConfig || !this.repoRoot) {
604329
606243
  this.onMessage(msg);
604330
606244
  return;
@@ -604332,7 +606246,6 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
604332
606246
  const toolContext = this.resolveToolContext(msg, isAdmin);
604333
606247
  const isAdminDM = toolContext === "telegram-admin-dm";
604334
606248
  const sessionKey = this.sessionKeyForMessage(msg);
604335
- const telegramSlash = this.telegramSlashName(normalizedCommandText);
604336
606249
  if (msg.text.trim().startsWith("/") && TELEGRAM_REMINDER_SLASH_COMMANDS.has(telegramSlash)) {
604337
606250
  await this.handleTelegramReminderSlash(msg, normalizedCommandText, toolContext);
604338
606251
  return;
@@ -604897,12 +606810,12 @@ ${conversationStream}`
604897
606810
  }
604898
606811
  retainTelegramVisibleReplyDraft(subAgent, draft, streamToolNames = subAgent.currentStreamToolNames) {
604899
606812
  if (subAgent.visibleReplyText) return;
604900
- const clean3 = cleanTelegramVisibleReply(draft);
604901
- if (!clean3) return;
606813
+ const clean5 = cleanTelegramVisibleReply(draft);
606814
+ if (!clean5) return;
604902
606815
  const toolNames = [...streamToolNames];
604903
606816
  const hasNonCompletionTool = toolNames.some((name10) => name10 !== "task_complete");
604904
606817
  if (hasNonCompletionTool) return;
604905
- subAgent.visibleReplyText = clean3;
606818
+ subAgent.visibleReplyText = clean5;
604906
606819
  }
604907
606820
  retainTelegramVisibleReplyFromCompletedStream(subAgent) {
604908
606821
  this.retainTelegramVisibleReplyDraft(
@@ -605685,6 +607598,7 @@ Scoped workspace: ${scopedRoot}`,
605685
607598
  return new IdentityMemoryTool(repoRoot, {
605686
607599
  sourceSurface: "telegram",
605687
607600
  scope,
607601
+ sessionId: currentMsg ? this.sessionKeyForMessage(currentMsg) : chatId !== void 0 ? `chat:${String(chatId)}` : void 0,
605688
607602
  sender: currentMsg ? this.telegramMemorySenderFromMessage(currentMsg) : void 0,
605689
607603
  message: message2,
605690
607604
  replyTo: currentMsg ? this.telegramMemoryReplyRef(currentMsg) : void 0,
@@ -605720,7 +607634,7 @@ Scoped workspace: ${scopedRoot}`,
605720
607634
  `${index + 1}. message_id ${entry.messageId || "unknown"}`,
605721
607635
  currentMsg?.replyToMessageId === entry.messageId ? "replied-to" : "",
605722
607636
  telegramCachedMediaIsImage(entry) ? "image" : telegramCachedMediaIsPdf(entry) ? "pdf" : telegramCachedMediaIsAudio(entry) ? "audio" : telegramCachedMediaIsVideo(entry) ? "video" : entry.mediaType,
605723
- `file=${basename26(entry.localPath)}`,
607637
+ `file=${basename27(entry.localPath)}`,
605724
607638
  `path=${entry.localPath}`,
605725
607639
  entry.caption ? `caption=${truncateTelegramContextLine(entry.caption, 140)}` : ""
605726
607640
  ].filter(Boolean);
@@ -605832,8 +607746,8 @@ Scoped workspace: ${scopedRoot}`,
605832
607746
  bridge.rememberTelegramDeliveredArtifactForMessage(currentMsg, file.logicalPath ?? file.path);
605833
607747
  return {
605834
607748
  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}.`,
607749
+ output: `Sent Telegram file: ${basename27(file.path)} as ${kind} to ${String(target.chatId)}${messageId ? ` (message_id ${messageId})` : ""}`,
607750
+ llmContent: `Sent ${basename27(file.path)} to Telegram as ${kind}.`,
605837
607751
  durationMs: performance.now() - start2,
605838
607752
  mutated: false,
605839
607753
  mutatedFiles: []
@@ -606039,15 +607953,43 @@ ${visionContext}]`;
606039
607953
  } else {
606040
607954
  description = `[${sourceLabel}image received and saved to ${localPath}${caption ? ` — caption: "${caption}"` : ""}. You can use image_read, ocr, or vision tools to analyze it.]`;
606041
607955
  }
607956
+ const ingestPayload = this.telegramMemoryIngestPayload(msg, media, localPath, source, cacheEntry.extractedContent);
607957
+ let visualIdentityContext = "";
607958
+ let ingestReachedDaemon = false;
606042
607959
  try {
606043
- await fetch("http://127.0.0.1:11435/v1/memory/ingest", {
607960
+ const ingestResponse = await fetch("http://127.0.0.1:11435/v1/memory/ingest", {
606044
607961
  method: "POST",
606045
607962
  headers: { "Content-Type": "application/json" },
606046
- body: JSON.stringify(this.telegramMemoryIngestPayload(msg, media, localPath, source, cacheEntry.extractedContent)),
607963
+ body: JSON.stringify(ingestPayload),
606047
607964
  signal: AbortSignal.timeout(2e3)
606048
607965
  });
607966
+ ingestReachedDaemon = ingestResponse.ok;
607967
+ if (ingestResponse.ok) {
607968
+ const ingested = await ingestResponse.json().catch(() => null);
607969
+ const block = ingested?.visualIdentity?.contextBlock;
607970
+ if (typeof block === "string" && block.trim()) visualIdentityContext = block.trim();
607971
+ }
606049
607972
  } catch {
606050
607973
  }
607974
+ if (!ingestReachedDaemon && this.repoRoot) {
607975
+ try {
607976
+ const association = await associateVisualIdentityFromImage({
607977
+ repoRoot: this.repoRoot,
607978
+ imagePath: localPath,
607979
+ sourceSurface: "telegram",
607980
+ scope: ingestPayload["scope"],
607981
+ sender: ingestPayload["sender"],
607982
+ message: ingestPayload["message"],
607983
+ replyTo: ingestPayload["replyTo"],
607984
+ sessionId: typeof ingestPayload["sessionId"] === "string" ? ingestPayload["sessionId"] : this.sessionKeyForMessage(msg),
607985
+ media: ingestPayload["media"],
607986
+ extractedContent: cacheEntry.extractedContent
607987
+ });
607988
+ if (association.contextBlock) visualIdentityContext = association.contextBlock;
607989
+ } catch {
607990
+ }
607991
+ }
607992
+ description = appendMediaContextBlock(description, visualIdentityContext);
606051
607993
  } else if (type === "audio" || type === "voice") {
606052
607994
  let transcription = null;
606053
607995
  try {
@@ -606221,7 +608163,7 @@ ${text}`.trim());
606221
608163
  if (!existsSync108(media.value)) throw new Error(`File does not exist: ${media.value}`);
606222
608164
  const buffer2 = readFileSync88(media.value);
606223
608165
  const boundary = `----omnius-media-${Date.now()}-${Math.random().toString(36).slice(2)}`;
606224
- const filename = basename26(media.value);
608166
+ const filename = basename27(media.value);
606225
608167
  const contentType = mimeForPath(media.value, media.kind);
606226
608168
  const parts = [];
606227
608169
  const addField = (name10, value2) => {
@@ -606326,7 +608268,7 @@ Content-Type: ${contentType}\r
606326
608268
  continue;
606327
608269
  }
606328
608270
  const buffer2 = readFileSync88(pathOrFileId);
606329
- const filename = basename26(pathOrFileId);
608271
+ const filename = basename27(pathOrFileId);
606330
608272
  parts.push(Buffer.from(`--${boundary}\r
606331
608273
  `));
606332
608274
  parts.push(Buffer.from(
@@ -606356,11 +608298,11 @@ Content-Type: ${mimeForPath(pathOrFileId, field === "photo" ? "image" : "video")
606356
608298
  * Telegram accepts @botusername in chat_id for the supported bot-to-bot subset.
606357
608299
  */
606358
608300
  async sendMessageToBot(username, text) {
606359
- const clean3 = username.trim().replace(/^@/, "");
606360
- if (!/^[A-Za-z0-9_]{5,32}$/.test(clean3)) {
608301
+ const clean5 = username.trim().replace(/^@/, "");
608302
+ if (!/^[A-Za-z0-9_]{5,32}$/.test(clean5)) {
606361
608303
  throw new Error("Expected a Telegram bot username (5-32 chars, letters/numbers/underscore).");
606362
608304
  }
606363
- return this.sendMessage(`@${clean3}`, text);
608305
+ return this.sendMessage(`@${clean5}`, text);
606364
608306
  }
606365
608307
  async setMyCommands(commands) {
606366
608308
  const apiCommands = commands.map((cmd) => ({
@@ -607390,7 +609332,7 @@ __export(projects_exports, {
607390
609332
  });
607391
609333
  import { readFileSync as readFileSync90, writeFileSync as writeFileSync59, mkdirSync as mkdirSync65, existsSync as existsSync110, statSync as statSync37, renameSync as renameSync6 } from "node:fs";
607392
609334
  import { homedir as homedir37 } from "node:os";
607393
- import { basename as basename27, join as join125, resolve as resolve42 } from "node:path";
609335
+ import { basename as basename28, join as join125, resolve as resolve42 } from "node:path";
607394
609336
  import { randomUUID as randomUUID14 } from "node:crypto";
607395
609337
  function readAll2() {
607396
609338
  try {
@@ -607438,7 +609380,7 @@ function registerProject(root, pid) {
607438
609380
  } else {
607439
609381
  entry = {
607440
609382
  root: canonical,
607441
- name: basename27(canonical) || canonical,
609383
+ name: basename28(canonical) || canonical,
607442
609384
  firstSeen: now,
607443
609385
  lastSeen: now,
607444
609386
  pid: pid ?? null,
@@ -608164,24 +610106,24 @@ function stripMappedPrefix(ip) {
608164
610106
  }
608165
610107
  function isLoopbackIP(ip) {
608166
610108
  if (!ip) return false;
608167
- const clean3 = stripMappedPrefix(ip);
608168
- if (clean3 === "::1") return true;
608169
- if (/^127\./.test(clean3)) return true;
610109
+ const clean5 = stripMappedPrefix(ip);
610110
+ if (clean5 === "::1") return true;
610111
+ if (/^127\./.test(clean5)) return true;
608170
610112
  return false;
608171
610113
  }
608172
610114
  function isPrivateIP(ip) {
608173
610115
  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);
610116
+ const clean5 = stripMappedPrefix(ip);
610117
+ if (/^10\./.test(clean5)) return true;
610118
+ if (/^192\.168\./.test(clean5)) return true;
610119
+ const m2 = /^172\.(\d{1,3})\./.exec(clean5);
608178
610120
  if (m2) {
608179
610121
  const second3 = parseInt(m2[1], 10);
608180
610122
  if (second3 >= 16 && second3 <= 31) return true;
608181
610123
  }
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;
610124
+ if (/^169\.254\./.test(clean5)) return true;
610125
+ if (/^f[cd][0-9a-f]{2}:/i.test(clean5)) return true;
610126
+ if (/^fe[89ab][0-9a-f]:/i.test(clean5)) return true;
608185
610127
  return false;
608186
610128
  }
608187
610129
  function isAllowedIP(ip, mode) {
@@ -608207,14 +610149,14 @@ var init_access_policy = __esm({
608207
610149
  });
608208
610150
 
608209
610151
  // packages/cli/src/api/project-preferences.ts
608210
- import { createHash as createHash22 } from "node:crypto";
610152
+ import { createHash as createHash24 } from "node:crypto";
608211
610153
  import { existsSync as existsSync111, mkdirSync as mkdirSync66, readFileSync as readFileSync91, renameSync as renameSync7, writeFileSync as writeFileSync60, unlinkSync as unlinkSync23 } from "node:fs";
608212
610154
  import { homedir as homedir38 } from "node:os";
608213
610155
  import { join as join126, resolve as resolve43 } from "node:path";
608214
610156
  import { randomUUID as randomUUID15 } from "node:crypto";
608215
610157
  function projectKey(root) {
608216
610158
  const canonical = resolve43(root);
608217
- return createHash22("sha256").update(canonical).digest("hex").slice(0, 16);
610159
+ return createHash24("sha256").update(canonical).digest("hex").slice(0, 16);
608218
610160
  }
608219
610161
  function projectDir(root) {
608220
610162
  return join126(PROJECTS_DIR, projectKey(root));
@@ -608306,7 +610248,7 @@ __export(voicechat_exports, {
608306
610248
  VoiceChatSession: () => VoiceChatSession
608307
610249
  });
608308
610250
  import { EventEmitter as EventEmitter11 } from "node:events";
608309
- function clamp018(x) {
610251
+ function clamp019(x) {
608310
610252
  return x < 0 ? 0 : x > 1 ? 1 : x;
608311
610253
  }
608312
610254
  function alnumRatio(s2) {
@@ -608345,9 +610287,9 @@ function computeSignalFromText(text, confidence2) {
608345
610287
  else score = 0.15;
608346
610288
  score -= repeatingCharPenalty(t2) * 0.4;
608347
610289
  if (typeof confidence2 === "number" && !Number.isNaN(confidence2)) {
608348
- score = 0.7 * score + 0.3 * clamp018(confidence2);
610290
+ score = 0.7 * score + 0.3 * clamp019(confidence2);
608349
610291
  }
608350
- return clamp018(score);
610292
+ return clamp019(score);
608351
610293
  }
608352
610294
  function truncateForLog(s2, n2) {
608353
610295
  return s2.length <= n2 ? s2 : s2.slice(0, n2 - 1) + "…";
@@ -608617,7 +610559,7 @@ Rules:
608617
610559
  }, MAX_SEGMENT_MS);
608618
610560
  }
608619
610561
  this.captureBuffer = text;
608620
- this.lastSignalScore = typeof snr === "number" && !Number.isNaN(snr) ? clamp018(snr) : computeSignalFromText(text, confidence2);
610562
+ this.lastSignalScore = typeof snr === "number" && !Number.isNaN(snr) ? clamp019(snr) : computeSignalFromText(text, confidence2);
608621
610563
  this.emit("snr", { score: this.lastSignalScore });
608622
610564
  this.onPartialTranscript(text);
608623
610565
  if (this.silenceTimer) clearTimeout(this.silenceTimer);
@@ -609392,7 +611334,7 @@ var init_disk_task_output = __esm({
609392
611334
  });
609393
611335
 
609394
611336
  // packages/cli/src/api/http.ts
609395
- import { createHash as createHash23 } from "node:crypto";
611337
+ import { createHash as createHash25 } from "node:crypto";
609396
611338
  function problemDetails(opts) {
609397
611339
  const p2 = {
609398
611340
  type: opts.type ?? "about:blank",
@@ -609455,7 +611397,7 @@ function paginated(items, page2, total) {
609455
611397
  }
609456
611398
  function computeEtag(payload) {
609457
611399
  const json = typeof payload === "string" ? payload : JSON.stringify(payload);
609458
- const hash = createHash23("sha1").update(json).digest("hex").slice(0, 16);
611400
+ const hash = createHash25("sha1").update(json).digest("hex").slice(0, 16);
609459
611401
  return `W/"${hash}"`;
609460
611402
  }
609461
611403
  function checkNotModified(req2, res, etag) {
@@ -616604,13 +618546,20 @@ async function sendMessage() {
616604
618546
  showStreamingIndicator(msgDiv, 'thinking');
616605
618547
 
616606
618548
  try {
618549
+ const attachmentContextBlock = _attachments.length > 0
618550
+ ? await uploadChatAttachments(chatSessionId)
618551
+ : '';
618552
+
616607
618553
  // Prepend context files as a FILES block so the agent can read them
616608
618554
  // without having to guess paths. The user picked these via right-click
616609
618555
  // "Add to context" on the workspace tree.
616610
618556
  let messageWithContext = sendText;
618557
+ if (attachmentContextBlock) {
618558
+ messageWithContext = attachmentContextBlock + '\\n\\n' + messageWithContext;
618559
+ }
616611
618560
  if (contextFiles.length > 0) {
616612
618561
  const filesBlock = 'FILES IN CONTEXT:\\n' + contextFiles.map(p => ' - ' + p).join('\\n') + '\\n\\n';
616613
- messageWithContext = filesBlock + sendText;
618562
+ messageWithContext = filesBlock + messageWithContext;
616614
618563
  }
616615
618564
 
616616
618565
  // FRESH-SESSION: pull the one-shot fresh flag the newChatSession()
@@ -621863,6 +623812,51 @@ function _removeAttachment(i) {
621863
623812
  _renderAttachChips();
621864
623813
  }
621865
623814
 
623815
+ function _fileToBase64(file) {
623816
+ return new Promise((resolve, reject) => {
623817
+ const reader = new FileReader();
623818
+ reader.onload = () => {
623819
+ const raw = String(reader.result || '');
623820
+ const comma = raw.indexOf(',');
623821
+ resolve(comma >= 0 ? raw.slice(comma + 1) : raw);
623822
+ };
623823
+ reader.onerror = () => reject(reader.error || new Error('file_read_failed'));
623824
+ reader.readAsDataURL(file);
623825
+ });
623826
+ }
623827
+
623828
+ async function uploadChatAttachments(sessionId) {
623829
+ if (!_attachments.length) return '';
623830
+ const blocks = [];
623831
+ for (const attachment of _attachments) {
623832
+ try {
623833
+ const base64 = await _fileToBase64(attachment.file);
623834
+ const response = await fetch('/v1/chat/attachments', {
623835
+ method: 'POST',
623836
+ headers: headers(),
623837
+ body: JSON.stringify({
623838
+ session_id: sessionId || '',
623839
+ filename: attachment.name,
623840
+ mimeType: attachment.file && attachment.file.type ? attachment.file.type : '',
623841
+ base64,
623842
+ }),
623843
+ });
623844
+ if (!response.ok) {
623845
+ blocks.push('GUI attachment failed: ' + attachment.name);
623846
+ continue;
623847
+ }
623848
+ const data = await response.json();
623849
+ blocks.push(data.contextBlock || ('GUI attachment saved: ' + (data.path || attachment.name)));
623850
+ } catch (err) {
623851
+ blocks.push('GUI attachment failed: ' + attachment.name + ' (' + ((err && err.message) ? err.message : String(err)) + ')');
623852
+ }
623853
+ }
623854
+ _attachments = [];
623855
+ _renderAttachChips();
623856
+ if (!blocks.length) return '';
623857
+ return 'GUI ATTACHMENTS:\\n' + blocks.map((block) => String(block).split('\\n').map((line) => ' ' + line).join('\\n')).join('\\n\\n');
623858
+ }
623859
+
621866
623860
  // Drag & drop on the input row
621867
623861
  (function _wireDropZone() {
621868
623862
  const row = document.getElementById('input-row');
@@ -623458,7 +625452,7 @@ __export(embedding_workers_exports, {
623458
625452
  startEmbeddingWorkers: () => startEmbeddingWorkers,
623459
625453
  stopEmbeddingWorkers: () => stopEmbeddingWorkers
623460
625454
  });
623461
- import { basename as basename28, join as join136 } from "node:path";
625455
+ import { basename as basename29, join as join136 } from "node:path";
623462
625456
  function startEmbeddingWorkers(opts) {
623463
625457
  if (_running) return;
623464
625458
  _running = true;
@@ -623524,7 +625518,7 @@ async function runEmbeddingTask(modality, episodeId, taskId, opts) {
623524
625518
  try {
623525
625519
  if (!_aligner) {
623526
625520
  const stateRoot = process.env.OMNIUS_DIR || process.cwd();
623527
- const omniusDir = basename28(stateRoot) === ".omnius" ? stateRoot : join136(stateRoot, ".omnius");
625521
+ const omniusDir = basename29(stateRoot) === ".omnius" ? stateRoot : join136(stateRoot, ".omnius");
623528
625522
  const memDir = join136(omniusDir, "memory");
623529
625523
  _aligner = new EmbeddingAligner(
623530
625524
  `${modality}-${emb.length}`,
@@ -623645,7 +625639,7 @@ import { homedir as homedir45 } from "node:os";
623645
625639
  import { spawn as spawn29, execSync as execSync57 } from "node:child_process";
623646
625640
  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
625641
  import { randomBytes as randomBytes24, randomUUID as randomUUID16 } from "node:crypto";
623648
- import { createHash as createHash25 } from "node:crypto";
625642
+ import { createHash as createHash27 } from "node:crypto";
623649
625643
  function memoryDbPaths3(baseDir = process.cwd()) {
623650
625644
  const dir = join137(baseDir, ".omnius");
623651
625645
  return {
@@ -629518,7 +631512,7 @@ function listScheduledTasks() {
629518
631512
  const schedule = String(t2?.schedule || t2?.cron || t2?.when || "");
629519
631513
  const enabled2 = typeof t2?.enabled === "boolean" ? t2.enabled : true;
629520
631514
  const realId = typeof t2?.id === "string" && t2.id ? t2.id : null;
629521
- const fallbackId = createHash25("sha1").update(`${file}#${i2}`).digest("hex").slice(0, 16);
631515
+ const fallbackId = createHash27("sha1").update(`${file}#${i2}`).digest("hex").slice(0, 16);
629522
631516
  const uid = realId || fallbackId;
629523
631517
  const key = `${uid}`;
629524
631518
  if (seen.has(key)) return;
@@ -629635,8 +631629,8 @@ function deleteScheduledById(id) {
629635
631629
  if (id) candidates.push(id);
629636
631630
  if (typeof entry?.id === "string" && entry.id && !candidates.includes(entry.id)) candidates.push(entry.id);
629637
631631
  try {
629638
- const { createHash: createHash26 } = require4("node:crypto");
629639
- const fallback = createHash26("sha1").update(`${target.file}#${target.index}`).digest("hex").slice(0, 16);
631632
+ const { createHash: createHash28 } = require4("node:crypto");
631633
+ const fallback = createHash28("sha1").update(`${target.file}#${target.index}`).digest("hex").slice(0, 16);
629640
631634
  if (!candidates.includes(fallback)) candidates.push(fallback);
629641
631635
  } catch {
629642
631636
  }
@@ -630391,6 +632385,10 @@ function startApiServer(options2 = {}) {
630391
632385
  handleMemoryIngest(req2, res, ollamaUrl);
630392
632386
  return;
630393
632387
  }
632388
+ if (req2.method === "POST" && url.pathname === "/v1/chat/attachments") {
632389
+ handleChatAttachmentUpload(req2, res);
632390
+ return;
632391
+ }
630394
632392
  if (req2.method === "GET" && url.pathname === "/v1/memory/entities") {
630395
632393
  handleEntitiesList(req2, res);
630396
632394
  return;
@@ -630948,6 +632946,126 @@ async function handleAudioEmbed(req2, res) {
630948
632946
  jsonResponse(res, 400, { error: "bad_request", message: err instanceof Error ? err.message : String(err) });
630949
632947
  }
630950
632948
  }
632949
+ async function handleChatAttachmentUpload(req2, res) {
632950
+ try {
632951
+ const body = await parseJsonBody(req2);
632952
+ if (!body || typeof body !== "object") {
632953
+ jsonResponse(res, 400, { error: "bad_request" });
632954
+ return;
632955
+ }
632956
+ const b = body;
632957
+ const filename = typeof b.filename === "string" && b.filename.trim() ? b.filename.trim() : `attachment-${Date.now()}.bin`;
632958
+ const base642 = typeof b.base64 === "string" ? b.base64 : "";
632959
+ if (!base642) {
632960
+ jsonResponse(res, 400, { error: "missing_base64" });
632961
+ return;
632962
+ }
632963
+ const safeName2 = filename.replace(/[^a-zA-Z0-9._-]/g, "-").slice(0, 180) || `attachment-${Date.now()}.bin`;
632964
+ const dir = join137(process.cwd(), ".omnius", "gui-attachments");
632965
+ mkdirSync74(dir, { recursive: true });
632966
+ const localPath = join137(dir, `${Date.now()}-${randomUUID16().slice(0, 8)}-${safeName2}`);
632967
+ writeFileSync66(localPath, Buffer.from(base642, "base64"));
632968
+ const mimeType = typeof b.mimeType === "string" ? b.mimeType : typeof b.mime_type === "string" ? b.mime_type : "";
632969
+ const isImage = mimeType.toLowerCase().startsWith("image/") || /\.(png|jpe?g|gif|webp|bmp|tiff?)$/i.test(safeName2);
632970
+ const sessionId = typeof b.sessionId === "string" ? b.sessionId : typeof b.session_id === "string" ? b.session_id : void 0;
632971
+ const scope = typeof b.scope === "object" && b.scope ? b.scope : { kind: "gui", id: sessionId || "web", title: "GUI session" };
632972
+ const sender = typeof b.sender === "object" && b.sender ? b.sender : { id: "gui-user", displayName: "GUI user" };
632973
+ const message2 = {
632974
+ id: typeof b.message_id === "string" || typeof b.message_id === "number" ? b.message_id : `attachment:${randomUUID16()}`,
632975
+ timestamp: Date.now(),
632976
+ text: typeof b.caption === "string" && b.caption.trim() ? b.caption.trim() : `GUI attachment: ${safeName2}`
632977
+ };
632978
+ const media = {
632979
+ path: localPath,
632980
+ mediaType: isImage ? "photo" : "document",
632981
+ mimeType: mimeType || void 0,
632982
+ caption: typeof b.caption === "string" ? b.caption : void 0
632983
+ };
632984
+ const modality = isImage ? "visual" : "text";
632985
+ const { EpisodeStore: EpisodeStore3, TemporalGraph: TemporalGraph3, MultimodalIdentityService: MultimodalIdentityService2 } = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
632986
+ const dbPaths = memoryDbPaths3(process.cwd());
632987
+ const graph = new TemporalGraph3(dbPaths.knowledge);
632988
+ const store2 = new EpisodeStore3(dbPaths.episodes, graph);
632989
+ let ingestId = "";
632990
+ try {
632991
+ const embeddings = {};
632992
+ if (isImage) {
632993
+ try {
632994
+ const provider = globalThis.__omniusVisionProvider;
632995
+ const emb = provider ? await provider({ path: localPath }) : null;
632996
+ if (emb) embeddings.imageClip = emb;
632997
+ } catch {
632998
+ }
632999
+ }
633000
+ const service = new MultimodalIdentityService2({ episodeStore: store2, graph });
633001
+ ingestId = service.ingest({
633002
+ sourceSurface: "gui",
633003
+ sessionId,
633004
+ scope,
633005
+ sender,
633006
+ message: message2,
633007
+ modality,
633008
+ content: `GUI attachment: ${safeName2}`,
633009
+ media,
633010
+ labels: [safeName2, typeof b.caption === "string" ? b.caption : ""].filter(Boolean),
633011
+ embeddings
633012
+ }).episodeId;
633013
+ } finally {
633014
+ store2.close();
633015
+ graph.close();
633016
+ }
633017
+ let visualIdentity = void 0;
633018
+ let contextBlock = [
633019
+ `GUI attachment saved: ${safeName2}`,
633020
+ `path: ${localPath}`,
633021
+ mimeType ? `mime_type: ${mimeType}` : ""
633022
+ ].filter(Boolean).join("\n");
633023
+ if (isImage) {
633024
+ try {
633025
+ const { associateVisualIdentityFromImage: associateVisualIdentityFromImage2 } = await Promise.resolve().then(() => (init_visual_identity_association(), visual_identity_association_exports));
633026
+ const association = await associateVisualIdentityFromImage2({
633027
+ repoRoot: process.cwd(),
633028
+ imagePath: localPath,
633029
+ sourceSurface: "gui",
633030
+ scope,
633031
+ sender,
633032
+ message: message2,
633033
+ sessionId,
633034
+ media
633035
+ });
633036
+ visualIdentity = {
633037
+ matches: association.matches,
633038
+ recalledEpisodes: association.recalledEpisodes,
633039
+ committedEpisodeIds: association.committedEpisodeIds,
633040
+ contextBlock: association.contextBlock,
633041
+ degradedReason: association.degradedReason
633042
+ };
633043
+ if (association.contextBlock) contextBlock += `
633044
+
633045
+ ${association.contextBlock}`;
633046
+ } catch (err) {
633047
+ visualIdentity = {
633048
+ matches: [],
633049
+ recalledEpisodes: [],
633050
+ committedEpisodeIds: [],
633051
+ contextBlock: "",
633052
+ degradedReason: err instanceof Error ? err.message : String(err)
633053
+ };
633054
+ }
633055
+ }
633056
+ jsonResponse(res, 200, {
633057
+ id: ingestId,
633058
+ path: localPath,
633059
+ filename: safeName2,
633060
+ mimeType,
633061
+ modality,
633062
+ contextBlock,
633063
+ visualIdentity
633064
+ });
633065
+ } catch (err) {
633066
+ jsonResponse(res, 400, { error: "bad_request", message: err instanceof Error ? err.message : String(err) });
633067
+ }
633068
+ }
630951
633069
  async function handleMemoryIngest(req2, res, ollamaUrl) {
630952
633070
  try {
630953
633071
  const body = await parseJsonBody(req2);
@@ -630971,6 +633089,7 @@ async function handleMemoryIngest(req2, res, ollamaUrl) {
630971
633089
  };
630972
633090
  const media = Object.values(rawMedia).some((value2) => value2 != null && String(value2).trim() !== "") ? rawMedia : void 0;
630973
633091
  const sourceSurface = String(b.sourceSurface || b.source_surface || "api");
633092
+ const sessionId = typeof b.sessionId === "string" ? b.sessionId : typeof b.session_id === "string" ? b.session_id : void 0;
630974
633093
  const scope = typeof b.scope === "object" && b.scope ? b.scope : {
630975
633094
  kind: b.scope_kind || (b.chat_type === "private" ? "private" : b.chat_id ? "group" : "global"),
630976
633095
  id: String(b.scope_id || b.chat_id || sourceSurface),
@@ -631026,6 +633145,7 @@ async function handleMemoryIngest(req2, res, ollamaUrl) {
631026
633145
  const service = new MultimodalIdentityService2({ episodeStore: epStore, graph: kg });
631027
633146
  const result = service.ingest({
631028
633147
  sourceSurface,
633148
+ sessionId,
631029
633149
  scope,
631030
633150
  sender,
631031
633151
  message: message2,
@@ -631039,7 +633159,42 @@ async function handleMemoryIngest(req2, res, ollamaUrl) {
631039
633159
  identityAssertions,
631040
633160
  embeddings
631041
633161
  });
631042
- jsonResponse(res, 200, { id: result.episodeId, ...result });
633162
+ let visualIdentity = void 0;
633163
+ if (modality === "visual" && mediaPath) {
633164
+ try {
633165
+ const { associateVisualIdentityFromImage: associateVisualIdentityFromImage2 } = await Promise.resolve().then(() => (init_visual_identity_association(), visual_identity_association_exports));
633166
+ const association = await associateVisualIdentityFromImage2({
633167
+ repoRoot: process.cwd(),
633168
+ imagePath: mediaPath,
633169
+ sourceSurface,
633170
+ scope,
633171
+ sender,
633172
+ message: message2,
633173
+ replyTo,
633174
+ sessionId,
633175
+ media,
633176
+ extractedContent: b.extracted_content || b.extractedContent,
633177
+ threshold: typeof b.threshold === "number" ? b.threshold : 0.5,
633178
+ limit: typeof b.limit === "number" ? b.limit : 5
633179
+ });
633180
+ visualIdentity = {
633181
+ matches: association.matches,
633182
+ recalledEpisodes: association.recalledEpisodes,
633183
+ committedEpisodeIds: association.committedEpisodeIds,
633184
+ contextBlock: association.contextBlock,
633185
+ degradedReason: association.degradedReason
633186
+ };
633187
+ } catch (err) {
633188
+ visualIdentity = {
633189
+ matches: [],
633190
+ recalledEpisodes: [],
633191
+ committedEpisodeIds: [],
633192
+ contextBlock: "",
633193
+ degradedReason: err instanceof Error ? err.message : String(err)
633194
+ };
633195
+ }
633196
+ }
633197
+ jsonResponse(res, 200, { id: result.episodeId, ...result, visualIdentity });
631043
633198
  } catch (err) {
631044
633199
  jsonResponse(res, 400, { error: "bad_request", message: err instanceof Error ? err.message : String(err) });
631045
633200
  }
@@ -631690,6 +633845,7 @@ function buildSubAgentTools(repoRoot, config) {
631690
633845
  id: process.env["OMNIUS_SESSION_ID"] || "terminal",
631691
633846
  title: "TUI session"
631692
633847
  },
633848
+ sessionId: process.env["OMNIUS_SESSION_ID"] || "terminal",
631693
633849
  sender: { id: "local-user", displayName: "User" }
631694
633850
  }),
631695
633851
  new VideoUnderstandTool(repoRoot),
@@ -631842,6 +633998,7 @@ function buildTools(repoRoot, config, contextWindowSize, modelTier) {
631842
633998
  id: process.env["OMNIUS_SESSION_ID"] || "terminal",
631843
633999
  title: "TUI session"
631844
634000
  },
634001
+ sessionId: process.env["OMNIUS_SESSION_ID"] || "terminal",
631845
634002
  sender: { id: "local-user", displayName: "User" }
631846
634003
  }),
631847
634004
  new AsrListenTool(),
@@ -636637,8 +638794,28 @@ Log: ${nexusLogPath}`)
636637
638794
  visionContext = formatImageContextPrefix2(ingressResult);
636638
638795
  } catch {
636639
638796
  }
638797
+ let visualIdentityContext = "";
638798
+ try {
638799
+ const association = await associateVisualIdentityFromImage({
638800
+ repoRoot,
638801
+ imagePath: pasted.path,
638802
+ sourceSurface: "tui",
638803
+ scope: {
638804
+ kind: "terminal",
638805
+ id: process.env["OMNIUS_SESSION_ID"] || "terminal",
638806
+ title: "TUI session"
638807
+ },
638808
+ sender: { id: "local-user", displayName: "User" },
638809
+ message: { timestamp: Date.now(), text: "User pasted a clipboard image." },
638810
+ sessionId: process.env["OMNIUS_SESSION_ID"] || "terminal",
638811
+ media: { path: pasted.path, mediaType: "photo", mimeType: pasted.mime },
638812
+ extractedContent: visionContext
638813
+ });
638814
+ visualIdentityContext = association.contextBlock;
638815
+ } catch {
638816
+ }
636640
638817
  const asciiContext = await asciiContextPromise;
636641
- const imageContext = [visionContext, asciiContext].filter(Boolean).join("\n\n");
638818
+ const imageContext = [visionContext, visualIdentityContext, asciiContext].filter(Boolean).join("\n\n");
636642
638819
  if (activeTask) {
636643
638820
  activeTask.runner.injectImage(
636644
638821
  pasted.buffer.toString("base64"),
@@ -637094,9 +639271,9 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
637094
639271
  return `Skill invoked: ${result.name}`;
637095
639272
  }
637096
639273
  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;
639274
+ 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();
639275
+ if (!clean5) return null;
639276
+ return clean5.length > 3900 ? clean5.slice(0, 3900) + "\n..." : clean5;
637100
639277
  } catch (err) {
637101
639278
  process.stdout.write = origWrite;
637102
639279
  throw err;
@@ -638806,12 +640983,32 @@ Execute this skill now. Follow the behavioral guidance above.`;
638806
640983
  "Shared image",
638807
640984
  writeContent
638808
640985
  );
640986
+ let visualIdentityContext = "";
640987
+ try {
640988
+ const association = await associateVisualIdentityFromImage({
640989
+ repoRoot,
640990
+ imagePath: imgPath,
640991
+ sourceSurface: "tui",
640992
+ scope: {
640993
+ kind: "terminal",
640994
+ id: process.env["OMNIUS_SESSION_ID"] || "terminal",
640995
+ title: "TUI session"
640996
+ },
640997
+ sender: { id: "local-user", displayName: "User" },
640998
+ message: { timestamp: Date.now(), text: `User shared image: ${cleanPath}` },
640999
+ sessionId: process.env["OMNIUS_SESSION_ID"] || "terminal",
641000
+ media: { path: imgPath, mediaType: "photo", mimeType: mime }
641001
+ });
641002
+ visualIdentityContext = association.contextBlock;
641003
+ } catch {
641004
+ }
641005
+ const combinedImageContext = [asciiContext, visualIdentityContext].filter(Boolean).join("\n\n");
638809
641006
  activeTask.runner.injectImage(
638810
641007
  base642,
638811
641008
  mime,
638812
- asciiContext ? `User shared image: ${cleanPath}
641009
+ combinedImageContext ? `User shared image: ${cleanPath}
638813
641010
 
638814
- ${asciiContext}` : `User shared image: ${cleanPath}`
641011
+ ${combinedImageContext}` : `User shared image: ${cleanPath}`
638815
641012
  );
638816
641013
  writeContent(() => renderUserInterrupt(`[Image: ${cleanPath}]`));
638817
641014
  } catch {
@@ -638960,15 +641157,38 @@ ${result.text}`;
638960
641157
  }
638961
641158
  let fullInput = input;
638962
641159
  if (isImage && fullInput === input) {
641160
+ const imgPath = resolve46(repoRoot, cleanPath);
638963
641161
  const asciiContext = await renderAsciiPreviewForImage(
638964
- resolve46(repoRoot, cleanPath),
641162
+ imgPath,
638965
641163
  cleanPath,
638966
641164
  "Image context",
638967
641165
  writeContent
638968
641166
  );
638969
- fullInput = asciiContext ? `The user has provided an image file: ${cleanPath}.
641167
+ let visualIdentityContext = "";
641168
+ try {
641169
+ const ext = extname17(cleanPath).toLowerCase();
641170
+ const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
641171
+ const association = await associateVisualIdentityFromImage({
641172
+ repoRoot,
641173
+ imagePath: imgPath,
641174
+ sourceSurface: "tui",
641175
+ scope: {
641176
+ kind: "terminal",
641177
+ id: process.env["OMNIUS_SESSION_ID"] || "terminal",
641178
+ title: "TUI session"
641179
+ },
641180
+ sender: { id: "local-user", displayName: "User" },
641181
+ message: { timestamp: Date.now(), text: `User provided image file: ${cleanPath}` },
641182
+ sessionId: process.env["OMNIUS_SESSION_ID"] || "terminal",
641183
+ media: { path: imgPath, mediaType: "photo", mimeType: mime }
641184
+ });
641185
+ visualIdentityContext = association.contextBlock;
641186
+ } catch {
641187
+ }
641188
+ const imageContext = [asciiContext, visualIdentityContext].filter(Boolean).join("\n\n");
641189
+ fullInput = imageContext ? `The user has provided an image file: ${cleanPath}.
638970
641190
 
638971
- ${asciiContext}
641191
+ ${imageContext}
638972
641192
 
638973
641193
  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
641194
  }
@@ -639168,13 +641388,13 @@ NEW TASK: ${fullInput}`;
639168
641388
  writeContent(() => renderError(errMsg));
639169
641389
  if (failureStore) {
639170
641390
  try {
639171
- const { createHash: createHash26 } = await import("node:crypto");
641391
+ const { createHash: createHash28 } = await import("node:crypto");
639172
641392
  failureStore.insert({
639173
641393
  taskId: "",
639174
641394
  sessionId: `${Date.now()}`,
639175
641395
  repoRoot,
639176
641396
  failureType: "runtime-error",
639177
- fingerprint: createHash26("sha256").update(errMsg.slice(0, 200)).digest("hex").slice(0, 16),
641397
+ fingerprint: createHash28("sha256").update(errMsg.slice(0, 200)).digest("hex").slice(0, 16),
639178
641398
  filePath: null,
639179
641399
  errorMessage: errMsg.slice(0, 500),
639180
641400
  context: null,
@@ -639922,6 +642142,7 @@ var init_interactive = __esm({
639922
642142
  init_model_picker();
639923
642143
  init_project_context();
639924
642144
  init_identity_memory_tool();
642145
+ init_visual_identity_association();
639925
642146
  init_dist7();
639926
642147
  init_omnius_directory();
639927
642148
  init_render();