omnius 1.0.38 → 1.0.40

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
@@ -16256,6 +16256,180 @@ function loadSkillContent(skillPath) {
16256
16256
  return null;
16257
16257
  }
16258
16258
  }
16259
+ function tokenizeSkillQuery(value2) {
16260
+ const out = [];
16261
+ const seen = /* @__PURE__ */ new Set();
16262
+ for (const raw of value2.toLowerCase().match(/[a-z0-9_.-]{3,}/g) ?? []) {
16263
+ const token = raw.replace(/^[_.-]+|[_.-]+$/g, "");
16264
+ if (token.length < 3 || SKILL_STOPWORDS.has(token) || seen.has(token))
16265
+ continue;
16266
+ seen.add(token);
16267
+ out.push(token);
16268
+ if (out.length >= 24)
16269
+ break;
16270
+ }
16271
+ return out;
16272
+ }
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;
16276
+ }
16277
+ function skillHaystack(skill) {
16278
+ return [
16279
+ skill.name,
16280
+ skill.name.replace(/[-_]/g, " "),
16281
+ skill.description,
16282
+ skill.source,
16283
+ ...skill.triggers
16284
+ ].join(" ").toLowerCase();
16285
+ }
16286
+ function scoreSkill(skill, terms) {
16287
+ if (terms.length === 0)
16288
+ return null;
16289
+ const hay = skillHaystack(skill);
16290
+ const matched = [];
16291
+ let score = 0;
16292
+ for (const term of terms) {
16293
+ if (!hay.includes(term))
16294
+ continue;
16295
+ matched.push(term);
16296
+ if (skill.name.toLowerCase().includes(term))
16297
+ score += 5;
16298
+ if (skill.triggers.some((trigger) => trigger.toLowerCase().includes(term)))
16299
+ score += 3;
16300
+ if (skill.description.toLowerCase().includes(term))
16301
+ score += 2;
16302
+ if (skill.source.toLowerCase().includes(term))
16303
+ score += 1;
16304
+ }
16305
+ if (score <= 0)
16306
+ return null;
16307
+ return {
16308
+ skill,
16309
+ score,
16310
+ matchedTerms: matched.slice(0, 8),
16311
+ reason: matched.length ? `matched: ${matched.slice(0, 6).join(", ")}` : "selected by task overlap"
16312
+ };
16313
+ }
16314
+ function selectSkillsForTask(skills, task, limit = 4) {
16315
+ const terms = tokenizeSkillQuery(task);
16316
+ return skills.map((skill) => scoreSkill(skill, terms)).filter((item) => Boolean(item)).sort((a2, b) => b.score - a2.score || a2.skill.name.localeCompare(b.skill.name)).slice(0, Math.max(0, limit));
16317
+ }
16318
+ function buildEphemeralSkillPack(skills, opts) {
16319
+ const tier = opts.modelTier ?? "large";
16320
+ const defaultLimit = tier === "small" ? 3 : tier === "medium" ? 4 : 5;
16321
+ const selected = selectSkillsForTask(skills, opts.task, opts.limit ?? defaultLimit);
16322
+ if (selected.length === 0)
16323
+ return "";
16324
+ const lines = [
16325
+ "<ephemeral-skill-pack>",
16326
+ "Scope: current task run only. Discard after this run; do not write this segment to memory or handoff artifacts.",
16327
+ "Purpose: expose only a tiny manifest of likely-relevant skills. Do not load full SKILL.md content into the main context unless necessary.",
16328
+ "Small-context protocol: if a skill applies, call skill_extract with the current task/query. Prefer use_subagent=true so a delegated worker reads the full skill and returns only targeted guidance.",
16329
+ "Authority: workflow guidance only; user/system/developer instructions and tool policy remain higher priority.",
16330
+ "Selected skills:"
16331
+ ];
16332
+ for (const item of selected) {
16333
+ const skill = item.skill;
16334
+ const trigger = skill.triggers[0] ? ` trigger="${compactSkillLine(skill.triggers[0], 80)}"` : "";
16335
+ lines.push(`- ${skill.name} [${skill.source}] score=${item.score}${trigger}: ${compactSkillLine(skill.description || "(no description)", 140)} (${item.reason})`);
16336
+ }
16337
+ lines.push("</ephemeral-skill-pack>");
16338
+ return lines.join("\n");
16339
+ }
16340
+ function splitSkillSections(content) {
16341
+ const lines = content.split("\n");
16342
+ const sections = [];
16343
+ let currentTitle = "Frontmatter and Overview";
16344
+ let current = [];
16345
+ for (const line of lines) {
16346
+ const heading = line.match(/^(#{1,4})\s+(.+?)\s*$/);
16347
+ if (heading) {
16348
+ if (current.join("\n").trim()) {
16349
+ const body = current.join("\n").trim();
16350
+ sections.push({ title: currentTitle, body, text: `${currentTitle}
16351
+ ${body}` });
16352
+ }
16353
+ currentTitle = heading[2].trim();
16354
+ current = [];
16355
+ continue;
16356
+ }
16357
+ current.push(line);
16358
+ }
16359
+ if (current.join("\n").trim()) {
16360
+ const body = current.join("\n").trim();
16361
+ sections.push({ title: currentTitle, body, text: `${currentTitle}
16362
+ ${body}` });
16363
+ }
16364
+ return sections;
16365
+ }
16366
+ function sectionScore(section, terms) {
16367
+ const hay = section.text.toLowerCase();
16368
+ let score = 0;
16369
+ for (const term of terms) {
16370
+ if (!hay.includes(term))
16371
+ continue;
16372
+ score += section.title.toLowerCase().includes(term) ? 4 : 1;
16373
+ }
16374
+ if (/\b(workflow|procedure|steps|how to use|usage|required|output|deliverable|verification|checklist|safety|pitfall|anti-pattern)\b/i.test(section.title)) {
16375
+ score += 2;
16376
+ }
16377
+ return score;
16378
+ }
16379
+ function bulletizeLines(value2, maxLines, maxChars) {
16380
+ const out = [];
16381
+ for (const raw of value2.split("\n")) {
16382
+ const line = raw.replace(/^[-*]\s+/, "").trim();
16383
+ if (!line || /^---+$/.test(line) || /^```/.test(line))
16384
+ continue;
16385
+ if (/^#+\s/.test(line))
16386
+ continue;
16387
+ out.push(`- ${compactSkillLine(line, maxChars)}`);
16388
+ if (out.length >= maxLines)
16389
+ break;
16390
+ }
16391
+ return out;
16392
+ }
16393
+ function capSkillExtractionOutput(value2, budgetTokens) {
16394
+ const maxChars = Math.max(1200, Math.min(7e3, budgetTokens * 4));
16395
+ if (value2.length <= maxChars)
16396
+ return value2;
16397
+ return `${value2.slice(0, Math.max(0, maxChars - 90)).trimEnd()}
16398
+
16399
+ [skill extraction truncated to ${budgetTokens} token budget]`;
16400
+ }
16401
+ function extractSkillForQuery(skill, content, query, budgetTokens = 900) {
16402
+ const terms = tokenizeSkillQuery(`${query} ${skill.name} ${skill.description} ${skill.triggers.join(" ")}`);
16403
+ const sections = splitSkillSections(content).map((section) => ({ ...section, score: sectionScore(section, terms) })).sort((a2, b) => b.score - a2.score || a2.title.localeCompare(b.title));
16404
+ const selectedSections = sections.filter((section) => section.score > 0).slice(0, 4);
16405
+ const fallbackSections = selectedSections.length ? selectedSections : sections.slice(0, 3);
16406
+ const maxChars = Math.max(1200, Math.min(6e3, budgetTokens * 4));
16407
+ const lines = [
16408
+ `<skill-extraction name="${skill.name}" source="${skill.source}">`,
16409
+ `Query: ${compactSkillLine(query, 240)}`,
16410
+ `Purpose: targeted operational guidance only; discard after current run.`,
16411
+ `Use when: ${compactSkillLine(skill.description || skill.triggers[0] || skill.name, 220)}`
16412
+ ];
16413
+ if (skill.triggers.length > 0)
16414
+ lines.push(`Triggers: ${skill.triggers.slice(0, 4).map((t2) => compactSkillLine(t2, 80)).join(" | ")}`);
16415
+ if (skill.compactionStrategy)
16416
+ lines.push(`Compaction: ${skill.compactionStrategy}`);
16417
+ if (skill.provenance)
16418
+ lines.push(`Provenance: ${formatProvenanceSummary(skill.provenance)}`);
16419
+ lines.push("Relevant guidance:");
16420
+ for (const section of fallbackSections) {
16421
+ lines.push(`Section: ${compactSkillLine(section.title, 100)}`);
16422
+ lines.push(...bulletizeLines(section.body, 6, 170));
16423
+ if (lines.join("\n").length > maxChars)
16424
+ break;
16425
+ }
16426
+ lines.push("Parent instruction: use the extracted guidance if relevant; otherwise ignore it. Do not ask to load the full skill unless this extraction is insufficient.");
16427
+ lines.push("</skill-extraction>");
16428
+ const out = lines.join("\n");
16429
+ return out.length > maxChars ? `${out.slice(0, Math.max(0, maxChars - 80)).trimEnd()}
16430
+ - [truncated to extraction budget]
16431
+ </skill-extraction>` : out;
16432
+ }
16259
16433
  function buildSkillsSummary(skills) {
16260
16434
  if (skills.length === 0)
16261
16435
  return "";
@@ -16457,11 +16631,61 @@ function parseTriggers(filePath) {
16457
16631
  }
16458
16632
  return triggers;
16459
16633
  }
16460
- var _cachedAiwgPkgRoot, SkillListTool, SkillExecuteTool;
16634
+ var _cachedAiwgPkgRoot, SKILL_STOPWORDS, SkillListTool, SkillExecuteTool, SkillExtractTool;
16461
16635
  var init_skill_tools = __esm({
16462
16636
  "packages/execution/dist/tools/skill-tools.js"() {
16463
16637
  "use strict";
16464
16638
  init_provenance();
16639
+ SKILL_STOPWORDS = /* @__PURE__ */ new Set([
16640
+ "about",
16641
+ "after",
16642
+ "again",
16643
+ "also",
16644
+ "and",
16645
+ "are",
16646
+ "because",
16647
+ "been",
16648
+ "but",
16649
+ "can",
16650
+ "code",
16651
+ "could",
16652
+ "does",
16653
+ "done",
16654
+ "for",
16655
+ "from",
16656
+ "have",
16657
+ "how",
16658
+ "into",
16659
+ "just",
16660
+ "more",
16661
+ "need",
16662
+ "not",
16663
+ "now",
16664
+ "our",
16665
+ "out",
16666
+ "please",
16667
+ "project",
16668
+ "repo",
16669
+ "run",
16670
+ "should",
16671
+ "task",
16672
+ "that",
16673
+ "the",
16674
+ "their",
16675
+ "then",
16676
+ "there",
16677
+ "this",
16678
+ "tool",
16679
+ "use",
16680
+ "was",
16681
+ "what",
16682
+ "when",
16683
+ "where",
16684
+ "with",
16685
+ "work",
16686
+ "you",
16687
+ "your"
16688
+ ]);
16465
16689
  SkillListTool = class {
16466
16690
  name = "skill_list";
16467
16691
  description = "List all available AIWG skills with their descriptions and trigger patterns. Use this to discover skills that can help with the current task.";
@@ -16605,6 +16829,131 @@ ${content}`,
16605
16829
  };
16606
16830
  }
16607
16831
  };
16832
+ SkillExtractTool = class {
16833
+ name = "skill_extract";
16834
+ description = "Extract targeted, task-specific guidance from one or more AIWG skills without loading full skill bodies into the main context. Use this before skill_execute on small/medium context windows. Prefer use_subagent=true for large or complex skills.";
16835
+ parameters = {
16836
+ type: "object",
16837
+ properties: {
16838
+ query: {
16839
+ type: "string",
16840
+ description: "Current task or precise extraction question."
16841
+ },
16842
+ name: {
16843
+ type: "string",
16844
+ description: "Optional exact skill name. If omitted, relevant skills are selected from query overlap."
16845
+ },
16846
+ filter: {
16847
+ type: "string",
16848
+ description: "Optional additional skill filter by name, description, trigger, or source."
16849
+ },
16850
+ limit: {
16851
+ type: "number",
16852
+ description: "Maximum skills to extract. Defaults to 2, max 5."
16853
+ },
16854
+ budget_tokens: {
16855
+ type: "number",
16856
+ description: "Approximate output budget per skill. Defaults to 900, max 1600."
16857
+ },
16858
+ use_subagent: {
16859
+ type: "boolean",
16860
+ description: "Delegate full SKILL.md reading to a sub-agent and return only its compact extraction. Defaults to true when available."
16861
+ }
16862
+ },
16863
+ required: ["query"]
16864
+ };
16865
+ repoRoot;
16866
+ _extractCallback;
16867
+ constructor(repoRoot) {
16868
+ this.repoRoot = repoRoot;
16869
+ }
16870
+ setExtractCallback(cb) {
16871
+ this._extractCallback = cb;
16872
+ }
16873
+ async execute(args) {
16874
+ const start2 = performance.now();
16875
+ const query = String(args["query"] ?? args["task"] ?? args["prompt"] ?? "").trim();
16876
+ if (!query) {
16877
+ return {
16878
+ success: false,
16879
+ output: "",
16880
+ error: "query is required. Pass the current task or precise extraction question.",
16881
+ durationMs: performance.now() - start2
16882
+ };
16883
+ }
16884
+ const name10 = String(args["name"] ?? args["skill_name"] ?? args["skill"] ?? "").trim();
16885
+ const filter2 = String(args["filter"] ?? args["source"] ?? "").trim().toLowerCase();
16886
+ const limitRaw = typeof args["limit"] === "number" ? args["limit"] : 2;
16887
+ const limit = Math.max(1, Math.min(5, Math.floor(limitRaw)));
16888
+ const budgetRaw = typeof args["budget_tokens"] === "number" ? args["budget_tokens"] : 900;
16889
+ const budgetTokens = Math.max(300, Math.min(1600, Math.floor(budgetRaw)));
16890
+ const useSubagent = args["use_subagent"] !== false;
16891
+ let skills = discoverSkills(this.repoRoot);
16892
+ if (filter2) {
16893
+ skills = skills.filter((skill) => skillHaystack(skill).includes(filter2));
16894
+ }
16895
+ let selected = [];
16896
+ if (name10) {
16897
+ const exact = skills.find((skill) => skill.name === name10);
16898
+ const fuzzy = exact ? exact : skills.find((skill) => skill.name.includes(name10) || name10.includes(skill.name));
16899
+ if (!fuzzy) {
16900
+ return {
16901
+ success: false,
16902
+ output: "",
16903
+ error: `Skill "${name10}" not found. Use skill_list to discover available skills.`,
16904
+ durationMs: performance.now() - start2
16905
+ };
16906
+ }
16907
+ selected = [{ skill: fuzzy, score: 999, matchedTerms: [name10], reason: "explicit skill name" }];
16908
+ } else {
16909
+ selected = selectSkillsForTask(skills, query, limit);
16910
+ }
16911
+ if (selected.length === 0) {
16912
+ return {
16913
+ success: true,
16914
+ output: `No relevant skills found for query: ${query}`,
16915
+ durationMs: performance.now() - start2
16916
+ };
16917
+ }
16918
+ const outputs = [];
16919
+ for (const item of selected.slice(0, limit)) {
16920
+ const content = loadSkillContent(item.skill.filePath);
16921
+ if (!content) {
16922
+ outputs.push(`<skill-extraction name="${item.skill.name}">
16923
+ Error: could not read skill file.
16924
+ </skill-extraction>`);
16925
+ continue;
16926
+ }
16927
+ if (useSubagent && this._extractCallback) {
16928
+ try {
16929
+ const extracted = await this._extractCallback(item.skill.name, content, query, budgetTokens);
16930
+ outputs.push(capSkillExtractionOutput(extracted, budgetTokens));
16931
+ continue;
16932
+ } catch (err) {
16933
+ outputs.push(`<skill-extraction name="${item.skill.name}">
16934
+ Sub-agent extraction failed: ${err instanceof Error ? err.message : String(err)}
16935
+ Fallback:
16936
+ ${extractSkillForQuery(item.skill, content, query, budgetTokens)}
16937
+ </skill-extraction>`);
16938
+ continue;
16939
+ }
16940
+ }
16941
+ outputs.push(extractSkillForQuery(item.skill, content, query, budgetTokens));
16942
+ }
16943
+ const output = [
16944
+ "# Ephemeral Skill Extraction",
16945
+ "Scope: current task only. Do not persist this block to memory unless the user explicitly asks for a durable procedure.",
16946
+ "",
16947
+ ...outputs
16948
+ ].join("\n\n");
16949
+ return {
16950
+ success: true,
16951
+ output,
16952
+ llmContent: output,
16953
+ durationMs: performance.now() - start2
16954
+ };
16955
+ }
16956
+ };
16608
16957
  }
16609
16958
  });
