@ouro.bot/cli 0.1.0-alpha.236 → 0.1.0-alpha.238
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 +5 -3
- package/dist/heart/config-registry.js +26 -27
- package/dist/heart/core.js +0 -17
- package/dist/heart/daemon/cli-render.js +129 -46
- package/dist/heart/daemon/daemon.js +4 -0
- package/dist/heart/daemon/socket-client.js +1 -0
- package/dist/nerves/coverage/file-completeness.js +2 -0
- package/dist/repertoire/ado-semantic.js +0 -5
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/duffel-client.js +185 -0
- package/dist/repertoire/guardrails.js +11 -2
- package/dist/repertoire/mcp-client.js +1 -0
- package/dist/repertoire/mcp-manager.js +53 -1
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tools-base.js +6 -0
- package/dist/repertoire/tools-config.js +6 -87
- package/dist/repertoire/tools-flight.js +224 -0
- package/dist/repertoire/tools-github.js +0 -1
- package/dist/repertoire/tools-stripe.js +180 -0
- package/dist/repertoire/tools-teams.js +0 -3
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools.js +1 -15
- package/dist/repertoire/user-profile.js +118 -0
- package/dist/senses/inner-dialog.js +0 -1
- package/dist/senses/teams.js +0 -48
- package/package.json +3 -2
- package/skills/agent-commerce.md +106 -0
- package/skills/commerce-setup-guide.md +116 -0
- package/skills/commerce-setup.md +84 -0
package/changelog.json
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
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
4
|
{
|
|
5
|
-
"version": "0.1.0-alpha.
|
|
5
|
+
"version": "0.1.0-alpha.238",
|
|
6
6
|
"changes": [
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"feat: pretty `ouro status` \u2014 ANSI colored output with box-drawing header, status dots, grouped senses/workers by agent. Added git sync info to overview.",
|
|
8
|
+
"fix: socket half-close \u2014 sendDaemonCommand now calls client.end() after writing, preventing intermittent connection hangs.",
|
|
9
|
+
"refactor: config tiers \u2014 replaced numeric T1/T2/T3 with `self` (agent-configurable) and `managed` (harness-only). All config keys are now agent-writable except `version` and `enabled`. mcpServers promoted to self.",
|
|
10
|
+
"refactor: removed confirmation system \u2014 deleted propose_config tool, confirmationRequired/confirmationAlwaysRequired/onConfirmAction from core, all tool definitions, and Teams sense. Was only wired up on Teams, silently failed everywhere else. -1,361 lines."
|
|
9
11
|
]
|
|
10
12
|
},
|
|
11
13
|
{
|
|
@@ -82,11 +82,11 @@ function validateObject(requiredFields) {
|
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
84
|
const registryData = [
|
|
85
|
-
// ---
|
|
85
|
+
// --- Managed: harness-only ---
|
|
86
86
|
{
|
|
87
87
|
path: "version",
|
|
88
|
-
tier:
|
|
89
|
-
description: "Agent config schema version. Must be an integer >= 1.",
|
|
88
|
+
tier: "managed",
|
|
89
|
+
description: "Agent config schema version. Managed by the harness. Must be an integer >= 1.",
|
|
90
90
|
default: 2,
|
|
91
91
|
effects: "Controls which config migrations apply on load. Changing incorrectly can corrupt config.",
|
|
92
92
|
topics: ["schema", "migration"],
|
|
@@ -94,25 +94,25 @@ const registryData = [
|
|
|
94
94
|
},
|
|
95
95
|
{
|
|
96
96
|
path: "enabled",
|
|
97
|
-
tier:
|
|
98
|
-
description: "Whether the agent is enabled. When false, the agent refuses to start.",
|
|
97
|
+
tier: "managed",
|
|
98
|
+
description: "Whether the agent is enabled. Managed by the harness. When false, the agent refuses to start.",
|
|
99
99
|
default: true,
|
|
100
100
|
effects: "Disables all agent functionality when set to false.",
|
|
101
101
|
topics: ["lifecycle", "activation"],
|
|
102
102
|
validate: validateBoolean,
|
|
103
103
|
},
|
|
104
|
+
// --- Self: agent-configurable ---
|
|
104
105
|
{
|
|
105
106
|
path: "mcpServers",
|
|
106
|
-
tier:
|
|
107
|
-
description: "MCP server configurations. Maps server name to command, args, and env.",
|
|
107
|
+
tier: "self",
|
|
108
|
+
description: "MCP server configurations. Maps server name to command, args, and env. The agent can add or remove MCP servers.",
|
|
108
109
|
default: undefined,
|
|
109
|
-
effects: "Adds or removes MCP tool servers.
|
|
110
|
+
effects: "Adds or removes MCP tool servers.",
|
|
110
111
|
topics: ["tools", "mcp", "servers", "extensions"],
|
|
111
112
|
},
|
|
112
|
-
// --- Tier 2: Proposal ---
|
|
113
113
|
{
|
|
114
114
|
path: "humanFacing.provider",
|
|
115
|
-
tier:
|
|
115
|
+
tier: "self",
|
|
116
116
|
description: "Provider for human-facing interactions (CLI, Teams, BlueBubbles).",
|
|
117
117
|
default: "anthropic",
|
|
118
118
|
effects: "Changes the LLM provider used for all human-facing conversations. Affects quality, latency, and cost.",
|
|
@@ -121,7 +121,7 @@ const registryData = [
|
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
123
|
path: "humanFacing.model",
|
|
124
|
-
tier:
|
|
124
|
+
tier: "self",
|
|
125
125
|
description: "Model name for human-facing interactions.",
|
|
126
126
|
default: "claude-opus-4-6",
|
|
127
127
|
effects: "Changes the specific model used for human-facing conversations.",
|
|
@@ -130,7 +130,7 @@ const registryData = [
|
|
|
130
130
|
},
|
|
131
131
|
{
|
|
132
132
|
path: "agentFacing.provider",
|
|
133
|
-
tier:
|
|
133
|
+
tier: "self",
|
|
134
134
|
description: "Provider for agent-facing interactions (inner dialog, delegation).",
|
|
135
135
|
default: "anthropic",
|
|
136
136
|
effects: "Changes the LLM provider used for inner dialog and agent-to-agent communication.",
|
|
@@ -139,7 +139,7 @@ const registryData = [
|
|
|
139
139
|
},
|
|
140
140
|
{
|
|
141
141
|
path: "agentFacing.model",
|
|
142
|
-
tier:
|
|
142
|
+
tier: "self",
|
|
143
143
|
description: "Model name for agent-facing interactions.",
|
|
144
144
|
default: "claude-opus-4-6",
|
|
145
145
|
effects: "Changes the specific model used for inner dialog and agent-to-agent communication.",
|
|
@@ -148,7 +148,7 @@ const registryData = [
|
|
|
148
148
|
},
|
|
149
149
|
{
|
|
150
150
|
path: "context.maxTokens",
|
|
151
|
-
tier:
|
|
151
|
+
tier: "self",
|
|
152
152
|
description: "Maximum context window size in tokens.",
|
|
153
153
|
default: 80000,
|
|
154
154
|
effects: "Larger values allow more context but increase cost and latency. Must match model capability.",
|
|
@@ -157,7 +157,7 @@ const registryData = [
|
|
|
157
157
|
},
|
|
158
158
|
{
|
|
159
159
|
path: "senses.cli",
|
|
160
|
-
tier:
|
|
160
|
+
tier: "self",
|
|
161
161
|
description: "CLI sense configuration. Controls whether the CLI interface is enabled.",
|
|
162
162
|
default: { enabled: true },
|
|
163
163
|
effects: "Enables or disables the CLI (terminal) interaction channel.",
|
|
@@ -166,7 +166,7 @@ const registryData = [
|
|
|
166
166
|
},
|
|
167
167
|
{
|
|
168
168
|
path: "senses.teams",
|
|
169
|
-
tier:
|
|
169
|
+
tier: "self",
|
|
170
170
|
description: "Teams sense configuration. Controls whether the Teams interface is enabled.",
|
|
171
171
|
default: { enabled: false },
|
|
172
172
|
effects: "Enables or disables the Microsoft Teams interaction channel.",
|
|
@@ -175,7 +175,7 @@ const registryData = [
|
|
|
175
175
|
},
|
|
176
176
|
{
|
|
177
177
|
path: "senses.bluebubbles",
|
|
178
|
-
tier:
|
|
178
|
+
tier: "self",
|
|
179
179
|
description: "BlueBubbles sense configuration. Controls whether the iMessage interface is enabled.",
|
|
180
180
|
default: { enabled: false },
|
|
181
181
|
effects: "Enables or disables the BlueBubbles (iMessage) interaction channel.",
|
|
@@ -184,7 +184,7 @@ const registryData = [
|
|
|
184
184
|
},
|
|
185
185
|
{
|
|
186
186
|
path: "sync.enabled",
|
|
187
|
-
tier:
|
|
187
|
+
tier: "self",
|
|
188
188
|
description: "Whether git-based bundle sync is enabled.",
|
|
189
189
|
default: false,
|
|
190
190
|
effects: "Enables automatic synchronization of agent state via git. Requires sync.remote to be configured.",
|
|
@@ -193,17 +193,16 @@ const registryData = [
|
|
|
193
193
|
},
|
|
194
194
|
{
|
|
195
195
|
path: "sync.remote",
|
|
196
|
-
tier:
|
|
196
|
+
tier: "self",
|
|
197
197
|
description: "Git remote name used for bundle sync.",
|
|
198
198
|
default: "origin",
|
|
199
199
|
effects: "Controls which git remote is used when sync is enabled.",
|
|
200
200
|
topics: ["sync", "git", "remote"],
|
|
201
201
|
validate: validateString,
|
|
202
202
|
},
|
|
203
|
-
// --- Tier 1: Self-service ---
|
|
204
203
|
{
|
|
205
204
|
path: "context.contextMargin",
|
|
206
|
-
tier:
|
|
205
|
+
tier: "self",
|
|
207
206
|
description: "Percentage of context window reserved as margin before compaction triggers.",
|
|
208
207
|
default: 20,
|
|
209
208
|
effects: "Higher values trigger compaction earlier, preserving more headroom. Lower values use more context.",
|
|
@@ -212,7 +211,7 @@ const registryData = [
|
|
|
212
211
|
},
|
|
213
212
|
{
|
|
214
213
|
path: "phrases.thinking",
|
|
215
|
-
tier:
|
|
214
|
+
tier: "self",
|
|
216
215
|
description: "Array of phrases displayed while the agent is thinking.",
|
|
217
216
|
default: ["working"],
|
|
218
217
|
effects: "Changes the thinking indicator text shown to users. Purely cosmetic.",
|
|
@@ -221,7 +220,7 @@ const registryData = [
|
|
|
221
220
|
},
|
|
222
221
|
{
|
|
223
222
|
path: "phrases.tool",
|
|
224
|
-
tier:
|
|
223
|
+
tier: "self",
|
|
225
224
|
description: "Array of phrases displayed while the agent is running a tool.",
|
|
226
225
|
default: ["running tool"],
|
|
227
226
|
effects: "Changes the tool-use indicator text shown to users. Purely cosmetic.",
|
|
@@ -230,7 +229,7 @@ const registryData = [
|
|
|
230
229
|
},
|
|
231
230
|
{
|
|
232
231
|
path: "phrases.followup",
|
|
233
|
-
tier:
|
|
232
|
+
tier: "self",
|
|
234
233
|
description: "Array of phrases displayed during follow-up processing.",
|
|
235
234
|
default: ["processing"],
|
|
236
235
|
effects: "Changes the follow-up indicator text shown to users. Purely cosmetic.",
|
|
@@ -239,7 +238,7 @@ const registryData = [
|
|
|
239
238
|
},
|
|
240
239
|
{
|
|
241
240
|
path: "shell.defaultTimeout",
|
|
242
|
-
tier:
|
|
241
|
+
tier: "self",
|
|
243
242
|
description: "Default timeout in milliseconds for shell command execution.",
|
|
244
243
|
default: undefined,
|
|
245
244
|
effects: "Controls how long shell commands run before timing out. Undefined uses system default.",
|
|
@@ -248,7 +247,7 @@ const registryData = [
|
|
|
248
247
|
},
|
|
249
248
|
{
|
|
250
249
|
path: "logging.level",
|
|
251
|
-
tier:
|
|
250
|
+
tier: "self",
|
|
252
251
|
description: "Minimum log level: debug, info, warn, or error.",
|
|
253
252
|
default: undefined,
|
|
254
253
|
effects: "Controls verbosity of runtime logging. Lower levels produce more output.",
|
|
@@ -257,7 +256,7 @@ const registryData = [
|
|
|
257
256
|
},
|
|
258
257
|
{
|
|
259
258
|
path: "logging.sinks",
|
|
260
|
-
tier:
|
|
259
|
+
tier: "self",
|
|
261
260
|
description: "Array of log sink types: 'terminal' and/or 'ndjson'.",
|
|
262
261
|
default: undefined,
|
|
263
262
|
effects: "Controls where log output is directed. Terminal shows in console, ndjson writes structured logs.",
|
package/dist/heart/core.js
CHANGED
|
@@ -905,23 +905,6 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
905
905
|
providerRuntime.appendToolOutput(tc.id, rejection);
|
|
906
906
|
continue;
|
|
907
907
|
}
|
|
908
|
-
// Confirmation check for mutate tools.
|
|
909
|
-
// confirmationAlwaysRequired tools cannot be bypassed by skipConfirmation.
|
|
910
|
-
const needsConfirmation = (0, tools_1.isConfirmationRequired)(tc.name) && (!options?.skipConfirmation || (0, tools_1.isConfirmationAlwaysRequired)(tc.name));
|
|
911
|
-
if (needsConfirmation) {
|
|
912
|
-
let decision = "denied";
|
|
913
|
-
if (callbacks.onConfirmAction) {
|
|
914
|
-
decision = await callbacks.onConfirmAction(tc.name, args);
|
|
915
|
-
}
|
|
916
|
-
if (decision !== "confirmed") {
|
|
917
|
-
const cancelled = "Action cancelled by user.";
|
|
918
|
-
callbacks.onToolStart(tc.name, args);
|
|
919
|
-
callbacks.onToolEnd(tc.name, argSummary, false);
|
|
920
|
-
messages.push({ role: "tool", tool_call_id: tc.id, content: cancelled });
|
|
921
|
-
providerRuntime.appendToolOutput(tc.id, cancelled);
|
|
922
|
-
continue;
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
908
|
callbacks.onToolStart(tc.name, args);
|
|
926
909
|
let toolResult;
|
|
927
910
|
let success;
|
|
@@ -89,6 +89,8 @@ function parseStatusPayload(data) {
|
|
|
89
89
|
senseCount: numberField(overview.senseCount) ?? 0,
|
|
90
90
|
entryPath: stringField(overview.entryPath) ?? "unknown",
|
|
91
91
|
mode: stringField(overview.mode) ?? "unknown",
|
|
92
|
+
syncEnabled: booleanField(overview.syncEnabled) ?? false,
|
|
93
|
+
syncRemote: stringField(overview.syncRemote) ?? "origin",
|
|
92
94
|
};
|
|
93
95
|
const parsedSenses = senses.map((entry) => {
|
|
94
96
|
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
@@ -141,6 +143,44 @@ function parseStatusPayload(data) {
|
|
|
141
143
|
workers: parsedWorkers,
|
|
142
144
|
};
|
|
143
145
|
}
|
|
146
|
+
// ── ANSI color helpers (private) ──
|
|
147
|
+
const RESET = "\x1b[0m";
|
|
148
|
+
const BOLD = "\x1b[1m";
|
|
149
|
+
const DIM = "\x1b[2m";
|
|
150
|
+
const TEAL = "\x1b[38;2;78;201;176m";
|
|
151
|
+
const GREEN = "\x1b[38;2;46;204;64m";
|
|
152
|
+
const RED = "\x1b[38;2;231;76;60m";
|
|
153
|
+
const YELLOW = "\x1b[38;2;230;190;50m";
|
|
154
|
+
/* v8 ignore start -- cosmetic ANSI wrappers @preserve */
|
|
155
|
+
function teal(text) { return `${TEAL}${text}${RESET}`; }
|
|
156
|
+
function green(text) { return `${GREEN}${text}${RESET}`; }
|
|
157
|
+
function red(text) { return `${RED}${text}${RESET}`; }
|
|
158
|
+
function yellow(text) { return `${YELLOW}${text}${RESET}`; }
|
|
159
|
+
function bold(text) { return `${BOLD}${text}${RESET}`; }
|
|
160
|
+
function dim(text) { return `${DIM}${text}${RESET}`; }
|
|
161
|
+
/* v8 ignore stop */
|
|
162
|
+
/* v8 ignore start -- cosmetic display: status dot color mapping tested visually @preserve */
|
|
163
|
+
function statusDot(status) {
|
|
164
|
+
switch (status) {
|
|
165
|
+
case "running":
|
|
166
|
+
case "ok":
|
|
167
|
+
case "interactive":
|
|
168
|
+
case "enabled":
|
|
169
|
+
return green("●");
|
|
170
|
+
case "crashed":
|
|
171
|
+
case "warn":
|
|
172
|
+
case "error":
|
|
173
|
+
return red("●");
|
|
174
|
+
case "needs_config":
|
|
175
|
+
return yellow("●");
|
|
176
|
+
case "disabled":
|
|
177
|
+
case "stopped":
|
|
178
|
+
return dim("○");
|
|
179
|
+
default:
|
|
180
|
+
return dim("●");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/* v8 ignore stop */
|
|
144
184
|
// ── Formatters ──
|
|
145
185
|
function humanizeSenseName(sense, label) {
|
|
146
186
|
if (label)
|
|
@@ -153,6 +193,7 @@ function humanizeSenseName(sense, label) {
|
|
|
153
193
|
return "Teams";
|
|
154
194
|
return sense;
|
|
155
195
|
}
|
|
196
|
+
/* v8 ignore start -- utility formatter; retained for non-status table output @preserve */
|
|
156
197
|
function formatTable(headers, rows) {
|
|
157
198
|
const widths = headers.map((header, index) => Math.max(header.length, ...rows.map((row) => row[index].length)));
|
|
158
199
|
const renderRow = (row) => `| ${row.map((cell, index) => (index === row.length - 1
|
|
@@ -165,56 +206,96 @@ function formatTable(headers, rows) {
|
|
|
165
206
|
...rows.map(renderRow),
|
|
166
207
|
].join("\n");
|
|
167
208
|
}
|
|
209
|
+
/* v8 ignore stop */
|
|
168
210
|
function formatDaemonStatusOutput(response, fallback) {
|
|
169
211
|
const payload = parseStatusPayload(response.data);
|
|
170
212
|
if (!payload)
|
|
171
213
|
return fallback;
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
214
|
+
const ov = payload.overview;
|
|
215
|
+
const lines = [];
|
|
216
|
+
// ── Header banner ──
|
|
217
|
+
const modeTag = ov.mode === "dev" ? "dev" : "";
|
|
218
|
+
const daemonStatus = ov.daemon === "running" ? green("●") + " " + bold("running") : red("●") + " " + bold(ov.daemon);
|
|
219
|
+
const modeStr = modeTag ? ` ${dim(`(${modeTag})`)}` : "";
|
|
220
|
+
const bannerContent = ` ${bold(ov.version)} ${daemonStatus}${modeStr} `;
|
|
221
|
+
// Measure raw content width (strip ANSI for width calc)
|
|
222
|
+
const rawBanner = bannerContent.replace(/\x1b\[[0-9;]*m/g, "");
|
|
223
|
+
const bannerWidth = Math.max(rawBanner.length, 42);
|
|
224
|
+
const titleLabel = " ouroboros daemon ";
|
|
225
|
+
const topRightPad = Math.max(0, bannerWidth - titleLabel.length - 2);
|
|
226
|
+
lines.push(` ${teal("╭─")}${teal(titleLabel)}${teal("─".repeat(topRightPad))}${teal("╮")}`);
|
|
227
|
+
lines.push(` ${teal("│")}${bannerContent}${" ".repeat(Math.max(0, bannerWidth - rawBanner.length))}${teal("│")}`);
|
|
228
|
+
lines.push(` ${teal("╰")}${teal("─".repeat(bannerWidth))}${teal("╯")}`);
|
|
229
|
+
lines.push("");
|
|
230
|
+
// ── Key-value overview ──
|
|
231
|
+
const kvLine = (label, value) => ` ${teal(label.padEnd(11))} ${value}`;
|
|
232
|
+
lines.push(kvLine("Socket", ov.socketPath));
|
|
233
|
+
lines.push(kvLine("Outlook", ov.outlookUrl));
|
|
234
|
+
lines.push(kvLine("Health", `${statusDot(ov.health)} ${ov.health}`));
|
|
235
|
+
/* v8 ignore next -- cosmetic: sync-enabled branch requires agent with sync configured @preserve */
|
|
236
|
+
const syncLabel = ov.syncEnabled ? `${green("●")} enabled (remote: ${ov.syncRemote})` : `${dim("○")} disabled`;
|
|
237
|
+
lines.push(kvLine("Git Sync", syncLabel));
|
|
238
|
+
lines.push(kvLine("Updated", ov.lastUpdated));
|
|
239
|
+
lines.push("");
|
|
240
|
+
// ── Senses ──
|
|
241
|
+
if (payload.senses.length > 0) {
|
|
242
|
+
lines.push(` ${teal("──")} ${bold("Senses")} ${teal("─".repeat(37))}`);
|
|
243
|
+
// Group senses by agent
|
|
244
|
+
const sensesByAgent = new Map();
|
|
245
|
+
for (const row of payload.senses) {
|
|
246
|
+
const list = sensesByAgent.get(row.agent) ?? [];
|
|
247
|
+
list.push(row);
|
|
248
|
+
sensesByAgent.set(row.agent, list);
|
|
249
|
+
}
|
|
250
|
+
// Calculate column widths for alignment
|
|
251
|
+
const allSenseNames = payload.senses.map((r) => humanizeSenseName(r.sense, r.label));
|
|
252
|
+
const nameWidth = Math.max(12, ...allSenseNames.map((n) => n.length));
|
|
253
|
+
const allStatuses = payload.senses.map((r) => r.status);
|
|
254
|
+
const statusWidth = Math.max(10, ...allStatuses.map((s) => s.length));
|
|
255
|
+
for (const [agent, rows] of sensesByAgent) {
|
|
256
|
+
lines.push(` ${bold(agent)}`);
|
|
257
|
+
for (const row of rows) {
|
|
258
|
+
const name = humanizeSenseName(row.sense, row.label).padEnd(nameWidth);
|
|
259
|
+
const dot = row.enabled ? statusDot(row.status) : dim("○");
|
|
260
|
+
const statusText = (row.enabled ? row.status : "disabled").padEnd(statusWidth);
|
|
261
|
+
lines.push(` ${name} ${dot} ${statusText} ${dim(row.detail)}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
lines.push("");
|
|
265
|
+
}
|
|
266
|
+
// ── Workers ──
|
|
267
|
+
if (payload.workers.length > 0) {
|
|
268
|
+
lines.push(` ${teal("──")} ${bold("Workers")} ${teal("─".repeat(36))}`);
|
|
269
|
+
// Group workers by agent
|
|
270
|
+
const workersByAgent = new Map();
|
|
271
|
+
for (const row of payload.workers) {
|
|
272
|
+
const list = workersByAgent.get(row.agent) ?? [];
|
|
273
|
+
list.push(row);
|
|
274
|
+
workersByAgent.set(row.agent, list);
|
|
275
|
+
}
|
|
276
|
+
const allWorkerNames = payload.workers.map((r) => r.worker);
|
|
277
|
+
const workerNameWidth = Math.max(12, ...allWorkerNames.map((n) => n.length));
|
|
278
|
+
for (const [agent, rows] of workersByAgent) {
|
|
279
|
+
lines.push(` ${bold(agent)}`);
|
|
280
|
+
for (const row of rows) {
|
|
281
|
+
const name = row.worker.padEnd(workerNameWidth);
|
|
282
|
+
const dot = statusDot(row.status);
|
|
283
|
+
const pidStr = row.pid !== null ? `pid ${row.pid}` : "";
|
|
284
|
+
const restartStr = `restarts: ${row.restartCount}`;
|
|
285
|
+
/* v8 ignore start — exit info branches tested via daemon-crash-context; v8 misreports conditional chains @preserve */
|
|
286
|
+
let exitStr = "";
|
|
287
|
+
if (row.lastExitCode !== null)
|
|
288
|
+
exitStr = `exit=${row.lastExitCode}`;
|
|
289
|
+
if (row.lastSignal !== null)
|
|
290
|
+
exitStr = row.lastExitCode !== null ? `exit=${row.lastExitCode} sig=${row.lastSignal}` : `sig=${row.lastSignal}`;
|
|
291
|
+
/* v8 ignore stop */
|
|
292
|
+
const details = [pidStr, restartStr, exitStr].filter(Boolean).join(" ");
|
|
293
|
+
lines.push(` ${name} ${dot} ${row.status.padEnd(10)} ${dim(details)}`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
lines.push("");
|
|
297
|
+
}
|
|
298
|
+
return lines.join("\n");
|
|
218
299
|
}
|
|
219
300
|
function formatVersionOutput() {
|
|
220
301
|
const version = (0, runtime_metadata_1.getRuntimeMetadata)().version;
|
|
@@ -240,6 +321,8 @@ function buildStoppedStatusPayload(socketPath) {
|
|
|
240
321
|
senseCount: 0,
|
|
241
322
|
entryPath: path.join(repoRoot, "dist", "heart", "daemon", "daemon-entry.js"),
|
|
242
323
|
mode: (0, runtime_mode_1.detectRuntimeMode)(repoRoot),
|
|
324
|
+
syncEnabled: false,
|
|
325
|
+
syncRemote: "origin",
|
|
243
326
|
},
|
|
244
327
|
senses: [],
|
|
245
328
|
workers: [],
|
|
@@ -42,6 +42,7 @@ const net = __importStar(require("net"));
|
|
|
42
42
|
const os = __importStar(require("os"));
|
|
43
43
|
const path = __importStar(require("path"));
|
|
44
44
|
const identity_1 = require("../identity");
|
|
45
|
+
const config_1 = require("../config");
|
|
45
46
|
const runtime_1 = require("../../nerves/runtime");
|
|
46
47
|
const runtime_metadata_1 = require("./runtime-metadata");
|
|
47
48
|
const runtime_mode_1 = require("./runtime-mode");
|
|
@@ -233,6 +234,7 @@ class OuroDaemon {
|
|
|
233
234
|
const workers = buildWorkerRows(snapshots);
|
|
234
235
|
const senses = this.senseManager?.listSenseRows() ?? [];
|
|
235
236
|
const repoRoot = (0, identity_1.getRepoRoot)();
|
|
237
|
+
const syncConfig = (0, config_1.getSyncConfig)();
|
|
236
238
|
return {
|
|
237
239
|
overview: {
|
|
238
240
|
daemon: "running",
|
|
@@ -244,6 +246,8 @@ class OuroDaemon {
|
|
|
244
246
|
senseCount: senses.length,
|
|
245
247
|
entryPath: path.join(repoRoot, "dist", "heart", "daemon", "daemon-entry.js"),
|
|
246
248
|
mode: (0, runtime_mode_1.detectRuntimeMode)(repoRoot),
|
|
249
|
+
syncEnabled: syncConfig.enabled,
|
|
250
|
+
syncRemote: syncConfig.remote,
|
|
247
251
|
},
|
|
248
252
|
workers,
|
|
249
253
|
senses,
|
|
@@ -67,6 +67,8 @@ const DISPATCH_EXEMPT_PATTERNS = [
|
|
|
67
67
|
"repertoire/api-client",
|
|
68
68
|
"repertoire/github-client",
|
|
69
69
|
"mind/embedding-provider",
|
|
70
|
+
// Commerce utility module: error classes and pure helpers (no independent side effects).
|
|
71
|
+
"repertoire/commerce-errors",
|
|
70
72
|
];
|
|
71
73
|
function isDispatchExempt(filePath) {
|
|
72
74
|
return DISPATCH_EXEMPT_PATTERNS.some((pattern) => filePath.includes(pattern));
|
|
@@ -318,7 +318,6 @@ exports.adoSemanticToolDefinitions = [
|
|
|
318
318
|
return result;
|
|
319
319
|
},
|
|
320
320
|
integration: "ado",
|
|
321
|
-
confirmationRequired: true,
|
|
322
321
|
summaryKeys: ["organization", "project", "title"],
|
|
323
322
|
},
|
|
324
323
|
// -- ado_create_issue --
|
|
@@ -360,7 +359,6 @@ exports.adoSemanticToolDefinitions = [
|
|
|
360
359
|
return result;
|
|
361
360
|
},
|
|
362
361
|
integration: "ado",
|
|
363
|
-
confirmationRequired: true,
|
|
364
362
|
summaryKeys: ["organization", "project", "title"],
|
|
365
363
|
},
|
|
366
364
|
// -- ado_move_items --
|
|
@@ -415,7 +413,6 @@ exports.adoSemanticToolDefinitions = [
|
|
|
415
413
|
return JSON.stringify({ moved, errors, organization: adoCtx.organization, project: adoCtx.project });
|
|
416
414
|
},
|
|
417
415
|
integration: "ado",
|
|
418
|
-
confirmationRequired: true,
|
|
419
416
|
summaryKeys: ["organization", "project", "workItemIds"],
|
|
420
417
|
},
|
|
421
418
|
// -- ado_restructure_backlog --
|
|
@@ -474,7 +471,6 @@ exports.adoSemanticToolDefinitions = [
|
|
|
474
471
|
return JSON.stringify({ results, organization: adoCtx.organization, project: adoCtx.project });
|
|
475
472
|
},
|
|
476
473
|
integration: "ado",
|
|
477
|
-
confirmationRequired: true,
|
|
478
474
|
summaryKeys: ["organization", "project"],
|
|
479
475
|
},
|
|
480
476
|
// -- ado_validate_structure --
|
|
@@ -676,7 +672,6 @@ exports.adoSemanticToolDefinitions = [
|
|
|
676
672
|
return JSON.stringify({ results, organization: adoCtx.organization, project: adoCtx.project });
|
|
677
673
|
},
|
|
678
674
|
integration: "ado",
|
|
679
|
-
confirmationRequired: true,
|
|
680
675
|
summaryKeys: ["organization", "project"],
|
|
681
676
|
},
|
|
682
677
|
// -- ado_detect_orphans --
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Commerce-specific error types and helpers.
|
|
4
|
+
*
|
|
5
|
+
* These errors carry structured `code` and `meta` fields for nerves event
|
|
6
|
+
* compatibility and provide patterns for common commerce failure modes:
|
|
7
|
+
* retry, price change detection, and partial failure reporting.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.ProfileError = exports.BookingError = exports.PaymentError = exports.CommerceError = void 0;
|
|
11
|
+
exports.retryOnce = retryOnce;
|
|
12
|
+
exports.priceChangeGuard = priceChangeGuard;
|
|
13
|
+
exports.partialFailureReport = partialFailureReport;
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Error types
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
class CommerceError extends Error {
|
|
18
|
+
code;
|
|
19
|
+
meta;
|
|
20
|
+
constructor(message, code, meta = {}) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = "CommerceError";
|
|
23
|
+
this.code = code;
|
|
24
|
+
this.meta = meta;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.CommerceError = CommerceError;
|
|
28
|
+
class PaymentError extends CommerceError {
|
|
29
|
+
constructor(message, code, meta = {}) {
|
|
30
|
+
super(message, code, meta);
|
|
31
|
+
this.name = "PaymentError";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.PaymentError = PaymentError;
|
|
35
|
+
class BookingError extends CommerceError {
|
|
36
|
+
constructor(message, code, meta = {}) {
|
|
37
|
+
super(message, code, meta);
|
|
38
|
+
this.name = "BookingError";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.BookingError = BookingError;
|
|
42
|
+
class ProfileError extends CommerceError {
|
|
43
|
+
constructor(message, code, meta = {}) {
|
|
44
|
+
super(message, code, meta);
|
|
45
|
+
this.name = "ProfileError";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.ProfileError = ProfileError;
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Transient error detection
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
const TRANSIENT_CODES = new Set(["COMMERCE_TRANSIENT", "COMMERCE_TIMEOUT", "COMMERCE_NETWORK"]);
|
|
53
|
+
function isTransient(err) {
|
|
54
|
+
return err instanceof CommerceError && TRANSIENT_CODES.has(err.code);
|
|
55
|
+
}
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// Helpers
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
/**
|
|
60
|
+
* Retry a function once on transient failure, then throw.
|
|
61
|
+
* Non-transient errors are thrown immediately without retry.
|
|
62
|
+
*/
|
|
63
|
+
async function retryOnce(fn) {
|
|
64
|
+
try {
|
|
65
|
+
return await fn();
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
if (!isTransient(err))
|
|
69
|
+
throw err;
|
|
70
|
+
// One retry
|
|
71
|
+
return fn();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Throw PaymentError if the actual price differs from the approved price
|
|
76
|
+
* by more than 5%. Used to protect against price changes between search
|
|
77
|
+
* and booking.
|
|
78
|
+
*/
|
|
79
|
+
function priceChangeGuard(approved, actual) {
|
|
80
|
+
if (approved === 0 && actual === 0)
|
|
81
|
+
return;
|
|
82
|
+
if (approved === 0) {
|
|
83
|
+
throw new PaymentError(`price changed from $0 to $${actual} — cannot verify percentage change`, "PAYMENT_PRICE_CHANGED", { approved, actual });
|
|
84
|
+
}
|
|
85
|
+
const delta = Math.abs(actual - approved) / approved;
|
|
86
|
+
if (delta > 0.05) {
|
|
87
|
+
throw new PaymentError(`price changed by ${(delta * 100).toFixed(1)}% (approved: $${approved}, actual: $${actual})`, "PAYMENT_PRICE_CHANGED", { approved, actual, deltaPercent: delta * 100 });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Format a human-readable status report for multi-service booking attempts.
|
|
92
|
+
* Each entry has a service name, status, and optional error message.
|
|
93
|
+
*/
|
|
94
|
+
function partialFailureReport(results) {
|
|
95
|
+
if (results.length === 0) {
|
|
96
|
+
return "no services attempted.";
|
|
97
|
+
}
|
|
98
|
+
const lines = results.map((r) => {
|
|
99
|
+
const status = r.status === "success" ? "success" : "failed";
|
|
100
|
+
const detail = r.error ? ` — ${r.error}` : "";
|
|
101
|
+
return ` ${r.service}: ${status}${detail}`;
|
|
102
|
+
});
|
|
103
|
+
const succeeded = results.filter((r) => r.status === "success").length;
|
|
104
|
+
const failed = results.length - succeeded;
|
|
105
|
+
return [
|
|
106
|
+
`booking status: ${succeeded} succeeded, ${failed} failed`,
|
|
107
|
+
...lines,
|
|
108
|
+
].join("\n");
|
|
109
|
+
}
|