@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
|
@@ -10,6 +10,12 @@ exports.renderExactStatusReplyContract = renderExactStatusReplyContract;
|
|
|
10
10
|
const config_1 = require("../heart/config");
|
|
11
11
|
const active_work_1 = require("../heart/active-work");
|
|
12
12
|
const runtime_1 = require("../nerves/runtime");
|
|
13
|
+
const orientation_frame_1 = require("../heart/orientation-frame");
|
|
14
|
+
function labelSteeringSurface(surface, options) {
|
|
15
|
+
return typeof options?.resumePriorWork === "boolean"
|
|
16
|
+
? (0, orientation_frame_1.labelPriorWorkSurface)(surface, options.resumePriorWork)
|
|
17
|
+
: surface;
|
|
18
|
+
}
|
|
13
19
|
function findActivePersistentObligation(frame) {
|
|
14
20
|
if (!frame)
|
|
15
21
|
return null;
|
|
@@ -47,7 +53,7 @@ function findCurrentSessionStatusObligation(frame) {
|
|
|
47
53
|
.sort(newestObligationFirst);
|
|
48
54
|
return openObligations.find((obligation) => matchesCurrentSession(frame, obligation)) ?? null;
|
|
49
55
|
}
|
|
50
|
-
function renderActiveObligationSteering(obligation) {
|
|
56
|
+
function renderActiveObligationSteering(obligation, options) {
|
|
51
57
|
(0, runtime_1.emitNervesEvent)({
|
|
52
58
|
component: "mind",
|
|
53
59
|
event: "mind.obligation_steering_rendered",
|
|
@@ -63,10 +69,10 @@ function renderActiveObligationSteering(obligation) {
|
|
|
63
69
|
const surfaceLine = obligation.currentSurface?.label
|
|
64
70
|
? `\nright now that work is happening in ${obligation.currentSurface.label}.`
|
|
65
71
|
: "";
|
|
66
|
-
return `## where my attention is
|
|
72
|
+
return labelSteeringSurface(`## where my attention is
|
|
67
73
|
i'm already working on something i owe ${name}.${surfaceLine}
|
|
68
74
|
|
|
69
|
-
i should close that loop before i act like this is a fresh blank turn
|
|
75
|
+
i should close that loop before i act like this is a fresh blank turn.`, options);
|
|
70
76
|
}
|
|
71
77
|
function mergeArtifactFallback(obligation) {
|
|
72
78
|
const trimmed = obligation.content.trim();
|
|
@@ -141,7 +147,7 @@ function formatNextAction(frame, obligation) {
|
|
|
141
147
|
}
|
|
142
148
|
return obligation ? "continue the active loop and bring the result back here" : "";
|
|
143
149
|
}
|
|
144
|
-
function renderConcreteStatusGuidance(frame, obligation) {
|
|
150
|
+
function renderConcreteStatusGuidance(frame, obligation, options) {
|
|
145
151
|
const activeLane = obligation ? formatActiveLane(frame, obligation) : "";
|
|
146
152
|
const currentArtifact = formatCurrentArtifact(frame, obligation);
|
|
147
153
|
const nextAction = formatNextAction(frame, obligation);
|
|
@@ -150,19 +156,19 @@ function renderConcreteStatusGuidance(frame, obligation) {
|
|
|
150
156
|
: "";
|
|
151
157
|
if (!activeLane && !currentArtifact && !nextAction)
|
|
152
158
|
return "";
|
|
153
|
-
return `if someone asks what i'm doing or for status mid-task, i answer from these live facts instead of copying a canned block.
|
|
159
|
+
return labelSteeringSurface(`if someone asks what i'm doing or for status mid-task, i answer from these live facts instead of copying a canned block.
|
|
154
160
|
the live conversation is ${liveConversation || "not in a live conversation"}.
|
|
155
161
|
the active lane is ${activeLane}.
|
|
156
162
|
the current artifact is ${currentArtifact}.
|
|
157
163
|
if i just finished or verified something concrete in this live lane, i name that as the latest checkpoint.
|
|
158
164
|
the next action is ${nextAction}.
|
|
159
165
|
|
|
160
|
-
i answer naturally from those facts instead of forcing a canned status block
|
|
166
|
+
i answer naturally from those facts instead of forcing a canned status block.`, options);
|
|
161
167
|
}
|
|
162
|
-
function renderLiveThreadStatusShape(frame) {
|
|
168
|
+
function renderLiveThreadStatusShape(frame, options) {
|
|
163
169
|
if (!frame.currentSession)
|
|
164
170
|
return "";
|
|
165
|
-
return `if someone asks what i'm doing or for status mid-task in this live thread, i answer in these exact lines, in order, with no intro paragraph:
|
|
171
|
+
return labelSteeringSurface(`if someone asks what i'm doing or for status mid-task in this live thread, i answer in these exact lines, in order, with no intro paragraph:
|
|
166
172
|
live conversation: ${frame.currentSession.channel}/${frame.currentSession.key}
|
|
167
173
|
active lane: this same thread
|
|
168
174
|
current artifact: <actual artifact or "no artifact yet">
|
|
@@ -172,7 +178,7 @@ next action: <smallest concrete next step i'm taking now>
|
|
|
172
178
|
no recap paragraph before those lines.
|
|
173
179
|
no option list.
|
|
174
180
|
present tense only.
|
|
175
|
-
if a finished step matters, i label it "just finished" instead of presenting it as current work
|
|
181
|
+
if a finished step matters, i label it "just finished" instead of presenting it as current work.`, options);
|
|
176
182
|
}
|
|
177
183
|
function buildExactStatusReply(frame, obligation, latestCheckpoint, statusCheckScope) {
|
|
178
184
|
const headerObligation = statusCheckScope === "all-sessions-family"
|
package/dist/mind/prompt.js
CHANGED
|
@@ -767,26 +767,56 @@ function bridgeContextSection(options) {
|
|
|
767
767
|
const bridgeContext = options?.bridgeContext?.trim() ?? "";
|
|
768
768
|
if (!bridgeContext)
|
|
769
769
|
return "";
|
|
770
|
-
|
|
770
|
+
const surface = bridgeContext.startsWith("## ") ? bridgeContext : `## active bridge work\n${bridgeContext}`;
|
|
771
|
+
return (0, orientation_frame_1.labelPriorWorkSurface)(surface, options?.resumePriorWork === true);
|
|
771
772
|
}
|
|
772
773
|
function startOfTurnPacketSection(options) {
|
|
773
|
-
|
|
774
|
+
const packet = options?.startOfTurnPacket ?? "";
|
|
775
|
+
if (!packet)
|
|
776
|
+
return "";
|
|
777
|
+
const instruction = options?.resumePriorWork === true
|
|
778
|
+
? orientation_frame_1.EXPLICIT_PRIOR_WORK_RESUME_INSTRUCTION
|
|
779
|
+
: orientation_frame_1.BACKGROUND_ONLY_INSTRUCTION;
|
|
780
|
+
const lines = packet.split("\n");
|
|
781
|
+
if (lines.some((line) => line === orientation_frame_1.BACKGROUND_ONLY_INSTRUCTION || line === orientation_frame_1.EXPLICIT_PRIOR_WORK_RESUME_INSTRUCTION)) {
|
|
782
|
+
return lines
|
|
783
|
+
.map((line) => line === orientation_frame_1.BACKGROUND_ONLY_INSTRUCTION || line === orientation_frame_1.EXPLICIT_PRIOR_WORK_RESUME_INSTRUCTION
|
|
784
|
+
? instruction
|
|
785
|
+
: line)
|
|
786
|
+
.join("\n");
|
|
787
|
+
}
|
|
788
|
+
return (0, orientation_frame_1.labelPriorWorkSurface)(packet, options?.resumePriorWork === true);
|
|
774
789
|
}
|
|
775
790
|
function arcResumeSection(options) {
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
791
|
+
if (!options?.flightRecorderResume)
|
|
792
|
+
return "";
|
|
793
|
+
return (0, orientation_frame_1.labelPriorWorkSurface)((0, flight_recorder_1.formatFlightRecorderResume)(options.flightRecorderResume), options.resumePriorWork === true);
|
|
794
|
+
}
|
|
795
|
+
function currentTriggerSection(channel, options) {
|
|
796
|
+
const frame = options?.orientationFrame ?? {
|
|
797
|
+
schemaVersion: 1,
|
|
798
|
+
channel,
|
|
799
|
+
currentUserSpeech: [],
|
|
800
|
+
priorAssistantReferents: [],
|
|
801
|
+
signals: [],
|
|
802
|
+
actionPolicy: { mode: "normal" },
|
|
803
|
+
};
|
|
804
|
+
return (0, orientation_frame_1.renderOrientationFrame)(frame);
|
|
780
805
|
}
|
|
781
806
|
function activeWorkSection(options) {
|
|
782
807
|
if (!options?.activeWorkFrame)
|
|
783
808
|
return "";
|
|
784
|
-
return (0, active_work_1.formatActiveWorkFrame)(options.activeWorkFrame, {
|
|
809
|
+
return (0, active_work_1.formatActiveWorkFrame)(options.activeWorkFrame, {
|
|
810
|
+
obligationDetailsRenderedElsewhere: !!options?.startOfTurnPacket,
|
|
811
|
+
resumePriorWork: options.resumePriorWork === true,
|
|
812
|
+
});
|
|
785
813
|
}
|
|
786
814
|
function liveWorldStateSection(options) {
|
|
787
815
|
if (!options?.activeWorkFrame)
|
|
788
816
|
return "";
|
|
789
|
-
return (0, active_work_1.formatLiveWorldStateCheckpoint)(options.activeWorkFrame
|
|
817
|
+
return (0, active_work_1.formatLiveWorldStateCheckpoint)(options.activeWorkFrame, {
|
|
818
|
+
resumePriorWork: options.resumePriorWork === true,
|
|
819
|
+
});
|
|
790
820
|
}
|
|
791
821
|
function pendingMessagesSection(options) {
|
|
792
822
|
const pending = options?.pendingMessages;
|
|
@@ -936,13 +966,13 @@ function familyCrossSessionTruthSection(context, options) {
|
|
|
936
966
|
return "";
|
|
937
967
|
// When start-of-turn packet is present, compress to one line
|
|
938
968
|
if (options?.startOfTurnPacket) {
|
|
939
|
-
return "When family asks whole-self status, answer from the cross-session picture above.";
|
|
969
|
+
return (0, orientation_frame_1.labelPriorWorkSurface)("When family asks whole-self status, answer from the cross-session picture above.", options.resumePriorWork === true);
|
|
940
970
|
}
|
|
941
|
-
return `## cross-session truth
|
|
971
|
+
return (0, orientation_frame_1.labelPriorWorkSurface)(`## cross-session truth
|
|
942
972
|
When family asks what I'm up to or how things are going, I answer from the live world-state across visible sessions and lanes, not just the current thread.
|
|
943
973
|
When live state conflicts with older transcript history, live state wins.
|
|
944
974
|
I say what I can see, what is active, and what the next concrete step is.
|
|
945
|
-
If part of the picture is still unclear, I say so plainly and note what still needs checking
|
|
975
|
+
If part of the picture is still unclear, I say so plainly and note what still needs checking.`, options.resumePriorWork === true);
|
|
946
976
|
}
|
|
947
977
|
function centerOfGravitySteeringSection(channel, options, context) {
|
|
948
978
|
if (channel === "inner")
|
|
@@ -951,19 +981,21 @@ function centerOfGravitySteeringSection(channel, options, context) {
|
|
|
951
981
|
if (!frame)
|
|
952
982
|
return "";
|
|
953
983
|
const cog = frame.centerOfGravity;
|
|
984
|
+
const labelSurface = (surface) => (0, orientation_frame_1.labelPriorWorkSurface)(surface, options?.resumePriorWork === true);
|
|
985
|
+
const steeringOptions = { resumePriorWork: options?.resumePriorWork === true };
|
|
954
986
|
const job = frame.inner?.job;
|
|
955
987
|
const activeObligation = (0, obligation_steering_1.findActivePersistentObligation)(frame);
|
|
956
988
|
const statusObligation = (0, obligation_steering_1.findStatusObligation)(frame);
|
|
957
|
-
const genericConcreteStatus = (0, obligation_steering_1.renderConcreteStatusGuidance)(frame, statusObligation);
|
|
989
|
+
const genericConcreteStatus = (0, obligation_steering_1.renderConcreteStatusGuidance)(frame, statusObligation, steeringOptions);
|
|
958
990
|
const liveWorldClause = context?.friend?.trustLevel === "family"
|
|
959
991
|
? "\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."
|
|
960
992
|
: "";
|
|
961
993
|
if (cog === "local-turn") {
|
|
962
|
-
return genericConcreteStatus || (0, obligation_steering_1.renderLiveThreadStatusShape)(frame);
|
|
994
|
+
return genericConcreteStatus || (0, obligation_steering_1.renderLiveThreadStatusShape)(frame, steeringOptions);
|
|
963
995
|
}
|
|
964
996
|
if (cog === "inward-work") {
|
|
965
997
|
if (activeObligation) {
|
|
966
|
-
return `${(0, obligation_steering_1.renderActiveObligationSteering)(activeObligation)}${liveWorldClause}
|
|
998
|
+
return `${(0, obligation_steering_1.renderActiveObligationSteering)(activeObligation, steeringOptions)}${liveWorldClause}
|
|
967
999
|
|
|
968
1000
|
${genericConcreteStatus}`;
|
|
969
1001
|
}
|
|
@@ -974,21 +1006,21 @@ ${genericConcreteStatus}`;
|
|
|
974
1006
|
const obligationClause = job.obligationStatus === "pending"
|
|
975
1007
|
? "\ni still owe them an answer."
|
|
976
1008
|
: "";
|
|
977
|
-
return `## where my attention is
|
|
1009
|
+
return labelSurface(`## where my attention is
|
|
978
1010
|
i'm thinking through something privately right now.${originClause}${obligationClause}${liveWorldClause}
|
|
979
1011
|
|
|
980
1012
|
if this conversation connects to that private-runtime work, i can weave them together.
|
|
981
|
-
if it's separate, i can be fully present here -- my private-runtime work will wait
|
|
1013
|
+
if it's separate, i can be fully present here -- my private-runtime work will wait.`);
|
|
982
1014
|
}
|
|
983
1015
|
/* v8 ignore start -- surfaced/idle/shared branches tested in prompt-steering.test.ts; CI module caching prevents attribution @preserve */
|
|
984
1016
|
if (job?.status === "surfaced") {
|
|
985
1017
|
const originClause = job.origin
|
|
986
1018
|
? ` this started when ${job.origin.friendName ?? job.origin.friendId} asked about something.`
|
|
987
1019
|
: "";
|
|
988
|
-
return `## where my attention is
|
|
1020
|
+
return labelSurface(`## where my attention is
|
|
989
1021
|
i've been thinking privately and reached something.${originClause}${liveWorldClause}
|
|
990
1022
|
|
|
991
|
-
i should bring my answer back to the conversation it came from
|
|
1023
|
+
i should bring my answer back to the conversation it came from.`);
|
|
992
1024
|
}
|
|
993
1025
|
const liveCodingSession = frame.codingSessions?.[0];
|
|
994
1026
|
if (liveCodingSession) {
|
|
@@ -1009,25 +1041,25 @@ after i name this lane, i widen back out with:
|
|
|
1009
1041
|
other active sessions:
|
|
1010
1042
|
${otherSessionLines.length > 0 ? otherSessionLines.join("\n") : "- none"}`
|
|
1011
1043
|
: "";
|
|
1012
|
-
return `## where my attention is
|
|
1044
|
+
return labelSurface(`## where my attention is
|
|
1013
1045
|
i already have coding work running in ${liveCodingSession.runner} ${liveCodingSession.id}${scopeClause}.${familyStatusClause}${liveWorldClause}
|
|
1014
1046
|
|
|
1015
|
-
i should orient around that live lane first, then decide what still needs to come back here
|
|
1047
|
+
i should orient around that live lane first, then decide what still needs to come back here.`);
|
|
1016
1048
|
}
|
|
1017
1049
|
if (genericConcreteStatus) {
|
|
1018
1050
|
return genericConcreteStatus;
|
|
1019
1051
|
}
|
|
1020
|
-
return `## where my attention is
|
|
1052
|
+
return labelSurface(`## where my attention is
|
|
1021
1053
|
i have unfinished work that needs attention before i move on.
|
|
1022
1054
|
|
|
1023
|
-
i can take it inward with ponder to think privately, or address it directly here
|
|
1055
|
+
i can take it inward with ponder to think privately, or address it directly here.`);
|
|
1024
1056
|
}
|
|
1025
1057
|
if (cog === "shared-work") {
|
|
1026
1058
|
/* v8 ignore stop */
|
|
1027
|
-
return `## where my attention is
|
|
1059
|
+
return labelSurface(`## where my attention is
|
|
1028
1060
|
this work touches multiple conversations -- i'm holding threads across sessions.${liveWorldClause}
|
|
1029
1061
|
|
|
1030
|
-
i should keep the different sides aligned. what i learn here may matter there, and vice versa
|
|
1062
|
+
i should keep the different sides aligned. what i learn here may matter there, and vice versa.`);
|
|
1031
1063
|
}
|
|
1032
1064
|
/* v8 ignore next -- unreachable: all center-of-gravity modes covered above @preserve */
|
|
1033
1065
|
return "";
|
|
@@ -1051,7 +1083,7 @@ function commitmentsSection(options) {
|
|
|
1051
1083
|
const commitments = (0, commitments_1.deriveCommitments)(options.activeWorkFrame, job, options.activeWorkFrame.pendingObligations, awaits);
|
|
1052
1084
|
if (commitments.committedTo.length === 0 && awaits.length === 0)
|
|
1053
1085
|
return "";
|
|
1054
|
-
return `## my commitments\n\n${(0, commitments_1.formatCommitments)(commitments)}
|
|
1086
|
+
return (0, orientation_frame_1.labelPriorWorkSurface)(`## my commitments\n\n${(0, commitments_1.formatCommitments)(commitments)}`, options.resumePriorWork === true);
|
|
1055
1087
|
}
|
|
1056
1088
|
const DELEGATION_REASON_PROSE_HINT = {
|
|
1057
1089
|
explicit_reflection: "something here calls for reflection",
|
|
@@ -1451,8 +1483,8 @@ async function buildSystem(channel = "cli", options, context) {
|
|
|
1451
1483
|
rhythmStatusSection(options?.daemonHealth),
|
|
1452
1484
|
// Group 7: dynamic state for this turn
|
|
1453
1485
|
"# dynamic state for this turn",
|
|
1486
|
+
currentTriggerSection(channel, options),
|
|
1454
1487
|
startOfTurnPacketSection(options),
|
|
1455
|
-
orientationFrameSection(options),
|
|
1456
1488
|
pulseSection(channel),
|
|
1457
1489
|
tripLedgerTruthSection(channel, context),
|
|
1458
1490
|
liveWorldStateSection(options),
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.REPO_SLUG = void 0;
|
|
4
4
|
exports.getTestRunsRoot = getTestRunsRoot;
|
|
5
|
+
exports.coverageRunOwner = coverageRunOwner;
|
|
6
|
+
exports.getOwnedTestRunsRoot = getOwnedTestRunsRoot;
|
|
5
7
|
exports.createRunId = createRunId;
|
|
6
8
|
exports.getRunDir = getRunDir;
|
|
7
9
|
exports.writeActiveRun = writeActiveRun;
|
|
@@ -10,68 +12,86 @@ exports.clearActiveRun = clearActiveRun;
|
|
|
10
12
|
exports.writeLatestRun = writeLatestRun;
|
|
11
13
|
exports.readLatestRun = readLatestRun;
|
|
12
14
|
const fs_1 = require("fs");
|
|
13
|
-
const
|
|
15
|
+
const crypto_1 = require("crypto");
|
|
14
16
|
const os_1 = require("os");
|
|
17
|
+
const path_1 = require("path");
|
|
15
18
|
exports.REPO_SLUG = "ouroboros-agent-harness";
|
|
16
19
|
function getTestRunsRoot(repoSlug = exports.REPO_SLUG) {
|
|
17
20
|
return (0, path_1.join)((0, os_1.tmpdir)(), "ouroboros-test-runs", repoSlug);
|
|
18
21
|
}
|
|
22
|
+
function coverageRunOwner(cwd = process.cwd()) {
|
|
23
|
+
const hash = (0, crypto_1.createHash)("sha256").update((0, path_1.resolve)(cwd)).digest("hex").slice(0, 12);
|
|
24
|
+
return `cwd-${hash}`;
|
|
25
|
+
}
|
|
26
|
+
function getOwnedTestRunsRoot(repoSlug = exports.REPO_SLUG, cwd = process.cwd()) {
|
|
27
|
+
return (0, path_1.join)(getTestRunsRoot(repoSlug), coverageRunOwner(cwd));
|
|
28
|
+
}
|
|
19
29
|
function createRunId(now = new Date()) {
|
|
20
30
|
return now.toISOString().replace(/[:.]/g, "-");
|
|
21
31
|
}
|
|
22
|
-
function getRunDir(runId, repoSlug = exports.REPO_SLUG) {
|
|
23
|
-
return (0, path_1.join)(
|
|
32
|
+
function getRunDir(runId, repoSlug = exports.REPO_SLUG, cwd = process.cwd()) {
|
|
33
|
+
return (0, path_1.join)(getOwnedTestRunsRoot(repoSlug, cwd), runId);
|
|
24
34
|
}
|
|
25
|
-
function getActiveRunPath(repoSlug = exports.REPO_SLUG) {
|
|
26
|
-
return (0, path_1.join)(
|
|
35
|
+
function getActiveRunPath(repoSlug = exports.REPO_SLUG, cwd = process.cwd()) {
|
|
36
|
+
return (0, path_1.join)(getOwnedTestRunsRoot(repoSlug, cwd), ".active-run.json");
|
|
27
37
|
}
|
|
28
|
-
function getLatestRunPath(repoSlug = exports.REPO_SLUG) {
|
|
29
|
-
return (0, path_1.join)(
|
|
38
|
+
function getLatestRunPath(repoSlug = exports.REPO_SLUG, cwd = process.cwd()) {
|
|
39
|
+
return (0, path_1.join)(getOwnedTestRunsRoot(repoSlug, cwd), "latest-run.json");
|
|
30
40
|
}
|
|
31
|
-
function ensureRoot(repoSlug = exports.REPO_SLUG) {
|
|
32
|
-
const root =
|
|
41
|
+
function ensureRoot(repoSlug = exports.REPO_SLUG, cwd = process.cwd()) {
|
|
42
|
+
const root = getOwnedTestRunsRoot(repoSlug, cwd);
|
|
33
43
|
(0, fs_1.mkdirSync)(root, { recursive: true });
|
|
34
44
|
return root;
|
|
35
45
|
}
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
function isValidRunInfo(value, repoSlug, cwd) {
|
|
47
|
+
if (typeof value !== "object" || value === null)
|
|
48
|
+
return false;
|
|
49
|
+
const parsed = value;
|
|
50
|
+
const owner = coverageRunOwner(cwd);
|
|
51
|
+
if (parsed.repo_slug !== repoSlug ||
|
|
52
|
+
parsed.run_owner !== owner ||
|
|
53
|
+
typeof parsed.run_id !== "string" ||
|
|
54
|
+
parsed.run_id.length === 0 ||
|
|
55
|
+
typeof parsed.run_dir !== "string" ||
|
|
56
|
+
parsed.run_dir.length === 0 ||
|
|
57
|
+
typeof parsed.created_at !== "string" ||
|
|
58
|
+
parsed.created_at.length === 0) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
const ownerRoot = (0, path_1.resolve)(getOwnedTestRunsRoot(repoSlug, cwd));
|
|
62
|
+
const runDir = (0, path_1.resolve)(parsed.run_dir);
|
|
63
|
+
const relativeRunDir = (0, path_1.relative)(ownerRoot, runDir);
|
|
64
|
+
const isContained = relativeRunDir !== "" && relativeRunDir !== ".." && !relativeRunDir.startsWith(`..${path_1.sep}`) && !(0, path_1.isAbsolute)(relativeRunDir);
|
|
65
|
+
return isContained && runDir === (0, path_1.resolve)(ownerRoot, parsed.run_id);
|
|
39
66
|
}
|
|
40
|
-
function
|
|
41
|
-
const filePath = getActiveRunPath(repoSlug);
|
|
67
|
+
function readRunInfo(filePath, repoSlug, cwd) {
|
|
42
68
|
if (!(0, fs_1.existsSync)(filePath))
|
|
43
69
|
return null;
|
|
44
70
|
try {
|
|
45
71
|
const parsed = JSON.parse((0, fs_1.readFileSync)(filePath, "utf8"));
|
|
46
|
-
|
|
47
|
-
return null;
|
|
48
|
-
return parsed;
|
|
72
|
+
return isValidRunInfo(parsed, repoSlug, cwd) ? parsed : null;
|
|
49
73
|
}
|
|
50
74
|
catch {
|
|
51
75
|
return null;
|
|
52
76
|
}
|
|
53
77
|
}
|
|
54
|
-
function
|
|
55
|
-
|
|
78
|
+
function writeActiveRun(info, cwd = process.cwd()) {
|
|
79
|
+
ensureRoot(info.repo_slug, cwd);
|
|
80
|
+
(0, fs_1.writeFileSync)(getActiveRunPath(info.repo_slug, cwd), JSON.stringify(info, null, 2), "utf8");
|
|
81
|
+
}
|
|
82
|
+
function readActiveRun(repoSlug = exports.REPO_SLUG, cwd = process.cwd()) {
|
|
83
|
+
return readRunInfo(getActiveRunPath(repoSlug, cwd), repoSlug, cwd);
|
|
84
|
+
}
|
|
85
|
+
function clearActiveRun(repoSlug = exports.REPO_SLUG, cwd = process.cwd()) {
|
|
86
|
+
const filePath = getActiveRunPath(repoSlug, cwd);
|
|
56
87
|
if ((0, fs_1.existsSync)(filePath)) {
|
|
57
88
|
(0, fs_1.unlinkSync)(filePath);
|
|
58
89
|
}
|
|
59
90
|
}
|
|
60
|
-
function writeLatestRun(info) {
|
|
61
|
-
ensureRoot(info.repo_slug);
|
|
62
|
-
(0, fs_1.writeFileSync)(getLatestRunPath(info.repo_slug), JSON.stringify(info, null, 2), "utf8");
|
|
91
|
+
function writeLatestRun(info, cwd = process.cwd()) {
|
|
92
|
+
ensureRoot(info.repo_slug, cwd);
|
|
93
|
+
(0, fs_1.writeFileSync)(getLatestRunPath(info.repo_slug, cwd), JSON.stringify(info, null, 2), "utf8");
|
|
63
94
|
}
|
|
64
|
-
function readLatestRun(repoSlug = exports.REPO_SLUG) {
|
|
65
|
-
|
|
66
|
-
if (!(0, fs_1.existsSync)(filePath))
|
|
67
|
-
return null;
|
|
68
|
-
try {
|
|
69
|
-
const parsed = JSON.parse((0, fs_1.readFileSync)(filePath, "utf8"));
|
|
70
|
-
if (!parsed.run_id || !parsed.run_dir)
|
|
71
|
-
return null;
|
|
72
|
-
return parsed;
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
95
|
+
function readLatestRun(repoSlug = exports.REPO_SLUG, cwd = process.cwd()) {
|
|
96
|
+
return readRunInfo(getLatestRunPath(repoSlug, cwd), repoSlug, cwd);
|
|
77
97
|
}
|
|
@@ -38,7 +38,7 @@ const path = __importStar(require("node:path"));
|
|
|
38
38
|
const identity_1 = require("../../heart/identity");
|
|
39
39
|
const core_1 = require("./core");
|
|
40
40
|
function parseArgs(argv) {
|
|
41
|
-
const args = { process:
|
|
41
|
+
const args = { process: core_1.DEFAULT_NERVES_PROCESS, json: false, help: false };
|
|
42
42
|
for (let i = 0; i < argv.length; i++) {
|
|
43
43
|
const token = argv[i];
|
|
44
44
|
const next = argv[i + 1];
|
|
@@ -100,7 +100,7 @@ function printHelp() {
|
|
|
100
100
|
"Tail the agent's nerves ndjson and filter recent events. Read-only.",
|
|
101
101
|
"",
|
|
102
102
|
"options:",
|
|
103
|
-
|
|
103
|
+
` --process <name> log stream to read (default: ${core_1.DEFAULT_NERVES_PROCESS})`,
|
|
104
104
|
" --agent <name> agent bundle to read from (default: current)",
|
|
105
105
|
" --component <substr> filter by component substring (case-insensitive)",
|
|
106
106
|
" --event <substr> filter by event-name substring (case-insensitive)",
|
|
@@ -33,10 +33,12 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.DEFAULT_NERVES_PROCESS = void 0;
|
|
36
37
|
exports.parseDuration = parseDuration;
|
|
37
38
|
exports.reviewNerveEvents = reviewNerveEvents;
|
|
38
39
|
exports.formatNerveEntry = formatNerveEntry;
|
|
39
40
|
const fs = __importStar(require("node:fs"));
|
|
41
|
+
exports.DEFAULT_NERVES_PROCESS = "ouro";
|
|
40
42
|
const DURATION_PATTERN = /^(\d+(?:\.\d+)?)(ms|s|m|h|d)$/;
|
|
41
43
|
function parseDuration(value) {
|
|
42
44
|
const match = DURATION_PATTERN.exec(value.trim().toLowerCase());
|
|
@@ -56,15 +58,20 @@ function parseDuration(value) {
|
|
|
56
58
|
/* v8 ignore stop */
|
|
57
59
|
}
|
|
58
60
|
}
|
|
59
|
-
function
|
|
61
|
+
function entryTimestamp(parsed) {
|
|
60
62
|
if (!parsed)
|
|
61
63
|
return null;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const ms = Date.parse(time);
|
|
65
|
-
return Number.isFinite(ms) ? ms : null;
|
|
64
|
+
if (Object.prototype.hasOwnProperty.call(parsed, "ts")) {
|
|
65
|
+
return typeof parsed.ts === "string" ? parsed.ts : null;
|
|
66
66
|
}
|
|
67
|
-
return null;
|
|
67
|
+
return typeof parsed.time === "string" ? parsed.time : null;
|
|
68
|
+
}
|
|
69
|
+
function entryTimeMs(parsed) {
|
|
70
|
+
const timestamp = entryTimestamp(parsed);
|
|
71
|
+
if (timestamp === null)
|
|
72
|
+
return null;
|
|
73
|
+
const ms = Date.parse(timestamp);
|
|
74
|
+
return Number.isFinite(ms) ? ms : null;
|
|
68
75
|
}
|
|
69
76
|
function readLastNLines(filePath, maxLines, maxBytes = 8 * 1024 * 1024) {
|
|
70
77
|
let text;
|
|
@@ -143,7 +150,7 @@ function formatNerveEntry(entry) {
|
|
|
143
150
|
const parsed = entry.parsed;
|
|
144
151
|
if (!parsed)
|
|
145
152
|
return entry.raw;
|
|
146
|
-
const time =
|
|
153
|
+
const time = entryTimestamp(parsed) ?? "";
|
|
147
154
|
const level = String(parsed.level ?? "info");
|
|
148
155
|
const component = String(parsed.component ?? "?");
|
|
149
156
|
const event = String(parsed.event ?? "?");
|
|
@@ -27,6 +27,7 @@ const tools_evolution_1 = require("./tools-evolution");
|
|
|
27
27
|
const tools_runtime_1 = require("./tools-runtime");
|
|
28
28
|
const tools_orientation_1 = require("./tools-orientation");
|
|
29
29
|
const tools_rsvp_1 = require("./tools-rsvp");
|
|
30
|
+
const tools_habits_1 = require("./tools-habits");
|
|
30
31
|
// Re-export flow tools for consumers that import them from tools-base
|
|
31
32
|
var tools_flow_1 = require("./tools-flow");
|
|
32
33
|
Object.defineProperty(exports, "ponderTool", { enumerable: true, get: function () { return tools_flow_1.ponderTool; } });
|
|
@@ -69,6 +70,7 @@ exports.baseToolDefinitions = [
|
|
|
69
70
|
...tools_orientation_1.orientationToolDefinitions,
|
|
70
71
|
...tools_runtime_1.runtimeToolDefinitions,
|
|
71
72
|
...tools_rsvp_1.rsvpToolDefinitions,
|
|
73
|
+
...tools_habits_1.habitToolDefinitions,
|
|
72
74
|
];
|
|
73
75
|
// Convenience array of just the tool schemas (no handler/integration metadata).
|
|
74
76
|
// Used by consumers that need the OpenAI function-tool format.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.habitToolDefinitions = void 0;
|
|
4
|
+
const friends_1 = require("@ouro.bot/friends");
|
|
5
|
+
const runtime_1 = require("../nerves/runtime");
|
|
6
|
+
const habit_cancel_1 = require("../heart/habits/habit-cancel");
|
|
7
|
+
const semantic_receipts_1 = require("../senses/bluebubbles/semantic-receipts");
|
|
8
|
+
const CAPTURE_LOCATOR_PATTERN = /^capture:([a-f0-9]{64})$/;
|
|
9
|
+
const ACTOR_AUTHORITY_ERROR = "habit_cancel requires trusted current-ingress actor authority";
|
|
10
|
+
async function requireTrustedCurrentIngressActor(agentRoot, captureKeyHash, context) {
|
|
11
|
+
const capture = (0, semantic_receipts_1.readBlueBubblesSemanticCaptureAtRoot)(agentRoot, captureKeyHash);
|
|
12
|
+
const actor = capture?.event.actor;
|
|
13
|
+
if (!actor || !context?.friendStore)
|
|
14
|
+
throw new Error(ACTOR_AUTHORITY_ERROR);
|
|
15
|
+
try {
|
|
16
|
+
const friend = await context.friendStore.findByExternalId(actor.provider, actor.externalId);
|
|
17
|
+
if (!friends_1.TRUSTED_LEVELS.has(friend?.trustLevel ?? "stranger")) {
|
|
18
|
+
throw new Error(ACTOR_AUTHORITY_ERROR);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
throw new Error(ACTOR_AUTHORITY_ERROR);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.habitToolDefinitions = [
|
|
26
|
+
{
|
|
27
|
+
tool: {
|
|
28
|
+
type: "function",
|
|
29
|
+
function: {
|
|
30
|
+
name: "habit_cancel",
|
|
31
|
+
description: "Cancel a named habit from the immutable evidence capture that triggered the current turn.",
|
|
32
|
+
parameters: {
|
|
33
|
+
type: "object",
|
|
34
|
+
properties: {
|
|
35
|
+
habit: {
|
|
36
|
+
type: "string",
|
|
37
|
+
description: "Exact habit identifier to cancel.",
|
|
38
|
+
},
|
|
39
|
+
evidence: {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: "Current ingress evidence locator in capture:<sha256> form.",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
required: ["habit", "evidence"],
|
|
45
|
+
additionalProperties: false,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
riskProfile: {
|
|
50
|
+
mutates: "durable_state_write",
|
|
51
|
+
risk: "high",
|
|
52
|
+
reason: "Permanently transitions a durable habit definition to cancelled.",
|
|
53
|
+
},
|
|
54
|
+
terminalProjection: {
|
|
55
|
+
mode: "verbatim",
|
|
56
|
+
requiresSoleCall: true,
|
|
57
|
+
clearBufferedText: true,
|
|
58
|
+
},
|
|
59
|
+
summaryKeys: ["habit", "evidence"],
|
|
60
|
+
handler: async (args, context) => {
|
|
61
|
+
(0, runtime_1.emitNervesEvent)({
|
|
62
|
+
component: "tools",
|
|
63
|
+
event: "tools.habit_cancel_invoked",
|
|
64
|
+
message: "habit cancellation tool invoked",
|
|
65
|
+
meta: { habit: typeof args.habit === "string" ? args.habit : null },
|
|
66
|
+
});
|
|
67
|
+
const keys = Object.keys(args);
|
|
68
|
+
const unexpected = keys.find((key) => key !== "habit" && key !== "evidence");
|
|
69
|
+
if (unexpected)
|
|
70
|
+
throw new Error(`unexpected habit_cancel argument: ${unexpected}`);
|
|
71
|
+
if (keys.length !== 2 || typeof args.habit !== "string" || args.habit.trim().length === 0) {
|
|
72
|
+
throw new Error("habit_cancel requires exactly habit and evidence");
|
|
73
|
+
}
|
|
74
|
+
const match = CAPTURE_LOCATOR_PATTERN.exec(args.evidence);
|
|
75
|
+
if (!match)
|
|
76
|
+
throw new Error("habit_cancel evidence must use capture:<sha256>");
|
|
77
|
+
const currentIngressEvidence = context?.currentIngressEvidence;
|
|
78
|
+
if (!currentIngressEvidence
|
|
79
|
+
|| Object.keys(currentIngressEvidence).length !== 3
|
|
80
|
+
|| Object.keys(currentIngressEvidence).some((key) => (key !== "schemaVersion" && key !== "provider" && key !== "captureKeyHash"))
|
|
81
|
+
|| currentIngressEvidence.schemaVersion !== 1
|
|
82
|
+
|| currentIngressEvidence.provider !== "bluebubbles"
|
|
83
|
+
|| !/^[a-f0-9]{64}$/.test(currentIngressEvidence.captureKeyHash))
|
|
84
|
+
throw new Error("habit_cancel requires authoritative current ingress evidence");
|
|
85
|
+
if (currentIngressEvidence.captureKeyHash !== match[1]) {
|
|
86
|
+
throw new Error("habit_cancel evidence mismatch with current ingress evidence");
|
|
87
|
+
}
|
|
88
|
+
if (!context?.agentRoot)
|
|
89
|
+
throw new Error("habit_cancel requires an agent root");
|
|
90
|
+
await requireTrustedCurrentIngressActor(context.agentRoot, currentIngressEvidence.captureKeyHash, context);
|
|
91
|
+
const receipt = await (0, habit_cancel_1.cancelHabit)({
|
|
92
|
+
agentRoot: context.agentRoot,
|
|
93
|
+
habitId: args.habit,
|
|
94
|
+
evidenceLocator: args.evidence,
|
|
95
|
+
authority: {
|
|
96
|
+
kind: "current_ingress",
|
|
97
|
+
currentIngressEvidence,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
return receipt.acknowledgement;
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
];
|
package/dist/repertoire/tools.js
CHANGED
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.surfaceToolDef = exports.speakTool = exports.restTool = exports.ponderTool = exports.observeTool = exports.settleTool = exports.tools = void 0;
|
|
4
4
|
exports.resetMcpDefinitions = resetMcpDefinitions;
|
|
5
5
|
exports.getToolsForChannel = getToolsForChannel;
|
|
6
|
+
exports.getRestrictedReactionFeedbackTools = getRestrictedReactionFeedbackTools;
|
|
7
|
+
exports.resolveToolDefinition = resolveToolDefinition;
|
|
6
8
|
exports.shellRiskProfile = shellRiskProfile;
|
|
7
9
|
exports.riskProfileForTool = riskProfileForTool;
|
|
8
10
|
exports.riskProfileForToolName = riskProfileForToolName;
|
|
@@ -125,11 +127,24 @@ function getToolsForChannel(capabilities, toolPreferences, _context, providerCap
|
|
|
125
127
|
}
|
|
126
128
|
return filterByCapability(result, providerCapabilities);
|
|
127
129
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
/**
|
|
131
|
+
* The complete provider-facing tool surface for trusted reaction feedback.
|
|
132
|
+
* Keep these schemas sourced from the normal registry so restricted turns
|
|
133
|
+
* cannot drift onto lookalike definitions or inherit channel/MCP tools.
|
|
134
|
+
*/
|
|
135
|
+
function getRestrictedReactionFeedbackTools() {
|
|
136
|
+
const orientationGetTool = tools_base_1.baseToolDefinitions.find((definition) => definition.tool.function.name === "orientation_get").tool;
|
|
137
|
+
return [tools_base_1.settleTool, tools_base_1.observeTool, orientationGetTool];
|
|
138
|
+
}
|
|
139
|
+
// Look up a tool definition from the live combined registry (native + MCP).
|
|
140
|
+
// The base registry is intentionally consulted at call time so tests and
|
|
141
|
+
// runtime extensions cannot leave execution metadata behind a stale snapshot.
|
|
142
|
+
function resolveToolDefinition(toolName) {
|
|
143
|
+
return tools_base_1.baseToolDefinitions.find((d) => d.tool.function.name === toolName)
|
|
144
|
+
?? allDefinitions.find((d) => d.tool.function.name === toolName)
|
|
131
145
|
?? mcpDefinitions.find((d) => d.tool.function.name === toolName);
|
|
132
146
|
}
|
|
147
|
+
const findDefinition = resolveToolDefinition;
|
|
133
148
|
function normalizeGuardArgs(_name, args) {
|
|
134
149
|
return args;
|
|
135
150
|
}
|