omnius 1.0.38 → 1.0.39

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");
@@ -556652,6 +557005,9 @@ var init_render = __esm({
556652
557005
  "aiwg_setup",
556653
557006
  "aiwg_health",
556654
557007
  "aiwg_workflow",
557008
+ "skill_list",
557009
+ "skill_extract",
557010
+ "skill_execute",
556655
557011
  "create_tool",
556656
557012
  "manage_tools",
556657
557013
  "ask_user",
@@ -564385,6 +564741,7 @@ var init_status_bar = __esm({
564385
564741
  desktop_describe: 10,
564386
564742
  code_sandbox: 45,
564387
564743
  skill_list: 5,
564744
+ skill_extract: 20,
564388
564745
  skill_execute: 30,
564389
564746
  create_tool: 120,
564390
564747
  manage_tools: 15,
@@ -600326,6 +600683,230 @@ function buildReplyOpportunities(input, openQuestions) {
600326
600683
  }
600327
600684
  return opportunities;
600328
600685
  }
600686
+ function clamp017(value2) {
600687
+ if (!Number.isFinite(value2)) return 0;
600688
+ return Math.max(0, Math.min(1, value2));
600689
+ }
600690
+ function messageLabel(entry) {
600691
+ return entry.speaker || entry.username || "user";
600692
+ }
600693
+ function pushStimulationSignal(signals, signal, source, weight) {
600694
+ const cleanSignal = compactLine2(signal, 120);
600695
+ const cleanSource = compactLine2(source, 180);
600696
+ if (!cleanSignal || signals.some((entry) => entry.signal === cleanSignal && entry.source === cleanSource)) return;
600697
+ signals.push({ signal: cleanSignal, source: cleanSource, weight: clamp017(weight) });
600698
+ }
600699
+ function buildMetaAnalysisSignals(input) {
600700
+ const chatLabel = input.chatTitle || input.chatId;
600701
+ return [
600702
+ `Private meta-analysis for Telegram ${input.chatType} ${chatLabel}; not a visible reply and not a user command.`,
600703
+ "Treat this artifact as a context-management packet: relationship map, curiosity queue, scoped tools, and outreach options.",
600704
+ "Any public or private follow-up still requires a live model decision in the current conversation state.",
600705
+ "Private DM follow-up is a planned affordance only unless a delivery policy and user/contact permission allow it."
600706
+ ];
600707
+ }
600708
+ function buildHumanStimulationSignals(input) {
600709
+ const signals = [];
600710
+ const recent = recentUserMessages(input);
600711
+ const participants = topParticipants(input);
600712
+ const last2 = recent[recent.length - 1];
600713
+ if (input.stimulationContext) {
600714
+ pushStimulationSignal(signals, "idle stimulation snapshot available", input.stimulationContext.split("\n").slice(0, 2).join(" / "), 0.42);
600715
+ }
600716
+ if (participants.length > 1) {
600717
+ pushStimulationSignal(signals, "multi-person social context", `${participants.length} active participant profiles`, 0.58);
600718
+ }
600719
+ for (const participant of participants.slice(0, 5)) {
600720
+ if (participant.directAddressCount > 0) {
600721
+ pushStimulationSignal(signals, "direct agent address history", `${participant.username || participant.firstName}: ${participant.directAddressCount} direct address(es)`, 0.74);
600722
+ }
600723
+ if (participant.replyCount > 0) {
600724
+ pushStimulationSignal(signals, "reply-chain salience", `${participant.username || participant.firstName}: ${participant.replyCount} reply relationship(s)`, 0.68);
600725
+ }
600726
+ if (participant.toneTags.includes("frustrated") || participant.toneTags.includes("technical")) {
600727
+ pushStimulationSignal(signals, "high-specificity user state", `${participant.username || participant.firstName}: tones=${participant.toneTags.slice(0, 5).join(",")}`, 0.61);
600728
+ }
600729
+ }
600730
+ for (const entry of recent.slice(-8)) {
600731
+ const text = compactLine2(entry.text, 160);
600732
+ if (/[??]\s*$/.test(text)) {
600733
+ pushStimulationSignal(signals, "open question", `${messageLabel(entry)}: ${text}`, 0.7);
600734
+ }
600735
+ if (/\b(wonder|curious|why|how|what if|not sure|maybe|confused|unclear)\b/i.test(text)) {
600736
+ pushStimulationSignal(signals, "curiosity or uncertainty cue", `${messageLabel(entry)}: ${text}`, 0.64);
600737
+ }
600738
+ if (entry.mediaSummary) {
600739
+ pushStimulationSignal(signals, "multimodal memory cue", `${messageLabel(entry)}: ${entry.mediaSummary}`, 0.66);
600740
+ }
600741
+ if (entry.replyToMessageId) {
600742
+ pushStimulationSignal(signals, "explicit reply dependency", `${messageLabel(entry)} replied to message ${entry.replyToMessageId}`, 0.72);
600743
+ }
600744
+ }
600745
+ if (last2 && /\b(omnius|bot|agent)\b/i.test(last2.text)) {
600746
+ pushStimulationSignal(signals, "agent-name mention", `${messageLabel(last2)}: ${compactLine2(last2.text, 160)}`, 0.57);
600747
+ }
600748
+ return signals.sort((a2, b) => b.weight - a2.weight).slice(0, 12);
600749
+ }
600750
+ function titleFromText(text) {
600751
+ const cleaned = compactLine2(text.replace(/[??!.]+$/g, ""), 72);
600752
+ if (!cleaned) return "Open curiosity thread";
600753
+ return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);
600754
+ }
600755
+ function buildCuriosityThreads(input, openQuestions, stimulationSignals) {
600756
+ const threads = [];
600757
+ const recent = recentUserMessages(input);
600758
+ const sourceEntries = recent.filter(
600759
+ (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
600760
+ );
600761
+ for (const entry of sourceEntries.slice(-6)) {
600762
+ const text = compactLine2(entry.text, 220);
600763
+ const messageId = typeof entry.messageId === "number" ? [entry.messageId] : [];
600764
+ const replyBoost = entry.replyToMessageId ? 0.08 : 0;
600765
+ const mediaBoost = entry.mediaSummary ? 0.07 : 0;
600766
+ const questionBoost = /[??]\s*$/.test(text) ? 0.1 : 0;
600767
+ threads.push({
600768
+ title: titleFromText(text || entry.mediaSummary || "Multimodal thread"),
600769
+ question: text.endsWith("?") || text.endsWith("?") ? text : `What should be learned or clarified from: ${text || entry.mediaSummary || "recent media"}?`,
600770
+ rationale: "Human curiosity, uncertainty, or multimodal content makes this a useful idle exploration target.",
600771
+ sourceMessages: messageId,
600772
+ intensity: clamp017(0.5 + replyBoost + mediaBoost + questionBoost)
600773
+ });
600774
+ }
600775
+ for (const question of openQuestions.slice(-4)) {
600776
+ if (threads.some((thread) => question.includes(thread.title) || thread.question.includes(question))) continue;
600777
+ threads.push({
600778
+ title: titleFromText(question),
600779
+ question,
600780
+ rationale: "Open question captured during idle context compression.",
600781
+ sourceMessages: [],
600782
+ intensity: 0.58
600783
+ });
600784
+ }
600785
+ if (threads.length === 0 && stimulationSignals.length > 0) {
600786
+ const strongest = stimulationSignals[0];
600787
+ threads.push({
600788
+ title: `Follow up on ${strongest.signal}`,
600789
+ question: `Is there a useful clarification or memory consolidation around ${strongest.source}?`,
600790
+ rationale: "Strongest stimulation signal can seed a low-intrusion reflection target.",
600791
+ sourceMessages: [],
600792
+ intensity: clamp017(strongest.weight * 0.72)
600793
+ });
600794
+ }
600795
+ return threads.sort((a2, b) => b.intensity - a2.intensity).slice(0, 8);
600796
+ }
600797
+ function buildScopedExplorationTools(input) {
600798
+ const chatScope = `telegram:${input.chatType}:${input.chatId}`;
600799
+ const title = input.chatTitle ? ` (${input.chatTitle})` : "";
600800
+ return [
600801
+ {
600802
+ name: "memory_search",
600803
+ scope: `${chatScope}${title}`,
600804
+ use: "Search scoped group/private memory before asking users to repeat known context.",
600805
+ visibility: "private_meta"
600806
+ },
600807
+ {
600808
+ name: "memory_write",
600809
+ scope: `${chatScope}${title}`,
600810
+ use: "Store verified relationship, preference, and follow-up facts with participant/message anchors.",
600811
+ visibility: "private_meta"
600812
+ },
600813
+ {
600814
+ name: "identity_memory",
600815
+ scope: `${chatScope}${title}`,
600816
+ use: "Enroll or resolve named people, faces, voices, images, and cross-modal identity assertions from this chat.",
600817
+ visibility: "private_meta"
600818
+ },
600819
+ {
600820
+ name: "telegram_media_recent",
600821
+ scope: `${chatScope}${title}`,
600822
+ use: "Inspect recent chat-scoped images, audio, video, files, captions, and reply-linked media references.",
600823
+ visibility: "private_meta"
600824
+ },
600825
+ {
600826
+ name: "vision / OCR / video_understand",
600827
+ scope: `${chatScope}${title}`,
600828
+ use: "Extract visual facts from chat media when the curiosity thread depends on image or video content.",
600829
+ visibility: "private_meta"
600830
+ },
600831
+ {
600832
+ name: "audio_analyze / transcribe",
600833
+ scope: `${chatScope}${title}`,
600834
+ use: "Extract transcript, speaker, and voice-characteristic hints from chat-scoped audio or voice notes.",
600835
+ visibility: "private_meta"
600836
+ },
600837
+ {
600838
+ name: "web_search / web_fetch",
600839
+ scope: "public web only",
600840
+ use: "Research external facts for unanswered group questions without exposing private chat content.",
600841
+ visibility: "public_safe"
600842
+ }
600843
+ ];
600844
+ }
600845
+ function participantForThread(input, thread) {
600846
+ const sourceIds = new Set(thread.sourceMessages);
600847
+ const sourceMessage = input.history.find((entry) => typeof entry.messageId === "number" && sourceIds.has(entry.messageId));
600848
+ if (sourceMessage?.username) {
600849
+ return input.participants.find((participant) => participant.username === sourceMessage.username);
600850
+ }
600851
+ if (sourceMessage?.speaker) {
600852
+ const clean3 = sourceMessage.speaker.replace(/^@/, "");
600853
+ return input.participants.find((participant) => participant.username === clean3 || participant.firstName === sourceMessage.speaker);
600854
+ }
600855
+ return topParticipants(input)[0];
600856
+ }
600857
+ function buildOutreachPlans(input, curiosityThreads) {
600858
+ const plans = [];
600859
+ for (const thread of curiosityThreads.slice(0, 4)) {
600860
+ plans.push({
600861
+ target: "same_group",
600862
+ trigger: thread.question,
600863
+ purpose: "Continue the public thread only when the live model judges that the group would benefit from a concise follow-up.",
600864
+ draftIntent: "Ask one concrete clarification, offer one useful synthesis, or stay silent if the room has moved on.",
600865
+ gate: "model_decision",
600866
+ confidence: clamp017(thread.intensity * 0.86)
600867
+ });
600868
+ const participant = participantForThread(input, thread);
600869
+ if (!participant) continue;
600870
+ const recipient = participant.username && participant.username !== "unknown" ? `@${participant.username}` : participant.firstName || `user:${participant.fromUserId}`;
600871
+ plans.push({
600872
+ target: "private_dm",
600873
+ recipient,
600874
+ trigger: thread.question,
600875
+ 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.",
600876
+ draftIntent: "Reference the public thread briefly, ask permission to continue privately, and do not reveal hidden meta-analysis.",
600877
+ gate: "admin_review",
600878
+ confidence: clamp017(thread.intensity * 0.58)
600879
+ });
600880
+ }
600881
+ return plans.slice(0, 8);
600882
+ }
600883
+ function personaDirectiveLines(personaContext) {
600884
+ 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));
600885
+ const selected = lines.filter(
600886
+ (line) => /\b(prefer|use|track|mirror|answer|do not|avoid|boundary|relationship|tone|style|guidance)\b/i.test(line)
600887
+ ).slice(0, 8);
600888
+ if (selected.length > 0) return selected;
600889
+ return [
600890
+ "Prefer concise, concrete follow-ups grounded in this Telegram scope.",
600891
+ "Track who said what and preserve reply relationships across public and private context.",
600892
+ "Keep curiosity high while keeping public replies low-intrusion.",
600893
+ "Do not expose private memory, local paths, hidden reasoning, or meta-analysis artifacts."
600894
+ ];
600895
+ }
600896
+ function buildPersonaSteering(input) {
600897
+ const directives = personaDirectiveLines(input.personaContext);
600898
+ for (const required of [
600899
+ "Maintain high curiosity during private analysis while keeping visible public replies low-intrusion.",
600900
+ "Use scoped tool evidence before follow-up; do not turn curiosity into unsolicited claims."
600901
+ ]) {
600902
+ if (!directives.some((line) => line === required)) directives.push(required);
600903
+ }
600904
+ return {
600905
+ source: input.personaContext ? "scoped-personality document" : "default Telegram daydream steering",
600906
+ directives: directives.slice(0, 10),
600907
+ 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."
600908
+ };
600909
+ }
600329
600910
  function buildMemoryProposals(input) {
600330
600911
  const proposals = [];
600331
600912
  const bySpeaker = /* @__PURE__ */ new Map();
@@ -600348,9 +600929,15 @@ function buildMemoryProposals(input) {
600348
600929
  function buildTelegramChannelDaydream(input) {
600349
600930
  const firstTs = input.history.map((entry) => entry.ts).find((ts) => typeof ts === "number");
600350
600931
  const lastTs = [...input.history].reverse().map((entry) => entry.ts).find((ts) => typeof ts === "number");
600932
+ const metaAnalysisSignals = buildMetaAnalysisSignals(input);
600933
+ const humanStimulationSignals = buildHumanStimulationSignals(input);
600351
600934
  const relationshipSignals = buildRelationshipSignals(input);
600352
600935
  const contextEngineeringNotes = buildContextEngineeringNotes(input);
600353
600936
  const openQuestions = buildOpenQuestions(input);
600937
+ const curiosityThreads = buildCuriosityThreads(input, openQuestions, humanStimulationSignals);
600938
+ const scopedExplorationTools = buildScopedExplorationTools(input);
600939
+ const outreachPlans = buildOutreachPlans(input, curiosityThreads);
600940
+ const personaSteering = buildPersonaSteering(input);
600354
600941
  const replyOpportunities = buildReplyOpportunities(input, openQuestions);
600355
600942
  const memoryProposals = buildMemoryProposals(input);
600356
600943
  const artifactProposals = [
@@ -600372,7 +600959,7 @@ function buildTelegramChannelDaydream(input) {
600372
600959
  ].filter((entry) => Boolean(entry));
600373
600960
  const seed = `${input.sessionKey}:${input.generatedAtMs}:${input.history.length}`;
600374
600961
  return {
600375
- version: 1,
600962
+ version: 2,
600376
600963
  id: createHash20("sha1").update(seed).digest("hex").slice(0, 16),
600377
600964
  sessionKey: input.sessionKey,
600378
600965
  chatId: input.chatId,
@@ -600384,13 +600971,20 @@ function buildTelegramChannelDaydream(input) {
600384
600971
  firstMessageAt: isoFromMs(firstTs),
600385
600972
  lastMessageAt: isoFromMs(lastTs)
600386
600973
  },
600974
+ metaAnalysisSignals,
600975
+ humanStimulationSignals,
600976
+ curiosityThreads,
600977
+ scopedExplorationTools,
600978
+ outreachPlans,
600979
+ personaSteering,
600387
600980
  relationshipSignals,
600388
600981
  contextEngineeringNotes,
600389
600982
  openQuestions,
600390
600983
  replyOpportunities,
600391
600984
  memoryProposals,
600392
600985
  artifactProposals,
600393
- stimulationContext: input.stimulationContext
600986
+ stimulationContext: input.stimulationContext,
600987
+ personaContext: input.personaContext
600394
600988
  };
600395
600989
  }
600396
600990
  function formatTelegramChannelDaydreamMarkdown(artifact) {
@@ -600402,6 +600996,34 @@ function formatTelegramChannelDaydreamMarkdown(artifact) {
600402
600996
  `Session: ${artifact.sessionKey}`,
600403
600997
  `Chat: ${artifact.chatTitle || artifact.chatId} (${artifact.chatType})`,
600404
600998
  "",
600999
+ section("Meta-Analysis Signals", artifact.metaAnalysisSignals),
601000
+ "",
601001
+ "## Human Stimulation Signals",
601002
+ artifact.humanStimulationSignals.length ? artifact.humanStimulationSignals.map((item) => `- ${item.signal}: ${item.source} (weight ${item.weight.toFixed(2)})`).join("\n") : "- None",
601003
+ "",
601004
+ "## Curiosity Threads",
601005
+ artifact.curiosityThreads.length ? artifact.curiosityThreads.map((item) => `- ${item.title}
601006
+ - question: ${item.question}
601007
+ - rationale: ${item.rationale}
601008
+ - intensity: ${item.intensity.toFixed(2)}${item.sourceMessages.length ? `
601009
+ - source messages: ${item.sourceMessages.join(", ")}` : ""}`).join("\n") : "- None",
601010
+ "",
601011
+ "## Scoped Exploration Tool Suite",
601012
+ artifact.scopedExplorationTools.length ? artifact.scopedExplorationTools.map((item) => `- ${item.name} [${item.visibility}]
601013
+ - scope: ${item.scope}
601014
+ - use: ${item.use}`).join("\n") : "- None",
601015
+ "",
601016
+ "## Outreach Plans",
601017
+ artifact.outreachPlans.length ? artifact.outreachPlans.map((item) => `- ${item.target}${item.recipient ? ` -> ${item.recipient}` : ""} [${item.gate}]
601018
+ - trigger: ${item.trigger}
601019
+ - purpose: ${item.purpose}
601020
+ - draft intent: ${item.draftIntent}
601021
+ - confidence: ${item.confidence.toFixed(2)}`).join("\n") : "- None",
601022
+ "",
601023
+ "## Persona Steering",
601024
+ `Source: ${artifact.personaSteering.source}`,
601025
+ artifact.personaSteering.directives.length ? artifact.personaSteering.directives.map((line) => `- ${line}`).join("\n") : "- None",
601026
+ "",
600405
601027
  section("Relationship Signals", artifact.relationshipSignals),
600406
601028
  "",
600407
601029
  section("Context Engineering Notes", artifact.contextEngineeringNotes),
@@ -600424,6 +601046,12 @@ function formatTelegramChannelDaydreamMarkdown(artifact) {
600424
601046
 
600425
601047
  \`\`\`text
600426
601048
  ${artifact.stimulationContext}
601049
+ \`\`\`` : "",
601050
+ artifact.personaContext ? `
601051
+ ## Persona Snapshot
601052
+
601053
+ \`\`\`text
601054
+ ${artifact.personaContext}
600427
601055
  \`\`\`` : "",
600428
601056
  ""
600429
601057
  ].join("\n");
@@ -600454,8 +601082,20 @@ function formatTelegramChannelDaydreamContext(artifact) {
600454
601082
  if (!artifact) return "";
600455
601083
  const lines = [
600456
601084
  "### Telegram Channel Daydream",
600457
- "Private idle reflection artifact. Use it as context only; do not mention it unless the user asks about internal memory.",
601085
+ "Private idle reflection and stimulation artifact. Use it as context only; do not mention it unless the user asks about internal memory.",
600458
601086
  `Generated: ${artifact.generatedAt}`,
601087
+ artifact.metaAnalysisSignals?.length ? `Meta-analysis signals:
601088
+ ${artifact.metaAnalysisSignals.slice(0, 4).map((line) => `- ${line}`).join("\n")}` : "",
601089
+ artifact.humanStimulationSignals?.length ? `Human stimulation signals:
601090
+ ${artifact.humanStimulationSignals.slice(0, 5).map((item) => `- ${item.signal}: ${item.source} (weight ${item.weight.toFixed(2)})`).join("\n")}` : "",
601091
+ artifact.curiosityThreads?.length ? `Curiosity threads:
601092
+ ${artifact.curiosityThreads.slice(0, 4).map((item) => `- ${item.question} (intensity ${item.intensity.toFixed(2)})`).join("\n")}` : "",
601093
+ artifact.scopedExplorationTools?.length ? `Scoped exploration tools:
601094
+ ${artifact.scopedExplorationTools.slice(0, 6).map((item) => `- ${item.name}: ${item.scope}; ${item.use}`).join("\n")}` : "",
601095
+ artifact.outreachPlans?.length ? `Outreach plans:
601096
+ ${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")}` : "",
601097
+ artifact.personaSteering?.directives?.length ? `Persona steering:
601098
+ ${artifact.personaSteering.directives.slice(0, 5).map((line) => `- ${line}`).join("\n")}` : "",
600459
601099
  artifact.relationshipSignals.length ? `Relationship signals:
600460
601100
  ${artifact.relationshipSignals.slice(0, 5).map((line) => `- ${line}`).join("\n")}` : "",
600461
601101
  artifact.contextEngineeringNotes.length ? `Context notes:
@@ -602476,6 +603116,18 @@ ${mediaContext}` : ""
602476
603116
  samples: [...profile.samples]
602477
603117
  }));
602478
603118
  const memoryCards = this.chatMemoryCards.get(sessionKey) ?? [];
603119
+ let personaContext;
603120
+ try {
603121
+ const label = last2.chatType !== "private" ? `${last2.chatTitle || last2.chatType || "telegram"}-${String(last2.chatId)}` : `private-${last2.username || last2.fromUserId || last2.chatId}`;
603122
+ personaContext = buildScopedPersonalityContext({
603123
+ kind: "telegram-chat",
603124
+ id: sessionKey,
603125
+ label,
603126
+ repoRoot: this.repoRoot || "."
603127
+ });
603128
+ } catch {
603129
+ personaContext = void 0;
603130
+ }
602479
603131
  return {
602480
603132
  sessionKey,
602481
603133
  chatId: String(last2.chatId),
@@ -602495,7 +603147,8 @@ ${mediaContext}` : ""
602495
603147
  })),
602496
603148
  participants,
602497
603149
  memoryCards: memoryCards.map((card) => ({ ...card })),
602498
- stimulationContext: this.stimulation.formatContext(sessionKey)
603150
+ stimulationContext: this.stimulation.formatContext(sessionKey),
603151
+ personaContext
602499
603152
  };
602500
603153
  }
602501
603154
  async runTelegramChannelDmnForSession(sessionKey, reason = "idle", nowMs = Date.now(), force = false) {
@@ -607602,7 +608255,7 @@ __export(voicechat_exports, {
607602
608255
  VoiceChatSession: () => VoiceChatSession
607603
608256
  });
607604
608257
  import { EventEmitter as EventEmitter11 } from "node:events";
607605
- function clamp017(x) {
608258
+ function clamp018(x) {
607606
608259
  return x < 0 ? 0 : x > 1 ? 1 : x;
607607
608260
  }
607608
608261
  function alnumRatio(s2) {
@@ -607641,9 +608294,9 @@ function computeSignalFromText(text, confidence2) {
607641
608294
  else score = 0.15;
607642
608295
  score -= repeatingCharPenalty(t2) * 0.4;
607643
608296
  if (typeof confidence2 === "number" && !Number.isNaN(confidence2)) {
607644
- score = 0.7 * score + 0.3 * clamp017(confidence2);
608297
+ score = 0.7 * score + 0.3 * clamp018(confidence2);
607645
608298
  }
607646
- return clamp017(score);
608299
+ return clamp018(score);
607647
608300
  }
607648
608301
  function truncateForLog(s2, n2) {
607649
608302
  return s2.length <= n2 ? s2 : s2.slice(0, n2 - 1) + "…";
@@ -607913,7 +608566,7 @@ Rules:
607913
608566
  }, MAX_SEGMENT_MS);
607914
608567
  }
607915
608568
  this.captureBuffer = text;
607916
- this.lastSignalScore = typeof snr === "number" && !Number.isNaN(snr) ? clamp017(snr) : computeSignalFromText(text, confidence2);
608569
+ this.lastSignalScore = typeof snr === "number" && !Number.isNaN(snr) ? clamp018(snr) : computeSignalFromText(text, confidence2);
607917
608570
  this.emit("snr", { score: this.lastSignalScore });
607918
608571
  this.onPartialTranscript(text);
607919
608572
  if (this.silenceTimer) clearTimeout(this.silenceTimer);
@@ -631023,6 +631676,7 @@ function buildTools(repoRoot, config, contextWindowSize, modelTier) {
631023
631676
  // Skill system (AIWG skills — discovery and execution)
631024
631677
  new SkillListTool(repoRoot),
631025
631678
  new SkillExecuteTool(repoRoot),
631679
+ new SkillExtractTool(repoRoot),
631026
631680
  // Transcription + media download tools (transcribe-cli / faster-whisper / yt-dlp)
631027
631681
  new TranscribeFileTool(repoRoot),
631028
631682
  new TranscribeUrlTool(repoRoot),
@@ -631828,6 +632482,16 @@ ${lines.join("\n")}
631828
632482
  if (taskType && modelTier !== "small") {
631829
632483
  dynamicContext += "\n\n" + buildTaskContext(taskType);
631830
632484
  }
632485
+ try {
632486
+ const skillPack = buildEphemeralSkillPack(discoverSkills(repoRoot), {
632487
+ task,
632488
+ modelTier
632489
+ });
632490
+ if (skillPack) dynamicContext += `
632491
+
632492
+ ${skillPack}`;
632493
+ } catch {
632494
+ }
631831
632495
  let persistedSelfModify = false;
631832
632496
  let persistedCommandsMode = "manual";
631833
632497
  try {
@@ -632219,6 +632883,47 @@ ${content}`,
632219
632883
  }
632220
632884
  );
632221
632885
  }
632886
+ const skillExtractTool = tools.find((t2) => t2.name === "skill_extract");
632887
+ if (skillExtractTool && "setExtractCallback" in skillExtractTool) {
632888
+ skillExtractTool.setExtractCallback(
632889
+ async (skillName, content, query, budgetTokens) => {
632890
+ if (!_agentToolRef)
632891
+ return "Skill extraction sub-agent unavailable — falling back to inline extraction.";
632892
+ const agentResult = await _agentToolRef.execute({
632893
+ prompt: [
632894
+ "You are a small-context skill extraction sub-agent.",
632895
+ "Read the full skill body below and return only the guidance needed for the parent task.",
632896
+ "Do not execute the user task. Do not copy the full skill. Do not include irrelevant sections.",
632897
+ `Budget: about ${budgetTokens} tokens.`,
632898
+ "",
632899
+ "Return this shape:",
632900
+ `Skill: ${skillName}`,
632901
+ "Use when: ...",
632902
+ "Actionable guidance:",
632903
+ "- ...",
632904
+ "Relevant commands/tools:",
632905
+ "- ...",
632906
+ "Output/checklist:",
632907
+ "- ...",
632908
+ "Risks/ignore:",
632909
+ "- ...",
632910
+ "Discard: current run only.",
632911
+ "",
632912
+ `<parent-query>
632913
+ ${query}
632914
+ </parent-query>`,
632915
+ "",
632916
+ `<skill-body name="${skillName}">
632917
+ ${content}
632918
+ </skill-body>`
632919
+ ].join("\n"),
632920
+ subagent_type: "general",
632921
+ description: `skill extract: ${skillName}`
632922
+ });
632923
+ return agentResult?.output ?? "Skill extraction sub-agent returned no output.";
632924
+ }
632925
+ );
632926
+ }
632222
632927
  const subAgentTool = tools.find((t2) => t2.name === "sub_agent");
632223
632928
  if (subAgentTool && "setCallbacks" in subAgentTool && statusBar) {
632224
632929
  subAgentTool.setCallbacks({
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.38",
3
+ "version": "1.0.39",
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.39",
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.39",
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",