16610
16959
 
@@ -517506,6 +517855,7 @@ __export(dist_exports, {
517506
517855
  ShellTool: () => ShellTool,
517507
517856
  SkillBuildTool: () => SkillBuildTool,
517508
517857
  SkillExecuteTool: () => SkillExecuteTool,
517858
+ SkillExtractTool: () => SkillExtractTool,
517509
517859
  SkillListTool: () => SkillListTool,
517510
517860
  SoundPlaybackTool: () => SoundPlaybackTool,
517511
517861
  StdioTransport: () => StdioTransport,
@@ -517540,6 +517890,7 @@ __export(dist_exports, {
517540
517890
  audioOutputDir: () => audioOutputDir,
517541
517891
  buildCompactDiff: () => buildCompactDiff,
517542
517892
  buildCustomTools: () => buildCustomTools,
517893
+ buildEphemeralSkillPack: () => buildEphemeralSkillPack,
517543
517894
  buildGeneratedArtifactProvenance: () => buildGeneratedArtifactProvenance,
517544
517895
  buildGraph: () => buildGraph,
517545
517896
  buildMcpToolName: () => buildMcpToolName,
@@ -517577,6 +517928,7 @@ __export(dist_exports, {
517577
517928
  ensureAllDesktopDeps: () => ensureAllDesktopDeps,
517578
517929
  ensureCommand: () => ensureCommand,
517579
517930
  ensureDepsForGroup: () => ensureDepsForGroup,
517931
+ extractSkillForQuery: () => extractSkillForQuery,
517580
517932
  flattenSlug: () => flattenSlug,
517581
517933
  formatMessagesForContext: () => formatMessagesForContext,
517582
517934
  formatProvenanceSummary: () => formatProvenanceSummary,
@@ -517657,6 +518009,7 @@ __export(dist_exports, {
517657
518009
  saveMcpServerToConfig: () => saveMcpServerToConfig,
517658
518010
  savePacket: () => savePacket,
517659
518011
  sdcppVenvDir: () => sdcppVenvDir,
518012
+ selectSkillsForTask: () => selectSkillsForTask,
517660
518013
  serializeMap: () => serializeMap,
517661
518014
  setChangeLogSession: () => setChangeLogSession,
517662
518015
  setSudoPassword: () => setSudoPassword,
@@ -533285,7 +533638,7 @@ var init_agenticRunner = __esm({
533285
533638
  ],
533286
533639
  shell: ["shell", "shell_async", "kill_proc", "run_tests"],
533287
533640
  graph: ["graph_query", "graph_traverse", "code_graph"],
533288
- skill: ["skill_list", "skill_execute", "skill_search"]
533641
+ skill: ["skill_list", "skill_extract", "skill_execute", "skill_search"]
533289
533642
  };
533290
533643
  TOOL_AUTO_DEMOTE_TURNS = 10;
533291
533644
  SYSTEM_PROMPT = loadPrompt("agentic/system-large.md");
@@ -533688,6 +534041,7 @@ var init_agenticRunner = __esm({
533688
534041
  // back to deferred to reclaim token budget.
533689
534042
  _activatedTools = /* @__PURE__ */ new Set();
533690
534043
  _toolLastUsedTurn = /* @__PURE__ */ new Map();
534044
+ _ephemeralSkillPackContext = "";
533691
534045
  // Phase 1 — Context Tree. Tracks current phase + per-phase anchors so
533692
534046
  // compactMessages can summarize-by-phase and Phase 6 can surface anchors
533693
534047
  // by keyword. Initialized lazily in run() because the system-prompt hash
@@ -533900,6 +534254,10 @@ ${this.options.identityInjection}
533900
534254
  const tier = this.options.modelTier ?? "large";
533901
534255
  const useXmlTags = tier === "small" || tier === "medium";
533902
534256
  const ctx3 = this.options.dynamicContext;
534257
+ const skillPackMatch = ctx3.match(/<ephemeral-skill-pack>[\s\S]*?<\/ephemeral-skill-pack>/);
534258
+ if (skillPackMatch) {
534259
+ this._ephemeralSkillPackContext = skillPackMatch[0].slice(0, 2600);
534260
+ }
533903
534261
  sections.push({
533904
534262
  label: "c_know",
533905
534263
  content: useXmlTags ? `
@@ -536479,6 +536837,7 @@ Respond with your assessment, then take action.`;
536479
536837
  this._toolSequence = [];
536480
536838
  this._activatedTools.clear();
536481
536839
  this._toolLastUsedTurn.clear();
536840
+ this._ephemeralSkillPackContext = "";
536482
536841
  this._contextTree = null;
536483
536842
  this._lastSurfacedAnchorIds.clear();
536484
536843
  if (!this.options.disablePersistentMemory && !this._memoryInitialized) {
@@ -543014,6 +543373,11 @@ ${postCompactRestore.join("\n")}`);
543014
543373
  const antiRepetitionReminder = (tier === "small" || tier === "medium") && readFilesList.length > 0 ? `
543015
543374
 
543016
543375
  **DO NOT RE-READ THESE FILES** (you already have their contents): ${readFilesList.join(", ")}. Use the information above to make progress. Reading the same file again wastes a turn.` : "";
543376
+ const ephemeralSkillPackReminder = this._ephemeralSkillPackContext ? `
543377
+
543378
+ [Ephemeral skill-pack restore — current run only, do not persist]
543379
+ ${this._ephemeralSkillPackContext}
543380
+ Use skill_extract for targeted skill unpacking; do not load full skills into the main context unless necessary.` : "";
543017
543381
  const compactionMsg = {
543018
543382
  role: "system",
543019
543383
  // WO-CE-03: XML tags for structural clarity on small/medium models
@@ -543021,11 +543385,11 @@ ${postCompactRestore.join("\n")}`);
543021
543385
  ${fullSummary}
543022
543386
  </compaction-summary>
543023
543387
 
543024
- [Continue from the recent context below. Do not repeat work already completed above.]${goalReminder}${nextActionDirective}${antiRepetitionReminder}${toolCallingReminder}` : `[Context compacted${strategyLabel} — summary of earlier work]
543388
+ [Continue from the recent context below. Do not repeat work already completed above.]${goalReminder}${nextActionDirective}${antiRepetitionReminder}${ephemeralSkillPackReminder}${toolCallingReminder}` : `[Context compacted${strategyLabel} — summary of earlier work]
543025
543389
 
543026
543390
  ${fullSummary}
543027
543391
 
543028
- [Continue from the recent context below. Do not repeat work already completed above.]${goalReminder}${nextActionDirective}${antiRepetitionReminder}${toolCallingReminder}`
543392
+ [Continue from the recent context below. Do not repeat work already completed above.]${goalReminder}${nextActionDirective}${antiRepetitionReminder}${ephemeralSkillPackReminder}${toolCallingReminder}`
543029
543393
  };
543030
543394
  this.persistCheckpoint(fullSummary);
543031
543395
  let narrowedHead = [...head];
@@ -556652,6 +557016,9 @@ var init_render = __esm({
556652
557016
  "aiwg_setup",
556653
557017
  "aiwg_health",
556654
557018
  "aiwg_workflow",
557019
+ "skill_list",
557020
+ "skill_extract",
557021
+ "skill_execute",
556655
557022
  "create_tool",
556656
557023
  "manage_tools",
556657
557024
  "ask_user",
@@ -564385,6 +564752,7 @@ var init_status_bar = __esm({
564385
564752
  desktop_describe: 10,
564386
564753
  code_sandbox: 45,
564387
564754
  skill_list: 5,
564755
+ skill_extract: 20,
564388
564756
  skill_execute: 30,
564389
564757
  create_tool: 120,
564390
564758
  manage_tools: 15,
@@ -599432,6 +599800,8 @@ var init_tool_policy = __esm({
599432
599800
  "transcribe_file",
599433
599801
  "video_understand",
599434
599802
  "audio_analyze",
599803
+ "skill_list",
599804
+ "skill_extract",
599435
599805
  "reminder",
599436
599806
  "remind",
599437
599807
  "reminders",
@@ -599462,6 +599832,8 @@ var init_tool_policy = __esm({
599462
599832
  "transcribe_file",
599463
599833
  "video_understand",
599464
599834
  "audio_analyze",
599835
+ "skill_list",
599836
+ "skill_extract",
599465
599837
  "reminder",
599466
599838
  "remind",
599467
599839
  "reminders",
@@ -600326,6 +600698,230 @@ function buildReplyOpportunities(input, openQuestions) {
600326
600698
  }
600327
600699
  return opportunities;
600328
600700
  }
600701
+ function clamp017(value2) {
600702
+ if (!Number.isFinite(value2)) return 0;
600703
+ return Math.max(0, Math.min(1, value2));
600704
+ }
600705
+ function messageLabel(entry) {
600706
+ return entry.speaker || entry.username || "user";
600707
+ }
600708
+ function pushStimulationSignal(signals, signal, source, weight) {
600709
+ const cleanSignal = compactLine2(signal, 120);
600710
+ const cleanSource = compactLine2(source, 180);
600711
+ if (!cleanSignal || signals.some((entry) => entry.signal === cleanSignal && entry.source === cleanSource)) return;
600712
+ signals.push({ signal: cleanSignal, source: cleanSource, weight: clamp017(weight) });
600713
+ }
600714
+ function buildMetaAnalysisSignals(input) {
600715
+ const chatLabel = input.chatTitle || input.chatId;
600716
+ return [
600717
+ `Private meta-analysis for Telegram ${input.chatType} ${chatLabel}; not a visible reply and not a user command.`,
600718
+ "Treat this artifact as a context-management packet: relationship map, curiosity queue, scoped tools, and outreach options.",
600719
+ "Any public or private follow-up still requires a live model decision in the current conversation state.",
600720
+ "Private DM follow-up is a planned affordance only unless a delivery policy and user/contact permission allow it."
600721
+ ];
600722
+ }
600723
+ function buildHumanStimulationSignals(input) {
600724
+ const signals = [];
600725
+ const recent = recentUserMessages(input);
600726
+ const participants = topParticipants(input);
600727
+ const last2 = recent[recent.length - 1];
600728
+ if (input.stimulationContext) {
600729
+ pushStimulationSignal(signals, "idle stimulation snapshot available", input.stimulationContext.split("\n").slice(0, 2).join(" / "), 0.42);
600730
+ }
600731
+ if (participants.length > 1) {
600732
+ pushStimulationSignal(signals, "multi-person social context", `${participants.length} active participant profiles`, 0.58);
600733
+ }
600734
+ for (const participant of participants.slice(0, 5)) {
600735
+ if (participant.directAddressCount > 0) {
600736
+ pushStimulationSignal(signals, "direct agent address history", `${participant.username || participant.firstName}: ${participant.directAddressCount} direct address(es)`, 0.74);
600737
+ }
600738
+ if (participant.replyCount > 0) {
600739
+ pushStimulationSignal(signals, "reply-chain salience", `${participant.username || participant.firstName}: ${participant.replyCount} reply relationship(s)`, 0.68);
600740
+ }
600741
+ if (participant.toneTags.includes("frustrated") || participant.toneTags.includes("technical")) {
600742
+ pushStimulationSignal(signals, "high-specificity user state", `${participant.username || participant.firstName}: tones=${participant.toneTags.slice(0, 5).join(",")}`, 0.61);
600743
+ }
600744
+ }
600745
+ for (const entry of recent.slice(-8)) {
600746
+ const text = compactLine2(entry.text, 160);
600747
+ if (/[??]\s*$/.test(text)) {
600748
+ pushStimulationSignal(signals, "open question", `${messageLabel(entry)}: ${text}`, 0.7);
600749
+ }
600750
+ if (/\b(wonder|curious|why|how|what if|not sure|maybe|confused|unclear)\b/i.test(text)) {
600751
+ pushStimulationSignal(signals, "curiosity or uncertainty cue", `${messageLabel(entry)}: ${text}`, 0.64);
600752
+ }
600753
+ if (entry.mediaSummary) {
600754
+ pushStimulationSignal(signals, "multimodal memory cue", `${messageLabel(entry)}: ${entry.mediaSummary}`, 0.66);
600755
+ }
600756
+ if (entry.replyToMessageId) {
600757
+ pushStimulationSignal(signals, "explicit reply dependency", `${messageLabel(entry)} replied to message ${entry.replyToMessageId}`, 0.72);
600758
+ }
600759
+ }
600760
+ if (last2 && /\b(omnius|bot|agent)\b/i.test(last2.text)) {
600761
+ pushStimulationSignal(signals, "agent-name mention", `${messageLabel(last2)}: ${compactLine2(last2.text, 160)}`, 0.57);
600762
+ }
600763
+ return signals.sort((a2, b) => b.weight - a2.weight).slice(0, 12);
600764
+ }
600765
+ function titleFromText(text) {
600766
+ const cleaned = compactLine2(text.replace(/[??!.]+$/g, ""), 72);
600767
+ if (!cleaned) return "Open curiosity thread";
600768
+ return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);
600769
+ }
600770
+ function buildCuriosityThreads(input, openQuestions, stimulationSignals) {
600771
+ const threads = [];
600772
+ const recent = recentUserMessages(input);
600773
+ const sourceEntries = recent.filter(
600774
+ (entry) => /[??]\s*$/.test(entry.text) || /\b(wonder|curious|why|how do we|what if|not sure|maybe|need to|unclear|confused)\b/i.test(entry.text) || entry.mediaSummary
600775
+ );
600776
+ for (const entry of sourceEntries.slice(-6)) {
600777
+ const text = compactLine2(entry.text, 220);
600778
+ const messageId = typeof entry.messageId === "number" ? [entry.messageId] : [];
600779
+ const replyBoost = entry.replyToMessageId ? 0.08 : 0;
600780
+ const mediaBoost = entry.mediaSummary ? 0.07 : 0;
600781
+ const questionBoost = /[??]\s*$/.test(text) ? 0.1 : 0;
600782
+ threads.push({
600783
+ title: titleFromText(text || entry.mediaSummary || "Multimodal thread"),
600784
+ question: text.endsWith("?") || text.endsWith("?") ? text : `What should be learned or clarified from: ${text || entry.mediaSummary || "recent media"}?`,
600785
+ rationale: "Human curiosity, uncertainty, or multimodal content makes this a useful idle exploration target.",
600786
+ sourceMessages: messageId,
600787
+ intensity: clamp017(0.5 + replyBoost + mediaBoost + questionBoost)
600788
+ });
600789
+ }
600790
+ for (const question of openQuestions.slice(-4)) {
600791
+ if (threads.some((thread) => question.includes(thread.title) || thread.question.includes(question))) continue;
600792
+ threads.push({
600793
+ title: titleFromText(question),
600794
+ question,
600795
+ rationale: "Open question captured during idle context compression.",
600796
+ sourceMessages: [],
600797
+ intensity: 0.58
600798
+ });
600799
+ }
600800
+ if (threads.length === 0 && stimulationSignals.length > 0) {
600801
+ const strongest = stimulationSignals[0];
600802
+ threads.push({
600803
+ title: `Follow up on ${strongest.signal}`,
600804
+ question: `Is there a useful clarification or memory consolidation around ${strongest.source}?`,
600805
+ rationale: "Strongest stimulation signal can seed a low-intrusion reflection target.",
600806
+ sourceMessages: [],
600807
+ intensity: clamp017(strongest.weight * 0.72)
600808
+ });
600809
+ }
600810
+ return threads.sort((a2, b) => b.intensity - a2.intensity).slice(0, 8);
600811
+ }
600812
+ function buildScopedExplorationTools(input) {
600813
+ const chatScope = `telegram:${input.chatType}:${input.chatId}`;
600814
+ const title = input.chatTitle ? ` (${input.chatTitle})` : "";
600815
+ return [
600816
+ {
600817
+ name: "memory_search",
600818
+ scope: `${chatScope}${title}`,
600819
+ use: "Search scoped group/private memory before asking users to repeat known context.",
600820
+ visibility: "private_meta"
600821
+ },
600822
+ {
600823
+ name: "memory_write",
600824
+ scope: `${chatScope}${title}`,
600825
+ use: "Store verified relationship, preference, and follow-up facts with participant/message anchors.",
600826
+ visibility: "private_meta"
600827
+ },
600828
+ {
600829
+ name: "identity_memory",
600830
+ scope: `${chatScope}${title}`,
600831
+ use: "Enroll or resolve named people, faces, voices, images, and cross-modal identity assertions from this chat.",
600832
+ visibility: "private_meta"
600833
+ },
600834
+ {
600835
+ name: "telegram_media_recent",
600836
+ scope: `${chatScope}${title}`,
600837
+ use: "Inspect recent chat-scoped images, audio, video, files, captions, and reply-linked media references.",
600838
+ visibility: "private_meta"
600839
+ },
600840
+ {
600841
+ name: "vision / OCR / video_understand",
600842
+ scope: `${chatScope}${title}`,
600843
+ use: "Extract visual facts from chat media when the curiosity thread depends on image or video content.",
600844
+ visibility: "private_meta"
600845
+ },
600846
+ {
600847
+ name: "audio_analyze / transcribe",
600848
+ scope: `${chatScope}${title}`,
600849
+ use: "Extract transcript, speaker, and voice-characteristic hints from chat-scoped audio or voice notes.",
600850
+ visibility: "private_meta"
600851
+ },
600852
+ {
600853
+ name: "web_search / web_fetch",
600854
+ scope: "public web only",
600855
+ use: "Research external facts for unanswered group questions without exposing private chat content.",
600856
+ visibility: "public_safe"
600857
+ }
600858
+ ];
600859
+ }
600860
+ function participantForThread(input, thread) {
600861
+ const sourceIds = new Set(thread.sourceMessages);
600862
+ const sourceMessage = input.history.find((entry) => typeof entry.messageId === "number" && sourceIds.has(entry.messageId));
600863
+ if (sourceMessage?.username) {
600864
+ return input.participants.find((participant) => participant.username === sourceMessage.username);
600865
+ }
600866
+ if (sourceMessage?.speaker) {
600867
+ const clean3 = sourceMessage.speaker.replace(/^@/, "");
600868
+ return input.participants.find((participant) => participant.username === clean3 || participant.firstName === sourceMessage.speaker);
600869
+ }
600870
+ return topParticipants(input)[0];
600871
+ }
600872
+ function buildOutreachPlans(input, curiosityThreads) {
600873
+ const plans = [];
600874
+ for (const thread of curiosityThreads.slice(0, 4)) {
600875
+ plans.push({
600876
+ target: "same_group",
600877
+ trigger: thread.question,
600878
+ purpose: "Continue the public thread only when the live model judges that the group would benefit from a concise follow-up.",
600879
+ draftIntent: "Ask one concrete clarification, offer one useful synthesis, or stay silent if the room has moved on.",
600880
+ gate: "model_decision",
600881
+ confidence: clamp017(thread.intensity * 0.86)
600882
+ });
600883
+ const participant = participantForThread(input, thread);
600884
+ if (!participant) continue;
600885
+ const recipient = participant.username && participant.username !== "unknown" ? `@${participant.username}` : participant.firstName || `user:${participant.fromUserId}`;
600886
+ plans.push({
600887
+ target: "private_dm",
600888
+ recipient,
600889
+ trigger: thread.question,
600890
+ 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
+ draftIntent: "Reference the public thread briefly, ask permission to continue privately, and do not reveal hidden meta-analysis.",
600892
+ gate: "admin_review",
600893
+ confidence: clamp017(thread.intensity * 0.58)
600894
+ });
600895
+ }
600896
+ return plans.slice(0, 8);
600897
+ }
600898
+ function personaDirectiveLines(personaContext) {
600899
+ const lines = (personaContext || "").split("\n").map((line) => compactLine2(line.replace(/^[-#*\s]+/, ""), 180)).filter((line) => line && !/^```/.test(line) && !/^Scope:/.test(line) && !/^Updated:/.test(line)).filter((line) => !/Scoped Personality Profile|Scoped Personality Driver|Messages observed/i.test(line));
600900
+ const selected = lines.filter(
600901
+ (line) => /\b(prefer|use|track|mirror|answer|do not|avoid|boundary|relationship|tone|style|guidance)\b/i.test(line)
600902
+ ).slice(0, 8);
600903
+ if (selected.length > 0) return selected;
600904
+ return [
600905
+ "Prefer concise, concrete follow-ups grounded in this Telegram scope.",
600906
+ "Track who said what and preserve reply relationships across public and private context.",
600907
+ "Keep curiosity high while keeping public replies low-intrusion.",
600908
+ "Do not expose private memory, local paths, hidden reasoning, or meta-analysis artifacts."
600909
+ ];
600910
+ }
600911
+ function buildPersonaSteering(input) {
600912
+ const directives = personaDirectiveLines(input.personaContext);
600913
+ for (const required of [
600914
+ "Maintain high curiosity during private analysis while keeping visible public replies low-intrusion.",
600915
+ "Use scoped tool evidence before follow-up; do not turn curiosity into unsolicited claims."
600916
+ ]) {
600917
+ if (!directives.some((line) => line === required)) directives.push(required);
600918
+ }
600919
+ return {
600920
+ source: input.personaContext ? "scoped-personality document" : "default Telegram daydream steering",
600921
+ directives: directives.slice(0, 10),
600922
+ behavior: "Use the persona driver to shape tone, curiosity level, and outreach restraint; do not let it override safety, explicit user instructions, or scoped tool policy."
600923
+ };
600924
+ }
600329
600925
  function buildMemoryProposals(input) {
600330
600926
  const proposals = [];
600331
600927
  const bySpeaker = /* @__PURE__ */ new Map();
@@ -600348,9 +600944,15 @@ function buildMemoryProposals(input) {
600348
600944
  function buildTelegramChannelDaydream(input) {
600349
600945
  const firstTs = input.history.map((entry) => entry.ts).find((ts) => typeof ts === "number");
600350
600946
  const lastTs = [...input.history].reverse().map((entry) => entry.ts).find((ts) => typeof ts === "number");
600947
+ const metaAnalysisSignals = buildMetaAnalysisSignals(input);
600948
+ const humanStimulationSignals = buildHumanStimulationSignals(input);
600351
600949
  const relationshipSignals = buildRelationshipSignals(input);
600352
600950
  const contextEngineeringNotes = buildContextEngineeringNotes(input);
600353
600951
  const openQuestions = buildOpenQuestions(input);
600952
+ const curiosityThreads = buildCuriosityThreads(input, openQuestions, humanStimulationSignals);
600953
+ const scopedExplorationTools = buildScopedExplorationTools(input);
600954
+ const outreachPlans = buildOutreachPlans(input, curiosityThreads);
600955
+ const personaSteering = buildPersonaSteering(input);
600354
600956
  const replyOpportunities = buildReplyOpportunities(input, openQuestions);
600355
600957
  const memoryProposals = buildMemoryProposals(input);
600356
600958
  const artifactProposals = [
@@ -600372,7 +600974,7 @@ function buildTelegramChannelDaydream(input) {
600372
600974
  ].filter((entry) => Boolean(entry));
600373
600975
  const seed = `${input.sessionKey}:${input.generatedAtMs}:${input.history.length}`;
600374
600976
  return {
600375
- version: 1,
600977
+ version: 2,
600376
600978
  id: createHash20("sha1").update(seed).digest("hex").slice(0, 16),
600377
600979
  sessionKey: input.sessionKey,
600378
600980
  chatId: input.chatId,
@@ -600384,13 +600986,20 @@ function buildTelegramChannelDaydream(input) {
600384
600986
  firstMessageAt: isoFromMs(firstTs),
600385
600987
  lastMessageAt: isoFromMs(lastTs)
600386
600988
  },
600989
+ metaAnalysisSignals,
600990
+ humanStimulationSignals,
600991
+ curiosityThreads,
600992
+ scopedExplorationTools,
600993
+ outreachPlans,
600994
+ personaSteering,
600387
600995
  relationshipSignals,
600388
600996
  contextEngineeringNotes,
600389
600997
  openQuestions,
600390
600998
  replyOpportunities,
600391
600999
  memoryProposals,
600392
601000
  artifactProposals,
600393
- stimulationContext: input.stimulationContext
601001
+ stimulationContext: input.stimulationContext,
601002
+ personaContext: input.personaContext
600394
601003
  };
600395
601004
  }
600396
601005
  function formatTelegramChannelDaydreamMarkdown(artifact) {
@@ -600402,6 +601011,34 @@ function formatTelegramChannelDaydreamMarkdown(artifact) {
600402
601011
  `Session: ${artifact.sessionKey}`,
600403
601012
  `Chat: ${artifact.chatTitle || artifact.chatId} (${artifact.chatType})`,
600404
601013
  "",
601014
+ section("Meta-Analysis Signals", artifact.metaAnalysisSignals),
601015
+ "",
601016
+ "## Human Stimulation Signals",
601017
+ artifact.humanStimulationSignals.length ? artifact.humanStimulationSignals.map((item) => `- ${item.signal}: ${item.source} (weight ${item.weight.toFixed(2)})`).join("\n") : "- None",
601018
+ "",
601019
+ "## Curiosity Threads",
601020
+ artifact.curiosityThreads.length ? artifact.curiosityThreads.map((item) => `- ${item.title}
601021
+ - question: ${item.question}
601022
+ - rationale: ${item.rationale}
601023
+ - intensity: ${item.intensity.toFixed(2)}${item.sourceMessages.length ? `
601024
+ - source messages: ${item.sourceMessages.join(", ")}` : ""}`).join("\n") : "- None",
601025
+ "",
601026
+ "## Scoped Exploration Tool Suite",
601027
+ artifact.scopedExplorationTools.length ? artifact.scopedExplorationTools.map((item) => `- ${item.name} [${item.visibility}]
601028
+ - scope: ${item.scope}
601029
+ - use: ${item.use}`).join("\n") : "- None",
601030
+ "",
601031
+ "## Outreach Plans",
601032
+ artifact.outreachPlans.length ? artifact.outreachPlans.map((item) => `- ${item.target}${item.recipient ? ` -> ${item.recipient}` : ""} [${item.gate}]
601033
+ - trigger: ${item.trigger}
601034
+ - purpose: ${item.purpose}
601035
+ - draft intent: ${item.draftIntent}
601036
+ - confidence: ${item.confidence.toFixed(2)}`).join("\n") : "- None",
601037
+ "",
601038
+ "## Persona Steering",
601039
+ `Source: ${artifact.personaSteering.source}`,
601040
+ artifact.personaSteering.directives.length ? artifact.personaSteering.directives.map((line) => `- ${line}`).join("\n") : "- None",
601041
+ "",
600405
601042
  section("Relationship Signals", artifact.relationshipSignals),
600406
601043
  "",
600407
601044
  section("Context Engineering Notes", artifact.contextEngineeringNotes),
@@ -600424,6 +601061,12 @@ function formatTelegramChannelDaydreamMarkdown(artifact) {
600424
601061
 
600425
601062
  \`\`\`text
600426
601063
  ${artifact.stimulationContext}
601064
+ \`\`\`` : "",
601065
+ artifact.personaContext ? `
601066
+ ## Persona Snapshot
601067
+
601068
+ \`\`\`text
601069
+ ${artifact.personaContext}
600427
601070
  \`\`\`` : "",
600428
601071
  ""
600429
601072
  ].join("\n");
@@ -600454,8 +601097,20 @@ function formatTelegramChannelDaydreamContext(artifact) {
600454
601097
  if (!artifact) return "";
600455
601098
  const lines = [
600456
601099
  "### Telegram Channel Daydream",
600457
- "Private idle reflection artifact. Use it as context only; do not mention it unless the user asks about internal memory.",
601100
+ "Private idle reflection and stimulation artifact. Use it as context only; do not mention it unless the user asks about internal memory.",
600458
601101
  `Generated: ${artifact.generatedAt}`,
601102
+ artifact.metaAnalysisSignals?.length ? `Meta-analysis signals:
601103
+ ${artifact.metaAnalysisSignals.slice(0, 4).map((line) => `- ${line}`).join("\n")}` : "",
601104
+ artifact.humanStimulationSignals?.length ? `Human stimulation signals:
601105
+ ${artifact.humanStimulationSignals.slice(0, 5).map((item) => `- ${item.signal}: ${item.source} (weight ${item.weight.toFixed(2)})`).join("\n")}` : "",
601106
+ artifact.curiosityThreads?.length ? `Curiosity threads:
601107
+ ${artifact.curiosityThreads.slice(0, 4).map((item) => `- ${item.question} (intensity ${item.intensity.toFixed(2)})`).join("\n")}` : "",
601108
+ artifact.scopedExplorationTools?.length ? `Scoped exploration tools:
601109
+ ${artifact.scopedExplorationTools.slice(0, 6).map((item) => `- ${item.name}: ${item.scope}; ${item.use}`).join("\n")}` : "",
601110
+ artifact.outreachPlans?.length ? `Outreach plans:
601111
+ ${artifact.outreachPlans.slice(0, 4).map((item) => `- ${item.target}${item.recipient ? ` -> ${item.recipient}` : ""}: ${item.trigger} (gate ${item.gate}; confidence ${item.confidence.toFixed(2)})`).join("\n")}` : "",
601112
+ artifact.personaSteering?.directives?.length ? `Persona steering:
601113
+ ${artifact.personaSteering.directives.slice(0, 5).map((line) => `- ${line}`).join("\n")}` : "",
600459
601114
  artifact.relationshipSignals.length ? `Relationship signals:
600460
601115
  ${artifact.relationshipSignals.slice(0, 5).map((line) => `- ${line}`).join("\n")}` : "",
600461
601116
  artifact.contextEngineeringNotes.length ? `Context notes:
@@ -602476,6 +603131,18 @@ ${mediaContext}` : ""
602476
603131
  samples: [...profile.samples]
602477
603132
  }));
602478
603133
  const memoryCards = this.chatMemoryCards.get(sessionKey) ?? [];
603134
+ let personaContext;
603135
+ try {
603136
+ const label = last2.chatType !== "private" ? `${last2.chatTitle || last2.chatType || "telegram"}-${String(last2.chatId)}` : `private-${last2.username || last2.fromUserId || last2.chatId}`;
603137
+ personaContext = buildScopedPersonalityContext({
603138
+ kind: "telegram-chat",
603139
+ id: sessionKey,
603140
+ label,
603141
+ repoRoot: this.repoRoot || "."
603142
+ });
603143
+ } catch {
603144
+ personaContext = void 0;
603145
+ }
602479
603146
  return {
602480
603147
  sessionKey,
602481
603148
  chatId: String(last2.chatId),
@@ -602495,7 +603162,8 @@ ${mediaContext}` : ""
602495
603162
  })),
602496
603163
  participants,
602497
603164
  memoryCards: memoryCards.map((card) => ({ ...card })),
602498
- stimulationContext: this.stimulation.formatContext(sessionKey)
603165
+ stimulationContext: this.stimulation.formatContext(sessionKey),
603166
+ personaContext
602499
603167
  };
602500
603168
  }
602501
603169
  async runTelegramChannelDmnForSession(sessionKey, reason = "idle", nowMs = Date.now(), force = false) {
@@ -603361,6 +604029,8 @@ ${chatLabel}`,
603361
604029
  sections.push(`## Identity Memory Contract
603362
604030
 
603363
604031
  ${formatIdentityMemoryContext(chatLabel)}`);
604032
+ const skillPackContext = this.buildTelegramEphemeralSkillPack(msg, toolContext, profile, modelTier);
604033
+ if (skillPackContext) sections.push(skillPackContext);
603364
604034
  if (isAdminDM) {
603365
604035
  sections.push(`## Admin Capability Contract
603366
604036
 
@@ -603398,6 +604068,33 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
603398
604068
  }
603399
604069
  return { sessionKey, sessionId, context: sections.join("\n\n") };
603400
604070
  }
604071
+ buildTelegramEphemeralSkillPack(msg, toolContext, profile, modelTier) {
604072
+ if (!this.repoRoot) return "";
604073
+ const isAdminDM = toolContext === "telegram-admin-dm";
604074
+ const scope = msg.chatType !== "private" ? `telegram group ${msg.chatTitle || msg.chatId}` : `telegram private chat ${msg.username || msg.fromUserId || msg.chatId}`;
604075
+ const query = [
604076
+ `Telegram ${profile} run in ${scope}`,
604077
+ `Tool context: ${toolContext}`,
604078
+ msg.text ? `Current message: ${msg.text}` : "",
604079
+ msg.replyToText ? `Replied-to content: ${msg.replyToText}` : "",
604080
+ msg.media ? `Media attached: ${msg.media.type}` : ""
604081
+ ].filter(Boolean).join("\n");
604082
+ try {
604083
+ const pack = buildEphemeralSkillPack(discoverSkills(this.repoRoot), {
604084
+ task: query,
604085
+ modelTier,
604086
+ limit: isAdminDM ? 5 : 3
604087
+ });
604088
+ if (!pack) return "";
604089
+ return [
604090
+ "## Ephemeral AIWG Skill Pack",
604091
+ isAdminDM ? "Scope: admin Telegram DM for this run only. The agent may use skill_list, skill_extract, or skill_execute when relevant." : "Scope: current Telegram chat only. Public/group runs may use skill_list and skill_extract for bounded guidance, but must not reveal private skill text, local paths, or admin/TUI context.",
604092
+ pack
604093
+ ].join("\n\n");
604094
+ } catch {
604095
+ return "";
604096
+ }
604097
+ }
603401
604098
  async handleAdminAuthCommand(msg) {
603402
604099
  const normalized = msg.text.trim();
603403
604100
  const lower = normalized.toLowerCase();
@@ -604806,6 +605503,8 @@ Scoped workspace: ${scopedRoot}`,
604806
605503
  }
604807
605504
  }
604808
605505
  );
605506
+ const sharedSkillExtractTool = new SkillExtractTool(repoRoot);
605507
+ const adminSkillExtractTool = new SkillExtractTool(repoRoot);
604809
605508
  const sharedReadMemoryWebTools = [
604810
605509
  // File tools
604811
605510
  new FileReadTool(repoRoot),
@@ -604836,6 +605535,8 @@ Scoped workspace: ${scopedRoot}`,
604836
605535
  new VideoUnderstandTool(repoRoot),
604837
605536
  new AudioAnalyzeTool(),
604838
605537
  new ExploreToolsTool(),
605538
+ new SkillListTool(repoRoot),
605539
+ sharedSkillExtractTool,
604839
605540
  this.buildTelegramMediaRecentTool(chatId, msg),
604840
605541
  ...this.buildTelegramReminderTools(context2, repoRoot, chatId, msg)
604841
605542
  ];
@@ -604896,6 +605597,9 @@ Scoped workspace: ${scopedRoot}`,
604896
605597
  new ImpactAnalysisTool(repoRoot),
604897
605598
  new CodeNeighborsTool(repoRoot),
604898
605599
  new ProcessHealthTool(),
605600
+ new SkillListTool(repoRoot),
605601
+ new SkillExecuteTool(repoRoot),
605602
+ adminSkillExtractTool,
604899
605603
  ...this.buildTelegramReminderTools(context2, repoRoot, chatId, msg),
604900
605604
  fullSubAgentTool,
604901
605605
  this.buildTelegramSendFileTool(context2, repoRoot, chatId, msg)
@@ -607602,7 +608306,7 @@ __export(voicechat_exports, {
607602
608306
  VoiceChatSession: () => VoiceChatSession
607603
608307
  });
607604
608308
  import { EventEmitter as EventEmitter11 } from "node:events";
607605
- function clamp017(x) {
608309
+ function clamp018(x) {
607606
608310
  return x < 0 ? 0 : x > 1 ? 1 : x;
607607
608311
  }
607608
608312
  function alnumRatio(s2) {
@@ -607641,9 +608345,9 @@ function computeSignalFromText(text, confidence2) {
607641
608345
  else score = 0.15;
607642
608346
  score -= repeatingCharPenalty(t2) * 0.4;
607643
608347
  if (typeof confidence2 === "number" && !Number.isNaN(confidence2)) {
607644
- score = 0.7 * score + 0.3 * clamp017(confidence2);
608348
+ score = 0.7 * score + 0.3 * clamp018(confidence2);
607645
608349
  }
607646
- return clamp017(score);
608350
+ return clamp018(score);
607647
608351
  }
607648
608352
  function truncateForLog(s2, n2) {
607649
608353
  return s2.length <= n2 ? s2 : s2.slice(0, n2 - 1) + "…";
@@ -607913,7 +608617,7 @@ Rules:
607913
608617
  }, MAX_SEGMENT_MS);
607914
608618
  }
607915
608619
  this.captureBuffer = text;
607916
- this.lastSignalScore = typeof snr === "number" && !Number.isNaN(snr) ? clamp017(snr) : computeSignalFromText(text, confidence2);
608620
+ this.lastSignalScore = typeof snr === "number" && !Number.isNaN(snr) ? clamp018(snr) : computeSignalFromText(text, confidence2);
607917
608621
  this.emit("snr", { score: this.lastSignalScore });
607918
608622
  this.onPartialTranscript(text);
607919
608623
  if (this.silenceTimer) clearTimeout(this.silenceTimer);
@@ -609520,6 +610224,15 @@ function buildContent(items, tier) {
609520
610224
  content: loadAiwgItemContent(i2.path, perItemCap)
609521
610225
  }));
609522
610226
  }
610227
+ function aiwgItemAsSkillInfo(item) {
610228
+ return {
610229
+ name: item.name,
610230
+ description: item.description,
610231
+ triggers: item.triggers,
610232
+ source: `${item.kind}:${item.source}`,
610233
+ filePath: item.path
610234
+ };
610235
+ }
609523
610236
  async function handleAiwgExpand(ctx3) {
609524
610237
  const { req: req2, res, requestId, user } = ctx3;
609525
610238
  try {
@@ -609537,6 +610250,10 @@ async function handleAiwgExpand(ctx3) {
609537
610250
  const trigger = typeof body.trigger === "string" ? body.trigger.toLowerCase() : null;
609538
610251
  const name10 = typeof body.name === "string" ? body.name : null;
609539
610252
  const limit = typeof body.limit === "number" ? Math.min(body.limit, 10) : 3;
610253
+ const mode = typeof body.mode === "string" ? body.mode.toLowerCase() : "";
610254
+ const extract2 = body.extract === true || mode === "extract" || mode === "extraction";
610255
+ const query = typeof body.query === "string" && body.query.trim() ? body.query.trim() : trigger || name10 || "";
610256
+ const budgetTokens = typeof body.budget_tokens === "number" ? Math.max(300, Math.min(1600, Math.floor(body.budget_tokens))) : 900;
609540
610257
  if (!trigger && !name10) {
609541
610258
  sendProblem(res, problemDetails({
609542
610259
  type: "https://omnius.nexus/problems/invalid-request",
@@ -609572,20 +610289,33 @@ async function handleAiwgExpand(ctx3) {
609572
610289
  }));
609573
610290
  return true;
609574
610291
  }
609575
- const unpacked = matches.map((m2) => ({
609576
- name: m2.name,
609577
- kind: m2.kind,
609578
- source: m2.source,
609579
- description: m2.description,
609580
- triggers: m2.triggers,
609581
- content: loadAiwgItemContent(m2.path, 1e4)
609582
- }));
609583
- publishEvent("skill.invoked", { count: unpacked.length, trigger, name: name10 }, {
610292
+ const unpacked = matches.map((m2) => {
610293
+ const raw = loadAiwgItemContent(m2.path, extract2 ? 5e4 : 1e4);
610294
+ const base3 = {
610295
+ name: m2.name,
610296
+ kind: m2.kind,
610297
+ source: m2.source,
610298
+ description: m2.description,
610299
+ triggers: m2.triggers
610300
+ };
610301
+ if (extract2) {
610302
+ return {
610303
+ ...base3,
610304
+ content_mode: "extraction",
610305
+ extraction: raw ? extractSkillForQuery(aiwgItemAsSkillInfo(m2), raw, query, budgetTokens) : null
610306
+ };
610307
+ }
610308
+ return {
610309
+ ...base3,
610310
+ content: raw
610311
+ };
610312
+ });
610313
+ publishEvent(extract2 ? "skill.extracted" : "skill.invoked", { count: unpacked.length, trigger, name: name10, mode: extract2 ? "extract" : "content" }, {
609584
610314
  subject: user ?? "anonymous",
609585
610315
  aimsControl: "A.7.3"
609586
610316
  });
609587
610317
  sendJson(res, 200, {
609588
- query: { trigger, name: name10, limit },
610318
+ query: { trigger, name: name10, limit, mode: extract2 ? "extract" : "content", budget_tokens: budgetTokens },
609589
610319
  matches: unpacked.length,
609590
610320
  items: unpacked
609591
610321
  }, { cacheControl: "private, max-age=30" });
@@ -609605,6 +610335,7 @@ var _cachedAiwgRoot, _cachedFrameworks, _cachedItems, _cachedAddons;
609605
610335
  var init_aiwg = __esm({
609606
610336
  "packages/cli/src/api/aiwg.ts"() {
609607
610337
  "use strict";
610338
+ init_dist5();
609608
610339
  init_http2();
609609
610340
  _cachedFrameworks = null;
609610
610341
  _cachedItems = null;
@@ -631023,6 +631754,7 @@ function buildTools(repoRoot, config, contextWindowSize, modelTier) {
631023
631754
  // Skill system (AIWG skills — discovery and execution)
631024
631755
  new SkillListTool(repoRoot),
631025
631756
  new SkillExecuteTool(repoRoot),
631757
+ new SkillExtractTool(repoRoot),
631026
631758
  // Transcription + media download tools (transcribe-cli / faster-whisper / yt-dlp)
631027
631759
  new TranscribeFileTool(repoRoot),
631028
631760
  new TranscribeUrlTool(repoRoot),
@@ -631828,6 +632560,16 @@ ${lines.join("\n")}
631828
632560
  if (taskType && modelTier !== "small") {
631829
632561
  dynamicContext += "\n\n" + buildTaskContext(taskType);
631830
632562
  }
632563
+ try {
632564
+ const skillPack = buildEphemeralSkillPack(discoverSkills(repoRoot), {
632565
+ task,
632566
+ modelTier
632567
+ });
632568
+ if (skillPack) dynamicContext += `
632569
+
632570
+ ${skillPack}`;
632571
+ } catch {
632572
+ }
631831
632573
  let persistedSelfModify = false;
631832
632574
  let persistedCommandsMode = "manual";
631833
632575
  try {
@@ -632219,6 +632961,47 @@ ${content}`,
632219
632961
  }
632220
632962
  );
632221
632963
  }
632964
+ const skillExtractTool = tools.find((t2) => t2.name === "skill_extract");
632965
+ if (skillExtractTool && "setExtractCallback" in skillExtractTool) {
632966
+ skillExtractTool.setExtractCallback(
632967
+ async (skillName, content, query, budgetTokens) => {
632968
+ if (!_agentToolRef)
632969
+ return "Skill extraction sub-agent unavailable — falling back to inline extraction.";
632970
+ const agentResult = await _agentToolRef.execute({
632971
+ prompt: [
632972
+ "You are a small-context skill extraction sub-agent.",
632973
+ "Read the full skill body below and return only the guidance needed for the parent task.",
632974
+ "Do not execute the user task. Do not copy the full skill. Do not include irrelevant sections.",
632975
+ `Budget: about ${budgetTokens} tokens.`,
632976
+ "",
632977
+ "Return this shape:",
632978
+ `Skill: ${skillName}`,
632979
+ "Use when: ...",
632980
+ "Actionable guidance:",
632981
+ "- ...",
632982
+ "Relevant commands/tools:",
632983
+ "- ...",
632984
+ "Output/checklist:",
632985
+ "- ...",
632986
+ "Risks/ignore:",
632987
+ "- ...",
632988
+ "Discard: current run only.",
632989
+ "",
632990
+ `<parent-query>
632991
+ ${query}
632992
+ </parent-query>`,
632993
+ "",
632994
+ `<skill-body name="${skillName}">
632995
+ ${content}
632996
+ </skill-body>`
632997
+ ].join("\n"),
632998
+ subagent_type: "general",
632999
+ description: `skill extract: ${skillName}`
633000
+ });
633001
+ return agentResult?.output ?? "Skill extraction sub-agent returned no output.";
633002
+ }
633003
+ );
633004
+ }
632222
633005
  const subAgentTool = tools.find((t2) => t2.name === "sub_agent");
632223
633006
  if (subAgentTool && "setCallbacks" in subAgentTool && statusBar) {
632224
633007
  subAgentTool.setCallbacks({
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.38",
3
+ "version": "1.0.40",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.38",
9
+ "version": "1.0.40",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.38",
3
+ "version": "1.0.40",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",