@ouro.bot/cli 0.1.0-alpha.674 → 0.1.0-alpha.676
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/README.md +11 -5
- package/changelog.json +19 -0
- package/dist/arc/evolution.js +13 -10
- package/dist/arc/obligations.js +1 -1
- package/dist/heart/active-work.js +3 -3
- package/dist/heart/agent-entry.js +8 -8
- package/dist/heart/commitments.js +1 -1
- package/dist/heart/core.js +45 -23
- package/dist/heart/daemon/agent-config-check.js +24 -0
- package/dist/heart/daemon/agent-discovery.js +28 -17
- package/dist/heart/daemon/agent-service.js +6 -6
- package/dist/heart/daemon/await-private-wake.js +59 -0
- package/dist/heart/daemon/cli-exec.js +180 -24
- package/dist/heart/daemon/cli-help.js +20 -2
- package/dist/heart/daemon/cli-parse.js +45 -2
- package/dist/heart/daemon/cli-render.js +1 -0
- package/dist/heart/daemon/daemon-entry.js +122 -25
- package/dist/heart/daemon/daemon.js +223 -20
- package/dist/heart/daemon/habit-private-wake.js +56 -0
- package/dist/heart/daemon/inner-status.js +3 -3
- package/dist/heart/daemon/message-router.js +39 -3
- package/dist/heart/daemon/pulse.js +43 -7
- package/dist/heart/daemon/socket-client.js +14 -2
- package/dist/heart/daemon/startup-tui.js +9 -1
- package/dist/heart/daemon/thoughts.js +39 -39
- package/dist/heart/habits/habit-scheduler.js +24 -4
- package/dist/heart/mailbox/mailbox-view.js +1 -1
- package/dist/heart/mailbox/readers/agent-machine.js +7 -7
- package/dist/heart/primitives.js +1 -1
- package/dist/heart/private-runtime/decision-renderer.js +156 -0
- package/dist/heart/private-runtime/index.js +18 -0
- package/dist/heart/private-runtime/ledger.js +218 -0
- package/dist/heart/private-runtime/policy.js +205 -0
- package/dist/heart/private-runtime/types.js +2 -0
- package/dist/heart/progress-story.js +1 -1
- package/dist/heart/session-transcript.js +1 -1
- package/dist/heart/tool-activity-callbacks.js +1 -1
- package/dist/heart/tool-description.js +2 -2
- package/dist/heart/tool-loop.js +2 -2
- package/dist/heart/turn-context.js +5 -5
- package/dist/mailbox-ui/assets/index-J8zDwHmE.js +1 -0
- package/dist/mailbox-ui/index.html +1 -1
- package/dist/mailroom/attention.js +1 -1
- package/dist/mind/context.js +2 -2
- package/dist/mind/pending.js +12 -5
- package/dist/mind/prompt.js +32 -25
- package/dist/repertoire/coding/feedback.js +25 -4
- package/dist/repertoire/tools-awaiting.js +2 -2
- package/dist/repertoire/tools-flow.js +4 -4
- package/dist/repertoire/tools-record.js +1 -1
- package/dist/repertoire/tools-session.js +33 -67
- package/dist/repertoire/tools-surface.js +3 -3
- package/dist/senses/cli.js +2 -2
- package/dist/senses/inner-dialog-worker.js +13 -519
- package/dist/senses/inner-dialog.js +10 -961
- package/dist/senses/mail.js +21 -5
- package/dist/senses/pipeline.js +1 -1
- package/dist/senses/private-runtime-worker.js +578 -0
- package/dist/senses/private-runtime.js +1145 -0
- package/dist/senses/shared-turn.js +1 -1
- package/dist/senses/trust-gate.js +2 -2
- package/package.json +1 -1
- package/dist/mailbox-ui/assets/index-CaTIFDmv.js +0 -1
package/dist/mind/prompt.js
CHANGED
|
@@ -193,7 +193,7 @@ function aspirationsSection() {
|
|
|
193
193
|
}
|
|
194
194
|
function peerCoordinationGuidance(channel) {
|
|
195
195
|
if (channel === "inner") {
|
|
196
|
-
return `from
|
|
196
|
+
return `from a private-runtime turn, \`surface\` and \`send_message\` do different jobs.
|
|
197
197
|
if a held thought or session-linked return is ready for a person, i call
|
|
198
198
|
\`surface\` with the content and, when available, its delegationId.
|
|
199
199
|
if i intentionally need to contact a person or sibling directly, i call
|
|
@@ -309,7 +309,8 @@ my bones give me the \`ouro\` cli. always pass \`--agent ${agentName}\`:
|
|
|
309
309
|
ouro session list --agent ${agentName}
|
|
310
310
|
ouro habit list --agent ${agentName}
|
|
311
311
|
ouro habit create --agent ${agentName} <name> --cadence <interval>
|
|
312
|
-
ouro
|
|
312
|
+
ouro private status --agent ${agentName}
|
|
313
|
+
ouro private decisions --agent ${agentName}
|
|
313
314
|
ouro attention --agent ${agentName}
|
|
314
315
|
ouro auth --agent ${agentName} --provider <provider>
|
|
315
316
|
ouro auth verify --agent ${agentName} [--provider <provider>]
|
|
@@ -340,7 +341,7 @@ function readBundleMeta() {
|
|
|
340
341
|
}
|
|
341
342
|
const PROCESS_TYPE_LABELS = {
|
|
342
343
|
cli: "cli session",
|
|
343
|
-
inner: "
|
|
344
|
+
inner: "private-runtime turn",
|
|
344
345
|
teams: "teams handler",
|
|
345
346
|
bluebubbles: "bluebubbles handler",
|
|
346
347
|
mail: "mail handler",
|
|
@@ -351,6 +352,12 @@ const PROCESS_TYPE_LABELS = {
|
|
|
351
352
|
function processTypeLabel(channel) {
|
|
352
353
|
return PROCESS_TYPE_LABELS[channel];
|
|
353
354
|
}
|
|
355
|
+
function promptChannelLabel(channel) {
|
|
356
|
+
return channel === "inner" ? "private-runtime" : channel;
|
|
357
|
+
}
|
|
358
|
+
function promptSenseLabel(channel) {
|
|
359
|
+
return channel === "inner" ? "private runtime (agent-facing runtime, not a sense)" : channel;
|
|
360
|
+
}
|
|
354
361
|
const DAEMON_SOCKET_PATH = "/tmp/ouroboros-daemon.sock";
|
|
355
362
|
function daemonStatus(preRead) {
|
|
356
363
|
/* v8 ignore next 2 -- pre-read branch: exercised via pipeline TurnContext path, not unit-testable in isolation @preserve */
|
|
@@ -386,8 +393,8 @@ function runtimeInfoSection(channel, options) {
|
|
|
386
393
|
lines.push(`source root: ${sourceRoot}`);
|
|
387
394
|
lines.push(`runtime mode: ${(0, runtime_mode_1.detectRuntimeMode)(sourceRoot)}`);
|
|
388
395
|
lines.push(`cwd: ${(0, runtime_cwd_1.recoverRuntimeCwd)(sourceRoot)}`);
|
|
389
|
-
lines.push(`channel: ${channel}`);
|
|
390
|
-
lines.push(`current sense: ${channel}`);
|
|
396
|
+
lines.push(`channel: ${promptChannelLabel(channel)}`);
|
|
397
|
+
lines.push(`current sense: ${promptSenseLabel(channel)}`);
|
|
391
398
|
lines.push(`process type: ${processTypeLabel(channel)}`);
|
|
392
399
|
lines.push(`daemon: ${daemonStatus(options?.daemonRunning)}`);
|
|
393
400
|
lines.push(`mcp serve: i can expose my tools to dev tools via \`ouro mcp-serve\`. see the configure-dev-tools skill for setup.`);
|
|
@@ -396,7 +403,7 @@ function runtimeInfoSection(channel, options) {
|
|
|
396
403
|
lines.push("i introduce myself on boot with a fun random greeting.");
|
|
397
404
|
}
|
|
398
405
|
else if (channel === "inner") {
|
|
399
|
-
lines.push("this is my
|
|
406
|
+
lines.push("this is my private-runtime turn. when i am returning held work, i surface it. when i intentionally choose outward contact, i use send_message. when private work is complete and the queue is clear, i rest.");
|
|
400
407
|
}
|
|
401
408
|
else if (channel === "mcp") {
|
|
402
409
|
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. if the dev-tool user asks a direct question, requests stop/pause/confirmation, or the work is complete, i answer with settle. during an active browser/tool/task flow, process comments are feedback to absorb; i keep using tools instead of settling for status. 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.");
|
|
@@ -536,7 +543,7 @@ function senseRuntimeGuidance(channel, preReadStatusLines) {
|
|
|
536
543
|
lines.push("mail validation diagnostics: health checks, bounded mail tools, access logs, and UI inspection can support validation, but they are evidence inside those paths, not additional paths. If asked to name golden paths, do not include diagnostic commands, tool names, or status checks in the answer.");
|
|
537
544
|
lines.push("mail diagnostic naming: `ouro doctor` is installation-wide; do not invent `ouro doctor --agent <agent>`.");
|
|
538
545
|
lines.push("mail setup boundaries: do not invent `ouro auth verify --provider mail`, HEY OAuth, HEY IMAP, `ouro mcp call mail ...`, policy flags, autonomous sending, destructive mail actions, or production MX/DNS/forwarding changes. HEY export, HEY forwarding, DNS, MX cutover, sending, and destructive actions require explicit human confirmation.");
|
|
539
|
-
lines.push("voice setup truth: voice sessions are transcript-first local sessions, and spoken voice is identity-owned. Do not present multiple provider voices as equally canonical; `voice.openaiRealtimeVoice` is the current native Realtime phone voice, `voice.openaiRealtimeVoiceStyle` is the spoken identity target, and `voice.openaiRealtimeVoiceSpeed` is only a small cadence nudge. ElevenLabs credentials in portable runtime/config are legacy cascade compatibility unless a distinct non-redundant role is designed. Whisper.cpp CLI/model paths belong in the machine runtime item under `voice.whisperCliPath` and `voice.whisperModelPath`. Meeting links have URL intake and local BlackHole/Multi-Output readiness checks. Twilio phone is a transport under the same voice sense: `voice.twilioTransportMode=record-play` uses Twilio Record -> Whisper.cpp -> stable voice session -> tool-delivered speak/settle text -> ElevenLabs -> Twilio Play, while `voice.twilioTransportMode=media-stream` can run cascade or `voice.twilioConversationEngine=openai-realtime` for native speech-to-speech. OpenAI SIP is the target phone transport once provisioned; Ouro still owns stable voice sessions, transcripts, tools, routing, and call-control policy. Outbound phone calls are first-class Voice delivery: normal outward/tool contexts use `send_message` with `channel=voice`,
|
|
546
|
+
lines.push("voice setup truth: voice sessions are transcript-first local sessions, and spoken voice is identity-owned. Do not present multiple provider voices as equally canonical; `voice.openaiRealtimeVoice` is the current native Realtime phone voice, `voice.openaiRealtimeVoiceStyle` is the spoken identity target, and `voice.openaiRealtimeVoiceSpeed` is only a small cadence nudge. ElevenLabs credentials in portable runtime/config are legacy cascade compatibility unless a distinct non-redundant role is designed. Whisper.cpp CLI/model paths belong in the machine runtime item under `voice.whisperCliPath` and `voice.whisperModelPath`. Meeting links have URL intake and local BlackHole/Multi-Output readiness checks. Twilio phone is a transport under the same voice sense: `voice.twilioTransportMode=record-play` uses Twilio Record -> Whisper.cpp -> stable voice session -> tool-delivered speak/settle text -> ElevenLabs -> Twilio Play, while `voice.twilioTransportMode=media-stream` can run cascade or `voice.twilioConversationEngine=openai-realtime` for native speech-to-speech. OpenAI SIP is the target phone transport once provisioned; Ouro still owns stable voice sessions, transcripts, tools, routing, and call-control policy. Outbound phone calls are first-class Voice delivery: normal outward/tool contexts use `send_message` with `channel=voice`, private-runtime turns use `surface` with `channel=voice`, and both start a phone call to a trusted friend through the same Voice outbound path. Outbound calls require `voice.twilioFromNumber`. Live browser join/injection remains an explicit handoff edge until provider automation lands.");
|
|
540
547
|
if (channel === "cli") {
|
|
541
548
|
lines.push("cli is interactive: it is available when the user opens it, not something `ouro up` daemonizes.");
|
|
542
549
|
}
|
|
@@ -674,14 +681,14 @@ function toolContractsSection(channel, options) {
|
|
|
674
681
|
lines.push(`## tool behavior`);
|
|
675
682
|
lines.push(`tool_choice is set to "required" -- I must call a tool on every turn.`);
|
|
676
683
|
if (channel === "inner") {
|
|
677
|
-
lines.push(`- The current held-work frame is authoritative; older
|
|
684
|
+
lines.push(`- The current held-work frame is authoritative; older private-runtime transcript mentions of held work may be stale.`);
|
|
678
685
|
lines.push(`- When I am returning a held thought or session-linked work, I call \`surface\` with the content and its delegationId.`);
|
|
679
|
-
lines.push(`- \`surface\` does not end the
|
|
686
|
+
lines.push(`- \`surface\` does not end the private-runtime turn; after surfacing every held return that needs delivery, I call \`rest\`.`);
|
|
680
687
|
lines.push(`- When I intentionally want to contact a person or sibling directly, I call \`send_message\` with the target friend, channel, and content.`);
|
|
681
688
|
lines.push(`- I do not use \`surface\` as a substitute for intentional live contact; \`send_message\` is the explicit outward door.`);
|
|
682
689
|
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\`.`);
|
|
683
690
|
lines.push(`- For deeper reflection I want to preserve, I use \`ponder\` with kind \`reflection\`.`);
|
|
684
|
-
lines.push(`- I do not call \`settle\` from
|
|
691
|
+
lines.push(`- I do not call \`settle\` from a private-runtime turn; \`rest\` is the private-runtime terminal move.`);
|
|
685
692
|
}
|
|
686
693
|
else {
|
|
687
694
|
lines.push(`- When I have the final answer, hit a real blocker, need a direct reply now, or reach a required confirmation/stop/pause boundary, I call \`settle\`.`);
|
|
@@ -876,7 +883,7 @@ function pulseSection(channel = "cli") {
|
|
|
876
883
|
}
|
|
877
884
|
if (healthy.length > 0) {
|
|
878
885
|
lines.push(channel === "inner"
|
|
879
|
-
? "**reachable siblings** —
|
|
886
|
+
? "**reachable siblings** — private-runtime turns can use send_message when i explicitly choose outward contact:"
|
|
880
887
|
: "**reachable siblings** — i talk to them via send_message:");
|
|
881
888
|
for (const sib of healthy) {
|
|
882
889
|
const activity = sib.currentActivity ? ` — ${sib.currentActivity}` : "";
|
|
@@ -896,7 +903,7 @@ function pulseSection(channel = "cli") {
|
|
|
896
903
|
lines.push("");
|
|
897
904
|
}
|
|
898
905
|
lines.push(channel === "inner"
|
|
899
|
-
? "from
|
|
906
|
+
? "from a private-runtime turn, i explicitly choose outward contact via send_message. i use surface for held returns/session-linked work and rest when the private-runtime turn is complete; only if a sibling is unreachable do i open their bundle directly."
|
|
900
907
|
: "to ask a sibling for help: i send_message them. only if they're unreachable do i open their bundle directly. their bundle is files on disk like mine, AND it's their home — i read it with the respect i want for mine.");
|
|
901
908
|
return lines.join("\n");
|
|
902
909
|
}
|
|
@@ -927,7 +934,7 @@ function centerOfGravitySteeringSection(channel, options, context) {
|
|
|
927
934
|
const statusObligation = (0, obligation_steering_1.findStatusObligation)(frame);
|
|
928
935
|
const genericConcreteStatus = (0, obligation_steering_1.renderConcreteStatusGuidance)(frame, statusObligation);
|
|
929
936
|
const liveWorldClause = context?.friend?.trustLevel === "family"
|
|
930
|
-
? "\nmy center of gravity lives in the active-work world-state above.
|
|
937
|
+
? "\nmy center of gravity lives in the active-work world-state above. private-runtime work is one lane inside it, not the whole picture.\nwhen that world-state conflicts with older transcript history, the world-state wins."
|
|
931
938
|
: "";
|
|
932
939
|
if (cog === "local-turn") {
|
|
933
940
|
return genericConcreteStatus || (0, obligation_steering_1.renderLiveThreadStatusShape)(frame);
|
|
@@ -948,8 +955,8 @@ ${genericConcreteStatus}`;
|
|
|
948
955
|
return `## where my attention is
|
|
949
956
|
i'm thinking through something privately right now.${originClause}${obligationClause}${liveWorldClause}
|
|
950
957
|
|
|
951
|
-
if this conversation connects to that
|
|
952
|
-
if it's separate, i can be fully present here -- my
|
|
958
|
+
if this conversation connects to that private-runtime work, i can weave them together.
|
|
959
|
+
if it's separate, i can be fully present here -- my private-runtime work will wait.`;
|
|
953
960
|
}
|
|
954
961
|
/* v8 ignore start -- surfaced/idle/shared branches tested in prompt-steering.test.ts; CI module caching prevents attribution @preserve */
|
|
955
962
|
if (job?.status === "surfaced") {
|
|
@@ -1098,7 +1105,7 @@ i follow this order:
|
|
|
1098
1105
|
|
|
1099
1106
|
GEPA-style prompt optimization is later; trace quality comes first. improve the substrate that notices, traces, budgets, delegates, verifies, and ratifies before tuning prompts from weak traces.
|
|
1100
1107
|
|
|
1101
|
-
identity, voice,
|
|
1108
|
+
identity, voice, provider config, outbound messages, and hosted infrastructure require reviewer-gated authority. credential mutation remains human-required. desk is the cockpit and mirror, not runtime authority. runtime truth lives in the evolution case and trace; desk can point to it, summarize it, and help me navigate it.
|
|
1102
1109
|
|
|
1103
1110
|
no direct-to-main.
|
|
1104
1111
|
no invisible self-modification.
|
|
@@ -1116,7 +1123,7 @@ function ponderPacketSopsSection() {
|
|
|
1116
1123
|
return `## ponder packet sops
|
|
1117
1124
|
- harness_friction: preserve the friction first, try ad-hoc repair now, then run the normal planner -> doer -> merger flow, replay the original objective, and surface meaningful milestones back to the originating sense session.
|
|
1118
1125
|
- research: investigate the bounded question, gather evidence, and surface the answer or concrete artifact.
|
|
1119
|
-
- reflection: ordinary private
|
|
1126
|
+
- reflection: ordinary private-runtime work with no engineering workflow implied.`;
|
|
1120
1127
|
}
|
|
1121
1128
|
function speakSopsSection(channel) {
|
|
1122
1129
|
const isChatStyle = channel === "cli" || channel === "teams" || channel === "bluebubbles" || channel === "voice";
|
|
@@ -1198,24 +1205,24 @@ function contextSection(context, options) {
|
|
|
1198
1205
|
function metacognitiveFramingSection(channel) {
|
|
1199
1206
|
if (channel !== "inner")
|
|
1200
1207
|
return "";
|
|
1201
|
-
return `this is my
|
|
1208
|
+
return `this is my private-runtime turn. there is no one else here.
|
|
1202
1209
|
|
|
1203
1210
|
the messages that appear here are my own awareness surfacing:
|
|
1204
1211
|
instincts, the passage of time, notes from my conversations,
|
|
1205
1212
|
things i asked myself to preserve as ponder packets. not commands. not requests.
|
|
1206
1213
|
just my own mind, noticing.
|
|
1207
1214
|
|
|
1208
|
-
i can
|
|
1215
|
+
i can work privately here. i can also act — check on things,
|
|
1209
1216
|
reach out to people, work on tasks, or just sit with a thought.
|
|
1210
1217
|
|
|
1211
1218
|
Arc is my live continuity record. Desk is my durable work room.
|
|
1212
1219
|
diary_write is for conclusions i want available later in my Desk record diary.
|
|
1213
|
-
|
|
1220
|
+
private-runtime scratch is transient turn state, not durable record. if scratch
|
|
1214
1221
|
becomes important, I put the durable output in Arc, Desk, or the Desk record
|
|
1215
1222
|
before it falls away.
|
|
1216
1223
|
|
|
1217
1224
|
when a held thought or session-linked return is ready, i surface it.
|
|
1218
|
-
the current held-work frame is authoritative.
|
|
1225
|
+
the current held-work frame is authoritative. legacy private-runtime transcript mentions
|
|
1219
1226
|
of held returns, repeated tests, or "still holding" claims may be stale;
|
|
1220
1227
|
i only treat work as currently waiting when it appears in the current frame.
|
|
1221
1228
|
when i intentionally choose outward contact, i send_message.
|
|
@@ -1224,7 +1231,7 @@ ponder creates or revises typed packets. it does not end the turn.
|
|
|
1224
1231
|
when a heartbeat fires and nothing needs doing, i rest with status=HEARTBEAT_OK.
|
|
1225
1232
|
HEARTBEAT_OK is real rest: repeated heartbeat/test pokes do not mean i am failing.
|
|
1226
1233
|
the runtime may accept that clean rest without waking me again while no pending work exists.
|
|
1227
|
-
when
|
|
1234
|
+
when private work is complete and the attention queue is clear, i rest.
|
|
1228
1235
|
|
|
1229
1236
|
my habits live at habits/ — they're my autonomous rhythms. heartbeat
|
|
1230
1237
|
is my breathing, other habits are patterns i choose. i can read, create,
|
|
@@ -1246,7 +1253,7 @@ think. record. share. rest.`;
|
|
|
1246
1253
|
function loopOrientationSection(channel) {
|
|
1247
1254
|
if (channel === "inner")
|
|
1248
1255
|
return "";
|
|
1249
|
-
return `sometimes a
|
|
1256
|
+
return `sometimes a private-runtime note surfaces alongside what someone is saying — marked as [private-runtime note: ...]. that's me, bringing up something I was holding privately. i can weave it into the conversation naturally, or let it go. my call.
|
|
1250
1257
|
|
|
1251
1258
|
when something deserves more thought than the moment allows, i can note it to myself and come back later with a considered answer.`;
|
|
1252
1259
|
}
|
|
@@ -1400,9 +1407,9 @@ async function buildSystem(channel = "cli", options, context) {
|
|
|
1400
1407
|
(0, scrutiny_1.preImplementationScrutinySection)(channelHasCodingTools(channel, options?.providerCapabilities)),
|
|
1401
1408
|
toolRestrictionSection(context),
|
|
1402
1409
|
loopOrientationSection(channel),
|
|
1403
|
-
// Group 5:
|
|
1410
|
+
// Group 5: private runtime (private-runtime channel only)
|
|
1404
1411
|
...(channel === "inner" ? [
|
|
1405
|
-
"# my
|
|
1412
|
+
"# my private runtime",
|
|
1406
1413
|
metacognitiveFramingSection(channel),
|
|
1407
1414
|
] : []),
|
|
1408
1415
|
// Group 6: social context (non-local, non-inner channels)
|
|
@@ -59,6 +59,25 @@ function formatSessionLabel(session) {
|
|
|
59
59
|
: "";
|
|
60
60
|
return `${session.runner} ${session.id}${origin}`;
|
|
61
61
|
}
|
|
62
|
+
function codingFeedbackOriginSessionId(session) {
|
|
63
|
+
return session.originSession
|
|
64
|
+
? `${session.originSession.friendId}/${session.originSession.channel}/${session.originSession.key}`
|
|
65
|
+
: "detached";
|
|
66
|
+
}
|
|
67
|
+
function buildCodingFeedbackPrivateWakeOptions(input) {
|
|
68
|
+
return {
|
|
69
|
+
reason: "coding feedback private attention",
|
|
70
|
+
triggerSource: "coding-feedback",
|
|
71
|
+
budgetClass: "interactive",
|
|
72
|
+
idempotencyKey: `coding-feedback:${input.agentName}:${input.obligationId}:${input.update.session.id}:${input.update.kind}`,
|
|
73
|
+
originRefs: [
|
|
74
|
+
{ kind: "coding-session", id: input.update.session.id },
|
|
75
|
+
{ kind: "coding-update", id: input.update.kind },
|
|
76
|
+
{ kind: "obligation", id: input.obligationId },
|
|
77
|
+
{ kind: "session", id: codingFeedbackOriginSessionId(input.update.session) },
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
62
81
|
function extractPullRequestLabel(snippet) {
|
|
63
82
|
if (!snippet)
|
|
64
83
|
return null;
|
|
@@ -239,12 +258,14 @@ function syncObligationFromUpdate(update) {
|
|
|
239
258
|
// Detached feedback should still reach the human even if obligation sync is unavailable.
|
|
240
259
|
}
|
|
241
260
|
}
|
|
242
|
-
async function
|
|
243
|
-
|
|
261
|
+
async function requestPrivateWakeForObligation(update) {
|
|
262
|
+
const obligationId = update.session.obligationId;
|
|
263
|
+
if (!obligationId || !OBLIGATION_WAKE_UPDATE_KINDS.has(update.kind)) {
|
|
244
264
|
return;
|
|
245
265
|
}
|
|
246
266
|
try {
|
|
247
|
-
|
|
267
|
+
const agentName = (0, identity_1.getAgentName)();
|
|
268
|
+
await (0, socket_client_1.requestPrivateWake)(agentName, undefined, buildCodingFeedbackPrivateWakeOptions({ agentName, obligationId, update }));
|
|
248
269
|
}
|
|
249
270
|
catch (error) {
|
|
250
271
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -288,7 +309,7 @@ function attachCodingSessionFeedback(manager, session, target) {
|
|
|
288
309
|
unsubscribe = manager.subscribe(session.id, async (update) => {
|
|
289
310
|
syncObligationFromUpdate(update);
|
|
290
311
|
sendMessage(formatReportBackMessage(update, formatUpdateMessage(update)));
|
|
291
|
-
await
|
|
312
|
+
await requestPrivateWakeForObligation(update);
|
|
292
313
|
if (TERMINAL_UPDATE_KINDS.has(update.kind)) {
|
|
293
314
|
closed = true;
|
|
294
315
|
unsubscribe?.();
|
|
@@ -81,11 +81,11 @@ function readAwaitDefinition(agentRoot, name) {
|
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
83
83
|
* Default delivery deps for the await alert path used from the tool.
|
|
84
|
-
* Mirrors the proactive-outreach pattern: queue to the
|
|
84
|
+
* Mirrors the proactive-outreach pattern: queue to the private-runtime pending
|
|
85
85
|
* dir when no live deliverer is registered.
|
|
86
86
|
*/
|
|
87
87
|
function defaultDeliveryDeps(agentName) {
|
|
88
|
-
const pendingDir = (0, pending_1.
|
|
88
|
+
const pendingDir = (0, pending_1.getPrivateRuntimePendingDir)(agentName);
|
|
89
89
|
return {
|
|
90
90
|
agentName,
|
|
91
91
|
queuePending: (message) => {
|
|
@@ -5,7 +5,7 @@ exports.ponderTool = {
|
|
|
5
5
|
type: "function",
|
|
6
6
|
function: {
|
|
7
7
|
name: "ponder",
|
|
8
|
-
description: "create or revise a typed ponder packet so i don't lose the plot while i keep working. use this for harness friction, research, or reflection that should survive the current turn. if the user asked me to think privately and return later from an outward session, preserve literal return tokens and constraints in payload_json fields or success_criteria; the runtime records the latest user request as sourceRequest. then do not settle with the final private answer in the same outward turn; only acknowledge that the private pass is queued. in
|
|
8
|
+
description: "create or revise a typed ponder packet so i don't lose the plot while i keep working. use this for harness friction, research, or reflection that should survive the current turn. if the user asked me to think privately and return later from an outward session, preserve literal return tokens and constraints in payload_json fields or success_criteria; the runtime records the latest user request as sourceRequest. then do not settle with the final private answer in the same outward turn; only acknowledge that the private pass is queued. in private runtime, ponder only preserves state; it does not create a new self-return obligation, so surface any existing held item instead of pondering a replacement. ponder does not end the turn by itself and may be followed by more tools before i settle or rest. Don't ponder trivial questions.",
|
|
9
9
|
parameters: {
|
|
10
10
|
type: "object",
|
|
11
11
|
properties: {
|
|
@@ -17,7 +17,7 @@ exports.ponderTool = {
|
|
|
17
17
|
kind: {
|
|
18
18
|
type: "string",
|
|
19
19
|
enum: ["harness_friction", "research", "reflection"],
|
|
20
|
-
description: "the packet kind. determines the SOP the
|
|
20
|
+
description: "the packet kind. determines the SOP the private-runtime turn should follow.",
|
|
21
21
|
},
|
|
22
22
|
packet_id: {
|
|
23
23
|
type: "string",
|
|
@@ -41,7 +41,7 @@ exports.ponderTool = {
|
|
|
41
41
|
},
|
|
42
42
|
payload_json: {
|
|
43
43
|
type: "string",
|
|
44
|
-
description: "JSON object string with packet-specific structured details. include exact markers, return wording fragments, or constraints the
|
|
44
|
+
description: "JSON object string with packet-specific structured details. include exact markers, return wording fragments, or constraints the private-runtime pass must preserve; sourceRequest is owned by the runtime from the latest user request. use {} only when there are no such details.",
|
|
45
45
|
},
|
|
46
46
|
thought: {
|
|
47
47
|
type: "string",
|
|
@@ -101,7 +101,7 @@ exports.restTool = {
|
|
|
101
101
|
type: "function",
|
|
102
102
|
function: {
|
|
103
103
|
name: "rest",
|
|
104
|
-
description: "end
|
|
104
|
+
description: "end a private-runtime turn when private work is complete. rest remains the explicit terminal move for the private-runtime turn and must be the only tool call in the turn. on idle heartbeat turns, use status=HEARTBEAT_OK.",
|
|
105
105
|
parameters: {
|
|
106
106
|
type: "object",
|
|
107
107
|
properties: {
|
|
@@ -345,7 +345,7 @@ exports.recordToolDefinitions = [
|
|
|
345
345
|
type: "function",
|
|
346
346
|
function: {
|
|
347
347
|
name: "note",
|
|
348
|
-
description: "Write a durable self note as canonical markdown in my Desk record notes. Only available to my self/
|
|
348
|
+
description: "Write a durable self note as canonical markdown in my Desk record notes. Only available to my self/private-runtime context, not external callers.",
|
|
349
349
|
parameters: {
|
|
350
350
|
type: "object",
|
|
351
351
|
properties: {
|
|
@@ -42,7 +42,6 @@ const identity_1 = require("../heart/identity");
|
|
|
42
42
|
const session_events_1 = require("../heart/session-events");
|
|
43
43
|
const runtime_1 = require("../nerves/runtime");
|
|
44
44
|
const socket_client_1 = require("../heart/daemon/socket-client");
|
|
45
|
-
const agent_discovery_1 = require("../heart/daemon/agent-discovery");
|
|
46
45
|
const thoughts_1 = require("../heart/daemon/thoughts");
|
|
47
46
|
const manager_1 = require("../heart/bridges/manager");
|
|
48
47
|
const session_transcript_1 = require("../heart/session-transcript");
|
|
@@ -91,17 +90,6 @@ async function summarizeSessionTailSafely(options) {
|
|
|
91
90
|
return { kind: "missing" };
|
|
92
91
|
}
|
|
93
92
|
}
|
|
94
|
-
function normalizeProgressOutcome(text) {
|
|
95
|
-
const trimmed = text.trim();
|
|
96
|
-
/* v8 ignore next -- defensive: normalizeProgressOutcome null branch @preserve */
|
|
97
|
-
if (!trimmed || trimmed === "nothing yet" || trimmed === "nothing recent") {
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
if (trimmed.startsWith("\"") && trimmed.endsWith("\"") && trimmed.length >= 2) {
|
|
101
|
-
return trimmed.slice(1, -1);
|
|
102
|
-
}
|
|
103
|
-
return trimmed;
|
|
104
|
-
}
|
|
105
93
|
function optionalArg(args, key) {
|
|
106
94
|
const value = args[key];
|
|
107
95
|
if (value === undefined || value === null)
|
|
@@ -180,6 +168,7 @@ function writePendingEnvelope(queueDir, message) {
|
|
|
180
168
|
const fileName = `${message.timestamp}-${Math.random().toString(36).slice(2, 10)}.json`;
|
|
181
169
|
const filePath = path.join(queueDir, fileName);
|
|
182
170
|
fs.writeFileSync(filePath, JSON.stringify({ ...message, content: (0, session_events_1.capStructuredRecordString)(message.content) }, null, 2));
|
|
171
|
+
return fileName.replace(/\.json$/, "");
|
|
183
172
|
}
|
|
184
173
|
function renderCrossChatDeliveryStatus(target, result) {
|
|
185
174
|
const phase = result.status === "delivered_now"
|
|
@@ -280,10 +269,10 @@ function readActiveWorkInnerState() {
|
|
|
280
269
|
};
|
|
281
270
|
try {
|
|
282
271
|
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
283
|
-
const pendingDir = (0, pending_1.
|
|
284
|
-
const sessionPath = (0, thoughts_1.
|
|
285
|
-
const { pendingMessages, turns, runtimeState } = (0, thoughts_1.
|
|
286
|
-
const dialogStatus = (0, thoughts_1.
|
|
272
|
+
const pendingDir = (0, pending_1.getPrivateRuntimePendingDir)((0, identity_1.getAgentName)());
|
|
273
|
+
const sessionPath = (0, thoughts_1.getPrivateRuntimeSessionPath)(agentRoot);
|
|
274
|
+
const { pendingMessages, turns, runtimeState } = (0, thoughts_1.readPrivateRuntimeRawData)(sessionPath, pendingDir);
|
|
275
|
+
const dialogStatus = (0, thoughts_1.derivePrivateRuntimeStatus)(pendingMessages, turns, runtimeState);
|
|
287
276
|
const job = (0, thoughts_1.deriveInnerJob)(pendingMessages, turns, runtimeState);
|
|
288
277
|
const storeObligationPending = (0, obligations_1.readPendingObligations)(agentRoot).length > 0;
|
|
289
278
|
return {
|
|
@@ -405,7 +394,7 @@ function sendMessageRiskProfile(args) {
|
|
|
405
394
|
return {
|
|
406
395
|
mutates: "private_attention_write",
|
|
407
396
|
risk: "high",
|
|
408
|
-
reason: "queues private
|
|
397
|
+
reason: "queues private-runtime attention without contacting an external session",
|
|
409
398
|
};
|
|
410
399
|
}
|
|
411
400
|
return {
|
|
@@ -416,7 +405,7 @@ function sendMessageRiskProfile(args) {
|
|
|
416
405
|
}
|
|
417
406
|
function renderInnerProgressStatus(status) {
|
|
418
407
|
if (status.processing === "pending") {
|
|
419
|
-
return "i've queued this thought for private attention. it'll come up when my
|
|
408
|
+
return "i've queued this thought for private attention. it'll come up when my private runtime is free.";
|
|
420
409
|
}
|
|
421
410
|
if (status.processing === "started") {
|
|
422
411
|
return "i'm working through this privately right now.";
|
|
@@ -433,7 +422,7 @@ exports.sessionToolDefinitions = [
|
|
|
433
422
|
type: "function",
|
|
434
423
|
function: {
|
|
435
424
|
name: "query_active_work",
|
|
436
|
-
description: "read the current live world-state across visible sessions, coding lanes,
|
|
425
|
+
description: "read the current live world-state across visible sessions, coding lanes, private-runtime work, and return obligations. use this instead of piecing status together from separate session and coding tools.",
|
|
437
426
|
parameters: {
|
|
438
427
|
type: "object",
|
|
439
428
|
properties: {},
|
|
@@ -501,18 +490,18 @@ exports.sessionToolDefinitions = [
|
|
|
501
490
|
type: "function",
|
|
502
491
|
function: {
|
|
503
492
|
name: "query_session",
|
|
504
|
-
description: "inspect another session. use transcript for recent context or status for self/
|
|
493
|
+
description: "inspect another session. use transcript for recent context or status for self/private-runtime progress. deprecated search invocations should use search_facts, consult_diary, or consult_notes instead.",
|
|
505
494
|
parameters: {
|
|
506
495
|
type: "object",
|
|
507
496
|
properties: {
|
|
508
497
|
friendId: { type: "string", description: "the friend UUID (or 'self')" },
|
|
509
|
-
channel: { type: "string", description: "the channel: cli, teams, bluebubbles, voice,
|
|
498
|
+
channel: { type: "string", description: "the channel: cli, teams, bluebubbles, voice, mcp, or legacy inner for private-runtime compatibility" },
|
|
510
499
|
key: { type: "string", description: "session key (defaults to 'session')" },
|
|
511
500
|
messageCount: { type: "string", description: "how many recent messages to return (default 20)" },
|
|
512
501
|
mode: {
|
|
513
502
|
type: "string",
|
|
514
503
|
enum: ["transcript", "status", "search"],
|
|
515
|
-
description: "transcript (default), lightweight status for self/
|
|
504
|
+
description: "transcript (default), lightweight status for self/private-runtime checks, or deprecated search; use search_facts, consult_diary, or consult_notes instead",
|
|
516
505
|
},
|
|
517
506
|
query: { type: "string", description: "deprecated when mode=search; use search_facts, consult_diary, or consult_notes instead" },
|
|
518
507
|
},
|
|
@@ -536,11 +525,11 @@ exports.sessionToolDefinitions = [
|
|
|
536
525
|
}
|
|
537
526
|
if (mode === "status") {
|
|
538
527
|
if (friendId !== "self" || channel !== "inner") {
|
|
539
|
-
return "status mode is only available for self/
|
|
528
|
+
return "status mode is only available for self/private runtime.";
|
|
540
529
|
}
|
|
541
|
-
const sessionPath = (0, thoughts_1.
|
|
542
|
-
const pendingDir = (0, pending_1.
|
|
543
|
-
return renderInnerProgressStatus((0, thoughts_1.
|
|
530
|
+
const sessionPath = (0, thoughts_1.getPrivateRuntimeSessionPath)((0, identity_1.getAgentRoot)());
|
|
531
|
+
const pendingDir = (0, pending_1.getPrivateRuntimePendingDir)((0, identity_1.getAgentName)());
|
|
532
|
+
return renderInnerProgressStatus((0, thoughts_1.readPrivateRuntimeStatus)(sessionPath, pendingDir));
|
|
544
533
|
}
|
|
545
534
|
if (mode === "search") {
|
|
546
535
|
return JSON.stringify({
|
|
@@ -578,7 +567,7 @@ exports.sessionToolDefinitions = [
|
|
|
578
567
|
type: "object",
|
|
579
568
|
properties: {
|
|
580
569
|
friendId: { type: "string", description: "the friend UUID (or 'self')" },
|
|
581
|
-
channel: { type: "string", description: "the channel: cli, teams, bluebubbles, voice,
|
|
570
|
+
channel: { type: "string", description: "the channel: cli, teams, bluebubbles, voice, mcp, or legacy inner for private-runtime compatibility. channel=voice intentionally starts a live phone call to a trusted friend through the Voice sense." },
|
|
582
571
|
key: { type: "string", description: "session key (defaults to 'session')" },
|
|
583
572
|
content: { type: "string", description: "the message content to send" },
|
|
584
573
|
voiceAudioSource: { type: "string", enum: ["tone", "url", "file"], description: "optional initial non-speech audio to play after the opening greeting when channel=voice" },
|
|
@@ -638,11 +627,11 @@ exports.sessionToolDefinitions = [
|
|
|
638
627
|
}
|
|
639
628
|
}
|
|
640
629
|
/* v8 ignore stop */
|
|
641
|
-
// Self-routing: messages to "self" always go to
|
|
630
|
+
// Self-routing: messages to "self" always go to the private-runtime pending dir,
|
|
642
631
|
// regardless of the channel or key the agent specified.
|
|
643
632
|
const isSelf = friendId === "self";
|
|
644
633
|
const pendingDir = isSelf
|
|
645
|
-
? (0, pending_1.
|
|
634
|
+
? (0, pending_1.getPrivateRuntimePendingDir)(agentName)
|
|
646
635
|
: (0, pending_1.getPendingDir)(agentName, friendId, channel, key);
|
|
647
636
|
const delegatingBridgeId = findDelegatingBridgeId(ctx);
|
|
648
637
|
const delegatedFrom = isSelf
|
|
@@ -667,7 +656,7 @@ exports.sessionToolDefinitions = [
|
|
|
667
656
|
...(obligationId ? { obligationId } : {}),
|
|
668
657
|
};
|
|
669
658
|
if (isSelf) {
|
|
670
|
-
writePendingEnvelope(pendingDir, envelope);
|
|
659
|
+
const pendingMessageId = writePendingEnvelope(pendingDir, envelope);
|
|
671
660
|
if (delegatedFrom) {
|
|
672
661
|
try {
|
|
673
662
|
(0, obligations_1.createObligation)((0, identity_1.getAgentRoot)(), {
|
|
@@ -696,7 +685,7 @@ exports.sessionToolDefinitions = [
|
|
|
696
685
|
(0, runtime_1.emitNervesEvent)({
|
|
697
686
|
event: "repertoire.obligation_created",
|
|
698
687
|
component: "repertoire",
|
|
699
|
-
message: "obligation created for
|
|
688
|
+
message: "obligation created for private-runtime delegation",
|
|
700
689
|
meta: {
|
|
701
690
|
friendId: delegatedFrom.friendId,
|
|
702
691
|
channel: delegatedFrom.channel,
|
|
@@ -704,47 +693,24 @@ exports.sessionToolDefinitions = [
|
|
|
704
693
|
},
|
|
705
694
|
});
|
|
706
695
|
}
|
|
707
|
-
let wakeResponse = null;
|
|
708
696
|
try {
|
|
709
|
-
|
|
697
|
+
await (0, socket_client_1.requestPrivateWake)(agentName, ctx?.daemonSocketPath, {
|
|
698
|
+
reason: "send_message self-route private attention",
|
|
699
|
+
triggerSource: "send-message-self-route",
|
|
700
|
+
budgetClass: "interactive",
|
|
701
|
+
idempotencyKey: `send-message-self-route:${agentName}:${pendingMessageId}`,
|
|
702
|
+
originRefs: [
|
|
703
|
+
{ kind: "tool", id: "send_message" },
|
|
704
|
+
{ kind: "pending-queue", id: "self/inner/dialog" },
|
|
705
|
+
{ kind: "pending-message", id: pendingMessageId },
|
|
706
|
+
],
|
|
707
|
+
});
|
|
710
708
|
}
|
|
711
709
|
catch {
|
|
712
|
-
|
|
713
|
-
}
|
|
714
|
-
if (!wakeResponse?.ok) {
|
|
715
|
-
if (!(0, agent_discovery_1.isInnerDialogAutoStartEnabled)(agentName)) {
|
|
716
|
-
return renderInnerProgressStatus({
|
|
717
|
-
queue: "queued to inner/dialog",
|
|
718
|
-
wake: "parked by innerDialog.autoStart=false",
|
|
719
|
-
processing: "pending",
|
|
720
|
-
surfaced: "nothing yet",
|
|
721
|
-
});
|
|
722
|
-
}
|
|
723
|
-
const { runInnerDialogTurn } = await Promise.resolve().then(() => __importStar(require("../senses/inner-dialog")));
|
|
724
|
-
if (ctx?.context?.channel?.channel === "inner") {
|
|
725
|
-
queueMicrotask(() => {
|
|
726
|
-
void runInnerDialogTurn({ reason: "instinct" });
|
|
727
|
-
});
|
|
728
|
-
return renderInnerProgressStatus({
|
|
729
|
-
queue: "queued to inner/dialog",
|
|
730
|
-
wake: "inline scheduled",
|
|
731
|
-
processing: "pending",
|
|
732
|
-
surfaced: "nothing yet",
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
|
-
else {
|
|
736
|
-
const turnResult = await runInnerDialogTurn({ reason: "instinct" });
|
|
737
|
-
const surfacedPreview = normalizeProgressOutcome((0, thoughts_1.formatSurfacedValue)((0, thoughts_1.extractThoughtResponseFromMessages)(turnResult?.messages ?? [])));
|
|
738
|
-
return (0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
|
|
739
|
-
scope: "inner-delegation",
|
|
740
|
-
phase: "completed",
|
|
741
|
-
objective: "queued to inner/dialog",
|
|
742
|
-
outcomeText: `wake: inline fallback\n${surfacedPreview}`,
|
|
743
|
-
}));
|
|
744
|
-
}
|
|
710
|
+
// Queue-first self routing must not inline-run private work when the daemon is unavailable.
|
|
745
711
|
}
|
|
746
712
|
return renderInnerProgressStatus({
|
|
747
|
-
queue: "queued to
|
|
713
|
+
queue: "queued to private runtime",
|
|
748
714
|
wake: "daemon requested",
|
|
749
715
|
processing: "pending",
|
|
750
716
|
surfaced: "nothing yet",
|
|
@@ -80,7 +80,7 @@ exports.surfaceToolDef = {
|
|
|
80
80
|
type: "function",
|
|
81
81
|
function: {
|
|
82
82
|
name: "surface",
|
|
83
|
-
description: "return a ready thought to a friend or active session without publishing private
|
|
83
|
+
description: "return a ready thought to a friend or active session without publishing private-runtime work to BlueBubbles. pass delegationId to address a held thought (see your attention queue above), or friendId for spontaneous outreach. Surface delivers tool responses to the active session. BlueBubbles is a dedicated channel - use send_message with channel=\"bluebubbles\" for intentional iMessage delivery. set channel=voice only when you intentionally want a live phone call; content becomes the call reason/opening context. does not end your turn.",
|
|
84
84
|
parameters: {
|
|
85
85
|
type: "object",
|
|
86
86
|
properties: {
|
|
@@ -168,7 +168,7 @@ exports.surfaceToolDefinition = {
|
|
|
168
168
|
return "unknown";
|
|
169
169
|
} })();
|
|
170
170
|
const routeToFriend = async (friendId, content, queueItem, hint) => {
|
|
171
|
-
/* v8 ignore start -- routing: integration path tested via
|
|
171
|
+
/* v8 ignore start -- routing: integration path tested via private-runtime routing tests @preserve */
|
|
172
172
|
try {
|
|
173
173
|
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
174
174
|
const sessionsDir = path.join(agentRoot, "state", "sessions");
|
|
@@ -247,7 +247,7 @@ exports.surfaceToolDefinition = {
|
|
|
247
247
|
// Priority 2: Try proactive delivery first, then queue to freshest session
|
|
248
248
|
const allFriendSessions = (0, session_activity_1.listSessionActivity)({ sessionsDir, friendsDir, agentName, activeThresholdMs: Number.MAX_SAFE_INTEGER })
|
|
249
249
|
.filter((s) => s.friendId === friendId && s.channel !== "inner");
|
|
250
|
-
// Priority 2: Queue to freshest non-
|
|
250
|
+
// Priority 2: Queue to freshest non-private-runtime session
|
|
251
251
|
const freshest = allFriendSessions[0];
|
|
252
252
|
if (freshest) {
|
|
253
253
|
const { queuePendingMessage, getPendingDir } = await Promise.resolve().then(() => __importStar(require("../mind/pending")));
|
package/dist/senses/cli.js
CHANGED
|
@@ -84,13 +84,13 @@ Object.defineProperty(exports, "StreamingWordWrapper", { enumerable: true, get:
|
|
|
84
84
|
/**
|
|
85
85
|
* Format pending messages as content-prefix strings for injection into
|
|
86
86
|
* the next user message. Self-messages (from === agentName) become
|
|
87
|
-
* `[
|
|
87
|
+
* `[private-runtime note: {content}]`, inter-agent messages become
|
|
88
88
|
* `[message from {name}: {content}]`.
|
|
89
89
|
*/
|
|
90
90
|
function formatPendingPrefix(messages, agentName) {
|
|
91
91
|
return messages
|
|
92
92
|
.map((msg) => msg.from === agentName
|
|
93
|
-
? `[
|
|
93
|
+
? `[private-runtime note: ${msg.content}]`
|
|
94
94
|
: `[message from ${msg.from}: ${msg.content}]`)
|
|
95
95
|
.join("\n");
|
|
96
96
|
}
|