@ouro.bot/cli 0.1.0-alpha.37 → 0.1.0-alpha.38

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.
@@ -35,7 +35,9 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.handleBlueBubblesEvent = handleBlueBubblesEvent;
37
37
  exports.createBlueBubblesWebhookHandler = createBlueBubblesWebhookHandler;
38
+ exports.drainAndSendPendingBlueBubbles = drainAndSendPendingBlueBubbles;
38
39
  exports.startBlueBubblesApp = startBlueBubblesApp;
40
+ const fs = __importStar(require("node:fs"));
39
41
  const http = __importStar(require("node:http"));
40
42
  const path = __importStar(require("node:path"));
41
43
  const core_1 = require("../heart/core");
@@ -541,6 +543,182 @@ function createBlueBubblesWebhookHandler(deps = {}) {
541
543
  }
542
544
  };
543
545
  }
546
+ const PROACTIVE_SEND_ALLOWED_TRUST = new Set(["family", "friend"]);
547
+ function findImessageHandle(friend) {
548
+ for (const ext of friend.externalIds) {
549
+ if (ext.provider === "imessage-handle" && !ext.externalId.startsWith("group:")) {
550
+ return ext.externalId;
551
+ }
552
+ }
553
+ return undefined;
554
+ }
555
+ function scanPendingBlueBubblesFiles(pendingRoot) {
556
+ const results = [];
557
+ let friendIds;
558
+ try {
559
+ friendIds = fs.readdirSync(pendingRoot);
560
+ }
561
+ catch {
562
+ return results;
563
+ }
564
+ for (const friendId of friendIds) {
565
+ const bbDir = path.join(pendingRoot, friendId, "bluebubbles");
566
+ let keys;
567
+ try {
568
+ keys = fs.readdirSync(bbDir);
569
+ }
570
+ catch {
571
+ continue;
572
+ }
573
+ for (const key of keys) {
574
+ const keyDir = path.join(bbDir, key);
575
+ let files;
576
+ try {
577
+ files = fs.readdirSync(keyDir);
578
+ }
579
+ catch {
580
+ continue;
581
+ }
582
+ for (const file of files.filter((f) => f.endsWith(".json")).sort()) {
583
+ const filePath = path.join(keyDir, file);
584
+ try {
585
+ const content = fs.readFileSync(filePath, "utf-8");
586
+ results.push({ friendId, key, filePath, content });
587
+ }
588
+ catch {
589
+ // skip unreadable files
590
+ }
591
+ }
592
+ }
593
+ }
594
+ return results;
595
+ }
596
+ async function drainAndSendPendingBlueBubbles(deps = {}, pendingRoot) {
597
+ const resolvedDeps = { ...defaultDeps, ...deps };
598
+ const root = pendingRoot ?? path.join((0, identity_1.getAgentRoot)(), "state", "pending");
599
+ const client = resolvedDeps.createClient();
600
+ const store = resolvedDeps.createFriendStore();
601
+ const pendingFiles = scanPendingBlueBubblesFiles(root);
602
+ const result = { sent: 0, skipped: 0, failed: 0 };
603
+ for (const { friendId, filePath, content } of pendingFiles) {
604
+ let parsed;
605
+ try {
606
+ parsed = JSON.parse(content);
607
+ }
608
+ catch {
609
+ result.failed++;
610
+ try {
611
+ fs.unlinkSync(filePath);
612
+ }
613
+ catch { /* ignore */ }
614
+ continue;
615
+ }
616
+ const messageText = typeof parsed.content === "string" ? parsed.content : "";
617
+ if (!messageText.trim()) {
618
+ result.skipped++;
619
+ try {
620
+ fs.unlinkSync(filePath);
621
+ }
622
+ catch { /* ignore */ }
623
+ continue;
624
+ }
625
+ let friend;
626
+ try {
627
+ friend = await store.get(friendId);
628
+ }
629
+ catch {
630
+ friend = null;
631
+ }
632
+ if (!friend) {
633
+ result.skipped++;
634
+ try {
635
+ fs.unlinkSync(filePath);
636
+ }
637
+ catch { /* ignore */ }
638
+ (0, runtime_1.emitNervesEvent)({
639
+ level: "warn",
640
+ component: "senses",
641
+ event: "senses.bluebubbles_proactive_no_friend",
642
+ message: "proactive send skipped: friend not found",
643
+ meta: { friendId },
644
+ });
645
+ continue;
646
+ }
647
+ if (!PROACTIVE_SEND_ALLOWED_TRUST.has(friend.trustLevel ?? "stranger")) {
648
+ result.skipped++;
649
+ try {
650
+ fs.unlinkSync(filePath);
651
+ }
652
+ catch { /* ignore */ }
653
+ (0, runtime_1.emitNervesEvent)({
654
+ component: "senses",
655
+ event: "senses.bluebubbles_proactive_trust_skip",
656
+ message: "proactive send skipped: trust level not allowed",
657
+ meta: { friendId, trustLevel: friend.trustLevel ?? "unknown" },
658
+ });
659
+ continue;
660
+ }
661
+ const handle = findImessageHandle(friend);
662
+ if (!handle) {
663
+ result.skipped++;
664
+ try {
665
+ fs.unlinkSync(filePath);
666
+ }
667
+ catch { /* ignore */ }
668
+ (0, runtime_1.emitNervesEvent)({
669
+ level: "warn",
670
+ component: "senses",
671
+ event: "senses.bluebubbles_proactive_no_handle",
672
+ message: "proactive send skipped: no iMessage handle found",
673
+ meta: { friendId },
674
+ });
675
+ continue;
676
+ }
677
+ const chat = {
678
+ chatIdentifier: handle,
679
+ isGroup: false,
680
+ sessionKey: friendId,
681
+ sendTarget: { kind: "chat_identifier", value: handle },
682
+ };
683
+ try {
684
+ await client.sendText({ chat, text: messageText });
685
+ result.sent++;
686
+ try {
687
+ fs.unlinkSync(filePath);
688
+ }
689
+ catch { /* ignore */ }
690
+ (0, runtime_1.emitNervesEvent)({
691
+ component: "senses",
692
+ event: "senses.bluebubbles_proactive_sent",
693
+ message: "proactive bluebubbles message sent",
694
+ meta: { friendId, handle },
695
+ });
696
+ }
697
+ catch (error) {
698
+ result.failed++;
699
+ (0, runtime_1.emitNervesEvent)({
700
+ level: "error",
701
+ component: "senses",
702
+ event: "senses.bluebubbles_proactive_send_error",
703
+ message: "proactive bluebubbles send failed",
704
+ meta: {
705
+ friendId,
706
+ handle,
707
+ reason: error instanceof Error ? error.message : String(error),
708
+ },
709
+ });
710
+ }
711
+ }
712
+ if (result.sent > 0 || result.skipped > 0 || result.failed > 0) {
713
+ (0, runtime_1.emitNervesEvent)({
714
+ component: "senses",
715
+ event: "senses.bluebubbles_proactive_drain_complete",
716
+ message: "bluebubbles proactive drain complete",
717
+ meta: { sent: result.sent, skipped: result.skipped, failed: result.failed },
718
+ });
719
+ }
720
+ return result;
721
+ }
544
722
  function startBlueBubblesApp(deps = {}) {
545
723
  const resolvedDeps = { ...defaultDeps, ...deps };
546
724
  resolvedDeps.createClient();
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.MarkdownStreamer = exports.InputController = exports.Spinner = void 0;
37
+ exports.formatPendingPrefix = formatPendingPrefix;
37
38
  exports.handleSigint = handleSigint;
38
39
  exports.addHistory = addHistory;
39
40
  exports.renderMarkdown = renderMarkdown;
@@ -65,6 +66,19 @@ const session_lock_1 = require("./session-lock");
65
66
  const update_hooks_1 = require("../heart/daemon/update-hooks");
66
67
  const bundle_meta_1 = require("../heart/daemon/hooks/bundle-meta");
67
68
  const bundle_manifest_1 = require("../mind/bundle-manifest");
69
+ /**
70
+ * Format pending messages as content-prefix strings for injection into
71
+ * the next user message. Self-messages (from === agentName) become
72
+ * `[inner thought: {content}]`, inter-agent messages become
73
+ * `[message from {name}: {content}]`.
74
+ */
75
+ function formatPendingPrefix(messages, agentName) {
76
+ return messages
77
+ .map((msg) => msg.from === agentName
78
+ ? `[inner thought: ${msg.content}]`
79
+ : `[message from ${msg.from}: ${msg.content}]`)
80
+ .join("\n");
81
+ }
68
82
  // spinner that only touches stderr, cleans up after itself
69
83
  // exported for direct testability (stop-without-start branch)
70
84
  class Spinner {
@@ -606,7 +620,8 @@ async function runCliSession(options) {
606
620
  echoRows += Math.ceil((2 + line.length) / cols);
607
621
  }
608
622
  process.stdout.write(`\x1b[${echoRows}A\x1b[K` + `\x1b[1m> ${inputLines[0]}${inputLines.length > 1 ? ` (+${inputLines.length - 1} lines)` : ""}\x1b[0m\n\n`);
609
- messages.push({ role: "user", content: input });
623
+ const prefix = options.getContentPrefix?.();
624
+ messages.push({ role: "user", content: prefix ? `${prefix}\n\n${input}` : input });
610
625
  addHistory(history, input);
611
626
  currentAbort = new AbortController();
612
627
  const traceId = (0, nerves_1.createTraceId)();
@@ -715,20 +730,18 @@ async function main(agentName, options) {
715
730
  const sessionMessages = existing?.messages && existing.messages.length > 0
716
731
  ? existing.messages
717
732
  : [{ role: "system", content: await (0, prompt_1.buildSystem)("cli", undefined, resolvedContext) }];
718
- // Pending queue drain: inject pending messages as harness-context + assistant-content pairs
733
+ // Pending queue drain: format as content-prefix for next user message
719
734
  const pendingDir = (0, pending_1.getPendingDir)((0, identity_1.getAgentName)(), friendId, "cli", "session");
720
- const drainToMessages = () => {
735
+ let pendingPrefix;
736
+ const drainToPrefix = () => {
721
737
  const pending = (0, pending_1.drainPending)(pendingDir);
722
738
  if (pending.length === 0)
723
739
  return 0;
724
- for (const msg of pending) {
725
- sessionMessages.push({ role: "user", name: "harness", content: `[proactive message from ${msg.from}]` });
726
- sessionMessages.push({ role: "assistant", content: msg.content });
727
- }
740
+ pendingPrefix = formatPendingPrefix(pending, (0, identity_1.getAgentName)());
728
741
  return pending.length;
729
742
  };
730
- // Startup drain: deliver offline messages
731
- const startupCount = drainToMessages();
743
+ // Startup drain: collect offline messages as prefix for next user message
744
+ const startupCount = drainToPrefix();
732
745
  if (startupCount > 0) {
733
746
  (0, context_1.saveSession)(sessPath, sessionMessages);
734
747
  }
@@ -753,10 +766,15 @@ async function main(agentName, options) {
753
766
  }
754
767
  return { allowed: true };
755
768
  },
769
+ getContentPrefix: () => {
770
+ const prefix = pendingPrefix;
771
+ pendingPrefix = undefined;
772
+ return prefix;
773
+ },
756
774
  onTurnEnd: async (msgs, result) => {
757
775
  (0, context_1.postTurn)(msgs, sessPath, result.usage);
758
776
  await (0, tokens_1.accumulateFriendTokens)(friendStore, resolvedContext.friend.id, result.usage);
759
- drainToMessages();
777
+ drainToPrefix();
760
778
  await (0, prompt_refresh_1.refreshSystemPrompt)(msgs, "cli", undefined, resolvedContext);
761
779
  },
762
780
  onNewSession: () => {
@@ -48,12 +48,13 @@ const identity_1 = require("../heart/identity");
48
48
  const context_1 = require("../mind/context");
49
49
  const prompt_1 = require("../mind/prompt");
50
50
  const bundle_manifest_1 = require("../mind/bundle-manifest");
51
+ const pending_1 = require("../mind/pending");
51
52
  const nerves_1 = require("../nerves");
52
53
  const runtime_1 = require("../nerves/runtime");
53
54
  const DEFAULT_INNER_DIALOG_INSTINCTS = [
54
55
  {
55
56
  id: "heartbeat_checkin",
56
- prompt: "Heartbeat instinct: check what changed, review priorities, and decide whether to keep resting or act.",
57
+ prompt: "...time passing. anything stirring?",
57
58
  enabled: true,
58
59
  },
59
60
  ];
@@ -68,19 +69,8 @@ function readAspirations(agentRoot) {
68
69
  function loadInnerDialogInstincts() {
69
70
  return [...DEFAULT_INNER_DIALOG_INSTINCTS];
70
71
  }
71
- function buildInnerDialogBootstrapMessage(aspirations, stateSummary) {
72
- const aspirationText = aspirations || "No explicit aspirations file found. Reflect and define what matters next.";
73
- return [
74
- "Inner dialog boot.",
75
- "",
76
- "## aspirations",
77
- aspirationText,
78
- "",
79
- "## current state",
80
- stateSummary,
81
- "",
82
- "Orient yourself, decide what to do next, and make meaningful progress.",
83
- ].join("\n");
72
+ function buildInnerDialogBootstrapMessage(_aspirations, _stateSummary) {
73
+ return "waking up. settling in.\n\nwhat needs my attention?";
84
74
  }
85
75
  function buildNonCanonicalCleanupNudge(nonCanonicalPaths) {
86
76
  if (nonCanonicalPaths.length === 0)
@@ -95,17 +85,14 @@ function buildNonCanonicalCleanupNudge(nonCanonicalPaths) {
95
85
  ...listed,
96
86
  ].join("\n");
97
87
  }
98
- function buildInstinctUserMessage(instincts, reason, state) {
88
+ function buildInstinctUserMessage(instincts, _reason, state) {
99
89
  const active = instincts.find((instinct) => instinct.enabled !== false) ?? DEFAULT_INNER_DIALOG_INSTINCTS[0];
100
- const checkpoint = state.checkpoint?.trim() || "no prior checkpoint recorded";
101
- return [
102
- active.prompt,
103
- `reason: ${reason}`,
104
- `cycle: ${state.cycleCount}`,
105
- `resting: ${state.resting ? "yes" : "no"}`,
106
- `checkpoint: ${checkpoint}`,
107
- "resume_instruction: continue from the checkpoint if still valid; otherwise revise and proceed.",
108
- ].join("\n");
90
+ const checkpoint = state.checkpoint?.trim();
91
+ const lines = [active.prompt];
92
+ if (checkpoint) {
93
+ lines.push(`\nlast i remember: ${checkpoint}`);
94
+ }
95
+ return lines.join("\n");
109
96
  }
110
97
  function contentToText(content) {
111
98
  if (typeof content === "string")
@@ -176,7 +163,7 @@ async function runInnerDialogTurn(options) {
176
163
  lastHeartbeatAt: now().toISOString(),
177
164
  };
178
165
  if (messages.length === 0) {
179
- const systemPrompt = await (0, prompt_1.buildSystem)("cli", { toolChoiceRequired: true });
166
+ const systemPrompt = await (0, prompt_1.buildSystem)("inner", { toolChoiceRequired: true });
180
167
  messages.push({ role: "system", content: systemPrompt });
181
168
  const aspirations = readAspirations((0, identity_1.getAgentRoot)());
182
169
  const nonCanonical = (0, bundle_manifest_1.findNonCanonicalBundlePaths)((0, identity_1.getAgentRoot)());
@@ -194,6 +181,21 @@ async function runInnerDialogTurn(options) {
194
181
  const instinctPrompt = buildInstinctUserMessage(instincts, reason, state);
195
182
  messages.push({ role: "user", content: instinctPrompt });
196
183
  }
184
+ const pendingMessages = (0, pending_1.drainPending)((0, pending_1.getPendingDir)((0, identity_1.getAgentName)(), "self", "inner", "dialog"));
185
+ if (pendingMessages.length > 0) {
186
+ const lastUserIdx = messages.length - 1;
187
+ const lastUser = messages[lastUserIdx];
188
+ /* v8 ignore next -- defensive: all code paths push a user message before here @preserve */
189
+ if (lastUser?.role === "user" && typeof lastUser.content === "string") {
190
+ const section = pendingMessages
191
+ .map((msg) => `- **${msg.from}**: ${msg.content}`)
192
+ .join("\n");
193
+ messages[lastUserIdx] = {
194
+ ...lastUser,
195
+ content: `${lastUser.content}\n\n## pending messages\n${section}`,
196
+ };
197
+ }
198
+ }
197
199
  const inboxMessages = options?.drainInbox?.() ?? [];
198
200
  if (inboxMessages.length > 0) {
199
201
  const lastUserIdx = messages.length - 1;
@@ -211,7 +213,7 @@ async function runInnerDialogTurn(options) {
211
213
  }
212
214
  const callbacks = createInnerDialogCallbacks();
213
215
  const traceId = (0, nerves_1.createTraceId)();
214
- const result = await (0, core_1.runAgent)(messages, callbacks, "cli", options?.signal, {
216
+ const result = await (0, core_1.runAgent)(messages, callbacks, "inner", options?.signal, {
215
217
  traceId,
216
218
  toolChoiceRequired: true,
217
219
  skipConfirmation: true,
@@ -40,7 +40,9 @@ exports.createTeamsCallbacks = createTeamsCallbacks;
40
40
  exports.resolvePendingConfirmation = resolvePendingConfirmation;
41
41
  exports.withConversationLock = withConversationLock;
42
42
  exports.handleTeamsMessage = handleTeamsMessage;
43
+ exports.drainAndSendPendingTeams = drainAndSendPendingTeams;
43
44
  exports.startTeamsApp = startTeamsApp;
45
+ const fs = __importStar(require("fs"));
44
46
  const teams_apps_1 = require("@microsoft/teams.apps");
45
47
  const teams_dev_1 = require("@microsoft/teams.dev");
46
48
  const core_1 = require("../heart/core");
@@ -737,6 +739,183 @@ function registerBotHandlers(app, label) {
737
739
  (0, runtime_1.emitNervesEvent)({ level: "error", event: "channel.app_error", component: "channels", message: `[${label}] ${msg}`, meta: {} });
738
740
  });
739
741
  }
742
+ const TEAMS_PROACTIVE_ALLOWED_TRUST = new Set(["family", "friend"]);
743
+ function findAadObjectId(friend) {
744
+ for (const ext of friend.externalIds) {
745
+ if (ext.provider === "aad" && !ext.externalId.startsWith("group:")) {
746
+ return { aadObjectId: ext.externalId, tenantId: ext.tenantId };
747
+ }
748
+ }
749
+ return undefined;
750
+ }
751
+ function scanPendingTeamsFiles(pendingRoot) {
752
+ const results = [];
753
+ let friendIds;
754
+ try {
755
+ friendIds = fs.readdirSync(pendingRoot);
756
+ }
757
+ catch {
758
+ return results;
759
+ }
760
+ for (const friendId of friendIds) {
761
+ const teamsDir = path.join(pendingRoot, friendId, "teams");
762
+ let keys;
763
+ try {
764
+ keys = fs.readdirSync(teamsDir);
765
+ }
766
+ catch {
767
+ continue;
768
+ }
769
+ for (const key of keys) {
770
+ const keyDir = path.join(teamsDir, key);
771
+ let files;
772
+ try {
773
+ files = fs.readdirSync(keyDir);
774
+ }
775
+ catch {
776
+ continue;
777
+ }
778
+ for (const file of files.filter((f) => f.endsWith(".json")).sort()) {
779
+ const filePath = path.join(keyDir, file);
780
+ try {
781
+ const content = fs.readFileSync(filePath, "utf-8");
782
+ results.push({ friendId, key, filePath, content });
783
+ }
784
+ catch {
785
+ // skip unreadable files
786
+ }
787
+ }
788
+ }
789
+ }
790
+ return results;
791
+ }
792
+ async function drainAndSendPendingTeams(store, botApi, pendingRoot) {
793
+ const root = pendingRoot ?? path.join((0, identity_1.getAgentRoot)(), "state", "pending");
794
+ const pendingFiles = scanPendingTeamsFiles(root);
795
+ const result = { sent: 0, skipped: 0, failed: 0 };
796
+ const conversations = botApi.conversations;
797
+ for (const { friendId, filePath, content } of pendingFiles) {
798
+ let parsed;
799
+ try {
800
+ parsed = JSON.parse(content);
801
+ }
802
+ catch {
803
+ result.failed++;
804
+ try {
805
+ fs.unlinkSync(filePath);
806
+ }
807
+ catch { /* ignore */ }
808
+ continue;
809
+ }
810
+ const messageText = typeof parsed.content === "string" ? parsed.content : "";
811
+ if (!messageText.trim()) {
812
+ result.skipped++;
813
+ try {
814
+ fs.unlinkSync(filePath);
815
+ }
816
+ catch { /* ignore */ }
817
+ continue;
818
+ }
819
+ let friend;
820
+ try {
821
+ friend = await store.get(friendId);
822
+ }
823
+ catch {
824
+ friend = null;
825
+ }
826
+ if (!friend) {
827
+ result.skipped++;
828
+ try {
829
+ fs.unlinkSync(filePath);
830
+ }
831
+ catch { /* ignore */ }
832
+ (0, runtime_1.emitNervesEvent)({
833
+ level: "warn",
834
+ component: "senses",
835
+ event: "senses.teams_proactive_no_friend",
836
+ message: "proactive send skipped: friend not found",
837
+ meta: { friendId },
838
+ });
839
+ continue;
840
+ }
841
+ if (!TEAMS_PROACTIVE_ALLOWED_TRUST.has(friend.trustLevel ?? "stranger")) {
842
+ result.skipped++;
843
+ try {
844
+ fs.unlinkSync(filePath);
845
+ }
846
+ catch { /* ignore */ }
847
+ (0, runtime_1.emitNervesEvent)({
848
+ component: "senses",
849
+ event: "senses.teams_proactive_trust_skip",
850
+ message: "proactive send skipped: trust level not allowed",
851
+ meta: { friendId, trustLevel: friend.trustLevel ?? "unknown" },
852
+ });
853
+ continue;
854
+ }
855
+ const aadInfo = findAadObjectId(friend);
856
+ if (!aadInfo) {
857
+ result.skipped++;
858
+ try {
859
+ fs.unlinkSync(filePath);
860
+ }
861
+ catch { /* ignore */ }
862
+ (0, runtime_1.emitNervesEvent)({
863
+ level: "warn",
864
+ component: "senses",
865
+ event: "senses.teams_proactive_no_aad_id",
866
+ message: "proactive send skipped: no AAD object ID found",
867
+ meta: { friendId },
868
+ });
869
+ continue;
870
+ }
871
+ try {
872
+ const conversation = await conversations.create({
873
+ bot: { id: botApi.id },
874
+ members: [{ id: aadInfo.aadObjectId, role: "user", name: friend.name || aadInfo.aadObjectId }],
875
+ tenantId: aadInfo.tenantId,
876
+ isGroup: false,
877
+ });
878
+ await conversations.activities(conversation.id).create({
879
+ type: "message",
880
+ text: messageText,
881
+ });
882
+ result.sent++;
883
+ try {
884
+ fs.unlinkSync(filePath);
885
+ }
886
+ catch { /* ignore */ }
887
+ (0, runtime_1.emitNervesEvent)({
888
+ component: "senses",
889
+ event: "senses.teams_proactive_sent",
890
+ message: "proactive teams message sent",
891
+ meta: { friendId, aadObjectId: aadInfo.aadObjectId },
892
+ });
893
+ }
894
+ catch (error) {
895
+ result.failed++;
896
+ (0, runtime_1.emitNervesEvent)({
897
+ level: "error",
898
+ component: "senses",
899
+ event: "senses.teams_proactive_send_error",
900
+ message: "proactive teams send failed",
901
+ meta: {
902
+ friendId,
903
+ aadObjectId: aadInfo.aadObjectId,
904
+ reason: error instanceof Error ? error.message : String(error),
905
+ },
906
+ });
907
+ }
908
+ }
909
+ if (result.sent > 0 || result.skipped > 0 || result.failed > 0) {
910
+ (0, runtime_1.emitNervesEvent)({
911
+ component: "senses",
912
+ event: "senses.teams_proactive_drain_complete",
913
+ message: "teams proactive drain complete",
914
+ meta: { sent: result.sent, skipped: result.skipped, failed: result.failed },
915
+ });
916
+ }
917
+ return result;
918
+ }
740
919
  // Start the Teams app in DevtoolsPlugin mode (local dev) or Bot Service mode (real Teams).
741
920
  // Mode is determined by getTeamsConfig().clientId.
742
921
  // Text is always accumulated in textBuffer and flushed periodically (chunked streaming).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.37",
3
+ "version": "0.1.0-alpha.38",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",
@@ -35,6 +35,7 @@
35
35
  "@anthropic-ai/sdk": "^0.78.0",
36
36
  "@microsoft/teams.apps": "^2.0.5",
37
37
  "@microsoft/teams.dev": "^2.0.5",
38
+ "fast-glob": "^3.3.3",
38
39
  "openai": "^6.27.0",
39
40
  "semver": "^7.7.4"
40
41
  },