@rubytech/create-maxy-code 0.1.559 → 0.1.561
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/package.json +1 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +15 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.d.ts +25 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.js +50 -1
- package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.js.map +1 -1
- package/payload/platform/plugins/admin/skills/agent-builder/SKILL.md +15 -10
- package/payload/platform/plugins/admin/skills/agent-builder/references/agent-pattern.md +6 -3
- package/payload/platform/plugins/admin/skills/specialist-management/SKILL.md +26 -0
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +10 -0
- package/payload/platform/scripts/__tests__/authored-agent-durable-copy.test.sh +78 -0
- package/payload/platform/scripts/__tests__/provision-bundle-orphan.test.sh +132 -0
- package/payload/platform/scripts/lib/provision-account-dir.sh +47 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +8 -4
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +39 -5
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +26 -5
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/server/server.js +7 -7
|
@@ -3,12 +3,37 @@ export interface RosterCensusInput {
|
|
|
3
3
|
agentsMdPath: string;
|
|
4
4
|
/** `<accountDir>/specialists/agents` — the directory spawn-context reads. */
|
|
5
5
|
specialistsAgentsDir: string;
|
|
6
|
+
/** Directories whose own `*.md` files are durable copies. Today just
|
|
7
|
+
* `$PLATFORM_ROOT/templates/specialists/agents`. Required, not optional:
|
|
8
|
+
* a caller that omitted them would report every live specialist as
|
|
9
|
+
* undurable. */
|
|
10
|
+
durableDirs: string[];
|
|
11
|
+
/** Plugin-tree roots. For each, every `<root>/<plugin>/agents/*.md` is a
|
|
12
|
+
* durable copy: the account tree (`store-skill`'s home for authored
|
|
13
|
+
* agents), the platform tree, and premium-plugins. A premium agent's only
|
|
14
|
+
* copy lives in its bundle and a reinstall re-delivers it, so omitting that
|
|
15
|
+
* root would report five standing false positives on beacons alone. */
|
|
16
|
+
durablePluginRoots: string[];
|
|
17
|
+
/** `<installRoot>/premium-plugins`. Omitted, missing, or holding no bundle
|
|
18
|
+
* directory yields an empty `bundleOrphans`: none of those states is
|
|
19
|
+
* evidence that the account's whole premium set is orphaned. */
|
|
20
|
+
premiumPluginsDir?: string;
|
|
6
21
|
}
|
|
7
22
|
export interface RosterCensusResult {
|
|
8
23
|
declared: number;
|
|
9
24
|
dispatchable: number;
|
|
10
25
|
/** Declared names with no `<name>.md` in the dispatch directory. */
|
|
11
26
|
orphaned: string[];
|
|
27
|
+
/** Live specialist filenames, `.md` included, absent from every durable
|
|
28
|
+
* root. Filenames rather than roster names because the durable copy is
|
|
29
|
+
* stored under the filename, and a `--` agent's two differ. */
|
|
30
|
+
undurable: string[];
|
|
31
|
+
/** Task 2418 — basenames of `*--*.md` in the dispatch directory with no file
|
|
32
|
+
* of that name under `<premiumPluginsDir>/<bundle>/agents/`. Dispatchable
|
|
33
|
+
* today, with no bundle behind them. Walks the directory rather than the
|
|
34
|
+
* roster, because dispatchability is a property of the directory
|
|
35
|
+
* `spawn-context.ts` reads, not of what AGENTS.md advertises. */
|
|
36
|
+
bundleOrphans: string[];
|
|
12
37
|
}
|
|
13
38
|
export declare function runRosterCensus(input: RosterCensusInput): RosterCensusResult;
|
|
14
39
|
//# sourceMappingURL=roster-census.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"roster-census.d.ts","sourceRoot":"","sources":["../../src/lib/roster-census.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"roster-census.d.ts","sourceRoot":"","sources":["../../src/lib/roster-census.ts"],"names":[],"mappings":"AAgCA,MAAM,WAAW,iBAAiB;IAChC,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,oBAAoB,EAAE,MAAM,CAAC;IAC7B;;;qBAGiB;IACjB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB;;;;4EAIwE;IACxE,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B;;qEAEiE;IACjE,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB;;oEAEgE;IAChE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB;;;;sEAIkE;IAClE,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAyCD,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,kBAAkB,CAgC5E"}
|
|
@@ -1,5 +1,40 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
/** `*.md` entries directly in `dir`. A missing directory is normal — not every
|
|
4
|
+
* install carries premium-plugins — and contributes nothing. An unreadable one
|
|
5
|
+
* is not: the throw propagates to the emit point, which reports
|
|
6
|
+
* `[roster-census] error` rather than a short durable set, because a partial
|
|
7
|
+
* set would name durable specialists as undurable. */
|
|
8
|
+
function markdownIn(dir) {
|
|
9
|
+
if (!existsSync(dir))
|
|
10
|
+
return [];
|
|
11
|
+
return readdirSync(dir).filter((entry) => entry.endsWith(".md"));
|
|
12
|
+
}
|
|
13
|
+
function subdirs(dir) {
|
|
14
|
+
if (!existsSync(dir))
|
|
15
|
+
return [];
|
|
16
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
17
|
+
.filter((entry) => entry.isDirectory())
|
|
18
|
+
.map((entry) => entry.name);
|
|
19
|
+
}
|
|
20
|
+
function findBundleOrphans(specialistsAgentsDir, premiumPluginsDir) {
|
|
21
|
+
if (!premiumPluginsDir)
|
|
22
|
+
return [];
|
|
23
|
+
const bundles = subdirs(premiumPluginsDir);
|
|
24
|
+
// No bundle resolvable, whether the root is missing or merely empty, is not
|
|
25
|
+
// evidence of a fleet of orphans: both are indistinguishable from a brand
|
|
26
|
+
// that legitimately ships none (paths.ts bundleMode defaults to 'none').
|
|
27
|
+
// Provisioning applies the identical guard, so the two surfaces never
|
|
28
|
+
// disagree. An unreadable root throws, the same doctrine as markdownIn.
|
|
29
|
+
if (bundles.length === 0)
|
|
30
|
+
return [];
|
|
31
|
+
// Any bundle's agents/ dir: `--` names are already bundle-unique, so this
|
|
32
|
+
// asks the same question as parsing the prefix without deriving meaning from
|
|
33
|
+
// a filename.
|
|
34
|
+
return markdownIn(specialistsAgentsDir)
|
|
35
|
+
.filter((file) => file.includes("--"))
|
|
36
|
+
.filter((file) => !bundles.some((b) => existsSync(join(premiumPluginsDir, b, "agents", file))));
|
|
37
|
+
}
|
|
3
38
|
export function runRosterCensus(input) {
|
|
4
39
|
const body = existsSync(input.agentsMdPath)
|
|
5
40
|
? readFileSync(input.agentsMdPath, "utf8")
|
|
@@ -9,10 +44,24 @@ export function runRosterCensus(input) {
|
|
|
9
44
|
declared.push(match[1]);
|
|
10
45
|
}
|
|
11
46
|
const orphaned = declared.filter((name) => !existsSync(join(input.specialistsAgentsDir, `${name}.md`)));
|
|
47
|
+
const durable = new Set();
|
|
48
|
+
for (const dir of input.durableDirs) {
|
|
49
|
+
for (const filename of markdownIn(dir))
|
|
50
|
+
durable.add(filename);
|
|
51
|
+
}
|
|
52
|
+
for (const root of input.durablePluginRoots) {
|
|
53
|
+
for (const plugin of subdirs(root)) {
|
|
54
|
+
for (const filename of markdownIn(join(root, plugin, "agents")))
|
|
55
|
+
durable.add(filename);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const undurable = markdownIn(input.specialistsAgentsDir).filter((filename) => !durable.has(filename));
|
|
12
59
|
return {
|
|
13
60
|
declared: declared.length,
|
|
14
61
|
dispatchable: declared.length - orphaned.length,
|
|
15
62
|
orphaned,
|
|
63
|
+
undurable,
|
|
64
|
+
bundleOrphans: findBundleOrphans(input.specialistsAgentsDir, input.premiumPluginsDir),
|
|
16
65
|
};
|
|
17
66
|
}
|
|
18
67
|
//# sourceMappingURL=roster-census.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"roster-census.js","sourceRoot":"","sources":["../../src/lib/roster-census.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"roster-census.js","sourceRoot":"","sources":["../../src/lib/roster-census.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAsEjC;;;;uDAIuD;AACvD,SAAS,UAAU,CAAC,GAAW;IAC7B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAChC,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,OAAO,CAAC,GAAW;IAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAChC,OAAO,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;SAC7C,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;SACtC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,iBAAiB,CACxB,oBAA4B,EAC5B,iBAA0B;IAE1B,IAAI,CAAC,iBAAiB;QAAE,OAAO,EAAE,CAAC;IAClC,MAAM,OAAO,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC3C,4EAA4E;IAC5E,0EAA0E;IAC1E,yEAAyE;IACzE,sEAAsE;IACtE,wEAAwE;IACxE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,0EAA0E;IAC1E,6EAA6E;IAC7E,cAAc;IACd,OAAO,UAAU,CAAC,oBAAoB,CAAC;SACpC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;SACrC,MAAM,CACL,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CACvF,CAAC;AACN,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAwB;IACtD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,YAAY,CAAC;QACzC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,YAAY,EAAE,MAAM,CAAC;QAC1C,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,mCAAmC,CAAC,EAAE,CAAC;QACvE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAC9B,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,oBAAoB,EAAE,GAAG,IAAI,KAAK,CAAC,CAAC,CACtE,CAAC;IAEF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;QACpC,KAAK,MAAM,QAAQ,IAAI,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAChE,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,kBAAkB,EAAE,CAAC;QAC5C,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,KAAK,MAAM,QAAQ,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACzF,CAAC;IACH,CAAC;IACD,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,MAAM,CAC7D,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CACrC,CAAC;IAEF,OAAO;QACL,QAAQ,EAAE,QAAQ,CAAC,MAAM;QACzB,YAAY,EAAE,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM;QAC/C,QAAQ;QACR,SAAS;QACT,aAAa,EAAE,iBAAiB,CAAC,KAAK,CAAC,oBAAoB,EAAE,KAAK,CAAC,iBAAiB,CAAC;KACtF,CAAC;AACJ,CAAC"}
|
|
@@ -96,18 +96,23 @@ Show the operator the complete file and ask them to review.
|
|
|
96
96
|
## Step 6: Install through specialist-management
|
|
97
97
|
|
|
98
98
|
The agent persists as part of an admin-managed plugin: the same durable home `store-skill` uses,
|
|
99
|
-
so it survives a reinstall and the agent and its skills stay together.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
(a `PLUGIN.md` and the `.user-mirror` marker in both the account and mirror trees) before
|
|
99
|
+
so it survives a reinstall and the agent and its skills stay together. That home is the account's
|
|
100
|
+
own plugin tree, and it is the only one: operator plugins never live under
|
|
101
|
+
`$PLATFORM_ROOT/plugins`, which is immutable.
|
|
102
|
+
|
|
103
|
+
Write the composed file to `plugins/{plugin}/agents/{plugin}--{name}.md`. That copy is the
|
|
104
|
+
durable one, and the live copy is only ever made by copying it, so no order of operations leaves
|
|
105
|
+
a dispatchable agent that nothing can restore. `store-skill` already created the plugin when you
|
|
106
|
+
built the agent's skills; if the agent bundles only existing skills, so no plugin was created,
|
|
107
|
+
scaffold it first the way `store-skill` does — a `PLUGIN.md` under `plugins/{plugin}/` — before
|
|
109
108
|
writing the agent file.
|
|
110
109
|
|
|
110
|
+
Then follow `specialist-management`'s "Install an account-authored agent" path, which copies the
|
|
111
|
+
durable file into `specialists/agents/` and appends the `- **specialists:{name}**: {description}`
|
|
112
|
+
line to the admin `AGENTS.md`. Do not invent install logic, and never write an agent file
|
|
113
|
+
straight into `specialists/agents/`: a live file with no durable copy behind it is exactly what
|
|
114
|
+
this step exists to prevent.
|
|
115
|
+
|
|
111
116
|
## Step 7: Confirm
|
|
112
117
|
|
|
113
118
|
Tell the operator:
|
|
@@ -16,9 +16,12 @@ The roster splits along one line: what an agent is *for*.
|
|
|
16
16
|
writing, data, coding. It owns a capability surface, not a single artefact. These ship in
|
|
17
17
|
`platform/templates/specialists/agents/` and resolve as `specialists:<name>`.
|
|
18
18
|
- **Vertical bundle agent.** A job title that owns one deliverable and the skills that produce
|
|
19
|
-
it: a negotiator owns the buyer-pipeline package, a quoter owns the priced quote.
|
|
20
|
-
|
|
21
|
-
`
|
|
19
|
+
it: a negotiator owns the buyer-pipeline package, a quoter owns the priced quote. Named
|
|
20
|
+
`{plugin}--{agent}.md` and resolving as `specialists:<name>`, these have two homes. A
|
|
21
|
+
bundle-shipped one lives in its premium plugin's `agents/` directory. An operator-authored one
|
|
22
|
+
lives in the account's own `plugins/{plugin}/agents/`, the durable home `store-skill` uses;
|
|
23
|
+
that copy is what a reinstall preserves and what the live copy in `specialists/agents/` is
|
|
24
|
+
made from.
|
|
22
25
|
|
|
23
26
|
`agent-builder` authors vertical bundle agents. The forcing question is "what one thing does
|
|
24
27
|
this agent hand back?" If the honest answer is "several unrelated things", that is several
|
|
@@ -9,6 +9,11 @@ Invoked by the admin agent directly.
|
|
|
9
9
|
|
|
10
10
|
Manage specialist subagents at any time via conversation.
|
|
11
11
|
|
|
12
|
+
Authoring a *new* agent from scratch is `agent-builder`, not this skill. That skill decides the
|
|
13
|
+
deliverable, the model, the skills it owns and its tool allowlist, composes the file, and writes
|
|
14
|
+
the durable copy; it then hands the finished agent back here to install. This skill installs,
|
|
15
|
+
removes, and activates agents whose files already exist.
|
|
16
|
+
|
|
12
17
|
## Install a specialist
|
|
13
18
|
|
|
14
19
|
1. Use Bash: `cp $PLATFORM_ROOT/templates/specialists/agents/{name}.md specialists/agents/{name}.md`
|
|
@@ -45,3 +50,24 @@ For each `.md` file in `$PLATFORM_ROOT/plugins/{plugin-name}/agents/`:
|
|
|
45
50
|
4. Confirm removal to the user.
|
|
46
51
|
|
|
47
52
|
Core specialists (files without `--` in the name) are never affected by premium plugin operations.
|
|
53
|
+
|
|
54
|
+
## Account-authored plugin agents
|
|
55
|
+
|
|
56
|
+
An agent authored through `agent-builder` lives in the account's own plugin tree at
|
|
57
|
+
`plugins/{plugin-name}/agents/{plugin-name}--{agent-name}.md`. That copy is the durable one: it
|
|
58
|
+
is what survives a reinstall, and it is the only source the live copy is made from. A file in
|
|
59
|
+
`specialists/agents/` with nothing behind it in a durable tree cannot be restored by anything —
|
|
60
|
+
the boot-time `[roster-census]` line names those in its `undurable=` field.
|
|
61
|
+
|
|
62
|
+
### Install an account-authored agent
|
|
63
|
+
|
|
64
|
+
For each `.md` file in `plugins/{plugin-name}/agents/`:
|
|
65
|
+
|
|
66
|
+
1. Check whether `specialists/agents/{filename}` already exists — skip if so (idempotent).
|
|
67
|
+
2. Use Bash: `cp plugins/{plugin-name}/agents/{filename} specialists/agents/{filename}`
|
|
68
|
+
3. Extract the `name` and `description` fields from the copied file's YAML frontmatter.
|
|
69
|
+
4. Read `agents/admin/AGENTS.md`. If it already contains `specialists:{name}`, skip. Otherwise append: `- **specialists:{name}**: {description}` using Edit.
|
|
70
|
+
5. Confirm each installed specialist to the user — active from next session start.
|
|
71
|
+
|
|
72
|
+
The copy source is always the durable file. Never compose an agent directly into
|
|
73
|
+
`specialists/agents/`: that produces a dispatchable agent nothing can restore.
|
|
@@ -9,6 +9,16 @@ Invoked by the admin agent directly.
|
|
|
9
9
|
|
|
10
10
|
This is the platform's release timeline, newest first. Each entry shows the date it shipped and the version it shipped in, so you can tell the operator how current their install is. To compare, read the installed version from `capabilities-here` and match it against the versions below. Keep answers high level and in plain English; this is a summary, not a full commit log.
|
|
11
11
|
|
|
12
|
+
## 2026-08-04 (0.1.561)
|
|
13
|
+
|
|
14
|
+
- A public Telegram bot now answers. Messages were arriving and the conversation staying alive, but nothing the visitor sent ever became a turn, so a public bot received everything and replied to nothing. Public bots now open through the same locked-down path the platform already uses elsewhere: the ordinary setup with only the reply tool available.
|
|
15
|
+
|
|
16
|
+
## 2026-08-04 (0.1.560)
|
|
17
|
+
|
|
18
|
+
- A specialist you wrote is no longer lost if it is deleted. Authoring now always leaves a durable copy in the plugin that owns it, and the boot-time check names any live specialist that has no such copy.
|
|
19
|
+
- A specialist whose plugin has been removed no longer stays dispatchable forever. Upgrades now re-check those files instead of skipping them unconditionally, and the boot-time check names any that can still be dispatched with nothing behind them.
|
|
20
|
+
- A message sent to a public conversation now becomes a turn. A connection the platform needs in order to hand an incoming message to the assistant had been removed from public conversations as unnecessary; without it the message arrived and was never acted on.
|
|
21
|
+
|
|
12
22
|
## 2026-08-04 (0.1.559)
|
|
13
23
|
|
|
14
24
|
- Upgrading no longer deletes specialists. Every upgrade was removing any specialist that did not ship with the install, including ones written for you, and never putting them back. An upgrade now only replaces what it actually ships, and reports at boot any specialist the roster cannot dispatch.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Task 2417 — the two admin skills that document specialist authoring must
|
|
3
|
+
# never document a live copy that is not made from a durable one.
|
|
4
|
+
#
|
|
5
|
+
# Task 1962 (ba626832b) dropped the platform mirror. agent-builder was never
|
|
6
|
+
# updated: it kept telling the agent to write the composed file "in both
|
|
7
|
+
# places" and to scaffold a `.user-mirror` marker in a mirror tree that no
|
|
8
|
+
# longer exists, then handed install to a specialist-management flow sourcing
|
|
9
|
+
# from $PLATFORM_ROOT/plugins/{plugin}/agents/ — a root an account-authored
|
|
10
|
+
# plugin never occupies. Every documented step after "compose the file" named a
|
|
11
|
+
# directory that was not there, so the agent improvised a bare Write into
|
|
12
|
+
# specialists/agents/ and the durable copy was never made. These are the four
|
|
13
|
+
# checkable shapes of that regression.
|
|
14
|
+
set -u
|
|
15
|
+
ROOT="$(cd "$(dirname "$0")/../../.." && pwd)"
|
|
16
|
+
AB="$ROOT/platform/plugins/admin/skills/agent-builder/SKILL.md"
|
|
17
|
+
SM="$ROOT/platform/plugins/admin/skills/specialist-management/SKILL.md"
|
|
18
|
+
# agent-builder Step 0 declares this reference authoritative and forbids
|
|
19
|
+
# paraphrasing it, so a wrong home here overrides Step 6 rather than merely
|
|
20
|
+
# duplicating it.
|
|
21
|
+
AP="$ROOT/platform/plugins/admin/skills/agent-builder/references/agent-pattern.md"
|
|
22
|
+
PASS=0; FAIL=0
|
|
23
|
+
|
|
24
|
+
for f in "$AB" "$SM" "$AP"; do
|
|
25
|
+
[[ -f "$f" ]] || { echo "FAIL: missing $f" >&2; exit 1; }
|
|
26
|
+
done
|
|
27
|
+
|
|
28
|
+
# 1. Every documented `cp` into specialists/agents/ sources from a durable root.
|
|
29
|
+
bad=$(grep -hoE 'cp [^`]*specialists/agents/[^`]*' "$AB" "$SM" "$AP" \
|
|
30
|
+
| grep -vE 'cp \$PLATFORM_ROOT/templates/specialists/agents/' \
|
|
31
|
+
| grep -vE 'cp \$PLATFORM_ROOT/plugins/\{plugin-name\}/agents/' \
|
|
32
|
+
| grep -vE 'cp plugins/\{plugin-name\}/agents/' || true)
|
|
33
|
+
if [[ -z "$bad" ]]; then
|
|
34
|
+
echo "PASS: every documented cp into specialists/agents/ sources from a durable root"; PASS=$((PASS+1))
|
|
35
|
+
else
|
|
36
|
+
echo "FAIL: cp into specialists/agents/ from a non-durable source:" >&2
|
|
37
|
+
printf ' %s\n' "$bad" >&2; FAIL=$((FAIL+1))
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# 2. The account-authored install flow exists. Without this, check 1 passes
|
|
41
|
+
# vacuously once the flow is deleted.
|
|
42
|
+
if grep -qE 'cp plugins/\{plugin-name\}/agents/' "$SM"; then
|
|
43
|
+
echo "PASS: specialist-management documents the account-authored install flow"; PASS=$((PASS+1))
|
|
44
|
+
else
|
|
45
|
+
echo "FAIL: specialist-management has no cp from plugins/{plugin-name}/agents/" >&2; FAIL=$((FAIL+1))
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# 3. No platform mirror. 1962 removed it; a reference to it here is stale by
|
|
49
|
+
# construction and sends the agent to a tree that is not there. Matched by
|
|
50
|
+
# the three shapes the stale text actually used, not by the bare word:
|
|
51
|
+
# "mirrors the reference agent.yaml" is ordinary English and must pass.
|
|
52
|
+
MIRROR='\.user-mirror|platform mirror|mirror tree|mirrored under'
|
|
53
|
+
if ! grep -qiE "$MIRROR" "$AB" "$SM" "$AP"; then
|
|
54
|
+
echo "PASS: no skill in the authoring path references a platform mirror"; PASS=$((PASS+1))
|
|
55
|
+
else
|
|
56
|
+
echo "FAIL: a platform-mirror reference survives (1962 removed the mirror):" >&2
|
|
57
|
+
grep -inE "$MIRROR" "$AB" "$SM" "$AP" >&2; FAIL=$((FAIL+1))
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# 4. agent-builder names the account plugin tree as the durable home.
|
|
61
|
+
if grep -qE 'plugins/\{plugin\}/agents/' "$AB"; then
|
|
62
|
+
echo "PASS: agent-builder names the account plugin tree as the durable home"; PASS=$((PASS+1))
|
|
63
|
+
else
|
|
64
|
+
echo "FAIL: agent-builder does not name plugins/{plugin}/agents/ as the durable home" >&2; FAIL=$((FAIL+1))
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# 5. So does the reference Step 0 defers to. Without this, agent-pattern.md can
|
|
68
|
+
# say an authored agent lives in a premium bundle — a tree the account cannot
|
|
69
|
+
# write — and that overrides Step 6, because Step 0 forbids paraphrasing it.
|
|
70
|
+
if grep -qE 'plugins/\{plugin\}/agents/' "$AP"; then
|
|
71
|
+
echo "PASS: agent-pattern names the account plugin tree for an authored agent"; PASS=$((PASS+1))
|
|
72
|
+
else
|
|
73
|
+
echo "FAIL: agent-pattern does not name plugins/{plugin}/agents/ as an authored agent's home" >&2; FAIL=$((FAIL+1))
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
echo "---"
|
|
77
|
+
echo "authored-agent-durable-copy: $PASS passed, $FAIL failed"
|
|
78
|
+
[[ $FAIL -eq 0 ]]
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Task 2418 — a `--` agent whose premium bundle is no longer on the install tree
|
|
3
|
+
# stays dispatchable forever.
|
|
4
|
+
#
|
|
5
|
+
# 2415's retirement loop deletes only what its manifest claims, and the manifest
|
|
6
|
+
# names only files copied from $TEMPLATES_DIR, so a premium `--` file is never in
|
|
7
|
+
# it. Nothing noticed when the bundle behind it left the tree: on beacons,
|
|
8
|
+
# writer-craft--manuscript-reviewer.md was dispatchable from May 13 onward with
|
|
9
|
+
# no writer-craft bundle under premium-plugins/.
|
|
10
|
+
set -uo pipefail
|
|
11
|
+
SCRIPT_DIR_T="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
PLAT_SCRIPTS="$SCRIPT_DIR_T/.."
|
|
13
|
+
REAL_PROJECT="$(cd "$PLAT_SCRIPTS/.." && pwd)"
|
|
14
|
+
PASS=0; FAIL=0; FAILED=()
|
|
15
|
+
assert_absent() { if [ ! -e "$1" ]; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); FAILED+=("$2: expected absent [$1]"); fi; }
|
|
16
|
+
assert_present() { if [ -e "$1" ]; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); FAILED+=("$2: expected present [$1]"); fi; }
|
|
17
|
+
assert_eq() { if [ "$1" = "$2" ]; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); FAILED+=("$3: expected [$2] got [$1]"); fi; }
|
|
18
|
+
|
|
19
|
+
. "$PLAT_SCRIPTS/lib/provision-account-dir.sh"
|
|
20
|
+
export SCRIPT_DIR="$PLAT_SCRIPTS"
|
|
21
|
+
export USERS_FILE="/nonexistent/users.json"
|
|
22
|
+
|
|
23
|
+
# A temp platform/ whose sibling premium-plugins/ this test controls. plugins and
|
|
24
|
+
# config are symlinked: provisioning reads both, and neither is under test here.
|
|
25
|
+
T=$(mktemp -d)
|
|
26
|
+
mkdir -p "$T/platform" "$T/premium-plugins/present-bundle/agents"
|
|
27
|
+
cp -R "$REAL_PROJECT/templates" "$T/platform/templates"
|
|
28
|
+
ln -s "$REAL_PROJECT/plugins" "$T/platform/plugins"
|
|
29
|
+
ln -s "$REAL_PROJECT/config" "$T/platform/config"
|
|
30
|
+
export PROJECT_DIR="$T/platform"
|
|
31
|
+
export TEMPLATES_DIR="$T/platform/templates"
|
|
32
|
+
|
|
33
|
+
ACCT="$T/acct"
|
|
34
|
+
ACCT_ID="24182418-2418-2418-2418-241824182418"
|
|
35
|
+
ORPHANED="$ACCT/specialists/agents-orphaned"
|
|
36
|
+
|
|
37
|
+
# One `--` agent whose bundle ships it, one whose bundle is gone.
|
|
38
|
+
mkdir -p "$ACCT/specialists/agents"
|
|
39
|
+
printf -- '---\nname: keeper\n---\nShipped by a bundle that is still installed.\n' \
|
|
40
|
+
> "$T/premium-plugins/present-bundle/agents/present-bundle--keeper.md"
|
|
41
|
+
cp "$T/premium-plugins/present-bundle/agents/present-bundle--keeper.md" \
|
|
42
|
+
"$ACCT/specialists/agents/present-bundle--keeper.md"
|
|
43
|
+
printf -- '---\nname: orphan\n---\nIts bundle is no longer on the tree.\n' \
|
|
44
|
+
> "$ACCT/specialists/agents/gone-bundle--orphan.md"
|
|
45
|
+
|
|
46
|
+
# --- Run 1 — the orphan is reconciled, the keeper is not. --------------------
|
|
47
|
+
OUT1="$(provision_account_dir "$ACCT" "$ACCT_ID" "house" 2>&1)"
|
|
48
|
+
|
|
49
|
+
assert_present "$ACCT/specialists/agents/present-bundle--keeper.md" "run1:bundle-present-survives"
|
|
50
|
+
assert_absent "$ACCT/specialists/agents/gone-bundle--orphan.md" "run1:orphan-undispatchable"
|
|
51
|
+
assert_present "$ORPHANED/gone-bundle--orphan.md" "run1:orphan-preserved"
|
|
52
|
+
assert_absent "$ACCT/specialists/agents-disabled/gone-bundle--orphan.md" "run1:not-in-disable-quarantine"
|
|
53
|
+
assert_eq "$(printf '%s' "$OUT1" | grep -c 'op=specialist-orphaned name=gone-bundle--orphan.md reason=bundle-absent')" "1" "run1:orphan-named"
|
|
54
|
+
assert_eq "$(printf '%s' "$OUT1" | grep -c 'op=specialist-orphan-reconcile orphaned=1')" "1" "run1:count-one"
|
|
55
|
+
|
|
56
|
+
# --- Run 2 — nothing left to reconcile: zero is still logged. ----------------
|
|
57
|
+
OUT2="$(provision_account_dir "$ACCT" "$ACCT_ID" "house" 2>&1)"
|
|
58
|
+
assert_eq "$(printf '%s' "$OUT2" | grep -c 'op=specialist-orphan-reconcile orphaned=0')" "1" "run2:zero-count-logged"
|
|
59
|
+
assert_eq "$(printf '%s' "$OUT2" | grep -c 'op=specialist-orphaned name=')" "0" "run2:nothing-reconciled"
|
|
60
|
+
assert_present "$ACCT/specialists/agents/present-bundle--keeper.md" "run2:keeper-still-dispatchable"
|
|
61
|
+
|
|
62
|
+
# --- An orphan of a name already quarantined replaces it. -------------------
|
|
63
|
+
# The copy leaving the dispatch directory is the one that was live, so it is the
|
|
64
|
+
# copy worth keeping. Pinned because a later change to `mv -n` would silently
|
|
65
|
+
# keep the stale one and contradict the contract doc.
|
|
66
|
+
printf -- '---\nname: orphan\n---\nSTALE QUARANTINED COPY\n' > "$ORPHANED/gone-bundle--orphan.md"
|
|
67
|
+
printf -- '---\nname: orphan\n---\nLIVE COPY\n' \
|
|
68
|
+
> "$ACCT/specialists/agents/gone-bundle--orphan.md"
|
|
69
|
+
provision_account_dir "$ACCT" "$ACCT_ID" "house" >/dev/null 2>&1
|
|
70
|
+
assert_eq "$(grep -c 'LIVE COPY' "$ORPHANED/gone-bundle--orphan.md")" "1" "requarantine:live-copy-kept"
|
|
71
|
+
assert_eq "$(grep -c 'STALE' "$ORPHANED/gone-bundle--orphan.md")" "0" "requarantine:stale-copy-replaced"
|
|
72
|
+
|
|
73
|
+
# --- Run 3 — premium root present but holding no bundles. -------------------
|
|
74
|
+
# Indistinguishable from a brand that ships none (paths.ts bundleMode defaults
|
|
75
|
+
# to 'none') and from a partial install. Reconciling here would strip the whole
|
|
76
|
+
# premium set off the account, so it reconciles nothing, exactly as 2415 refuses
|
|
77
|
+
# to read zero templates as twelve simultaneous retirements.
|
|
78
|
+
rm -rf "$T/premium-plugins/present-bundle"
|
|
79
|
+
OUT3="$(provision_account_dir "$ACCT" "$ACCT_ID" "house" 2>&1)"
|
|
80
|
+
assert_present "$ACCT/specialists/agents/present-bundle--keeper.md" "run3:empty-premium-root-moves-nothing"
|
|
81
|
+
assert_eq "$(printf '%s' "$OUT3" | grep -c 'op=specialist-orphan-reconcile orphaned=0 reason=no-premium-bundles')" "1" "run3:loud-line"
|
|
82
|
+
assert_eq "$(printf '%s' "$OUT3" | grep -c 'op=specialist-orphaned name=')" "0" "run3:no-reconciliations"
|
|
83
|
+
|
|
84
|
+
# --- Run 4 — premium root missing entirely. Same answer, same reason. --------
|
|
85
|
+
rm -rf "$T/premium-plugins"
|
|
86
|
+
OUT4="$(provision_account_dir "$ACCT" "$ACCT_ID" "house" 2>&1)"
|
|
87
|
+
assert_present "$ACCT/specialists/agents/present-bundle--keeper.md" "run4:no-premium-root-moves-nothing"
|
|
88
|
+
assert_eq "$(printf '%s' "$OUT4" | grep -c 'op=specialist-orphan-reconcile orphaned=0 reason=no-premium-bundles')" "1" "run4:loud-line"
|
|
89
|
+
assert_eq "$(printf '%s' "$OUT4" | grep -c 'op=specialist-orphaned name=')" "0" "run4:no-reconciliations"
|
|
90
|
+
|
|
91
|
+
rm -rf "$T"
|
|
92
|
+
|
|
93
|
+
# --- The install path runs under `set -euo pipefail`; this suite does not. ---
|
|
94
|
+
# setup-account.sh:17 sets all three and calls provision_account_dir at :63, so
|
|
95
|
+
# an `&&` list whose test fails could abort a real install where it cannot abort
|
|
96
|
+
# here. Both new branches end in one: the `[ -d "$_b" ]` bundle count and the
|
|
97
|
+
# `[ "$_found" = 1 ] && continue` miss. A subshell running the real options is
|
|
98
|
+
# the only place that difference is visible.
|
|
99
|
+
set_e_case() {
|
|
100
|
+
local premium_state="$1" expect_moved="$2" label="$3"
|
|
101
|
+
local out rc
|
|
102
|
+
out="$(
|
|
103
|
+
set -euo pipefail
|
|
104
|
+
. "$PLAT_SCRIPTS/lib/provision-account-dir.sh"
|
|
105
|
+
export SCRIPT_DIR="$PLAT_SCRIPTS" USERS_FILE="/nonexistent/users.json"
|
|
106
|
+
S=$(mktemp -d)
|
|
107
|
+
mkdir -p "$S/platform"
|
|
108
|
+
cp -R "$REAL_PROJECT/templates" "$S/platform/templates"
|
|
109
|
+
ln -s "$REAL_PROJECT/plugins" "$S/platform/plugins"
|
|
110
|
+
ln -s "$REAL_PROJECT/config" "$S/platform/config"
|
|
111
|
+
export PROJECT_DIR="$S/platform" TEMPLATES_DIR="$S/platform/templates"
|
|
112
|
+
[ "$premium_state" = "with-bundle" ] && {
|
|
113
|
+
mkdir -p "$S/premium-plugins/present-bundle/agents"
|
|
114
|
+
printf -- '---\n' > "$S/premium-plugins/present-bundle/agents/present-bundle--keeper.md"
|
|
115
|
+
}
|
|
116
|
+
A="$S/acct"; mkdir -p "$A/specialists/agents"
|
|
117
|
+
printf -- '---\nname: orphan\n---\n' > "$A/specialists/agents/gone-bundle--orphan.md"
|
|
118
|
+
provision_account_dir "$A" "24182418-2418-2418-2418-241824182418" "house" >/dev/null 2>&1
|
|
119
|
+
[ -f "$A/specialists/agents-orphaned/gone-bundle--orphan.md" ] && printf 'moved' || printf 'kept'
|
|
120
|
+
rm -rf "$S"
|
|
121
|
+
)"; rc=$?
|
|
122
|
+
assert_eq "$rc" "0" "$label:no-set-e-abort"
|
|
123
|
+
assert_eq "$out" "$expect_moved" "$label:outcome"
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# No premium root: the guard branch, where the bundle count test never matches.
|
|
127
|
+
set_e_case "bare" "kept" "set-e:no-premium-root"
|
|
128
|
+
# A bundle present but not shipping this file: the `_found=0` reconcile branch.
|
|
129
|
+
set_e_case "with-bundle" "moved" "set-e:orphan-moved"
|
|
130
|
+
|
|
131
|
+
echo "PASS=$PASS FAIL=$FAIL"
|
|
132
|
+
if [ "$FAIL" -gt 0 ]; then printf '%s\n' "${FAILED[@]}"; exit 1; fi
|
|
@@ -373,6 +373,53 @@ for n in names:
|
|
|
373
373
|
fi
|
|
374
374
|
fi
|
|
375
375
|
|
|
376
|
+
# Task 2418 — a `--` agent whose bundle has left the install tree stays
|
|
377
|
+
# dispatchable forever. The retirement loop above deletes only what the
|
|
378
|
+
# manifest claims, and the manifest names only files copied from
|
|
379
|
+
# $TEMPLATES_DIR, so a premium `--` file is never in it and nothing notices
|
|
380
|
+
# when its bundle goes. Measured on beacons: writer-craft--manuscript-reviewer
|
|
381
|
+
# dispatchable since May 13 with no writer-craft bundle under premium-plugins/.
|
|
382
|
+
#
|
|
383
|
+
# MOVE, never delete. The bundle is gone, so unlike a retired template there is
|
|
384
|
+
# nothing to restore from and this may be the only copy on the machine. It does
|
|
385
|
+
# NOT go to agents-disabled/: that directory is paired with agents-disabled.json
|
|
386
|
+
# and a file there with no store entry is exactly the drift described above.
|
|
387
|
+
#
|
|
388
|
+
# Runs AFTER the disabled block so that in the drift state (store names it,
|
|
389
|
+
# file still live) the disable wins and the store and its directory agree.
|
|
390
|
+
local _premium_root="$PROJECT_DIR/../premium-plugins"
|
|
391
|
+
local _bundle_count=0 _b
|
|
392
|
+
for _b in "$_premium_root"/*/; do
|
|
393
|
+
[ -d "$_b" ] && _bundle_count=$((_bundle_count + 1))
|
|
394
|
+
done
|
|
395
|
+
if [ "$_bundle_count" -eq 0 ]; then
|
|
396
|
+
# No bundle resolvable, whether the root is missing or merely empty. Both
|
|
397
|
+
# make every `--` file test as orphaned, and both are indistinguishable from
|
|
398
|
+
# a brand that legitimately ships none (paths.ts bundleMode defaults to
|
|
399
|
+
# 'none'). Acting would strip the account's whole premium set. Same doctrine
|
|
400
|
+
# as no-templates-on-disk above.
|
|
401
|
+
echo " op=specialist-orphan-reconcile orphaned=0 reason=no-premium-bundles"
|
|
402
|
+
else
|
|
403
|
+
local _orphaned=0 _cand _base _twin _found
|
|
404
|
+
for _cand in "$ACCOUNT_DIR/specialists/agents/"*--*.md; do
|
|
405
|
+
[ -f "$_cand" ] || continue
|
|
406
|
+
_base="$(basename "$_cand")"
|
|
407
|
+
# Any bundle's agents/ dir. `--` names are already bundle-unique, so this
|
|
408
|
+
# answers the same question as parsing the prefix, without reintroducing a
|
|
409
|
+
# filename-derived predicate.
|
|
410
|
+
_found=0
|
|
411
|
+
for _twin in "$_premium_root"/*/agents/"$_base"; do
|
|
412
|
+
[ -f "$_twin" ] && { _found=1; break; }
|
|
413
|
+
done
|
|
414
|
+
[ "$_found" = 1 ] && continue
|
|
415
|
+
mkdir -p "$ACCOUNT_DIR/specialists/agents-orphaned"
|
|
416
|
+
mv -f "$_cand" "$ACCOUNT_DIR/specialists/agents-orphaned/$_base"
|
|
417
|
+
echo " op=specialist-orphaned name=$_base reason=bundle-absent"
|
|
418
|
+
_orphaned=$((_orphaned + 1))
|
|
419
|
+
done
|
|
420
|
+
echo " op=specialist-orphan-reconcile orphaned=$_orphaned"
|
|
421
|
+
fi
|
|
422
|
+
|
|
376
423
|
rm -f "$ACCOUNT_DIR/.claude/agents/"*.md 2>/dev/null || true
|
|
377
424
|
|
|
378
425
|
# SOUL.md — user-controlled. Only create if missing. Never overwrite.
|
|
@@ -15,10 +15,14 @@ import { TelegramChannelStore } from './telegram-channel-store.js';
|
|
|
15
15
|
* widened this position to allow `undefined` because rc-spawn's role was
|
|
16
16
|
* absent for a non-admin/non-public request; null now means "the body named
|
|
17
17
|
* none", and the handler resolves it against the prior sidecar before
|
|
18
|
-
* deciding whether that is an error. `passive` is not accepted here:
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* deciding whether that is an error. `passive` is not accepted here: it is a
|
|
19
|
+
* /public-spawn persona role, resolved at that boundary from role:'public'
|
|
20
|
+
* plus the passive flag, and no client may POST it.
|
|
21
|
+
*
|
|
22
|
+
* Task 2423 — rc-spawn DOES compose a persona now, for role:'public' only: the
|
|
23
|
+
* named agent's IDENTITY/SOUL/KNOWLEDGE via composeAppendSystemPrompt, the same
|
|
24
|
+
* composer /public-spawn uses. An admin spawn is unchanged and still composes
|
|
25
|
+
* only reseat / voice / specialist-domains / authenticated-identity fragments. */
|
|
22
26
|
export declare function parseRcSpawnRole(body: {
|
|
23
27
|
role?: unknown;
|
|
24
28
|
}): Extract<AgentRole, 'admin' | 'public'> | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAoB3B,OAAO,KAAK,EAAU,SAAS,EAAW,MAAM,YAAY,CAAA;AAK5D,OAAO,EAkBL,KAAK,SAAS,EAEf,MAAM,kBAAkB,CAAA;AAqBzB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAoBlD,OAAO,KAAK,EAAE,SAAS,EAAyB,MAAM,iBAAiB,CAAA;AACvE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAA;AAEzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAE3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAQlE
|
|
1
|
+
{"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -50,7 +50,7 @@ import { emitRcLife } from './rc-life.js';
|
|
|
50
50
|
import { createRcChildLog } from './rc-child-log.js';
|
|
51
51
|
// Task 1214 — the admin voice-section reader, injected into the rc-spawn
|
|
52
52
|
// --append-system-prompt for channel-bound admin sessions.
|
|
53
|
-
import { extractVoiceSection, renderSpecialistDomains } from './system-prompt.js';
|
|
53
|
+
import { extractVoiceSection, renderSpecialistDomains, composeAppendSystemPrompt } from './system-prompt.js';
|
|
54
54
|
import { readSpecialistRoster } from './specialist-roster.js';
|
|
55
55
|
import { readJsonlSession, readJsonlSessionAtPath } from './jsonl-enumerator.js';
|
|
56
56
|
import { computeMetering } from './session-metering.js';
|
|
@@ -68,10 +68,14 @@ const ROLES = ['admin', 'public'];
|
|
|
68
68
|
* widened this position to allow `undefined` because rc-spawn's role was
|
|
69
69
|
* absent for a non-admin/non-public request; null now means "the body named
|
|
70
70
|
* none", and the handler resolves it against the prior sidecar before
|
|
71
|
-
* deciding whether that is an error. `passive` is not accepted here:
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
71
|
+
* deciding whether that is an error. `passive` is not accepted here: it is a
|
|
72
|
+
* /public-spawn persona role, resolved at that boundary from role:'public'
|
|
73
|
+
* plus the passive flag, and no client may POST it.
|
|
74
|
+
*
|
|
75
|
+
* Task 2423 — rc-spawn DOES compose a persona now, for role:'public' only: the
|
|
76
|
+
* named agent's IDENTITY/SOUL/KNOWLEDGE via composeAppendSystemPrompt, the same
|
|
77
|
+
* composer /public-spawn uses. An admin spawn is unchanged and still composes
|
|
78
|
+
* only reseat / voice / specialist-domains / authenticated-identity fragments. */
|
|
75
79
|
export function parseRcSpawnRole(body) {
|
|
76
80
|
return body.role === 'admin' || body.role === 'public' ? body.role : null;
|
|
77
81
|
}
|
|
@@ -2798,6 +2802,11 @@ export function buildHttpApp(deps) {
|
|
|
2798
2802
|
// sidecar, so the refusal is not here; it is where both sources have been
|
|
2799
2803
|
// consulted, just below `priorSidecar`.
|
|
2800
2804
|
const rcRole = parseRcSpawnRole(body);
|
|
2805
|
+
// Task 2423 — the agent a public bot answers as. Same validation
|
|
2806
|
+
// /public-spawn applies, so one grammar governs both doors.
|
|
2807
|
+
const rcAgentSlug = typeof body.agentSlug === 'string' && /^[A-Za-z0-9_-]{1,64}$/.test(body.agentSlug)
|
|
2808
|
+
? body.agentSlug
|
|
2809
|
+
: null;
|
|
2801
2810
|
const adminChannel = typeof body.channel === 'string' && CHANNELS.includes(body.channel)
|
|
2802
2811
|
? body.channel
|
|
2803
2812
|
: undefined;
|
|
@@ -3470,6 +3479,31 @@ export function buildHttpApp(deps) {
|
|
|
3470
3479
|
appendSegments.push(authenticatedIdentityPrompt(resolvedName));
|
|
3471
3480
|
deps.logger(`[rc-spawn] op=identity-injected userId=${resolvedAdminUserId ? resolvedAdminUserId.slice(0, 8) : 'none'} resolved=${resolvedName ? 1 : 0} source=${identitySource}`);
|
|
3472
3481
|
}
|
|
3482
|
+
// Task 2423 — a public rc spawn composes its named agent's persona. This is
|
|
3483
|
+
// the one thing Task 2368 moved public Telegram off this door for, and it is
|
|
3484
|
+
// why that path landed on /public-spawn, which does not deliver at all
|
|
3485
|
+
// (Task 2420: a hand-spawn reproducing its argv AND all 60 env vars replies;
|
|
3486
|
+
// only spawnClaudeSession does not). The composer is the same one
|
|
3487
|
+
// /public-spawn uses, so the two doors cannot disagree about what a public
|
|
3488
|
+
// agent's prompt is. Refuses rather than answering personaless: a visitor
|
|
3489
|
+
// met by an agent with no identity is worse than one met by silence, and the
|
|
3490
|
+
// 503 matches the shape the other pre-spawn refusals on this door return.
|
|
3491
|
+
//
|
|
3492
|
+
// Gated on the slug being SUPPLIED, not on the role alone. A public WhatsApp
|
|
3493
|
+
// spawn also reaches this door with role:'public' and no slug, and has
|
|
3494
|
+
// always composed no persona here; refusing those would be a regression in
|
|
3495
|
+
// a path this task does not touch. Three cases, not four: slug present and
|
|
3496
|
+
// resolves, compose; slug present and does not resolve, refuse; slug absent,
|
|
3497
|
+
// unchanged.
|
|
3498
|
+
if (effectiveRcRole === 'public' && rcAgentSlug) {
|
|
3499
|
+
const composed = composeAppendSystemPrompt(deps.host, { role: 'public', agentSlug: rcAgentSlug, accountDir: effectiveAccountDir }, {});
|
|
3500
|
+
if (!composed.ok) {
|
|
3501
|
+
deps.logger(`[rc-spawn] op=public-persona-refused reason=${composed.reason} slug=${rcAgentSlug} detail=${composed.detail}`);
|
|
3502
|
+
return c.json({ error: 'spawn-failed', reason: composed.reason }, 503);
|
|
3503
|
+
}
|
|
3504
|
+
appendSegments.push(composed.block);
|
|
3505
|
+
deps.logger(`[rc-spawn] op=public-persona-composed slug=${rcAgentSlug} identityBytes=${composed.identityBytes} soulBytes=${composed.soulBytes} knowledgeBytes=${composed.knowledgeBytes}`);
|
|
3506
|
+
}
|
|
3473
3507
|
if (appendSegments.length > 0) {
|
|
3474
3508
|
argv.push('--append-system-prompt', appendSegments.join('\n\n'));
|
|
3475
3509
|
}
|