@ouro.bot/cli 0.1.0-alpha.49 → 0.1.0-alpha.50
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 +7 -0
- package/dist/heart/active-work.js +157 -0
- package/dist/heart/bridges/manager.js +37 -0
- package/dist/heart/bridges/state-machine.js +20 -0
- package/dist/heart/core.js +6 -1
- package/dist/heart/daemon/daemon-cli.js +15 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/session-activity.js +169 -0
- package/dist/mind/context.js +14 -6
- package/dist/mind/pending.js +52 -8
- package/dist/mind/prompt.js +33 -66
- package/dist/repertoire/tools-base.js +89 -10
- package/dist/senses/bluebubbles.js +110 -0
- package/dist/senses/cli.js +4 -2
- package/dist/senses/debug-activity.js +26 -5
- package/dist/senses/inner-dialog.js +86 -2
- package/dist/senses/pipeline.js +104 -6
- package/dist/senses/teams.js +17 -3
- package/package.json +1 -1
package/dist/mind/pending.js
CHANGED
|
@@ -35,8 +35,11 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.INNER_DIALOG_PENDING = void 0;
|
|
37
37
|
exports.getPendingDir = getPendingDir;
|
|
38
|
+
exports.getDeferredReturnDir = getDeferredReturnDir;
|
|
38
39
|
exports.getInnerDialogPendingDir = getInnerDialogPendingDir;
|
|
39
40
|
exports.hasPendingMessages = hasPendingMessages;
|
|
41
|
+
exports.enqueueDeferredReturn = enqueueDeferredReturn;
|
|
42
|
+
exports.drainDeferredReturns = drainDeferredReturns;
|
|
40
43
|
exports.drainPending = drainPending;
|
|
41
44
|
const fs = __importStar(require("fs"));
|
|
42
45
|
const path = __importStar(require("path"));
|
|
@@ -45,6 +48,9 @@ const runtime_1 = require("../nerves/runtime");
|
|
|
45
48
|
function getPendingDir(agentName, friendId, channel, key) {
|
|
46
49
|
return path.join((0, identity_1.getAgentRoot)(agentName), "state", "pending", friendId, channel, key);
|
|
47
50
|
}
|
|
51
|
+
function getDeferredReturnDir(agentName, friendId) {
|
|
52
|
+
return path.join((0, identity_1.getAgentRoot)(agentName), "state", "pending-returns", friendId);
|
|
53
|
+
}
|
|
48
54
|
/** Canonical inner-dialog pending path segments. */
|
|
49
55
|
exports.INNER_DIALOG_PENDING = { friendId: "self", channel: "inner", key: "dialog" };
|
|
50
56
|
/** Returns the pending dir for this agent's inner dialog. */
|
|
@@ -61,15 +67,22 @@ function hasPendingMessages(pendingDir) {
|
|
|
61
67
|
return false;
|
|
62
68
|
}
|
|
63
69
|
}
|
|
64
|
-
function
|
|
65
|
-
|
|
66
|
-
|
|
70
|
+
function writeQueueFile(queueDir, message) {
|
|
71
|
+
fs.mkdirSync(queueDir, { recursive: true });
|
|
72
|
+
const fileName = `${message.timestamp}-${Math.random().toString(36).slice(2, 10)}.json`;
|
|
73
|
+
const filePath = path.join(queueDir, fileName);
|
|
74
|
+
fs.writeFileSync(filePath, JSON.stringify(message, null, 2));
|
|
75
|
+
return filePath;
|
|
76
|
+
}
|
|
77
|
+
function drainQueue(queueDir) {
|
|
78
|
+
if (!fs.existsSync(queueDir))
|
|
79
|
+
return { messages: [], recovered: 0 };
|
|
67
80
|
let entries;
|
|
68
81
|
try {
|
|
69
|
-
entries = fs.readdirSync(
|
|
82
|
+
entries = fs.readdirSync(queueDir);
|
|
70
83
|
}
|
|
71
84
|
catch {
|
|
72
|
-
return [];
|
|
85
|
+
return { messages: [], recovered: 0 };
|
|
73
86
|
}
|
|
74
87
|
// Collect both .json (new) and .processing (crash recovery)
|
|
75
88
|
const jsonFiles = entries.filter(f => f.endsWith(".json") && !f.endsWith(".processing"));
|
|
@@ -81,9 +94,9 @@ function drainPending(pendingDir) {
|
|
|
81
94
|
].sort((a, b) => a.file.localeCompare(b.file));
|
|
82
95
|
const messages = [];
|
|
83
96
|
for (const { file, needsRename } of allFiles) {
|
|
84
|
-
const srcPath = path.join(
|
|
97
|
+
const srcPath = path.join(queueDir, file);
|
|
85
98
|
const processingPath = needsRename
|
|
86
|
-
? path.join(
|
|
99
|
+
? path.join(queueDir, file + ".processing")
|
|
87
100
|
: srcPath;
|
|
88
101
|
try {
|
|
89
102
|
if (needsRename) {
|
|
@@ -102,11 +115,42 @@ function drainPending(pendingDir) {
|
|
|
102
115
|
catch { /* ignore */ }
|
|
103
116
|
}
|
|
104
117
|
}
|
|
118
|
+
return {
|
|
119
|
+
messages,
|
|
120
|
+
recovered: processingFiles.length,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function enqueueDeferredReturn(agentName, friendId, message) {
|
|
124
|
+
const queueDir = getDeferredReturnDir(agentName, friendId);
|
|
125
|
+
const filePath = writeQueueFile(queueDir, message);
|
|
126
|
+
(0, runtime_1.emitNervesEvent)({
|
|
127
|
+
event: "mind.deferred_return_enqueued",
|
|
128
|
+
component: "mind",
|
|
129
|
+
message: "deferred return queued for later friend delivery",
|
|
130
|
+
meta: { friendId, queueDir },
|
|
131
|
+
});
|
|
132
|
+
return filePath;
|
|
133
|
+
}
|
|
134
|
+
function drainDeferredReturns(agentName, friendId) {
|
|
135
|
+
const queueDir = getDeferredReturnDir(agentName, friendId);
|
|
136
|
+
const { messages } = drainQueue(queueDir);
|
|
137
|
+
(0, runtime_1.emitNervesEvent)({
|
|
138
|
+
event: "mind.deferred_returns_drained",
|
|
139
|
+
component: "mind",
|
|
140
|
+
message: "deferred friend returns drained",
|
|
141
|
+
meta: { friendId, queueDir, count: messages.length },
|
|
142
|
+
});
|
|
143
|
+
return messages;
|
|
144
|
+
}
|
|
145
|
+
function drainPending(pendingDir) {
|
|
146
|
+
if (!fs.existsSync(pendingDir))
|
|
147
|
+
return [];
|
|
148
|
+
const { messages, recovered } = drainQueue(pendingDir);
|
|
105
149
|
(0, runtime_1.emitNervesEvent)({
|
|
106
150
|
event: "mind.pending_drained",
|
|
107
151
|
component: "mind",
|
|
108
152
|
message: "pending queue drained",
|
|
109
|
-
meta: { pendingDir, count: messages.length, recovered
|
|
153
|
+
meta: { pendingDir, count: messages.length, recovered },
|
|
110
154
|
});
|
|
111
155
|
return messages;
|
|
112
156
|
}
|
package/dist/mind/prompt.js
CHANGED
|
@@ -56,6 +56,8 @@ const runtime_1 = require("../nerves/runtime");
|
|
|
56
56
|
const bundle_manifest_1 = require("./bundle-manifest");
|
|
57
57
|
const first_impressions_1 = require("./first-impressions");
|
|
58
58
|
const tasks_1 = require("../repertoire/tasks");
|
|
59
|
+
const session_activity_1 = require("../heart/session-activity");
|
|
60
|
+
const active_work_1 = require("../heart/active-work");
|
|
59
61
|
// Lazy-loaded psyche text cache
|
|
60
62
|
let _psycheCache = null;
|
|
61
63
|
let _senseStatusLinesCache = null;
|
|
@@ -85,80 +87,25 @@ function resetPsycheCache() {
|
|
|
85
87
|
_senseStatusLinesCache = null;
|
|
86
88
|
}
|
|
87
89
|
const DEFAULT_ACTIVE_THRESHOLD_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
88
|
-
function resolveFriendName(friendId, friendsDir, agentName) {
|
|
89
|
-
if (friendId === "self")
|
|
90
|
-
return agentName;
|
|
91
|
-
try {
|
|
92
|
-
const raw = fs.readFileSync(path.join(friendsDir, `${friendId}.json`), "utf-8");
|
|
93
|
-
const record = JSON.parse(raw);
|
|
94
|
-
return record.name ?? friendId;
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
return friendId;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
90
|
function buildSessionSummary(options) {
|
|
101
91
|
const { sessionsDir, friendsDir, agentName, currentFriendId, currentChannel, currentKey, activeThresholdMs = DEFAULT_ACTIVE_THRESHOLD_MS, } = options;
|
|
102
|
-
if (!fs.existsSync(sessionsDir))
|
|
103
|
-
return "";
|
|
104
92
|
const now = Date.now();
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
let channels;
|
|
116
|
-
try {
|
|
117
|
-
channels = fs.readdirSync(friendPath);
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
for (const channel of channels) {
|
|
123
|
-
const channelPath = path.join(friendPath, channel);
|
|
124
|
-
let keys;
|
|
125
|
-
try {
|
|
126
|
-
keys = fs.readdirSync(channelPath);
|
|
127
|
-
}
|
|
128
|
-
catch {
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
for (const keyFile of keys) {
|
|
132
|
-
if (!keyFile.endsWith(".json"))
|
|
133
|
-
continue;
|
|
134
|
-
const key = keyFile.replace(/\.json$/, "");
|
|
135
|
-
// Exclude current session
|
|
136
|
-
if (friendId === currentFriendId && channel === currentChannel && key === currentKey) {
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
const filePath = path.join(channelPath, keyFile);
|
|
140
|
-
let mtimeMs;
|
|
141
|
-
try {
|
|
142
|
-
mtimeMs = fs.statSync(filePath).mtimeMs;
|
|
143
|
-
}
|
|
144
|
-
catch {
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
if (now - mtimeMs > activeThresholdMs)
|
|
148
|
-
continue;
|
|
149
|
-
const displayName = resolveFriendName(friendId, friendsDir, agentName);
|
|
150
|
-
entries.push({ friendId, displayName, channel, key, lastActivityMs: mtimeMs });
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
93
|
+
const query = {
|
|
94
|
+
sessionsDir,
|
|
95
|
+
friendsDir,
|
|
96
|
+
agentName,
|
|
97
|
+
activeThresholdMs,
|
|
98
|
+
currentSession: currentFriendId && currentChannel && currentKey
|
|
99
|
+
? { friendId: currentFriendId, channel: currentChannel, key: currentKey }
|
|
100
|
+
: null,
|
|
101
|
+
};
|
|
102
|
+
const entries = (0, session_activity_1.listSessionActivity)(query);
|
|
154
103
|
if (entries.length === 0)
|
|
155
104
|
return "";
|
|
156
|
-
// Sort by most recent first
|
|
157
|
-
entries.sort((a, b) => b.lastActivityMs - a.lastActivityMs);
|
|
158
105
|
const lines = ["## active sessions"];
|
|
159
106
|
for (const entry of entries) {
|
|
160
107
|
const ago = formatTimeAgo(now - entry.lastActivityMs);
|
|
161
|
-
lines.push(`- ${entry.
|
|
108
|
+
lines.push(`- ${entry.friendName}/${entry.channel}/${entry.key} (last: ${ago})`);
|
|
162
109
|
}
|
|
163
110
|
return lines.join("\n");
|
|
164
111
|
}
|
|
@@ -413,11 +360,29 @@ function memoryFriendToolContractSection() {
|
|
|
413
360
|
- My task board is always loaded - I already know my work.`;
|
|
414
361
|
}
|
|
415
362
|
function bridgeContextSection(options) {
|
|
363
|
+
if (options?.activeWorkFrame)
|
|
364
|
+
return "";
|
|
416
365
|
const bridgeContext = options?.bridgeContext?.trim() ?? "";
|
|
417
366
|
if (!bridgeContext)
|
|
418
367
|
return "";
|
|
419
368
|
return bridgeContext.startsWith("## ") ? bridgeContext : `## active bridge work\n${bridgeContext}`;
|
|
420
369
|
}
|
|
370
|
+
function activeWorkSection(options) {
|
|
371
|
+
if (!options?.activeWorkFrame)
|
|
372
|
+
return "";
|
|
373
|
+
return (0, active_work_1.formatActiveWorkFrame)(options.activeWorkFrame);
|
|
374
|
+
}
|
|
375
|
+
function delegationHintSection(options) {
|
|
376
|
+
if (!options?.delegationDecision)
|
|
377
|
+
return "";
|
|
378
|
+
const lines = [
|
|
379
|
+
"## delegation hint",
|
|
380
|
+
`target: ${options.delegationDecision.target}`,
|
|
381
|
+
`reasons: ${options.delegationDecision.reasons.length > 0 ? options.delegationDecision.reasons.join(", ") : "none"}`,
|
|
382
|
+
`outward closure: ${options.delegationDecision.outwardClosureRequired ? "required" : "not required"}`,
|
|
383
|
+
];
|
|
384
|
+
return lines.join("\n");
|
|
385
|
+
}
|
|
421
386
|
function toolBehaviorSection(options) {
|
|
422
387
|
if (!(options?.toolChoiceRequired ?? true))
|
|
423
388
|
return "";
|
|
@@ -549,6 +514,8 @@ async function buildSystem(channel = "cli", options, context) {
|
|
|
549
514
|
mixedTrustGroupSection(context),
|
|
550
515
|
skillsSection(),
|
|
551
516
|
taskBoardSection(),
|
|
517
|
+
activeWorkSection(options),
|
|
518
|
+
delegationHintSection(options),
|
|
552
519
|
bridgeContextSection(options),
|
|
553
520
|
buildSessionSummary({
|
|
554
521
|
sessionsDir: path.join((0, identity_1.getAgentRoot)(), "state", "sessions"),
|
|
@@ -49,6 +49,7 @@ const session_recall_1 = require("../heart/session-recall");
|
|
|
49
49
|
const tools_1 = require("./coding/tools");
|
|
50
50
|
const memory_1 = require("../mind/memory");
|
|
51
51
|
const pending_1 = require("../mind/pending");
|
|
52
|
+
const progress_story_1 = require("../heart/progress-story");
|
|
52
53
|
// Tracks which file paths have been read via read_file in this session.
|
|
53
54
|
// edit_file requires a file to be read first (must-read-first guard).
|
|
54
55
|
exports.editFileReadTracker = new Set();
|
|
@@ -65,14 +66,79 @@ function buildContextDiff(lines, changeStart, changeEnd, contextSize = 3) {
|
|
|
65
66
|
}
|
|
66
67
|
const NO_SESSION_FOUND_MESSAGE = "no session found for that friend/channel/key combination.";
|
|
67
68
|
const EMPTY_SESSION_MESSAGE = "session exists but has no non-system messages.";
|
|
69
|
+
function findDelegatingBridgeId(ctx) {
|
|
70
|
+
const currentSession = ctx?.currentSession;
|
|
71
|
+
if (!currentSession)
|
|
72
|
+
return undefined;
|
|
73
|
+
return ctx?.activeBridges?.find((bridge) => bridge.lifecycle === "active"
|
|
74
|
+
&& bridge.attachedSessions.some((session) => session.friendId === currentSession.friendId
|
|
75
|
+
&& session.channel === currentSession.channel
|
|
76
|
+
&& session.key === currentSession.key))?.id;
|
|
77
|
+
}
|
|
68
78
|
async function recallSessionSafely(options) {
|
|
69
79
|
try {
|
|
70
80
|
return await (0, session_recall_1.recallSession)(options);
|
|
71
81
|
}
|
|
72
|
-
catch {
|
|
82
|
+
catch (error) {
|
|
83
|
+
if (options.summarize) {
|
|
84
|
+
(0, runtime_1.emitNervesEvent)({
|
|
85
|
+
component: "daemon",
|
|
86
|
+
event: "daemon.session_recall_summary_fallback",
|
|
87
|
+
message: "session recall summarization failed; using raw transcript",
|
|
88
|
+
meta: {
|
|
89
|
+
friendId: options.friendId,
|
|
90
|
+
channel: options.channel,
|
|
91
|
+
key: options.key,
|
|
92
|
+
error: error instanceof Error ? error.message : String(error),
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
try {
|
|
96
|
+
return await (0, session_recall_1.recallSession)({
|
|
97
|
+
...options,
|
|
98
|
+
summarize: undefined,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return { kind: "missing" };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
73
105
|
return { kind: "missing" };
|
|
74
106
|
}
|
|
75
107
|
}
|
|
108
|
+
function normalizeProgressOutcome(text) {
|
|
109
|
+
const trimmed = text.trim();
|
|
110
|
+
if (!trimmed || trimmed === "nothing yet" || trimmed === "nothing recent") {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
if (trimmed.startsWith("\"") && trimmed.endsWith("\"") && trimmed.length >= 2) {
|
|
114
|
+
return trimmed.slice(1, -1);
|
|
115
|
+
}
|
|
116
|
+
return trimmed;
|
|
117
|
+
}
|
|
118
|
+
function renderInnerProgressStatus(status) {
|
|
119
|
+
if (status.processing === "pending") {
|
|
120
|
+
return (0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
|
|
121
|
+
scope: "inner-delegation",
|
|
122
|
+
phase: "queued",
|
|
123
|
+
objective: status.queue,
|
|
124
|
+
outcomeText: `wake: ${status.wake}`,
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
if (status.processing === "started") {
|
|
128
|
+
return (0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
|
|
129
|
+
scope: "inner-delegation",
|
|
130
|
+
phase: "processing",
|
|
131
|
+
outcomeText: `wake: ${status.wake}`,
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
const completedOutcome = normalizeProgressOutcome(status.surfaced) ?? status.surfaced;
|
|
135
|
+
return (0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
|
|
136
|
+
scope: "inner-delegation",
|
|
137
|
+
phase: "completed",
|
|
138
|
+
objective: null,
|
|
139
|
+
outcomeText: completedOutcome,
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
76
142
|
exports.baseToolDefinitions = [
|
|
77
143
|
{
|
|
78
144
|
tool: {
|
|
@@ -725,7 +791,7 @@ exports.baseToolDefinitions = [
|
|
|
725
791
|
}
|
|
726
792
|
const sessionPath = (0, thoughts_1.getInnerDialogSessionPath)((0, identity_1.getAgentRoot)());
|
|
727
793
|
const pendingDir = (0, pending_1.getInnerDialogPendingDir)((0, identity_1.getAgentName)());
|
|
728
|
-
return (
|
|
794
|
+
return renderInnerProgressStatus((0, thoughts_1.readInnerDialogStatus)(sessionPath, pendingDir));
|
|
729
795
|
}
|
|
730
796
|
const sessFile = (0, config_1.resolveSessionPath)(friendId, channel, key);
|
|
731
797
|
const recall = await recallSessionSafely({
|
|
@@ -780,6 +846,17 @@ exports.baseToolDefinitions = [
|
|
|
780
846
|
fs.mkdirSync(pendingDir, { recursive: true });
|
|
781
847
|
const fileName = `${now}-${Math.random().toString(36).slice(2, 10)}.json`;
|
|
782
848
|
const filePath = path.join(pendingDir, fileName);
|
|
849
|
+
const delegatingBridgeId = findDelegatingBridgeId(ctx);
|
|
850
|
+
const delegatedFrom = isSelf
|
|
851
|
+
&& ctx?.currentSession
|
|
852
|
+
&& !(ctx.currentSession.friendId === "self" && ctx.currentSession.channel === "inner")
|
|
853
|
+
? {
|
|
854
|
+
friendId: ctx.currentSession.friendId,
|
|
855
|
+
channel: ctx.currentSession.channel,
|
|
856
|
+
key: ctx.currentSession.key,
|
|
857
|
+
...(delegatingBridgeId ? { bridgeId: delegatingBridgeId } : {}),
|
|
858
|
+
}
|
|
859
|
+
: undefined;
|
|
783
860
|
const envelope = {
|
|
784
861
|
from: agentName,
|
|
785
862
|
friendId,
|
|
@@ -787,6 +864,7 @@ exports.baseToolDefinitions = [
|
|
|
787
864
|
key,
|
|
788
865
|
content,
|
|
789
866
|
timestamp: now,
|
|
867
|
+
...(delegatedFrom ? { delegatedFrom } : {}),
|
|
790
868
|
};
|
|
791
869
|
fs.writeFileSync(filePath, JSON.stringify(envelope, null, 2));
|
|
792
870
|
if (isSelf) {
|
|
@@ -803,7 +881,7 @@ exports.baseToolDefinitions = [
|
|
|
803
881
|
queueMicrotask(() => {
|
|
804
882
|
void runInnerDialogTurn({ reason: "instinct" });
|
|
805
883
|
});
|
|
806
|
-
return (
|
|
884
|
+
return renderInnerProgressStatus({
|
|
807
885
|
queue: "queued to inner/dialog",
|
|
808
886
|
wake: "inline scheduled",
|
|
809
887
|
processing: "pending",
|
|
@@ -812,15 +890,16 @@ exports.baseToolDefinitions = [
|
|
|
812
890
|
}
|
|
813
891
|
else {
|
|
814
892
|
const turnResult = await runInnerDialogTurn({ reason: "instinct" });
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
893
|
+
const surfacedPreview = normalizeProgressOutcome((0, thoughts_1.formatSurfacedValue)((0, thoughts_1.extractThoughtResponseFromMessages)(turnResult?.messages ?? [])));
|
|
894
|
+
return (0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
|
|
895
|
+
scope: "inner-delegation",
|
|
896
|
+
phase: "completed",
|
|
897
|
+
objective: "queued to inner/dialog",
|
|
898
|
+
outcomeText: `wake: inline fallback\n${surfacedPreview}`,
|
|
899
|
+
}));
|
|
821
900
|
}
|
|
822
901
|
}
|
|
823
|
-
return (
|
|
902
|
+
return renderInnerProgressStatus({
|
|
824
903
|
queue: "queued to inner/dialog",
|
|
825
904
|
wake: "daemon requested",
|
|
826
905
|
processing: "pending",
|
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.handleBlueBubblesEvent = handleBlueBubblesEvent;
|
|
37
37
|
exports.recoverMissedBlueBubblesMessages = recoverMissedBlueBubblesMessages;
|
|
38
38
|
exports.createBlueBubblesWebhookHandler = createBlueBubblesWebhookHandler;
|
|
39
|
+
exports.sendProactiveBlueBubblesMessageToSession = sendProactiveBlueBubblesMessageToSession;
|
|
39
40
|
exports.drainAndSendPendingBlueBubbles = drainAndSendPendingBlueBubbles;
|
|
40
41
|
exports.startBlueBubblesApp = startBlueBubblesApp;
|
|
41
42
|
const fs = __importStar(require("node:fs"));
|
|
@@ -620,6 +621,7 @@ async function handleBlueBubblesNormalizedEvent(event, resolvedDeps, source) {
|
|
|
620
621
|
hasExistingGroupWithFamily,
|
|
621
622
|
enforceTrustGate: trust_gate_1.enforceTrustGate,
|
|
622
623
|
drainPending: pending_1.drainPending,
|
|
624
|
+
drainDeferredReturns: (deferredFriendId) => (0, pending_1.drainDeferredReturns)(resolvedDeps.getAgentName(), deferredFriendId),
|
|
623
625
|
runAgent: (msgs, cb, channel, sig, opts) => resolvedDeps.runAgent(msgs, cb, channel, sig, {
|
|
624
626
|
...opts,
|
|
625
627
|
toolContext: {
|
|
@@ -840,6 +842,114 @@ function findImessageHandle(friend) {
|
|
|
840
842
|
}
|
|
841
843
|
return undefined;
|
|
842
844
|
}
|
|
845
|
+
function extractChatIdentifierFromSessionKey(sessionKey) {
|
|
846
|
+
if (sessionKey.startsWith("chat:")) {
|
|
847
|
+
const chatGuid = sessionKey.slice("chat:".length).trim();
|
|
848
|
+
const parts = chatGuid.split(";");
|
|
849
|
+
return parts.length >= 3 ? parts[2]?.trim() || undefined : undefined;
|
|
850
|
+
}
|
|
851
|
+
if (sessionKey.startsWith("chat_identifier:")) {
|
|
852
|
+
const identifier = sessionKey.slice("chat_identifier:".length).trim();
|
|
853
|
+
return identifier || undefined;
|
|
854
|
+
}
|
|
855
|
+
return undefined;
|
|
856
|
+
}
|
|
857
|
+
function buildChatRefForSessionKey(friend, sessionKey) {
|
|
858
|
+
if (sessionKey.startsWith("chat:")) {
|
|
859
|
+
const chatGuid = sessionKey.slice("chat:".length).trim();
|
|
860
|
+
if (!chatGuid)
|
|
861
|
+
return null;
|
|
862
|
+
return {
|
|
863
|
+
chatGuid,
|
|
864
|
+
chatIdentifier: extractChatIdentifierFromSessionKey(sessionKey) ?? findImessageHandle(friend),
|
|
865
|
+
isGroup: chatGuid.includes(";+;"),
|
|
866
|
+
sessionKey,
|
|
867
|
+
sendTarget: { kind: "chat_guid", value: chatGuid },
|
|
868
|
+
participantHandles: [],
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
const chatIdentifier = extractChatIdentifierFromSessionKey(sessionKey) ?? findImessageHandle(friend);
|
|
872
|
+
if (!chatIdentifier)
|
|
873
|
+
return null;
|
|
874
|
+
return {
|
|
875
|
+
chatIdentifier,
|
|
876
|
+
isGroup: false,
|
|
877
|
+
sessionKey,
|
|
878
|
+
sendTarget: { kind: "chat_identifier", value: chatIdentifier },
|
|
879
|
+
participantHandles: [],
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
async function sendProactiveBlueBubblesMessageToSession(params, deps = {}) {
|
|
883
|
+
const resolvedDeps = { ...defaultDeps, ...deps };
|
|
884
|
+
const client = resolvedDeps.createClient();
|
|
885
|
+
const store = resolvedDeps.createFriendStore();
|
|
886
|
+
let friend;
|
|
887
|
+
try {
|
|
888
|
+
friend = await store.get(params.friendId);
|
|
889
|
+
}
|
|
890
|
+
catch {
|
|
891
|
+
friend = null;
|
|
892
|
+
}
|
|
893
|
+
if (!friend) {
|
|
894
|
+
(0, runtime_1.emitNervesEvent)({
|
|
895
|
+
level: "warn",
|
|
896
|
+
component: "senses",
|
|
897
|
+
event: "senses.bluebubbles_proactive_no_friend",
|
|
898
|
+
message: "proactive send skipped: friend not found",
|
|
899
|
+
meta: { friendId: params.friendId, sessionKey: params.sessionKey },
|
|
900
|
+
});
|
|
901
|
+
return { delivered: false, reason: "friend_not_found" };
|
|
902
|
+
}
|
|
903
|
+
if (!types_1.TRUSTED_LEVELS.has(friend.trustLevel ?? "stranger")) {
|
|
904
|
+
(0, runtime_1.emitNervesEvent)({
|
|
905
|
+
component: "senses",
|
|
906
|
+
event: "senses.bluebubbles_proactive_trust_skip",
|
|
907
|
+
message: "proactive send skipped: trust level not allowed",
|
|
908
|
+
meta: { friendId: params.friendId, sessionKey: params.sessionKey, trustLevel: friend.trustLevel ?? "unknown" },
|
|
909
|
+
});
|
|
910
|
+
return { delivered: false, reason: "trust_skip" };
|
|
911
|
+
}
|
|
912
|
+
const chat = buildChatRefForSessionKey(friend, params.sessionKey);
|
|
913
|
+
if (!chat) {
|
|
914
|
+
(0, runtime_1.emitNervesEvent)({
|
|
915
|
+
level: "warn",
|
|
916
|
+
component: "senses",
|
|
917
|
+
event: "senses.bluebubbles_proactive_no_handle",
|
|
918
|
+
message: "proactive send skipped: no iMessage handle found",
|
|
919
|
+
meta: { friendId: params.friendId, sessionKey: params.sessionKey },
|
|
920
|
+
});
|
|
921
|
+
return { delivered: false, reason: "missing_target" };
|
|
922
|
+
}
|
|
923
|
+
try {
|
|
924
|
+
await client.sendText({ chat, text: params.text });
|
|
925
|
+
(0, runtime_1.emitNervesEvent)({
|
|
926
|
+
component: "senses",
|
|
927
|
+
event: "senses.bluebubbles_proactive_sent",
|
|
928
|
+
message: "proactive bluebubbles message sent",
|
|
929
|
+
meta: {
|
|
930
|
+
friendId: params.friendId,
|
|
931
|
+
sessionKey: params.sessionKey,
|
|
932
|
+
chatGuid: chat.chatGuid ?? null,
|
|
933
|
+
chatIdentifier: chat.chatIdentifier ?? null,
|
|
934
|
+
},
|
|
935
|
+
});
|
|
936
|
+
return { delivered: true };
|
|
937
|
+
}
|
|
938
|
+
catch (error) {
|
|
939
|
+
(0, runtime_1.emitNervesEvent)({
|
|
940
|
+
level: "error",
|
|
941
|
+
component: "senses",
|
|
942
|
+
event: "senses.bluebubbles_proactive_send_error",
|
|
943
|
+
message: "proactive bluebubbles send failed",
|
|
944
|
+
meta: {
|
|
945
|
+
friendId: params.friendId,
|
|
946
|
+
sessionKey: params.sessionKey,
|
|
947
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
948
|
+
},
|
|
949
|
+
});
|
|
950
|
+
return { delivered: false, reason: "send_error" };
|
|
951
|
+
}
|
|
952
|
+
}
|
|
843
953
|
function scanPendingBlueBubblesFiles(pendingRoot) {
|
|
844
954
|
const results = [];
|
|
845
955
|
let friendIds;
|
package/dist/senses/cli.js
CHANGED
|
@@ -740,11 +740,12 @@ async function main(agentName, options) {
|
|
|
740
740
|
: [{ role: "system", content: await (0, prompt_1.buildSystem)("cli", undefined, resolvedContext) }];
|
|
741
741
|
// Per-turn pipeline input: CLI capabilities and pending dir
|
|
742
742
|
const cliCapabilities = (0, channel_1.getChannelCapabilities)("cli");
|
|
743
|
-
const
|
|
743
|
+
const currentAgentName = (0, identity_1.getAgentName)();
|
|
744
|
+
const pendingDir = (0, pending_1.getPendingDir)(currentAgentName, friendId, "cli", "session");
|
|
744
745
|
const summarize = (0, core_1.createSummarize)();
|
|
745
746
|
try {
|
|
746
747
|
await runCliSession({
|
|
747
|
-
agentName:
|
|
748
|
+
agentName: currentAgentName,
|
|
748
749
|
pasteDebounceMs,
|
|
749
750
|
messages: sessionMessages,
|
|
750
751
|
runTurn: async (messages, userInput, callbacks, signal) => {
|
|
@@ -765,6 +766,7 @@ async function main(agentName, options) {
|
|
|
765
766
|
externalId: localExternalId,
|
|
766
767
|
enforceTrustGate: trust_gate_1.enforceTrustGate,
|
|
767
768
|
drainPending: pending_1.drainPending,
|
|
769
|
+
drainDeferredReturns: (deferredFriendId) => (0, pending_1.drainDeferredReturns)(currentAgentName, deferredFriendId),
|
|
768
770
|
runAgent: (msgs, cb, channel, sig, opts) => (0, core_1.runAgent)(msgs, cb, channel, sig, {
|
|
769
771
|
...opts,
|
|
770
772
|
toolContext: {
|
|
@@ -4,6 +4,7 @@ exports.createDebugActivityController = createDebugActivityController;
|
|
|
4
4
|
const format_1 = require("../mind/format");
|
|
5
5
|
const phrases_1 = require("../mind/phrases");
|
|
6
6
|
const runtime_1 = require("../nerves/runtime");
|
|
7
|
+
const progress_story_1 = require("../heart/progress-story");
|
|
7
8
|
function createDebugActivityController(options) {
|
|
8
9
|
let queue = Promise.resolve();
|
|
9
10
|
let statusMessageGuid;
|
|
@@ -81,19 +82,31 @@ function createDebugActivityController(options) {
|
|
|
81
82
|
nextPhrase(pool);
|
|
82
83
|
return;
|
|
83
84
|
}
|
|
84
|
-
setStatus(
|
|
85
|
+
setStatus((0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
|
|
86
|
+
scope: "shared-work",
|
|
87
|
+
phase: "processing",
|
|
88
|
+
objective: `${nextPhrase(pool)}...`,
|
|
89
|
+
})));
|
|
85
90
|
},
|
|
86
91
|
onToolStart(name, args) {
|
|
87
92
|
hadToolRun = true;
|
|
88
93
|
followupShown = false;
|
|
89
94
|
const argSummary = Object.values(args).join(", ");
|
|
90
95
|
const detail = argSummary ? ` (${argSummary})` : "";
|
|
91
|
-
setStatus(
|
|
96
|
+
setStatus((0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
|
|
97
|
+
scope: "shared-work",
|
|
98
|
+
phase: "processing",
|
|
99
|
+
objective: `running ${name}${detail}...`,
|
|
100
|
+
})));
|
|
92
101
|
},
|
|
93
102
|
onToolEnd(name, summary, success) {
|
|
94
103
|
hadToolRun = true;
|
|
95
104
|
followupShown = false;
|
|
96
|
-
setStatus((0,
|
|
105
|
+
setStatus((0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
|
|
106
|
+
scope: "shared-work",
|
|
107
|
+
phase: "processing",
|
|
108
|
+
objective: (0, format_1.formatToolResult)(name, summary, success),
|
|
109
|
+
})));
|
|
97
110
|
},
|
|
98
111
|
onTextChunk(text) {
|
|
99
112
|
if (!text || !hadToolRun || followupShown) {
|
|
@@ -103,10 +116,18 @@ function createDebugActivityController(options) {
|
|
|
103
116
|
if (options.suppressFollowupPhraseStatus) {
|
|
104
117
|
return;
|
|
105
118
|
}
|
|
106
|
-
setStatus(
|
|
119
|
+
setStatus((0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
|
|
120
|
+
scope: "shared-work",
|
|
121
|
+
phase: "processing",
|
|
122
|
+
objective: `${nextPhrase(options.followupPhrases)}...`,
|
|
123
|
+
})));
|
|
107
124
|
},
|
|
108
125
|
onError(error) {
|
|
109
|
-
setStatus((0,
|
|
126
|
+
setStatus((0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
|
|
127
|
+
scope: "shared-work",
|
|
128
|
+
phase: "errored",
|
|
129
|
+
outcomeText: (0, format_1.formatError)(error),
|
|
130
|
+
})));
|
|
110
131
|
this.finish();
|
|
111
132
|
},
|
|
112
133
|
async drain() {
|