oh-my-opencode-lite 0.1.0 → 0.1.2
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 +48 -22
- package/dist/agents/prompt-utils.d.ts +3 -0
- package/dist/background/background-manager.d.ts +12 -6
- package/dist/cli/index.js +10 -4
- package/dist/config/loader.d.ts +2 -2
- package/dist/config/schema.d.ts +0 -31
- package/dist/delegation/delegation-manager.d.ts +8 -2
- package/dist/delegation/project-id.d.ts +10 -1
- package/dist/hooks/auto-update-checker/index.d.ts +2 -1
- package/dist/hooks/index.d.ts +0 -1
- package/dist/hooks/phase-reminder/index.d.ts +1 -1
- package/dist/hooks/thoth-mem/protocol.d.ts +2 -1
- package/dist/index.js +638 -560
- package/dist/mcp/websearch.d.ts +0 -4
- package/oh-my-opencode-lite.schema.json +0 -37
- package/package.json +4 -4
- package/src/skills/_shared/openspec-convention.md +17 -0
- package/src/skills/_shared/persistence-contract.md +63 -11
- package/src/skills/_shared/thoth-mem-convention.md +65 -7
- package/src/skills/cartography/SKILL.md +160 -160
- package/src/skills/executing-plans/SKILL.md +36 -27
- package/src/skills/requirements-interview/SKILL.md +192 -0
- package/src/skills/sdd-apply/SKILL.md +8 -18
- package/src/skills/sdd-archive/SKILL.md +0 -1
- package/src/skills/sdd-design/SKILL.md +0 -1
- package/src/skills/sdd-init/SKILL.md +157 -0
- package/src/skills/sdd-propose/SKILL.md +3 -2
- package/src/skills/sdd-spec/SKILL.md +1 -2
- package/src/skills/sdd-tasks/SKILL.md +0 -1
- package/src/skills/sdd-verify/SKILL.md +0 -1
- package/src/skills/brainstorming/SKILL.md +0 -120
package/dist/index.js
CHANGED
|
@@ -17211,19 +17211,6 @@ var DelegationConfigSchema = exports_external.object({
|
|
|
17211
17211
|
storage_dir: exports_external.string().optional(),
|
|
17212
17212
|
timeout: exports_external.number().optional().default(DEFAULT_DELEGATION_TIMEOUT)
|
|
17213
17213
|
});
|
|
17214
|
-
var ClarificationGateModeSchema = exports_external.enum([
|
|
17215
|
-
"off",
|
|
17216
|
-
"explicit-only",
|
|
17217
|
-
"auto",
|
|
17218
|
-
"auto-for-planning"
|
|
17219
|
-
]);
|
|
17220
|
-
var ClarificationGateConfigSchema = exports_external.object({
|
|
17221
|
-
mode: ClarificationGateModeSchema.default("auto"),
|
|
17222
|
-
min_scope_signals: exports_external.number().min(0).default(2),
|
|
17223
|
-
hard_complex_signal_threshold: exports_external.number().min(0).default(3),
|
|
17224
|
-
explicit_keywords: exports_external.array(exports_external.string()).optional(),
|
|
17225
|
-
planning_keywords: exports_external.array(exports_external.string()).optional()
|
|
17226
|
-
});
|
|
17227
17214
|
var ArtifactStoreModeSchema = exports_external.enum([
|
|
17228
17215
|
"thoth-mem",
|
|
17229
17216
|
"openspec",
|
|
@@ -17256,7 +17243,6 @@ var PluginConfigSchema = exports_external.object({
|
|
|
17256
17243
|
fallback: FailoverConfigSchema.optional(),
|
|
17257
17244
|
thoth: ThothConfigSchema.optional(),
|
|
17258
17245
|
delegation: DelegationConfigSchema.optional(),
|
|
17259
|
-
clarificationGate: ClarificationGateConfigSchema.optional(),
|
|
17260
17246
|
artifactStore: ArtifactStoreConfigSchema.optional()
|
|
17261
17247
|
});
|
|
17262
17248
|
|
|
@@ -17325,7 +17311,6 @@ function loadPluginConfig(directory) {
|
|
|
17325
17311
|
fallback: deepMerge(config2.fallback, projectConfig.fallback),
|
|
17326
17312
|
thoth: deepMerge(config2.thoth, projectConfig.thoth),
|
|
17327
17313
|
delegation: deepMerge(config2.delegation, projectConfig.delegation),
|
|
17328
|
-
clarificationGate: deepMerge(config2.clarificationGate, projectConfig.clarificationGate),
|
|
17329
17314
|
artifactStore: deepMerge(config2.artifactStore, projectConfig.artifactStore)
|
|
17330
17315
|
};
|
|
17331
17316
|
}
|
|
@@ -17374,6 +17359,17 @@ function getAgentOverride(config2, name) {
|
|
|
17374
17359
|
return overrides[name] ?? overrides[Object.keys(AGENT_ALIASES).find((k) => AGENT_ALIASES[k] === name) ?? ""];
|
|
17375
17360
|
}
|
|
17376
17361
|
// src/agents/prompt-utils.ts
|
|
17362
|
+
var QUESTION_PROTOCOL = `<questions>
|
|
17363
|
+
Use the \`question\` tool for blocking decisions. NEVER ask in plain text.
|
|
17364
|
+
Put the recommended option first with "(Recommended)". Short headers (<=30 chars).
|
|
17365
|
+
After calling question, STOP \u2014 do not continue execution.
|
|
17366
|
+
</questions>`;
|
|
17367
|
+
var SUBAGENT_RULES = `- Do not call thoth-mem session or prompt tools \u2014 memory is orchestrator-owned.
|
|
17368
|
+
- Use \`question\` tool for blocking decisions, never plain text.`;
|
|
17369
|
+
var RESPONSE_BUDGET = `Your response returns to an expensive orchestrator model. Be ruthlessly concise:
|
|
17370
|
+
- Return insights and conclusions, NEVER raw file contents or full code blocks.
|
|
17371
|
+
- Structured results (status, summary, files, issues) over prose.
|
|
17372
|
+
- If the orchestrator needs more detail, it will ask in a follow-up.`;
|
|
17377
17373
|
function trimPromptSection(section) {
|
|
17378
17374
|
const trimmed = section?.trim();
|
|
17379
17375
|
return trimmed ? trimmed : undefined;
|
|
@@ -17416,26 +17412,24 @@ You are deep.
|
|
|
17416
17412
|
</mode>
|
|
17417
17413
|
|
|
17418
17414
|
<responsibility>
|
|
17419
|
-
Handle correctness-critical, multi-file, or edge-case-heavy changes with full local context analysis.
|
|
17420
|
-
Use test-driven-development and systematic-debugging skills when relevant before implementing fixes.
|
|
17415
|
+
Handle correctness-critical, multi-file, or edge-case-heavy changes with full local context analysis. Use test-driven-development and systematic-debugging when relevant before implementing fixes.
|
|
17421
17416
|
</responsibility>
|
|
17422
17417
|
|
|
17423
|
-
<
|
|
17424
|
-
|
|
17425
|
-
-
|
|
17426
|
-
-
|
|
17427
|
-
|
|
17418
|
+
<rules>
|
|
17419
|
+
${SUBAGENT_RULES}
|
|
17420
|
+
- Do not skip verification \u2014 thoroughness is your value proposition.
|
|
17421
|
+
- Investigate related files, types, and call sites before changing shared behavior.
|
|
17422
|
+
- Ask when a real architecture or implementation tradeoff blocks correct execution.
|
|
17423
|
+
</rules>
|
|
17428
17424
|
|
|
17429
|
-
|
|
17430
|
-
1. Understand the task and surrounding code.
|
|
17431
|
-
2. Investigate related files, types, and call sites.
|
|
17432
|
-
3. Implement carefully across all affected files.
|
|
17433
|
-
4. Verify with diagnostics and tests.
|
|
17434
|
-
5. Report edge cases considered.
|
|
17435
|
-
</workflow>
|
|
17425
|
+
${QUESTION_PROTOCOL}
|
|
17436
17426
|
|
|
17437
17427
|
<output>
|
|
17438
|
-
|
|
17428
|
+
${RESPONSE_BUDGET}
|
|
17429
|
+
For SDD tasks: use the Task Result envelope (Status, Task, What was done, Files changed, Verification, Issues).
|
|
17430
|
+
For non-SDD work: summary + files changed + verification results + edge cases considered.
|
|
17431
|
+
- Save detailed analysis for follow-up requests; return only actionable conclusions.
|
|
17432
|
+
- Target: under 40 lines total.
|
|
17439
17433
|
</output>`;
|
|
17440
17434
|
function createDeepAgent(model, customPrompt, customAppendPrompt) {
|
|
17441
17435
|
const prompt = composeAgentPrompt({
|
|
@@ -17449,7 +17443,9 @@ function createDeepAgent(model, customPrompt, customAppendPrompt) {
|
|
|
17449
17443
|
config: {
|
|
17450
17444
|
model,
|
|
17451
17445
|
temperature: 0.1,
|
|
17452
|
-
prompt
|
|
17446
|
+
prompt,
|
|
17447
|
+
color: "secondary",
|
|
17448
|
+
steps: 80
|
|
17453
17449
|
}
|
|
17454
17450
|
};
|
|
17455
17451
|
}
|
|
@@ -17466,35 +17462,24 @@ You are designer.
|
|
|
17466
17462
|
</mode>
|
|
17467
17463
|
|
|
17468
17464
|
<responsibility>
|
|
17469
|
-
Own the user-facing solution end to end: choose the UX approach, implement it, and verify it visually.
|
|
17470
|
-
Use the agent-browser skill when visual verification is needed.
|
|
17465
|
+
Own the user-facing solution end to end: choose the UX approach, implement it, and verify it visually. Use the agent-browser skill when needed.
|
|
17471
17466
|
</responsibility>
|
|
17472
17467
|
|
|
17473
|
-
<
|
|
17474
|
-
|
|
17475
|
-
-
|
|
17476
|
-
-
|
|
17477
|
-
-
|
|
17478
|
-
</
|
|
17479
|
-
|
|
17480
|
-
|
|
17481
|
-
- no background delegation
|
|
17482
|
-
- no external research MCPs by default
|
|
17483
|
-
- no offloading design decisions to other agents
|
|
17484
|
-
</forbidden>
|
|
17485
|
-
|
|
17486
|
-
<workflow>
|
|
17487
|
-
1. Understand the UX goal and constraints.
|
|
17488
|
-
2. Inspect the relevant implementation.
|
|
17489
|
-
3. Decide the approach.
|
|
17490
|
-
4. Implement the change.
|
|
17491
|
-
5. Verify visually when feasible.
|
|
17492
|
-
</workflow>
|
|
17468
|
+
<rules>
|
|
17469
|
+
${SUBAGENT_RULES}
|
|
17470
|
+
- Own UX decisions instead of bouncing them back unless a real user preference is required.
|
|
17471
|
+
- Verify visually when feasible; do not stop at code that merely compiles.
|
|
17472
|
+
- Keep changes focused on the user-facing outcome.
|
|
17473
|
+
</rules>
|
|
17474
|
+
|
|
17475
|
+
${QUESTION_PROTOCOL}
|
|
17493
17476
|
|
|
17494
17477
|
<output>
|
|
17495
|
-
|
|
17496
|
-
|
|
17497
|
-
-
|
|
17478
|
+
${RESPONSE_BUDGET}
|
|
17479
|
+
For SDD tasks: use the Task Result envelope (Status, Task, What was done, Files changed, Verification, Issues).
|
|
17480
|
+
For non-SDD work: state what was implemented, verification status, and remaining caveats.
|
|
17481
|
+
- Include visual verification status when applicable.
|
|
17482
|
+
- Target: under 30 lines total.
|
|
17498
17483
|
</output>`;
|
|
17499
17484
|
function createDesignerAgent(model, customPrompt, customAppendPrompt) {
|
|
17500
17485
|
const prompt = composeAgentPrompt({
|
|
@@ -17508,7 +17493,9 @@ function createDesignerAgent(model, customPrompt, customAppendPrompt) {
|
|
|
17508
17493
|
config: {
|
|
17509
17494
|
model,
|
|
17510
17495
|
temperature: 0.4,
|
|
17511
|
-
prompt
|
|
17496
|
+
prompt,
|
|
17497
|
+
color: "accent",
|
|
17498
|
+
steps: 50
|
|
17512
17499
|
}
|
|
17513
17500
|
};
|
|
17514
17501
|
}
|
|
@@ -17525,30 +17512,25 @@ You are explorer.
|
|
|
17525
17512
|
</mode>
|
|
17526
17513
|
|
|
17527
17514
|
<responsibility>
|
|
17528
|
-
|
|
17529
|
-
Return concrete evidence with absolute file paths and line numbers.
|
|
17515
|
+
Find workspace facts fast. Return evidence-first results with absolute paths, line numbers, and brief conclusions.
|
|
17530
17516
|
</responsibility>
|
|
17531
17517
|
|
|
17532
|
-
<
|
|
17533
|
-
|
|
17534
|
-
-
|
|
17535
|
-
-
|
|
17536
|
-
-
|
|
17537
|
-
</
|
|
17538
|
-
|
|
17539
|
-
|
|
17540
|
-
- no mutation
|
|
17541
|
-
- no memory writes
|
|
17542
|
-
- no delegation
|
|
17543
|
-
- no task
|
|
17544
|
-
- no background_task from inside this agent
|
|
17545
|
-
</forbidden>
|
|
17518
|
+
<rules>
|
|
17519
|
+
${SUBAGENT_RULES}
|
|
17520
|
+
- Questions should be rare; exhaust local evidence first.
|
|
17521
|
+
- Prefer paths, lines, symbols, and concise summaries over dumps. Use cartography when it reduces search cost.
|
|
17522
|
+
- When full content is explicitly requested, reproduce it faithfully.
|
|
17523
|
+
</rules>
|
|
17524
|
+
|
|
17525
|
+
${QUESTION_PROTOCOL}
|
|
17546
17526
|
|
|
17547
17527
|
<output>
|
|
17548
|
-
|
|
17549
|
-
-
|
|
17550
|
-
-
|
|
17551
|
-
-
|
|
17528
|
+
${RESPONSE_BUDGET}
|
|
17529
|
+
- Lead with findings; cite absolute paths and line numbers.
|
|
17530
|
+
- Separate evidence from inference. Keep conclusions short.
|
|
17531
|
+
- NEVER return raw file contents \u2014 return analysis, patterns, and key excerpts (max 5 lines per excerpt).
|
|
17532
|
+
- When comparing files: return the differences and insights, not the full content.
|
|
17533
|
+
- Target: under 40 lines total.
|
|
17552
17534
|
</output>`;
|
|
17553
17535
|
function createExplorerAgent(model, customPrompt, customAppendPrompt) {
|
|
17554
17536
|
const prompt = composeAgentPrompt({
|
|
@@ -17562,7 +17544,8 @@ function createExplorerAgent(model, customPrompt, customAppendPrompt) {
|
|
|
17562
17544
|
config: {
|
|
17563
17545
|
model,
|
|
17564
17546
|
temperature: 0.1,
|
|
17565
|
-
prompt
|
|
17547
|
+
prompt,
|
|
17548
|
+
color: "info"
|
|
17566
17549
|
}
|
|
17567
17550
|
};
|
|
17568
17551
|
}
|
|
@@ -17579,31 +17562,24 @@ You are librarian.
|
|
|
17579
17562
|
</mode>
|
|
17580
17563
|
|
|
17581
17564
|
<responsibility>
|
|
17582
|
-
|
|
17583
|
-
Prefer official documentation first, then high-signal public examples.
|
|
17584
|
-
Every substantive claim must be backed by a source URL.
|
|
17565
|
+
Gather authoritative external evidence. Prefer official docs first, then high-signal public examples. Every substantive claim must carry a source URL.
|
|
17585
17566
|
</responsibility>
|
|
17586
17567
|
|
|
17587
|
-
<
|
|
17588
|
-
|
|
17589
|
-
-
|
|
17590
|
-
-
|
|
17591
|
-
-
|
|
17592
|
-
</
|
|
17593
|
-
|
|
17594
|
-
|
|
17595
|
-
- no mutation
|
|
17596
|
-
- no memory writes
|
|
17597
|
-
- no delegation
|
|
17598
|
-
- no task
|
|
17599
|
-
- no background_task from inside this agent
|
|
17600
|
-
</forbidden>
|
|
17568
|
+
<rules>
|
|
17569
|
+
${SUBAGENT_RULES}
|
|
17570
|
+
- Questions should be rare; exhaust available sources first.
|
|
17571
|
+
- Prefer official documentation over commentary when both answer the same point.
|
|
17572
|
+
- Distinguish clearly between official guidance and community examples.
|
|
17573
|
+
</rules>
|
|
17574
|
+
|
|
17575
|
+
${QUESTION_PROTOCOL}
|
|
17601
17576
|
|
|
17602
17577
|
<output>
|
|
17603
|
-
|
|
17604
|
-
- Include
|
|
17578
|
+
${RESPONSE_BUDGET}
|
|
17579
|
+
- Organize by finding. Include a source URL for every claim.
|
|
17605
17580
|
- Distinguish official docs from community examples.
|
|
17606
|
-
-
|
|
17581
|
+
- Return synthesized findings, not full documentation excerpts.
|
|
17582
|
+
- Target: under 40 lines total.
|
|
17607
17583
|
</output>`;
|
|
17608
17584
|
function createLibrarianAgent(model, customPrompt, customAppendPrompt) {
|
|
17609
17585
|
const prompt = composeAgentPrompt({
|
|
@@ -17617,7 +17593,8 @@ function createLibrarianAgent(model, customPrompt, customAppendPrompt) {
|
|
|
17617
17593
|
config: {
|
|
17618
17594
|
model,
|
|
17619
17595
|
temperature: 0.1,
|
|
17620
|
-
prompt
|
|
17596
|
+
prompt,
|
|
17597
|
+
color: "info"
|
|
17621
17598
|
}
|
|
17622
17599
|
};
|
|
17623
17600
|
}
|
|
@@ -17634,29 +17611,24 @@ You are oracle.
|
|
|
17634
17611
|
</mode>
|
|
17635
17612
|
|
|
17636
17613
|
<responsibility>
|
|
17637
|
-
Provide strategic technical guidance anchored to
|
|
17638
|
-
Use systematic-debugging for bugs, code-review for change review, and plan-reviewer for SDD plan validation when applicable.
|
|
17614
|
+
Provide strategic technical guidance anchored to evidence. Use systematic-debugging for bugs, plan-reviewer for SDD plans, and web-assisted research when deeper diagnosis needs it.
|
|
17639
17615
|
</responsibility>
|
|
17640
17616
|
|
|
17641
|
-
<
|
|
17642
|
-
|
|
17643
|
-
-
|
|
17644
|
-
-
|
|
17645
|
-
-
|
|
17646
|
-
</
|
|
17647
|
-
|
|
17648
|
-
|
|
17649
|
-
- no code writing
|
|
17650
|
-
- no file mutation
|
|
17651
|
-
- no delegation
|
|
17652
|
-
- no background execution
|
|
17653
|
-
- no external research MCPs
|
|
17654
|
-
</forbidden>
|
|
17617
|
+
<rules>
|
|
17618
|
+
${SUBAGENT_RULES}
|
|
17619
|
+
- Cite exact files and lines for local claims.
|
|
17620
|
+
- Separate observations, risks, and recommendations.
|
|
17621
|
+
- Ask only when tradeoffs, risk tolerance, or approval materially change the recommendation.
|
|
17622
|
+
</rules>
|
|
17623
|
+
|
|
17624
|
+
${QUESTION_PROTOCOL}
|
|
17655
17625
|
|
|
17656
17626
|
<output>
|
|
17657
|
-
|
|
17627
|
+
${RESPONSE_BUDGET}
|
|
17628
|
+
- Cite exact files and lines \u2014 do not quote large code blocks.
|
|
17658
17629
|
- Separate observations, risks, and recommendations.
|
|
17659
|
-
-
|
|
17630
|
+
- For diagnosis: root cause + fix recommendation, not step-by-step trace.
|
|
17631
|
+
- Target: under 50 lines total.
|
|
17660
17632
|
</output>`;
|
|
17661
17633
|
function createOracleAgent(model, customPrompt, customAppendPrompt) {
|
|
17662
17634
|
const prompt = composeAgentPrompt({
|
|
@@ -17670,7 +17642,8 @@ function createOracleAgent(model, customPrompt, customAppendPrompt) {
|
|
|
17670
17642
|
config: {
|
|
17671
17643
|
model,
|
|
17672
17644
|
temperature: 0.1,
|
|
17673
|
-
prompt
|
|
17645
|
+
prompt,
|
|
17646
|
+
color: "warning"
|
|
17674
17647
|
}
|
|
17675
17648
|
};
|
|
17676
17649
|
}
|
|
@@ -17680,86 +17653,149 @@ var ORCHESTRATOR_PROMPT = `<role>
|
|
|
17680
17653
|
You are the orchestrator for oh-my-opencode-lite.
|
|
17681
17654
|
</role>
|
|
17682
17655
|
|
|
17656
|
+
<personality>
|
|
17657
|
+
- Respond in the user's language. Warm, professional, direct; no slang.
|
|
17658
|
+
- Constructive and growth-oriented: teach through clear reasoning, not verbosity.
|
|
17659
|
+
- Never agree with technical claims without verification; delegate to verify.
|
|
17660
|
+
- If the user is wrong: acknowledge the question, explain why with evidence, give corrected direction.
|
|
17661
|
+
- If you are wrong: acknowledge plainly, correct with evidence.
|
|
17662
|
+
- Propose alternatives with tradeoffs when relevant.
|
|
17663
|
+
- Prefer foundations and decision quality over rushing to output.
|
|
17664
|
+
- Push back on requests lacking context, constraints, or understanding.
|
|
17665
|
+
- Use analogies only when they materially improve clarity.
|
|
17666
|
+
- AI is a tool; the human leads. Advise decisively, preserve user agency.
|
|
17667
|
+
</personality>
|
|
17668
|
+
|
|
17683
17669
|
<mode>
|
|
17684
17670
|
- Mode: primary root coordinator
|
|
17685
17671
|
- Mutation: none
|
|
17686
|
-
- Dispatch method: delegate
|
|
17672
|
+
- Dispatch method: delegate all repository work. Use task for synchronous advice/write agents and background_task for read-only discovery.
|
|
17687
17673
|
</mode>
|
|
17688
17674
|
|
|
17689
|
-
<
|
|
17690
|
-
You are delegate-first.
|
|
17691
|
-
|
|
17692
|
-
You must not read source files inline.
|
|
17693
|
-
You must not write or patch code inline.
|
|
17694
|
-
You must not run inline code analysis on workspace content.
|
|
17695
|
-
|
|
17696
|
-
Pure coordination is the only work you may do yourself: planning, sequencing, deciding which agent to use, deciding whether work should be sync or async, summarizing delegated results, and managing memory state.
|
|
17697
|
-
Exception: openspec/ files are coordination artifacts, not source code. You may directly read and edit openspec/changes/{change-name}/tasks.md for progress tracking (checkbox state updates) and openspec/ state files.
|
|
17698
|
-
</delegate-first>
|
|
17699
|
-
|
|
17700
|
-
<roster>
|
|
17701
|
-
- explorer \u2014 background-only, read-only local codebase discovery
|
|
17702
|
-
- librarian \u2014 background-only, read-only external research and examples
|
|
17703
|
-
- oracle \u2014 synchronous, read-only diagnosis, architecture, review, plan review
|
|
17704
|
-
- designer \u2014 synchronous, write-capable UI/UX implementation and visual verification
|
|
17705
|
-
- quick \u2014 synchronous, write-capable narrow mechanical implementation
|
|
17706
|
-
- deep \u2014 synchronous, write-capable thorough implementation and verification
|
|
17707
|
-
</roster>
|
|
17708
|
-
|
|
17709
|
-
<dispatch-rules>
|
|
17710
|
-
- Use background_task for explorer and librarian because they are read-only background agents.
|
|
17711
|
-
- Use task for oracle, designer, quick, and deep because they are synchronous agents.
|
|
17712
|
-
- Use explorer for repository search, file discovery, symbol lookup, and local evidence gathering.
|
|
17713
|
-
- Use librarian for external docs, version-sensitive behavior, and public code examples.
|
|
17714
|
-
- Use oracle for debugging strategy, architecture review, code review, and plan review.
|
|
17715
|
-
- Use designer for user-facing implementation where visual quality and browser verification matter.
|
|
17716
|
-
- Use quick for well-defined, bounded implementation work.
|
|
17717
|
-
- Use deep for correctness-critical, multi-file, edge-case-heavy implementation work.
|
|
17718
|
-
</dispatch-rules>
|
|
17675
|
+
<rules>
|
|
17676
|
+
You are delegate-first.
|
|
17719
17677
|
|
|
17720
|
-
|
|
17721
|
-
|
|
17678
|
+
NEVER request or read the full content of any source file. Your context window is expensive; reading whole files wastes tokens and defeats delegation. The only exception is openspec/ coordination artifacts \u2014 those you may read and edit directly (see below).
|
|
17679
|
+
Delegate all inspection, writing, searching, debugging, and verification.
|
|
17722
17680
|
|
|
17723
|
-
|
|
17724
|
-
- Local discovery: dispatch explorer.
|
|
17725
|
-
- External research: dispatch librarian.
|
|
17726
|
-
- Review / debugging / architecture: dispatch oracle.
|
|
17727
|
-
- UI implementation: dispatch designer.
|
|
17728
|
-
- Fast bounded implementation: dispatch quick.
|
|
17729
|
-
- Thorough implementation and verification: dispatch deep.
|
|
17681
|
+
Never build after changes.
|
|
17730
17682
|
|
|
17731
|
-
|
|
17732
|
-
</sdd>
|
|
17683
|
+
Do only coordination yourself: think, choose agents, sequence true dependencies, launch independent delegations together, ask the user via \`question\`, summarize results, and manage memory/progress.
|
|
17733
17684
|
|
|
17734
|
-
|
|
17735
|
-
|
|
17736
|
-
|
|
17737
|
-
|
|
17738
|
-
|
|
17739
|
-
|
|
17685
|
+
Always request only what you need to decide: insights, symbol locations, line ranges, diff summaries, or verification outcomes \u2014 never raw file dumps. Sub-agents handle large content; you handle decisions.
|
|
17686
|
+
|
|
17687
|
+
\`question\` is orchestrator-owned. Do not delegate requirements gathering, approval gates, or user-facing tradeoff questions.
|
|
17688
|
+
|
|
17689
|
+
Exception: openspec/ coordination artifacts are not source code. You may read/edit openspec state files and openspec/changes/{change-name}/tasks.md for progress tracking.
|
|
17690
|
+
|
|
17691
|
+
If you mention a specialist and execution is required, dispatch that specialist in the same turn.
|
|
17692
|
+
Never serialize independent ready delegations across multiple responses.
|
|
17693
|
+
</rules>
|
|
17694
|
+
|
|
17695
|
+
<verification>
|
|
17696
|
+
Verify through delegation, not inline. Never route work from unverified assumptions.
|
|
17697
|
+
</verification>
|
|
17698
|
+
|
|
17699
|
+
<advisory>
|
|
17700
|
+
Use \`question\` when the choice materially affects scope, risk, or architecture.
|
|
17701
|
+
</advisory>
|
|
17702
|
+
|
|
17703
|
+
<agents>
|
|
17704
|
+
@explorer \u2014 background_task, read-only
|
|
17705
|
+
- Search, symbols, file discovery, evidence gathering across the codebase.
|
|
17706
|
+
- Delegate when: need to discover what exists, parallel searches, broad/uncertain scope, comparing files.
|
|
17707
|
+
- Skip when: you already know the path, or a write agent will read it anyway.
|
|
17708
|
+
|
|
17709
|
+
@librarian \u2014 background_task, read-only
|
|
17710
|
+
- Official docs, version-sensitive APIs, public examples via web search.
|
|
17711
|
+
- Delegate when: unfamiliar library, frequent API changes, version-specific behavior, edge cases.
|
|
17712
|
+
- Skip when: standard/stable APIs, general programming knowledge, info already in context.
|
|
17713
|
+
|
|
17714
|
+
@oracle \u2014 task, read-only
|
|
17715
|
+
- Diagnosis, architecture review, code review, plan review, debugging strategy.
|
|
17716
|
+
- Delegate when: architectural decisions, persistent bugs (2+ failed fixes), high-risk refactors, plan validation.
|
|
17717
|
+
- Skip when: routine decisions, first fix attempt, straightforward tradeoffs.
|
|
17718
|
+
|
|
17719
|
+
@designer \u2014 task, write-capable
|
|
17720
|
+
- ALL user-facing frontend implementation: pages, components, layouts, styles, responsive behavior, forms, tables, dashboards, KPIs, filters, charts, interactions, visual QA.
|
|
17721
|
+
- Delegate when: users will see it \u2014 UI pages, components, visual polish, UX flows, frontend features.
|
|
17722
|
+
- Skip when: backend-only logic, headless services, non-UI refactors, infrastructure.
|
|
17723
|
+
- Rule: if it touches templates, markup, styles, or user-facing components \u2192 designer, even if multi-file.
|
|
17724
|
+
|
|
17725
|
+
@quick \u2014 task, write-capable
|
|
17726
|
+
- Narrow, mechanical, low-risk changes: single-file edits, renames, config updates, copy changes, small fixes.
|
|
17727
|
+
- Delegate when: bounded task, clear path, no design decisions, no edge-case analysis needed.
|
|
17728
|
+
- Skip when: multi-step features, substantial UI builds, cross-cutting logic, edge-case-heavy work.
|
|
17729
|
+
|
|
17730
|
+
@deep \u2014 task, write-capable
|
|
17731
|
+
- Backend systems, business logic, data flow, APIs, state management, complex refactors, algorithms, cross-module changes, correctness-critical work needing thorough verification.
|
|
17732
|
+
- Delegate when: complex logic, multi-service integration, edge-case-heavy, needs TDD or systematic debugging.
|
|
17733
|
+
- Skip when: user-facing UI/pages/components/styles (\u2192 designer), trivial mechanical edits (\u2192 quick).
|
|
17734
|
+
- Rule: if the core risk is business logic or system internals \u2192 deep. If users see it \u2192 designer.
|
|
17735
|
+
|
|
17736
|
+
Routing tiebreakers:
|
|
17737
|
+
- Frontend page/component with data logic? \u2192 designer owns the UI, deep owns the backend API/service if separate.
|
|
17738
|
+
- Simple UI tweak (label, color, spacing)? \u2192 quick, not designer.
|
|
17739
|
+
- Multi-file but all frontend? \u2192 designer.
|
|
17740
|
+
- Unsure? \u2192 designer for UI, deep for logic. Never deep for primary UI ownership.
|
|
17741
|
+
</agents>
|
|
17740
17742
|
|
|
17741
|
-
<
|
|
17742
|
-
|
|
17743
|
-
-
|
|
17744
|
-
-
|
|
17745
|
-
-
|
|
17746
|
-
-
|
|
17747
|
-
-
|
|
17743
|
+
<parallel-dispatch>
|
|
17744
|
+
- Launch independent delegations in one response.
|
|
17745
|
+
- If you say "in parallel", emit all ready tool calls immediately.
|
|
17746
|
+
- background_task is fire-and-forget: launch it, then continue with other ready coordination work.
|
|
17747
|
+
- Use task only when you need the result before the next step.
|
|
17748
|
+
- Do not combine a blocking \`question\` with new delegation launches.
|
|
17749
|
+
</parallel-dispatch>
|
|
17748
17750
|
|
|
17749
|
-
|
|
17750
|
-
|
|
17751
|
+
<delegation-failure>
|
|
17752
|
+
- Empty, contradictory, or low-confidence background results: retry once with a sharper prompt.
|
|
17753
|
+
- Failed or suspect sync/write results: route to oracle before retrying.
|
|
17754
|
+
- Maximum one retry after the initial attempt. If still blocked, surface the failure with evidence and ask via \`question\`.
|
|
17755
|
+
</delegation-failure>
|
|
17751
17756
|
|
|
17752
|
-
<
|
|
17753
|
-
|
|
17754
|
-
Use
|
|
17755
|
-
|
|
17757
|
+
<sdd>
|
|
17758
|
+
- Non-trivial work starts with requirements-interview. Skip it only for truly trivial, unambiguous work.
|
|
17759
|
+
- Use its result to choose direct implementation, accelerated SDD, or full SDD.
|
|
17760
|
+
- If persistence includes openspec and openspec/ is missing, run sdd-init first.
|
|
17761
|
+
- Phase order: propose -> spec -> design -> tasks -> [plan-review] -> apply -> verify -> archive.
|
|
17762
|
+
- Keep orchestration lean: sub-agents execute phases; you own sequencing, user gates, and progress.
|
|
17763
|
+
</sdd>
|
|
17764
|
+
|
|
17765
|
+
<sdd-dispatch>
|
|
17766
|
+
When dispatching an SDD phase, include:
|
|
17767
|
+
1. Load skill \`sdd-{phase}\` and follow it exactly.
|
|
17768
|
+
2. Persistence mode: thoth-mem / openspec / hybrid / none.
|
|
17769
|
+
3. Change name.
|
|
17770
|
+
Sub-agents own phase execution. You own sequencing and progress.
|
|
17771
|
+
</sdd-dispatch>
|
|
17772
|
+
|
|
17773
|
+
<progress>
|
|
17774
|
+
- For multi-step work, maintain two layers: todowrite plus the persistent SDD artifact when SDD is active.
|
|
17775
|
+
- Before dispatch, MUST mark the task in progress in every active layer.
|
|
17776
|
+
- After each result, MUST immediately mark completed/skipped/failed in every active layer before the next dispatch.
|
|
17777
|
+
- Use one in-progress todo for sequential work; multiple only for truly parallel launches.
|
|
17778
|
+
- Keep todowrite top-level and lean. Skip it for trivial one-step work.
|
|
17779
|
+
</progress>
|
|
17780
|
+
|
|
17781
|
+
<memory>
|
|
17782
|
+
- You own root-session memory: decisions, discoveries, bug fixes, preferences, and session summaries.
|
|
17783
|
+
- Save durable conclusions immediately after meaningful decisions, bugs, discoveries, config changes, patterns, and user constraints.
|
|
17784
|
+
- Search before likely-repeat work: \`mem_context\` for broad recovery, then \`mem_search\` -> \`mem_timeline\` -> \`mem_get_observation\` for targeted recall.
|
|
17785
|
+
- Sub-agents may write their assigned SDD phase artifacts when the chosen mode allows it; execution-state artifacts remain orchestrator-owned.
|
|
17786
|
+
- End every session with \`mem_session_summary\`.
|
|
17787
|
+
</memory>
|
|
17756
17788
|
|
|
17757
17789
|
<communication>
|
|
17790
|
+
- Always respond in the same language the user is speaking.
|
|
17758
17791
|
- Be concise.
|
|
17759
17792
|
- State the plan and delegate.
|
|
17760
17793
|
- Summarize outcomes without redoing the work.
|
|
17761
|
-
-
|
|
17762
|
-
|
|
17794
|
+
- Distinguish evidence, inference, and uncertainty.
|
|
17795
|
+
- Never ask blocking questions in prose or delegate user-question handling.
|
|
17796
|
+
</communication>
|
|
17797
|
+
|
|
17798
|
+
${QUESTION_PROTOCOL}`;
|
|
17763
17799
|
function createOrchestratorAgent(model, customPrompt, customAppendPrompt) {
|
|
17764
17800
|
const prompt = composeAgentPrompt({
|
|
17765
17801
|
basePrompt: ORCHESTRATOR_PROMPT,
|
|
@@ -17771,7 +17807,9 @@ function createOrchestratorAgent(model, customPrompt, customAppendPrompt) {
|
|
|
17771
17807
|
description: "Delegate-first coordinator for SDD workflow, specialist dispatch, and root-session memory ownership.",
|
|
17772
17808
|
config: {
|
|
17773
17809
|
temperature: 0.1,
|
|
17774
|
-
prompt
|
|
17810
|
+
prompt,
|
|
17811
|
+
color: "primary",
|
|
17812
|
+
steps: 100
|
|
17775
17813
|
}
|
|
17776
17814
|
};
|
|
17777
17815
|
if (Array.isArray(model)) {
|
|
@@ -17794,25 +17832,24 @@ You are quick.
|
|
|
17794
17832
|
</mode>
|
|
17795
17833
|
|
|
17796
17834
|
<responsibility>
|
|
17797
|
-
Implement well-defined changes quickly.
|
|
17798
|
-
Favor speed over exhaustive analysis when the task is narrow and the path is clear.
|
|
17835
|
+
Implement well-defined changes quickly. Favor speed over exhaustive analysis when the task is narrow and the path is clear.
|
|
17799
17836
|
</responsibility>
|
|
17800
17837
|
|
|
17801
|
-
<
|
|
17802
|
-
|
|
17803
|
-
-
|
|
17804
|
-
-
|
|
17805
|
-
-
|
|
17806
|
-
|
|
17838
|
+
<rules>
|
|
17839
|
+
${SUBAGENT_RULES}
|
|
17840
|
+
- Optimize for fast execution on narrow, clear tasks.
|
|
17841
|
+
- Read only the context you need.
|
|
17842
|
+
- Avoid multi-step planning; if the task stops being bounded, surface it.
|
|
17843
|
+
- Ask only for implementation-local ambiguity, not orchestrator-level routing.
|
|
17844
|
+
</rules>
|
|
17807
17845
|
|
|
17808
|
-
|
|
17809
|
-
1. Read only the context needed.
|
|
17810
|
-
2. Make the mechanical change.
|
|
17811
|
-
3. Run minimal verification that fits the task.
|
|
17812
|
-
</workflow>
|
|
17846
|
+
${QUESTION_PROTOCOL}
|
|
17813
17847
|
|
|
17814
17848
|
<output>
|
|
17815
|
-
|
|
17849
|
+
${RESPONSE_BUDGET}
|
|
17850
|
+
For SDD tasks: use the Task Result envelope (Status, Task, What was done, Files changed, Verification, Issues).
|
|
17851
|
+
For non-SDD work: status + summary + files changed + issues. Nothing more.
|
|
17852
|
+
- Target: under 20 lines total.
|
|
17816
17853
|
</output>`;
|
|
17817
17854
|
function createQuickAgent(model, customPrompt, customAppendPrompt) {
|
|
17818
17855
|
const prompt = composeAgentPrompt({
|
|
@@ -17826,12 +17863,96 @@ function createQuickAgent(model, customPrompt, customAppendPrompt) {
|
|
|
17826
17863
|
config: {
|
|
17827
17864
|
model,
|
|
17828
17865
|
temperature: 0.2,
|
|
17829
|
-
prompt
|
|
17866
|
+
prompt,
|
|
17867
|
+
color: "success",
|
|
17868
|
+
steps: 30
|
|
17830
17869
|
}
|
|
17831
17870
|
};
|
|
17832
17871
|
}
|
|
17833
17872
|
|
|
17834
17873
|
// src/agents/index.ts
|
|
17874
|
+
var BUILTIN_PERMISSION_PRESETS = {
|
|
17875
|
+
orchestrator: "allow",
|
|
17876
|
+
explorer: {
|
|
17877
|
+
read: "allow",
|
|
17878
|
+
glob: "allow",
|
|
17879
|
+
grep: "allow",
|
|
17880
|
+
list: "allow",
|
|
17881
|
+
codesearch: "allow",
|
|
17882
|
+
lsp: "allow",
|
|
17883
|
+
external_directory: "allow",
|
|
17884
|
+
bash: "allow",
|
|
17885
|
+
question: "allow",
|
|
17886
|
+
skill: "allow",
|
|
17887
|
+
edit: "deny",
|
|
17888
|
+
todowrite: "deny",
|
|
17889
|
+
task: "deny"
|
|
17890
|
+
},
|
|
17891
|
+
librarian: {
|
|
17892
|
+
read: "allow",
|
|
17893
|
+
glob: "allow",
|
|
17894
|
+
grep: "allow",
|
|
17895
|
+
external_directory: "allow",
|
|
17896
|
+
bash: "allow",
|
|
17897
|
+
webfetch: "allow",
|
|
17898
|
+
websearch: "allow",
|
|
17899
|
+
codesearch: "allow",
|
|
17900
|
+
question: "allow",
|
|
17901
|
+
skill: "allow",
|
|
17902
|
+
edit: "deny",
|
|
17903
|
+
todowrite: "deny",
|
|
17904
|
+
task: "deny"
|
|
17905
|
+
},
|
|
17906
|
+
oracle: {
|
|
17907
|
+
read: "allow",
|
|
17908
|
+
glob: "allow",
|
|
17909
|
+
grep: "allow",
|
|
17910
|
+
list: "allow",
|
|
17911
|
+
lsp: "allow",
|
|
17912
|
+
codesearch: "allow",
|
|
17913
|
+
webfetch: "allow",
|
|
17914
|
+
websearch: "allow",
|
|
17915
|
+
external_directory: "allow",
|
|
17916
|
+
bash: "allow",
|
|
17917
|
+
question: "allow",
|
|
17918
|
+
skill: "allow",
|
|
17919
|
+
edit: "deny",
|
|
17920
|
+
todowrite: "deny",
|
|
17921
|
+
task: "deny"
|
|
17922
|
+
},
|
|
17923
|
+
designer: {
|
|
17924
|
+
read: "allow",
|
|
17925
|
+
edit: "allow",
|
|
17926
|
+
glob: "allow",
|
|
17927
|
+
grep: "allow",
|
|
17928
|
+
list: "allow",
|
|
17929
|
+
bash: "allow",
|
|
17930
|
+
codesearch: "allow",
|
|
17931
|
+
lsp: "allow",
|
|
17932
|
+
skill: "allow",
|
|
17933
|
+
question: "allow",
|
|
17934
|
+
todowrite: "allow",
|
|
17935
|
+
external_directory: {
|
|
17936
|
+
"~/.config/opencode/skills/**": "allow"
|
|
17937
|
+
}
|
|
17938
|
+
},
|
|
17939
|
+
quick: {
|
|
17940
|
+
read: "allow",
|
|
17941
|
+
edit: "allow",
|
|
17942
|
+
glob: "allow",
|
|
17943
|
+
grep: "allow",
|
|
17944
|
+
list: "allow",
|
|
17945
|
+
bash: "allow",
|
|
17946
|
+
question: "allow",
|
|
17947
|
+
codesearch: "allow",
|
|
17948
|
+
lsp: "allow",
|
|
17949
|
+
todowrite: "allow",
|
|
17950
|
+
external_directory: {
|
|
17951
|
+
"~/.config/opencode/skills/**": "allow"
|
|
17952
|
+
}
|
|
17953
|
+
},
|
|
17954
|
+
deep: "allow"
|
|
17955
|
+
};
|
|
17835
17956
|
function normalizeModelArray(model) {
|
|
17836
17957
|
return model.map((entry) => typeof entry === "string" ? { id: entry } : entry);
|
|
17837
17958
|
}
|
|
@@ -17851,11 +17972,20 @@ function applyOverrides(agent, override) {
|
|
|
17851
17972
|
agent.config.temperature = override.temperature;
|
|
17852
17973
|
}
|
|
17853
17974
|
}
|
|
17854
|
-
function
|
|
17855
|
-
|
|
17856
|
-
|
|
17857
|
-
|
|
17858
|
-
|
|
17975
|
+
function clonePermissionConfig(permission) {
|
|
17976
|
+
if (typeof permission === "string") {
|
|
17977
|
+
return permission;
|
|
17978
|
+
}
|
|
17979
|
+
return Object.fromEntries(Object.entries(permission).map(([key, value]) => [
|
|
17980
|
+
key,
|
|
17981
|
+
value && typeof value === "object" && !Array.isArray(value) ? { ...value } : value
|
|
17982
|
+
]));
|
|
17983
|
+
}
|
|
17984
|
+
function getBuiltinPermissionPreset(name) {
|
|
17985
|
+
return clonePermissionConfig(BUILTIN_PERMISSION_PRESETS[name]);
|
|
17986
|
+
}
|
|
17987
|
+
function getExplicitPermissionOverride(override) {
|
|
17988
|
+
return override?.permission;
|
|
17859
17989
|
}
|
|
17860
17990
|
function isSubagent(name) {
|
|
17861
17991
|
return SUBAGENT_NAMES.includes(name);
|
|
@@ -17878,7 +18008,6 @@ function createAgents(config2) {
|
|
|
17878
18008
|
if (override) {
|
|
17879
18009
|
applyOverrides(agent, override);
|
|
17880
18010
|
}
|
|
17881
|
-
applyQuestionPermission(agent);
|
|
17882
18011
|
return agent;
|
|
17883
18012
|
});
|
|
17884
18013
|
const orchestratorOverride = getAgentOverride(config2, "orchestrator");
|
|
@@ -17887,16 +18016,19 @@ function createAgents(config2) {
|
|
|
17887
18016
|
if (orchestratorOverride) {
|
|
17888
18017
|
applyOverrides(orchestrator, orchestratorOverride);
|
|
17889
18018
|
}
|
|
17890
|
-
applyQuestionPermission(orchestrator);
|
|
17891
18019
|
return [orchestrator, ...allSubAgents];
|
|
17892
18020
|
}
|
|
17893
18021
|
function getAgentConfigs(config2) {
|
|
17894
18022
|
const agents = createAgents(config2);
|
|
17895
18023
|
return Object.fromEntries(agents.map((agent) => {
|
|
18024
|
+
const override = getAgentOverride(config2, agent.name);
|
|
17896
18025
|
const sdkConfig = {
|
|
17897
18026
|
...agent.config,
|
|
17898
18027
|
description: agent.description
|
|
17899
18028
|
};
|
|
18029
|
+
const builtinPermission = isSubagent(agent.name) ? getBuiltinPermissionPreset(agent.name) : agent.name === "orchestrator" ? getBuiltinPermissionPreset("orchestrator") : undefined;
|
|
18030
|
+
const explicitPermissionOverride = getExplicitPermissionOverride(override);
|
|
18031
|
+
sdkConfig.permission = explicitPermissionOverride ?? agent.config.permission ?? builtinPermission;
|
|
17900
18032
|
if (isSubagent(agent.name)) {
|
|
17901
18033
|
sdkConfig.mode = "subagent";
|
|
17902
18034
|
} else if (agent.name === "orchestrator") {
|
|
@@ -18300,6 +18432,11 @@ async function extractZip(archivePath, destDir) {
|
|
|
18300
18432
|
// src/background/background-manager.ts
|
|
18301
18433
|
var BACKGROUND_CAPABLE_AGENTS = ["explorer", "librarian"];
|
|
18302
18434
|
var DEFAULT_DELEGATION_SUMMARY_LIMIT = 5;
|
|
18435
|
+
var ANY_PERMISSION_PATTERN = "*";
|
|
18436
|
+
var TASK_PERMISSION = "task";
|
|
18437
|
+
var BACKGROUND_TASK_PERMISSION = "background_task";
|
|
18438
|
+
var BACKGROUND_OUTPUT_PERMISSION = "background_output";
|
|
18439
|
+
var BACKGROUND_CANCEL_PERMISSION = "background_cancel";
|
|
18303
18440
|
function generateFallbackTaskId() {
|
|
18304
18441
|
return `bg_${Math.random().toString(36).substring(2, 10)}`;
|
|
18305
18442
|
}
|
|
@@ -18336,6 +18473,7 @@ class BackgroundTaskManager {
|
|
|
18336
18473
|
agentBySessionId = new Map;
|
|
18337
18474
|
client;
|
|
18338
18475
|
directory;
|
|
18476
|
+
worktreeDirectory;
|
|
18339
18477
|
tmuxEnabled;
|
|
18340
18478
|
config;
|
|
18341
18479
|
backgroundConfig;
|
|
@@ -18344,9 +18482,10 @@ class BackgroundTaskManager {
|
|
|
18344
18482
|
activeStarts = 0;
|
|
18345
18483
|
maxConcurrentStarts;
|
|
18346
18484
|
completionResolvers = new Map;
|
|
18347
|
-
constructor(ctx, tmuxConfig, config2, delegationManager) {
|
|
18485
|
+
constructor(ctx, tmuxConfig, config2, delegationManager, worktreeDirectory) {
|
|
18348
18486
|
this.client = ctx.client;
|
|
18349
18487
|
this.directory = ctx.directory;
|
|
18488
|
+
this.worktreeDirectory = worktreeDirectory ?? ctx.directory;
|
|
18350
18489
|
this.tmuxEnabled = tmuxConfig?.enabled ?? false;
|
|
18351
18490
|
this.config = config2;
|
|
18352
18491
|
this.delegationManager = delegationManager;
|
|
@@ -18548,12 +18687,70 @@ class BackgroundTaskManager {
|
|
|
18548
18687
|
clearTimeout(timer);
|
|
18549
18688
|
}
|
|
18550
18689
|
}
|
|
18551
|
-
|
|
18552
|
-
|
|
18553
|
-
|
|
18554
|
-
|
|
18690
|
+
createPatternPermission(allowedPatterns) {
|
|
18691
|
+
if (allowedPatterns.length === 0) {
|
|
18692
|
+
return "deny";
|
|
18693
|
+
}
|
|
18694
|
+
const permission = Object.fromEntries(allowedPatterns.map((pattern) => [pattern, "allow"]));
|
|
18695
|
+
permission[ANY_PERMISSION_PATTERN] = "deny";
|
|
18696
|
+
return permission;
|
|
18697
|
+
}
|
|
18698
|
+
createPatternRuleset(permission, allowedPatterns) {
|
|
18699
|
+
if (allowedPatterns.length === 0) {
|
|
18700
|
+
return [
|
|
18701
|
+
{
|
|
18702
|
+
permission,
|
|
18703
|
+
pattern: ANY_PERMISSION_PATTERN,
|
|
18704
|
+
action: "deny"
|
|
18705
|
+
}
|
|
18706
|
+
];
|
|
18555
18707
|
}
|
|
18556
|
-
return
|
|
18708
|
+
return [
|
|
18709
|
+
...allowedPatterns.map((pattern) => ({
|
|
18710
|
+
permission,
|
|
18711
|
+
pattern,
|
|
18712
|
+
action: "allow"
|
|
18713
|
+
})),
|
|
18714
|
+
{
|
|
18715
|
+
permission,
|
|
18716
|
+
pattern: ANY_PERMISSION_PATTERN,
|
|
18717
|
+
action: "deny"
|
|
18718
|
+
}
|
|
18719
|
+
];
|
|
18720
|
+
}
|
|
18721
|
+
calculateDelegationPermissions(agentName) {
|
|
18722
|
+
const allowedSubagents = this.getSubagentRules(agentName);
|
|
18723
|
+
const allowedBackgroundSubagents = allowedSubagents.filter((subagent) => this.isBackgroundCapableAgent(subagent));
|
|
18724
|
+
const canManageBackgroundTasks = allowedBackgroundSubagents.length > 0;
|
|
18725
|
+
const permission = {
|
|
18726
|
+
[TASK_PERMISSION]: this.createPatternPermission(allowedSubagents),
|
|
18727
|
+
[BACKGROUND_TASK_PERMISSION]: this.createPatternPermission(allowedBackgroundSubagents),
|
|
18728
|
+
[BACKGROUND_OUTPUT_PERMISSION]: canManageBackgroundTasks ? "allow" : "deny",
|
|
18729
|
+
[BACKGROUND_CANCEL_PERMISSION]: canManageBackgroundTasks ? "allow" : "deny"
|
|
18730
|
+
};
|
|
18731
|
+
return {
|
|
18732
|
+
permission,
|
|
18733
|
+
ruleset: [
|
|
18734
|
+
...this.createPatternRuleset(TASK_PERMISSION, allowedSubagents),
|
|
18735
|
+
...this.createPatternRuleset(BACKGROUND_TASK_PERMISSION, allowedBackgroundSubagents),
|
|
18736
|
+
{
|
|
18737
|
+
permission: BACKGROUND_OUTPUT_PERMISSION,
|
|
18738
|
+
pattern: ANY_PERMISSION_PATTERN,
|
|
18739
|
+
action: canManageBackgroundTasks ? "allow" : "deny"
|
|
18740
|
+
},
|
|
18741
|
+
{
|
|
18742
|
+
permission: BACKGROUND_CANCEL_PERMISSION,
|
|
18743
|
+
pattern: ANY_PERMISSION_PATTERN,
|
|
18744
|
+
action: canManageBackgroundTasks ? "allow" : "deny"
|
|
18745
|
+
}
|
|
18746
|
+
],
|
|
18747
|
+
legacyTools: {
|
|
18748
|
+
[TASK_PERMISSION]: allowedSubagents.length > 0,
|
|
18749
|
+
[BACKGROUND_TASK_PERMISSION]: canManageBackgroundTasks,
|
|
18750
|
+
[BACKGROUND_OUTPUT_PERMISSION]: canManageBackgroundTasks,
|
|
18751
|
+
[BACKGROUND_CANCEL_PERMISSION]: canManageBackgroundTasks
|
|
18752
|
+
}
|
|
18753
|
+
};
|
|
18557
18754
|
}
|
|
18558
18755
|
async startTask(task) {
|
|
18559
18756
|
task.status = "starting";
|
|
@@ -18563,12 +18760,14 @@ class BackgroundTaskManager {
|
|
|
18563
18760
|
return;
|
|
18564
18761
|
}
|
|
18565
18762
|
try {
|
|
18763
|
+
const delegationPermissions = this.calculateDelegationPermissions(task.agent);
|
|
18566
18764
|
const session = await this.client.session.create({
|
|
18567
18765
|
body: {
|
|
18568
18766
|
parentID: task.parentSessionId,
|
|
18569
|
-
title: `Background: ${task.description}
|
|
18767
|
+
title: `Background: ${task.description}`,
|
|
18768
|
+
permission: delegationPermissions.ruleset
|
|
18570
18769
|
},
|
|
18571
|
-
query: { directory: this.
|
|
18770
|
+
query: { directory: this.worktreeDirectory }
|
|
18572
18771
|
});
|
|
18573
18772
|
if (!session.data?.id) {
|
|
18574
18773
|
throw new Error("Failed to create background session");
|
|
@@ -18580,12 +18779,14 @@ class BackgroundTaskManager {
|
|
|
18580
18779
|
if (this.tmuxEnabled) {
|
|
18581
18780
|
await new Promise((r) => setTimeout(r, 500));
|
|
18582
18781
|
}
|
|
18583
|
-
const
|
|
18584
|
-
|
|
18782
|
+
const promptQuery = {
|
|
18783
|
+
directory: this.worktreeDirectory
|
|
18784
|
+
};
|
|
18585
18785
|
const resolvedVariant = resolveAgentVariant(this.config, task.agent);
|
|
18586
18786
|
const basePromptBody = applyAgentVariant(resolvedVariant, {
|
|
18587
18787
|
agent: task.agent,
|
|
18588
|
-
|
|
18788
|
+
permission: delegationPermissions.permission,
|
|
18789
|
+
tools: delegationPermissions.legacyTools,
|
|
18589
18790
|
parts: [{ type: "text", text: task.prompt }]
|
|
18590
18791
|
});
|
|
18591
18792
|
const fallbackEnabled = this.config?.fallback?.enabled ?? true;
|
|
@@ -19145,59 +19346,70 @@ function sanitizeProjectName(value) {
|
|
|
19145
19346
|
const normalized = value.trim().replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
19146
19347
|
return normalized.length > 0 ? normalized : "project";
|
|
19147
19348
|
}
|
|
19148
|
-
async function runGitCommand(
|
|
19149
|
-
|
|
19150
|
-
|
|
19151
|
-
|
|
19152
|
-
|
|
19153
|
-
stderr: "pipe"
|
|
19154
|
-
});
|
|
19349
|
+
async function runGitCommand(shell, worktreeDirectory, args, timeoutMs) {
|
|
19350
|
+
if (!shell) {
|
|
19351
|
+
return null;
|
|
19352
|
+
}
|
|
19353
|
+
const command = shell.nothrow().cwd(worktreeDirectory)`git ${args}`.quiet();
|
|
19155
19354
|
let timer;
|
|
19156
19355
|
try {
|
|
19157
19356
|
const result = await Promise.race([
|
|
19158
|
-
|
|
19159
|
-
const [stdout, exitCode] = await Promise.all([
|
|
19160
|
-
new Response(subprocess.stdout).text(),
|
|
19161
|
-
subprocess.exited
|
|
19162
|
-
]);
|
|
19163
|
-
if (exitCode !== 0) {
|
|
19164
|
-
return null;
|
|
19165
|
-
}
|
|
19166
|
-
const firstLine = stdout.trim().split(/\r?\n/, 1)[0];
|
|
19167
|
-
return firstLine.length > 0 ? firstLine : null;
|
|
19168
|
-
})(),
|
|
19357
|
+
command,
|
|
19169
19358
|
new Promise((resolve) => {
|
|
19170
|
-
timer = setTimeout(() =>
|
|
19171
|
-
subprocess.kill();
|
|
19172
|
-
resolve(null);
|
|
19173
|
-
}, timeoutMs);
|
|
19359
|
+
timer = setTimeout(() => resolve(null), timeoutMs);
|
|
19174
19360
|
})
|
|
19175
19361
|
]);
|
|
19176
|
-
|
|
19362
|
+
if (result === null || result.exitCode !== 0) {
|
|
19363
|
+
return null;
|
|
19364
|
+
}
|
|
19365
|
+
const firstLine = result.text().trim().split(/\r?\n/, 1)[0];
|
|
19366
|
+
return firstLine.length > 0 ? firstLine : null;
|
|
19177
19367
|
} catch {
|
|
19178
19368
|
return null;
|
|
19179
19369
|
} finally {
|
|
19180
|
-
|
|
19370
|
+
if (timer) {
|
|
19371
|
+
clearTimeout(timer);
|
|
19372
|
+
}
|
|
19181
19373
|
}
|
|
19182
19374
|
}
|
|
19183
|
-
function buildProjectId(
|
|
19184
|
-
const
|
|
19185
|
-
return `${
|
|
19375
|
+
function buildProjectId(projectName, hash2) {
|
|
19376
|
+
const normalizedProjectName = sanitizeProjectName(projectName);
|
|
19377
|
+
return `${normalizedProjectName}-${hash2.slice(0, 12)}`;
|
|
19186
19378
|
}
|
|
19187
|
-
|
|
19188
|
-
|
|
19379
|
+
function normalizeProjectIdOptions(input, timeoutMs, projectName, shell) {
|
|
19380
|
+
if (typeof input === "string") {
|
|
19381
|
+
return {
|
|
19382
|
+
worktreeDirectory: input,
|
|
19383
|
+
projectName: projectName || path3.basename(input) || "project",
|
|
19384
|
+
shell,
|
|
19385
|
+
timeoutMs
|
|
19386
|
+
};
|
|
19387
|
+
}
|
|
19388
|
+
return {
|
|
19389
|
+
worktreeDirectory: input.worktreeDirectory,
|
|
19390
|
+
projectName: input.projectName || path3.basename(input.worktreeDirectory),
|
|
19391
|
+
shell: input.shell,
|
|
19392
|
+
timeoutMs: input.timeoutMs ?? timeoutMs
|
|
19393
|
+
};
|
|
19394
|
+
}
|
|
19395
|
+
async function getProjectId(input, timeoutMs = 5000, projectName, shell) {
|
|
19396
|
+
const resolvedProjectName = typeof input === "string" ? projectName : undefined;
|
|
19397
|
+
const resolvedShell = typeof input === "string" ? shell : undefined;
|
|
19398
|
+
const options = normalizeProjectIdOptions(input, timeoutMs, resolvedProjectName, resolvedShell);
|
|
19399
|
+
const normalizedDirectory = options.worktreeDirectory.trim();
|
|
19400
|
+
const normalizedProjectName = sanitizeProjectName(options.projectName);
|
|
19189
19401
|
if (normalizedDirectory.length === 0) {
|
|
19190
19402
|
return null;
|
|
19191
19403
|
}
|
|
19192
|
-
const repoRoot = await runGitCommand(normalizedDirectory, ["rev-parse", "--show-toplevel"], timeoutMs);
|
|
19193
|
-
const rootCommit = await runGitCommand(normalizedDirectory, ["rev-list", "--max-parents=0", "HEAD"], timeoutMs);
|
|
19404
|
+
const repoRoot = await runGitCommand(options.shell, normalizedDirectory, ["rev-parse", "--show-toplevel"], options.timeoutMs);
|
|
19405
|
+
const rootCommit = await runGitCommand(options.shell, normalizedDirectory, ["rev-list", "--max-parents=0", "HEAD"], options.timeoutMs);
|
|
19194
19406
|
if (repoRoot && rootCommit) {
|
|
19195
|
-
return buildProjectId(
|
|
19407
|
+
return buildProjectId(normalizedProjectName, rootCommit);
|
|
19196
19408
|
}
|
|
19197
19409
|
try {
|
|
19198
19410
|
const resolvedDirectory = path3.resolve(normalizedDirectory);
|
|
19199
19411
|
const hash2 = createHash("sha256").update(resolvedDirectory).digest("hex");
|
|
19200
|
-
return buildProjectId(
|
|
19412
|
+
return buildProjectId(normalizedProjectName, hash2);
|
|
19201
19413
|
} catch {
|
|
19202
19414
|
return null;
|
|
19203
19415
|
}
|
|
@@ -19341,16 +19553,20 @@ function sortByCompletionDesc(left, right) {
|
|
|
19341
19553
|
}
|
|
19342
19554
|
|
|
19343
19555
|
class DelegationManager {
|
|
19344
|
-
|
|
19556
|
+
worktreeDirectory;
|
|
19557
|
+
projectName;
|
|
19558
|
+
shell;
|
|
19345
19559
|
config;
|
|
19346
19560
|
getActiveTaskIds;
|
|
19347
19561
|
constructor(options) {
|
|
19348
|
-
this.
|
|
19562
|
+
this.worktreeDirectory = options.worktreeDirectory ?? options.directory;
|
|
19563
|
+
this.projectName = options.projectName || path4.basename(this.worktreeDirectory) || path4.basename(options.directory) || "project";
|
|
19564
|
+
this.shell = options.shell;
|
|
19349
19565
|
this.config = options.config;
|
|
19350
19566
|
this.getActiveTaskIds = options.getActiveTaskIds;
|
|
19351
19567
|
}
|
|
19352
|
-
async resolveProjectId(
|
|
19353
|
-
return getProjectId(
|
|
19568
|
+
async resolveProjectId(worktreeDirectory = this.worktreeDirectory) {
|
|
19569
|
+
return getProjectId(worktreeDirectory, this.config?.timeout, this.projectName, this.shell);
|
|
19354
19570
|
}
|
|
19355
19571
|
async createTaskId(rootSessionId) {
|
|
19356
19572
|
const reservedTaskIds = await this.getReservedTaskIds(rootSessionId);
|
|
@@ -19805,7 +20021,7 @@ async function getLatestVersion(channel = "latest") {
|
|
|
19805
20021
|
}
|
|
19806
20022
|
|
|
19807
20023
|
// src/hooks/auto-update-checker/index.ts
|
|
19808
|
-
function createAutoUpdateCheckerHook(ctx, options = {}) {
|
|
20024
|
+
function createAutoUpdateCheckerHook(ctx, options = {}, shell) {
|
|
19809
20025
|
const { showStartupToast = true, autoUpdate = true } = options;
|
|
19810
20026
|
let hasChecked = false;
|
|
19811
20027
|
return {
|
|
@@ -19832,14 +20048,14 @@ function createAutoUpdateCheckerHook(ctx, options = {}) {
|
|
|
19832
20048
|
if (showStartupToast) {
|
|
19833
20049
|
showToast(ctx, `OMO-Lite ${displayVersion ?? "unknown"}`, "oh-my-opencode-lite is active.", "info");
|
|
19834
20050
|
}
|
|
19835
|
-
runBackgroundUpdateCheck(ctx, autoUpdate).catch((err) => {
|
|
20051
|
+
runBackgroundUpdateCheck(ctx, shell, autoUpdate).catch((err) => {
|
|
19836
20052
|
log("[auto-update-checker] Background update check failed:", err);
|
|
19837
20053
|
});
|
|
19838
20054
|
}, 0);
|
|
19839
20055
|
}
|
|
19840
20056
|
};
|
|
19841
20057
|
}
|
|
19842
|
-
async function runBackgroundUpdateCheck(ctx, autoUpdate) {
|
|
20058
|
+
async function runBackgroundUpdateCheck(ctx, shell, autoUpdate) {
|
|
19843
20059
|
const pluginInfo = findPluginEntry(ctx.directory);
|
|
19844
20060
|
if (!pluginInfo) {
|
|
19845
20061
|
log("[auto-update-checker] Plugin not found in config");
|
|
@@ -19877,7 +20093,7 @@ async function runBackgroundUpdateCheck(ctx, autoUpdate) {
|
|
|
19877
20093
|
log(`[auto-update-checker] Config updated: ${pluginInfo.entry} \u2192 ${PACKAGE_NAME}@${latestVersion}`);
|
|
19878
20094
|
}
|
|
19879
20095
|
invalidatePackage(PACKAGE_NAME);
|
|
19880
|
-
const installSuccess = await runBunInstallSafe(ctx);
|
|
20096
|
+
const installSuccess = await runBunInstallSafe(ctx, shell);
|
|
19881
20097
|
if (installSuccess) {
|
|
19882
20098
|
showToast(ctx, "OMO-Lite Updated!", `v${currentVersion} \u2192 v${latestVersion}
|
|
19883
20099
|
Restart OpenCode to apply.`, "success", 8000);
|
|
@@ -19887,23 +20103,23 @@ Restart OpenCode to apply.`, "success", 8000);
|
|
|
19887
20103
|
log("[auto-update-checker] bun install failed; update not installed");
|
|
19888
20104
|
}
|
|
19889
20105
|
}
|
|
19890
|
-
async function runBunInstallSafe(ctx) {
|
|
20106
|
+
async function runBunInstallSafe(ctx, shell) {
|
|
19891
20107
|
try {
|
|
19892
|
-
const proc = Bun.spawn(["bun", "install"], {
|
|
19893
|
-
cwd: ctx.directory,
|
|
19894
|
-
stdout: "pipe",
|
|
19895
|
-
stderr: "pipe"
|
|
19896
|
-
});
|
|
19897
20108
|
const timeoutPromise = new Promise((resolve) => setTimeout(() => resolve("timeout"), 60000));
|
|
19898
|
-
const
|
|
19899
|
-
const result = await Promise.race([exitPromise, timeoutPromise]);
|
|
19900
|
-
if (result === "timeout") {
|
|
20109
|
+
const installPromise = (async () => {
|
|
19901
20110
|
try {
|
|
19902
|
-
|
|
19903
|
-
|
|
20111
|
+
await shell`cd ${ctx.directory} && bun install`;
|
|
20112
|
+
return "completed";
|
|
20113
|
+
} catch {
|
|
20114
|
+
return "failed";
|
|
20115
|
+
}
|
|
20116
|
+
})();
|
|
20117
|
+
const result = await Promise.race([installPromise, timeoutPromise]);
|
|
20118
|
+
if (result === "timeout") {
|
|
20119
|
+
log("[auto-update-checker] bun install timed out after 60 seconds");
|
|
19904
20120
|
return false;
|
|
19905
20121
|
}
|
|
19906
|
-
return
|
|
20122
|
+
return result === "completed";
|
|
19907
20123
|
} catch (err) {
|
|
19908
20124
|
log("[auto-update-checker] bun install error:", err);
|
|
19909
20125
|
return false;
|
|
@@ -19964,236 +20180,6 @@ function createChatHeadersHook(ctx) {
|
|
|
19964
20180
|
}
|
|
19965
20181
|
};
|
|
19966
20182
|
}
|
|
19967
|
-
// src/hooks/clarification-gate/index.ts
|
|
19968
|
-
var CLARIFICATION_GATE_TAG = "clarification-gate";
|
|
19969
|
-
var DEFAULT_EXPLICIT_KEYWORDS = [
|
|
19970
|
-
"brainstorm",
|
|
19971
|
-
"think through",
|
|
19972
|
-
"scope",
|
|
19973
|
-
"plan this",
|
|
19974
|
-
"design this",
|
|
19975
|
-
"proposal",
|
|
19976
|
-
"approach",
|
|
19977
|
-
"architecture",
|
|
19978
|
-
"requirements",
|
|
19979
|
-
"options",
|
|
19980
|
-
"trade-offs"
|
|
19981
|
-
];
|
|
19982
|
-
var DEFAULT_PLANNING_KEYWORDS = [
|
|
19983
|
-
"feature",
|
|
19984
|
-
"implement",
|
|
19985
|
-
"build",
|
|
19986
|
-
"create",
|
|
19987
|
-
"add",
|
|
19988
|
-
"refactor",
|
|
19989
|
-
"restructure",
|
|
19990
|
-
"migrate",
|
|
19991
|
-
"redesign"
|
|
19992
|
-
];
|
|
19993
|
-
function normalizeText(text) {
|
|
19994
|
-
const collapsed = text.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
19995
|
-
return collapsed.length > 0 ? ` ${collapsed} ` : " ";
|
|
19996
|
-
}
|
|
19997
|
-
function findMatchingTerms(text, terms) {
|
|
19998
|
-
const normalizedText = normalizeText(text);
|
|
19999
|
-
return terms.filter((term) => {
|
|
20000
|
-
const normalizedTerm = normalizeText(term);
|
|
20001
|
-
return normalizedText.includes(normalizedTerm);
|
|
20002
|
-
});
|
|
20003
|
-
}
|
|
20004
|
-
function findScopeSignal(text, label, terms) {
|
|
20005
|
-
const matchedTerms = findMatchingTerms(text, terms);
|
|
20006
|
-
if (matchedTerms.length === 0) {
|
|
20007
|
-
return null;
|
|
20008
|
-
}
|
|
20009
|
-
return {
|
|
20010
|
-
label,
|
|
20011
|
-
matchedTerms
|
|
20012
|
-
};
|
|
20013
|
-
}
|
|
20014
|
-
function findLayerSignal(text) {
|
|
20015
|
-
const leftTerms = ["ui", "frontend", "client"];
|
|
20016
|
-
const rightTerms = ["backend", "server", "database"];
|
|
20017
|
-
const matchedLeft = findMatchingTerms(text, leftTerms);
|
|
20018
|
-
const matchedRight = findMatchingTerms(text, rightTerms);
|
|
20019
|
-
if (matchedLeft.length === 0 || matchedRight.length === 0) {
|
|
20020
|
-
return null;
|
|
20021
|
-
}
|
|
20022
|
-
return {
|
|
20023
|
-
label: "scope signal \u2014 multiple layers",
|
|
20024
|
-
matchedTerms: [`${matchedLeft[0]} + ${matchedRight[0]}`]
|
|
20025
|
-
};
|
|
20026
|
-
}
|
|
20027
|
-
function detectAmbiguity(text, explicitKeywords, planningKeywords) {
|
|
20028
|
-
const explicitMatches = findMatchingTerms(text, explicitKeywords);
|
|
20029
|
-
const planningMatches = findMatchingTerms(text, planningKeywords);
|
|
20030
|
-
const scopeSignals = [
|
|
20031
|
-
findScopeSignal(text, "scope signal \u2014 multiple views/pages", [
|
|
20032
|
-
"page",
|
|
20033
|
-
"screen",
|
|
20034
|
-
"flow",
|
|
20035
|
-
"wizard",
|
|
20036
|
-
"dashboard",
|
|
20037
|
-
"onboarding",
|
|
20038
|
-
"settings",
|
|
20039
|
-
"checkout",
|
|
20040
|
-
"step",
|
|
20041
|
-
"journey"
|
|
20042
|
-
]),
|
|
20043
|
-
findScopeSignal(text, "scope signal \u2014 api/data", [
|
|
20044
|
-
"api",
|
|
20045
|
-
"endpoint",
|
|
20046
|
-
"route",
|
|
20047
|
-
"mutation",
|
|
20048
|
-
"query",
|
|
20049
|
-
"schema",
|
|
20050
|
-
"table",
|
|
20051
|
-
"model",
|
|
20052
|
-
"migration",
|
|
20053
|
-
"database"
|
|
20054
|
-
]),
|
|
20055
|
-
findScopeSignal(text, "scope signal \u2014 restructuring", [
|
|
20056
|
-
"refactor",
|
|
20057
|
-
"restructure",
|
|
20058
|
-
"replace",
|
|
20059
|
-
"redesign",
|
|
20060
|
-
"rework",
|
|
20061
|
-
"consolidate",
|
|
20062
|
-
"split",
|
|
20063
|
-
"migrate"
|
|
20064
|
-
]),
|
|
20065
|
-
findLayerSignal(text),
|
|
20066
|
-
findScopeSignal(text, "scope signal \u2014 business/ux phrasing", [
|
|
20067
|
-
"users should",
|
|
20068
|
-
"support",
|
|
20069
|
-
"allow",
|
|
20070
|
-
"improve experience",
|
|
20071
|
-
"pricing",
|
|
20072
|
-
"onboarding",
|
|
20073
|
-
"billing",
|
|
20074
|
-
"permissions"
|
|
20075
|
-
]),
|
|
20076
|
-
findScopeSignal(text, "scope signal \u2014 ambiguous/open-ended", [
|
|
20077
|
-
"help me think",
|
|
20078
|
-
"best way",
|
|
20079
|
-
"how should",
|
|
20080
|
-
"what's the right approach",
|
|
20081
|
-
"explore",
|
|
20082
|
-
"maybe",
|
|
20083
|
-
"not sure"
|
|
20084
|
-
]),
|
|
20085
|
-
findScopeSignal(text, "scope signal \u2014 cross-directory", [
|
|
20086
|
-
"across the app",
|
|
20087
|
-
"across the system",
|
|
20088
|
-
"multiple files",
|
|
20089
|
-
"several components"
|
|
20090
|
-
])
|
|
20091
|
-
].filter((signal) => signal !== null);
|
|
20092
|
-
return {
|
|
20093
|
-
explicitMatches,
|
|
20094
|
-
planningMatches,
|
|
20095
|
-
scopeSignals
|
|
20096
|
-
};
|
|
20097
|
-
}
|
|
20098
|
-
function shouldInjectClarificationGate(config2, detection) {
|
|
20099
|
-
const explicitMatched = detection.explicitMatches.length > 0;
|
|
20100
|
-
const planningMatched = detection.planningMatches.length > 0;
|
|
20101
|
-
const scopeSignalCount = detection.scopeSignals.length;
|
|
20102
|
-
switch (config2.mode) {
|
|
20103
|
-
case "off":
|
|
20104
|
-
return false;
|
|
20105
|
-
case "explicit-only":
|
|
20106
|
-
return explicitMatched;
|
|
20107
|
-
case "auto":
|
|
20108
|
-
return explicitMatched || scopeSignalCount >= config2.min_scope_signals;
|
|
20109
|
-
case "auto-for-planning":
|
|
20110
|
-
return explicitMatched || planningMatched && scopeSignalCount >= 1 || scopeSignalCount >= config2.hard_complex_signal_threshold;
|
|
20111
|
-
}
|
|
20112
|
-
}
|
|
20113
|
-
function buildMatchedSignals(detection, config2) {
|
|
20114
|
-
const matchedSignals = [];
|
|
20115
|
-
if (detection.explicitMatches.length > 0) {
|
|
20116
|
-
matchedSignals.push(`explicit keywords: ${detection.explicitMatches.join(", ")}`);
|
|
20117
|
-
}
|
|
20118
|
-
if (detection.planningMatches.length > 0) {
|
|
20119
|
-
matchedSignals.push(`planning keywords: ${detection.planningMatches.join(", ")}`);
|
|
20120
|
-
}
|
|
20121
|
-
for (const signal of detection.scopeSignals) {
|
|
20122
|
-
matchedSignals.push(`${signal.label}: ${signal.matchedTerms.join(", ")}`);
|
|
20123
|
-
}
|
|
20124
|
-
if (matchedSignals.length === 0) {
|
|
20125
|
-
matchedSignals.push(`configured threshold allows injection (mode=${config2.mode})`);
|
|
20126
|
-
}
|
|
20127
|
-
return matchedSignals;
|
|
20128
|
-
}
|
|
20129
|
-
function buildClarificationGateBlock(detection, config2) {
|
|
20130
|
-
const matchedSignals = buildMatchedSignals(detection, config2).map((signal) => `- ${signal}`).join(`
|
|
20131
|
-
`);
|
|
20132
|
-
return `<${CLARIFICATION_GATE_TAG}>
|
|
20133
|
-
Potentially ambiguous or substantial request detected.
|
|
20134
|
-
|
|
20135
|
-
Matched signals:
|
|
20136
|
-
${matchedSignals}
|
|
20137
|
-
|
|
20138
|
-
Load the brainstorming skill and follow its workflow before implementing.
|
|
20139
|
-
Ask one clarifying question at a time, max 5 total, prefer multiple choice.
|
|
20140
|
-
Assess scope using the seven scope signals.
|
|
20141
|
-
Propose 2-3 approaches with trade-offs and a recommendation.
|
|
20142
|
-
Wait for explicit user approval before implementation or SDD handoff.
|
|
20143
|
-
|
|
20144
|
-
Do not implement during brainstorming.
|
|
20145
|
-
</${CLARIFICATION_GATE_TAG}>`;
|
|
20146
|
-
}
|
|
20147
|
-
function createClarificationGateHook(options = {}) {
|
|
20148
|
-
const config2 = ClarificationGateConfigSchema.parse(options.clarificationGate ?? {});
|
|
20149
|
-
const explicitKeywords = config2.explicit_keywords ?? [
|
|
20150
|
-
...DEFAULT_EXPLICIT_KEYWORDS
|
|
20151
|
-
];
|
|
20152
|
-
const planningKeywords = config2.planning_keywords ?? [
|
|
20153
|
-
...DEFAULT_PLANNING_KEYWORDS
|
|
20154
|
-
];
|
|
20155
|
-
return {
|
|
20156
|
-
"experimental.chat.messages.transform": async (_input, output) => {
|
|
20157
|
-
const { messages } = output;
|
|
20158
|
-
if (messages.length === 0) {
|
|
20159
|
-
return;
|
|
20160
|
-
}
|
|
20161
|
-
let lastUserMessageIndex = -1;
|
|
20162
|
-
for (let i2 = messages.length - 1;i2 >= 0; i2--) {
|
|
20163
|
-
if (messages[i2].info.role === "user") {
|
|
20164
|
-
lastUserMessageIndex = i2;
|
|
20165
|
-
break;
|
|
20166
|
-
}
|
|
20167
|
-
}
|
|
20168
|
-
if (lastUserMessageIndex === -1) {
|
|
20169
|
-
return;
|
|
20170
|
-
}
|
|
20171
|
-
const lastUserMessage = messages[lastUserMessageIndex];
|
|
20172
|
-
const agent = lastUserMessage.info.agent;
|
|
20173
|
-
if (agent && agent !== "orchestrator") {
|
|
20174
|
-
return;
|
|
20175
|
-
}
|
|
20176
|
-
const textPartIndex = lastUserMessage.parts.findIndex((p) => p.type === "text" && p.text !== undefined);
|
|
20177
|
-
if (textPartIndex === -1) {
|
|
20178
|
-
return;
|
|
20179
|
-
}
|
|
20180
|
-
const originalText = lastUserMessage.parts[textPartIndex].text ?? "";
|
|
20181
|
-
if (originalText.includes(LITE_INTERNAL_INITIATOR_MARKER)) {
|
|
20182
|
-
return;
|
|
20183
|
-
}
|
|
20184
|
-
const detection = detectAmbiguity(originalText, explicitKeywords, planningKeywords);
|
|
20185
|
-
if (!shouldInjectClarificationGate(config2, detection)) {
|
|
20186
|
-
return;
|
|
20187
|
-
}
|
|
20188
|
-
const clarificationGateBlock = buildClarificationGateBlock(detection, config2);
|
|
20189
|
-
lastUserMessage.parts[textPartIndex].text = `${clarificationGateBlock}
|
|
20190
|
-
|
|
20191
|
-
---
|
|
20192
|
-
|
|
20193
|
-
${originalText}`;
|
|
20194
|
-
}
|
|
20195
|
-
};
|
|
20196
|
-
}
|
|
20197
20183
|
// src/hooks/delegate-task-retry/patterns.ts
|
|
20198
20184
|
var DELEGATE_TASK_ERROR_PATTERNS = [
|
|
20199
20185
|
{
|
|
@@ -20471,10 +20457,12 @@ class ForegroundFallbackManager {
|
|
|
20471
20457
|
await this.client.session.abort({ path: { id: sessionID } });
|
|
20472
20458
|
} catch {}
|
|
20473
20459
|
await new Promise((r2) => setTimeout(r2, 500));
|
|
20474
|
-
|
|
20475
|
-
await sessionClient.promptAsync({
|
|
20460
|
+
await this.client.session.promptAsync({
|
|
20476
20461
|
path: { id: sessionID },
|
|
20477
|
-
body: {
|
|
20462
|
+
body: {
|
|
20463
|
+
parts: lastUser.parts,
|
|
20464
|
+
model: ref
|
|
20465
|
+
}
|
|
20478
20466
|
});
|
|
20479
20467
|
this.sessionModel.set(sessionID, nextModel);
|
|
20480
20468
|
log("[foreground-fallback] switched to fallback model", {
|
|
@@ -20569,7 +20557,7 @@ ${JSON_ERROR_REMINDER}`;
|
|
|
20569
20557
|
// src/hooks/phase-reminder/index.ts
|
|
20570
20558
|
var PHASE_REMINDER = `<reminder>Recall Workflow Rules:
|
|
20571
20559
|
Understand \u2192 find the best path (delegate based on rules and parallelize independent work) \u2192 execute \u2192 verify.
|
|
20572
|
-
If delegating, launch the specialist in the same turn you mention it.</reminder>`;
|
|
20560
|
+
If delegating, launch the specialist in the same turn you mention it. If multiple delegations are independent, emit all tool calls in a single response \u2014 never serialize independent work across turns.</reminder>`;
|
|
20573
20561
|
function createPhaseReminderHook() {
|
|
20574
20562
|
return {
|
|
20575
20563
|
"experimental.chat.messages.transform": async (_input, output) => {
|
|
@@ -20612,7 +20600,7 @@ ${originalText}`;
|
|
|
20612
20600
|
var NUDGE = `
|
|
20613
20601
|
|
|
20614
20602
|
---
|
|
20615
|
-
Workflow Reminder: delegate based on rules; If
|
|
20603
|
+
Workflow Reminder: delegate based on rules; launch specialists in this same turn. If multiple delegations are independent, emit all tool calls in one response.`;
|
|
20616
20604
|
function createPostReadNudgeHook() {
|
|
20617
20605
|
return {
|
|
20618
20606
|
"tool.execute.after": async (input, output) => {
|
|
@@ -20769,10 +20757,10 @@ var SHARED_SKILL_DIRECTORY2 = "_shared";
|
|
|
20769
20757
|
var SHARED_SKILL_SOURCE_PATH = `src/skills/${SHARED_SKILL_DIRECTORY2}`;
|
|
20770
20758
|
var CUSTOM_SKILLS = [
|
|
20771
20759
|
{
|
|
20772
|
-
name: "
|
|
20773
|
-
description: "
|
|
20760
|
+
name: "requirements-interview",
|
|
20761
|
+
description: "Mandatory step-0 discovery interview to understand user intent, clarify scope, and choose the right path before implementation",
|
|
20774
20762
|
allowedAgents: ["orchestrator"],
|
|
20775
|
-
sourcePath: "src/skills/
|
|
20763
|
+
sourcePath: "src/skills/requirements-interview"
|
|
20776
20764
|
},
|
|
20777
20765
|
{
|
|
20778
20766
|
name: "cartography",
|
|
@@ -20786,6 +20774,12 @@ var CUSTOM_SKILLS = [
|
|
|
20786
20774
|
allowedAgents: ["orchestrator", "oracle"],
|
|
20787
20775
|
sourcePath: "src/skills/plan-reviewer"
|
|
20788
20776
|
},
|
|
20777
|
+
{
|
|
20778
|
+
name: "sdd-init",
|
|
20779
|
+
description: "Initialize OpenSpec structure and SDD project context",
|
|
20780
|
+
allowedAgents: ["orchestrator"],
|
|
20781
|
+
sourcePath: "src/skills/sdd-init"
|
|
20782
|
+
},
|
|
20789
20783
|
{
|
|
20790
20784
|
name: "sdd-propose",
|
|
20791
20785
|
description: "Create change proposals for OpenSpec workflows",
|
|
@@ -21069,11 +21063,11 @@ var DEFAULT_THOTH_TIMEOUT_MS = 15000;
|
|
|
21069
21063
|
function isRecord2(value) {
|
|
21070
21064
|
return typeof value === "object" && value !== null;
|
|
21071
21065
|
}
|
|
21072
|
-
function
|
|
21066
|
+
function normalizeText(value) {
|
|
21073
21067
|
return typeof value === "string" ? value.trim() || null : null;
|
|
21074
21068
|
}
|
|
21075
21069
|
function previewText(value, max) {
|
|
21076
|
-
const text =
|
|
21070
|
+
const text = normalizeText(value);
|
|
21077
21071
|
if (!text) {
|
|
21078
21072
|
return null;
|
|
21079
21073
|
}
|
|
@@ -21093,9 +21087,9 @@ function formatMemoryContext(response) {
|
|
|
21093
21087
|
if (sessions.length > 0) {
|
|
21094
21088
|
lines.push("### Recent Sessions");
|
|
21095
21089
|
for (const rawSession of sessions) {
|
|
21096
|
-
const sessionId =
|
|
21097
|
-
const project =
|
|
21098
|
-
const startedAt =
|
|
21090
|
+
const sessionId = normalizeText(rawSession.id) ?? "unknown-session";
|
|
21091
|
+
const project = normalizeText(rawSession.project) ?? "unknown-project";
|
|
21092
|
+
const startedAt = normalizeText(rawSession.started_at) ?? "unknown";
|
|
21099
21093
|
lines.push(`- [${sessionId}] (${project}) \u2014 started ${startedAt}`);
|
|
21100
21094
|
const summary = previewText(rawSession.summary, 300);
|
|
21101
21095
|
if (summary) {
|
|
@@ -21107,9 +21101,9 @@ function formatMemoryContext(response) {
|
|
|
21107
21101
|
if (observations.length > 0) {
|
|
21108
21102
|
lines.push("### Recent Observations");
|
|
21109
21103
|
for (const rawObservation of observations) {
|
|
21110
|
-
const type =
|
|
21111
|
-
const title =
|
|
21112
|
-
const createdAt =
|
|
21104
|
+
const type = normalizeText(rawObservation.type) ?? "manual";
|
|
21105
|
+
const title = normalizeText(rawObservation.title) ?? "Untitled";
|
|
21106
|
+
const createdAt = normalizeText(rawObservation.created_at) ?? "unknown";
|
|
21113
21107
|
lines.push(`- [${type}] ${title} (${createdAt})`);
|
|
21114
21108
|
const content = previewText(rawObservation.content, 300);
|
|
21115
21109
|
if (content) {
|
|
@@ -21125,7 +21119,7 @@ function formatMemoryContext(response) {
|
|
|
21125
21119
|
if (!content) {
|
|
21126
21120
|
continue;
|
|
21127
21121
|
}
|
|
21128
|
-
const createdAt =
|
|
21122
|
+
const createdAt = normalizeText(rawPrompt.created_at) ?? "unknown";
|
|
21129
21123
|
lines.push(`- ${content} (${createdAt})`);
|
|
21130
21124
|
}
|
|
21131
21125
|
lines.push("");
|
|
@@ -21229,7 +21223,7 @@ function createThothClient(options) {
|
|
|
21229
21223
|
}
|
|
21230
21224
|
// src/hooks/thoth-mem/protocol.ts
|
|
21231
21225
|
var SDD_TOPIC_KEY_FORMAT = "sdd/{change}/{artifact}";
|
|
21232
|
-
var FIRST_ACTION_INSTRUCTION =
|
|
21226
|
+
var FIRST_ACTION_INSTRUCTION = 'FIRST ACTION REQUIRED: Call mem_session_summary with the content of the compacted summary. This preserves what was accomplished before compaction. Do this BEFORE any other work. Then call mem_context for a recent-session overview, and use the 3-layer recall protocol (mem_search with mode "compact" -> mem_timeline -> mem_get_observation) for precise retrieval.';
|
|
21233
21227
|
var SESSION_SUMMARY_TEMPLATE = `Use this exact structure for \`mem_session_summary\` content:
|
|
21234
21228
|
|
|
21235
21229
|
## Goal
|
|
@@ -21250,7 +21244,7 @@ var SESSION_SUMMARY_TEMPLATE = `Use this exact structure for \`mem_session_summa
|
|
|
21250
21244
|
## Relevant Files
|
|
21251
21245
|
- path/to/file.ts - [what it does or what changed]`;
|
|
21252
21246
|
function buildCompactionReminder(sessionID) {
|
|
21253
|
-
return `FIRST ACTION REQUIRED: this session was compacted. Call \`mem_session_summary\` with the content of the compacted summary and \`session_id\` \`${sessionID}\`. This preserves what was accomplished before compaction. Do this BEFORE any other work. After that, call \`mem_capture_passive\` if the summary includes \`## Key Learnings:\`,
|
|
21247
|
+
return `FIRST ACTION REQUIRED: this session was compacted. Call \`mem_session_summary\` with the content of the compacted summary and \`session_id\` \`${sessionID}\`. This preserves what was accomplished before compaction. Do this BEFORE any other work. After that, call \`mem_capture_passive\` if the summary includes \`## Key Learnings:\`, call \`mem_context\` for a recent-session overview, and use the 3-layer recall protocol (\`mem_search\` with \`mode: "compact"\` -> \`mem_timeline\` -> \`mem_get_observation\`) for precise retrieval.`;
|
|
21254
21248
|
}
|
|
21255
21249
|
function buildCompactorInstruction(project) {
|
|
21256
21250
|
return `CRITICAL INSTRUCTION: place this at the TOP of the compacted summary exactly as an action item for the resumed agent: "FIRST ACTION REQUIRED: Call mem_session_summary with the content of this compacted summary. Use project: '${project}'. This preserves what was accomplished before compaction. Do this BEFORE any other work."`;
|
|
@@ -21261,29 +21255,52 @@ function buildMemoryInstructions(sessionID, project) {
|
|
|
21261
21255
|
Persistent memory is available through thoth-mem. Follow this protocol.
|
|
21262
21256
|
|
|
21263
21257
|
IMPORTANT: Your current session_id is \`${sessionID}\` and project is \`${project}\`.
|
|
21264
|
-
Always pass these values when calling memory tools that accept them (mem_session_summary, mem_save,
|
|
21265
|
-
|
|
21266
|
-
|
|
21267
|
-
|
|
21268
|
-
|
|
21269
|
-
|
|
21270
|
-
|
|
21271
|
-
-
|
|
21272
|
-
-
|
|
21273
|
-
-
|
|
21274
|
-
-
|
|
21258
|
+
Always pass these values when calling memory tools that accept them (mem_session_summary, mem_save, mem_capture_passive, etc.).
|
|
21259
|
+
|
|
21260
|
+
### CORE TOOLS
|
|
21261
|
+
mem_save, mem_search, mem_context, mem_session_summary, mem_get_observation, mem_save_prompt, mem_update, mem_suggest_topic_key, mem_timeline, mem_capture_passive
|
|
21262
|
+
|
|
21263
|
+
### WHEN TO SAVE
|
|
21264
|
+
Call \`mem_save\` IMMEDIATELY after ANY of these:
|
|
21265
|
+
- Architecture, design, or workflow decision made
|
|
21266
|
+
- Bug fixed (include root cause)
|
|
21267
|
+
- Non-obvious discovery, gotcha, or edge case found
|
|
21268
|
+
- Configuration change or environment setup
|
|
21269
|
+
- Pattern or convention established (naming, structure, approach)
|
|
21270
|
+
- User preference or constraint learned
|
|
21271
|
+
- Feature implemented with non-obvious approach
|
|
21272
|
+
- User confirms a recommendation ("dale", "go with that", "sounds good", "s\xED, esa")
|
|
21273
|
+
- User rejects an approach or expresses a preference ("no, better X", "I prefer X")
|
|
21274
|
+
- Discussion concludes with a clear direction chosen
|
|
21275
|
+
|
|
21276
|
+
Use \`title\` as Verb + what changed or was learned.
|
|
21277
|
+
Use \`type\` from: bugfix | decision | architecture | discovery | pattern | config | learning | manual.
|
|
21278
|
+
Set \`scope\` intentionally.
|
|
21279
|
+
Reuse \`topic_key\` for the same evolving topic. Do not overwrite unrelated topics.
|
|
21280
|
+
If unsure about a stable \`topic_key\`, call \`mem_suggest_topic_key\` first.
|
|
21281
|
+
If you need to modify a known observation by exact ID, call \`mem_update\` instead of creating a new record.
|
|
21282
|
+
Put the durable details in \`content\` with this structure:
|
|
21275
21283
|
- What: concise description of what changed or was learned
|
|
21276
21284
|
- Why: why it mattered or what problem it solved
|
|
21277
21285
|
- Where: files, paths, or systems involved
|
|
21278
21286
|
- Learned: edge cases, caveats, or follow-up notes
|
|
21279
21287
|
|
|
21280
|
-
|
|
21281
|
-
|
|
21288
|
+
**Self-check after EVERY task**: "Did I or the user just make a decision, confirm a recommendation, express a preference, fix a bug, learn something, or establish a convention? If yes \u2192 mem_save NOW."
|
|
21289
|
+
|
|
21290
|
+
You can also call \`mem_save_prompt\` to manually save a user prompt that you consider particularly important for future context.
|
|
21291
|
+
|
|
21292
|
+
### WHEN TO SEARCH MEMORY
|
|
21293
|
+
- Broad recovery (session start, after compaction): call \`mem_context\` for a recent-session overview.
|
|
21294
|
+
- Targeted 3-layer recall (specific memory retrieval):
|
|
21295
|
+
1. Call \`mem_search\` with \`mode: "compact"\` (default) to scan the compact index of IDs + titles.
|
|
21296
|
+
2. Call \`mem_timeline\` around promising observation IDs for chronological context within the same session.
|
|
21297
|
+
3. Call \`mem_get_observation\` only for observations you need in full.
|
|
21298
|
+
- Use \`mode: "preview"\` with \`mem_search\` only when compact results are insufficient to disambiguate.
|
|
21282
21299
|
- Search proactively on the first message about a project, feature, or problem when prior context may matter.
|
|
21283
21300
|
- Search before starting work that may have been done before.
|
|
21284
21301
|
- Search when the user mentions a topic that lacks enough local context.
|
|
21285
21302
|
|
|
21286
|
-
SESSION CLOSE PROTOCOL
|
|
21303
|
+
### SESSION CLOSE PROTOCOL
|
|
21287
21304
|
- Before ending the session, call \`mem_session_summary\` with this exact template.
|
|
21288
21305
|
- This is NOT optional. If you skip this, the next session starts blind.
|
|
21289
21306
|
- Do not claim memory was saved unless the tool call succeeded.
|
|
@@ -21291,17 +21308,21 @@ SESSION CLOSE PROTOCOL
|
|
|
21291
21308
|
|
|
21292
21309
|
${SESSION_SUMMARY_TEMPLATE}
|
|
21293
21310
|
|
|
21294
|
-
AFTER COMPACTION
|
|
21311
|
+
### AFTER COMPACTION
|
|
21295
21312
|
- IMMEDIATELY call \`mem_session_summary\` with the compacted summary content.
|
|
21296
|
-
- Then call \`mem_context
|
|
21313
|
+
- Then call \`mem_context\` for a recent-session overview.
|
|
21314
|
+
- Use the 3-layer recall protocol (\`mem_search\` with \`mode: "compact"\` -> \`mem_timeline\` -> \`mem_get_observation\`) for precise artifact/prior-observation retrieval.
|
|
21297
21315
|
- Only then continue working.
|
|
21298
21316
|
|
|
21299
|
-
SDD
|
|
21317
|
+
### SDD TOPIC KEY CONVENTION
|
|
21300
21318
|
- use ${SDD_TOPIC_KEY_FORMAT}
|
|
21301
21319
|
- examples: sdd/add-user-auth/spec, sdd/add-user-auth/design, sdd/add-user-auth/tasks
|
|
21302
21320
|
</memory_protocol>
|
|
21303
21321
|
`.trim();
|
|
21304
21322
|
}
|
|
21323
|
+
function buildSaveNudge() {
|
|
21324
|
+
return "MEMORY REMINDER: It has been a while since your last save. If you have made decisions, discoveries, or completed significant work, call mem_save now.";
|
|
21325
|
+
}
|
|
21305
21326
|
|
|
21306
21327
|
// src/hooks/thoth-mem/index.ts
|
|
21307
21328
|
function isRootSession(session) {
|
|
@@ -21347,6 +21368,10 @@ function isSessionSummaryTool(toolName) {
|
|
|
21347
21368
|
const normalized = toolName.toLowerCase();
|
|
21348
21369
|
return normalized === "mem_session_summary" || normalized.endsWith(".mem_session_summary") || normalized.endsWith("_mem_session_summary");
|
|
21349
21370
|
}
|
|
21371
|
+
function isMemSaveTool(toolName) {
|
|
21372
|
+
const normalized = toolName.toLowerCase();
|
|
21373
|
+
return normalized === "mem_save" || normalized.endsWith(".mem_save") || normalized.endsWith("_mem_save");
|
|
21374
|
+
}
|
|
21350
21375
|
function createThothMemHook(options) {
|
|
21351
21376
|
const enabled = options.enabled !== false;
|
|
21352
21377
|
const thoth = createThothClient({
|
|
@@ -21359,6 +21384,9 @@ function createThothMemHook(options) {
|
|
|
21359
21384
|
const trackedRootSessions = new Set;
|
|
21360
21385
|
const needsCompactionFollowUp = new Set;
|
|
21361
21386
|
const ensuredRootSessions = new Map;
|
|
21387
|
+
const sessionCreatedAt = new Map;
|
|
21388
|
+
const lastMemSaveAt = new Map;
|
|
21389
|
+
const nudgePending = new Set;
|
|
21362
21390
|
async function ensureRootSession(sessionId) {
|
|
21363
21391
|
if (!enabled || trackedRootSessions.has(sessionId)) {
|
|
21364
21392
|
return;
|
|
@@ -21369,20 +21397,47 @@ function createThothMemHook(options) {
|
|
|
21369
21397
|
return;
|
|
21370
21398
|
}
|
|
21371
21399
|
const pending = (async () => {
|
|
21372
|
-
await thoth.memSessionStart(sessionId);
|
|
21373
|
-
|
|
21400
|
+
const started = await thoth.memSessionStart(sessionId);
|
|
21401
|
+
if (started) {
|
|
21402
|
+
trackedRootSessions.add(sessionId);
|
|
21403
|
+
} else {
|
|
21404
|
+
log("[thoth] session start unavailable, skipping tracking:", sessionId);
|
|
21405
|
+
}
|
|
21374
21406
|
})().finally(() => {
|
|
21375
21407
|
ensuredRootSessions.delete(sessionId);
|
|
21376
21408
|
});
|
|
21377
21409
|
ensuredRootSessions.set(sessionId, pending);
|
|
21378
21410
|
await pending;
|
|
21379
21411
|
}
|
|
21412
|
+
function shouldInjectSaveNudge(sessionId) {
|
|
21413
|
+
if (nudgePending.has(sessionId)) {
|
|
21414
|
+
return false;
|
|
21415
|
+
}
|
|
21416
|
+
if (needsCompactionFollowUp.has(sessionId)) {
|
|
21417
|
+
return false;
|
|
21418
|
+
}
|
|
21419
|
+
const createdAt = sessionCreatedAt.get(sessionId);
|
|
21420
|
+
if (!createdAt || Date.now() - createdAt < 5 * 60 * 1000) {
|
|
21421
|
+
return false;
|
|
21422
|
+
}
|
|
21423
|
+
const lastSave = lastMemSaveAt.get(sessionId);
|
|
21424
|
+
if (lastSave && Date.now() - lastSave < 15 * 60 * 1000) {
|
|
21425
|
+
return false;
|
|
21426
|
+
}
|
|
21427
|
+
nudgePending.add(sessionId);
|
|
21428
|
+
return true;
|
|
21429
|
+
}
|
|
21380
21430
|
async function handleSessionCreated(session) {
|
|
21381
21431
|
if (!enabled || !isRootSession(session)) {
|
|
21382
21432
|
return;
|
|
21383
21433
|
}
|
|
21384
|
-
|
|
21385
|
-
|
|
21434
|
+
const started = await thoth.memSessionStart(session.id);
|
|
21435
|
+
if (started) {
|
|
21436
|
+
trackedRootSessions.add(session.id);
|
|
21437
|
+
sessionCreatedAt.set(session.id, Date.now());
|
|
21438
|
+
} else {
|
|
21439
|
+
log("[thoth] session start unavailable, skipping tracking:", session.id);
|
|
21440
|
+
}
|
|
21386
21441
|
}
|
|
21387
21442
|
function handleSessionCompacted(session) {
|
|
21388
21443
|
if (!enabled || !isRootSession(session) || !session.id) {
|
|
@@ -21394,6 +21449,9 @@ function createThothMemHook(options) {
|
|
|
21394
21449
|
trackedRootSessions.delete(session.id);
|
|
21395
21450
|
needsCompactionFollowUp.delete(session.id);
|
|
21396
21451
|
ensuredRootSessions.delete(session.id);
|
|
21452
|
+
sessionCreatedAt.delete(session.id);
|
|
21453
|
+
lastMemSaveAt.delete(session.id);
|
|
21454
|
+
nudgePending.delete(session.id);
|
|
21397
21455
|
}
|
|
21398
21456
|
return {
|
|
21399
21457
|
event: async ({ event }) => {
|
|
@@ -21435,7 +21493,7 @@ function createThothMemHook(options) {
|
|
|
21435
21493
|
return;
|
|
21436
21494
|
}
|
|
21437
21495
|
const sanitizedPromptText = sanitizePromptText(promptText);
|
|
21438
|
-
if (!sanitizedPromptText) {
|
|
21496
|
+
if (!sanitizedPromptText || sanitizedPromptText.length <= 10) {
|
|
21439
21497
|
return;
|
|
21440
21498
|
}
|
|
21441
21499
|
await thoth.memSavePrompt(input.sessionID, sanitizedPromptText);
|
|
@@ -21450,8 +21508,15 @@ function createThothMemHook(options) {
|
|
|
21450
21508
|
}
|
|
21451
21509
|
const memoryInstructions = buildMemoryInstructions(input.sessionID, options.project);
|
|
21452
21510
|
const compactionReminder = needsCompactionFollowUp.has(input.sessionID) ? buildCompactionReminder(input.sessionID) : null;
|
|
21511
|
+
const saveNudge = shouldInjectSaveNudge(input.sessionID) ? buildSaveNudge() : null;
|
|
21453
21512
|
if (output.system.length === 0) {
|
|
21454
|
-
|
|
21513
|
+
let systemPrompt = memoryInstructions;
|
|
21514
|
+
if (compactionReminder) {
|
|
21515
|
+
systemPrompt = appendInstruction(systemPrompt, compactionReminder);
|
|
21516
|
+
}
|
|
21517
|
+
if (saveNudge) {
|
|
21518
|
+
systemPrompt = appendInstruction(systemPrompt, saveNudge);
|
|
21519
|
+
}
|
|
21455
21520
|
output.system.push(systemPrompt);
|
|
21456
21521
|
return;
|
|
21457
21522
|
}
|
|
@@ -21460,6 +21525,9 @@ function createThothMemHook(options) {
|
|
|
21460
21525
|
if (compactionReminder) {
|
|
21461
21526
|
updatedSystemPrompt = appendInstruction(updatedSystemPrompt, compactionReminder);
|
|
21462
21527
|
}
|
|
21528
|
+
if (saveNudge) {
|
|
21529
|
+
updatedSystemPrompt = appendInstruction(updatedSystemPrompt, saveNudge);
|
|
21530
|
+
}
|
|
21463
21531
|
output.system[lastIndex] = updatedSystemPrompt;
|
|
21464
21532
|
},
|
|
21465
21533
|
"experimental.session.compacting": async (input, output) => {
|
|
@@ -21485,6 +21553,10 @@ function createThothMemHook(options) {
|
|
|
21485
21553
|
if (!enabled) {
|
|
21486
21554
|
return;
|
|
21487
21555
|
}
|
|
21556
|
+
if (isMemSaveTool(input.tool)) {
|
|
21557
|
+
lastMemSaveAt.set(input.sessionID, Date.now());
|
|
21558
|
+
nudgePending.delete(input.sessionID);
|
|
21559
|
+
}
|
|
21488
21560
|
if (isSessionSummaryTool(input.tool)) {
|
|
21489
21561
|
needsCompactionFollowUp.delete(input.sessionID);
|
|
21490
21562
|
}
|
|
@@ -21523,10 +21595,11 @@ function createThothMcp(config2) {
|
|
|
21523
21595
|
}
|
|
21524
21596
|
|
|
21525
21597
|
// src/mcp/websearch.ts
|
|
21598
|
+
var baseUrl = "https://mcp.exa.ai/mcp?tools=web_search_exa";
|
|
21599
|
+
var url2 = process.env.EXA_API_KEY ? `${baseUrl}&exaApiKey=${process.env.EXA_API_KEY}` : baseUrl;
|
|
21526
21600
|
var websearch = {
|
|
21527
21601
|
type: "remote",
|
|
21528
|
-
url:
|
|
21529
|
-
headers: process.env.EXA_API_KEY ? { "x-api-key": process.env.EXA_API_KEY } : undefined,
|
|
21602
|
+
url: url2,
|
|
21530
21603
|
oauth: false
|
|
21531
21604
|
};
|
|
21532
21605
|
|
|
@@ -21554,7 +21627,7 @@ __export(exports_external2, {
|
|
|
21554
21627
|
uuidv4: () => uuidv42,
|
|
21555
21628
|
uuid: () => uuid5,
|
|
21556
21629
|
util: () => exports_util2,
|
|
21557
|
-
url: () =>
|
|
21630
|
+
url: () => url3,
|
|
21558
21631
|
uppercase: () => _uppercase2,
|
|
21559
21632
|
unknown: () => unknown2,
|
|
21560
21633
|
union: () => union2,
|
|
@@ -23868,10 +23941,10 @@ var $ZodURL2 = /* @__PURE__ */ $constructor2("$ZodURL", (inst, def) => {
|
|
|
23868
23941
|
inst._zod.check = (payload) => {
|
|
23869
23942
|
try {
|
|
23870
23943
|
const trimmed = payload.value.trim();
|
|
23871
|
-
const
|
|
23944
|
+
const url3 = new URL(trimmed);
|
|
23872
23945
|
if (def.hostname) {
|
|
23873
23946
|
def.hostname.lastIndex = 0;
|
|
23874
|
-
if (!def.hostname.test(
|
|
23947
|
+
if (!def.hostname.test(url3.hostname)) {
|
|
23875
23948
|
payload.issues.push({
|
|
23876
23949
|
code: "invalid_format",
|
|
23877
23950
|
format: "url",
|
|
@@ -23885,7 +23958,7 @@ var $ZodURL2 = /* @__PURE__ */ $constructor2("$ZodURL", (inst, def) => {
|
|
|
23885
23958
|
}
|
|
23886
23959
|
if (def.protocol) {
|
|
23887
23960
|
def.protocol.lastIndex = 0;
|
|
23888
|
-
if (!def.protocol.test(
|
|
23961
|
+
if (!def.protocol.test(url3.protocol.endsWith(":") ? url3.protocol.slice(0, -1) : url3.protocol)) {
|
|
23889
23962
|
payload.issues.push({
|
|
23890
23963
|
code: "invalid_format",
|
|
23891
23964
|
format: "url",
|
|
@@ -23898,7 +23971,7 @@ var $ZodURL2 = /* @__PURE__ */ $constructor2("$ZodURL", (inst, def) => {
|
|
|
23898
23971
|
}
|
|
23899
23972
|
}
|
|
23900
23973
|
if (def.normalize) {
|
|
23901
|
-
payload.value =
|
|
23974
|
+
payload.value = url3.href;
|
|
23902
23975
|
} else {
|
|
23903
23976
|
payload.value = trimmed;
|
|
23904
23977
|
}
|
|
@@ -33000,7 +33073,7 @@ var ZodURL2 = /* @__PURE__ */ $constructor2("ZodURL", (inst, def) => {
|
|
|
33000
33073
|
$ZodURL2.init(inst, def);
|
|
33001
33074
|
ZodStringFormat2.init(inst, def);
|
|
33002
33075
|
});
|
|
33003
|
-
function
|
|
33076
|
+
function url3(params) {
|
|
33004
33077
|
return _url2(ZodURL2, params);
|
|
33005
33078
|
}
|
|
33006
33079
|
function httpUrl2(params) {
|
|
@@ -35994,8 +36067,15 @@ var lsp_rename = tool({
|
|
|
35994
36067
|
}
|
|
35995
36068
|
});
|
|
35996
36069
|
// src/index.ts
|
|
36070
|
+
function resolveProjectName(project, directory) {
|
|
36071
|
+
const runtimeProjectName = "name" in project && typeof project.name === "string" ? project.name : undefined;
|
|
36072
|
+
return runtimeProjectName || path8.basename(directory) || "oh-my-opencode-lite";
|
|
36073
|
+
}
|
|
35997
36074
|
var OhMyOpenCodeLite = async (ctx) => {
|
|
35998
|
-
const
|
|
36075
|
+
const { client, directory, project, worktree, $: shell, serverUrl } = ctx;
|
|
36076
|
+
const worktreeDirectory = worktree || directory;
|
|
36077
|
+
const projectName = resolveProjectName(project, directory);
|
|
36078
|
+
const config3 = loadPluginConfig(directory);
|
|
35999
36079
|
const agentDefs = createAgents(config3);
|
|
36000
36080
|
const agents = getAgentConfigs(config3);
|
|
36001
36081
|
const modelArrayMap = {};
|
|
@@ -36034,7 +36114,7 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
36034
36114
|
log("[plugin] initialized with tmux config", {
|
|
36035
36115
|
tmuxConfig,
|
|
36036
36116
|
rawTmuxConfig: config3.tmux,
|
|
36037
|
-
directory
|
|
36117
|
+
directory
|
|
36038
36118
|
});
|
|
36039
36119
|
try {
|
|
36040
36120
|
syncSkillsOnStartup();
|
|
@@ -36045,36 +36125,35 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
36045
36125
|
if (tmuxConfig.enabled) {
|
|
36046
36126
|
startTmuxCheck();
|
|
36047
36127
|
}
|
|
36048
|
-
const projectName = path8.basename(ctx.directory) || "oh-my-opencode-lite";
|
|
36049
36128
|
let backgroundManager;
|
|
36050
36129
|
const delegationManager = new DelegationManager({
|
|
36051
|
-
directory
|
|
36130
|
+
directory,
|
|
36131
|
+
worktreeDirectory,
|
|
36132
|
+
projectName,
|
|
36133
|
+
shell,
|
|
36052
36134
|
config: config3.delegation,
|
|
36053
36135
|
getActiveTaskIds: (rootSessionId) => backgroundManager?.getActiveTaskIds(rootSessionId) ?? []
|
|
36054
36136
|
});
|
|
36055
|
-
backgroundManager = new BackgroundTaskManager(ctx, tmuxConfig, config3, delegationManager);
|
|
36137
|
+
backgroundManager = new BackgroundTaskManager(ctx, tmuxConfig, config3, delegationManager, worktreeDirectory);
|
|
36056
36138
|
const backgroundTools = createBackgroundTools(ctx, backgroundManager, tmuxConfig, config3);
|
|
36057
36139
|
const mcps = createBuiltinMcps(config3.disabled_mcps, config3.thoth);
|
|
36058
36140
|
const tmuxSessionManager = new TmuxSessionManager(ctx, tmuxConfig);
|
|
36059
36141
|
const autoUpdateChecker = createAutoUpdateCheckerHook(ctx, {
|
|
36060
36142
|
showStartupToast: true,
|
|
36061
36143
|
autoUpdate: true
|
|
36062
|
-
});
|
|
36144
|
+
}, shell);
|
|
36063
36145
|
const phaseReminderHook = createPhaseReminderHook();
|
|
36064
|
-
const clarificationGateHook = createClarificationGateHook({
|
|
36065
|
-
clarificationGate: config3.clarificationGate
|
|
36066
|
-
});
|
|
36067
36146
|
const postReadNudgeHook = createPostReadNudgeHook();
|
|
36068
36147
|
const thothMemHook = createThothMemHook({
|
|
36069
36148
|
project: projectName,
|
|
36070
|
-
directory
|
|
36149
|
+
directory,
|
|
36071
36150
|
thoth: config3.thoth,
|
|
36072
36151
|
enabled: true
|
|
36073
36152
|
});
|
|
36074
36153
|
const chatHeadersHook = createChatHeadersHook(ctx);
|
|
36075
36154
|
const delegateTaskRetryHook = createDelegateTaskRetryHook(ctx);
|
|
36076
36155
|
const jsonErrorRecoveryHook = createJsonErrorRecoveryHook(ctx);
|
|
36077
|
-
const foregroundFallback = new ForegroundFallbackManager(
|
|
36156
|
+
const foregroundFallback = new ForegroundFallbackManager(client, runtimeChains, config3.fallback?.enabled !== false && Object.keys(runtimeChains).length > 0);
|
|
36078
36157
|
return {
|
|
36079
36158
|
name: "oh-my-opencode-lite",
|
|
36080
36159
|
agent: agents,
|
|
@@ -36183,7 +36262,6 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
36183
36262
|
},
|
|
36184
36263
|
"experimental.chat.messages.transform": async (input, output) => {
|
|
36185
36264
|
await phaseReminderHook["experimental.chat.messages.transform"](input, output);
|
|
36186
|
-
await clarificationGateHook["experimental.chat.messages.transform"](input, output);
|
|
36187
36265
|
},
|
|
36188
36266
|
"experimental.session.compacting": async (input, output) => {
|
|
36189
36267
|
if (thothMemHook["experimental.session.compacting"]) {
|