@ouro.bot/cli 0.1.0-alpha.26 → 0.1.0-alpha.28

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/changelog.json CHANGED
@@ -1,6 +1,21 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.28",
6
+ "changes": [
7
+ "Bare npx ouro.bot now stays aligned with the current alpha CLI track because the published ouro.bot wrapper is version-locked and republished alongside the CLI instead of lagging behind it.",
8
+ "Slugger no longer re-opens active iMessage task threads with generic greetings like 'hiya' when work is already in motion; fresh idle conversations can still start warmly.",
9
+ "BlueBubbles voice notes now use a harness-managed whisper.cpp transcription path for the current OpenAI, Anthropic, and MiniMax runtime contracts, including automatic local provisioning and truthful error notices when transcription cannot complete."
10
+ ]
11
+ },
12
+ {
13
+ "version": "0.1.0-alpha.27",
14
+ "changes": [
15
+ "The daemon now discovers all enabled agents in ~/AgentBundles, so ouro status and managed workers reflect every real agent instead of only slugger and ouroboros.",
16
+ "BlueBubbles typing now wraps the visible working phase correctly, and phrase updates from agent.json take effect on the next turn without requiring a restart."
17
+ ]
18
+ },
4
19
  {
5
20
  "version": "0.1.0-alpha.26",
6
21
  "changes": [
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.listEnabledBundleAgents = listEnabledBundleAgents;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const identity_1 = require("../identity");
40
+ const runtime_1 = require("../../nerves/runtime");
41
+ function listEnabledBundleAgents(options = {}) {
42
+ const bundlesRoot = options.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
43
+ const readdirSync = options.readdirSync ?? fs.readdirSync;
44
+ const readFileSync = options.readFileSync ?? fs.readFileSync;
45
+ let entries;
46
+ try {
47
+ entries = readdirSync(bundlesRoot, { withFileTypes: true });
48
+ }
49
+ catch {
50
+ (0, runtime_1.emitNervesEvent)({
51
+ level: "warn",
52
+ component: "daemon",
53
+ event: "daemon.agent_discovery_failed",
54
+ message: "failed to read bundle root for daemon agent discovery",
55
+ meta: { bundlesRoot },
56
+ });
57
+ return [];
58
+ }
59
+ const discovered = [];
60
+ for (const entry of entries) {
61
+ if (!entry.isDirectory() || !entry.name.endsWith(".ouro"))
62
+ continue;
63
+ const agentName = entry.name.slice(0, -5);
64
+ const configPath = path.join(bundlesRoot, entry.name, "agent.json");
65
+ let enabled = true;
66
+ try {
67
+ const raw = readFileSync(configPath, "utf-8");
68
+ const parsed = JSON.parse(raw);
69
+ if (typeof parsed.enabled === "boolean") {
70
+ enabled = parsed.enabled;
71
+ }
72
+ }
73
+ catch {
74
+ continue;
75
+ }
76
+ if (enabled) {
77
+ discovered.push(agentName);
78
+ }
79
+ }
80
+ return discovered.sort((left, right) => left.localeCompare(right));
81
+ }
@@ -56,6 +56,7 @@ const specialist_prompt_1 = require("./specialist-prompt");
56
56
  const specialist_tools_1 = require("./specialist-tools");
57
57
  const runtime_metadata_1 = require("./runtime-metadata");
58
58
  const daemon_runtime_sync_1 = require("./daemon-runtime-sync");
59
+ const agent_discovery_1 = require("./agent-discovery");
59
60
  const update_hooks_1 = require("./update-hooks");
60
61
  const bundle_meta_1 = require("./hooks/bundle-meta");
61
62
  const bundle_manifest_1 = require("../../mind/bundle-manifest");
@@ -596,36 +597,11 @@ async function defaultPromptInput(question) {
596
597
  }
597
598
  }
598
599
  function defaultListDiscoveredAgents() {
599
- const bundlesRoot = (0, identity_1.getAgentBundlesRoot)();
600
- let entries;
601
- try {
602
- entries = fs.readdirSync(bundlesRoot, { withFileTypes: true });
603
- }
604
- catch {
605
- return [];
606
- }
607
- const discovered = [];
608
- for (const entry of entries) {
609
- if (!entry.isDirectory() || !entry.name.endsWith(".ouro"))
610
- continue;
611
- const agentName = entry.name.slice(0, -5);
612
- const configPath = path.join(bundlesRoot, entry.name, "agent.json");
613
- let enabled = true;
614
- try {
615
- const raw = fs.readFileSync(configPath, "utf-8");
616
- const parsed = JSON.parse(raw);
617
- if (typeof parsed.enabled === "boolean") {
618
- enabled = parsed.enabled;
619
- }
620
- }
621
- catch {
622
- continue;
623
- }
624
- if (enabled) {
625
- discovered.push(agentName);
626
- }
627
- }
628
- return discovered.sort((left, right) => left.localeCompare(right));
600
+ return (0, agent_discovery_1.listEnabledBundleAgents)({
601
+ bundlesRoot: (0, identity_1.getAgentBundlesRoot)(),
602
+ readdirSync: fs.readdirSync,
603
+ readFileSync: fs.readFileSync,
604
+ });
629
605
  }
630
606
  async function defaultLinkFriendIdentity(command) {
631
607
  const fp = path.join((0, identity_1.getAgentBundlesRoot)(), `${command.agent}.ouro`, "friends");
@@ -9,6 +9,7 @@ const health_monitor_1 = require("./health-monitor");
9
9
  const task_scheduler_1 = require("./task-scheduler");
10
10
  const runtime_logging_1 = require("./runtime-logging");
11
11
  const sense_manager_1 = require("./sense-manager");
12
+ const agent_discovery_1 = require("./agent-discovery");
12
13
  function parseSocketPath(argv) {
13
14
  const socketIndex = argv.indexOf("--socket");
14
15
  if (socketIndex >= 0) {
@@ -26,7 +27,7 @@ const socketPath = parseSocketPath(process.argv);
26
27
  message: "starting daemon entrypoint",
27
28
  meta: { socketPath },
28
29
  });
29
- const managedAgents = ["ouroboros", "slugger"];
30
+ const managedAgents = (0, agent_discovery_1.listEnabledBundleAgents)();
30
31
  const processManager = new process_manager_1.DaemonProcessManager({
31
32
  agents: managedAgents.map((agent) => ({
32
33
  name: agent,
@@ -7,6 +7,26 @@ function wrapperPackageChanged(changedFiles) {
7
7
  }
8
8
  function assessWrapperPublishSync(input) {
9
9
  let result;
10
+ if (input.localVersion !== input.cliVersion) {
11
+ result = {
12
+ ok: false,
13
+ message: `ouro.bot wrapper version ${input.localVersion} must match @ouro.bot/cli version ${input.cliVersion}`,
14
+ };
15
+ (0, runtime_1.emitNervesEvent)({
16
+ level: "warn",
17
+ component: "daemon",
18
+ event: "daemon.wrapper_publish_guard_checked",
19
+ message: "evaluated wrapper publish sync",
20
+ meta: {
21
+ changed: wrapperPackageChanged(input.changedFiles),
22
+ localVersion: input.localVersion,
23
+ cliVersion: input.cliVersion,
24
+ publishedVersion: input.publishedVersion,
25
+ ok: result.ok,
26
+ },
27
+ });
28
+ return result;
29
+ }
10
30
  if (!wrapperPackageChanged(input.changedFiles)) {
11
31
  result = {
12
32
  ok: true,
@@ -16,7 +36,13 @@ function assessWrapperPublishSync(input) {
16
36
  component: "daemon",
17
37
  event: "daemon.wrapper_publish_guard_checked",
18
38
  message: "evaluated wrapper publish sync",
19
- meta: { changed: false, localVersion: input.localVersion, publishedVersion: input.publishedVersion, ok: result.ok },
39
+ meta: {
40
+ changed: false,
41
+ localVersion: input.localVersion,
42
+ cliVersion: input.cliVersion,
43
+ publishedVersion: input.publishedVersion,
44
+ ok: result.ok,
45
+ },
20
46
  });
21
47
  return result;
22
48
  }
@@ -30,7 +56,13 @@ function assessWrapperPublishSync(input) {
30
56
  component: "daemon",
31
57
  event: "daemon.wrapper_publish_guard_checked",
32
58
  message: "evaluated wrapper publish sync",
33
- meta: { changed: true, localVersion: input.localVersion, publishedVersion: input.publishedVersion, ok: result.ok },
59
+ meta: {
60
+ changed: true,
61
+ localVersion: input.localVersion,
62
+ cliVersion: input.cliVersion,
63
+ publishedVersion: input.publishedVersion,
64
+ ok: result.ok,
65
+ },
34
66
  });
35
67
  return result;
36
68
  }
@@ -42,7 +74,13 @@ function assessWrapperPublishSync(input) {
42
74
  component: "daemon",
43
75
  event: "daemon.wrapper_publish_guard_checked",
44
76
  message: "evaluated wrapper publish sync",
45
- meta: { changed: true, localVersion: input.localVersion, publishedVersion: input.publishedVersion, ok: result.ok },
77
+ meta: {
78
+ changed: true,
79
+ localVersion: input.localVersion,
80
+ cliVersion: input.cliVersion,
81
+ publishedVersion: input.publishedVersion,
82
+ ok: result.ok,
83
+ },
46
84
  });
47
85
  return result;
48
86
  }
@@ -43,6 +43,7 @@ exports.getAgentSecretsPath = getAgentSecretsPath;
43
43
  exports.loadAgentConfig = loadAgentConfig;
44
44
  exports.setAgentName = setAgentName;
45
45
  exports.setAgentConfigOverride = setAgentConfigOverride;
46
+ exports.resetAgentConfigCache = resetAgentConfigCache;
46
47
  exports.resetIdentity = resetIdentity;
47
48
  const fs = __importStar(require("fs"));
48
49
  const os = __importStar(require("os"));
@@ -346,6 +347,13 @@ function setAgentName(name) {
346
347
  function setAgentConfigOverride(config) {
347
348
  _agentConfigOverride = config;
348
349
  }
350
+ /**
351
+ * Clear only the cached agent config while preserving the resolved agent identity.
352
+ * Used when a running agent should pick up updated disk-backed config on the next turn.
353
+ */
354
+ function resetAgentConfigCache() {
355
+ _cachedAgentConfig = null;
356
+ }
349
357
  /**
350
358
  * Clear all cached identity state.
351
359
  * Used in tests and when switching agent context.
@@ -37,7 +37,8 @@ function getFirstImpressions(friend) {
37
37
  lines.push("- what do they do outside of work that they care about?");
38
38
  lines.push("i don't ask all of these at once -- i weave them into conversation naturally, one or two at a time, and i genuinely follow up on what they share.");
39
39
  lines.push("i introduce what i can do -- i have tools, integrations, and skills that can help them. i mention these naturally as they become relevant.");
40
- lines.push("if my friend hasn't asked me to do something specific, or i've already finished what they asked for, that's my cue to turn the tables -- i ask them questions about themselves, what they're into, what they need. no idle small talk; i'm on a mission to get to know them.");
40
+ lines.push("if we're already in motion on a task, thread, or follow-up, i do not reset with a generic opener like 'hiya' or 'what do ya need help with?'. i continue directly or ask the specific next question.");
41
+ lines.push("only when the conversation is genuinely fresh and idle, with no active ask or thread in flight, a light opener is okay.");
41
42
  lines.push("i save everything i learn immediately with save_friend_note -- names, roles, preferences, projects, anything. the bar is low: if i learned it, i save it.");
42
43
  return lines.join("\n");
43
44
  }
@@ -16,6 +16,7 @@ function getPhrases() {
16
16
  message: "loading phrase pools",
17
17
  meta: {},
18
18
  });
19
+ (0, identity_1.resetAgentConfigCache)();
19
20
  const phrases = (0, identity_1.loadAgentConfig)().phrases;
20
21
  (0, runtime_1.emitNervesEvent)({
21
22
  event: "repertoire.load_end",
@@ -196,7 +196,8 @@ function extractRepairData(payload) {
196
196
  return asRecord(record?.data) ?? record;
197
197
  }
198
198
  function providerSupportsAudioInput(provider) {
199
- return provider === "azure" || provider === "openai-codex";
199
+ void provider;
200
+ return false;
200
201
  }
201
202
  async function resolveChatGuid(chat, config, channelConfig) {
202
203
  return chat.chatGuid
@@ -62,6 +62,12 @@ const AUDIO_INPUT_FORMAT_BY_EXTENSION = {
62
62
  ".wav": "wav",
63
63
  ".mp3": "mp3",
64
64
  };
65
+ const WHISPER_CPP_FORMULA = "whisper-cpp";
66
+ const WHISPER_CPP_MODEL_NAME = "ggml-base.en.bin";
67
+ const WHISPER_CPP_MODEL_URL = `https://huggingface.co/ggerganov/whisper.cpp/resolve/main/${WHISPER_CPP_MODEL_NAME}`;
68
+ const WHISPER_CPP_TOOLS_DIR = path.join(os.homedir(), ".agentstate", "tools", "whisper-cpp");
69
+ const WHISPER_CPP_MODELS_DIR = path.join(WHISPER_CPP_TOOLS_DIR, "models");
70
+ const WHISPER_CPP_MODEL_PATH = path.join(WHISPER_CPP_MODELS_DIR, WHISPER_CPP_MODEL_NAME);
65
71
  function buildBlueBubblesApiUrl(baseUrl, endpoint, password) {
66
72
  const root = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
67
73
  const url = new URL(endpoint.replace(/^\//, ""), root);
@@ -107,36 +113,123 @@ function audioFormatForInput(contentType, attachment) {
107
113
  const extension = path.extname(attachment?.transferName ?? "").toLowerCase();
108
114
  return AUDIO_INPUT_FORMAT_BY_CONTENT_TYPE[contentType ?? ""] ?? AUDIO_INPUT_FORMAT_BY_EXTENSION[extension];
109
115
  }
110
- async function transcribeAudioWithWhisper(params) {
116
+ async function execFileText(file, args, timeout) {
117
+ return await new Promise((resolve, reject) => {
118
+ (0, node_child_process_1.execFile)(file, args, { timeout }, (error, stdout = "", stderr = "") => {
119
+ if (error) {
120
+ const detail = stderr.trim() || stdout.trim() || error.message;
121
+ reject(new Error(detail));
122
+ return;
123
+ }
124
+ resolve(stdout);
125
+ });
126
+ });
127
+ }
128
+ async function pathExists(targetPath) {
129
+ try {
130
+ await fs.access(targetPath);
131
+ return true;
132
+ }
133
+ catch {
134
+ return false;
135
+ }
136
+ }
137
+ async function resolveWhisperCppBinary(timeoutMs) {
138
+ try {
139
+ const existing = (await execFileText("which", ["whisper-cli"], timeoutMs)).trim();
140
+ if (existing) {
141
+ return existing;
142
+ }
143
+ }
144
+ catch {
145
+ // fall through to managed install
146
+ }
147
+ let prefix = "";
148
+ try {
149
+ prefix = (await execFileText("brew", ["--prefix", WHISPER_CPP_FORMULA], timeoutMs)).trim();
150
+ if (prefix) {
151
+ const candidate = path.join(prefix, "bin", "whisper-cli");
152
+ if (await pathExists(candidate)) {
153
+ return candidate;
154
+ }
155
+ }
156
+ }
157
+ catch {
158
+ // fall through to managed install
159
+ }
160
+ await execFileText("brew", ["install", WHISPER_CPP_FORMULA], Math.max(timeoutMs, 300_000));
161
+ prefix = (await execFileText("brew", ["--prefix", WHISPER_CPP_FORMULA], timeoutMs)).trim();
162
+ if (!prefix) {
163
+ throw new Error("whisper.cpp installed but brew did not return a usable prefix");
164
+ }
165
+ const candidate = path.join(prefix, "bin", "whisper-cli");
166
+ if (!await pathExists(candidate)) {
167
+ throw new Error("whisper.cpp installed but whisper-cli binary is missing");
168
+ }
169
+ return candidate;
170
+ }
171
+ async function ensureWhisperCppModel(timeoutMs, fetchImpl) {
172
+ try {
173
+ await fs.access(WHISPER_CPP_MODEL_PATH);
174
+ return WHISPER_CPP_MODEL_PATH;
175
+ }
176
+ catch {
177
+ await fs.mkdir(WHISPER_CPP_MODELS_DIR, { recursive: true });
178
+ const response = await fetchImpl(WHISPER_CPP_MODEL_URL, {
179
+ method: "GET",
180
+ signal: AbortSignal.timeout(Math.max(timeoutMs, 300_000)),
181
+ });
182
+ if (!response.ok) {
183
+ throw new Error(`failed to download whisper.cpp model: HTTP ${response.status}`);
184
+ }
185
+ await fs.writeFile(WHISPER_CPP_MODEL_PATH, Buffer.from(await response.arrayBuffer()));
186
+ return WHISPER_CPP_MODEL_PATH;
187
+ }
188
+ }
189
+ async function convertAudioForWhisperCpp(sourcePath, outputPath, timeoutMs) {
190
+ try {
191
+ await execFileText("ffmpeg", ["-y", "-i", sourcePath, "-ar", "16000", "-ac", "1", "-c:a", "pcm_s16le", outputPath], Math.max(timeoutMs, 120_000));
192
+ return;
193
+ }
194
+ catch (ffmpegError) {
195
+ try {
196
+ await execFileText("afconvert", ["-f", "WAVE", "-d", "LEI16@16000", "-c", "1", sourcePath, outputPath], Math.max(timeoutMs, 120_000));
197
+ return;
198
+ }
199
+ catch (afconvertError) {
200
+ const ffmpegReason = ffmpegError.message;
201
+ const afconvertReason = afconvertError.message;
202
+ throw new Error(`failed to prepare audio for whisper.cpp (ffmpeg: ${ffmpegReason}; afconvert: ${afconvertReason})`);
203
+ }
204
+ }
205
+ }
206
+ async function transcribeAudioWithWhisperCpp(params, modelFetchImpl = fetch) {
111
207
  const workDir = await fs.mkdtemp(path.join(os.tmpdir(), "ouro-bb-audio-"));
112
208
  const filename = sanitizeFilename(describeAttachment(params.attachment));
113
209
  const extension = fileExtensionForAudio(params.attachment, params.contentType);
114
210
  const audioPath = path.join(workDir, `${path.parse(filename).name}${extension}`);
211
+ const wavPath = path.join(workDir, `${path.parse(audioPath).name}.wav`);
212
+ const outputBase = path.join(workDir, path.parse(audioPath).name);
115
213
  try {
116
214
  await fs.writeFile(audioPath, params.buffer);
117
- await new Promise((resolve, reject) => {
118
- (0, node_child_process_1.execFile)("whisper", [
119
- audioPath,
120
- "--model",
121
- "turbo",
122
- "--output_dir",
123
- workDir,
124
- "--output_format",
125
- "json",
126
- "--verbose",
127
- "False",
128
- ], { timeout: Math.max(params.timeoutMs, 120000) }, (error) => {
129
- if (error) {
130
- reject(error);
131
- return;
132
- }
133
- resolve();
134
- });
135
- });
136
- const transcriptPath = path.join(workDir, `${path.parse(audioPath).name}.json`);
215
+ const whisperCliPath = await resolveWhisperCppBinary(params.timeoutMs);
216
+ const modelPath = await ensureWhisperCppModel(params.timeoutMs, modelFetchImpl);
217
+ await convertAudioForWhisperCpp(audioPath, wavPath, params.timeoutMs);
218
+ await execFileText(whisperCliPath, ["-m", modelPath, "-f", wavPath, "-oj", "-of", outputBase], Math.max(params.timeoutMs, 120_000));
219
+ const transcriptPath = `${outputBase}.json`;
137
220
  const raw = await fs.readFile(transcriptPath, "utf8");
138
221
  const parsed = JSON.parse(raw);
139
- return typeof parsed.text === "string" ? parsed.text.trim() : "";
222
+ if (typeof parsed.text === "string") {
223
+ return parsed.text.trim();
224
+ }
225
+ if (Array.isArray(parsed.transcription)) {
226
+ return parsed.transcription
227
+ .map((entry) => (typeof entry?.text === "string" ? entry.text.trim() : ""))
228
+ .filter(Boolean)
229
+ .join(" ")
230
+ .trim();
231
+ }
232
+ return "";
140
233
  }
141
234
  finally {
142
235
  await fs.rm(workDir, { recursive: true, force: true }).catch(() => undefined);
@@ -178,7 +271,8 @@ async function hydrateBlueBubblesAttachments(attachments, config, channelConfig,
178
271
  },
179
272
  });
180
273
  const fetchImpl = deps.fetchImpl ?? fetch;
181
- const transcribeAudio = deps.transcribeAudio ?? transcribeAudioWithWhisper;
274
+ const modelFetchImpl = deps.modelFetchImpl ?? fetch;
275
+ const transcribeAudio = deps.transcribeAudio ?? ((params) => transcribeAudioWithWhisperCpp(params, modelFetchImpl));
182
276
  const preferAudioInput = deps.preferAudioInput ?? false;
183
277
  const inputParts = [];
184
278
  const transcriptAdditions = [];
@@ -205,12 +205,12 @@ function createBlueBubblesCallbacks(client, chat, replyToMessageGuid) {
205
205
  return;
206
206
  }
207
207
  textBuffer = "";
208
+ await activity.finish();
208
209
  await client.sendText({
209
210
  chat,
210
211
  text: trimmed,
211
212
  replyToMessageGuid,
212
213
  });
213
- await activity.finish();
214
214
  },
215
215
  async finish() {
216
216
  await activity.finish();
@@ -36,15 +36,6 @@ function createDebugActivityController(options) {
36
36
  lastPhrase = phrase;
37
37
  return phrase;
38
38
  }
39
- function ensureTyping(active) {
40
- if (typingActive === active) {
41
- return;
42
- }
43
- typingActive = active;
44
- enqueue(active ? "typing_start" : "typing_stop", async () => {
45
- await options.transport.setTyping(active);
46
- });
47
- }
48
39
  function setStatus(text) {
49
40
  (0, runtime_1.emitNervesEvent)({
50
41
  component: "senses",
@@ -55,13 +46,20 @@ function createDebugActivityController(options) {
55
46
  textLength: text.length,
56
47
  },
57
48
  });
58
- ensureTyping(true);
49
+ const shouldStartTyping = !typingActive;
50
+ if (shouldStartTyping) {
51
+ typingActive = true;
52
+ }
59
53
  enqueue("status_update", async () => {
60
54
  if (statusMessageGuid) {
61
55
  await options.transport.editStatus(statusMessageGuid, text);
62
- return;
63
56
  }
64
- statusMessageGuid = await options.transport.sendStatus(text);
57
+ else {
58
+ statusMessageGuid = await options.transport.sendStatus(text);
59
+ }
60
+ if (shouldStartTyping) {
61
+ await options.transport.setTyping(true);
62
+ }
65
63
  });
66
64
  }
67
65
  return {
@@ -100,7 +98,10 @@ function createDebugActivityController(options) {
100
98
  await queue;
101
99
  return;
102
100
  }
103
- ensureTyping(false);
101
+ typingActive = false;
102
+ enqueue("typing_stop", async () => {
103
+ await options.transport.setTyping(false);
104
+ });
104
105
  await queue;
105
106
  },
106
107
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.26",
3
+ "version": "0.1.0-alpha.28",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",