@lawaty/lacode 0.5.0 → 0.7.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 +45 -1
- package/README.md +59 -53
- package/dist/agents.js +69 -78
- package/dist/commands.js +90 -63
- package/dist/index.js +118 -194
- package/docs/BENCHMARKS.md +7 -9
- package/docs/CONFIGURATION.md +12 -12
- package/docs/CONTEXT.md +39 -26
- package/docs/INSTALL.md +16 -16
- package/docs/OMNIROUTE.md +12 -25
- package/docs/TUNNEL.md +16 -14
- package/docs/USER-STORIES.md +25 -26
- package/package.json +4 -1
package/dist/commands.js
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
|
-
// src/lib/
|
|
2
|
-
var
|
|
1
|
+
// src/lib/agent-instructions.ts
|
|
2
|
+
var AGENT_INSTRUCTIONS_COMMAND = "agent-instructions";
|
|
3
|
+
var AGENT_INSTRUCTIONS_WRITER = "instructions";
|
|
4
|
+
|
|
5
|
+
// src/lib/pool.ts
|
|
6
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync as readFileSync2, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { homedir } from "node:os";
|
|
8
|
+
import { join as join2 } from "node:path";
|
|
9
|
+
|
|
10
|
+
// src/lib/package-root.ts
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
12
|
+
import { dirname, join } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
var PACKAGE_NAME = "@lawaty/lacode";
|
|
15
|
+
var findPackageRoot = (start) => {
|
|
16
|
+
let dir = start;
|
|
17
|
+
for (let up = 0; up < 6; up++) {
|
|
18
|
+
try {
|
|
19
|
+
if (JSON.parse(readFileSync(join(dir, "package.json"), "utf8")).name === PACKAGE_NAME) return dir;
|
|
20
|
+
} catch {
|
|
21
|
+
}
|
|
22
|
+
const parent = dirname(dir);
|
|
23
|
+
if (parent === dir) break;
|
|
24
|
+
dir = parent;
|
|
25
|
+
}
|
|
26
|
+
return join(start, "..", "..");
|
|
27
|
+
};
|
|
28
|
+
var PACKAGE_ROOT = findPackageRoot(dirname(fileURLToPath(import.meta.url)));
|
|
29
|
+
|
|
30
|
+
// src/lib/pool.ts
|
|
31
|
+
var CLAIM_TTL_MS = 10 * 60 * 1e3;
|
|
32
|
+
var DEAD_FILE_MS = 2 * CLAIM_TTL_MS;
|
|
33
|
+
var ORPHAN_TMP_MS = 60 * 1e3;
|
|
34
|
+
var CATALOG_TTL_MS = 10 * 60 * 1e3;
|
|
35
|
+
var STUCK_MIN_AGE_MS = 5 * 60 * 1e3;
|
|
36
|
+
var STUCK_IDLE_MS = 3 * 60 * 1e3;
|
|
37
|
+
var MAX_CLAIM_AGE_MS = 30 * 60 * 1e3;
|
|
38
|
+
var HANG_COOLDOWN_MS = 10 * 60 * 1e3;
|
|
39
|
+
var UNAVAILABLE_COOLDOWN_MS = 60 * 1e3;
|
|
40
|
+
var MAX_WEIGHT = 100;
|
|
41
|
+
var MAX_SLOTS = 4;
|
|
42
|
+
var COOLDOWN_BASE_MS = 60 * 1e3;
|
|
43
|
+
var COOLDOWN_MAX_MS = 15 * 60 * 1e3;
|
|
44
|
+
var QUOTA_COOLDOWN_BASE_MS = 15 * 60 * 1e3;
|
|
45
|
+
var QUOTA_COOLDOWN_MAX_MS = 6 * 60 * 60 * 1e3;
|
|
46
|
+
var STRIKE_DECAY_MS = 30 * 60 * 1e3;
|
|
47
|
+
var PRESETS_DIR = join2(PACKAGE_ROOT, "presets");
|
|
48
|
+
var DEFAULT_PRESET = join2(PRESETS_DIR, "free-tier.json");
|
|
49
|
+
var USER_CONFIG_DIR = join2(homedir(), ".config", "lacode");
|
|
50
|
+
var USER_MODELS_PATH = join2(USER_CONFIG_DIR, "pool.json");
|
|
51
|
+
var USER_MODELS_TXT = join2(USER_CONFIG_DIR, "pool.txt");
|
|
52
|
+
var CHILD_MEMORY_MS = 30 * 6e4;
|
|
3
53
|
|
|
4
54
|
// src/commands.ts
|
|
5
55
|
var COMMANDS = {
|
|
@@ -22,13 +72,7 @@ Remember: write conclusions, not inventories. Link to the project's existing
|
|
|
22
72
|
documentation instead of duplicating it.
|
|
23
73
|
|
|
24
74
|
Report what you created or updated, what you deliberately left out, and
|
|
25
|
-
anything that contradicts the code or existing docs
|
|
26
|
-
|
|
27
|
-
If \`.opencode/context/manager.md\` does not exist, mention in your final
|
|
28
|
-
report that the client can add it themselves \u2014 their own file with
|
|
29
|
-
\`## Current\`, \`## Next\`, \`## Confirmed\` and \`## Instructions\` \u2014 for what
|
|
30
|
-
is being worked on, what comes next, what they have ratified, and standing
|
|
31
|
-
direction. Never create it.`
|
|
75
|
+
anything that contradicts the code or existing docs.`
|
|
32
76
|
},
|
|
33
77
|
"context-update": {
|
|
34
78
|
description: "Incrementally update .opencode/context/ after recent work via the context agent.",
|
|
@@ -59,9 +103,8 @@ timestamp-only edits. If nothing material changed, make no edits and report
|
|
|
59
103
|
subtask: true,
|
|
60
104
|
template: `Context review.
|
|
61
105
|
|
|
62
|
-
Everything outside the five context files is read-only for this run
|
|
63
|
-
|
|
64
|
-
code under any circumstances.
|
|
106
|
+
Everything outside the five context files is read-only for this run. Do not
|
|
107
|
+
modify source code under any circumstances.
|
|
65
108
|
|
|
66
109
|
Audit the five context files for:
|
|
67
110
|
|
|
@@ -74,18 +117,15 @@ Audit the five context files for:
|
|
|
74
117
|
- missing canonical entry points for significant subsystems
|
|
75
118
|
- unsupported assumptions \u2014 anything stated as fact that you cannot verify,
|
|
76
119
|
or that lacks a confidence marker
|
|
77
|
-
- \`manager.md\`, if present: is \`## Current\` and \`## Next\` still true of
|
|
78
|
-
recent work, is every \`## Confirmed\` entry still true of the code, has it
|
|
79
|
-
outgrown roughly 150 lines \u2014 report findings, but never edit it
|
|
80
120
|
|
|
81
121
|
Fix what you find in \`.opencode/context/\` only, then report: findings, fixes,
|
|
82
122
|
and anything you could not verify.`
|
|
83
123
|
},
|
|
84
|
-
|
|
85
|
-
description: "Apply a client instruction to .
|
|
86
|
-
agent:
|
|
124
|
+
[AGENT_INSTRUCTIONS_COMMAND]: {
|
|
125
|
+
description: "Apply a client instruction to the project's AGENTS.md. Client-only \u2014 runs the instructions agent, the only writer of that file.",
|
|
126
|
+
agent: AGENT_INSTRUCTIONS_WRITER,
|
|
87
127
|
subtask: true,
|
|
88
|
-
template: `Apply this instruction to
|
|
128
|
+
template: `Apply this instruction to the project's \`AGENTS.md\`, and edit only that file:
|
|
89
129
|
|
|
90
130
|
$ARGUMENTS
|
|
91
131
|
|
|
@@ -121,6 +161,36 @@ Print one PASS/FAIL line per check below, with the exact fix for any FAIL.
|
|
|
121
161
|
whether or not the gateway advertises it, so it needs no allowlist entry.
|
|
122
162
|
|
|
123
163
|
Change nothing. Print a final one-line summary of failures.`
|
|
164
|
+
},
|
|
165
|
+
"pool": {
|
|
166
|
+
description: "Modify this machine's LaCode pool from a short instruction \u2014 the shared free-model slot table in ~/.config/lacode/pool.txt. Restarts opencode afterwards.",
|
|
167
|
+
template: `Apply this change to the LaCode pool on this machine:
|
|
168
|
+
|
|
169
|
+
$ARGUMENTS
|
|
170
|
+
|
|
171
|
+
The pool is ONE global slot table shared by every pooled base (explore, implement, context) \u2014 there are no per-base model lists, so "put X on explore" means "give X a slot", not a per-base override.
|
|
172
|
+
|
|
173
|
+
1. Establish the baseline. Read \`${USER_MODELS_TXT}\`. If it is absent, the pool is resolving from the bundled preset \`presets/free-tier.json\`; read that file to learn what is in effect. Call the \`pool_status\` tool only if you need live load or cooldown state.
|
|
174
|
+
|
|
175
|
+
2. Resolve the instruction above into a target slot table. These are the rules the loader enforces \u2014 never write a file that breaks one:
|
|
176
|
+
- one slot per line: \`<provider>/<model-id> [weight]\`
|
|
177
|
+
- at most ${MAX_SLOTS} slots, and a model may appear only once
|
|
178
|
+
- weight is optional (default 1) and must be a number in (0, ${MAX_WEIGHT}]. It is the soft ceiling on concurrent claims and the slot's share of the rotation \u2014 NOT a priority; pick order follows line order.
|
|
179
|
+
- \`#\` starts a comment (whole-line or trailing); blank lines are ignored.
|
|
180
|
+
If the instruction names a model ambiguously, or would need more than ${MAX_SLOTS} slots, stop and ask rather than guessing.
|
|
181
|
+
|
|
182
|
+
3. Write the WHOLE file, not a patch. This file is an override, not an edit target: any slot you drop disappears, and slot numbers are line positions, so a partial write silently renumbers the rotation. Keep the existing comment header. If the write fails on a missing directory, create \`~/.config/lacode\` first and retry.
|
|
183
|
+
|
|
184
|
+
4. Report the before \u2192 after slot table, the resolved path, and this reminder in substance: nothing hot-reloads, so every running opencode process must be restarted before the new pool takes effect.
|
|
185
|
+
|
|
186
|
+
Two traps that fail silently, so check and state them explicitly:
|
|
187
|
+
- A slot's provider must already exist in the user's opencode config, or that slot cannot resolve at all. If the instruction adds a model on a provider that is not configured, say so.
|
|
188
|
+
- If a slot model is \`omniroute/<id>\`, that id must also appear in the plugin's \`omniroute.models\` allowlist (or be the \`combo\` id, which needs no entry) \u2014 otherwise the pool resolves and every spawn fails with \`ProviderModelNotFoundError\`.
|
|
189
|
+
- A provider \`whitelist\` is an allowlist, and the plugin appends your model to it at config time. Nothing for you to do there.
|
|
190
|
+
|
|
191
|
+
If the instruction is to RESET or REVERT rather than to change models, delete \`${USER_MODELS_TXT}\` instead, and report that the pool falls back to the bundled preset \u2014 with the same restart reminder.
|
|
192
|
+
|
|
193
|
+
Change nothing else. In particular do not touch \`presets/free-tier.json\`, \`~/.config/lacode/pool.json\`, \`FALLBACK_SLOTS\` in \`src/lib/pool.ts\`, or \`tests/models-snapshot.json\`: this command owns the per-machine override and nothing else.`
|
|
124
194
|
}
|
|
125
195
|
};
|
|
126
196
|
function applyCommands(cfg, commands = COMMANDS) {
|
|
@@ -137,50 +207,7 @@ function applyCommands(cfg, commands = COMMANDS) {
|
|
|
137
207
|
}
|
|
138
208
|
return { added, kept };
|
|
139
209
|
}
|
|
140
|
-
var INSTRUCTION_MARKER = "<!-- lacode:context-map-instruction:v1 -->";
|
|
141
|
-
var INSTRUCTION_OPT_OUT = "<!-- lacode:context-map-instruction:off -->";
|
|
142
|
-
var STANDING_INSTRUCTION = `## Repository context map
|
|
143
|
-
|
|
144
|
-
This project may keep a navigation map in \`.opencode/context/\`. Before broad
|
|
145
|
-
exploration, check whether it exists (\`ls .opencode/context\` \u2014 never the \`glob\`
|
|
146
|
-
tool, which skips hidden directories). If \`manager.md\` exists there, read it
|
|
147
|
-
first: it is the client's own statement of intent \u2014 what is being worked on,
|
|
148
|
-
what comes next, \`## Confirmed\` lists what the client has ratified, and
|
|
149
|
-
\`## Instructions\` holds standing managerial directions. It outranks every
|
|
150
|
-
other context file and every inference of yours about what matters right now.
|
|
151
|
-
It says nothing about where code lives, so it cannot contradict the code;
|
|
152
|
-
treat its \`## Confirmed\` entries as settled decisions, not as a licence to
|
|
153
|
-
skip reading the code. Any heading in it is direction, so read the whole
|
|
154
|
-
file rather than only the sections above. The rest of the map remains usable,
|
|
155
|
-
just unratified where \`## Confirmed\` does not cover it. Then read only the
|
|
156
|
-
file relevant to the task \u2014 \`architecture.md\`, \`contexts.md\`,
|
|
157
|
-
\`conventions.md\`, \`workflows.md\`, \`decisions.md\` \u2014 and start from the
|
|
158
|
-
canonical entry points it lists.
|
|
159
|
-
|
|
160
|
-
Source code is authoritative: if the map disagrees with the code,
|
|
161
|
-
trust the code and say so; never edit source to match it.
|
|
162
|
-
Never edit the five context files yourself \u2014 the context agent owns them.
|
|
163
|
-
\`manager.md\` is the client's own file and the context agent is explicitly
|
|
164
|
-
permission-denied from writing it. You may write it only when the client
|
|
165
|
-
explicitly asks you to in this conversation, and then only what they asked
|
|
166
|
-
for; never on your own initiative, and never to "keep it up to date". Say in
|
|
167
|
-
your final report if your work revealed something that belongs to the map, or
|
|
168
|
-
if \`manager.md\` and the code disagree.`;
|
|
169
|
-
function applyInstructions(cfg) {
|
|
170
|
-
const existing = Array.isArray(cfg.instructions) ? cfg.instructions : [];
|
|
171
|
-
const texts = existing.filter((e) => typeof e === "string");
|
|
172
|
-
if (texts.some((e) => e.includes(INSTRUCTION_OPT_OUT))) return { added: false };
|
|
173
|
-
if (texts.some((e) => e.includes(INSTRUCTION_MARKER))) return { added: false };
|
|
174
|
-
cfg.instructions = [...existing, `${INSTRUCTION_MARKER}
|
|
175
|
-
|
|
176
|
-
${STANDING_INSTRUCTION}`];
|
|
177
|
-
return { added: true };
|
|
178
|
-
}
|
|
179
210
|
export {
|
|
180
211
|
COMMANDS,
|
|
181
|
-
|
|
182
|
-
INSTRUCTION_OPT_OUT,
|
|
183
|
-
STANDING_INSTRUCTION,
|
|
184
|
-
applyCommands,
|
|
185
|
-
applyInstructions
|
|
212
|
+
applyCommands
|
|
186
213
|
};
|