@ouro.bot/cli 0.1.0-alpha.724 → 0.1.0-alpha.725
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 +12 -0
- package/dist/heart/active-work.js +10 -3
- package/dist/heart/core.js +343 -77
- package/dist/heart/daemon/cli-exec.js +72 -14
- package/dist/heart/daemon/cli-help.js +13 -2
- package/dist/heart/daemon/cli-parse.js +82 -1
- package/dist/heart/daemon/daemon.js +286 -33
- package/dist/heart/daemon/doctor.js +21 -6
- package/dist/heart/daemon/habit-private-wake.js +6 -1
- package/dist/heart/habits/habit-cancel.js +810 -0
- package/dist/heart/habits/habit-lifecycle.js +1327 -0
- package/dist/heart/habits/habit-migration.js +28 -23
- package/dist/heart/habits/habit-parser.js +72 -8
- package/dist/heart/habits/habit-runtime-state.js +3 -24
- package/dist/heart/habits/habit-scheduler.js +268 -50
- package/dist/heart/habits/habit-session.js +16 -0
- package/dist/heart/hatch/hatch-flow.js +6 -4
- package/dist/heart/mailbox/readers/runtime-readers.js +26 -27
- package/dist/heart/orientation-frame.js +58 -1
- package/dist/heart/provider-attempt.js +4 -1
- package/dist/heart/providers/anthropic.js +23 -2
- package/dist/heart/start-of-turn-packet.js +14 -11
- package/dist/heart/streaming.js +565 -185
- package/dist/mind/obligation-steering.js +15 -9
- package/dist/mind/prompt.js +58 -26
- package/dist/nerves/coverage/run-artifacts.js +55 -35
- package/dist/nerves/review/cli.js +2 -2
- package/dist/nerves/review/core.js +14 -7
- package/dist/repertoire/tools-base.js +2 -0
- package/dist/repertoire/tools-habits.js +103 -0
- package/dist/repertoire/tools.js +18 -3
- package/dist/rsvp/cli.js +126 -20
- package/dist/rsvp/diagnostics.js +12 -2
- package/dist/rsvp/habit-policy.js +7 -0
- package/dist/rsvp/habit-stage.js +19 -4
- package/dist/rsvp/native-habit-runner.js +151 -11
- package/dist/rsvp/outbound-state.js +360 -0
- package/dist/senses/bluebubbles/client.js +90 -17
- package/dist/senses/bluebubbles/inbound-log.js +1 -0
- package/dist/senses/bluebubbles/index.js +662 -425
- package/dist/senses/bluebubbles/model.js +57 -13
- package/dist/senses/bluebubbles/mutation-log.js +21 -10
- package/dist/senses/bluebubbles/reaction-policy.js +55 -0
- package/dist/senses/bluebubbles/semantic-receipts.js +1818 -0
- package/dist/senses/cli/tui-store.js +1 -0
- package/dist/senses/cli.js +1 -0
- package/dist/senses/habit-lifecycle-guard.js +24 -0
- package/dist/senses/ingress-evidence.js +2 -0
- package/dist/senses/pipeline.js +90 -24
- package/dist/senses/private-runtime-worker.js +21 -20
- package/dist/senses/private-runtime.js +79 -29
- package/dist/senses/shared-turn.js +1 -0
- package/dist/senses/teams.js +1 -0
- package/package.json +6 -1
package/dist/senses/cli.js
CHANGED
|
@@ -311,6 +311,7 @@ function createCliCallbacks() {
|
|
|
311
311
|
const streamer = new MarkdownStreamer();
|
|
312
312
|
const wrapper = new cli_layout_1.StreamingWordWrapper();
|
|
313
313
|
return {
|
|
314
|
+
settleOutputMode: "final_only",
|
|
314
315
|
onModelStart: () => {
|
|
315
316
|
currentSpinner?.stop();
|
|
316
317
|
setSpinner(null);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.privateRuntimeHabitRejectionReason = privateRuntimeHabitRejectionReason;
|
|
4
|
+
const runtime_1 = require("../nerves/runtime");
|
|
5
|
+
function privateRuntimeHabitRejectionReason(habit, boundary) {
|
|
6
|
+
if (habit.status === "active")
|
|
7
|
+
return null;
|
|
8
|
+
const reason = `habit status ${habit.status} is non-executable before private runtime execution`;
|
|
9
|
+
(0, runtime_1.emitNervesEvent)({
|
|
10
|
+
level: "warn",
|
|
11
|
+
component: "senses",
|
|
12
|
+
event: "senses.habit_lifecycle_rejected",
|
|
13
|
+
message: "habit lifecycle state rejected before private runtime execution",
|
|
14
|
+
meta: {
|
|
15
|
+
boundary,
|
|
16
|
+
habitName: habit.name,
|
|
17
|
+
status: habit.status,
|
|
18
|
+
degradedReason: habit.status === "degraded" ? habit.degradedReason : null,
|
|
19
|
+
detail: habit.status === "degraded" ? habit.degradedDetail : null,
|
|
20
|
+
reason,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
return reason;
|
|
24
|
+
}
|
package/dist/senses/pipeline.js
CHANGED
|
@@ -39,6 +39,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
39
39
|
})();
|
|
40
40
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
41
|
exports.emitObligationTransitionEpisodes = emitObligationTransitionEpisodes;
|
|
42
|
+
exports.selectCheckpointCurrentAsk = selectCheckpointCurrentAsk;
|
|
42
43
|
exports.handleInboundTurn = handleInboundTurn;
|
|
43
44
|
const fs = __importStar(require("fs"));
|
|
44
45
|
const path = __importStar(require("path"));
|
|
@@ -203,6 +204,16 @@ function latestUserAuthoredText(messages, continuityIngressTexts) {
|
|
|
203
204
|
.filter(Boolean);
|
|
204
205
|
return userMessages[userMessages.length - 1];
|
|
205
206
|
}
|
|
207
|
+
function selectCheckpointCurrentAsk(input) {
|
|
208
|
+
const frameSpeech = input.orientationFrame?.currentUserSpeech
|
|
209
|
+
.map((speech) => speech.trim())
|
|
210
|
+
.filter(Boolean)
|
|
211
|
+
.at(-1) ?? null;
|
|
212
|
+
if (input.orientationFrame?.speechKind === "reaction")
|
|
213
|
+
return frameSpeech;
|
|
214
|
+
const currentUtterance = input.currentUserMessage?.trim();
|
|
215
|
+
return currentUtterance || frameSpeech;
|
|
216
|
+
}
|
|
206
217
|
function sessionRefForFlightRecorder(session) {
|
|
207
218
|
return `${session.friendId}/${session.channel}/${session.key}`;
|
|
208
219
|
}
|
|
@@ -212,6 +223,22 @@ function isTerminalWithoutContinuation(outcome, hasActiveContinuation) {
|
|
|
212
223
|
|| outcome === "rested"
|
|
213
224
|
|| outcome === "superseded");
|
|
214
225
|
}
|
|
226
|
+
function selectPostTurnContinuityState(input) {
|
|
227
|
+
const continuingState = {
|
|
228
|
+
...(input.mustResolveBeforeHandoff ? { mustResolveBeforeHandoff: true } : {}),
|
|
229
|
+
...(typeof input.lastFriendActivityAt === "string" ? { lastFriendActivityAt: input.lastFriendActivityAt } : {}),
|
|
230
|
+
};
|
|
231
|
+
const clearsMustResolveBeforeHandoff = input.outcome === "settled"
|
|
232
|
+
|| input.outcome === "blocked"
|
|
233
|
+
|| input.outcome === "superseded"
|
|
234
|
+
|| input.outcome === "observed";
|
|
235
|
+
if (!clearsMustResolveBeforeHandoff) {
|
|
236
|
+
return Object.keys(continuingState).length > 0 ? continuingState : undefined;
|
|
237
|
+
}
|
|
238
|
+
return typeof input.lastFriendActivityAt === "string"
|
|
239
|
+
? { lastFriendActivityAt: input.lastFriendActivityAt }
|
|
240
|
+
: undefined;
|
|
241
|
+
}
|
|
215
242
|
function readPostTurnFlightRecorderArcSnapshot(agentRoot) {
|
|
216
243
|
const latest = (0, flight_recorder_1.readFlightRecorderResume)(agentRoot);
|
|
217
244
|
return {
|
|
@@ -223,7 +250,7 @@ function readPostTurnFlightRecorderArcSnapshot(agentRoot) {
|
|
|
223
250
|
unverifiedClaimIds: latest.unverifiedClaimIds,
|
|
224
251
|
};
|
|
225
252
|
}
|
|
226
|
-
function
|
|
253
|
+
function recordTurnFlightRecorderCheckpoint(input) {
|
|
227
254
|
const hasActiveContinuation = input.mustResolveBeforeHandoff
|
|
228
255
|
|| input.activeObligationIds.length > 0
|
|
229
256
|
|| input.activeReturnObligationIds.length > 0
|
|
@@ -240,9 +267,11 @@ function recordPostTurnFlightRecorderCheckpoint(input) {
|
|
|
240
267
|
? "inspect the latest session and wait for new input before acting"
|
|
241
268
|
: "continue the current held work and update Arc/Desk with the next checkpoint");
|
|
242
269
|
(0, flight_recorder_1.recordFlightRecorderEvent)(input.agentRoot, {
|
|
243
|
-
kind: "post_turn_persisted",
|
|
270
|
+
kind: input.sessionPersisted ? "post_turn_persisted" : "blocker_detected",
|
|
244
271
|
sessionRef: sessionRefForFlightRecorder(input.currentSession),
|
|
245
|
-
summary:
|
|
272
|
+
summary: input.sessionPersisted
|
|
273
|
+
? `persisted ${input.currentSession.channel}/${input.currentSession.key} turn with outcome ${input.outcome}`
|
|
274
|
+
: `runAgent threw before session persistence for ${input.currentSession.channel}/${input.currentSession.key}`,
|
|
246
275
|
currentAsk: input.currentAsk,
|
|
247
276
|
nextSafeAction,
|
|
248
277
|
blockedBecause,
|
|
@@ -255,6 +284,7 @@ function recordPostTurnFlightRecorderCheckpoint(input) {
|
|
|
255
284
|
unverifiedClaimIds: input.unverifiedClaimIds,
|
|
256
285
|
meta: {
|
|
257
286
|
outcome: input.outcome,
|
|
287
|
+
sessionPersisted: input.sessionPersisted,
|
|
258
288
|
mustResolveBeforeHandoff: input.mustResolveBeforeHandoff,
|
|
259
289
|
sessionPath: input.currentSession.sessionPath,
|
|
260
290
|
},
|
|
@@ -447,6 +477,9 @@ function prepareInboundRunLedger(input) {
|
|
|
447
477
|
}
|
|
448
478
|
}
|
|
449
479
|
async function handleInboundTurn(input) {
|
|
480
|
+
// Continuity resumption is structured caller intent. Never infer it from the
|
|
481
|
+
// current message or from similarity to prior obligations.
|
|
482
|
+
const resumePriorWork = input.runAgentOptions?.resumePriorWork === true;
|
|
450
483
|
// Reset session-scoped state when the session changes
|
|
451
484
|
const sessionKey = `${input.channel}/${input.sessionKey ?? "session"}`;
|
|
452
485
|
if (sessionKey !== _lastSessionKey) {
|
|
@@ -826,7 +859,7 @@ async function handleInboundTurn(input) {
|
|
|
826
859
|
if (capabilities) {
|
|
827
860
|
startOfTurnPacket.capabilities = capabilities;
|
|
828
861
|
}
|
|
829
|
-
renderedStartOfTurnPacket = (0, start_of_turn_packet_1.renderStartOfTurnPacket)(startOfTurnPacket);
|
|
862
|
+
renderedStartOfTurnPacket = (0, start_of_turn_packet_1.renderStartOfTurnPacket)(startOfTurnPacket, { resumePriorWork });
|
|
830
863
|
if (!renderedStartOfTurnPacket)
|
|
831
864
|
renderedStartOfTurnPacket = undefined;
|
|
832
865
|
// Update self-presence
|
|
@@ -854,6 +887,7 @@ async function handleInboundTurn(input) {
|
|
|
854
887
|
?? latestUserAuthoredText(input.messages, input.continuityIngressTexts);
|
|
855
888
|
let runAgentOptions = {
|
|
856
889
|
...input.runAgentOptions,
|
|
890
|
+
resumePriorWork,
|
|
857
891
|
...(orientationFrame ? { orientationFrame } : {}),
|
|
858
892
|
...(liveLatencyMode ? { skipKeptNotes: true } : {}),
|
|
859
893
|
bridgeContext,
|
|
@@ -892,6 +926,10 @@ async function handleInboundTurn(input) {
|
|
|
892
926
|
resolvedContext,
|
|
893
927
|
runAgentOptions,
|
|
894
928
|
}));
|
|
929
|
+
const checkpointCurrentAsk = selectCheckpointCurrentAsk({
|
|
930
|
+
currentUserMessage: runAgentOptions.toolContext?.currentUserMessage ?? currentUserMessage,
|
|
931
|
+
orientationFrame: runAgentOptions.orientationFrame,
|
|
932
|
+
});
|
|
895
933
|
const runLedger = prepareInboundRunLedger({
|
|
896
934
|
channel: input.channel,
|
|
897
935
|
senseType: input.capabilities.senseType,
|
|
@@ -920,6 +958,37 @@ async function handleInboundTurn(input) {
|
|
|
920
958
|
usage: (0, run_ledger_1.usageMetadataFromUsageData)(undefined, "reported-unavailable"),
|
|
921
959
|
errorName: error instanceof Error ? error.name : "NonErrorThrown",
|
|
922
960
|
}));
|
|
961
|
+
try {
|
|
962
|
+
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
963
|
+
const postTurnArc = readPostTurnFlightRecorderArcSnapshot(agentRoot);
|
|
964
|
+
recordTurnFlightRecorderCheckpoint({
|
|
965
|
+
agentRoot,
|
|
966
|
+
currentSession,
|
|
967
|
+
currentAsk: checkpointCurrentAsk,
|
|
968
|
+
nextSafeAction: activeWorkFrame.resumeHandle?.nextAction
|
|
969
|
+
?? activeWorkFrame.primaryObligation?.nextAction?.trim()
|
|
970
|
+
?? null,
|
|
971
|
+
outcome: "errored",
|
|
972
|
+
sessionPersisted: false,
|
|
973
|
+
mustResolveBeforeHandoff,
|
|
974
|
+
activeObligationIds: postTurnArc.activeObligationIds,
|
|
975
|
+
activeReturnObligationIds: postTurnArc.activeReturnObligationIds,
|
|
976
|
+
activePacketIds: postTurnArc.activePacketIds,
|
|
977
|
+
openEvolutionCaseIds: postTurnArc.openEvolutionCaseIds,
|
|
978
|
+
recentClaimIds: postTurnArc.recentClaimIds,
|
|
979
|
+
unverifiedClaimIds: postTurnArc.unverifiedClaimIds,
|
|
980
|
+
});
|
|
981
|
+
await refreshContextLossSentinelNonFatal({ agentName: (0, identity_1.getAgentName)(), agentRoot, trigger: "post_turn" });
|
|
982
|
+
}
|
|
983
|
+
catch (checkpointError) {
|
|
984
|
+
(0, runtime_1.emitNervesEvent)({
|
|
985
|
+
level: "warn",
|
|
986
|
+
component: "senses",
|
|
987
|
+
event: "senses.flight_recorder_checkpoint_error",
|
|
988
|
+
message: "failed to record thrown-turn flight recorder checkpoint",
|
|
989
|
+
meta: { error: checkpointError instanceof Error ? checkpointError.message : String(checkpointError) },
|
|
990
|
+
});
|
|
991
|
+
}
|
|
923
992
|
throw error;
|
|
924
993
|
}
|
|
925
994
|
const runLedgerLifecycle = result.outcome === "errored" ? "error" : "completed";
|
|
@@ -946,19 +1015,22 @@ async function handleInboundTurn(input) {
|
|
|
946
1015
|
/* v8 ignore next -- defensive: error always set when errored @preserve */
|
|
947
1016
|
result.error?.message ?? "unknown error", classification, currentProvider, currentBinding.model, agentName, inventory, {}, { currentLane });
|
|
948
1017
|
input.failoverState.pending = failoverContext;
|
|
949
|
-
|
|
1018
|
+
const failoverNextState = selectPostTurnContinuityState({
|
|
1019
|
+
outcome: "errored",
|
|
1020
|
+
mustResolveBeforeHandoff,
|
|
1021
|
+
lastFriendActivityAt,
|
|
1022
|
+
});
|
|
1023
|
+
await input.postTurn(sessionMessages, session.sessionPath, result.usage, undefined, failoverNextState);
|
|
950
1024
|
try {
|
|
951
1025
|
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
952
1026
|
const postTurnArc = readPostTurnFlightRecorderArcSnapshot(agentRoot);
|
|
953
|
-
|
|
1027
|
+
recordTurnFlightRecorderCheckpoint({
|
|
954
1028
|
agentRoot,
|
|
955
1029
|
currentSession,
|
|
956
|
-
currentAsk:
|
|
957
|
-
?? activeWorkFrame.primaryObligation?.content?.trim()
|
|
958
|
-
?? currentUserMessage
|
|
959
|
-
?? null,
|
|
1030
|
+
currentAsk: checkpointCurrentAsk,
|
|
960
1031
|
nextSafeAction: failoverContext.userMessage,
|
|
961
1032
|
outcome: "errored",
|
|
1033
|
+
sessionPersisted: true,
|
|
962
1034
|
mustResolveBeforeHandoff,
|
|
963
1035
|
activeObligationIds: postTurnArc.activeObligationIds,
|
|
964
1036
|
activeReturnObligationIds: postTurnArc.activeReturnObligationIds,
|
|
@@ -1012,30 +1084,24 @@ async function handleInboundTurn(input) {
|
|
|
1012
1084
|
// Episode emission is non-fatal
|
|
1013
1085
|
}
|
|
1014
1086
|
// Step 6: postTurn
|
|
1015
|
-
const
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
? (typeof lastFriendActivityAt === "string"
|
|
1021
|
-
? { lastFriendActivityAt }
|
|
1022
|
-
: undefined)
|
|
1023
|
-
: (Object.keys(continuingState).length > 0 ? continuingState : undefined);
|
|
1087
|
+
const nextState = selectPostTurnContinuityState({
|
|
1088
|
+
outcome: result.outcome,
|
|
1089
|
+
mustResolveBeforeHandoff,
|
|
1090
|
+
lastFriendActivityAt,
|
|
1091
|
+
});
|
|
1024
1092
|
await input.postTurn(sessionMessages, session.sessionPath, result.usage, undefined, nextState);
|
|
1025
1093
|
try {
|
|
1026
1094
|
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
1027
1095
|
const postTurnArc = readPostTurnFlightRecorderArcSnapshot(agentRoot);
|
|
1028
|
-
|
|
1096
|
+
recordTurnFlightRecorderCheckpoint({
|
|
1029
1097
|
agentRoot,
|
|
1030
1098
|
currentSession,
|
|
1031
|
-
currentAsk:
|
|
1032
|
-
?? activeWorkFrame.primaryObligation?.content?.trim()
|
|
1033
|
-
?? currentUserMessage
|
|
1034
|
-
?? null,
|
|
1099
|
+
currentAsk: checkpointCurrentAsk,
|
|
1035
1100
|
nextSafeAction: activeWorkFrame.resumeHandle?.nextAction
|
|
1036
1101
|
?? activeWorkFrame.primaryObligation?.nextAction?.trim()
|
|
1037
1102
|
?? null,
|
|
1038
1103
|
outcome: result.outcome ?? "observed",
|
|
1104
|
+
sessionPersisted: true,
|
|
1039
1105
|
mustResolveBeforeHandoff,
|
|
1040
1106
|
activeObligationIds: postTurnArc.activeObligationIds,
|
|
1041
1107
|
activeReturnObligationIds: postTurnArc.activeReturnObligationIds,
|
|
@@ -55,6 +55,7 @@ const habit_policy_1 = require("../rsvp/habit-policy");
|
|
|
55
55
|
const spend_ledger_1 = require("../rsvp/spend-ledger");
|
|
56
56
|
const run_ledger_1 = require("../heart/run-ledger");
|
|
57
57
|
const autonomy_budget_1 = require("../heart/autonomy-budget");
|
|
58
|
+
const habit_lifecycle_guard_1 = require("./habit-lifecycle-guard");
|
|
58
59
|
function isUsageData(value) {
|
|
59
60
|
const usage = value;
|
|
60
61
|
return !!usage
|
|
@@ -275,21 +276,6 @@ function deriveHabitSummarySnapshot(habitRun) {
|
|
|
275
276
|
nextLikelyStep: null,
|
|
276
277
|
};
|
|
277
278
|
}
|
|
278
|
-
function fallbackHabitFile(habitName) {
|
|
279
|
-
return {
|
|
280
|
-
name: habitName,
|
|
281
|
-
title: habitName,
|
|
282
|
-
cadence: null,
|
|
283
|
-
status: "active",
|
|
284
|
-
lastRun: null,
|
|
285
|
-
created: null,
|
|
286
|
-
tools: [],
|
|
287
|
-
origin: null,
|
|
288
|
-
surface: { family: false, originator: false, extra: [] },
|
|
289
|
-
continuity: { mode: "fresh" },
|
|
290
|
-
body: "",
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
279
|
function readHabitForRun(agentRoot, habitName, errors) {
|
|
294
280
|
const habitPath = path.join(agentRoot, "habits", `${habitName}.md`);
|
|
295
281
|
try {
|
|
@@ -305,16 +291,20 @@ function readHabitForRun(agentRoot, habitName, errors) {
|
|
|
305
291
|
message: "habit file could not be read for habit session",
|
|
306
292
|
meta: { habitName, habitPath, reason },
|
|
307
293
|
});
|
|
308
|
-
return
|
|
294
|
+
return (0, habit_parser_1.createDegradedHabitFile)(habitPath, "read_error");
|
|
309
295
|
}
|
|
310
296
|
}
|
|
311
297
|
async function prepareHabitRun(habitName, trigger, startedAt) {
|
|
312
298
|
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
313
299
|
const errors = [];
|
|
314
300
|
const habit = (0, habit_runtime_state_1.applyHabitRuntimeState)(agentRoot, readHabitForRun(agentRoot, habitName, errors));
|
|
301
|
+
if (habit.status === "degraded" && habit.degradedDetail !== null) {
|
|
302
|
+
errors.push(habit.degradedDetail);
|
|
303
|
+
}
|
|
304
|
+
const lifecycleBlockedReason = (0, habit_lifecycle_guard_1.privateRuntimeHabitRejectionReason)(habit, "private-runtime-worker");
|
|
315
305
|
const blockedReason = (0, habit_policy_1.isRsvpHabitName)(habitName) && !habit.rsvp
|
|
316
306
|
? "RSVP habit metadata is required before private runtime execution"
|
|
317
|
-
:
|
|
307
|
+
: lifecycleBlockedReason;
|
|
318
308
|
if (blockedReason && !errors.includes(blockedReason))
|
|
319
309
|
errors.push(blockedReason);
|
|
320
310
|
const runId = (0, flight_recorder_1.createHabitRunId)(habitName, new Date(startedAt));
|
|
@@ -422,6 +412,11 @@ function createPrivateRuntimeWorker(runTurn = (options) => (0, private_runtime_1
|
|
|
422
412
|
async function reuseHeartbeatOkRest(habitName) {
|
|
423
413
|
const nowIso = new Date(nowSource()).toISOString();
|
|
424
414
|
const habitRun = await prepareHabitRun(habitName, "overdue", nowIso);
|
|
415
|
+
if (habitRun.blockedReason) {
|
|
416
|
+
clearHeartbeatRestShield();
|
|
417
|
+
recordHabitCompletion(habitRun, nowIso);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
425
420
|
habitRun.results.push({ turnOutcome: "rested", restStatus: "HEARTBEAT_OK" });
|
|
426
421
|
recordHabitCompletion(habitRun, nowIso);
|
|
427
422
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -475,9 +470,9 @@ function createPrivateRuntimeWorker(runTurn = (options) => (0, private_runtime_1
|
|
|
475
470
|
});
|
|
476
471
|
}
|
|
477
472
|
}
|
|
478
|
-
async function run(reason, taskId, habitName, awaitName, trigger, privateTurnDecision) {
|
|
473
|
+
async function run(reason, taskId, habitName, awaitName, trigger, privateTurnDecision, noSend) {
|
|
479
474
|
if (running) {
|
|
480
|
-
queue.push({ reason, taskId, habitName, awaitName, trigger, privateTurnDecision });
|
|
475
|
+
queue.push({ reason, taskId, habitName, awaitName, trigger, privateTurnDecision, noSend });
|
|
481
476
|
return;
|
|
482
477
|
}
|
|
483
478
|
running = true;
|
|
@@ -488,12 +483,14 @@ function createPrivateRuntimeWorker(runTurn = (options) => (0, private_runtime_1
|
|
|
488
483
|
let nextAwaitName = awaitName;
|
|
489
484
|
let nextTrigger = trigger;
|
|
490
485
|
let nextPrivateTurnDecision = privateTurnDecision;
|
|
486
|
+
let nextNoSend = reason === "habit" ? noSend : undefined;
|
|
491
487
|
let nextHabitRun = null;
|
|
492
488
|
let consecutiveInstinctTurns = reason === "instinct" ? 1 : 0;
|
|
493
489
|
runLoop: do {
|
|
494
490
|
const currentReason = nextReason;
|
|
495
491
|
const currentHabitName = nextHabitName;
|
|
496
492
|
const currentTrigger = nextTrigger ?? "overdue";
|
|
493
|
+
const currentNoSend = nextNoSend;
|
|
497
494
|
const currentHabitRun = currentReason === "habit" && currentHabitName
|
|
498
495
|
? nextHabitRun && nextHabitRun.habit.name === currentHabitName
|
|
499
496
|
? nextHabitRun
|
|
@@ -545,6 +542,7 @@ function createPrivateRuntimeWorker(runTurn = (options) => (0, private_runtime_1
|
|
|
545
542
|
taskId: nextTaskId,
|
|
546
543
|
habitName: nextHabitName,
|
|
547
544
|
awaitName: nextAwaitName,
|
|
545
|
+
...(currentNoSend ? { noSend: true } : {}),
|
|
548
546
|
...(nextPrivateTurnDecision ? { privateTurnDecision: nextPrivateTurnDecision } : {}),
|
|
549
547
|
...(currentHabitRun
|
|
550
548
|
? {
|
|
@@ -614,6 +612,7 @@ function createPrivateRuntimeWorker(runTurn = (options) => (0, private_runtime_1
|
|
|
614
612
|
nextAwaitName = next.awaitName;
|
|
615
613
|
nextTrigger = next.trigger;
|
|
616
614
|
nextPrivateTurnDecision = next.privateTurnDecision;
|
|
615
|
+
nextNoSend = next.reason === "habit" ? next.noSend : undefined;
|
|
617
616
|
consecutiveInstinctTurns = nextReason === "instinct" ? consecutiveInstinctTurns + 1 : 0;
|
|
618
617
|
continue runLoop;
|
|
619
618
|
}
|
|
@@ -650,6 +649,7 @@ function createPrivateRuntimeWorker(runTurn = (options) => (0, private_runtime_1
|
|
|
650
649
|
nextAwaitName = undefined;
|
|
651
650
|
nextTrigger = currentTrigger;
|
|
652
651
|
nextPrivateTurnDecision = undefined;
|
|
652
|
+
nextNoSend = currentNoSend;
|
|
653
653
|
nextHabitRun = currentHabitRun;
|
|
654
654
|
}
|
|
655
655
|
else {
|
|
@@ -661,6 +661,7 @@ function createPrivateRuntimeWorker(runTurn = (options) => (0, private_runtime_1
|
|
|
661
661
|
nextAwaitName = undefined;
|
|
662
662
|
nextTrigger = undefined;
|
|
663
663
|
nextPrivateTurnDecision = undefined;
|
|
664
|
+
nextNoSend = undefined;
|
|
664
665
|
}
|
|
665
666
|
continue;
|
|
666
667
|
}
|
|
@@ -684,7 +685,7 @@ function createPrivateRuntimeWorker(runTurn = (options) => (0, private_runtime_1
|
|
|
684
685
|
return;
|
|
685
686
|
}
|
|
686
687
|
recordHabitFireForRecursion(habitName);
|
|
687
|
-
await run("habit", undefined, maybeMessage.habitName, undefined, maybeMessage.trigger ?? "overdue", maybeMessage.privateTurnDecision);
|
|
688
|
+
await run("habit", undefined, maybeMessage.habitName, undefined, maybeMessage.trigger ?? "overdue", maybeMessage.privateTurnDecision, maybeMessage.noSend);
|
|
688
689
|
return;
|
|
689
690
|
}
|
|
690
691
|
if (maybeMessage.type === "await") {
|
|
@@ -76,6 +76,7 @@ const await_parser_1 = require("../heart/awaiting/await-parser");
|
|
|
76
76
|
const await_runtime_state_1 = require("../heart/awaiting/await-runtime-state");
|
|
77
77
|
const habit_parser_1 = require("../heart/habits/habit-parser");
|
|
78
78
|
const habit_runtime_state_1 = require("../heart/habits/habit-runtime-state");
|
|
79
|
+
const habit_lifecycle_guard_1 = require("./habit-lifecycle-guard");
|
|
79
80
|
const cadence_1 = require("../heart/daemon/cadence");
|
|
80
81
|
const habit_policy_1 = require("../rsvp/habit-policy");
|
|
81
82
|
const daemon_health_1 = require("../heart/daemon/daemon-health");
|
|
@@ -344,6 +345,14 @@ function assertPayloadBinding(decision, kind, id) {
|
|
|
344
345
|
return;
|
|
345
346
|
throw new Error(`private-runtime decision payload mismatch: missing ${kind} origin ref ${id}`);
|
|
346
347
|
}
|
|
348
|
+
function assertNoSendBinding(decision, noSend) {
|
|
349
|
+
const refs = decision.originRefs.filter((ref) => ref.kind === "capability" && ref.id === "no-send");
|
|
350
|
+
if (noSend === true && refs.length === 1)
|
|
351
|
+
return;
|
|
352
|
+
if (noSend === undefined && refs.length === 0)
|
|
353
|
+
return;
|
|
354
|
+
throw new Error("private-runtime decision payload mismatch: no-send capability binding does not match the turn");
|
|
355
|
+
}
|
|
347
356
|
function ledgerDecisionFor(decision) {
|
|
348
357
|
const ledgerPath = decision.ledgerLocator?.path;
|
|
349
358
|
if (!ledgerPath) {
|
|
@@ -389,6 +398,7 @@ function assertPrivateTurnDecisionAllowed(input) {
|
|
|
389
398
|
assertPayloadBinding(decision, "task", options?.taskId);
|
|
390
399
|
assertPayloadBinding(decision, "habit", options?.habitName);
|
|
391
400
|
assertPayloadBinding(decision, "await", options?.awaitName);
|
|
401
|
+
assertNoSendBinding(decision, options?.noSend);
|
|
392
402
|
const ledgerRow = ledgerDecisionFor(decision);
|
|
393
403
|
const decidedAtMs = Date.parse(ledgerRow.decidedAt);
|
|
394
404
|
if (!Number.isFinite(decidedAtMs)) {
|
|
@@ -489,6 +499,7 @@ function extractToolCallNames(messages) {
|
|
|
489
499
|
}
|
|
490
500
|
function createPrivateRuntimeCallbacks() {
|
|
491
501
|
return {
|
|
502
|
+
settleOutputMode: "final_only",
|
|
492
503
|
onModelStart: () => { },
|
|
493
504
|
onModelStreamStart: () => { },
|
|
494
505
|
onTextChunk: () => { },
|
|
@@ -795,6 +806,32 @@ function buildHabitSurfacePolicy(origin, surface) {
|
|
|
795
806
|
lines.push("- use send_message for intentional contact; use surface only for a held return tied to an existing return obligation.");
|
|
796
807
|
return lines.join("\n");
|
|
797
808
|
}
|
|
809
|
+
function reduceHabitSessionToNoSend(habitSession) {
|
|
810
|
+
const deniedTools = new Set([
|
|
811
|
+
...(habitSession?.permissionEnvelope.deniedTools ?? []),
|
|
812
|
+
...(habitSession?.toolPolicy.grantedTools ?? []),
|
|
813
|
+
...(habitSession?.toolPolicy.deniedTools ?? []),
|
|
814
|
+
"send_message",
|
|
815
|
+
"surface",
|
|
816
|
+
]);
|
|
817
|
+
return {
|
|
818
|
+
...habitSession,
|
|
819
|
+
noSend: true,
|
|
820
|
+
permissionEnvelope: {
|
|
821
|
+
schemaVersion: 1,
|
|
822
|
+
canMessageOutward: false,
|
|
823
|
+
returnRoutes: [],
|
|
824
|
+
deniedTools: [...deniedTools],
|
|
825
|
+
warnings: [...(habitSession?.permissionEnvelope.warnings ?? [])],
|
|
826
|
+
},
|
|
827
|
+
toolPolicy: {
|
|
828
|
+
requestedTools: habitSession?.toolPolicy.requestedTools ?? null,
|
|
829
|
+
grantedTools: [],
|
|
830
|
+
deniedTools: [...deniedTools],
|
|
831
|
+
outwardMessagingAllowed: false,
|
|
832
|
+
},
|
|
833
|
+
};
|
|
834
|
+
}
|
|
798
835
|
async function runPrivateRuntimeTurn(options) {
|
|
799
836
|
const now = options?.now ?? (() => new Date());
|
|
800
837
|
const reason = options?.reason ?? "instinct";
|
|
@@ -873,37 +910,43 @@ async function runPrivateRuntimeTurn(options) {
|
|
|
873
910
|
let habitSurface = { family: true, originator: true, extra: [] };
|
|
874
911
|
if (preparedHabit) {
|
|
875
912
|
if (rsvpHabit && !preparedHabit.rsvp) {
|
|
876
|
-
|
|
913
|
+
const detail = preparedHabit.status === "degraded"
|
|
914
|
+
? preparedHabit.degradedDetail
|
|
915
|
+
: null;
|
|
916
|
+
throw new Error(`RSVP habit metadata is required before private runtime execution: ${detail ?? habitName}`);
|
|
877
917
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
habitTools = preparedHabit.tools;
|
|
882
|
-
habitOrigin = preparedHabit.origin;
|
|
883
|
-
habitSurface = preparedHabit.surface;
|
|
918
|
+
const blockedReason = (0, habit_lifecycle_guard_1.privateRuntimeHabitRejectionReason)(preparedHabit, "private-runtime");
|
|
919
|
+
if (blockedReason)
|
|
920
|
+
throw new Error(blockedReason);
|
|
884
921
|
}
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
habitLastRun = parsed.lastRun;
|
|
895
|
-
habitTools = parsed.tools;
|
|
896
|
-
habitOrigin = parsed.origin;
|
|
897
|
-
habitSurface = parsed.surface;
|
|
898
|
-
}
|
|
899
|
-
catch (error) {
|
|
900
|
-
if (rsvpHabit) {
|
|
901
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
902
|
-
throw new Error(`RSVP habit metadata is required before private runtime execution: ${reason}`);
|
|
903
|
-
}
|
|
904
|
-
// Habit file missing or unreadable
|
|
922
|
+
let currentHabit;
|
|
923
|
+
try {
|
|
924
|
+
const habitContent = fs.readFileSync(habitFilePath, "utf-8");
|
|
925
|
+
currentHabit = (0, habit_runtime_state_1.applyHabitRuntimeState)(agentRoot, (0, habit_parser_1.parseHabitFile)(habitContent, habitFilePath));
|
|
926
|
+
}
|
|
927
|
+
catch (error) {
|
|
928
|
+
const readReason = error instanceof Error ? error.message : String(error);
|
|
929
|
+
if (rsvpHabit) {
|
|
930
|
+
throw new Error(`RSVP habit metadata is required before private runtime execution: ${readReason}`);
|
|
905
931
|
}
|
|
932
|
+
const degraded = (0, habit_parser_1.createDegradedHabitFile)(habitFilePath, "read_error", "", readReason);
|
|
933
|
+
const blockedReason = (0, habit_lifecycle_guard_1.privateRuntimeHabitRejectionReason)(degraded, "private-runtime");
|
|
934
|
+
throw new Error(blockedReason);
|
|
935
|
+
}
|
|
936
|
+
if (rsvpHabit && !currentHabit.rsvp) {
|
|
937
|
+
const detail = currentHabit.status === "degraded" ? currentHabit.degradedDetail : null;
|
|
938
|
+
throw new Error(`RSVP habit metadata is required before private runtime execution: ${detail ?? habitName}`);
|
|
906
939
|
}
|
|
940
|
+
const currentBlockedReason = (0, habit_lifecycle_guard_1.privateRuntimeHabitRejectionReason)(currentHabit, "private-runtime");
|
|
941
|
+
if (currentBlockedReason)
|
|
942
|
+
throw new Error(currentBlockedReason);
|
|
943
|
+
const executableHabit = preparedHabit ?? currentHabit;
|
|
944
|
+
habitBody = executableHabit.body || undefined;
|
|
945
|
+
habitTitle = executableHabit.title || habitName;
|
|
946
|
+
habitLastRun = executableHabit.lastRun;
|
|
947
|
+
habitTools = executableHabit.tools;
|
|
948
|
+
habitOrigin = executableHabit.origin;
|
|
949
|
+
habitSurface = executableHabit.surface;
|
|
907
950
|
// If the habit file couldn't be read at all (no body, no title parsed), error message
|
|
908
951
|
if (habitBody === undefined && habitTitle === habitName) {
|
|
909
952
|
userContent = `habit "${habitName}" could not be read (file not found or unreadable). check habits/${habitName}.md exists.`;
|
|
@@ -1027,6 +1070,12 @@ async function runPrivateRuntimeTurn(options) {
|
|
|
1027
1070
|
meta: { habitName: options.habitName },
|
|
1028
1071
|
});
|
|
1029
1072
|
}
|
|
1073
|
+
if (options?.noSend === true) {
|
|
1074
|
+
habitToolsResolved = [];
|
|
1075
|
+
}
|
|
1076
|
+
const effectiveHabitSession = options?.noSend === true
|
|
1077
|
+
? reduceHabitSessionToNoSend(options.habitSession)
|
|
1078
|
+
: options?.habitSession;
|
|
1030
1079
|
const sessionLoader = {
|
|
1031
1080
|
loadOrCreate: async () => {
|
|
1032
1081
|
if (existingMessages.length > 0) {
|
|
@@ -1111,9 +1160,10 @@ async function runPrivateRuntimeTurn(options) {
|
|
|
1111
1160
|
toolContext: {
|
|
1112
1161
|
signin: async () => undefined,
|
|
1113
1162
|
delegatedOrigins: attentionQueue,
|
|
1114
|
-
...(options?.
|
|
1163
|
+
...(options?.noSend ? { noSend: true } : {}),
|
|
1164
|
+
...(effectiveHabitSession ? { habitSession: effectiveHabitSession } : {}),
|
|
1115
1165
|
},
|
|
1116
|
-
...(
|
|
1166
|
+
...(effectiveHabitSession ? { habitSession: effectiveHabitSession } : {}),
|
|
1117
1167
|
},
|
|
1118
1168
|
});
|
|
1119
1169
|
// Post-turn routeDelegatedCompletion removed: delivery is now inline via surface tool.
|
|
@@ -288,6 +288,7 @@ async function runSenseTurn(options) {
|
|
|
288
288
|
};
|
|
289
289
|
/* v8 ignore start — no-op callback stubs; only onTextChunk does real work (covered via mock) */
|
|
290
290
|
const callbacks = {
|
|
291
|
+
settleOutputMode: "retractable_buffer",
|
|
291
292
|
onModelStart: () => { },
|
|
292
293
|
onModelStreamStart: () => { },
|
|
293
294
|
onTextChunk: (chunk) => { pendingResponseText += chunk; },
|
package/dist/senses/teams.js
CHANGED
|
@@ -409,6 +409,7 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
409
409
|
}
|
|
410
410
|
}
|
|
411
411
|
return {
|
|
412
|
+
settleOutputMode: "final_only",
|
|
412
413
|
onModelStart: () => {
|
|
413
414
|
if (hadRealOutput)
|
|
414
415
|
return; // real output already shown; don't overwrite with phrases
|
package/package.json
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ouro.bot/cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.725",
|
|
4
|
+
"engines": {
|
|
5
|
+
"node": ">=22"
|
|
6
|
+
},
|
|
4
7
|
"main": "dist/heart/daemon/ouro-entry.js",
|
|
5
8
|
"bin": {
|
|
6
9
|
"cli": "dist/heart/daemon/ouro-bot-entry.js",
|
|
@@ -61,6 +64,7 @@
|
|
|
61
64
|
"@ouro.bot/friends": "0.1.0-alpha.7",
|
|
62
65
|
"@types/react": "^17.0.91",
|
|
63
66
|
"@types/ws": "^8.18.1",
|
|
67
|
+
"better-sqlite3": "13.0.2",
|
|
64
68
|
"fast-glob": "^3.3.3",
|
|
65
69
|
"ink": "^3.2.0",
|
|
66
70
|
"mailparser": "^3.9.8",
|
|
@@ -77,6 +81,7 @@
|
|
|
77
81
|
},
|
|
78
82
|
"devDependencies": {
|
|
79
83
|
"@testing-library/react": "^16.3.2",
|
|
84
|
+
"@types/better-sqlite3": "7.6.13",
|
|
80
85
|
"@types/mailparser": "^3.4.6",
|
|
81
86
|
"@types/semver": "^7.7.1",
|
|
82
87
|
"@types/smtp-server": "^3.5.13",
|