@ouro.bot/cli 0.1.0-alpha.1 → 0.1.0-alpha.100
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/AdoptionSpecialist.ouro/agent.json +70 -9
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +5 -2
- package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
- package/README.md +147 -205
- package/assets/ouroboros.png +0 -0
- package/changelog.json +596 -0
- package/dist/heart/active-work.js +251 -0
- package/dist/heart/bridges/manager.js +358 -0
- package/dist/heart/bridges/state-machine.js +135 -0
- package/dist/heart/bridges/store.js +123 -0
- package/dist/heart/commitments.js +109 -0
- package/dist/heart/config.js +102 -23
- package/dist/heart/core.js +512 -94
- package/dist/heart/cross-chat-delivery.js +146 -0
- package/dist/heart/daemon/agent-discovery.js +81 -0
- package/dist/heart/daemon/auth-flow.js +430 -0
- package/dist/heart/daemon/daemon-cli.js +1935 -185
- package/dist/heart/daemon/daemon-entry.js +55 -6
- package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
- package/dist/heart/daemon/daemon.js +218 -9
- package/dist/heart/daemon/hatch-animation.js +35 -0
- package/dist/heart/daemon/hatch-flow.js +10 -83
- package/dist/heart/daemon/hatch-specialist.js +6 -1
- package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
- package/dist/heart/daemon/launchd.js +159 -0
- package/dist/heart/daemon/log-tailer.js +147 -0
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/os-cron.js +260 -0
- package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
- package/dist/heart/daemon/ouro-bot-wrapper.js +4 -3
- package/dist/heart/daemon/ouro-path-installer.js +260 -0
- package/dist/heart/daemon/ouro-uti.js +11 -2
- package/dist/heart/daemon/ouro-version-manager.js +171 -0
- package/dist/heart/daemon/process-manager.js +32 -2
- package/dist/heart/daemon/run-hooks.js +37 -0
- package/dist/heart/daemon/runtime-logging.js +61 -14
- package/dist/heart/daemon/runtime-metadata.js +219 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/sense-manager.js +307 -0
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- package/dist/heart/daemon/socket-client.js +202 -0
- package/dist/heart/daemon/specialist-orchestrator.js +129 -0
- package/dist/heart/daemon/specialist-prompt.js +99 -0
- package/dist/heart/daemon/specialist-tools.js +283 -0
- package/dist/heart/daemon/staged-restart.js +114 -0
- package/dist/heart/daemon/task-scheduler.js +4 -1
- package/dist/heart/daemon/thoughts.js +507 -0
- package/dist/heart/daemon/update-checker.js +111 -0
- package/dist/heart/daemon/update-hooks.js +138 -0
- package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/identity.js +153 -23
- package/dist/heart/kicks.js +1 -19
- package/dist/heart/model-capabilities.js +48 -0
- package/dist/heart/obligations.js +191 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/providers/anthropic.js +77 -9
- package/dist/heart/providers/azure.js +86 -7
- package/dist/heart/providers/github-copilot.js +149 -0
- package/dist/heart/providers/minimax.js +4 -0
- package/dist/heart/providers/openai-codex.js +12 -3
- package/dist/heart/safe-workspace.js +381 -0
- package/dist/heart/sense-truth.js +61 -0
- package/dist/heart/session-activity.js +169 -0
- package/dist/heart/session-recall.js +116 -0
- package/dist/heart/streaming.js +103 -22
- package/dist/heart/target-resolution.js +123 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/mind/associative-recall.js +37 -4
- package/dist/mind/bundle-manifest.js +70 -0
- package/dist/mind/context.js +141 -11
- package/dist/mind/first-impressions.js +16 -2
- package/dist/mind/friends/channel.js +43 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/store-file.js +19 -0
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +9 -1
- package/dist/mind/memory.js +89 -26
- package/dist/mind/obligation-steering.js +31 -0
- package/dist/mind/pending.js +160 -0
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt-refresh.js +20 -0
- package/dist/mind/prompt.js +499 -8
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +15 -2
- package/dist/nerves/coverage/file-completeness.js +14 -4
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/index.js +12 -0
- package/dist/repertoire/ado-client.js +4 -2
- package/dist/repertoire/coding/feedback.js +210 -0
- package/dist/repertoire/coding/index.js +4 -1
- package/dist/repertoire/coding/manager.js +69 -4
- package/dist/repertoire/coding/spawner.js +21 -3
- package/dist/repertoire/coding/tools.js +105 -2
- package/dist/repertoire/data/ado-endpoints.json +188 -0
- package/dist/repertoire/guardrails.js +290 -0
- package/dist/repertoire/mcp-client.js +254 -0
- package/dist/repertoire/mcp-manager.js +195 -0
- package/dist/repertoire/skills.js +3 -26
- package/dist/repertoire/tasks/board.js +12 -0
- package/dist/repertoire/tasks/index.js +23 -9
- package/dist/repertoire/tasks/transitions.js +1 -2
- package/dist/repertoire/tools-base.js +770 -213
- package/dist/repertoire/tools-bluebubbles.js +93 -0
- package/dist/repertoire/tools-teams.js +58 -25
- package/dist/repertoire/tools.js +106 -53
- package/dist/senses/bluebubbles-client.js +484 -0
- package/dist/senses/bluebubbles-entry.js +13 -0
- package/dist/senses/bluebubbles-inbound-log.js +109 -0
- package/dist/senses/bluebubbles-media.js +339 -0
- package/dist/senses/bluebubbles-model.js +261 -0
- package/dist/senses/bluebubbles-mutation-log.js +116 -0
- package/dist/senses/bluebubbles-runtime-state.js +109 -0
- package/dist/senses/bluebubbles-session-cleanup.js +72 -0
- package/dist/senses/bluebubbles.js +1181 -0
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +452 -99
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/debug-activity.js +154 -0
- package/dist/senses/inner-dialog-worker.js +47 -18
- package/dist/senses/inner-dialog.js +387 -70
- package/dist/senses/pipeline.js +307 -0
- package/dist/senses/session-lock.js +119 -0
- package/dist/senses/teams.js +574 -129
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +16 -4
- package/subagents/README.md +4 -68
- package/dist/heart/daemon/subagent-installer.js +0 -125
- package/dist/inner-worker-entry.js +0 -4
- package/subagents/work-doer.md +0 -233
- package/subagents/work-merger.md +0 -593
- package/subagents/work-planner.md +0 -373
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createBridgeState = createBridgeState;
|
|
4
|
+
exports.bridgeStateLabel = bridgeStateLabel;
|
|
5
|
+
exports.activateBridge = activateBridge;
|
|
6
|
+
exports.beginBridgeProcessing = beginBridgeProcessing;
|
|
7
|
+
exports.queueBridgeFollowUp = queueBridgeFollowUp;
|
|
8
|
+
exports.advanceBridgeAfterTurn = advanceBridgeAfterTurn;
|
|
9
|
+
exports.suspendBridge = suspendBridge;
|
|
10
|
+
exports.completeBridge = completeBridge;
|
|
11
|
+
exports.cancelBridge = cancelBridge;
|
|
12
|
+
exports.reconcileBridgeState = reconcileBridgeState;
|
|
13
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
14
|
+
function transition(state, next, action) {
|
|
15
|
+
(0, runtime_1.emitNervesEvent)({
|
|
16
|
+
component: "engine",
|
|
17
|
+
event: "engine.bridge_state_transition",
|
|
18
|
+
message: "bridge state transitioned",
|
|
19
|
+
meta: {
|
|
20
|
+
action,
|
|
21
|
+
from: bridgeStateLabel(state),
|
|
22
|
+
to: bridgeStateLabel(next),
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
return next;
|
|
26
|
+
}
|
|
27
|
+
function assertNonTerminal(state, action) {
|
|
28
|
+
if (state.lifecycle === "completed" || state.lifecycle === "cancelled") {
|
|
29
|
+
throw new Error(`cannot ${action} a terminal bridge`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function createBridgeState() {
|
|
33
|
+
return {
|
|
34
|
+
lifecycle: "forming",
|
|
35
|
+
runtime: "idle",
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function bridgeStateLabel(state) {
|
|
39
|
+
switch (state.lifecycle) {
|
|
40
|
+
case "forming":
|
|
41
|
+
return "forming";
|
|
42
|
+
case "suspended":
|
|
43
|
+
return "suspended";
|
|
44
|
+
case "completed":
|
|
45
|
+
return "completed";
|
|
46
|
+
case "cancelled":
|
|
47
|
+
return "cancelled";
|
|
48
|
+
case "active":
|
|
49
|
+
if (state.runtime === "processing")
|
|
50
|
+
return "active-processing";
|
|
51
|
+
if (state.runtime === "awaiting-follow-up")
|
|
52
|
+
return "awaiting-follow-up";
|
|
53
|
+
return "active-idle";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function activateBridge(state) {
|
|
57
|
+
assertNonTerminal(state, "activate");
|
|
58
|
+
if (state.lifecycle !== "forming" && state.lifecycle !== "suspended") {
|
|
59
|
+
throw new Error("cannot activate bridge from current state");
|
|
60
|
+
}
|
|
61
|
+
return transition(state, { lifecycle: "active", runtime: "idle" }, "activate");
|
|
62
|
+
}
|
|
63
|
+
function beginBridgeProcessing(state) {
|
|
64
|
+
assertNonTerminal(state, "process");
|
|
65
|
+
if (state.lifecycle !== "active" || state.runtime !== "idle") {
|
|
66
|
+
throw new Error("cannot process bridge from current state");
|
|
67
|
+
}
|
|
68
|
+
return transition(state, { lifecycle: "active", runtime: "processing" }, "begin-processing");
|
|
69
|
+
}
|
|
70
|
+
function queueBridgeFollowUp(state) {
|
|
71
|
+
assertNonTerminal(state, "queue");
|
|
72
|
+
if (state.lifecycle !== "active") {
|
|
73
|
+
throw new Error("cannot queue follow-up for non-active bridge");
|
|
74
|
+
}
|
|
75
|
+
if (state.runtime === "processing") {
|
|
76
|
+
return transition(state, { lifecycle: "active", runtime: "awaiting-follow-up" }, "queue-follow-up");
|
|
77
|
+
}
|
|
78
|
+
if (state.runtime === "awaiting-follow-up") {
|
|
79
|
+
return state;
|
|
80
|
+
}
|
|
81
|
+
throw new Error("cannot queue follow-up when bridge is not processing");
|
|
82
|
+
}
|
|
83
|
+
function advanceBridgeAfterTurn(state) {
|
|
84
|
+
assertNonTerminal(state, "advance");
|
|
85
|
+
if (state.lifecycle !== "active") {
|
|
86
|
+
throw new Error("cannot advance non-active bridge");
|
|
87
|
+
}
|
|
88
|
+
if (state.runtime === "processing") {
|
|
89
|
+
return transition(state, { lifecycle: "active", runtime: "idle" }, "finish-processing");
|
|
90
|
+
}
|
|
91
|
+
if (state.runtime === "awaiting-follow-up") {
|
|
92
|
+
return transition(state, { lifecycle: "active", runtime: "processing" }, "resume-follow-up");
|
|
93
|
+
}
|
|
94
|
+
throw new Error("cannot advance an idle bridge");
|
|
95
|
+
}
|
|
96
|
+
function suspendBridge(state) {
|
|
97
|
+
assertNonTerminal(state, "suspend");
|
|
98
|
+
if ((state.lifecycle !== "forming" && state.lifecycle !== "active") || state.runtime !== "idle") {
|
|
99
|
+
throw new Error("cannot suspend bridge from current state");
|
|
100
|
+
}
|
|
101
|
+
return transition(state, { lifecycle: "suspended", runtime: "idle" }, "suspend");
|
|
102
|
+
}
|
|
103
|
+
function completeBridge(state) {
|
|
104
|
+
assertNonTerminal(state, "complete");
|
|
105
|
+
if (state.runtime !== "idle") {
|
|
106
|
+
throw new Error("cannot complete a bridge mid-turn");
|
|
107
|
+
}
|
|
108
|
+
return transition(state, { lifecycle: "completed", runtime: "idle" }, "complete");
|
|
109
|
+
}
|
|
110
|
+
function cancelBridge(state) {
|
|
111
|
+
assertNonTerminal(state, "cancel");
|
|
112
|
+
if (state.runtime !== "idle") {
|
|
113
|
+
throw new Error("cannot cancel a bridge mid-turn");
|
|
114
|
+
}
|
|
115
|
+
return transition(state, { lifecycle: "cancelled", runtime: "idle" }, "cancel");
|
|
116
|
+
}
|
|
117
|
+
function reconcileBridgeState(state, input) {
|
|
118
|
+
if (state.lifecycle === "completed" || state.lifecycle === "cancelled") {
|
|
119
|
+
return state;
|
|
120
|
+
}
|
|
121
|
+
if (state.runtime !== "idle") {
|
|
122
|
+
return state;
|
|
123
|
+
}
|
|
124
|
+
const hasLiveSignal = input.hasAttachedSessionActivity || input.hasLiveTask || input.currentSessionAttached;
|
|
125
|
+
if (state.lifecycle === "suspended") {
|
|
126
|
+
return hasLiveSignal ? activateBridge(state) : state;
|
|
127
|
+
}
|
|
128
|
+
if (state.lifecycle === "forming") {
|
|
129
|
+
return hasLiveSignal ? activateBridge(state) : suspendBridge(state);
|
|
130
|
+
}
|
|
131
|
+
if (state.lifecycle === "active") {
|
|
132
|
+
return hasLiveSignal ? state : suspendBridge(state);
|
|
133
|
+
}
|
|
134
|
+
return state;
|
|
135
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getBridgeStateRoot = getBridgeStateRoot;
|
|
37
|
+
exports.createBridgeStore = createBridgeStore;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const identity_1 = require("../identity");
|
|
41
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
42
|
+
function sessionIdentityMatches(session, candidate) {
|
|
43
|
+
return (session.friendId === candidate.friendId
|
|
44
|
+
&& session.channel === candidate.channel
|
|
45
|
+
&& session.key === candidate.key);
|
|
46
|
+
}
|
|
47
|
+
function bridgeFilePath(rootDir, id) {
|
|
48
|
+
return path.join(rootDir, `${id}.json`);
|
|
49
|
+
}
|
|
50
|
+
function getBridgeStateRoot() {
|
|
51
|
+
return path.join((0, identity_1.getAgentStateRoot)(), "bridges");
|
|
52
|
+
}
|
|
53
|
+
function createBridgeStore(options = {}) {
|
|
54
|
+
const rootDir = options.rootDir ?? getBridgeStateRoot();
|
|
55
|
+
function ensureRoot() {
|
|
56
|
+
fs.mkdirSync(rootDir, { recursive: true });
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
save(bridge) {
|
|
60
|
+
ensureRoot();
|
|
61
|
+
fs.writeFileSync(bridgeFilePath(rootDir, bridge.id), JSON.stringify(bridge, null, 2), "utf-8");
|
|
62
|
+
(0, runtime_1.emitNervesEvent)({
|
|
63
|
+
component: "engine",
|
|
64
|
+
event: "engine.bridge_store_save",
|
|
65
|
+
message: "saved bridge record",
|
|
66
|
+
meta: {
|
|
67
|
+
bridgeId: bridge.id,
|
|
68
|
+
rootDir,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
return bridge;
|
|
72
|
+
},
|
|
73
|
+
get(id) {
|
|
74
|
+
const filePath = bridgeFilePath(rootDir, id);
|
|
75
|
+
try {
|
|
76
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
77
|
+
return JSON.parse(raw);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
list() {
|
|
84
|
+
ensureRoot();
|
|
85
|
+
const files = fs.readdirSync(rootDir).filter((entry) => entry.endsWith(".json")).sort();
|
|
86
|
+
const bridges = files
|
|
87
|
+
.map((fileName) => {
|
|
88
|
+
try {
|
|
89
|
+
return JSON.parse(fs.readFileSync(path.join(rootDir, fileName), "utf-8"));
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
.filter((bridge) => bridge !== null);
|
|
96
|
+
(0, runtime_1.emitNervesEvent)({
|
|
97
|
+
component: "engine",
|
|
98
|
+
event: "engine.bridge_store_list",
|
|
99
|
+
message: "listed bridge records",
|
|
100
|
+
meta: {
|
|
101
|
+
rootDir,
|
|
102
|
+
count: bridges.length,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
return bridges;
|
|
106
|
+
},
|
|
107
|
+
findBySession(session) {
|
|
108
|
+
const matches = this.list().filter((bridge) => bridge.attachedSessions.some((candidate) => sessionIdentityMatches(session, candidate)));
|
|
109
|
+
(0, runtime_1.emitNervesEvent)({
|
|
110
|
+
component: "engine",
|
|
111
|
+
event: "engine.bridge_store_find_by_session",
|
|
112
|
+
message: "located bridges for canonical session",
|
|
113
|
+
meta: {
|
|
114
|
+
friendId: session.friendId,
|
|
115
|
+
channel: session.channel,
|
|
116
|
+
key: session.key,
|
|
117
|
+
count: matches.length,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
return matches;
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deriveCommitments = deriveCommitments;
|
|
4
|
+
exports.formatCommitments = formatCommitments;
|
|
5
|
+
const obligations_1 = require("./obligations");
|
|
6
|
+
const runtime_1 = require("../nerves/runtime");
|
|
7
|
+
function describeActiveObligation(obligation) {
|
|
8
|
+
if (obligation.status === "pending") {
|
|
9
|
+
return `i owe ${obligation.origin.friendId}: ${obligation.content}`;
|
|
10
|
+
}
|
|
11
|
+
const surface = obligation.currentSurface?.label;
|
|
12
|
+
const statusText = obligation.status.replaceAll("_", " ");
|
|
13
|
+
if (surface) {
|
|
14
|
+
return `i owe ${obligation.origin.friendId}: ${obligation.content} (${statusText} in ${surface})`;
|
|
15
|
+
}
|
|
16
|
+
return `i owe ${obligation.origin.friendId}: ${obligation.content} (${statusText})`;
|
|
17
|
+
}
|
|
18
|
+
function deriveCommitments(activeWorkFrame, innerJob, pendingObligations) {
|
|
19
|
+
const committedTo = [];
|
|
20
|
+
const completionCriteria = [];
|
|
21
|
+
const safeToIgnore = [];
|
|
22
|
+
// Persistent obligations from the obligation store
|
|
23
|
+
if (pendingObligations && pendingObligations.length > 0) {
|
|
24
|
+
let hasAdvancedObligation = false;
|
|
25
|
+
for (const ob of pendingObligations) {
|
|
26
|
+
if (!(0, obligations_1.isOpenObligationStatus)(ob.status))
|
|
27
|
+
continue;
|
|
28
|
+
committedTo.push(describeActiveObligation(ob));
|
|
29
|
+
if (ob.status !== "pending")
|
|
30
|
+
hasAdvancedObligation = true;
|
|
31
|
+
}
|
|
32
|
+
completionCriteria.push("fulfill my outstanding obligations");
|
|
33
|
+
if (hasAdvancedObligation) {
|
|
34
|
+
completionCriteria.push("close my active obligation loops");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Obligation (from current turn -- kept for backward compat)
|
|
38
|
+
if (typeof activeWorkFrame.currentObligation === "string" && activeWorkFrame.currentObligation.trim().length > 0) {
|
|
39
|
+
committedTo.push(`i told them i'd ${activeWorkFrame.currentObligation.trim()}`);
|
|
40
|
+
}
|
|
41
|
+
// Inner job
|
|
42
|
+
if (innerJob.status === "queued" || innerJob.status === "running") {
|
|
43
|
+
const contentSuffix = innerJob.content ? ` -- ${innerJob.content.slice(0, 60)}` : "";
|
|
44
|
+
committedTo.push(`i'm thinking through something privately${contentSuffix}`);
|
|
45
|
+
}
|
|
46
|
+
else if (innerJob.status === "surfaced") {
|
|
47
|
+
committedTo.push("i finished thinking about something and need to bring it back");
|
|
48
|
+
}
|
|
49
|
+
// mustResolveBeforeHandoff
|
|
50
|
+
if (activeWorkFrame.mustResolveBeforeHandoff) {
|
|
51
|
+
committedTo.push("i need to finish what i started before moving on");
|
|
52
|
+
completionCriteria.push("resolve the current thread before moving on");
|
|
53
|
+
}
|
|
54
|
+
// Bridges
|
|
55
|
+
for (const bridge of activeWorkFrame.bridges) {
|
|
56
|
+
committedTo.push(`i have shared work: ${bridge.summary || bridge.objective}`);
|
|
57
|
+
}
|
|
58
|
+
if (activeWorkFrame.bridges.length > 0) {
|
|
59
|
+
completionCriteria.push("keep shared work aligned across sessions");
|
|
60
|
+
}
|
|
61
|
+
// Tasks
|
|
62
|
+
for (const taskName of activeWorkFrame.taskPressure?.liveTaskNames ?? []) {
|
|
63
|
+
committedTo.push(`i'm tracking: ${taskName}`);
|
|
64
|
+
}
|
|
65
|
+
// Obligation completion criteria
|
|
66
|
+
if (innerJob.obligationStatus === "pending") {
|
|
67
|
+
const name = innerJob.origin?.friendName ?? innerJob.origin?.friendId ?? "them";
|
|
68
|
+
completionCriteria.push(`bring my answer back to ${name}`);
|
|
69
|
+
}
|
|
70
|
+
// Default completion criteria
|
|
71
|
+
if (completionCriteria.length === 0) {
|
|
72
|
+
completionCriteria.push("just be present in this conversation");
|
|
73
|
+
}
|
|
74
|
+
// Safe to ignore
|
|
75
|
+
if (innerJob.status === "idle" && !(activeWorkFrame.inner?.hasPending)) {
|
|
76
|
+
safeToIgnore.push("no private thinking in progress");
|
|
77
|
+
}
|
|
78
|
+
if (activeWorkFrame.bridges.length === 0) {
|
|
79
|
+
safeToIgnore.push("no shared work to coordinate");
|
|
80
|
+
}
|
|
81
|
+
if ((activeWorkFrame.taskPressure?.liveTaskNames ?? []).length === 0) {
|
|
82
|
+
safeToIgnore.push("no active tasks to track");
|
|
83
|
+
}
|
|
84
|
+
(0, runtime_1.emitNervesEvent)({
|
|
85
|
+
component: "engine",
|
|
86
|
+
event: "engine.commitments_derive",
|
|
87
|
+
message: "derived commitments frame",
|
|
88
|
+
meta: { committedCount: committedTo.length, criteriaCount: completionCriteria.length },
|
|
89
|
+
});
|
|
90
|
+
return { committedTo, completionCriteria, safeToIgnore };
|
|
91
|
+
}
|
|
92
|
+
function formatCommitments(commitments) {
|
|
93
|
+
const sections = [];
|
|
94
|
+
if (commitments.committedTo.length === 0) {
|
|
95
|
+
sections.push("i'm not holding anything specific right now. i'm free to be present.");
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
sections.push("## what i'm holding right now");
|
|
99
|
+
sections.push("");
|
|
100
|
+
sections.push(commitments.committedTo.map((c) => `- ${c}`).join("\n"));
|
|
101
|
+
}
|
|
102
|
+
sections.push("");
|
|
103
|
+
sections.push("## what \"done\" looks like");
|
|
104
|
+
sections.push(commitments.completionCriteria.map((c) => `- ${c}`).join("\n"));
|
|
105
|
+
sections.push("");
|
|
106
|
+
sections.push("## what i can let go of");
|
|
107
|
+
sections.push(commitments.safeToIgnore.map((c) => `- ${c}`).join("\n"));
|
|
108
|
+
return sections.join("\n");
|
|
109
|
+
}
|
package/dist/heart/config.js
CHANGED
|
@@ -35,23 +35,30 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.loadConfig = loadConfig;
|
|
37
37
|
exports.resetConfigCache = resetConfigCache;
|
|
38
|
-
exports.
|
|
38
|
+
exports.patchRuntimeConfig = patchRuntimeConfig;
|
|
39
39
|
exports.getAzureConfig = getAzureConfig;
|
|
40
40
|
exports.getMinimaxConfig = getMinimaxConfig;
|
|
41
41
|
exports.getAnthropicConfig = getAnthropicConfig;
|
|
42
42
|
exports.getOpenAICodexConfig = getOpenAICodexConfig;
|
|
43
|
+
exports.getGithubCopilotConfig = getGithubCopilotConfig;
|
|
43
44
|
exports.getTeamsConfig = getTeamsConfig;
|
|
45
|
+
exports.getTeamsSecondaryConfig = getTeamsSecondaryConfig;
|
|
44
46
|
exports.getContextConfig = getContextConfig;
|
|
45
47
|
exports.getOAuthConfig = getOAuthConfig;
|
|
48
|
+
exports.resolveOAuthForTenant = resolveOAuthForTenant;
|
|
46
49
|
exports.getTeamsChannelConfig = getTeamsChannelConfig;
|
|
50
|
+
exports.getBlueBubblesConfig = getBlueBubblesConfig;
|
|
51
|
+
exports.getBlueBubblesChannelConfig = getBlueBubblesChannelConfig;
|
|
47
52
|
exports.getIntegrationsConfig = getIntegrationsConfig;
|
|
48
53
|
exports.getOpenAIEmbeddingsApiKey = getOpenAIEmbeddingsApiKey;
|
|
49
54
|
exports.getLogsDir = getLogsDir;
|
|
55
|
+
exports.sanitizeKey = sanitizeKey;
|
|
56
|
+
exports.slugify = slugify;
|
|
57
|
+
exports.resolveSessionPath = resolveSessionPath;
|
|
50
58
|
exports.sessionPath = sessionPath;
|
|
51
59
|
exports.logPath = logPath;
|
|
52
60
|
const fs = __importStar(require("fs"));
|
|
53
61
|
const path = __importStar(require("path"));
|
|
54
|
-
const os = __importStar(require("os"));
|
|
55
62
|
const identity_1 = require("./identity");
|
|
56
63
|
const runtime_1 = require("../nerves/runtime");
|
|
57
64
|
const DEFAULT_SECRETS_TEMPLATE = {
|
|
@@ -64,6 +71,7 @@ const DEFAULT_SECRETS_TEMPLATE = {
|
|
|
64
71
|
endpoint: "",
|
|
65
72
|
deployment: "",
|
|
66
73
|
apiVersion: "2025-04-01-preview",
|
|
74
|
+
managedIdentityClientId: "",
|
|
67
75
|
},
|
|
68
76
|
minimax: {
|
|
69
77
|
model: "",
|
|
@@ -74,24 +82,46 @@ const DEFAULT_SECRETS_TEMPLATE = {
|
|
|
74
82
|
setupToken: "",
|
|
75
83
|
},
|
|
76
84
|
"openai-codex": {
|
|
77
|
-
model: "gpt-5.
|
|
85
|
+
model: "gpt-5.4",
|
|
78
86
|
oauthAccessToken: "",
|
|
79
87
|
},
|
|
88
|
+
"github-copilot": {
|
|
89
|
+
model: "claude-sonnet-4.6",
|
|
90
|
+
githubToken: "",
|
|
91
|
+
baseUrl: "",
|
|
92
|
+
},
|
|
80
93
|
},
|
|
81
94
|
teams: {
|
|
82
95
|
clientId: "",
|
|
83
96
|
clientSecret: "",
|
|
84
97
|
tenantId: "",
|
|
98
|
+
managedIdentityClientId: "",
|
|
85
99
|
},
|
|
86
100
|
oauth: {
|
|
87
101
|
graphConnectionName: "graph",
|
|
88
102
|
adoConnectionName: "ado",
|
|
89
103
|
githubConnectionName: "",
|
|
90
104
|
},
|
|
105
|
+
teamsSecondary: {
|
|
106
|
+
clientId: "",
|
|
107
|
+
clientSecret: "",
|
|
108
|
+
tenantId: "",
|
|
109
|
+
managedIdentityClientId: "",
|
|
110
|
+
},
|
|
91
111
|
teamsChannel: {
|
|
92
112
|
skipConfirmation: true,
|
|
93
113
|
port: 3978,
|
|
94
114
|
},
|
|
115
|
+
bluebubbles: {
|
|
116
|
+
serverUrl: "",
|
|
117
|
+
password: "",
|
|
118
|
+
accountId: "default",
|
|
119
|
+
},
|
|
120
|
+
bluebubblesChannel: {
|
|
121
|
+
port: 18790,
|
|
122
|
+
webhookPath: "/bluebubbles-webhook",
|
|
123
|
+
requestTimeoutMs: 30000,
|
|
124
|
+
},
|
|
95
125
|
integrations: {
|
|
96
126
|
perplexityApiKey: "",
|
|
97
127
|
openaiEmbeddingsApiKey: "",
|
|
@@ -104,15 +134,19 @@ function defaultRuntimeConfig() {
|
|
|
104
134
|
minimax: { ...DEFAULT_SECRETS_TEMPLATE.providers.minimax },
|
|
105
135
|
anthropic: { ...DEFAULT_SECRETS_TEMPLATE.providers.anthropic },
|
|
106
136
|
"openai-codex": { ...DEFAULT_SECRETS_TEMPLATE.providers["openai-codex"] },
|
|
137
|
+
"github-copilot": { ...DEFAULT_SECRETS_TEMPLATE.providers["github-copilot"] },
|
|
107
138
|
},
|
|
108
139
|
teams: { ...DEFAULT_SECRETS_TEMPLATE.teams },
|
|
140
|
+
teamsSecondary: { ...DEFAULT_SECRETS_TEMPLATE.teamsSecondary },
|
|
109
141
|
oauth: { ...DEFAULT_SECRETS_TEMPLATE.oauth },
|
|
110
142
|
context: { ...identity_1.DEFAULT_AGENT_CONTEXT },
|
|
111
143
|
teamsChannel: { ...DEFAULT_SECRETS_TEMPLATE.teamsChannel },
|
|
144
|
+
bluebubbles: { ...DEFAULT_SECRETS_TEMPLATE.bluebubbles },
|
|
145
|
+
bluebubblesChannel: { ...DEFAULT_SECRETS_TEMPLATE.bluebubblesChannel },
|
|
112
146
|
integrations: { ...DEFAULT_SECRETS_TEMPLATE.integrations },
|
|
113
147
|
};
|
|
114
148
|
}
|
|
115
|
-
let
|
|
149
|
+
let _runtimeConfigOverride = null;
|
|
116
150
|
let _testContextOverride = null;
|
|
117
151
|
function resolveConfigPath() {
|
|
118
152
|
return (0, identity_1.getAgentSecretsPath)();
|
|
@@ -134,15 +168,6 @@ function deepMerge(defaults, partial) {
|
|
|
134
168
|
return result;
|
|
135
169
|
}
|
|
136
170
|
function loadConfig() {
|
|
137
|
-
if (_cachedConfig) {
|
|
138
|
-
(0, runtime_1.emitNervesEvent)({
|
|
139
|
-
event: "config.load",
|
|
140
|
-
component: "config/identity",
|
|
141
|
-
message: "config loaded from cache",
|
|
142
|
-
meta: { source: "cache" },
|
|
143
|
-
});
|
|
144
|
-
return _cachedConfig;
|
|
145
|
-
}
|
|
146
171
|
const configPath = resolveConfigPath();
|
|
147
172
|
// Auto-create config directory if it doesn't exist
|
|
148
173
|
const configDir = path.dirname(configPath);
|
|
@@ -203,7 +228,10 @@ function loadConfig() {
|
|
|
203
228
|
},
|
|
204
229
|
});
|
|
205
230
|
}
|
|
206
|
-
|
|
231
|
+
const mergedConfig = deepMerge(defaultRuntimeConfig(), sanitizedFileData);
|
|
232
|
+
const config = _runtimeConfigOverride
|
|
233
|
+
? deepMerge(mergedConfig, _runtimeConfigOverride)
|
|
234
|
+
: mergedConfig;
|
|
207
235
|
(0, runtime_1.emitNervesEvent)({
|
|
208
236
|
event: "config.load",
|
|
209
237
|
component: "config/identity",
|
|
@@ -211,22 +239,22 @@ function loadConfig() {
|
|
|
211
239
|
meta: {
|
|
212
240
|
source: "disk",
|
|
213
241
|
used_defaults_only: Object.keys(fileData).length === 0,
|
|
242
|
+
override_applied: _runtimeConfigOverride !== null,
|
|
214
243
|
},
|
|
215
244
|
});
|
|
216
|
-
return
|
|
245
|
+
return config;
|
|
217
246
|
}
|
|
218
247
|
function resetConfigCache() {
|
|
219
|
-
|
|
248
|
+
_runtimeConfigOverride = null;
|
|
220
249
|
_testContextOverride = null;
|
|
221
250
|
}
|
|
222
|
-
function
|
|
223
|
-
loadConfig(); // ensure _cachedConfig exists
|
|
251
|
+
function patchRuntimeConfig(partial) {
|
|
224
252
|
const contextPatch = partial.context;
|
|
225
253
|
if (contextPatch) {
|
|
226
254
|
const base = _testContextOverride ?? identity_1.DEFAULT_AGENT_CONTEXT;
|
|
227
255
|
_testContextOverride = deepMerge(base, contextPatch);
|
|
228
256
|
}
|
|
229
|
-
|
|
257
|
+
_runtimeConfigOverride = deepMerge((_runtimeConfigOverride ?? {}), partial);
|
|
230
258
|
}
|
|
231
259
|
function getAzureConfig() {
|
|
232
260
|
const config = loadConfig();
|
|
@@ -244,10 +272,18 @@ function getOpenAICodexConfig() {
|
|
|
244
272
|
const config = loadConfig();
|
|
245
273
|
return { ...config.providers["openai-codex"] };
|
|
246
274
|
}
|
|
275
|
+
function getGithubCopilotConfig() {
|
|
276
|
+
const config = loadConfig();
|
|
277
|
+
return { ...config.providers["github-copilot"] };
|
|
278
|
+
}
|
|
247
279
|
function getTeamsConfig() {
|
|
248
280
|
const config = loadConfig();
|
|
249
281
|
return { ...config.teams };
|
|
250
282
|
}
|
|
283
|
+
function getTeamsSecondaryConfig() {
|
|
284
|
+
const config = loadConfig();
|
|
285
|
+
return { ...config.teamsSecondary };
|
|
286
|
+
}
|
|
251
287
|
function getContextConfig() {
|
|
252
288
|
if (_testContextOverride) {
|
|
253
289
|
return { ..._testContextOverride };
|
|
@@ -268,11 +304,41 @@ function getOAuthConfig() {
|
|
|
268
304
|
const config = loadConfig();
|
|
269
305
|
return { ...config.oauth };
|
|
270
306
|
}
|
|
307
|
+
/** Resolve OAuth connection names for a specific tenant, falling back to defaults. */
|
|
308
|
+
function resolveOAuthForTenant(tenantId) {
|
|
309
|
+
const base = getOAuthConfig();
|
|
310
|
+
const overrides = tenantId ? base.tenantOverrides?.[tenantId] : undefined;
|
|
311
|
+
return {
|
|
312
|
+
graphConnectionName: overrides?.graphConnectionName ?? base.graphConnectionName,
|
|
313
|
+
adoConnectionName: overrides?.adoConnectionName ?? base.adoConnectionName,
|
|
314
|
+
githubConnectionName: overrides?.githubConnectionName ?? base.githubConnectionName,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
271
317
|
function getTeamsChannelConfig() {
|
|
272
318
|
const config = loadConfig();
|
|
273
319
|
const { skipConfirmation, flushIntervalMs, port } = config.teamsChannel;
|
|
274
320
|
return { skipConfirmation, flushIntervalMs, port };
|
|
275
321
|
}
|
|
322
|
+
function getBlueBubblesConfig() {
|
|
323
|
+
const config = loadConfig();
|
|
324
|
+
const { serverUrl, password, accountId } = config.bluebubbles;
|
|
325
|
+
if (!serverUrl.trim()) {
|
|
326
|
+
throw new Error("bluebubbles.serverUrl is required in secrets.json to run the BlueBubbles sense.");
|
|
327
|
+
}
|
|
328
|
+
if (!password.trim()) {
|
|
329
|
+
throw new Error("bluebubbles.password is required in secrets.json to run the BlueBubbles sense.");
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
serverUrl: serverUrl.trim(),
|
|
333
|
+
password: password.trim(),
|
|
334
|
+
accountId: accountId.trim() || "default",
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function getBlueBubblesChannelConfig() {
|
|
338
|
+
const config = loadConfig();
|
|
339
|
+
const { port, webhookPath, requestTimeoutMs } = config.bluebubblesChannel;
|
|
340
|
+
return { port, webhookPath, requestTimeoutMs };
|
|
341
|
+
}
|
|
276
342
|
function getIntegrationsConfig() {
|
|
277
343
|
const config = loadConfig();
|
|
278
344
|
return { ...config.integrations };
|
|
@@ -281,16 +347,29 @@ function getOpenAIEmbeddingsApiKey() {
|
|
|
281
347
|
return getIntegrationsConfig().openaiEmbeddingsApiKey;
|
|
282
348
|
}
|
|
283
349
|
function getLogsDir() {
|
|
284
|
-
return path.join(
|
|
350
|
+
return path.join((0, identity_1.getAgentRoot)(), "state", "logs");
|
|
285
351
|
}
|
|
286
352
|
function sanitizeKey(key) {
|
|
287
353
|
return key.replace(/[/:]/g, "_");
|
|
288
354
|
}
|
|
289
|
-
function
|
|
290
|
-
|
|
291
|
-
|
|
355
|
+
function slugify(value) {
|
|
356
|
+
return value
|
|
357
|
+
.trim()
|
|
358
|
+
.toLowerCase()
|
|
359
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
360
|
+
.replace(/^-+/, "")
|
|
361
|
+
.replace(/-+$/, "");
|
|
362
|
+
}
|
|
363
|
+
function resolveSessionPath(friendId, channel, key, options) {
|
|
364
|
+
const dir = path.join((0, identity_1.getAgentRoot)(), "state", "sessions", friendId, channel);
|
|
365
|
+
if (options?.ensureDir) {
|
|
366
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
367
|
+
}
|
|
292
368
|
return path.join(dir, sanitizeKey(key) + ".json");
|
|
293
369
|
}
|
|
370
|
+
function sessionPath(friendId, channel, key) {
|
|
371
|
+
return resolveSessionPath(friendId, channel, key, { ensureDir: true });
|
|
372
|
+
}
|
|
294
373
|
function logPath(channel, key) {
|
|
295
374
|
return path.join(getLogsDir(), channel, sanitizeKey(key) + ".ndjson");
|
|
296
375
|
}
|