@ouro.bot/cli 0.1.0-alpha.196 → 0.1.0-alpha.199
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 +1 -1
- package/dist/heart/daemon/agent-config-check.js +149 -0
- package/dist/heart/daemon/daemon-entry.js +8 -0
- package/dist/heart/daemon/process-manager.js +18 -0
- package/dist/heart/identity.js +12 -1
- package/dist/heart/{wake-packet.js → start-of-turn-packet.js} +15 -15
- package/dist/mind/prompt.js +7 -7
- package/dist/repertoire/coding/context-pack.js +4 -4
- package/dist/senses/pipeline.js +9 -9
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.199",
|
|
6
|
+
"changes": [
|
|
7
|
+
"refactor: rename wake packet to start-of-turn packet across codebase (types, modules, events, tests)"
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"version": "0.1.0-alpha.197",
|
|
12
|
+
"changes": [
|
|
13
|
+
"fix(daemon): validate agent config before spawn \u2014 skips agents with missing credentials instead of crash-looping"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
4
16
|
{
|
|
5
17
|
"version": "0.1.0-alpha.196",
|
|
6
18
|
"changes": [
|
|
@@ -670,7 +670,7 @@ function formatActiveWorkFrame(frame, options) {
|
|
|
670
670
|
// Enriched obligation data (with resumeHint, stalenessClass, waitingOn) is rendered elsewhere.
|
|
671
671
|
// Avoid duplicating the full list — just anchor the count here.
|
|
672
672
|
lines.push("");
|
|
673
|
-
lines.push(`return obligations: ${openCount} active (canonical details in **Owed** section of
|
|
673
|
+
lines.push(`return obligations: ${openCount} active (canonical details in **Owed** section of start-of-turn packet)`);
|
|
674
674
|
}
|
|
675
675
|
else if (openCount > 0) {
|
|
676
676
|
lines.push("");
|
|
@@ -0,0 +1,149 @@
|
|
|
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.checkAgentConfig = checkAgentConfig;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const identity_1 = require("../identity");
|
|
40
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
41
|
+
/**
|
|
42
|
+
* Pre-spawn validation: ensures agent.json exists and required secrets are present.
|
|
43
|
+
* Returns `{ ok: true }` when the agent is ready to run, or a descriptive error
|
|
44
|
+
* with an actionable fix message when something is missing.
|
|
45
|
+
*/
|
|
46
|
+
function checkAgentConfig(agentName, bundlesRoot, secretsRoot) {
|
|
47
|
+
const agentJsonPath = path.join(bundlesRoot, `${agentName}.ouro`, "agent.json");
|
|
48
|
+
let raw;
|
|
49
|
+
try {
|
|
50
|
+
raw = fs.readFileSync(agentJsonPath, "utf-8");
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
error: `agent.json not found at ${agentJsonPath}`,
|
|
56
|
+
fix: `Run 'ouro hatch ${agentName}' to create the agent bundle, or verify that ${bundlesRoot}/${agentName}.ouro/ exists.`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
let parsed;
|
|
60
|
+
try {
|
|
61
|
+
parsed = JSON.parse(raw);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return {
|
|
65
|
+
ok: false,
|
|
66
|
+
error: `agent.json at ${agentJsonPath} contains invalid JSON`,
|
|
67
|
+
fix: `Open ${agentJsonPath} and fix the JSON syntax.`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// Disabled agents are valid — they just won't run
|
|
71
|
+
if (parsed.enabled === false) {
|
|
72
|
+
return { ok: true };
|
|
73
|
+
}
|
|
74
|
+
// Resolve provider: humanFacing.provider > config.provider
|
|
75
|
+
let provider;
|
|
76
|
+
const humanFacing = parsed.humanFacing;
|
|
77
|
+
if (humanFacing && typeof humanFacing.provider === "string") {
|
|
78
|
+
provider = humanFacing.provider;
|
|
79
|
+
}
|
|
80
|
+
else if (typeof parsed.provider === "string") {
|
|
81
|
+
provider = parsed.provider;
|
|
82
|
+
}
|
|
83
|
+
if (!provider) {
|
|
84
|
+
return {
|
|
85
|
+
ok: false,
|
|
86
|
+
error: `agent.json for '${agentName}' has no provider configured`,
|
|
87
|
+
fix: `Add humanFacing.provider to ${agentJsonPath}. Valid providers: ${Object.keys(identity_1.PROVIDER_CREDENTIALS).join(", ")}`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const requiredFields = identity_1.PROVIDER_CREDENTIALS[provider];
|
|
91
|
+
if (!requiredFields) {
|
|
92
|
+
return {
|
|
93
|
+
ok: false,
|
|
94
|
+
error: `Unknown provider '${provider}' in agent.json for '${agentName}'`,
|
|
95
|
+
fix: `Set humanFacing.provider to one of: ${Object.keys(identity_1.PROVIDER_CREDENTIALS).join(", ")}`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
// Azure with managed identity does not require apiKey — skip secrets check
|
|
99
|
+
// when endpoint + deployment are present (managed identity auth)
|
|
100
|
+
const secretsJsonPath = path.join(secretsRoot, agentName, "secrets.json");
|
|
101
|
+
let secrets;
|
|
102
|
+
try {
|
|
103
|
+
const secretsRaw = fs.readFileSync(secretsJsonPath, "utf-8");
|
|
104
|
+
secrets = JSON.parse(secretsRaw);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return {
|
|
108
|
+
ok: false,
|
|
109
|
+
error: `secrets.json not found or unreadable at ${secretsJsonPath}`,
|
|
110
|
+
fix: `Run 'ouro auth ${agentName}' to configure credentials, or create ${secretsJsonPath} with providers.${provider} credentials.`,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const providers = secrets.providers;
|
|
114
|
+
const providerSecrets = providers?.[provider];
|
|
115
|
+
if (!providerSecrets) {
|
|
116
|
+
return {
|
|
117
|
+
ok: false,
|
|
118
|
+
error: `secrets.json for '${agentName}' is missing providers.${provider} section`,
|
|
119
|
+
fix: `Run 'ouro auth ${agentName}' to configure ${provider} credentials.`,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// Azure special case: managed identity only needs endpoint + deployment
|
|
123
|
+
if (provider === "azure") {
|
|
124
|
+
const hasEndpoint = typeof providerSecrets.endpoint === "string" && providerSecrets.endpoint.length > 0;
|
|
125
|
+
const hasDeployment = typeof providerSecrets.deployment === "string" && providerSecrets.deployment.length > 0;
|
|
126
|
+
const hasManagedId = typeof providerSecrets.managedIdentityClientId === "string" && providerSecrets.managedIdentityClientId.length > 0;
|
|
127
|
+
if (hasEndpoint && hasDeployment && hasManagedId) {
|
|
128
|
+
return { ok: true };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const missing = requiredFields.filter((field) => {
|
|
132
|
+
const val = providerSecrets[field];
|
|
133
|
+
return typeof val !== "string" || val.length === 0;
|
|
134
|
+
});
|
|
135
|
+
if (missing.length > 0) {
|
|
136
|
+
return {
|
|
137
|
+
ok: false,
|
|
138
|
+
error: `secrets.json for '${agentName}' is missing required ${provider} credentials: ${missing.join(", ")}`,
|
|
139
|
+
fix: `Run 'ouro auth ${agentName}' to set up ${provider} credentials, or add the missing fields to providers.${provider} in ${secretsJsonPath}.`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
(0, runtime_1.emitNervesEvent)({
|
|
143
|
+
component: "daemon",
|
|
144
|
+
event: "daemon.agent_config_valid",
|
|
145
|
+
message: "agent config validation passed",
|
|
146
|
+
meta: { agent: agentName, provider },
|
|
147
|
+
});
|
|
148
|
+
return { ok: true };
|
|
149
|
+
}
|
|
@@ -54,6 +54,8 @@ const habit_migration_1 = require("./habit-migration");
|
|
|
54
54
|
const os_cron_deps_1 = require("./os-cron-deps");
|
|
55
55
|
const os_cron_1 = require("./os-cron");
|
|
56
56
|
const daemon_tombstone_1 = require("./daemon-tombstone");
|
|
57
|
+
const os = __importStar(require("os"));
|
|
58
|
+
const agent_config_check_1 = require("./agent-config-check");
|
|
57
59
|
function parseSocketPath(argv) {
|
|
58
60
|
const socketIndex = argv.indexOf("--socket");
|
|
59
61
|
if (socketIndex >= 0) {
|
|
@@ -92,6 +94,12 @@ const processManager = new process_manager_1.DaemonProcessManager({
|
|
|
92
94
|
autoStart: true,
|
|
93
95
|
})),
|
|
94
96
|
existsSync: fs.existsSync,
|
|
97
|
+
/* v8 ignore next 4 -- wiring: delegates to checkAgentConfig which has full unit tests @preserve */
|
|
98
|
+
configCheck: (agent) => {
|
|
99
|
+
const bundlesRoot = (0, identity_1.getAgentBundlesRoot)();
|
|
100
|
+
const secretsRoot = path.join(os.homedir(), ".agentsecrets");
|
|
101
|
+
return (0, agent_config_check_1.checkAgentConfig)(agent, bundlesRoot, secretsRoot);
|
|
102
|
+
},
|
|
95
103
|
});
|
|
96
104
|
const scheduler = new task_scheduler_1.TaskDrivenScheduler({
|
|
97
105
|
agents: [...managedAgents],
|
|
@@ -54,6 +54,7 @@ class DaemonProcessManager {
|
|
|
54
54
|
cooldownRecoveryMs;
|
|
55
55
|
maxCooldownRetries;
|
|
56
56
|
existsSyncFn;
|
|
57
|
+
configCheckFn;
|
|
57
58
|
constructor(options) {
|
|
58
59
|
this.maxRestartsPerHour = options.maxRestartsPerHour ?? 10;
|
|
59
60
|
this.stabilityThresholdMs = options.stabilityThresholdMs ?? 60_000;
|
|
@@ -66,6 +67,7 @@ class DaemonProcessManager {
|
|
|
66
67
|
this.setTimeoutFn = options.setTimeoutFn ?? ((cb, delay) => setTimeout(cb, delay));
|
|
67
68
|
this.clearTimeoutFn = options.clearTimeoutFn ?? ((timer) => clearTimeout(timer));
|
|
68
69
|
this.existsSyncFn = options.existsSync ?? null;
|
|
70
|
+
this.configCheckFn = options.configCheck ?? null;
|
|
69
71
|
for (const agent of options.agents) {
|
|
70
72
|
this.agents.set(agent.name, {
|
|
71
73
|
config: agent,
|
|
@@ -104,6 +106,22 @@ class DaemonProcessManager {
|
|
|
104
106
|
this.clearRestartTimer(state);
|
|
105
107
|
state.stopRequested = false;
|
|
106
108
|
state.snapshot.status = "starting";
|
|
109
|
+
if (this.configCheckFn) {
|
|
110
|
+
const result = this.configCheckFn(agent);
|
|
111
|
+
if (!result.ok) {
|
|
112
|
+
state.snapshot.status = "crashed";
|
|
113
|
+
(0, runtime_1.emitNervesEvent)({
|
|
114
|
+
level: "error",
|
|
115
|
+
component: "daemon",
|
|
116
|
+
event: "daemon.agent_config_invalid",
|
|
117
|
+
message: result.error ?? "agent config validation failed",
|
|
118
|
+
meta: { agent, fix: result.fix ?? null },
|
|
119
|
+
});
|
|
120
|
+
process.stderr.write(`[daemon] ${agent}: ${result.error}\n` +
|
|
121
|
+
(result.fix ? ` Fix: ${result.fix}\n` : ""));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
107
125
|
const runCwd = (0, identity_1.getRepoRoot)();
|
|
108
126
|
const entryScript = path.join((0, identity_1.getRepoRoot)(), "dist", state.config.entry);
|
|
109
127
|
if (this.existsSyncFn && !this.existsSyncFn(entryScript)) {
|
package/dist/heart/identity.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.HARNESS_CANONICAL_REPO_URL = exports.DEFAULT_AGENT_SENSES = exports.DEFAULT_AGENT_PHRASES = exports.DEFAULT_AGENT_CONTEXT = void 0;
|
|
36
|
+
exports.HARNESS_CANONICAL_REPO_URL = exports.PROVIDER_CREDENTIALS = exports.DEFAULT_AGENT_SENSES = exports.DEFAULT_AGENT_PHRASES = exports.DEFAULT_AGENT_CONTEXT = void 0;
|
|
37
37
|
exports.buildDefaultAgentTemplate = buildDefaultAgentTemplate;
|
|
38
38
|
exports.getAgentName = getAgentName;
|
|
39
39
|
exports.getRepoRoot = getRepoRoot;
|
|
@@ -208,6 +208,17 @@ function resolveOptionalAgentName(agentName) {
|
|
|
208
208
|
function getAgentStateRoot(agentName) {
|
|
209
209
|
return path.join(getAgentRoot(resolveOptionalAgentName(agentName)), "state");
|
|
210
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* Required credential fields per provider.
|
|
213
|
+
* Used by daemon config-check to validate secrets before spawning an agent.
|
|
214
|
+
*/
|
|
215
|
+
exports.PROVIDER_CREDENTIALS = {
|
|
216
|
+
azure: ["apiKey", "endpoint", "deployment"],
|
|
217
|
+
minimax: ["apiKey"],
|
|
218
|
+
anthropic: ["setupToken"],
|
|
219
|
+
"openai-codex": ["oauthAccessToken"],
|
|
220
|
+
"github-copilot": ["githubToken", "baseUrl"],
|
|
221
|
+
};
|
|
211
222
|
exports.HARNESS_CANONICAL_REPO_URL = "https://github.com/ouroborosbot/ouroboros.git";
|
|
212
223
|
function getAgentRepoWorkspacesRoot(agentName) {
|
|
213
224
|
return path.join(getAgentStateRoot(resolveOptionalAgentName(agentName)), "workspaces");
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.
|
|
5
|
-
exports.
|
|
3
|
+
exports.buildStartOfTurnPacket = buildStartOfTurnPacket;
|
|
4
|
+
exports.renderStartOfTurnPacket = renderStartOfTurnPacket;
|
|
5
|
+
exports.renderCompactStartOfTurnPacket = renderCompactStartOfTurnPacket;
|
|
6
6
|
const runtime_1 = require("../nerves/runtime");
|
|
7
7
|
const tempo_1 = require("./tempo");
|
|
8
8
|
function estimateTokens(text) {
|
|
@@ -87,7 +87,7 @@ function buildResumeHint(view, obligations) {
|
|
|
87
87
|
return "";
|
|
88
88
|
return hints.join("; ");
|
|
89
89
|
}
|
|
90
|
-
function
|
|
90
|
+
function buildStartOfTurnPacket(view, opts) {
|
|
91
91
|
const tempo = view.tempo;
|
|
92
92
|
const tokenBudget = tempo_1.TEMPO_BUDGETS[tempo];
|
|
93
93
|
const effectiveObligations = opts?.canonicalObligations ? opts.canonicalObligations.all : view.activeObligations;
|
|
@@ -103,8 +103,8 @@ function buildWakePacket(view, opts) {
|
|
|
103
103
|
};
|
|
104
104
|
(0, runtime_1.emitNervesEvent)({
|
|
105
105
|
component: "heart",
|
|
106
|
-
event: "heart.
|
|
107
|
-
message: `
|
|
106
|
+
event: "heart.start_of_turn_packet_built",
|
|
107
|
+
message: `start-of-turn packet built: tempo=${tempo}`,
|
|
108
108
|
meta: {
|
|
109
109
|
tempo,
|
|
110
110
|
plotLineTokens: estimateTokens(packet.plotLine),
|
|
@@ -117,13 +117,13 @@ function buildWakePacket(view, opts) {
|
|
|
117
117
|
return packet;
|
|
118
118
|
}
|
|
119
119
|
/**
|
|
120
|
-
* Renders a
|
|
120
|
+
* Renders a start-of-turn packet to prompt text, respecting token budget.
|
|
121
121
|
* Truncation priority (last truncated first):
|
|
122
122
|
* resumeHint (PROTECTED) > obligations > cares > plotLine > presence
|
|
123
123
|
* So presence is truncated first, then plotLine, then cares, then obligations.
|
|
124
124
|
* resumeHint is never truncated.
|
|
125
125
|
*/
|
|
126
|
-
function
|
|
126
|
+
function renderStartOfTurnPacket(packet) {
|
|
127
127
|
const budget = packet.tokenBudget;
|
|
128
128
|
// Assemble sections in priority order (highest priority first for budget allocation)
|
|
129
129
|
// Each section is { label, content, priority } where lower priority number = truncated first
|
|
@@ -137,8 +137,8 @@ function renderWakePacket(packet) {
|
|
|
137
137
|
if (sections.length === 0) {
|
|
138
138
|
(0, runtime_1.emitNervesEvent)({
|
|
139
139
|
component: "heart",
|
|
140
|
-
event: "heart.
|
|
141
|
-
message: "
|
|
140
|
+
event: "heart.start_of_turn_packet_rendered",
|
|
141
|
+
message: "start-of-turn packet rendered: empty",
|
|
142
142
|
meta: { tokens: 0, tempo: packet.tempo },
|
|
143
143
|
});
|
|
144
144
|
return "";
|
|
@@ -167,8 +167,8 @@ function renderWakePacket(packet) {
|
|
|
167
167
|
}
|
|
168
168
|
(0, runtime_1.emitNervesEvent)({
|
|
169
169
|
component: "heart",
|
|
170
|
-
event: "heart.
|
|
171
|
-
message: `
|
|
170
|
+
event: "heart.start_of_turn_packet_rendered",
|
|
171
|
+
message: `start-of-turn packet rendered: ${tokens} tokens`,
|
|
172
172
|
meta: { tokens, tempo: packet.tempo, sectionCount: sections.length },
|
|
173
173
|
});
|
|
174
174
|
return rendered;
|
|
@@ -200,7 +200,7 @@ function formatSections(sections) {
|
|
|
200
200
|
* Ultra-compact version for coding context (max 200 tokens).
|
|
201
201
|
* Just resumeHint + top obligation + top care, single-line bullets.
|
|
202
202
|
*/
|
|
203
|
-
function
|
|
203
|
+
function renderCompactStartOfTurnPacket(packet) {
|
|
204
204
|
const parts = [];
|
|
205
205
|
if (packet.resumeHint) {
|
|
206
206
|
parts.push(`next: ${packet.resumeHint}`);
|
|
@@ -220,8 +220,8 @@ function renderCompactWakePacket(packet) {
|
|
|
220
220
|
const result = compact.length > maxChars ? compact.slice(0, maxChars) : compact;
|
|
221
221
|
(0, runtime_1.emitNervesEvent)({
|
|
222
222
|
component: "heart",
|
|
223
|
-
event: "heart.
|
|
224
|
-
message: `compact
|
|
223
|
+
event: "heart.start_of_turn_packet_compact_rendered",
|
|
224
|
+
message: `compact start-of-turn packet: ${estimateTokens(result)} tokens`,
|
|
225
225
|
meta: { tokens: estimateTokens(result) },
|
|
226
226
|
});
|
|
227
227
|
return result;
|
package/dist/mind/prompt.js
CHANGED
|
@@ -39,7 +39,7 @@ exports.bodyMapSection = bodyMapSection;
|
|
|
39
39
|
exports.mcpToolsSection = mcpToolsSection;
|
|
40
40
|
exports.runtimeInfoSection = runtimeInfoSection;
|
|
41
41
|
exports.toolRestrictionSection = toolRestrictionSection;
|
|
42
|
-
exports.
|
|
42
|
+
exports.startOfTurnPacketSection = startOfTurnPacketSection;
|
|
43
43
|
exports.centerOfGravitySteeringSection = centerOfGravitySteeringSection;
|
|
44
44
|
exports.commitmentsSection = commitmentsSection;
|
|
45
45
|
exports.delegationHintSection = delegationHintSection;
|
|
@@ -516,13 +516,13 @@ function bridgeContextSection(options) {
|
|
|
516
516
|
return "";
|
|
517
517
|
return bridgeContext.startsWith("## ") ? bridgeContext : `## active bridge work\n${bridgeContext}`;
|
|
518
518
|
}
|
|
519
|
-
function
|
|
520
|
-
return options?.
|
|
519
|
+
function startOfTurnPacketSection(options) {
|
|
520
|
+
return options?.startOfTurnPacket ?? "";
|
|
521
521
|
}
|
|
522
522
|
function activeWorkSection(options) {
|
|
523
523
|
if (!options?.activeWorkFrame)
|
|
524
524
|
return "";
|
|
525
|
-
return (0, active_work_1.formatActiveWorkFrame)(options.activeWorkFrame, { obligationDetailsRenderedElsewhere: !!options?.
|
|
525
|
+
return (0, active_work_1.formatActiveWorkFrame)(options.activeWorkFrame, { obligationDetailsRenderedElsewhere: !!options?.startOfTurnPacket });
|
|
526
526
|
}
|
|
527
527
|
function liveWorldStateSection(options) {
|
|
528
528
|
if (!options?.activeWorkFrame)
|
|
@@ -544,8 +544,8 @@ function familyCrossSessionTruthSection(context, options) {
|
|
|
544
544
|
return "";
|
|
545
545
|
if (context?.friend?.trustLevel !== "family")
|
|
546
546
|
return "";
|
|
547
|
-
// When
|
|
548
|
-
if (options?.
|
|
547
|
+
// When start-of-turn packet is present, compress to one line
|
|
548
|
+
if (options?.startOfTurnPacket) {
|
|
549
549
|
return "When family asks whole-self status, answer from the cross-session picture above.";
|
|
550
550
|
}
|
|
551
551
|
return `## cross-session truth
|
|
@@ -1070,7 +1070,7 @@ async function buildSystem(channel = "cli", options, context) {
|
|
|
1070
1070
|
] : []),
|
|
1071
1071
|
// Group 7: dynamic state for this turn
|
|
1072
1072
|
"# dynamic state for this turn",
|
|
1073
|
-
|
|
1073
|
+
startOfTurnPacketSection(options),
|
|
1074
1074
|
liveWorldStateSection(options),
|
|
1075
1075
|
pendingMessagesSection(options),
|
|
1076
1076
|
activeWorkSection(options),
|
|
@@ -224,7 +224,7 @@ function buildScopeContent(request, contextFiles, skills, agentName) {
|
|
|
224
224
|
formatSkills(skills),
|
|
225
225
|
].join("\n");
|
|
226
226
|
}
|
|
227
|
-
function buildStateContent(request, contextKey, generatedAt, snapshot, existingSessions, agentName, identityPacket, activeWorkFrame,
|
|
227
|
+
function buildStateContent(request, contextKey, generatedAt, snapshot, existingSessions, agentName, identityPacket, activeWorkFrame, startOfTurnPacket) {
|
|
228
228
|
const gitSection = snapshot.available
|
|
229
229
|
? [
|
|
230
230
|
`repoRoot: ${snapshot.repoRoot ?? "unknown"}`,
|
|
@@ -241,7 +241,7 @@ function buildStateContent(request, contextKey, generatedAt, snapshot, existingS
|
|
|
241
241
|
`agent: ${request.parentAgent ?? agentName}`,
|
|
242
242
|
formatOrigin(request),
|
|
243
243
|
`obligationId: ${request.obligationId ?? "none"}`,
|
|
244
|
-
...(
|
|
244
|
+
...(startOfTurnPacket ? ["", "## Continuity", startOfTurnPacket] : []),
|
|
245
245
|
"",
|
|
246
246
|
formatIdentitySection(identityPacket),
|
|
247
247
|
"",
|
|
@@ -281,7 +281,7 @@ function prepareCodingContextPack(input, deps = {}) {
|
|
|
281
281
|
const generatedAt = nowIso();
|
|
282
282
|
const identityPacket = buildIdentityPacket(snapshot, input.request);
|
|
283
283
|
const scopeContent = buildScopeContent(input.request, contextFiles, skills, agentName);
|
|
284
|
-
const stateContent = buildStateContent(input.request, contextKey, generatedAt, snapshot, existingSessions, agentName, identityPacket, input.activeWorkFrame, input.
|
|
284
|
+
const stateContent = buildStateContent(input.request, contextKey, generatedAt, snapshot, existingSessions, agentName, identityPacket, input.activeWorkFrame, input.startOfTurnPacket || undefined);
|
|
285
285
|
mkdirSync(contextDir, { recursive: true });
|
|
286
286
|
writeFileSync(scopeFile, `${scopeContent}\n`, "utf-8");
|
|
287
287
|
writeFileSync(stateFile, `${stateContent}\n`, "utf-8");
|
|
@@ -310,7 +310,7 @@ function prepareCodingContextPack(input, deps = {}) {
|
|
|
310
310
|
}
|
|
311
311
|
/**
|
|
312
312
|
* Emit a coding milestone episode when a session completes or blocks.
|
|
313
|
-
* This is the outbound leg of the coding round-trip: compact
|
|
313
|
+
* This is the outbound leg of the coding round-trip: compact start-of-turn packet goes IN,
|
|
314
314
|
* episodes come BACK OUT.
|
|
315
315
|
*/
|
|
316
316
|
function emitCodingEpisode(agentRoot, session, outcome) {
|
package/dist/senses/pipeline.js
CHANGED
|
@@ -61,7 +61,7 @@ const provider_ping_1 = require("../heart/provider-ping");
|
|
|
61
61
|
const auth_flow_1 = require("../heart/daemon/auth-flow");
|
|
62
62
|
const tempo_1 = require("../heart/tempo");
|
|
63
63
|
const temporal_view_1 = require("../heart/temporal-view");
|
|
64
|
-
const
|
|
64
|
+
const start_of_turn_packet_1 = require("../heart/start-of-turn-packet");
|
|
65
65
|
const presence_1 = require("../heart/presence");
|
|
66
66
|
const episodes_1 = require("../mind/episodes");
|
|
67
67
|
const cares_1 = require("../heart/cares");
|
|
@@ -457,8 +457,8 @@ async function handleInboundTurn(input) {
|
|
|
457
457
|
for (const msg of input.messages) {
|
|
458
458
|
sessionMessages.push(msg);
|
|
459
459
|
}
|
|
460
|
-
// Step 4b: Continuity pipeline — derive tempo, build
|
|
461
|
-
let
|
|
460
|
+
// Step 4b: Continuity pipeline — derive tempo, build start-of-turn packet, snapshot obligations
|
|
461
|
+
let renderedStartOfTurnPacket;
|
|
462
462
|
const preTurnObligationIds = new Set(pendingObligations.map((ob) => `${ob.id}:${ob.status}`));
|
|
463
463
|
try {
|
|
464
464
|
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
@@ -485,15 +485,15 @@ async function handleInboundTurn(input) {
|
|
|
485
485
|
activeCares,
|
|
486
486
|
},
|
|
487
487
|
});
|
|
488
|
-
const
|
|
488
|
+
const startOfTurnPacket = (0, start_of_turn_packet_1.buildStartOfTurnPacket)(temporalView, {
|
|
489
489
|
canonicalObligations: {
|
|
490
490
|
primary: activeWorkFrame.primaryObligation,
|
|
491
491
|
all: activeWorkFrame.pendingObligations,
|
|
492
492
|
},
|
|
493
493
|
});
|
|
494
|
-
|
|
495
|
-
if (!
|
|
496
|
-
|
|
494
|
+
renderedStartOfTurnPacket = (0, start_of_turn_packet_1.renderStartOfTurnPacket)(startOfTurnPacket);
|
|
495
|
+
if (!renderedStartOfTurnPacket)
|
|
496
|
+
renderedStartOfTurnPacket = undefined;
|
|
497
497
|
// Update self-presence
|
|
498
498
|
const presence = (0, presence_1.derivePresence)(agentRoot, agentName, {
|
|
499
499
|
activeSessions: sessionActivity.length + 1,
|
|
@@ -509,7 +509,7 @@ async function handleInboundTurn(input) {
|
|
|
509
509
|
level: "warn",
|
|
510
510
|
component: "senses",
|
|
511
511
|
event: "senses.continuity_error",
|
|
512
|
-
message: "continuity pipeline failed, continuing without
|
|
512
|
+
message: "continuity pipeline failed, continuing without start-of-turn packet",
|
|
513
513
|
meta: { error: continuityError instanceof Error ? continuityError.message : String(continuityError) },
|
|
514
514
|
});
|
|
515
515
|
}
|
|
@@ -520,7 +520,7 @@ async function handleInboundTurn(input) {
|
|
|
520
520
|
bridgeContext,
|
|
521
521
|
activeWorkFrame,
|
|
522
522
|
delegationDecision,
|
|
523
|
-
|
|
523
|
+
startOfTurnPacket: renderedStartOfTurnPacket,
|
|
524
524
|
pendingMessages: pending.length > 0 ? pending.map((msg) => ({ from: msg.from, content: msg.content })) : undefined,
|
|
525
525
|
currentSessionKey: currentSession.key,
|
|
526
526
|
currentObligation,
|