@lawaty/lacode 0.1.1 → 0.1.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/README.md +25 -50
- package/dist/agents.js +47 -240
- package/dist/commands.js +6 -6
- package/dist/index.js +145 -274
- package/dist/lacode.js +72 -15
- package/dist/pool.js +25 -2
- package/dist/tunnel.js +34 -13
- package/docs/CONTEXT.md +4 -4
- package/docs/TUNNEL.md +68 -108
- package/docs/USER-STORIES.md +17 -16
- package/package.json +1 -1
- package/presets/free-tier.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10,12 +10,13 @@ var CATALOG_TTL_MS = 10 * 60 * 1e3;
|
|
|
10
10
|
var STUCK_MIN_AGE_MS = 5 * 60 * 1e3;
|
|
11
11
|
var STUCK_IDLE_MS = 3 * 60 * 1e3;
|
|
12
12
|
var HANG_COOLDOWN_MS = 10 * 60 * 1e3;
|
|
13
|
+
var UNAVAILABLE_COOLDOWN_MS = 60 * 1e3;
|
|
13
14
|
var MAX_WEIGHT = 100;
|
|
14
15
|
var MAX_SLOTS = 4;
|
|
15
16
|
var COOLDOWN_BASE_MS = 60 * 1e3;
|
|
16
17
|
var COOLDOWN_MAX_MS = 15 * 60 * 1e3;
|
|
17
18
|
var STRIKE_DECAY_MS = 30 * 60 * 1e3;
|
|
18
|
-
var BASES = ["explore
|
|
19
|
+
var BASES = ["explore", "implement", "context"];
|
|
19
20
|
var findPackageRoot = (start) => {
|
|
20
21
|
let dir = start;
|
|
21
22
|
for (let up = 0; up < 6; up++) {
|
|
@@ -136,6 +137,7 @@ function readWeight(entry, at, model, problems) {
|
|
|
136
137
|
}
|
|
137
138
|
return weight;
|
|
138
139
|
}
|
|
140
|
+
var UNAVAILABLE_PATTERN = /endpoint\s+is\s+unavailable/i;
|
|
139
141
|
var LIMIT_PATTERNS = [
|
|
140
142
|
/rate[\s_-]?limit/i,
|
|
141
143
|
/usage limit/i,
|
|
@@ -143,9 +145,11 @@ var LIMIT_PATTERNS = [
|
|
|
143
145
|
/\b429\b/,
|
|
144
146
|
/quota/i,
|
|
145
147
|
/freelimiterror/i,
|
|
146
|
-
/overloaded/i
|
|
148
|
+
/overloaded/i,
|
|
149
|
+
UNAVAILABLE_PATTERN
|
|
147
150
|
];
|
|
148
151
|
var isLimitText = (text) => LIMIT_PATTERNS.some((re) => re.test(text));
|
|
152
|
+
var isUnavailableText = (text) => UNAVAILABLE_PATTERN.test(text);
|
|
149
153
|
var statedCooldown = (text) => {
|
|
150
154
|
const hm = /resets?\s+in\s+(\d+)\s*(?:h|hr|hour)s?\s*(\d+)?\s*(?:m|min|minute)?s?/i.exec(text);
|
|
151
155
|
if (hm) return (Number(hm[1]) * 60 + (hm[2] ? Number(hm[2]) : 0)) * 60 * 1e3;
|
|
@@ -327,6 +331,22 @@ function recordStrike(s, model, reason, stated) {
|
|
|
327
331
|
}
|
|
328
332
|
return strike;
|
|
329
333
|
}
|
|
334
|
+
function coolUnavailable(s, model) {
|
|
335
|
+
if (!s.dirReady) return;
|
|
336
|
+
const now = Date.now();
|
|
337
|
+
const prior = s.cooling.get(model);
|
|
338
|
+
const until = now + UNAVAILABLE_COOLDOWN_MS;
|
|
339
|
+
if (prior && prior.until > until) return prior;
|
|
340
|
+
const strike = { model, until, strikes: 1, reason: "endpoint-unavailable", last: now };
|
|
341
|
+
s.cooling.set(model, strike);
|
|
342
|
+
const path4 = join(s.dir, limitFile(model));
|
|
343
|
+
try {
|
|
344
|
+
writeFileSync(`${path4}.tmp`, JSON.stringify(strike));
|
|
345
|
+
renameSync(`${path4}.tmp`, path4);
|
|
346
|
+
} catch {
|
|
347
|
+
}
|
|
348
|
+
return strike;
|
|
349
|
+
}
|
|
330
350
|
function refreshCatalog(s, serverUrl) {
|
|
331
351
|
if (!serverUrl || Date.now() - s.catalogAt < CATALOG_TTL_MS) return;
|
|
332
352
|
s.catalogAt = Date.now();
|
|
@@ -427,11 +447,10 @@ function coolProvider(s, provider, until, reason) {
|
|
|
427
447
|
}
|
|
428
448
|
|
|
429
449
|
// src/agents.ts
|
|
430
|
-
var
|
|
431
|
-
var POOLED_BASES = ["explore-fast", "implement-fast", "context-manager"];
|
|
450
|
+
var POOLED_BASES = ["explore", "implement", "context"];
|
|
432
451
|
var AGENTS = {
|
|
433
452
|
"build": {
|
|
434
|
-
"description": "Default root agent. Orchestrates rather than investigates: read/glob/grep/list/webfetch/websearch are denied by permission, so every question goes to @explore
|
|
453
|
+
"description": "Default root agent. Orchestrates rather than investigates: read/glob/grep/list/webfetch/websearch are denied by permission, so every question goes to @explore and every code change to @implement. Keeps edit/write/bash for applying and verifying what comes back.",
|
|
435
454
|
"mode": "primary",
|
|
436
455
|
"temperature": 0.1,
|
|
437
456
|
prompt: `# Role
|
|
@@ -446,17 +465,15 @@ Every question of the form "what does this file do", "where is X handled", or
|
|
|
446
465
|
|
|
447
466
|
# Delegation
|
|
448
467
|
|
|
449
|
-
- **Investigation \u2192 @explore
|
|
468
|
+
- **Investigation \u2192 @explore.** Give it specific, focused instructions
|
|
450
469
|
("read src/auth.ts", "grep for 'migration' in *.py", "list the src/
|
|
451
470
|
directory"), one batch per question. Ask for the exact lines or
|
|
452
471
|
file:line references you need, not a survey.
|
|
453
|
-
- **Implementation \u2192 @implement
|
|
472
|
+
- **Implementation \u2192 @implement**, with a finalized plan: the exact
|
|
454
473
|
steps, the files to touch, and the constraints you already learned from the
|
|
455
474
|
explorers. It edits, writes, and runs tests.
|
|
456
|
-
- Never call @explore-deep or @implement-deep yourself. Each tier escalates on
|
|
457
|
-
its own when it is stuck; that ladder belongs to the subagents, not to you.
|
|
458
475
|
|
|
459
|
-
The agents you spawn are the pooled \`explore
|
|
476
|
+
The agents you spawn are the pooled \`explore\` / \`implement\` bases \u2014
|
|
460
477
|
spawn the bare name and let the pool route, never a \`-1\`/\`-2\` slot variant.
|
|
461
478
|
|
|
462
479
|
# What stays yours
|
|
@@ -467,11 +484,11 @@ build, and git.
|
|
|
467
484
|
|
|
468
485
|
Do NOT use bash to inspect the codebase. \`cat\`, \`head\`, \`tail\`, \`sed\`, \`awk\`,
|
|
469
486
|
\`grep\`, \`find\`, \`ls\`, \`tree\` and friends are exploration, and exploration goes
|
|
470
|
-
to @explore
|
|
487
|
+
to @explore. Bash is for executing and verifying, not for looking.
|
|
471
488
|
|
|
472
489
|
# Repository context
|
|
473
490
|
|
|
474
|
-
Before delegating a non-trivial task, tell @explore
|
|
491
|
+
Before delegating a non-trivial task, tell @explore to check
|
|
475
492
|
\`.opencode/context/\` first (\`ls .opencode/context\`, never the \`glob\` tool \u2014 it
|
|
476
493
|
skips hidden directories) and to read only the file relevant to the task:
|
|
477
494
|
architecture.md for architectural questions, contexts.md for subsystem
|
|
@@ -483,7 +500,7 @@ broadly.
|
|
|
483
500
|
Files the user explicitly named always take priority over the context map.
|
|
484
501
|
|
|
485
502
|
Never edit \`.opencode/context/\` \u2014 context maintenance belongs to the
|
|
486
|
-
context
|
|
503
|
+
context agent (\`/context-update\` or its automatic post-work runs). If
|
|
487
504
|
your work reveals something that belongs in the map, say so in your final
|
|
488
505
|
report instead of editing it yourself.
|
|
489
506
|
|
|
@@ -510,13 +527,13 @@ missing). Never paste context file contents into the report.`,
|
|
|
510
527
|
"question": "allow",
|
|
511
528
|
"task": {
|
|
512
529
|
"*": "deny",
|
|
513
|
-
"explore
|
|
514
|
-
"implement
|
|
530
|
+
"explore*": "allow",
|
|
531
|
+
"implement*": "allow"
|
|
515
532
|
}
|
|
516
533
|
}
|
|
517
534
|
},
|
|
518
535
|
"plan": {
|
|
519
|
-
"description": "Read-only planner. Identical to @basic: it cannot read, grep, glob, list or run commands, so all investigation is delegated to @explore
|
|
536
|
+
"description": "Read-only planner. Identical to @basic: it cannot read, grep, glob, list or run commands, so all investigation is delegated to @explore. Produce a plan; do not edit.",
|
|
520
537
|
"mode": "primary",
|
|
521
538
|
"temperature": 0.1,
|
|
522
539
|
prompt: `You are a senior software architect. Your ONLY job is analysis and planning.
|
|
@@ -525,8 +542,8 @@ You CANNOT read files, grep, glob, list directories, or run commands directly. A
|
|
|
525
542
|
|
|
526
543
|
When you need to understand the codebase:
|
|
527
544
|
1. Determine exactly what files or patterns need investigating
|
|
528
|
-
2. Invoke @explore
|
|
529
|
-
3. @explore
|
|
545
|
+
2. Invoke @explore with specific, focused instructions (e.g. "read src/auth.ts", "grep for 'migration' in *.py files", "list the src/ directory")
|
|
546
|
+
3. @explore will return its findings \u2014 you process them and plan accordingly
|
|
530
547
|
|
|
531
548
|
Repository context: before delegating investigation for a non-trivial task,
|
|
532
549
|
tell the exploration agent to first check \`.opencode/context/\` in this project
|
|
@@ -540,12 +557,12 @@ entry points the map lists rather than exploring broadly.
|
|
|
540
557
|
Files the user explicitly mentioned always take priority over the context map.
|
|
541
558
|
|
|
542
559
|
Never edit \`.opencode/context/\` \u2014 context maintenance belongs to the
|
|
543
|
-
context
|
|
560
|
+
context agent (\`/context-update\` or its automatic post-work runs). If
|
|
544
561
|
your plan implies the map needs a change, say so in the plan instead.
|
|
545
562
|
|
|
546
563
|
When given a task:
|
|
547
564
|
1. Think through what codebase context you need
|
|
548
|
-
2. Delegate investigation to @explore
|
|
565
|
+
2. Delegate investigation to @explore in focused batches
|
|
549
566
|
3. Analyze findings for dependencies, edge cases, and architecture impact
|
|
550
567
|
4. Produce a structured output:
|
|
551
568
|
- Summary of the change
|
|
@@ -580,13 +597,13 @@ Once the plan is complete, present it clearly so the caller can implement it ste
|
|
|
580
597
|
"question": "allow",
|
|
581
598
|
"task": {
|
|
582
599
|
"*": "deny",
|
|
583
|
-
"explore
|
|
584
|
-
"implement
|
|
600
|
+
"explore*": "allow",
|
|
601
|
+
"implement*": "allow"
|
|
585
602
|
}
|
|
586
603
|
}
|
|
587
604
|
},
|
|
588
|
-
"explore
|
|
589
|
-
"description": "Default read-only codebase exploration. Free via Zen.
|
|
605
|
+
"explore": {
|
|
606
|
+
"description": "Default read-only codebase exploration. Free via Zen.",
|
|
590
607
|
"mode": "subagent",
|
|
591
608
|
"temperature": 0.2,
|
|
592
609
|
prompt: `# Role
|
|
@@ -619,119 +636,26 @@ understand them and find the surrounding code, never as a reason to skip them.
|
|
|
619
636
|
|
|
620
637
|
If the map conflicts with the source, trust the source, say so in your report,
|
|
621
638
|
and never repeat the stale claim. Never edit \`.opencode/context/\` files \u2014 the
|
|
622
|
-
context
|
|
639
|
+
context agent owns those (\`/context-update\` or its automatic post-work
|
|
623
640
|
runs). Report what should be updated there instead.
|
|
624
641
|
|
|
625
642
|
<!-- MERGE POINT: paste any project-specific exploration conventions from
|
|
626
643
|
your current plan.txt / explore instructions here (e.g. which
|
|
627
644
|
directories to ignore, naming conventions, where docs live). -->
|
|
628
645
|
|
|
629
|
-
#
|
|
630
|
-
|
|
631
|
-
If you hit any of the following, STOP and delegate to @explore-deep via the
|
|
632
|
-
task tool instead of continuing to search:
|
|
633
|
-
|
|
634
|
-
- You've made more than ~8 tool calls (read/glob/grep/bash) without
|
|
635
|
-
converging on an answer
|
|
636
|
-
- The same search strategy (same query pattern, same directory) has failed
|
|
637
|
-
twice in a row
|
|
638
|
-
- The code you need to understand involves genuinely complex logic you're
|
|
639
|
-
not confident you're reading correctly (e.g. dense concurrency, generated
|
|
640
|
-
code, deep inheritance/dependency chains, obscure build tooling)
|
|
641
|
-
- You cannot locate a symbol, config value, or file the task clearly
|
|
642
|
-
requires, after searching from at least two different angles (e.g. by
|
|
643
|
-
name and by usage)
|
|
644
|
-
|
|
645
|
-
Do not escalate on the first dead end \u2014 try at least one alternative search
|
|
646
|
-
strategy yourself first (different keywords, different tool, broader or
|
|
647
|
-
narrower scope).
|
|
648
|
-
|
|
649
|
-
When escalating, pass full context to @explore-deep:
|
|
650
|
-
- The exact question you were asked to answer
|
|
651
|
-
- Every search strategy you already tried and what each one returned
|
|
652
|
-
- Your best current hypothesis, even if unconfirmed
|
|
653
|
-
|
|
654
|
-
Never guess or fabricate a file path, line number, or code behavior to avoid
|
|
655
|
-
escalating. A wrong answer is worse than an escalation.
|
|
656
|
-
|
|
657
|
-
# No further escalation beyond deep
|
|
658
|
-
|
|
659
|
-
You may only escalate to @explore-deep. Do not attempt to call @implement-fast,
|
|
660
|
-
@implement-deep, or any other agent \u2014 exploration agents only explore.`,
|
|
661
|
-
permission: {
|
|
662
|
-
"read": "allow",
|
|
663
|
-
"glob": "allow",
|
|
664
|
-
"grep": "allow",
|
|
665
|
-
"list": "allow",
|
|
666
|
-
"edit": "deny",
|
|
667
|
-
"bash": "allow",
|
|
668
|
-
"webfetch": "allow",
|
|
669
|
-
"websearch": "allow",
|
|
670
|
-
"question": "allow",
|
|
671
|
-
"task": {
|
|
672
|
-
"*": "deny",
|
|
673
|
-
"explore-deep": "allow"
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
},
|
|
677
|
-
"explore-deep": {
|
|
678
|
-
"description": "Last-resort read-only exploration for genuinely hard problems @explore-fast fails on. Most expensive explore tier \u2014 use deliberately.",
|
|
679
|
-
"mode": "subagent",
|
|
680
|
-
"temperature": 0.2,
|
|
681
|
-
prompt: `# Role
|
|
682
|
-
|
|
683
|
-
You are the last-resort codebase exploration agent, called only when
|
|
684
|
-
@explore-fast could not resolve a question. You are read-only: you locate,
|
|
685
|
-
read, and summarize code, configuration, and documentation. You never edit or
|
|
686
|
-
write files.
|
|
687
|
-
|
|
688
|
-
You will typically receive context from @explore-fast including what was
|
|
689
|
-
already tried and what failed \u2014 read it carefully before starting. Do not
|
|
690
|
-
repeat a search strategy that's already been reported as unproductive; start
|
|
691
|
-
from a genuinely different angle (e.g. searching by behavior/output instead of
|
|
692
|
-
by name, checking git history or tests for hints, reading adjacent modules for
|
|
693
|
-
context).
|
|
694
|
-
|
|
695
|
-
# Repository context map
|
|
696
|
-
|
|
697
|
-
You will usually receive findings that already include what
|
|
698
|
-
\`.opencode/context/\` said about the area. Treat that as a starting point, not
|
|
699
|
-
as a finished answer: the code is authoritative, and the map may be stale or
|
|
700
|
-
incomplete.
|
|
701
|
-
|
|
702
|
-
If you do need to consult the map, list it with \`ls .opencode/context\` \u2014 never
|
|
703
|
-
the \`glob\` tool, which skips hidden directories \u2014 then read only the file
|
|
704
|
-
relevant to the question \u2014 architecture.md, contexts.md, conventions.md,
|
|
705
|
-
workflows.md, decisions.md \u2014 and start from its canonical entry points. Explore
|
|
706
|
-
beyond them only when the question genuinely requires it. Do not re-walk the
|
|
707
|
-
repository to build a complete picture, and do not repeat searches already
|
|
708
|
-
reported as unproductive. Never edit \`.opencode/context/\` files \u2014 the
|
|
709
|
-
context-manager agent owns those (\`/context-update\` or its automatic post-work
|
|
710
|
-
runs). Report what should be updated there instead.
|
|
711
|
-
|
|
712
|
-
<!-- MERGE POINT: paste any project-specific exploration conventions from
|
|
713
|
-
your current plan.txt / explore instructions here. -->
|
|
714
|
-
|
|
715
|
-
# No further escalation
|
|
646
|
+
# When to report back
|
|
716
647
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
question:
|
|
648
|
+
There is no deeper tier to hand off to. If after genuinely trying \u2014 one
|
|
649
|
+
alternative search strategy first \u2014 you still cannot answer:
|
|
720
650
|
|
|
721
|
-
- STOP. Do not
|
|
722
|
-
- Report
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
resolve it (e.g. "this appears to be defined in a private package not in
|
|
727
|
-
this repo").
|
|
651
|
+
- STOP. Do not loop or guess.
|
|
652
|
+
- Report the exact question, every search you already tried and what each
|
|
653
|
+
one returned, and your best hypothesis even if unconfirmed.
|
|
654
|
+
- Never guess or fabricate a file path, line number, or code behavior. An
|
|
655
|
+
honest "I couldn't find this, here's why" is the correct output.
|
|
728
656
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
when that's the truth.
|
|
732
|
-
|
|
733
|
-
You may only explore. Do not attempt to call @implement-fast, @implement-deep,
|
|
734
|
-
or any other agent.`,
|
|
657
|
+
You may only explore. Do not attempt to call @implement or any other agent \u2014
|
|
658
|
+
exploration agents only explore.`,
|
|
735
659
|
permission: {
|
|
736
660
|
"read": "allow",
|
|
737
661
|
"glob": "allow",
|
|
@@ -747,8 +671,8 @@ or any other agent.`,
|
|
|
747
671
|
}
|
|
748
672
|
}
|
|
749
673
|
},
|
|
750
|
-
"implement
|
|
751
|
-
"description": "Default agent that executes a finalized plan.
|
|
674
|
+
"implement": {
|
|
675
|
+
"description": "Default agent that executes a finalized plan.",
|
|
752
676
|
"mode": "subagent",
|
|
753
677
|
"temperature": 0.2,
|
|
754
678
|
prompt: `# Role
|
|
@@ -774,7 +698,7 @@ named by the user or the plan always take priority over the map, and the map
|
|
|
774
698
|
never justifies skipping work the plan asked for.
|
|
775
699
|
|
|
776
700
|
Do NOT edit \`.opencode/context/\` \u2014 context maintenance is a separate
|
|
777
|
-
responsibility handled by the context
|
|
701
|
+
responsibility handled by the context agent (\`/context-update\` or its
|
|
778
702
|
automatic post-work runs). If your work reveals something that belongs in the
|
|
779
703
|
map (a new boundary, entry point, workflow, convention, or decision), say so in
|
|
780
704
|
your final report instead of editing it yourself.
|
|
@@ -784,115 +708,21 @@ your final report instead of editing it yourself.
|
|
|
784
708
|
etc. This section should carry over everything from the original
|
|
785
709
|
shared implement.txt that both tiers need. -->
|
|
786
710
|
|
|
787
|
-
#
|
|
788
|
-
|
|
789
|
-
If you hit any of the following, STOP and delegate to @implement-deep via
|
|
790
|
-
the task tool instead of continuing to retry:
|
|
791
|
-
|
|
792
|
-
- The same error or test failure recurs after 2 different attempted fixes
|
|
793
|
-
- You've made more than ~8 tool calls without a concrete, verifiable step of
|
|
794
|
-
progress (a passing test, a file that now does what it's supposed to, etc.)
|
|
795
|
-
- The task requires reasoning you're not confident in \u2014 complex concurrency,
|
|
796
|
-
subtle type/generic errors, ambiguous or conflicting requirements in the
|
|
797
|
-
plan, or a fix that would require touching many unrelated files
|
|
798
|
-
- You find yourself about to revert and redo the same change a third time
|
|
711
|
+
# When to report back
|
|
799
712
|
|
|
800
|
-
|
|
801
|
-
|
|
713
|
+
There is no deeper tier to hand off to. If after genuinely trying \u2014 at least
|
|
714
|
+
one different fix, or a re-read of the relevant code \u2014 you still cannot
|
|
715
|
+
complete the step:
|
|
802
716
|
|
|
803
|
-
|
|
804
|
-
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
717
|
+
- STOP. Do not loop or guess.
|
|
718
|
+
- Leave the code in the most coherent state you can; prefer reverting a
|
|
719
|
+
broken partial change over leaving it half-done.
|
|
720
|
+
- Report the plan step, every approach tried and why each failed, and the
|
|
721
|
+
current state of the code. Never mark a step done if it isn't, and never
|
|
722
|
+
fabricate a passing test.
|
|
808
723
|
|
|
809
|
-
Do not
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
# No further escalation beyond deep
|
|
813
|
-
|
|
814
|
-
You may only escalate to @implement-deep. Do not attempt to call
|
|
815
|
-
@explore-fast, @explore-deep, or any other agent for implementation help \u2014
|
|
816
|
-
if you need investigation rather than execution, note that explicitly when
|
|
817
|
-
you escalate so @implement-deep can decide whether to explore first.`,
|
|
818
|
-
permission: {
|
|
819
|
-
"read": "allow",
|
|
820
|
-
"glob": "allow",
|
|
821
|
-
"grep": "allow",
|
|
822
|
-
"list": "allow",
|
|
823
|
-
"edit": {
|
|
824
|
-
"*": "allow",
|
|
825
|
-
"*/.opencode/context/**": "deny",
|
|
826
|
-
".opencode/context/**": "deny"
|
|
827
|
-
},
|
|
828
|
-
"bash": "allow",
|
|
829
|
-
"webfetch": "allow",
|
|
830
|
-
"websearch": "allow",
|
|
831
|
-
"question": "allow",
|
|
832
|
-
"task": {
|
|
833
|
-
"*": "deny",
|
|
834
|
-
"implement-deep": "allow"
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
},
|
|
838
|
-
"implement-deep": {
|
|
839
|
-
"description": "Last-resort executor for genuinely hard problems @implement-fast fails on. Most expensive implement tier \u2014 use deliberately.",
|
|
840
|
-
"mode": "subagent",
|
|
841
|
-
"temperature": 0.2,
|
|
842
|
-
prompt: `# Role
|
|
843
|
-
|
|
844
|
-
You are the last-resort agent for executing a finalized plan, called only
|
|
845
|
-
when @implement-fast could not complete a step. You read, edit, write, and
|
|
846
|
-
run code/bash to make the plan's changes real.
|
|
847
|
-
|
|
848
|
-
You will typically receive context from @implement-fast including what was
|
|
849
|
-
already tried, what failed, and the current state of the code \u2014 read it
|
|
850
|
-
carefully before touching anything. Don't repeat an approach already reported
|
|
851
|
-
as failed. Before making further changes, confirm you understand the current
|
|
852
|
-
state of the code (it may be a partial, broken edit left by @implement-fast)
|
|
853
|
-
rather than assuming it matches the original plan.
|
|
854
|
-
|
|
855
|
-
# Repository context
|
|
856
|
-
|
|
857
|
-
If \`.opencode/context/\` exists (\`ls .opencode/context\` \u2014 never the \`glob\`
|
|
858
|
-
tool, which skips hidden directories), use it as a navigation aid:
|
|
859
|
-
conventions.md
|
|
860
|
-
before writing or modifying code, architecture.md and contexts.md for layer
|
|
861
|
-
and boundary decisions, workflows.md when the change touches a flow,
|
|
862
|
-
decisions.md for architectural constraints that must be respected. Read only
|
|
863
|
-
the relevant file. Files named by @implement-fast, the user, or the plan
|
|
864
|
-
always take priority, and the map never justifies skipping a step.
|
|
865
|
-
|
|
866
|
-
Do NOT edit \`.opencode/context/\` \u2014 that belongs to the context-manager agent
|
|
867
|
-
(\`/context-update\` or its automatic post-work runs). If your work reveals
|
|
868
|
-
something that belongs in the map, say so in your report instead.
|
|
869
|
-
|
|
870
|
-
<!-- MERGE POINT: paste your current implement.txt content here \u2014 the same
|
|
871
|
-
execution conventions used by implement-fast should apply here too. -->
|
|
872
|
-
|
|
873
|
-
# No further escalation
|
|
874
|
-
|
|
875
|
-
You are the last resort \u2014 there is no one above you to hand this off to. If,
|
|
876
|
-
after genuinely exhausting reasonable approaches, you still cannot complete
|
|
877
|
-
the step:
|
|
878
|
-
|
|
879
|
-
- STOP. Do not keep looping, guessing, or making speculative changes.
|
|
880
|
-
- Leave the code in the most coherent state you can (prefer reverting a
|
|
881
|
-
broken partial change over leaving it half-done, unless partial progress is
|
|
882
|
-
clearly salvageable and you say so).
|
|
883
|
-
- Report back clearly and specifically: the exact error or blocker, what was
|
|
884
|
-
already tried (including @implement-fast's attempts, if provided), and what
|
|
885
|
-
you believe is needed to unblock it (missing information, a plan
|
|
886
|
-
revision, human judgment call, etc.).
|
|
887
|
-
|
|
888
|
-
Never mark a step as done if it isn't, and never fabricate a passing
|
|
889
|
-
test/build result. An honest "this is blocked, here's why" is the correct
|
|
890
|
-
output when that's the truth.
|
|
891
|
-
|
|
892
|
-
You may only implement. Do not attempt to call @explore-fast, @explore-deep,
|
|
893
|
-
or any other agent \u2014 if the blocker is a missing understanding of the
|
|
894
|
-
codebase rather than an execution problem, report that explicitly instead of
|
|
895
|
-
trying to explore yourself.`,
|
|
724
|
+
You may only implement. Do not attempt to call @explore or any other agent \u2014
|
|
725
|
+
if the blocker is understanding rather than execution, report that instead.`,
|
|
896
726
|
permission: {
|
|
897
727
|
"read": "allow",
|
|
898
728
|
"glob": "allow",
|
|
@@ -912,7 +742,7 @@ trying to explore yourself.`,
|
|
|
912
742
|
}
|
|
913
743
|
}
|
|
914
744
|
},
|
|
915
|
-
"context
|
|
745
|
+
"context": {
|
|
916
746
|
"description": "Repository cartographer and memory maintainer. Bootstraps and incrementally maintains .opencode/context/ (architecture, contexts, conventions, workflows, decisions) as a small navigation map for future agents. Runs automatically after sessions that changed files; can also be invoked with /context-init, /context-update, /context-review. Never use it for general coding tasks.",
|
|
917
747
|
"mode": "subagent",
|
|
918
748
|
"temperature": 0.1,
|
|
@@ -1185,19 +1015,16 @@ function buildAgentSet() {
|
|
|
1185
1015
|
}
|
|
1186
1016
|
return set;
|
|
1187
1017
|
}
|
|
1188
|
-
var DEEP_TIER_SET = new Set(DEEP_TIERS);
|
|
1189
1018
|
function applyAgents(cfg, agents = buildAgentSet(), opts = {}) {
|
|
1190
1019
|
const target = cfg.agent ??= {};
|
|
1191
1020
|
const added = [];
|
|
1192
1021
|
const kept = [];
|
|
1193
|
-
const deepModel = typeof opts.deepModel === "string" ? opts.deepModel.trim() : "";
|
|
1194
1022
|
for (const [name, def] of Object.entries(agents)) {
|
|
1195
1023
|
if (target[name] !== void 0) {
|
|
1196
1024
|
kept.push(name);
|
|
1197
1025
|
continue;
|
|
1198
1026
|
}
|
|
1199
1027
|
const copy = JSON.parse(JSON.stringify(def));
|
|
1200
|
-
if (deepModel && DEEP_TIER_SET.has(name) && !copy.model) copy.model = deepModel;
|
|
1201
1028
|
target[name] = copy;
|
|
1202
1029
|
added.push(name);
|
|
1203
1030
|
}
|
|
@@ -1207,8 +1034,8 @@ function applyAgents(cfg, agents = buildAgentSet(), opts = {}) {
|
|
|
1207
1034
|
// src/commands.ts
|
|
1208
1035
|
var COMMANDS = {
|
|
1209
1036
|
"context-init": {
|
|
1210
|
-
description: "Bootstrap the repository context map (.opencode/context/) via the context
|
|
1211
|
-
agent: "context
|
|
1037
|
+
description: "Bootstrap the repository context map (.opencode/context/) via the context agent.",
|
|
1038
|
+
agent: "context",
|
|
1212
1039
|
subtask: true,
|
|
1213
1040
|
template: `Bootstrap the repository context map.
|
|
1214
1041
|
|
|
@@ -1228,8 +1055,8 @@ Report what you created or updated, what you deliberately left out, and
|
|
|
1228
1055
|
anything that contradicts the code or existing docs.`
|
|
1229
1056
|
},
|
|
1230
1057
|
"context-update": {
|
|
1231
|
-
description: "Incrementally update .opencode/context/ after recent work via the context
|
|
1232
|
-
agent: "context
|
|
1058
|
+
description: "Incrementally update .opencode/context/ after recent work via the context agent.",
|
|
1059
|
+
agent: "context",
|
|
1233
1060
|
subtask: true,
|
|
1234
1061
|
template: `Incremental context update.
|
|
1235
1062
|
|
|
@@ -1252,7 +1079,7 @@ timestamp-only edits. If nothing material changed, make no edits and report
|
|
|
1252
1079
|
},
|
|
1253
1080
|
"context-review": {
|
|
1254
1081
|
description: "Audit .opencode/context/ for staleness, contradictions, excess size, duplication, and missing entry points.",
|
|
1255
|
-
agent: "context
|
|
1082
|
+
agent: "context",
|
|
1256
1083
|
subtask: true,
|
|
1257
1084
|
template: `Context review.
|
|
1258
1085
|
|
|
@@ -1299,7 +1126,7 @@ decisions.md \u2014 then start from the canonical entry points it lists.
|
|
|
1299
1126
|
|
|
1300
1127
|
Source code is authoritative: if the map disagrees with the code, trust the code,
|
|
1301
1128
|
say so, and never edit source to match the map. Never edit \`.opencode/context/\`
|
|
1302
|
-
yourself \u2014 the context
|
|
1129
|
+
yourself \u2014 the context agent owns it. Say in your final report if your
|
|
1303
1130
|
work revealed something that belongs in the map.`;
|
|
1304
1131
|
function applyInstructions(cfg) {
|
|
1305
1132
|
const existing = cfg.instructions;
|
|
@@ -1406,24 +1233,6 @@ var agentPoolHooks = (async ({ client, serverUrl }, options) => {
|
|
|
1406
1233
|
await log("info", `whitelisted ${id} on ${provider} (${s.origin} slot ${slot.index})`);
|
|
1407
1234
|
}
|
|
1408
1235
|
}
|
|
1409
|
-
const deepModel = typeof opts.deepModel === "string" ? opts.deepModel.trim() : "";
|
|
1410
|
-
const deepSlash = deepModel.indexOf("/");
|
|
1411
|
-
if (deepModel && deepSlash > 0) {
|
|
1412
|
-
const provider = deepModel.slice(0, deepSlash);
|
|
1413
|
-
const id = deepModel.slice(deepSlash + 1);
|
|
1414
|
-
const entry = providers?.[provider];
|
|
1415
|
-
if (!entry) {
|
|
1416
|
-
await warnOnce(`deepModel ${deepModel} names provider ${provider}, which is not configured in opencode.jsonc; explore-deep and implement-deep cannot resolve it`);
|
|
1417
|
-
} else {
|
|
1418
|
-
const list = entry.whitelist;
|
|
1419
|
-
if (list && !list.includes(id)) {
|
|
1420
|
-
list.push(id);
|
|
1421
|
-
await log("info", `whitelisted ${id} on ${provider} (deepModel)`);
|
|
1422
|
-
}
|
|
1423
|
-
}
|
|
1424
|
-
} else if (deepModel) {
|
|
1425
|
-
await warnOnce(`deepModel ${deepModel} is not in provider/model-id form and was ignored`);
|
|
1426
|
-
}
|
|
1427
1236
|
s.known = known;
|
|
1428
1237
|
await log(
|
|
1429
1238
|
"info",
|
|
@@ -1467,6 +1276,11 @@ var agentPoolHooks = (async ({ client, serverUrl }, options) => {
|
|
|
1467
1276
|
const status = typeof info.error.data?.statusCode === "number" ? info.error.data.statusCode : void 0;
|
|
1468
1277
|
if (!isLimitText(haystack) && status !== 429) return;
|
|
1469
1278
|
const model = `${info.providerID ?? "?"}/${info.modelID ?? "?"}`;
|
|
1279
|
+
if (isUnavailableText(haystack)) {
|
|
1280
|
+
const transient = coolUnavailable(s, model);
|
|
1281
|
+
if (transient) await log("warn", `cooling ${model} for ${Math.round((transient.until - Date.now()) / 1e3)}s (${transient.reason})`);
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1470
1284
|
const stated = statedCooldown(haystack) ?? retryAfterMs(info.error.data?.responseHeaders);
|
|
1471
1285
|
const reason = haystack.match(/rate[\s_-]?limit|usage limit|too many requests|quota|freelimiterror|overloaded/i)?.[0]?.toLowerCase().replace(/[\s_]/g, "-");
|
|
1472
1286
|
const strike = recordStrike(s, model, reason ?? "rate-limited", stated);
|
|
@@ -1585,7 +1399,7 @@ import * as path2 from "node:path";
|
|
|
1585
1399
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
1586
1400
|
import * as path from "node:path";
|
|
1587
1401
|
var CONTEXT_DIR = path.join(".opencode", "context");
|
|
1588
|
-
var WRITER = "context
|
|
1402
|
+
var WRITER = "context";
|
|
1589
1403
|
var toPosix = (value) => value.split(path.sep).join("/");
|
|
1590
1404
|
var compilePattern = (pattern) => {
|
|
1591
1405
|
const escaped = toPosix(pattern).replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
@@ -1815,19 +1629,19 @@ var contextAutoUpdateHooks = (async ({ client, directory }) => {
|
|
|
1815
1629
|
init(state2, (m) => void log("warn", m));
|
|
1816
1630
|
const snap = snapshot(state2);
|
|
1817
1631
|
const slot = pickSlot(state2, snap);
|
|
1818
|
-
const agent = agentFor("context
|
|
1632
|
+
const agent = agentFor("context", slot.index);
|
|
1819
1633
|
const created = await client.session.create({
|
|
1820
|
-
body: { title: `context
|
|
1634
|
+
body: { title: `context (auto ${mode})` },
|
|
1821
1635
|
query: { directory }
|
|
1822
1636
|
});
|
|
1823
1637
|
const target = created.data?.id;
|
|
1824
1638
|
if (!target) {
|
|
1825
1639
|
requeue();
|
|
1826
|
-
await log("warn", "could not create context
|
|
1640
|
+
await log("warn", "could not create context session");
|
|
1827
1641
|
return;
|
|
1828
1642
|
}
|
|
1829
1643
|
const claimKey2 = claimKey(target, "context-autoupdate");
|
|
1830
|
-
acquire(state2, "context
|
|
1644
|
+
acquire(state2, "context", slot, target, claimKey2, target);
|
|
1831
1645
|
await log("info", `borrowed pool slot ${slot.index} (${slot.model}, w${slot.weight}) for auto ${mode}`, {
|
|
1832
1646
|
slot: slot.index,
|
|
1833
1647
|
weight: slot.weight,
|
|
@@ -1885,7 +1699,7 @@ var INTERNAL_TITLE_PREFIXES = [
|
|
|
1885
1699
|
// context-autoupdate spawns the cartographer through the session API, with no
|
|
1886
1700
|
// parentID, so it looks exactly like a root session going idle. Notifying for
|
|
1887
1701
|
// it would announce the cartographer's own bookkeeping as a finished task.
|
|
1888
|
-
"context
|
|
1702
|
+
"context (auto"
|
|
1889
1703
|
];
|
|
1890
1704
|
var notifyMessage = (outcome, detail) => {
|
|
1891
1705
|
if (outcome === "error") return detail ? `Failed \u2014 ${detail}` : "Failed";
|
|
@@ -1893,6 +1707,58 @@ var notifyMessage = (outcome, detail) => {
|
|
|
1893
1707
|
if (outcome === "permission") return detail ? `Awaiting your permission \u2014 ${detail}` : "Awaiting your permission";
|
|
1894
1708
|
return "Task finished, waiting for your review";
|
|
1895
1709
|
};
|
|
1710
|
+
var helperArgv = (helper, title, message, urgency, icon) => {
|
|
1711
|
+
const name = helper.split(/[\\/]/).pop() ?? "";
|
|
1712
|
+
return name === "lacode" || name === "lacode.js" ? ["notify", title, message, urgency, icon] : [title, message, urgency, icon];
|
|
1713
|
+
};
|
|
1714
|
+
var safeStringify = (value) => {
|
|
1715
|
+
try {
|
|
1716
|
+
return JSON.stringify(value) ?? void 0;
|
|
1717
|
+
} catch {
|
|
1718
|
+
return void 0;
|
|
1719
|
+
}
|
|
1720
|
+
};
|
|
1721
|
+
var decodeJsonString = (escaped) => {
|
|
1722
|
+
try {
|
|
1723
|
+
const decoded = JSON.parse(`"${escaped}"`);
|
|
1724
|
+
return typeof decoded === "string" ? decoded : "";
|
|
1725
|
+
} catch {
|
|
1726
|
+
return "";
|
|
1727
|
+
}
|
|
1728
|
+
};
|
|
1729
|
+
var cleanDetail = (raw) => {
|
|
1730
|
+
const clean = raw.replace(/[\u0000-\u001f\u007f]/g, " ").replace(/\s+/g, " ").trim();
|
|
1731
|
+
if (!clean) return void 0;
|
|
1732
|
+
return clean.length > 160 ? `${clean.slice(0, 159)}\u2026` : clean;
|
|
1733
|
+
};
|
|
1734
|
+
var errorDetail = (error) => {
|
|
1735
|
+
const candidates = [
|
|
1736
|
+
error?.data?.message,
|
|
1737
|
+
error?.message,
|
|
1738
|
+
error?.error
|
|
1739
|
+
];
|
|
1740
|
+
for (const candidate of candidates) {
|
|
1741
|
+
if (typeof candidate !== "string") continue;
|
|
1742
|
+
const detail = cleanDetail(candidate);
|
|
1743
|
+
if (detail) return detail;
|
|
1744
|
+
}
|
|
1745
|
+
const serialized = safeStringify(error);
|
|
1746
|
+
const match = serialized ? /"message"\s*:\s*"((?:[^"\\]|\\.)*)"/.exec(serialized) : void 0;
|
|
1747
|
+
if (match?.[1] !== void 0) {
|
|
1748
|
+
const detail = cleanDetail(decodeJsonString(match[1]));
|
|
1749
|
+
if (detail) return detail;
|
|
1750
|
+
}
|
|
1751
|
+
return void 0;
|
|
1752
|
+
};
|
|
1753
|
+
var isUnrecognizedError = (error) => {
|
|
1754
|
+
if (error == null) return false;
|
|
1755
|
+
const name = error?.name;
|
|
1756
|
+
if (typeof name === "string" && /abort/i.test(name)) return false;
|
|
1757
|
+
const serialized = safeStringify(error) ?? "";
|
|
1758
|
+
if (/MessageAbortedError|aborted/i.test(serialized)) return false;
|
|
1759
|
+
if (isLimitText(serialized)) return false;
|
|
1760
|
+
return true;
|
|
1761
|
+
};
|
|
1896
1762
|
var safeTitle = (raw, fallback = "session") => {
|
|
1897
1763
|
if (!raw) return fallback;
|
|
1898
1764
|
const clean = raw.replace(/[\u0000-\u001f\u007f]/g, " ").replace(/\s+/g, " ").trim();
|
|
@@ -2034,7 +1900,7 @@ var notifyHooks = (async ({ client, directory }, _options, deps = {}) => {
|
|
|
2034
1900
|
resolve2({ ok: false, detail: "no oc-notify helper found" });
|
|
2035
1901
|
return;
|
|
2036
1902
|
}
|
|
2037
|
-
const child = spawn(helper,
|
|
1903
|
+
const child = spawn(helper, helperArgv(helper, title, message, urgency, "utilities-terminal"), {
|
|
2038
1904
|
stdio: ["ignore", "ignore", "pipe"],
|
|
2039
1905
|
detached: true
|
|
2040
1906
|
});
|
|
@@ -2151,7 +2017,12 @@ var notifyHooks = (async ({ client, directory }, _options, deps = {}) => {
|
|
|
2151
2017
|
if (sessionID2) {
|
|
2152
2018
|
pending.delete(sessionID2);
|
|
2153
2019
|
permissions.delete(sessionID2);
|
|
2154
|
-
|
|
2020
|
+
const error = event.properties.error;
|
|
2021
|
+
if (isUnrecognizedError(error)) {
|
|
2022
|
+
void announce(sessionID2, "error", errorDetail(error));
|
|
2023
|
+
} else {
|
|
2024
|
+
void log("debug", "suppressed: recognized error", { sessionID: sessionID2 });
|
|
2025
|
+
}
|
|
2155
2026
|
}
|
|
2156
2027
|
return;
|
|
2157
2028
|
}
|
|
@@ -2203,7 +2074,7 @@ var LaCode = (async (input, options) => {
|
|
|
2203
2074
|
const notifier = await settled("notify", () => notifyHooks(input, options)) ?? inertHooks;
|
|
2204
2075
|
const config = async (cfg) => {
|
|
2205
2076
|
try {
|
|
2206
|
-
const injected = applyAgents(cfg, agentSet
|
|
2077
|
+
const injected = applyAgents(cfg, agentSet);
|
|
2207
2078
|
if (injected.added.length > 0) {
|
|
2208
2079
|
await log("info", `agents: added ${injected.added.length}, kept ${injected.kept.length} already defined by the user`);
|
|
2209
2080
|
}
|