@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 +8 -0
- package/dist/heart/core.js +2 -3
- package/dist/mind/prompt.js +6 -3
- package/dist/repertoire/tools-flow.js +1 -1
- package/dist/repertoire/tools-surface.js +3 -3
- package/dist/senses/attention-queue.js +1 -1
- package/dist/senses/bluebubbles/index.js +26 -0
- package/dist/senses/habit-turn-message.js +1 -1
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/teams.js +25 -0
- package/package.json +1 -1
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": [
|
package/dist/heart/core.js
CHANGED
|
@@ -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
|
|
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
|
-
...(
|
|
573
|
+
...(!isInnerDialog ? [tools_1.observeTool] : []),
|
|
575
574
|
...(!isInnerDialog ? [tools_1.settleTool] : []),
|
|
576
575
|
];
|
|
577
576
|
const steeringFollowUps = options?.drainSteeringFollowUps?.() ?? [];
|
package/dist/mind/prompt.js
CHANGED
|
@@ -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
|
|
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
|
|
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: "
|
|
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: "
|
|
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,
|
|
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,
|
|
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 = ["
|
|
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) => `
|
|
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
|
+
}
|
package/dist/senses/teams.js
CHANGED
|
@@ -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,
|