@adaptic/maestro 1.8.1 → 1.8.3
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/bin/maestro.mjs +15 -3
- package/package.json +1 -1
- package/plugins/maestro-skills/skills/board-deck.md +2 -2
- package/plugins/maestro-skills/skills/decision-brief.md +6 -6
- package/plugins/maestro-skills/skills/draft-comms.md +9 -9
- package/plugins/maestro-skills/skills/evening-wrap.md +2 -2
- package/plugins/maestro-skills/skills/hiring-triage.md +4 -4
- package/plugins/maestro-skills/skills/inbox-triage.md +5 -5
- package/plugins/maestro-skills/skills/morning-brief.md +4 -4
- package/plugins/maestro-skills/skills/pipeline-review.md +2 -2
- package/plugins/maestro-skills/skills/regulatory-status.md +2 -2
- package/plugins/maestro-skills/skills/schedule-meeting.md +3 -3
- package/plugins/maestro-skills/skills/slack-followup.md +5 -5
- package/plugins/maestro-skills/skills/weekly-memo.md +5 -5
- package/scaffold/CLAUDE.md +21 -0
- package/scripts/daemon/classifier.mjs +21 -5
- package/scripts/daemon/maestro-daemon.mjs +46 -7
- package/scripts/hooks/block-mcp-slack-send.sh +1 -1
- package/scripts/huddle/audio-bridge.mjs +17 -17
- package/scripts/huddle/boot-slack-cdp.sh +1 -1
- package/scripts/huddle/huddle-controller.mjs +3 -3
- package/scripts/huddle/huddle-server.mjs +21 -7
- package/scripts/huddle/launch-slack.sh +2 -2
- package/scripts/huddle/package-lock.json +2 -2
- package/scripts/huddle/package.json +2 -2
- package/scripts/huddle/setup-audio.sh +6 -6
- package/scripts/huddle/start-call.mjs +2 -2
- package/scripts/huddle/test-pipeline.mjs +2 -2
- package/scripts/local-triggers/generate-plists.sh +15 -1
- package/scripts/local-triggers/generate-plists.test.mjs +9 -2
- package/scripts/parse-voice-transcript.mjs +4 -9
- package/scripts/poller/gmail-poller.mjs +8 -2
- package/scripts/poller/intra-session-check.mjs +4 -3
- package/scripts/poller-launchd/install.sh +48 -10
- package/scripts/pre_draft_lookup.py +2 -2
- package/scripts/self-optimization/compute-metrics.py +23 -2
- package/scripts/setup/boot-claude-session.sh +14 -5
- package/scripts/setup/render-environment-yaml.mjs +133 -0
- package/scripts/watchdog/ai.maestro.memory-watchdog.plist +3 -3
- package/scripts/watchdog/force-reboot.sh +3 -3
- package/scripts/watchdog/memory-watchdog.sh +11 -5
- package/workflows/daily/applicant-triage.yaml +3 -3
- package/workflows/daily/comms-triage.yaml +1 -1
- package/workflows/daily/morning-brief.yaml +1 -1
- package/workflows/daily/slack-followup-sweep.yaml +1 -1
- package/workflows/weekly/hiring-review.yaml +3 -3
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* render-environment-yaml.mjs
|
|
4
|
+
*
|
|
5
|
+
* Derive `config/environment.yaml` from `config/agent.json` (the SOT).
|
|
6
|
+
* Replaces the previously-static, agent-name-hardcoded environment.yaml so
|
|
7
|
+
* that operator_persona / operator_role / ceo / hostname / paths / agent
|
|
8
|
+
* blocks always reflect what agent.json says.
|
|
9
|
+
*
|
|
10
|
+
* Called by `maestro upgrade` (post-merge) and safe to run by hand:
|
|
11
|
+
*
|
|
12
|
+
* node scripts/setup/render-environment-yaml.mjs # write
|
|
13
|
+
* node scripts/setup/render-environment-yaml.mjs --dry-run # preview
|
|
14
|
+
* node scripts/setup/render-environment-yaml.mjs --check # exit 1 if drift
|
|
15
|
+
*
|
|
16
|
+
* Output is canonical (deterministic). If the existing file already matches,
|
|
17
|
+
* nothing is written. The first 5 lines of the rendered file always include
|
|
18
|
+
* a `# regenerated-from: config/agent.json` banner so operators editing it
|
|
19
|
+
* directly know they're editing a derived file.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
23
|
+
import { resolve, join } from "node:path";
|
|
24
|
+
|
|
25
|
+
const AGENT_DIR = process.env.AGENT_ROOT || process.env.AGENT_DIR || process.cwd();
|
|
26
|
+
const AGENT_JSON = join(AGENT_DIR, "config", "agent.json");
|
|
27
|
+
const TARGET = join(AGENT_DIR, "config", "environment.yaml");
|
|
28
|
+
|
|
29
|
+
function fail(msg) {
|
|
30
|
+
process.stderr.write(`[render-environment-yaml] ${msg}\n`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!existsSync(AGENT_JSON)) {
|
|
35
|
+
fail(`config/agent.json not found at ${AGENT_JSON} — nothing to derive from.`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let agent;
|
|
39
|
+
try {
|
|
40
|
+
agent = JSON.parse(readFileSync(AGENT_JSON, "utf-8"));
|
|
41
|
+
} catch (err) {
|
|
42
|
+
fail(`failed to parse agent.json: ${err.message}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Render a stable, predictable YAML. We don't use a YAML library to keep
|
|
46
|
+
// this script dep-free and to control the exact formatting & comment
|
|
47
|
+
// placement. Mind the trailing newline on every value.
|
|
48
|
+
function quote(s) {
|
|
49
|
+
if (typeof s !== "string") return `"${s ?? ""}"`;
|
|
50
|
+
// No double-quotes in agent identity strings expected; if any appear,
|
|
51
|
+
// escape them properly.
|
|
52
|
+
return `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function yamlValue(v) {
|
|
56
|
+
if (v === null || v === undefined) return '""';
|
|
57
|
+
if (typeof v === "number" || typeof v === "boolean") return String(v);
|
|
58
|
+
return quote(String(v));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function render(agent) {
|
|
62
|
+
const repoSlug = agent.repoSlug || `${(agent.firstName || "agent").toLowerCase()}-ai`;
|
|
63
|
+
const home = `~/${repoSlug}`;
|
|
64
|
+
const lines = [
|
|
65
|
+
"# Environment Configuration",
|
|
66
|
+
"# regenerated-from: config/agent.json by scripts/setup/render-environment-yaml.mjs",
|
|
67
|
+
"# DO NOT EDIT BY HAND — changes will be overwritten on every upgrade.",
|
|
68
|
+
"# Edit config/agent.json (the SOT) and re-run the renderer instead.",
|
|
69
|
+
"",
|
|
70
|
+
"system:",
|
|
71
|
+
` name: maestro`,
|
|
72
|
+
` version: 1.0.0`,
|
|
73
|
+
` operator_persona: ${yamlValue(agent.fullName)}`,
|
|
74
|
+
` operator_role: ${yamlValue(agent.title)}`,
|
|
75
|
+
` company: ${yamlValue(agent.company)}`,
|
|
76
|
+
` ceo: ${yamlValue(agent.principal?.fullName || "")}`,
|
|
77
|
+
` timezone: ${yamlValue(agent.timezone || "UTC")}`,
|
|
78
|
+
"",
|
|
79
|
+
"machine:",
|
|
80
|
+
` type: mac-mini`,
|
|
81
|
+
` os: macOS`,
|
|
82
|
+
` hostname: ${yamlValue(agent.machineName || "")}`,
|
|
83
|
+
` purpose: Autonomous agent operations node`,
|
|
84
|
+
"",
|
|
85
|
+
"paths:",
|
|
86
|
+
` agent_home: ${home}`,
|
|
87
|
+
` logs: ${home}/logs`,
|
|
88
|
+
` outputs: ${home}/outputs`,
|
|
89
|
+
"",
|
|
90
|
+
"agent:",
|
|
91
|
+
` name: ${yamlValue(agent.fullName)}`,
|
|
92
|
+
` email: ${yamlValue(agent.email)}`,
|
|
93
|
+
` phone: ${yamlValue(agent.phone || "")}`,
|
|
94
|
+
"",
|
|
95
|
+
];
|
|
96
|
+
return lines.join("\n");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const rendered = render(agent);
|
|
100
|
+
const args = process.argv.slice(2);
|
|
101
|
+
const dryRun = args.includes("--dry-run") || args.includes("-n");
|
|
102
|
+
const check = args.includes("--check");
|
|
103
|
+
|
|
104
|
+
if (check) {
|
|
105
|
+
if (!existsSync(TARGET)) {
|
|
106
|
+
process.stdout.write("config/environment.yaml missing\n");
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
const current = readFileSync(TARGET, "utf-8");
|
|
110
|
+
if (current === rendered) {
|
|
111
|
+
process.stdout.write("environment.yaml is up to date with agent.json\n");
|
|
112
|
+
process.exit(0);
|
|
113
|
+
}
|
|
114
|
+
process.stdout.write("environment.yaml drifts from agent.json — rerun the renderer\n");
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (dryRun) {
|
|
119
|
+
process.stdout.write(rendered);
|
|
120
|
+
process.exit(0);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Skip the write if already up to date — keeps `upgrade` idempotent.
|
|
124
|
+
if (existsSync(TARGET)) {
|
|
125
|
+
const current = readFileSync(TARGET, "utf-8");
|
|
126
|
+
if (current === rendered) {
|
|
127
|
+
process.stdout.write("environment.yaml already up to date\n");
|
|
128
|
+
process.exit(0);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
writeFileSync(TARGET, rendered);
|
|
133
|
+
process.stdout.write(`environment.yaml regenerated from agent.json (${rendered.split("\n").length} lines)\n`);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<key>ProgramArguments</key>
|
|
9
9
|
<array>
|
|
10
10
|
<string>/bin/bash</string>
|
|
11
|
-
<string
|
|
11
|
+
<string>__AGENT_DIR__/scripts/watchdog/memory-watchdog.sh</string>
|
|
12
12
|
</array>
|
|
13
13
|
|
|
14
14
|
<key>StartInterval</key>
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
<true/>
|
|
19
19
|
|
|
20
20
|
<key>StandardOutPath</key>
|
|
21
|
-
<string
|
|
21
|
+
<string>__AGENT_DIR__/logs/watchdog/launchd-stdout.log</string>
|
|
22
22
|
|
|
23
23
|
<key>StandardErrorPath</key>
|
|
24
|
-
<string
|
|
24
|
+
<string>__AGENT_DIR__/logs/watchdog/launchd-stderr.log</string>
|
|
25
25
|
|
|
26
26
|
<key>EnvironmentVariables</key>
|
|
27
27
|
<dict>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# ./scripts/watchdog/force-reboot.sh --status # Check heartbeat and uptime
|
|
13
13
|
#
|
|
14
14
|
# Remote usage (from another machine via SSH):
|
|
15
|
-
# ssh
|
|
15
|
+
# ssh <user>@<mac-mini> "~/maestro/scripts/watchdog/force-reboot.sh --graceful"
|
|
16
16
|
#
|
|
17
17
|
# =============================================================================
|
|
18
18
|
#
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
# - With `pmset autorestart 1`, it will boot automatically
|
|
44
44
|
#
|
|
45
45
|
# 5. SSH + SYSDIAGNOSE RESET (partial freeze, SSH still works):
|
|
46
|
-
# - ssh
|
|
47
|
-
# - Or: ssh
|
|
46
|
+
# - ssh <user>@<mac-mini> "sudo reboot"
|
|
47
|
+
# - Or: ssh <user>@<mac-mini> "sudo shutdown -r now"
|
|
48
48
|
#
|
|
49
49
|
# =============================================================================
|
|
50
50
|
|
|
@@ -26,8 +26,9 @@ set -euo pipefail
|
|
|
26
26
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
27
27
|
MAESTRO_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
28
28
|
|
|
29
|
-
# Auto-detect agent directory (
|
|
30
|
-
# The watchdog runs from maestro but protects the whole machine
|
|
29
|
+
# Auto-detect agent directory (any ~/<name>-ai or override via AGENT_DIR env).
|
|
30
|
+
# The watchdog runs from maestro but protects the whole machine — agents are
|
|
31
|
+
# discovered dynamically rather than hardcoded.
|
|
31
32
|
AGENT_DIR="${AGENT_DIR:-}"
|
|
32
33
|
|
|
33
34
|
# --- Ensure system paths are available ----------------------------------------
|
|
@@ -203,9 +204,14 @@ trigger_emergency_stop() {
|
|
|
203
204
|
echo "$reason" > "$stop_file"
|
|
204
205
|
log_event "critical" "emergency_stop_triggered" "$reason"
|
|
205
206
|
|
|
206
|
-
# Also create in any detected agent
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
# Also create in any detected agent directory. Discovers ~/<name>-ai
|
|
208
|
+
# directories dynamically so the watchdog protects every agent on this
|
|
209
|
+
# machine without a hardcoded list. AGENT_DIR override still wins.
|
|
210
|
+
if [ -n "$AGENT_DIR" ] && [ -d "$AGENT_DIR" ]; then
|
|
211
|
+
echo "$reason" > "$AGENT_DIR/.emergency-stop" 2>/dev/null || true
|
|
212
|
+
fi
|
|
213
|
+
for agent_dir in "$HOME"/*-ai; do
|
|
214
|
+
if [ -d "$agent_dir" ] && [ -f "$agent_dir/config/agent.json" ]; then
|
|
209
215
|
echo "$reason" > "$agent_dir/.emergency-stop" 2>/dev/null || true
|
|
210
216
|
fi
|
|
211
217
|
done
|
|
@@ -107,7 +107,7 @@ steps:
|
|
|
107
107
|
classification: outputs/briefs/daily/{date}/candidate-classification.md
|
|
108
108
|
category: advance
|
|
109
109
|
tone: warm_professional
|
|
110
|
-
voice:
|
|
110
|
+
voice: agent
|
|
111
111
|
sla: 24h
|
|
112
112
|
outputs:
|
|
113
113
|
file: outputs/comms/hiring/{date}/daily-advance-drafts.md
|
|
@@ -123,7 +123,7 @@ steps:
|
|
|
123
123
|
classification: outputs/briefs/daily/{date}/candidate-classification.md
|
|
124
124
|
category: hold
|
|
125
125
|
tone: professional
|
|
126
|
-
voice:
|
|
126
|
+
voice: agent
|
|
127
127
|
outputs:
|
|
128
128
|
file: outputs/comms/hiring/{date}/daily-hold-drafts.md
|
|
129
129
|
|
|
@@ -138,7 +138,7 @@ steps:
|
|
|
138
138
|
classification: outputs/briefs/daily/{date}/candidate-classification.md
|
|
139
139
|
category: reject
|
|
140
140
|
tone: kind_encouraging
|
|
141
|
-
voice:
|
|
141
|
+
voice: agent
|
|
142
142
|
sla: 48h
|
|
143
143
|
outputs:
|
|
144
144
|
file: outputs/comms/hiring/{date}/daily-rejection-drafts.md
|
|
@@ -62,7 +62,7 @@ steps:
|
|
|
62
62
|
timeout: 300
|
|
63
63
|
parallel_with: market-sweep
|
|
64
64
|
inputs:
|
|
65
|
-
account:
|
|
65
|
+
account: "{{agent.email}}" # resolved from config/agent.json at workflow execution
|
|
66
66
|
mode: observe # read-only, never act
|
|
67
67
|
priority_senders: [] # from config/contacts.yaml
|
|
68
68
|
outputs:
|
|
@@ -43,7 +43,7 @@ steps:
|
|
|
43
43
|
depends_on: [check-commitments]
|
|
44
44
|
inputs:
|
|
45
45
|
overdue_items: outputs/followups/daily/{date}/slack-commitments-status.md
|
|
46
|
-
voice:
|
|
46
|
+
voice: agent # Follow up in the agent's own voice (mode resolved from config/agent.json voiceModes)
|
|
47
47
|
outputs:
|
|
48
48
|
file: outputs/followups/daily/{date}/slack-followup-drafts.md
|
|
49
49
|
|
|
@@ -65,7 +65,7 @@ steps:
|
|
|
65
65
|
screening_results: outputs/briefs/weekly/{date}/screening-results.md
|
|
66
66
|
category: advance
|
|
67
67
|
tone: warm_professional
|
|
68
|
-
voice:
|
|
68
|
+
voice: agent
|
|
69
69
|
outputs:
|
|
70
70
|
file: outputs/comms/hiring/{date}/advance-drafts.md
|
|
71
71
|
|
|
@@ -80,7 +80,7 @@ steps:
|
|
|
80
80
|
screening_results: outputs/briefs/weekly/{date}/screening-results.md
|
|
81
81
|
category: reject
|
|
82
82
|
tone: kind_encouraging
|
|
83
|
-
voice:
|
|
83
|
+
voice: agent
|
|
84
84
|
outputs:
|
|
85
85
|
file: outputs/comms/hiring/{date}/rejection-drafts.md
|
|
86
86
|
|
|
@@ -95,7 +95,7 @@ steps:
|
|
|
95
95
|
screening_results: outputs/briefs/weekly/{date}/screening-results.md
|
|
96
96
|
category: hold
|
|
97
97
|
tone: professional
|
|
98
|
-
voice:
|
|
98
|
+
voice: agent
|
|
99
99
|
outputs:
|
|
100
100
|
file: outputs/comms/hiring/{date}/hold-drafts.md
|
|
101
101
|
|