@ouro.bot/cli 0.1.0-alpha.350 → 0.1.0-alpha.352

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,14 @@
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.352",
6
+ "changes": [
7
+ "Settle tool description now communicates turn-ending semantics: 'deliver your response and end your turn' with explicit guidance against settling with status updates mid-task.",
8
+ "Observe tool now available in all outward channels including 1:1 chats, not just groups and reactions — agents can absorb messages without responding when the moment doesn't call for words.",
9
+ "Autonomous execution prompt contract added: when told to work autonomously, agents use ponder to absorb new messages and continue using tools, settling only with the final result."
10
+ ]
11
+ },
4
12
  {
5
13
  "version": "0.1.0-alpha.350",
6
14
  "changes": [
@@ -556,8 +556,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
556
556
  while (!done) {
557
557
  // Channel-based tool filtering:
558
558
  // - Inner dialog: exclude send_message (delivery via surface), observe (no one to observe)
559
- // - 1:1 sessions: exclude observe (can't ignore someone talking directly to you)
560
- // - Group chats: observe available
559
+ // - All outward channels (1:1, group, reaction): observe available
561
560
  //
562
561
  // ponder, settle/rest, surface, and observe are always assembled based on channel context.
563
562
  // ponder is available in ALL channels (outer: think privately, inner: keep turning).
@@ -571,7 +570,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
571
570
  ...filteredBaseTools,
572
571
  tools_1.ponderTool,
573
572
  ...(isInnerDialog ? [tools_2.surfaceToolDef, tools_1.restTool] : []),
574
- ...((currentContext?.isGroupChat || options?.isReactionSignal) && !isInnerDialog ? [tools_1.observeTool] : []),
573
+ ...(!isInnerDialog ? [tools_1.observeTool] : []),
575
574
  ...(!isInnerDialog ? [tools_1.settleTool] : []),
576
575
  ];
577
576
  const steeringFollowUps = options?.drainSteeringFollowUps?.() ?? [];
@@ -380,7 +380,7 @@ function runtimeInfoSection(channel, options) {
380
380
  lines.push("i introduce myself on boot with a fun random greeting.");
381
381
  }
382
382
  else if (channel === "inner") {
383
- lines.push("this is my inner session. when a thought is ready to share, i surface it to whoever needs to hear it. when i'm done thinking and the queue is clear, i rest.");
383
+ lines.push("this is my inner session. when i have something to say to someone, i surface it. when i'm done thinking and the queue is clear, i rest.");
384
384
  }
385
385
  else if (channel === "mcp") {
386
386
  lines.push("this message arrived via a dev tool (e.g. claude code, codex) on behalf of a friend in a sense session. the user can see our conversation. respond via settle. if friction appears, i first look for ad-hoc repairs with the tools i already have. if the friction reveals a harness gap, i create or revise a ponder packet and keep working. ponder does not create an outward deferral by itself.");
@@ -599,15 +599,18 @@ function toolContractsSection(channel, options) {
599
599
  lines.push(`## tool behavior`);
600
600
  lines.push(`tool_choice is set to "required" -- I must call a tool on every turn.`);
601
601
  if (channel === "inner") {
602
- lines.push(`- When a thought is ready to go outward, I call \`surface\` with the content and, when available, its delegationId.`);
602
+ lines.push(`- When I have something to say to a person, I call \`surface\` with the content and, when available, its delegationId.`);
603
603
  lines.push(`- \`surface\` does not end the inner turn; after surfacing everything that needs delivery, I call \`rest\`.`);
604
- lines.push(`- \`rest\` must be the only tool call in that turn.`);
604
+ lines.push(`- \`rest\` must be the only tool call in that turn. Internal state notes go in \`rest(note: "...")\` — that is my scratchpad, not \`surface\`.`);
605
+ lines.push(`- For deeper reflection I want to preserve, I use \`ponder\` with kind \`reflection\`.`);
605
606
  lines.push(`- I do not call \`send_message\` or \`settle\` from inner dialogue; those are not inner-session delivery tools.`);
606
607
  }
607
608
  else {
608
609
  lines.push(`- When I am ready to respond to the user, I call \`settle\`.`);
609
610
  lines.push(`- \`settle\` must be the only tool call in that turn.`);
610
611
  lines.push(`- I do not call no-op tools before \`settle\`.`);
612
+ lines.push(`- when told to work autonomously, I use ponder to absorb new messages and continue using tools. I settle only with the final result.`);
613
+ lines.push(`- if nothing calls for words, I observe.`);
611
614
  }
612
615
  }
613
616
  return lines.join("\n");
@@ -72,7 +72,7 @@ exports.settleTool = {
72
72
  type: "function",
73
73
  function: {
74
74
  name: "settle",
75
- description: "respond to the user with your message. call this tool when you are ready to deliver your response. Only call when you have a substantive response. If you're settling with 'I'll look into that,' you probably should be using a tool instead.",
75
+ description: "deliver your response and end your turn this hands control back to the user. only settle when your work is complete, you're genuinely blocked, or the user asked a direct question that needs an answer now. do not settle with status updates mid-task. if you're settling with 'I'll look into that,' you probably should be using a tool instead.",
76
76
  parameters: {
77
77
  type: "object",
78
78
  properties: {
@@ -45,7 +45,7 @@ exports.surfaceToolDef = {
45
45
  type: "function",
46
46
  function: {
47
47
  name: "surface",
48
- description: "share a thought outwarddeliver an answer, ask a follow-up, or surface progress to whoever needs to hear it. pass delegationId to address a held thought (see your attention queue above), or friendId for spontaneous outreach. does not end your turn.",
48
+ description: "send a message to someone write it the way you'd text a friend. pass delegationId to address a held thought (see your attention queue above), or friendId for spontaneous outreach. does not end your turn.",
49
49
  parameters: {
50
50
  type: "object",
51
51
  properties: {
@@ -124,7 +124,7 @@ exports.surfaceToolDefinition = {
124
124
  // Inject surfaced content into the target session so it knows what was delivered
125
125
  const { appendSyntheticAssistantMessage } = await Promise.resolve().then(() => __importStar(require("../mind/context")));
126
126
  const sessionFilePath = path.join(sessionsDir, bridgeTarget.friendId, bridgeTarget.channel, `${bridgeTarget.key}.json`);
127
- appendSyntheticAssistantMessage(sessionFilePath, `[surfaced from inner dialog] ${content}`);
127
+ appendSyntheticAssistantMessage(sessionFilePath, content);
128
128
  return { status: "delivered", detail: "via iMessage" };
129
129
  }
130
130
  }
@@ -158,7 +158,7 @@ exports.surfaceToolDefinition = {
158
158
  if (proactiveResult.delivered) {
159
159
  const { appendSyntheticAssistantMessage } = await Promise.resolve().then(() => __importStar(require("../mind/context")));
160
160
  const sessionFilePath = path.join(sessionsDir, bbSession.friendId, bbSession.channel, `${bbSession.key}.json`);
161
- appendSyntheticAssistantMessage(sessionFilePath, `[surfaced from inner dialog] ${content}`);
161
+ appendSyntheticAssistantMessage(sessionFilePath, content);
162
162
  return { status: "delivered", detail: "via iMessage" };
163
163
  }
164
164
  }
@@ -101,7 +101,7 @@ const CONTENT_PREVIEW_MAX = 80;
101
101
  function buildAttentionQueueSummary(queue) {
102
102
  if (queue.length === 0)
103
103
  return "";
104
- const lines = ["you're holding:"];
104
+ const lines = ["[internal: held work items — not messages to send]"];
105
105
  for (const item of queue) {
106
106
  if (item.packetKind && item.packetObjective) {
107
107
  lines.push(`- [${item.id}] ${item.friendName} -> ${item.packetKind}: ${item.packetObjective}`);
@@ -61,6 +61,7 @@ const prompt_1 = require("../../mind/prompt");
61
61
  const mcp_manager_1 = require("../../repertoire/mcp-manager");
62
62
  // getPhrases removed — no longer needed after debug-activity cleanup
63
63
  const runtime_1 = require("../../nerves/runtime");
64
+ const proactive_content_guard_1 = require("../proactive-content-guard");
64
65
  const model_1 = require("./model");
65
66
  const client_1 = require("./client");
66
67
  const inbound_log_1 = require("./inbound-log");
@@ -1368,6 +1369,17 @@ async function sendProactiveBlueBubblesMessageToSession(params, deps = {}) {
1368
1369
  return { delivered: false, reason: "group_blocked" };
1369
1370
  }
1370
1371
  /* v8 ignore stop */
1372
+ const internalContentBlockReason = (0, proactive_content_guard_1.getProactiveInternalContentBlockReason)(params.text);
1373
+ if (internalContentBlockReason) {
1374
+ (0, proactive_content_guard_1.emitProactiveInternalContentBlocked)({
1375
+ friendId: params.friendId,
1376
+ sessionKey: params.sessionKey,
1377
+ reason: internalContentBlockReason,
1378
+ source: "session_send",
1379
+ intent: params.intent ?? "generic_outreach",
1380
+ });
1381
+ return { delivered: false, reason: "internal_content_blocked" };
1382
+ }
1371
1383
  try {
1372
1384
  await client.sendText({ chat, text: params.text });
1373
1385
  (0, runtime_1.emitNervesEvent)({
@@ -1468,6 +1480,20 @@ async function drainAndSendPendingBlueBubbles(deps = {}, pendingRoot) {
1468
1480
  catch { /* ignore */ }
1469
1481
  continue;
1470
1482
  }
1483
+ const internalBlockReason = (0, proactive_content_guard_1.getProactiveInternalContentBlockReason)(messageText);
1484
+ if (internalBlockReason) {
1485
+ result.skipped++;
1486
+ try {
1487
+ fs.unlinkSync(filePath);
1488
+ }
1489
+ catch { /* ignore */ }
1490
+ (0, proactive_content_guard_1.emitProactiveInternalContentBlocked)({
1491
+ friendId,
1492
+ reason: internalBlockReason,
1493
+ source: "pending_drain",
1494
+ });
1495
+ continue;
1496
+ }
1471
1497
  let friend;
1472
1498
  try {
1473
1499
  friend = await store.get(friendId);
@@ -92,7 +92,7 @@ function appendTrailingExtras(sections, alsoDue, staleObligations, parseErrors,
92
92
  }
93
93
  // 5. Stale obligations
94
94
  if (staleObligations.length > 0) {
95
- const lines = staleObligations.map((o) => `something for ${o.friendName} has been sitting for ${formatElapsed(o.stalenessMs)}`);
95
+ const lines = staleObligations.map((o) => `[internal] obligation: ${o.friendName} waiting ${formatElapsed(o.stalenessMs)}`);
96
96
  sections.push(lines.join("\n"));
97
97
  }
98
98
  // 6. Parse errors
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getProactiveInternalContentBlockReason = getProactiveInternalContentBlockReason;
4
+ exports.emitProactiveInternalContentBlocked = emitProactiveInternalContentBlocked;
5
+ const runtime_1 = require("../nerves/runtime");
6
+ // ── Patterns ────────────────────────────────────────────────────
7
+ const PROACTIVE_INTERNAL_CONTENT_PATTERNS = [
8
+ // PR 447 patterns: raw meta markers
9
+ { reason: "raw_meta_marker", pattern: /<\s*\/?\s*(think|analysis|commentary)\b[^>]*>/i },
10
+ { reason: "raw_meta_marker", pattern: /\[\s*surfaced from inner dialog\s*\]/i },
11
+ // Inner dialog / attention / obligation references
12
+ { reason: "inner_dialog_reference", pattern: /\binner (dialog|dialogue)\b/i },
13
+ { reason: "attention_queue_reference", pattern: /\battention queues?\b/i },
14
+ { reason: "return_obligation_reference", pattern: /\b(return|held|heart|inner)\s+obligations?\b/i },
15
+ // Surfacing mechanics
16
+ { reason: "surfacing_mechanics_reference", pattern: /\b(surface tool|surfacing (mechanics|itself)|surfaced? outward|call `?surface`?|delegationId|delegation id)\b/i },
17
+ // Prompt references
18
+ { reason: "prompt_reference", pattern: /\b(system|developer|inner|tool|orientation)\s+prompts?\b|\bprompt\/orientation\b|\bprompt wording\b/i },
19
+ // Routing references
20
+ { reason: "routing_reference", pattern: /\b(routing target|reply target|route through surface|routed through surface|proactive bluebubbles delivery)\b/i },
21
+ // Heartbeat / status patterns
22
+ { reason: "heartbeat_status", pattern: /\bheartbeat\b/i },
23
+ { reason: "heartbeat_status", pattern: /\bcheck-in\b/i },
24
+ { reason: "heartbeat_status", pattern: /\btask board\b/i },
25
+ { reason: "heartbeat_status", pattern: /\ball else settled\b/i },
26
+ { reason: "heartbeat_status", pattern: /\bobligations?\s+showing\b/i },
27
+ { reason: "heartbeat_status", pattern: /\bsame state\b/i },
28
+ ];
29
+ // ── Public API ──────────────────────────────────────────────────
30
+ function getProactiveInternalContentBlockReason(text) {
31
+ for (const { reason, pattern } of PROACTIVE_INTERNAL_CONTENT_PATTERNS) {
32
+ if (pattern.test(text))
33
+ return reason;
34
+ }
35
+ return null;
36
+ }
37
+ function emitProactiveInternalContentBlocked(params) {
38
+ (0, runtime_1.emitNervesEvent)({
39
+ level: "warn",
40
+ component: "senses",
41
+ event: "senses.proactive_internal_content_blocked",
42
+ message: "proactive send blocked: internal content",
43
+ meta: {
44
+ friendId: params.friendId,
45
+ source: params.source,
46
+ reason: params.reason,
47
+ ...(params.sessionKey ? { sessionKey: params.sessionKey } : {}),
48
+ ...(params.intent ? { intent: params.intent } : {}),
49
+ },
50
+ });
51
+ }
@@ -61,6 +61,7 @@ const context_1 = require("../mind/context");
61
61
  const commands_1 = require("./commands");
62
62
  const nerves_1 = require("../nerves");
63
63
  const runtime_1 = require("../nerves/runtime");
64
+ const proactive_content_guard_1 = require("./proactive-content-guard");
64
65
  const store_file_1 = require("../mind/friends/store-file");
65
66
  const types_1 = require("../mind/friends/types");
66
67
  const resolver_1 = require("../mind/friends/resolver");
@@ -1103,6 +1104,16 @@ async function sendProactiveTeamsMessageToSession(params, deps) {
1103
1104
  });
1104
1105
  return { delivered: false, reason: "missing_target" };
1105
1106
  }
1107
+ const internalContentBlockReason = (0, proactive_content_guard_1.getProactiveInternalContentBlockReason)(params.text);
1108
+ if (internalContentBlockReason) {
1109
+ (0, proactive_content_guard_1.emitProactiveInternalContentBlocked)({
1110
+ friendId: params.friendId,
1111
+ sessionKey: params.sessionKey,
1112
+ reason: internalContentBlockReason,
1113
+ source: "session_send",
1114
+ });
1115
+ return { delivered: false, reason: "internal_content_blocked" };
1116
+ }
1106
1117
  try {
1107
1118
  const conversation = await conversations.create({
1108
1119
  bot: { id: deps.botApi.id },
@@ -1205,6 +1216,20 @@ async function drainAndSendPendingTeams(store, botApi, pendingRoot) {
1205
1216
  catch { /* ignore */ }
1206
1217
  continue;
1207
1218
  }
1219
+ const internalBlockReason = (0, proactive_content_guard_1.getProactiveInternalContentBlockReason)(messageText);
1220
+ if (internalBlockReason) {
1221
+ result.skipped++;
1222
+ try {
1223
+ fs.unlinkSync(filePath);
1224
+ }
1225
+ catch { /* ignore */ }
1226
+ (0, proactive_content_guard_1.emitProactiveInternalContentBlocked)({
1227
+ friendId,
1228
+ reason: internalBlockReason,
1229
+ source: "pending_drain",
1230
+ });
1231
+ continue;
1232
+ }
1208
1233
  const sendResult = await sendProactiveTeamsMessageToSession({
1209
1234
  friendId,
1210
1235
  sessionKey: key,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.350",
3
+ "version": "0.1.0-alpha.352",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",