@massa-ai/codex-plugin 1.52.0 → 1.54.1
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/install.sh
CHANGED
|
@@ -184,8 +184,28 @@ if (typeof cfg.hooks !== "object" || cfg.hooks === null) {
|
|
|
184
184
|
cfg.hooks = {};
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
+
// Ownership test — marker first, command shape as the fallback. The marker is
|
|
188
|
+
// precise but not the only way our entries reach this file: an entry written by
|
|
189
|
+
// a release before the marker existed, or copied by hand from documentation,
|
|
190
|
+
// carries none, and was invisible to BOTH directions here — never deduped on
|
|
191
|
+
// install, never removed on uninstall. Claude's copy of this merge had exactly
|
|
192
|
+
// that gap and it double-fired every lifecycle event (measured live
|
|
193
|
+
// 2026-08-17). Referencing the massa-ai-hook binary is unambiguous; a user hook
|
|
194
|
+
// that merely mentions massa-ai elsewhere does not match.
|
|
195
|
+
//
|
|
196
|
+
// isOwned covers both shapes this file has ever held: the flat pre-fix entry
|
|
197
|
+
// (command at the top level) and the matcher group (commands inside hooks[]).
|
|
198
|
+
const OWNED_COMMAND = /massa-ai-hook/;
|
|
199
|
+
|
|
200
|
+
function commandIsOwned(h) {
|
|
201
|
+
return Boolean(h) && typeof h.command === "string" && OWNED_COMMAND.test(h.command);
|
|
202
|
+
}
|
|
203
|
+
|
|
187
204
|
function isOwned(e) {
|
|
188
|
-
|
|
205
|
+
if (!e || typeof e !== "object") return false;
|
|
206
|
+
if (e._massaAiOwned === true) return true;
|
|
207
|
+
if (commandIsOwned(e)) return true;
|
|
208
|
+
return Array.isArray(e.hooks) && e.hooks.length > 0 && e.hooks.every(commandIsOwned);
|
|
189
209
|
}
|
|
190
210
|
|
|
191
211
|
// A correctly-shaped owned entry is a matcher group: its "hooks" is an array.
|
|
@@ -202,10 +222,18 @@ if (mode === "uninstall") {
|
|
|
202
222
|
// Remove owned entries from nested location + perform flat→nested migration
|
|
203
223
|
const hooks = cfg.hooks;
|
|
204
224
|
|
|
205
|
-
//
|
|
206
|
-
|
|
225
|
+
// Back up before removing: this now deletes unmarked entries it did not
|
|
226
|
+
// necessarily write, so the install path's backup discipline applies here too.
|
|
227
|
+
if (existed) {
|
|
228
|
+
fs.copyFileSync(file, `${file}.massa-ai.bak-${ts}`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Remove owned entries from the correct nested location. Every event, not
|
|
232
|
+
// just the ones this release writes — the predicate identifies our commands
|
|
233
|
+
// rather than a location.
|
|
234
|
+
for (const evt of Object.keys(hooks)) {
|
|
207
235
|
if (Array.isArray(hooks[evt])) {
|
|
208
|
-
hooks[evt] = hooks[evt].filter((e) => !(e
|
|
236
|
+
hooks[evt] = hooks[evt].filter((e) => !isOwned(e));
|
|
209
237
|
if (hooks[evt].length === 0) delete hooks[evt];
|
|
210
238
|
}
|
|
211
239
|
}
|
|
@@ -307,7 +335,7 @@ install_bundled_skills() {
|
|
|
307
335
|
fi
|
|
308
336
|
|
|
309
337
|
local installed=0 name src dest
|
|
310
|
-
for name in massa-ai persona-router; do
|
|
338
|
+
for name in massa-ai persona-router profile; do
|
|
311
339
|
src="$SCRIPT_DIR/skills/$name"
|
|
312
340
|
[[ -d "$src" ]] || continue
|
|
313
341
|
dest="$HARNESS_SKILLS_DIR/$name"
|
|
@@ -336,7 +364,7 @@ if (typeof data.platforms !== "object" || data.platforms === null || Array.isArr
|
|
|
336
364
|
}
|
|
337
365
|
data.version = 2;
|
|
338
366
|
const prev = data.platforms[host];
|
|
339
|
-
data.platforms[host] = { root, skillsOwner: "plugin", skills: ["massa-ai", "persona-router"] };
|
|
367
|
+
data.platforms[host] = { root, skillsOwner: "plugin", skills: ["massa-ai", "persona-router", "profile"] };
|
|
340
368
|
// The whole-record replace must not drop fields a previous successful install
|
|
341
369
|
// wrote (R2) — re-attach them. modelProfile (T10, MPS-03 round-trip
|
|
342
370
|
// obligation) is engine-owned; installRoute is installer-owned but written by
|
|
@@ -381,7 +409,7 @@ NODE
|
|
|
381
409
|
)"
|
|
382
410
|
[[ "$raw_owner" == "plugin" ]] && {
|
|
383
411
|
local name
|
|
384
|
-
for name in massa-ai persona-router; do
|
|
412
|
+
for name in massa-ai persona-router profile; do
|
|
385
413
|
rm -rf "$HARNESS_SKILLS_DIR/$name"
|
|
386
414
|
done
|
|
387
415
|
rmdir "$HARNESS_SKILLS_DIR" 2>/dev/null || true
|
|
@@ -687,6 +715,35 @@ for src in "$ACTIVE_AGENTS_SRC/"massa-ai-*.toml; do
|
|
|
687
715
|
done
|
|
688
716
|
vecho " + ${specialist_count} subagent specialists (generated from skills/agents/*/SKILL.md)"
|
|
689
717
|
|
|
718
|
+
# Shed retired specialists — IPT-02 site 3 (D1 copy-then-prune, D2/AC-02.1a).
|
|
719
|
+
# The copy loop above writes the current set FIRST; only then do we remove
|
|
720
|
+
# owned destination entries the bundle no longer ships. Never the reverse —
|
|
721
|
+
# an interrupted run must never leave the user with fewer agents than before
|
|
722
|
+
# (D1). The removal POPULATION is the destination directory ($AGENTS_DIR,
|
|
723
|
+
# every *.toml, not just files matching the massa-ai- prefix — mirrors the
|
|
724
|
+
# --uninstall loop's population above); the current bundle
|
|
725
|
+
# ($ACTIVE_AGENTS_SRC) supplies only the keep-predicate. Never loop over the
|
|
726
|
+
# bundle to decide removals (D2, AC-02.1a).
|
|
727
|
+
#
|
|
728
|
+
# Ownership test is per-host (D3/AC-02.2), taken verbatim from this file's own
|
|
729
|
+
# --uninstall above: the first line must be exactly "# massa-ai-owned". This
|
|
730
|
+
# is NOT a name-prefix test — $AGENTS_DIR is a directory shared with
|
|
731
|
+
# user-authored agents, and a blind `rm -f massa-ai-*.toml` would delete a
|
|
732
|
+
# user's own file that happens to share the name prefix but carries no marker
|
|
733
|
+
# (AC-02.3, AC-02.5).
|
|
734
|
+
pruned=0
|
|
735
|
+
for f in "$AGENTS_DIR/"*.toml; do
|
|
736
|
+
[[ -f "$f" ]] || continue
|
|
737
|
+
head -n1 "$f" | grep -q "^# massa-ai-owned$" || continue # ownership test (D3)
|
|
738
|
+
base="$(basename "$f")"
|
|
739
|
+
[[ -f "$ACTIVE_AGENTS_SRC/$base" ]] && continue # keep-list: still shipped
|
|
740
|
+
rm -f "$f"
|
|
741
|
+
pruned=$((pruned + 1))
|
|
742
|
+
done
|
|
743
|
+
if [[ "$pruned" -gt 0 ]]; then
|
|
744
|
+
vecho " - pruned ${pruned} retired massa-ai-owned agent TOML files from $AGENTS_DIR/"
|
|
745
|
+
fi
|
|
746
|
+
|
|
690
747
|
install_variant_tree
|
|
691
748
|
|
|
692
749
|
# Skills bundling (PDO-08, 09): install massa-ai/persona-router into the
|
package/package.json
CHANGED
package/skills/massa-ai/SKILL.md
CHANGED
|
@@ -46,7 +46,7 @@ Before reading any massa-ai file:
|
|
|
46
46
|
Never pass `workflowSessionId` in that field. Use `synapse_task_begin`/`synapse_task_end`
|
|
47
47
|
for task envelopes and `synapse_prefetch` to warm the buffer on file open.
|
|
48
48
|
- Prefer the shared v2 retrieval order; fall back gracefully if the massa-ai
|
|
49
|
-
server or Synapse is unavailable. The full tool surface includes
|
|
49
|
+
server or Synapse is unavailable. The full tool surface includes 59 tools
|
|
50
50
|
(see `references/mcp-tools.md`): indexing, search, symbol graph
|
|
51
51
|
(`trace_path`, `impact_analysis`, `get_architecture`), memory CRUD
|
|
52
52
|
(`remember`, `recall`, `memory_update`, `memory_delete`), checkpoints
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# massa-ai Tool Contracts (
|
|
1
|
+
# massa-ai Tool Contracts (59 Tools)
|
|
2
2
|
|
|
3
3
|
Load when exact MCP schemas, REST fallbacks, response modes, or
|
|
4
4
|
polling rules are needed. Prefer the active tool declaration over copied
|
|
@@ -103,6 +103,8 @@ depends on exact content.
|
|
|
103
103
|
| `handoff_accept` | Accept an open handoff (open→accepted) | Req: `id`. Opt: `projectId`. |
|
|
104
104
|
| `handoff_cancel` | Cancel/expire an open handoff | Req: `id`. Opt: `projectId`. |
|
|
105
105
|
| `handoff_list_pending` | List open handoffs, oldest-first | Req: `projectId`. Opt: `targetAgent`. |
|
|
106
|
+
| `handoff_update` | Edit targetAgent/summary/openQuestions/nextSteps/files by id | Req: `id`. Opt: `projectId`, `targetAgent`, `summary`, `openQuestions`, `nextSteps`, `files` — at least one editable field required. Unknown/restricted fields (`status`, `acceptedAt`, etc.) are rejected by name. |
|
|
107
|
+
| `handoff_delete` | Hard-delete a handoff, any status | Req: `id`. Opt: `projectId`. |
|
|
106
108
|
|
|
107
109
|
## MCP Capability Matrix — Auto-improvement (Proposals)
|
|
108
110
|
|
|
@@ -111,6 +113,9 @@ depends on exact content.
|
|
|
111
113
|
| `list_proposals` | List pending auto-improvement proposals, newest-first | Req: `projectId`. |
|
|
112
114
|
| `approve_proposal` | Approve a proposal; applies the memory edit | Req: `id`. Opt: `projectId`, `source`. |
|
|
113
115
|
| `reject_proposal` | Reject a proposal (no edit applied) | Req: `id`. Opt: `projectId`, `reason`. |
|
|
116
|
+
| `create_proposal` | Create a pending proposal by hand | Req: `projectId`, `kind` (`memory.create`\|`memory.update`\|`memory.tag`), `payload`. Opt: `rationale`, `targetMemoryId`. `payload` is validated against the same per-kind rules the store enforces on read. |
|
|
117
|
+
| `update_proposal` | Edit rationale/payload by id, any status | Req: `id`. Opt: `projectId`, `rationale`, `payload` — at least one editable field required. `kind`/`targetMemoryId`/`status`/`decidedAt` are immutable. A `payload` edit re-validates against the row's existing `kind`. |
|
|
118
|
+
| `delete_proposal` | Hard-delete a proposal, any status | Req: `id`. Opt: `projectId`. Deleting an approved proposal does not reverse its applied memory edit. |
|
|
114
119
|
|
|
115
120
|
## MCP Capability Matrix — Passive Capture
|
|
116
121
|
|