@ouro.bot/cli 0.1.0-alpha.19 → 0.1.0-alpha.191
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +187 -191
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +1 -1
- package/changelog.json +1200 -0
- package/dist/heart/active-work.js +832 -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/cares.js +174 -0
- package/dist/heart/commitments.js +111 -0
- package/dist/heart/config.js +40 -30
- package/dist/heart/core.js +620 -124
- package/dist/heart/cross-chat-delivery.js +146 -0
- package/dist/heart/daemon/agent-discovery.js +81 -0
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/auth-flow.js +477 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/daemon-cli.js +2470 -210
- package/dist/heart/daemon/daemon-entry.js +157 -4
- package/dist/heart/daemon/daemon-health.js +136 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
- package/dist/heart/daemon/daemon-tombstone.js +123 -0
- package/dist/heart/daemon/daemon.js +449 -17
- package/dist/heart/daemon/habit-migration.js +181 -0
- package/dist/heart/daemon/habit-parser.js +119 -0
- package/dist/heart/daemon/habit-scheduler.js +369 -0
- package/dist/heart/daemon/hatch-flow.js +31 -112
- package/dist/heart/daemon/hatch-specialist.js +3 -3
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/launchd.js +165 -0
- package/dist/heart/daemon/log-tailer.js +4 -3
- package/dist/heart/daemon/mcp-server.js +591 -0
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/os-cron-deps.js +134 -0
- package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
- package/dist/heart/daemon/ouro-path-installer.js +78 -34
- package/dist/heart/daemon/ouro-version-manager.js +171 -0
- package/dist/heart/daemon/outlook-http.js +399 -0
- package/dist/heart/daemon/outlook-read.js +1331 -0
- package/dist/heart/daemon/outlook-render.js +1032 -0
- package/dist/heart/daemon/outlook-types.js +7 -0
- package/dist/heart/daemon/outlook-view.js +194 -0
- package/dist/heart/daemon/process-manager.js +75 -0
- package/dist/heart/daemon/run-hooks.js +39 -0
- package/dist/heart/daemon/runtime-logging.js +58 -15
- package/dist/heart/daemon/runtime-metadata.js +219 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +50 -2
- package/dist/heart/daemon/session-id-resolver.js +131 -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-prompt.js +10 -7
- package/dist/heart/daemon/specialist-tools.js +49 -3
- package/dist/heart/daemon/staged-restart.js +114 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- 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 +136 -55
- package/dist/heart/intentions.js +168 -0
- package/dist/heart/kicks.js +2 -20
- package/dist/heart/migrate-config.js +127 -0
- package/dist/heart/model-capabilities.js +48 -0
- package/dist/heart/obligations.js +220 -0
- package/dist/heart/presence.js +185 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-failover.js +88 -0
- package/dist/heart/provider-ping.js +172 -0
- package/dist/heart/providers/anthropic-token.js +163 -0
- package/dist/heart/providers/anthropic.js +187 -41
- package/dist/heart/providers/azure.js +116 -10
- package/dist/heart/providers/github-copilot.js +157 -0
- package/dist/heart/providers/minimax.js +33 -3
- package/dist/heart/providers/openai-codex.js +51 -16
- package/dist/heart/session-activity.js +172 -0
- package/dist/heart/session-recall.js +216 -0
- package/dist/heart/streaming.js +36 -27
- package/dist/heart/target-resolution.js +127 -0
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/tool-activity-callbacks.js +36 -0
- package/dist/heart/tool-description.js +135 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/heart/wake-packet.js +226 -0
- package/dist/mind/associative-recall.js +122 -32
- package/dist/mind/bundle-manifest.js +14 -0
- package/dist/mind/context.js +125 -14
- package/dist/mind/{memory.js → diary.js} +70 -56
- package/dist/mind/episodes.js +116 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/first-impressions.js +16 -2
- package/dist/mind/friends/channel.js +56 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/store-file.js +36 -0
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +8 -0
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/obligations.js +134 -0
- package/dist/mind/pending.js +76 -9
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt.js +779 -102
- package/dist/mind/scrutiny.js +173 -0
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/coverage/audit.js +1 -1
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +63 -3
- package/dist/nerves/observation.js +20 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/outlook-ui/assets/index-DN6vVYPm.css +1 -0
- package/dist/outlook-ui/assets/index-M1l1tDTX.js +61 -0
- package/dist/outlook-ui/index.html +15 -0
- package/dist/repertoire/ado-semantic.js +11 -5
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +330 -0
- package/dist/repertoire/coding/feedback.js +197 -30
- package/dist/repertoire/coding/manager.js +149 -2
- package/dist/repertoire/coding/spawner.js +55 -9
- package/dist/repertoire/coding/tools.js +170 -7
- package/dist/repertoire/guardrails.js +291 -0
- package/dist/repertoire/mcp-client.js +254 -0
- package/dist/repertoire/mcp-manager.js +251 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +14 -23
- package/dist/repertoire/tasks/board.js +43 -5
- package/dist/repertoire/tasks/fix.js +182 -0
- package/dist/repertoire/tasks/index.js +28 -10
- package/dist/repertoire/tasks/lifecycle.js +2 -2
- package/dist/repertoire/tasks/parser.js +3 -2
- package/dist/repertoire/tasks/scanner.js +194 -37
- package/dist/repertoire/tasks/transitions.js +11 -28
- package/dist/repertoire/tools-base.js +1345 -320
- package/dist/repertoire/tools-bluebubbles.js +94 -0
- package/dist/repertoire/tools-github.js +1 -6
- package/dist/repertoire/tools-teams.js +12 -59
- package/dist/repertoire/tools.js +318 -124
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/attention-queue.js +97 -0
- package/dist/senses/bluebubbles-client.js +102 -3
- package/dist/senses/bluebubbles-entry.js +2 -0
- package/dist/senses/bluebubbles-inbound-log.js +109 -0
- package/dist/senses/bluebubbles-media.js +118 -23
- package/dist/senses/bluebubbles-model.js +12 -4
- package/dist/senses/bluebubbles-mutation-log.js +45 -5
- package/dist/senses/bluebubbles-runtime-state.js +109 -0
- package/dist/senses/bluebubbles-session-cleanup.js +72 -0
- package/dist/senses/bluebubbles.js +1008 -102
- package/dist/senses/cli/adapter.js +274 -0
- package/dist/senses/cli/ink-app.js +156 -0
- package/dist/senses/cli/inline-diff.js +64 -0
- package/dist/senses/cli/message-list.js +51 -0
- package/dist/senses/cli/ouro-tui.js +349 -0
- package/dist/senses/cli/spinner-imperative.js +135 -0
- package/dist/senses/cli/spinner.js +101 -0
- package/dist/senses/cli/status-line.js +60 -0
- package/dist/senses/cli/streaming-markdown.js +526 -0
- package/dist/senses/cli/tool-display.js +83 -0
- package/dist/senses/cli/tool-render.js +85 -0
- package/dist/senses/cli/tui-store.js +198 -0
- package/dist/senses/cli/virtual-list.js +35 -0
- package/dist/senses/cli-entry.js +1 -1
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +508 -243
- package/dist/senses/commands.js +65 -1
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/habit-turn-message.js +108 -0
- package/dist/senses/inner-dialog-worker.js +112 -19
- package/dist/senses/inner-dialog.js +592 -84
- package/dist/senses/pipeline.js +633 -0
- package/dist/senses/shared-turn.js +196 -0
- package/dist/senses/surface-tool.js +95 -0
- package/dist/senses/teams.js +667 -120
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +16 -6
- package/skills/configure-dev-tools.md +81 -0
- package/subagents/README.md +4 -70
- package/dist/heart/daemon/subagent-installer.js +0 -134
- package/dist/senses/debug-activity.js +0 -107
- package/subagents/work-doer.md +0 -235
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -382
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.upsertGroupContextParticipants = upsertGroupContextParticipants;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
6
|
+
const CURRENT_SCHEMA_VERSION = 1;
|
|
7
|
+
function normalizeDisplayName(externalId, displayName) {
|
|
8
|
+
const trimmed = displayName?.trim();
|
|
9
|
+
return trimmed && trimmed.length > 0 ? trimmed : externalId;
|
|
10
|
+
}
|
|
11
|
+
function buildNameNotes(name, now) {
|
|
12
|
+
return name !== "Unknown"
|
|
13
|
+
? { name: { value: name, savedAt: now } }
|
|
14
|
+
: {};
|
|
15
|
+
}
|
|
16
|
+
function dedupeParticipants(participants) {
|
|
17
|
+
const deduped = new Map();
|
|
18
|
+
for (const participant of participants) {
|
|
19
|
+
const externalId = participant.externalId.trim();
|
|
20
|
+
if (!externalId)
|
|
21
|
+
continue;
|
|
22
|
+
const key = `${participant.provider}:${externalId}`;
|
|
23
|
+
if (!deduped.has(key)) {
|
|
24
|
+
deduped.set(key, {
|
|
25
|
+
...participant,
|
|
26
|
+
externalId,
|
|
27
|
+
displayName: participant.displayName?.trim() || undefined,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return Array.from(deduped.values());
|
|
32
|
+
}
|
|
33
|
+
function createGroupExternalId(provider, groupExternalId, linkedAt) {
|
|
34
|
+
return {
|
|
35
|
+
provider,
|
|
36
|
+
externalId: groupExternalId,
|
|
37
|
+
linkedAt,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function shouldPromoteToAcquaintance(friend) {
|
|
41
|
+
return (friend.trustLevel ?? "stranger") === "stranger";
|
|
42
|
+
}
|
|
43
|
+
function createAcquaintanceRecord(participant, groupExternalId, linkedAt) {
|
|
44
|
+
const name = normalizeDisplayName(participant.externalId, participant.displayName);
|
|
45
|
+
return {
|
|
46
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
47
|
+
name,
|
|
48
|
+
role: "acquaintance",
|
|
49
|
+
trustLevel: "acquaintance",
|
|
50
|
+
connections: [],
|
|
51
|
+
externalIds: [
|
|
52
|
+
{
|
|
53
|
+
provider: participant.provider,
|
|
54
|
+
externalId: participant.externalId,
|
|
55
|
+
linkedAt,
|
|
56
|
+
},
|
|
57
|
+
createGroupExternalId(participant.provider, groupExternalId, linkedAt),
|
|
58
|
+
],
|
|
59
|
+
tenantMemberships: [],
|
|
60
|
+
toolPreferences: {},
|
|
61
|
+
notes: buildNameNotes(name, linkedAt),
|
|
62
|
+
totalTokens: 0,
|
|
63
|
+
createdAt: linkedAt,
|
|
64
|
+
updatedAt: linkedAt,
|
|
65
|
+
schemaVersion: CURRENT_SCHEMA_VERSION,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
async function upsertGroupContextParticipants(input) {
|
|
69
|
+
(0, runtime_1.emitNervesEvent)({
|
|
70
|
+
component: "friends",
|
|
71
|
+
event: "friends.group_context_upsert_start",
|
|
72
|
+
message: "upserting shared-group participant context",
|
|
73
|
+
meta: {
|
|
74
|
+
participantCount: input.participants.length,
|
|
75
|
+
hasGroupExternalId: input.groupExternalId.trim().length > 0,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
const groupExternalId = input.groupExternalId.trim();
|
|
79
|
+
if (!groupExternalId) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
const now = input.now ?? (() => new Date().toISOString());
|
|
83
|
+
const participants = dedupeParticipants(input.participants);
|
|
84
|
+
const results = [];
|
|
85
|
+
for (const participant of participants) {
|
|
86
|
+
const linkedAt = now();
|
|
87
|
+
const existing = await input.store.findByExternalId(participant.provider, participant.externalId);
|
|
88
|
+
if (!existing) {
|
|
89
|
+
const created = createAcquaintanceRecord(participant, groupExternalId, linkedAt);
|
|
90
|
+
await input.store.put(created.id, created);
|
|
91
|
+
results.push({
|
|
92
|
+
friendId: created.id,
|
|
93
|
+
name: created.name,
|
|
94
|
+
trustLevel: "acquaintance",
|
|
95
|
+
created: true,
|
|
96
|
+
updated: false,
|
|
97
|
+
addedGroupExternalId: true,
|
|
98
|
+
});
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const hasGroupExternalId = existing.externalIds.some((externalId) => externalId.externalId === groupExternalId);
|
|
102
|
+
const promoteToAcquaintance = shouldPromoteToAcquaintance(existing);
|
|
103
|
+
const trustLevel = promoteToAcquaintance
|
|
104
|
+
? "acquaintance"
|
|
105
|
+
: existing.trustLevel;
|
|
106
|
+
const role = promoteToAcquaintance
|
|
107
|
+
? "acquaintance"
|
|
108
|
+
: existing.role;
|
|
109
|
+
const updatedExternalIds = hasGroupExternalId
|
|
110
|
+
? existing.externalIds
|
|
111
|
+
: [...existing.externalIds, createGroupExternalId(participant.provider, groupExternalId, linkedAt)];
|
|
112
|
+
const updated = promoteToAcquaintance || !hasGroupExternalId;
|
|
113
|
+
const record = updated
|
|
114
|
+
? {
|
|
115
|
+
...existing,
|
|
116
|
+
role,
|
|
117
|
+
trustLevel,
|
|
118
|
+
externalIds: updatedExternalIds,
|
|
119
|
+
updatedAt: linkedAt,
|
|
120
|
+
}
|
|
121
|
+
: existing;
|
|
122
|
+
if (updated) {
|
|
123
|
+
await input.store.put(record.id, record);
|
|
124
|
+
}
|
|
125
|
+
results.push({
|
|
126
|
+
friendId: record.id,
|
|
127
|
+
name: record.name,
|
|
128
|
+
trustLevel,
|
|
129
|
+
created: false,
|
|
130
|
+
updated,
|
|
131
|
+
addedGroupExternalId: !hasGroupExternalId,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
(0, runtime_1.emitNervesEvent)({
|
|
135
|
+
component: "friends",
|
|
136
|
+
event: "friends.group_context_upsert_end",
|
|
137
|
+
message: "upserted shared-group participant context",
|
|
138
|
+
meta: {
|
|
139
|
+
participantCount: participants.length,
|
|
140
|
+
updatedCount: results.filter((result) => result.created || result.updated).length,
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
return results;
|
|
144
|
+
}
|
|
@@ -100,6 +100,25 @@ class FileFriendStore {
|
|
|
100
100
|
}
|
|
101
101
|
return entries.some((entry) => entry.endsWith(".json"));
|
|
102
102
|
}
|
|
103
|
+
async listAll() {
|
|
104
|
+
let entries;
|
|
105
|
+
try {
|
|
106
|
+
entries = await fsPromises.readdir(this.friendsPath);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
const records = [];
|
|
112
|
+
for (const entry of entries) {
|
|
113
|
+
if (!entry.endsWith(".json"))
|
|
114
|
+
continue;
|
|
115
|
+
const raw = await this.readJson(path.join(this.friendsPath, entry));
|
|
116
|
+
if (!raw)
|
|
117
|
+
continue;
|
|
118
|
+
records.push(this.normalize(raw));
|
|
119
|
+
}
|
|
120
|
+
return records;
|
|
121
|
+
}
|
|
103
122
|
normalize(raw) {
|
|
104
123
|
const trustLevel = raw.trustLevel;
|
|
105
124
|
const normalizedTrustLevel = trustLevel === "family" ||
|
|
@@ -108,6 +127,8 @@ class FileFriendStore {
|
|
|
108
127
|
trustLevel === "stranger"
|
|
109
128
|
? trustLevel
|
|
110
129
|
: DEFAULT_TRUST_LEVEL;
|
|
130
|
+
const kind = raw.kind === "human" || raw.kind === "agent" ? raw.kind : "human";
|
|
131
|
+
const agentMeta = kind === "agent" ? this.normalizeAgentMeta(raw.agentMeta) : undefined;
|
|
111
132
|
return {
|
|
112
133
|
id: raw.id,
|
|
113
134
|
name: raw.name,
|
|
@@ -134,6 +155,21 @@ class FileFriendStore {
|
|
|
134
155
|
createdAt: typeof raw.createdAt === "string" ? raw.createdAt : new Date().toISOString(),
|
|
135
156
|
updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : new Date().toISOString(),
|
|
136
157
|
schemaVersion: typeof raw.schemaVersion === "number" ? raw.schemaVersion : 1,
|
|
158
|
+
kind,
|
|
159
|
+
agentMeta,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
normalizeAgentMeta(raw) {
|
|
163
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
164
|
+
return undefined;
|
|
165
|
+
const meta = raw;
|
|
166
|
+
if (typeof meta.bundleName !== "string")
|
|
167
|
+
return undefined;
|
|
168
|
+
return {
|
|
169
|
+
bundleName: meta.bundleName,
|
|
170
|
+
familiarity: typeof meta.familiarity === "number" ? meta.familiarity : 0,
|
|
171
|
+
sharedMissions: Array.isArray(meta.sharedMissions) ? meta.sharedMissions : [],
|
|
172
|
+
outcomes: Array.isArray(meta.outcomes) ? meta.outcomes : [],
|
|
137
173
|
};
|
|
138
174
|
}
|
|
139
175
|
async readJson(filePath) {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.describeTrustContext = describeTrustContext;
|
|
4
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
5
|
+
function findRelatedGroupId(friend) {
|
|
6
|
+
return friend.externalIds.find((externalId) => externalId.externalId.startsWith("group:"))?.externalId;
|
|
7
|
+
}
|
|
8
|
+
function resolveLevel(friend) {
|
|
9
|
+
return friend.trustLevel ?? "stranger";
|
|
10
|
+
}
|
|
11
|
+
function describeTrustContext(input) {
|
|
12
|
+
const level = resolveLevel(input.friend);
|
|
13
|
+
const relatedGroupId = findRelatedGroupId(input.friend);
|
|
14
|
+
const explanation = level === "family" || level === "friend"
|
|
15
|
+
? {
|
|
16
|
+
level,
|
|
17
|
+
basis: "direct",
|
|
18
|
+
summary: level === "family"
|
|
19
|
+
? "direct family trust"
|
|
20
|
+
: "direct trusted relationship",
|
|
21
|
+
why: "this relationship is directly trusted rather than inferred through a shared group or cold first contact.",
|
|
22
|
+
permits: [
|
|
23
|
+
"local operations when appropriate",
|
|
24
|
+
"proactive follow-through",
|
|
25
|
+
"full collaborative problem solving",
|
|
26
|
+
],
|
|
27
|
+
constraints: [],
|
|
28
|
+
}
|
|
29
|
+
: level === "acquaintance"
|
|
30
|
+
? {
|
|
31
|
+
level,
|
|
32
|
+
basis: "shared_group",
|
|
33
|
+
summary: relatedGroupId
|
|
34
|
+
? "known through the shared project group"
|
|
35
|
+
: "known through a shared group context",
|
|
36
|
+
why: relatedGroupId
|
|
37
|
+
? `this trust comes from the shared group context ${relatedGroupId}, not from direct endorsement.`
|
|
38
|
+
: "this trust comes from shared group context rather than direct endorsement.",
|
|
39
|
+
permits: [
|
|
40
|
+
"group-safe coordination",
|
|
41
|
+
"normal conversation inside the shared context",
|
|
42
|
+
],
|
|
43
|
+
constraints: [
|
|
44
|
+
"guarded local actions",
|
|
45
|
+
"do not assume broad private authority",
|
|
46
|
+
],
|
|
47
|
+
relatedGroupId,
|
|
48
|
+
}
|
|
49
|
+
: {
|
|
50
|
+
level,
|
|
51
|
+
basis: "unknown",
|
|
52
|
+
summary: "truly unknown first-contact context",
|
|
53
|
+
why: "this person is not known through direct trust or a shared group context.",
|
|
54
|
+
permits: [
|
|
55
|
+
"safe first-contact orientation only",
|
|
56
|
+
],
|
|
57
|
+
constraints: [
|
|
58
|
+
"first contact does not reach the full model on open channels",
|
|
59
|
+
"no local or privileged actions",
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
(0, runtime_1.emitNervesEvent)({
|
|
63
|
+
component: "friends",
|
|
64
|
+
event: "friends.trust_explained",
|
|
65
|
+
message: "built explicit trust explanation",
|
|
66
|
+
meta: {
|
|
67
|
+
channel: input.channel,
|
|
68
|
+
level: explanation.level,
|
|
69
|
+
basis: explanation.basis,
|
|
70
|
+
hasRelatedGroup: Boolean(explanation.relatedGroupId),
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
return explanation;
|
|
74
|
+
}
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
// Context kernel type definitions.
|
|
3
3
|
// FriendRecord (merged identity + memory), channel capabilities, and resolved context.
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.TRUSTED_LEVELS = void 0;
|
|
5
6
|
exports.isIdentityProvider = isIdentityProvider;
|
|
6
7
|
exports.isIntegration = isIntegration;
|
|
8
|
+
exports.isTrustedLevel = isTrustedLevel;
|
|
7
9
|
const runtime_1 = require("../../nerves/runtime");
|
|
8
10
|
const IDENTITY_PROVIDERS = new Set(["aad", "local", "teams-conversation", "imessage-handle"]);
|
|
9
11
|
function isIdentityProvider(value) {
|
|
@@ -19,3 +21,9 @@ const INTEGRATIONS = new Set(["ado", "github", "graph"]);
|
|
|
19
21
|
function isIntegration(value) {
|
|
20
22
|
return typeof value === "string" && INTEGRATIONS.has(value);
|
|
21
23
|
}
|
|
24
|
+
/** Trust levels that grant full tool access and proactive send capability. */
|
|
25
|
+
exports.TRUSTED_LEVELS = new Set(["family", "friend"]);
|
|
26
|
+
/** Whether a trust level grants full access (family or friend). Defaults to "friend" for legacy records. */
|
|
27
|
+
function isTrustedLevel(trustLevel) {
|
|
28
|
+
return exports.TRUSTED_LEVELS.has(trustLevel ?? "friend");
|
|
29
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
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.indexJournalFiles = indexJournalFiles;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const runtime_1 = require("../nerves/runtime");
|
|
40
|
+
const TEXT_EXTENSIONS = new Set([".md", ".txt"]);
|
|
41
|
+
const PREVIEW_CHAR_LIMIT = 500;
|
|
42
|
+
function readExistingIndex(indexPath) {
|
|
43
|
+
try {
|
|
44
|
+
const raw = fs.readFileSync(indexPath, "utf8");
|
|
45
|
+
const parsed = JSON.parse(raw);
|
|
46
|
+
if (!Array.isArray(parsed))
|
|
47
|
+
return [];
|
|
48
|
+
return parsed;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function extractPreview(content) {
|
|
55
|
+
const trimmed = content.trim();
|
|
56
|
+
if (!trimmed)
|
|
57
|
+
return "";
|
|
58
|
+
return trimmed.split("\n")[0].replace(/^#+\s*/, "").trim();
|
|
59
|
+
}
|
|
60
|
+
async function indexJournalFiles(journalDir, indexPath, embedProvider) {
|
|
61
|
+
// Read existing index
|
|
62
|
+
const existingIndex = readExistingIndex(indexPath);
|
|
63
|
+
const indexMap = new Map();
|
|
64
|
+
for (const entry of existingIndex) {
|
|
65
|
+
indexMap.set(entry.filename, entry);
|
|
66
|
+
}
|
|
67
|
+
// Scan journal dir for text files
|
|
68
|
+
let dirEntries;
|
|
69
|
+
try {
|
|
70
|
+
dirEntries = fs.readdirSync(journalDir, { withFileTypes: true });
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
(0, runtime_1.emitNervesEvent)({
|
|
74
|
+
component: "mind",
|
|
75
|
+
event: "mind.journal_index_scan",
|
|
76
|
+
message: "journal dir not found or unreadable",
|
|
77
|
+
meta: { journalDir },
|
|
78
|
+
});
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
const textFiles = dirEntries.filter((entry) => {
|
|
82
|
+
if (!entry.isFile())
|
|
83
|
+
return false;
|
|
84
|
+
if (entry.name.startsWith("."))
|
|
85
|
+
return false;
|
|
86
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
87
|
+
return TEXT_EXTENSIONS.has(ext);
|
|
88
|
+
});
|
|
89
|
+
if (textFiles.length === 0) {
|
|
90
|
+
(0, runtime_1.emitNervesEvent)({
|
|
91
|
+
component: "mind",
|
|
92
|
+
event: "mind.journal_index_scan",
|
|
93
|
+
message: "no text files found in journal",
|
|
94
|
+
meta: { journalDir },
|
|
95
|
+
});
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
let newlyIndexed = 0;
|
|
99
|
+
for (const file of textFiles) {
|
|
100
|
+
const filePath = path.join(journalDir, file.name);
|
|
101
|
+
let stat;
|
|
102
|
+
try {
|
|
103
|
+
stat = fs.statSync(filePath);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
/* v8 ignore next -- filesystem race: file deleted between readdir and stat @preserve */
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
// Check if already indexed with same mtime
|
|
110
|
+
const existing = indexMap.get(file.name);
|
|
111
|
+
if (existing && existing.mtime === stat.mtimeMs) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
// Read content for embedding
|
|
115
|
+
let content;
|
|
116
|
+
try {
|
|
117
|
+
content = fs.readFileSync(filePath, "utf8");
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
/* v8 ignore next -- filesystem race: file deleted between stat and read @preserve */
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
const preview = extractPreview(content);
|
|
124
|
+
const embedText = content.slice(0, PREVIEW_CHAR_LIMIT);
|
|
125
|
+
// Generate embedding
|
|
126
|
+
let embedding;
|
|
127
|
+
try {
|
|
128
|
+
const vectors = await embedProvider.embed([embedText]);
|
|
129
|
+
embedding = vectors[0] ?? [];
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
(0, runtime_1.emitNervesEvent)({
|
|
133
|
+
level: "warn",
|
|
134
|
+
component: "mind",
|
|
135
|
+
event: "mind.journal_embedding_error",
|
|
136
|
+
message: "embedding failed for journal file",
|
|
137
|
+
meta: { filename: file.name },
|
|
138
|
+
});
|
|
139
|
+
embedding = [];
|
|
140
|
+
}
|
|
141
|
+
indexMap.set(file.name, {
|
|
142
|
+
filename: file.name,
|
|
143
|
+
embedding,
|
|
144
|
+
mtime: stat.mtimeMs,
|
|
145
|
+
preview,
|
|
146
|
+
});
|
|
147
|
+
newlyIndexed++;
|
|
148
|
+
}
|
|
149
|
+
// Write updated index back
|
|
150
|
+
if (newlyIndexed > 0) {
|
|
151
|
+
const updatedIndex = Array.from(indexMap.values());
|
|
152
|
+
fs.writeFileSync(indexPath, JSON.stringify(updatedIndex, null, 2), "utf8");
|
|
153
|
+
}
|
|
154
|
+
(0, runtime_1.emitNervesEvent)({
|
|
155
|
+
component: "mind",
|
|
156
|
+
event: "mind.journal_index_complete",
|
|
157
|
+
message: "journal indexing complete",
|
|
158
|
+
meta: { journalDir, newlyIndexed, total: indexMap.size },
|
|
159
|
+
});
|
|
160
|
+
return newlyIndexed;
|
|
161
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findActivePersistentObligation = findActivePersistentObligation;
|
|
4
|
+
exports.findStatusObligation = findStatusObligation;
|
|
5
|
+
exports.renderActiveObligationSteering = renderActiveObligationSteering;
|
|
6
|
+
exports.renderConcreteStatusGuidance = renderConcreteStatusGuidance;
|
|
7
|
+
exports.renderLiveThreadStatusShape = renderLiveThreadStatusShape;
|
|
8
|
+
exports.buildExactStatusReply = buildExactStatusReply;
|
|
9
|
+
exports.renderExactStatusReplyContract = renderExactStatusReplyContract;
|
|
10
|
+
const config_1 = require("../heart/config");
|
|
11
|
+
const active_work_1 = require("../heart/active-work");
|
|
12
|
+
const runtime_1 = require("../nerves/runtime");
|
|
13
|
+
function findActivePersistentObligation(frame) {
|
|
14
|
+
if (!frame)
|
|
15
|
+
return null;
|
|
16
|
+
return (frame.pendingObligations ?? []).find((ob) => ob.status !== "pending" && ob.status !== "fulfilled") ?? null;
|
|
17
|
+
}
|
|
18
|
+
function obligationTimestampMs(obligation) {
|
|
19
|
+
return Date.parse(obligation.updatedAt ?? obligation.createdAt);
|
|
20
|
+
}
|
|
21
|
+
function newestObligationFirst(left, right) {
|
|
22
|
+
return obligationTimestampMs(right) - obligationTimestampMs(left);
|
|
23
|
+
}
|
|
24
|
+
function matchesCurrentSession(frame, obligation) {
|
|
25
|
+
return matchesSessionOrigin(frame, obligation.origin);
|
|
26
|
+
}
|
|
27
|
+
function matchesSessionOrigin(frame, origin) {
|
|
28
|
+
return Boolean(frame.currentSession
|
|
29
|
+
&& origin.friendId === frame.currentSession.friendId
|
|
30
|
+
&& origin.channel === frame.currentSession.channel
|
|
31
|
+
&& (0, config_1.sanitizeKey)(origin.key) === (0, config_1.sanitizeKey)(frame.currentSession.key));
|
|
32
|
+
}
|
|
33
|
+
function findStatusObligation(frame) {
|
|
34
|
+
if (!frame)
|
|
35
|
+
return null;
|
|
36
|
+
const openObligations = [...(frame.pendingObligations ?? [])]
|
|
37
|
+
.filter((obligation) => obligation.status !== "fulfilled")
|
|
38
|
+
.sort(newestObligationFirst);
|
|
39
|
+
const sameSession = openObligations.find((obligation) => matchesCurrentSession(frame, obligation));
|
|
40
|
+
if (sameSession)
|
|
41
|
+
return sameSession;
|
|
42
|
+
return openObligations[0] ?? null;
|
|
43
|
+
}
|
|
44
|
+
function findCurrentSessionStatusObligation(frame) {
|
|
45
|
+
const openObligations = [...(frame.pendingObligations ?? [])]
|
|
46
|
+
.filter((obligation) => obligation.status !== "fulfilled")
|
|
47
|
+
.sort(newestObligationFirst);
|
|
48
|
+
return openObligations.find((obligation) => matchesCurrentSession(frame, obligation)) ?? null;
|
|
49
|
+
}
|
|
50
|
+
function renderActiveObligationSteering(obligation) {
|
|
51
|
+
(0, runtime_1.emitNervesEvent)({
|
|
52
|
+
component: "mind",
|
|
53
|
+
event: "mind.obligation_steering_rendered",
|
|
54
|
+
message: "rendered active obligation steering",
|
|
55
|
+
meta: {
|
|
56
|
+
hasObligation: Boolean(obligation),
|
|
57
|
+
hasSurface: Boolean(obligation?.currentSurface?.label),
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
if (!obligation)
|
|
61
|
+
return "";
|
|
62
|
+
const name = obligation.origin.friendId;
|
|
63
|
+
const surfaceLine = obligation.currentSurface?.label
|
|
64
|
+
? `\nright now that work is happening in ${obligation.currentSurface.label}.`
|
|
65
|
+
: "";
|
|
66
|
+
return `## where my attention is
|
|
67
|
+
i'm already working on something i owe ${name}.${surfaceLine}
|
|
68
|
+
|
|
69
|
+
i should close that loop before i act like this is a fresh blank turn.`;
|
|
70
|
+
}
|
|
71
|
+
function mergeArtifactFallback(obligation) {
|
|
72
|
+
const trimmed = obligation.content.trim();
|
|
73
|
+
if (!trimmed)
|
|
74
|
+
return "the fix";
|
|
75
|
+
const stripped = trimmed.replace(/^merge(?:\s+|$)/i, "").trim();
|
|
76
|
+
return stripped || "the fix";
|
|
77
|
+
}
|
|
78
|
+
function formatMergeArtifact(obligation) {
|
|
79
|
+
const currentArtifact = obligation.currentArtifact?.trim();
|
|
80
|
+
if (currentArtifact)
|
|
81
|
+
return currentArtifact;
|
|
82
|
+
if (obligation.currentSurface?.kind === "merge") {
|
|
83
|
+
const surfaceLabel = obligation.currentSurface.label.trim();
|
|
84
|
+
if (surfaceLabel)
|
|
85
|
+
return surfaceLabel;
|
|
86
|
+
}
|
|
87
|
+
return mergeArtifactFallback(obligation);
|
|
88
|
+
}
|
|
89
|
+
function formatActiveLane(frame, obligation) {
|
|
90
|
+
const liveCodingSession = frame.codingSessions?.[0];
|
|
91
|
+
if (liveCodingSession) {
|
|
92
|
+
const sameThread = frame.currentSession
|
|
93
|
+
&& liveCodingSession.originSession
|
|
94
|
+
&& liveCodingSession.originSession.friendId === frame.currentSession.friendId
|
|
95
|
+
&& liveCodingSession.originSession.channel === frame.currentSession.channel
|
|
96
|
+
&& liveCodingSession.originSession.key === frame.currentSession.key;
|
|
97
|
+
return sameThread
|
|
98
|
+
? `${liveCodingSession.runner} ${liveCodingSession.id} for this same thread`
|
|
99
|
+
: liveCodingSession.originSession
|
|
100
|
+
? `${liveCodingSession.runner} ${liveCodingSession.id} for ${liveCodingSession.originSession.channel}/${liveCodingSession.originSession.key}`
|
|
101
|
+
: `${liveCodingSession.runner} ${liveCodingSession.id}`;
|
|
102
|
+
}
|
|
103
|
+
return obligation.currentSurface?.label
|
|
104
|
+
|| (matchesCurrentSession(frame, obligation) ? "this same thread" : "this live loop");
|
|
105
|
+
}
|
|
106
|
+
function formatCurrentArtifact(frame, obligation) {
|
|
107
|
+
if (obligation?.currentArtifact)
|
|
108
|
+
return obligation.currentArtifact;
|
|
109
|
+
if (obligation?.currentSurface?.kind === "merge")
|
|
110
|
+
return obligation.currentSurface.label;
|
|
111
|
+
if ((frame.codingSessions ?? []).length > 0)
|
|
112
|
+
return "no PR or merge artifact yet";
|
|
113
|
+
return obligation ? "no artifact yet" : "";
|
|
114
|
+
}
|
|
115
|
+
function formatNextAction(frame, obligation) {
|
|
116
|
+
const obligationHasConcreteArtifact = Boolean(obligation?.currentArtifact?.trim())
|
|
117
|
+
|| obligation?.currentSurface?.kind === "merge";
|
|
118
|
+
if (obligation?.status === "waiting_for_merge") {
|
|
119
|
+
return obligation.nextAction?.trim() || `wait for checks, merge ${formatMergeArtifact(obligation)}, then update runtime`;
|
|
120
|
+
}
|
|
121
|
+
if (obligation?.status === "updating_runtime") {
|
|
122
|
+
return obligation.nextAction?.trim() || "update runtime, verify version/changelog, then re-observe";
|
|
123
|
+
}
|
|
124
|
+
if (obligationHasConcreteArtifact && obligation?.nextAction?.trim()) {
|
|
125
|
+
return obligation.nextAction.trim();
|
|
126
|
+
}
|
|
127
|
+
const liveCodingSession = frame.codingSessions?.[0];
|
|
128
|
+
if (liveCodingSession?.status === "waiting_input") {
|
|
129
|
+
return `answer ${liveCodingSession.runner} ${liveCodingSession.id} and continue`;
|
|
130
|
+
}
|
|
131
|
+
if (liveCodingSession?.status === "stalled") {
|
|
132
|
+
return `unstick ${liveCodingSession.runner} ${liveCodingSession.id} and continue`;
|
|
133
|
+
}
|
|
134
|
+
if (liveCodingSession) {
|
|
135
|
+
return "finish the coding pass and bring the result back here";
|
|
136
|
+
}
|
|
137
|
+
if (obligation?.nextAction?.trim())
|
|
138
|
+
return obligation.nextAction.trim();
|
|
139
|
+
if (obligation?.content?.trim()) {
|
|
140
|
+
return `work on "${obligation.content.trim()}" and bring back a concrete artifact`;
|
|
141
|
+
}
|
|
142
|
+
return obligation ? "continue the active loop and bring the result back here" : "";
|
|
143
|
+
}
|
|
144
|
+
function renderConcreteStatusGuidance(frame, obligation) {
|
|
145
|
+
const activeLane = obligation ? formatActiveLane(frame, obligation) : "";
|
|
146
|
+
const currentArtifact = formatCurrentArtifact(frame, obligation);
|
|
147
|
+
const nextAction = formatNextAction(frame, obligation);
|
|
148
|
+
const liveConversation = frame.currentSession
|
|
149
|
+
? `${frame.currentSession.channel}/${frame.currentSession.key}`
|
|
150
|
+
: "";
|
|
151
|
+
if (!activeLane && !currentArtifact && !nextAction)
|
|
152
|
+
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.
|
|
154
|
+
the live conversation is ${liveConversation || "not in a live conversation"}.
|
|
155
|
+
the active lane is ${activeLane}.
|
|
156
|
+
the current artifact is ${currentArtifact}.
|
|
157
|
+
if i just finished or verified something concrete in this live lane, i name that as the latest checkpoint.
|
|
158
|
+
the next action is ${nextAction}.
|
|
159
|
+
|
|
160
|
+
i answer naturally from those facts instead of forcing a canned status block.`;
|
|
161
|
+
}
|
|
162
|
+
function renderLiveThreadStatusShape(frame) {
|
|
163
|
+
if (!frame.currentSession)
|
|
164
|
+
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:
|
|
166
|
+
live conversation: ${frame.currentSession.channel}/${frame.currentSession.key}
|
|
167
|
+
active lane: this same thread
|
|
168
|
+
current artifact: <actual artifact or "no artifact yet">
|
|
169
|
+
latest checkpoint: <freshest concrete thing i just finished or verified>
|
|
170
|
+
next action: <smallest concrete next step i'm taking now>
|
|
171
|
+
|
|
172
|
+
no recap paragraph before those lines.
|
|
173
|
+
no option list.
|
|
174
|
+
present tense only.
|
|
175
|
+
if a finished step matters, i label it "just finished" instead of presenting it as current work.`;
|
|
176
|
+
}
|
|
177
|
+
function buildExactStatusReply(frame, obligation, latestCheckpoint, statusCheckScope) {
|
|
178
|
+
const headerObligation = statusCheckScope === "all-sessions-family"
|
|
179
|
+
? findCurrentSessionStatusObligation(frame)
|
|
180
|
+
: obligation;
|
|
181
|
+
const liveConversation = frame.currentSession
|
|
182
|
+
? `${frame.currentSession.channel}/${frame.currentSession.key}`
|
|
183
|
+
: "not in a live conversation";
|
|
184
|
+
const activeLane = headerObligation
|
|
185
|
+
? formatActiveLane(frame, headerObligation)
|
|
186
|
+
: (frame.currentSession ? "this same thread" : "this live loop");
|
|
187
|
+
const currentArtifact = formatCurrentArtifact(frame, headerObligation) || "no artifact yet";
|
|
188
|
+
const nextAction = formatNextAction(frame, headerObligation) || "continue the active loop and bring the result back here";
|
|
189
|
+
const latest = latestCheckpoint.trim() || "<freshest concrete thing i just finished or verified>";
|
|
190
|
+
const lines = [
|
|
191
|
+
`live conversation: ${liveConversation}`,
|
|
192
|
+
`active lane: ${activeLane}`,
|
|
193
|
+
`current artifact: ${currentArtifact}`,
|
|
194
|
+
`latest checkpoint: ${latest}`,
|
|
195
|
+
`next action: ${nextAction}`,
|
|
196
|
+
];
|
|
197
|
+
if (statusCheckScope === "all-sessions-family") {
|
|
198
|
+
lines.push("other active sessions:");
|
|
199
|
+
const summaries = (0, active_work_1.formatOtherActiveSessionSummaries)(frame);
|
|
200
|
+
lines.push(...(summaries.length > 0 ? summaries : ["- none"]));
|
|
201
|
+
}
|
|
202
|
+
return lines.join("\n");
|
|
203
|
+
}
|
|
204
|
+
function renderExactStatusReplyContract(frame, obligation, statusCheckScope) {
|
|
205
|
+
const headerObligation = statusCheckScope === "all-sessions-family"
|
|
206
|
+
? findCurrentSessionStatusObligation(frame)
|
|
207
|
+
: obligation;
|
|
208
|
+
if (statusCheckScope === "all-sessions-family") {
|
|
209
|
+
return `reply using exactly this status shape and nothing else:
|
|
210
|
+
live conversation: ${frame.currentSession ? `${frame.currentSession.channel}/${frame.currentSession.key}` : "not in a live conversation"}
|
|
211
|
+
active lane: ${headerObligation ? formatActiveLane(frame, headerObligation) : (frame.currentSession ? "this same thread" : "this live loop")}
|
|
212
|
+
current artifact: ${formatCurrentArtifact(frame, headerObligation) || "no artifact yet"}
|
|
213
|
+
latest checkpoint: <freshest concrete thing i just finished or verified>
|
|
214
|
+
next action: ${formatNextAction(frame, headerObligation) || "continue the active loop and bring the result back here"}
|
|
215
|
+
other active sessions:
|
|
216
|
+
- <session label>: <what i'm doing there right now>`;
|
|
217
|
+
}
|
|
218
|
+
return `reply using exactly these five lines and nothing else:
|
|
219
|
+
${buildExactStatusReply(frame, obligation, "<freshest concrete thing i just finished or verified>")}
|
|
220
|
+
`;
|
|
221
|
+
}
|