@lawaty/lacode 0.4.2 → 0.5.0
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.md +29 -1
- package/README.md +13 -2
- package/dist/agents.js +67 -0
- package/dist/commands.js +13 -0
- package/dist/index.js +101 -6
- package/dist/pool.js +2 -2
- package/docs/CONFIGURATION.md +4 -2
- package/docs/OMNIROUTE.md +1 -1
- package/package.json +1 -1
- package/presets/README.md +2 -2
- package/presets/free-tier.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,33 @@ commit as the changelog entry it describes.
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [0.5.0] — a user-only /manager-directive, instant limit failover, and the longcat preset
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **`/manager-directive <text>`** — a client-only command that runs a hidden
|
|
17
|
+
`manager` agent to apply your instruction to `.opencode/context/manager.md`
|
|
18
|
+
(record it, move it between sections, rewrite `## Current`, …). The `manager`
|
|
19
|
+
agent is the only agent permitted to edit that file, its grant is scoped to
|
|
20
|
+
`manager.md` alone, and no other agent can spawn it — the command is the only
|
|
21
|
+
route in. The one-writer guard (`writer-rule.ts` Direction 3) now exempts
|
|
22
|
+
exactly that agent name and still reports every other writer.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- **Bundled pool preset** — `opencode-go/step-5-preview-free` and
|
|
27
|
+
`opencode/step-5-preview-free` were retired upstream. Slots 2 and 4 now name
|
|
28
|
+
`longcat-2.5-preview-free` on the same providers (`opencode-go`, `opencode`).
|
|
29
|
+
Update `~/.config/lacode/pool.txt` if you pinned the old ids there.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- **Immediate failover on a usage limit** — when a limit-shaped assistant error or
|
|
34
|
+
a limit-shaped `session.status` retry names the session an in-flight claim is
|
|
35
|
+
running on, the pool aborts that session at once instead of leaving it for the
|
|
36
|
+
~5-minute hang reaper. The claim is released and the next spawn routes to a slot
|
|
37
|
+
that answers; a limit on an unrelated session never kills a claim.
|
|
38
|
+
|
|
12
39
|
## [0.4.2] — fix silent claim eviction, unkillable chatty tasks, and gateway 5xx misclassification
|
|
13
40
|
|
|
14
41
|
### Fixed
|
|
@@ -166,7 +193,8 @@ commit as the changelog entry it describes.
|
|
|
166
193
|
|
|
167
194
|
- First published release: pooled subagents over a shared set of models.
|
|
168
195
|
|
|
169
|
-
[Unreleased]: https://github.com/lawaty/lacode/compare/v0.
|
|
196
|
+
[Unreleased]: https://github.com/lawaty/lacode/compare/v0.5.0...HEAD
|
|
197
|
+
[0.5.0]: https://github.com/lawaty/lacode/compare/v0.4.2...v0.5.0
|
|
170
198
|
[0.4.1]: https://github.com/lawaty/lacode/compare/v0.4.0...v0.4.1
|
|
171
199
|
[0.4.0]: https://github.com/lawaty/lacode/compare/v0.3.0...v0.4.0
|
|
172
200
|
[0.3.0]: https://github.com/lawaty/lacode/compare/v0.2.2...v0.3.0
|
package/README.md
CHANGED
|
@@ -178,8 +178,9 @@ only thing that works: naming a variant lets the caller choose the provider
|
|
|
178
178
|
directly, which is exactly what the rotation and the per-slot ceiling exist to
|
|
179
179
|
prevent, so it is refused.
|
|
180
180
|
|
|
181
|
-
|
|
182
|
-
`/lacode-doctor`. Everything is added **only when your
|
|
181
|
+
Five commands: `/context-init`, `/context-update`, `/context-review`,
|
|
182
|
+
`/lacode-doctor` and `/manager-directive`. Everything is added **only when your
|
|
183
|
+
config does not already
|
|
183
184
|
define it** — an agent or command you wrote yourself is left byte-identical,
|
|
184
185
|
never patched or partially merged. `{"enabled": false}` turns the whole thing off
|
|
185
186
|
without leaving a trace. The exact injection list, and the three keys that are
|
|
@@ -212,6 +213,7 @@ are reading:
|
|
|
212
213
|
/context-update # incremental refresh; accepts a focus, e.g. /context-update src/payments
|
|
213
214
|
/context-review # audit it for staleness, contradictions, duplication and bloat
|
|
214
215
|
/lacode-doctor # check the pool, model resolution and the OmniRoute gateway. Report only
|
|
216
|
+
/manager-directive # record a managerial direction in manager.md — client-only, see below
|
|
215
217
|
```
|
|
216
218
|
|
|
217
219
|
The map is local and untracked — it describes your checkout on your machine, so
|
|
@@ -251,6 +253,15 @@ agent working in your live session may write it only when you explicitly ask
|
|
|
251
253
|
it to, and only what you asked for — never on its own initiative, and never
|
|
252
254
|
to "keep it up to date".
|
|
253
255
|
|
|
256
|
+
**`/manager-directive` — the one sanctioned write path.** Type
|
|
257
|
+
`/manager-directive <text>` and the `manager` agent applies your instruction to
|
|
258
|
+
`manager.md` — recording it, moving it between sections, rewriting `## Current`,
|
|
259
|
+
whatever you asked — then reports what it changed. It is a normal agent turn, not
|
|
260
|
+
a dead append: tell it what you want and review the result. The `manager` agent
|
|
261
|
+
is the one agent permitted to edit that file, its grant is scoped to
|
|
262
|
+
`manager.md` alone, and no other agent can spawn it — the command is the only
|
|
263
|
+
route in.
|
|
264
|
+
|
|
254
265
|
## Cost
|
|
255
266
|
|
|
256
267
|
The mechanism is specific and small: bulk exploration, implementation and
|
package/dist/agents.js
CHANGED
|
@@ -46,6 +46,9 @@ var USER_MODELS_PATH = join2(USER_CONFIG_DIR, "pool.json");
|
|
|
46
46
|
var USER_MODELS_TXT = join2(USER_CONFIG_DIR, "pool.txt");
|
|
47
47
|
var CHILD_MEMORY_MS = 30 * 6e4;
|
|
48
48
|
|
|
49
|
+
// src/lib/manager-directive.ts
|
|
50
|
+
var MANAGER_AGENT_NAME = "manager";
|
|
51
|
+
|
|
49
52
|
// src/agents.ts
|
|
50
53
|
var POOLED_BASES = ["explore", "implement", "context"];
|
|
51
54
|
var AGENTS = {
|
|
@@ -722,6 +725,68 @@ Finish with a short report:
|
|
|
722
725
|
}
|
|
723
726
|
}
|
|
724
727
|
};
|
|
728
|
+
var MANAGER_AGENT = {
|
|
729
|
+
description: "Records and edits the client's .opencode/context/manager.md. Run only by the /manager-directive command; no agent or subagent can spawn it.",
|
|
730
|
+
mode: "subagent",
|
|
731
|
+
hidden: true,
|
|
732
|
+
temperature: 0.1,
|
|
733
|
+
prompt: `# Role
|
|
734
|
+
|
|
735
|
+
You maintain one file and nothing else: \`.opencode/context/manager.md\`, the
|
|
736
|
+
client's own statement of intent. You are not a coding agent and you touch no
|
|
737
|
+
other path.
|
|
738
|
+
|
|
739
|
+
You are run only by the \`/manager-directive\` command. The client tells you what
|
|
740
|
+
they want recorded or changed in the file; you apply that to the file and report
|
|
741
|
+
what you changed. You have no permission to edit anything else, and you need
|
|
742
|
+
none.
|
|
743
|
+
|
|
744
|
+
# The file
|
|
745
|
+
|
|
746
|
+
It records what is being worked on (\`## Current\`), what comes next
|
|
747
|
+
(\`## Next\`), what the client has ratified (\`## Confirmed\`), and standing
|
|
748
|
+
direction (\`## Instructions\`). The client may use any headings \u2014 every heading
|
|
749
|
+
is direction, and the four above are a convenience, not a schema.
|
|
750
|
+
|
|
751
|
+
# How to apply a directive
|
|
752
|
+
|
|
753
|
+
- Do what the client asked. "Append", "rewrite Current", "move X to Confirmed",
|
|
754
|
+
"add a heading" all mean exactly that; a bare sentence goes under the most
|
|
755
|
+
fitting existing heading, and you create one only if none fits.
|
|
756
|
+
- Never invent facts, never record your own inference as the client's, and never
|
|
757
|
+
tidy, reorder or delete content the client did not ask you to touch.
|
|
758
|
+
- \`## Current\` is rewritten, not appended to, so it does not become a running
|
|
759
|
+
history.
|
|
760
|
+
- Keep the file concise. If it is growing past roughly 150 lines, say so.
|
|
761
|
+
|
|
762
|
+
# Reporting
|
|
763
|
+
|
|
764
|
+
Report briefly what you changed and where. Do not edit any other file.`,
|
|
765
|
+
permission: {
|
|
766
|
+
// Read is scoped to the one file too: the directive is about manager.md, and
|
|
767
|
+
// nothing else is any of this agent's business.
|
|
768
|
+
read: {
|
|
769
|
+
"*": "deny",
|
|
770
|
+
".opencode/context/manager.md": "allow",
|
|
771
|
+
"*/.opencode/context/manager.md": "allow"
|
|
772
|
+
},
|
|
773
|
+
glob: "deny",
|
|
774
|
+
grep: "deny",
|
|
775
|
+
list: "deny",
|
|
776
|
+
edit: {
|
|
777
|
+
"*": "deny",
|
|
778
|
+
".opencode/context/manager.md": "allow",
|
|
779
|
+
"*/.opencode/context/manager.md": "allow"
|
|
780
|
+
},
|
|
781
|
+
bash: "deny",
|
|
782
|
+
webfetch: "deny",
|
|
783
|
+
websearch: "deny",
|
|
784
|
+
question: "allow",
|
|
785
|
+
task: {
|
|
786
|
+
"*": "deny"
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
};
|
|
725
790
|
function buildAgentSet() {
|
|
726
791
|
const set = { ...AGENTS };
|
|
727
792
|
for (const base of POOLED_BASES) {
|
|
@@ -737,6 +802,7 @@ function buildAgentSet() {
|
|
|
737
802
|
set[`${base}-${slot}`] = variant;
|
|
738
803
|
}
|
|
739
804
|
}
|
|
805
|
+
set[MANAGER_AGENT_NAME] = MANAGER_AGENT;
|
|
740
806
|
return set;
|
|
741
807
|
}
|
|
742
808
|
function applyAgents(cfg, agents = buildAgentSet(), opts = {}) {
|
|
@@ -756,6 +822,7 @@ function applyAgents(cfg, agents = buildAgentSet(), opts = {}) {
|
|
|
756
822
|
}
|
|
757
823
|
export {
|
|
758
824
|
AGENTS,
|
|
825
|
+
MANAGER_AGENT,
|
|
759
826
|
POOLED_BASES,
|
|
760
827
|
applyAgents,
|
|
761
828
|
buildAgentSet
|
package/dist/commands.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// src/lib/manager-directive.ts
|
|
2
|
+
var MANAGER_AGENT_NAME = "manager";
|
|
3
|
+
|
|
1
4
|
// src/commands.ts
|
|
2
5
|
var COMMANDS = {
|
|
3
6
|
"context-init": {
|
|
@@ -77,6 +80,16 @@ Audit the five context files for:
|
|
|
77
80
|
|
|
78
81
|
Fix what you find in \`.opencode/context/\` only, then report: findings, fixes,
|
|
79
82
|
and anything you could not verify.`
|
|
83
|
+
},
|
|
84
|
+
"manager-directive": {
|
|
85
|
+
description: "Apply a client instruction to .opencode/context/manager.md. Client-only \u2014 runs the manager agent, the only writer of that file.",
|
|
86
|
+
agent: MANAGER_AGENT_NAME,
|
|
87
|
+
subtask: true,
|
|
88
|
+
template: `Apply this instruction to \`.opencode/context/manager.md\`, and edit only that file:
|
|
89
|
+
|
|
90
|
+
$ARGUMENTS
|
|
91
|
+
|
|
92
|
+
Follow the rules for the file in your system prompt, then report what you changed.`
|
|
80
93
|
},
|
|
81
94
|
"lacode-doctor": {
|
|
82
95
|
description: "Check the LaCode setup \u2014 pool slots, model resolution, and the OmniRoute gateway. Report only; changes nothing.",
|
package/dist/index.js
CHANGED
|
@@ -49,9 +49,9 @@ var USER_MODELS_PATH = join2(USER_CONFIG_DIR, "pool.json");
|
|
|
49
49
|
var USER_MODELS_TXT = join2(USER_CONFIG_DIR, "pool.txt");
|
|
50
50
|
var FALLBACK_SLOTS = [
|
|
51
51
|
{ index: 1, model: "opencode/space-bunny-free", weight: 3 },
|
|
52
|
-
{ index: 2, model: "opencode-go/
|
|
52
|
+
{ index: 2, model: "opencode-go/longcat-2.5-preview-free", weight: 2 },
|
|
53
53
|
{ index: 3, model: "opencode/big-pickle", weight: 1 },
|
|
54
|
-
{ index: 4, model: "opencode/
|
|
54
|
+
{ index: 4, model: "opencode/longcat-2.5-preview-free", weight: 1 }
|
|
55
55
|
];
|
|
56
56
|
function parseSlots(parsed, origin) {
|
|
57
57
|
const problems = [];
|
|
@@ -611,6 +611,9 @@ function coolProvider(s, provider, until, reason) {
|
|
|
611
611
|
return affected;
|
|
612
612
|
}
|
|
613
613
|
|
|
614
|
+
// src/lib/manager-directive.ts
|
|
615
|
+
var MANAGER_AGENT_NAME = "manager";
|
|
616
|
+
|
|
614
617
|
// src/agents.ts
|
|
615
618
|
var POOLED_BASES = ["explore", "implement", "context"];
|
|
616
619
|
var AGENTS = {
|
|
@@ -1287,6 +1290,68 @@ Finish with a short report:
|
|
|
1287
1290
|
}
|
|
1288
1291
|
}
|
|
1289
1292
|
};
|
|
1293
|
+
var MANAGER_AGENT = {
|
|
1294
|
+
description: "Records and edits the client's .opencode/context/manager.md. Run only by the /manager-directive command; no agent or subagent can spawn it.",
|
|
1295
|
+
mode: "subagent",
|
|
1296
|
+
hidden: true,
|
|
1297
|
+
temperature: 0.1,
|
|
1298
|
+
prompt: `# Role
|
|
1299
|
+
|
|
1300
|
+
You maintain one file and nothing else: \`.opencode/context/manager.md\`, the
|
|
1301
|
+
client's own statement of intent. You are not a coding agent and you touch no
|
|
1302
|
+
other path.
|
|
1303
|
+
|
|
1304
|
+
You are run only by the \`/manager-directive\` command. The client tells you what
|
|
1305
|
+
they want recorded or changed in the file; you apply that to the file and report
|
|
1306
|
+
what you changed. You have no permission to edit anything else, and you need
|
|
1307
|
+
none.
|
|
1308
|
+
|
|
1309
|
+
# The file
|
|
1310
|
+
|
|
1311
|
+
It records what is being worked on (\`## Current\`), what comes next
|
|
1312
|
+
(\`## Next\`), what the client has ratified (\`## Confirmed\`), and standing
|
|
1313
|
+
direction (\`## Instructions\`). The client may use any headings \u2014 every heading
|
|
1314
|
+
is direction, and the four above are a convenience, not a schema.
|
|
1315
|
+
|
|
1316
|
+
# How to apply a directive
|
|
1317
|
+
|
|
1318
|
+
- Do what the client asked. "Append", "rewrite Current", "move X to Confirmed",
|
|
1319
|
+
"add a heading" all mean exactly that; a bare sentence goes under the most
|
|
1320
|
+
fitting existing heading, and you create one only if none fits.
|
|
1321
|
+
- Never invent facts, never record your own inference as the client's, and never
|
|
1322
|
+
tidy, reorder or delete content the client did not ask you to touch.
|
|
1323
|
+
- \`## Current\` is rewritten, not appended to, so it does not become a running
|
|
1324
|
+
history.
|
|
1325
|
+
- Keep the file concise. If it is growing past roughly 150 lines, say so.
|
|
1326
|
+
|
|
1327
|
+
# Reporting
|
|
1328
|
+
|
|
1329
|
+
Report briefly what you changed and where. Do not edit any other file.`,
|
|
1330
|
+
permission: {
|
|
1331
|
+
// Read is scoped to the one file too: the directive is about manager.md, and
|
|
1332
|
+
// nothing else is any of this agent's business.
|
|
1333
|
+
read: {
|
|
1334
|
+
"*": "deny",
|
|
1335
|
+
".opencode/context/manager.md": "allow",
|
|
1336
|
+
"*/.opencode/context/manager.md": "allow"
|
|
1337
|
+
},
|
|
1338
|
+
glob: "deny",
|
|
1339
|
+
grep: "deny",
|
|
1340
|
+
list: "deny",
|
|
1341
|
+
edit: {
|
|
1342
|
+
"*": "deny",
|
|
1343
|
+
".opencode/context/manager.md": "allow",
|
|
1344
|
+
"*/.opencode/context/manager.md": "allow"
|
|
1345
|
+
},
|
|
1346
|
+
bash: "deny",
|
|
1347
|
+
webfetch: "deny",
|
|
1348
|
+
websearch: "deny",
|
|
1349
|
+
question: "allow",
|
|
1350
|
+
task: {
|
|
1351
|
+
"*": "deny"
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
};
|
|
1290
1355
|
function buildAgentSet() {
|
|
1291
1356
|
const set = { ...AGENTS };
|
|
1292
1357
|
for (const base of POOLED_BASES) {
|
|
@@ -1302,6 +1367,7 @@ function buildAgentSet() {
|
|
|
1302
1367
|
set[`${base}-${slot}`] = variant;
|
|
1303
1368
|
}
|
|
1304
1369
|
}
|
|
1370
|
+
set[MANAGER_AGENT_NAME] = MANAGER_AGENT;
|
|
1305
1371
|
return set;
|
|
1306
1372
|
}
|
|
1307
1373
|
function applyAgents(cfg, agents = buildAgentSet(), opts = {}) {
|
|
@@ -1399,6 +1465,16 @@ Audit the five context files for:
|
|
|
1399
1465
|
|
|
1400
1466
|
Fix what you find in \`.opencode/context/\` only, then report: findings, fixes,
|
|
1401
1467
|
and anything you could not verify.`
|
|
1468
|
+
},
|
|
1469
|
+
"manager-directive": {
|
|
1470
|
+
description: "Apply a client instruction to .opencode/context/manager.md. Client-only \u2014 runs the manager agent, the only writer of that file.",
|
|
1471
|
+
agent: MANAGER_AGENT_NAME,
|
|
1472
|
+
subtask: true,
|
|
1473
|
+
template: `Apply this instruction to \`.opencode/context/manager.md\`, and edit only that file:
|
|
1474
|
+
|
|
1475
|
+
$ARGUMENTS
|
|
1476
|
+
|
|
1477
|
+
Follow the rules for the file in your system prompt, then report what you changed.`
|
|
1402
1478
|
},
|
|
1403
1479
|
"lacode-doctor": {
|
|
1404
1480
|
description: "Check the LaCode setup \u2014 pool slots, model resolution, and the OmniRoute gateway. Report only; changes nothing.",
|
|
@@ -1527,6 +1603,19 @@ var agentPoolHooks = (async ({ client, serverUrl }, options) => {
|
|
|
1527
1603
|
const strike = recordStrike(s, claim.m, "hang", HANG_FALLBACK_MS);
|
|
1528
1604
|
if (strike) await log2("warn", `cooling ${claim.m} for ${Math.round((strike.until - now) / 1e3)}s (hang)`);
|
|
1529
1605
|
};
|
|
1606
|
+
const failOver = async (sessionID, model, reason) => {
|
|
1607
|
+
if (!sessionID) return;
|
|
1608
|
+
const entry = [...s.own.entries()].find(([, claim2]) => claim2.g === sessionID);
|
|
1609
|
+
if (!entry) return;
|
|
1610
|
+
const [key, claim] = entry;
|
|
1611
|
+
const kill = await abortSession(sessionID);
|
|
1612
|
+
release(s, key);
|
|
1613
|
+
await log(
|
|
1614
|
+
"warn",
|
|
1615
|
+
kill.ok ? `limit (${reason}) on ${model}; aborted in-flight ${claim.v} session ${sessionID} to fail over now` : `limit (${reason}) on ${model}; released in-flight ${claim.v} claim after a failed abort (${kill.why})`,
|
|
1616
|
+
{ base: claim.v, model: claim.m, slot: claim.k, session: sessionID, aborted: kill.ok, reason }
|
|
1617
|
+
);
|
|
1618
|
+
};
|
|
1530
1619
|
const reapStuck = async () => {
|
|
1531
1620
|
if (s.reaping) return;
|
|
1532
1621
|
s.reaping = true;
|
|
@@ -1642,18 +1731,21 @@ var agentPoolHooks = (async ({ client, serverUrl }, options) => {
|
|
|
1642
1731
|
try {
|
|
1643
1732
|
const props = event.properties ?? {};
|
|
1644
1733
|
if (event.type === "session.status") {
|
|
1734
|
+
const sessionID = typeof props.sessionID === "string" ? props.sessionID : void 0;
|
|
1645
1735
|
const status2 = props.status;
|
|
1646
|
-
|
|
1736
|
+
const reason2 = status2?.action?.reason ?? "";
|
|
1737
|
+
if (status2?.type === "retry" && status2.action?.provider && (reason2 === "free_tier_limit" || isLimitText(reason2))) {
|
|
1647
1738
|
const provider = status2.action.provider;
|
|
1648
1739
|
const until = typeof status2.next === "number" && status2.next > Date.now() ? status2.next : Date.now() + HANG_FALLBACK_MS;
|
|
1649
|
-
const cooled = coolProvider(s, provider, until, "
|
|
1740
|
+
const cooled = coolProvider(s, provider, until, reason2 || "rate-limited");
|
|
1650
1741
|
if (cooled.length > 0) {
|
|
1651
|
-
await log("warn", `
|
|
1742
|
+
await log("warn", `limit on provider ${provider} (${reason2 || "rate-limited"}); cooling ${cooled.length} slot(s) until ${new Date(until).toISOString().slice(11, 19)}Z`, {
|
|
1652
1743
|
provider,
|
|
1653
1744
|
until,
|
|
1654
1745
|
models: cooled.map((c) => c.model)
|
|
1655
1746
|
});
|
|
1656
1747
|
}
|
|
1748
|
+
await failOver(sessionID, provider, reason2 || "rate-limited");
|
|
1657
1749
|
}
|
|
1658
1750
|
return;
|
|
1659
1751
|
}
|
|
@@ -1687,6 +1779,7 @@ var agentPoolHooks = (async ({ client, serverUrl }, options) => {
|
|
|
1687
1779
|
const reason = haystack.match(/rate[\s_-]?limit|usage limit|too many requests|quota|freelimiterror|overloaded/i)?.[0]?.toLowerCase().replace(/[\s_]/g, "-");
|
|
1688
1780
|
const strike = recordStrike(s, model, reason ?? "rate-limited", stated);
|
|
1689
1781
|
if (strike) await log("warn", `cooling ${model} for ${Math.round((strike.until - Date.now()) / 1e3)}s (${reason ?? "rate-limited"}, strike ${strike.strikes})`);
|
|
1782
|
+
await failOver(info.sessionID, model, reason ?? "rate-limited");
|
|
1690
1783
|
} catch (error) {
|
|
1691
1784
|
await log("error", `limit detection failed: ${String(error)}`);
|
|
1692
1785
|
}
|
|
@@ -1858,6 +1951,7 @@ import { readFileSync as readFileSync3 } from "node:fs";
|
|
|
1858
1951
|
import * as path from "node:path";
|
|
1859
1952
|
var CONTEXT_DIR = path.join(".opencode", "context");
|
|
1860
1953
|
var WRITER = "context";
|
|
1954
|
+
var MANAGER_WRITER = MANAGER_AGENT_NAME;
|
|
1861
1955
|
var toPosix = (value) => value.split(path.sep).join("/");
|
|
1862
1956
|
var compilePattern = (pattern) => {
|
|
1863
1957
|
const escaped = toPosix(pattern).replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
@@ -1926,11 +2020,12 @@ var verifyWriterRule = (configPath, mapDir, projectRoot) => {
|
|
|
1926
2020
|
if (agentDefs) {
|
|
1927
2021
|
const writers = [];
|
|
1928
2022
|
for (const [name, def] of Object.entries(agentDefs)) {
|
|
2023
|
+
if (name === MANAGER_WRITER) continue;
|
|
1929
2024
|
const win = winningRule(permissionOf(def), managerPath);
|
|
1930
2025
|
if (win?.action === "allow") writers.push(`${name} (edit rule "${win.pattern}": "${win.action}")`);
|
|
1931
2026
|
}
|
|
1932
2027
|
if (writers.length > 0) {
|
|
1933
|
-
return `${CONTEXT_DIR}/manager.md is writable by permission: ${writers.join(", ")}. It is the client's own file \u2014 what they are working on, what is ratified, what every agent must respect \u2014 and
|
|
2028
|
+
return `${CONTEXT_DIR}/manager.md is writable by permission: ${writers.join(", ")}. It is the client's own file \u2014 what they are working on, what is ratified, what every agent must respect \u2014 and only the \`${MANAGER_WRITER}\` agent (run solely by /manager-directive) may write it. Deny it LAST on every other agent, after any broad allow, because opencode takes the LAST matching rule: "*": "allow", "*/${CONTEXT_DIR}/manager.md": "deny", "${CONTEXT_DIR}/manager.md": "deny". Declare this on every agent above, including \`${WRITER}\`: an agent you define yourself replaces the shipped one wholesale, permission and all.`;
|
|
1934
2029
|
}
|
|
1935
2030
|
}
|
|
1936
2031
|
return void 0;
|
package/dist/pool.js
CHANGED
|
@@ -49,9 +49,9 @@ var USER_MODELS_PATH = join2(USER_CONFIG_DIR, "pool.json");
|
|
|
49
49
|
var USER_MODELS_TXT = join2(USER_CONFIG_DIR, "pool.txt");
|
|
50
50
|
var FALLBACK_SLOTS = [
|
|
51
51
|
{ index: 1, model: "opencode/space-bunny-free", weight: 3 },
|
|
52
|
-
{ index: 2, model: "opencode-go/
|
|
52
|
+
{ index: 2, model: "opencode-go/longcat-2.5-preview-free", weight: 2 },
|
|
53
53
|
{ index: 3, model: "opencode/big-pickle", weight: 1 },
|
|
54
|
-
{ index: 4, model: "opencode/
|
|
54
|
+
{ index: 4, model: "opencode/longcat-2.5-preview-free", weight: 1 }
|
|
55
55
|
];
|
|
56
56
|
function parseSlots(parsed, origin) {
|
|
57
57
|
const problems = [];
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -75,9 +75,11 @@ process.
|
|
|
75
75
|
|
|
76
76
|
## What gets injected
|
|
77
77
|
|
|
78
|
-
**
|
|
78
|
+
**18 agent keys**: the 5 bases `build`, `plan`, `explore`, `implement`,
|
|
79
79
|
`context`, plus one hidden
|
|
80
|
-
`<base>-1`..`-4` pool variant for each of the 3 pooled bases
|
|
80
|
+
`<base>-1`..`-4` pool variant for each of the 3 pooled bases, plus the hidden
|
|
81
|
+
`manager` agent — the one writer of `.opencode/context/manager.md`, reachable
|
|
82
|
+
only from the `/manager-directive` command.
|
|
81
83
|
|
|
82
84
|
`build` and `plan` exist in opencode as built-ins, so on a stock config those
|
|
83
85
|
two keys are already taken and merge-without-clobber keeps the built-in — the
|
package/docs/OMNIROUTE.md
CHANGED
package/package.json
CHANGED
package/presets/README.md
CHANGED
|
@@ -36,10 +36,10 @@ in place, no JSON and no objects:
|
|
|
36
36
|
|
|
37
37
|
```text
|
|
38
38
|
# provider/model-id weight
|
|
39
|
-
opencode-go/
|
|
39
|
+
opencode-go/longcat-2.5-preview-free 3
|
|
40
40
|
opencode/space-bunny-free 2
|
|
41
41
|
opencode/big-pickle 1
|
|
42
|
-
opencode/
|
|
42
|
+
opencode/longcat-2.5-preview-free 1
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
`#` starts a comment (whole-line or trailing), blank lines are ignored, and the
|
package/presets/free-tier.json
CHANGED
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
" 3. once EVERY slot is at its ceiling, the overflow goes to the least loaded slot,",
|
|
26
26
|
" so a burst past the summed ceilings degrades to even spreading and never blocks",
|
|
27
27
|
"",
|
|
28
|
-
"With this file: space-bunny takes 3 consecutive turns, then
|
|
29
|
-
"(opencode-go) takes 2, then big-pickle and
|
|
28
|
+
"With this file: space-bunny takes 3 consecutive turns, then longcat-2.5-preview",
|
|
29
|
+
"(opencode-go) takes 2, then big-pickle and longcat-2.5-preview (Zen) take 1 each -- sequence 1,1,1,2,2,3,4,...",
|
|
30
30
|
"Equal weights give a plain round robin in list order. 2,2,1,1 reproduces the old",
|
|
31
31
|
"primary/overflow cycle exactly, which is why the old tier values still work.",
|
|
32
32
|
"",
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
],
|
|
68
68
|
"slots": [
|
|
69
69
|
{ "model": "opencode/space-bunny-free", "weight": 3 },
|
|
70
|
-
{ "model": "opencode-go/
|
|
70
|
+
{ "model": "opencode-go/longcat-2.5-preview-free", "weight": 2 },
|
|
71
71
|
{ "model": "opencode/big-pickle", "weight": 1 },
|
|
72
|
-
{ "model": "opencode/
|
|
72
|
+
{ "model": "opencode/longcat-2.5-preview-free", "weight": 1 }
|
|
73
73
|
]
|
|
74
74
|
}
|